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 <ddek...@apache.org> Authored: Sun Jul 30 02:04:56 2017 +0200 Committer: ddekany <ddek...@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>x</@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; - } - - } - -}