Repository: incubator-freemarker Updated Branches: refs/heads/2.3-gae 81e33b951 -> 5f821f174
Renamed macro_caller_template_name to caller_template_name, as now it also works for #function. Updated and cleaned up related tests. Project: http://git-wip-us.apache.org/repos/asf/incubator-freemarker/repo Commit: http://git-wip-us.apache.org/repos/asf/incubator-freemarker/commit/5f821f17 Tree: http://git-wip-us.apache.org/repos/asf/incubator-freemarker/tree/5f821f17 Diff: http://git-wip-us.apache.org/repos/asf/incubator-freemarker/diff/5f821f17 Branch: refs/heads/2.3-gae Commit: 5f821f174a6226440e45b4fe6e015acfdb6bfdd7 Parents: 81e33b9 Author: ddekany <ddek...@apache.org> Authored: Sat Mar 10 08:20:08 2018 +0100 Committer: ddekany <ddek...@apache.org> Committed: Sat Mar 10 08:20:08 2018 +0100 ---------------------------------------------------------------------- .../java/freemarker/core/BuiltinVariable.java | 16 +- src/main/java/freemarker/core/Environment.java | 7 +- src/main/java/freemarker/core/Macro.java | 4 +- src/main/java/freemarker/core/MethodCall.java | 2 +- src/manual/en_US/book.xml | 41 +++-- .../freemarker/core/CallerTemplateNameTest.java | 163 +++++++++++++++++++ .../core/MacroCallerTemplateNameTest.java | 157 ------------------ 7 files changed, 204 insertions(+), 186 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/5f821f17/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 5e435db..b42dcb3 100644 --- a/src/main/java/freemarker/core/BuiltinVariable.java +++ b/src/main/java/freemarker/core/BuiltinVariable.java @@ -76,11 +76,14 @@ final class BuiltinVariable extends Expression { static final String NOW = "now"; static final String GET_OPTIONAL_TEMPLATE = "get_optional_template"; static final String GET_OPTIONAL_TEMPLATE_CC = "getOptionalTemplate"; - static final String MACRO_CALLER_TEMPLATE_NAME = "macro_caller_template_name"; - static final String MACRO_CALLER_TEMPLATE_NAME_CC = "macroCallerTemplateName"; + static final String CALLER_TEMPLATE_NAME = "caller_template_name"; + static final String CALLER_TEMPLATE_NAME_CC = "callerTemplateName"; static final String[] SPEC_VAR_NAMES = new String[] { + // IMPORTANT! Keep this sorted alphabetically! AUTO_ESC_CC, AUTO_ESC, + CALLER_TEMPLATE_NAME_CC, + CALLER_TEMPLATE_NAME, CURRENT_NODE_CC, CURRENT_TEMPLATE_NAME_CC, CURRENT_NODE, @@ -98,8 +101,6 @@ final class BuiltinVariable extends Expression { LOCALE_OBJECT_CC, LOCALE_OBJECT, LOCALS, - MACRO_CALLER_TEMPLATE_NAME_CC, - MACRO_CALLER_TEMPLATE_NAME, MAIN, MAIN_TEMPLATE_NAME_CC, MAIN_TEMPLATE_NAME, @@ -254,13 +255,14 @@ final class BuiltinVariable extends Expression { if (name == GET_OPTIONAL_TEMPLATE_CC) { return GetOptionalTemplateMethod.INSTANCE_CC; } - if (name == MACRO_CALLER_TEMPLATE_NAME || name == MACRO_CALLER_TEMPLATE_NAME_CC) { + if (name == CALLER_TEMPLATE_NAME || name == CALLER_TEMPLATE_NAME_CC) { Context ctx = env.getCurrentMacroContext(); if (ctx == null) { throw new TemplateException( - "Can't get ." + name + " here, as there's no macro call in context.", env); + "Can't get ." + name + " here, as there's no macro or function (that's " + + "implemented in the template) call in context.", env); } - TemplateElement callPlace = ctx.callPlace; + TemplateObject callPlace = ctx.callPlace; String name = callPlace != null ? callPlace.getTemplate().getName() : null; return name != null ? new SimpleScalar(name) : TemplateScalarModel.EMPTY_STRING; } http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/5f821f17/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 2b3ba1f..6c8e202 100644 --- a/src/main/java/freemarker/core/Environment.java +++ b/src/main/java/freemarker/core/Environment.java @@ -594,8 +594,9 @@ public final class Environment extends Configurable { void invokeNestedContent(BodyInstruction.Context bodyCtx) throws TemplateException, IOException { Macro.Context invokingMacroContext = getCurrentMacroContext(); LocalContextStack prevLocalContextStack = localContextStack; - TemplateElement callPlace = invokingMacroContext.callPlace; - TemplateElement[] nestedContentBuffer = callPlace != null ? callPlace.getChildBuffer() : null; + TemplateObject callPlace = invokingMacroContext.callPlace; + TemplateElement[] nestedContentBuffer = callPlace instanceof TemplateElement + ? ((TemplateElement) callPlace).getChildBuffer() : null; if (nestedContentBuffer != null) { this.currentMacroContext = invokingMacroContext.prevMacroContext; currentNamespace = invokingMacroContext.nestedContentNamespace; @@ -736,7 +737,7 @@ public final class Environment extends Configurable { */ void invoke(Macro macro, Map namedArgs, List positionalArgs, - List bodyParameterNames, TemplateElement callPlace) throws TemplateException, IOException { + List bodyParameterNames, TemplateObject callPlace) throws TemplateException, IOException { if (macro == Macro.DO_NOTHING_MACRO) { return; } http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/5f821f17/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 9f743cb..7829c76 100644 --- a/src/main/java/freemarker/core/Macro.java +++ b/src/main/java/freemarker/core/Macro.java @@ -154,14 +154,14 @@ public final class Macro extends TemplateElement implements TemplateModel { class Context implements LocalContext { final Environment.Namespace localVars; - final TemplateElement callPlace; + final TemplateObject callPlace; final Environment.Namespace nestedContentNamespace; final List nestedContentParameterNames; final LocalContextStack prevLocalContextStack; final Context prevMacroContext; Context(Environment env, - TemplateElement callPlace, + TemplateObject callPlace, List nestedContentParameterNames) { this.localVars = env.new Namespace(); this.callPlace = callPlace; http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/5f821f17/src/main/java/freemarker/core/MethodCall.java ---------------------------------------------------------------------- diff --git a/src/main/java/freemarker/core/MethodCall.java b/src/main/java/freemarker/core/MethodCall.java index a50e496..675c30c 100644 --- a/src/main/java/freemarker/core/MethodCall.java +++ b/src/main/java/freemarker/core/MethodCall.java @@ -73,7 +73,7 @@ final class MethodCall extends Expression { Writer prevOut = env.getOut(); try { env.setOut(NullWriter.INSTANCE); - env.invoke(func, null, arguments.items, null, null); + env.invoke(func, null, arguments.items, null, this); } catch (IOException e) { // Should not occur throw new TemplateException("Unexpected exception during function execution", e, env); http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/5f821f17/src/manual/en_US/book.xml ---------------------------------------------------------------------- diff --git a/src/manual/en_US/book.xml b/src/manual/en_US/book.xml index f45a516..564898a 100644 --- a/src/manual/en_US/book.xml +++ b/src/manual/en_US/book.xml @@ -23212,6 +23212,28 @@ There was no specific handler for node y <listitem> <para><indexterm> + <primary>caller_template_name</primary> + </indexterm><literal>caller_template_name</literal> (available + since FreeMarker 2.3.28): Returns the name (path) of the template + from which the current <link + linkend="ref.directive.macro">macro</link> or <link + linkend="ref.directive.function">function</link> was called. It's + mostly useful if you want to resolve paths relative to 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 + or function call. In particular, directives and + <quote>methods</quote> implemented in Java will not influence the + value of this variable; it's only for macros and functions + implemented in templates. (<phrase + role="forProgrammers"><literal>TemplateDirectiveModel</literal> + implementations can get similar information via the + <literal>freemarker.core.DirectiveCallPlace</literal> + object.</phrase>)</para> + </listitem> + + <listitem> + <para><indexterm> <primary>current_template_name</primary> </indexterm><literal>current_template_name</literal>: The name of the template where we are now (available since FreeMarker 2.3.23). @@ -23311,18 +23333,6 @@ There was no specific handler for node y </listitem> <listitem> - <para><indexterm> - <primary>macro_caller_template_name</primary> - </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. 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> <para><literal>main</literal>: A hash that you can use to access the main <link linkend="dgui_misc_namespace">namespace</link>. Note that global variables like the variables of data-model are @@ -27282,12 +27292,11 @@ TemplateModel x = env.getVariable("x"); // get variable x</programlisting> <listitem> <para>Added new <link linkend="ref_specvar">special - variable</link>, <literal>macro_caller_template_name</literal> - (<link + variable</link>, <literal>caller_template_name</literal> (<link xlink:href="https://issues.apache.org/jira/browse/FREEMARKER-83">FREEMARKER-83</link>), which 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.</para> + current macro or function was called. It's mostly useful if you + want to resolve paths relative to the caller template.</para> </listitem> <listitem> http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/5f821f17/src/test/java/freemarker/core/CallerTemplateNameTest.java ---------------------------------------------------------------------- diff --git a/src/test/java/freemarker/core/CallerTemplateNameTest.java b/src/test/java/freemarker/core/CallerTemplateNameTest.java new file mode 100644 index 0000000..699dd85 --- /dev/null +++ b/src/test/java/freemarker/core/CallerTemplateNameTest.java @@ -0,0 +1,163 @@ +/* + * 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 freemarker.core; + +import org.junit.Test; + +import freemarker.template.Configuration; +import freemarker.test.TemplateTest; + +public class CallerTemplateNameTest extends TemplateTest { + + @Override + protected Configuration createConfiguration() throws Exception { + Configuration cfg = super.createConfiguration(); + cfg.setIncompatibleImprovements(Configuration.VERSION_2_3_28); + return cfg; + } + + @Test + public void testBaics() throws Exception { + addTemplate("main.ftl", "" + + "<#macro m>${.callerTemplateName}</#macro>" + + "<#function f()><#return .callerTemplateName></#function>" + + "<@m /> ${f()} [<#include 'other.ftl'>] <@m /> ${f()}"); + addTemplate("other.ftl", "" + + "<@m /> ${f()} [<#include 'yet-another.ftl'>] <@m /> ${f()}"); + addTemplate("yet-another.ftl", "" + + "<@m /> ${f()}"); + + assertOutputForNamed("main.ftl", "" + + "main.ftl main.ftl " + + "[other.ftl other.ftl " + + "[yet-another.ftl yet-another.ftl] " + + "other.ftl other.ftl] " + + "main.ftl main.ftl"); + } + + @Test + public void testNoCaller() throws Exception { + assertErrorContains("${.callerTemplateName}", "no macro or function", ".callerTemplateName"); + assertErrorContains("${.caller_template_name}", "no macro or function", ".caller_template_name"); + + assertErrorContains("" + + "<#macro m><#nested></#macro>" + + "<@m>${.callerTemplateName}</@>", + "no macro or function", ".callerTemplateName"); + + addTemplate("main.ftl", "${.callerTemplateName}"); + assertErrorContainsForNamed("main.ftl", "no macro or function"); + } + + @Test + public void testNamelessCaller() throws Exception { + assertOutput("" + + "<#macro m2>${.callerTemplateName}</#macro>" + + "[<@m2/>]", + "[]"); + } + + @Test + public void testNested() throws Exception { + addTemplate("main.ftl", "" + + "<#include 'lib1.ftl'>" + + "<#include 'lib2.ftl'>" + + "<@m1 />"); + addTemplate("lib1.ftl", "" + + "<#macro m1>" + + "${.callerTemplateName} [<@m2>${.callerTemplateName}</@m2>] ${.callerTemplateName}" + + "</#macro>"); + addTemplate("lib2.ftl", "" + + "<#macro m2>" + + "${.callerTemplateName} [<#nested>] ${.callerTemplateName}" + + "</#macro>"); + assertOutputForNamed("main.ftl", "" + + "main.ftl [lib1.ftl [main.ftl] lib1.ftl] main.ftl"); + } + + @Test + public void testSelfCaller() throws Exception { + addTemplate("main.ftl", "" + + "<#macro m>${.callerTemplateName}</#macro>" + + "<@m />"); + assertOutputForNamed("main.ftl", "main.ftl"); + } + + @Test + public void testImportedTemplateCaller() throws Exception { + addTemplate("main.ftl", "" + + "<#import 'lib/foo.ftl' as foo>" + + "<@foo.m />, <@foo.m2 />"); + addTemplate("lib/foo.ftl", "" + + "<#macro m>${.callerTemplateName}</#macro>" + + "<#macro m2><@m3/></#macro>" + + "<#macro m3>${.callerTemplateName}</#macro>"); + assertOutputForNamed("main.ftl", + "main.ftl, lib/foo.ftl"); + } + + @Test + public void testNestedIntoNonUserDirectives() throws Exception { + addTemplate("main.ftl", "" + + "<#macro m><#list 1..2 as _><#if true>${.callerTemplateName}</#if>;</#list></#macro>" + + "<@m/>"); + assertOutputForNamed("main.ftl", "main.ftl;main.ftl;"); + } + + @Test + public void testUsedInArgument() throws Exception { + addTemplate("main.ftl", "" + + "<#include 'inc.ftl'>" + + "<#macro start>" + + "<@m .callerTemplateName />" + + "<@m2 />" + + "</#macro>" + + "<@start />"); + addTemplate("inc.ftl", "" + + "<#macro m x y=.callerTemplateName>" + + "x: ${x}; y: ${y}; caller: ${.callerTemplateName};" + + "</#macro>" + + "<#macro m2><@m .callerTemplateName /></#macro>"); + + 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 + public void testReturnsLookupName() throws Exception { + addTemplate("main_en.ftl", "" + + "<#macro m>${.callerTemplateName}</#macro>" + + "<@m />"); + assertOutputForNamed("main.ftl", "main.ftl"); // Not main_en.ftl + } + + @Test + public void testLegacyCall() throws Exception { + addTemplate("main_en.ftl", "" + + "<#macro m>${.callerTemplateName}</#macro>" + + "<#call m>"); + assertOutputForNamed("main.ftl", "main.ftl"); // Not main_en.ftl + } + +} http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/5f821f17/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 deleted file mode 100644 index 5a26b8f..0000000 --- a/src/test/java/freemarker/core/MacroCallerTemplateNameTest.java +++ /dev/null @@ -1,157 +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 freemarker.core; - -import org.junit.Test; - -import freemarker.template.Configuration; -import freemarker.test.TemplateTest; - -public class MacroCallerTemplateNameTest extends TemplateTest { - - @Override - protected Configuration createConfiguration() throws Exception { - Configuration cfg = super.createConfiguration(); - cfg.setIncompatibleImprovements(Configuration.VERSION_2_3_28); - return cfg; - } - - @Test - public void testNoCaller() throws Exception { - 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}</@>", - "no macro call", ".macroCallerTemplateName"); - - 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/>]", - "[]"); - assertOutput("" - + "<#macro m2>${.macroCallerTemplateName}</#macro>" - + "[<@m2/>]", - "[]"); - } - - @Test - public void testSameTemplateCaller() throws Exception { - addTemplate("main.ftl", "" - + "<#macro m>${.macroCallerTemplateName}</#macro>" - + "<@m />, <#attempt>${.macroCallerTemplateName}<#recover>-</#attempt>"); - assertOutputForNamed("main.ftl", "main.ftl, -"); - } - - @Test - public void testIncludedTemplateCaller() throws Exception { - addTemplate("main.ftl", "" - + "<#include 'lib/foo.ftl'>" - + "<@m />, <@m2 />"); - addTemplate("lib/foo.ftl", "" - + "<#macro m>${.macroCallerTemplateName}</#macro>" - + "<#macro m2><@m3/></#macro>" - + "<#macro m3>${.macroCallerTemplateName}</#macro>"); - assertOutputForNamed("main.ftl", - "main.ftl, lib/foo.ftl"); - } - - @Test - public void testImportedTemplateCaller() throws Exception { - addTemplate("main.ftl", "" - + "<#import 'lib/foo.ftl' as foo>" - + "<@foo.m />, <@foo.m2 />"); - addTemplate("lib/foo.ftl", "" - + "<#macro m>${.macroCallerTemplateName}</#macro>" - + "<#macro m2><@m3/></#macro>" - + "<#macro m3>${.macroCallerTemplateName}</#macro>"); - assertOutputForNamed("main.ftl", - "main.ftl, lib/foo.ftl"); - } - - @Test - public void testNestedIntoNonUserDirectives() throws Exception { - addTemplate("main.ftl", "" - + "<#macro m><#list 1..2 as _><#if true>${.macroCallerTemplateName}</#if>;</#list></#macro>" - + "<@m/>"); - assertOutputForNamed("main.ftl", "main.ftl;main.ftl;"); - } - - @Test - public void testMulitpleLevels() throws Exception { - addTemplate("main.ftl", "" - + "<#include 'inc1.ftl'>" - + "<@m1 />"); - addTemplate("inc1.ftl", "" - + "<#include 'inc2.ftl'>" - + "<#macro m1>m1: ${.macroCallerTemplateName}; <@m2 /></#macro>"); - addTemplate("inc2.ftl", "" - + "<#macro m2>m2: ${.macroCallerTemplateName};</#macro>"); - assertOutputForNamed("main.ftl", "m1: main.ftl; m2: inc1.ftl;"); - } - - @Test - public void testUsedInArgument() throws Exception { - addTemplate("main.ftl", "" - + "<#include 'inc.ftl'>" - + "<#macro start>" - + "<@m .macroCallerTemplateName />" - + "<@m2 />" - + "</#macro>" - + "<@start />"); - addTemplate("inc.ftl", "" - + "<#macro m x y=.macroCallerTemplateName>" - + "x: ${x}; y: ${y}; caller: ${.macroCallerTemplateName};" - + "</#macro>" - + "<#macro m2><@m .macroCallerTemplateName /></#macro>"); - - 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 - public void testReturnsLookupName() throws Exception { - addTemplate("main_en.ftl", "" - + "<#macro m>${.macroCallerTemplateName}</#macro>" - + "<@m />"); - assertOutputForNamed("main.ftl", "main.ftl"); // Not main_en.ftl - } - - @Test - public void testLegacyCall() throws Exception { - addTemplate("main_en.ftl", "" - + "<#macro m>${.macroCallerTemplateName}</#macro>" - + "<#call m>"); - assertOutputForNamed("main.ftl", "main.ftl"); // Not main_en.ftl - } - -}