You are viewing a plain text version of this content. The canonical link for it is here.
Posted to notifications@freemarker.apache.org by dd...@apache.org on 2017/08/07 22:32:21 UTC
[19/21] incubator-freemarker git commit: FREEMARKER-63: Lot of
refinement in the API-s and implementation. #macro now creates a
`TemplateDirectiveModel`,
and #function now creates `TemplateFunctionModel` (though the function/method
call syntax doesn't ye
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/3cacd9ed/freemarker-core-test/src/test/resources/org/apache/freemarker/core/templatesuite/templates/comparisons.ftl
----------------------------------------------------------------------
diff --git a/freemarker-core-test/src/test/resources/org/apache/freemarker/core/templatesuite/templates/comparisons.ftl b/freemarker-core-test/src/test/resources/org/apache/freemarker/core/templatesuite/templates/comparisons.ftl
index a711d4d..3314765 100644
--- a/freemarker-core-test/src/test/resources/org/apache/freemarker/core/templatesuite/templates/comparisons.ftl
+++ b/freemarker-core-test/src/test/resources/org/apache/freemarker/core/templatesuite/templates/comparisons.ftl
@@ -85,134 +85,134 @@
<#-- Signum-based optimization test, all 9 permutations: -->
<#-- 1 -->
-<@assert test= !(0 != 0) />
-<@assert test= (0 == 0) />
-<@assert test= !(0 > 0) />
-<@assert test= (0 >= 0) />
-<@assert test= !(0 < 0) />
-<@assert test= (0 <= 0) />
+<@assert !(0 != 0) />
+<@assert (0 == 0) />
+<@assert !(0 > 0) />
+<@assert (0 >= 0) />
+<@assert !(0 < 0) />
+<@assert (0 <= 0) />
<#-- 2 -->
-<@assert test= !(3 != 3) />
-<@assert test= (3 == 3) />
-<@assert test= !(3 > 3) />
-<@assert test= (3 >= 3) />
-<@assert test= !(3 < 3) />
-<@assert test= (3 <= 3) />
+<@assert !(3 != 3) />
+<@assert (3 == 3) />
+<@assert !(3 > 3) />
+<@assert (3 >= 3) />
+<@assert !(3 < 3) />
+<@assert (3 <= 3) />
<#-- 3 -->
-<@assert test= !(-3 != -3) />
-<@assert test= (-3 == -3) />
-<@assert test= !(-3 > -3) />
-<@assert test= (-3 >= -3) />
-<@assert test= !(-3 < -3) />
-<@assert test= (-3 <= -3) />
+<@assert !(-3 != -3) />
+<@assert (-3 == -3) />
+<@assert !(-3 > -3) />
+<@assert (-3 >= -3) />
+<@assert !(-3 < -3) />
+<@assert (-3 <= -3) />
<#-- 4 -->
-<@assert test= (3 != 0) />
-<@assert test= !(3 == 0) />
-<@assert test= (3 > 0) />
-<@assert test= (3 >= 0) />
-<@assert test= !(3 < 0) />
-<@assert test= !(3 <= 0) />
+<@assert (3 != 0) />
+<@assert !(3 == 0) />
+<@assert (3 > 0) />
+<@assert (3 >= 0) />
+<@assert !(3 < 0) />
+<@assert !(3 <= 0) />
<#-- 5 -->
-<@assert test= (0 != 3) />
-<@assert test= !(0 == 3) />
-<@assert test= !(0 > 3) />
-<@assert test= !(0 >= 3) />
-<@assert test= (0 < 3) />
-<@assert test= (0 <= 3) />
+<@assert (0 != 3) />
+<@assert !(0 == 3) />
+<@assert !(0 > 3) />
+<@assert !(0 >= 3) />
+<@assert (0 < 3) />
+<@assert (0 <= 3) />
<#-- 6 -->
-<@assert test= (-3 != 0) />
-<@assert test= !(-3 == 0) />
-<@assert test= !(-3 > 0) />
-<@assert test= !(-3 >= 0) />
-<@assert test= (-3 < 0) />
-<@assert test= (-3 <= 0) />
+<@assert (-3 != 0) />
+<@assert !(-3 == 0) />
+<@assert !(-3 > 0) />
+<@assert !(-3 >= 0) />
+<@assert (-3 < 0) />
+<@assert (-3 <= 0) />
<#-- 7 -->
-<@assert test= (0 != -3) />
-<@assert test= !(0 == -3) />
-<@assert test= (0 > -3) />
-<@assert test= (0 >= -3) />
-<@assert test= !(0 < -3) />
-<@assert test= !(0 <= -3) />
+<@assert (0 != -3) />
+<@assert !(0 == -3) />
+<@assert (0 > -3) />
+<@assert (0 >= -3) />
+<@assert !(0 < -3) />
+<@assert !(0 <= -3) />
<#-- 8 -->
-<@assert test= (-3 != 3) />
-<@assert test= !(-3 == 3) />
-<@assert test= !(-3 > 3) />
-<@assert test= !(-3 >= 3) />
-<@assert test= (-3 < 3) />
-<@assert test= (-3 <= 3) />
+<@assert (-3 != 3) />
+<@assert !(-3 == 3) />
+<@assert !(-3 > 3) />
+<@assert !(-3 >= 3) />
+<@assert (-3 < 3) />
+<@assert (-3 <= 3) />
<#-- 9 -->
-<@assert test= (3 != -3) />
-<@assert test= !(3 == -3) />
-<@assert test= (3 > -3) />
-<@assert test= (3 >= -3) />
-<@assert test= !(3 < -3) />
-<@assert test= !(3 <= -3) />
+<@assert (3 != -3) />
+<@assert !(3 == -3) />
+<@assert (3 > -3) />
+<@assert (3 >= -3) />
+<@assert !(3 < -3) />
+<@assert !(3 <= -3) />
<#-- Again, now on runtime: -->
<#assign m3 = -3>
<#assign p3 = 3>
<#assign z = 0>
<#-- 1 -->
-<@assert test= !(z != z) />
-<@assert test= (z == z) />
-<@assert test= !(z > z) />
-<@assert test= (z >= z) />
-<@assert test= !(z < z) />
-<@assert test= (z <= z) />
+<@assert !(z != z) />
+<@assert (z == z) />
+<@assert !(z > z) />
+<@assert (z >= z) />
+<@assert !(z < z) />
+<@assert (z <= z) />
<#-- 2 -->
-<@assert test= !(p3 != p3) />
-<@assert test= (p3 == p3) />
-<@assert test= !(p3 > p3) />
-<@assert test= (p3 >= p3) />
-<@assert test= !(p3 < p3) />
-<@assert test= (p3 <= p3) />
+<@assert !(p3 != p3) />
+<@assert (p3 == p3) />
+<@assert !(p3 > p3) />
+<@assert (p3 >= p3) />
+<@assert !(p3 < p3) />
+<@assert (p3 <= p3) />
<#-- 3 -->
-<@assert test= !(m3 != m3) />
-<@assert test= (m3 == m3) />
-<@assert test= !(m3 > m3) />
-<@assert test= (m3 >= m3) />
-<@assert test= !(m3 < m3) />
-<@assert test= (m3 <= m3) />
+<@assert !(m3 != m3) />
+<@assert (m3 == m3) />
+<@assert !(m3 > m3) />
+<@assert (m3 >= m3) />
+<@assert !(m3 < m3) />
+<@assert (m3 <= m3) />
<#-- 4 -->
-<@assert test= (p3 != z) />
-<@assert test= !(p3 == z) />
-<@assert test= (p3 > z) />
-<@assert test= (p3 >= z) />
-<@assert test= !(p3 < z) />
-<@assert test= !(p3 <= z) />
+<@assert (p3 != z) />
+<@assert !(p3 == z) />
+<@assert (p3 > z) />
+<@assert (p3 >= z) />
+<@assert !(p3 < z) />
+<@assert !(p3 <= z) />
<#-- 5 -->
-<@assert test= (z != p3) />
-<@assert test= !(z == p3) />
-<@assert test= !(z > p3) />
-<@assert test= !(z >= p3) />
-<@assert test= (z < p3) />
-<@assert test= (z <= p3) />
+<@assert (z != p3) />
+<@assert !(z == p3) />
+<@assert !(z > p3) />
+<@assert !(z >= p3) />
+<@assert (z < p3) />
+<@assert (z <= p3) />
<#-- 6 -->
-<@assert test= (m3 != z) />
-<@assert test= !(m3 == z) />
-<@assert test= !(m3 > z) />
-<@assert test= !(m3 >= z) />
-<@assert test= (m3 < z) />
-<@assert test= (m3 <= z) />
+<@assert (m3 != z) />
+<@assert !(m3 == z) />
+<@assert !(m3 > z) />
+<@assert !(m3 >= z) />
+<@assert (m3 < z) />
+<@assert (m3 <= z) />
<#-- 7 -->
-<@assert test= (z != m3) />
-<@assert test= !(z == m3) />
-<@assert test= (z > m3) />
-<@assert test= (z >= m3) />
-<@assert test= !(z < m3) />
-<@assert test= !(z <= m3) />
+<@assert (z != m3) />
+<@assert !(z == m3) />
+<@assert (z > m3) />
+<@assert (z >= m3) />
+<@assert !(z < m3) />
+<@assert !(z <= m3) />
<#-- 8 -->
-<@assert test= (m3 != p3) />
-<@assert test= !(m3 == p3) />
-<@assert test= !(m3 > p3) />
-<@assert test= !(m3 >= p3) />
-<@assert test= (m3 < p3) />
-<@assert test= (m3 <= p3) />
+<@assert (m3 != p3) />
+<@assert !(m3 == p3) />
+<@assert !(m3 > p3) />
+<@assert !(m3 >= p3) />
+<@assert (m3 < p3) />
+<@assert (m3 <= p3) />
<#-- 9 -->
-<@assert test= (p3 != m3) />
-<@assert test= !(p3 == m3) />
-<@assert test= (p3 > m3) />
-<@assert test= (p3 >= m3) />
-<@assert test= !(p3 < m3) />
-<@assert test= !(p3 <= m3) />
+<@assert (p3 != m3) />
+<@assert !(p3 == m3) />
+<@assert (p3 > m3) />
+<@assert (p3 >= m3) />
+<@assert !(p3 < m3) />
+<@assert !(p3 <= m3) />
</body>
</html>
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/3cacd9ed/freemarker-core-test/src/test/resources/org/apache/freemarker/core/templatesuite/templates/date-type-builtins.ftl
----------------------------------------------------------------------
diff --git a/freemarker-core-test/src/test/resources/org/apache/freemarker/core/templatesuite/templates/date-type-builtins.ftl b/freemarker-core-test/src/test/resources/org/apache/freemarker/core/templatesuite/templates/date-type-builtins.ftl
index 2cadc42..9e3025d 100644
--- a/freemarker-core-test/src/test/resources/org/apache/freemarker/core/templatesuite/templates/date-type-builtins.ftl
+++ b/freemarker-core-test/src/test/resources/org/apache/freemarker/core/templatesuite/templates/date-type-builtins.ftl
@@ -18,25 +18,25 @@
-->
<#setting timeZone = "UTC">
-<@assert test=unknown?isUnknownDateLike />
-<@assert test=!timeOnly?isUnknownDateLike />
-<@assert test=!dateOnly?isUnknownDateLike />
-<@assert test=!dateTime?isUnknownDateLike />
+<@assert unknown?isUnknownDateLike />
+<@assert !timeOnly?isUnknownDateLike />
+<@assert !dateOnly?isUnknownDateLike />
+<@assert !dateTime?isUnknownDateLike />
-<@assert test=!unknown?isDateOnly />
-<@assert test=!timeOnly?isDateOnly />
-<@assert test=dateOnly?isDateOnly />
-<@assert test=!dateTime?isDateOnly />
+<@assert !unknown?isDateOnly />
+<@assert !timeOnly?isDateOnly />
+<@assert dateOnly?isDateOnly />
+<@assert !dateTime?isDateOnly />
-<@assert test=!unknown?isTime />
-<@assert test=timeOnly?isTime />
-<@assert test=!dateOnly?isTime />
-<@assert test=!dateTime?isTime />
+<@assert !unknown?isTime />
+<@assert timeOnly?isTime />
+<@assert !dateOnly?isTime />
+<@assert !dateTime?isTime />
-<@assert test=!unknown?isDatetime />
-<@assert test=!timeOnly?isDatetime />
-<@assert test=!dateOnly?isDatetime />
-<@assert test=dateTime?isDatetime />
+<@assert !unknown?isDatetime />
+<@assert !timeOnly?isDatetime />
+<@assert !dateOnly?isDatetime />
+<@assert dateTime?isDatetime />
<@assertFails message="isn't known if">${unknown?string.xs}</@>
<@assertEquals expected="2003-04-05T06:07:08Z" actual=unknown?dateTimeIfUnknown?string.xs />
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/3cacd9ed/freemarker-core-test/src/test/resources/org/apache/freemarker/core/templatesuite/templates/dateparsing.ftl
----------------------------------------------------------------------
diff --git a/freemarker-core-test/src/test/resources/org/apache/freemarker/core/templatesuite/templates/dateparsing.ftl b/freemarker-core-test/src/test/resources/org/apache/freemarker/core/templatesuite/templates/dateparsing.ftl
index 401371e..753dd4b 100644
--- a/freemarker-core-test/src/test/resources/org/apache/freemarker/core/templatesuite/templates/dateparsing.ftl
+++ b/freemarker-core-test/src/test/resources/org/apache/freemarker/core/templatesuite/templates/dateparsing.ftl
@@ -42,8 +42,8 @@
<#assign gmt01Str='1998-10-30T19:30:44.512'?dateTime.xs?string />
<#setting timeZone="default">
<#assign defStr='1998-10-30T19:30:44.512'?dateTime.xs?string />
-<@assert test = gmtStr != gmt01Str />
-<@assert test = defStr != gmtStr || defStr != gmt01Str />
+<@assert gmtStr != gmt01Str />
+<@assert defStr != gmtStr || defStr != gmt01Str />
<#assign refDate = "AD 1998-10-30 +0000"?date>
<#assign refTime = "15:30:44.512 +0000"?time>
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/3cacd9ed/freemarker-core-test/src/test/resources/org/apache/freemarker/core/templatesuite/templates/existence-operators.ftl
----------------------------------------------------------------------
diff --git a/freemarker-core-test/src/test/resources/org/apache/freemarker/core/templatesuite/templates/existence-operators.ftl b/freemarker-core-test/src/test/resources/org/apache/freemarker/core/templatesuite/templates/existence-operators.ftl
index d8122bf..916d91e 100644
--- a/freemarker-core-test/src/test/resources/org/apache/freemarker/core/templatesuite/templates/existence-operators.ftl
+++ b/freemarker-core-test/src/test/resources/org/apache/freemarker/core/templatesuite/templates/existence-operators.ftl
@@ -46,19 +46,19 @@
<#list ['V', 1.5] as v>
<@assertEquals actual=v!'-' expected=v />
<@assertEquals actual=(v)!'-' expected=v />
- <@assert test=v?? />
- <@assert test=(v)?? />
+ <@assert v?? />
+ <@assert (v)?? />
<@assertEquals actual=v?default('-') expected=v />
<@assertEquals actual=(v)?default('-') expected=v />
- <@assert test=v?exists />
- <@assert test=(v)?exists />
+ <@assert v?exists />
+ <@assert (v)?exists />
<@assertEquals actual=v?ifExists expected=v />
<@assertEquals actual=(v)?ifExists expected=v />
- <@assert test=v?hasContent />
- <@assert test=(v)?hasContent />
+ <@assert v?hasContent />
+ <@assert (v)?hasContent />
</#list>
-<@assert test=!v?? />
-<@assert test=!v?exists />
+<@assert !v?? />
+<@assert !v?exists />
<@isNonFastIRE>${v}</@> <#-- To check that it isn't an IRE.FAST_INSTANCE -->
<@isIRE>${u.v!'-'}</@>
@@ -91,16 +91,16 @@
<#assign u = { 'v': 'V' } >
<@assertEquals actual=u.v!'-' expected='V' />
<@assertEquals actual=(u.v)!'-' expected='V' />
-<@assert test=u.v?? />
-<@assert test=(u.v)?? />
+<@assert u.v?? />
+<@assert (u.v)?? />
<@assertEquals actual=u.v?default('-') expected='V' />
<@assertEquals actual=(u.v)?default('-') expected='V' />
-<@assert test=u.v?exists />
-<@assert test=(u.v)?exists />
+<@assert u.v?exists />
+<@assert (u.v)?exists />
<@assertEquals actual=u.v?ifExists expected='V' />
<@assertEquals actual=(u.v)?ifExists expected='V' />
-<@assert test=u.v?hasContent />
-<@assert test=(u.v)?hasContent />
+<@assert u.v?hasContent />
+<@assert (u.v)?hasContent />
<#list 1..4 as i>
<#if i == 3><#assign x = 'X'></#if>
@@ -111,7 +111,7 @@
<#attempt>
${fails}
<#recover>
- <@assert test=isNonFastIREMessage(.error) />
+ <@assert isNonFastIREMessage(.error) />
</#attempt>
</#macro>
<@attemptTest />
@@ -121,12 +121,12 @@ ${(callMacroFromExpression(attemptTest))!}
<#attempt>
<@interpretTest />
<#recover>
- <@assert test=isNonFastIREMessage(.error) />
+ <@assert isNonFastIREMessage(.error) />
</#attempt>
<#attempt>
${(callMacroFromExpression(interpretTest))!}
<#recover>
- <@assert test=isNonFastIREMessage(.error) />
+ <@assert isNonFastIREMessage(.error) />
</#attempt>
<@assertEquals actual='fails'?eval!'-' expected='-' />
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/3cacd9ed/freemarker-core-test/src/test/resources/org/apache/freemarker/core/templatesuite/templates/hashconcat.ftl
----------------------------------------------------------------------
diff --git a/freemarker-core-test/src/test/resources/org/apache/freemarker/core/templatesuite/templates/hashconcat.ftl b/freemarker-core-test/src/test/resources/org/apache/freemarker/core/templatesuite/templates/hashconcat.ftl
index 127d828..362f2fa 100644
--- a/freemarker-core-test/src/test/resources/org/apache/freemarker/core/templatesuite/templates/hashconcat.ftl
+++ b/freemarker-core-test/src/test/resources/org/apache/freemarker/core/templatesuite/templates/hashconcat.ftl
@@ -48,7 +48,7 @@ a + b + {} + b + {} + a:
[@dump a + b + {} + b + {} + a /]
-[#macro dump s]
+[#macro dump s{positional}]
[#list s?keys as k]
${k} = ${s[k]}
[/#list]
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/3cacd9ed/freemarker-core-test/src/test/resources/org/apache/freemarker/core/templatesuite/templates/identifier-non-ascii.ftl
----------------------------------------------------------------------
diff --git a/freemarker-core-test/src/test/resources/org/apache/freemarker/core/templatesuite/templates/identifier-non-ascii.ftl b/freemarker-core-test/src/test/resources/org/apache/freemarker/core/templatesuite/templates/identifier-non-ascii.ftl
index 59a4b0e..3050064 100644
--- a/freemarker-core-test/src/test/resources/org/apache/freemarker/core/templatesuite/templates/identifier-non-ascii.ftl
+++ b/freemarker-core-test/src/test/resources/org/apache/freemarker/core/templatesuite/templates/identifier-non-ascii.ftl
@@ -18,4 +18,4 @@
-->
<#macro árvíztűrőTükörfúrógép טֶקסט>${טֶקסט}</#macro>
<#assign 한국어_키보드="Korean Keyboard">
-<@árvíztűrőTükörfúrógép 한국어_키보드 />
\ No newline at end of file
+<@árvíztűrőTükörfúrógép טֶקסט=한국어_키보드 />
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/3cacd9ed/freemarker-core-test/src/test/resources/org/apache/freemarker/core/templatesuite/templates/list.ftl
----------------------------------------------------------------------
diff --git a/freemarker-core-test/src/test/resources/org/apache/freemarker/core/templatesuite/templates/list.ftl b/freemarker-core-test/src/test/resources/org/apache/freemarker/core/templatesuite/templates/list.ftl
index 280491c..8ce79d4 100644
--- a/freemarker-core-test/src/test/resources/org/apache/freemarker/core/templatesuite/templates/list.ftl
+++ b/freemarker-core-test/src/test/resources/org/apache/freemarker/core/templatesuite/templates/list.ftl
@@ -38,7 +38,7 @@
<@testList listables.emptyIterator />
-<#macro testList seq>
+<#macro testList seq{positional}>
Size: <#attempt>${seq?size}<#recover>failed</#attempt>
Items: <#list seq as i>@${i_index} ${i}<#if i_has_next>, <#else>.</#if></#list>
</#macro>
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/3cacd9ed/freemarker-core-test/src/test/resources/org/apache/freemarker/core/templatesuite/templates/list2.ftl
----------------------------------------------------------------------
diff --git a/freemarker-core-test/src/test/resources/org/apache/freemarker/core/templatesuite/templates/list2.ftl b/freemarker-core-test/src/test/resources/org/apache/freemarker/core/templatesuite/templates/list2.ftl
index 6ac21d5..9893c1b 100644
--- a/freemarker-core-test/src/test/resources/org/apache/freemarker/core/templatesuite/templates/list2.ftl
+++ b/freemarker-core-test/src/test/resources/org/apache/freemarker/core/templatesuite/templates/list2.ftl
@@ -34,7 +34,7 @@
<@testList listables.getEmptyIterator />
-<#macro testList xs>
+<#macro testList xs{positional}>
=== [${resolve(xs)?join(", ")}] ===
<#assign resolveCallCnt = 0>
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/3cacd9ed/freemarker-core-test/src/test/resources/org/apache/freemarker/core/templatesuite/templates/list3.ftl
----------------------------------------------------------------------
diff --git a/freemarker-core-test/src/test/resources/org/apache/freemarker/core/templatesuite/templates/list3.ftl b/freemarker-core-test/src/test/resources/org/apache/freemarker/core/templatesuite/templates/list3.ftl
index d2fcf71..293a643 100644
--- a/freemarker-core-test/src/test/resources/org/apache/freemarker/core/templatesuite/templates/list3.ftl
+++ b/freemarker-core-test/src/test/resources/org/apache/freemarker/core/templatesuite/templates/list3.ftl
@@ -22,7 +22,7 @@
</#list>
]
-<#macro hits xs style="">
+<#macro hits xs{positional} style="">
<#list xs>
<p>${xs?size} hits:
<div class="hits">
@@ -50,9 +50,9 @@
<@hits ['a', 'b'] />
-<@hits ['a', 'b'], "other" />
+<@hits ['a', 'b'] style="other" />
-<@hits ['a', 'b'], "hidden" />
+<@hits ['a', 'b'] style="hidden" />
<@hits [] />
@@ -63,7 +63,7 @@
<@testAutoClosedSep [1] />
<@testAutoClosedSep [] />
-<#macro testAutoClosedSep xs>
+<#macro testAutoClosedSep xs{positional}>
<#list xs as x>${x}<#sep>, <#else>Empty</#list>
<#list xs as x>${x}<#sep><#if x_index == 0> /*first*/, <#else>, </#if><#else>Empty</#list>
<#list xs>[<#items as x>${x}<#sep>, </#items>]<#else>Empty</#list>
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/3cacd9ed/freemarker-core-test/src/test/resources/org/apache/freemarker/core/templatesuite/templates/listhash.ftl
----------------------------------------------------------------------
diff --git a/freemarker-core-test/src/test/resources/org/apache/freemarker/core/templatesuite/templates/listhash.ftl b/freemarker-core-test/src/test/resources/org/apache/freemarker/core/templatesuite/templates/listhash.ftl
index 6f22b43..f74ce65 100644
--- a/freemarker-core-test/src/test/resources/org/apache/freemarker/core/templatesuite/templates/listhash.ftl
+++ b/freemarker-core-test/src/test/resources/org/apache/freemarker/core/templatesuite/templates/listhash.ftl
@@ -18,7 +18,7 @@
-->
<#setting booleanFormat='Y,N'>
-<#macro listings maps>
+<#macro listings maps{positional}>
<#list maps as m>
Map:
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/3cacd9ed/freemarker-core-test/src/test/resources/org/apache/freemarker/core/templatesuite/templates/macros.ftl
----------------------------------------------------------------------
diff --git a/freemarker-core-test/src/test/resources/org/apache/freemarker/core/templatesuite/templates/macros.ftl b/freemarker-core-test/src/test/resources/org/apache/freemarker/core/templatesuite/templates/macros.ftl
index b746321..181326c 100644
--- a/freemarker-core-test/src/test/resources/org/apache/freemarker/core/templatesuite/templates/macros.ftl
+++ b/freemarker-core-test/src/test/resources/org/apache/freemarker/core/templatesuite/templates/macros.ftl
@@ -45,7 +45,7 @@
<p>Function is defined, now let's call it:</p>
- <@español urls.home, images.home, "Home" /><#t>
+ <@español url=urls.home image=images.home alt="Home" /><#t>
<p>Again, but with different parameters:</p>
@@ -63,7 +63,7 @@
<p>A recursive function call:</p>
-<#macro recurse dummy a=3>
+<#macro recurse dummy{positional}, a{positional}=3>
<#if (a > 0)>
<@recurse dummy, a - 1 />
</#if>
@@ -83,19 +83,18 @@ foo=${foo} baz=[<#list bar?keys?sort as key>${key}=${bar[key]}<#if key_has_next>
<@catchall foo="a" bar="b"/>
<@catchall foo="a" bar="b" baz="c"/>
-<#macro fmt pattern args...>
- <#list args as arg>
- <#local pattern = pattern?replace("{" + arg_index + "}", arg)>
+<#macro fmt pattern{positional} args...>
+ <#list args as k, v>
+ <#local pattern = pattern?replace("{" + k + "}", v)>
</#list>
${pattern}<#lt>
</#macro>
-<#macro m a=1 b=2>
-</#macro>
+<#macro m a=1 b=2></#macro>
<@assertFails message='"c"'><@m c=3 /></@>
-<@assertFails message='3'><@m 9, 8, 7 /></@>
+<@assertFails message='position'><@m 1, 2 /></@>
-<@fmt "Hello {0}! Today is {1}.", "World", "Monday" />
+<@fmt "Hello {name}! Today is {today}." name="World" today="Monday" />
</body>
</html>
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/3cacd9ed/freemarker-core-test/src/test/resources/org/apache/freemarker/core/templatesuite/templates/macros2.ftl
----------------------------------------------------------------------
diff --git a/freemarker-core-test/src/test/resources/org/apache/freemarker/core/templatesuite/templates/macros2.ftl b/freemarker-core-test/src/test/resources/org/apache/freemarker/core/templatesuite/templates/macros2.ftl
deleted file mode 100644
index 55ceefd..0000000
--- a/freemarker-core-test/src/test/resources/org/apache/freemarker/core/templatesuite/templates/macros2.ftl
+++ /dev/null
@@ -1,35 +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.
--->
-<#macro m1 a b=a>
-${a} ${b}
-</#macro>
-<@m1 a="1"/>
-<#macro m2 a=b b="">
-${a} ${b}
-</#macro>
-<@m2 b="2"/>
-<#macro m3 d b=c[a] a=d c={"3":"4"}>
-${b}
-</#macro>
-<@m3 d="3"/>
-<#attempt>
-<@m3 d="4"/>
-<#recover>
-m3 with d="4" Failed!
-</#attempt>
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/3cacd9ed/freemarker-core-test/src/test/resources/org/apache/freemarker/core/templatesuite/templates/number-math-builtins.ftl
----------------------------------------------------------------------
diff --git a/freemarker-core-test/src/test/resources/org/apache/freemarker/core/templatesuite/templates/number-math-builtins.ftl b/freemarker-core-test/src/test/resources/org/apache/freemarker/core/templatesuite/templates/number-math-builtins.ftl
index 19e37d5..2951f6e 100644
--- a/freemarker-core-test/src/test/resources/org/apache/freemarker/core/templatesuite/templates/number-math-builtins.ftl
+++ b/freemarker-core-test/src/test/resources/org/apache/freemarker/core/templatesuite/templates/number-math-builtins.ftl
@@ -22,14 +22,14 @@
<@assertEquals actual=3.5?abs expected=3.5 />
<@assertEquals actual=(-3.5)?abs expected=3.5 />
-<@assert test=fNan?abs?isNan />
-<@assert test=dNan?abs?isNan />
-<@assert test=fNinf?abs?isInfinite />
-<@assert test=dPinf?abs?isInfinite />
-<@assert test=fNinf lt 0 />
-<@assert test=dPinf gt 0 />
-<@assert test=fNinf?abs gt 0 />
-<@assert test=dPinf?abs gt 0 />
+<@assert fNan?abs?isNan />
+<@assert dNan?abs?isNan />
+<@assert fNinf?abs?isInfinite />
+<@assert dPinf?abs?isInfinite />
+<@assert fNinf lt 0 />
+<@assert dPinf gt 0 />
+<@assert fNinf?abs gt 0 />
+<@assert dPinf?abs gt 0 />
<@assertEquals actual=fn?abs expected=0.05 />
<@assertEquals actual=dn?abs expected=0.05 />
@@ -49,30 +49,30 @@
<@assertEquals actual=bip?abs expected=5 />
<@assertEquals actual=bdp?abs expected=0.05 />
-<@assert test=!0?isInfinite />
-<@assert test=!fn?isInfinite />
-<@assert test=!dn?isInfinite />
-<@assert test=!ineg?isInfinite />
-<@assert test=!ln?isInfinite />
-<@assert test=!sn?isInfinite />
-<@assert test=!bn?isInfinite />
-<@assert test=!bin?isInfinite />
-<@assert test=!bdn?isInfinite />
-<@assert test=!fNan?isInfinite />
-<@assert test=!dNan?isInfinite />
-<@assert test=fNinf?isInfinite />
-<@assert test=dPinf?isInfinite />
+<@assert !0?isInfinite />
+<@assert !fn?isInfinite />
+<@assert !dn?isInfinite />
+<@assert !ineg?isInfinite />
+<@assert !ln?isInfinite />
+<@assert !sn?isInfinite />
+<@assert !bn?isInfinite />
+<@assert !bin?isInfinite />
+<@assert !bdn?isInfinite />
+<@assert !fNan?isInfinite />
+<@assert !dNan?isInfinite />
+<@assert fNinf?isInfinite />
+<@assert dPinf?isInfinite />
-<@assert test=!0?isNan />
-<@assert test=!fn?isNan />
-<@assert test=!dn?isNan />
-<@assert test=!ineg?isNan />
-<@assert test=!ln?isNan />
-<@assert test=!sn?isNan />
-<@assert test=!bn?isNan />
-<@assert test=!bin?isNan />
-<@assert test=!bdn?isNan />
-<@assert test=fNan?isNan />
-<@assert test=dNan?isNan />
-<@assert test=!fNinf?isNan />
-<@assert test=!dPinf?isNan />
\ No newline at end of file
+<@assert !0?isNan />
+<@assert !fn?isNan />
+<@assert !dn?isNan />
+<@assert !ineg?isNan />
+<@assert !ln?isNan />
+<@assert !sn?isNan />
+<@assert !bn?isNan />
+<@assert !bin?isNan />
+<@assert !bdn?isNan />
+<@assert fNan?isNan />
+<@assert dNan?isNan />
+<@assert !fNinf?isNan />
+<@assert !dPinf?isNan />
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/3cacd9ed/freemarker-core-test/src/test/resources/org/apache/freemarker/core/templatesuite/templates/recover.ftl
----------------------------------------------------------------------
diff --git a/freemarker-core-test/src/test/resources/org/apache/freemarker/core/templatesuite/templates/recover.ftl b/freemarker-core-test/src/test/resources/org/apache/freemarker/core/templatesuite/templates/recover.ftl
index afcc423..3fd916b 100644
--- a/freemarker-core-test/src/test/resources/org/apache/freemarker/core/templatesuite/templates/recover.ftl
+++ b/freemarker-core-test/src/test/resources/org/apache/freemarker/core/templatesuite/templates/recover.ftl
@@ -25,15 +25,15 @@
<#attempt>
Let's try to output an undefined variable: ${undefinedVariable}
<#recover>
- Well, that did not work.<@assert test=.error?contains('undefinedVariable') />
+ Well, that did not work.<@assert .error?contains('undefinedVariable') />
Now we nest another attempt/recover here:
<#attempt>
${sequence[1]}
<#recover>
- Oops...<@assert test=.error?contains('sequence[1]') />
+ Oops...<@assert .error?contains('sequence[1]') />
Remember, freeMarker sequences are zero-based! ${sequence[0]}
</#attempt>
- Now we check the current error message.<@assert test=.error?contains('undefinedVariable') />
+ Now we check the current error message.<@assert .error?contains('undefinedVariable') />
</#attempt>
<#attempt>
<#include "nonexistent_template">
@@ -43,5 +43,5 @@
<#attempt>
<#include "undefined.ftl">
<#recover>
- The included template had a problem.<@assert test=.error?contains('undefined_variable') />
+ The included template had a problem.<@assert .error?contains('undefined_variable') />
</#attempt>
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/3cacd9ed/freemarker-core-test/src/test/resources/org/apache/freemarker/core/templatesuite/templates/root.ftl
----------------------------------------------------------------------
diff --git a/freemarker-core-test/src/test/resources/org/apache/freemarker/core/templatesuite/templates/root.ftl b/freemarker-core-test/src/test/resources/org/apache/freemarker/core/templatesuite/templates/root.ftl
index d4ca2c3..e6b19a0 100644
--- a/freemarker-core-test/src/test/resources/org/apache/freemarker/core/templatesuite/templates/root.ftl
+++ b/freemarker-core-test/src/test/resources/org/apache/freemarker/core/templatesuite/templates/root.ftl
@@ -36,7 +36,7 @@
<p>Ensure that root lookups are unaffected by local variables:</p>
-<#macro test message>
+<#macro test message{positional}>
${.dataModel.message}
${message}
</#macro>
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/3cacd9ed/freemarker-core-test/src/test/resources/org/apache/freemarker/core/templatesuite/templates/sequence-builtins.ftl
----------------------------------------------------------------------
diff --git a/freemarker-core-test/src/test/resources/org/apache/freemarker/core/templatesuite/templates/sequence-builtins.ftl b/freemarker-core-test/src/test/resources/org/apache/freemarker/core/templatesuite/templates/sequence-builtins.ftl
index b86feb9..7eb3453 100644
--- a/freemarker-core-test/src/test/resources/org/apache/freemarker/core/templatesuite/templates/sequence-builtins.ftl
+++ b/freemarker-core-test/src/test/resources/org/apache/freemarker/core/templatesuite/templates/sequence-builtins.ftl
@@ -310,17 +310,17 @@ Chunk
<#assign ls = ['a', 'b', 'c', 'd', 'e', 'f', 'g']>
<#list ['NULL', '-'] as fill>
<#list [1, 2, 3, 4, 5, 10] as columns>
- <@printTable ls, columns, fill />
+ <@printTable ls columns=columns fill=fill />
</#list>
</#list>
-<@printTable [1, 2, 3, 4, 5, 6, 7, 8, 9], 3, 'NULL' />
-<@printTable [1, 2, 3, 4, 5, 6, 7, 8, 9], 3, '-' />
-<@printTable [1], 3, 'NULL' />
-<@printTable [1], 3, '-' />
-<@printTable [], 3, 'NULL' />
-<@printTable [], 3, '-' />
-
-<#macro printTable ls columns fill>
+<@printTable [1, 2, 3, 4, 5, 6, 7, 8, 9] columns=3 fill='NULL' />
+<@printTable [1, 2, 3, 4, 5, 6, 7, 8, 9] columns=3 fill='-' />
+<@printTable [1] columns=3 fill='NULL' />
+<@printTable [1] columns=3 fill='-' />
+<@printTable [] columns=3 fill='NULL' />
+<@printTable [] columns=3 fill='-' />
+
+<#macro printTable ls{positional} columns fill>
columns = ${columns}, fill = ${fill}:<#lt>
<#if fill=='NULL'>
<#local rows = ls?chunk(columns)>
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/3cacd9ed/freemarker-core-test/src/test/resources/org/apache/freemarker/core/templatesuite/templates/string-builtin-coercion.ftl
----------------------------------------------------------------------
diff --git a/freemarker-core-test/src/test/resources/org/apache/freemarker/core/templatesuite/templates/string-builtin-coercion.ftl b/freemarker-core-test/src/test/resources/org/apache/freemarker/core/templatesuite/templates/string-builtin-coercion.ftl
index 475d0cb..40bb4ad 100644
--- a/freemarker-core-test/src/test/resources/org/apache/freemarker/core/templatesuite/templates/string-builtin-coercion.ftl
+++ b/freemarker-core-test/src/test/resources/org/apache/freemarker/core/templatesuite/templates/string-builtin-coercion.ftl
@@ -18,17 +18,17 @@
-->
Was broken 2.3.19:
<#setting numberFormat="0.#">
-<@assert test=1232?contains('2') />
-<@assert test=1232?indexOf('2') == 1 />
-<@assert test=1232?lastIndexOf('2') == 3 />
-<@assert test=1232?leftPad(6) == ' 1232' /><@assert test=1232?leftPad(6, '0') == '001232' />
-<@assert test=1232?rightPad(6) == '1232 ' /><@assert test=1232?rightPad(6, '0') == '123200' />
-<@assert test=1232?matches('[1-3]+') />
-<@assert test=1232?replace('2', 'z') == '1z3z' />
-<@assert test=1232?replace('2', 'z', 'r') == '1z3z' />
-<@assert test=1232?split('2')[1] == '3' /><@assert test=1232?split('2')[2] == '' />
-<@assert test=1232?split('2', 'r')[1] == '3' />
+<@assert 1232?contains('2') />
+<@assert 1232?indexOf('2') == 1 />
+<@assert 1232?lastIndexOf('2') == 3 />
+<@assert 1232?leftPad(6) == ' 1232' /><@assert 1232?leftPad(6, '0') == '001232' />
+<@assert 1232?rightPad(6) == '1232 ' /><@assert 1232?rightPad(6, '0') == '123200' />
+<@assert 1232?matches('[1-3]+') />
+<@assert 1232?replace('2', 'z') == '1z3z' />
+<@assert 1232?replace('2', 'z', 'r') == '1z3z' />
+<@assert 1232?split('2')[1] == '3' /><@assert 1232?split('2')[2] == '' />
+<@assert 1232?split('2', 'r')[1] == '3' />
Was no broken in 2.3.19:
-<@assert test=1232?startsWith('12') />
-<@assert test=1232?endsWith('32') />
+<@assert 1232?startsWith('12') />
+<@assert 1232?endsWith('32') />
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/3cacd9ed/freemarker-core-test/src/test/resources/org/apache/freemarker/core/templatesuite/templates/string-builtins-regexps.ftl
----------------------------------------------------------------------
diff --git a/freemarker-core-test/src/test/resources/org/apache/freemarker/core/templatesuite/templates/string-builtins-regexps.ftl b/freemarker-core-test/src/test/resources/org/apache/freemarker/core/templatesuite/templates/string-builtins-regexps.ftl
index 5b506ed..f3a4a9f 100644
--- a/freemarker-core-test/src/test/resources/org/apache/freemarker/core/templatesuite/templates/string-builtins-regexps.ftl
+++ b/freemarker-core-test/src/test/resources/org/apache/freemarker/core/templatesuite/templates/string-builtins-regexps.ftl
@@ -42,8 +42,8 @@ ${"test\nFoo"?matches('.*\n^foo', 'mi')?string} == true
${"test\nFoo"?matches('.*^foo', 'ism')?string} == true
${"test\nFoo"?matches('.*^foo', 'smi')?string} == true
<#setting booleanFormat="True,False">
-<@assert test=false?matches('[eslaF]+') />
-<@assert test='False'?matches('[eslaF]+') />
+<@assert false?matches('[eslaF]+') />
+<@assert 'False'?matches('[eslaF]+') />
<#assign s = "Code without test coverage\nis considered to be BROKEN">
@@ -124,7 +124,7 @@ ${false?replace('[abc]', 'A', 'r')} == FAlse
Fails in 2.4
</#attempt>
-<#macro dumpList xs>[<#list xs as x>${x}<#if x_has_next>, </#if></#list>]</#macro>
+<#macro dumpList xs{positional}>[<#list xs as x>${x}<#if x_has_next>, </#if></#list>]</#macro>
<@dumpList "fooXbarxbaaz"?split("X") /> == [foo, barxbaaz]
<@dumpList "fooXbarxbaaz"?split("X", "") /> == [foo, barxbaaz]
<@dumpList "fooXbarxbaaz"?split("X", "i") /> == [foo, bar, baaz]
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/3cacd9ed/freemarker-core-test/src/test/resources/org/apache/freemarker/core/templatesuite/templates/var-layers.ftl
----------------------------------------------------------------------
diff --git a/freemarker-core-test/src/test/resources/org/apache/freemarker/core/templatesuite/templates/var-layers.ftl b/freemarker-core-test/src/test/resources/org/apache/freemarker/core/templatesuite/templates/var-layers.ftl
index 32a6119..80e5c2a 100644
--- a/freemarker-core-test/src/test/resources/org/apache/freemarker/core/templatesuite/templates/var-layers.ftl
+++ b/freemarker-core-test/src/test/resources/org/apache/freemarker/core/templatesuite/templates/var-layers.ftl
@@ -30,7 +30,7 @@ Invisiblity test 3.: <#global q = 1><#if .main.q?exists || .namespace.q?exists |
--
<@lib.foo/>
--
-<#macro foo x>
+<#macro foo x{positional}>
${x} = ${.locals.x}
<#local x = 2>
${x} = ${.locals.x}
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/3cacd9ed/freemarker-core-test/src/test/resources/org/apache/freemarker/core/templatesuite/testcases.xml
----------------------------------------------------------------------
diff --git a/freemarker-core-test/src/test/resources/org/apache/freemarker/core/templatesuite/testcases.xml b/freemarker-core-test/src/test/resources/org/apache/freemarker/core/templatesuite/testcases.xml
index d79ee43..7ed2215 100644
--- a/freemarker-core-test/src/test/resources/org/apache/freemarker/core/templatesuite/testcases.xml
+++ b/freemarker-core-test/src/test/resources/org/apache/freemarker/core/templatesuite/testcases.xml
@@ -117,7 +117,6 @@ Note that for the incompatibleImprovements setting you can specify a list of ver
</testCase>
<testCase name="loopvariable" />
<testCase name="macros"/>
- <testCase name="macros2"/>
<testCase name="macros-return"/>
<testCase name="multimodels"/>
<testCase name="nested" />
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/3cacd9ed/freemarker-core/src/main/java/org/apache/freemarker/core/ASTDirMacro.java
----------------------------------------------------------------------
diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/ASTDirMacro.java b/freemarker-core/src/main/java/org/apache/freemarker/core/ASTDirMacro.java
deleted file mode 100644
index 4bed2fd..0000000
--- a/freemarker-core/src/main/java/org/apache/freemarker/core/ASTDirMacro.java
+++ /dev/null
@@ -1,325 +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.util.Collection;
-import java.util.Collections;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Map;
-
-import org.apache.freemarker.core.model.TemplateModel;
-import org.apache.freemarker.core.model.TemplateModelException;
-import org.apache.freemarker.core.model.TemplateModelIterator;
-import org.apache.freemarker.core.util._StringUtil;
-
-/**
- * AST directive node: {@code #macro}
- */
-final class ASTDirMacro extends ASTDirective implements TemplateModel {
-
- static final ASTDirMacro DO_NOTHING_MACRO = new ASTDirMacro(".pass",
- Collections.EMPTY_LIST,
- Collections.EMPTY_MAP,
- null, false,
- TemplateElements.EMPTY);
-
- final static int TYPE_MACRO = 0;
- final static int TYPE_FUNCTION = 1;
-
- private final String name;
- private final String[] paramNames;
- private final Map paramDefaults;
- private final String catchAllParamName;
- private final boolean function;
-
- ASTDirMacro(String name, List argumentNames, Map args,
- String catchAllParamName, boolean function,
- TemplateElements children) {
- this.name = name;
- paramNames = (String[]) argumentNames.toArray(new String[argumentNames.size()]);
- paramDefaults = args;
-
- this.function = function;
- this.catchAllParamName = catchAllParamName;
-
- setChildren(children);
- }
-
- public String getCatchAll() {
- return catchAllParamName;
- }
-
- public String[] getArgumentNames() {
- return paramNames.clone();
- }
-
- String[] getArgumentNamesInternal() {
- return paramNames;
- }
-
- boolean hasArgNamed(String name) {
- return paramDefaults.containsKey(name);
- }
-
- public String getName() {
- return name;
- }
-
- @Override
- ASTElement[] accept(Environment env) {
- env.visitMacroDef(this);
- return null;
- }
-
- @Override
- protected String dump(boolean canonical) {
- StringBuilder sb = new StringBuilder();
- if (canonical) sb.append('<');
- sb.append(getASTNodeDescriptor());
- sb.append(' ');
- sb.append(_StringUtil.toFTLTopLevelTragetIdentifier(name));
- if (function) sb.append('(');
- int argCnt = paramNames.length;
- for (int i = 0; i < argCnt; i++) {
- if (function) {
- if (i != 0) {
- sb.append(", ");
- }
- } else {
- sb.append(' ');
- }
- String argName = paramNames[i];
- sb.append(_StringUtil.toFTLTopLevelIdentifierReference(argName));
- if (paramDefaults != null && paramDefaults.get(argName) != null) {
- sb.append('=');
- ASTExpression defaultExpr = (ASTExpression) paramDefaults.get(argName);
- if (function) {
- sb.append(defaultExpr.getCanonicalForm());
- } else {
- MessageUtil.appendExpressionAsUntearable(sb, defaultExpr);
- }
- }
- }
- if (catchAllParamName != null) {
- if (function) {
- if (argCnt != 0) {
- sb.append(", ");
- }
- } else {
- sb.append(' ');
- }
- sb.append(catchAllParamName);
- sb.append("...");
- }
- if (function) sb.append(')');
- if (canonical) {
- sb.append('>');
- sb.append(getChildrenCanonicalForm());
- sb.append("</").append(getASTNodeDescriptor()).append('>');
- }
- return sb.toString();
- }
-
- @Override
- String getASTNodeDescriptor() {
- return function ? "#function" : "#macro";
- }
-
- public boolean isFunction() {
- return function;
- }
-
- class Context implements LocalContext {
- final Environment.Namespace localVars;
- final ASTElement[] nestedContentBuffer;
- final Environment.Namespace nestedContentNamespace;
- final List<String> nestedContentParameterNames;
- final LocalContextStack prevLocalContextStack;
- final Context prevMacroContext;
-
- Context(Environment env,
- ASTElement[] nestedContentBuffer,
- List<String> nestedContentParameterNames) {
- localVars = env.new Namespace();
- this.nestedContentBuffer = nestedContentBuffer;
- nestedContentNamespace = env.getCurrentNamespace();
- this.nestedContentParameterNames = nestedContentParameterNames;
- prevLocalContextStack = env.getLocalContextStack();
- prevMacroContext = env.getCurrentMacroContext();
- }
-
-
- ASTDirMacro getMacro() {
- return ASTDirMacro.this;
- }
-
- // Set default parameters, check if all the required parameters are defined.
- void sanityCheck(Environment env) throws TemplateException {
- boolean resolvedAnArg, hasUnresolvedArg;
- ASTExpression firstUnresolvedExpression;
- InvalidReferenceException firstReferenceException;
- do {
- firstUnresolvedExpression = null;
- firstReferenceException = null;
- resolvedAnArg = hasUnresolvedArg = false;
- for (int i = 0; i < paramNames.length; ++i) {
- String argName = paramNames[i];
- if (localVars.get(argName) == null) {
- ASTExpression valueExp = (ASTExpression) paramDefaults.get(argName);
- if (valueExp != null) {
- try {
- TemplateModel tm = valueExp.eval(env);
- if (tm == null) {
- if (!hasUnresolvedArg) {
- firstUnresolvedExpression = valueExp;
- hasUnresolvedArg = true;
- }
- } else {
- localVars.put(argName, tm);
- resolvedAnArg = true;
- }
- } catch (InvalidReferenceException e) {
- if (!hasUnresolvedArg) {
- hasUnresolvedArg = true;
- firstReferenceException = e;
- }
- }
- } else {
- boolean argWasSpecified = localVars.containsKey(argName);
- throw new _MiscTemplateException(env,
- new _ErrorDescriptionBuilder(
- "When calling macro ", new _DelayedJQuote(name),
- ", required parameter ", new _DelayedJQuote(argName),
- " (parameter #", Integer.valueOf(i + 1), ") was ",
- (argWasSpecified
- ? "specified, but had null/missing value."
- : "not specified.")
- ).tip(argWasSpecified
- ? new Object[] {
- "If the parameter value expression on the caller side is known to "
- + "be legally null/missing, you may want to specify a default "
- + "value for it with the \"!\" operator, like "
- + "paramValue!defaultValue." }
- : new Object[] {
- "If the omission was deliberate, you may consider making the "
- + "parameter optional in the macro by specifying a default value "
- + "for it, like ", "<#macro macroName paramName=defaultExpr>", ")" }
- ));
- }
- }
- }
- } while (resolvedAnArg && hasUnresolvedArg);
- if (hasUnresolvedArg) {
- if (firstReferenceException != null) {
- throw firstReferenceException;
- } else {
- throw InvalidReferenceException.getInstance(firstUnresolvedExpression, env);
- }
- }
- }
-
- /**
- * @return the local variable of the given name
- * or null if it doesn't exist.
- */
- @Override
- public TemplateModel getLocalVariable(String name) throws TemplateModelException {
- return localVars.get(name);
- }
-
- Environment.Namespace getLocals() {
- return localVars;
- }
-
- /**
- * Set a local variable in this macro
- */
- void setLocalVar(String name, TemplateModel var) {
- localVars.put(name, var);
- }
-
- @Override
- public Collection<String> getLocalVariableNames() throws TemplateModelException {
- HashSet<String> result = new HashSet<>();
- for (TemplateModelIterator it = localVars.keys().iterator(); it.hasNext(); ) {
- result.add(it.next().toString());
- }
- return result;
- }
- }
-
- @Override
- int getParameterCount() {
- return 1/*name*/ + paramNames.length * 2/*name=default*/ + 1/*catchAll*/ + 1/*type*/;
- }
-
- @Override
- Object getParameterValue(int idx) {
- if (idx == 0) {
- return name;
- } else {
- final int argDescsEnd = paramNames.length * 2 + 1;
- if (idx < argDescsEnd) {
- String paramName = paramNames[(idx - 1) / 2];
- if (idx % 2 != 0) {
- return paramName;
- } else {
- return paramDefaults.get(paramName);
- }
- } else if (idx == argDescsEnd) {
- return catchAllParamName;
- } else if (idx == argDescsEnd + 1) {
- return function ? TYPE_FUNCTION : TYPE_MACRO;
- } else {
- throw new IndexOutOfBoundsException();
- }
- }
- }
-
- @Override
- ParameterRole getParameterRole(int idx) {
- if (idx == 0) {
- return ParameterRole.ASSIGNMENT_TARGET;
- } else {
- final int argDescsEnd = paramNames.length * 2 + 1;
- if (idx < argDescsEnd) {
- if (idx % 2 != 0) {
- return ParameterRole.PARAMETER_NAME;
- } else {
- return ParameterRole.PARAMETER_DEFAULT;
- }
- } else if (idx == argDescsEnd) {
- return ParameterRole.CATCH_ALL_PARAMETER_NAME;
- } else if (idx == argDescsEnd + 1) {
- return ParameterRole.AST_NODE_SUBTYPE;
- } else {
- throw new IndexOutOfBoundsException();
- }
- }
- }
-
- @Override
- boolean isNestedBlockRepeater() {
- // Because of recursive calls
- return true;
- }
-
-}
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/3cacd9ed/freemarker-core/src/main/java/org/apache/freemarker/core/ASTDirMacroOrFunction.java
----------------------------------------------------------------------
diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/ASTDirMacroOrFunction.java b/freemarker-core/src/main/java/org/apache/freemarker/core/ASTDirMacroOrFunction.java
new file mode 100644
index 0000000..6aecfff
--- /dev/null
+++ b/freemarker-core/src/main/java/org/apache/freemarker/core/ASTDirMacroOrFunction.java
@@ -0,0 +1,369 @@
+/*
+ * 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.util.Collection;
+import java.util.HashSet;
+import java.util.List;
+
+import org.apache.freemarker.core.model.ArgumentArrayLayout;
+import org.apache.freemarker.core.model.TemplateModel;
+import org.apache.freemarker.core.model.TemplateModelException;
+import org.apache.freemarker.core.model.TemplateModelIterator;
+import org.apache.freemarker.core.util.StringToIndexMap;
+import org.apache.freemarker.core.util._StringUtil;
+
+/**
+ * AST directive node: {@code #macro} or {@code #function}
+ */
+final class ASTDirMacroOrFunction extends ASTDirective implements TemplateModel {
+
+ static final ASTDirMacroOrFunction PASS_MACRO = new ASTDirMacroOrFunction(
+ false, ".pass",
+ null, null,
+ null, null,
+ TemplateElements.EMPTY);
+
+ static final int TYPE_MACRO = 0;
+ static final int TYPE_FUNCTION = 1;
+
+ static final String POSITIONAL_PARAMETER_OPTION_NAME = "positional";
+ static final String NAMED_PARAMETER_OPTION_NAME = "named";
+
+ private final String name;
+ private final boolean function;
+ /** See {@link #getParameterDefinitionByArgumentArrayIndex()}. */
+ private final ParameterDefinition[] paramDefsByArgArrayIdx;
+ private final ArgumentArrayLayout argsLayout;
+
+ ASTDirMacroOrFunction(
+ boolean function,
+ String name,
+ List<ParameterDefinition> predefPosParamDefs,
+ ParameterDefinition posVarargsParamDef,
+ List<ParameterDefinition> predefNamedParamDefs,
+ ParameterDefinition namedVarargsParamDef,
+ TemplateElements children) {
+ this.function = function;
+ this.name = name;
+
+ int predefPosParamsCnt = predefPosParamDefs != null ? predefPosParamDefs.size() : 0;
+ int predefNamedParamsCnt = predefNamedParamDefs != null ? predefNamedParamDefs.size() : 0;
+
+ paramDefsByArgArrayIdx = new ParameterDefinition[
+ predefPosParamsCnt + predefNamedParamsCnt
+ + (posVarargsParamDef != null ? 1 : 0) + (namedVarargsParamDef != null ? 1 : 0)];
+
+ int dstIdx = 0;
+ if (predefPosParamDefs != null) {
+ for (ParameterDefinition predefPosParam : predefPosParamDefs) {
+ paramDefsByArgArrayIdx[dstIdx++] = predefPosParam;
+ }
+ }
+
+ StringToIndexMap.Entry[] nameToIdxEntries;
+ if (predefNamedParamsCnt != 0) {
+ nameToIdxEntries = new StringToIndexMap.Entry[predefNamedParamsCnt];
+ for (int i = 0; i < predefNamedParamsCnt; i++) {
+ ParameterDefinition predefNamedParam = predefNamedParamDefs.get(i);
+ paramDefsByArgArrayIdx[dstIdx] = predefNamedParam;
+ nameToIdxEntries[i] = new StringToIndexMap.Entry(predefNamedParam.name, dstIdx);
+ dstIdx++;
+ }
+ } else {
+ nameToIdxEntries = null;
+ }
+
+ if (posVarargsParamDef != null) {
+ paramDefsByArgArrayIdx[dstIdx++] = posVarargsParamDef;
+ }
+ if (namedVarargsParamDef != null) {
+ paramDefsByArgArrayIdx[dstIdx++] = namedVarargsParamDef;
+ }
+
+ argsLayout = ArgumentArrayLayout.create(
+ predefPosParamsCnt,
+ posVarargsParamDef != null,
+ nameToIdxEntries != null ? StringToIndexMap.of(nameToIdxEntries) : null,
+ namedVarargsParamDef != null);
+
+ setChildren(children);
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ @Override
+ ASTElement[] accept(Environment env) {
+ env.visitMacroOrFunctionDefinition(this);
+ return null;
+ }
+
+ @Override
+ String getASTNodeDescriptor() {
+ return function ? "#function" : "#macro";
+ }
+
+ boolean isFunction() {
+ return function;
+ }
+
+ ArgumentArrayLayout getArgumentArrayLayout() {
+ return argsLayout;
+ }
+
+ /**
+ * Returns the definition of the parameters by the index according the {@link ArgumentArrayLayout}.
+ */
+ ParameterDefinition[] getParameterDefinitionByArgumentArrayIndex() {
+ return paramDefsByArgArrayIdx;
+ }
+
+ @Override
+ protected String dump(boolean canonical) {
+ StringBuilder sb = new StringBuilder();
+ if (canonical) sb.append('<');
+ sb.append(getASTNodeDescriptor());
+ sb.append(' ');
+ sb.append(_StringUtil.toFTLTopLevelTragetIdentifier(name));
+
+ if (function) sb.append('(');
+
+ boolean firstParam = true;
+ // Positional params:
+ int predefPosArgCnt = argsLayout.getPredefinedPositionalArgumentCount();
+ for (int idx = 0; idx < predefPosArgCnt; idx++) {
+ if (!firstParam) {
+ sb.append(", ");
+ } else {
+ if (!function) sb.append(" ");
+ firstParam = false;
+ }
+
+ ParameterDefinition paramDef = paramDefsByArgArrayIdx[idx];
+
+ sb.append(_StringUtil.toFTLTopLevelIdentifierReference(paramDef.name));
+ if (!function) {
+ sb.append("{").append(POSITIONAL_PARAMETER_OPTION_NAME).append("}");
+ }
+
+ if (paramDef.defaultExpression != null) {
+ sb.append('=');
+ sb.append(paramDef.defaultExpression.getCanonicalForm());
+ }
+ }
+ int posVarargsArgIdx = argsLayout.getPositionalVarargsArgumentIndex();
+ if (posVarargsArgIdx != -1) {
+ if (!firstParam) {
+ sb.append(", ");
+ } else {
+ if (!function) sb.append(" ");
+ firstParam = false;
+ }
+
+ sb.append(_StringUtil.toFTLTopLevelIdentifierReference(paramDefsByArgArrayIdx[posVarargsArgIdx].name));
+ if (!function) {
+ sb.append("{").append(POSITIONAL_PARAMETER_OPTION_NAME).append("}");
+ }
+ sb.append("...");
+ }
+ // Named params:
+ int predefNamedArgCnt = argsLayout.getPredefinedNamedArgumentsMap().size();
+ for (int idx = predefPosArgCnt; idx < predefPosArgCnt + predefNamedArgCnt; idx++) {
+ if (function) {
+ if (!firstParam) {
+ sb.append(", ");
+ } else {
+ firstParam = false;
+ }
+ } else {
+ sb.append(" ");
+ firstParam = false;
+ }
+
+ ParameterDefinition paramDef = paramDefsByArgArrayIdx[idx];
+
+ sb.append(_StringUtil.toFTLTopLevelIdentifierReference(paramDef.name));
+ if (function) {
+ sb.append("{").append(NAMED_PARAMETER_OPTION_NAME).append("}");
+ }
+
+ if (paramDef.defaultExpression != null) {
+ sb.append('=');
+ sb.append(paramDef.defaultExpression.getCanonicalForm());
+ }
+ }
+ int namedVarargsArgIdx = argsLayout.getNamedVarargsArgumentIndex();
+ if (namedVarargsArgIdx != -1) {
+ if (function) {
+ if (!firstParam) {
+ sb.append(", ");
+ } else {
+ firstParam = false;
+ }
+ } else {
+ sb.append(" ");
+ firstParam = false;
+ }
+
+ sb.append(_StringUtil.toFTLTopLevelIdentifierReference(paramDefsByArgArrayIdx[namedVarargsArgIdx].name));
+ if (function) {
+ sb.append("{").append(NAMED_PARAMETER_OPTION_NAME).append("}");
+ }
+ sb.append("...");
+ }
+
+ if (function) sb.append(')');
+
+ if (canonical) {
+ sb.append('>');
+ sb.append(getChildrenCanonicalForm());
+ sb.append("</").append(getASTNodeDescriptor()).append('>');
+ }
+ return sb.toString();
+ }
+
+ class Context implements LocalContext {
+ final Environment.Namespace localVars;
+ final Environment.TemplateLanguageCallable callable;
+ final CallPlace callPlace;
+ final Environment.Namespace nestedContentNamespace;
+ final LocalContextStack prevLocalContextStack;
+ final Context prevMacroContext;
+
+ /**
+ * @param callPlace Not {@code null}
+ * @param callable Not {@code null}
+ * @param env Not {@code null}
+ */
+ Context(Environment.TemplateLanguageCallable callable, CallPlace callPlace, Environment env) {
+ localVars = env.new Namespace(); // TODO [FM3] Pass in expected Map size
+ this.callable = callable;
+ this.callPlace = callPlace;
+ nestedContentNamespace = env.getCurrentNamespace();
+ prevLocalContextStack = env.getLocalContextStack();
+ prevMacroContext = env.getCurrentMacroContext();
+ }
+
+ ASTDirMacroOrFunction getMacro() {
+ return ASTDirMacroOrFunction.this;
+ }
+
+ /**
+ * @return the local variable of the given name
+ * or null if it doesn't exist.
+ */
+ @Override
+ public TemplateModel getLocalVariable(String name) throws TemplateModelException {
+ return localVars.get(name);
+ }
+
+ Environment.Namespace getLocals() {
+ return localVars;
+ }
+
+ /**
+ * Set a local variable in this macro
+ */
+ void setLocalVar(String name, TemplateModel var) {
+ localVars.put(name, var);
+ }
+
+ @Override
+ public Collection<String> getLocalVariableNames() throws TemplateModelException {
+ HashSet<String> result = new HashSet<>();
+ for (TemplateModelIterator it = localVars.keys().iterator(); it.hasNext(); ) {
+ result.add(it.next().toString());
+ }
+ return result;
+ }
+ }
+
+ @Override
+ int getParameterCount() {
+ return 1/*name*/ + paramDefsByArgArrayIdx.length + 1/*type*/;
+ }
+
+ @Override
+ Object getParameterValue(int idx) {
+ if (idx == 0) {
+ return name;
+ } else if (idx - 1 < paramDefsByArgArrayIdx.length) {
+ // TODO [FM3] This is not traversable with AST node API-s down to the default expression. Also, it's not
+ // extractable if the parameter is positional, named, and if it's varargs. But, as it's likely that the AST
+ // API will change, fixing this was postponed.
+ return paramDefsByArgArrayIdx[idx - 1];
+ } else if (idx == getParameterCount() - 1) {
+ return function ? TYPE_FUNCTION : TYPE_MACRO;
+ } else {
+ throw new IndexOutOfBoundsException();
+ }
+ }
+
+ @Override
+ ParameterRole getParameterRole(int idx) {
+ if (idx == 0) {
+ return ParameterRole.ASSIGNMENT_TARGET;
+ } else if (idx - 1 < paramDefsByArgArrayIdx.length) {
+ return ParameterRole.PARAMETER_DEFINITION;
+ } else if (idx == getParameterCount() - 1) {
+ return ParameterRole.AST_NODE_SUBTYPE;
+ } else {
+ throw new IndexOutOfBoundsException();
+ }
+ }
+
+ @Override
+ boolean isNestedBlockRepeater() {
+ // Because of recursive calls
+ return true;
+ }
+
+ /**
+ * A parameter definition from the #macro/#function start tag.
+ */
+ static class ParameterDefinition {
+ private final String name;
+ private final ASTExpression defaultExpression;
+
+ ParameterDefinition(String name, ASTExpression defaultExpression) {
+ this.name = name;
+ this.defaultExpression = defaultExpression;
+ }
+
+ String getName() {
+ return name;
+ }
+
+ ASTExpression getDefaultExpression() {
+ return defaultExpression;
+ }
+
+ @Override
+ public String toString() {
+ return "ParameterDefinition(" +
+ "name=" + _StringUtil.jQuote(name)
+ + (defaultExpression != null ? ", default=" + defaultExpression.getCanonicalForm() : "")
+ + ')';
+ }
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/3cacd9ed/freemarker-core/src/main/java/org/apache/freemarker/core/ASTDirNested.java
----------------------------------------------------------------------
diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/ASTDirNested.java b/freemarker-core/src/main/java/org/apache/freemarker/core/ASTDirNested.java
index da427ea..a849aaf 100644
--- a/freemarker-core/src/main/java/org/apache/freemarker/core/ASTDirNested.java
+++ b/freemarker-core/src/main/java/org/apache/freemarker/core/ASTDirNested.java
@@ -20,12 +20,9 @@
package org.apache.freemarker.core;
import java.io.IOException;
-import java.util.Collection;
-import java.util.Collections;
import java.util.List;
import org.apache.freemarker.core.model.TemplateModel;
-import org.apache.freemarker.core.model.TemplateModelException;
/**
* AST directive node: {@code #nested}.
@@ -33,30 +30,40 @@ import org.apache.freemarker.core.model.TemplateModelException;
final class ASTDirNested extends ASTDirective {
- private List bodyParameters;
+ private List<ASTExpression> nestedContentParameters;
- ASTDirNested(List bodyParameters) {
- this.bodyParameters = bodyParameters;
+ ASTDirNested(List nestedContentParameters) {
+ this.nestedContentParameters = nestedContentParameters;
}
- List getBodyParameters() {
- return bodyParameters;
+ List getNestedContentParameters() {
+ return nestedContentParameters;
}
- /**
- * There is actually a subtle but essential point in the code below.
- * A macro operates in the context in which it's defined. However,
- * a nested block within a macro instruction is defined in the
- * context in which the macro was invoked. So, we actually need to
- * temporarily switch the namespace and macro context back to
- * what it was before macro invocation to implement this properly.
- * I (JR) realized this thanks to some incisive comments from Daniel Dekany.
- */
@Override
ASTElement[] accept(Environment env) throws IOException, TemplateException {
- Context bodyContext = new Context(env);
- env.invokeNestedContent(bodyContext);
+ CallPlace macroCallPlace = env.getCurrentMacroContext().callPlace;
+
+ // When nestedContParamCnt < nestedContentParameters.size(), then we just skip calculating the extra parameters,
+ // and CallPlace.executeNestedContent will be successful. Note sure if this lenient behavior is a good idea,
+ // but for now it's inherited from FM2, so TODO [FM3].
+ // When nestedContParamCnt > nestedContentParameters.size(), then later
+ // CallPlace.executeNestedContent will throw exception, but we let that happen so that the error message
+ // generation remains centralized. (In FM2 not even this was an error.)
+ TemplateModel[] nestedContParamValues;
+ if (nestedContentParameters != null) {
+ nestedContParamValues = new TemplateModel[
+ Math.min(macroCallPlace.getNestedContentParameterCount(), nestedContentParameters.size())];
+ for (int i = 0; i < nestedContParamValues.length; i++) {
+ nestedContParamValues[i] = nestedContentParameters.get(i).eval(env);
+ }
+ } else {
+ nestedContParamValues = null;
+ }
+
+ env.executeNestedContentOfMacro(nestedContParamValues);
+
return null;
}
@@ -65,10 +72,10 @@ final class ASTDirNested extends ASTDirective {
StringBuilder sb = new StringBuilder();
if (canonical) sb.append('<');
sb.append(getASTNodeDescriptor());
- if (bodyParameters != null) {
- for (int i = 0; i < bodyParameters.size(); i++) {
+ if (nestedContentParameters != null) {
+ for (int i = 0; i < nestedContentParameters.size(); i++) {
sb.append(' ');
- sb.append(((ASTExpression) bodyParameters.get(i)).getCanonicalForm());
+ sb.append(nestedContentParameters.get(i).getCanonicalForm());
}
}
if (canonical) sb.append('>');
@@ -82,13 +89,13 @@ final class ASTDirNested extends ASTDirective {
@Override
int getParameterCount() {
- return bodyParameters != null ? bodyParameters.size() : 0;
+ return nestedContentParameters != null ? nestedContentParameters.size() : 0;
}
@Override
Object getParameterValue(int idx) {
checkIndex(idx);
- return bodyParameters.get(idx);
+ return nestedContentParameters.get(idx);
}
@Override
@@ -98,7 +105,7 @@ final class ASTDirNested extends ASTDirective {
}
private void checkIndex(int idx) {
- if (bodyParameters == null || idx >= bodyParameters.size()) {
+ if (nestedContentParameters == null || idx >= nestedContentParameters.size()) {
throw new IndexOutOfBoundsException();
}
}
@@ -118,40 +125,6 @@ final class ASTDirNested extends ASTDirective {
return true;
}
- class Context implements LocalContext {
- ASTDirMacro.Context invokingMacroContext;
- Environment.Namespace bodyVars;
-
- Context(Environment env) throws TemplateException {
- invokingMacroContext = env.getCurrentMacroContext();
- List<String> bodyParameterNames = invokingMacroContext.nestedContentParameterNames;
- if (bodyParameters != null) {
- for (int i = 0; i < bodyParameters.size(); i++) {
- ASTExpression exp = (ASTExpression) bodyParameters.get(i);
- TemplateModel tm = exp.eval(env);
- if (bodyParameterNames != null && i < bodyParameterNames.size()) {
- String bodyParameterName = bodyParameterNames.get(i);
- if (bodyVars == null) {
- bodyVars = env.new Namespace();
- }
- bodyVars.put(bodyParameterName, tm);
- }
- }
- }
- }
-
- @Override
- public TemplateModel getLocalVariable(String name) throws TemplateModelException {
- return bodyVars == null ? null : bodyVars.get(name);
- }
-
- @Override
- public Collection<String> getLocalVariableNames() {
- List<String> bodyParameterNames = invokingMacroContext.nestedContentParameterNames;
- return bodyParameterNames == null ? Collections.<String>emptyList() : bodyParameterNames;
- }
- }
-
@Override
boolean isNestedBlockRepeater() {
return false;
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/3cacd9ed/freemarker-core/src/main/java/org/apache/freemarker/core/ASTDirReturn.java
----------------------------------------------------------------------
diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/ASTDirReturn.java b/freemarker-core/src/main/java/org/apache/freemarker/core/ASTDirReturn.java
index b3fe6a5..8493310 100644
--- a/freemarker-core/src/main/java/org/apache/freemarker/core/ASTDirReturn.java
+++ b/freemarker-core/src/main/java/org/apache/freemarker/core/ASTDirReturn.java
@@ -35,7 +35,7 @@ final class ASTDirReturn extends ASTDirective {
if (exp != null) {
env.setLastReturnValue(exp.eval(env));
}
- if (nextSibling() == null && getParent() instanceof ASTDirMacro) {
+ if (nextSibling() == null && getParent() instanceof ASTDirMacroOrFunction) {
// Avoid unnecessary exception throwing
return null;
}
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/3cacd9ed/freemarker-core/src/main/java/org/apache/freemarker/core/ASTDynamicTopLevelCall.java
----------------------------------------------------------------------
diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/ASTDynamicTopLevelCall.java b/freemarker-core/src/main/java/org/apache/freemarker/core/ASTDynamicTopLevelCall.java
index 32f14ca..3fe5f74 100644
--- a/freemarker-core/src/main/java/org/apache/freemarker/core/ASTDynamicTopLevelCall.java
+++ b/freemarker-core/src/main/java/org/apache/freemarker/core/ASTDynamicTopLevelCall.java
@@ -22,11 +22,10 @@ package org.apache.freemarker.core;
import java.io.IOException;
import java.io.Writer;
import java.util.Collection;
-import java.util.LinkedHashMap;
+import java.util.List;
import org.apache.freemarker.core.ThreadInterruptionSupportTemplatePostProcessor.ASTThreadInterruptionCheck;
import org.apache.freemarker.core.model.ArgumentArrayLayout;
-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.TemplateDirectiveModel;
@@ -35,8 +34,9 @@ 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.FTLUtil;
import org.apache.freemarker.core.util.StringToIndexMap;
-import org.apache.freemarker.core.util._ArrayAdapterList;
+import org.apache.freemarker.core.util._CollectionUtil;
import org.apache.freemarker.core.util._StringUtil;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
@@ -52,7 +52,7 @@ import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
* is also validated (how many positional parameters are allowed, what named parameters are supported) then. Hence, the
* call is "dynamic".
*/
-class ASTDynamicTopLevelCall extends ASTDirective implements CallPlace {
+class ASTDynamicTopLevelCall extends ASTDirective implements CallPlace {
static final class NamedArgument {
private final String name;
@@ -70,6 +70,7 @@ class ASTDynamicTopLevelCall extends ASTDirective implements CallPlace {
private final StringToIndexMap nestedContentParamNames;
private final boolean allowCallingFunctions;
+ // Concurrently accessed, but need not be volatile
private CustomDataHolder customDataHolder;
/**
@@ -110,44 +111,17 @@ class ASTDynamicTopLevelCall extends ASTDirective implements CallPlace {
nestedContentSupported = directive.isNestedContentSupported();
} 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);
+ throw new NonDirectiveException(
+ "Calling functions is not allowed. You can only call directives (like macros) here.", env);
}
callableValue = (TemplateCallableModel) callableValueTM;
directive = null;
function = (TemplateFunctionModel) callableValue;
nestedContentSupported = false;
- } else if (callableValueTM instanceof ASTDirMacro) {
- // TODO [FM3][CF] Until macros were refactored to be TemplateDirectiveModel-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),
- nestedContentParamNames != null ? nestedContentParamNames.getKeys() : null,
- getChildBuffer());
- return null;
} else if (callableValueTM == null) {
throw InvalidReferenceException.getInstance(callableValueExp, env);
} else {
- throw new NonUserDefinedDirectiveLikeException(callableValueExp, callableValueTM, env);
+ throw new NonDirectiveException(callableValueExp, callableValueTM, env);
}
}
@@ -170,27 +144,43 @@ class ASTDynamicTopLevelCall extends ASTDirective implements CallPlace {
}
if (posVarargsArgIdx != -1) {
- int posVarargCnt = positionalArgs != null ? positionalArgs.length - predefPosArgCnt : 0;
+ int posVarargsLength = positionalArgs != null ? positionalArgs.length - predefPosArgCnt : 0;
TemplateSequenceModel varargsSeq;
- if (posVarargCnt <= 0) {
+ if (posVarargsLength <= 0) {
varargsSeq = Constants.EMPTY_SEQUENCE;
} else {
- NativeSequence nativeSeq = new NativeSequence(posVarargCnt);
+ NativeSequence nativeSeq = new NativeSequence(posVarargsLength);
varargsSeq = nativeSeq;
- for (int posVarargIdx = 0; posVarargIdx < posVarargCnt; posVarargIdx++) {
+ for (int posVarargIdx = 0; posVarargIdx < posVarargsLength; posVarargIdx++) {
nativeSeq.add(positionalArgs[predefPosArgCnt + posVarargIdx].eval(env));
}
}
execArgs[posVarargsArgIdx] = varargsSeq;
} else if (positionalArgs != null && positionalArgs.length > predefPosArgCnt) {
- throw new _MiscTemplateException(env,
- "The target callable ",
+ checkSupportsAnyParameters(callableValue, env);
+ List<String> validPredefNames = argsLayout.getPredefinedNamedArgumentsMap().getKeys();
+ _ErrorDescriptionBuilder errorDesc = new _ErrorDescriptionBuilder(
+ "The target ", FTLUtil.getCallableTypeName(callableValue), " ",
(predefPosArgCnt != 0
- ? new Object[] { "can only have ", predefPosArgCnt }
+ ? new Object[]{ "can only have ", predefPosArgCnt }
: "can't have"
),
" arguments passed by position, but the invocation has ",
- positionalArgs.length, " such arguments.");
+ positionalArgs.length, " such arguments. Try to pass arguments by name (as in ",
+ "<@example x=1 y=2 />", ").",
+ (!validPredefNames.isEmpty()
+ ? new Object[] { " The supported parameter names are:\n",
+ new _DelayedJQuotedListing(validPredefNames)}
+ : _CollectionUtil.EMPTY_OBJECT_ARRAY)
+ );
+ if (callableValue instanceof Environment.TemplateLanguageDirective) {
+ errorDesc.tip("You can pass a parameter by position (i.e., without specifying its name, as you"
+ + " have tried now) when the macro has defined that parameter to be a positional parameter. "
+ + "See in the documentation how, and when that's a good practice.");
+ }
+ throw new _MiscTemplateException(env,
+ errorDesc
+ );
}
int namedVarargsArgumentIndex = argsLayout.getNamedVarargsArgumentIndex();
@@ -204,15 +194,19 @@ class ASTDynamicTopLevelCall extends ASTDirective implements CallPlace {
} else {
if (namedVarargsHash == null) {
if (namedVarargsArgumentIndex == -1) {
+ checkSupportsAnyParameters(callableValue, env);
Collection<String> validNames = predefNamedArgsMap.getKeys();
throw new _MiscTemplateException(env,
validNames == null || validNames.isEmpty()
? new Object[] {
- "The target callable doesn't have any by-name-passed parameters (like ",
- new _DelayedJQuote(namedArg.name), ")"
+ "The called ", FTLUtil.getCallableTypeName(callableValue),
+ " can't have arguments that are passed by name (like ",
+ new _DelayedJQuote(namedArg.name), "). Try to pass arguments by position "
+ + "(i.e, without name, as in ", "<@example 1, 2, 3 />" , ")."
}
: new Object[] {
- "The target callable has no by-name-passed parameter called ",
+ "The called ", FTLUtil.getCallableTypeName(callableValue),
+ " has no parameter that's passed by name and is called ",
new _DelayedJQuote(namedArg.name), ". The supported parameter names are:\n",
new _DelayedJQuotedListing(validNames)
});
@@ -231,17 +225,25 @@ class ASTDynamicTopLevelCall extends ASTDirective implements CallPlace {
if (directive != null) {
directive.execute(execArgs, this, env.getOut(), env);
} else {
- TemplateModel result = function.execute(execArgs, env, this);
+ TemplateModel result = function.execute(execArgs, this, env);
if (result == null) {
throw new _MiscTemplateException(env, "Function has returned no value (or null)");
}
- // TODO [FM3][CF]
+ // TODO [FM3] Implement it when we have a such language... it should work like `${f()}`.
throw new BugException("Top-level function call not yet implemented");
}
return null;
}
+ private void checkSupportsAnyParameters(TemplateCallableModel callableValue, Environment env)
+ throws _MiscTemplateException {
+ if (callableValue.getArgumentArrayLayout().getTotalLength() == 0) {
+ throw new _MiscTemplateException(env,
+ "The called ", FTLUtil.getCallableTypeName(callableValue), " doesn't support any parameters.");
+ }
+ }
+
@Override
boolean isNestedBlockRepeater() {
return true;
@@ -392,10 +394,10 @@ class ASTDynamicTopLevelCall extends ASTDirective implements CallPlace {
}
@Override
- public void executeNestedContent(TemplateModel[] nestedContentParamValues, Writer out, Environment env)
+ public void executeNestedContent(TemplateModel[] nestedContentArgs, Writer out, Environment env)
throws TemplateException, IOException {
int nestedContentParamNamesSize = nestedContentParamNames != null ? nestedContentParamNames.size() : 0;
- int nestedContentParamValuesSize = nestedContentParamValues != null ? nestedContentParamValues.length : 0;
+ int nestedContentParamValuesSize = nestedContentArgs != null ? nestedContentArgs.length : 0;
if (nestedContentParamValuesSize != nestedContentParamNamesSize) {
throw new _MiscTemplateException(env,
"The invocation declares ", (nestedContentParamNamesSize != 0 ? nestedContentParamNamesSize : "no"),
@@ -407,15 +409,26 @@ class ASTDynamicTopLevelCall extends ASTDirective implements CallPlace {
nestedContentParamValuesSize, " parameters. You need to declare ", nestedContentParamValuesSize,
" nested content parameters.");
}
- env.visit(getChildBuffer(), nestedContentParamNames, nestedContentParamValues, out);
+ env.visit(getChildBuffer(), nestedContentParamNames, nestedContentArgs, out);
}
@Override
+ public int getFirstTargetJavaParameterTypeIndex() {
+ // TODO [FM3]
+ return -1;
+ }
+
+ @Override
+ public Class<?> getTargetJavaParameterType(int argIndex) {
+ // TODO [FM3]
+ return null;
+ }
+
+ @SuppressWarnings("SynchronizationOnLocalVariableOrMethodParameter")
@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.
+ // We are using double-checked locking, but utilizing Java Memory Model "final" behavior, so it's correct.
CustomDataHolder customDataHolder = this.customDataHolder; // Findbugs false alarm
if (customDataHolder == null) { // Findbugs false alarm
@@ -426,9 +439,7 @@ class ASTDynamicTopLevelCall extends ASTDirective implements CallPlace {
this.customDataHolder = customDataHolder;
}
}
- }
-
- if (customDataHolder.providerIdentity != providerIdentity) {
+ } else if (customDataHolder.providerIdentity != providerIdentity) {
synchronized (this) {
customDataHolder = this.customDataHolder;
if (customDataHolder == null || customDataHolder.providerIdentity != providerIdentity) {
@@ -441,7 +452,17 @@ class ASTDynamicTopLevelCall extends ASTDirective implements CallPlace {
return customDataHolder.customData;
}
- private CustomDataHolder createNewCustomData(Object provierIdentity, CommonSupplier supplier)
+ @Override
+ public boolean isCustomDataSupported() {
+ return true;
+ }
+
+ @Override
+ public boolean isNestedOutputCacheable() {
+ return isChildrenOutputCacheable();
+ }
+
+ private static CustomDataHolder createNewCustomData(Object providerIdentity, CommonSupplier<?> supplier)
throws CallPlaceCustomDataInitializationException {
CustomDataHolder customDataHolder;
Object customData;
@@ -450,42 +471,23 @@ class ASTDynamicTopLevelCall extends ASTDirective implements CallPlace {
} catch (Exception e) {
throw new CallPlaceCustomDataInitializationException(
"Failed to initialize custom data for provider identity "
- + _StringUtil.tryToString(provierIdentity) + " via factory "
+ + _StringUtil.tryToString(providerIdentity) + " via factory "
+ _StringUtil.tryToString(supplier), e);
}
if (customData == null) {
throw new NullPointerException("CommonSupplier.get() has returned null");
}
- customDataHolder = new CustomDataHolder(provierIdentity, customData);
+ customDataHolder = new CustomDataHolder(providerIdentity, 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 {
+ static class CustomDataHolder {
+ // It's important that all fields are final (Java Memory Model behaves specially with finals)!
private final Object providerIdentity;
private final Object customData;
- public CustomDataHolder(Object providerIdentity, Object customData) {
+
+ private CustomDataHolder(Object providerIdentity, Object customData) {
this.providerIdentity = providerIdentity;
this.customData = customData;
}