FREEMARKER-63: Change 1: Replaced the "loop variable" term with the more 
generic "nested content parameter" term. (In FM2, loop variables were 
introduced earlier than nested content parameter, so the two term coulnd't be 
unified. So it's just one less term in FM3.)

Change 2: CallPlace.executeNestedContent now requires the nested content 
paramter value array to have exactly the same length as the number of 
parameters declared by the caller.


Project: http://git-wip-us.apache.org/repos/asf/incubator-freemarker/repo
Commit: 
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/commit/589d9b80
Tree: http://git-wip-us.apache.org/repos/asf/incubator-freemarker/tree/589d9b80
Diff: http://git-wip-us.apache.org/repos/asf/incubator-freemarker/diff/589d9b80

Branch: refs/heads/3
Commit: 589d9b80e218a39d2ac128b1108952992868cc40
Parents: 126c506
Author: ddekany <ddek...@apache.org>
Authored: Sun Jul 30 20:38:01 2017 +0200
Committer: ddekany <ddek...@apache.org>
Committed: Sun Jul 30 20:38:01 2017 +0200

----------------------------------------------------------------------
 .../core/FM2ASTToFM3SourceConverter.java        |  26 +--
 .../apache/freemarker/core/ListErrorsTest.java  |  20 +--
 .../core/TemplateCallableModelTest.java         |   8 +-
 .../core/userpkg/AllFeaturesDirective.java      |  17 +-
 .../TwoNestedContentParamsDirective.java        |  53 ++++++
 .../org/apache/freemarker/core/ast-1.ast        |  12 +-
 .../apache/freemarker/core/ast-locations.ast    |  12 +-
 .../freemarker/core/ast-whitespacestripping.ast |   2 +-
 .../org/apache/freemarker/core/ASTDirItems.java |  42 ++---
 .../org/apache/freemarker/core/ASTDirList.java  | 162 ++++++++++---------
 .../freemarker/core/ASTDynamicTopLevelCall.java |  59 +++----
 .../apache/freemarker/core/ASTExpBuiltIn.java   |  20 +--
 .../freemarker/core/BuiltInForLoopVariable.java |  48 ------
 .../core/BuiltInForNestedContentParameter.java  |  49 ++++++
 .../core/BuiltInsForLoopVariables.java          | 156 ------------------
 .../BuiltInsForNestedContentParameters.java     | 156 ++++++++++++++++++
 .../org/apache/freemarker/core/Environment.java |  29 ++--
 .../apache/freemarker/core/ParameterRole.java   |   2 +-
 .../apache/freemarker/core/model/CallPlace.java |  32 ++--
 freemarker-core/src/main/javacc/FTL.jj          | 142 ++++++++--------
 20 files changed, 561 insertions(+), 486 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/589d9b80/freemarker-converter/src/main/java/freemarker/core/FM2ASTToFM3SourceConverter.java
----------------------------------------------------------------------
diff --git 
a/freemarker-converter/src/main/java/freemarker/core/FM2ASTToFM3SourceConverter.java
 
b/freemarker-converter/src/main/java/freemarker/core/FM2ASTToFM3SourceConverter.java
index ed86057..57a912f 100644
--- 
a/freemarker-converter/src/main/java/freemarker/core/FM2ASTToFM3SourceConverter.java
+++ 
b/freemarker-converter/src/main/java/freemarker/core/FM2ASTToFM3SourceConverter.java
@@ -671,14 +671,16 @@ public class FM2ASTToFM3SourceConverter {
 
         int paramCnt = node.getParameterCount();
         assertNodeContent(paramCnt <= 2, node, "Expected at most 2 
parameters");
-        String loopVar1 = getParam(node, 0, 
ParameterRole.TARGET_LOOP_VARIABLE, String.class);
-        String loopVar2 = paramCnt >= 2 ? getParam(node, 1, 
ParameterRole.TARGET_LOOP_VARIABLE, String.class) : null;
+        String nestedContParamName1 = getParam(node, 0, 
ParameterRole.TARGET_LOOP_VARIABLE, String.class);
+        String nestedContParamName2 =
+                paramCnt >= 2 ? getParam(node, 1, 
ParameterRole.TARGET_LOOP_VARIABLE, String.class)
+                : null;
 
-        print(FTLUtil.escapeIdentifier(loopVar1));
+        print(FTLUtil.escapeIdentifier(nestedContParamName1));
         pos = getPositionAfterIdentifier(pos);
-        if (loopVar2 != null) {
+        if (nestedContParamName2 != null) {
             pos = printSeparatorAndWSAndExpComments(pos, ",");
-            print(FTLUtil.escapeIdentifier(loopVar2));
+            print(FTLUtil.escapeIdentifier(nestedContParamName2));
             pos = getPositionAfterIdentifier(pos);
         }
 
@@ -1318,20 +1320,20 @@ public class FM2ASTToFM3SourceConverter {
             paramIdx += 2;
         }
 
-        // Print loop variables:
+        // Print nested content parameters (aka. loop variables):
         int pos = lastParamEnd;
-        boolean beforeFirstLoopVar = true;
+        boolean beforeFirstNestedContParam = true;
         while (paramIdx < paramCount) {
-            pos = printSeparatorAndWSAndExpComments(pos, beforeFirstLoopVar ? 
";" : ",");
+            pos = printSeparatorAndWSAndExpComments(pos, 
beforeFirstNestedContParam ? ";" : ",");
 
-            String loopVarName = getParam(node, paramIdx, 
ParameterRole.TARGET_LOOP_VARIABLE, String.class);
-            print(_StringUtil.toFTLTopLevelIdentifierReference(loopVarName));
+            String nestedContParamName = getParam(node, paramIdx, 
ParameterRole.TARGET_LOOP_VARIABLE, String.class);
+            
print(_StringUtil.toFTLTopLevelIdentifierReference(nestedContParamName));
             String identifierInSrc = readIdentifier(pos);
             assertNodeContent(identifierInSrc.length() != 0, node,
-                    "Can't find loop variable identifier in source");
+                    "Can't find nested content parameter name in the source");
             pos += identifierInSrc.length(); // skip loop var name
 
-            beforeFirstLoopVar = false;
+            beforeFirstNestedContParam = false;
             paramIdx++;
         }
 

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/589d9b80/freemarker-core-test/src/test/java/org/apache/freemarker/core/ListErrorsTest.java
----------------------------------------------------------------------
diff --git 
a/freemarker-core-test/src/test/java/org/apache/freemarker/core/ListErrorsTest.java
 
b/freemarker-core-test/src/test/java/org/apache/freemarker/core/ListErrorsTest.java
index 8986efe..8944dd1 100644
--- 
a/freemarker-core-test/src/test/java/org/apache/freemarker/core/ListErrorsTest.java
+++ 
b/freemarker-core-test/src/test/java/org/apache/freemarker/core/ListErrorsTest.java
@@ -53,11 +53,11 @@ public class ListErrorsTest extends TemplateTest {
         assertErrorContains("<#list xs><#macro m><#items as 
x></#items></#macro></#list>",
                 "#items", "must be inside", "#list");
         assertErrorContains("<#list xs as x><#items as 
x>${x}</#items></#list>",
-                "#list", "must not have", "#items", "as loopVar");
+                "#list", "must not have", "#items", "as someItem");
         assertErrorContains("<#list xs><#list xs as x><#items as 
x>${x}</#items></#list></#list>",
-                "#list", "must not have", "#items", "as loopVar");
+                "#list", "must not have", "#items", "as someItem");
         assertErrorContains("<#list xs></#list>",
-                "#list", "must have", "#items", "as loopVar");
+                "#list", "must have", "#items", "as someItem");
     }
 
     @Test
@@ -81,17 +81,17 @@ public class ListErrorsTest extends TemplateTest {
     }
     
     @Test
-    public void testInvalidLoopVarBuiltinLHO() {
+    public void testInvalidNestedContentParameterBuiltinLHO() {
         assertErrorContains("<#list foos>${foo?index}</#list>",
-                "?index", "foo", "no loop variable");
+                "?index", "foo", "nested content parameter", "none in scope");
         assertErrorContains("<#list foos as foo></#list>${foo?index}",
-                "?index", "foo" , "no loop variable");
+                "?index", "foo" , "nested content parameter", "none in scope");
         assertErrorContains("<#list foos as foo><#macro 
m>${foo?index}</#macro></#list>",
-                "?index", "foo" , "no loop variable");
+                "?index", "foo" , "nested content parameter", "none in scope");
         assertErrorContains("<#list foos as foo><#function 
f()>${foo?index}</#function></#list>",
-                "?index", "foo" , "no loop variable");
+                "?index", "foo" , "nested content parameter", "none in scope");
         assertErrorContains("<#list xs as x>${foo?index}</#list>",
-                "?index", "foo" , "no loop variable");
+                "?index", "foo" , "nested content parameter", "none in scope");
         assertErrorContains("<#list foos as foo><@m; 
foo>${foo?index}</@></#list>",
                 "?index", "foo" , "user defined directive");
         assertErrorContains(
@@ -115,7 +115,7 @@ public class ListErrorsTest extends TemplateTest {
         assertErrorContains("<#list {} as i></#list>",
                 "as k, v");
         assertErrorContains("<#list [] as k, v></#list>",
-                "only one loop variable");
+                "only one nested content parameter");
     }
 
     @Test

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/589d9b80/freemarker-core-test/src/test/java/org/apache/freemarker/core/TemplateCallableModelTest.java
----------------------------------------------------------------------
diff --git 
a/freemarker-core-test/src/test/java/org/apache/freemarker/core/TemplateCallableModelTest.java
 
b/freemarker-core-test/src/test/java/org/apache/freemarker/core/TemplateCallableModelTest.java
index 74edbaf..a51c657 100644
--- 
a/freemarker-core-test/src/test/java/org/apache/freemarker/core/TemplateCallableModelTest.java
+++ 
b/freemarker-core-test/src/test/java/org/apache/freemarker/core/TemplateCallableModelTest.java
@@ -25,6 +25,7 @@ import 
org.apache.freemarker.core.userpkg.AllFeaturesDirective;
 import org.apache.freemarker.core.userpkg.NamedVarargsOnlyDirective;
 import org.apache.freemarker.core.userpkg.PositionalVarargsOnlyDirective;
 import org.apache.freemarker.core.userpkg.TwoNamedParamsDirective;
+import org.apache.freemarker.core.userpkg.TwoNestedContentParamsDirective;
 import org.apache.freemarker.core.userpkg.TwoPositionalParamsDirective;
 import org.apache.freemarker.core.userpkg.UpperCaseDirective;
 import org.apache.freemarker.test.TemplateTest;
@@ -160,7 +161,12 @@ public class TemplateCallableModelTest extends 
TemplateTest {
         assertErrorContains("<@n 9 />", "can't have arguments passed by 
position");
         assertErrorContains("<@n n3=9 />", "has no", "\"n3\"", "supported", 
"\"n1\", \"n2\"");
         assertErrorContains("<@p n1=9 />", "doesn't have any by-name-passed");
-        assertErrorContains("<@a 1; i, j, k, l>x</@a>", "(4: \"i\", \"j\", 
\"k\", \"l\")", "(3)");
+
+        addToDataModel("tncp", TwoNestedContentParamsDirective.INSTANCE);
+        assertErrorContains("<@tncp />", " no ", " 2 ");
+        assertErrorContains("<@tncp ; i>${i}</@>", " 1 ", "\"i\"", " 2 ");
+        assertOutput("<@tncp ; i, j>${i} ${j}</@>", "1 2");
+        assertErrorContains("<@tncp ; i, j, k>${i}</@>", " 3 ", "\"i\", \"j\", 
\"k\"", " 2 ");
     }
 
     @Test

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/589d9b80/freemarker-core-test/src/test/java/org/apache/freemarker/core/userpkg/AllFeaturesDirective.java
----------------------------------------------------------------------
diff --git 
a/freemarker-core-test/src/test/java/org/apache/freemarker/core/userpkg/AllFeaturesDirective.java
 
b/freemarker-core-test/src/test/java/org/apache/freemarker/core/userpkg/AllFeaturesDirective.java
index c8d7329..e3be217 100644
--- 
a/freemarker-core-test/src/test/java/org/apache/freemarker/core/userpkg/AllFeaturesDirective.java
+++ 
b/freemarker-core-test/src/test/java/org/apache/freemarker/core/userpkg/AllFeaturesDirective.java
@@ -95,9 +95,9 @@ public class AllFeaturesDirective extends 
TestTemplateDirectiveModel {
         printParam(N1_ARG_NAME, n1, out);
         printParam(N2_ARG_NAME, n2, out);
         printParam("nVarargs", nOthers, out);
-        int loopVariableCount = callPlace.getLoopVariableCount();
-        if (loopVariableCount != 0) {
-            out.write("; " + loopVariableCount);
+        int nestedContParamCnt = callPlace.getNestedContentParameterCount();
+        if (nestedContParamCnt != 0) {
+            out.write("; " + nestedContParamCnt);
         }
         out.write(")");
 
@@ -106,12 +106,13 @@ public class AllFeaturesDirective extends 
TestTemplateDirectiveModel {
             if (p1 != null) {
                 int intP1 = p1.getAsNumber().intValue();
                 for (int i = 0; i < intP1; i++) {
-                    // We limit the number of loop variables passed to 3, so 
that related errors can be tested.
-                    TemplateModel[] loopVariableValues = new 
TemplateModel[Math.min(loopVariableCount, 3)];
-                    for (int loopVarIdx = 0; loopVarIdx < 
loopVariableValues.length; loopVarIdx++) {
-                        loopVariableValues[loopVarIdx] = new SimpleNumber((i + 
1) * (loopVarIdx + 1));
+                    // We dynamically set as many nested content parameters as 
many the caller has declared; this is
+                    // unusual, and is for testing purposes only.
+                    TemplateModel[] nestedContParamValues = new 
TemplateModel[nestedContParamCnt];
+                    for (int paramIdx = 0; paramIdx < 
nestedContParamValues.length; paramIdx++) {
+                        nestedContParamValues[paramIdx] = new SimpleNumber((i 
+ 1) * (paramIdx + 1));
                     }
-                    callPlace.executeNestedContent(loopVariableValues, out, 
env);
+                    callPlace.executeNestedContent(nestedContParamValues, out, 
env);
                 }
             }
             out.write("}");

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/589d9b80/freemarker-core-test/src/test/java/org/apache/freemarker/core/userpkg/TwoNestedContentParamsDirective.java
----------------------------------------------------------------------
diff --git 
a/freemarker-core-test/src/test/java/org/apache/freemarker/core/userpkg/TwoNestedContentParamsDirective.java
 
b/freemarker-core-test/src/test/java/org/apache/freemarker/core/userpkg/TwoNestedContentParamsDirective.java
new file mode 100644
index 0000000..eee1978
--- /dev/null
+++ 
b/freemarker-core-test/src/test/java/org/apache/freemarker/core/userpkg/TwoNestedContentParamsDirective.java
@@ -0,0 +1,53 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.freemarker.core.userpkg;
+
+import java.io.IOException;
+import java.io.Writer;
+
+import org.apache.freemarker.core.Environment;
+import org.apache.freemarker.core.TemplateException;
+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.TemplateModel;
+import org.apache.freemarker.core.model.impl.SimpleNumber;
+
+public class TwoNestedContentParamsDirective extends 
TestTemplateDirectiveModel {
+
+    public static final TwoNestedContentParamsDirective INSTANCE = new 
TwoNestedContentParamsDirective();
+
+    private TwoNestedContentParamsDirective() {
+        //
+    }
+
+    @Override
+    public void execute(TemplateModel[] args, CallPlace callPlace, Writer out, 
Environment env)
+            throws TemplateException, IOException {
+        callPlace.executeNestedContent(
+                new TemplateModel[] { Constants.ONE, new SimpleNumber(2) },
+                out, env);
+    }
+
+    @Override
+    public ArgumentArrayLayout getArgumentArrayLayout() {
+        return ArgumentArrayLayout.PARAMETERLESS;
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/589d9b80/freemarker-core-test/src/test/resources/org/apache/freemarker/core/ast-1.ast
----------------------------------------------------------------------
diff --git 
a/freemarker-core-test/src/test/resources/org/apache/freemarker/core/ast-1.ast 
b/freemarker-core-test/src/test/resources/org/apache/freemarker/core/ast-1.ast
index b7cf57f..18e46e9 100644
--- 
a/freemarker-core-test/src/test/resources/org/apache/freemarker/core/ast-1.ast
+++ 
b/freemarker-core-test/src/test/resources/org/apache/freemarker/core/ast-1.ast
@@ -25,8 +25,8 @@
         - argument value: 1  // o.a.f.c.ASTExpNumberLiteral
         - argument name: "y"  // String
         - argument value: 2  // o.a.f.c.ASTExpNumberLiteral
-        - target loop variable: "b1"  // String
-        - target loop variable: "b2"  // String
+        - nested content parameter: "b1"  // String
+        - nested content parameter: "b2"  // String
         #text  // o.a.f.c.ASTStaticText
             - content: "x"  // String
     #text  // o.a.f.c.ASTStaticText
@@ -37,8 +37,8 @@
             - right-hand operand: "bar"  // String
         - argument value: 1  // o.a.f.c.ASTExpNumberLiteral
         - argument value: 2  // o.a.f.c.ASTExpNumberLiteral
-        - target loop variable: "b1"  // String
-        - target loop variable: "b2"  // String
+        - nested content parameter: "b1"  // String
+        - nested content parameter: "b2"  // String
         #text  // o.a.f.c.ASTStaticText
             - content: "y"  // String
     #text  // o.a.f.c.ASTStaticText
@@ -149,7 +149,7 @@
         - content: "\n8 "  // String
     #list  // o.a.f.c.ASTDirList
         - list source: xs  // o.a.f.c.ASTExpVariable
-        - target loop variable: "x"  // String
+        - nested content parameter: "x"  // String
     #text  // o.a.f.c.ASTStaticText
         - content: "\n9 "  // String
     #list-#else-container  // o.a.f.c.ASTDirListElseContainer
@@ -158,7 +158,7 @@
             #text  // o.a.f.c.ASTStaticText
                 - content: "["  // String
             #items  // o.a.f.c.ASTDirItems
-                - target loop variable: "x"  // String
+                - nested content parameter: "x"  // String
                 ${...}  // o.a.f.c.ASTDollarInterpolation
                     - content: x  // o.a.f.c.ASTExpVariable
                 #sep  // o.a.f.c.ASTDirSep

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/589d9b80/freemarker-core-test/src/test/resources/org/apache/freemarker/core/ast-locations.ast
----------------------------------------------------------------------
diff --git 
a/freemarker-core-test/src/test/resources/org/apache/freemarker/core/ast-locations.ast
 
b/freemarker-core-test/src/test/resources/org/apache/freemarker/core/ast-locations.ast
index 2466cfd..5bdaded 100644
--- 
a/freemarker-core-test/src/test/resources/org/apache/freemarker/core/ast-locations.ast
+++ 
b/freemarker-core-test/src/test/resources/org/apache/freemarker/core/ast-locations.ast
@@ -107,21 +107,21 @@
         - content: "\n"  // String
     #list  // o.a.f.c.ASTDirList; Location 11:1-11:22
         - list source: s  // o.a.f.c.ASTExpVariable; Location 11:8-11:8
-        - target loop variable: "i"  // String
+        - nested content parameter: "i"  // String
     #list  // o.a.f.c.ASTDirList; Location 12:1-12:23
         - list source: s  // o.a.f.c.ASTExpVariable; Location 12:8-12:8
-        - target loop variable: "i"  // String
+        - nested content parameter: "i"  // String
         #text  // o.a.f.c.ASTStaticText; Location 12:15-12:15
             - content: "1"  // String
     #text  // o.a.f.c.ASTStaticText; Location 12:24-12:24
         - content: "\n"  // String
     #list  // o.a.f.c.ASTDirList; Location 13:1-13:28
         - list source: s  // o.a.f.c.ASTExpVariable; Location 13:8-13:8
-        - target loop variable: "i"  // String
+        - nested content parameter: "i"  // String
         #sep  // o.a.f.c.ASTDirSep; Location 13:15-13:20
     #list  // o.a.f.c.ASTDirList; Location 14:1-14:30
         - list source: s  // o.a.f.c.ASTExpVariable; Location 14:8-14:8
-        - target loop variable: "i"  // String
+        - nested content parameter: "i"  // String
         #text  // o.a.f.c.ASTStaticText; Location 14:15-14:15
             - content: "1"  // String
         #sep  // o.a.f.c.ASTDirSep; Location 14:16-14:22
@@ -132,14 +132,14 @@
     #list  // o.a.f.c.ASTDirList; Location 15:1-15:45
         - list source: s  // o.a.f.c.ASTExpVariable; Location 15:8-15:8
         #items  // o.a.f.c.ASTDirItems; Location 15:10-15:37
-            - target loop variable: "i"  // String
+            - nested content parameter: "i"  // String
             #sep  // o.a.f.c.ASTDirSep; Location 15:23-15:28
     #list  // o.a.f.c.ASTDirList; Location 16:1-16:49
         - list source: s  // o.a.f.c.ASTExpVariable; Location 16:8-16:8
         #text  // o.a.f.c.ASTStaticText; Location 16:10-16:10
             - content: "1"  // String
         #items  // o.a.f.c.ASTDirItems; Location 16:11-16:40
-            - target loop variable: "i"  // String
+            - nested content parameter: "i"  // String
             #text  // o.a.f.c.ASTStaticText; Location 16:24-16:24
                 - content: "1"  // String
             #sep  // o.a.f.c.ASTDirSep; Location 16:25-16:31

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/589d9b80/freemarker-core-test/src/test/resources/org/apache/freemarker/core/ast-whitespacestripping.ast
----------------------------------------------------------------------
diff --git 
a/freemarker-core-test/src/test/resources/org/apache/freemarker/core/ast-whitespacestripping.ast
 
b/freemarker-core-test/src/test/resources/org/apache/freemarker/core/ast-whitespacestripping.ast
index dd87f1f..ba69aa5 100644
--- 
a/freemarker-core-test/src/test/resources/org/apache/freemarker/core/ast-whitespacestripping.ast
+++ 
b/freemarker-core-test/src/test/resources/org/apache/freemarker/core/ast-whitespacestripping.ast
@@ -27,7 +27,7 @@
         - content: "\n"  // String
     #list  // o.a.f.c.ASTDirList
         - list source: x  // o.a.f.c.ASTExpVariable
-        - target loop variable: "xs"  // String
+        - nested content parameter: "xs"  // String
         #text  // o.a.f.c.ASTStaticText
             - content: "    "  // String
         ${...}  // o.a.f.c.ASTDollarInterpolation

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/589d9b80/freemarker-core/src/main/java/org/apache/freemarker/core/ASTDirItems.java
----------------------------------------------------------------------
diff --git 
a/freemarker-core/src/main/java/org/apache/freemarker/core/ASTDirItems.java 
b/freemarker-core/src/main/java/org/apache/freemarker/core/ASTDirItems.java
index f2c62cd..7290153 100644
--- a/freemarker-core/src/main/java/org/apache/freemarker/core/ASTDirItems.java
+++ b/freemarker-core/src/main/java/org/apache/freemarker/core/ASTDirItems.java
@@ -28,17 +28,17 @@ import org.apache.freemarker.core.util._StringUtil;
  */
 class ASTDirItems extends ASTDirective {
 
-    private final String loopVarName;
-    private final String loopVar2Name;
+    private final String nestedContentParamName;
+    private final String nestedContentParam2Name;
 
     /**
-     * @param loopVar2Name
-     *            For non-hash listings always {@code null}, for hash listings 
{@code loopVarName} and
-     *            {@code loopVarName2} holds the key- and value loop variable 
names.
+     * @param nestedContentParam2Name
+     *            For non-hash listings always {@code null}, for hash listings 
{@code nestedContentParamName} and
+     *            {@code nestedContentParamName2} holds the key- and value 
nested content parameter names.
      */
-    ASTDirItems(String loopVarName, String loopVar2Name, TemplateElements 
children) {
-        this.loopVarName = loopVarName;
-        this.loopVar2Name = loopVar2Name;
+    ASTDirItems(String nestedContentParamName, String nestedContentParam2Name, 
TemplateElements children) {
+        this.nestedContentParamName = nestedContentParamName;
+        this.nestedContentParam2Name = nestedContentParam2Name;
         setChildren(children);
     }
 
@@ -51,7 +51,7 @@ class ASTDirItems extends ASTDirective {
                     getASTNodeDescriptor(), " without iteration in context");
         }
         
-        iterCtx.loopForItemsElement(env, getChildBuffer(), loopVarName, 
loopVar2Name);
+        iterCtx.loopForItemsElement(env, getChildBuffer(), 
nestedContentParamName, nestedContentParam2Name);
         return null;
     }
 
@@ -66,10 +66,10 @@ class ASTDirItems extends ASTDirective {
         if (canonical) sb.append('<');
         sb.append(getASTNodeDescriptor());
         sb.append(" as ");
-        sb.append(_StringUtil.toFTLTopLevelIdentifierReference(loopVarName));
-        if (loopVar2Name != null) {
+        
sb.append(_StringUtil.toFTLTopLevelIdentifierReference(nestedContentParamName));
+        if (nestedContentParam2Name != null) {
             sb.append(", ");
-            
sb.append(_StringUtil.toFTLTopLevelIdentifierReference(loopVar2Name));
+            
sb.append(_StringUtil.toFTLTopLevelIdentifierReference(nestedContentParam2Name));
         }
         if (canonical) {
             sb.append('>');
@@ -88,18 +88,18 @@ class ASTDirItems extends ASTDirective {
 
     @Override
     int getParameterCount() {
-        return loopVar2Name != null ? 2 : 1;
+        return nestedContentParam2Name != null ? 2 : 1;
     }
 
     @Override
     Object getParameterValue(int idx) {
         switch (idx) {
         case 0:
-            if (loopVarName == null) throw new IndexOutOfBoundsException();
-            return loopVarName;
+            if (nestedContentParamName == null) throw new 
IndexOutOfBoundsException();
+            return nestedContentParamName;
         case 1:
-            if (loopVar2Name == null) throw new IndexOutOfBoundsException();
-            return loopVar2Name;
+            if (nestedContentParam2Name == null) throw new 
IndexOutOfBoundsException();
+            return nestedContentParam2Name;
         default: throw new IndexOutOfBoundsException();
         }
     }
@@ -108,11 +108,11 @@ class ASTDirItems extends ASTDirective {
     ParameterRole getParameterRole(int idx) {
         switch (idx) {
         case 0:
-            if (loopVarName == null) throw new IndexOutOfBoundsException();
-            return ParameterRole.TARGET_LOOP_VARIABLE;
+            if (nestedContentParamName == null) throw new 
IndexOutOfBoundsException();
+            return ParameterRole.NESTED_CONTENT_PARAMETER;
         case 1:
-            if (loopVar2Name == null) throw new IndexOutOfBoundsException();
-            return ParameterRole.TARGET_LOOP_VARIABLE;
+            if (nestedContentParam2Name == null) throw new 
IndexOutOfBoundsException();
+            return ParameterRole.NESTED_CONTENT_PARAMETER;
         default: throw new IndexOutOfBoundsException();
         }
     }

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/589d9b80/freemarker-core/src/main/java/org/apache/freemarker/core/ASTDirList.java
----------------------------------------------------------------------
diff --git 
a/freemarker-core/src/main/java/org/apache/freemarker/core/ASTDirList.java 
b/freemarker-core/src/main/java/org/apache/freemarker/core/ASTDirList.java
index c11ecef..14c7189 100644
--- a/freemarker-core/src/main/java/org/apache/freemarker/core/ASTDirList.java
+++ b/freemarker-core/src/main/java/org/apache/freemarker/core/ASTDirList.java
@@ -44,36 +44,36 @@ import org.apache.freemarker.core.util._StringUtil;
 final class ASTDirList extends ASTDirective {
 
     private final ASTExpression listedExp;
-    private final String loopVarName;
-    private final String loopVar2Name;
+    private final String nestedContentParamName;
+    private final String nestedContentParam2Name;
     private final boolean hashListing;
 
     /**
      * @param listedExp
      *            a variable referring to a sequence or collection or extended 
hash to list
-     * @param loopVarName
+     * @param nestedContentParamName
      *            The name of the variable that will hold the value of the 
current item when looping through listed value,
      *            or {@code null} if we have a nested {@code #items}. If this 
is a hash listing then this variable will holds the value
      *            of the hash key.
-     * @param loopVar2Name
+     * @param nestedContentParam2Name
      *            The name of the variable that will hold the value of the 
current item when looping through the list,
      *            or {@code null} if we have a nested {@code #items}. If this 
is a hash listing then it variable will hold the value
      *            from the key-value pair.
      * @param childrenBeforeElse
-     *            The nested content to execute if the listed value wasn't 
empty; can't be {@code null}. If the loop variable
-     *            was specified in the start tag, this is also what we will 
iterate over.
+     *            The nested content to execute if the listed value wasn't 
empty; can't be {@code null}. If the
+     *            nested content parameter is specified in the start tag, this 
is also what we will iterate over.
      * @param hashListing
      *            Whether this is a key-value pair listing, or a usual 
listing. This is properly set even if we have
      *            a nested {@code #items}.
      */
     ASTDirList(ASTExpression listedExp,
-                  String loopVarName,
-                  String loopVar2Name,
+                  String nestedContentParamName,
+                  String nestedContentParam2Name,
                   TemplateElements childrenBeforeElse,
                   boolean hashListing) {
         this.listedExp = listedExp;
-        this.loopVarName = loopVarName;
-        this.loopVar2Name = loopVar2Name;
+        this.nestedContentParamName = nestedContentParamName;
+        this.nestedContentParam2Name = nestedContentParam2Name;
         setChildren(childrenBeforeElse);
         this.hashListing = hashListing;
     }
@@ -94,25 +94,25 @@ final class ASTDirList extends ASTDirective {
             listedExp.assertNonNull(null, env);
         }
 
-        return env.visitIteratorBlock(new IterationContext(listedValue, 
loopVarName, loopVar2Name));
+        return env.visitIteratorBlock(new IterationContext(listedValue, 
nestedContentParamName, nestedContentParam2Name));
     }
 
     /**
-     * @param loopVariableName
-     *            Then name of the loop variable whose context we are looking 
for, or {@code null} if we simply look for
-     *            the innermost context.
+     * @param nestedContentParamName
+     *            Then name of the nested content parameter whose context we 
are looking for, or {@code null} if we
+     *            simply look for the innermost context.
      * @return The matching context or {@code null} if no such context exists.
      */
-    static IterationContext findEnclosingIterationContext(Environment env, 
String loopVariableName)
+    static IterationContext findEnclosingIterationContext(Environment env, 
String nestedContentParamName)
             throws _MiscTemplateException {
         LocalContextStack ctxStack = env.getLocalContextStack();
         if (ctxStack != null) {
             for (int i = ctxStack.size() - 1; i >= 0; i--) {
                 Object ctx = ctxStack.get(i);
                 if (ctx instanceof IterationContext
-                        && (loopVariableName == null
-                            || loopVariableName.equals(((IterationContext) 
ctx).getLoopVariableName())
-                            || loopVariableName.equals(((IterationContext) 
ctx).getLoopVariable2Name())
+                        && (nestedContentParamName == null
+                            || 
nestedContentParamName.equals(((IterationContext) 
ctx).getNestedContentParameter1Name())
+                            || 
nestedContentParamName.equals(((IterationContext) 
ctx).getNestedContentParameter2Name())
                             )) {
                     return (IterationContext) ctx;
                 }
@@ -128,12 +128,12 @@ final class ASTDirList extends ASTDirective {
         buf.append(getASTNodeDescriptor());
         buf.append(' ');
         buf.append(listedExp.getCanonicalForm());
-        if (loopVarName != null) {
+        if (nestedContentParamName != null) {
             buf.append(" as ");
-            
buf.append(_StringUtil.toFTLTopLevelIdentifierReference(loopVarName));
-            if (loopVar2Name != null) {
+            
buf.append(_StringUtil.toFTLTopLevelIdentifierReference(nestedContentParamName));
+            if (nestedContentParam2Name != null) {
                 buf.append(", ");
-                
buf.append(_StringUtil.toFTLTopLevelIdentifierReference(loopVar2Name));
+                
buf.append(_StringUtil.toFTLTopLevelIdentifierReference(nestedContentParam2Name));
             }
         }
         if (canonical) {
@@ -150,7 +150,7 @@ final class ASTDirList extends ASTDirective {
     
     @Override
     int getParameterCount() {
-        return 1 + (loopVarName != null ? 1 : 0) + (loopVar2Name != null ? 1 : 
0);
+        return 1 + (nestedContentParamName != null ? 1 : 0) + 
(nestedContentParam2Name != null ? 1 : 0);
     }
 
     @Override
@@ -159,11 +159,11 @@ final class ASTDirList extends ASTDirective {
         case 0:
             return listedExp;
         case 1:
-            if (loopVarName == null) throw new IndexOutOfBoundsException();
-            return loopVarName;
+            if (nestedContentParamName == null) throw new 
IndexOutOfBoundsException();
+            return nestedContentParamName;
         case 2:
-            if (loopVar2Name == null) throw new IndexOutOfBoundsException();
-            return loopVar2Name;
+            if (nestedContentParam2Name == null) throw new 
IndexOutOfBoundsException();
+            return nestedContentParam2Name;
         default: throw new IndexOutOfBoundsException();
         }
     }
@@ -174,11 +174,11 @@ final class ASTDirList extends ASTDirective {
         case 0:
             return ParameterRole.LIST_SOURCE;
         case 1:
-            if (loopVarName == null) throw new IndexOutOfBoundsException();
-            return ParameterRole.TARGET_LOOP_VARIABLE;
+            if (nestedContentParamName == null) throw new 
IndexOutOfBoundsException();
+            return ParameterRole.NESTED_CONTENT_PARAMETER;
         case 2:
-            if (loopVar2Name == null) throw new IndexOutOfBoundsException();
-            return ParameterRole.TARGET_LOOP_VARIABLE;
+            if (nestedContentParam2Name == null) throw new 
IndexOutOfBoundsException();
+            return ParameterRole.NESTED_CONTENT_PARAMETER;
         default: throw new IndexOutOfBoundsException();
         }
     }    
@@ -190,7 +190,7 @@ final class ASTDirList extends ASTDirective {
 
     @Override
     boolean isNestedBlockRepeater() {
-        return loopVarName != null;
+        return nestedContentParamName != null;
     }
 
     /**
@@ -203,50 +203,51 @@ final class ASTDirList extends ASTDirective {
         
         private Object openedIterator;
         private boolean hasNext;
-        private TemplateModel loopVar;
-        private TemplateModel loopVar2;
+        private TemplateModel nestedContentParam;
+        private TemplateModel nestedContentParam2;
         private int index;
         private boolean alreadyEntered;
         private Collection localVarNames = null;
         
         /** If the {@code #list} has nested {@code #items}, it's {@code null} 
outside the {@code #items}. */
-        private String loopVarName;
+        private String nestedContentParam1Name;
         /** Used if we list key-value pairs */
-        private String loopVar2Name;
+        private String nestedContentParam2Name;
         
         private final TemplateModel listedValue;
         
-        public IterationContext(TemplateModel listedValue, String loopVarName, 
String loopVar2Name) {
+        public IterationContext(TemplateModel listedValue,
+                String nestedContentParamName, String nestedContentParam2Name) 
{
             this.listedValue = listedValue;
-            this.loopVarName = loopVarName;
-            this.loopVar2Name = loopVar2Name;
+            this.nestedContentParam1Name = nestedContentParamName;
+            this.nestedContentParam2Name = nestedContentParam2Name;
         }
         
         boolean accept(Environment env) throws TemplateException, IOException {
             return executeNestedContent(env, getChildBuffer());
         }
 
-        void loopForItemsElement(Environment env, ASTElement[] childBuffer, 
String loopVarName, String loopVar2Name)
-                    throws
-                TemplateException, IOException {
+        void loopForItemsElement(Environment env, ASTElement[] childBuffer,
+                String nestedContentParamName, String nestedContentParam2Name)
+                throws TemplateException, IOException {
             try {
                 if (alreadyEntered) {
                     throw new _MiscTemplateException(env,
                             "The #items directive was already entered earlier 
for this listing.");
                 }
                 alreadyEntered = true;
-                this.loopVarName = loopVarName;
-                this.loopVar2Name = loopVar2Name;
+                this.nestedContentParam1Name = nestedContentParamName;
+                this.nestedContentParam2Name = nestedContentParam2Name;
                 executeNestedContent(env, childBuffer);
             } finally {
-                this.loopVarName = null;
-                this.loopVar2Name = null;
+                this.nestedContentParam1Name = null;
+                this.nestedContentParam2Name = null;
             }
         }
 
         /**
-         * Executes the given block for the {@link #listedValue}: if {@link 
#loopVarName} is non-{@code null}, then for
-         * each list item once, otherwise once if {@link #listedValue} isn't 
empty.
+         * Executes the given block for the {@link #listedValue}: if {@link 
#nestedContentParam1Name} is non-{@code
+         * null}, then for each list item once, otherwise once if {@link 
#listedValue} isn't empty.
          */
         private boolean executeNestedContent(Environment env, ASTElement[] 
childBuffer)
                 throws TemplateException, IOException {
@@ -265,10 +266,10 @@ final class ASTDirList extends ASTDirective {
                                 : ((TemplateModelIterator) openedIterator);
                 listNotEmpty = iterModel.hasNext();
                 if (listNotEmpty) {
-                    if (loopVarName != null) {
+                    if (nestedContentParam1Name != null) {
                         try {
                             do {
-                                loopVar = iterModel.next();
+                                nestedContentParam = iterModel.next();
                                 hasNext = iterModel.hasNext();
                                 env.visit(childBuffer);
                                 index++;
@@ -289,10 +290,10 @@ final class ASTDirList extends ASTDirective {
                 final int size = seqModel.size();
                 listNotEmpty = size != 0;
                 if (listNotEmpty) {
-                    if (loopVarName != null) {
+                    if (nestedContentParam1Name != null) {
                         try {
                             for (index = 0; index < size; index++) {
-                                loopVar = seqModel.get(index);
+                                nestedContentParam = seqModel.get(index);
                                 hasNext = (size > index + 1);
                                 env.visit(childBuffer);
                             }
@@ -308,8 +309,8 @@ final class ASTDirList extends ASTDirective {
                 throw new NonSequenceOrCollectionException(env,
                         new _ErrorDescriptionBuilder("The value you try to 
list is ",
                                 new _DelayedAOrAn(new 
_DelayedFTLTypeDescription(listedValue)),
-                                ", thus you must specify two loop variables 
after the \"as\"; one for the key, and "
-                                + "another for the value, like ", "<#... as k, 
v>", ")."
+                                ", thus you must declare two nested content 
parameters after the \"as\"; one for the "
+                                + "key, and another for the value, like ", 
"<#... as k, v>", ")."
                                 ));
             } else {
                 throw new NonSequenceOrCollectionException(
@@ -329,12 +330,12 @@ final class ASTDirList extends ASTDirective {
                                     : (KeyValuePairIterator) openedIterator;
                     hashNotEmpty = kvpIter.hasNext();
                     if (hashNotEmpty) {
-                        if (loopVarName != null) {
+                        if (nestedContentParam1Name != null) {
                             try {
                                 do {
                                     KeyValuePair kvp = kvpIter.next();
-                                    loopVar = kvp.getKey();
-                                    loopVar2 = kvp.getValue();
+                                    nestedContentParam = kvp.getKey();
+                                    nestedContentParam2 = kvp.getValue();
                                     hasNext = kvpIter.hasNext();
                                     env.visit(childBuffer);
                                     index++;
@@ -353,24 +354,27 @@ final class ASTDirList extends ASTDirective {
                     TemplateModelIterator keysIter = 
listedHash.keys().iterator();
                     hashNotEmpty = keysIter.hasNext();
                     if (hashNotEmpty) {
-                        if (loopVarName != null) {
+                        if (nestedContentParam1Name != null) {
                             try {
                                 do {
-                                    loopVar = keysIter.next();
-                                    if (!(loopVar instanceof 
TemplateScalarModel)) {
+                                    nestedContentParam = keysIter.next();
+                                    if (!(nestedContentParam instanceof 
TemplateScalarModel)) {
                                         throw new NonStringException(env,
                                                 new _ErrorDescriptionBuilder(
                                                         "When listing 
key-value pairs of traditional hash "
                                                         + "implementations, 
all keys must be strings, but one of them "
                                                         + "was ",
-                                                        new _DelayedAOrAn(new 
_DelayedFTLTypeDescription(loopVar)), "."
+                                                        new _DelayedAOrAn(
+                                                                new 
_DelayedFTLTypeDescription(nestedContentParam)),
+                                                        "."
                                                         ).tip("The listed 
value's TemplateModel class was ",
                                                                 new 
_DelayedShortClassName(listedValue.getClass()),
                                                                 ", which 
doesn't implement ",
                                                                 new 
_DelayedShortClassName(TemplateHashModelEx2.class),
                                                                 ", which leads 
to this restriction."));
                                     }
-                                    loopVar2 = 
listedHash.get(((TemplateScalarModel) loopVar).getAsString());
+                                    nestedContentParam2 = 
listedHash.get(((TemplateScalarModel) nestedContentParam)
+                                            .getAsString());
                                     hasNext = keysIter.hasNext();
                                     env.visit(childBuffer);
                                     index++;
@@ -388,8 +392,8 @@ final class ASTDirList extends ASTDirective {
                 throw new NonSequenceOrCollectionException(env,
                         new _ErrorDescriptionBuilder("The value you try to 
list is ",
                                 new _DelayedAOrAn(new 
_DelayedFTLTypeDescription(listedValue)),
-                                ", thus you must specify only one loop 
variable after the \"as\" (there's no separate "
-                                + "key and value)."
+                                ", thus you must declare only one nested 
content parameter after the \"as\" (there's "
+                                + "no separate key and value)."
                                 ));
             } else {
                 throw new NonExtendedHashException(
@@ -398,21 +402,21 @@ final class ASTDirList extends ASTDirective {
             return hashNotEmpty;
         }
 
-        String getLoopVariableName() {
-            return loopVarName;
+        String getNestedContentParameter1Name() {
+            return nestedContentParam1Name;
         }
 
-        String getLoopVariable2Name() {
-            return loopVar2Name;
+        String getNestedContentParameter2Name() {
+            return nestedContentParam2Name;
         }
         
         @Override
         public TemplateModel getLocalVariable(String name) {
-            String loopVarName = this.loopVarName;
-            if (loopVarName != null && name.startsWith(loopVarName)) {
-                switch(name.length() - loopVarName.length()) {
+            String nestedContentParamName = this.nestedContentParam1Name;
+            if (nestedContentParamName != null && 
name.startsWith(nestedContentParamName)) {
+                switch(name.length() - nestedContentParamName.length()) {
                     case 0: 
-                        return loopVar;
+                        return nestedContentParam;
                     case 6: 
                         if (name.endsWith(LOOP_STATE_INDEX)) {
                             return new SimpleNumber(index);
@@ -426,8 +430,8 @@ final class ASTDirList extends ASTDirective {
                 }
             }
             
-            if (name.equals(loopVar2Name)) {
-                return loopVar2;
+            if (name.equals(nestedContentParam2Name)) {
+                return nestedContentParam2;
             }
             
             return null;
@@ -435,13 +439,13 @@ final class ASTDirList extends ASTDirective {
         
         @Override
         public Collection<String> getLocalVariableNames() {
-            String loopVarName = this.loopVarName;
-            if (loopVarName != null) {
+            String nestedContentParamName = this.nestedContentParam1Name;
+            if (nestedContentParamName != null) {
                 if (localVarNames == null) {
                     localVarNames = new ArrayList(3);
-                    localVarNames.add(loopVarName);
-                    localVarNames.add(loopVarName + LOOP_STATE_INDEX);
-                    localVarNames.add(loopVarName + LOOP_STATE_HAS_NEXT);
+                    localVarNames.add(nestedContentParamName);
+                    localVarNames.add(nestedContentParamName + 
LOOP_STATE_INDEX);
+                    localVarNames.add(nestedContentParamName + 
LOOP_STATE_HAS_NEXT);
                 }
                 return localVarNames;
             } else {

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/589d9b80/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 b178547..eb06e23 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
@@ -66,7 +66,7 @@ class ASTDynamicTopLevelCall extends ASTDirective implements 
CallPlace {
     private final ASTExpression callableValueExp;
     private final ASTExpression[] positionalArgs;
     private final NamedArgument[] namedArgs;
-    private final StringToIndexMap loopVarNames;
+    private final StringToIndexMap nestedContentParamNames;
     private final boolean allowCallingFunctions;
 
     private CustomDataHolder customDataHolder;
@@ -77,19 +77,19 @@ class ASTDynamicTopLevelCall extends ASTDirective 
implements CallPlace {
      */
     ASTDynamicTopLevelCall(
             ASTExpression callableValueExp, boolean allowCallingFunctions,
-            ASTExpression[] positionalArgs, NamedArgument[] namedArgs, 
StringToIndexMap loopVarNames,
+            ASTExpression[] positionalArgs, NamedArgument[] namedArgs, 
StringToIndexMap nestedContentParamNames,
             TemplateElements children) {
         this.callableValueExp = callableValueExp;
         this.allowCallingFunctions = allowCallingFunctions;
 
         if (positionalArgs != null && positionalArgs.length == 0
                 || namedArgs != null && namedArgs.length == 0
-                || loopVarNames != null && loopVarNames.size() == 0) {
+                || nestedContentParamNames != null && 
nestedContentParamNames.size() == 0) {
             throw new IllegalArgumentException("Use null instead of empty 
collections");
         }
         this.positionalArgs = positionalArgs;
         this.namedArgs = namedArgs;
-        this.loopVarNames = loopVarNames;
+        this.nestedContentParamNames = nestedContentParamNames;
 
         setChildren(children);
     }
@@ -137,7 +137,7 @@ class ASTDynamicTopLevelCall extends ASTDirective 
implements CallPlace {
                 env.invoke(macro,
                         macroNamedArgs,
                         _ArrayAdapterList.adapt(positionalArgs),
-                        loopVarNames != null ? loopVarNames.getKeys() : null,
+                        nestedContentParamNames != null ? 
nestedContentParamNames.getKeys() : null,
                         getChildBuffer());
                 return null;
             } else if (callableValueTM == null) {
@@ -269,16 +269,16 @@ class ASTDynamicTopLevelCall extends ASTDirective 
implements CallPlace {
                 MessageUtil.appendExpressionAsUntearable(sb, namedArg.value);
             }
         }
-        if (loopVarNames != null) {
+        if (nestedContentParamNames != null) {
             sb.append("; ");
             boolean first = true;
-            for (String loopVarName : loopVarNames.getKeys()) {
+            for (String nestedContentParamName : 
nestedContentParamNames.getKeys()) {
                 if (!first) {
                     sb.append(", ");
                 } else {
                     first = false;
                 }
-                
sb.append(_StringUtil.toFTLTopLevelIdentifierReference(loopVarName));
+                
sb.append(_StringUtil.toFTLTopLevelIdentifierReference(nestedContentParamName));
             }
         }
         if (canonical) {
@@ -309,7 +309,7 @@ class ASTDynamicTopLevelCall extends ASTDirective 
implements CallPlace {
         return 1/*nameExp*/
                 + (positionalArgs != null ? positionalArgs.length : 0)
                 + (namedArgs != null ? namedArgs.length * 2 : 0)
-                + (loopVarNames != null ? loopVarNames.size() : 0);
+                + (nestedContentParamNames != null ? 
nestedContentParamNames.size() : 0);
     }
 
     @Override
@@ -329,9 +329,10 @@ class ASTDynamicTopLevelCall extends ASTDirective 
implements CallPlace {
                     return (idx - base) % 2 == 0 ? namedArg.name : 
namedArg.value;
                 } else {
                     base += namedArgsSize * 2;
-                    final int bodyParameterNamesSize = loopVarNames != null ? 
loopVarNames.size() : 0;
+                    final int bodyParameterNamesSize = nestedContentParamNames 
!= null
+                            ? nestedContentParamNames.size() : 0;
                     if (idx - base < bodyParameterNamesSize) {
-                        return loopVarNames.getKeys().get(idx - base);
+                        return nestedContentParamNames.getKeys().get(idx - 
base);
                     } else {
                         throw new IndexOutOfBoundsException();
                     }
@@ -356,9 +357,10 @@ class ASTDynamicTopLevelCall extends ASTDirective 
implements CallPlace {
                     return (idx - base) % 2 == 0 ? ParameterRole.ARGUMENT_NAME 
: ParameterRole.ARGUMENT_VALUE;
                 } else {
                     base += namedArgsSize * 2;
-                    final int bodyParameterNamesSize = loopVarNames != null ? 
loopVarNames.size() : 0;
+                    final int bodyParameterNamesSize = nestedContentParamNames 
!= null
+                            ? nestedContentParamNames.size() : 0;
                     if (idx - base < bodyParameterNamesSize) {
-                        return ParameterRole.TARGET_LOOP_VARIABLE;
+                        return ParameterRole.NESTED_CONTENT_PARAMETER;
                     } else {
                         throw new IndexOutOfBoundsException();
                     }
@@ -377,26 +379,27 @@ class ASTDynamicTopLevelCall extends ASTDirective 
implements CallPlace {
     }
 
     @Override
-    public int getLoopVariableCount() {
-        return loopVarNames != null ? loopVarNames.size() : 0;
+    public int getNestedContentParameterCount() {
+        return nestedContentParamNames != null ? 
nestedContentParamNames.size() : 0;
     }
 
     @Override
-    public void executeNestedContent(TemplateModel[] loopVarValues, Writer 
out, Environment env)
+    public void executeNestedContent(TemplateModel[] nestedContentParamValues, 
Writer out, Environment env)
             throws TemplateException, IOException {
-        if (loopVarNames != null) {
-            int loopVarNamesSize = loopVarNames.size();
-            int loopVarValuesSize = loopVarValues != null ? 
loopVarValues.length : 0;
-            if (loopVarValuesSize < loopVarNamesSize) {
-                throw new _MiscTemplateException(this,
-                        "The invocation declares more nested content 
parameters (",
-                        loopVarNamesSize, ": ", new 
_DelayedJQuotedListing(loopVarNames.getKeys()),
-                        ") than what the called object intends to pass (",
-                        loopVarValuesSize, "). Declare no more than ", 
loopVarValuesSize,
-                        " nested content parameters.");
-            }
+        int nestedContentParamNamesSize = nestedContentParamNames != null ? 
nestedContentParamNames.size() : 0;
+        int nestedContentParamValuesSize = nestedContentParamValues != null ? 
nestedContentParamValues.length : 0;
+        if (nestedContentParamValuesSize != nestedContentParamNamesSize) {
+            throw new _MiscTemplateException(this,
+                    "The invocation declares ", (nestedContentParamNamesSize 
!= 0 ? nestedContentParamNamesSize : "no"),
+                    " nested content parameter(s)",
+                    (nestedContentParamNamesSize != 0
+                            ? new Object[] { " (", new 
_DelayedJQuotedListing(nestedContentParamNames.getKeys()), ")", }
+                            : ""),
+                    ", but the called object intends to pass ",
+                    nestedContentParamValuesSize, " parameters. You need to 
declare ", nestedContentParamValuesSize,
+                    " nested content parameters.");
         }
-        env.visit(getChildBuffer(), loopVarNames, loopVarValues, out);
+        env.visit(getChildBuffer(), nestedContentParamNames, 
nestedContentParamValues, out);
     }
 
     @Override

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/589d9b80/freemarker-core/src/main/java/org/apache/freemarker/core/ASTExpBuiltIn.java
----------------------------------------------------------------------
diff --git 
a/freemarker-core/src/main/java/org/apache/freemarker/core/ASTExpBuiltIn.java 
b/freemarker-core/src/main/java/org/apache/freemarker/core/ASTExpBuiltIn.java
index 4101b29..a74f83f 100644
--- 
a/freemarker-core/src/main/java/org/apache/freemarker/core/ASTExpBuiltIn.java
+++ 
b/freemarker-core/src/main/java/org/apache/freemarker/core/ASTExpBuiltIn.java
@@ -111,14 +111,14 @@ abstract class ASTExpBuiltIn extends ASTExpression 
implements Cloneable {
         putBI("float", new floatBI());
         putBI("floor", new floorBI());
         putBI("chunk", new chunkBI());
-        putBI("counter", new BuiltInsForLoopVariables.counterBI());
-        putBI("itemCycle", new BuiltInsForLoopVariables.item_cycleBI());
+        putBI("counter", new BuiltInsForNestedContentParameters.counterBI());
+        putBI("itemCycle", new 
BuiltInsForNestedContentParameters.item_cycleBI());
         putBI("hasApi", new BuiltInsForMultipleTypes.has_apiBI());
         putBI("hasContent", new BuiltInsForExistenceHandling.has_contentBI());
-        putBI("hasNext", new BuiltInsForLoopVariables.has_nextBI());
+        putBI("hasNext", new BuiltInsForNestedContentParameters.has_nextBI());
         putBI("html", new BuiltInsForStringsEncoding.htmlBI());
         putBI("ifExists", new BuiltInsForExistenceHandling.if_existsBI());
-        putBI("index", new BuiltInsForLoopVariables.indexBI());
+        putBI("index", new BuiltInsForNestedContentParameters.indexBI());
         putBI("indexOf", new BuiltInsForStringsBasic.index_ofBI(false));
         putBI("int", new intBI());
         putBI("interpret", new BuiltInsForStringsMisc.interpretBI());
@@ -129,9 +129,9 @@ abstract class ASTExpBuiltIn extends ASTExpression 
implements Cloneable {
         putBI("isDate", bi);  // misnomer
         putBI("isDateLike", bi);
         putBI("isDateOnly", new 
BuiltInsForMultipleTypes.is_dateOfTypeBI(TemplateDateModel.DATE));
-        putBI("isEvenItem", new BuiltInsForLoopVariables.is_even_itemBI());
-        putBI("isFirst", new BuiltInsForLoopVariables.is_firstBI());
-        putBI("isLast", new BuiltInsForLoopVariables.is_lastBI());
+        putBI("isEvenItem", new 
BuiltInsForNestedContentParameters.is_even_itemBI());
+        putBI("isFirst", new BuiltInsForNestedContentParameters.is_firstBI());
+        putBI("isLast", new BuiltInsForNestedContentParameters.is_lastBI());
         putBI("isUnknownDateLike", new 
BuiltInsForMultipleTypes.is_dateOfTypeBI(TemplateDateModel.UNKNOWN));
         putBI("isDatetime", new 
BuiltInsForMultipleTypes.is_dateOfTypeBI(TemplateDateModel.DATE_TIME));
         putBI("isDirective", new BuiltInsForMultipleTypes.is_directiveBI());
@@ -146,7 +146,7 @@ abstract class ASTExpBuiltIn extends ASTExpression 
implements Cloneable {
         putBI("isNan", new is_nanBI());
         putBI("isNode", new BuiltInsForMultipleTypes.is_nodeBI());
         putBI("isNumber", new BuiltInsForMultipleTypes.is_numberBI());
-        putBI("isOddItem", new BuiltInsForLoopVariables.is_odd_itemBI());
+        putBI("isOddItem", new 
BuiltInsForNestedContentParameters.is_odd_itemBI());
         putBI("isSequence", new BuiltInsForMultipleTypes.is_sequenceBI());
         putBI("isString", new BuiltInsForMultipleTypes.is_stringBI());
         putBI("isTime", new 
BuiltInsForMultipleTypes.is_dateOfTypeBI(TemplateDateModel.TIME));
@@ -243,8 +243,8 @@ abstract class ASTExpBuiltIn extends ASTExpression 
implements Cloneable {
         putBI("parent", new parentBI());
         putBI("previousSibling", new previousSiblingBI());
         putBI("nextSibling", new nextSiblingBI());
-        putBI("itemParity", new BuiltInsForLoopVariables.item_parityBI());
-        putBI("itemParityCap", new 
BuiltInsForLoopVariables.item_parity_capBI());
+        putBI("itemParity", new 
BuiltInsForNestedContentParameters.item_parityBI());
+        putBI("itemParityCap", new 
BuiltInsForNestedContentParameters.item_parity_capBI());
         putBI("reverse", new reverseBI());
         putBI("rightPad", new BuiltInsForStringsBasic.padBI(false));
         putBI("root", new rootBI());

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/589d9b80/freemarker-core/src/main/java/org/apache/freemarker/core/BuiltInForLoopVariable.java
----------------------------------------------------------------------
diff --git 
a/freemarker-core/src/main/java/org/apache/freemarker/core/BuiltInForLoopVariable.java
 
b/freemarker-core/src/main/java/org/apache/freemarker/core/BuiltInForLoopVariable.java
deleted file mode 100644
index 5b7d9c3..0000000
--- 
a/freemarker-core/src/main/java/org/apache/freemarker/core/BuiltInForLoopVariable.java
+++ /dev/null
@@ -1,48 +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 org.apache.freemarker.core.ASTDirList.IterationContext;
-import org.apache.freemarker.core.model.TemplateModel;
-
-abstract class BuiltInForLoopVariable extends SpecialBuiltIn {
-    
-    private String loopVarName;
-    
-    void bindToLoopVariable(String loopVarName) {
-        this.loopVarName = loopVarName;
-    }
-    
-    @Override
-    TemplateModel _eval(Environment env) throws TemplateException {
-        IterationContext iterCtx = 
ASTDirList.findEnclosingIterationContext(env, loopVarName);
-        if (iterCtx == null) {
-            // The parser should prevent this situation
-            throw new _MiscTemplateException(
-                    this, env,
-                    "There's no iteration in context that uses loop variable 
", new _DelayedJQuote(loopVarName), ".");
-        }
-        
-        return calculateResult(iterCtx, env);
-    }
-
-    abstract TemplateModel calculateResult(IterationContext iterCtx, 
Environment env) throws TemplateException;
-    
-}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/589d9b80/freemarker-core/src/main/java/org/apache/freemarker/core/BuiltInForNestedContentParameter.java
----------------------------------------------------------------------
diff --git 
a/freemarker-core/src/main/java/org/apache/freemarker/core/BuiltInForNestedContentParameter.java
 
b/freemarker-core/src/main/java/org/apache/freemarker/core/BuiltInForNestedContentParameter.java
new file mode 100644
index 0000000..9847352
--- /dev/null
+++ 
b/freemarker-core/src/main/java/org/apache/freemarker/core/BuiltInForNestedContentParameter.java
@@ -0,0 +1,49 @@
+/*
+ * 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 org.apache.freemarker.core.ASTDirList.IterationContext;
+import org.apache.freemarker.core.model.TemplateModel;
+
+abstract class BuiltInForNestedContentParameter extends SpecialBuiltIn {
+    
+    private String nestedContentParamName;
+    
+    void bindToNestedContentParameter(String nestedContentParamName) {
+        this.nestedContentParamName = nestedContentParamName;
+    }
+    
+    @Override
+    TemplateModel _eval(Environment env) throws TemplateException {
+        IterationContext iterCtx = 
ASTDirList.findEnclosingIterationContext(env, nestedContentParamName);
+        if (iterCtx == null) {
+            // The parser should prevent this situation
+            throw new _MiscTemplateException(
+                    this, env,
+                    "There's no iteration in context that uses the nested 
content parameter ",
+                    new _DelayedJQuote( nestedContentParamName), ".");
+        }
+        
+        return calculateResult(iterCtx, env);
+    }
+
+    abstract TemplateModel calculateResult(IterationContext iterCtx, 
Environment env) throws TemplateException;
+    
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/589d9b80/freemarker-core/src/main/java/org/apache/freemarker/core/BuiltInsForLoopVariables.java
----------------------------------------------------------------------
diff --git 
a/freemarker-core/src/main/java/org/apache/freemarker/core/BuiltInsForLoopVariables.java
 
b/freemarker-core/src/main/java/org/apache/freemarker/core/BuiltInsForLoopVariables.java
deleted file mode 100644
index 8d55e0e..0000000
--- 
a/freemarker-core/src/main/java/org/apache/freemarker/core/BuiltInsForLoopVariables.java
+++ /dev/null
@@ -1,156 +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.List;
-
-import org.apache.freemarker.core.ASTDirList.IterationContext;
-import org.apache.freemarker.core.model.TemplateBooleanModel;
-import org.apache.freemarker.core.model.TemplateMethodModelEx;
-import org.apache.freemarker.core.model.TemplateModel;
-import org.apache.freemarker.core.model.TemplateModelException;
-import org.apache.freemarker.core.model.impl.SimpleNumber;
-import org.apache.freemarker.core.model.impl.SimpleScalar;
-
-
-class BuiltInsForLoopVariables {
-    
-    static class indexBI extends BuiltInForLoopVariable {
-
-        @Override
-        TemplateModel calculateResult(IterationContext iterCtx, Environment 
env) throws TemplateException {
-            return new SimpleNumber(iterCtx.getIndex());
-        }
-        
-    }
-    
-    static class counterBI extends BuiltInForLoopVariable {
-
-        @Override
-        TemplateModel calculateResult(IterationContext iterCtx, Environment 
env) throws TemplateException {
-            return new SimpleNumber(iterCtx.getIndex() + 1);
-        }
-        
-    }
-
-    static abstract class BooleanBuiltInForLoopVariable extends 
BuiltInForLoopVariable {
-
-        @Override
-        final TemplateModel calculateResult(IterationContext iterCtx, 
Environment env) throws TemplateException {
-            return calculateBooleanResult(iterCtx, env) ? 
TemplateBooleanModel.TRUE : TemplateBooleanModel.FALSE;
-        }
-
-        protected abstract boolean calculateBooleanResult(IterationContext 
iterCtx, Environment env);
-        
-    }
-    
-    static class has_nextBI extends BooleanBuiltInForLoopVariable {
-
-        @Override
-        protected boolean calculateBooleanResult(IterationContext iterCtx, 
Environment env) {
-            return iterCtx.hasNext();
-        }
-
-    }
-
-    static class is_lastBI extends BooleanBuiltInForLoopVariable {
-
-        @Override
-        protected boolean calculateBooleanResult(IterationContext iterCtx, 
Environment env) {
-            return !iterCtx.hasNext();
-        }
-        
-    }
-
-    static class is_firstBI extends BooleanBuiltInForLoopVariable {
-
-        @Override
-        protected boolean calculateBooleanResult(IterationContext iterCtx, 
Environment env) {
-            return iterCtx.getIndex() == 0;
-        }
-        
-    }
-
-    static class is_odd_itemBI extends BooleanBuiltInForLoopVariable {
-
-        @Override
-        protected boolean calculateBooleanResult(IterationContext iterCtx, 
Environment env) {
-            return iterCtx.getIndex() % 2 == 0;
-        }
-        
-    }
-
-    static class is_even_itemBI extends BooleanBuiltInForLoopVariable {
-
-        @Override
-        protected boolean calculateBooleanResult(IterationContext iterCtx, 
Environment env) {
-            return iterCtx.getIndex() % 2 != 0;
-        }
-        
-    }
-    
-    static class item_parityBI extends BuiltInForLoopVariable {
-        
-        private static final SimpleScalar ODD = new SimpleScalar("odd");
-        private static final SimpleScalar EVEN = new SimpleScalar("even");
-
-        @Override
-        TemplateModel calculateResult(IterationContext iterCtx, Environment 
env) throws TemplateException {
-            return iterCtx.getIndex() % 2 == 0 ? ODD: EVEN;
-        }
-        
-    }
-
-    static class item_parity_capBI extends BuiltInForLoopVariable {
-        
-        private static final SimpleScalar ODD = new SimpleScalar("Odd");
-        private static final SimpleScalar EVEN = new SimpleScalar("Even");
-
-        @Override
-        TemplateModel calculateResult(IterationContext iterCtx, Environment 
env) throws TemplateException {
-            return iterCtx.getIndex() % 2 == 0 ? ODD: EVEN;
-        }
-        
-    }
-
-    static class item_cycleBI extends BuiltInForLoopVariable {
-
-        private class BIMethod implements TemplateMethodModelEx {
-            
-            private final IterationContext iterCtx;
-    
-            private BIMethod(IterationContext iterCtx) {
-                this.iterCtx = iterCtx;
-            }
-    
-            @Override
-            public Object exec(List args) throws TemplateModelException {
-                checkMethodArgCount(args, 1, Integer.MAX_VALUE);
-                return args.get(iterCtx.getIndex() % args.size());
-            }
-        }
-        
-        @Override
-        TemplateModel calculateResult(IterationContext iterCtx, Environment 
env) throws TemplateException {
-            return new BIMethod(iterCtx);
-        }
-        
-    }
-    
-}

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/589d9b80/freemarker-core/src/main/java/org/apache/freemarker/core/BuiltInsForNestedContentParameters.java
----------------------------------------------------------------------
diff --git 
a/freemarker-core/src/main/java/org/apache/freemarker/core/BuiltInsForNestedContentParameters.java
 
b/freemarker-core/src/main/java/org/apache/freemarker/core/BuiltInsForNestedContentParameters.java
new file mode 100644
index 0000000..a4c4d8f
--- /dev/null
+++ 
b/freemarker-core/src/main/java/org/apache/freemarker/core/BuiltInsForNestedContentParameters.java
@@ -0,0 +1,156 @@
+/*
+ * 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.List;
+
+import org.apache.freemarker.core.ASTDirList.IterationContext;
+import org.apache.freemarker.core.model.TemplateBooleanModel;
+import org.apache.freemarker.core.model.TemplateMethodModelEx;
+import org.apache.freemarker.core.model.TemplateModel;
+import org.apache.freemarker.core.model.TemplateModelException;
+import org.apache.freemarker.core.model.impl.SimpleNumber;
+import org.apache.freemarker.core.model.impl.SimpleScalar;
+
+
+class BuiltInsForNestedContentParameters {
+    
+    static class indexBI extends BuiltInForNestedContentParameter {
+
+        @Override
+        TemplateModel calculateResult(IterationContext iterCtx, Environment 
env) throws TemplateException {
+            return new SimpleNumber(iterCtx.getIndex());
+        }
+        
+    }
+    
+    static class counterBI extends BuiltInForNestedContentParameter {
+
+        @Override
+        TemplateModel calculateResult(IterationContext iterCtx, Environment 
env) throws TemplateException {
+            return new SimpleNumber(iterCtx.getIndex() + 1);
+        }
+        
+    }
+
+    static abstract class BooleanBuiltInForNestedContentParameter extends 
BuiltInForNestedContentParameter {
+
+        @Override
+        final TemplateModel calculateResult(IterationContext iterCtx, 
Environment env) throws TemplateException {
+            return calculateBooleanResult(iterCtx, env) ? 
TemplateBooleanModel.TRUE : TemplateBooleanModel.FALSE;
+        }
+
+        protected abstract boolean calculateBooleanResult(IterationContext 
iterCtx, Environment env);
+        
+    }
+    
+    static class has_nextBI extends BooleanBuiltInForNestedContentParameter {
+
+        @Override
+        protected boolean calculateBooleanResult(IterationContext iterCtx, 
Environment env) {
+            return iterCtx.hasNext();
+        }
+
+    }
+
+    static class is_lastBI extends BooleanBuiltInForNestedContentParameter {
+
+        @Override
+        protected boolean calculateBooleanResult(IterationContext iterCtx, 
Environment env) {
+            return !iterCtx.hasNext();
+        }
+        
+    }
+
+    static class is_firstBI extends BooleanBuiltInForNestedContentParameter {
+
+        @Override
+        protected boolean calculateBooleanResult(IterationContext iterCtx, 
Environment env) {
+            return iterCtx.getIndex() == 0;
+        }
+        
+    }
+
+    static class is_odd_itemBI extends BooleanBuiltInForNestedContentParameter 
{
+
+        @Override
+        protected boolean calculateBooleanResult(IterationContext iterCtx, 
Environment env) {
+            return iterCtx.getIndex() % 2 == 0;
+        }
+        
+    }
+
+    static class is_even_itemBI extends 
BooleanBuiltInForNestedContentParameter {
+
+        @Override
+        protected boolean calculateBooleanResult(IterationContext iterCtx, 
Environment env) {
+            return iterCtx.getIndex() % 2 != 0;
+        }
+        
+    }
+    
+    static class item_parityBI extends BuiltInForNestedContentParameter {
+        
+        private static final SimpleScalar ODD = new SimpleScalar("odd");
+        private static final SimpleScalar EVEN = new SimpleScalar("even");
+
+        @Override
+        TemplateModel calculateResult(IterationContext iterCtx, Environment 
env) throws TemplateException {
+            return iterCtx.getIndex() % 2 == 0 ? ODD: EVEN;
+        }
+        
+    }
+
+    static class item_parity_capBI extends BuiltInForNestedContentParameter {
+        
+        private static final SimpleScalar ODD = new SimpleScalar("Odd");
+        private static final SimpleScalar EVEN = new SimpleScalar("Even");
+
+        @Override
+        TemplateModel calculateResult(IterationContext iterCtx, Environment 
env) throws TemplateException {
+            return iterCtx.getIndex() % 2 == 0 ? ODD: EVEN;
+        }
+        
+    }
+
+    static class item_cycleBI extends BuiltInForNestedContentParameter {
+
+        private class BIMethod implements TemplateMethodModelEx {
+            
+            private final IterationContext iterCtx;
+    
+            private BIMethod(IterationContext iterCtx) {
+                this.iterCtx = iterCtx;
+            }
+    
+            @Override
+            public Object exec(List args) throws TemplateModelException {
+                checkMethodArgCount(args, 1, Integer.MAX_VALUE);
+                return args.get(iterCtx.getIndex() % args.size());
+            }
+        }
+        
+        @Override
+        TemplateModel calculateResult(IterationContext iterCtx, Environment 
env) throws TemplateException {
+            return new BIMethod(iterCtx);
+        }
+        
+    }
+    
+}

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/589d9b80/freemarker-core/src/main/java/org/apache/freemarker/core/Environment.java
----------------------------------------------------------------------
diff --git 
a/freemarker-core/src/main/java/org/apache/freemarker/core/Environment.java 
b/freemarker-core/src/main/java/org/apache/freemarker/core/Environment.java
index ad6538d..af798b4 100644
--- a/freemarker-core/src/main/java/org/apache/freemarker/core/Environment.java
+++ b/freemarker-core/src/main/java/org/apache/freemarker/core/Environment.java
@@ -429,22 +429,22 @@ public final class Environment extends 
MutableProcessingConfiguration<Environmen
 
     void visit(
             ASTElement[] childBuffer,
-            final StringToIndexMap loopVarNames, final TemplateModel[] 
loopVarValues,
+            final StringToIndexMap nestedContentParamNames, final 
TemplateModel[] nestedContentParamValues,
             Writer out)
         throws IOException, TemplateException {
-        if (loopVarNames == null) { // This is by far the most frequent case
+        if (nestedContentParamNames == null) { // This is by far the most 
frequent case
             visit(childBuffer, out);
         } else {
             pushLocalContext(new LocalContext() {
                 @Override
                 public TemplateModel getLocalVariable(String name) throws 
TemplateModelException {
-                    int index = loopVarNames.get(name);
-                    return index != -1 ? loopVarValues[index] : null;
+                    int index = nestedContentParamNames.get(name);
+                    return index != -1 ? nestedContentParamValues[index] : 
null;
                 }
 
                 @Override
                 public Collection<String> getLocalVariableNames() throws 
TemplateModelException {
-                    return loopVarNames.getKeys();
+                    return nestedContentParamNames.getKeys();
                 }
             });
             try {
@@ -1955,10 +1955,11 @@ public final class Environment extends 
MutableProcessingConfiguration<Environmen
     }
 
     /**
-     * Returns the loop or macro local variable corresponding to this variable 
name. Possibly null. (Note that the
-     * misnomer is kept for backward compatibility: loop variables are not 
local variables according to our
-     * terminology.)
+     * Returns the loop or macro nested content parameter variables 
corresponding to this variable name. Possibly null.
+     * (Note that the misnomer is kept for backward compatibility: nested 
content parameters are not local variables
+     * according to our terminology.)
      */
+    // TODO [FM3] Don't return nested content params anymore (see JavaDoc)? 
But, LocalContext does return them...
     public TemplateModel getLocalVariable(String name) throws 
TemplateModelException {
         if (localContextStack != null) {
             for (int i = localContextStack.size() - 1; i >= 0; i--) {
@@ -1977,7 +1978,7 @@ public final class Environment extends 
MutableProcessingConfiguration<Environmen
      * correspondent to an FTL top-level variable reading expression. That is, 
it tries to find the the variable in this
      * order:
      * <ol>
-     * <li>An loop variable (if we're in a loop or user defined directive 
body) such as foo_has_next
+     * <li>A nested content parameter such as {@code x} in {@code <#list xs as 
x>}
      * <li>A local variable (if we're in a macro)
      * <li>A variable defined in the current namespace (say, via &lt;#assign 
...&gt;)
      * <li>A variable defined globally (say, via &lt;#global ....&gt;)
@@ -2055,11 +2056,11 @@ public final class Environment extends 
MutableProcessingConfiguration<Environmen
     /**
      * Returns a set of variable names that are known at the time of call. 
This includes names of all shared variables
      * in the {@link Configuration}, names of all global variables that were 
assigned during the template processing,
-     * names of all variables in the current name-space, names of all local 
variables and loop variables. If the passed
-     * root data model implements the {@link TemplateHashModelEx} interface, 
then all names it retrieves through a call
-     * to {@link TemplateHashModelEx#keys()} method are returned as well. The 
method returns a new Set object on each
-     * call that is completely disconnected from the Environment. That is, 
modifying the set will have no effect on the
-     * Environment object.
+     * names of all variables in the current name-space, names of all local 
variables and nested content parameters. If
+     * the passed root data model implements the {@link TemplateHashModelEx} 
interface, then all names it retrieves
+     * through a call to {@link TemplateHashModelEx#keys()} method are 
returned as well. The method returns a new Set
+     * object on each call that is completely disconnected from the 
Environment. That is, modifying the set will have no
+     * effect on the Environment object.
      */
     public Set getKnownVariableNames() throws TemplateModelException {
         // shared vars.

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/589d9b80/freemarker-core/src/main/java/org/apache/freemarker/core/ParameterRole.java
----------------------------------------------------------------------
diff --git 
a/freemarker-core/src/main/java/org/apache/freemarker/core/ParameterRole.java 
b/freemarker-core/src/main/java/org/apache/freemarker/core/ParameterRole.java
index 146f0b8..ccae7b7 100644
--- 
a/freemarker-core/src/main/java/org/apache/freemarker/core/ParameterRole.java
+++ 
b/freemarker-core/src/main/java/org/apache/freemarker/core/ParameterRole.java
@@ -50,7 +50,7 @@ final class ParameterRole {
     static final ParameterRole PLACEHOLDER_VARIABLE = new 
ParameterRole("placeholder variable");
     static final ParameterRole EXPRESSION_TEMPLATE = new 
ParameterRole("expression template");
     static final ParameterRole LIST_SOURCE = new ParameterRole("list source");
-    static final ParameterRole TARGET_LOOP_VARIABLE = new 
ParameterRole("target loop variable");
+    static final ParameterRole NESTED_CONTENT_PARAMETER = new 
ParameterRole("nested content parameter");
     static final ParameterRole TEMPLATE_NAME = new ParameterRole("template 
name");
     static final ParameterRole IGNORE_MISSING_PARAMETER = new 
ParameterRole("\"ignore_missing\" parameter");
     static final ParameterRole PARAMETER_NAME = new ParameterRole("parameter 
name");

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/589d9b80/freemarker-core/src/main/java/org/apache/freemarker/core/model/CallPlace.java
----------------------------------------------------------------------
diff --git 
a/freemarker-core/src/main/java/org/apache/freemarker/core/model/CallPlace.java 
b/freemarker-core/src/main/java/org/apache/freemarker/core/model/CallPlace.java
index 7761eeb..749c313 100644
--- 
a/freemarker-core/src/main/java/org/apache/freemarker/core/model/CallPlace.java
+++ 
b/freemarker-core/src/main/java/org/apache/freemarker/core/model/CallPlace.java
@@ -47,29 +47,27 @@ public interface CallPlace {
     boolean hasNestedContent();
 
     /**
-     * The number of loop variables specified for this call. Note that users 
are generally allowed to omit loop
-     * variables when calling a directive. So this can be useful to avoid 
calculating the values of loop variables that
-     * the caller will not receive anyway. While it's an error if the user 
specifies too many loop variables, the
-     * implementor of the {@link TemplateDirectiveModel} shouldn't check for 
that condition, as far as they will call
-     * {@link #executeNestedContent(TemplateModel[], Writer, Environment)}, 
which will the should throw a {@link
-     * TemplateException} with a descriptive error message then.
+     * The number of nested content parameters in this call (like 2 in {@code 
<@foo xs; k, v>...</@>}). If you want the
+     * caller to specify a predefined number of nested content parameters, 
then this is not interesting for you, and
+     * just pass an array of that length to {@link 
#executeNestedContent(TemplateModel[], Writer, Environment)}. If,
+     * however, you want to allow the caller to declare less parameters, then 
this is how you know how much
+     * parameters you should omit when calling {@link 
#executeNestedContent(TemplateModel[], Writer, Environment)}.
      */
-    int getLoopVariableCount();
+    int getNestedContentParameterCount();
 
     /**
      * Executed the nested content; it there's none, it just does nothing.
      *
-     * @param loopVarValues
-     *         The loop variable values to pass to the nested content, or 
{@code null} if there's none. It's not a
-     *         problem if this array is longer than the number of loop 
variables than the caller of the directive has
-     *         declared (as in {@code <@foo bar; i, j />} there are 2 loop 
variables declared); the directive
-     *         caller simply won't receive the excess variables. If the caller 
declares more loop variables than the
-     *         length of this array though, then a {@link TemplateException} 
will thrown by FreeMarker with a
-     *         descriptive error message. Thus, the caller of this method need 
not be concerned about the
-     *         number of loop variables declared by the caller (unless to 
avoid calculating loop variable values
-     *         unnecessarily, in which case use {@link 
#getLoopVariableCount()}).
+     * @param nestedContentParamValues
+     *         The nested content parameter values to pass to the nested 
content (as in {@code <@foo bar; i, j>${i},
+     *         ${j}</@foo>} there are 2 such parameters, whose value you set 
here), or {@code null} if there's none.
+     *         This array must be {@link #getNestedContentParameterCount()} 
long, or else {@link TemplateException} will thrown by
+     *         FreeMarker with a descriptive error message that tells to user 
the they need to declare that many nested
+     *         content parameters as the length of this array. If you want to 
allow the the caller to not declare some
+     *         of nested content parameters, then you have to make this array 
shorter according to {@link
+     *         #getNestedContentParameterCount()}.
      */
-    void executeNestedContent(TemplateModel[] loopVarValues, Writer out, 
Environment env)
+    void executeNestedContent(TemplateModel[] nestedContentParamValues, Writer 
out, Environment env)
             throws TemplateException, IOException;
 
     // 
-------------------------------------------------------------------------------------------------------------


Reply via email to