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/10/20 20:58:02 UTC

incubator-freemarker git commit: Removed TemplateHasmModelEx2 (as TemplateHasmModelEx now can do what it did)

Repository: incubator-freemarker
Updated Branches:
  refs/heads/3 884d22afd -> 994775e49


Removed TemplateHasmModelEx2 (as TemplateHasmModelEx now can do what it did)


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

Branch: refs/heads/3
Commit: 994775e49b526d04b12deb7428577309f2052bfc
Parents: 884d22a
Author: ddekany <dd...@apache.org>
Authored: Fri Oct 20 22:57:56 2017 +0200
Committer: ddekany <dd...@apache.org>
Committed: Fri Oct 20 22:57:56 2017 +0200

----------------------------------------------------------------------
 FM3-CHANGE-LOG.txt                              |   2 +-
 .../freemarker/core/DirectiveCallPlaceTest.java |   4 +-
 .../freemarker/core/ListBreakContinueTest.java  |  44 +-------
 .../apache/freemarker/core/ListErrorsTest.java  |  13 ---
 .../model/impl/RestrictedObjectWrapperTest.java |   8 +-
 .../core/templatesuite/models/Listables.java    |  68 +-----------
 .../core/userpkg/AllFeaturesDirective.java      |   6 +-
 .../core/userpkg/AllFeaturesFunction.java       |   6 +-
 .../core/userpkg/TestTemplateCallableModel.java |   7 +-
 .../core/templatesuite/expected/listhash.txt    |  19 ----
 .../core/templatesuite/templates/listhash.ftl   |   3 +-
 .../org/apache/freemarker/core/ASTDirList.java  |  77 +++-----------
 .../apache/freemarker/core/ASTExpDefault.java   |   5 +-
 .../freemarker/core/ASTExpHashLiteral.java      |   3 +-
 .../core/BuiltInsForMultipleTypes.java          |   4 +-
 .../apache/freemarker/core/NativeHashEx.java    | 105 ++++++++++++++++++
 .../apache/freemarker/core/NativeHashEx2.java   | 106 -------------------
 .../apache/freemarker/core/_CallableUtils.java  |   4 +-
 .../core/model/ArgumentArrayLayout.java         |   6 +-
 .../freemarker/core/model/EmptyHashModel.java   |   3 +-
 .../core/model/GeneralPurposeNothing.java       |   2 +-
 .../core/model/TemplateHashModel.java           |   4 +-
 .../core/model/TemplateHashModelEx.java         |   7 +-
 .../core/model/TemplateHashModelEx2.java        |  30 ------
 .../core/model/impl/DefaultMapAdapter.java      |   3 +-
 .../model/impl/MapKeyValuePairIterator.java     |   3 +-
 .../freemarker/core/model/impl/SimpleHash.java  |   3 +-
 .../freemarker/servlet/jsp/JspTagModelBase.java |   3 +-
 .../servlet/jsp/SimpleTagDirectiveModel.java    |   6 +-
 .../servlet/jsp/TagDirectiveModel.java          |   4 +-
 .../freemarker/spring/model/UrlFunction.java    |   4 +-
 31 files changed, 174 insertions(+), 388 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/994775e4/FM3-CHANGE-LOG.txt
----------------------------------------------------------------------
diff --git a/FM3-CHANGE-LOG.txt b/FM3-CHANGE-LOG.txt
index 8564541..b7d4987 100644
--- a/FM3-CHANGE-LOG.txt
+++ b/FM3-CHANGE-LOG.txt
@@ -435,7 +435,7 @@ Core / Models and Object wrapping
       `TemplateCollectoinModel` has `getCollectionSize()` and `isEmptyCollection` method. So this affects
       `TemplateHashModelEx` implementations as well.
   - Map-like interfaces:
-    - [TODO: in progress] `TemplateHashModelEx2` was removed, as the `keyValuePairIterator()` method was moved to `TemplateHashModelEx`,
+    - `TemplateHashModelEx2` was removed, as the `keyValuePairIterator()` method was moved to `TemplateHashModelEx`,
        so now the two interfaces would be the same.
 - BeanModel.keys() and values() are now final methods. Override BeanModel.keySet() and/or get(String) instead.
       

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/994775e4/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 2e8f8cc..a7944e6 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
@@ -28,7 +28,7 @@ import java.util.concurrent.atomic.AtomicInteger;
 
 import org.apache.freemarker.core.model.ArgumentArrayLayout;
 import org.apache.freemarker.core.model.TemplateDirectiveModel;
-import org.apache.freemarker.core.model.TemplateHashModelEx2;
+import org.apache.freemarker.core.model.TemplateHashModelEx;
 import org.apache.freemarker.core.model.TemplateModel;
 import org.apache.freemarker.core.model.TemplateStringModel;
 import org.apache.freemarker.core.util.CommonSupplier;
@@ -230,7 +230,7 @@ public class DirectiveCallPlaceTest extends TemplateTest {
         @Override
         public void execute(TemplateModel[] args, CallPlace callPlace, Writer out, Environment env)
                 throws TemplateException, IOException {
-            TemplateHashModelEx2 varargs = (TemplateHashModelEx2) args[ARGS_LAYOUT.getNamedVarargsArgumentIndex()];
+            TemplateHashModelEx varargs = (TemplateHashModelEx) args[ARGS_LAYOUT.getNamedVarargsArgumentIndex()];
             if (varargs.getHashSize() > 0) {
                 out.write("(p=");
                 out.write(((TemplateStringModel) varargs.get("p")).getAsString());

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/994775e4/freemarker-core-test/src/test/java/org/apache/freemarker/core/ListBreakContinueTest.java
----------------------------------------------------------------------
diff --git a/freemarker-core-test/src/test/java/org/apache/freemarker/core/ListBreakContinueTest.java b/freemarker-core-test/src/test/java/org/apache/freemarker/core/ListBreakContinueTest.java
index f7ef9b7..ec485f1 100644
--- a/freemarker-core-test/src/test/java/org/apache/freemarker/core/ListBreakContinueTest.java
+++ b/freemarker-core-test/src/test/java/org/apache/freemarker/core/ListBreakContinueTest.java
@@ -26,9 +26,7 @@ public class ListBreakContinueTest extends TemplateTest {
 
     @Test
     public void testHash() throws IOException, TemplateException {
-        testHash(ImmutableMap.of("a", 1, "b", 2, "c", 3, "d", 4, "e", 5)); // Listing a TemplateHashModelEx2
-        testHash(new NonEx2Hash((TemplateHashModelEx) getConfiguration().getObjectWrapper().wrap(
-                ImmutableMap.of("a", 1, "b", 2, "c", 3, "d", 4, "e", 5)))); // Listing a TemplateHashModelEx (non-Ex2)
+        testHash(ImmutableMap.of("a", 1, "b", 2, "c", 3, "d", 4, "e", 5)); // Listing a TemplateHashModelEx
     }
 
     private void testHash(Object listed) throws IOException, TemplateException {
@@ -41,44 +39,4 @@ public class ListBreakContinueTest extends TemplateTest {
                 "B(a=1) A(a=1), B(b=2) A(b=2), B(c=3) Continue! B(d=4) A(d=4), B(e=5) A(e=5)");
     }
     
-    /** Hides the Ex2 features of another hash */
-    // TODO [FM3][CF] Remove
-    static class NonEx2Hash implements TemplateHashModelEx {
-        private final TemplateHashModelEx delegate;
-
-        public NonEx2Hash(TemplateHashModelEx delegate) {
-            this.delegate = delegate;
-        }
-
-        @Override
-        public TemplateModel get(String key) throws TemplateException {
-            return delegate.get(key);
-        }
-
-        @Override
-        public int getHashSize() throws TemplateException {
-            return delegate.getHashSize();
-        }
-
-        @Override
-        public TemplateCollectionModel keys() throws TemplateException {
-            return delegate.keys();
-        }
-
-        @Override
-        public boolean isEmptyHash() throws TemplateException {
-            return delegate.isEmptyHash();
-        }
-
-        @Override
-        public TemplateCollectionModel values() throws TemplateException {
-            return delegate.values();
-        }
-
-        @Override
-        public KeyValuePairIterator keyValuePairIterator() throws TemplateException {
-            return delegate.keyValuePairIterator();
-        }
-    }
-    
 }

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/994775e4/freemarker-core-test/src/test/java/org/apache/freemarker/core/ListErrorsTest.java
----------------------------------------------------------------------
diff --git a/freemarker-core-test/src/test/java/org/apache/freemarker/core/ListErrorsTest.java b/freemarker-core-test/src/test/java/org/apache/freemarker/core/ListErrorsTest.java
index 8944dd1..4607df5 100644
--- a/freemarker-core-test/src/test/java/org/apache/freemarker/core/ListErrorsTest.java
+++ b/freemarker-core-test/src/test/java/org/apache/freemarker/core/ListErrorsTest.java
@@ -21,13 +21,9 @@ package org.apache.freemarker.core;
 
 import java.io.IOException;
 
-import org.apache.freemarker.core.model.impl.DefaultObjectWrapper;
 import org.apache.freemarker.test.TemplateTest;
-import org.apache.freemarker.core.templatesuite.models.Listables;
 import org.junit.Test;
 
-import com.google.common.collect.ImmutableMap;
-
 public class ListErrorsTest extends TemplateTest {
     
     @Test
@@ -117,14 +113,5 @@ public class ListErrorsTest extends TemplateTest {
         assertErrorContains("<#list [] as k, v></#list>",
                 "only one nested content parameter");
     }
-
-    @Test
-    public void testNonEx2NonStringKey() throws IOException, TemplateException {
-        addToDataModel("m", new Listables.NonEx2MapAdapter(ImmutableMap.of("k1", "v1", 2, "v2"),
-                new DefaultObjectWrapper.Builder(Configuration.VERSION_3_0_0).build()));
-        assertOutput("<#list m?keys as k>${k};</#list>", "k1;2;");
-        assertErrorContains("<#list m as k, v></#list>",
-                "string", "number", ".TemplateHashModelEx2");
-    }
     
 }

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/994775e4/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 8a6317c..6247fba 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,14 +41,14 @@ 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.TemplateDateModel;
-import org.apache.freemarker.core.model.TemplateHashModelEx2;
+import org.apache.freemarker.core.model.TemplateHashModelEx;
+import org.apache.freemarker.core.model.TemplateIterableModel;
 import org.apache.freemarker.core.model.TemplateModelWithAPISupport;
 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.TemplateStringModel;
 import org.apache.freemarker.core.model.impl.DefaultObjectWrapperTest.TestBean;
 import org.junit.Test;
 import org.w3c.dom.Document;
@@ -108,7 +108,7 @@ public class RestrictedObjectWrapperTest {
         assertTrue(sow.wrap(new String[0]) instanceof TemplateSequenceModel);
         assertTrue(sow.wrap(new ArrayList().iterator()) instanceof TemplateIterableModel);
         assertTrue(sow.wrap(new HashSet()) instanceof TemplateCollectionModel);
-        assertTrue(sow.wrap(new HashMap()) instanceof TemplateHashModelEx2);
+        assertTrue(sow.wrap(new HashMap()) instanceof TemplateHashModelEx);
         assertNull(sow.wrap(null));
     }
 

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/994775e4/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 f5b9516..f3bf2ff 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
@@ -30,21 +30,12 @@ import java.util.TreeSet;
 
 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.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.MapKeyValuePairIterator;
-import org.apache.freemarker.core.model.impl.SimpleCollection;
 import org.apache.freemarker.core.model.impl.SimpleHash;
 
-import com.google.common.collect.ImmutableMap;
-
 @SuppressWarnings("boxing")
 public class Listables {
     
@@ -66,8 +57,6 @@ public class Listables {
         LINKED_LIST = list;
     }
 
-    private static final List<Integer> EMPTY_LINKED_LIST = new LinkedList<>();
-
     private static final Set<Integer> SET;
     static {
         Set<Integer> set = new TreeSet<>();
@@ -109,7 +98,7 @@ public class Listables {
         return Collections.<Integer>emptySet().iterator();
     }
     
-    public List<TemplateHashModelEx2> getHashEx2s() throws TemplateException {
+    public List<TemplateHashModelEx> getHashExs() throws TemplateException {
         Map<Object, Object> map;
         map = new LinkedHashMap<>();
         map.put("k1", "v1");
@@ -119,12 +108,12 @@ public class Listables {
         map.put(true, "v5");
         map.put(false, null);
         
-        return getMapsWrappedAsEx2(map);
+        return getMapsWrappedAsEx(map);
     }
 
     public List<? extends TemplateHashModelEx> getEmptyHashes() throws ObjectWrappingException {
         List<TemplateHashModelEx> emptyMaps = new ArrayList<>();
-        emptyMaps.addAll(getMapsWrappedAsEx2(Collections.emptyMap()));
+        emptyMaps.addAll(getMapsWrappedAsEx(Collections.emptyMap()));
         emptyMaps.add((TemplateHashModelEx) new DefaultObjectWrapper.Builder(Configuration.VERSION_3_0_0).build()
                 .wrap(Collections.emptyMap()));
         return emptyMaps;
@@ -133,8 +122,8 @@ public class Listables {
     /**
      * Returns the map wrapped on various ways.
      */
-    private List<TemplateHashModelEx2> getMapsWrappedAsEx2(Map<?, ?> map) throws ObjectWrappingException {
-        List<TemplateHashModelEx2> maps = new ArrayList<>();
+    private List<TemplateHashModelEx> getMapsWrappedAsEx(Map<?, ?> map) throws ObjectWrappingException {
+        List<TemplateHashModelEx> maps = new ArrayList<>();
         
         DefaultObjectWrapper ow = new DefaultObjectWrapper.Builder(Configuration.VERSION_3_0_0).build();
         maps.add(new SimpleHash(map, ow));
@@ -143,51 +132,4 @@ public class Listables {
         return maps;
     }
     
-    public TemplateHashModelEx getHashNonEx2() {
-        return new NonEx2MapAdapter(ImmutableMap.of("k1", 11, "k2", 22),
-                new DefaultObjectWrapper.Builder(Configuration.VERSION_3_0_0).build());
-    }
-    
-    // TODO [FM3][CF] Remove
-    public static class NonEx2MapAdapter extends WrappingTemplateModel implements TemplateHashModelEx {
-
-        private final Map<?, ?> map;
-        
-        public NonEx2MapAdapter(Map<?, ?> map, ObjectWrapper wrapper) {
-            super(wrapper);
-            this.map = map;
-        }
-        
-        @Override
-        public TemplateModel get(String key) throws TemplateException {
-            return wrap(map.get(key));
-        }
-        
-        @Override
-        public boolean isEmptyHash() {
-            return map.isEmpty();
-        }
-        
-        @Override
-        public int getHashSize() {
-            return map.size();
-        }
-        
-        @Override
-        public TemplateCollectionModel keys() {
-            return new SimpleCollection(map.keySet(), getObjectWrapper());
-        }
-        
-        @Override
-        public TemplateCollectionModel values() {
-            return new SimpleCollection(map.values(), getObjectWrapper());
-        }
-
-        @Override
-        public KeyValuePairIterator keyValuePairIterator() throws TemplateException {
-            return new MapKeyValuePairIterator(map, getObjectWrapper());
-        }
-        
-    }
-    
 }

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/994775e4/freemarker-core-test/src/test/java/org/apache/freemarker/core/userpkg/AllFeaturesDirective.java
----------------------------------------------------------------------
diff --git a/freemarker-core-test/src/test/java/org/apache/freemarker/core/userpkg/AllFeaturesDirective.java b/freemarker-core-test/src/test/java/org/apache/freemarker/core/userpkg/AllFeaturesDirective.java
index 9302345..94116bb 100644
--- a/freemarker-core-test/src/test/java/org/apache/freemarker/core/userpkg/AllFeaturesDirective.java
+++ b/freemarker-core-test/src/test/java/org/apache/freemarker/core/userpkg/AllFeaturesDirective.java
@@ -29,7 +29,7 @@ import org.apache.freemarker.core.Environment;
 import org.apache.freemarker.core.TemplateException;
 import org.apache.freemarker.core.model.ArgumentArrayLayout;
 import org.apache.freemarker.core.model.TemplateDirectiveModel;
-import org.apache.freemarker.core.model.TemplateHashModelEx2;
+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.SimpleNumber;
@@ -67,12 +67,12 @@ public class AllFeaturesDirective extends TestTemplateCallableModel implements T
                 (TemplateSequenceModel) args[P_VARARGS_ARG_IDX],
                 getOptionalNumberArgument(args, N1_ARG_IDX, this),
                 getOptionalNumberArgument(args, N2_ARG_IDX, this),
-                (TemplateHashModelEx2) args[N_VARARGS_ARG_IDX],
+                (TemplateHashModelEx) args[N_VARARGS_ARG_IDX],
                 out, env, callPlace);
     }
 
     private void execute(Number p1, Number p2, TemplateSequenceModel pOthers,
-            Number n1, Number n2, TemplateHashModelEx2 nOthers,
+            Number n1, Number n2, TemplateHashModelEx nOthers,
             Writer out, Environment env, CallPlace callPlace) throws IOException, TemplateException {
         out.write("#a(");
         printParam("p1", p1, out, true);

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/994775e4/freemarker-core-test/src/test/java/org/apache/freemarker/core/userpkg/AllFeaturesFunction.java
----------------------------------------------------------------------
diff --git a/freemarker-core-test/src/test/java/org/apache/freemarker/core/userpkg/AllFeaturesFunction.java b/freemarker-core-test/src/test/java/org/apache/freemarker/core/userpkg/AllFeaturesFunction.java
index b758149..b8c07b4 100644
--- a/freemarker-core-test/src/test/java/org/apache/freemarker/core/userpkg/AllFeaturesFunction.java
+++ b/freemarker-core-test/src/test/java/org/apache/freemarker/core/userpkg/AllFeaturesFunction.java
@@ -26,7 +26,7 @@ import org.apache.freemarker.core.Environment;
 import org.apache.freemarker.core.TemplateException;
 import org.apache.freemarker.core.model.ArgumentArrayLayout;
 import org.apache.freemarker.core.model.TemplateFunctionModel;
-import org.apache.freemarker.core.model.TemplateHashModelEx2;
+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.SimpleString;
@@ -68,11 +68,11 @@ public class AllFeaturesFunction extends TestTemplateCallableModel implements Te
                 (TemplateSequenceModel) args[P_VARARGS_ARG_IDX],
                 getOptionalNumberArgument(args, N1_ARG_IDX, this),
                 getOptionalNumberArgument(args, N2_ARG_IDX, this),
-                (TemplateHashModelEx2) args[N_VARARGS_ARG_IDX]);
+                (TemplateHashModelEx) args[N_VARARGS_ARG_IDX]);
     }
 
     private TemplateModel execute(Number p1, Number p2, TemplateSequenceModel pOthers,
-            Number n1, Number n2, TemplateHashModelEx2 nOthers) throws TemplateException {
+            Number n1, Number n2, TemplateHashModelEx nOthers) throws TemplateException {
         StringBuilder sb = new StringBuilder();
 
         sb.append("fa(");

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/994775e4/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 21c6ed5..7aef5f6 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
@@ -25,10 +25,9 @@ import java.io.Writer;
 import org.apache.freemarker.core.TemplateException;
 import org.apache.freemarker.core.model.TemplateCallableModel;
 import org.apache.freemarker.core.model.TemplateHashModelEx;
-import org.apache.freemarker.core.model.TemplateHashModelEx2;
 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.TemplateStringModel;
 import org.apache.freemarker.core.util.TemplateLanguageUtils;
 import org.apache.freemarker.core.util._StringUtils;
 
@@ -76,8 +75,8 @@ public abstract class TestTemplateCallableModel implements TemplateCallableModel
                 printValue(((TemplateSequenceModel) value).get(i), sb);
             }
             sb.append(']');
-        } else if (value instanceof TemplateHashModelEx2) {
-            TemplateHashModelEx.KeyValuePairIterator it = ((TemplateHashModelEx2) value).keyValuePairIterator();
+        } else if (value instanceof TemplateHashModelEx) {
+            TemplateHashModelEx.KeyValuePairIterator it = ((TemplateHashModelEx) value).keyValuePairIterator();
             sb.append('{');
             while (it.hasNext()) {
                 TemplateHashModelEx.KeyValuePair kvp = it.next();

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/994775e4/freemarker-core-test/src/test/resources/org/apache/freemarker/core/templatesuite/expected/listhash.txt
----------------------------------------------------------------------
diff --git a/freemarker-core-test/src/test/resources/org/apache/freemarker/core/templatesuite/expected/listhash.txt b/freemarker-core-test/src/test/resources/org/apache/freemarker/core/templatesuite/expected/listhash.txt
index e251238..afc503f 100644
--- a/freemarker-core-test/src/test/resources/org/apache/freemarker/core/templatesuite/expected/listhash.txt
+++ b/freemarker-core-test/src/test/resources/org/apache/freemarker/core/templatesuite/expected/listhash.txt
@@ -81,25 +81,6 @@ Non-empty maps:
       ]
     }
 
-    Map:
-    
-    [
-      k1 = 11
-      k2 = 22
-    ]
-  
-    [
-      k1 = 11; // @0=@0; odd=odd; Y=Y
-      k2 = 22 // @1=@1; even=even; N=N
-    ]
-  
-    {
-      [
-        k1 = 11; // @0=@0; odd=odd; Y=Y
-        k2 = 22 // @1=@1; even=even; N=N
-      ]
-    }
-
 
 Empty maps:
 

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/994775e4/freemarker-core-test/src/test/resources/org/apache/freemarker/core/templatesuite/templates/listhash.ftl
----------------------------------------------------------------------
diff --git a/freemarker-core-test/src/test/resources/org/apache/freemarker/core/templatesuite/templates/listhash.ftl b/freemarker-core-test/src/test/resources/org/apache/freemarker/core/templatesuite/templates/listhash.ftl
index f74ce65..6d72f96 100644
--- a/freemarker-core-test/src/test/resources/org/apache/freemarker/core/templatesuite/templates/listhash.ftl
+++ b/freemarker-core-test/src/test/resources/org/apache/freemarker/core/templatesuite/templates/listhash.ftl
@@ -53,8 +53,7 @@
 
 Non-empty maps:
 
-<@listings listables.hashEx2s />
-<@listings [ listables.hashNonEx2 ] />
+<@listings listables.hashExs />
 
 Empty maps:
 

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/994775e4/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 7e4e86c..8034018 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
@@ -26,13 +26,9 @@ import java.util.Collections;
 
 import org.apache.freemarker.core.model.TemplateBooleanModel;
 import org.apache.freemarker.core.model.TemplateHashModelEx;
-import org.apache.freemarker.core.model.TemplateHashModelEx.KeyValuePair;
-import org.apache.freemarker.core.model.TemplateHashModelEx.KeyValuePairIterator;
-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.TemplateModelIterator;
-import org.apache.freemarker.core.model.TemplateStringModel;
 import org.apache.freemarker.core.model.impl.SimpleNumber;
 import org.apache.freemarker.core.util._StringUtils;
 
@@ -308,60 +304,17 @@ final class ASTDirList extends ASTDirective {
             final boolean hashNotEmpty;
             if (listedValue instanceof TemplateHashModelEx) {
                 TemplateHashModelEx listedHash = (TemplateHashModelEx) listedValue; 
-                if (listedHash instanceof TemplateHashModelEx2) {
-                    TemplateHashModelEx.KeyValuePairIterator kvpIter
-                            = openedIterator == null ? ((TemplateHashModelEx2) listedHash).keyValuePairIterator()
-                                    : (TemplateHashModelEx.KeyValuePairIterator) openedIterator;
-                    hashNotEmpty = kvpIter.hasNext();
-                    if (hashNotEmpty) {
-                        if (nestedContentParam1Name != null) {
-                            listLoop: do {
-                                    TemplateHashModelEx.KeyValuePair kvp = kvpIter.next();
-                                    nestedContentParam = kvp.getKey();
-                                    nestedContentParam2 = kvp.getValue();
-                                    hasNext = kvpIter.hasNext();
-                                    try {
-                                        env.visit(childBuffer);
-                                    } catch (BreakOrContinueException br) {
-                                        if (br == BreakOrContinueException.BREAK_INSTANCE) {
-                                            break listLoop;
-                                        }
-                                    }
-                                    index++;
-                                } while (hasNext);
-                            openedIterator = null;
-                        } else {
-                            // We will reuse this at the #iterms
-                            openedIterator = kvpIter;
-                            env.visit(childBuffer);
-                        }
-                    }
-                } else { //  not a TemplateHashModelEx2, but still a TemplateHashModelEx
-                    TemplateModelIterator keysIter = listedHash.keys().iterator();
-                    hashNotEmpty = keysIter.hasNext();
-                    if (hashNotEmpty) {
-                        if (nestedContentParam1Name != null) {
-                            listLoop: do {
-                                nestedContentParam = keysIter.next();
-                                if (!(nestedContentParam instanceof TemplateStringModel)) {
-                                    throw new TemplateException(env,
-                                            new _ErrorDescriptionBuilder(
-                                                    "When listing key-value pairs of traditional hash "
-                                                    + "implementations, all keys must be strings, but one of them "
-                                                    + "was ",
-                                                    new _DelayedAOrAn(
-                                                            new _DelayedTemplateLanguageTypeDescription(
-                                                                    nestedContentParam)),
-                                                    "."
-                                                    ).tip("The listed value's TemplateModel class was ",
-                                                            new _DelayedShortClassName(listedValue.getClass()),
-                                                            ", which doesn't implement ",
-                                                            new _DelayedShortClassName(TemplateHashModelEx2.class),
-                                                            ", which leads to this restriction."));
-                                }
-                                nestedContentParam2 = listedHash.get(((TemplateStringModel) nestedContentParam)
-                                        .getAsString());
-                                hasNext = keysIter.hasNext();
+                TemplateHashModelEx.KeyValuePairIterator kvpIter
+                        = openedIterator == null ? listedHash.keyValuePairIterator()
+                                : (TemplateHashModelEx.KeyValuePairIterator) openedIterator;
+                hashNotEmpty = kvpIter.hasNext();
+                if (hashNotEmpty) {
+                    if (nestedContentParam1Name != null) {
+                        listLoop: do {
+                                TemplateHashModelEx.KeyValuePair kvp = kvpIter.next();
+                                nestedContentParam = kvp.getKey();
+                                nestedContentParam2 = kvp.getValue();
+                                hasNext = kvpIter.hasNext();
                                 try {
                                     env.visit(childBuffer);
                                 } catch (BreakOrContinueException br) {
@@ -371,9 +324,11 @@ final class ASTDirList extends ASTDirective {
                                 }
                                 index++;
                             } while (hasNext);
-                        } else {
-                            env.visit(childBuffer);
-                        }
+                        openedIterator = null;
+                    } else {
+                        // We will reuse this at the #iterms
+                        openedIterator = kvpIter;
+                        env.visit(childBuffer);
                     }
                 }
             } else if (listedValue instanceof TemplateIterableModel) {

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/994775e4/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 1a54521..e0aee94 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
@@ -22,7 +22,6 @@ 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.TemplateModel;
 import org.apache.freemarker.core.model.TemplateModelIterator;
 import org.apache.freemarker.core.model.TemplateSequenceModel;
@@ -31,8 +30,8 @@ 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 EmptyStringAndSequenceAndHash implements TemplateStringModel, TemplateSequenceModel,
-            TemplateHashModelEx2 {
+    static private class EmptyStringAndSequenceAndHash
+            implements TemplateStringModel, TemplateSequenceModel, TemplateHashModelEx {
         @Override
         public String getAsString() {
             return "";

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/994775e4/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 c062c18..6c337dd 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
@@ -26,7 +26,6 @@ import java.util.ListIterator;
 
 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.TemplateModel;
 import org.apache.freemarker.core.model.TemplateModelIterator;
 
@@ -105,7 +104,7 @@ final class ASTExpHashLiteral extends ASTExpression {
     	return new ASTExpHashLiteral(clonedKeys, clonedValues);
     }
 
-    private class LinkedHash implements TemplateHashModelEx2 {
+    private class LinkedHash implements TemplateHashModelEx {
 
         private HashMap<String, TemplateModel> map;
         private TemplateCollectionModel keyCollection, valueCollection; // ordered lists of keys and values

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/994775e4/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 9c48dc3..4f9a08d 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,20 +25,20 @@ 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.TemplateDateModel;
 import org.apache.freemarker.core.model.TemplateDirectiveModel;
 import org.apache.freemarker.core.model.TemplateFunctionModel;
 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.TemplateModelWithAPISupport;
 import org.apache.freemarker.core.model.TemplateNodeModel;
 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.TemplateStringModel;
 import org.apache.freemarker.core.model.impl.SimpleDate;
 import org.apache.freemarker.core.model.impl.SimpleNumber;
 import org.apache.freemarker.core.model.impl.SimpleString;

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/994775e4/freemarker-core/src/main/java/org/apache/freemarker/core/NativeHashEx.java
----------------------------------------------------------------------
diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/NativeHashEx.java b/freemarker-core/src/main/java/org/apache/freemarker/core/NativeHashEx.java
new file mode 100644
index 0000000..0ffcf8c
--- /dev/null
+++ b/freemarker-core/src/main/java/org/apache/freemarker/core/NativeHashEx.java
@@ -0,0 +1,105 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.freemarker.core;
+
+import java.io.Serializable;
+import java.util.Iterator;
+import java.util.LinkedHashMap;
+import java.util.Map;
+
+import org.apache.freemarker.core.model.ObjectWrapper;
+import org.apache.freemarker.core.model.TemplateCollectionModel;
+import org.apache.freemarker.core.model.TemplateHashModelEx;
+import org.apache.freemarker.core.model.TemplateModel;
+import org.apache.freemarker.core.model.impl.SimpleString;
+
+/**
+ * A hash where each value is already a {@link TemplateModel}, so no {@link ObjectWrapper} need to be specified.
+ *
+ * <p>While this class allows adding items, doing so is not thread-safe, and thus only meant to be done during the
+ * initialization of the sequence.
+ */
+class NativeHashEx implements TemplateHashModelEx, Serializable {
+
+    private final LinkedHashMap<String, TemplateModel> map;
+
+    public NativeHashEx() {
+        this.map = new LinkedHashMap<>();
+    }
+
+    @Override
+    public int getHashSize() throws TemplateException {
+        return map.size();
+    }
+
+    @Override
+    public TemplateModel get(String key) throws TemplateException {
+        return map.get(key);
+    }
+
+    @Override
+    public boolean isEmptyHash() throws TemplateException {
+        return map.isEmpty();
+    }
+
+    @Override
+    public TemplateHashModelEx.KeyValuePairIterator keyValuePairIterator() throws TemplateException {
+        return new TemplateHashModelEx.KeyValuePairIterator() {
+            private final Iterator<Map.Entry<String, TemplateModel>> entrySetIterator = map.entrySet().iterator();
+
+            @Override
+            public boolean hasNext() throws TemplateException {
+                return entrySetIterator.hasNext();
+            }
+
+            @Override
+            public TemplateHashModelEx.KeyValuePair next() throws TemplateException {
+                return new TemplateHashModelEx.KeyValuePair() {
+                    private final Map.Entry<String, TemplateModel> entry = entrySetIterator.next();
+
+                    @Override
+                    public TemplateModel getKey() throws TemplateException {
+                        return new SimpleString(entry.getKey());
+                    }
+
+                    @Override
+                    public TemplateModel getValue() throws TemplateException {
+                        return entry.getValue();
+                    }
+                };
+            }
+        };
+    }
+
+    @Override
+    public TemplateCollectionModel keys() throws TemplateException {
+        return new NativeStringCollectionCollection(map.keySet());
+    }
+
+    @Override
+    public TemplateCollectionModel values() throws TemplateException {
+        return new NativeCollection(map.values());
+    }
+
+    public TemplateModel put(String key, TemplateModel value) {
+        return map.put(key, value);
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/994775e4/freemarker-core/src/main/java/org/apache/freemarker/core/NativeHashEx2.java
----------------------------------------------------------------------
diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/NativeHashEx2.java b/freemarker-core/src/main/java/org/apache/freemarker/core/NativeHashEx2.java
deleted file mode 100644
index 0074171..0000000
--- a/freemarker-core/src/main/java/org/apache/freemarker/core/NativeHashEx2.java
+++ /dev/null
@@ -1,106 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *   http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-
-package org.apache.freemarker.core;
-
-import java.io.Serializable;
-import java.util.Iterator;
-import java.util.LinkedHashMap;
-import java.util.Map;
-
-import org.apache.freemarker.core.model.ObjectWrapper;
-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.TemplateModel;
-import org.apache.freemarker.core.model.impl.SimpleString;
-
-/**
- * A hash where each value is already a {@link TemplateModel}, so no {@link ObjectWrapper} need to be specified.
- *
- * <p>While this class allows adding items, doing so is not thread-safe, and thus only meant to be done during the
- * initialization of the sequence.
- */
-class NativeHashEx2 implements TemplateHashModelEx2, Serializable {
-
-    private final LinkedHashMap<String, TemplateModel> map;
-
-    public NativeHashEx2() {
-        this.map = new LinkedHashMap<>();
-    }
-
-    @Override
-    public int getHashSize() throws TemplateException {
-        return map.size();
-    }
-
-    @Override
-    public TemplateModel get(String key) throws TemplateException {
-        return map.get(key);
-    }
-
-    @Override
-    public boolean isEmptyHash() throws TemplateException {
-        return map.isEmpty();
-    }
-
-    @Override
-    public TemplateHashModelEx.KeyValuePairIterator keyValuePairIterator() throws TemplateException {
-        return new TemplateHashModelEx.KeyValuePairIterator() {
-            private final Iterator<Map.Entry<String, TemplateModel>> entrySetIterator = map.entrySet().iterator();
-
-            @Override
-            public boolean hasNext() throws TemplateException {
-                return entrySetIterator.hasNext();
-            }
-
-            @Override
-            public TemplateHashModelEx.KeyValuePair next() throws TemplateException {
-                return new TemplateHashModelEx.KeyValuePair() {
-                    private final Map.Entry<String, TemplateModel> entry = entrySetIterator.next();
-
-                    @Override
-                    public TemplateModel getKey() throws TemplateException {
-                        return new SimpleString(entry.getKey());
-                    }
-
-                    @Override
-                    public TemplateModel getValue() throws TemplateException {
-                        return entry.getValue();
-                    }
-                };
-            }
-        };
-    }
-
-    @Override
-    public TemplateCollectionModel keys() throws TemplateException {
-        return new NativeStringCollectionCollection(map.keySet());
-    }
-
-    @Override
-    public TemplateCollectionModel values() throws TemplateException {
-        return new NativeCollection(map.values());
-    }
-
-    public TemplateModel put(String key, TemplateModel value) {
-        return map.put(key, value);
-    }
-
-}

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/994775e4/freemarker-core/src/main/java/org/apache/freemarker/core/_CallableUtils.java
----------------------------------------------------------------------
diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/_CallableUtils.java b/freemarker-core/src/main/java/org/apache/freemarker/core/_CallableUtils.java
index d1019e1..346ec31 100644
--- a/freemarker-core/src/main/java/org/apache/freemarker/core/_CallableUtils.java
+++ b/freemarker-core/src/main/java/org/apache/freemarker/core/_CallableUtils.java
@@ -139,7 +139,7 @@ public class _CallableUtils {
         }
 
         int namedVarargsArgumentIndex = argsLayout.getNamedVarargsArgumentIndex();
-        NativeHashEx2 namedVarargsHash = null;
+        NativeHashEx namedVarargsHash = null;
         if (namedArgs != null) {
             StringToIndexMap predefNamedArgsMap = argsLayout.getPredefinedNamedArgumentsMap();
             for (NamedArgument namedArg : namedArgs) {
@@ -165,7 +165,7 @@ public class _CallableUtils {
                                             });
                         }
 
-                        namedVarargsHash = new NativeHashEx2();
+                        namedVarargsHash = new NativeHashEx();
                     }
                     namedVarargsHash.put(namedArg.name, namedArg.value.eval(env));
                 }

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/994775e4/freemarker-core/src/main/java/org/apache/freemarker/core/model/ArgumentArrayLayout.java
----------------------------------------------------------------------
diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/model/ArgumentArrayLayout.java b/freemarker-core/src/main/java/org/apache/freemarker/core/model/ArgumentArrayLayout.java
index 78e7d46..3da8d6d 100644
--- a/freemarker-core/src/main/java/org/apache/freemarker/core/model/ArgumentArrayLayout.java
+++ b/freemarker-core/src/main/java/org/apache/freemarker/core/model/ArgumentArrayLayout.java
@@ -57,9 +57,9 @@ import org.apache.freemarker.core.util.StringToIndexMap;
  *     an empty {@link TemplateSequenceModel} (like {@link TemplateSequenceModel#EMPTY_SEQUENCE}).
  * <li>
  *     If there's a named varargs argument, then 1 element for the positional varargs parameter (whose value will be
- *     a {@link TemplateHashModelEx2}), at index {@link #getNamedVarargsArgumentIndex()}. This must not be left
+ *     a {@link TemplateHashModelEx}), at index {@link #getNamedVarargsArgumentIndex()}. This must not be left
  *     {@code null} in the argument array. In case there are 0 named varargs, the caller must set it to an empty
- *     {@link TemplateHashModelEx2} (like {@link TemplateHashModel#EMPTY_HASH}).
+ *     {@link TemplateHashModelEx} (like {@link TemplateHashModel#EMPTY_HASH}).
  * </ol>
  * <p>
  * The length of the argument array (allocated by the caller of {@code execute}) is {@link #getTotalLength()}}, or
@@ -221,7 +221,7 @@ public final class ArgumentArrayLayout {
     /**
      * Returns the index of the varargs argument into which named arguments that aren't predefined (via {@link
      * #getPredefinedNamedArgumentsMap()}) are collected, or -1 if there's no such varargs argument. The value of the
-     * named varargs argument is a {@link TemplateHashModelEx2} with string keys that collects all the named arguments
+     * named varargs argument is a {@link TemplateHashModelEx} with string keys that collects all the named arguments
      * that aren't present in the {@link #getPredefinedNamedArgumentsMap()}. The iteration order of this hash
      * corresponds to the order in which the arguments were specified on the call site (in a template, typically).
      * The value of this argument can't be {@code null}.

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/994775e4/freemarker-core/src/main/java/org/apache/freemarker/core/model/EmptyHashModel.java
----------------------------------------------------------------------
diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/model/EmptyHashModel.java b/freemarker-core/src/main/java/org/apache/freemarker/core/model/EmptyHashModel.java
index 20aebe2..530214b 100644
--- a/freemarker-core/src/main/java/org/apache/freemarker/core/model/EmptyHashModel.java
+++ b/freemarker-core/src/main/java/org/apache/freemarker/core/model/EmptyHashModel.java
@@ -23,7 +23,8 @@ import java.io.Serializable;
 
 import org.apache.freemarker.core.TemplateException;
 
-class EmptyHashModel implements TemplateHashModelEx2, Serializable {
+@SuppressWarnings("serial")
+class EmptyHashModel implements TemplateHashModelEx, Serializable {
 
     @Override
     public int getHashSize() throws TemplateException {

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/994775e4/freemarker-core/src/main/java/org/apache/freemarker/core/model/GeneralPurposeNothing.java
----------------------------------------------------------------------
diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/model/GeneralPurposeNothing.java b/freemarker-core/src/main/java/org/apache/freemarker/core/model/GeneralPurposeNothing.java
index cf3490e..4fbf496 100644
--- a/freemarker-core/src/main/java/org/apache/freemarker/core/model/GeneralPurposeNothing.java
+++ b/freemarker-core/src/main/java/org/apache/freemarker/core/model/GeneralPurposeNothing.java
@@ -30,7 +30,7 @@ import org.apache.freemarker.core.TemplateException;
  */
 // TODO [FM3] As `exp!` doesn't use this, are the other use cases necessary and correct?
 final class GeneralPurposeNothing
-implements TemplateBooleanModel, TemplateStringModel, TemplateSequenceModel, TemplateHashModelEx2,
+implements TemplateBooleanModel, TemplateStringModel, TemplateSequenceModel, TemplateHashModelEx,
         TemplateFunctionModel {
 
     static final TemplateModel INSTANCE = new GeneralPurposeNothing();

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/994775e4/freemarker-core/src/main/java/org/apache/freemarker/core/model/TemplateHashModel.java
----------------------------------------------------------------------
diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/model/TemplateHashModel.java b/freemarker-core/src/main/java/org/apache/freemarker/core/model/TemplateHashModel.java
index d15d1fd..61b3494 100644
--- a/freemarker-core/src/main/java/org/apache/freemarker/core/model/TemplateHashModel.java
+++ b/freemarker-core/src/main/java/org/apache/freemarker/core/model/TemplateHashModel.java
@@ -23,13 +23,13 @@ import org.apache.freemarker.core.TemplateException;
 
 /**
  * "hash" template language data type: an object that contains other objects accessible through string keys
- * (sub-variable names).
+ * (sub-variable names). It, in itself, doesn't support listing the keys or values ({@link TemplateHashModelEx} does).
  * 
  * <p>In templates they are used like {@code myHash.myKey} or {@code myHash[myDynamicKey]}. 
  */
 public interface TemplateHashModel extends TemplateModel {
 
-    TemplateHashModelEx2 EMPTY_HASH = new EmptyHashModel();
+    TemplateHashModelEx EMPTY_HASH = new EmptyHashModel();
 
     /**
      * Gets a <tt>TemplateModel</tt> from the hash.

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/994775e4/freemarker-core/src/main/java/org/apache/freemarker/core/model/TemplateHashModelEx.java
----------------------------------------------------------------------
diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/model/TemplateHashModelEx.java b/freemarker-core/src/main/java/org/apache/freemarker/core/model/TemplateHashModelEx.java
index 30ad102..ef22897 100644
--- a/freemarker-core/src/main/java/org/apache/freemarker/core/model/TemplateHashModelEx.java
+++ b/freemarker-core/src/main/java/org/apache/freemarker/core/model/TemplateHashModelEx.java
@@ -22,14 +22,17 @@ package org.apache.freemarker.core.model;
 import java.util.Iterator;
 
 import org.apache.freemarker.core.TemplateException;
+import org.apache.freemarker.core.model.impl.DefaultMapAdapter;
 import org.apache.freemarker.core.model.impl.SimpleHash;
 
 /**
  * "extended hash" template language data type; extends {@link TemplateHashModel} by allowing
- * iterating through its keys and values.
+ * iterating through its key-value pairs, or just the keys, or just the values.
  * 
  * <p>In templates they are used like hashes, but these will also work (among others):
- * {@code myExtHash?size}, {@code myExtHash?keys}, {@code myExtHash?values}.
+ * {@code myExtHash?size}, {@code myExtHash?keys}, {@code myExtHash?values}, {@code <#list myMap as k, v>}.
+ * 
+ * @see DefaultMapAdapter
  * @see SimpleHash
  */
 public interface TemplateHashModelEx extends TemplateHashModel {

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/994775e4/freemarker-core/src/main/java/org/apache/freemarker/core/model/TemplateHashModelEx2.java
----------------------------------------------------------------------
diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/model/TemplateHashModelEx2.java b/freemarker-core/src/main/java/org/apache/freemarker/core/model/TemplateHashModelEx2.java
deleted file mode 100644
index 30a92d4..0000000
--- a/freemarker-core/src/main/java/org/apache/freemarker/core/model/TemplateHashModelEx2.java
+++ /dev/null
@@ -1,30 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *   http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.apache.freemarker.core.model;
-
-/**
- * Adds key-value pair listing capability to {@link TemplateHashModelEx}. While in many cases that can also be achieved
- * with {@link #keys()} and then {@link #get(String)}, that has some problems. One is that {@link #get(String)} only
- * accepts string keys, while {@link #keys()} can return non-string keys too. The other is that calling {@link #keys()}
- * and then {@link #get(String)} for each key can be slower than listing the key-value pairs in one go.
- */
-// TODO [FM3][CF] Remove
-public interface TemplateHashModelEx2 extends TemplateHashModelEx {
-    
-}

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/994775e4/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/DefaultMapAdapter.java
----------------------------------------------------------------------
diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/DefaultMapAdapter.java b/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/DefaultMapAdapter.java
index 9f66026..4deca4c 100644
--- a/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/DefaultMapAdapter.java
+++ b/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/DefaultMapAdapter.java
@@ -29,7 +29,6 @@ import org.apache.freemarker.core.model.ObjectWrapper;
 import org.apache.freemarker.core.model.ObjectWrapperWithAPISupport;
 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.TemplateModel;
 import org.apache.freemarker.core.model.TemplateModelWithAPISupport;
 import org.apache.freemarker.core.model.WrapperTemplateModel;
@@ -50,7 +49,7 @@ import org.apache.freemarker.core.model.WrappingTemplateModel;
  * {@code true}, which is the default when its {@code incompatibleImprovements} property is 2.3.22 or higher.
  */
 public class DefaultMapAdapter extends WrappingTemplateModel
-        implements TemplateHashModelEx2, AdapterTemplateModel, WrapperTemplateModel, TemplateModelWithAPISupport {
+        implements TemplateHashModelEx, AdapterTemplateModel, WrapperTemplateModel, TemplateModelWithAPISupport {
 
     private final Map map;
 

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/994775e4/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/MapKeyValuePairIterator.java
----------------------------------------------------------------------
diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/MapKeyValuePairIterator.java b/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/MapKeyValuePairIterator.java
index 539267c..0161775 100644
--- a/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/MapKeyValuePairIterator.java
+++ b/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/MapKeyValuePairIterator.java
@@ -27,11 +27,10 @@ import org.apache.freemarker.core.model.ObjectWrapper;
 import org.apache.freemarker.core.model.ObjectWrappingException;
 import org.apache.freemarker.core.model.TemplateHashModelEx;
 import org.apache.freemarker.core.model.TemplateHashModelEx.KeyValuePairIterator;
-import org.apache.freemarker.core.model.TemplateHashModelEx2;
 import org.apache.freemarker.core.model.TemplateModel;
 
 /**
- *  Implementation of {@link KeyValuePairIterator} for a {@link TemplateHashModelEx2} that wraps or otherwise uses a
+ *  Implementation of {@link KeyValuePairIterator} for a {@link TemplateHashModelEx} that wraps or otherwise uses a
  *  {@link Map} internally.
  */
 public class MapKeyValuePairIterator implements TemplateHashModelEx.KeyValuePairIterator {

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/994775e4/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/SimpleHash.java
----------------------------------------------------------------------
diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/SimpleHash.java b/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/SimpleHash.java
index 4ed6a8d..47eb1db 100644
--- a/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/SimpleHash.java
+++ b/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/SimpleHash.java
@@ -32,7 +32,6 @@ import org.apache.freemarker.core.model.ObjectWrapper;
 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.TemplateModel;
 import org.apache.freemarker.core.model.WrappingTemplateModel;
 
@@ -74,7 +73,7 @@ import org.apache.freemarker.core.model.WrappingTemplateModel;
  * @see DefaultMapAdapter
  * @see TemplateHashModelEx
  */
-public class SimpleHash extends WrappingTemplateModel implements TemplateHashModelEx2 {
+public class SimpleHash extends WrappingTemplateModel implements TemplateHashModelEx {
 
     private final Map map;
     private boolean putFailed;

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/994775e4/freemarker-servlet/src/main/java/org/apache/freemarker/servlet/jsp/JspTagModelBase.java
----------------------------------------------------------------------
diff --git a/freemarker-servlet/src/main/java/org/apache/freemarker/servlet/jsp/JspTagModelBase.java b/freemarker-servlet/src/main/java/org/apache/freemarker/servlet/jsp/JspTagModelBase.java
index 52e7f9d..3e01c54 100644
--- a/freemarker-servlet/src/main/java/org/apache/freemarker/servlet/jsp/JspTagModelBase.java
+++ b/freemarker-servlet/src/main/java/org/apache/freemarker/servlet/jsp/JspTagModelBase.java
@@ -40,7 +40,6 @@ import org.apache.freemarker.core._DelayedShortClassName;
 import org.apache.freemarker.core._ErrorDescriptionBuilder;
 import org.apache.freemarker.core.model.ObjectWrapperAndUnwrapper;
 import org.apache.freemarker.core.model.TemplateHashModelEx;
-import org.apache.freemarker.core.model.TemplateHashModelEx2;
 import org.apache.freemarker.core.model.TemplateModelWithOriginName;
 import org.apache.freemarker.core.model.TemplateStringModel;
 import org.apache.freemarker.core.model.impl.DefaultObjectWrapper;
@@ -69,7 +68,7 @@ abstract class JspTagModelBase implements TemplateModelWithOriginName {
         return tagClass.newInstance();
     }
     
-    void setupTag(JspTag tag, TemplateHashModelEx2 args, ObjectWrapperAndUnwrapper wrapper)
+    void setupTag(JspTag tag, TemplateHashModelEx args, ObjectWrapperAndUnwrapper wrapper)
             throws TemplateException,
         InvocationTargetException, 
         IllegalAccessException {

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/994775e4/freemarker-servlet/src/main/java/org/apache/freemarker/servlet/jsp/SimpleTagDirectiveModel.java
----------------------------------------------------------------------
diff --git a/freemarker-servlet/src/main/java/org/apache/freemarker/servlet/jsp/SimpleTagDirectiveModel.java b/freemarker-servlet/src/main/java/org/apache/freemarker/servlet/jsp/SimpleTagDirectiveModel.java
index 01bbfa4..28155d7 100644
--- a/freemarker-servlet/src/main/java/org/apache/freemarker/servlet/jsp/SimpleTagDirectiveModel.java
+++ b/freemarker-servlet/src/main/java/org/apache/freemarker/servlet/jsp/SimpleTagDirectiveModel.java
@@ -30,12 +30,12 @@ import javax.servlet.jsp.tagext.JspTag;
 import javax.servlet.jsp.tagext.SimpleTag;
 import javax.servlet.jsp.tagext.Tag;
 
+import org.apache.freemarker.core.CallPlace;
 import org.apache.freemarker.core.Environment;
 import org.apache.freemarker.core.TemplateException;
 import org.apache.freemarker.core.model.ArgumentArrayLayout;
-import org.apache.freemarker.core.CallPlace;
 import org.apache.freemarker.core.model.TemplateDirectiveModel;
-import org.apache.freemarker.core.model.TemplateHashModelEx2;
+import org.apache.freemarker.core.model.TemplateHashModelEx;
 import org.apache.freemarker.core.model.TemplateModel;
 
 /**
@@ -71,7 +71,7 @@ class SimpleTagDirectiveModel extends JspTagModelBase implements TemplateDirecti
                 if (parentTag != null) {
                     tag.setParent(parentTag);
                 }
-                setupTag(tag, (TemplateHashModelEx2) args[ARGS_LAYOUT.getNamedVarargsArgumentIndex()],
+                setupTag(tag, (TemplateHashModelEx) args[ARGS_LAYOUT.getNamedVarargsArgumentIndex()],
                         pageContext.getObjectWrapper());
                 if (callPlace.hasNestedContent()) {
                     tag.setJspBody(new JspFragment() {

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/994775e4/freemarker-servlet/src/main/java/org/apache/freemarker/servlet/jsp/TagDirectiveModel.java
----------------------------------------------------------------------
diff --git a/freemarker-servlet/src/main/java/org/apache/freemarker/servlet/jsp/TagDirectiveModel.java b/freemarker-servlet/src/main/java/org/apache/freemarker/servlet/jsp/TagDirectiveModel.java
index d5705c8..3831c20 100644
--- a/freemarker-servlet/src/main/java/org/apache/freemarker/servlet/jsp/TagDirectiveModel.java
+++ b/freemarker-servlet/src/main/java/org/apache/freemarker/servlet/jsp/TagDirectiveModel.java
@@ -36,7 +36,7 @@ import org.apache.freemarker.core.Environment;
 import org.apache.freemarker.core.TemplateException;
 import org.apache.freemarker.core.model.ArgumentArrayLayout;
 import org.apache.freemarker.core.model.TemplateDirectiveModel;
-import org.apache.freemarker.core.model.TemplateHashModelEx2;
+import org.apache.freemarker.core.model.TemplateHashModelEx;
 import org.apache.freemarker.core.model.TemplateModel;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -72,7 +72,7 @@ class TagDirectiveModel extends JspTagModelBase implements TemplateDirectiveMode
             Tag parentTag = (Tag) pageContext.peekTopTag(Tag.class);
             tag.setParent(parentTag);
             tag.setPageContext(pageContext);
-            setupTag(tag, (TemplateHashModelEx2) args[ARGS_LAYOUT.getNamedVarargsArgumentIndex()],
+            setupTag(tag, (TemplateHashModelEx) args[ARGS_LAYOUT.getNamedVarargsArgumentIndex()],
                     pageContext.getObjectWrapper());
             // If the parent of this writer is not a JspWriter itself, use
             // a little Writer-to-JspWriter adapter...

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/994775e4/freemarker-spring/src/main/java/org/apache/freemarker/spring/model/UrlFunction.java
----------------------------------------------------------------------
diff --git a/freemarker-spring/src/main/java/org/apache/freemarker/spring/model/UrlFunction.java b/freemarker-spring/src/main/java/org/apache/freemarker/spring/model/UrlFunction.java
index 3381f9c..66b458f 100644
--- a/freemarker-spring/src/main/java/org/apache/freemarker/spring/model/UrlFunction.java
+++ b/freemarker-spring/src/main/java/org/apache/freemarker/spring/model/UrlFunction.java
@@ -38,8 +38,6 @@ import org.apache.freemarker.core.model.ArgumentArrayLayout;
 import org.apache.freemarker.core.model.ObjectWrapperAndUnwrapper;
 import org.apache.freemarker.core.model.TemplateBooleanModel;
 import org.apache.freemarker.core.model.TemplateHashModelEx;
-import org.apache.freemarker.core.model.TemplateHashModelEx.KeyValuePairIterator;
-import org.apache.freemarker.core.model.TemplateHashModelEx2;
 import org.apache.freemarker.core.model.TemplateModel;
 import org.apache.freemarker.core.model.TemplateNumberModel;
 import org.apache.freemarker.core.model.TemplateStringModel;
@@ -110,7 +108,7 @@ class UrlFunction extends AbstractSpringTemplateFunctionModel {
 
         List<_KeyValuePair<String, String>> params = Collections.emptyList();
         final int paramsVarargsIndex = ARGS_LAYOUT.getNamedVarargsArgumentIndex();
-        final TemplateHashModelEx2 paramsHashModel = (TemplateHashModelEx2) args[paramsVarargsIndex];
+        final TemplateHashModelEx paramsHashModel = (TemplateHashModelEx) args[paramsVarargsIndex];
 
         if (!paramsHashModel.isEmptyHash()) {
             params = new ArrayList<>();