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 23:14:16 UTC

incubator-freemarker git commit: `TemplateHashModel` doesn't have `isEmpty()` method anymore. (As the point of a pure `TemplateHashModel` is that it's not able to list its keys, almost all `isEmpty()` implementations in FM2 were just dummies returning `f

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


`TemplateHashModel` doesn't have `isEmpty()` method anymore. (As the point of a pure `TemplateHashModel` is that it's not able to list its keys, almost all `isEmpty()` implementations in FM2 were just dummies returning `false`.) Note that `?hashContent` now returns `true` for `TemplateHashModel` that aren't also `TemplateHashModelEx2`-s, based on the idea that for some `key` (which you may don't know) `get(key)` might returns something.


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

Branch: refs/heads/3
Commit: ba722858c095c57f05aedc97c63d2d8d3023d88b
Parents: 994775e
Author: ddekany <dd...@apache.org>
Authored: Sat Oct 21 01:13:56 2017 +0200
Committer: ddekany <dd...@apache.org>
Committed: Sat Oct 21 01:13:56 2017 +0200

----------------------------------------------------------------------
 FM3-CHANGE-LOG.txt                              |   6 ++
 .../freemarker/core/ConcatenatedHashTest.java   |  10 +-
 .../model/impl/DefaultObjectWrapperTest.java    |   2 +-
 .../core/templatesuite/models/BooleanHash1.java |   8 --
 .../core/templatesuite/models/BooleanHash2.java |   8 --
 .../core/templatesuite/models/MultiModel1.java  |   9 +-
 .../core/templatesuite/models/MultiModel3.java  |   5 -
 .../core/templatesuite/models/MultiModel4.java  |   7 +-
 .../core/templatesuite/models/MultiModel5.java  |   7 +-
 .../freemarker/core/ASTExpAddOrConcat.java      |  11 +-
 .../freemarker/core/ASTExpBuiltInVariable.java  |  10 +-
 .../apache/freemarker/core/ASTExpression.java   |   3 +-
 .../core/BuiltInsForMultipleTypes.java          |  17 ---
 .../org/apache/freemarker/core/Environment.java | 103 ++++++++-----------
 .../core/debug/RmiDebuggedEnvironmentImpl.java  |   2 +-
 .../core/model/TemplateHashModel.java           |   1 -
 .../core/model/TemplateHashModelEx.java         |   5 +
 .../core/model/impl/ClassBasedModelFactory.java |   5 -
 .../model/impl/TemplateHashModelAdapter.java    |  11 +-
 .../freemarker/dom/AttributeNodeModel.java      |   5 -
 .../freemarker/dom/CharacterDataNodeModel.java  |   5 -
 .../apache/freemarker/dom/DocumentModel.java    |   5 -
 .../freemarker/dom/DocumentTypeModel.java       |   4 -
 .../org/apache/freemarker/dom/ElementModel.java |   7 +-
 .../apache/freemarker/dom/NodeListModel.java    |   9 +-
 .../org/apache/freemarker/dom/NodeModel.java    |   8 --
 .../org/apache/freemarker/dom/PINodeModel.java  |   4 -
 .../servlet/HttpSessionHashModel.java           |   9 +-
 .../servlet/ServletContextHashModel.java        |   6 +-
 .../freemarker/servlet/jsp/TaglibFactory.java   |  14 +--
 .../model/SpringTemplateCallableHashModel.java  |  10 +-
 31 files changed, 92 insertions(+), 224 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/ba722858/FM3-CHANGE-LOG.txt
----------------------------------------------------------------------
diff --git a/FM3-CHANGE-LOG.txt b/FM3-CHANGE-LOG.txt
index b7d4987..494b7eb 100644
--- a/FM3-CHANGE-LOG.txt
+++ b/FM3-CHANGE-LOG.txt
@@ -437,6 +437,10 @@ Core / Models and Object wrapping
   - Map-like interfaces:
     - `TemplateHashModelEx2` was removed, as the `keyValuePairIterator()` method was moved to `TemplateHashModelEx`,
        so now the two interfaces would be the same.
+    - `TemplateHashModel` doesn't have `isEmpty()` method anymore. (As the point of a pure `TemplateHashModel` is that
+      it's not able to list its keys, almost all `isEmpty()` implementations in FM2 were just dummies returning `false`.)
+      Note that `?hashContent` now returns `true` for `TemplateHashModel` that aren't also `TemplateHashModelEx2`-s,
+      based on the idea that for some `key` (which you may don't know) `get(key)` might returns something.
 - BeanModel.keys() and values() are now final methods. Override BeanModel.keySet() and/or get(String) instead.
       
 Core / Template loading and caching
@@ -550,6 +554,8 @@ Core / Miscellaneous
   using simple `TemplateException` instead. Catching these exceptions specifically wasn't very useful, while they
   have bloated the public API (and the code).
 - Added `org.apache.freemarker.core.util.CallableUtils`, to help in implementing (and invoking) `TemplateCallableModel`-s.
+- Renamed `Environment.getDataModel()` to `getDataModelWithSharedVariableFallback()`
+  Renamed `Environment.getGlobalVariables()` to `getGloballyVisibleVariables()()`
 
 Build / development process changes
 -----------------------------------

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/ba722858/freemarker-core-test/src/test/java/org/apache/freemarker/core/ConcatenatedHashTest.java
----------------------------------------------------------------------
diff --git a/freemarker-core-test/src/test/java/org/apache/freemarker/core/ConcatenatedHashTest.java b/freemarker-core-test/src/test/java/org/apache/freemarker/core/ConcatenatedHashTest.java
index 70c5c07..dca1349 100644
--- a/freemarker-core-test/src/test/java/org/apache/freemarker/core/ConcatenatedHashTest.java
+++ b/freemarker-core-test/src/test/java/org/apache/freemarker/core/ConcatenatedHashTest.java
@@ -107,12 +107,6 @@ public class ConcatenatedHashTest {
     private TemplateHashModel toHashModelNonEx(Map<String, ?> map) throws ObjectWrappingException {
         final TemplateHashModelEx tm = toHashModelEx(map);
         return new TemplateHashModel() {
-
-            @Override
-            public boolean isEmptyHash() throws TemplateException {
-                return tm.isEmptyHash();
-            }
-
             @Override
             public TemplateModel get(String key) throws TemplateException {
                 return tm.get(key);
@@ -127,12 +121,12 @@ public class ConcatenatedHashTest {
             assertEquals(ent.getValue(), ow.unwrap(value));
         }
 
-        assertEquals(expected.isEmpty(), actual.isEmptyHash());
-
         if (actual instanceof TemplateHashModelEx) {
             TemplateHashModelEx actualEx = (TemplateHashModelEx) actual;
 
             assertEquals(expected.size(), actualEx.getHashSize());
+            
+            assertEquals(expected.isEmpty(), actualEx.isEmptyHash());
 
             // Keys:
             {

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/ba722858/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 00b569f..d2b37f6 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
@@ -272,7 +272,7 @@ public class DefaultObjectWrapperTest {
         }
 
         {
-            assertTrue(((TemplateHashModel) OW.wrap(Collections.emptyMap())).isEmptyHash());
+            assertTrue(((TemplateHashModelEx) OW.wrap(Collections.emptyMap())).isEmptyHash());
         }
     }
 

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/ba722858/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 9302f68..da94fa6 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
@@ -47,12 +47,4 @@ public class BooleanHash1 implements TemplateHashModel {
             return new SimpleString( "Just another key..." );
         }
     }
-
-    /**
-     * @return true if this object is empty.
-     */
-    @Override
-    public boolean isEmptyHash() {
-        return true;
-    }
 }

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/ba722858/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 54e9cb3..bf7dcce 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
@@ -39,12 +39,4 @@ public class BooleanHash2 implements TemplateHashModel {
     public TemplateModel get(String key) {
         return null;
     }
-
-    /**
-     * @return true if this object is empty.
-     */
-    @Override
-    public boolean isEmptyHash() {
-        return false;
-    }
 }

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/ba722858/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 b008ac2..a1459b4 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
@@ -25,12 +25,12 @@ 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.TemplateStringModel;
 import org.apache.freemarker.core.model.impl.DefaultObjectWrapper;
 import org.apache.freemarker.core.model.impl.SimpleHash;
-import org.apache.freemarker.core.model.impl.SimpleString;
 import org.apache.freemarker.core.model.impl.SimpleSequence;
+import org.apache.freemarker.core.model.impl.SimpleString;
 
 /**
  * Testcase to see how FreeMarker deals with multiple Template models.
@@ -76,11 +76,6 @@ public class MultiModel1 implements TemplateHashModel,
     }
 
     @Override
-    public boolean isEmptyHash() {
-        return false;
-    }
-
-    @Override
     public TemplateModel get(int i) throws TemplateException {
         return m_cListModel.get( i );
     }

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/ba722858/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 4f96f13..673e3bc 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,11 +35,6 @@ public class MultiModel3 implements TemplateStringModel, TemplateHashModel {
     }
 
     @Override
-    public boolean isEmptyHash() {
-        return false;
-    }
-
-    @Override
     public TemplateModel get(String key) {
         if ( key.equals( "selftest" )) {
             return new SimpleString( "Selftest from MultiModel3!" );

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/ba722858/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 9d6b8e5..262d48e 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
@@ -25,8 +25,8 @@ 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;
+import org.apache.freemarker.core.model.impl.SimpleString;
 
 /**
  * Testcase to see how FreeMarker deals with multiple Template models.
@@ -64,11 +64,6 @@ public class MultiModel4 implements TemplateSequenceModel, TemplateHashModel {
     }
 
     @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/ba722858/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 c794dd4..2a353dc 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
@@ -25,8 +25,8 @@ 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;
+import org.apache.freemarker.core.model.impl.SimpleString;
 
 /**
  * Testcase to see how FreeMarker deals with multiple Template models.
@@ -51,11 +51,6 @@ public class MultiModel5 implements TemplateSequenceModel, TemplateHashModel {
     }
 
     @Override
-    public boolean isEmptyHash() {
-        return false;
-    }
-
-    @Override
     public int getCollectionSize() {
         return m_cList.getCollectionSize();
     }

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/ba722858/freemarker-core/src/main/java/org/apache/freemarker/core/ASTExpAddOrConcat.java
----------------------------------------------------------------------
diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/ASTExpAddOrConcat.java b/freemarker-core/src/main/java/org/apache/freemarker/core/ASTExpAddOrConcat.java
index 320c427..38ed36d 100644
--- a/freemarker-core/src/main/java/org/apache/freemarker/core/ASTExpAddOrConcat.java
+++ b/freemarker-core/src/main/java/org/apache/freemarker/core/ASTExpAddOrConcat.java
@@ -266,11 +266,6 @@ final class ASTExpAddOrConcat extends ASTExpression {
             return (model != null) ? model : left.get(key);
         }
 
-        @Override
-        public boolean isEmptyHash()
-        throws TemplateException {
-            return left.isEmptyHash() && right.isEmptyHash();
-        }
     }
 
     private static final class ConcatenatedHashEx extends ConcatenatedHash implements TemplateHashModelEx {
@@ -288,6 +283,12 @@ final class ASTExpAddOrConcat extends ASTExpression {
         }
 
         @Override
+        public boolean isEmptyHash()
+        throws TemplateException {
+            return ((TemplateHashModelEx) left).isEmptyHash() && ((TemplateHashModelEx) right).isEmptyHash();
+        }
+        
+        @Override
         public TemplateCollectionModel keys() throws TemplateException {
             initKvps();
             return new TemplateCollectionModel() {

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/ba722858/freemarker-core/src/main/java/org/apache/freemarker/core/ASTExpBuiltInVariable.java
----------------------------------------------------------------------
diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/ASTExpBuiltInVariable.java b/freemarker-core/src/main/java/org/apache/freemarker/core/ASTExpBuiltInVariable.java
index 4570948..19b0b04 100644
--- a/freemarker-core/src/main/java/org/apache/freemarker/core/ASTExpBuiltInVariable.java
+++ b/freemarker-core/src/main/java/org/apache/freemarker/core/ASTExpBuiltInVariable.java
@@ -141,14 +141,14 @@ final class ASTExpBuiltInVariable extends ASTExpression {
             return env.getMainNamespace();
         }
         if (name == GLOBALS) {
-            return env.getGlobalVariables();
+            return env.getGloballyVisibleVariables();
         }
         if (name == LOCALS) {
             ASTDirMacroOrFunction.Context ctx = env.getCurrentMacroContext();
             return ctx == null ? null : ctx.getLocals();
         }
         if (name == DATA_MODEL) {
-            return env.getDataModel();
+            return env.getDataModelWithSharedVariableFallback();
         }
         if (name == VARS) {
             return new VarsHash(env);
@@ -226,7 +226,6 @@ final class ASTExpBuiltInVariable extends ASTExpression {
     }
 
     static class VarsHash implements TemplateHashModel {
-        
         Environment env;
         
         VarsHash(Environment env) {
@@ -237,11 +236,6 @@ final class ASTExpBuiltInVariable extends ASTExpression {
         public TemplateModel get(String key) throws TemplateException {
             return env.getVariable(key);
         }
-        
-        @Override
-        public boolean isEmptyHash() {
-            return false;
-        }
     }
     
     @Override

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/ba722858/freemarker-core/src/main/java/org/apache/freemarker/core/ASTExpression.java
----------------------------------------------------------------------
diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/ASTExpression.java b/freemarker-core/src/main/java/org/apache/freemarker/core/ASTExpression.java
index d92b477..31c855e 100644
--- a/freemarker-core/src/main/java/org/apache/freemarker/core/ASTExpression.java
+++ b/freemarker-core/src/main/java/org/apache/freemarker/core/ASTExpression.java
@@ -23,6 +23,7 @@ import org.apache.freemarker.core.model.TemplateBooleanModel;
 import org.apache.freemarker.core.model.TemplateCollectionModel;
 import org.apache.freemarker.core.model.TemplateDateModel;
 import org.apache.freemarker.core.model.TemplateHashModel;
+import org.apache.freemarker.core.model.TemplateHashModelEx;
 import org.apache.freemarker.core.model.TemplateIterableModel;
 import org.apache.freemarker.core.model.TemplateMarkupOutputModel;
 import org.apache.freemarker.core.model.TemplateModel;
@@ -197,7 +198,7 @@ abstract class ASTExpression extends ASTNode {
         } else if (model instanceof TemplateIterableModel) {
             return !((TemplateIterableModel) model).iterator().hasNext();
         } else if (model instanceof TemplateHashModel) {
-            return ((TemplateHashModel) model).isEmptyHash();
+            return (model instanceof TemplateHashModelEx) ? ((TemplateHashModelEx) model).isEmptyHash() : false;
         } else if (model instanceof TemplateNumberModel
                 || model instanceof TemplateDateModel
                 || model instanceof TemplateBooleanModel) {

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/ba722858/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 4f9a08d..cf31218 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
@@ -170,11 +170,6 @@ class BuiltInsForMultipleTypes {
                 return dateType;
             }
     
-            @Override
-            public boolean isEmptyHash() {
-                return false;
-            }
-    
             private Object parse(TemplateDateFormat df)
             throws TemplateException {
                 try {
@@ -530,17 +525,11 @@ class BuiltInsForMultipleTypes {
                 }
                 return cachedValue;
             }
-    
-            @Override
-            public boolean isEmptyHash() {
-                return false;
-            }
         }
         
         private class NumberFormatter extends BuiltInCallableImpl
                 implements TemplateStringModel, TemplateHashModel, TemplateFunctionModel {
             private final TemplateNumberModel numberModel;
-            private final Number number;
             private final Environment env;
             private final TemplateNumberFormat defaultFormat;
             private String cachedValue;
@@ -550,7 +539,6 @@ class BuiltInsForMultipleTypes {
                 
                 // As we format lazily, we need a snapshot of the format inputs:
                 this.numberModel = numberModel;
-                number = _EvalUtils.modelToNumber(numberModel, target);  // for BackwardCompatibleTemplateNumberFormat-s
                 defaultFormat = env.getTemplateNumberFormat(stringBI.this);
             }
 
@@ -581,11 +569,6 @@ class BuiltInsForMultipleTypes {
                 }
                 return cachedValue;
             }
-    
-            @Override
-            public boolean isEmptyHash() {
-                return false;
-            }
         }
     
         @Override

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/ba722858/freemarker-core/src/main/java/org/apache/freemarker/core/Environment.java
----------------------------------------------------------------------
diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/Environment.java b/freemarker-core/src/main/java/org/apache/freemarker/core/Environment.java
index 75bada7..ad6b79b 100644
--- a/freemarker-core/src/main/java/org/apache/freemarker/core/Environment.java
+++ b/freemarker-core/src/main/java/org/apache/freemarker/core/Environment.java
@@ -2221,64 +2221,53 @@ public final class Environment extends MutableProcessingConfiguration<Environmen
     }
 
     /**
-     * Returns the data-model (also known as the template context in some other template engines).
-     */
-    public TemplateHashModel getDataModel() {
-        final TemplateHashModel result = new TemplateHashModel() {
-
-            @Override
-            public boolean isEmptyHash() {
-                return false;
-            }
-
-            @Override
-            public TemplateModel get(String key) throws TemplateException {
-                TemplateModel value = rootDataModel.get(key);
-                if (value == null) {
-                    value = configuration.getWrappedSharedVariable(key);
-                }
-                return value;
-            }
-        };
-
-        if (rootDataModel instanceof TemplateHashModelEx) {
-            return new TemplateHashModelEx() {
-
-                @Override
-                public boolean isEmptyHash() throws TemplateException {
-                    return result.isEmptyHash();
+     * Returns a view of the data-model (also known as the template context in some other template engines) 
+     * that falls back to {@linkplain Configuration#getSharedVariables() shared variables}.
+     */
+    public TemplateHashModel getDataModelWithSharedVariableFallback() {
+        return rootDataModel instanceof TemplateHashModelEx
+                ? new TemplateHashModelEx() {
+                    @Override
+                    public boolean isEmptyHash() throws TemplateException {
+                        return ((TemplateHashModelEx) rootDataModel).isEmptyHash();
+                    }
+    
+                    @Override
+                    public TemplateModel get(String key) throws TemplateException {
+                        TemplateModel value = rootDataModel.get(key);
+                        return value != null ? value : configuration.getWrappedSharedVariable(key);
+                    }
+    
+                    // NB: The methods below do not take into account
+                    // configuration shared variables even though
+                    // the hash will return them, if only for BWC reasons
+                    @Override
+                    public TemplateCollectionModel values() throws TemplateException {
+                        return ((TemplateHashModelEx) rootDataModel).values();
+                    }
+    
+                    @Override
+                    public TemplateCollectionModel keys() throws TemplateException {
+                        return ((TemplateHashModelEx) rootDataModel).keys();
+                    }
+                    
+                    @Override
+                    public KeyValuePairIterator keyValuePairIterator() throws TemplateException {
+                        return ((TemplateHashModelEx) rootDataModel).keyValuePairIterator();
+                    }
+    
+                    @Override
+                    public int getHashSize() throws TemplateException {
+                        return ((TemplateHashModelEx) rootDataModel).getHashSize();
+                    }
                 }
-
+            : new TemplateHashModel() {
                 @Override
                 public TemplateModel get(String key) throws TemplateException {
-                    return result.get(key);
-                }
-
-                // NB: The methods below do not take into account
-                // configuration shared variables even though
-                // the hash will return them, if only for BWC reasons
-                @Override
-                public TemplateCollectionModel values() throws TemplateException {
-                    return ((TemplateHashModelEx) rootDataModel).values();
-                }
-
-                @Override
-                public TemplateCollectionModel keys() throws TemplateException {
-                    return ((TemplateHashModelEx) rootDataModel).keys();
-                }
-                
-                @Override
-                public KeyValuePairIterator keyValuePairIterator() throws TemplateException {
-                    return ((TemplateHashModelEx) rootDataModel).keyValuePairIterator();
-                }
-
-                @Override
-                public int getHashSize() throws TemplateException {
-                    return ((TemplateHashModelEx) rootDataModel).getHashSize();
+                    TemplateModel value = rootDataModel.get(key);
+                    return value != null ? value : configuration.getWrappedSharedVariable(key);
                 }
             };
-        }
-        return result;
     }
 
     /**
@@ -2286,14 +2275,8 @@ public final class Environment extends MutableProcessingConfiguration<Environmen
      * hash. That is, you see the variables created with <code>&lt;#global ...&gt;</code>, and the variables of the
      * data-model. To invoke new global variables, use {@link #setGlobalVariable setGlobalVariable}.
      */
-    public TemplateHashModel getGlobalVariables() {
+    public TemplateHashModel getGloballyVisibleVariables() {
         return new TemplateHashModel() {
-
-            @Override
-            public boolean isEmptyHash() {
-                return false;
-            }
-
             @Override
             public TemplateModel get(String key) throws TemplateException {
                 TemplateModel result = globalNamespace.get(key);

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/ba722858/freemarker-core/src/main/java/org/apache/freemarker/core/debug/RmiDebuggedEnvironmentImpl.java
----------------------------------------------------------------------
diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/debug/RmiDebuggedEnvironmentImpl.java b/freemarker-core/src/main/java/org/apache/freemarker/core/debug/RmiDebuggedEnvironmentImpl.java
index 7fad936..9f85237 100644
--- a/freemarker-core/src/main/java/org/apache/freemarker/core/debug/RmiDebuggedEnvironmentImpl.java
+++ b/freemarker-core/src/main/java/org/apache/freemarker/core/debug/RmiDebuggedEnvironmentImpl.java
@@ -328,7 +328,7 @@ class RmiDebuggedEnvironmentImpl extends RmiDebugModelImpl implements DebuggedEn
                 return ((Environment) ProcessingConfiguration).getCurrentNamespace();
             }
             if ("dataModel".equals(key)) {
-                return ((Environment) ProcessingConfiguration).getDataModel();
+                return ((Environment) ProcessingConfiguration).getDataModelWithSharedVariableFallback();
             }
             if ("globalNamespace".equals(key)) {
                 return ((Environment) ProcessingConfiguration).getGlobalNamespace();

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/ba722858/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 61b3494..489d5cc 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
@@ -41,5 +41,4 @@ public interface TemplateHashModel extends TemplateModel {
      */
     TemplateModel get(String key) throws TemplateException;
 
-    boolean isEmptyHash() throws TemplateException;
 }

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/ba722858/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 ef22897..e6a3f14 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
@@ -82,6 +82,11 @@ public interface TemplateHashModelEx extends TemplateHashModel {
     int getHashSize() throws TemplateException;
 
     /**
+     * Returns if the hash size is 0. In some implementations this can be faster than {@link #getHashSize()}.
+     */
+    boolean isEmptyHash() throws TemplateException;
+    
+    /**
      * @return a iterable returning the keys in the hash. Every element of the returned by the iterable must implement
      * the {@link TemplateStringModel} (as the keys of hashes are always strings).
      */

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/ba722858/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/ClassBasedModelFactory.java
----------------------------------------------------------------------
diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/ClassBasedModelFactory.java b/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/ClassBasedModelFactory.java
index 31701cb..087b1c7 100644
--- a/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/ClassBasedModelFactory.java
+++ b/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/ClassBasedModelFactory.java
@@ -134,11 +134,6 @@ abstract class ClassBasedModelFactory implements TemplateHashModel {
             cache.remove(clazz.getName());
         }
     }
-
-    @Override
-    public boolean isEmptyHash() {
-        return false;
-    }
     
     protected abstract TemplateModel createModel(Class clazz) 
     throws TemplateException;

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/ba722858/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/TemplateHashModelAdapter.java
----------------------------------------------------------------------
diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/TemplateHashModelAdapter.java b/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/TemplateHashModelAdapter.java
index b9ca888..5f8a528 100644
--- a/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/TemplateHashModelAdapter.java
+++ b/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/TemplateHashModelAdapter.java
@@ -55,7 +55,7 @@ class TemplateHashModelAdapter extends AbstractMap implements TemplateModelAdapt
     @Override
     public boolean isEmpty() {
         try {
-            return model.isEmptyHash();
+            return getModelEx().isEmptyHash();
         } catch (TemplateException e) {
             throw new UndeclaredThrowableException(e);
         }
@@ -69,6 +69,15 @@ class TemplateHashModelAdapter extends AbstractMap implements TemplateModelAdapt
             throw new UndeclaredThrowableException(e);
         }
     }
+    
+    @Override
+    public int size() {
+        try {
+            return getModelEx().getHashSize();
+        } catch (TemplateException e) {
+            throw new UndeclaredThrowableException(e);
+        }
+    }
 
     @Override
     public boolean containsKey(Object key) {

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/ba722858/freemarker-dom/src/main/java/org/apache/freemarker/dom/AttributeNodeModel.java
----------------------------------------------------------------------
diff --git a/freemarker-dom/src/main/java/org/apache/freemarker/dom/AttributeNodeModel.java b/freemarker-dom/src/main/java/org/apache/freemarker/dom/AttributeNodeModel.java
index b7f7830..f4144ce 100644
--- a/freemarker-dom/src/main/java/org/apache/freemarker/dom/AttributeNodeModel.java
+++ b/freemarker-dom/src/main/java/org/apache/freemarker/dom/AttributeNodeModel.java
@@ -44,11 +44,6 @@ class AttributeNodeModel extends NodeModel implements TemplateStringModel {
     }
     
     @Override
-    public boolean isEmptyHash() {
-        return true;
-    }
-    
-    @Override
     String getQualifiedName() {
         String nsURI = node.getNamespaceURI();
         if (nsURI == null || nsURI.equals(""))

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/ba722858/freemarker-dom/src/main/java/org/apache/freemarker/dom/CharacterDataNodeModel.java
----------------------------------------------------------------------
diff --git a/freemarker-dom/src/main/java/org/apache/freemarker/dom/CharacterDataNodeModel.java b/freemarker-dom/src/main/java/org/apache/freemarker/dom/CharacterDataNodeModel.java
index 024c333..5fad90d 100644
--- a/freemarker-dom/src/main/java/org/apache/freemarker/dom/CharacterDataNodeModel.java
+++ b/freemarker-dom/src/main/java/org/apache/freemarker/dom/CharacterDataNodeModel.java
@@ -38,9 +38,4 @@ class CharacterDataNodeModel extends NodeModel implements TemplateStringModel {
     public String getNodeName() {
         return (node instanceof Comment) ? "@comment" : "@text";
     }
-    
-    @Override
-    public boolean isEmptyHash() {
-        return true;
-    }
 }
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/ba722858/freemarker-dom/src/main/java/org/apache/freemarker/dom/DocumentModel.java
----------------------------------------------------------------------
diff --git a/freemarker-dom/src/main/java/org/apache/freemarker/dom/DocumentModel.java b/freemarker-dom/src/main/java/org/apache/freemarker/dom/DocumentModel.java
index 13b8cf2..6b42e4f 100644
--- a/freemarker-dom/src/main/java/org/apache/freemarker/dom/DocumentModel.java
+++ b/freemarker-dom/src/main/java/org/apache/freemarker/dom/DocumentModel.java
@@ -68,9 +68,4 @@ class DocumentModel extends NodeModel implements TemplateHashModel {
         }
         return rootElement;
     }
-    
-    @Override
-    public boolean isEmptyHash() {
-        return false;
-    }
 } 
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/ba722858/freemarker-dom/src/main/java/org/apache/freemarker/dom/DocumentTypeModel.java
----------------------------------------------------------------------
diff --git a/freemarker-dom/src/main/java/org/apache/freemarker/dom/DocumentTypeModel.java b/freemarker-dom/src/main/java/org/apache/freemarker/dom/DocumentTypeModel.java
index f71506d..e29347e 100644
--- a/freemarker-dom/src/main/java/org/apache/freemarker/dom/DocumentTypeModel.java
+++ b/freemarker-dom/src/main/java/org/apache/freemarker/dom/DocumentTypeModel.java
@@ -49,8 +49,4 @@ class DocumentTypeModel extends NodeModel {
         return "@document_type$" + node.getNodeName();
     }
     
-    @Override
-    public boolean isEmptyHash() {
-        return true;
-    }
 }
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/ba722858/freemarker-dom/src/main/java/org/apache/freemarker/dom/ElementModel.java
----------------------------------------------------------------------
diff --git a/freemarker-dom/src/main/java/org/apache/freemarker/dom/ElementModel.java b/freemarker-dom/src/main/java/org/apache/freemarker/dom/ElementModel.java
index f571e6c..9975097 100644
--- a/freemarker-dom/src/main/java/org/apache/freemarker/dom/ElementModel.java
+++ b/freemarker-dom/src/main/java/org/apache/freemarker/dom/ElementModel.java
@@ -25,8 +25,8 @@ import org.apache.freemarker.core.Environment;
 import org.apache.freemarker.core.Template;
 import org.apache.freemarker.core.TemplateException;
 import org.apache.freemarker.core.model.TemplateModel;
-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.SimpleString;
 import org.apache.freemarker.core.util._StringUtils;
 import org.w3c.dom.Attr;
@@ -40,11 +40,6 @@ class ElementModel extends NodeModel implements TemplateStringModel {
         super(element);
     }
     
-    @Override
-    public boolean isEmptyHash() {
-        return false;
-    }
-    
     /**
      * An Element node supports various hash keys.
      * Any key that corresponds to the tag name of any child elements

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/ba722858/freemarker-dom/src/main/java/org/apache/freemarker/dom/NodeListModel.java
----------------------------------------------------------------------
diff --git a/freemarker-dom/src/main/java/org/apache/freemarker/dom/NodeListModel.java b/freemarker-dom/src/main/java/org/apache/freemarker/dom/NodeListModel.java
index 99d7e61..129f5fc 100644
--- a/freemarker-dom/src/main/java/org/apache/freemarker/dom/NodeListModel.java
+++ b/freemarker-dom/src/main/java/org/apache/freemarker/dom/NodeListModel.java
@@ -32,10 +32,10 @@ import org.apache.freemarker.core.model.TemplateHashModel;
 import org.apache.freemarker.core.model.TemplateModel;
 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.impl.SimpleString;
+import org.apache.freemarker.core.model.TemplateStringModel;
 import org.apache.freemarker.core.model.impl.SimpleSequence;
+import org.apache.freemarker.core.model.impl.SimpleString;
 import org.w3c.dom.NamedNodeMap;
 import org.w3c.dom.Node;
 import org.w3c.dom.NodeList;
@@ -103,11 +103,6 @@ class NodeListModel extends SimpleSequence implements TemplateHashModel, _Unexpe
     }
     
     @Override
-    public boolean isEmptyHash() {
-        return false;
-    }
-    
-    @Override
     public TemplateModel get(String key) throws TemplateException {
         int size = getCollectionSize();
         if (size == 1) {

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/ba722858/freemarker-dom/src/main/java/org/apache/freemarker/dom/NodeModel.java
----------------------------------------------------------------------
diff --git a/freemarker-dom/src/main/java/org/apache/freemarker/dom/NodeModel.java b/freemarker-dom/src/main/java/org/apache/freemarker/dom/NodeModel.java
index ac4494a..356a852 100644
--- a/freemarker-dom/src/main/java/org/apache/freemarker/dom/NodeModel.java
+++ b/freemarker-dom/src/main/java/org/apache/freemarker/dom/NodeModel.java
@@ -211,14 +211,6 @@ abstract public class NodeModel implements TemplateNodeModelEx, TemplateHashMode
         throw new TemplateException("Unknown node type: " + nodeType + ". This should be impossible!");
     }
 
-    /**
-     * Always returns {@code false}.
-     */
-    @Override
-    public boolean isEmptyHash() throws TemplateException {
-        return false;
-    }
-
     @Override
     public TemplateModelIterator iterator() throws TemplateException {
         return new SingleItemTemplateModelIterator(this);

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/ba722858/freemarker-dom/src/main/java/org/apache/freemarker/dom/PINodeModel.java
----------------------------------------------------------------------
diff --git a/freemarker-dom/src/main/java/org/apache/freemarker/dom/PINodeModel.java b/freemarker-dom/src/main/java/org/apache/freemarker/dom/PINodeModel.java
index bbd9c68..87c2dfc 100644
--- a/freemarker-dom/src/main/java/org/apache/freemarker/dom/PINodeModel.java
+++ b/freemarker-dom/src/main/java/org/apache/freemarker/dom/PINodeModel.java
@@ -38,8 +38,4 @@ class PINodeModel extends NodeModel implements TemplateStringModel {
         return "@pi$" + ((ProcessingInstruction) node).getTarget();
     }
     
-    @Override
-    public boolean isEmptyHash() {
-        return true;
-    }
 }
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/ba722858/freemarker-servlet/src/main/java/org/apache/freemarker/servlet/HttpSessionHashModel.java
----------------------------------------------------------------------
diff --git a/freemarker-servlet/src/main/java/org/apache/freemarker/servlet/HttpSessionHashModel.java b/freemarker-servlet/src/main/java/org/apache/freemarker/servlet/HttpSessionHashModel.java
index d2f5723..bf7f5a6 100644
--- a/freemarker-servlet/src/main/java/org/apache/freemarker/servlet/HttpSessionHashModel.java
+++ b/freemarker-servlet/src/main/java/org/apache/freemarker/servlet/HttpSessionHashModel.java
@@ -34,7 +34,7 @@ import org.apache.freemarker.core.model.TemplateModel;
 /**
  * TemplateHashModel wrapper for a HttpSession attributes.
  */
-
+// TODO [FM3] Shouldn't this be a TemplateHashModelEx? The attribute names are known after all.
 public final class HttpSessionHashModel implements TemplateHashModel, Serializable {
     private static final long serialVersionUID = 1L;
     private transient HttpSession session;
@@ -106,10 +106,5 @@ public final class HttpSessionHashModel implements TemplateHashModel, Serializab
         return (session != null && session != currentSession) || 
             (session == null && request == null);
     }
-
-    @Override
-    public boolean isEmptyHash() throws TemplateException {
-        checkSessionExistence();
-        return session == null || !session.getAttributeNames().hasMoreElements();
-    }
+    
 }

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/ba722858/freemarker-servlet/src/main/java/org/apache/freemarker/servlet/ServletContextHashModel.java
----------------------------------------------------------------------
diff --git a/freemarker-servlet/src/main/java/org/apache/freemarker/servlet/ServletContextHashModel.java b/freemarker-servlet/src/main/java/org/apache/freemarker/servlet/ServletContextHashModel.java
index d66ea94..6a75777 100644
--- a/freemarker-servlet/src/main/java/org/apache/freemarker/servlet/ServletContextHashModel.java
+++ b/freemarker-servlet/src/main/java/org/apache/freemarker/servlet/ServletContextHashModel.java
@@ -30,6 +30,7 @@ import org.apache.freemarker.core.model.TemplateModel;
 /**
  * TemplateHashModel wrapper for a ServletContext attributes.
  */
+//TODO [FM3] Shouldn't this be a TemplateHashModelEx? The attribute names are known after all.
 public final class ServletContextHashModel implements TemplateHashModel {
     private final GenericServlet servlet;
     private final ServletContext servletctx;
@@ -47,11 +48,6 @@ public final class ServletContextHashModel implements TemplateHashModel {
         return wrapper.wrap(servletctx.getAttribute(key));
     }
 
-    @Override
-    public boolean isEmptyHash() {
-        return !servletctx.getAttributeNames().hasMoreElements();
-    }
-    
     /**
      * Returns the underlying servlet. Can return null if this object was
      * created using the deprecated constructor.

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/ba722858/freemarker-servlet/src/main/java/org/apache/freemarker/servlet/jsp/TaglibFactory.java
----------------------------------------------------------------------
diff --git a/freemarker-servlet/src/main/java/org/apache/freemarker/servlet/jsp/TaglibFactory.java b/freemarker-servlet/src/main/java/org/apache/freemarker/servlet/jsp/TaglibFactory.java
index 9d113bb..1845a39 100644
--- a/freemarker-servlet/src/main/java/org/apache/freemarker/servlet/jsp/TaglibFactory.java
+++ b/freemarker-servlet/src/main/java/org/apache/freemarker/servlet/jsp/TaglibFactory.java
@@ -353,14 +353,6 @@ public class TaglibFactory implements TemplateHashModel {
     }
 
     /**
-     * Returns false.
-     */
-    @Override
-    public boolean isEmptyHash() {
-        return false;
-    }
-
-    /**
      * See {@link Builder#getMetaInfTldSources()}
      */
     public ServletContext getServletContext() {
@@ -1490,6 +1482,7 @@ public class TaglibFactory implements TemplateHashModel {
 
     }
 
+    // TODO [FM3] Shouldn't this be a TemplateHashModelEx? The names are known after all.
     private static final class Taglib implements TemplateHashModel {
 
         private final Map<String, TemplateModel> tagsAndFunctions;
@@ -1503,11 +1496,6 @@ public class TaglibFactory implements TemplateHashModel {
             return tagsAndFunctions.get(key);
         }
 
-        @Override
-        public boolean isEmptyHash() {
-            return tagsAndFunctions.isEmpty();
-        }
-
         private static Map<String, TemplateModel> parseToTagsAndFunctions(
                 ServletContext ctx, TldLocation tldLocation, ObjectWrapper objectWrapper) throws IOException, SAXException {
             final TldParserForTaglibBuilding tldParser = new TldParserForTaglibBuilding(objectWrapper);

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/ba722858/freemarker-spring/src/main/java/org/apache/freemarker/spring/model/SpringTemplateCallableHashModel.java
----------------------------------------------------------------------
diff --git a/freemarker-spring/src/main/java/org/apache/freemarker/spring/model/SpringTemplateCallableHashModel.java b/freemarker-spring/src/main/java/org/apache/freemarker/spring/model/SpringTemplateCallableHashModel.java
index 40f7d59..eb17233 100644
--- a/freemarker-spring/src/main/java/org/apache/freemarker/spring/model/SpringTemplateCallableHashModel.java
+++ b/freemarker-spring/src/main/java/org/apache/freemarker/spring/model/SpringTemplateCallableHashModel.java
@@ -34,6 +34,7 @@ import org.apache.freemarker.core.model.TemplateStringModel;
 /**
  * TemplateHashModel wrapper for templates using Spring directives, functions and internal models.
  */
+//TODO [FM3] Shouldn't this be a TemplateHashModelEx?
 public final class SpringTemplateCallableHashModel implements TemplateHashModel, Serializable {
 
     private static final long serialVersionUID = 1L;
@@ -55,7 +56,7 @@ public final class SpringTemplateCallableHashModel implements TemplateHashModel,
      */
     private static final String EVALUATION_CONTEXT_MODEL = "evaluationContextModel";
 
-    private Map<String, TemplateModel> modelsMap = new HashMap<>();
+    private final Map<String, TemplateModel> modelsMap = new HashMap<>();
 
     public SpringTemplateCallableHashModel(final HttpServletRequest request, final HttpServletResponse response) {
         modelsMap.put(MessageFunction.NAME, new MessageFunction(request, response));
@@ -73,11 +74,6 @@ public final class SpringTemplateCallableHashModel implements TemplateHashModel,
         return modelsMap.get(key);
     }
 
-    @Override
-    public boolean isEmptyHash() throws TemplateException {
-        return false;
-    }
-
     TemplateStringModel getNestedPathModel() throws TemplateException {
         return (TemplateStringModel) get(NESTED_PATH_MODEL);
     }
@@ -87,7 +83,7 @@ public final class SpringTemplateCallableHashModel implements TemplateHashModel,
     }
 
     TemplateModel getEvaluationContextModel() throws TemplateException {
-        return (TemplateModel) get(EVALUATION_CONTEXT_MODEL);
+        return get(EVALUATION_CONTEXT_MODEL);
     }
 
     void setEvaluationContextModel(TemplateModel evaluationContextModel) {