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/09/01 18:58:24 UTC
[5/6] incubator-freemarker git commit: Reworked list/iterable-like
TemplateModel interfaced. Now we have TemplateIterableModel (which is like
TemplateCollectionModel in FM2),
TemplateCollectionModel (which similar to TemplateCollectionModelEx in FM2)
tha
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/cae86e18/freemarker-core-test/src/test/resources/org/apache/freemarker/core/templatesuite/templates/range.ftl
----------------------------------------------------------------------
diff --git a/freemarker-core-test/src/test/resources/org/apache/freemarker/core/templatesuite/templates/range.ftl b/freemarker-core-test/src/test/resources/org/apache/freemarker/core/templatesuite/templates/range.ftl
index 18c035d..738dc76 100644
--- a/freemarker-core-test/src/test/resources/org/apache/freemarker/core/templatesuite/templates/range.ftl
+++ b/freemarker-core-test/src/test/resources/org/apache/freemarker/core/templatesuite/templates/range.ftl
@@ -16,7 +16,308 @@
specific language governing permissions and limitations
under the License.
-->
-<#include 'range-common.ftl'>
+<#-- A version of "?join" that fails at null-s in the sequence: -->
+<#function join(seq, sep='')>
+ <#local r = "">
+ <#list seq as i>
+ <#local r = r + i>
+ <#if i_has_next>
+ <#local r = r + sep>
+ </#if>
+ </#list>
+ <#return r>
+</#function>
+
+<#----------------------->
+<#-- Range expressions -->
+
+<@assertEquals actual=join(1..2, ' ') expected="1 2" />
+<@assertEquals actual=join(1..1, ' ') expected="1" />
+<@assertEquals actual=join(1..0, ' ') expected="1 0" />
+<@assertEquals actual=join(1..-1, ' ') expected="1 0 -1" />
+<@assertEquals actual=join(-1..-1, ' ') expected="-1" />
+<@assertEquals actual=join(-1..1, ' ') expected="-1 0 1" />
+
+<@assertEquals actual=join(1..<3, ' ') expected="1 2" />
+<@assertEquals actual=join(1..<2, ' ') expected="1" />
+<@assertEquals actual=join(1..<1, ' ') expected="" />
+<@assertEquals actual=join(1..<0, ' ') expected="1" />
+<@assertEquals actual=join(1..<-1, ' ') expected="1 0" />
+<@assertEquals actual=join(1..<-2, ' ') expected="1 0 -1" />
+<@assertEquals actual=join(-1..<0, ' ') expected="-1" />
+<@assertEquals actual=join(-1..<2, ' ') expected="-1 0 1" />
+
+<@assertEquals actual=join(1..!3, ' ') expected="1 2" />
+<@assertEquals actual=join(1..!2, ' ') expected="1" />
+<@assertEquals actual=join(1..!1, ' ') expected="" />
+<@assertEquals actual=join(1..!0, ' ') expected="1" />
+<@assertEquals actual=join(1..!-1, ' ') expected="1 0" />
+<@assertEquals actual=join(1..!-2, ' ') expected="1 0 -1" />
+<@assertEquals actual=join(-1..!0, ' ') expected="-1" />
+<@assertEquals actual=join(-1..!2, ' ') expected="-1 0 1" />
+
+<@assertEquals actual=join(1..*2, ' ') expected="1 2" />
+<@assertEquals actual=join(1..*1, ' ') expected="1" />
+<@assertEquals actual=join(1..*0, ' ') expected="" />
+<@assertEquals actual=join(1..*-1, ' ') expected="1" />
+<@assertEquals actual=join(1..*-2, ' ') expected="1 0" />
+<@assertEquals actual=join(1..*-3, ' ') expected="1 0 -1" />
+<@assertEquals actual=join(-1..*1, ' ') expected="-1" />
+<@assertEquals actual=join(-1..*3, ' ') expected="-1 0 1" />
+
+<@assertEquals actual=1 expected=(0..0)?size />
+<@assertEquals actual=1 expected=(1..1)?size />
+<@assertEquals actual=1 expected=(2..2)?size />
+<@assertEquals actual=2 expected=(0..1)?size />
+<@assertEquals actual=2 expected=(1..2)?size />
+<@assertEquals actual=2 expected=(2..3)?size />
+<@assertEquals actual=3 expected=(2..4)?size />
+<@assertEquals actual=2 expected=(1..0)?size />
+<@assertEquals actual=2 expected=(2..1)?size />
+<@assertEquals actual=2 expected=(3..2)?size />
+<@assertEquals actual=3 expected=(4..2)?size />
+
+<@assertEquals actual=0 expected=(0..<0)?size />
+<@assertEquals actual=0 expected=(1..<1)?size />
+<@assertEquals actual=0 expected=(2..<2)?size />
+<@assertEquals actual=1 expected=(0..<1)?size />
+<@assertEquals actual=1 expected=(1..<2)?size />
+<@assertEquals actual=1 expected=(2..<3)?size />
+<@assertEquals actual=2 expected=(2..<4)?size />
+<@assertEquals actual=1 expected=(1..<0)?size />
+<@assertEquals actual=1 expected=(2..<1)?size />
+<@assertEquals actual=1 expected=(3..<2)?size />
+<@assertEquals actual=2 expected=(4..<2)?size />
+
+<@assertEquals actual=0 expected=(0..*0)?size />
+<@assertEquals actual=0 expected=(1..*0)?size />
+<@assertEquals actual=0 expected=(2..*0)?size />
+<@assertEquals actual=1 expected=(0..*1)?size />
+<@assertEquals actual=1 expected=(1..*1)?size />
+<@assertEquals actual=1 expected=(2..*1)?size />
+<@assertEquals actual=2 expected=(2..*2)?size />
+<@assertEquals actual=1 expected=(0..*-1)?size />
+<@assertEquals actual=1 expected=(1..*-1)?size />
+<@assertEquals actual=1 expected=(2..*-1)?size />
+<@assertEquals actual=2 expected=(0..*-2)?size />
+<@assertEquals actual=2 expected=(1..*-2)?size />
+<@assertEquals actual=2 expected=(2..*-2)?size />
+
+
+<#--------------------->
+<#-- String slicing: -->
+
+<#assign s = 'abcd'>
+
+<@assertEquals actual=s[0..] expected="abcd" />
+<@assertEquals actual=s[1..] expected="bcd" />
+<@assertEquals actual=s[2..] expected="cd" />
+<@assertEquals actual=s[3..] expected="d" />
+<@assertEquals actual=s[4..] expected="" />
+<@assertFails message="5 is out of bounds">
+ <#assign _ = s[5..] />
+</...@assertFails>
+<@assertFails message="6 is out of bounds">
+ <#assign _ = s[6..] />
+</...@assertFails>
+
+<@assertEquals actual=s[1..2] expected="bc" />
+<@assertEquals actual=s[1..1] expected="b" />
+<@assertEquals actual=s[0..1] expected="ab" />
+<@assertEquals actual=s[0..0] expected="a" />
+<@assertFails message="4 is out of bounds">
+ <#assign _ = s[1..4] />
+</...@assertFails>
+<@assertFails message="5 is out of bounds">
+ <#assign _ = s[1..5] />
+</...@assertFails>
+<@assertFails message="negative">
+ <#assign _ = s[-1..1] />
+</...@assertFails>
+<@assertFails message="negative">
+ <#assign _ = s[-2..1] />
+</...@assertFails>
+<@assertFails message="negative">
+ <#assign _ = s[0..-1] />
+</...@assertFails>
+
+<@assertEquals actual=s[1..<3] expected="bc" />
+<@assertEquals actual=s[1..!3] expected="bc" />
+<@assertEquals actual=s[1..<2] expected="b" />
+<@assertEquals actual=s[1..<0] expected="b" />
+<@assertEquals actual=s[1..<1] expected="" />
+<@assertEquals actual=s[0..<0] expected="" />
+<@assertEquals actual=s[5..<5] expected="" />
+<@assertEquals actual=s[6..<6] expected="" />
+<@assertEquals actual=s[-5..<-5] expected="" />
+<@assertFails message="negative">
+ <#assign _ = s[-5..<1] />
+</...@assertFails>
+<@assertFails message="negative">
+ <#assign _ = s[2..<-4] />
+</...@assertFails>
+<@assertFails message="decreasing">
+ <#assign _ = s[2..<0] />
+</...@assertFails>
+
+<@assertEquals actual=s[1..*-1] expected="b" />
+<@assertEquals actual=s[1..*0] expected="" />
+<@assertEquals actual=s[1..*1] expected="b" />
+<@assertEquals actual=s[1..*2] expected="bc" />
+<@assertEquals actual=s[1..*3] expected="bcd" />
+<@assertEquals actual=s[1..*4] expected="bcd" />
+<@assertEquals actual=s[1..*5] expected="bcd" />
+<@assertEquals actual=s[4..*1] expected="" />
+<@assertEquals actual=s[5..*0] expected="" />
+<@assertEquals actual=s[6..*0] expected="" />
+<@assertEquals actual=s[-5..*0] expected="" />
+<@assertEquals actual=s[0..*0] expected="" />
+<@assertEquals actual=s[0..*-1] expected="a" />
+<@assertEquals actual=s[0..*-2] expected="a" />
+<@assertEquals actual=s[0..*-3] expected="a" />
+<@assertFails message="5 is out of bounds">
+ <#assign _ = s[5..*1] />
+</...@assertFails>
+<@assertFails message="negative">
+ <#assign _ = s[-1..*1] />
+</...@assertFails>
+<@assertFails message="negative">
+ <#assign _ = s[-2..*1] />
+</...@assertFails>
+<@assertFails message="decreasing">
+ <#assign _ = s[1..*-2] />
+</...@assertFails>
+<@assertFails message="decreasing">
+ <#assign _ = s[1..*-3] />
+</...@assertFails>
+<@assertFails message="4 is out of bounds">
+ <#assign _ = s[4..*-1] />
+</...@assertFails>
+
+<#-- FreeMarker 2 string backward-range bug not supported anymore: -->
+<@assertFails message="decreasing">
+ <#assign _ = s[1..0] />
+</...@assertFails>
+<@assertFails message="decreasing">
+ <#assign _ = s[2..1] />
+</...@assertFails>
+<@assertFails message="negative">
+ <#assign _ = s[0..-1] />
+</...@assertFails>
+<@assertFails message="decreasing">
+ <#assign _ = s[3..1] />
+</...@assertFails>
+<#-- The bug was never emulated for operators introduced after 2.3.20: -->
+<@assertFails message="decreasing">
+ <#assign _ = s[3..<1] />
+</...@assertFails>
+<@assertFails message="decreasing">
+ <#assign _ = s[3..*-2] />
+</...@assertFails>
+
+<#assign r = 1..2>
+<@assertEquals actual=s[r] expected="bc" />
+<#assign r = 2..1>
+<@assertFails message="decreasing">
+ <#assign _ = s[r] />
+</...@assertFails>
+<#assign r = 1..<2>
+<@assertEquals actual=s[r] expected="b" />
+<#assign r = 2..<4>
+<@assertEquals actual=s[r] expected="cd" />
+<#assign r = 2..>
+<@assertEquals actual=s[r] expected="cd" />
+<#assign r = 1..*2>
+<@assertEquals actual=s[r] expected="bc" />
+
+<#----------------------->
+<#-- Sequence slicing: -->
+
+<#assign s = ['a', 'b', 'c', 'd']>
+
+<@assertEquals actual=join(s[0..]) expected="abcd" />
+<@assertEquals actual=join(s[1..]) expected="bcd" />
+<@assertEquals actual=join(s[2..]) expected="cd" />
+<@assertEquals actual=join(s[3..]) expected="d" />
+<@assertEquals actual=join(s[4..]) expected="" />
+<@assertFails message="5 is out of bounds">
+ <#assign _ = s[5..] />
+</...@assertFails>
+<@assertFails message="negative">
+ <#assign _ = s[-1..] />
+</...@assertFails>
+
+<@assertEquals actual=join(s[1..2]) expected="bc" />
+<@assertEquals actual=join(s[1..1]) expected="b" />
+<@assertEquals actual=join(s[0..1]) expected="ab" />
+<@assertEquals actual=join(s[0..0]) expected="a" />
+<@assertFails message="5 is out of bounds">
+ <#assign _ = s[1..5] />
+</...@assertFails>
+<@assertFails message="negative">
+ <#assign _ = s[-1..0] />
+</...@assertFails>
+<@assertFails message="negative">
+ <#assign _ = s[0..-1] />
+</...@assertFails>
+
+<@assertEquals actual=join(s[1..<3]) expected="bc" />
+<@assertEquals actual=join(s[1..!3]) expected="bc" />
+<@assertEquals actual=join(s[1..<2]) expected="b" />
+<@assertEquals actual=join(s[1..<0]) expected="b" />
+<@assertEquals actual=join(s[1..<1]) expected="" />
+<@assertEquals actual=join(s[0..<0]) expected="" />
+
+<@assertEquals actual=join(s[1..0]) expected="ba" />
+<@assertEquals actual=join(s[2..1]) expected="cb" />
+<@assertEquals actual=join(s[2..0]) expected="cba" />
+<@assertEquals actual=join(s[2..<0]) expected="cb" />
+<@assertEquals actual=join(s[1..<0]) expected="b" />
+<@assertEquals actual=join(s[0..<0]) expected="" />
+<@assertEquals actual=join(s[3..<1]) expected="dc" />
+<@assertEquals actual=join(s[2..<1]) expected="c" />
+<@assertEquals actual=join(s[1..<1]) expected="" />
+<@assertEquals actual=join(s[0..<1]) expected="a" />
+<@assertEquals actual=join(s[0..<0]) expected="" />
+<@assertEquals actual=join(s[5..<5]) expected="" />
+<@assertEquals actual=join(s[-5..<-5]) expected="" />
+
+<@assertEquals actual=join(s[0..*-4]) expected="a" />
+<@assertEquals actual=join(s[1..*-4]) expected="ba" />
+<@assertEquals actual=join(s[1..*-3]) expected="ba" />
+<@assertEquals actual=join(s[1..*-2]) expected="ba" />
+<@assertEquals actual=join(s[1..*-1]) expected="b" />
+<@assertEquals actual=join(s[1..*0]) expected="" />
+<@assertEquals actual=join(s[1..*1]) expected="b" />
+<@assertEquals actual=join(s[1..*2]) expected="bc" />
+<@assertEquals actual=join(s[1..*3]) expected="bcd" />
+<@assertEquals actual=join(s[1..*4]) expected="bcd" />
+<@assertEquals actual=join(s[1..*5]) expected="bcd" />
+<@assertEquals actual=join(s[0..*3]) expected="abc" />
+<@assertEquals actual=join(s[2..*3]) expected="cd" />
+<@assertEquals actual=join(s[3..*3]) expected="d" />
+<@assertEquals actual=join(s[4..*3]) expected="" />
+<@assertFails message="5 is out of bounds">
+ <#assign _ = s[5..*3] />
+</...@assertFails>
+<@assertFails message="negative">
+ <#assign _ = s[-1..*2] />
+</...@assertFails>
+
+<#assign r = 1..2>
+<@assertEquals actual=join(s[r]) expected="bc" />
+<#assign r = 2..0>
+<@assertEquals actual=join(s[r]) expected="cba" />
+<#assign r = 1..<2>
+<@assertEquals actual=join(s[r]) expected="b" />
+<#assign r = 2..<0>
+<@assertEquals actual=join(s[r]) expected="cb" />
+<#assign r = 2..>
+<@assertEquals actual=join(s[r]) expected="cd" />
+<#assign r = 1..*2>
+<@assertEquals actual=join(s[r]) expected="bc" />
+<#assign r = 1..*-9>
+<@assertEquals actual=join(s[r]) expected="ba" />
<@assertEquals actual=(4..)?size expected=2147483647 />
<@assertEquals actual=limitedJoin(4.., 3) expected="4, 5, 6, ..." />
@@ -24,9 +325,8 @@
<@assertEquals actual=(4..)[0] expected=4 />
<@assertEquals actual=(4..)[1] expected=5 />
<@assertEquals actual=(4..)[1000000] expected=1000004 />
-<@assertFails message="out of bounds">
- <@assertEquals actual=(4..)[-1] expected=5 />
-</@>
+<@assert !(4..)[-1]?? />
+<@assert !(1..2)[2]?? />
<#assign r = 2147483646..>
<@assertEquals actual=r?size expected=2147483647 />
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/cae86e18/freemarker-core-test/src/test/resources/org/apache/freemarker/core/templatesuite/templates/type-builtins.ftl
----------------------------------------------------------------------
diff --git a/freemarker-core-test/src/test/resources/org/apache/freemarker/core/templatesuite/templates/type-builtins.ftl b/freemarker-core-test/src/test/resources/org/apache/freemarker/core/templatesuite/templates/type-builtins.ftl
index 3442824..489bd2f 100644
--- a/freemarker-core-test/src/test/resources/org/apache/freemarker/core/templatesuite/templates/type-builtins.ftl
+++ b/freemarker-core-test/src/test/resources/org/apache/freemarker/core/templatesuite/templates/type-builtins.ftl
@@ -17,12 +17,12 @@
under the License.
-->
<#setting booleanFormat="1,0">
-StNuBoHaHxSeCoCxEnInFuDiNo
+StNuBoHaHxItCoSeFuDiNo
<#list [
"a", 1, false,
- testfunction, testmacro,
- {"a":1}, [1], testcollection, testcollectionEx,
- testnode,
+ testFunction, testMacro,
+ {"a":1}, [1], testIterable, testCollection,
+ testNode,
bean, bean.m, bean.mOverloaded
] as x>
${x?isString} <#t>
@@ -30,13 +30,11 @@ StNuBoHaHxSeCoCxEnInFuDiNo
${x?isBoolean} <#t>
${x?isHash} <#t>
${x?isHashEx} <#t>
- ${x?isSequence} <#t>
+ ${x?isIterable} <#t>
${x?isCollection} <#t>
- ${x?isCollectionEx} <#t>
- ${x?isEnumerable} <#t>
- ${x?isIndexable} <#t>
+ ${x?isSequence} <#t>
${x?isFunction} <#t>
${x?isDirective} <#t>
${x?isNode}<#lt>
</#list>
-<#macro testmacro></#macro>
\ No newline at end of file
+<#macro testMacro></#macro>
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/cae86e18/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 b795ca1..1d0927f 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
@@ -25,15 +25,14 @@ import java.util.Collection;
import java.util.Collections;
import org.apache.freemarker.core.model.TemplateBooleanModel;
-import org.apache.freemarker.core.model.TemplateCollectionModel;
import org.apache.freemarker.core.model.TemplateHashModelEx;
import org.apache.freemarker.core.model.TemplateHashModelEx2;
import org.apache.freemarker.core.model.TemplateHashModelEx2.KeyValuePair;
import org.apache.freemarker.core.model.TemplateHashModelEx2.KeyValuePairIterator;
+import org.apache.freemarker.core.model.TemplateIterableModel;
import org.apache.freemarker.core.model.TemplateModel;
import org.apache.freemarker.core.model.TemplateModelIterator;
import org.apache.freemarker.core.model.TemplateStringModel;
-import org.apache.freemarker.core.model.TemplateSequenceModel;
import org.apache.freemarker.core.model.impl.SimpleNumber;
import org.apache.freemarker.core.util._StringUtils;
@@ -50,7 +49,7 @@ final class ASTDirList extends ASTDirective {
/**
* @param listedExp
- * a variable referring to a sequence or collection or extended hash to list
+ * a variable referring to an iterable or extended hash that we want to list
* @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
@@ -252,15 +251,15 @@ final class ASTDirList extends ASTDirective {
private boolean executeNestedContent(Environment env, ASTElement[] childBuffer)
throws TemplateException, IOException {
return !hashListing
- ? executedNestedContentForCollOrSeqListing(env, childBuffer)
+ ? executedNestedContentForIterableListing(env, childBuffer)
: executedNestedContentForHashListing(env, childBuffer);
}
- private boolean executedNestedContentForCollOrSeqListing(Environment env, ASTElement[] childBuffer)
+ private boolean executedNestedContentForIterableListing(Environment env, ASTElement[] childBuffer)
throws IOException, TemplateException {
final boolean listNotEmpty;
- if (listedValue instanceof TemplateCollectionModel) {
- final TemplateCollectionModel collModel = (TemplateCollectionModel) listedValue;
+ if (listedValue instanceof TemplateIterableModel) {
+ final TemplateIterableModel collModel = (TemplateIterableModel) listedValue;
final TemplateModelIterator iterModel
= openedIterator == null ? collModel.iterator()
: ((TemplateModelIterator) openedIterator);
@@ -279,31 +278,12 @@ final class ASTDirList extends ASTDirective {
}
openedIterator = null;
} else {
- // We must reuse this later, because TemplateCollectionModel-s that wrap an Iterator only
+ // We must reuse this later, because TemplateIterableModel-s that wrap an Iterator only
// allow one iterator() call.
openedIterator = iterModel;
env.visit(childBuffer);
}
}
- } else if (listedValue instanceof TemplateSequenceModel) {
- final TemplateSequenceModel seqModel = (TemplateSequenceModel) listedValue;
- final int size = seqModel.size();
- listNotEmpty = size != 0;
- if (listNotEmpty) {
- if (nestedContentParam1Name != null) {
- try {
- for (index = 0; index < size; index++) {
- nestedContentParam = seqModel.get(index);
- hasNext = (size > index + 1);
- env.visit(childBuffer);
- }
- } catch (ASTDirBreak.Break br) {
- // Silently exit loop
- }
- } else {
- env.visit(childBuffer);
- }
- }
} else if (listedValue instanceof TemplateHashModelEx) {
throw new TemplateException(env,
new _ErrorDescriptionBuilder("The value you try to list is ",
@@ -314,8 +294,8 @@ final class ASTDirList extends ASTDirective {
} else {
throw MessageUtils.newUnexpectedOperandTypeException(
listedExp, listedValue,
- MessageUtils.SEQUENCE_OR_COLLECTION,
- MessageUtils.EXPECTED_TYPES_SEQUENCE_OR_COLLECTION,
+ MessageUtils.EXPECTED_TYPE_ITERABLE_DESC,
+ TemplateIterableModel.class,
null, env);
}
return listNotEmpty;
@@ -390,8 +370,7 @@ final class ASTDirList extends ASTDirective {
}
}
}
- } else if (listedValue instanceof TemplateCollectionModel
- || listedValue instanceof TemplateSequenceModel) {
+ } else if (listedValue instanceof TemplateIterableModel) {
throw new TemplateException(env,
new _ErrorDescriptionBuilder("The value you try to list is ",
new _DelayedAOrAn(new _DelayedTemplateLanguageTypeDescription(listedValue)),
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/cae86e18/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
index 645aff4..786bfcf 100644
--- a/freemarker-core/src/main/java/org/apache/freemarker/core/ASTDirMacroOrFunction.java
+++ b/freemarker-core/src/main/java/org/apache/freemarker/core/ASTDirMacroOrFunction.java
@@ -26,6 +26,7 @@ import java.util.List;
import org.apache.freemarker.core.model.ArgumentArrayLayout;
import org.apache.freemarker.core.model.TemplateModel;
import org.apache.freemarker.core.model.TemplateModelIterator;
+import org.apache.freemarker.core.model.TemplateStringModel;
import org.apache.freemarker.core.util.StringToIndexMap;
import org.apache.freemarker.core.util._StringUtils;
@@ -290,7 +291,7 @@ final class ASTDirMacroOrFunction extends ASTDirective implements TemplateModel
public Collection<String> getLocalVariableNames() throws TemplateException {
HashSet<String> result = new HashSet<>();
for (TemplateModelIterator it = localVars.keys().iterator(); it.hasNext(); ) {
- result.add(it.next().toString());
+ result.add(((TemplateStringModel) it.next()).getAsString());
}
return result;
}
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/cae86e18/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 a849aaf..25344e6 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
@@ -45,10 +45,10 @@ final class ASTDirNested extends ASTDirective {
ASTElement[] accept(Environment env) throws IOException, TemplateException {
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
+ // When nestedContParamCnt < nestedContentParameters.getCollectionSize(), 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.getCollectionSize(), 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;
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/cae86e18/freemarker-core/src/main/java/org/apache/freemarker/core/ASTExpAddOrConcat.java
----------------------------------------------------------------------
diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/ASTExpAddOrConcat.java b/freemarker-core/src/main/java/org/apache/freemarker/core/ASTExpAddOrConcat.java
index a076d7e..5fa8eac 100644
--- a/freemarker-core/src/main/java/org/apache/freemarker/core/ASTExpAddOrConcat.java
+++ b/freemarker-core/src/main/java/org/apache/freemarker/core/ASTExpAddOrConcat.java
@@ -23,16 +23,15 @@ import java.util.HashSet;
import java.util.Set;
import org.apache.freemarker.core.arithmetic.ArithmeticEngine;
-import org.apache.freemarker.core.model.TemplateCollectionModel;
import org.apache.freemarker.core.model.TemplateHashModel;
import org.apache.freemarker.core.model.TemplateHashModelEx;
+import org.apache.freemarker.core.model.TemplateIterableModel;
import org.apache.freemarker.core.model.TemplateMarkupOutputModel;
import org.apache.freemarker.core.model.TemplateModel;
import org.apache.freemarker.core.model.TemplateModelIterator;
import org.apache.freemarker.core.model.TemplateNumberModel;
-import org.apache.freemarker.core.model.TemplateStringModel;
import org.apache.freemarker.core.model.TemplateSequenceModel;
-import org.apache.freemarker.core.model.impl.CollectionAndSequence;
+import org.apache.freemarker.core.model.TemplateStringModel;
import org.apache.freemarker.core.model.impl.SimpleNumber;
import org.apache.freemarker.core.model.impl.SimpleString;
@@ -122,9 +121,9 @@ final class ASTExpAddOrConcat extends ASTExpression {
if (leftModel instanceof TemplateHashModelEx && rightModel instanceof TemplateHashModelEx) {
TemplateHashModelEx leftModelEx = (TemplateHashModelEx) leftModel;
TemplateHashModelEx rightModelEx = (TemplateHashModelEx) rightModel;
- if (leftModelEx.size() == 0) {
+ if (leftModelEx.getHashSize() == 0) {
return rightModelEx;
- } else if (rightModelEx.size() == 0) {
+ } else if (rightModelEx.getHashSize() == 0) {
return leftModelEx;
} else {
return new ConcatenatedHashEx(leftModelEx, rightModelEx);
@@ -179,9 +178,7 @@ final class ASTExpAddOrConcat extends ASTExpression {
return ParameterRole.forBinaryOperatorOperand(idx);
}
- private static final class ConcatenatedSequence
- implements
- TemplateSequenceModel {
+ private static final class ConcatenatedSequence implements TemplateSequenceModel {
private final TemplateSequenceModel left;
private final TemplateSequenceModel right;
@@ -191,21 +188,63 @@ final class ASTExpAddOrConcat extends ASTExpression {
}
@Override
- public int size()
+ public int getCollectionSize()
throws TemplateException {
- return left.size() + right.size();
+ return left.getCollectionSize() + right.getCollectionSize();
}
@Override
- public TemplateModel get(int i)
- throws TemplateException {
- int ls = left.size();
- return i < ls ? left.get(i) : right.get(i - ls);
+ public boolean isEmptyCollection() throws TemplateException {
+ return left.isEmptyCollection() && right.isEmptyCollection();
+ }
+
+ @Override
+ public TemplateModel get(int i) throws TemplateException {
+ int leftSize = left.getCollectionSize();
+ return i < leftSize ? left.get(i) : right.get(i - leftSize);
+ }
+
+ @Override
+ public TemplateModelIterator iterator() throws TemplateException {
+ return new ConcatenatedTemplateModelIterator(left.iterator(), right.iterator());
}
}
- private static class ConcatenatedHash
- implements TemplateHashModel {
+ private static class ConcatenatedTemplateModelIterator implements TemplateModelIterator {
+ private final TemplateModelIterator left;
+ private final TemplateModelIterator right;
+ private boolean leftExhausted;
+
+ ConcatenatedTemplateModelIterator(TemplateModelIterator left, TemplateModelIterator right) {
+ this.left = left;
+ this.right = right;
+ }
+
+ @Override
+ public TemplateModel next() throws TemplateException {
+ if (!leftExhausted) {
+ if (left.hasNext()) {
+ return left.next();
+ }
+ leftExhausted = true;
+ }
+ return right.next();
+ }
+
+ @Override
+ public boolean hasNext() throws TemplateException {
+ if (!leftExhausted) {
+ if (left.hasNext()) {
+ return true;
+ }
+ leftExhausted = true;
+ }
+ // At this point leftExhausted is true.
+ return right.hasNext();
+ }
+ }
+
+ private static class ConcatenatedHash implements TemplateHashModel {
protected final TemplateHashModel left;
protected final TemplateHashModel right;
@@ -222,17 +261,16 @@ final class ASTExpAddOrConcat extends ASTExpression {
}
@Override
- public boolean isEmpty()
+ public boolean isEmptyHash()
throws TemplateException {
- return left.isEmpty() && right.isEmpty();
+ return left.isEmptyHash() && right.isEmptyHash();
}
}
- private static final class ConcatenatedHashEx
- extends ConcatenatedHash
+ private static final class ConcatenatedHashEx extends ConcatenatedHash
implements TemplateHashModelEx {
- private CollectionAndSequence keys;
- private CollectionAndSequence values;
+ private TemplateSequenceModel keys;
+ private TemplateSequenceModel values;
private int size;
ConcatenatedHashEx(TemplateHashModelEx left, TemplateHashModelEx right) {
@@ -240,34 +278,33 @@ final class ASTExpAddOrConcat extends ASTExpression {
}
@Override
- public int size() throws TemplateException {
+ public int getHashSize() throws TemplateException {
initKeys();
return size;
}
@Override
- public TemplateCollectionModel keys()
+ public TemplateIterableModel keys()
throws TemplateException {
initKeys();
return keys;
}
@Override
- public TemplateCollectionModel values()
+ public TemplateIterableModel values()
throws TemplateException {
initValues();
return values;
}
- private void initKeys()
- throws TemplateException {
+ private void initKeys() throws TemplateException {
if (keys == null) {
HashSet keySet = new HashSet();
NativeSequence keySeq = new NativeSequence(32);
addKeys(keySet, keySeq, (TemplateHashModelEx) left);
addKeys(keySet, keySeq, (TemplateHashModelEx) right);
size = keySet.size();
- keys = new CollectionAndSequence(keySeq);
+ keys = keySeq;
}
}
@@ -277,24 +314,23 @@ final class ASTExpAddOrConcat extends ASTExpression {
while (it.hasNext()) {
TemplateStringModel tsm = (TemplateStringModel) it.next();
if (set.add(tsm.getAsString())) {
- // The first occurence of the key decides the index;
- // this is consisten with stuff like java.util.LinkedHashSet.
+ // The first occurrence of the key decides the index;
+ // this is consistent with stuff like java.util.LinkedHashSet.
keySeq.add(tsm);
}
}
}
- private void initValues()
- throws TemplateException {
+ private void initValues() throws TemplateException {
if (values == null) {
- NativeSequence seq = new NativeSequence(size());
- // Note: size() invokes initKeys() if needed.
+ NativeSequence seq = new NativeSequence(getHashSize());
+ // Note: getCollectionSize() invokes initKeys() if needed.
- int ln = keys.size();
+ int ln = keys.getCollectionSize();
for (int i = 0; i < ln; i++) {
seq.add(get(((TemplateStringModel) keys.get(i)).getAsString()));
}
- values = new CollectionAndSequence(seq);
+ values = seq;
}
}
}
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/cae86e18/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 8e542ba..b0aa3b0 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
@@ -121,8 +121,8 @@ abstract class ASTExpBuiltIn extends ASTExpression implements Cloneable {
putBI("int", new intBI());
putBI("interpret", new BuiltInsForStringsMisc.interpretBI());
putBI("isBoolean", new BuiltInsForMultipleTypes.is_booleanBI());
+ putBI("isIterable", new BuiltInsForMultipleTypes.is_iterableBI());
putBI("isCollection", new BuiltInsForMultipleTypes.is_collectionBI());
- putBI("isCollectionEx", new BuiltInsForMultipleTypes.is_collection_exBI());
is_dateLikeBI bi = new BuiltInsForMultipleTypes.is_dateLikeBI();
putBI("isDate", bi); // misnomer
putBI("isDateLike", bi);
@@ -133,11 +133,9 @@ abstract class ASTExpBuiltIn extends ASTExpression implements Cloneable {
putBI("isUnknownDateLike", new BuiltInsForMultipleTypes.is_dateOfTypeBI(TemplateDateModel.UNKNOWN));
putBI("isDatetime", new BuiltInsForMultipleTypes.is_dateOfTypeBI(TemplateDateModel.DATE_TIME));
putBI("isDirective", new BuiltInsForMultipleTypes.is_directiveBI());
- putBI("isEnumerable", new BuiltInsForMultipleTypes.is_enumerableBI());
putBI("isHashEx", new BuiltInsForMultipleTypes.is_hash_exBI());
putBI("isHash", new BuiltInsForMultipleTypes.is_hashBI());
putBI("isInfinite", new is_infiniteBI());
- putBI("isIndexable", new BuiltInsForMultipleTypes.is_indexableBI());
putBI("isMarkupOutput", new BuiltInsForMultipleTypes.is_markup_outputBI());
putBI("isFunction", new BuiltInsForMultipleTypes.is_functionBI());
putBI("isNan", new is_nanBI());
@@ -250,8 +248,8 @@ abstract class ASTExpBuiltIn extends ASTExpression implements Cloneable {
putBI("removeBeginning", new BuiltInsForStringsBasic.remove_beginningBI());
putBI("rtf", new BuiltInsForStringsEncoding.rtfBI());
putBI("seqContains", new seq_containsBI());
- putBI("seqIndexOf", new seq_index_ofBI(1));
- putBI("seqLastIndexOf", new seq_index_ofBI(-1));
+ putBI("seqIndexOf", new seq_index_ofBI(true));
+ putBI("seqLastIndexOf", new seq_index_ofBI(false));
putBI("short", new shortBI());
putBI("size", new BuiltInsForMultipleTypes.sizeBI());
putBI("sortBy", new sort_byBI());
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/cae86e18/freemarker-core/src/main/java/org/apache/freemarker/core/ASTExpBuiltInVariable.java
----------------------------------------------------------------------
diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/ASTExpBuiltInVariable.java b/freemarker-core/src/main/java/org/apache/freemarker/core/ASTExpBuiltInVariable.java
index 6ca8b9e..4570948 100644
--- a/freemarker-core/src/main/java/org/apache/freemarker/core/ASTExpBuiltInVariable.java
+++ b/freemarker-core/src/main/java/org/apache/freemarker/core/ASTExpBuiltInVariable.java
@@ -239,7 +239,7 @@ final class ASTExpBuiltInVariable extends ASTExpression {
}
@Override
- public boolean isEmpty() {
+ public boolean isEmptyHash() {
return false;
}
}
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/cae86e18/freemarker-core/src/main/java/org/apache/freemarker/core/ASTExpDefault.java
----------------------------------------------------------------------
diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/ASTExpDefault.java b/freemarker-core/src/main/java/org/apache/freemarker/core/ASTExpDefault.java
index 6e892c9..07e12fb 100644
--- a/freemarker-core/src/main/java/org/apache/freemarker/core/ASTExpDefault.java
+++ b/freemarker-core/src/main/java/org/apache/freemarker/core/ASTExpDefault.java
@@ -20,49 +20,75 @@
package org.apache.freemarker.core;
-import org.apache.freemarker.core.model.TemplateCollectionModel;
-import org.apache.freemarker.core.model.TemplateHashModelEx;
+import org.apache.freemarker.core.model.TemplateHashModelEx2;
+import org.apache.freemarker.core.model.TemplateIterableModel;
import org.apache.freemarker.core.model.TemplateModel;
-import org.apache.freemarker.core.model.TemplateStringModel;
+import org.apache.freemarker.core.model.TemplateModelIterator;
import org.apache.freemarker.core.model.TemplateSequenceModel;
+import org.apache.freemarker.core.model.TemplateStringModel;
/** {@code exp!defExp}, {@code (exp)!defExp} and the same two with {@code (exp)!}. */
class ASTExpDefault extends ASTExpression {
-
- static private class EmptyStringAndSequence
- implements TemplateStringModel, TemplateSequenceModel, TemplateHashModelEx {
- @Override
+
+ static private class EmptyStringAndSequenceAndHash implements TemplateStringModel, TemplateSequenceModel,
+ TemplateHashModelEx2 {
+ @Override
public String getAsString() {
- return "";
- }
- @Override
+ return "";
+ }
+
+ @Override
public TemplateModel get(int i) {
- return null;
- }
- @Override
+ return null;
+ }
+
+ @Override
+ public TemplateModelIterator iterator() {
+ return TemplateModelIterator.EMPTY_ITERATOR;
+ }
+
+ @Override
public TemplateModel get(String s) {
- return null;
- }
- @Override
- public int size() {
- return 0;
- }
- @Override
- public boolean isEmpty() {
- return true;
- }
- @Override
- public TemplateCollectionModel keys() {
- return TemplateCollectionModel.EMPTY_COLLECTION;
- }
- @Override
- public TemplateCollectionModel values() {
- return TemplateCollectionModel.EMPTY_COLLECTION;
- }
-
- }
-
- static final TemplateModel EMPTY_STRING_AND_SEQUENCE = new EmptyStringAndSequence();
+ return null;
+ }
+
+ @Override
+ public int getCollectionSize() {
+ return 0;
+ }
+
+ @Override
+ public boolean isEmptyCollection() throws TemplateException {
+ return true;
+ }
+
+ @Override
+ public int getHashSize() throws TemplateException {
+ return 0;
+ }
+
+ @Override
+ public boolean isEmptyHash() {
+ return true;
+ }
+
+ @Override
+ public TemplateIterableModel keys() {
+ return TemplateIterableModel.EMPTY_ITERABLE;
+ }
+
+ @Override
+ public TemplateIterableModel values() {
+ return TemplateIterableModel.EMPTY_ITERABLE;
+ }
+
+ @Override
+ public KeyValuePairIterator keyValuePairIterator() throws TemplateException {
+ return KeyValuePairIterator.EMPTY_KEY_VALUE_PAIR_ITERATOR;
+ }
+ }
+
+ static final TemplateModel EMPTY_STRING_AND_SEQUENCE_AND_HASH = new EmptyStringAndSequenceAndHash();
private final ASTExpression lho, rho;
@@ -88,7 +114,7 @@ class ASTExpDefault extends ASTExpression {
}
if (left != null) return left;
- else if (rho == null) return EMPTY_STRING_AND_SEQUENCE;
+ else if (rho == null) return EMPTY_STRING_AND_SEQUENCE_AND_HASH;
else return rho.eval(env);
}
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/cae86e18/freemarker-core/src/main/java/org/apache/freemarker/core/ASTExpDynamicKeyName.java
----------------------------------------------------------------------
diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/ASTExpDynamicKeyName.java b/freemarker-core/src/main/java/org/apache/freemarker/core/ASTExpDynamicKeyName.java
index 63e2ea2..e14e009 100644
--- a/freemarker-core/src/main/java/org/apache/freemarker/core/ASTExpDynamicKeyName.java
+++ b/freemarker-core/src/main/java/org/apache/freemarker/core/ASTExpDynamicKeyName.java
@@ -78,14 +78,7 @@ final class ASTExpDynamicKeyName extends ASTExpression {
Environment env)
throws TemplateException {
if (targetModel instanceof TemplateSequenceModel) {
- TemplateSequenceModel tsm = (TemplateSequenceModel) targetModel;
- int size;
- try {
- size = tsm.size();
- } catch (Exception e) {
- size = Integer.MAX_VALUE;
- }
- return index < size ? tsm.get(index) : null;
+ return ((TemplateSequenceModel) targetModel).get(index);
}
String s;
@@ -147,7 +140,7 @@ final class ASTExpDynamicKeyName extends ASTExpression {
}
}
- final int size = range.size();
+ final int size = range.getCollectionSize();
final boolean rightUnbounded = range.isRightUnbounded();
final boolean rightAdaptive = range.isRightAdaptive();
@@ -157,14 +150,14 @@ final class ASTExpDynamicKeyName extends ASTExpression {
return emptyResult(targetSeq != null);
}
- final int firstIdx = range.getBegining();
+ final int firstIdx = range.getBeginning();
if (firstIdx < 0) {
throw new TemplateException(keyExpression,
"Negative range start index (", Integer.valueOf(firstIdx),
") isn't allowed for a range used for slicing.");
}
- final int targetSize = targetStr != null ? targetStr.length() : targetSeq.size();
+ final int targetSize = targetStr != null ? targetStr.length() : targetSeq.getCollectionSize();
final int step = range.getStep();
// Right-adaptive increasing ranges can start 1 after the last element of the target, because they are like
@@ -221,23 +214,13 @@ final class ASTExpDynamicKeyName extends ASTExpression {
// List items are already wrapped, so the wrapper will be null:
return resultSeq;
} else {
- final int exclEndIdx;
if (step < 0 && resultSize > 1) {
- if (!(range.isAffactedByStringSlicingBug() && resultSize == 2)) {
- throw new TemplateException(keyExpression,
- "Decreasing ranges aren't allowed for slicing strings (as it would give reversed text). "
- + "The index range was: first = ", Integer.valueOf(firstIdx),
- ", last = ", Integer.valueOf(firstIdx + (resultSize - 1) * step));
- } else {
- // Emulate the legacy bug, where "foo"[n .. n-1] gives "" instead of an error (if n >= 1).
- // Fix this in FTL [2.4]
- exclEndIdx = firstIdx;
- }
- } else {
- exclEndIdx = firstIdx + resultSize;
+ throw new TemplateException(keyExpression,
+ "Decreasing ranges aren't allowed for slicing strings (as it would give reversed text). "
+ + "The index range was: first = ", Integer.valueOf(firstIdx),
+ ", last = ", Integer.valueOf(firstIdx + (resultSize - 1) * step));
}
-
- return new SimpleString(targetStr.substring(firstIdx, exclEndIdx));
+ return new SimpleString(targetStr.substring(firstIdx, firstIdx + resultSize));
}
}
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/cae86e18/freemarker-core/src/main/java/org/apache/freemarker/core/ASTExpHashLiteral.java
----------------------------------------------------------------------
diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/ASTExpHashLiteral.java b/freemarker-core/src/main/java/org/apache/freemarker/core/ASTExpHashLiteral.java
index f188631..a6eaab4 100644
--- a/freemarker-core/src/main/java/org/apache/freemarker/core/ASTExpHashLiteral.java
+++ b/freemarker-core/src/main/java/org/apache/freemarker/core/ASTExpHashLiteral.java
@@ -24,11 +24,11 @@ import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.ListIterator;
-import org.apache.freemarker.core.model.TemplateCollectionModel;
+import org.apache.freemarker.core.model.TemplateIterableModel;
import org.apache.freemarker.core.model.TemplateHashModelEx2;
import org.apache.freemarker.core.model.TemplateModel;
import org.apache.freemarker.core.model.TemplateModelIterator;
-import org.apache.freemarker.core.model.impl.CollectionAndSequence;
+import org.apache.freemarker.core.model.impl.IterableAndSequence;
/**
* AST expression node: <tt>{ keyExp: valueExp, ... }</tt>
@@ -108,7 +108,7 @@ final class ASTExpHashLiteral extends ASTExpression {
private class LinkedHash implements TemplateHashModelEx2 {
private HashMap<String, TemplateModel> map;
- private TemplateCollectionModel keyCollection, valueCollection; // ordered lists of keys and values
+ private TemplateIterableModel keyCollection, valueCollection; // ordered lists of keys and values
LinkedHash(Environment env) throws TemplateException {
map = new LinkedHashMap<>();
@@ -123,22 +123,22 @@ final class ASTExpHashLiteral extends ASTExpression {
}
@Override
- public int size() {
+ public int getHashSize() {
return size;
}
@Override
- public TemplateCollectionModel keys() {
+ public TemplateIterableModel keys() {
if (keyCollection == null) {
- keyCollection = new CollectionAndSequence(new NativeStringCollectionCollectionEx(map.keySet()));
+ keyCollection = new IterableAndSequence(new NativeStringCollectionCollection(map.keySet()));
}
return keyCollection;
}
@Override
- public TemplateCollectionModel values() {
+ public TemplateIterableModel values() {
if (valueCollection == null) {
- valueCollection = new CollectionAndSequence(new NativeCollectionEx(map.values()));
+ valueCollection = new IterableAndSequence(new NativeCollection(map.values()));
}
return valueCollection;
}
@@ -149,7 +149,7 @@ final class ASTExpHashLiteral extends ASTExpression {
}
@Override
- public boolean isEmpty() {
+ public boolean isEmptyHash() {
return size == 0;
}
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/cae86e18/freemarker-core/src/main/java/org/apache/freemarker/core/ASTExpListLiteral.java
----------------------------------------------------------------------
diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/ASTExpListLiteral.java b/freemarker-core/src/main/java/org/apache/freemarker/core/ASTExpListLiteral.java
index 91d101a..1d3f8d9 100644
--- a/freemarker-core/src/main/java/org/apache/freemarker/core/ASTExpListLiteral.java
+++ b/freemarker-core/src/main/java/org/apache/freemarker/core/ASTExpListLiteral.java
@@ -21,11 +21,8 @@ package org.apache.freemarker.core;
import java.io.IOException;
import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
import java.util.ListIterator;
-import org.apache.freemarker.core.model.TemplateFunctionModel;
import org.apache.freemarker.core.model.TemplateModel;
import org.apache.freemarker.core.model.TemplateSequenceModel;
@@ -34,9 +31,9 @@ import org.apache.freemarker.core.model.TemplateSequenceModel;
*/
final class ASTExpListLiteral extends ASTExpression {
- final ArrayList/*<ASTExpression>*/ items;
+ final ArrayList<ASTExpression> items;
- ASTExpListLiteral(ArrayList items) {
+ ASTExpListLiteral(ArrayList<ASTExpression> items) {
this.items = items;
items.trimToSize();
}
@@ -53,30 +50,6 @@ final class ASTExpListLiteral extends ASTExpression {
return list;
}
- /**
- * For {@link TemplateFunctionModel} calls, returns the list of arguments as {@link TemplateModel}-s.
- */
- // TODO [FM3][CF] This will be removed
- List<TemplateModel> getModelList(Environment env) throws TemplateException {
- int size = items.size();
- switch(size) {
- case 0: {
- return Collections.emptyList();
- }
- case 1: {
- return Collections.singletonList(((ASTExpression) items.get(0)).eval(env));
- }
- default: {
- List result = new ArrayList(items.size());
- for (ListIterator iterator = items.listIterator(); iterator.hasNext(); ) {
- ASTExpression exp = (ASTExpression) iterator.next();
- result.add(exp.eval(env));
- }
- return result;
- }
- }
- }
-
public int size() {
return items.size();
}
@@ -86,7 +59,7 @@ final class ASTExpListLiteral extends ASTExpression {
StringBuilder buf = new StringBuilder("[");
int size = items.size();
for (int i = 0; i < size; i++) {
- ASTExpression value = (ASTExpression) items.get(i);
+ ASTExpression value = items.get(i);
buf.append(value.getCanonicalForm());
if (i != size - 1) {
buf.append(", ");
@@ -107,7 +80,7 @@ final class ASTExpListLiteral extends ASTExpression {
return true;
}
for (int i = 0; i < items.size(); i++) {
- ASTExpression exp = (ASTExpression) items.get(i);
+ ASTExpression exp = items.get(i);
if (!exp.isLiteral()) {
return false;
}
@@ -118,7 +91,7 @@ final class ASTExpListLiteral extends ASTExpression {
// A hacky routine used by ASTDirVisit and ASTDirRecurse
TemplateSequenceModel evaluateStringsToNamespaces(Environment env) throws TemplateException {
TemplateSequenceModel val = (TemplateSequenceModel) eval(env);
- NativeSequence result = new NativeSequence(val.size());
+ NativeSequence result = new NativeSequence(val.getCollectionSize());
for (int i = 0; i < items.size(); i++) {
Object itemExpr = items.get(i);
if (itemExpr instanceof ASTExpStringLiteral) {
@@ -138,12 +111,13 @@ final class ASTExpListLiteral extends ASTExpression {
return result;
}
+ @SuppressWarnings("unchecked")
@Override
protected ASTExpression deepCloneWithIdentifierReplaced_inner(
String replacedIdentifier, ASTExpression replacement, ReplacemenetState replacementState) {
- ArrayList clonedValues = (ArrayList) items.clone();
- for (ListIterator iter = clonedValues.listIterator(); iter.hasNext(); ) {
- iter.set(((ASTExpression) iter.next()).deepCloneWithIdentifierReplaced(
+ ArrayList<ASTExpression> clonedValues = (ArrayList<ASTExpression>) items.clone();
+ for (ListIterator<ASTExpression> iter = clonedValues.listIterator(); iter.hasNext(); ) {
+ iter.set(iter.next().deepCloneWithIdentifierReplaced(
replacedIdentifier, replacement, replacementState));
}
return new ASTExpListLiteral(clonedValues);
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/cae86e18/freemarker-core/src/main/java/org/apache/freemarker/core/ASTExpRange.java
----------------------------------------------------------------------
diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/ASTExpRange.java b/freemarker-core/src/main/java/org/apache/freemarker/core/ASTExpRange.java
index 7b86e3e..edd80a5 100644
--- a/freemarker-core/src/main/java/org/apache/freemarker/core/ASTExpRange.java
+++ b/freemarker-core/src/main/java/org/apache/freemarker/core/ASTExpRange.java
@@ -55,7 +55,7 @@ final class ASTExpRange extends ASTExpression {
begin, endType != END_SIZE_LIMITED ? lhoValue : begin + lhoValue,
endType == END_INCLUSIVE, endType == END_SIZE_LIMITED);
} else {
- return new ListableRightUnboundedRangeModel(begin);
+ return new RightUnboundedRangeModel(begin);
}
}
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/cae86e18/freemarker-core/src/main/java/org/apache/freemarker/core/ASTExpression.java
----------------------------------------------------------------------
diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/ASTExpression.java b/freemarker-core/src/main/java/org/apache/freemarker/core/ASTExpression.java
index 6b6d5e2..69bb34c 100644
--- a/freemarker-core/src/main/java/org/apache/freemarker/core/ASTExpression.java
+++ b/freemarker-core/src/main/java/org/apache/freemarker/core/ASTExpression.java
@@ -23,11 +23,11 @@ import org.apache.freemarker.core.model.TemplateBooleanModel;
import org.apache.freemarker.core.model.TemplateCollectionModel;
import org.apache.freemarker.core.model.TemplateDateModel;
import org.apache.freemarker.core.model.TemplateHashModel;
+import org.apache.freemarker.core.model.TemplateIterableModel;
import org.apache.freemarker.core.model.TemplateMarkupOutputModel;
import org.apache.freemarker.core.model.TemplateModel;
import org.apache.freemarker.core.model.TemplateNumberModel;
import org.apache.freemarker.core.model.TemplateStringModel;
-import org.apache.freemarker.core.model.TemplateSequenceModel;
import org.apache.freemarker.core.model.impl.BeanModel;
/**
@@ -75,7 +75,7 @@ abstract class ASTExpression extends ASTNode {
}
/**
- * @param seqTip Tip to display if the value type is not coercable, but it's sequence or collection.
+ * @param seqTip Tip to display if the value type is not coercable, but it's iterable.
*/
String evalAndCoerceToPlainText(Environment env, String seqTip) throws TemplateException {
return _EvalUtils.coerceModelToPlainText(eval(env), this, seqTip, env);
@@ -86,7 +86,7 @@ abstract class ASTExpression extends ASTNode {
}
/**
- * @param seqTip Tip to display if the value type is not coercable, but it's sequence or collection.
+ * @param seqTip Tip to display if the value type is not coercable, but it's iterable.
*/
Object evalAndCoerceToStringOrMarkup(Environment env, String seqTip) throws TemplateException {
return _EvalUtils.coerceModelToStringOrMarkup(eval(env), this, seqTip, env);
@@ -97,7 +97,7 @@ abstract class ASTExpression extends ASTNode {
}
/**
- * @param seqTip Tip to display if the value type is not coercable, but it's sequence or collection.
+ * @param seqTip Tip to display if the value type is not coercable, but it's iterable.
*/
String evalAndCoerceToStringOrUnsupportedMarkup(Environment env, String seqTip) throws TemplateException {
return _EvalUtils.coerceModelToStringOrUnsupportedMarkup(eval(env), this, seqTip, env);
@@ -176,9 +176,9 @@ abstract class ASTExpression extends ASTNode {
static boolean isEmpty(TemplateModel model) throws TemplateException {
if (model instanceof BeanModel) {
- return ((BeanModel) model).isEmpty();
- } else if (model instanceof TemplateSequenceModel) {
- return ((TemplateSequenceModel) model).size() == 0;
+ return ((BeanModel) model).isEmptyHash();
+ } else if (model instanceof TemplateCollectionModel) {
+ return ((TemplateCollectionModel) model).isEmptyCollection();
} else if (model instanceof TemplateStringModel) {
String s = ((TemplateStringModel) model).getAsString();
return (s == null || s.length() == 0);
@@ -187,10 +187,10 @@ abstract class ASTExpression extends ASTNode {
} else if (model instanceof TemplateMarkupOutputModel) { // Note: happens just after FTL string check
TemplateMarkupOutputModel mo = (TemplateMarkupOutputModel) model;
return mo.getOutputFormat().isEmpty(mo);
- } else if (model instanceof TemplateCollectionModel) {
- return !((TemplateCollectionModel) model).iterator().hasNext();
+ } else if (model instanceof TemplateIterableModel) {
+ return !((TemplateIterableModel) model).iterator().hasNext();
} else if (model instanceof TemplateHashModel) {
- return ((TemplateHashModel) model).isEmpty();
+ return ((TemplateHashModel) model).isEmptyHash();
} else if (model instanceof TemplateNumberModel
|| model instanceof TemplateDateModel
|| model instanceof TemplateBooleanModel) {
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/cae86e18/freemarker-core/src/main/java/org/apache/freemarker/core/BoundedRangeModel.java
----------------------------------------------------------------------
diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/BoundedRangeModel.java b/freemarker-core/src/main/java/org/apache/freemarker/core/BoundedRangeModel.java
index 05efa98..9988200 100644
--- a/freemarker-core/src/main/java/org/apache/freemarker/core/BoundedRangeModel.java
+++ b/freemarker-core/src/main/java/org/apache/freemarker/core/BoundedRangeModel.java
@@ -20,6 +20,9 @@
package org.apache.freemarker.core;
+import org.apache.freemarker.core.model.TemplateModel;
+import org.apache.freemarker.core.model.TemplateModelIterator;
+
/**
* A range between two integers (maybe 0 long).
*/
@@ -27,8 +30,7 @@ final class BoundedRangeModel extends RangeModel {
private final int step, size;
private final boolean rightAdaptive;
- private final boolean affectedByStringSlicingBug;
-
+
/**
* @param inclusiveEnd Tells if the {@code end} index is part of the range.
* @param rightAdaptive Tells if the right end of the range adapts to the size of the sliced value, if otherwise
@@ -39,14 +41,35 @@ final class BoundedRangeModel extends RangeModel {
step = begin <= end ? 1 : -1;
size = Math.abs(end - begin) + (inclusiveEnd ? 1 : 0);
this.rightAdaptive = rightAdaptive;
- affectedByStringSlicingBug = inclusiveEnd;
}
@Override
- public int size() {
+ public int getCollectionSize() {
return size;
}
-
+
+ @Override
+ public boolean isEmptyCollection() throws TemplateException {
+ return size == 0;
+ }
+
+ @Override
+ public TemplateModelIterator iterator() throws TemplateException {
+ return new TemplateModelIterator() {
+ private long nextIndex;
+
+ @Override
+ public TemplateModel next() throws TemplateException {
+ return uncheckedGet(nextIndex++);
+ }
+
+ @Override
+ public boolean hasNext() throws TemplateException {
+ return nextIndex < getCollectionSize();
+ }
+ };
+ }
+
@Override
int getStep() {
return step;
@@ -62,9 +85,4 @@ final class BoundedRangeModel extends RangeModel {
return rightAdaptive;
}
- @Override
- boolean isAffactedByStringSlicingBug() {
- return affectedByStringSlicingBug;
- }
-
}
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/cae86e18/freemarker-core/src/main/java/org/apache/freemarker/core/BuiltInForIterable.java
----------------------------------------------------------------------
diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/BuiltInForIterable.java b/freemarker-core/src/main/java/org/apache/freemarker/core/BuiltInForIterable.java
new file mode 100644
index 0000000..15556f8
--- /dev/null
+++ b/freemarker-core/src/main/java/org/apache/freemarker/core/BuiltInForIterable.java
@@ -0,0 +1,43 @@
+/*
+ * 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.model.TemplateIterableModel;
+import org.apache.freemarker.core.model.TemplateModel;
+
+abstract class BuiltInForIterable extends ASTExpBuiltIn {
+
+ @Override
+ TemplateModel _eval(Environment env)
+ throws TemplateException {
+ TemplateModel model = target.eval(env);
+ if (!(model instanceof TemplateIterableModel)) {
+ throw MessageUtils.newUnexpectedOperandTypeException(
+ target, model,
+ MessageUtils.EXPECTED_TYPE_ITERABLE_DESC,
+ TemplateIterableModel.class,
+ null, env);
+ }
+ return calculateResult((TemplateIterableModel) model);
+ }
+
+ abstract TemplateModel calculateResult(TemplateIterableModel model) throws TemplateException;
+
+}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/cae86e18/freemarker-core/src/main/java/org/apache/freemarker/core/BuiltInForSequence.java
----------------------------------------------------------------------
diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/BuiltInForSequence.java b/freemarker-core/src/main/java/org/apache/freemarker/core/BuiltInForSequence.java
index bac6c7f..cb7cc73 100644
--- a/freemarker-core/src/main/java/org/apache/freemarker/core/BuiltInForSequence.java
+++ b/freemarker-core/src/main/java/org/apache/freemarker/core/BuiltInForSequence.java
@@ -23,6 +23,7 @@ import org.apache.freemarker.core.model.TemplateModel;
import org.apache.freemarker.core.model.TemplateSequenceModel;
abstract class BuiltInForSequence extends ASTExpBuiltIn {
+
@Override
TemplateModel _eval(Environment env)
throws TemplateException {
@@ -32,6 +33,7 @@ abstract class BuiltInForSequence extends ASTExpBuiltIn {
}
return calculateResult((TemplateSequenceModel) model);
}
- abstract TemplateModel calculateResult(TemplateSequenceModel tsm)
- throws TemplateException;
+
+ abstract TemplateModel calculateResult(TemplateSequenceModel tsm) throws TemplateException;
+
}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/cae86e18/freemarker-core/src/main/java/org/apache/freemarker/core/BuiltInsForHashes.java
----------------------------------------------------------------------
diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/BuiltInsForHashes.java b/freemarker-core/src/main/java/org/apache/freemarker/core/BuiltInsForHashes.java
index e87bd77..4d2a9d5 100644
--- a/freemarker-core/src/main/java/org/apache/freemarker/core/BuiltInsForHashes.java
+++ b/freemarker-core/src/main/java/org/apache/freemarker/core/BuiltInsForHashes.java
@@ -19,11 +19,11 @@
package org.apache.freemarker.core;
-import org.apache.freemarker.core.model.TemplateCollectionModel;
+import org.apache.freemarker.core.model.TemplateIterableModel;
import org.apache.freemarker.core.model.TemplateHashModelEx;
import org.apache.freemarker.core.model.TemplateModel;
import org.apache.freemarker.core.model.TemplateSequenceModel;
-import org.apache.freemarker.core.model.impl.CollectionAndSequence;
+import org.apache.freemarker.core.model.impl.IterableAndSequence;
/**
* A holder for builtins that operate exclusively on hash left-hand value.
@@ -35,9 +35,9 @@ class BuiltInsForHashes {
@Override
TemplateModel calculateResult(TemplateHashModelEx hashExModel, Environment env)
throws TemplateException, InvalidReferenceException {
- TemplateCollectionModel keys = hashExModel.keys();
+ TemplateIterableModel keys = hashExModel.keys();
if (keys == null) throw newNullPropertyException("keys", hashExModel, env);
- return keys instanceof TemplateSequenceModel ? keys : new CollectionAndSequence(keys);
+ return keys instanceof TemplateSequenceModel ? keys : new IterableAndSequence(keys);
}
}
@@ -46,9 +46,9 @@ class BuiltInsForHashes {
@Override
TemplateModel calculateResult(TemplateHashModelEx hashExModel, Environment env)
throws TemplateException, InvalidReferenceException {
- TemplateCollectionModel values = hashExModel.values();
+ TemplateIterableModel values = hashExModel.values();
if (values == null) throw newNullPropertyException("values", hashExModel, env);
- return values instanceof TemplateSequenceModel ? values : new CollectionAndSequence(values);
+ return values instanceof TemplateSequenceModel ? values : new IterableAndSequence(values);
}
}
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/cae86e18/freemarker-core/src/main/java/org/apache/freemarker/core/BuiltInsForMultipleTypes.java
----------------------------------------------------------------------
diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/BuiltInsForMultipleTypes.java b/freemarker-core/src/main/java/org/apache/freemarker/core/BuiltInsForMultipleTypes.java
index d1f9761..9c48dc3 100644
--- a/freemarker-core/src/main/java/org/apache/freemarker/core/BuiltInsForMultipleTypes.java
+++ b/freemarker-core/src/main/java/org/apache/freemarker/core/BuiltInsForMultipleTypes.java
@@ -25,8 +25,8 @@ import java.util.Date;
import org.apache.freemarker.core.model.ArgumentArrayLayout;
import org.apache.freemarker.core.model.TemplateBooleanModel;
+import org.apache.freemarker.core.model.TemplateIterableModel;
import org.apache.freemarker.core.model.TemplateCollectionModel;
-import org.apache.freemarker.core.model.TemplateCollectionModelEx;
import org.apache.freemarker.core.model.TemplateDateModel;
import org.apache.freemarker.core.model.TemplateDirectiveModel;
import org.apache.freemarker.core.model.TemplateFunctionModel;
@@ -171,7 +171,7 @@ class BuiltInsForMultipleTypes {
}
@Override
- public boolean isEmpty() {
+ public boolean isEmptyHash() {
return false;
}
@@ -261,21 +261,21 @@ class BuiltInsForMultipleTypes {
}
}
- static class is_collectionBI extends ASTExpBuiltIn {
+ static class is_iterableBI extends ASTExpBuiltIn {
@Override
TemplateModel _eval(Environment env) throws TemplateException {
TemplateModel tm = target.eval(env);
target.assertNonNull(tm, env);
- return (tm instanceof TemplateCollectionModel) ? TemplateBooleanModel.TRUE : TemplateBooleanModel.FALSE;
+ return (tm instanceof TemplateIterableModel) ? TemplateBooleanModel.TRUE : TemplateBooleanModel.FALSE;
}
}
- static class is_collection_exBI extends ASTExpBuiltIn {
+ static class is_collectionBI extends ASTExpBuiltIn {
@Override
TemplateModel _eval(Environment env) throws TemplateException {
TemplateModel tm = target.eval(env);
target.assertNonNull(tm, env);
- return (tm instanceof TemplateCollectionModelEx) ? TemplateBooleanModel.TRUE : TemplateBooleanModel.FALSE;
+ return (tm instanceof TemplateCollectionModel) ? TemplateBooleanModel.TRUE : TemplateBooleanModel.FALSE;
}
}
@@ -317,16 +317,6 @@ class BuiltInsForMultipleTypes {
}
}
- static class is_enumerableBI extends ASTExpBuiltIn {
- @Override
- TemplateModel _eval(Environment env) throws TemplateException {
- TemplateModel tm = target.eval(env);
- target.assertNonNull(tm, env);
- return (tm instanceof TemplateSequenceModel || tm instanceof TemplateCollectionModel)
- ? TemplateBooleanModel.TRUE : TemplateBooleanModel.FALSE;
- }
- }
-
static class is_hash_exBI extends ASTExpBuiltIn {
@Override
TemplateModel _eval(Environment env) throws TemplateException {
@@ -345,15 +335,6 @@ class BuiltInsForMultipleTypes {
}
}
- static class is_indexableBI extends ASTExpBuiltIn {
- @Override
- TemplateModel _eval(Environment env) throws TemplateException {
- TemplateModel tm = target.eval(env);
- target.assertNonNull(tm, env);
- return (tm instanceof TemplateSequenceModel) ? TemplateBooleanModel.TRUE : TemplateBooleanModel.FALSE;
- }
- }
-
static class is_markup_outputBI extends ASTExpBuiltIn {
@Override
TemplateModel _eval(Environment env) throws TemplateException {
@@ -434,21 +415,15 @@ class BuiltInsForMultipleTypes {
TemplateModel model = target.eval(env);
final int size;
- if (model instanceof TemplateSequenceModel) {
- size = ((TemplateSequenceModel) model).size();
- } else if (model instanceof TemplateCollectionModelEx) {
- size = ((TemplateCollectionModelEx) model).size();
+ if (model instanceof TemplateCollectionModel) {
+ size = ((TemplateCollectionModel) model).getCollectionSize();
} else if (model instanceof TemplateHashModelEx) {
- size = ((TemplateHashModelEx) model).size();
+ size = ((TemplateHashModelEx) model).getHashSize();
} else {
throw MessageUtils.newUnexpectedOperandTypeException(
target, model,
- "extended-hash or sequence or extended collection",
- new Class[] {
- TemplateHashModelEx.class,
- TemplateSequenceModel.class,
- TemplateCollectionModelEx.class
- },
+ "collection (like a sequence) or extended-hash",
+ new Class[] { TemplateCollectionModel.class, TemplateHashModelEx.class },
null,
env);
}
@@ -557,7 +532,7 @@ class BuiltInsForMultipleTypes {
}
@Override
- public boolean isEmpty() {
+ public boolean isEmptyHash() {
return false;
}
}
@@ -608,7 +583,7 @@ class BuiltInsForMultipleTypes {
}
@Override
- public boolean isEmpty() {
+ public boolean isEmptyHash() {
return false;
}
}
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/cae86e18/freemarker-core/src/main/java/org/apache/freemarker/core/BuiltInsForNodes.java
----------------------------------------------------------------------
diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/BuiltInsForNodes.java b/freemarker-core/src/main/java/org/apache/freemarker/core/BuiltInsForNodes.java
index b660a40..e598ea4 100644
--- a/freemarker-core/src/main/java/org/apache/freemarker/core/BuiltInsForNodes.java
+++ b/freemarker-core/src/main/java/org/apache/freemarker/core/BuiltInsForNodes.java
@@ -64,7 +64,8 @@ class BuiltInsForNodes {
return this;
}
AncestorSequence result = new AncestorSequence(env);
- for (int seqIdx = 0; seqIdx < size(); seqIdx++) {
+ int size = getCollectionSize();
+ for (int seqIdx = 0; seqIdx < size; seqIdx++) {
TemplateNodeModel tnm = (TemplateNodeModel) get(seqIdx);
String nodeName = tnm.getNodeName();
String nsURI = tnm.getNodeNamespace();