Reworked list/iterable-like TemplateModel interfaced. Now we have 
TemplateIterableModel (which is like TemplateCollectionModel in FM2), 
TemplateCollectionModel (which similar to TemplateCollectionModelEx in FM2) 
that extends TemplateIterableModel, and TemplateSequenceModel (same as in FM2) 
that extends TemplateCollectionModel (it didn't extend anything but 
TemplateModel in FM2). This has brought some built-in name changes, and other 
cleanup (see FM3-CHANGE-LOG.txt diff for details). Notably, the isEmpty() and 
size() method of TemplateCollectionModel and of TemplateHashModel[Ex] has 
renamed so they don't clash anymore, and thus can return different values when 
a value is both a collection and a hash.


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

Branch: refs/heads/3
Commit: cae86e18e06df0475313072e71dfa2795816060f
Parents: e3e7f1e
Author: ddekany <ddek...@apache.org>
Authored: Fri Sep 1 20:57:49 2017 +0200
Committer: ddekany <ddek...@apache.org>
Committed: Fri Sep 1 20:57:49 2017 +0200

----------------------------------------------------------------------
 FM3-CHANGE-LOG.txt                              |  53 ++-
 .../core/FM2ASTToFM3SourceConverter.java        |   8 +
 .../converter/FM2ToFM3ConverterTest.java        |   4 +
 .../freemarker/core/DirectiveCallPlaceTest.java |   2 +-
 .../model/impl/DefaultObjectWrapperTest.java    | 123 ++++---
 .../model/impl/RestrictedObjectWrapperTest.java |   6 +-
 .../templatesuite/CoreTemplateTestSuite.java    |  12 +-
 .../templatesuite/models/AllTemplateModels.java |  52 ++-
 .../core/templatesuite/models/BooleanHash1.java |   4 +-
 .../core/templatesuite/models/BooleanHash2.java |   4 +-
 .../core/templatesuite/models/BooleanList1.java |  17 +-
 .../core/templatesuite/models/BooleanList2.java |  15 +-
 .../models/HashAndStringModel.java              |  35 +-
 .../core/templatesuite/models/Listables.java    |  16 +-
 .../core/templatesuite/models/MultiModel1.java  |  18 +-
 .../core/templatesuite/models/MultiModel3.java  |   2 +-
 .../core/templatesuite/models/MultiModel4.java  |  21 +-
 .../core/templatesuite/models/MultiModel5.java  |  19 +-
 .../core/userpkg/TestTemplateCallableModel.java |   2 +-
 .../templatesuite/expected/type-builtins.txt    |  28 +-
 .../templatesuite/templates/range-common.ftl    | 314 ----------------
 .../core/templatesuite/templates/range.ftl      | 308 +++++++++++++++-
 .../templatesuite/templates/type-builtins.ftl   |  16 +-
 .../org/apache/freemarker/core/ASTDirList.java  |  41 +--
 .../freemarker/core/ASTDirMacroOrFunction.java  |   3 +-
 .../apache/freemarker/core/ASTDirNested.java    |   8 +-
 .../freemarker/core/ASTExpAddOrConcat.java      | 108 ++++--
 .../apache/freemarker/core/ASTExpBuiltIn.java   |   8 +-
 .../freemarker/core/ASTExpBuiltInVariable.java  |   2 +-
 .../apache/freemarker/core/ASTExpDefault.java   |  98 +++--
 .../freemarker/core/ASTExpDynamicKeyName.java   |  35 +-
 .../freemarker/core/ASTExpHashLiteral.java      |  18 +-
 .../freemarker/core/ASTExpListLiteral.java      |  44 +--
 .../org/apache/freemarker/core/ASTExpRange.java |   2 +-
 .../apache/freemarker/core/ASTExpression.java   |  20 +-
 .../freemarker/core/BoundedRangeModel.java      |  38 +-
 .../freemarker/core/BuiltInForIterable.java     |  43 +++
 .../freemarker/core/BuiltInForSequence.java     |   6 +-
 .../freemarker/core/BuiltInsForHashes.java      |  12 +-
 .../core/BuiltInsForMultipleTypes.java          |  51 +--
 .../freemarker/core/BuiltInsForNodes.java       |   3 +-
 .../freemarker/core/BuiltInsForSequences.java   | 359 +++++++------------
 .../core/BuiltInsForStringsBasic.java           |   4 +-
 .../freemarker/core/BuiltInsForStringsMisc.java |   3 +-
 .../core/BuiltInsForStringsRegexp.java          |  97 ++---
 .../org/apache/freemarker/core/Environment.java |  40 ++-
 .../core/InvalidReferenceException.java         |   2 +-
 .../core/ListableRightUnboundedRangeModel.java  |  94 -----
 .../apache/freemarker/core/MessageUtils.java    |  21 +-
 .../freemarker/core/NativeCollection.java       |  55 +++
 .../freemarker/core/NativeCollectionEx.java     |  72 ----
 .../apache/freemarker/core/NativeHashEx2.java   |  14 +-
 .../apache/freemarker/core/NativeSequence.java  |  39 +-
 .../core/NativeStringArraySequence.java         |  27 +-
 .../core/NativeStringCollectionCollection.java  |  74 ++++
 .../NativeStringCollectionCollectionEx.java     |  78 ----
 .../core/NativeStringListSequence.java          |  27 +-
 .../core/NativeTemplateModelIterator.java       |  44 +++
 .../org/apache/freemarker/core/RangeModel.java  |  17 +-
 .../core/RightUnboundedRangeModel.java          |  74 +++-
 .../org/apache/freemarker/core/_EvalUtils.java  |  11 +-
 .../core/debug/RmiDebugModelImpl.java           |  10 +-
 .../core/debug/RmiDebuggedEnvironmentImpl.java  |  18 +-
 .../core/model/EmptyCollectionExModel.java      |  43 ---
 .../core/model/EmptyCollectionModel.java        |  43 +++
 .../freemarker/core/model/EmptyHashModel.java   |  12 +-
 .../core/model/EmptyIteratorModel.java          |   2 +-
 .../core/model/EmptySequenceModel.java          |  12 +-
 .../core/model/GeneralPurposeNothing.java       |  33 +-
 .../core/model/TemplateCollectionModel.java     |  32 +-
 .../core/model/TemplateCollectionModelEx.java   |  45 ---
 .../core/model/TemplateDirectiveModel.java      |   2 +-
 .../core/model/TemplateHashModel.java           |   2 +-
 .../core/model/TemplateHashModelEx.java         |  15 +-
 .../core/model/TemplateIterableModel.java       |  49 +++
 .../core/model/TemplateModelIterator.java       |  23 +-
 .../core/model/TemplateSequenceModel.java       |  25 +-
 .../freemarker/core/model/impl/BeanModel.java   |  24 +-
 .../core/model/impl/ClassBasedModelFactory.java |   2 +-
 .../core/model/impl/ClassIntrospector.java      |   6 +-
 .../core/model/impl/CollectionAdapter.java      |  88 -----
 .../core/model/impl/CollectionAndSequence.java  | 111 ------
 .../core/model/impl/DefaultArrayAdapter.java    | 258 ++++++++++++-
 .../model/impl/DefaultEnumerationAdapter.java   |  12 +-
 .../core/model/impl/DefaultIterableAdapter.java |  10 +-
 .../core/model/impl/DefaultIteratorAdapter.java |  12 +-
 .../core/model/impl/DefaultListAdapter.java     |  42 +--
 .../core/model/impl/DefaultMapAdapter.java      |  14 +-
 .../impl/DefaultNonListCollectionAdapter.java   |  12 +-
 .../core/model/impl/DefaultObjectWrapper.java   |  61 ++--
 .../DefaultUnassignableIteratorAdapter.java     |  59 ---
 .../freemarker/core/model/impl/HashAdapter.java | 181 ----------
 .../core/model/impl/IterableAndSequence.java    |  82 +++++
 .../IteratorToTemplateModelIteratorAdapter.java |  52 +++
 .../core/model/impl/ResourceBundleModel.java    |   6 +-
 .../core/model/impl/SequenceAdapter.java        |  68 ----
 .../impl/SequenceTemplateModelIterator.java     |  63 ++++
 .../freemarker/core/model/impl/SetAdapter.java  |  32 --
 .../core/model/impl/SimpleCollection.java       | 138 -------
 .../freemarker/core/model/impl/SimpleHash.java  |  14 +-
 .../core/model/impl/SimpleIterable.java         | 133 +++++++
 .../core/model/impl/SimpleSequence.java         |  31 +-
 .../impl/SingleItemTemplateModelIterator.java   |  48 +++
 .../freemarker/core/model/impl/StaticModel.java |  14 +-
 .../impl/TemplateCollectionModelAdapter.java    |  77 ++++
 .../model/impl/TemplateHashModelAdapter.java    | 185 ++++++++++
 .../impl/TemplateIterableModelAdapter.java      |  57 +++
 .../impl/TemplateModelIteratorAdapter.java      |  65 ++++
 .../model/impl/TemplateModelListSequence.java   |  33 +-
 .../impl/TemplateSequenceModelAdapter.java      |  88 +++++
 .../model/impl/TemplateSetModelAdapter.java     |  32 ++
 .../freemarker/core/util/CallableUtils.java     |  26 +-
 .../apache/freemarker/core/util/DeepUnwrap.java |  18 +-
 .../freemarker/core/util/StringToIndexMap.java  |   6 +-
 .../core/util/TemplateLanguageUtils.java        |   8 +-
 freemarker-core/src/main/javacc/FTL.jj          |   4 +-
 .../freemarker/dom/AttributeNodeModel.java      |   2 +-
 .../freemarker/dom/CharacterDataNodeModel.java  |   2 +-
 .../apache/freemarker/dom/DocumentModel.java    |   2 +-
 .../freemarker/dom/DocumentTypeModel.java       |   2 +-
 .../org/apache/freemarker/dom/ElementModel.java |   7 +-
 .../apache/freemarker/dom/NodeListModel.java    |  34 +-
 .../org/apache/freemarker/dom/NodeModel.java    |  32 +-
 .../org/apache/freemarker/dom/PINodeModel.java  |   2 +-
 .../dom/SunInternalXalanXPathSupport.java       |   2 +-
 .../freemarker/dom/XalanXPathSupport.java       |   2 +-
 .../servlet/HttpRequestHashModel.java           |  16 +-
 .../servlet/HttpRequestParametersHashModel.java |  16 +-
 .../servlet/HttpSessionHashModel.java           |   2 +-
 .../servlet/ServletContextHashModel.java        |   2 +-
 .../servlet/jsp/FreeMarkerPageContext.java      |   4 +
 .../freemarker/servlet/jsp/JspTagModelBase.java |   2 +-
 .../freemarker/servlet/jsp/TaglibFactory.java   |   4 +-
 .../expected/attributes-modernModels.txt        |   4 +-
 .../basic/WEB-INF/expected/attributes.txt       |   4 +-
 .../org/apache/freemarker/test/TestUtils.java   |   7 +-
 136 files changed, 3035 insertions(+), 2422 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/cae86e18/FM3-CHANGE-LOG.txt
----------------------------------------------------------------------
diff --git a/FM3-CHANGE-LOG.txt b/FM3-CHANGE-LOG.txt
index 0d6df14..012f1a0 100644
--- a/FM3-CHANGE-LOG.txt
+++ b/FM3-CHANGE-LOG.txt
@@ -121,12 +121,15 @@ Node: Changes already mentioned above aren't repeated 
here!
   - `?isMacro` was removed; use `?isDirective` instead, which returns `true` 
both for macros and directives defined otherwise.
   - `?isMethod` was removed; use `?isFunction` instead, which returns `true` 
both for Java methods and functions defined
      otherwise (such as with `#function`).
+- The FM2 `?isCollection` and `?isEnumerable` was replaced by `?isIterable`. 
`?isCollectionEx` was renamed to
+  `?isCollection`. (These follows the similar nomenclature change in the 
TemplateModel API-s)
+  `?isIndexable` was removed, and can be replaced with `?isSequence`.
 - The directive returned by `?interpret` doesn't allow nested content anymore. 
(It wasn't useful earlier either;
   the nested content was simply executed after the interpreted string.)
 - Changes in #macro/#functions
   - See major changes first, in the earlier chapter
   - It's not tolerated anymore if the caller of a macro has declared more 
nested content parameters than
-    what `#nested` passes to it (like in `<#macro m><#nested 1, 2></#macro> 
<@m ; i, j, k>...</@>`, where `k` has
+    what `#nested` passes to it (like in `<#macro m><#nested 1, 2></#macro> 
<@m ; iterator, j, k>...</@>`, where `k` has
     no corresponding value in `#nested`).
   - In `#macro` (and `function`) named parameters with default values need not 
be at the end of the parameter list
     anymore. (Positional parameters with default values need to be at the end 
of the list of positional parameters
@@ -134,10 +137,12 @@ Node: Changes already mentioned above aren't repeated 
here!
   - When parameter default expressions are evaluated, only the parameters 
defined earlier are already set.
     So `<#macro m a b=a>` works, but `<#macro m a=b b>` won't work anymore 
(unless there's `b` outside the
     parameter list), as `b` is not yet set when the default of `a` is 
calculated.
-- Built-ins don't convert their parameters to tring anymore; in FM some (not 
all) of them did, because they
+- Built-ins don't convert their parameters to string anymore; in FM some (not 
all) of them did, because they
   were based on the TemplateMethod interface (deprecated even in FM2) that 
required String paramteters and
-  was auto-converted by FM2.
-    
+  was auto-converted by FM2. (The template converter tool can't do this 
conversion.)
+- The string ranges like `someString[1..0]` (that is, where the end is one 
less than the beginning) aren't
+  tolerated anymore, and are errors. (The template converter tool can't do 
this conversion.)
+
   
 Java API changes
 ================
@@ -193,18 +198,31 @@ Major changes / features
 - Configuration is now immutable. You should use Configuration.Builder to set 
up the setting values, then create
   the Configuration with the builder's build() method.
 - Configuration defaults were changed to follow the current best practices 
(like default charset is UTF-8 everywhere)
-- Reworked callable `TemplateModel`-s (i.e., models that can be called like 
`foo(...)` or as `<@foo .../>`)
-  - Earlier there were several callable `TemplateModel` internfaces 
(`TemplateMethodModel`, `TemplateMethodModelEx`,
-    `TemplateDirectiveModel`, `TemplateTransformModel`). FM3 replaces them 
with only two new interfaces,
-    `TemplateDirectiveModel` (differs from the interface with identical name 
in FM2) and `TemplateFunctionModel`.
-    (These are both the subinterfaces of another new interface 
`TemplateCallableModel`.)
-  - All callable TemplateModel-s support passing parameters by position and by 
name, even in the same call
-    (e.g., `<@heading "Some title" icon="foo.jpg" />`, `sum(1, 2, 3, 
abs=true)`)
-  - `#macro` now produces a `TemplateDirectiveModel` and `#function` produces 
a `TemplateFunctionModel`. (Earlier, the
-    product was just a generic `TemplateModel` that could only be invoked 
using internal API-s, and had capabilities
-    that the callable TemplateModel-s coulnd't have.)
-- Renamed `TemplateScalarModel` to `TemplateStringModel`. (The 
`TemplateScalarModel` name come from the early FM,
-  where there where strings, numbers, booleans, etc. weren't separated.)
+- Reworked TemplateModel interfaces:
+  - Reworked callable `TemplateModel`-s (iterator.e., models that can be 
called like `foo(...)` or as `<@foo .../>`)
+    - Earlier there were several callable `TemplateModel` internfaces 
(`TemplateMethodModel`, `TemplateMethodModelEx`,
+      `TemplateDirectiveModel`, `TemplateTransformModel`). FM3 replaces them 
with only two new interfaces,
+      `TemplateDirectiveModel` (differs from the interface with identical name 
in FM2) and `TemplateFunctionModel`.
+      (These are both the subinterfaces of another new interface 
`TemplateCallableModel`.)
+    - All callable TemplateModel-s support passing parameters by position and 
by name, even in the same call
+      (e.g., `<@heading "Some title" icon="foo.jpg" />`, `sum(1, 2, 3, 
abs=true)`)
+    - `#macro` now produces a `TemplateDirectiveModel` and `#function` 
produces a `TemplateFunctionModel`. (Earlier, the
+      product was just a generic `TemplateModel` that could only be invoked 
using internal API-s, and had capabilities
+      that the callable TemplateModel-s coulnd't have.)
+  - Renamed `TemplateScalarModel` to `TemplateStringModel`. (The 
`TemplateScalarModel` name come from the early FM,
+    where strings, numbers, booleans, etc. weren't separated.)
+  - Renamed `TemplateCollectionModel` to `TemplateIterableModel`, and 
`TemplateCollectionModelEx` to
+    `TemplateCollectionModel`. (Since java.util.Iterable was added to Java, 
the TemplateCollectionModel
+    has become an unfortunate name, especailly as later 
TemplateCollectionModelEx was added, that was closer
+    to java.util.Collection than TemplateCollectionModel.)
+  - `TemplateSequenceModel` now extends `TemplateCollecitonModel`, and `#list` 
and other built-in mechanims prefer
+     using `iterator()` instead of an index loop.
+  - `TemplateModelIterator.next()` isn't required to handle anymore the case 
when there's no next item. If someone
+    doesn't check that with `hasNext()` before calling `next()`, anything can 
happen. This is to allow more efficient
+    implementations, which has become important now that `#list` and such 
prefers iterators over index loops.
+  - `isEmpty` of FM2 `TemplateCollectionModelEx` (now 
`TemplateCollectionModel`) and FM2 `TemplateHashModel` was renamed
+    to `isEmptyCollection` and `isEmptyHash`, so that for multi-typed values 
can give different results depending on the
+    type. Also, for collections `size()` was renamed to `getCollectionSize()`, 
and for hashes `getHashSize()`.
 - Removed freemarker.ext.log, our log abstraction layer from the times when 
there was no clear winner on this field.
   Added org.slf4j:slf4j-api as required dependency instead.
 - Added Spring support to the FreeMarker project (freemarker-spring module), 
instead of relying Spring developers
@@ -388,6 +406,9 @@ Core / Models and Object wrapping
   method of the model couldn't throw that as `TemplateModelException` is more 
specific. Now it can. Also it's not very
   useful in practice to catch `TemplateModelException` and the more generic 
`TemplateException` separately, so this
   simplification was made.
+- `TemplateSequenceModel.get(int)` is now expected to actually return `null` 
for any invalid index. In FM2 this was
+  the documented expectation, but many implementaitons throw exception instead 
of returning `null`, but FreeMarker
+  has tolerated that.
 
 Core / Template loading and caching
 ...................................

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/cae86e18/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 2ff86e9..0242882 100644
--- 
a/freemarker-converter/src/main/java/freemarker/core/FM2ASTToFM3SourceConverter.java
+++ 
b/freemarker-converter/src/main/java/freemarker/core/FM2ASTToFM3SourceConverter.java
@@ -1992,6 +1992,14 @@ public class FM2ASTToFM3SourceConverter {
             .put("datetime", "dateTime")
             .put("datetime_if_unknown", "dateTimeIfUnknown")
             .put("datetimeIfUnknown", "dateTimeIfUnknown")
+            .put("isCollection", "isIterable")
+            .put("is_collection", "isIterable")
+            .put("isEnumerable", "isIterable")
+            .put("is_enumerable", "isIterable")
+            .put("isCollectionEx", "isCollection")
+            .put("is_collection_ex", "isCollection")
+            .put("isIndexable", "isSequence")
+            .put("is_indexable", "isSequence")
             .build();
 
     private void printParameterSeparatorSource(Expression lho, Expression rho) 
{

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/cae86e18/freemarker-converter/src/test/java/org/freemarker/converter/FM2ToFM3ConverterTest.java
----------------------------------------------------------------------
diff --git 
a/freemarker-converter/src/test/java/org/freemarker/converter/FM2ToFM3ConverterTest.java
 
b/freemarker-converter/src/test/java/org/freemarker/converter/FM2ToFM3ConverterTest.java
index 0790518..632fb08 100644
--- 
a/freemarker-converter/src/test/java/org/freemarker/converter/FM2ToFM3ConverterTest.java
+++ 
b/freemarker-converter/src/test/java/org/freemarker/converter/FM2ToFM3ConverterTest.java
@@ -479,6 +479,10 @@ public class FM2ToFM3ConverterTest extends ConverterTest {
         assertConverted("${s?isDirective}", "${s?isMacro}");
         assertConverted("${s?isFunction}", "${s?is_method}");
         assertConverted("${s?isFunction}", "${s?isMethod}");
+        assertConverted("${s?isIterable}", "${s?isCollection}");
+        assertConverted("${s?isIterable}", "${s?is_collection}");
+        assertConverted("${s?isCollection}", "${s?isCollectionEx}");
+        assertConverted("${s?isCollection}", "${s?is_collection_ex}");
         assertConvertedSame("${s  ?   upperCase\t?\t\tleftPad(5)}");
         assertConvertedSame("${s <#--1--> ? <#--2--> upperCase}");
         // Runtime params:

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/cae86e18/freemarker-core-test/src/test/java/org/apache/freemarker/core/DirectiveCallPlaceTest.java
----------------------------------------------------------------------
diff --git 
a/freemarker-core-test/src/test/java/org/apache/freemarker/core/DirectiveCallPlaceTest.java
 
b/freemarker-core-test/src/test/java/org/apache/freemarker/core/DirectiveCallPlaceTest.java
index a24dacd..2e8f8cc 100644
--- 
a/freemarker-core-test/src/test/java/org/apache/freemarker/core/DirectiveCallPlaceTest.java
+++ 
b/freemarker-core-test/src/test/java/org/apache/freemarker/core/DirectiveCallPlaceTest.java
@@ -231,7 +231,7 @@ public class DirectiveCallPlaceTest extends TemplateTest {
         public void execute(TemplateModel[] args, CallPlace callPlace, Writer 
out, Environment env)
                 throws TemplateException, IOException {
             TemplateHashModelEx2 varargs = (TemplateHashModelEx2) 
args[ARGS_LAYOUT.getNamedVarargsArgumentIndex()];
-            if (varargs.size() > 0) {
+            if (varargs.getHashSize() > 0) {
                 out.write("(p=");
                 out.write(((TemplateStringModel) 
varargs.get("p")).getAsString());
                 out.write(")");

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/cae86e18/freemarker-core-test/src/test/java/org/apache/freemarker/core/model/impl/DefaultObjectWrapperTest.java
----------------------------------------------------------------------
diff --git 
a/freemarker-core-test/src/test/java/org/apache/freemarker/core/model/impl/DefaultObjectWrapperTest.java
 
b/freemarker-core-test/src/test/java/org/apache/freemarker/core/model/impl/DefaultObjectWrapperTest.java
index eb1c3d9..ba71c6e 100644
--- 
a/freemarker-core-test/src/test/java/org/apache/freemarker/core/model/impl/DefaultObjectWrapperTest.java
+++ 
b/freemarker-core-test/src/test/java/org/apache/freemarker/core/model/impl/DefaultObjectWrapperTest.java
@@ -35,6 +35,7 @@ import java.util.LinkedHashMap;
 import java.util.LinkedList;
 import java.util.List;
 import java.util.Map;
+import java.util.NoSuchElementException;
 import java.util.Set;
 import java.util.TreeMap;
 import java.util.TreeSet;
@@ -50,8 +51,8 @@ import org.apache.freemarker.core.model.AdapterTemplateModel;
 import org.apache.freemarker.core.model.ObjectWrapper;
 import org.apache.freemarker.core.model.ObjectWrappingException;
 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.TemplateHashModel;
 import org.apache.freemarker.core.model.TemplateHashModelEx;
 import org.apache.freemarker.core.model.TemplateModel;
@@ -172,8 +173,8 @@ public class DefaultObjectWrapperTest {
         CustomizedDefaultObjectWrapper ow = new 
CustomizedDefaultObjectWrapper(Configuration.VERSION_3_0_0);
         assertEquals(Configuration.VERSION_3_0_0, 
ow.getIncompatibleImprovements());
 
-        TemplateSequenceModel seq = (TemplateSequenceModel) ow.wrap(new 
Tupple(11, 22));
-        assertEquals(2, seq.size());
+        TemplateSequenceModel seq = (TemplateSequenceModel) ow.wrap(new 
Tuple(11, 22));
+        assertEquals(2, seq.getCollectionSize());
         assertEquals(11, ow.unwrap(seq.get(0)));
         assertEquals(22, ow.unwrap(seq.get(1)));
         
@@ -199,11 +200,11 @@ public class DefaultObjectWrapperTest {
         }
         {
             // Check custom TM usage and round trip:
-            final TemplateModel mr = ((JavaMethodModel) bean.get("incTupple"))
-                    .execute(new TemplateModel[] { ow.wrap(new Tupple<>(1, 2)) 
},
+            final TemplateModel mr = ((JavaMethodModel) bean.get("incTuple"))
+                    .execute(new TemplateModel[] { ow.wrap(new Tuple<>(1, 2)) 
},
                             NonTemplateCallPlace.INSTANCE);
-            assertEquals(new Tupple<>(2, 3), ow.unwrap(mr));
-            assertTrue(TuppleAdapter.class.isInstance(mr));
+            assertEquals(new Tuple<>(2, 3), ow.unwrap(mr));
+            assertTrue(TupleAdapter.class.isInstance(mr));
         }
     }
 
@@ -256,8 +257,8 @@ public class DefaultObjectWrapperTest {
 
         {
             TemplateHashModelEx hash = (TemplateHashModelEx) OW.wrap(testMap);
-            assertEquals(4, hash.size());
-            assertFalse(hash.isEmpty());
+            assertEquals(4, hash.getHashSize());
+            assertFalse(hash.isEmptyHash());
             assertNull(hash.get("e"));
             assertEquals(1, ((TemplateNumberModel) 
hash.get("a")).getAsNumber());
             assertNull(hash.get("b"));
@@ -271,11 +272,11 @@ public class DefaultObjectWrapperTest {
         }
 
         {
-            assertTrue(((TemplateHashModel) 
OW.wrap(Collections.emptyMap())).isEmpty());
+            assertTrue(((TemplateHashModel) 
OW.wrap(Collections.emptyMap())).isEmptyHash());
         }
     }
 
-    private void assertCollectionTMEquals(TemplateCollectionModel coll, 
Object... expectedItems)
+    private void assertCollectionTMEquals(TemplateIterableModel coll, 
Object... expectedItems)
             throws TemplateException {
         for (int i = 0; i < 2; i++) { // Run twice to check if we always get a 
new iterator
             int idx = 0;
@@ -314,8 +315,7 @@ public class DefaultObjectWrapperTest {
 
             TemplateSequenceModel seq = (TemplateSequenceModel) 
OW.wrap(testList);
             assertTrue(seq instanceof DefaultListAdapter);
-            assertFalse(seq instanceof TemplateCollectionModel); // Maybe 
changes at 2.4.0
-            assertEquals(4, seq.size());
+            assertEquals(4, seq.getCollectionSize());
             assertNull(seq.get(-1));
             assertEquals(1, ((TemplateNumberModel) seq.get(0)).getAsNumber());
             assertNull(seq.get(1));
@@ -334,25 +334,24 @@ public class DefaultObjectWrapperTest {
 
             TemplateSequenceModel seq = (TemplateSequenceModel) 
OW.wrap(testList);
             assertTrue(seq instanceof DefaultListAdapter);
-            assertTrue(seq instanceof TemplateCollectionModel); // Maybe 
changes at 2.4.0
-            assertEquals(3, seq.size());
+            assertEquals(3, seq.getCollectionSize());
             assertNull(seq.get(-1));
             assertEquals(1, ((TemplateNumberModel) seq.get(0)).getAsNumber());
             assertNull(seq.get(1));
             assertEquals("c", ((TemplateStringModel) 
seq.get(2)).getAsString());
             assertNull(seq.get(3));
 
-            assertCollectionTMEquals((TemplateCollectionModel) seq, 1, null, 
"c");
+            assertCollectionTMEquals((TemplateIterableModel) seq, 1, null, 
"c");
 
-            TemplateModelIterator it = ((TemplateCollectionModel) 
seq).iterator();
+            TemplateModelIterator it = ((TemplateIterableModel) 
seq).iterator();
             it.next();
             it.next();
             it.next();
             try {
                 it.next();
                 fail();
-            } catch (TemplateException e) {
-                assertThat(e.getMessage(), containsString("no more"));
+            } catch (NoSuchElementException e) {
+                // Expected, for this implementation
             }
         }
     }
@@ -384,7 +383,7 @@ public class DefaultObjectWrapperTest {
             final String[] testArray = new String[] { "a", null, "c" };
 
             TemplateSequenceModel seq = (TemplateSequenceModel) 
OW.wrap(testArray);
-            assertEquals(3, seq.size());
+            assertEquals(3, seq.getCollectionSize());
             assertNull(seq.get(-1));
             assertEquals("a", ((TemplateStringModel) 
seq.get(0)).getAsString());
             assertNull(seq.get(1));
@@ -395,7 +394,7 @@ public class DefaultObjectWrapperTest {
         {
             final int[] testArray = new int[] { 11, 22 };
             TemplateSequenceModel seq = (TemplateSequenceModel) 
OW.wrap(testArray);
-            assertEquals(2, seq.size());
+            assertEquals(2, seq.getCollectionSize());
             assertNull(seq.get(-1));
             assertEqualsAndSameClass(Integer.valueOf(11), 
((TemplateNumberModel) seq.get(0)).getAsNumber());
             assertEqualsAndSameClass(Integer.valueOf(22), 
((TemplateNumberModel) seq.get(1)).getAsNumber());
@@ -405,7 +404,7 @@ public class DefaultObjectWrapperTest {
         {
             final double[] testArray = new double[] { 11, 22 };
             TemplateSequenceModel seq = (TemplateSequenceModel) 
OW.wrap(testArray);
-            assertEquals(2, seq.size());
+            assertEquals(2, seq.getCollectionSize());
             assertNull(seq.get(-1));
             assertEqualsAndSameClass(Double.valueOf(11), 
((TemplateNumberModel) seq.get(0)).getAsNumber());
             assertEqualsAndSameClass(Double.valueOf(22), 
((TemplateNumberModel) seq.get(1)).getAsNumber());
@@ -415,7 +414,7 @@ public class DefaultObjectWrapperTest {
         {
             final boolean[] testArray = new boolean[] { true, false };
             TemplateSequenceModel seq = (TemplateSequenceModel) 
OW.wrap(testArray);
-            assertEquals(2, seq.size());
+            assertEquals(2, seq.getCollectionSize());
             assertNull(seq.get(-1));
             assertEqualsAndSameClass(Boolean.valueOf(true), 
((TemplateBooleanModel) seq.get(0)).getAsBoolean());
             assertEqualsAndSameClass(Boolean.valueOf(false), 
((TemplateBooleanModel) seq.get(1)).getAsBoolean());
@@ -425,7 +424,7 @@ public class DefaultObjectWrapperTest {
         {
             final char[] testArray = new char[] { 'a', 'b' };
             TemplateSequenceModel seq = (TemplateSequenceModel) 
OW.wrap(testArray);
-            assertEquals(2, seq.size());
+            assertEquals(2, seq.getCollectionSize());
             assertNull(seq.get(-1));
             assertEquals("a", ((TemplateStringModel) 
seq.get(0)).getAsString());
             assertEquals("b", ((TemplateStringModel) 
seq.get(1)).getAsString());
@@ -471,10 +470,10 @@ public class DefaultObjectWrapperTest {
             set.add("a");
             set.add("b");
             set.add("c");
-            TemplateCollectionModelEx coll = (TemplateCollectionModelEx) 
OW.wrap(set);
+            TemplateCollectionModel coll = (TemplateCollectionModel) 
OW.wrap(set);
             assertTrue(coll instanceof DefaultNonListCollectionAdapter);
-            assertEquals(3, coll.size());
-            assertFalse(coll.isEmpty());
+            assertEquals(3, coll.getCollectionSize());
+            assertFalse(coll.isEmptyCollection());
             assertCollectionTMEquals(coll, "a", "b", "c");
 
             assertRoundtrip(OW, set, DefaultNonListCollectionAdapter.class, 
TreeSet.class, "[a, b, c]");
@@ -487,7 +486,7 @@ public class DefaultObjectWrapperTest {
             final List<String> list = Collections.singletonList("b");
             set.add(list);
             set.add(null);
-            TemplateCollectionModelEx coll = (TemplateCollectionModelEx) 
OW.wrap(set);
+            TemplateCollectionModel coll = (TemplateCollectionModel) 
OW.wrap(set);
             TemplateModelIterator it = coll.iterator();
             final TemplateModel tm1 = it.next();
             Object obj1 = OW.unwrap(tm1);
@@ -507,7 +506,7 @@ public class DefaultObjectWrapperTest {
     public void testCollectionAdapterOutOfBounds() throws TemplateException {
         Set set = Collections.singleton(123);
 
-        TemplateCollectionModelEx coll = (TemplateCollectionModelEx) 
OW.wrap(set);
+        TemplateCollectionModel coll = (TemplateCollectionModel) OW.wrap(set);
         TemplateModelIterator it = coll.iterator();
 
         for (int i = 0; i < 3; i++) {
@@ -521,8 +520,8 @@ public class DefaultObjectWrapperTest {
             try {
                 it.next();
                 fail();
-            } catch (TemplateException e) {
-                assertThat(e.getMessage(), containsStringIgnoringCase("no 
more"));
+            } catch (NoSuchElementException e) {
+                // Expected, for this implementation
             }
         }
     }
@@ -532,9 +531,9 @@ public class DefaultObjectWrapperTest {
         Set set = new HashSet();
         set.add(null);
 
-        TemplateCollectionModelEx coll = (TemplateCollectionModelEx) 
OW.wrap(set);
-        assertEquals(1, coll.size());
-        assertFalse(coll.isEmpty());
+        TemplateCollectionModel coll = (TemplateCollectionModel) OW.wrap(set);
+        assertEquals(1, coll.getCollectionSize());
+        assertFalse(coll.isEmptyCollection());
         assertNull(coll.iterator().next());
     }
 
@@ -542,7 +541,7 @@ public class DefaultObjectWrapperTest {
     public void testIteratorWrapping() throws TemplateException, 
ClassNotFoundException {
         final List<String> list = ImmutableList.of("a", "b", "c");
         Iterator<String> it = list.iterator();
-        TemplateCollectionModel coll = (TemplateCollectionModel) OW.wrap(it);
+        TemplateIterableModel coll = (TemplateIterableModel) OW.wrap(it);
 
         assertRoundtrip(OW, coll, DefaultIteratorAdapter.class, 
Iterator.class, null);
 
@@ -558,8 +557,8 @@ public class DefaultObjectWrapperTest {
         try {
             itIt.next();
             fail();
-        } catch (TemplateException e) {
-            assertThat(e.getMessage(), containsStringIgnoringCase("no more"));
+        } catch (NoSuchElementException e) {
+            // Expected, for this implementation
         }
 
         try {
@@ -629,8 +628,8 @@ public class DefaultObjectWrapperTest {
         
         DefaultObjectWrapper ow = OW;
         TemplateModel tm = ow.wrap(iterable);
-        assertThat(tm, instanceOf(TemplateCollectionModel.class));
-        TemplateCollectionModel iterableTM = (TemplateCollectionModel) tm;
+        assertThat(tm, instanceOf(TemplateIterableModel.class));
+        TemplateIterableModel iterableTM = (TemplateIterableModel) tm;
 
         for (int i = 0; i < 2; i++) {
             TemplateModelIterator iteratorTM = iterableTM.iterator();
@@ -644,8 +643,8 @@ public class DefaultObjectWrapperTest {
             try {
                 iteratorTM.next();
                 fail();
-            } catch (TemplateException e) {
-                assertThat(e.getMessage(), containsStringIgnoringCase("no 
more"));
+            } catch (NoSuchElementException e) {
+                // Expected, for this implementation
             }
         }
 
@@ -782,12 +781,12 @@ public class DefaultObjectWrapperTest {
 
     }
     
-    private static class Tupple<E1, E2> {
+    private static class Tuple<E1, E2> {
         
         private final E1 e1;
         private final E2 e2;
 
-        public Tupple(E1 e1, E2 e2) {
+        public Tuple(E1 e1, E2 e2) {
             if (e1 == null || e2 == null) throw new NullPointerException();
             this.e1 = e1;
             this.e2 = e2;
@@ -815,7 +814,7 @@ public class DefaultObjectWrapperTest {
             if (this == obj) return true;
             if (obj == null) return false;
             if (getClass() != obj.getClass()) return false;
-            Tupple other = (Tupple) obj;
+            Tuple other = (Tuple) obj;
             if (e1 == null) {
                 if (other.e1 != null) return false;
             } else if (!e1.equals(other.e1)) return false;
@@ -838,8 +837,8 @@ public class DefaultObjectWrapperTest {
             return Collections.singletonList(1);
         }
 
-        public Tupple incTupple(Tupple<Integer, Integer> tupple) {
-            return new Tupple(tupple.e1 + 1, tupple.e2 + 1);
+        public Tuple incTuple(Tuple<Integer, Integer> tupple) {
+            return new Tuple(tupple.e1 + 1, tupple.e2 + 1);
         }
         
     }
@@ -852,8 +851,8 @@ public class DefaultObjectWrapperTest {
         
         @Override
         protected TemplateModel wrapGenericObject(final Object obj) throws 
ObjectWrappingException {
-            if (obj instanceof Tupple) {
-                return new TuppleAdapter((Tupple<?, ?>) obj, this);
+            if (obj instanceof Tuple) {
+                return new TupleAdapter((Tuple<?, ?>) obj, this);
             }
             
             return super.wrapGenericObject(obj);
@@ -861,35 +860,45 @@ public class DefaultObjectWrapperTest {
         
     }
     
-    private static class TuppleAdapter extends WrappingTemplateModel 
implements TemplateSequenceModel,
+    private static class TupleAdapter extends WrappingTemplateModel implements 
TemplateSequenceModel,
             AdapterTemplateModel {
         
-        private final Tupple<?, ?> tupple;
+        private final Tuple<?, ?> tuple;
         
-        public TuppleAdapter(Tupple<?, ?> tupple, ObjectWrapper ow) {
+        public TupleAdapter(Tuple<?, ?> tuple, ObjectWrapper ow) {
             super(ow);
-            this.tupple = tupple;
+            this.tuple = tuple;
         }
 
         @Override
-        public int size() throws TemplateException {
+        public int getCollectionSize() throws TemplateException {
             return 2;
         }
         
         @Override
         public TemplateModel get(int index) throws TemplateException {
             switch (index) {
-            case 0: return wrap(tupple.getE1());
-            case 1: return wrap(tupple.getE2());
+            case 0: return wrap(tuple.getE1());
+            case 1: return wrap(tuple.getE2());
             default: return null;
             }
         }
 
         @Override
+        public boolean isEmptyCollection() throws TemplateException {
+            return false;
+        }
+
+        @Override
+        public TemplateModelIterator iterator() throws TemplateException {
+            return new SequenceTemplateModelIterator(this);
+        }
+
+        @Override
         public Object getAdaptedObject(Class hint) {
-            return tupple;
+            return tuple;
         }
-        
+
     }
 
 }

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/cae86e18/freemarker-core-test/src/test/java/org/apache/freemarker/core/model/impl/RestrictedObjectWrapperTest.java
----------------------------------------------------------------------
diff --git 
a/freemarker-core-test/src/test/java/org/apache/freemarker/core/model/impl/RestrictedObjectWrapperTest.java
 
b/freemarker-core-test/src/test/java/org/apache/freemarker/core/model/impl/RestrictedObjectWrapperTest.java
index eb9ad4c..8a6317c 100644
--- 
a/freemarker-core-test/src/test/java/org/apache/freemarker/core/model/impl/RestrictedObjectWrapperTest.java
+++ 
b/freemarker-core-test/src/test/java/org/apache/freemarker/core/model/impl/RestrictedObjectWrapperTest.java
@@ -41,8 +41,8 @@ import org.apache.freemarker.core.Configuration;
 import org.apache.freemarker.core.TemplateException;
 import org.apache.freemarker.core.model.ObjectWrappingException;
 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.TemplateHashModelEx2;
 import org.apache.freemarker.core.model.TemplateModelWithAPISupport;
@@ -106,8 +106,8 @@ public class RestrictedObjectWrapperTest {
         assertTrue(sow.wrap(new Date()) instanceof TemplateDateModel);
         assertTrue(sow.wrap(new ArrayList()) instanceof TemplateSequenceModel);
         assertTrue(sow.wrap(new String[0]) instanceof TemplateSequenceModel);
-        assertTrue(sow.wrap(new ArrayList().iterator()) instanceof 
TemplateCollectionModel);
-        assertTrue(sow.wrap(new HashSet()) instanceof 
TemplateCollectionModelEx);
+        assertTrue(sow.wrap(new ArrayList().iterator()) instanceof 
TemplateIterableModel);
+        assertTrue(sow.wrap(new HashSet()) instanceof TemplateCollectionModel);
         assertTrue(sow.wrap(new HashMap()) instanceof TemplateHashModelEx2);
         assertNull(sow.wrap(null));
     }

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/cae86e18/freemarker-core-test/src/test/java/org/apache/freemarker/core/templatesuite/CoreTemplateTestSuite.java
----------------------------------------------------------------------
diff --git 
a/freemarker-core-test/src/test/java/org/apache/freemarker/core/templatesuite/CoreTemplateTestSuite.java
 
b/freemarker-core-test/src/test/java/org/apache/freemarker/core/templatesuite/CoreTemplateTestSuite.java
index e7a5caf..376e303 100644
--- 
a/freemarker-core-test/src/test/java/org/apache/freemarker/core/templatesuite/CoreTemplateTestSuite.java
+++ 
b/freemarker-core-test/src/test/java/org/apache/freemarker/core/templatesuite/CoreTemplateTestSuite.java
@@ -23,7 +23,7 @@ import org.apache.freemarker.core.model.TemplateDateModel;
 import org.apache.freemarker.core.model.impl.DefaultNonListCollectionAdapter;
 import org.apache.freemarker.core.model.impl.DefaultObjectWrapper;
 import org.apache.freemarker.core.model.impl.ResourceBundleModel;
-import org.apache.freemarker.core.model.impl.SimpleCollection;
+import org.apache.freemarker.core.model.impl.SimpleIterable;
 import org.apache.freemarker.core.model.impl.SimpleDate;
 import org.apache.freemarker.core.model.impl.SimpleNumber;
 import 
org.apache.freemarker.core.templatesuite.models.BooleanAndStringTemplateModel;
@@ -158,10 +158,10 @@ public class CoreTemplateTestSuite extends 
TemplateTestSuite {
         } else if (simpleTestName.equals("stringbimethods")) {
             dataModel.put("multi", new TestBoolean());
         } else if (simpleTestName.startsWith("type-builtins")) {
-            dataModel.put("testfunction", new SimpleTestFunction());
-            dataModel.put("testnode", new TestNode());
-            dataModel.put("testcollection", new SimpleCollection(new 
ArrayList<>(), dow));
-            dataModel.put("testcollectionEx", 
DefaultNonListCollectionAdapter.adapt(new HashSet<>(), dow));
+            dataModel.put("testFunction", new SimpleTestFunction());
+            dataModel.put("testNode", new TestNode());
+            dataModel.put("testIterable", new SimpleIterable(new 
ArrayList<>(), dow));
+            dataModel.put("testCollection", 
DefaultNonListCollectionAdapter.adapt(new HashSet<>(), dow));
             dataModel.put("bean", new TestBean());
         } else if (simpleTestName.equals("date-type-builtins")) {
             GregorianCalendar cal = new GregorianCalendar(2003, 4 - 1, 5, 6, 
7, 8);
@@ -197,7 +197,7 @@ public class CoreTemplateTestSuite extends 
TemplateTestSuite {
             listWithNull.add(null);
             dataModel.put("listWithNullsOnly", listWithNullsOnly);
 
-            dataModel.put("abcCollection", new SimpleCollection(abcSet, dow));
+            dataModel.put("abcCollection", new SimpleIterable(abcSet, dow));
 
             Set<String> set = new HashSet<>();
             set.add("a");

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/cae86e18/freemarker-core-test/src/test/java/org/apache/freemarker/core/templatesuite/models/AllTemplateModels.java
----------------------------------------------------------------------
diff --git 
a/freemarker-core-test/src/test/java/org/apache/freemarker/core/templatesuite/models/AllTemplateModels.java
 
b/freemarker-core-test/src/test/java/org/apache/freemarker/core/templatesuite/models/AllTemplateModels.java
index 4102057..3a9009b 100644
--- 
a/freemarker-core-test/src/test/java/org/apache/freemarker/core/templatesuite/models/AllTemplateModels.java
+++ 
b/freemarker-core-test/src/test/java/org/apache/freemarker/core/templatesuite/models/AllTemplateModels.java
@@ -23,7 +23,7 @@ import java.util.Date;
 
 import org.apache.freemarker.core.TemplateException;
 import org.apache.freemarker.core.model.TemplateBooleanModel;
-import org.apache.freemarker.core.model.TemplateCollectionModel;
+import org.apache.freemarker.core.model.TemplateIterableModel;
 import org.apache.freemarker.core.model.TemplateDateModel;
 import org.apache.freemarker.core.model.TemplateHashModelEx;
 import org.apache.freemarker.core.model.TemplateModel;
@@ -38,45 +38,28 @@ import org.apache.freemarker.core.model.impl.SimpleString;
  */
 public class AllTemplateModels implements
         TemplateStringModel, TemplateNumberModel, TemplateDateModel, 
TemplateBooleanModel,
-        TemplateHashModelEx, TemplateSequenceModel, TemplateCollectionModel {
+        TemplateHashModelEx, TemplateSequenceModel, TemplateIterableModel {
 
     public static final AllTemplateModels INSTANCE = new AllTemplateModels();
-    
-    private final TemplateModelIterator EMPTY_ITERATOR = new 
TemplateModelIterator() {
-
-        @Override
-        public TemplateModel next() throws TemplateException {
-            return null;
-        }
-
-        @Override
-        public boolean hasNext() throws TemplateException {
-            return false;
-        }
-        
-    };
-    
-    private final TemplateCollectionModel EMPTY_COLLECTION = new 
TemplateCollectionModel() {
-
-        @Override
-        public TemplateModelIterator iterator() throws TemplateException {
-            return EMPTY_ITERATOR;
-        }
-    };
-    
+
     @Override
     public TemplateModel get(String key) throws TemplateException {
         return new SimpleString("value for key " + key);
     }
 
     @Override
-    public boolean isEmpty() throws TemplateException {
+    public boolean isEmptyHash() throws TemplateException {
         return true;
     }
 
     @Override
+    public int getHashSize() throws TemplateException {
+        return 0;
+    }
+
+    @Override
     public TemplateModelIterator iterator() throws TemplateException {
-        return EMPTY_ITERATOR;
+        return TemplateModelIterator.EMPTY_ITERATOR;
     }
 
     @Override
@@ -85,18 +68,23 @@ public class AllTemplateModels implements
     }
 
     @Override
-    public int size() throws TemplateException {
+    public int getCollectionSize() throws TemplateException {
         return 0;
     }
 
     @Override
-    public TemplateCollectionModel keys() throws TemplateException {
-        return EMPTY_COLLECTION;
+    public boolean isEmptyCollection() throws TemplateException {
+        return true;
+    }
+
+    @Override
+    public TemplateIterableModel keys() throws TemplateException {
+        return EMPTY_ITERABLE;
     }
 
     @Override
-    public TemplateCollectionModel values() throws TemplateException {
-        return EMPTY_COLLECTION;
+    public TemplateIterableModel values() throws TemplateException {
+        return EMPTY_ITERABLE;
     }
 
     @Override

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/cae86e18/freemarker-core-test/src/test/java/org/apache/freemarker/core/templatesuite/models/BooleanHash1.java
----------------------------------------------------------------------
diff --git 
a/freemarker-core-test/src/test/java/org/apache/freemarker/core/templatesuite/models/BooleanHash1.java
 
b/freemarker-core-test/src/test/java/org/apache/freemarker/core/templatesuite/models/BooleanHash1.java
index 14d1f7c..9302f68 100644
--- 
a/freemarker-core-test/src/test/java/org/apache/freemarker/core/templatesuite/models/BooleanHash1.java
+++ 
b/freemarker-core-test/src/test/java/org/apache/freemarker/core/templatesuite/models/BooleanHash1.java
@@ -25,7 +25,7 @@ import org.apache.freemarker.core.model.TemplateModel;
 import org.apache.freemarker.core.model.impl.SimpleString;
 
 /**
- * Tests the impact that the isEmpty() has on template hash models.
+ * Tests the impact that the isEmptyHash() has on template hash models.
  */
 public class BooleanHash1 implements TemplateHashModel {
 
@@ -52,7 +52,7 @@ public class BooleanHash1 implements TemplateHashModel {
      * @return true if this object is empty.
      */
     @Override
-    public boolean isEmpty() {
+    public boolean isEmptyHash() {
         return true;
     }
 }

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/cae86e18/freemarker-core-test/src/test/java/org/apache/freemarker/core/templatesuite/models/BooleanHash2.java
----------------------------------------------------------------------
diff --git 
a/freemarker-core-test/src/test/java/org/apache/freemarker/core/templatesuite/models/BooleanHash2.java
 
b/freemarker-core-test/src/test/java/org/apache/freemarker/core/templatesuite/models/BooleanHash2.java
index 070b3e2..54e9cb3 100644
--- 
a/freemarker-core-test/src/test/java/org/apache/freemarker/core/templatesuite/models/BooleanHash2.java
+++ 
b/freemarker-core-test/src/test/java/org/apache/freemarker/core/templatesuite/models/BooleanHash2.java
@@ -23,7 +23,7 @@ import org.apache.freemarker.core.model.TemplateHashModel;
 import org.apache.freemarker.core.model.TemplateModel;
 
 /**
- * Tests the impact that the isEmpty() has on template hash models.
+ * Tests the impact that the isEmptyHash() has on template hash models.
  */
 public class BooleanHash2 implements TemplateHashModel {
 
@@ -44,7 +44,7 @@ public class BooleanHash2 implements TemplateHashModel {
      * @return true if this object is empty.
      */
     @Override
-    public boolean isEmpty() {
+    public boolean isEmptyHash() {
         return false;
     }
 }

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/cae86e18/freemarker-core-test/src/test/java/org/apache/freemarker/core/templatesuite/models/BooleanList1.java
----------------------------------------------------------------------
diff --git 
a/freemarker-core-test/src/test/java/org/apache/freemarker/core/templatesuite/models/BooleanList1.java
 
b/freemarker-core-test/src/test/java/org/apache/freemarker/core/templatesuite/models/BooleanList1.java
index 8f3c3fd..a95a898 100644
--- 
a/freemarker-core-test/src/test/java/org/apache/freemarker/core/templatesuite/models/BooleanList1.java
+++ 
b/freemarker-core-test/src/test/java/org/apache/freemarker/core/templatesuite/models/BooleanList1.java
@@ -23,11 +23,12 @@ import org.apache.freemarker.core.TemplateException;
 import org.apache.freemarker.core.model.ObjectWrapper;
 import org.apache.freemarker.core.model.TemplateBooleanModel;
 import org.apache.freemarker.core.model.TemplateModel;
+import org.apache.freemarker.core.model.TemplateModelIterator;
 import org.apache.freemarker.core.model.TemplateSequenceModel;
 import org.apache.freemarker.core.model.impl.SimpleSequence;
 
 /**
- * Model for testing the impact of isEmpty() on template list models. Every
+ * Model for testing the impact of isEmptyCollection() on template list 
models. Every
  * other method simply delegates to a SimpleList model.
  */
 public class BooleanList1 implements TemplateSequenceModel {
@@ -55,8 +56,18 @@ public class BooleanList1 implements TemplateSequenceModel {
     }
 
     @Override
-    public int size() {
-        return cList.size();
+    public int getCollectionSize() {
+        return cList.getCollectionSize();
+    }
+
+    @Override
+    public boolean isEmptyCollection() throws TemplateException {
+        return cList.isEmptyCollection();
+    }
+
+    @Override
+    public TemplateModelIterator iterator() throws TemplateException {
+        return cList.iterator();
     }
 
 }

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/cae86e18/freemarker-core-test/src/test/java/org/apache/freemarker/core/templatesuite/models/BooleanList2.java
----------------------------------------------------------------------
diff --git 
a/freemarker-core-test/src/test/java/org/apache/freemarker/core/templatesuite/models/BooleanList2.java
 
b/freemarker-core-test/src/test/java/org/apache/freemarker/core/templatesuite/models/BooleanList2.java
index 3911deb..5c4448b 100644
--- 
a/freemarker-core-test/src/test/java/org/apache/freemarker/core/templatesuite/models/BooleanList2.java
+++ 
b/freemarker-core-test/src/test/java/org/apache/freemarker/core/templatesuite/models/BooleanList2.java
@@ -22,6 +22,7 @@ package org.apache.freemarker.core.templatesuite.models;
 import org.apache.freemarker.core.TemplateException;
 import org.apache.freemarker.core.model.ObjectWrapper;
 import org.apache.freemarker.core.model.TemplateModel;
+import org.apache.freemarker.core.model.TemplateModelIterator;
 import org.apache.freemarker.core.model.TemplateSequenceModel;
 import org.apache.freemarker.core.model.impl.SimpleSequence;
 
@@ -47,7 +48,17 @@ public class BooleanList2 implements TemplateSequenceModel {
     }
 
     @Override
-    public int size() {
-        return cList.size();
+    public int getCollectionSize() {
+        return cList.getCollectionSize();
+    }
+
+    @Override
+    public boolean isEmptyCollection() throws TemplateException {
+        return cList.isEmptyCollection();
+    }
+
+    @Override
+    public TemplateModelIterator iterator() throws TemplateException {
+        return cList.iterator();
     }
 }

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/cae86e18/freemarker-core-test/src/test/java/org/apache/freemarker/core/templatesuite/models/HashAndStringModel.java
----------------------------------------------------------------------
diff --git 
a/freemarker-core-test/src/test/java/org/apache/freemarker/core/templatesuite/models/HashAndStringModel.java
 
b/freemarker-core-test/src/test/java/org/apache/freemarker/core/templatesuite/models/HashAndStringModel.java
index 63fc193..2e0104d 100644
--- 
a/freemarker-core-test/src/test/java/org/apache/freemarker/core/templatesuite/models/HashAndStringModel.java
+++ 
b/freemarker-core-test/src/test/java/org/apache/freemarker/core/templatesuite/models/HashAndStringModel.java
@@ -20,36 +20,15 @@
 package org.apache.freemarker.core.templatesuite.models;
 
 import org.apache.freemarker.core.TemplateException;
-import org.apache.freemarker.core.model.TemplateCollectionModel;
 import org.apache.freemarker.core.model.TemplateHashModelEx;
+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.impl.SimpleString;
 
 public class HashAndStringModel implements TemplateHashModelEx, 
TemplateStringModel {
     
     public static final HashAndStringModel INSTANCE = new HashAndStringModel();
-    
-    private final TemplateCollectionModel EMPTY_COLLECTION = new 
TemplateCollectionModel() {
-
-        @Override
-        public TemplateModelIterator iterator() throws TemplateException {
-            return new TemplateModelIterator() {
-
-                @Override
-                public TemplateModel next() throws TemplateException {
-                    return null;
-                }
-
-                @Override
-                public boolean hasNext() throws TemplateException {
-                    return false;
-                }
-                
-            };
-        }
-    };
 
     @Override
     public String getAsString() throws TemplateException {
@@ -62,23 +41,23 @@ public class HashAndStringModel implements 
TemplateHashModelEx, TemplateStringMo
     }
 
     @Override
-    public boolean isEmpty() throws TemplateException {
+    public boolean isEmptyHash() throws TemplateException {
         return true;
     }
 
     @Override
-    public int size() throws TemplateException {
+    public int getHashSize() throws TemplateException {
         return 0;
     }
 
     @Override
-    public TemplateCollectionModel keys() throws TemplateException {
-        return EMPTY_COLLECTION;
+    public TemplateIterableModel keys() throws TemplateException {
+        return TemplateIterableModel.EMPTY_ITERABLE;
     }
 
     @Override
-    public TemplateCollectionModel values() throws TemplateException {
-        return EMPTY_COLLECTION;
+    public TemplateIterableModel values() throws TemplateException {
+        return TemplateIterableModel.EMPTY_ITERABLE;
     }
 
 }

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/cae86e18/freemarker-core-test/src/test/java/org/apache/freemarker/core/templatesuite/models/Listables.java
----------------------------------------------------------------------
diff --git 
a/freemarker-core-test/src/test/java/org/apache/freemarker/core/templatesuite/models/Listables.java
 
b/freemarker-core-test/src/test/java/org/apache/freemarker/core/templatesuite/models/Listables.java
index 700af44..ed8b48d 100644
--- 
a/freemarker-core-test/src/test/java/org/apache/freemarker/core/templatesuite/models/Listables.java
+++ 
b/freemarker-core-test/src/test/java/org/apache/freemarker/core/templatesuite/models/Listables.java
@@ -32,14 +32,14 @@ import org.apache.freemarker.core.Configuration;
 import org.apache.freemarker.core.TemplateException;
 import org.apache.freemarker.core.model.ObjectWrapper;
 import org.apache.freemarker.core.model.ObjectWrappingException;
-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.TemplateHashModelEx2;
 import org.apache.freemarker.core.model.TemplateModel;
 import org.apache.freemarker.core.model.WrappingTemplateModel;
 import org.apache.freemarker.core.model.impl.DefaultMapAdapter;
 import org.apache.freemarker.core.model.impl.DefaultObjectWrapper;
-import org.apache.freemarker.core.model.impl.SimpleCollection;
+import org.apache.freemarker.core.model.impl.SimpleIterable;
 import org.apache.freemarker.core.model.impl.SimpleHash;
 
 import com.google.common.collect.ImmutableMap;
@@ -162,23 +162,23 @@ public class Listables {
         }
         
         @Override
-        public boolean isEmpty() {
+        public boolean isEmptyHash() {
             return map.isEmpty();
         }
         
         @Override
-        public int size() {
+        public int getHashSize() {
             return map.size();
         }
         
         @Override
-        public TemplateCollectionModel keys() {
-            return new SimpleCollection(map.keySet(), getObjectWrapper());
+        public TemplateIterableModel keys() {
+            return new SimpleIterable(map.keySet(), getObjectWrapper());
         }
         
         @Override
-        public TemplateCollectionModel values() {
-            return new SimpleCollection(map.values(), getObjectWrapper());
+        public TemplateIterableModel values() {
+            return new SimpleIterable(map.values(), getObjectWrapper());
         }
         
     }

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/cae86e18/freemarker-core-test/src/test/java/org/apache/freemarker/core/templatesuite/models/MultiModel1.java
----------------------------------------------------------------------
diff --git 
a/freemarker-core-test/src/test/java/org/apache/freemarker/core/templatesuite/models/MultiModel1.java
 
b/freemarker-core-test/src/test/java/org/apache/freemarker/core/templatesuite/models/MultiModel1.java
index 5bda604..b008ac2 100644
--- 
a/freemarker-core-test/src/test/java/org/apache/freemarker/core/templatesuite/models/MultiModel1.java
+++ 
b/freemarker-core-test/src/test/java/org/apache/freemarker/core/templatesuite/models/MultiModel1.java
@@ -24,6 +24,7 @@ import org.apache.freemarker.core.TemplateException;
 import org.apache.freemarker.core.model.ObjectWrapper;
 import org.apache.freemarker.core.model.TemplateHashModel;
 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.DefaultObjectWrapper;
@@ -75,7 +76,7 @@ public class MultiModel1 implements TemplateHashModel,
     }
 
     @Override
-    public boolean isEmpty() {
+    public boolean isEmptyHash() {
         return false;
     }
 
@@ -90,7 +91,18 @@ public class MultiModel1 implements TemplateHashModel,
     }
 
     @Override
-    public int size() throws TemplateException {
-        return m_cListModel.size();
+    public int getCollectionSize() throws TemplateException {
+        return m_cListModel.getCollectionSize();
     }
+
+    @Override
+    public boolean isEmptyCollection() throws TemplateException {
+        return m_cListModel.isEmptyCollection();
+    }
+
+    @Override
+    public TemplateModelIterator iterator() throws TemplateException {
+        return m_cListModel.iterator();
+    }
+
 }

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/cae86e18/freemarker-core-test/src/test/java/org/apache/freemarker/core/templatesuite/models/MultiModel3.java
----------------------------------------------------------------------
diff --git 
a/freemarker-core-test/src/test/java/org/apache/freemarker/core/templatesuite/models/MultiModel3.java
 
b/freemarker-core-test/src/test/java/org/apache/freemarker/core/templatesuite/models/MultiModel3.java
index 4048f73..4f96f13 100644
--- 
a/freemarker-core-test/src/test/java/org/apache/freemarker/core/templatesuite/models/MultiModel3.java
+++ 
b/freemarker-core-test/src/test/java/org/apache/freemarker/core/templatesuite/models/MultiModel3.java
@@ -35,7 +35,7 @@ public class MultiModel3 implements TemplateStringModel, 
TemplateHashModel {
     }
 
     @Override
-    public boolean isEmpty() {
+    public boolean isEmptyHash() {
         return false;
     }
 

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/cae86e18/freemarker-core-test/src/test/java/org/apache/freemarker/core/templatesuite/models/MultiModel4.java
----------------------------------------------------------------------
diff --git 
a/freemarker-core-test/src/test/java/org/apache/freemarker/core/templatesuite/models/MultiModel4.java
 
b/freemarker-core-test/src/test/java/org/apache/freemarker/core/templatesuite/models/MultiModel4.java
index f0b516a..9d6b8e5 100644
--- 
a/freemarker-core-test/src/test/java/org/apache/freemarker/core/templatesuite/models/MultiModel4.java
+++ 
b/freemarker-core-test/src/test/java/org/apache/freemarker/core/templatesuite/models/MultiModel4.java
@@ -23,6 +23,7 @@ import org.apache.freemarker.core.TemplateException;
 import org.apache.freemarker.core.model.ObjectWrapper;
 import org.apache.freemarker.core.model.TemplateHashModel;
 import org.apache.freemarker.core.model.TemplateModel;
+import org.apache.freemarker.core.model.TemplateModelIterator;
 import org.apache.freemarker.core.model.TemplateSequenceModel;
 import org.apache.freemarker.core.model.impl.SimpleString;
 import org.apache.freemarker.core.model.impl.SimpleSequence;
@@ -40,7 +41,7 @@ public class MultiModel4 implements TemplateSequenceModel, 
TemplateHashModel {
 
     @Override
     public TemplateModel get(int i) throws TemplateException {
-        return m_cList.get( i );
+        return m_cList.get(i);
     }
 
     @Override
@@ -53,13 +54,23 @@ public class MultiModel4 implements TemplateSequenceModel, 
TemplateHashModel {
     }
 
     @Override
-    public int size() {
-        return m_cList.size();
+    public int getCollectionSize() {
+        return m_cList.getCollectionSize();
     }
 
     @Override
-    public boolean isEmpty() {
-        return size() == 0;
+    public boolean isEmptyCollection() throws TemplateException {
+        return m_cList.isEmptyCollection();
+    }
+
+    @Override
+    public boolean isEmptyHash() throws TemplateException {
+        return false;
+    }
+
+    @Override
+    public TemplateModelIterator iterator() throws TemplateException {
+        return m_cList.iterator();
     }
 
 }

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/cae86e18/freemarker-core-test/src/test/java/org/apache/freemarker/core/templatesuite/models/MultiModel5.java
----------------------------------------------------------------------
diff --git 
a/freemarker-core-test/src/test/java/org/apache/freemarker/core/templatesuite/models/MultiModel5.java
 
b/freemarker-core-test/src/test/java/org/apache/freemarker/core/templatesuite/models/MultiModel5.java
index 02f2cc7..c794dd4 100644
--- 
a/freemarker-core-test/src/test/java/org/apache/freemarker/core/templatesuite/models/MultiModel5.java
+++ 
b/freemarker-core-test/src/test/java/org/apache/freemarker/core/templatesuite/models/MultiModel5.java
@@ -23,6 +23,7 @@ import org.apache.freemarker.core.TemplateException;
 import org.apache.freemarker.core.model.ObjectWrapper;
 import org.apache.freemarker.core.model.TemplateHashModel;
 import org.apache.freemarker.core.model.TemplateModel;
+import org.apache.freemarker.core.model.TemplateModelIterator;
 import org.apache.freemarker.core.model.TemplateSequenceModel;
 import org.apache.freemarker.core.model.impl.SimpleString;
 import org.apache.freemarker.core.model.impl.SimpleSequence;
@@ -41,17 +42,22 @@ public class MultiModel5 implements TemplateSequenceModel, 
TemplateHashModel {
 
     @Override
     public TemplateModel get(int i) throws TemplateException {
-        return m_cList.get( i );
+        return m_cList.get(i);
     }
 
     @Override
-    public boolean isEmpty() {
+    public boolean isEmptyCollection() throws TemplateException {
+        return m_cList.isEmptyCollection();
+    }
+
+    @Override
+    public boolean isEmptyHash() {
         return false;
     }
 
     @Override
-    public int size() {
-        return m_cList.size();
+    public int getCollectionSize() {
+        return m_cList.getCollectionSize();
     }
 
     @Override
@@ -63,4 +69,9 @@ public class MultiModel5 implements TemplateSequenceModel, 
TemplateHashModel {
         }
     }
 
+    @Override
+    public TemplateModelIterator iterator() throws TemplateException {
+        return m_cList.iterator();
+    }
+
 }

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/cae86e18/freemarker-core-test/src/test/java/org/apache/freemarker/core/userpkg/TestTemplateCallableModel.java
----------------------------------------------------------------------
diff --git 
a/freemarker-core-test/src/test/java/org/apache/freemarker/core/userpkg/TestTemplateCallableModel.java
 
b/freemarker-core-test/src/test/java/org/apache/freemarker/core/userpkg/TestTemplateCallableModel.java
index add7f44..879c7d8 100644
--- 
a/freemarker-core-test/src/test/java/org/apache/freemarker/core/userpkg/TestTemplateCallableModel.java
+++ 
b/freemarker-core-test/src/test/java/org/apache/freemarker/core/userpkg/TestTemplateCallableModel.java
@@ -66,7 +66,7 @@ public abstract class TestTemplateCallableModel implements 
TemplateCallableModel
         } else if (value instanceof TemplateStringModel) {
             
sb.append(TemplateLanguageUtils.toStringLiteral(((TemplateStringModel) 
value).getAsString()));
         } else if (value instanceof TemplateSequenceModel) {
-            int len = ((TemplateSequenceModel) value).size();
+            int len = ((TemplateSequenceModel) value).getCollectionSize();
             sb.append('[');
             for (int i = 0; i < len; i++) {
                 if (i != 0) {

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/cae86e18/freemarker-core-test/src/test/resources/org/apache/freemarker/core/templatesuite/expected/type-builtins.txt
----------------------------------------------------------------------
diff --git 
a/freemarker-core-test/src/test/resources/org/apache/freemarker/core/templatesuite/expected/type-builtins.txt
 
b/freemarker-core-test/src/test/resources/org/apache/freemarker/core/templatesuite/expected/type-builtins.txt
index 4d48327..c31a204 100644
--- 
a/freemarker-core-test/src/test/resources/org/apache/freemarker/core/templatesuite/expected/type-builtins.txt
+++ 
b/freemarker-core-test/src/test/resources/org/apache/freemarker/core/templatesuite/expected/type-builtins.txt
@@ -16,17 +16,17 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-StNuBoHaHxSeCoCxEnInFuDiNo
-1 0 0 0 0 0 0 0 0 0 0 0 0
-0 1 0 0 0 0 0 0 0 0 0 0 0
-0 0 1 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 1 0 0
-0 0 0 0 0 0 0 0 0 0 0 1 0
-0 0 0 1 1 0 0 0 0 0 0 0 0
-0 0 0 0 0 1 0 0 1 1 0 0 0
-0 0 0 0 0 0 1 0 1 0 0 0 0
-0 0 0 0 0 0 1 1 1 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 1
-1 0 0 1 1 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 1 0 0
-0 0 0 0 0 0 0 0 0 0 1 0 0
+StNuBoHaHxItCoSeFuDiNo
+1 0 0 0 0 0 0 0 0 0 0
+0 1 0 0 0 0 0 0 0 0 0
+0 0 1 0 0 0 0 0 0 0 0
+0 0 0 0 0 0 0 0 1 0 0
+0 0 0 0 0 0 0 0 0 1 0
+0 0 0 1 1 0 0 0 0 0 0
+0 0 0 0 0 1 1 1 0 0 0
+0 0 0 0 0 1 0 0 0 0 0
+0 0 0 0 0 1 1 0 0 0 0
+0 0 0 0 0 0 0 0 0 0 1
+1 0 0 1 1 0 0 0 0 0 0
+0 0 0 0 0 0 0 0 1 0 0
+0 0 0 0 0 0 0 0 1 0 0

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-common.ftl
----------------------------------------------------------------------
diff --git 
a/freemarker-core-test/src/test/resources/org/apache/freemarker/core/templatesuite/templates/range-common.ftl
 
b/freemarker-core-test/src/test/resources/org/apache/freemarker/core/templatesuite/templates/range-common.ftl
deleted file mode 100644
index bda67de..0000000
--- 
a/freemarker-core-test/src/test/resources/org/apache/freemarker/core/templatesuite/templates/range-common.ftl
+++ /dev/null
@@ -1,314 +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.
--->
-<#-- 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>
-
-<#-- Legacy string backward-range bug kept for compatibility: -->
-<@assertEquals actual=s[1..0] expected="" />
-<@assertEquals actual=s[2..1] expected="" />
-<@assertFails message="negative">
-  <@assertEquals actual=s[0..-1] expected="" />
-</@assertFails>
-<@assertFails message="decreasing">
-  <@assertEquals actual=s[3..1] expected="" />
-</@assertFails>
-<#-- But it isn't emulated for operators introduced after 2.3.20: -->
-<@assertFails message="decreasing">
-  <@assertEquals actual=s[3..<1] expected="" />
-</@assertFails>
-<@assertFails message="decreasing">
-  <@assertEquals actual=s[3..*-2] expected="" />
-</@assertFails>
-
-<#assign r = 1..2>
-<@assertEquals actual=s[r] expected="bc" />
-<#assign r = 2..1>
-<@assertEquals actual=s[r] expected="" />
-<#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" />

Reply via email to