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:13 UTC
[11/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
FREEMARKER-63: Removed TemplateTransformModel and the old TemplateDirectiveModel, renamed TemplateDirectiveModel2 to TemplateDirectiveModel. Removed the temporary `<~...>` syntax; now `<@...>` is used to call the new TemplateDirectiveModel. Lot of API refinement, like introduced ArgumentArrayLayout class. Several test cases won't yet pass... work in progress.
Project: http://git-wip-us.apache.org/repos/asf/incubator-freemarker/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-freemarker/commit/52a5f9eb
Tree: http://git-wip-us.apache.org/repos/asf/incubator-freemarker/tree/52a5f9eb
Diff: http://git-wip-us.apache.org/repos/asf/incubator-freemarker/diff/52a5f9eb
Branch: refs/heads/3
Commit: 52a5f9eb8881a2c976a2b84ff683d8c11238efca
Parents: a6399a7
Author: ddekany <dd...@apache.org>
Authored: Sun Jul 30 02:04:56 2017 +0200
Committer: ddekany <dd...@apache.org>
Committed: Sun Jul 30 02:04:56 2017 +0200
----------------------------------------------------------------------
FM3-CHANGE-LOG.txt | 10 +-
.../core/FM2ASTToFM3SourceConverter.java | 2 +
.../converter/FM2ToFM3ConverterTest.java | 3 +
.../freemarker/core/DirectiveCallPlaceTest.java | 82 ++--
.../EnvironmentGetTemplateVariantsTest.java | 17 +-
.../core/TagSyntaxVariationsTest.java | 19 +-
.../core/TemplateCallableModelTest.java | 175 ++++---
.../core/TheadInterruptingSupportTest.java | 33 +-
.../core/userpkg/AllFeaturesDirective.java | 66 +--
.../core/userpkg/NamedVarargsOnlyDirective.java | 59 +++
.../userpkg/PositionalVarargsOnlyDirective.java | 57 +++
.../userpkg/TestTemplateDirectiveModel.java | 4 +-
.../core/userpkg/TwoNamedParamsDirective.java | 50 +-
.../userpkg/TwoPositionalParamsDirective.java | 43 +-
.../core/userpkg/UpperCaseDirective.java | 40 +-
.../core/valueformat/NumberFormatTest.java | 12 +-
.../org/apache/freemarker/core/ast-1.ast | 4 +-
.../org/apache/freemarker/core/ast-range.ast | 2 +-
.../freemarker/core/ast-strlitinterpolation.ast | 2 +-
.../freemarker/core/ast-whitespacestripping.ast | 6 +-
.../freemarker/core/ASTDirDynamicCall.java | 468 ------------------
.../freemarker/core/ASTDirUserDefined.java | 344 -------------
.../freemarker/core/ASTDynamicTopLevelCall.java | 484 +++++++++++++++++++
.../apache/freemarker/core/ASTExpBuiltIn.java | 3 +-
.../core/BuiltInsForMultipleTypes.java | 13 +-
.../freemarker/core/BuiltInsForStringsMisc.java | 23 +-
...lPlaceCustomDataInitializationException.java | 3 +-
.../freemarker/core/DirectiveCallPlace.java | 135 ------
.../org/apache/freemarker/core/Environment.java | 197 +-------
.../NestedContentNotSupportedException.java | 28 +-
.../NonUserDefinedDirectiveLikeException.java | 5 +-
.../apache/freemarker/core/ParseException.java | 2 +-
.../core/TemplateCallableModelUtils.java | 2 -
...nterruptionSupportTemplatePostProcessor.java | 6 +-
.../freemarker/core/debug/DebugModel.java | 2 +-
.../core/debug/RmiDebugModelImpl.java | 4 +-
.../core/model/ArgumentArrayLayout.java | 199 ++++++++
.../apache/freemarker/core/model/CallPlace.java | 69 +--
.../core/model/TemplateCallableModel.java | 70 +--
.../core/model/TemplateDirectiveBody.java | 43 --
.../core/model/TemplateDirectiveModel.java | 91 ++--
.../core/model/TemplateDirectiveModel2.java | 51 --
.../core/model/TemplateFunctionModel.java | 2 +-
.../core/model/TemplateTransformModel.java | 54 ---
.../freemarker/core/model/TransformControl.java | 101 ----
.../apache/freemarker/core/util/FTLUtil.java | 3 -
.../freemarker/core/util/StringToIndexMap.java | 27 ++
freemarker-core/src/main/javacc/FTL.jj | 121 +----
.../apache/freemarker/servlet/IncludePage.java | 48 +-
.../freemarker/servlet/jsp/BodyContentImpl.java | 222 +++++++++
.../jsp/CustomTagAndELFunctionCombiner.java | 87 +---
.../servlet/jsp/FreeMarkerPageContext.java | 2 +-
.../freemarker/servlet/jsp/JspTagModelBase.java | 19 +-
.../servlet/jsp/SimpleTagDirectiveModel.java | 31 +-
.../servlet/jsp/TagDirectiveModel.java | 256 ++++++++++
.../servlet/jsp/TagTransformModel.java | 419 ----------------
.../freemarker/servlet/jsp/TaglibFactory.java | 6 +-
.../test/templateutil/AssertDirective.java | 42 +-
.../templateutil/AssertEqualsDirective.java | 63 +--
.../test/templateutil/AssertFailsDirective.java | 125 +++--
.../test/templateutil/NoOutputDirective.java | 17 +-
61 files changed, 1940 insertions(+), 2633 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/52a5f9eb/FM3-CHANGE-LOG.txt
----------------------------------------------------------------------
diff --git a/FM3-CHANGE-LOG.txt b/FM3-CHANGE-LOG.txt
index 7943289..6b347e5 100644
--- a/FM3-CHANGE-LOG.txt
+++ b/FM3-CHANGE-LOG.txt
@@ -48,6 +48,7 @@ Major changes / features
Examples:
`<@x x + 1 2 3 />` now must be written as `<@x x + 1, 2, 3 />`
`<#nested x y>` now must be written as `<#nested x, y>`
+- TemplateDirectiveModel was redesigned to be more universal.
Smaller changes
---------------
@@ -99,7 +100,14 @@ Node: Changes already mentioned above aren't repeated here!
Converter note: This conversion is done, but note that in the rare case where a template has no name (when
creating a `Template` directly with its constructor using `null` as the `name` parameter) `.templateName` was an
empty string, while `.currentTemplateName` will be null.
-
+- Refactroing of callable TemplateModel interfaces and related classes:
+ - TemplateDirectiveModel was redesigend (see in the major changes section)
+ - Removed `TemplateTransformModel`; `TemplateDirectiveModel` can do the same things, and more.
+ - Renamed `DirectiveCallPlace` to `CallPlace`
+ - Removed Environment.getDirectiveCallPlace(), as TemplateDirectiveModel-s now get the CallPlace as the parameter
+ of the `execute` method.
+ - ?isTransform was removed (as there are no transforms anymore).
+ Converter note: The template converter tool replaces it with ?isDirective
Java API changes
================
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/52a5f9eb/freemarker-converter/src/main/java/freemarker/core/FM2ASTToFM3SourceConverter.java
----------------------------------------------------------------------
diff --git a/freemarker-converter/src/main/java/freemarker/core/FM2ASTToFM3SourceConverter.java b/freemarker-converter/src/main/java/freemarker/core/FM2ASTToFM3SourceConverter.java
index 26bbe55..ed86057 100644
--- a/freemarker-converter/src/main/java/freemarker/core/FM2ASTToFM3SourceConverter.java
+++ b/freemarker-converter/src/main/java/freemarker/core/FM2ASTToFM3SourceConverter.java
@@ -1967,6 +1967,8 @@ public class FM2ASTToFM3SourceConverter {
private static Map<String, String> IRREGULAR_BUILT_IN_NAME_CONVERSIONS = new ImmutableMap.Builder<String, String>()
.put("webSafe", "html")
.put("web_safe", "html")
+ .put("is_transform", "isDirective")
+ .put("isTransform", "isDirective")
.put("iso_utc_fz", "isoUtcFZ")
.put("iso_utc_nz", "isoUtcNZ")
.put("iso_utc_ms_nz", "isoUtcMsNZ")
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/52a5f9eb/freemarker-converter/src/test/java/org/freemarker/converter/FM2ToFM3ConverterTest.java
----------------------------------------------------------------------
diff --git a/freemarker-converter/src/test/java/org/freemarker/converter/FM2ToFM3ConverterTest.java b/freemarker-converter/src/test/java/org/freemarker/converter/FM2ToFM3ConverterTest.java
index 61eaa35..63c2a92 100644
--- a/freemarker-converter/src/test/java/org/freemarker/converter/FM2ToFM3ConverterTest.java
+++ b/freemarker-converter/src/test/java/org/freemarker/converter/FM2ToFM3ConverterTest.java
@@ -472,6 +472,9 @@ public class FM2ToFM3ConverterTest extends ConverterTest {
public void testBuiltInExpressions() throws IOException, ConverterException {
assertConverted("${s?upperCase} ${s?leftPad(123)}", "${s?upper_case} ${s?left_pad(123)}");
assertConverted("${s?html}", "${s?web_safe}");
+ assertConverted("${s?html}", "${s?webSafe}");
+ assertConverted("${s?isDirective}", "${s?is_transform}");
+ assertConverted("${s?isDirective}", "${s?isTransform}");
assertConvertedSame("${s ? upperCase\t?\t\tleftPad(5)}");
assertConvertedSame("${s <#--1--> ? <#--2--> upperCase}");
// Runtime params:
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/52a5f9eb/freemarker-core-test/src/test/java/org/apache/freemarker/core/DirectiveCallPlaceTest.java
----------------------------------------------------------------------
diff --git a/freemarker-core-test/src/test/java/org/apache/freemarker/core/DirectiveCallPlaceTest.java b/freemarker-core-test/src/test/java/org/apache/freemarker/core/DirectiveCallPlaceTest.java
index 28744b3..37cd2b6 100644
--- a/freemarker-core-test/src/test/java/org/apache/freemarker/core/DirectiveCallPlaceTest.java
+++ b/freemarker-core-test/src/test/java/org/apache/freemarker/core/DirectiveCallPlaceTest.java
@@ -26,8 +26,10 @@ import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.atomic.AtomicInteger;
-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;
import org.apache.freemarker.core.model.TemplateModelException;
import org.apache.freemarker.core.model.TemplateScalarModel;
@@ -73,20 +75,14 @@ public class DirectiveCallPlaceTest extends TemplateTest {
"<@pa />\n"
+ "..<@pa\n"
+ "/><@pa>xxx</@>\n"
- + "<@pa>{<@pa/> <@pa/>}</@>\n"
- + "${curDirLine}<@argP p=curDirLine?string>${curDirLine}</...@argP>${curDirLine}\n"
- + "<#macro m p>(p=${p}){<#nested>}</#macro>\n"
- + "${curDirLine}<@m p=curDirLine?string>${curDirLine}</...@m>${curDirLine}");
+ + "<@pa>{<@pa/> <@pa/>}</@>\n");
assertOutputForNamed(
"positions.ftl",
"[positions.ftl:1:1-1:7]"
+ "..[positions.ftl:2:3-3:2]"
+ "[positions.ftl:3:3-3:14]xxx\n"
- + "[positions.ftl:4:1-4:24]{[positions.ftl:4:7-4:12] [positions.ftl:4:14-4:19]}\n"
- + "-(p=5){-}-\n"
- + "-(p=7){-}-"
- );
+ + "[positions.ftl:4:1-4:24]{[positions.ftl:4:7-4:12] [positions.ftl:4:14-4:19]}\n");
}
@SuppressWarnings("boxing")
@@ -97,7 +93,6 @@ public class DirectiveCallPlaceTest extends TemplateTest {
dm.put("lc", new CachingLowerCaseDirective());
dm.put("pa", new PositionAwareDirective());
dm.put("argP", new ArgPrinterDirective());
- dm.put("curDirLine", new CurDirLineScalar());
dm.put("x", 123);
return dm;
}
@@ -111,17 +106,16 @@ public class DirectiveCallPlaceTest extends TemplateTest {
static void resetCacheRecreationCount() {
cacheRecreationCount.set(0);
}
-
+
@Override
- public void execute(Environment env, Map params, TemplateModel[] loopVars, final TemplateDirectiveBody body)
+ public void execute(TemplateModel[] args, final CallPlace callPlace, Writer out, final Environment env)
throws TemplateException, IOException {
- if (body == null) {
+ if (callPlace.hasNestedContent()) {
return;
}
final String convertedText;
- final DirectiveCallPlace callPlace = env.getCurrentDirectiveCallPlace();
if (callPlace.isNestedOutputCacheable()) {
try {
convertedText = (String) callPlace.getOrCreateCustomData(
@@ -129,7 +123,7 @@ public class DirectiveCallPlaceTest extends TemplateTest {
@Override
public String get() throws TemplateException, IOException {
- return convertBodyText(body)
+ return convertBodyText(callPlace, env)
+ "[cached " + cacheRecreationCount.incrementAndGet() + "]";
}
@@ -138,18 +132,23 @@ public class DirectiveCallPlaceTest extends TemplateTest {
throw new TemplateModelException("Failed to pre-render nested content", e);
}
} else {
- convertedText = convertBodyText(body);
+ convertedText = convertBodyText(callPlace, env);
}
env.getOut().write(convertedText);
}
+ @Override
+ public ArgumentArrayLayout getArgumentArrayLayout() {
+ return ArgumentArrayLayout.PARAMETERLESS;
+ }
+
protected abstract Class getTextConversionIdentity();
- private String convertBodyText(TemplateDirectiveBody body) throws TemplateException,
+ private String convertBodyText(CallPlace callPlace, Environment env) throws TemplateException,
IOException {
StringWriter sw = new StringWriter();
- body.render(sw);
+ callPlace.executeNestedContent(null, sw, env);
return convertText(sw.toString());
}
@@ -188,10 +187,8 @@ public class DirectiveCallPlaceTest extends TemplateTest {
private static class PositionAwareDirective implements TemplateDirectiveModel {
@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 {
- Writer out = env.getOut();
- DirectiveCallPlace callPlace = env.getCurrentDirectiveCallPlace();
out.write("[");
out.write(getTemplateSourceName(callPlace));
out.write(":");
@@ -203,47 +200,46 @@ public class DirectiveCallPlaceTest extends TemplateTest {
out.write(":");
out.write(Integer.toString(callPlace.getEndColumn()));
out.write("]");
- if (body != null) {
- body.render(out);
- }
+ callPlace.executeNestedContent(null, out, env);
}
- private String getTemplateSourceName(DirectiveCallPlace callPlace) {
- return ((ASTDirUserDefined) callPlace).getTemplate().getSourceName();
+ @Override
+ public ArgumentArrayLayout getArgumentArrayLayout() {
+ return ArgumentArrayLayout.PARAMETERLESS;
+ }
+
+ private String getTemplateSourceName(CallPlace callPlace) {
+ return callPlace.getTemplate().getSourceName();
}
-
}
private static class ArgPrinterDirective implements TemplateDirectiveModel {
+ private static final ArgumentArrayLayout ARGS_LAYOUT = ArgumentArrayLayout.create(
+ 0, false,
+ null, true
+ );
+
@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 {
- final Writer out = env.getOut();
- if (params.size() > 0) {
+ TemplateHashModelEx2 varargs = (TemplateHashModelEx2) args[ARGS_LAYOUT.getNamedVarargsArgumentIndex()];
+ if (varargs.size() > 0) {
out.write("(p=");
- out.write(((TemplateScalarModel) params.get("p")).getAsString());
+ out.write(((TemplateScalarModel) varargs.get("p")).getAsString());
out.write(")");
}
- if (body != null) {
+ if (callPlace.hasNestedContent()) {
out.write("{");
- body.render(out);
+ callPlace.executeNestedContent(null, out, env);
out.write("}");
}
}
-
- }
-
- private static class CurDirLineScalar implements TemplateScalarModel {
@Override
- public String getAsString() throws TemplateModelException {
- DirectiveCallPlace callPlace = Environment.getCurrentEnvironment().getCurrentDirectiveCallPlace();
- return callPlace != null
- ? String.valueOf(Environment.getCurrentEnvironment().getCurrentDirectiveCallPlace().getBeginLine())
- : "-";
+ public ArgumentArrayLayout getArgumentArrayLayout() {
+ return ARGS_LAYOUT;
}
-
}
}
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/52a5f9eb/freemarker-core-test/src/test/java/org/apache/freemarker/core/EnvironmentGetTemplateVariantsTest.java
----------------------------------------------------------------------
diff --git a/freemarker-core-test/src/test/java/org/apache/freemarker/core/EnvironmentGetTemplateVariantsTest.java b/freemarker-core-test/src/test/java/org/apache/freemarker/core/EnvironmentGetTemplateVariantsTest.java
index fc3bbf5..40f7c63 100644
--- a/freemarker-core-test/src/test/java/org/apache/freemarker/core/EnvironmentGetTemplateVariantsTest.java
+++ b/freemarker-core-test/src/test/java/org/apache/freemarker/core/EnvironmentGetTemplateVariantsTest.java
@@ -24,9 +24,9 @@ import static org.junit.Assert.*;
import java.io.IOException;
import java.io.Writer;
import java.util.Collections;
-import java.util.Map;
-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.TemplateModel;
import org.apache.freemarker.core.model.impl.SimpleScalar;
@@ -197,17 +197,20 @@ public class EnvironmentGetTemplateVariantsTest extends TemplateTest {
@Override
protected Object createDataModel() {
return Collections.singletonMap("tNames", new TemplateDirectiveModel() {
-
@Override
- public void execute(Environment env, Map params, TemplateModel[] loopVars, TemplateDirectiveBody body)
- throws TemplateException, IOException {
- Writer out = env.getOut();
+ public void execute(TemplateModel[] args, CallPlace callPlace, Writer out,
+ Environment env) throws
+ IOException {
final String r = "<ct=" + env.getCurrentTemplate().getLookupName() + " mt="
+ env.getMainTemplate().getLookupName() + ">";
out.write(r);
env.setGlobalVariable("lastTNamesResult", new SimpleScalar(r));
}
-
+
+ @Override
+ public ArgumentArrayLayout getArgumentArrayLayout() {
+ return ArgumentArrayLayout.PARAMETERLESS;
+ }
});
}
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/52a5f9eb/freemarker-core-test/src/test/java/org/apache/freemarker/core/TagSyntaxVariationsTest.java
----------------------------------------------------------------------
diff --git a/freemarker-core-test/src/test/java/org/apache/freemarker/core/TagSyntaxVariationsTest.java b/freemarker-core-test/src/test/java/org/apache/freemarker/core/TagSyntaxVariationsTest.java
index 02c74df..ee521d9 100644
--- a/freemarker-core-test/src/test/java/org/apache/freemarker/core/TagSyntaxVariationsTest.java
+++ b/freemarker-core-test/src/test/java/org/apache/freemarker/core/TagSyntaxVariationsTest.java
@@ -25,9 +25,7 @@ import java.io.StringWriter;
import java.util.Collections;
import java.util.Map;
-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.userpkg.UpperCaseDirective;
import org.apache.freemarker.core.util._StringUtil;
import org.apache.freemarker.test.TestConfigurationBuilder;
@@ -194,19 +192,4 @@ public class TagSyntaxVariationsTest extends TestCase {
assertEquals(expected, out.toString());
}
- // This will be removed when the legacy TemplateDirectiveModel is removed; the use the other UpperCaseDirective
- // instead!
- private static class UpperCaseDirective implements TemplateDirectiveModel {
-
- private static final UpperCaseDirective INSTANCE = new UpperCaseDirective();
-
- @Override
- public void execute(Environment env, Map params, TemplateModel[] loopVars, TemplateDirectiveBody body)
- throws TemplateException, IOException {
- StringWriter sw = new StringWriter();
- body.render(sw);
- env.getOut().write(sw.toString().toUpperCase());
- }
- }
-
}
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/52a5f9eb/freemarker-core-test/src/test/java/org/apache/freemarker/core/TemplateCallableModelTest.java
----------------------------------------------------------------------
diff --git a/freemarker-core-test/src/test/java/org/apache/freemarker/core/TemplateCallableModelTest.java b/freemarker-core-test/src/test/java/org/apache/freemarker/core/TemplateCallableModelTest.java
index f5c1b61..74edbaf 100644
--- a/freemarker-core-test/src/test/java/org/apache/freemarker/core/TemplateCallableModelTest.java
+++ b/freemarker-core-test/src/test/java/org/apache/freemarker/core/TemplateCallableModelTest.java
@@ -22,6 +22,8 @@ package org.apache.freemarker.core;
import java.io.IOException;
import org.apache.freemarker.core.userpkg.AllFeaturesDirective;
+import org.apache.freemarker.core.userpkg.NamedVarargsOnlyDirective;
+import org.apache.freemarker.core.userpkg.PositionalVarargsOnlyDirective;
import org.apache.freemarker.core.userpkg.TwoNamedParamsDirective;
import org.apache.freemarker.core.userpkg.TwoPositionalParamsDirective;
import org.apache.freemarker.core.userpkg.UpperCaseDirective;
@@ -34,120 +36,137 @@ public class TemplateCallableModelTest extends TemplateTest {
@Before
public void addCommonData() {
addToDataModel("a", new AllFeaturesDirective());
- addToDataModel("p", new TwoPositionalParamsDirective());
- addToDataModel("n", new TwoNamedParamsDirective());
+ addToDataModel("p", TwoPositionalParamsDirective.INSTANCE);
+ addToDataModel("n", TwoNamedParamsDirective.INSTANCE);
+ addToDataModel("pvo", PositionalVarargsOnlyDirective.INSTANCE);
+ addToDataModel("nvo", NamedVarargsOnlyDirective.INSTANCE);
}
@Test
public void testArguments() throws IOException, TemplateException {
- assertOutput("<~p />",
+ assertOutput("<@p />",
"#p(p1=null, p2=null)");
- assertOutput("<~p 1 />",
+ assertOutput("<@p 1 />",
"#p(p1=1, p2=null)");
- assertOutput("<~p 1, 2 />",
+ assertOutput("<@p 1, 2 />",
"#p(p1=1, p2=2)");
- assertOutput("<~n />",
+ assertOutput("<@n />",
"#n(n1=null, n2=null)");
- assertOutput("<~n n1=11/>",
+ assertOutput("<@n n1=11/>",
"#n(n1=11, n2=null)");
- assertOutput("<~n n1=11 n2=22/>",
+ assertOutput("<@n n1=11 n2=22/>",
"#n(n1=11, n2=22)");
- assertOutput("<~a />",
- "#a(p1=null, p2=null, pOthers=[], n1=null, n2=null, nOthers={})");
- assertOutput("<~a 1, 2 />",
- "#a(p1=1, p2=2, pOthers=[], n1=null, n2=null, nOthers={})");
- assertOutput("<~a n1=11 n2=22 />",
- "#a(p1=null, p2=null, pOthers=[], n1=11, n2=22, nOthers={})");
-
- assertOutput("<~a 1, 2 n1=11 n2=22 />",
- "#a(p1=1, p2=2, pOthers=[], n1=11, n2=22, nOthers={})");
- assertOutput("<~a 1 n1=11 />",
- "#a(p1=1, p2=null, pOthers=[], n1=11, n2=null, nOthers={})");
- assertOutput("<~a 1, 2, 3 n1=11 n2=22 n3=33 />",
- "#a(p1=1, p2=2, pOthers=[3], n1=11, n2=22, nOthers={\"n3\": 33})");
- assertOutput("<~a 1 n1=11 n3=33 />",
- "#a(p1=1, p2=null, pOthers=[], n1=11, n2=null, nOthers={\"n3\": 33})");
- assertOutput("<~a 1 n1=11 a=1 b=2 c=3 d=4 e=5 f=6 g=7 />",
- "#a(p1=1, p2=null, pOthers=[], n1=11, n2=null, nOthers={"
+ assertOutput("<@pvo />",
+ "#pvo(pVarargs=[])");
+ assertOutput("<@pvo 1 />",
+ "#pvo(pVarargs=[1])");
+ assertOutput("<@pvo 1, 2 />",
+ "#pvo(pVarargs=[1, 2])");
+
+ assertOutput("<@nvo />",
+ "#nvo(nVarargs={})");
+ assertOutput("<@nvo n1=11 />",
+ "#nvo(nVarargs={\"n1\": 11})");
+ assertOutput("<@nvo n1=11 n2=22/>",
+ "#nvo(nVarargs={\"n1\": 11, \"n2\": 22})");
+
+ assertOutput("<@a />",
+ "#a(p1=null, p2=null, pVarargs=[], n1=null, n2=null, nVarargs={})");
+ assertOutput("<@a 1, 2 />",
+ "#a(p1=1, p2=2, pVarargs=[], n1=null, n2=null, nVarargs={})");
+ assertOutput("<@a n1=11 n2=22 />",
+ "#a(p1=null, p2=null, pVarargs=[], n1=11, n2=22, nVarargs={})");
+
+ assertOutput("<@a 1, 2 n1=11 n2=22 />",
+ "#a(p1=1, p2=2, pVarargs=[], n1=11, n2=22, nVarargs={})");
+ assertOutput("<@a 1 n1=11 />",
+ "#a(p1=1, p2=null, pVarargs=[], n1=11, n2=null, nVarargs={})");
+ assertOutput("<@a 1, 2, 3 n1=11 n2=22 n3=33 />",
+ "#a(p1=1, p2=2, pVarargs=[3], n1=11, n2=22, nVarargs={\"n3\": 33})");
+ assertOutput("<@a 1 n1=11 n3=33 />",
+ "#a(p1=1, p2=null, pVarargs=[], n1=11, n2=null, nVarargs={\"n3\": 33})");
+ assertOutput("<@a 1 n1=11 a=1 b=2 c=3 d=4 e=5 f=6 g=7 />",
+ "#a(p1=1, p2=null, pVarargs=[], n1=11, n2=null, nVarargs={"
+ "\"a\": 1, \"b\": 2, \"c\": 3, \"d\": 4, \"e\": 5, \"f\": 6, \"g\": 7})");
- assertOutput("<~a; a, b, c/>",
- "#a(p1=null, p2=null, pOthers=[], n1=null, n2=null, nOthers={}; 3)");
- assertOutput("<~a 1, 2; a, b, c />",
- "#a(p1=1, p2=2, pOthers=[], n1=null, n2=null, nOthers={}; 3)");
- assertOutput("<~a n1=11 n2=22; a, b, c />",
- "#a(p1=null, p2=null, pOthers=[], n1=11, n2=22, nOthers={}; 3)");
- assertOutput("<~a 1, 2 n1=11 n2=22; a, b, c />",
- "#a(p1=1, p2=2, pOthers=[], n1=11, n2=22, nOthers={}; 3)");
+ assertOutput("<@a; a, b, c/>",
+ "#a(p1=null, p2=null, pVarargs=[], n1=null, n2=null, nVarargs={}; 3)");
+ assertOutput("<@a 1, 2; a, b, c />",
+ "#a(p1=1, p2=2, pVarargs=[], n1=null, n2=null, nVarargs={}; 3)");
+ assertOutput("<@a n1=11 n2=22; a, b, c />",
+ "#a(p1=null, p2=null, pVarargs=[], n1=11, n2=22, nVarargs={}; 3)");
+ assertOutput("<@a 1, 2 n1=11 n2=22; a, b, c />",
+ "#a(p1=1, p2=2, pVarargs=[], n1=11, n2=22, nVarargs={}; 3)");
}
@Test
public void testNestedContent() throws IOException, TemplateException {
- assertOutput("<~a />",
- "#a(p1=null, p2=null, pOthers=[], n1=null, n2=null, nOthers={})");
- assertOutput("<~a></~a>",
- "#a(p1=null, p2=null, pOthers=[], n1=null, n2=null, nOthers={})");
-
- assertOutput("<~a>x</~a>",
- "#a(p1=null, p2=null, pOthers=[], n1=null, n2=null, nOthers={}) {}");
- assertOutput("<~a 1>x</~a>",
- "#a(p1=1, p2=null, pOthers=[], n1=null, n2=null, nOthers={}) {x}");
- assertOutput("<~a 3>x</~a>",
- "#a(p1=3, p2=null, pOthers=[], n1=null, n2=null, nOthers={}) {xxx}");
- assertOutput("<~a 3; i, j, k>[${i}${j}${k}]</~a>",
- "#a(p1=3, p2=null, pOthers=[], n1=null, n2=null, nOthers={}; 3) {[123][246][369]}");
- assertOutput("<#assign i = '-'>${i} <~a 3; i>${i}</~a> ${i}",
- "- #a(p1=3, p2=null, pOthers=[], n1=null, n2=null, nOthers={}; 1) {123} -");
+ assertOutput("<@a />",
+ "#a(p1=null, p2=null, pVarargs=[], n1=null, n2=null, nVarargs={})");
+ assertOutput("<@a...@a>",
+ "#a(p1=null, p2=null, pVarargs=[], n1=null, n2=null, nVarargs={})");
+
+ assertOutput("<@a...@a>",
+ "#a(p1=null, p2=null, pVarargs=[], n1=null, n2=null, nVarargs={}) {}");
+ assertOutput("<@a 1>x</...@a>",
+ "#a(p1=1, p2=null, pVarargs=[], n1=null, n2=null, nVarargs={}) {x}");
+ assertOutput("<@a 3>x</...@a>",
+ "#a(p1=3, p2=null, pVarargs=[], n1=null, n2=null, nVarargs={}) {xxx}");
+ assertOutput("<@a 3; i, j, k>[${i}${j}${k}]</...@a>",
+ "#a(p1=3, p2=null, pVarargs=[], n1=null, n2=null, nVarargs={}; 3) {[123][246][369]}");
+ assertOutput("<#assign i = '-'>${i} <@a 3; i>${i}</...@a> ${i}",
+ "- #a(p1=3, p2=null, pVarargs=[], n1=null, n2=null, nVarargs={}; 1) {123} -");
}
@Test
public void testSyntaxEdgeCases() throws IOException, TemplateException {
- assertOutput("<~a; x/>",
- "#a(p1=null, p2=null, pOthers=[], n1=null, n2=null, nOthers={}; 1)");
- assertOutput("<~a;x/>",
- "#a(p1=null, p2=null, pOthers=[], n1=null, n2=null, nOthers={}; 1)");
- assertOutput("<~a;x />",
- "#a(p1=null, p2=null, pOthers=[], n1=null, n2=null, nOthers={}; 1)");
-
- assertOutput("<~a 1 , 2 n1 = 11 n2 = 22 ; a , b , c />",
- "#a(p1=1, p2=2, pOthers=[], n1=11, n2=22, nOthers={}; 3)");
- assertOutput("<~a 1<#-- -->,<#-- -->2<#-- -->n1<#-- -->=<#-- -->11<#-- -->n2=22<#-- -->;"
+ assertOutput("<@a; x/>",
+ "#a(p1=null, p2=null, pVarargs=[], n1=null, n2=null, nVarargs={}; 1)");
+ assertOutput("<@a;x/>",
+ "#a(p1=null, p2=null, pVarargs=[], n1=null, n2=null, nVarargs={}; 1)");
+ assertOutput("<@a;x />",
+ "#a(p1=null, p2=null, pVarargs=[], n1=null, n2=null, nVarargs={}; 1)");
+
+ assertOutput("<@a 1 , 2 n1 = 11 n2 = 22 ; a , b , c />",
+ "#a(p1=1, p2=2, pVarargs=[], n1=11, n2=22, nVarargs={}; 3)");
+ assertOutput("<@a 1<#-- -->,<#-- -->2<#-- -->n1<#-- -->=<#-- -->11<#-- -->n2=22<#-- -->;"
+ "<#-- -->a<#-- -->,<#-- -->b<#-- -->,<#-- -->c<#-- -->/>",
- "#a(p1=1, p2=2, pOthers=[], n1=11, n2=22, nOthers={}; 3)");
- assertOutput("<~a\t1,2\tn1=11\tn2=22;a,b,c/>",
- "#a(p1=1, p2=2, pOthers=[], n1=11, n2=22, nOthers={}; 3)");
+ "#a(p1=1, p2=2, pVarargs=[], n1=11, n2=22, nVarargs={}; 3)");
+ assertOutput("<@a\t1,2\tn1=11\tn2=22;a,b,c/>",
+ "#a(p1=1, p2=2, pVarargs=[], n1=11, n2=22, nVarargs={}; 3)");
- assertOutput("<~a + 1 />",
- "#a(p1=1, p2=null, pOthers=[], n1=null, n2=null, nOthers={})");
+ assertOutput("<@a + 1 />",
+ "#a(p1=1, p2=null, pVarargs=[], n1=null, n2=null, nVarargs={})");
}
@Test
@SuppressWarnings("ThrowableNotThrown")
public void testParsingErrors() throws IOException, TemplateException {
- assertErrorContains("<~a, n1=1 />", "Remove comma", "between", "by position");
- assertErrorContains("<~a n1=1, n2=1 />", "Remove comma", "between", "by position");
- assertErrorContains("<~a n1=1, 2 />", "Remove comma", "between", "by position");
- assertErrorContains("<~a, 1 />", "Remove comma", "between", "by position");
- assertErrorContains("<~a 1, , 2 />", "Two commas");
- assertErrorContains("<~a 1 2 />", "Missing comma");
- assertErrorContains("<~a n1=1 2 />", "must be earlier than arguments passed by name");
+ assertErrorContains("<@a, n1=1 />", "Remove comma", "between", "by position");
+ assertErrorContains("<@a n1=1, n2=1 />", "Remove comma", "between", "by position");
+ assertErrorContains("<@a n1=1, 2 />", "Remove comma", "between", "by position");
+ assertErrorContains("<@a, 1 />", "Remove comma", "between", "by position");
+ assertErrorContains("<@a 1, , 2 />", "Two commas");
+ assertErrorContains("<@a 1 2 />", "Missing comma");
+ assertErrorContains("<@a n1=1 2 />", "must be earlier than arguments passed by name");
}
@Test
@SuppressWarnings("ThrowableNotThrown")
public void testRuntimeErrors() throws IOException, TemplateException {
- assertErrorContains("<~p 9, 9, 9 />", "can only have 2", "3", "by position");
- assertErrorContains("<~n 9 />", "can't have arguments passed by position");
- assertErrorContains("<~n n3=9 />", "has no", "\"n3\"", "supported", "\"n1\", \"n2\"");
- assertErrorContains("<~p n1=9 />", "doesn't have any by-name-passed");
+ assertErrorContains("<@p 9, 9, 9 />", "can only have 2", "3", "by position");
+ assertErrorContains("<@n 9 />", "can't have arguments passed by position");
+ assertErrorContains("<@n n3=9 />", "has no", "\"n3\"", "supported", "\"n1\", \"n2\"");
+ assertErrorContains("<@p n1=9 />", "doesn't have any by-name-passed");
+ assertErrorContains("<@a 1; i, j, k, l>x</...@a>", "(4: \"i\", \"j\", \"k\", \"l\")", "(3)");
}
@Test
public void testMacros() throws IOException, TemplateException {
assertOutput("<#macro m a b=22><#list 1..2 as n>[<#nested a * n, b * n>]</#list></#macro>"
- + "<~m 11; i, j>${i} ${j}</~m> <~m a=1 b=2; i, j>${i} ${j}</~m>",
+ + "<@m 11; i, j>${i} ${j}</...@m> <@m a=1 b=2; i, j>${i} ${j}</...@m>",
"[11 22][22 44] [1 2][2 4]");
assertOutput("<#macro m a b others...>[a=${a}, b=${b}<#if others?hasContent>, </#if>"
+ "<#if others?isSequence>"
@@ -156,14 +175,14 @@ public class TemplateCallableModelTest extends TemplateTest {
+ "<#list others as k, v>${k}=${v}<#sep>, </#list>"
+ "</#if>]"
+ "</#macro>"
- + "<~m 1, 2 /> <~m 1, 2, 3, 4 /> <~m a=1 b=2 /> <~m a=1 b=2 c=3 d=4 />",
+ + "<@m 1, 2 /> <@m 1, 2, 3, 4 /> <@m a=1 b=2 /> <@m a=1 b=2 c=3 d=4 />",
"[a=1, b=2] [a=1, b=2, 3, 4] [a=1, b=2] [a=1, b=2, c=3, d=4]");
}
@Test
public void testFilterDirective() throws IOException, TemplateException {
- addToDataModel("uc", new UpperCaseDirective());
- assertOutput("<~uc>foo ${1 + 1}</~>", "FOO 2");
+ addToDataModel("uc", UpperCaseDirective.INSTANCE);
+ assertOutput("<@uc>foo ${1 + 1}</@>", "FOO 2");
}
}
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/52a5f9eb/freemarker-core-test/src/test/java/org/apache/freemarker/core/TheadInterruptingSupportTest.java
----------------------------------------------------------------------
diff --git a/freemarker-core-test/src/test/java/org/apache/freemarker/core/TheadInterruptingSupportTest.java b/freemarker-core-test/src/test/java/org/apache/freemarker/core/TheadInterruptingSupportTest.java
index 003f66a..2ab270a 100644
--- a/freemarker-core-test/src/test/java/org/apache/freemarker/core/TheadInterruptingSupportTest.java
+++ b/freemarker-core-test/src/test/java/org/apache/freemarker/core/TheadInterruptingSupportTest.java
@@ -22,10 +22,11 @@ package org.apache.freemarker.core;
import static org.junit.Assert.*;
import java.io.IOException;
-import java.util.Map;
+import java.io.Writer;
import org.apache.freemarker.core.ThreadInterruptionSupportTemplatePostProcessor.TemplateProcessingThreadInterruptedException;
-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.TemplateModel;
import org.apache.freemarker.core.util._NullWriter;
@@ -119,35 +120,41 @@ public class TheadInterruptingSupportTest {
}
public class StartedDirective implements TemplateDirectiveModel {
-
@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 {
synchronized (TemplateRunnerThread.this) {
started = true;
TemplateRunnerThread.this.notifyAll();
}
}
-
+
+ @Override
+ public ArgumentArrayLayout getArgumentArrayLayout() {
+ return ArgumentArrayLayout.PARAMETERLESS;
+ }
}
public class CustomLoopDirective implements TemplateDirectiveModel {
@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 {
// Deliberate infinite loop
while (true) {
- body.render(_NullWriter.INSTANCE);
+ callPlace.executeNestedContent(null, _NullWriter.INSTANCE, env);
}
}
-
+
+ @Override
+ public ArgumentArrayLayout getArgumentArrayLayout() {
+ return ArgumentArrayLayout.PARAMETERLESS;
+ }
}
public class SleepDirective implements TemplateDirectiveModel {
-
@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 {
try {
Thread.sleep(100);
@@ -156,7 +163,11 @@ public class TheadInterruptingSupportTest {
Thread.currentThread().interrupt();
}
}
-
+
+ @Override
+ public ArgumentArrayLayout getArgumentArrayLayout() {
+ return ArgumentArrayLayout.PARAMETERLESS;
+ }
}
}
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/52a5f9eb/freemarker-core-test/src/test/java/org/apache/freemarker/core/userpkg/AllFeaturesDirective.java
----------------------------------------------------------------------
diff --git a/freemarker-core-test/src/test/java/org/apache/freemarker/core/userpkg/AllFeaturesDirective.java b/freemarker-core-test/src/test/java/org/apache/freemarker/core/userpkg/AllFeaturesDirective.java
index 437bb72..c8d7329 100644
--- a/freemarker-core-test/src/test/java/org/apache/freemarker/core/userpkg/AllFeaturesDirective.java
+++ b/freemarker-core-test/src/test/java/org/apache/freemarker/core/userpkg/AllFeaturesDirective.java
@@ -23,10 +23,10 @@ import static org.apache.freemarker.core.TemplateCallableModelUtils.*;
import java.io.IOException;
import java.io.Writer;
-import java.util.Collection;
import org.apache.freemarker.core.Environment;
import org.apache.freemarker.core.TemplateException;
+import org.apache.freemarker.core.model.ArgumentArrayLayout;
import org.apache.freemarker.core.model.CallPlace;
import org.apache.freemarker.core.model.TemplateHashModelEx2;
import org.apache.freemarker.core.model.TemplateModel;
@@ -39,14 +39,24 @@ public class AllFeaturesDirective extends TestTemplateDirectiveModel {
private static final int P1_ARG_IDX = 0;
private static final int P2_ARG_IDX = 1;
- private static final int P_OTHERS_ARG_IDX = 2;
- private static final int N1_ARG_IDX = 3;
- private static final int N2_ARG_IDX = 4;
- private static final int N_OTHERS_IDX = 5;
+ private static final int N1_ARG_IDX = 2;
+ private static final int N2_ARG_IDX = 3;
private static final String N1_ARG_NAME = "n1";
private static final String N2_ARG_NAME = "n2";
+ private static final ArgumentArrayLayout ARGS_LAYOUT = ArgumentArrayLayout.create(
+ 2,
+ true,
+ StringToIndexMap.of(
+ N1_ARG_NAME, N1_ARG_IDX,
+ N2_ARG_NAME, N2_ARG_IDX),
+ true
+ );
+
+ private static final int P_VARARGS_ARG_IDX = ARGS_LAYOUT.getPositionalVarargsArgumentIndex();
+ private static final int N_VARARGS_ARG_IDX = ARGS_LAYOUT.getNamedVarargsArgumentIndex();
+
private final boolean p1AllowNull;
private final boolean p2AllowNull;
private final boolean n1AllowNull;
@@ -63,19 +73,15 @@ public class AllFeaturesDirective extends TestTemplateDirectiveModel {
this.n2AllowNull = n2AllowNull;
}
- private static final StringToIndexMap PARAM_NAME_TO_IDX = StringToIndexMap.of(
- N1_ARG_NAME, N1_ARG_IDX,
- N2_ARG_NAME, N2_ARG_IDX);
-
@Override
public void execute(TemplateModel[] args, CallPlace callPlace, Writer out, Environment env)
throws TemplateException, IOException {
execute(castArgumentToNumber(args, P1_ARG_IDX, p1AllowNull, env),
castArgumentToNumber(args, P2_ARG_IDX, p2AllowNull, env),
- (TemplateSequenceModel) args[P_OTHERS_ARG_IDX],
+ (TemplateSequenceModel) args[P_VARARGS_ARG_IDX],
castArgumentToNumber(args[N1_ARG_IDX], N1_ARG_NAME, n1AllowNull, env),
castArgumentToNumber(args[N2_ARG_IDX], N2_ARG_NAME, n2AllowNull, env),
- (TemplateHashModelEx2) args[N_OTHERS_IDX],
+ (TemplateHashModelEx2) args[N_VARARGS_ARG_IDX],
out, env, callPlace);
}
@@ -85,10 +91,10 @@ public class AllFeaturesDirective extends TestTemplateDirectiveModel {
out.write("#a(");
printParam("p1", p1, out, true);
printParam("p2", p2, out);
- printParam("pOthers", pOthers, out);
+ printParam("pVarargs", pOthers, out);
printParam(N1_ARG_NAME, n1, out);
printParam(N2_ARG_NAME, n2, out);
- printParam("nOthers", nOthers, out);
+ printParam("nVarargs", nOthers, out);
int loopVariableCount = callPlace.getLoopVariableCount();
if (loopVariableCount != 0) {
out.write("; " + loopVariableCount);
@@ -100,8 +106,9 @@ public class AllFeaturesDirective extends TestTemplateDirectiveModel {
if (p1 != null) {
int intP1 = p1.getAsNumber().intValue();
for (int i = 0; i < intP1; i++) {
- TemplateModel[] loopVariableValues = new TemplateModel[loopVariableCount];
- for (int loopVarIdx = 0; loopVarIdx < loopVariableCount; loopVarIdx++) {
+ // We limit the number of loop variables passed to 3, so that related errors can be tested.
+ TemplateModel[] loopVariableValues = new TemplateModel[Math.min(loopVariableCount, 3)];
+ for (int loopVarIdx = 0; loopVarIdx < loopVariableValues.length; loopVarIdx++) {
loopVariableValues[loopVarIdx] = new SimpleNumber((i + 1) * (loopVarIdx + 1));
}
callPlace.executeNestedContent(loopVariableValues, out, env);
@@ -112,32 +119,7 @@ public class AllFeaturesDirective extends TestTemplateDirectiveModel {
}
@Override
- public int getPredefinedPositionalArgumentCount() {
- return 2;
- }
-
- @Override
- public boolean hasPositionalVarargsArgument() {
- return true;
- }
-
- @Override
- public int getPredefinedNamedArgumentIndex(String name) {
- return PARAM_NAME_TO_IDX.get(name);
- }
-
- @Override
- public int getNamedVarargsArgumentIndex() {
- return N_OTHERS_IDX;
- }
-
- @Override
- public Collection<String> getPredefinedNamedArgumentNames() {
- return PARAM_NAME_TO_IDX.getKeys();
- }
-
- @Override
- public int getArgumentArraySize() {
- return N_OTHERS_IDX + 1;
+ public ArgumentArrayLayout getArgumentArrayLayout() {
+ return ARGS_LAYOUT;
}
}
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/52a5f9eb/freemarker-core-test/src/test/java/org/apache/freemarker/core/userpkg/NamedVarargsOnlyDirective.java
----------------------------------------------------------------------
diff --git a/freemarker-core-test/src/test/java/org/apache/freemarker/core/userpkg/NamedVarargsOnlyDirective.java b/freemarker-core-test/src/test/java/org/apache/freemarker/core/userpkg/NamedVarargsOnlyDirective.java
new file mode 100644
index 0000000..aa69e8f
--- /dev/null
+++ b/freemarker-core-test/src/test/java/org/apache/freemarker/core/userpkg/NamedVarargsOnlyDirective.java
@@ -0,0 +1,59 @@
+/*
+ * 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.userpkg;
+
+import java.io.IOException;
+import java.io.Writer;
+
+import org.apache.freemarker.core.Environment;
+import org.apache.freemarker.core.NestedContentNotSupportedException;
+import org.apache.freemarker.core.TemplateException;
+import org.apache.freemarker.core.model.ArgumentArrayLayout;
+import org.apache.freemarker.core.model.CallPlace;
+import org.apache.freemarker.core.model.TemplateModel;
+
+public class NamedVarargsOnlyDirective extends TestTemplateDirectiveModel {
+
+ public static final NamedVarargsOnlyDirective INSTANCE = new NamedVarargsOnlyDirective();
+
+ private static final ArgumentArrayLayout ARGS_LAYOUT = ArgumentArrayLayout.create(
+ 0, false,
+ null, true);
+
+ private static final int NAMED_VARARGS_ARG_IDX = ARGS_LAYOUT.getNamedVarargsArgumentIndex();
+
+ private NamedVarargsOnlyDirective() {
+ //
+ }
+
+ @Override
+ public void execute(TemplateModel[] args, CallPlace callPlace, Writer out, Environment env)
+ throws TemplateException, IOException {
+ NestedContentNotSupportedException.check(callPlace);
+ out.write("#nvo(");
+ printParam("nVarargs", args[NAMED_VARARGS_ARG_IDX], out, true);
+ out.write(")");
+ }
+
+ @Override
+ public ArgumentArrayLayout getArgumentArrayLayout() {
+ return ARGS_LAYOUT;
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/52a5f9eb/freemarker-core-test/src/test/java/org/apache/freemarker/core/userpkg/PositionalVarargsOnlyDirective.java
----------------------------------------------------------------------
diff --git a/freemarker-core-test/src/test/java/org/apache/freemarker/core/userpkg/PositionalVarargsOnlyDirective.java b/freemarker-core-test/src/test/java/org/apache/freemarker/core/userpkg/PositionalVarargsOnlyDirective.java
new file mode 100644
index 0000000..cc3f9d8
--- /dev/null
+++ b/freemarker-core-test/src/test/java/org/apache/freemarker/core/userpkg/PositionalVarargsOnlyDirective.java
@@ -0,0 +1,57 @@
+/*
+ * 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.userpkg;
+
+import java.io.IOException;
+import java.io.Writer;
+
+import org.apache.freemarker.core.Environment;
+import org.apache.freemarker.core.NestedContentNotSupportedException;
+import org.apache.freemarker.core.TemplateException;
+import org.apache.freemarker.core.model.ArgumentArrayLayout;
+import org.apache.freemarker.core.model.CallPlace;
+import org.apache.freemarker.core.model.TemplateModel;
+
+public class PositionalVarargsOnlyDirective extends TestTemplateDirectiveModel {
+
+ public static final PositionalVarargsOnlyDirective INSTANCE = new PositionalVarargsOnlyDirective();
+
+ private static final ArgumentArrayLayout ARGS_LAYOUT = ArgumentArrayLayout.create(
+ 0, true,
+ null, false);
+
+ private PositionalVarargsOnlyDirective() {
+ //
+ }
+
+ @Override
+ public void execute(TemplateModel[] args, CallPlace callPlace, Writer out, Environment env)
+ throws TemplateException, IOException {
+ NestedContentNotSupportedException.check(callPlace);
+ out.write("#pvo(");
+ printParam("pVarargs", args[ARGS_LAYOUT.getPositionalVarargsArgumentIndex()], out, true);
+ out.write(")");
+ }
+
+ @Override
+ public ArgumentArrayLayout getArgumentArrayLayout() {
+ return ARGS_LAYOUT;
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/52a5f9eb/freemarker-core-test/src/test/java/org/apache/freemarker/core/userpkg/TestTemplateDirectiveModel.java
----------------------------------------------------------------------
diff --git a/freemarker-core-test/src/test/java/org/apache/freemarker/core/userpkg/TestTemplateDirectiveModel.java b/freemarker-core-test/src/test/java/org/apache/freemarker/core/userpkg/TestTemplateDirectiveModel.java
index c69497a..49bb049 100644
--- a/freemarker-core-test/src/test/java/org/apache/freemarker/core/userpkg/TestTemplateDirectiveModel.java
+++ b/freemarker-core-test/src/test/java/org/apache/freemarker/core/userpkg/TestTemplateDirectiveModel.java
@@ -22,7 +22,7 @@ package org.apache.freemarker.core.userpkg;
import java.io.IOException;
import java.io.Writer;
-import org.apache.freemarker.core.model.TemplateDirectiveModel2;
+import org.apache.freemarker.core.model.TemplateDirectiveModel;
import org.apache.freemarker.core.model.TemplateHashModelEx2;
import org.apache.freemarker.core.model.TemplateModel;
import org.apache.freemarker.core.model.TemplateModelException;
@@ -31,7 +31,7 @@ import org.apache.freemarker.core.model.TemplateScalarModel;
import org.apache.freemarker.core.model.TemplateSequenceModel;
import org.apache.freemarker.core.util.FTLUtil;
-public abstract class TestTemplateDirectiveModel implements TemplateDirectiveModel2 {
+public abstract class TestTemplateDirectiveModel implements TemplateDirectiveModel {
protected void printParam(String name, TemplateModel value, Writer out) throws IOException, TemplateModelException {
printParam(name, value, out, false);
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/52a5f9eb/freemarker-core-test/src/test/java/org/apache/freemarker/core/userpkg/TwoNamedParamsDirective.java
----------------------------------------------------------------------
diff --git a/freemarker-core-test/src/test/java/org/apache/freemarker/core/userpkg/TwoNamedParamsDirective.java b/freemarker-core-test/src/test/java/org/apache/freemarker/core/userpkg/TwoNamedParamsDirective.java
index ed1b501..dbff203 100644
--- a/freemarker-core-test/src/test/java/org/apache/freemarker/core/userpkg/TwoNamedParamsDirective.java
+++ b/freemarker-core-test/src/test/java/org/apache/freemarker/core/userpkg/TwoNamedParamsDirective.java
@@ -21,28 +21,41 @@ package org.apache.freemarker.core.userpkg;
import java.io.IOException;
import java.io.Writer;
-import java.util.Collection;
import org.apache.freemarker.core.Environment;
+import org.apache.freemarker.core.NestedContentNotSupportedException;
import org.apache.freemarker.core.TemplateException;
+import org.apache.freemarker.core.model.ArgumentArrayLayout;
import org.apache.freemarker.core.model.CallPlace;
import org.apache.freemarker.core.model.TemplateModel;
import org.apache.freemarker.core.util.StringToIndexMap;
public class TwoNamedParamsDirective extends TestTemplateDirectiveModel {
- private static final String N1_ARG_NAME = "n1";
- private static final String N2_ARG_NAME = "n2";
+ public static final TwoNamedParamsDirective INSTANCE = new TwoNamedParamsDirective();
+
private static final int N1_ARG_IDX = 0;
private static final int N2_ARG_IDX = 1;
- private static final StringToIndexMap PARAM_NAME_TO_IDX = StringToIndexMap.of(
+ private static final String N1_ARG_NAME = "n1";
+ private static final String N2_ARG_NAME = "n2";
+
+ private static final StringToIndexMap ARG_NAME_TO_IDX = StringToIndexMap.of(
N1_ARG_NAME, N1_ARG_IDX,
N2_ARG_NAME, N2_ARG_IDX);
+ private static final ArgumentArrayLayout ARGS_LAYOUT = ArgumentArrayLayout.create(
+ 0, false,
+ ARG_NAME_TO_IDX, false);
+
+ private TwoNamedParamsDirective() {
+ //
+ }
+
@Override
public void execute(TemplateModel[] args, CallPlace callPlace, Writer out, Environment env)
throws TemplateException, IOException {
+ NestedContentNotSupportedException.check(callPlace);
out.write("#n(");
printParam(N1_ARG_NAME, args[N1_ARG_IDX], out, true);
printParam(N2_ARG_NAME, args[N2_ARG_IDX], out);
@@ -50,32 +63,7 @@ public class TwoNamedParamsDirective extends TestTemplateDirectiveModel {
}
@Override
- public int getPredefinedPositionalArgumentCount() {
- return 0;
- }
-
- @Override
- public boolean hasPositionalVarargsArgument() {
- return false;
- }
-
- @Override
- public int getPredefinedNamedArgumentIndex(String name) {
- return PARAM_NAME_TO_IDX.get(name);
- }
-
- @Override
- public int getNamedVarargsArgumentIndex() {
- return -1;
- }
-
- @Override
- public int getArgumentArraySize() {
- return PARAM_NAME_TO_IDX.size();
- }
-
- @Override
- public Collection<String> getPredefinedNamedArgumentNames() {
- return PARAM_NAME_TO_IDX.getKeys();
+ public ArgumentArrayLayout getArgumentArrayLayout() {
+ return ARGS_LAYOUT;
}
}
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/52a5f9eb/freemarker-core-test/src/test/java/org/apache/freemarker/core/userpkg/TwoPositionalParamsDirective.java
----------------------------------------------------------------------
diff --git a/freemarker-core-test/src/test/java/org/apache/freemarker/core/userpkg/TwoPositionalParamsDirective.java b/freemarker-core-test/src/test/java/org/apache/freemarker/core/userpkg/TwoPositionalParamsDirective.java
index ef51640..52fe77e 100644
--- a/freemarker-core-test/src/test/java/org/apache/freemarker/core/userpkg/TwoPositionalParamsDirective.java
+++ b/freemarker-core-test/src/test/java/org/apache/freemarker/core/userpkg/TwoPositionalParamsDirective.java
@@ -21,18 +21,30 @@ package org.apache.freemarker.core.userpkg;
import java.io.IOException;
import java.io.Writer;
-import java.util.Collection;
import org.apache.freemarker.core.Environment;
+import org.apache.freemarker.core.NestedContentNotSupportedException;
import org.apache.freemarker.core.TemplateException;
+import org.apache.freemarker.core.model.ArgumentArrayLayout;
import org.apache.freemarker.core.model.CallPlace;
import org.apache.freemarker.core.model.TemplateModel;
public class TwoPositionalParamsDirective extends TestTemplateDirectiveModel {
+ public static final TwoPositionalParamsDirective INSTANCE = new TwoPositionalParamsDirective();
+
+ private static final ArgumentArrayLayout ARGS_LAYOUT = ArgumentArrayLayout.create(
+ 2, false,
+ null, false);
+
+ private TwoPositionalParamsDirective() {
+ //
+ }
+
@Override
public void execute(TemplateModel[] args, CallPlace callPlace, Writer out, Environment env)
throws TemplateException, IOException {
+ NestedContentNotSupportedException.check(callPlace);
out.write("#p(");
printParam("p1", args[0], out, true);
printParam("p2", args[1], out);
@@ -40,32 +52,7 @@ public class TwoPositionalParamsDirective extends TestTemplateDirectiveModel {
}
@Override
- public int getPredefinedPositionalArgumentCount() {
- return 2;
- }
-
- @Override
- public boolean hasPositionalVarargsArgument() {
- return false;
- }
-
- @Override
- public int getPredefinedNamedArgumentIndex(String name) {
- return -1;
- }
-
- @Override
- public int getNamedVarargsArgumentIndex() {
- return -1;
- }
-
- @Override
- public int getArgumentArraySize() {
- return getPredefinedPositionalArgumentCount();
- }
-
- @Override
- public Collection<String> getPredefinedNamedArgumentNames() {
- return null;
+ public ArgumentArrayLayout getArgumentArrayLayout() {
+ return ARGS_LAYOUT;
}
}
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/52a5f9eb/freemarker-core-test/src/test/java/org/apache/freemarker/core/userpkg/UpperCaseDirective.java
----------------------------------------------------------------------
diff --git a/freemarker-core-test/src/test/java/org/apache/freemarker/core/userpkg/UpperCaseDirective.java b/freemarker-core-test/src/test/java/org/apache/freemarker/core/userpkg/UpperCaseDirective.java
index b23e56c..507b820 100644
--- a/freemarker-core-test/src/test/java/org/apache/freemarker/core/userpkg/UpperCaseDirective.java
+++ b/freemarker-core-test/src/test/java/org/apache/freemarker/core/userpkg/UpperCaseDirective.java
@@ -22,14 +22,21 @@ package org.apache.freemarker.core.userpkg;
import java.io.IOException;
import java.io.StringWriter;
import java.io.Writer;
-import java.util.Collection;
import org.apache.freemarker.core.Environment;
import org.apache.freemarker.core.TemplateException;
+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.TemplateModel;
-public class UpperCaseDirective extends TestTemplateDirectiveModel {
+public class UpperCaseDirective implements TemplateDirectiveModel {
+
+ public static final UpperCaseDirective INSTANCE = new UpperCaseDirective();
+
+ private UpperCaseDirective() {
+ //
+ }
@Override
public void execute(TemplateModel[] args, CallPlace callPlace, Writer out, Environment env)
@@ -40,32 +47,7 @@ public class UpperCaseDirective extends TestTemplateDirectiveModel {
}
@Override
- public int getPredefinedPositionalArgumentCount() {
- return 0;
- }
-
- @Override
- public boolean hasPositionalVarargsArgument() {
- return false;
- }
-
- @Override
- public int getPredefinedNamedArgumentIndex(String name) {
- return -1;
- }
-
- @Override
- public int getNamedVarargsArgumentIndex() {
- return -1;
- }
-
- @Override
- public int getArgumentArraySize() {
- return 0;
- }
-
- @Override
- public Collection<String> getPredefinedNamedArgumentNames() {
- return null;
+ public ArgumentArrayLayout getArgumentArrayLayout() {
+ return ArgumentArrayLayout.PARAMETERLESS;
}
}
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/52a5f9eb/freemarker-core-test/src/test/java/org/apache/freemarker/core/valueformat/NumberFormatTest.java
----------------------------------------------------------------------
diff --git a/freemarker-core-test/src/test/java/org/apache/freemarker/core/valueformat/NumberFormatTest.java b/freemarker-core-test/src/test/java/org/apache/freemarker/core/valueformat/NumberFormatTest.java
index 8900d2b..830a16c 100644
--- a/freemarker-core-test/src/test/java/org/apache/freemarker/core/valueformat/NumberFormatTest.java
+++ b/freemarker-core-test/src/test/java/org/apache/freemarker/core/valueformat/NumberFormatTest.java
@@ -22,6 +22,7 @@ import static org.hamcrest.Matchers.*;
import static org.junit.Assert.*;
import java.io.IOException;
+import java.io.Writer;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.Collections;
@@ -33,7 +34,8 @@ import org.apache.freemarker.core.Environment;
import org.apache.freemarker.core.Template;
import org.apache.freemarker.core.TemplateConfiguration;
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.TemplateModel;
import org.apache.freemarker.core.model.TemplateModelException;
@@ -177,12 +179,16 @@ public class NumberFormatTest extends TemplateTest {
nm.setNumber(123);
addToDataModel("n", nm);
addToDataModel("incN", new TemplateDirectiveModel() {
-
@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 {
nm.setNumber(nm.getAsNumber().intValue() + 1);
}
+
+ @Override
+ public ArgumentArrayLayout getArgumentArrayLayout() {
+ return ArgumentArrayLayout.PARAMETERLESS;
+ }
});
assertOutput(
"<#assign s1 = n?string>"
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/52a5f9eb/freemarker-core-test/src/test/resources/org/apache/freemarker/core/ast-1.ast
----------------------------------------------------------------------
diff --git a/freemarker-core-test/src/test/resources/org/apache/freemarker/core/ast-1.ast b/freemarker-core-test/src/test/resources/org/apache/freemarker/core/ast-1.ast
index 90b4956..b7cf57f 100644
--- a/freemarker-core-test/src/test/resources/org/apache/freemarker/core/ast-1.ast
+++ b/freemarker-core-test/src/test/resources/org/apache/freemarker/core/ast-1.ast
@@ -19,7 +19,7 @@
#mixedContent // o.a.f.c.ASTImplicitParent
#text // o.a.f.c.ASTStaticText
- content: "1 " // String
- @ // o.a.f.c.ASTDirUserDefined
+ @ // o.a.f.c.ASTDynamicTopLevelCall
- callee: foo // o.a.f.c.ASTExpVariable
- argument name: "x" // String
- argument value: 1 // o.a.f.c.ASTExpNumberLiteral
@@ -31,7 +31,7 @@
- content: "x" // String
#text // o.a.f.c.ASTStaticText
- content: "\n2 " // String
- @ // o.a.f.c.ASTDirUserDefined
+ @ // o.a.f.c.ASTDynamicTopLevelCall
- callee: . // o.a.f.c.ASTExpDot
- left-hand operand: ns // o.a.f.c.ASTExpVariable
- right-hand operand: "bar" // String
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/52a5f9eb/freemarker-core-test/src/test/resources/org/apache/freemarker/core/ast-range.ast
----------------------------------------------------------------------
diff --git a/freemarker-core-test/src/test/resources/org/apache/freemarker/core/ast-range.ast b/freemarker-core-test/src/test/resources/org/apache/freemarker/core/ast-range.ast
index 474b298..9d9bb4b 100644
--- a/freemarker-core-test/src/test/resources/org/apache/freemarker/core/ast-range.ast
+++ b/freemarker-core-test/src/test/resources/org/apache/freemarker/core/ast-range.ast
@@ -257,7 +257,7 @@
- AST-node subtype: "0" // Integer
#text // o.a.f.c.ASTStaticText
- content: "\n\n" // String
- @ // o.a.f.c.ASTDirUserDefined
+ @ // o.a.f.c.ASTDynamicTopLevelCall
- callee: m // o.a.f.c.ASTExpVariable
- argument value: .. // o.a.f.c.ASTExpRange
- left-hand operand: * // o.a.f.c.ASTExpArithmetic
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/52a5f9eb/freemarker-core-test/src/test/resources/org/apache/freemarker/core/ast-strlitinterpolation.ast
----------------------------------------------------------------------
diff --git a/freemarker-core-test/src/test/resources/org/apache/freemarker/core/ast-strlitinterpolation.ast b/freemarker-core-test/src/test/resources/org/apache/freemarker/core/ast-strlitinterpolation.ast
index da4cf66..a87144e 100644
--- a/freemarker-core-test/src/test/resources/org/apache/freemarker/core/ast-strlitinterpolation.ast
+++ b/freemarker-core-test/src/test/resources/org/apache/freemarker/core/ast-strlitinterpolation.ast
@@ -19,7 +19,7 @@
#mixedContent // o.a.f.c.ASTImplicitParent
#text // o.a.f.c.ASTStaticText
- content: "1. " // String
- @ // o.a.f.c.ASTDirUserDefined
+ @ // o.a.f.c.ASTDynamicTopLevelCall
- callee: m // o.a.f.c.ASTExpVariable
- argument name: "x" // String
- argument value: dynamic "..." // o.a.f.c.ASTExpStringLiteral
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/52a5f9eb/freemarker-core-test/src/test/resources/org/apache/freemarker/core/ast-whitespacestripping.ast
----------------------------------------------------------------------
diff --git a/freemarker-core-test/src/test/resources/org/apache/freemarker/core/ast-whitespacestripping.ast b/freemarker-core-test/src/test/resources/org/apache/freemarker/core/ast-whitespacestripping.ast
index 41e3b12..dd87f1f 100644
--- a/freemarker-core-test/src/test/resources/org/apache/freemarker/core/ast-whitespacestripping.ast
+++ b/freemarker-core-test/src/test/resources/org/apache/freemarker/core/ast-whitespacestripping.ast
@@ -50,17 +50,17 @@
- namespace: null // Null
#text // o.a.f.c.ASTStaticText
- content: "\n" // String
- @ // o.a.f.c.ASTDirUserDefined
+ @ // o.a.f.c.ASTDynamicTopLevelCall
- callee: b // o.a.f.c.ASTExpVariable
#text // o.a.f.c.ASTStaticText
- content: " x\n" // String
#text // o.a.f.c.ASTStaticText
- content: "\n" // String
- @ // o.a.f.c.ASTDirUserDefined
+ @ // o.a.f.c.ASTDynamicTopLevelCall
- callee: c // o.a.f.c.ASTExpVariable
#text // o.a.f.c.ASTStaticText
- content: "\n" // String
- @ // o.a.f.c.ASTDirUserDefined
+ @ // o.a.f.c.ASTDynamicTopLevelCall
- callee: d // o.a.f.c.ASTExpVariable
#text // o.a.f.c.ASTStaticText
- content: "\na\n" // String
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/52a5f9eb/freemarker-core/src/main/java/org/apache/freemarker/core/ASTDirDynamicCall.java
----------------------------------------------------------------------
diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/ASTDirDynamicCall.java b/freemarker-core/src/main/java/org/apache/freemarker/core/ASTDirDynamicCall.java
deleted file mode 100644
index 23e081f..0000000
--- a/freemarker-core/src/main/java/org/apache/freemarker/core/ASTDirDynamicCall.java
+++ /dev/null
@@ -1,468 +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;
-
-import java.io.IOException;
-import java.io.Writer;
-import java.util.Collection;
-import java.util.LinkedHashMap;
-
-import org.apache.freemarker.core.model.CallPlace;
-import org.apache.freemarker.core.model.Constants;
-import org.apache.freemarker.core.model.TemplateCallableModel;
-import org.apache.freemarker.core.model.TemplateDirectiveModel2;
-import org.apache.freemarker.core.model.TemplateFunctionModel;
-import org.apache.freemarker.core.model.TemplateModel;
-import org.apache.freemarker.core.model.TemplateSequenceModel;
-import org.apache.freemarker.core.util.BugException;
-import org.apache.freemarker.core.util.CommonSupplier;
-import org.apache.freemarker.core.util.StringToIndexMap;
-import org.apache.freemarker.core.util._ArrayAdapterList;
-import org.apache.freemarker.core.util._StringUtil;
-
-import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
-
-/**
- * AST directive node: {@code <@exp ...>}.
- * Executes a {@link TemplateCallableModel} that's embedded directly into the static text. At least in the default
- * template language the value must be a {@link TemplateDirectiveModel2}, though technically calling a
- * {@link TemplateFunctionModel} is possible as well.
- * <p>
- * The {@link TemplateCallableModel} object is obtained on runtime by evaluating an expression, and the parameter list
- * is also validated (how many positional parameters are allowed, what named parameters are supported) then. Hence, the
- * call is "dynamic".
- */
-class ASTDirDynamicCall extends ASTDirective implements CallPlace {
-
- static final class NamedArgument {
- private final String name;
- private final ASTExpression value;
-
- public NamedArgument(String name, ASTExpression value) {
- this.name = name;
- this.value = value;
- }
- }
-
- private final ASTExpression callableValueExp;
- private final ASTExpression[] positionalArgs;
- private final NamedArgument[] namedArgs;
- private final StringToIndexMap loopVarNames;
- private final boolean allowCallingFunctions;
-
- private CustomDataHolder customDataHolder;
-
- /**
- * @param allowCallingFunctions Some template languages may allow calling {@link TemplateFunctionModel}-s
- * directly embedded into the static text, in which case this should be {@code true}.
- */
- ASTDirDynamicCall(
- ASTExpression callableValueExp, boolean allowCallingFunctions,
- ASTExpression[] positionalArgs, NamedArgument[] namedArgs, StringToIndexMap loopVarNames,
- TemplateElements children) {
- this.callableValueExp = callableValueExp;
- this.allowCallingFunctions = allowCallingFunctions;
-
- if (positionalArgs != null && positionalArgs.length == 0
- || namedArgs != null && namedArgs.length == 0
- || loopVarNames != null && loopVarNames.size() == 0) {
- throw new IllegalArgumentException("Use null instead of empty collections");
- }
- this.positionalArgs = positionalArgs;
- this.namedArgs = namedArgs;
- this.loopVarNames = loopVarNames;
-
- setChildren(children);
- }
-
- @Override
- ASTElement[] accept(Environment env) throws TemplateException, IOException {
- TemplateCallableModel callableValue;
- TemplateDirectiveModel2 directive;
- TemplateFunctionModel function;
- {
- TemplateModel callableValueTM = callableValueExp._eval(env);
- if (callableValueTM instanceof TemplateDirectiveModel2) {
- callableValue = (TemplateCallableModel) callableValueTM;
- directive = (TemplateDirectiveModel2) callableValueTM;
- function = null;
- } else if (callableValueTM instanceof TemplateFunctionModel) {
- if (!allowCallingFunctions) {
- // TODO [FM3][CF] Better exception
- throw new NonUserDefinedDirectiveLikeException(
- "Calling functions is not allowed on the top level in this template language", env);
- }
- callableValue = (TemplateCallableModel) callableValueTM;
- directive = null;
- function = (TemplateFunctionModel) callableValue;
- } else if (callableValueTM instanceof ASTDirMacro) {
- // TODO [FM3][CF] Until macros were refactored to be TemplateDirectiveModel2-s, we have this hack here.
- ASTDirMacro macro = (ASTDirMacro) callableValueTM;
- if (macro.isFunction()) {
- throw new _MiscTemplateException(env,
- "Routine ", new _DelayedJQuote(macro.getName()), " is a function, not a directive. "
- + "Functions can only be called from expressions, like in ${f()}, ${x + f()} or ",
- "<@someDirective someParam=f() />", ".");
- }
-
- // We have to convert arguments to the legacy data structures... yet again, it's only a temporary hack.
- LinkedHashMap<String, ASTExpression> macroNamedArgs;
- if (namedArgs != null) {
- macroNamedArgs = new LinkedHashMap<>(namedArgs.length * 4 / 3);
- for (NamedArgument namedArg : namedArgs) {
- macroNamedArgs.put(namedArg.name, namedArg.value);
- }
- } else {
- macroNamedArgs = null;
- }
- env.invoke(macro,
- macroNamedArgs,
- _ArrayAdapterList.adapt(positionalArgs),
- loopVarNames != null ? loopVarNames.getKeys() : null,
- getChildBuffer());
- return null;
- } else if (callableValueTM == null) {
- throw InvalidReferenceException.getInstance(callableValueExp, env);
- } else {
- throw new NonUserDefinedDirectiveLikeException(callableValueExp, callableValueTM, env);
- }
- }
-
- int predefPosArgCnt = callableValue.getPredefinedPositionalArgumentCount();
- boolean hasPosVarargsArg = callableValue.hasPositionalVarargsArgument();
-
- if (positionalArgs != null && positionalArgs.length > predefPosArgCnt && !hasPosVarargsArg) {
- throw new _MiscTemplateException(this,
- "The target callable ",
- (predefPosArgCnt != 0
- ? new Object[] { "can only have ", predefPosArgCnt }
- : "can't have"
- ),
- " arguments passed by position, but the invocation has ",
- positionalArgs.length, " such arguments.");
- }
-
- TemplateModel[] execArgs = new TemplateModel[callableValue.getArgumentArraySize()];
-
- // Fill predefined positional args:
- if (positionalArgs != null) {
- int actualPredefPosArgCnt = Math.min(positionalArgs.length, predefPosArgCnt);
- for (int argIdx = 0; argIdx < actualPredefPosArgCnt; argIdx++) {
- execArgs[argIdx] = positionalArgs[argIdx].eval(env);
- }
- }
-
- if (hasPosVarargsArg) {
- int posVarargCnt = positionalArgs != null ? positionalArgs.length - predefPosArgCnt : 0;
- TemplateSequenceModel varargsSeq;
- if (posVarargCnt <= 0) {
- varargsSeq = Constants.EMPTY_SEQUENCE;
- } else {
- NativeSequence nativeSeq = new NativeSequence(posVarargCnt);
- varargsSeq = nativeSeq;
- for (int posVarargIdx = 0; posVarargIdx < posVarargCnt; posVarargIdx++) {
- nativeSeq.add(positionalArgs[predefPosArgCnt + posVarargIdx].eval(env));
- }
- }
- execArgs[predefPosArgCnt] = varargsSeq;
- }
-
- int namedVarargsArgumentIndex = callableValue.getNamedVarargsArgumentIndex();
- NativeHashEx2 namedVarargsHash = null;
- if (namedArgs != null) {
- for (NamedArgument namedArg : namedArgs) {
- int argIdx = callableValue.getPredefinedNamedArgumentIndex(namedArg.name);
- if (argIdx != -1) {
- execArgs[argIdx] = namedArg.value.eval(env);
- } else {
- if (namedVarargsHash == null) {
- if (namedVarargsArgumentIndex == -1) {
- Collection<String> validNames = callableValue.getPredefinedNamedArgumentNames();
- throw new _MiscTemplateException(this,
- validNames == null || validNames.isEmpty()
- ? new Object[] {
- "The target callable doesn't have any by-name-passed parameters (like ",
- new _DelayedJQuote(namedArg.name), ")"
- }
- : new Object[] {
- "The target callable has no by-name-passed parameter called ",
- new _DelayedJQuote(namedArg.name), ". The supported parameter names are:\n",
- new _DelayedJQuotedListing(validNames)
- });
- }
- namedVarargsHash = new NativeHashEx2();
- }
- namedVarargsHash.put(namedArg.name, namedArg.value.eval(env));
- }
- }
- }
- if (namedVarargsArgumentIndex != -1) {
- execArgs[namedVarargsArgumentIndex] = namedVarargsHash != null ? namedVarargsHash : Constants.EMPTY_HASH;
- }
-
- if (directive != null) {
- directive.execute(execArgs, this, env.getOut(), env);
- } else {
- TemplateModel result = function.execute(execArgs, env, this);
- if (result == null) {
- throw new _MiscTemplateException(this, "Function has returned no value (or null)");
- }
- // TODO [FM3][CF]
- throw new BugException("Top-level function call not yet implemented");
- }
-
- return null;
- }
-
- @Override
- boolean isNestedBlockRepeater() {
- return true;
- }
-
- @Override
- boolean isShownInStackTrace() {
- return true;
- }
-
- @Override
- protected String dump(boolean canonical) {
- StringBuilder sb = new StringBuilder();
- if (canonical) sb.append('<');
- sb.append('@');
- MessageUtil.appendExpressionAsUntearable(sb, callableValueExp);
- boolean nameIsInParen = sb.charAt(sb.length() - 1) == ')';
- if (positionalArgs != null) {
- for (int i = 0; i < positionalArgs.length; i++) {
- ASTExpression argExp = (ASTExpression) positionalArgs[i];
- if (i != 0) {
- sb.append(',');
- }
- sb.append(' ');
- sb.append(argExp.getCanonicalForm());
- }
- }
- if (namedArgs != null) {
- for (NamedArgument namedArg : namedArgs) {
- sb.append(' ');
- sb.append(_StringUtil.toFTLTopLevelIdentifierReference(namedArg.name));
- sb.append('=');
- MessageUtil.appendExpressionAsUntearable(sb, namedArg.value);
- }
- }
- if (loopVarNames != null) {
- sb.append("; ");
- boolean first = true;
- for (String loopVarName : loopVarNames.getKeys()) {
- if (!first) {
- sb.append(", ");
- } else {
- first = false;
- }
- sb.append(_StringUtil.toFTLTopLevelIdentifierReference(loopVarName));
- }
- }
- if (canonical) {
- if (getChildCount() == 0) {
- sb.append("/>");
- } else {
- sb.append('>');
- sb.append(getChildrenCanonicalForm());
- sb.append("</@");
- if (!nameIsInParen
- && (callableValueExp instanceof ASTExpVariable
- || (callableValueExp instanceof ASTExpDot && ((ASTExpDot) callableValueExp).onlyHasIdentifiers()))) {
- sb.append(callableValueExp.getCanonicalForm());
- }
- sb.append('>');
- }
- }
- return sb.toString();
- }
-
- @Override
- String getASTNodeDescriptor() {
- return "~";
- }
-
- @Override
- int getParameterCount() {
- return 1/*nameExp*/
- + (positionalArgs != null ? positionalArgs.length : 0)
- + (namedArgs != null ? namedArgs.length * 2 : 0)
- + (loopVarNames != null ? loopVarNames.size() : 0);
- }
-
- @Override
- Object getParameterValue(int idx) {
- if (idx == 0) {
- return callableValueExp;
- } else {
- int base = 1;
- final int positionalArgsSize = positionalArgs != null ? positionalArgs.length : 0;
- if (idx - base < positionalArgsSize) {
- return positionalArgs[idx - base];
- } else {
- base += positionalArgsSize;
- final int namedArgsSize = namedArgs != null ? namedArgs.length : 0;
- if (idx - base < namedArgsSize * 2) {
- NamedArgument namedArg = namedArgs[(idx - base) / 2];
- return (idx - base) % 2 == 0 ? namedArg.name : namedArg.value;
- } else {
- base += namedArgsSize * 2;
- final int bodyParameterNamesSize = loopVarNames != null ? loopVarNames.size() : 0;
- if (idx - base < bodyParameterNamesSize) {
- return loopVarNames.getKeys().get(idx - base);
- } else {
- throw new IndexOutOfBoundsException();
- }
- }
- }
- }
- }
-
- @Override
- ParameterRole getParameterRole(int idx) {
- if (idx == 0) {
- return ParameterRole.CALLEE;
- } else {
- int base = 1;
- final int positionalArgsSize = positionalArgs != null ? positionalArgs.length : 0;
- if (idx - base < positionalArgsSize) {
- return ParameterRole.ARGUMENT_VALUE;
- } else {
- base += positionalArgsSize;
- final int namedArgsSize = namedArgs != null ? namedArgs.length : 0;
- if (idx - base < namedArgsSize * 2) {
- return (idx - base) % 2 == 0 ? ParameterRole.ARGUMENT_NAME : ParameterRole.ARGUMENT_VALUE;
- } else {
- base += namedArgsSize * 2;
- final int bodyParameterNamesSize = loopVarNames != null ? loopVarNames.size() : 0;
- if (idx - base < bodyParameterNamesSize) {
- return ParameterRole.TARGET_LOOP_VARIABLE;
- } else {
- throw new IndexOutOfBoundsException();
- }
- }
- }
- }
- }
-
- // -----------------------------------------------------------------------------------------------------------------
- // CallPlace API:
-
- @Override
- public boolean hasNestedContent() {
- return getChildCount() != 0;
- }
-
- @Override
- public int getLoopVariableCount() {
- return loopVarNames != null ? loopVarNames.size() : 0;
- }
-
- @Override
- public void executeNestedContent(TemplateModel[] loopVariableValues, Writer out, Environment env)
- throws TemplateException, IOException {
- env.visit(getChildBuffer(), loopVarNames, loopVariableValues, out);
- }
-
- @Override
- @SuppressFBWarnings(value={ "IS2_INCONSISTENT_SYNC", "DC_DOUBLECHECK" }, justification="Performance tricks")
- public Object getOrCreateCustomData(Object providerIdentity, CommonSupplier<?> supplier)
- throws CallPlaceCustomDataInitializationException {
- // We are using double-checked locking, utilizing Java memory model "final" trick.
- // Note that this.customDataHolder is NOT volatile.
-
- CustomDataHolder customDataHolder = this.customDataHolder; // Findbugs false alarm
- if (customDataHolder == null) { // Findbugs false alarm
- synchronized (this) {
- customDataHolder = this.customDataHolder;
- if (customDataHolder == null || customDataHolder.providerIdentity != providerIdentity) {
- customDataHolder = createNewCustomData(providerIdentity, supplier);
- this.customDataHolder = customDataHolder;
- }
- }
- }
-
- if (customDataHolder.providerIdentity != providerIdentity) {
- synchronized (this) {
- customDataHolder = this.customDataHolder;
- if (customDataHolder == null || customDataHolder.providerIdentity != providerIdentity) {
- customDataHolder = createNewCustomData(providerIdentity, supplier);
- this.customDataHolder = customDataHolder;
- }
- }
- }
-
- return customDataHolder.customData;
- }
-
- private CustomDataHolder createNewCustomData(Object provierIdentity, CommonSupplier supplier)
- throws CallPlaceCustomDataInitializationException {
- CustomDataHolder customDataHolder;
- Object customData;
- try {
- customData = supplier.get();
- } catch (Exception e) {
- throw new CallPlaceCustomDataInitializationException(
- "Failed to initialize custom data for provider identity "
- + _StringUtil.tryToString(provierIdentity) + " via factory "
- + _StringUtil.tryToString(supplier), e);
- }
- if (customData == null) {
- throw new NullPointerException("CommonSupplier.get() has returned null");
- }
- customDataHolder = new CustomDataHolder(provierIdentity, customData);
- return customDataHolder;
- }
-
- @Override
- public boolean isNestedOutputCacheable() {
- return isChildrenOutputCacheable();
- }
-
- @Override
- public int getFirstTargetJavaParameterTypeIndex() {
- // TODO [FM3]
- return -1;
- }
-
- @Override
- public Class<?> getTargetJavaParameterType(int argIndex) {
- // TODO [FM3]
- return null;
- }
-
- /**
- * Used for implementing double check locking in implementing the
- * {@link #getOrCreateCustomData(Object, CommonSupplier)}.
- */
- private static class CustomDataHolder {
-
- private final Object providerIdentity;
- private final Object customData;
- public CustomDataHolder(Object providerIdentity, Object customData) {
- this.providerIdentity = providerIdentity;
- this.customData = customData;
- }
-
- }
-
-}