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
Commit: 7adecaee25f2c5dddc8c75ff2aeb54fbefbf8458
Parents: 4a5eec4
Author: ddekany <ddekany@apache.org>
Authored: Fri Mar 9 22:38:35 2018 +0100
Committer: ddekany <ddekany@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
|