Repository: incubator-freemarker Updated Branches: refs/heads/2.3-gae 4a5eec42d -> 7adecaee2
FREEMARKER-83: Modified how .macro_caller_template_name works... now it has no surprising restrictions, and is simpler. Project: http://git-wip-us.apache.org/repos/asf/incubator-freemarker/repo Commit: http://git-wip-us.apache.org/repos/asf/incubator-freemarker/commit/7adecaee Tree: http://git-wip-us.apache.org/repos/asf/incubator-freemarker/tree/7adecaee Diff: http://git-wip-us.apache.org/repos/asf/incubator-freemarker/diff/7adecaee Branch: refs/heads/2.3-gae Commit: 7adecaee25f2c5dddc8c75ff2aeb54fbefbf8458 Parents: 4a5eec4 Author: ddekany <ddek...@apache.org> Authored: Fri Mar 9 22:38:35 2018 +0100 Committer: ddekany <ddek...@apache.org> Committed: Fri Mar 9 22:38:35 2018 +0100 ---------------------------------------------------------------------- .../java/freemarker/core/BuiltinVariable.java | 15 ++++---- src/main/java/freemarker/core/Environment.java | 36 ++------------------ src/main/java/freemarker/core/Macro.java | 6 ++-- src/main/java/freemarker/core/UnifiedCall.java | 2 +- src/manual/en_US/book.xml | 17 +++------ .../core/MacroCallerTemplateNameTest.java | 36 +++++++++++--------- 6 files changed, 38 insertions(+), 74 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/7adecaee/src/main/java/freemarker/core/BuiltinVariable.java ---------------------------------------------------------------------- diff --git a/src/main/java/freemarker/core/BuiltinVariable.java b/src/main/java/freemarker/core/BuiltinVariable.java index 9b9b135..13dd67d 100644 --- a/src/main/java/freemarker/core/BuiltinVariable.java +++ b/src/main/java/freemarker/core/BuiltinVariable.java @@ -22,6 +22,7 @@ package freemarker.core; import java.util.Arrays; import java.util.Date; +import freemarker.core.Macro.Context; import freemarker.template.Configuration; import freemarker.template.SimpleDate; import freemarker.template.SimpleScalar; @@ -30,6 +31,7 @@ import freemarker.template.TemplateException; import freemarker.template.TemplateHashModel; import freemarker.template.TemplateModel; import freemarker.template.TemplateModelException; +import freemarker.template.TemplateScalarModel; import freemarker.template._TemplateAPI; import freemarker.template.utility.StringUtil; @@ -253,14 +255,13 @@ final class BuiltinVariable extends Expression { return GetOptionalTemplateMethod.INSTANCE_CC; } if (name == MACRO_CALLER_TEMPLATE_NAME || name == MACRO_CALLER_TEMPLATE_NAME_CC) { - UnifiedCall caller; - try { - caller = env.getMacroCaller(); - } catch (IllegalStateException e) { - throw new TemplateException("Failed to resolve ." + name + ": " + e.getMessage(), e, env); + Context ctx = env.getCurrentMacroContext(); + if (ctx == null) { + throw new TemplateException( + "Can't get ." + name + " here, as there's no macro call in context.", env); } - String name = caller.getTemplate().getName(); - return name != null ? new SimpleScalar(name) : SimpleScalar.EMPTY_STRING; + String name = ctx.callPlace.getTemplate().getName(); + return name != null ? new SimpleScalar(name) : TemplateScalarModel.EMPTY_STRING; } throw new _MiscTemplateException(this, http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/7adecaee/src/main/java/freemarker/core/Environment.java ---------------------------------------------------------------------- diff --git a/src/main/java/freemarker/core/Environment.java b/src/main/java/freemarker/core/Environment.java index 5f20c26..2d4b585 100644 --- a/src/main/java/freemarker/core/Environment.java +++ b/src/main/java/freemarker/core/Environment.java @@ -281,36 +281,6 @@ public final class Environment extends Configurable { } /** - * Gets the non-{@code null} {@link UnifiedCall} of the caller of the macro whose context we are in; note - * that you can't call this from everywhere. Specifically, the FTL call stack must not contain {@code #nested} or a - * call to user-defined-directive after the stack entry of the {@code #macro} directive. This practically means that - * this should be called on the top-level inside {@code #macro}, or inside some core directives like {@code #if}. - * - * @throws IllegalStateException - * If there's no macro caller or it can't be figured out at this point of the template execution. - */ - UnifiedCall getMacroCaller() throws IllegalStateException { - for (int ln = instructionStackSize - 1; ln > 0; ln--) { - TemplateElement te = instructionStack[ln]; - if (te instanceof Macro) { - TemplateElement macroCaller = instructionStack[ln - 1]; - if (macroCaller instanceof UnifiedCall) { - return (UnifiedCall) macroCaller; - } - } - // Avoid returning the caller of @nested in `<#macro called><@inner>${getMacroCallerHere()}</@></#macro>`; - // the #macro that defines "inner" would break our logic above. - if (te instanceof BodyInstruction) { - throw new IllegalStateException( - "Can't get the location of the macro caller here, because you are inside an user-defined " - + "directive call that's nested inside the #macro directive. (You may " - + "need to get the caller location earlier, and store it in a local variable for later use.)"); - } - } - throw new IllegalStateException("There's no macro caller at this point."); - } - - /** * Deletes cached values that meant to be valid only during a single template execution. */ private void clearCachedValues() { @@ -624,7 +594,7 @@ public final class Environment extends Configurable { void invokeNestedContent(BodyInstruction.Context bodyCtx) throws TemplateException, IOException { Macro.Context invokingMacroContext = getCurrentMacroContext(); LocalContextStack prevLocalContextStack = localContextStack; - TemplateElement[] nestedContentBuffer = invokingMacroContext.nestedContentBuffer; + TemplateElement[] nestedContentBuffer = invokingMacroContext.callPlace.getChildBuffer(); if (nestedContentBuffer != null) { this.currentMacroContext = invokingMacroContext.prevMacroContext; currentNamespace = invokingMacroContext.nestedContentNamespace; @@ -765,7 +735,7 @@ public final class Environment extends Configurable { */ void invoke(Macro macro, Map namedArgs, List positionalArgs, - List bodyParameterNames, TemplateElement[] childBuffer) throws TemplateException, IOException { + List bodyParameterNames, TemplateElement callPlace) throws TemplateException, IOException { if (macro == Macro.DO_NOTHING_MACRO) { return; } @@ -780,7 +750,7 @@ public final class Environment extends Configurable { elementPushed = false; } try { - final Macro.Context macroCtx = macro.new Context(this, childBuffer, bodyParameterNames); + final Macro.Context macroCtx = macro.new Context(this, callPlace, bodyParameterNames); // Causes the evaluation of argument expressions: setMacroContextLocalsFromArguments(macroCtx, macro, namedArgs, positionalArgs); http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/7adecaee/src/main/java/freemarker/core/Macro.java ---------------------------------------------------------------------- diff --git a/src/main/java/freemarker/core/Macro.java b/src/main/java/freemarker/core/Macro.java index 2ea9c2c..9f743cb 100644 --- a/src/main/java/freemarker/core/Macro.java +++ b/src/main/java/freemarker/core/Macro.java @@ -154,17 +154,17 @@ public final class Macro extends TemplateElement implements TemplateModel { class Context implements LocalContext { final Environment.Namespace localVars; - final TemplateElement[] nestedContentBuffer; + final TemplateElement callPlace; final Environment.Namespace nestedContentNamespace; final List nestedContentParameterNames; final LocalContextStack prevLocalContextStack; final Context prevMacroContext; Context(Environment env, - TemplateElement[] nestedContentBuffer, + TemplateElement callPlace, List nestedContentParameterNames) { this.localVars = env.new Namespace(); - this.nestedContentBuffer = nestedContentBuffer; + this.callPlace = callPlace; this.nestedContentNamespace = env.getCurrentNamespace(); this.nestedContentParameterNames = nestedContentParameterNames; this.prevLocalContextStack = env.getLocalContextStack(); http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/7adecaee/src/main/java/freemarker/core/UnifiedCall.java ---------------------------------------------------------------------- diff --git a/src/main/java/freemarker/core/UnifiedCall.java b/src/main/java/freemarker/core/UnifiedCall.java index f445cd3..68777d3 100644 --- a/src/main/java/freemarker/core/UnifiedCall.java +++ b/src/main/java/freemarker/core/UnifiedCall.java @@ -80,7 +80,7 @@ final class UnifiedCall extends TemplateElement implements DirectiveCallPlace { + "Functions can only be called from expressions, like in ${f()}, ${x + f()} or ", "<@someDirective someParam=f() />", "."); } - env.invoke(macro, namedArgs, positionalArgs, bodyParameterNames, getChildBuffer()); + env.invoke(macro, namedArgs, positionalArgs, bodyParameterNames, this); } else { boolean isDirectiveModel = tm instanceof TemplateDirectiveModel; if (isDirectiveModel || tm instanceof TemplateTransformModel) { http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/7adecaee/src/manual/en_US/book.xml ---------------------------------------------------------------------- diff --git a/src/manual/en_US/book.xml b/src/manual/en_US/book.xml index 6c9c8bb..f45a516 100644 --- a/src/manual/en_US/book.xml +++ b/src/manual/en_US/book.xml @@ -23316,19 +23316,10 @@ There was no specific handler for node y </indexterm><literal>macro_caller_template_name</literal>: Returns the name (path) of the template from which the current macro was called. It's mostly useful if you want to resolve paths relative to - the caller template. If the caller template is nameless, this will - be an empty string (not a missing value). Reading this variable will - cause error if you aren't inside a macro call, also if you are - inside an user-defined directive call - (<literal><@<replaceable>...</replaceable>></literal>) that's - nested inside the <literal>macro</literal> directive (as in - <literal><#macro - m><@x>${.macro_caller_template_name}<#-- FAILS! - --></@></#macro></literal>). (Note that if <link - linkend="pgui_config_incompatible_improvements">incompatible - improvements</link> is set to less than 2.3.28, then when using this - variable in an argument to a macro, it will be incorrectly evaluated - to the caller of the called macro.)</para> + the caller template. To serve that purpose better, if the caller + template is nameless, this will be an empty string (not a missing + value). Reading this variable will cause error if you aren't inside + a macro call.</para> </listitem> <listitem> http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/7adecaee/src/test/java/freemarker/core/MacroCallerTemplateNameTest.java ---------------------------------------------------------------------- diff --git a/src/test/java/freemarker/core/MacroCallerTemplateNameTest.java b/src/test/java/freemarker/core/MacroCallerTemplateNameTest.java index 32b1d4e..5a26b8f 100644 --- a/src/test/java/freemarker/core/MacroCallerTemplateNameTest.java +++ b/src/test/java/freemarker/core/MacroCallerTemplateNameTest.java @@ -35,28 +35,31 @@ public class MacroCallerTemplateNameTest extends TemplateTest { @Test public void testNoCaller() throws Exception { - assertErrorContains("${.macroCallerTemplateName}", "no macro caller", ".macroCallerTemplateName"); - assertErrorContains("${.macro_caller_template_name}", "no macro caller", ".macro_caller_template_name"); + assertErrorContains("${.macroCallerTemplateName}", "no macro call", ".macroCallerTemplateName"); + assertErrorContains("${.macro_caller_template_name}", "no macro call", ".macro_caller_template_name"); assertErrorContains("" + "<#macro m><#nested></#macro>" + "<@m>${.macroCallerTemplateName}</@>", - "nested", ".macroCallerTemplateName"); + "no macro call", ".macroCallerTemplateName"); - assertErrorContains("" + addTemplate("main.ftl", "${.macroCallerTemplateName}"); + assertErrorContainsForNamed("main.ftl", "no macro call"); + } + + @Test + public void testNested() throws Exception { + assertOutput("" + "<#macro m><#nested></#macro>" + "<#macro m2><@m>${.macroCallerTemplateName}</@></#macro>" - + "<@m2/>", - "nested", ".macroCallerTemplateName"); + + "[<@m2/>]", + "[]"); assertOutput("" + "<#macro m2>${.macroCallerTemplateName}</#macro>" + "[<@m2/>]", "[]"); - - addTemplate("main.ftl", "${.macroCallerTemplateName}"); - assertErrorContainsForNamed("main.ftl", "no macro caller"); } - + @Test public void testSameTemplateCaller() throws Exception { addTemplate("main.ftl", "" @@ -127,13 +130,12 @@ public class MacroCallerTemplateNameTest extends TemplateTest { + "</#macro>" + "<#macro m2><@m .macroCallerTemplateName /></#macro>"); - assertOutputForNamed("main.ftl", "" - + "x: main.ftl; y: main.ftl; caller: main.ftl;" - + "x: main.ftl; y: inc.ftl; caller: inc.ftl;"); - getConfiguration().setIncompatibleImprovements(Configuration.VERSION_2_3_27); - assertOutputForNamed("main.ftl", "" - + "x: main.ftl; y: main.ftl; caller: main.ftl;" - + "x: inc.ftl; y: inc.ftl; caller: inc.ftl;"); + for (int i = 0; i < 2; i++) { + assertOutputForNamed("main.ftl", "" + + "x: main.ftl; y: main.ftl; caller: main.ftl;" + + "x: main.ftl; y: inc.ftl; caller: inc.ftl;"); + getConfiguration().setIncompatibleImprovements(Configuration.VERSION_2_3_27); // Has no effect + } } @Test