You are viewing a plain text version of this content. The canonical link for it is here.
Posted to notifications@freemarker.apache.org by dd...@apache.org on 2017/08/07 22:32:11 UTC
[09/21] incubator-freemarker git commit: FREEMARKER-63: Removed
TemplateTransformModel and the old TemplateDirectiveModel, renamed
TemplateDirectiveModel2 to TemplateDirectiveModel. Removed the temporary
`<~...>` syntax; now `<@...>` is used to call the
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/52a5f9eb/freemarker-core/src/main/java/org/apache/freemarker/core/model/ArgumentArrayLayout.java
----------------------------------------------------------------------
diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/model/ArgumentArrayLayout.java b/freemarker-core/src/main/java/org/apache/freemarker/core/model/ArgumentArrayLayout.java
new file mode 100644
index 0000000..2a34703
--- /dev/null
+++ b/freemarker-core/src/main/java/org/apache/freemarker/core/model/ArgumentArrayLayout.java
@@ -0,0 +1,199 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.freemarker.core.model;
+
+import org.apache.freemarker.core.util.StringToIndexMap;
+
+/**
+ * {@link TemplateCallableModel} subinterfaces define a method called {@code execute}, which has an argument array
+ * parameter, whose layout this class describes. Each parameter has a constant index in this array, which is the same
+ * for all invocations of the same {@link TemplateCallableModel} object (regardless if there are omitted optional
+ * parameters). Thus, the argument values can always be accessed at these constant indexes; no runtime name lookup is
+ * needed inside the {@code execute} method of the {@link TemplateCallableModel} implementation. The
+ * {@link ArgumentArrayLayout} object is usually stored a static final field of the {@link TemplateCallableModel}
+ * implementation class.
+ * <p>
+ * The layout of the array is as follows:
+ * <ol>
+ * <li>
+ * {@link #getPredefinedPositionalArgumentCount()} elements for the predefined positional parameters. Index 0
+ * corresponds to the 1st positional parameter. For omitted parameters the corresponding array element is {@code
+ * null}.
+ * <li>
+ * {@link #getPredefinedNamedArgumentsMap()}{@code .size()} elements for the predefined named arguments. These are at
+ * the indexes returned by {@link #getPredefinedNamedArgumentsMap()}{@code .get(String name)}. For omitted arguments
+ * the corresponding array element is {@code null}.
+ * <li>
+ * If there's a positional varargs argument, then one element for the positional varargs parameter, at
+ * index {@link #getPositionalVarargsArgumentIndex()}.
+ * <li>
+ * If there's a named varargs argument, then one element for the positional varargs parameter, at
+ * index {@link #getNamedVarargsArgumentIndex()}.
+ * </ol>
+ * <p>
+ * The length of the array is {@link #getTotalLength()}}, or more, in which case the extra elements should be
+ * ignored.
+ * <p>
+ * Instances of this class are immutable, thread-safe objects.
+ */
+public final class ArgumentArrayLayout {
+ private final int predefinedPositionalArgumentCount;
+ private final StringToIndexMap predefinedNamedArgumentsMap;
+
+ private final int positionalVarargsArgumentIndex;
+ private final int namedVarargsArgumentIndex;
+ private final int arrayLength;
+
+ /** Constant to be used when the {@link TemplateCallableModel} has no parameters. */
+ public static final ArgumentArrayLayout PARAMETERLESS = new ArgumentArrayLayout(
+ 0, false,
+ null, false);
+
+ /** Constant to be used when the {@link TemplateCallableModel} has 1 positional parameter, and no others. */
+ public static final ArgumentArrayLayout SINGLE_POSITIONAL_PARAMETER = new ArgumentArrayLayout(
+ 1, false,
+ null, false);
+
+ /**
+ * Creates a new instance, or returns some of the equivalent static constants (such as {@link #PARAMETERLESS} or
+ * {@link #SINGLE_POSITIONAL_PARAMETER}).
+ *
+ * @param predefinedPositionalArgumentCount
+ * The highest allowed number of positional arguments, not counting the positional varargs argument. The
+ * actual positional argument count can be less than this if there are optional positional argument. When
+ * calling the {@code execute} method of the {@link TemplateCallableModel}, this many items will be reserved
+ * for the positional arguments in the argument array (not counting the item for the positional varargs
+ * argument, if there's one). Positional arguments above this count will go to the varargs argument (if
+ * there's one, otherwise it's an error).
+ * @param hasPositionalVarargsArgument
+ * Specifies if there's a varargs argument into which positional arguments that aren't predefined are
+ * collected
+ * @param predefinedNamedArgumentsMap
+ * The valid names for named arguments (not counting named varargs arguments), and their indexes in the
+ * argument array passed to the {@code execute} method of the {@link TemplateCallableModel}. Can be {@code
+ * null}, which is equivalent to {@link StringToIndexMap#EMPTY}. Indexes must fall into the range starting
+ * with {@code predefinedPositionalArgumentCount} (inclusive), and ending with {@code
+ * predefinedPositionalArgumentCount}{@code + predefinedNamedArgumentsMap.size()} (exclusive). If not, an
+ * {@link IllegalArgumentException} will be thrown. (As {@link ArgumentArrayLayout}-s are normally created
+ * during class initialization, such an exception will later cause {@link NoClassDefFoundError} "Could not
+ * initialize" exceptions every time you try to access the class, which are not very informative. Only for
+ * the first access of the class will you get an {@link ExceptionInInitializerError} with the {@link
+ * IllegalArgumentException} as its cause exception, which contains the error details.)
+ * @param hasNamedVarargsArgument
+ * Specifies if there's a varargs argument into which named arguments that aren't predefined are collected
+ *
+ * @throws IllegalArgumentException
+ * If the {@code predefinedNamedArgumentsMap} contains indexes that are out of range. See the documentation
+ * of that parameter for more.
+ */
+ public static ArgumentArrayLayout create(
+ int predefinedPositionalArgumentCount, boolean hasPositionalVarargsArgument,
+ StringToIndexMap predefinedNamedArgumentsMap, boolean hasNamedVarargsArgument) {
+ if ((predefinedNamedArgumentsMap == null || predefinedNamedArgumentsMap == StringToIndexMap.EMPTY)
+ && !hasPositionalVarargsArgument && !hasNamedVarargsArgument) {
+ if (predefinedPositionalArgumentCount == 0) {
+ return PARAMETERLESS;
+ }
+ if (predefinedPositionalArgumentCount == 1) {
+ return SINGLE_POSITIONAL_PARAMETER;
+ }
+ }
+ return new ArgumentArrayLayout(
+ predefinedPositionalArgumentCount, hasPositionalVarargsArgument,
+ predefinedNamedArgumentsMap, hasNamedVarargsArgument);
+ }
+
+ private ArgumentArrayLayout(int predefinedPositionalArgumentCount, boolean hasPositionalVarargsArgument,
+ StringToIndexMap predefinedNamedArgumentsMap, boolean hasNamedVarargsArgument) {
+ if (predefinedNamedArgumentsMap == null) {
+ predefinedNamedArgumentsMap = StringToIndexMap.EMPTY;
+ }
+
+ this.predefinedPositionalArgumentCount = predefinedPositionalArgumentCount;
+ this.predefinedNamedArgumentsMap = predefinedNamedArgumentsMap;
+
+ int arrayLength = predefinedPositionalArgumentCount + predefinedNamedArgumentsMap.size();
+ if (hasPositionalVarargsArgument) {
+ positionalVarargsArgumentIndex = arrayLength;
+ arrayLength++;
+ } else {
+ positionalVarargsArgumentIndex = -1;
+ }
+ if (hasNamedVarargsArgument) {
+ namedVarargsArgumentIndex = arrayLength;
+ arrayLength++;
+ } else {
+ namedVarargsArgumentIndex = -1;
+ }
+ this.arrayLength = arrayLength;
+
+ predefinedNamedArgumentsMap.checkIndexRange(predefinedPositionalArgumentCount);
+ }
+
+ /**
+ * See the related parameter of {@link ArgumentArrayLayout#create(int, boolean, StringToIndexMap, boolean)}.
+ */
+ public int getPredefinedPositionalArgumentCount() {
+ return predefinedPositionalArgumentCount;
+ }
+
+ /**
+ * See the related parameter of {@link ArgumentArrayLayout#create(int, boolean, StringToIndexMap, boolean)}.
+ */
+ public StringToIndexMap getPredefinedNamedArgumentsMap() {
+ return predefinedNamedArgumentsMap;
+ }
+
+ /**
+ * Returns the index of the varargs argument into which positional arguments that aren't predefined are collected,
+ * or -1 if there's no such varargs argument. The value of the positional varargs argument is a {@link
+ * TemplateSequenceModel} that collects all positional arguments whose index would be greater than or equal to
+ * {@link #getPredefinedPositionalArgumentCount()}.
+ *
+ * @return -1 if there's no such named argument
+ */
+ public int getPositionalVarargsArgumentIndex() {
+ return positionalVarargsArgumentIndex;
+ }
+
+ /**
+ * Returns the index of the varargs argument into which named arguments that aren't predefined are collected, or -1
+ * if there's no such varargs argument. The value of the named varargs argument is a {@link TemplateHashModelEx2}
+ * with string keys that collects all the named arguments that aren't present in the {@link
+ * #getPredefinedNamedArgumentsMap()}. The iteration order of this hash follows the order in which the arguments
+ * were specified on the call site (in the template, typically).
+ *
+ * @return -1 if there's no such named argument
+ */
+ public int getNamedVarargsArgumentIndex() {
+ return namedVarargsArgumentIndex;
+ }
+
+ /**
+ * Returns the required (minimum) length of the {@code args} array that's passed to the {@code execute} method. As
+ * there's an index reserved for each predefined parameters, this length always includes the space reserved for
+ * optional parameters as well; it's not why it's said to be a minimum length. It's a minimum length because a
+ * longer array might be reused for better performance (but {@code execute} should never read those excess
+ * elements).
+ */
+ public int getTotalLength() {
+ return arrayLength;
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/52a5f9eb/freemarker-core/src/main/java/org/apache/freemarker/core/model/CallPlace.java
----------------------------------------------------------------------
diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/model/CallPlace.java b/freemarker-core/src/main/java/org/apache/freemarker/core/model/CallPlace.java
index 6518013..7761eeb 100644
--- a/freemarker-core/src/main/java/org/apache/freemarker/core/model/CallPlace.java
+++ b/freemarker-core/src/main/java/org/apache/freemarker/core/model/CallPlace.java
@@ -24,10 +24,8 @@ import java.io.Writer;
import java.util.IdentityHashMap;
import org.apache.freemarker.core.CallPlaceCustomDataInitializationException;
-import org.apache.freemarker.core.DirectiveCallPlace;
import org.apache.freemarker.core.Environment;
import org.apache.freemarker.core.Template;
-import org.apache.freemarker.core.TemplateCallableModelUtils;
import org.apache.freemarker.core.TemplateException;
import org.apache.freemarker.core.util.CommonSupplier;
@@ -49,19 +47,29 @@ public interface CallPlace {
boolean hasNestedContent();
/**
- * The number of loop variables specified for this call.
+ * The number of loop variables specified for this call. Note that users are generally allowed to omit loop
+ * variables when calling a directive. So this can be useful to avoid calculating the values of loop variables that
+ * the caller will not receive anyway. While it's an error if the user specifies too many loop variables, the
+ * implementor of the {@link TemplateDirectiveModel} shouldn't check for that condition, as far as they will call
+ * {@link #executeNestedContent(TemplateModel[], Writer, Environment)}, which will the should throw a {@link
+ * TemplateException} with a descriptive error message then.
*/
int getLoopVariableCount();
/**
* Executed the nested content; it there's none, it just does nothing.
*
- * @param loopVariableValues
- * The loop variables to pass to the nested content; not {@code null} (use {@link
- * TemplateCallableModelUtils#EMPTY_TEMPLATE_MODEL_ARRAY}. Its length must be equal to
- * {@link #getLoopVariableCount()}.
+ * @param loopVarValues
+ * The loop variable values to pass to the nested content, or {@code null} if there's none. It's not a
+ * problem if this array is longer than the number of loop variables than the caller of the directive has
+ * declared (as in {@code <@foo bar; i, j />} there are 2 loop variables declared); the directive
+ * caller simply won't receive the excess variables. If the caller declares more loop variables than the
+ * length of this array though, then a {@link TemplateException} will thrown by FreeMarker with a
+ * descriptive error message. Thus, the caller of this method need not be concerned about the
+ * number of loop variables declared by the caller (unless to avoid calculating loop variable values
+ * unnecessarily, in which case use {@link #getLoopVariableCount()}).
*/
- void executeNestedContent(TemplateModel[] loopVariableValues, Writer out, Environment env)
+ void executeNestedContent(TemplateModel[] loopVarValues, Writer out, Environment env)
throws TemplateException, IOException;
// -------------------------------------------------------------------------------------------------------------
@@ -102,41 +110,38 @@ public interface CallPlace {
/**
* Returns the custom data, or if that's {@code null}, then it creates and stores it in an atomic operation then
* returns it. This method is thread-safe, however, it doesn't ensure thread safe (like synchronized) access to the
- * custom data itself. See the top-level documentation of {@link DirectiveCallPlace} to understand the scope and
- * life-cycle of the custom data. Be sure that the custom data only depends on things that get their final value
- * during template parsing, not on runtime settings.
- *
+ * custom data itself. Be sure that the custom data only depends on things that get their final value during
+ * template parsing, not on runtime settings.
* <p>
* This method will block other calls while the {@code supplier} is executing, thus, the object will be
* <em>usually</em> created only once, even if multiple threads request the value when it's still {@code null}. It
* doesn't stand though when {@code providerIdentity} mismatches occur (see later). Furthermore, then it's also
- * possible that multiple objects created by the same {@link CommonSupplier} will be in use on the same time, because
- * of directive executions already running in parallel, and because of memory synchronization delays (hardware
- * dependent) between the threads.
+ * possible that multiple objects created by the same {@link CommonSupplier} will be in use on the same time,
+ * because of directive executions already running in parallel, and because of memory synchronization delays
+ * (hardware dependent) between the threads.
*
* @param providerIdentity
- * This is usually the class of the {@link TemplateDirectiveModel} that creates (and uses) the custom
- * data, or if you are using your own class for the custom data object (as opposed to a class from some
- * more generic API), then that class. This is needed as the same call place might calls different
- * directives depending on runtime conditions, and so it must be ensured that these directives won't
- * accidentally read each other's custom data, ending up with class cast exceptions or worse. In the
- * current implementation, if there's a {@code providerIdentity} mismatch (means, the
- * {@code providerIdentity} object used when the custom data was last set isn't the exactly same object
- * as the one provided with the parameter now), the previous custom data will be just ignored as if it
- * was {@code null}. So if multiple directives that use the custom data feature use the same call place,
- * the caching of the custom data can be inefficient, as they will keep overwriting each other's custom
- * data. (In a more generic implementation the {@code providerIdentity} would be a key in a
- * {@link IdentityHashMap}, but then this feature would be slower, while {@code providerIdentity}
- * mismatches aren't occurring in most applications.)
+ * This is usually the class of the {@link TemplateDirectiveModel} that creates (and uses) the custom data,
+ * or if you are using your own class for the custom data object (as opposed to a class from some more
+ * generic API), then that class. This is needed as the same call place might calls different directives
+ * depending on runtime conditions, and so it must be ensured that these directives won't accidentally read
+ * each other's custom data, ending up with class cast exceptions or worse. In the current implementation,
+ * if there's a {@code providerIdentity} mismatch (means, the {@code providerIdentity} object used when the
+ * custom data was last set isn't the exactly same object as the one provided with the parameter now), the
+ * previous custom data will be just ignored as if it was {@code null}. So if multiple directives that use
+ * the custom data feature use the same call place, the caching of the custom data can be inefficient, as
+ * they will keep overwriting each other's custom data. (In a more generic implementation the {@code
+ * providerIdentity} would be a key in a {@link IdentityHashMap}, but then this feature would be slower,
+ * while {@code providerIdentity} mismatches aren't occurring in most applications.)
* @param supplier
- * Called when the custom data wasn't yet set, to invoke its initial value. If this parameter is
- * {@code null} and the custom data wasn't set yet, then {@code null} will be returned. The returned
- * value of {@link CommonSupplier#get()} can be any kind of object, but can't be {@code null}.
+ * Called when the custom data wasn't yet set, to invoke its initial value. If this parameter is {@code
+ * null} and the custom data wasn't set yet, then {@code null} will be returned. The returned value of
+ * {@link CommonSupplier#get()} can be any kind of object, but can't be {@code null}.
*
* @return The current custom data object, or possibly {@code null} if there was no {@link CommonSupplier} provided.
*
* @throws CallPlaceCustomDataInitializationException
- * If the {@link CommonSupplier} had to be invoked but failed.
+ * If the {@link CommonSupplier} had to be invoked but failed.
*/
Object getOrCreateCustomData(Object providerIdentity, CommonSupplier<?> supplier)
throws CallPlaceCustomDataInitializationException;
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/52a5f9eb/freemarker-core/src/main/java/org/apache/freemarker/core/model/TemplateCallableModel.java
----------------------------------------------------------------------
diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/model/TemplateCallableModel.java b/freemarker-core/src/main/java/org/apache/freemarker/core/model/TemplateCallableModel.java
index 3588ea7..2f30981 100644
--- a/freemarker-core/src/main/java/org/apache/freemarker/core/model/TemplateCallableModel.java
+++ b/freemarker-core/src/main/java/org/apache/freemarker/core/model/TemplateCallableModel.java
@@ -19,77 +19,11 @@
package org.apache.freemarker.core.model;
-import java.util.Collection;
-
-import org.apache.freemarker.core.util.StringToIndexMap;
-
/**
- * Super interface of {@link TemplateFunctionModel} and {@link TemplateDirectiveModel2}.
+ * Super interface of {@link TemplateFunctionModel} and {@link TemplateDirectiveModel}.
*/
public interface TemplateCallableModel extends TemplateModel {
- // -------------------------------------------------------------------------------------------------------------
- // Arguments:
-
- /**
- * The highest allowed number of positional arguments, not counting the positional varargs argument. The actual
- * positional argument count can be less than this if there are optional positional argument. When calling the
- * {@code execute} method, this many items will be reserved for the positional arguments in the argument array (not
- * counting the item for the positional varargs argument, if there's one). Positional arguments
- * above this count will go to the varargs argument (if there's one, otherwise it's an error).
- */
- int getPredefinedPositionalArgumentCount();
-
- /**
- * Tells if there's no position varargs argument. If there is, then it must be in the argument array at the
- * index equals to {@link #getPredefinedPositionalArgumentCount()}. The positional varargs argument is a
- * {@link TemplateSequenceModel} that collects all positional arguments whose index would be greater than
- * or equal to {@link #getPredefinedPositionalArgumentCount()}.
- */
- boolean hasPositionalVarargsArgument();
-
- /**
- * For the given argument name (that corresponds to a parameter that meant to be passed by name, not by position)
- * return its intended index in the {@code args} array argument of the {@code execute} method, or -1 if there's
- * no such parameter. Consider using a static final {@link StringToIndexMap} field to implement this.
- *
- * @return -1 if there's no such named argument
- */
- int getPredefinedNamedArgumentIndex(String name);
-
- /**
- * Returns the index of the named varargs argument in the argument array, or -1 if there's no named varargs
- * argument. The named varargs argument is a {@link TemplateHashModelEx2} with string keys that collects all
- * the named arguments for which {@link #getPredefinedNamedArgumentIndex(String)} returns -1. The iteration order of this
- * hash follows the order in which the arguments were specified in the calling template.
- *
- * @return -1 if there's no named varargs argument
- */
- int getNamedVarargsArgumentIndex();
-
- /**
- * The valid names for arguments that are passed by name (not by position), in the order as they should be displayed
- * in error messages, or {@code null} if there's none. If you have implemented
- * {@link #getPredefinedNamedArgumentIndex(String)} with a {@link StringToIndexMap}, you should return
- * {@link StringToIndexMap#getKeys()} here.
- */
- Collection<String> getPredefinedNamedArgumentNames();
-
- /**
- * The required (minimum) length of the {@code args} array passed to the {@code execute} method. This length always
- * includes the space reserved for optional arguments; it's not why it's said to be a minimum length. It's a minimum
- * length because a longer array might be reused for better performance (but {@code execute} should never read
- * those excess elements).
- * The return value should be equal to the sum of these (but we don't want to calculate it on-the-fly,
- * for speed), or else {@link IndexOutOfBoundsException}-s might will occur:
- * <ul>
- * <li>{@link #getPredefinedPositionalArgumentCount()} (note that predefined optional arguments are counted in)
- * <li>If {@link #hasPositionalVarargsArgument()} is {@code true}, then 1, else 0.
- * <li>Size of {@link #getPredefinedNamedArgumentNames()} (again, predefined optional arguments are counted in)
- * <li>If {@link #getNamedVarargsArgumentIndex()} is not -1, then 1, else 0. (Also, obviously, if
- * {@link #getNamedVarargsArgumentIndex()} is not -1, then it's one less than the return value of this method.)
- * </ul>
- */
- int getArgumentArraySize();
+ ArgumentArrayLayout getArgumentArrayLayout();
}
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/52a5f9eb/freemarker-core/src/main/java/org/apache/freemarker/core/model/TemplateDirectiveBody.java
----------------------------------------------------------------------
diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/model/TemplateDirectiveBody.java b/freemarker-core/src/main/java/org/apache/freemarker/core/model/TemplateDirectiveBody.java
deleted file mode 100644
index 787d9d8..0000000
--- a/freemarker-core/src/main/java/org/apache/freemarker/core/model/TemplateDirectiveBody.java
+++ /dev/null
@@ -1,43 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-
-package org.apache.freemarker.core.model;
-
-import java.io.IOException;
-import java.io.Writer;
-
-import org.apache.freemarker.core.TemplateException;
-
-/**
- * Represents the nested content of a directive ({@link TemplateDirectiveModel}) invocation. An implementation of this
- * class is passed to {@link TemplateDirectiveModel#execute(org.apache.freemarker.core.Environment,
- * java.util.Map, TemplateModel[], TemplateDirectiveBody)}. The implementation of the method is
- * free to invoke it for any number of times, with any writer.
- */
-public interface TemplateDirectiveBody {
- /**
- * Renders the body of the directive body to the specified writer. The
- * writer is not flushed after the rendering. If you pass the environment's
- * writer, there is no need to flush it. If you supply your own writer, you
- * are responsible to flush/close it when you're done with using it (which
- * might be after multiple renderings).
- * @param out the writer to write the output to.
- */
- void render(Writer out) throws TemplateException, IOException;
-}
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/52a5f9eb/freemarker-core/src/main/java/org/apache/freemarker/core/model/TemplateDirectiveModel.java
----------------------------------------------------------------------
diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/model/TemplateDirectiveModel.java b/freemarker-core/src/main/java/org/apache/freemarker/core/model/TemplateDirectiveModel.java
index d9b2f96..6995afe 100644
--- a/freemarker-core/src/main/java/org/apache/freemarker/core/model/TemplateDirectiveModel.java
+++ b/freemarker-core/src/main/java/org/apache/freemarker/core/model/TemplateDirectiveModel.java
@@ -1,67 +1,48 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-
package org.apache.freemarker.core.model;
import java.io.IOException;
-import java.util.Map;
+import java.io.Writer;
import org.apache.freemarker.core.Environment;
import org.apache.freemarker.core.TemplateException;
-import org.apache.freemarker.core.util.DeepUnwrap;
/**
- * "directive" template language data type: used as user-defined directives
- * (much like macros) in templates. They can do arbitrary actions, write arbitrary
- * text to the template output, and trigger rendering of their nested content for
- * any number of times.
- *
- * <p>They are used in templates like {@code <@myDirective foo=1 bar="wombat">...</...@myDirective>} (or as
- * {@code <@myDirective foo=1 bar="wombat" />} - the nested content is optional).
+ * A {@link TemplateCallableModel} that (progressively) prints it result into the {@code out} object, instead of
+ * returning a single result at the end of the execution. Many of these won't print anything, but has other
+ * side-effects why it's useful for calling them, or do flow control. They are used in templates like
+ * {@code <@myDirective foo=1 bar="wombat">...</...@myDirective>} (or as {@code <@myDirective foo=1 bar="wombat" />} -
+ * the nested content is optional).
+ * <p>
+ * When called from expression context (and if the template language allows that!), the printed output will be captured,
+ * and will be the return value of the call. Depending on the output format of the directive, the type of that value
+ * will be {@link TemplateMarkupOutputModel} or {@link String}.
+ * <p>
+ * Note that {@link TemplateDirectiveModel} is a relatively low-level interface that puts more emphasis on
+ * performance than on ease of implementation. TODO [FM3]: Introduce a more convenient way for implementing directives.
*/
-public interface TemplateDirectiveModel extends TemplateModel {
+public interface TemplateDirectiveModel extends TemplateCallableModel {
+
/**
- * Executes this user-defined directive; called by FreeMarker when the user-defined
- * directive is called in the template.
+ * @param args
+ * The of argument values. Not {@code null}. If a parameter was omitted on the caller side, the
+ * corresponding array element will be {@code null}. For the indexed of arguments, see argument array layout
+ * in the {@link TemplateCallableModel} documentation.
+ * @param callPlace
+ * The place (in a template, normally) where this directive was called from. Not {@code null}. Note that
+ * {@link CallPlace#executeNestedContent(TemplateModel[], Writer, Environment)} can be used to execute the
+ * nested content. If the directive doesn't support nested content, it should check {@link
+ * CallPlace#hasNestedContent()} that return {@code false}, and otherwise throw exception.
+ * @param out
+ * Print the output here (if there's any)
+ * @param env
+ * The current processing environment. Not {@code null}.
*
- * @param env the current processing environment. Note that you can access
- * the output {@link java.io.Writer Writer} by {@link Environment#getOut()}.
- * @param params the parameters (if any) passed to the directive as a
- * map of key/value pairs where the keys are {@link String}-s and the
- * values are {@link TemplateModel} instances. This is never
- * <code>null</code>. If you need to convert the template models to POJOs,
- * you can use the utility methods in the {@link DeepUnwrap} class.
- * @param loopVars an array that corresponds to the "loop variables", in
- * the order as they appear in the directive call. ("Loop variables" are out-parameters
- * that are available to the nested body of the directive; see in the Manual.)
- * You set the loop variables by writing this array. The length of the array gives the
- * number of loop-variables that the caller has specified.
- * Never <code>null</code>, but can be a zero-length array.
- * @param body an object that can be used to render the nested content (body) of
- * the directive call. If the directive call has no nested content (i.e., it's like
- * <@myDirective /> or <@myDirective></@myDirective>), then this will be
- * <code>null</code>.
- *
- * @throws TemplateException If any problem occurs that's not an {@link IOException} during writing the template
- * output.
- * @throws IOException When writing the template output fails.
+ * @throws TemplateException
+ * If any problem occurs that's not an {@link IOException} during writing the template output.
+ * @throws IOException
+ * When writing the template output fails.
*/
- void execute(Environment env, Map params, TemplateModel[] loopVars,
- TemplateDirectiveBody body) throws TemplateException, IOException;
-}
\ No newline at end of file
+ void execute(TemplateModel[] args, CallPlace callPlace, Writer out, Environment env)
+ throws TemplateException, IOException;
+
+}
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/52a5f9eb/freemarker-core/src/main/java/org/apache/freemarker/core/model/TemplateDirectiveModel2.java
----------------------------------------------------------------------
diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/model/TemplateDirectiveModel2.java b/freemarker-core/src/main/java/org/apache/freemarker/core/model/TemplateDirectiveModel2.java
deleted file mode 100644
index 75bf2f2..0000000
--- a/freemarker-core/src/main/java/org/apache/freemarker/core/model/TemplateDirectiveModel2.java
+++ /dev/null
@@ -1,51 +0,0 @@
-package org.apache.freemarker.core.model;
-
-import java.io.IOException;
-import java.io.Writer;
-
-import org.apache.freemarker.core.Environment;
-import org.apache.freemarker.core.TemplateException;
-
-/**
- * A {@link TemplateCallableModel} that (progressively) prints it result into the {@code out} object, instead of
- * returning a single result at the end of the execution. Many of these won't print anything, but has other
- * side-effects why it's useful for calling them, or do flow control. They are used in templates like
- * {@code <@myDirective foo=1 bar="wombat">...</...@myDirective>} (or as {@code <@myDirective foo=1 bar="wombat" />} -
- * the nested content is optional).
- * <p>
- * When called from expression context (and if the template language allows that!), the printed output will be captured,
- * and will be the return value of the call. Depending on the output format of the directive, the type of that value
- * will be {@link TemplateMarkupOutputModel} or {@link String}.
- */
-// TODO [FM3][CF] Rename this to TemplateDirectiveModel
-public interface TemplateDirectiveModel2 extends TemplateCallableModel {
-
- /**
- * @param args
- * Array with {@link #getArgumentArraySize()} elements (or more, in which case the extra elements should be
- * ignored). Not {@code null}. If a parameter was omitted on the caller side, the corresponding array
- * element will be {@code null}. Parameters passed by position will be at the index that corresponds to the
- * position (the 1st argument is at index 0). However, positional parameters over {@link
- * #getPredefinedPositionalArgumentCount()} will be in the positional varargs sequence at index one higher,
- * assuming {@link #hasPositionalVarargsArgument()} is {@code true}. Parameters passed by name (rather than
- * by position) will be at the index returned be {@link #getPredefinedNamedArgumentIndex(String)}, or in the
- * named varargs hash at index {@link #getNamedVarargsArgumentIndex()}, assuming that's not -1.
- * @param callPlace
- * The place (in a template, normally) where this directive was called from. Not {@code null}. Note that
- * {@link CallPlace#executeNestedContent(TemplateModel[], Writer, Environment)} can be used to execute the
- * nested content. If the directive doesn't support nested content, it should check {@link
- * CallPlace#hasNestedContent()} that return {@code false}, and otherwise throw exception.
- * @param out
- * Print the output here (if there's any)
- * @param env
- * The current processing environment. Not {@code null}.
- *
- * @throws TemplateException
- * If any problem occurs that's not an {@link IOException} during writing the template output.
- * @throws IOException
- * When writing the template output fails.
- */
- void execute(TemplateModel[] args, CallPlace callPlace, Writer out, Environment env)
- throws TemplateException, IOException;
-
-}
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/52a5f9eb/freemarker-core/src/main/java/org/apache/freemarker/core/model/TemplateFunctionModel.java
----------------------------------------------------------------------
diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/model/TemplateFunctionModel.java b/freemarker-core/src/main/java/org/apache/freemarker/core/model/TemplateFunctionModel.java
index 7944d81..2ddfca0 100644
--- a/freemarker-core/src/main/java/org/apache/freemarker/core/model/TemplateFunctionModel.java
+++ b/freemarker-core/src/main/java/org/apache/freemarker/core/model/TemplateFunctionModel.java
@@ -7,7 +7,7 @@ import org.apache.freemarker.core.outputformat.OutputFormat;
/**
* A {@link TemplateCallableModel}, which returns its result as a {@link TemplateModel} at the end of the execution.
- * This is in contrast with {@link TemplateDirectiveModel2}, which writes its result progressively to the output.
+ * This is in contrast with {@link TemplateDirectiveModel}, which writes its result progressively to the output.
*
* <p>Some template languages may allow function calls directly embedded into static text, as in
* <code>text#f()text</code>. In that case, the language has to ensure that the return value is formatted according
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/52a5f9eb/freemarker-core/src/main/java/org/apache/freemarker/core/model/TemplateTransformModel.java
----------------------------------------------------------------------
diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/model/TemplateTransformModel.java b/freemarker-core/src/main/java/org/apache/freemarker/core/model/TemplateTransformModel.java
deleted file mode 100644
index 789d9bb..0000000
--- a/freemarker-core/src/main/java/org/apache/freemarker/core/model/TemplateTransformModel.java
+++ /dev/null
@@ -1,54 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-
-package org.apache.freemarker.core.model;
-
-import java.io.IOException;
-import java.io.Writer;
-import java.util.Map;
-
-import org.apache.freemarker.core.util.DeepUnwrap;
-
-/**
- * "transform" template language data type: user-defined directives
- * (much like macros) specialized on filtering output; you should rather use the newer {@link TemplateDirectiveModel}
- * instead. This certainly will be deprecated in FreeMarker 2.4.
- */
-public interface TemplateTransformModel extends TemplateModel {
-
- /**
- * Returns a writer that will be used by the engine to feed the
- * transformation input to the transform. Each call to this method
- * must return a new instance of the writer so that the transformation
- * is thread-safe.
- * @param out the character stream to which to write the transformed output
- * @param args the arguments (if any) passed to the transformation as a
- * map of key/value pairs where the keys are strings and the arguments are
- * TemplateModel instances. This is never null. If you need to convert the
- * template models to POJOs, you can use the utility methods in the
- * {@link DeepUnwrap} class.
- * @return a writer to which the engine will feed the transformation
- * input, or null if the transform does not support nested content (body).
- * The returned writer can implement the {@link TransformControl}
- * interface if it needs advanced control over the evaluation of the
- * transformation body.
- */
- Writer getWriter(Writer out, Map args)
- throws TemplateModelException, IOException;
-}
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/52a5f9eb/freemarker-core/src/main/java/org/apache/freemarker/core/model/TransformControl.java
----------------------------------------------------------------------
diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/model/TransformControl.java b/freemarker-core/src/main/java/org/apache/freemarker/core/model/TransformControl.java
deleted file mode 100644
index cd3965c..0000000
--- a/freemarker-core/src/main/java/org/apache/freemarker/core/model/TransformControl.java
+++ /dev/null
@@ -1,101 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-
-package org.apache.freemarker.core.model;
-
-import java.io.IOException;
-
-import org.apache.freemarker.core.TemplateException;
-
-/**
- * An interface that can be implemented by writers returned from
- * {@link TemplateTransformModel#getWriter(java.io.Writer, java.util.Map)}. The
- * methods on this
- * interfaces are callbacks that will be called by the template engine and that
- * give the writer a chance to better control the evaluation of the transform
- * body. The writer can instruct the engine to skip or to repeat body
- * evaluation, and gets notified about exceptions that are thrown during the
- * body evaluation.
- */
-public interface TransformControl {
- /**
- * Constant returned from {@link #afterBody()} that tells the
- * template engine to repeat transform body evaluation and feed
- * it again to the transform.
- */
- int REPEAT_EVALUATION = 0;
-
- /**
- * Constant returned from {@link #afterBody()} that tells the
- * template engine to end the transform and close the writer.
- */
- int END_EVALUATION = 1;
-
- /**
- * Constant returned from {@link #onStart()} that tells the
- * template engine to skip evaluation of the body.
- */
- int SKIP_BODY = 0;
-
- /**
- * Constant returned from {@link #onStart()} that tells the
- * template engine to evaluate the body.
- */
- int EVALUATE_BODY = 1;
-
- /**
- * Called before the body is evaluated for the first time.
- * @return
- * <ul>
- * <li><tt>SKIP_BODY</tt> if the transform wants to ignore the body. In this
- * case, only {@link java.io.Writer#close()} is called next and processing ends.</li>
- * <li><tt>EVALUATE_BODY</tt> to normally evaluate the body of the transform
- * and feed it to the writer</li>
- * </ul>
- */
- int onStart() throws TemplateModelException, IOException;
-
- /**
- * Called after the body has been evaluated.
- * @return
- * <ul>
- * <li><tt>END_EVALUATION</tt> if the transformation should be ended.</li>
- * <li><tt>REPEAT_EVALUATION</tt> to have the engine re-evaluate the
- * transform body and feed it again to the writer.</li>
- * </ul>
- */
- int afterBody() throws TemplateModelException, IOException;
-
- /**
- * Called if any exception occurs during the transform between the
- * {@link TemplateTransformModel#getWriter(java.io.Writer, java.util.Map)} call
- * and the {@link java.io.Writer#close()} call.
- * @param t the throwable that represents the exception. It can be any
- * non-checked throwable, as well as {@link TemplateException} and
- * {@link java.io.IOException}.
- *
- * @throws Throwable is recommended that the methods rethrow the received
- * throwable. If the method wants to throw another throwable, it should
- * either throw a non-checked throwable, or an instance of
- * {@link TemplateException} and {@link java.io.IOException}. Throwing any
- * other checked exception will cause the engine to rethrow it as
- * a {@link java.lang.reflect.UndeclaredThrowableException}.
- */
- void onError(Throwable t) throws Throwable;
-}
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/52a5f9eb/freemarker-core/src/main/java/org/apache/freemarker/core/util/FTLUtil.java
----------------------------------------------------------------------
diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/util/FTLUtil.java b/freemarker-core/src/main/java/org/apache/freemarker/core/util/FTLUtil.java
index 1da4f62..647ad90 100644
--- a/freemarker-core/src/main/java/org/apache/freemarker/core/util/FTLUtil.java
+++ b/freemarker-core/src/main/java/org/apache/freemarker/core/util/FTLUtil.java
@@ -40,7 +40,6 @@ import org.apache.freemarker.core.model.TemplateNodeModelEx;
import org.apache.freemarker.core.model.TemplateNumberModel;
import org.apache.freemarker.core.model.TemplateScalarModel;
import org.apache.freemarker.core.model.TemplateSequenceModel;
-import org.apache.freemarker.core.model.TemplateTransformModel;
import org.apache.freemarker.core.model.WrapperTemplateModel;
import org.apache.freemarker.core.model.impl.BeanAndStringModel;
import org.apache.freemarker.core.model.impl.BeanModel;
@@ -774,8 +773,6 @@ public final class FTLUtil {
if (TemplateDirectiveModel.class.isAssignableFrom(cl)) {
appendTypeName(sb, typeNamesAppended, "directive");
- } else if (TemplateTransformModel.class.isAssignableFrom(cl)) {
- appendTypeName(sb, typeNamesAppended, "transform");
}
if (TemplateSequenceModel.class.isAssignableFrom(cl)) {
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/52a5f9eb/freemarker-core/src/main/java/org/apache/freemarker/core/util/StringToIndexMap.java
----------------------------------------------------------------------
diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/util/StringToIndexMap.java b/freemarker-core/src/main/java/org/apache/freemarker/core/util/StringToIndexMap.java
index 55b7d5d..9edb980 100644
--- a/freemarker-core/src/main/java/org/apache/freemarker/core/util/StringToIndexMap.java
+++ b/freemarker-core/src/main/java/org/apache/freemarker/core/util/StringToIndexMap.java
@@ -28,6 +28,8 @@ import java.util.Map;
* Maps string keys to non-negative int-s. This isn't a {@link Map}, but a more specialized class. It's immutable.
* It's slower to create than {@link HashMap}, but usually is a bit faster to read, and when {@link HashMap} gets
* unlucky with clashing hash keys, then it can be significantly faster.
+ * <p>
+ * Instances of this class are immutable, thread-safe objects.
*/
public final class StringToIndexMap {
@@ -239,6 +241,31 @@ public final class StringToIndexMap {
return keys != null ? keys.size() : 1;
}
+ /**
+ * Checks if all entries are in the {@code start} - {@code start}+{@code size()} (exclusive) index range.
+ *
+ * @throws IllegalArgumentException If some entry is not in the specified index range.
+ */
+ public void checkIndexRange(int start) {
+ if (buckets == null) {
+ return;
+ }
+
+ int end = start + size();
+ for (Entry bucket : buckets) {
+ Entry entry = bucket;
+ while (entry != null) {
+ if (entry.value < start || entry.value >= end) {
+ throw new IllegalArgumentException(
+ "Entry " + _StringUtil.jQuote(entry.key) + " -> " + entry.value
+ + " is out of allowed index range: " + start + " ..< " + end);
+ }
+ entry = entry.nextInSameBucket;
+ }
+ }
+
+ }
+
private static int getPowerOf2GreaterThanOrEqualTo(int n) {
if (n == 0) {
return 0;
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/52a5f9eb/freemarker-core/src/main/javacc/FTL.jj
----------------------------------------------------------------------
diff --git a/freemarker-core/src/main/javacc/FTL.jj b/freemarker-core/src/main/javacc/FTL.jj
index c42c7a9..9de0521 100644
--- a/freemarker-core/src/main/javacc/FTL.jj
+++ b/freemarker-core/src/main/javacc/FTL.jj
@@ -35,7 +35,7 @@ import org.apache.freemarker.core.outputformat.impl.*;
import org.apache.freemarker.core.model.*;
import org.apache.freemarker.core.model.impl.*;
import org.apache.freemarker.core.util.*;
-import org.apache.freemarker.core.ASTDirDynamicCall.NamedArgument;
+import org.apache.freemarker.core.ASTDynamicTopLevelCall.NamedArgument;
import java.io.*;
import java.util.*;
import java.nio.charset.Charset;
@@ -569,7 +569,7 @@ TOKEN_MGR_DECLS:
return image.charAt(idx + charIdxInName);
}
- private void unifiedCall(Token tok) {
+ private void dynamicTopLevelCall(Token tok) {
char firstChar = tok.image.charAt(0);
if (autodetectTagSyntax && !directiveSyntaxEstablished) {
squBracTagSyntax = (firstChar == '[');
@@ -586,7 +586,7 @@ TOKEN_MGR_DECLS:
SwitchTo(NO_SPACE_EXPRESSION);
}
- private void unifiedCallEnd(Token tok) {
+ private void dynamicTopLevelCallEnd(Token tok) {
char firstChar = tok.image.charAt(0);
if (squBracTagSyntax && firstChar == '<') {
tok.kind = STATIC_TEXT_NON_WS;
@@ -846,13 +846,9 @@ TOKEN:
handleTagSyntaxAndSwitch(matchedToken, DEFAULT);
}
|
- <UNIFIED_CALL : "<@" | "[@" > { unifiedCall(matchedToken); }
+ <DYNAMIC_TOP_LEVEL_CALL : "<@" | "[@" > { dynamicTopLevelCall(matchedToken); }
|
- <UNIFIED_CALL_END : ("<" | "[") "/@" ((<ID>) ("."<ID>)*)? <CLOSE_TAG1>> { unifiedCallEnd(matchedToken); }
- |
- <DYNAMIC_DIRECTIVE_CALL : "<~" | "[~" > { unifiedCall(matchedToken); }
- |
- <DYNAMIC_DIRECTIVE_CALL_END : ("<" | "[") "/~" ((<ID>) ("."<ID>)*)? <CLOSE_TAG1>> { unifiedCallEnd(matchedToken); }
+ <DYNAMIC_TOP_LEVEL_CALL_END : ("<" | "[") "/@" ((<ID>) ("."<ID>)*)? <CLOSE_TAG1>> { dynamicTopLevelCallEnd(matchedToken); }
|
<FTL_HEADER : ("<#ftl" | "[#ftl") <BLANK>> { ftlHeader(matchedToken); }
|
@@ -3034,102 +3030,7 @@ ASTDirCompress Compress() :
}
}
-ASTElement UnifiedMacroTransform() :
-{
- Token start = null, end, t;
- HashMap namedArgs = null;
- ArrayList positionalArgs = null, bodyParameters = null;
- ASTExpression startTagNameExp;
- TemplateElements children;
- ASTExpression exp;
- int pushedCtxCount = 0;
-}
-{
- start = <UNIFIED_CALL>
- exp = ASTExpression()
- {
- if (exp instanceof ASTExpVariable || (exp instanceof ASTExpDot && ((ASTExpDot) exp).onlyHasIdentifiers())) {
- startTagNameExp = exp;
- } else {
- startTagNameExp = null;
- }
- }
- [<TERMINATING_WHITESPACE>]
- (
- LOOKAHEAD(<ID><ASSIGNMENT_EQUALS>)
- namedArgs = NamedArgs()
- |
- positionalArgs = PositionalArgs()
- )
- [
- <SEMICOLON>{bodyParameters = new ArrayList(4); }
- [
- t = <ID> { bodyParameters.add(t.image); }
- (
- <COMMA>
- t = <ID>{bodyParameters.add(t.image); }
- )*
- ]
- ]
- (
- end = <EMPTY_DIRECTIVE_END> { children = TemplateElements.EMPTY; }
- |
- (
- <DIRECTIVE_END> {
- if (bodyParameters != null && iteratorBlockContexts != null && !iteratorBlockContexts.isEmpty()) {
- // It's possible that we shadow a #list/#items loop variable, in which case that must be noted.
- int ctxsLen = iteratorBlockContexts.size();
- int bodyParsLen = bodyParameters.size();
- for (int bodyParIdx = 0; bodyParIdx < bodyParsLen; bodyParIdx++) {
- String bodyParName = (String) bodyParameters.get(bodyParIdx);
- walkCtxSack: for (int ctxIdx = ctxsLen - 1; ctxIdx >= 0; ctxIdx--) {
- ParserIteratorBlockContext ctx
- = (ParserIteratorBlockContext) iteratorBlockContexts.get(ctxIdx);
- if (ctx.loopVarName != null && ctx.loopVarName.equals(bodyParName)) {
- // If it wasn't already shadowed, shadow it:
- if (ctx.kind != ITERATOR_BLOCK_KIND_USER_DIRECTIVE) {
- ParserIteratorBlockContext shadowingCtx = pushIteratorBlockContext();
- shadowingCtx.loopVarName = bodyParName;
- shadowingCtx.kind = ITERATOR_BLOCK_KIND_USER_DIRECTIVE;
- pushedCtxCount++;
- }
- break walkCtxSack;
- }
- }
- }
- }
- }
- children = MixedContentElements()
- end = <UNIFIED_CALL_END>
- {
- for (int i = 0; i < pushedCtxCount; i++) {
- popIteratorBlockContext();
- }
-
- String endTagName = end.image.substring(3, end.image.length() - 1).trim();
- if (endTagName.length() > 0) {
- if (startTagNameExp == null) {
- throw new ParseException("Expecting </@>", template, end);
- } else {
- String startTagName = startTagNameExp.getCanonicalForm();
- if (!endTagName.equals(startTagName)) {
- throw new ParseException("Expecting </@> or </@" + startTagName + ">", template, end);
- }
- }
- }
- }
- )
- )
- {
- ASTElement result = (positionalArgs != null)
- ? new ASTDirUserDefined(exp, positionalArgs, children, bodyParameters)
- : new ASTDirUserDefined(exp, namedArgs, children, bodyParameters);
- result.setLocation(template, start, end);
- return result;
- }
-}
-
-ASTElement DynamicDirectiveCall() :
+ASTElement DynamicTopLevelCall() :
{
Token t;
ASTExpression exp;
@@ -3142,7 +3043,7 @@ ASTElement DynamicDirectiveCall() :
int pushedCtxCount = 0;
}
{
- start = <DYNAMIC_DIRECTIVE_CALL>
+ start = <DYNAMIC_TOP_LEVEL_CALL>
callableValueExp = ASTExpression()
[<TERMINATING_WHITESPACE>]
{
@@ -3278,7 +3179,7 @@ ASTElement DynamicDirectiveCall() :
children = MixedContentElements()
- end = <DYNAMIC_DIRECTIVE_CALL_END>
+ end = <DYNAMIC_TOP_LEVEL_CALL_END>
{
for (int i = 0; i < pushedCtxCount; i++) {
popIteratorBlockContext();
@@ -3301,7 +3202,7 @@ ASTElement DynamicDirectiveCall() :
)
)
{
- ASTElement result = new ASTDirDynamicCall(
+ ASTElement result = new ASTDynamicTopLevelCall(
callableValueExp, false,
trimmedPositionalArgs, trimmedNamedArgs, loopVarNames,
children);
@@ -3715,9 +3616,7 @@ ASTElement FreemarkerDirective() :
|
tp = Compress()
|
- tp = UnifiedMacroTransform()
- |
- tp = DynamicDirectiveCall()
+ tp = DynamicTopLevelCall()
|
tp = Items()
|
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/52a5f9eb/freemarker-servlet/src/main/java/org/apache/freemarker/servlet/IncludePage.java
----------------------------------------------------------------------
diff --git a/freemarker-servlet/src/main/java/org/apache/freemarker/servlet/IncludePage.java b/freemarker-servlet/src/main/java/org/apache/freemarker/servlet/IncludePage.java
index 608f1bb..c6adead 100644
--- a/freemarker-servlet/src/main/java/org/apache/freemarker/servlet/IncludePage.java
+++ b/freemarker-servlet/src/main/java/org/apache/freemarker/servlet/IncludePage.java
@@ -40,12 +40,14 @@ import org.apache.freemarker.core.Environment;
import org.apache.freemarker.core.TemplateException;
import org.apache.freemarker.core._DelayedFTLTypeDescription;
import org.apache.freemarker.core._MiscTemplateException;
+import org.apache.freemarker.core.model.ArgumentArrayLayout;
+import org.apache.freemarker.core.model.CallPlace;
import org.apache.freemarker.core.model.TemplateBooleanModel;
-import org.apache.freemarker.core.model.TemplateDirectiveBody;
import org.apache.freemarker.core.model.TemplateDirectiveModel;
import org.apache.freemarker.core.model.TemplateModel;
import org.apache.freemarker.core.model.TemplateScalarModel;
import org.apache.freemarker.core.util.DeepUnwrap;
+import org.apache.freemarker.core.util.StringToIndexMap;
/**
@@ -60,26 +62,45 @@ import org.apache.freemarker.core.util.DeepUnwrap;
* values of parameters.
*/
public class IncludePage implements TemplateDirectiveModel {
+
private final HttpServletRequest request;
private final HttpServletResponse response;
+
+ private static final int PATH_PARAM_IDX = 0;
+ private static final int INHERIT_PARAMS_PARAM_IDX = 1;
+ private static final int PARAMS_PARAM_IDX = 2;
+
+ private static final String PATH_PARAM_NAME = "path";
+ private static final String INHERIT_PARAMS_PARAM_NAME = "inherit_params";
+ private static final String PARAMS_PARAM_NAME = "params";
+
+ private static final StringToIndexMap NAME_TO_IDX_MAP = StringToIndexMap.of(
+ PATH_PARAM_NAME, PATH_PARAM_IDX,
+ INHERIT_PARAMS_PARAM_NAME, INHERIT_PARAMS_PARAM_IDX
+ );
+
+ private static final ArgumentArrayLayout ARGS_LAYOUT = ArgumentArrayLayout.create(
+ 0, false,
+ NAME_TO_IDX_MAP, false
+ );
public IncludePage(HttpServletRequest request, HttpServletResponse response) {
this.request = request;
this.response = response;
}
-
+
@Override
- public void execute(final Environment env, Map params,
- TemplateModel[] loopVars, TemplateDirectiveBody body)
- throws TemplateException, IOException {
+ public void execute(TemplateModel[] args, CallPlace callPlace, Writer out, Environment env)
+ throws TemplateException, IOException {
// Determine the path
- final TemplateModel path = (TemplateModel) params.get("path");
+ final TemplateModel path = args[PATH_PARAM_IDX];
if (path == null) {
throw new _MiscTemplateException(env, "Missing required parameter \"path\"");
}
if (!(path instanceof TemplateScalarModel)) {
throw new _MiscTemplateException(env,
- "Expected a scalar model. \"path\" is instead ", new _DelayedFTLTypeDescription(path));
+ "Expected a scalar model. \"", PATH_PARAM_NAME, "\" is instead ",
+ new _DelayedFTLTypeDescription(path));
}
final String strPath = ((TemplateScalarModel) path).getAsString();
if (strPath == null) {
@@ -113,21 +134,21 @@ public class IncludePage implements TemplateDirectiveModel {
// Determine inherit_params value
final boolean inheritParams;
- final TemplateModel inheritParamsModel = (TemplateModel) params.get("inherit_params");
+ final TemplateModel inheritParamsModel = args[INHERIT_PARAMS_PARAM_IDX];
if (inheritParamsModel == null) {
// defaults to true when not specified
inheritParams = true;
} else {
if (!(inheritParamsModel instanceof TemplateBooleanModel)) {
throw new _MiscTemplateException(env,
- "\"inherit_params\" should be a boolean but it's a(n) ",
+ "\"", INHERIT_PARAMS_PARAM_NAME, "\" should be a boolean but it's a(n) ",
inheritParamsModel.getClass().getName(), " instead");
}
inheritParams = ((TemplateBooleanModel) inheritParamsModel).getAsBoolean();
}
// Get explicit params, if any
- final TemplateModel paramsModel = (TemplateModel) params.get("params");
+ final TemplateModel paramsModel = args[PARAMS_PARAM_IDX];
// Determine whether we need to wrap the request
final HttpServletRequest wrappedRequest;
@@ -143,7 +164,7 @@ public class IncludePage implements TemplateDirectiveModel {
final Object unwrapped = DeepUnwrap.unwrap(paramsModel);
if (!(unwrapped instanceof Map)) {
throw new _MiscTemplateException(env,
- "Expected \"params\" to unwrap into a java.util.Map. It unwrapped into ",
+ "Expected \"", PARAMS_PARAM_NAME, "\" to unwrap into a java.util.Map. It unwrapped into ",
unwrapped.getClass().getName(), " instead.");
}
paramsMap = (Map) unwrapped;
@@ -163,6 +184,11 @@ public class IncludePage implements TemplateDirectiveModel {
}
}
+ @Override
+ public ArgumentArrayLayout getArgumentArrayLayout() {
+ return ARGS_LAYOUT;
+ }
+
private static final class CustomParamsRequest extends HttpServletRequestWrapper {
private final HashMap paramsMap;
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/52a5f9eb/freemarker-servlet/src/main/java/org/apache/freemarker/servlet/jsp/BodyContentImpl.java
----------------------------------------------------------------------
diff --git a/freemarker-servlet/src/main/java/org/apache/freemarker/servlet/jsp/BodyContentImpl.java b/freemarker-servlet/src/main/java/org/apache/freemarker/servlet/jsp/BodyContentImpl.java
new file mode 100644
index 0000000..af0a8e0
--- /dev/null
+++ b/freemarker-servlet/src/main/java/org/apache/freemarker/servlet/jsp/BodyContentImpl.java
@@ -0,0 +1,222 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.freemarker.servlet.jsp;
+
+import java.io.CharArrayReader;
+import java.io.CharArrayWriter;
+import java.io.IOException;
+import java.io.Reader;
+import java.io.Writer;
+
+import javax.servlet.jsp.JspWriter;
+import javax.servlet.jsp.tagext.BodyContent;
+
+/**
+ * An implementation of BodyContent that buffers it's input to a char[].
+ */
+class BodyContentImpl extends BodyContent {
+ private CharArrayWriter buf;
+
+ BodyContentImpl(JspWriter out, boolean buffer) {
+ super(out);
+ if (buffer) initBuffer();
+ }
+
+ void initBuffer() {
+ buf = new CharArrayWriter();
+ }
+
+ @Override
+ public void flush() throws IOException {
+ if (buf == null) {
+ getEnclosingWriter().flush();
+ }
+ }
+
+ @Override
+ public void clear() throws IOException {
+ if (buf != null) {
+ buf = new CharArrayWriter();
+ } else {
+ throw new IOException("Can't clear");
+ }
+ }
+
+ @Override
+ public void clearBuffer() throws IOException {
+ if (buf != null) {
+ buf = new CharArrayWriter();
+ } else {
+ throw new IOException("Can't clear");
+ }
+ }
+
+ @Override
+ public int getRemaining() {
+ return Integer.MAX_VALUE;
+ }
+
+ @Override
+ public void newLine() throws IOException {
+ write(JspWriterAdapter.NEWLINE);
+ }
+
+ @Override
+ public void close() throws IOException {
+ }
+
+ @Override
+ public void print(boolean arg0) throws IOException {
+ write(arg0 ? Boolean.TRUE.toString() : Boolean.FALSE.toString());
+ }
+
+ @Override
+ public void print(char arg0) throws IOException {
+ write(arg0);
+ }
+
+ @Override
+ public void print(char[] arg0) throws IOException {
+ write(arg0);
+ }
+
+ @Override
+ public void print(double arg0) throws IOException {
+ write(Double.toString(arg0));
+ }
+
+ @Override
+ public void print(float arg0) throws IOException {
+ write(Float.toString(arg0));
+ }
+
+ @Override
+ public void print(int arg0) throws IOException {
+ write(Integer.toString(arg0));
+ }
+
+ @Override
+ public void print(long arg0) throws IOException {
+ write(Long.toString(arg0));
+ }
+
+ @Override
+ public void print(Object arg0) throws IOException {
+ write(arg0 == null ? "null" : arg0.toString());
+ }
+
+ @Override
+ public void print(String arg0) throws IOException {
+ write(arg0);
+ }
+
+ @Override
+ public void println() throws IOException {
+ newLine();
+ }
+
+ @Override
+ public void println(boolean arg0) throws IOException {
+ print(arg0);
+ newLine();
+ }
+
+ @Override
+ public void println(char arg0) throws IOException {
+ print(arg0);
+ newLine();
+ }
+
+ @Override
+ public void println(char[] arg0) throws IOException {
+ print(arg0);
+ newLine();
+ }
+
+ @Override
+ public void println(double arg0) throws IOException {
+ print(arg0);
+ newLine();
+ }
+
+ @Override
+ public void println(float arg0) throws IOException {
+ print(arg0);
+ newLine();
+ }
+
+ @Override
+ public void println(int arg0) throws IOException {
+ print(arg0);
+ newLine();
+ }
+
+ @Override
+ public void println(long arg0) throws IOException {
+ print(arg0);
+ newLine();
+ }
+
+ @Override
+ public void println(Object arg0) throws IOException {
+ print(arg0);
+ newLine();
+ }
+
+ @Override
+ public void println(String arg0) throws IOException {
+ print(arg0);
+ newLine();
+ }
+
+ @Override
+ public void write(int c) throws IOException {
+ if (buf != null) {
+ buf.write(c);
+ } else {
+ getEnclosingWriter().write(c);
+ }
+ }
+
+ @Override
+ public void write(char[] cbuf, int off, int len) throws IOException {
+ if (buf != null) {
+ buf.write(cbuf, off, len);
+ } else {
+ getEnclosingWriter().write(cbuf, off, len);
+ }
+ }
+
+ @Override
+ public String getString() {
+ return buf.toString();
+ }
+
+ @Override
+ public Reader getReader() {
+ return new CharArrayReader(buf.toCharArray());
+ }
+
+ @Override
+ public void writeOut(Writer out) throws IOException {
+ buf.writeTo(out);
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/52a5f9eb/freemarker-servlet/src/main/java/org/apache/freemarker/servlet/jsp/CustomTagAndELFunctionCombiner.java
----------------------------------------------------------------------
diff --git a/freemarker-servlet/src/main/java/org/apache/freemarker/servlet/jsp/CustomTagAndELFunctionCombiner.java b/freemarker-servlet/src/main/java/org/apache/freemarker/servlet/jsp/CustomTagAndELFunctionCombiner.java
index cfb91b6..c51e611 100644
--- a/freemarker-servlet/src/main/java/org/apache/freemarker/servlet/jsp/CustomTagAndELFunctionCombiner.java
+++ b/freemarker-servlet/src/main/java/org/apache/freemarker/servlet/jsp/CustomTagAndELFunctionCombiner.java
@@ -21,17 +21,16 @@ package org.apache.freemarker.servlet.jsp;
import java.io.IOException;
import java.io.Writer;
import java.util.List;
-import java.util.Map;
import org.apache.freemarker.core.Environment;
import org.apache.freemarker.core.TemplateException;
import org.apache.freemarker.core._UnexpectedTypeErrorExplainerTemplateModel;
-import org.apache.freemarker.core.model.TemplateDirectiveBody;
+import org.apache.freemarker.core.model.ArgumentArrayLayout;
+import org.apache.freemarker.core.model.CallPlace;
import org.apache.freemarker.core.model.TemplateDirectiveModel;
import org.apache.freemarker.core.model.TemplateMethodModelEx;
import org.apache.freemarker.core.model.TemplateModel;
import org.apache.freemarker.core.model.TemplateModelException;
-import org.apache.freemarker.core.model.TemplateTransformModel;
import org.apache.freemarker.core.model.impl.JavaMethodModel;
import org.apache.freemarker.core.util.BugException;
import org.apache.freemarker.core.util._ClassUtil;
@@ -46,7 +45,7 @@ class CustomTagAndELFunctionCombiner {
/**
* @param customTag
- * Either a {@link TemplateDirectiveModel} or a {@link TemplateTransformModel}.
+ * A {@link TemplateDirectiveModel}.
*/
static TemplateModel combine(TemplateModel customTag, TemplateMethodModelEx elFunction) {
if (customTag instanceof TemplateDirectiveModel) {
@@ -55,12 +54,6 @@ class CustomTagAndELFunctionCombiner {
(TemplateDirectiveModel) customTag, (JavaMethodModel) elFunction) //
: new TemplateDirectiveModelAndTemplateMethodModelEx( //
(TemplateDirectiveModel) customTag, elFunction);
- } else if (customTag instanceof TemplateTransformModel) {
- return (elFunction instanceof JavaMethodModel)
- ? new TemplateTransformModelAndSimpleMethodModel( //
- (TemplateTransformModel) customTag, (JavaMethodModel) elFunction) //
- : new TemplateTransformModelAndTemplateMethodModelEx( //
- (TemplateTransformModel) customTag, elFunction);
} else {
throw new BugException(
"Unexpected custom JSP tag class: " + _ClassUtil.getShortClassNameOfObject(customTag));
@@ -72,8 +65,7 @@ class CustomTagAndELFunctionCombiner {
* {@link #combine(TemplateModel, TemplateMethodModelEx)}.
*/
static boolean canBeCombinedAsCustomTag(TemplateModel tm) {
- return (tm instanceof TemplateDirectiveModel || tm instanceof TemplateTransformModel)
- && !(tm instanceof CombinedTemplateModel);
+ return (tm instanceof TemplateDirectiveModel) && !(tm instanceof CombinedTemplateModel);
}
/**
@@ -107,16 +99,20 @@ class CustomTagAndELFunctionCombiner {
}
@Override
- public void execute(Environment env, Map params, TemplateModel[] loopVars, TemplateDirectiveBody body)
- throws TemplateException, IOException {
- templateDirectiveModel.execute(env, params, loopVars, body);
+ public Object[] explainTypeError(Class[] expectedClasses) {
+ return simpleMethodModel.explainTypeError(expectedClasses);
}
@Override
- public Object[] explainTypeError(Class[] expectedClasses) {
- return simpleMethodModel.explainTypeError(expectedClasses);
+ public void execute(TemplateModel[] args, CallPlace callPlace, Writer out, Environment env)
+ throws TemplateException, IOException {
+ templateDirectiveModel.execute(args, callPlace, out, env);
}
+ @Override
+ public ArgumentArrayLayout getArgumentArrayLayout() {
+ return templateDirectiveModel.getArgumentArrayLayout();
+ }
}
private static class TemplateDirectiveModelAndTemplateMethodModelEx extends CombinedTemplateModel
@@ -137,64 +133,15 @@ class CustomTagAndELFunctionCombiner {
}
@Override
- public void execute(Environment env, Map params, TemplateModel[] loopVars, TemplateDirectiveBody body)
+ public void execute(TemplateModel[] args, CallPlace callPlace, Writer out, Environment env)
throws TemplateException, IOException {
- templateDirectiveModel.execute(env, params, loopVars, body);
- }
-
- }
-
- private static class TemplateTransformModelAndTemplateMethodModelEx extends CombinedTemplateModel
- implements TemplateTransformModel, TemplateMethodModelEx {
-
- private final TemplateTransformModel templateTransformModel;
- private final TemplateMethodModelEx templateMethodModelEx;
-
- public TemplateTransformModelAndTemplateMethodModelEx( //
- TemplateTransformModel templateTransformModel, TemplateMethodModelEx templateMethodModelEx) {
- this.templateTransformModel = templateTransformModel;
- this.templateMethodModelEx = templateMethodModelEx;
+ templateDirectiveModel.execute(args, callPlace, out, env);
}
@Override
- public Object exec(List arguments) throws TemplateModelException {
- return templateMethodModelEx.exec(arguments);
+ public ArgumentArrayLayout getArgumentArrayLayout() {
+ return templateDirectiveModel.getArgumentArrayLayout();
}
-
- @Override
- public Writer getWriter(Writer out, Map args) throws TemplateModelException, IOException {
- return templateTransformModel.getWriter(out, args);
- }
-
- }
-
- private static class TemplateTransformModelAndSimpleMethodModel extends CombinedTemplateModel
- implements TemplateTransformModel, TemplateMethodModelEx, _UnexpectedTypeErrorExplainerTemplateModel {
-
- private final TemplateTransformModel templateTransformModel;
- private final JavaMethodModel simpleMethodModel;
-
- public TemplateTransformModelAndSimpleMethodModel( //
- TemplateTransformModel templateTransformModel, JavaMethodModel simpleMethodModel) {
- this.templateTransformModel = templateTransformModel;
- this.simpleMethodModel = simpleMethodModel;
- }
-
- @Override
- public Object exec(List arguments) throws TemplateModelException {
- return simpleMethodModel.exec(arguments);
- }
-
- @Override
- public Object[] explainTypeError(Class[] expectedClasses) {
- return simpleMethodModel.explainTypeError(expectedClasses);
- }
-
- @Override
- public Writer getWriter(Writer out, Map args) throws TemplateModelException, IOException {
- return templateTransformModel.getWriter(out, args);
- }
-
}
}
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/52a5f9eb/freemarker-servlet/src/main/java/org/apache/freemarker/servlet/jsp/FreeMarkerPageContext.java
----------------------------------------------------------------------
diff --git a/freemarker-servlet/src/main/java/org/apache/freemarker/servlet/jsp/FreeMarkerPageContext.java b/freemarker-servlet/src/main/java/org/apache/freemarker/servlet/jsp/FreeMarkerPageContext.java
index 4564d71..80f3f2b 100644
--- a/freemarker-servlet/src/main/java/org/apache/freemarker/servlet/jsp/FreeMarkerPageContext.java
+++ b/freemarker-servlet/src/main/java/org/apache/freemarker/servlet/jsp/FreeMarkerPageContext.java
@@ -387,7 +387,7 @@ abstract class FreeMarkerPageContext extends PageContext implements TemplateMode
@Override
public BodyContent pushBody() {
- return (BodyContent) pushWriter(new TagTransformModel.BodyContentImpl(getOut(), true));
+ return (BodyContent) pushWriter(new BodyContentImpl(getOut(), true));
}
@Override
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/52a5f9eb/freemarker-servlet/src/main/java/org/apache/freemarker/servlet/jsp/JspTagModelBase.java
----------------------------------------------------------------------
diff --git a/freemarker-servlet/src/main/java/org/apache/freemarker/servlet/jsp/JspTagModelBase.java b/freemarker-servlet/src/main/java/org/apache/freemarker/servlet/jsp/JspTagModelBase.java
index 8c6fd3a..99e8272 100644
--- a/freemarker-servlet/src/main/java/org/apache/freemarker/servlet/jsp/JspTagModelBase.java
+++ b/freemarker-servlet/src/main/java/org/apache/freemarker/servlet/jsp/JspTagModelBase.java
@@ -27,7 +27,6 @@ import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.math.BigDecimal;
import java.util.HashMap;
-import java.util.Iterator;
import java.util.Map;
import org.apache.freemarker.core.Template;
@@ -36,8 +35,9 @@ import org.apache.freemarker.core._DelayedShortClassName;
import org.apache.freemarker.core._ErrorDescriptionBuilder;
import org.apache.freemarker.core._TemplateModelException;
import org.apache.freemarker.core.model.ObjectWrapperAndUnwrapper;
-import org.apache.freemarker.core.model.TemplateModel;
+import org.apache.freemarker.core.model.TemplateHashModelEx2;
import org.apache.freemarker.core.model.TemplateModelException;
+import org.apache.freemarker.core.model.TemplateScalarModel;
import org.apache.freemarker.core.model.impl.DefaultObjectWrapper;
import org.apache.freemarker.core.util._StringUtil;
import org.apache.freemarker.servlet.jsp.SimpleTagDirectiveModel.TemplateExceptionWrapperJspException;
@@ -75,17 +75,17 @@ class JspTagModelBase {
return tagClass.newInstance();
}
- void setupTag(Object tag, Map args, ObjectWrapperAndUnwrapper wrapper)
- throws TemplateModelException,
+ void setupTag(Object tag, TemplateHashModelEx2 args, ObjectWrapperAndUnwrapper wrapper)
+ throws TemplateModelException,
InvocationTargetException,
IllegalAccessException {
if (args != null && !args.isEmpty()) {
final Object[] argArray = new Object[1];
- for (Iterator iter = args.entrySet().iterator(); iter.hasNext(); ) {
- final Map.Entry entry = (Map.Entry) iter.next();
- final Object arg = wrapper.unwrap((TemplateModel) entry.getValue());
+ for (TemplateHashModelEx2.KeyValuePairIterator iter = args.keyValuePairIterator(); iter.hasNext(); ) {
+ final TemplateHashModelEx2.KeyValuePair entry = iter.next();
+ final Object arg = wrapper.unwrap(entry.getValue());
argArray[0] = arg;
- final Object paramName = entry.getKey();
+ final String paramName = ((TemplateScalarModel) entry.getKey()).getAsString();
Method setterMethod = (Method) propertySetters.get(paramName);
if (setterMethod == null) {
if (dynaSetter == null) {
@@ -130,7 +130,8 @@ class JspTagModelBase {
}
}
- protected final TemplateModelException toTemplateModelExceptionOrRethrow(Exception e) throws TemplateModelException {
+ protected final TemplateModelException toTemplateModelExceptionOrRethrow(Throwable e)
+ throws TemplateModelException {
if (e instanceof RuntimeException && !isCommonRuntimeException((RuntimeException) e)) {
throw (RuntimeException) e;
}
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/52a5f9eb/freemarker-servlet/src/main/java/org/apache/freemarker/servlet/jsp/SimpleTagDirectiveModel.java
----------------------------------------------------------------------
diff --git a/freemarker-servlet/src/main/java/org/apache/freemarker/servlet/jsp/SimpleTagDirectiveModel.java b/freemarker-servlet/src/main/java/org/apache/freemarker/servlet/jsp/SimpleTagDirectiveModel.java
index 0e01e46..810a50c 100644
--- a/freemarker-servlet/src/main/java/org/apache/freemarker/servlet/jsp/SimpleTagDirectiveModel.java
+++ b/freemarker-servlet/src/main/java/org/apache/freemarker/servlet/jsp/SimpleTagDirectiveModel.java
@@ -22,7 +22,6 @@ package org.apache.freemarker.servlet.jsp;
import java.beans.IntrospectionException;
import java.io.IOException;
import java.io.Writer;
-import java.util.Map;
import javax.servlet.jsp.JspContext;
import javax.servlet.jsp.JspException;
@@ -33,15 +32,22 @@ import javax.servlet.jsp.tagext.Tag;
import org.apache.freemarker.core.Environment;
import org.apache.freemarker.core.TemplateException;
-import org.apache.freemarker.core.model.TemplateDirectiveBody;
+import org.apache.freemarker.core.model.ArgumentArrayLayout;
+import org.apache.freemarker.core.model.CallPlace;
import org.apache.freemarker.core.model.TemplateDirectiveModel;
+import org.apache.freemarker.core.model.TemplateHashModelEx2;
import org.apache.freemarker.core.model.TemplateModel;
/**
* Adapts a {@link SimpleTag}-based custom JSP tag to be a value that's callable in templates as an user-defined
- * directive. For {@link Tag}-based custom JSP tags {@link TagTransformModel} is used instead.
+ * directive. For {@link Tag}-based custom JSP tags {@link TagDirectiveModel} is used instead.
*/
class SimpleTagDirectiveModel extends JspTagModelBase implements TemplateDirectiveModel {
+
+ private static final ArgumentArrayLayout ARGS_LAYOUT = ArgumentArrayLayout.create(
+ 0, false,
+ null, true);
+
protected SimpleTagDirectiveModel(String tagName, Class tagClass) throws IntrospectionException {
super(tagName, tagClass);
if (!SimpleTag.class.isAssignableFrom(tagClass)) {
@@ -53,9 +59,8 @@ class SimpleTagDirectiveModel extends JspTagModelBase implements TemplateDirecti
}
@Override
- public void execute(Environment env, Map args, TemplateModel[] outArgs,
- final TemplateDirectiveBody body)
- throws TemplateException, IOException {
+ public void execute(TemplateModel[] args, final CallPlace callPlace, Writer out, final Environment env)
+ throws TemplateException, IOException {
try {
SimpleTag tag = (SimpleTag) getTagInstance();
final FreeMarkerPageContext pageContext = PageContextFactory.getCurrentPageContext();
@@ -66,8 +71,9 @@ class SimpleTagDirectiveModel extends JspTagModelBase implements TemplateDirecti
if (parentTag != null) {
tag.setParent(parentTag);
}
- setupTag(tag, args, pageContext.getObjectWrapper());
- if (body != null) {
+ setupTag(tag, (TemplateHashModelEx2) args[ARGS_LAYOUT.getNamedVarargsArgumentIndex()],
+ pageContext.getObjectWrapper());
+ if (callPlace.hasNestedContent()) {
tag.setJspBody(new JspFragment() {
@Override
public JspContext getJspContext() {
@@ -77,7 +83,7 @@ class SimpleTagDirectiveModel extends JspTagModelBase implements TemplateDirecti
@Override
public void invoke(Writer out) throws JspException, IOException {
try {
- body.render(out == null ? pageContext.getOut() : out);
+ callPlace.executeNestedContent(null, out == null ? pageContext.getOut() : out, env);
} catch (TemplateException e) {
throw new TemplateExceptionWrapperJspException(e);
}
@@ -99,7 +105,12 @@ class SimpleTagDirectiveModel extends JspTagModelBase implements TemplateDirecti
throw toTemplateModelExceptionOrRethrow(e);
}
}
-
+
+ @Override
+ public ArgumentArrayLayout getArgumentArrayLayout() {
+ return ARGS_LAYOUT;
+ }
+
static final class TemplateExceptionWrapperJspException extends JspException {
public TemplateExceptionWrapperJspException(Throwable cause) {