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/03/01 10:07:24 UTC

[2/2] incubator-freemarker git commit: Removed other BeansWrapper related classes that DefaultObjectWrapper doesn't use. This includes ModelCache and related classes, because DefaultObjectWrapper has only used the cache for "generic" classes (because tha

Removed other BeansWrapper related classes that DefaultObjectWrapper doesn't use. This includes ModelCache and related classes, because DefaultObjectWrapper has only used the cache for "generic" classes (because that's where it has fallen back to BeansWrapper.wrap), which is inconsistent and doesn't worth the caching overhead and complexity.


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

Branch: refs/heads/3
Commit: 780b76a6e9213d7632446933de1445450d89cada
Parents: 9a0a2d0
Author: ddekany <dd...@apache.org>
Authored: Wed Mar 1 08:39:26 2017 +0100
Committer: ddekany <dd...@apache.org>
Committed: Wed Mar 1 11:07:09 2017 +0100

----------------------------------------------------------------------
 .../freemarker/core/BuiltInsForSequences.java   |  12 +-
 .../freemarker/core/model/impl/ArrayModel.java  | 126 ----------------
 .../core/model/impl/BeanAndStringModel.java     |  53 +++++++
 .../freemarker/core/model/impl/BeanModel.java   |  13 +-
 .../core/model/impl/BeansModelCache.java        |  73 ----------
 .../core/model/impl/BooleanModel.java           |  40 ------
 .../core/model/impl/ClassIntrospector.java      |  12 --
 .../core/model/impl/CollectionModel.java        | 109 --------------
 .../freemarker/core/model/impl/DateModel.java   |  76 ----------
 .../core/model/impl/DefaultObjectWrapper.java   | 136 ++++--------------
 .../freemarker/core/model/impl/EnumModels.java  |  50 +++++++
 .../core/model/impl/EnumerationModel.java       | 108 --------------
 .../freemarker/core/model/impl/HashAdapter.java |   3 +-
 .../core/model/impl/IteratorModel.java          | 112 ---------------
 .../freemarker/core/model/impl/MapModel.java    | 120 ----------------
 .../freemarker/core/model/impl/ModelCache.java  | 143 -------------------
 .../core/model/impl/ModelFactory.java           |  34 -----
 .../freemarker/core/model/impl/NumberModel.java |  60 --------
 .../core/model/impl/OverloadedMethodsModel.java |  11 +-
 .../core/model/impl/ResourceBundleModel.java    |  11 +-
 .../core/model/impl/SimpleMapModel.java         | 129 -----------------
 .../core/model/impl/SimpleObjectWrapper.java    |  16 +--
 .../freemarker/core/model/impl/StringModel.java |  63 --------
 .../freemarker/core/model/impl/_EnumModels.java |  54 -------
 .../apache/freemarker/core/util/FTLUtil.java    |  23 +--
 .../apache/freemarker/core/util/_ClassUtil.java |   6 +-
 src/manual/en_US/FM3-CHANGE-LOG.txt             |  16 ++-
 .../core/SimpleObjectWrapperTest.java           |   3 +-
 ...jectWrapperModelFactoryRegistrationTest.java |  10 +-
 .../model/impl/DefaultObjectWrapperTest.java    |   6 +-
 .../core/model/impl/ModelCacheTest.java         |   2 +
 .../test/templatesuite/TemplateTestCase.java    |   2 -
 .../expected/boolean-formatting.txt             |   1 -
 .../templates/boolean-formatting.ftl            |   1 -
 34 files changed, 172 insertions(+), 1462 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/780b76a6/src/main/java/org/apache/freemarker/core/BuiltInsForSequences.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/freemarker/core/BuiltInsForSequences.java b/src/main/java/org/apache/freemarker/core/BuiltInsForSequences.java
index c54328c..0265919 100644
--- a/src/main/java/org/apache/freemarker/core/BuiltInsForSequences.java
+++ b/src/main/java/org/apache/freemarker/core/BuiltInsForSequences.java
@@ -44,7 +44,6 @@ import org.apache.freemarker.core.model.impl.CollectionAndSequence;
 import org.apache.freemarker.core.model.impl.SimpleNumber;
 import org.apache.freemarker.core.model.impl.SimpleScalar;
 import org.apache.freemarker.core.model.impl.TemplateModelListSequence;
-import org.apache.freemarker.core.model.impl.CollectionModel;
 import org.apache.freemarker.core.util.BugException;
 import org.apache.freemarker.core.util._StringUtil;
 
@@ -153,7 +152,7 @@ class BuiltInsForSequences {
             TemplateModel model = target.eval(env);
             // In 2.3.x only, we prefer TemplateSequenceModel for
             // backward compatibility. In 2.4.x, we prefer TemplateCollectionModel. 
-            if (model instanceof TemplateSequenceModel && !isBuggySeqButGoodCollection(model)) {
+            if (model instanceof TemplateSequenceModel) {
                 return calculateResultForSequence((TemplateSequenceModel) model);
             } else if (model instanceof TemplateCollectionModel) {
                 return calculateResultForColletion((TemplateCollectionModel) model);
@@ -352,7 +351,7 @@ class BuiltInsForSequences {
             TemplateModel model = target.eval(env);
             // In 2.3.x only, we prefer TemplateSequenceModel for
             // backward compatibility. In 2.4.x, we prefer TemplateCollectionModel. 
-            if (model instanceof TemplateSequenceModel && !isBuggySeqButGoodCollection(model)) {
+            if (model instanceof TemplateSequenceModel) {
                 return new BIMethodForSequence((TemplateSequenceModel) model, env);
             } else if (model instanceof TemplateCollectionModel) {
                 return new BIMethodForCollection((TemplateCollectionModel) model, env);
@@ -375,9 +374,9 @@ class BuiltInsForSequences {
                     throws TemplateException {
                 TemplateModel model = target.eval(env);
                 m_seq = model instanceof TemplateSequenceModel
-                            && !isBuggySeqButGoodCollection(model)
                         ? (TemplateSequenceModel) model
                         : null;
+                // [FM3] Rework the below
                 // In 2.3.x only, we deny the possibility of collection
                 // access if there's sequence access. This is so to minimize
                 // the change of compatibility issues; without this, objects
@@ -847,11 +846,6 @@ class BuiltInsForSequences {
         
     }
 
-    private static boolean isBuggySeqButGoodCollection(
-            TemplateModel model) {
-        return model instanceof CollectionModel && !((CollectionModel) model).getSupportsIndexedAccess();
-    }
-    
     private static boolean modelsEqual(
             int seqItemIndex, TemplateModel seqItem, TemplateModel searchedItem,
             Environment env)

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/780b76a6/src/main/java/org/apache/freemarker/core/model/impl/ArrayModel.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/freemarker/core/model/impl/ArrayModel.java b/src/main/java/org/apache/freemarker/core/model/impl/ArrayModel.java
deleted file mode 100644
index 12ef9f0..0000000
--- a/src/main/java/org/apache/freemarker/core/model/impl/ArrayModel.java
+++ /dev/null
@@ -1,126 +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.impl;
-
-import java.lang.reflect.Array;
-
-import org.apache.freemarker.core.model.ObjectWrapper;
-import org.apache.freemarker.core.model.TemplateCollectionModel;
-import org.apache.freemarker.core.model.TemplateModel;
-import org.apache.freemarker.core.model.TemplateModelException;
-import org.apache.freemarker.core.model.TemplateModelIterator;
-import org.apache.freemarker.core.model.TemplateSequenceModel;
-
-/**
- * <p>A class that will wrap an arbitrary array into {@link TemplateCollectionModel}
- * and {@link TemplateSequenceModel} interfaces. It supports element retrieval through the <tt>array[index]</tt>
- * syntax and can be iterated as a list.
- */
-public class ArrayModel
-extends
-    BeanModel
-implements
-    TemplateCollectionModel,
-    TemplateSequenceModel {
-    static final ModelFactory FACTORY =
-        new ModelFactory()
-        {
-            @Override
-            public TemplateModel create(Object object, ObjectWrapper wrapper) {
-                return new ArrayModel(object, (DefaultObjectWrapper) wrapper);
-            }
-        };
-        
-    // Cached length of the array
-    private final int length;
-
-    /**
-     * Creates a new model that wraps the specified array object.
-     * @param array the array object to wrap into a model.
-     * @param wrapper the {@link DefaultObjectWrapper} associated with this model.
-     * Every model has to have an associated {@link DefaultObjectWrapper} instance. The
-     * model gains many attributes from its wrapper, including the caching 
-     * behavior, method exposure level, method-over-item shadowing policy etc.
-     * @throws IllegalArgumentException if the passed object is not a Java array.
-     */
-    public ArrayModel(Object array, DefaultObjectWrapper wrapper) {
-        super(array, wrapper);
-        Class clazz = array.getClass();
-        if (!clazz.isArray())
-            throw new IllegalArgumentException("Object is not an array, it's " + array.getClass().getName());
-        length = Array.getLength(array);
-    }
-
-
-    @Override
-    public TemplateModelIterator iterator() {
-        return new Iterator();
-    }
-
-    @Override
-    public TemplateModel get(int index)
-    throws TemplateModelException {
-        try {
-            return wrap(Array.get(object, index));
-        } catch (IndexOutOfBoundsException e) {
-            return null; 
-//            throw new TemplateModelException("Index out of bounds: " + index);
-        }
-    }
-
-    private class Iterator
-    implements 
-        TemplateSequenceModel,
-        TemplateModelIterator {
-        private int position = 0;
-
-        @Override
-        public boolean hasNext() {
-            return position < length;
-        }
-
-        @Override
-        public TemplateModel get(int index)
-        throws TemplateModelException {
-            return ArrayModel.this.get(index);
-        }
-
-        @Override
-        public TemplateModel next()
-        throws TemplateModelException {
-            return position < length ? get(position++) : null;
-        }
-
-        @Override
-        public int size() {
-            return ArrayModel.this.size();
-        }
-    }
-
-    @Override
-    public int size() {
-        return length;
-    }
-
-    @Override
-    public boolean isEmpty() {
-        return length == 0;
-    }
-}

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/780b76a6/src/main/java/org/apache/freemarker/core/model/impl/BeanAndStringModel.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/freemarker/core/model/impl/BeanAndStringModel.java b/src/main/java/org/apache/freemarker/core/model/impl/BeanAndStringModel.java
new file mode 100644
index 0000000..d0ed56d
--- /dev/null
+++ b/src/main/java/org/apache/freemarker/core/model/impl/BeanAndStringModel.java
@@ -0,0 +1,53 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.freemarker.core.model.impl;
+
+import org.apache.freemarker.core.model.TemplateScalarModel;
+
+/**
+ * Subclass of {@link BeanModel} that exposes the return value of the {@link
+ * java.lang.Object#toString()} method through the {@link TemplateScalarModel}
+ * interface.
+ */
+// [FM3] Treating all beans as FTL strings was certainly a bad idea in FM2.
+public class BeanAndStringModel extends BeanModel implements TemplateScalarModel {
+
+    /**
+     * Creates a new model that wraps the specified object with BeanModel + scalar
+     * functionality.
+     * @param object the object to wrap into a model.
+     * @param wrapper the {@link DefaultObjectWrapper} associated with this model.
+     * Every model has to have an associated {@link DefaultObjectWrapper} instance. The
+     * model gains many attributes from its wrapper, including the caching 
+     * behavior, method exposure level, method-over-item shadowing policy etc.
+     */
+    public BeanAndStringModel(Object object, DefaultObjectWrapper wrapper) {
+        super(object, wrapper);
+    }
+
+    /**
+     * Returns the result of calling {@link Object#toString()} on the wrapped
+     * object.
+     */
+    @Override
+    public String getAsString() {
+        return object.toString();
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/780b76a6/src/main/java/org/apache/freemarker/core/model/impl/BeanModel.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/freemarker/core/model/impl/BeanModel.java b/src/main/java/org/apache/freemarker/core/model/impl/BeanModel.java
index 9c483d6..b0201b6 100644
--- a/src/main/java/org/apache/freemarker/core/model/impl/BeanModel.java
+++ b/src/main/java/org/apache/freemarker/core/model/impl/BeanModel.java
@@ -37,7 +37,6 @@ import org.apache.freemarker.core._DelayedFTLTypeDescription;
 import org.apache.freemarker.core._DelayedJQuote;
 import org.apache.freemarker.core._TemplateModelException;
 import org.apache.freemarker.core.model.AdapterTemplateModel;
-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;
@@ -60,8 +59,7 @@ import org.slf4j.Logger;
  */
 
 public class BeanModel
-implements
-    TemplateHashModelEx, AdapterTemplateModel, WrapperTemplateModel, TemplateModelWithAPISupport {
+        implements TemplateHashModelEx, AdapterTemplateModel, WrapperTemplateModel, TemplateModelWithAPISupport {
     
     private static final Logger LOG = _CoreLogs.OBJECT_WRAPPER;
     
@@ -70,15 +68,6 @@ implements
     
     // We use this to represent an unknown value as opposed to known value of null (JR)
     static final TemplateModel UNKNOWN = new SimpleScalar("UNKNOWN");
-    
-    static final ModelFactory FACTORY =
-        new ModelFactory()
-        {
-            @Override
-            public TemplateModel create(Object object, ObjectWrapper wrapper) {
-                return new BeanModel(object, (DefaultObjectWrapper) wrapper);
-            }
-        };
 
     // I've tried to use a volatile ConcurrentHashMap field instead of HashMap + synchronized(this), but oddly it was
     // a bit slower, at least on Java 8 u66. 

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/780b76a6/src/main/java/org/apache/freemarker/core/model/impl/BeansModelCache.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/freemarker/core/model/impl/BeansModelCache.java b/src/main/java/org/apache/freemarker/core/model/impl/BeansModelCache.java
deleted file mode 100644
index 7808d48..0000000
--- a/src/main/java/org/apache/freemarker/core/model/impl/BeansModelCache.java
+++ /dev/null
@@ -1,73 +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.impl;
-
-import java.util.HashSet;
-import java.util.Map;
-import java.util.Set;
-import java.util.concurrent.ConcurrentHashMap;
-
-import org.apache.freemarker.core.model.TemplateModel;
-
-import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
-
-public class BeansModelCache extends ModelCache {
-    private final Map classToFactory = new ConcurrentHashMap();
-    private final Set mappedClassNames = new HashSet();
-
-    private final DefaultObjectWrapper wrapper;
-    
-    BeansModelCache(DefaultObjectWrapper wrapper) {
-        this.wrapper = wrapper;
-    }
-    
-    @Override
-    protected boolean isCacheable(Object object) {
-        return object.getClass() != Boolean.class; 
-    }
-    
-    @Override
-    @SuppressFBWarnings(value="JLM_JSR166_UTILCONCURRENT_MONITORENTER", justification="Locks for factory creation only")
-    protected TemplateModel create(Object object) {
-        Class clazz = object.getClass();
-        
-        ModelFactory factory = (ModelFactory) classToFactory.get(clazz);
-        
-        if (factory == null) {
-            // Synchronized so that we won't unnecessarily create the same factory for multiple times in parallel.
-            synchronized (classToFactory) {
-                factory = (ModelFactory) classToFactory.get(clazz);
-                if (factory == null) {
-                    String className = clazz.getName();
-                    // clear mappings when class reloading is detected
-                    if (!mappedClassNames.add(className)) {
-                        classToFactory.clear();
-                        mappedClassNames.clear();
-                        mappedClassNames.add(className);
-                    }
-                    factory = wrapper.getModelFactory(clazz);
-                    classToFactory.put(clazz, factory);
-                }
-            }
-        }
-        
-        return factory.create(object, wrapper);
-    }
-}

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/780b76a6/src/main/java/org/apache/freemarker/core/model/impl/BooleanModel.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/freemarker/core/model/impl/BooleanModel.java b/src/main/java/org/apache/freemarker/core/model/impl/BooleanModel.java
deleted file mode 100644
index 9db421f..0000000
--- a/src/main/java/org/apache/freemarker/core/model/impl/BooleanModel.java
+++ /dev/null
@@ -1,40 +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.impl;
-
-import org.apache.freemarker.core.model.TemplateBooleanModel;
-
-/**
- * <p>A class that will wrap instances of {@link java.lang.Boolean} into a
- * {@link TemplateBooleanModel}.
- */
-public class BooleanModel extends BeanModel implements TemplateBooleanModel {
-    private final boolean value;
-    
-    public BooleanModel(Boolean bool, DefaultObjectWrapper wrapper) {
-        super(bool, wrapper, false);
-        value = bool.booleanValue();
-    }
-
-    @Override
-    public boolean getAsBoolean() {
-        return value;
-    }
-}

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/780b76a6/src/main/java/org/apache/freemarker/core/model/impl/ClassIntrospector.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/freemarker/core/model/impl/ClassIntrospector.java b/src/main/java/org/apache/freemarker/core/model/impl/ClassIntrospector.java
index 4dd8931..360d253 100644
--- a/src/main/java/org/apache/freemarker/core/model/impl/ClassIntrospector.java
+++ b/src/main/java/org/apache/freemarker/core/model/impl/ClassIntrospector.java
@@ -602,8 +602,6 @@ class ClassIntrospector {
                 if (regedMf != null) {
                     if (regedMf instanceof ClassBasedModelFactory) {
                         ((ClassBasedModelFactory) regedMf).clearCache();
-                    } else if (regedMf instanceof ModelCache) {
-                        ((ModelCache) regedMf).clearCache();
                     } else {
                         throw new BugException();
                     }
@@ -630,8 +628,6 @@ class ClassIntrospector {
                 if (regedMf != null) {
                     if (regedMf instanceof ClassBasedModelFactory) {
                         ((ClassBasedModelFactory) regedMf).removeFromCache(clazz);
-                    } else if (regedMf instanceof ModelCache) {
-                        ((ModelCache) regedMf).clearCache(); // doesn't support selective clearing ATM
                     } else {
                         throw new BugException();
                     }
@@ -670,10 +666,6 @@ class ClassIntrospector {
         registerModelFactory((Object) mf);
     }
 
-    void registerModelFactory(ModelCache mf) {
-        registerModelFactory((Object) mf);
-    }
-
     private void registerModelFactory(Object mf) {
         // Note that this `synchronized (sharedLock)` is also need for the DefaultObjectWrapper constructor to work safely.
         synchronized (sharedLock) {
@@ -686,10 +678,6 @@ class ClassIntrospector {
         unregisterModelFactory((Object) mf);
     }
 
-    void unregisterModelFactory(ModelCache mf) {
-        unregisterModelFactory((Object) mf);
-    }
-
     void unregisterModelFactory(Object mf) {
         synchronized (sharedLock) {
             for (Iterator<WeakReference<Object>> it = modelFactories.iterator(); it.hasNext(); ) {

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/780b76a6/src/main/java/org/apache/freemarker/core/model/impl/CollectionModel.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/freemarker/core/model/impl/CollectionModel.java b/src/main/java/org/apache/freemarker/core/model/impl/CollectionModel.java
deleted file mode 100644
index 36c6947..0000000
--- a/src/main/java/org/apache/freemarker/core/model/impl/CollectionModel.java
+++ /dev/null
@@ -1,109 +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.impl;
-
-import java.util.Collection;
-import java.util.List;
-
-import org.apache.freemarker.core.model.ObjectWrapper;
-import org.apache.freemarker.core.model.TemplateCollectionModel;
-import org.apache.freemarker.core.model.TemplateModel;
-import org.apache.freemarker.core.model.TemplateModelException;
-import org.apache.freemarker.core.model.TemplateModelIterator;
-import org.apache.freemarker.core.model.TemplateSequenceModel;
-
-/**
- * <p>A special case of {@link BeanModel} that can wrap Java collections
- * and that implements the {@link TemplateCollectionModel} in order to be usable 
- * in a <tt>&lt;#list&gt;</tt> block.</p>
- */
-public class CollectionModel
-extends
-    StringModel
-implements
-    TemplateCollectionModel,
-    TemplateSequenceModel {
-    static final ModelFactory FACTORY =
-        new ModelFactory()
-        {
-            @Override
-            public TemplateModel create(Object object, ObjectWrapper wrapper) {
-                return new CollectionModel((Collection) object, (DefaultObjectWrapper) wrapper);
-            }
-        };
-
-
-    /**
-     * Creates a new model that wraps the specified collection object.
-     * @param collection the collection object to wrap into a model.
-     * @param wrapper the {@link DefaultObjectWrapper} associated with this model.
-     * Every model has to have an associated {@link DefaultObjectWrapper} instance. The
-     * model gains many attributes from its wrapper, including the caching 
-     * behavior, method exposure level, method-over-item shadowing policy etc.
-     */
-    public CollectionModel(Collection collection, DefaultObjectWrapper wrapper) {
-        super(collection, wrapper);
-    }
-
-    /**
-     * Retrieves the i-th object from the collection, wrapped as a TemplateModel.
-     * @throws TemplateModelException if the index is out of bounds, or the
-     * underlying collection is not a List.
-     */
-    @Override
-    public TemplateModel get(int index)
-    throws TemplateModelException {
-        // Don't forget to keep getSupportsIndexedAccess in sync with this!
-        if (object instanceof List) {
-            try {
-                return wrap(((List) object).get(index));
-            } catch (IndexOutOfBoundsException e) {
-                return null;
-//                throw new TemplateModelException("Index out of bounds: " + index);
-            }
-        } else {
-            throw new TemplateModelException("Underlying collection is not a list, it's " + object.getClass().getName());
-        }
-    }
-    
-    /**
-     * Tells if {@link #get(int)} will always fail for this object.
-     * As this object implements {@link TemplateSequenceModel},
-     * {@link #get(int)} should always work, but due to a design flaw, for
-     * non-{@link List} wrapped objects {@link #get(int)} will always fail.
-     * This method exists to ease working this problem around.
-     * 
-     * @since 2.3.17 
-     */
-    public boolean getSupportsIndexedAccess() {
-        return object instanceof List;
-    }
-    
-    @Override
-    public TemplateModelIterator iterator() {
-        return new IteratorModel(((Collection) object).iterator(), wrapper);
-    }
-
-    @Override
-    public int size() {
-        return ((Collection) object).size();
-    }
-    
-}

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/780b76a6/src/main/java/org/apache/freemarker/core/model/impl/DateModel.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/freemarker/core/model/impl/DateModel.java b/src/main/java/org/apache/freemarker/core/model/impl/DateModel.java
deleted file mode 100644
index e16e375..0000000
--- a/src/main/java/org/apache/freemarker/core/model/impl/DateModel.java
+++ /dev/null
@@ -1,76 +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.impl;
-
-import java.util.Date;
-
-import org.apache.freemarker.core.model.ObjectWrapper;
-import org.apache.freemarker.core.model.TemplateDateModel;
-import org.apache.freemarker.core.model.TemplateModel;
-
-/**
- * Wraps arbitrary subclass of {@link java.util.Date} into a reflective model.
- * Beside acting as a {@link TemplateDateModel}, you can call all Java methods
- * on these objects as well.
- */
-public class DateModel extends BeanModel implements
-    TemplateDateModel {
-    static final ModelFactory FACTORY =
-        new ModelFactory()
-        {
-            @Override
-            public TemplateModel create(Object object, ObjectWrapper wrapper) {
-                return new DateModel((Date) object, (DefaultObjectWrapper) wrapper);
-            }
-        };
-
-    private final int type;
-    
-    /**
-     * Creates a new model that wraps the specified date object.
-     * @param date the date object to wrap into a model.
-     * @param wrapper the {@link DefaultObjectWrapper} associated with this model.
-     * Every model has to have an associated {@link DefaultObjectWrapper} instance. The
-     * model gains many attributes from its wrapper, including the caching 
-     * behavior, method exposure level, method-over-item shadowing policy etc.
-     */
-    public DateModel(Date date, DefaultObjectWrapper wrapper) {
-        super(date, wrapper);
-        if (date instanceof java.sql.Date) {
-            type = DATE;
-        } else if (date instanceof java.sql.Time) {
-            type = TIME;
-        } else if (date instanceof java.sql.Timestamp) {
-            type = DATETIME;
-        } else {
-            type = wrapper.getDefaultDateType();
-        }
-    }
-
-    @Override
-    public Date getAsDate() {
-        return (Date) object;
-    }
-
-    @Override
-    public int getDateType() {
-        return type;
-    }
-}

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/780b76a6/src/main/java/org/apache/freemarker/core/model/impl/DefaultObjectWrapper.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/freemarker/core/model/impl/DefaultObjectWrapper.java b/src/main/java/org/apache/freemarker/core/model/impl/DefaultObjectWrapper.java
index ebc5a3d..66aa016 100644
--- a/src/main/java/org/apache/freemarker/core/model/impl/DefaultObjectWrapper.java
+++ b/src/main/java/org/apache/freemarker/core/model/impl/DefaultObjectWrapper.java
@@ -150,22 +150,13 @@ public class DefaultObjectWrapper implements RichObjectWrapper, WriteProtectable
     private final StaticModels staticModels;
 
     /**
-     * {@link String} class name to {@link EnumerationModel} cache.
+     * {@link String} class name to an enum value hash.
      * This object only belongs to a single {@link DefaultObjectWrapper}.
      * This has to be final as {@link #getStaticModels()} might returns it any time and then it has to remain a good
      * reference.
      */
     private final ClassBasedModelFactory enumModels;
 
-    /**
-     * Object to wrapped object cache; not used by default.
-     * This object only belongs to a single {@link DefaultObjectWrapper}.
-     */
-    private final ModelCache modelCache;
-
-    private final BooleanModel falseModel;
-    private final BooleanModel trueModel;
-
     // -----------------------------------------------------------------------------------------------------------------
 
     // Why volatile: In principle it need not be volatile, but we want to catch modification attempts even if the
@@ -177,6 +168,8 @@ public class DefaultObjectWrapper implements RichObjectWrapper, WriteProtectable
     private ObjectWrapper outerIdentity = this;
     private boolean methodsShadowItems = true;
     private boolean strict;  // initialized by PropertyAssignments.apply
+    @Deprecated // Only exists to keep some JUnit tests working...
+    private boolean useModelCache;
 
     private final Version incompatibleImprovements;
 
@@ -278,12 +271,8 @@ public class DefaultObjectWrapper implements RichObjectWrapper, WriteProtectable
             sharedIntrospectionLock = classIntrospector.getSharedLock();
         }
 
-        falseModel = new BooleanModel(Boolean.FALSE, this);
-        trueModel = new BooleanModel(Boolean.TRUE, this);
-
         staticModels = new StaticModels(this);
-        enumModels = new _EnumModels(this);
-        modelCache = new BeansModelCache(this);
+        enumModels = new EnumModels(this);
         setUseModelCache(bwConf.getUseModelCache());
 
         finalizeConstruction(writeProtected);
@@ -553,16 +542,6 @@ public class DefaultObjectWrapper implements RichObjectWrapper, WriteProtectable
                     oldCI.unregisterModelFactory(enumModels);
                     enumModels.clearCache();
                 }
-                if (modelCache != null) {
-                    oldCI.unregisterModelFactory(modelCache);
-                    modelCache.clearCache();
-                }
-                if (trueModel != null) {
-                    trueModel.clearMemberCache();
-                }
-                if (falseModel != null) {
-                    falseModel.clearMemberCache();
-                }
             }
 
             classIntrospector = newCI;
@@ -578,9 +557,6 @@ public class DefaultObjectWrapper implements RichObjectWrapper, WriteProtectable
         if (enumModels != null) {
             classIntrospector.registerModelFactory(enumModels);
         }
-        if (modelCache != null) {
-            classIntrospector.registerModelFactory(modelCache);
-        }
     }
 
     /**
@@ -633,22 +609,22 @@ public class DefaultObjectWrapper implements RichObjectWrapper, WriteProtectable
     }
 
     /**
-     * Sets whether this wrapper caches the {@link TemplateModel}-s created for the Java objects that has wrapped with
-     * this object wrapper. Default is {@code false}.
-     * When set to {@code true}, calling {@link #wrap(Object)} multiple times for
-     * the same object will likely return the same model (although there is
-     * no guarantee as the cache items can be cleared any time).
+     * @deprecated Does nothing in FreeMarker 3 - we kept it for now to postopne reworking some JUnit tests.
      */
+    // [FM3] Remove
+    @Deprecated
     public void setUseModelCache(boolean useCache) {
         checkModifiable();
-        modelCache.setUseCache(useCache);
+        useModelCache = useCache;
     }
 
     /**
-     * @since 2.3.21
+     * @deprecated Does nothing in FreeMarker 3 - we kept it for now to postopne reworking some JUnit tests.
      */
+    // [FM3] Remove
+    @Deprecated
     public boolean getUseModelCache() {
-        return modelCache.getUseCache();
+        return useModelCache;
     }
 
     /**
@@ -666,7 +642,7 @@ public class DefaultObjectWrapper implements RichObjectWrapper, WriteProtectable
      * and dates will be wrapped into the corresponding {@code SimpleXxx} classes (like {@link SimpleNumber}).
      * {@link Map}-s, {@link List}-s, other {@link Collection}-s, arrays and {@link Iterator}-s will be wrapped into the
      * corresponding {@code DefaultXxxAdapter} classes ({@link DefaultMapAdapter}), depending on). After that, the
-     * wrapping is handled by {@link #handleUnknownType(Object)}, so see more there.
+     * wrapping is handled by {@link #handleNonBasicTypes(Object)}, so see more there.
      */
     @Override
     public TemplateModel wrap(Object obj) throws TemplateModelException {
@@ -723,29 +699,30 @@ public class DefaultObjectWrapper implements RichObjectWrapper, WriteProtectable
             return DefaultEnumerationAdapter.adapt((Enumeration<?>) obj, this);
         }
 
-        // [FM3] Via plugin mechanism, not by default anymore
-        if (obj instanceof Node) {
-            return handW3CNode((Node) obj);
-        }
-
-        return handleUnknownType(obj);
-    }
-
-    protected TemplateModel handW3CNode(Node node) throws TemplateModelException {
-        return NodeModel.wrap(node);
+        return handleNonBasicTypes(obj);
     }
 
     /**
-     * Called for an object that isn't considered to be of a "basic" Java type, like for an application specific type.
-     * In its default implementation, the object will be wrapped as a generic JavaBean.
+     * Called for an object that isn't considered to be of a "basic" Java type, like for all application specific types,
+     * but currently also for {@link Node}-s and {@link ResourceBundle}-s.
      *
      * <p>
      * When you override this method, you should first decide if you want to wrap the object in a custom way (and if so
      * then do it and return with the result), and if not, then you should call the super method (assuming the default
      * behavior is fine with you).
      */
-    protected TemplateModel handleUnknownType(Object obj) throws TemplateModelException {
-        return modelCache.getInstance(obj);
+    // [FM3] This is an awkward temporary solution, rework it.
+    protected TemplateModel handleNonBasicTypes(Object obj) throws TemplateModelException {
+        // [FM3] Via plugin mechanism, not by default anymore
+        if (obj instanceof Node) {
+            return NodeModel.wrap((Node) obj);
+        }
+
+        if (obj instanceof ResourceBundle) {
+            return new ResourceBundleModel((ResourceBundle) obj, this);
+        }
+
+        return new BeanAndStringModel(obj, this);
     }
 
     /**
@@ -774,58 +751,6 @@ public class DefaultObjectWrapper implements RichObjectWrapper, WriteProtectable
         return new APIModel(obj, this);
     }
 
-    private final ModelFactory BOOLEAN_FACTORY = new ModelFactory() {
-        @Override
-        public TemplateModel create(Object object, ObjectWrapper wrapper) {
-            return ((Boolean) object).booleanValue() ? trueModel : falseModel;
-        }
-    };
-
-    private static final ModelFactory ITERATOR_FACTORY = new ModelFactory() {
-        @Override
-        public TemplateModel create(Object object, ObjectWrapper wrapper) {
-            return new IteratorModel((Iterator<?>) object, (DefaultObjectWrapper) wrapper);
-        }
-    };
-
-    private static final ModelFactory ENUMERATION_FACTORY = new ModelFactory() {
-        @Override
-        public TemplateModel create(Object object, ObjectWrapper wrapper) {
-            return new EnumerationModel((Enumeration<?>) object, (DefaultObjectWrapper) wrapper);
-        }
-    };
-
-    protected ModelFactory getModelFactory(Class<?> clazz) {
-        if (Map.class.isAssignableFrom(clazz)) {
-            return SimpleMapModel.FACTORY;
-        }
-        if (Collection.class.isAssignableFrom(clazz)) {
-            return CollectionModel.FACTORY;
-        }
-        if (Number.class.isAssignableFrom(clazz)) {
-            return NumberModel.FACTORY;
-        }
-        if (Date.class.isAssignableFrom(clazz)) {
-            return DateModel.FACTORY;
-        }
-        if (Boolean.class == clazz) { // Boolean is final
-            return BOOLEAN_FACTORY;
-        }
-        if (ResourceBundle.class.isAssignableFrom(clazz)) {
-            return ResourceBundleModel.FACTORY;
-        }
-        if (Iterator.class.isAssignableFrom(clazz)) {
-            return ITERATOR_FACTORY;
-        }
-        if (Enumeration.class.isAssignableFrom(clazz)) {
-            return ENUMERATION_FACTORY;
-        }
-        if (clazz.isArray()) {
-            return ArrayModel.FACTORY;
-        }
-        return StringModel.FACTORY;
-    }
-
     /**
      * Attempts to unwrap a model into underlying object. Generally, this
      * method is the inverse of the {@link #wrap(Object)} method. In addition
@@ -1362,11 +1287,6 @@ public class DefaultObjectWrapper implements RichObjectWrapper, WriteProtectable
         return enumModels;
     }
 
-    /** For Unit tests only */
-    ModelCache getModelCache() {
-        return modelCache;
-    }
-
     /**
      * Creates a new instance of the specified class using the method call logic of this object wrapper for calling the
      * constructor. Overloaded constructors and varargs are supported. Only public constructors will be called.

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/780b76a6/src/main/java/org/apache/freemarker/core/model/impl/EnumModels.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/freemarker/core/model/impl/EnumModels.java b/src/main/java/org/apache/freemarker/core/model/impl/EnumModels.java
new file mode 100644
index 0000000..cc660b4
--- /dev/null
+++ b/src/main/java/org/apache/freemarker/core/model/impl/EnumModels.java
@@ -0,0 +1,50 @@
+/*
+ * 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.impl;
+
+import java.util.LinkedHashMap;
+import java.util.Map;
+
+import org.apache.freemarker.core.model.TemplateModel;
+
+class EnumModels extends ClassBasedModelFactory {
+
+    public EnumModels(DefaultObjectWrapper wrapper) {
+        super(wrapper);
+    }
+    
+    @Override
+    protected TemplateModel createModel(Class clazz) {
+        Object[] obj = clazz.getEnumConstants();
+        if (obj == null) {
+            // Return null - it'll manifest itself as undefined in the template.
+            // We're doing this rather than throw an exception as this way 
+            // people can use someEnumModel?default({}) to gracefully fall back 
+            // to an empty hash if they want to.
+            return null;
+        }
+        Map map = new LinkedHashMap();
+        for (Object anObj : obj) {
+            Enum value = (Enum) anObj;
+            map.put(value.name(), value);
+        }
+        return new SimpleHash(map, getWrapper());
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/780b76a6/src/main/java/org/apache/freemarker/core/model/impl/EnumerationModel.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/freemarker/core/model/impl/EnumerationModel.java b/src/main/java/org/apache/freemarker/core/model/impl/EnumerationModel.java
deleted file mode 100644
index d7b14cc..0000000
--- a/src/main/java/org/apache/freemarker/core/model/impl/EnumerationModel.java
+++ /dev/null
@@ -1,108 +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.impl;
-
-import java.util.Enumeration;
-import java.util.NoSuchElementException;
-
-import org.apache.freemarker.core.model.TemplateCollectionModel;
-import org.apache.freemarker.core.model.TemplateModel;
-import org.apache.freemarker.core.model.TemplateModelException;
-import org.apache.freemarker.core.model.TemplateModelIterator;
-
-/**
- * <p>A class that adds {@link TemplateModelIterator} functionality to the
- * {@link Enumeration} interface implementers. 
- * </p> <p>Using the model as a collection model is NOT thread-safe, as 
- * enumerations are inherently not thread-safe.
- * Further, you can iterate over it only once. Attempts to call the
- * {@link #iterator()} method after it was already driven to the end once will 
- * throw an exception.</p>
- */
-
-public class EnumerationModel
-extends
-    BeanModel
-implements
-    TemplateModelIterator,
-    TemplateCollectionModel {
-    private boolean accessed = false;
-    
-    /**
-     * Creates a new model that wraps the specified enumeration object.
-     * @param enumeration the enumeration object to wrap into a model.
-     * @param wrapper the {@link DefaultObjectWrapper} associated with this model.
-     * Every model has to have an associated {@link DefaultObjectWrapper} instance. The
-     * model gains many attributes from its wrapper, including the caching 
-     * behavior, method exposure level, method-over-item shadowing policy etc.
-     */
-    public EnumerationModel(Enumeration enumeration, DefaultObjectWrapper wrapper) {
-        super(enumeration, wrapper);
-    }
-
-    /**
-     * This allows the enumeration to be used in a <tt>&lt;#list&gt;</tt> block.
-     * @return "this"
-     */
-    @Override
-    public TemplateModelIterator iterator() throws TemplateModelException {
-        synchronized (this) {
-            if (accessed) {
-                throw new TemplateModelException(
-                    "This collection is stateful and can not be iterated over the" +
-                    " second time.");
-            }
-            accessed = true;
-        }
-        return this;
-    }
-    
-    /**
-     * Calls underlying {@link Enumeration#nextElement()}.
-     */
-    @Override
-    public boolean hasNext() {
-        return ((Enumeration) object).hasMoreElements();
-    }
-
-
-    /**
-     * Calls underlying {@link Enumeration#nextElement()} and wraps the result.
-     */
-    @Override
-    public TemplateModel next()
-    throws TemplateModelException {
-        try {
-            return wrap(((Enumeration) object).nextElement());
-        } catch (NoSuchElementException e) {
-            throw new TemplateModelException(
-                "No more elements in the enumeration.");
-        }
-    }
-
-    /**
-     * Returns {@link Enumeration#hasMoreElements()}. Therefore, an
-     * enumeration that has no more element evaluates to false, and an 
-     * enumeration that has further elements evaluates to true.
-     */
-    public boolean getAsBoolean() {
-        return hasNext();
-    }
-}

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/780b76a6/src/main/java/org/apache/freemarker/core/model/impl/HashAdapter.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/freemarker/core/model/impl/HashAdapter.java b/src/main/java/org/apache/freemarker/core/model/impl/HashAdapter.java
index 7fefec9..a4df4d0 100644
--- a/src/main/java/org/apache/freemarker/core/model/impl/HashAdapter.java
+++ b/src/main/java/org/apache/freemarker/core/model/impl/HashAdapter.java
@@ -34,8 +34,9 @@ import org.apache.freemarker.core.model.TemplateModelIterator;
 import org.apache.freemarker.core.util.UndeclaredThrowableException;
 
 /**
+ * Adapts a {@link TemplateHashModel} to a {@link Map}.
  */
-public class HashAdapter extends AbstractMap implements TemplateModelAdapter {
+class HashAdapter extends AbstractMap implements TemplateModelAdapter {
     private final DefaultObjectWrapper wrapper;
     private final TemplateHashModel model;
     private Set entrySet;

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/780b76a6/src/main/java/org/apache/freemarker/core/model/impl/IteratorModel.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/freemarker/core/model/impl/IteratorModel.java b/src/main/java/org/apache/freemarker/core/model/impl/IteratorModel.java
deleted file mode 100644
index 0eabf21..0000000
--- a/src/main/java/org/apache/freemarker/core/model/impl/IteratorModel.java
+++ /dev/null
@@ -1,112 +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.impl;
-
-import java.util.Iterator;
-import java.util.NoSuchElementException;
-
-import org.apache.freemarker.core.model.TemplateCollectionModel;
-import org.apache.freemarker.core.model.TemplateModel;
-import org.apache.freemarker.core.model.TemplateModelException;
-import org.apache.freemarker.core.model.TemplateModelIterator;
-
-/**
- * <p>A class that adds {@link TemplateModelIterator} functionality to the
- * {@link Iterator} interface implementers. 
- * </p>
- * <p>It differs from the {@link org.apache.freemarker.core.model.impl.SimpleCollection} in that 
- * it inherits from {@link BeanModel}, and therefore you can call methods on 
- * it directly, even to the effect of calling <tt>iterator.remove()</tt> in 
- * the template.</p> <p>Using the model as a collection model is NOT 
- * thread-safe, as iterators are inherently not thread-safe.
- * Further, you can iterate over it only once. Attempts to call the
- * {@link #iterator()} method after it was already driven to the end once will 
- * throw an exception.</p>
- */
-
-public class IteratorModel
-extends
-    BeanModel
-implements
-    TemplateModelIterator,
-    TemplateCollectionModel {
-    private boolean accessed = false;
-    
-    /**
-     * Creates a new model that wraps the specified iterator object.
-     * @param iterator the iterator object to wrap into a model.
-     * @param wrapper the {@link DefaultObjectWrapper} associated with this model.
-     * Every model has to have an associated {@link DefaultObjectWrapper} instance. The
-     * model gains many attributes from its wrapper, including the caching 
-     * behavior, method exposure level, method-over-item shadowing policy etc.
-     */
-    public IteratorModel(Iterator iterator, DefaultObjectWrapper wrapper) {
-        super(iterator, wrapper);
-    }
-
-    /**
-     * This allows the iterator to be used in a <tt>&lt;#list&gt;</tt> block.
-     * @return "this"
-     */
-    @Override
-    public TemplateModelIterator iterator() throws TemplateModelException {
-        synchronized (this) {
-            if (accessed) {
-                throw new TemplateModelException(
-                    "This collection is stateful and can not be iterated over the" +
-                    " second time.");
-            }
-            accessed = true;
-        }
-        return this;
-    }
-    
-    /**
-     * Calls underlying {@link Iterator#hasNext()}.
-     */
-    @Override
-    public boolean hasNext() {
-        return ((Iterator) object).hasNext();
-    }
-
-
-    /**
-     * Calls underlying {@link Iterator#next()} and wraps the result.
-     */
-    @Override
-    public TemplateModel next()
-    throws TemplateModelException {
-        try {
-            return wrap(((Iterator) object).next());
-        } catch (NoSuchElementException e) {
-            throw new TemplateModelException(
-                "No more elements in the iterator.", e);
-        }
-    }
-
-    /**
-     * Returns {@link Iterator#hasNext()}. Therefore, an
-     * iterator that has no more element evaluates to false, and an 
-     * iterator that has further elements evaluates to true.
-     */
-    public boolean getAsBoolean() {
-        return hasNext();
-    }
-}

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/780b76a6/src/main/java/org/apache/freemarker/core/model/impl/MapModel.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/freemarker/core/model/impl/MapModel.java b/src/main/java/org/apache/freemarker/core/model/impl/MapModel.java
deleted file mode 100644
index 95abb2a..0000000
--- a/src/main/java/org/apache/freemarker/core/model/impl/MapModel.java
+++ /dev/null
@@ -1,120 +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.impl;
-
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-
-import org.apache.freemarker.core.model.ObjectWrapper;
-import org.apache.freemarker.core.model.TemplateMethodModelEx;
-import org.apache.freemarker.core.model.TemplateModel;
-import org.apache.freemarker.core.model.TemplateModelException;
-
-/**
- * <p>A special case of {@link BeanModel} that adds implementation
- * for {@link TemplateMethodModelEx} on map objects that is a shortcut for the
- * <tt>Map.get()</tt> method. Note that if the passed argument itself is a
- * reflection-wrapper model, then the map lookup will be performed using the
- * wrapped object as the key. Note that you can call <tt>get()</tt> using the
- * <tt>map.key</tt> syntax inherited from {@link BeanModel} as well, 
- * however in that case the key is always a string.</p>
- * <p>The class itself does not implement the {@link org.apache.freemarker.core.model.TemplateCollectionModel}.
- * You can, however use <tt>map.entrySet()</tt>, <tt>map.keySet()</tt>, or
- * <tt>map.values()</tt> to obtain {@link org.apache.freemarker.core.model.TemplateCollectionModel} instances for 
- * various aspects of the map.</p>
- */
-public class MapModel
-extends
-    StringModel
-implements
-    TemplateMethodModelEx {
-    static final ModelFactory FACTORY =
-        new ModelFactory()
-        {
-            @Override
-            public TemplateModel create(Object object, ObjectWrapper wrapper) {
-                return new MapModel((Map) object, (DefaultObjectWrapper) wrapper);
-            }
-        };
-
-    /**
-     * Creates a new model that wraps the specified map object.
-     * @param map the map object to wrap into a model.
-     * @param wrapper the {@link DefaultObjectWrapper} associated with this model.
-     * Every model has to have an associated {@link DefaultObjectWrapper} instance. The
-     * model gains many attributes from its wrapper, including the caching 
-     * behavior, method exposure level, method-over-item shadowing policy etc.
-     */
-    public MapModel(Map map, DefaultObjectWrapper wrapper) {
-        super(map, wrapper);
-    }
-
-    /**
-     * The first argument is used as a key to call the map's <tt>get</tt> method.
-     */
-    @Override
-    public Object exec(List arguments)
-    throws TemplateModelException {
-        Object key = unwrap((TemplateModel) arguments.get(0));
-        return wrap(((Map) object).get(key));
-    }
-
-    /**
-     * Overridden to invoke the generic get method by casting to Map instead of 
-     * through reflection - should yield better performance.
-     */
-    @Override
-    protected TemplateModel invokeGenericGet(Map keyMap, Class clazz, String key)
-    throws TemplateModelException {
-        Map map = (Map) object;
-        Object val = map.get(key);
-        if (val == null) {
-            if (key.length() == 1) {
-                // just check for Character key if this is a single-character string
-                Character charKey = Character.valueOf(key.charAt(0));
-                val = map.get(charKey);
-                if (val == null && !(map.containsKey(key) || map.containsKey(charKey))) {
-                    return UNKNOWN;
-                }
-            } else if (!map.containsKey(key)) {
-                return UNKNOWN;
-            }
-        }
-        return wrap(val);
-    }
-
-    @Override
-    public boolean isEmpty() {
-        return ((Map) object).isEmpty() && super.isEmpty();
-    }
-
-    @Override
-    public int size() {
-        return keySet().size();
-    }
-
-    @Override
-    protected Set keySet() {
-        Set set = super.keySet();
-        set.addAll(((Map) object).keySet());
-        return set;
-    }
-}

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/780b76a6/src/main/java/org/apache/freemarker/core/model/impl/ModelCache.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/freemarker/core/model/impl/ModelCache.java b/src/main/java/org/apache/freemarker/core/model/impl/ModelCache.java
deleted file mode 100644
index 529b988..0000000
--- a/src/main/java/org/apache/freemarker/core/model/impl/ModelCache.java
+++ /dev/null
@@ -1,143 +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.impl;
-
-import java.lang.ref.ReferenceQueue;
-import java.lang.ref.SoftReference;
-import java.util.IdentityHashMap;
-import java.util.Map;
-
-import org.apache.freemarker.core.model.TemplateModel;
-import org.apache.freemarker.core.model.TemplateModelAdapter;
-
-/**
- * Internally used by various wrapper implementations to implement model
- * caching.
- */
-public abstract class ModelCache {
-    private boolean useCache = false;
-    private Map<Object, ModelReference> modelCache = null;
-    private ReferenceQueue<TemplateModel> refQueue = null;
-    
-    protected ModelCache() {
-    }
-    
-    /**
-     * Sets whether this wrapper caches model instances. Default is false.
-     * When set to true, calling {@link #getInstance(Object)} 
-     * multiple times for the same object will return the same model.
-     */
-    public synchronized void setUseCache(boolean useCache) {
-        this.useCache = useCache;
-        if (useCache) {
-            modelCache = new IdentityHashMap<>();
-            refQueue = new ReferenceQueue<>();
-        } else {
-            modelCache = null;
-            refQueue = null;
-        }
-    }
-
-    /**
-     * @since 2.3.21
-     */
-    public synchronized boolean getUseCache() {
-        return useCache;
-    }
-    
-    public TemplateModel getInstance(Object object) {
-        if (object instanceof TemplateModel) {
-            return (TemplateModel) object;
-        }
-        if (object instanceof TemplateModelAdapter) {
-            return ((TemplateModelAdapter) object).getTemplateModel();
-        }
-        if (useCache && isCacheable(object)) {
-            TemplateModel model = lookup(object);
-            if (model == null) {
-                model = create(object);
-                register(model, object);
-            }
-            return model;
-        } else {
-            return create(object);
-        }
-    }
-    
-    protected abstract TemplateModel create(Object object);
-    protected abstract boolean isCacheable(Object object);
-    
-    public void clearCache() {
-        if (modelCache != null) {
-            synchronized (modelCache) {
-                modelCache.clear();
-            }
-        }
-    }
-
-    private TemplateModel lookup(Object object) {
-        ModelReference ref = null;
-        // NOTE: we're doing minimal synchronizations -- which can lead to
-        // duplicate wrapper creation. However, this has no harmful side-effects and
-        // is a lesser performance hit.
-        synchronized (modelCache) {
-            ref = modelCache.get(object);
-        }
-
-        if (ref != null)
-            return ref.getModel();
-
-        return null;
-    }
-
-    private void register(TemplateModel model, Object object) {
-        synchronized (modelCache) {
-            // Remove cleared references
-            for (; ; ) {
-                ModelReference queuedRef = (ModelReference) refQueue.poll();
-                if (queuedRef == null) {
-                    break;
-                }
-                modelCache.remove(queuedRef.object);
-            }
-            // Register new reference
-            modelCache.put(object, new ModelReference(model, object, refQueue));
-        }
-    }
-
-    /**
-     * A special soft reference that is registered in the modelCache.
-     * When it gets cleared (that is, the model became unreachable)
-     * it will remove itself from the model cache.
-     */
-    private static final class ModelReference extends SoftReference<TemplateModel> {
-        Object object;
-
-        ModelReference(TemplateModel ref, Object object, ReferenceQueue<TemplateModel> refQueue) {
-            super(ref, refQueue);
-            this.object = object;
-        }
-
-        TemplateModel getModel() {
-            return get();
-        }
-    }
-
-}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/780b76a6/src/main/java/org/apache/freemarker/core/model/impl/ModelFactory.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/freemarker/core/model/impl/ModelFactory.java b/src/main/java/org/apache/freemarker/core/model/impl/ModelFactory.java
deleted file mode 100644
index cd74342..0000000
--- a/src/main/java/org/apache/freemarker/core/model/impl/ModelFactory.java
+++ /dev/null
@@ -1,34 +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.impl;
-
-import org.apache.freemarker.core.model.ObjectWrapper;
-import org.apache.freemarker.core.model.TemplateModel;
-
-/**
- * Interface used to create various wrapper models in the {@link ModelCache}.
- */
-public interface ModelFactory {
-    /**
-     * Create a wrapping model for the specified object that belongs to
-     * the specified wrapper.
-     */
-    TemplateModel create(Object object, ObjectWrapper wrapper);
-}

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/780b76a6/src/main/java/org/apache/freemarker/core/model/impl/NumberModel.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/freemarker/core/model/impl/NumberModel.java b/src/main/java/org/apache/freemarker/core/model/impl/NumberModel.java
deleted file mode 100644
index 70b50d9..0000000
--- a/src/main/java/org/apache/freemarker/core/model/impl/NumberModel.java
+++ /dev/null
@@ -1,60 +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.impl;
-
-import org.apache.freemarker.core.model.ObjectWrapper;
-import org.apache.freemarker.core.model.TemplateModel;
-import org.apache.freemarker.core.model.TemplateNumberModel;
-
-/**
- * Wraps arbitrary subclass of {@link java.lang.Number} into a reflective model.
- * Beside acting as a {@link TemplateNumberModel}, you can call all Java methods on
- * these objects as well.
- */
-public class NumberModel
-extends
-    BeanModel
-implements
-    TemplateNumberModel {
-    static final ModelFactory FACTORY =
-        new ModelFactory()
-        {
-            @Override
-            public TemplateModel create(Object object, ObjectWrapper wrapper) {
-                return new NumberModel((Number) object, (DefaultObjectWrapper) wrapper);
-            }
-        };
-    /**
-     * Creates a new model that wraps the specified number object.
-     * @param number the number object to wrap into a model.
-     * @param wrapper the {@link DefaultObjectWrapper} associated with this model.
-     * Every model has to have an associated {@link DefaultObjectWrapper} instance. The
-     * model gains many attributes from its wrapper, including the caching 
-     * behavior, method exposure level, method-over-item shadowing policy etc.
-     */
-    public NumberModel(Number number, DefaultObjectWrapper wrapper) {
-        super(number, wrapper);
-    }
-
-    @Override
-    public Number getAsNumber() {
-        return (Number) object;
-    }
-}

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/780b76a6/src/main/java/org/apache/freemarker/core/model/impl/OverloadedMethodsModel.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/freemarker/core/model/impl/OverloadedMethodsModel.java b/src/main/java/org/apache/freemarker/core/model/impl/OverloadedMethodsModel.java
index 204ff1f..c8b3681 100644
--- a/src/main/java/org/apache/freemarker/core/model/impl/OverloadedMethodsModel.java
+++ b/src/main/java/org/apache/freemarker/core/model/impl/OverloadedMethodsModel.java
@@ -23,19 +23,17 @@ package org.apache.freemarker.core.model.impl;
 import java.util.Collections;
 import java.util.List;
 
+import org.apache.freemarker.core.model.TemplateMethodModel;
 import org.apache.freemarker.core.model.TemplateMethodModelEx;
 import org.apache.freemarker.core.model.TemplateModel;
 import org.apache.freemarker.core.model.TemplateModelException;
 import org.apache.freemarker.core.model.TemplateSequenceModel;
 
 /**
- * Wraps a set of same-name overloaded methods behind {@link org.apache.freemarker.core.model.TemplateMethodModel} interface,
+ * Wraps a set of same-name overloaded methods behind {@link TemplateMethodModel} interface,
  * like if it was a single method, chooses among them behind the scenes on call-time based on the argument values.
  */
-public class OverloadedMethodsModel
-implements
-	TemplateMethodModelEx,
-	TemplateSequenceModel {
+public class OverloadedMethodsModel implements TemplateMethodModelEx, TemplateSequenceModel {
     private final Object object;
     private final OverloadedMethods overloadedMethods;
     private final DefaultObjectWrapper wrapper;
@@ -54,8 +52,7 @@ implements
      * unambiguously.
      */
     @Override
-    public Object exec(List arguments)
-    throws TemplateModelException {
+    public Object exec(List arguments) throws TemplateModelException {
         MemberAndArguments maa = overloadedMethods.getMemberAndArguments(arguments, wrapper);
         try {
             return maa.invokeMethod(wrapper, object);

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/780b76a6/src/main/java/org/apache/freemarker/core/model/impl/ResourceBundleModel.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/freemarker/core/model/impl/ResourceBundleModel.java b/src/main/java/org/apache/freemarker/core/model/impl/ResourceBundleModel.java
index 27bdf96..b8e28ed 100644
--- a/src/main/java/org/apache/freemarker/core/model/impl/ResourceBundleModel.java
+++ b/src/main/java/org/apache/freemarker/core/model/impl/ResourceBundleModel.java
@@ -31,7 +31,6 @@ import java.util.Set;
 
 import org.apache.freemarker.core._DelayedJQuote;
 import org.apache.freemarker.core._TemplateModelException;
-import org.apache.freemarker.core.model.ObjectWrapper;
 import org.apache.freemarker.core.model.TemplateMethodModelEx;
 import org.apache.freemarker.core.model.TemplateModel;
 import org.apache.freemarker.core.model.TemplateModelException;
@@ -56,14 +55,6 @@ public class ResourceBundleModel
     BeanModel
     implements
     TemplateMethodModelEx {
-    static final ModelFactory FACTORY =
-        new ModelFactory()
-        {
-            @Override
-            public TemplateModel create(Object object, ObjectWrapper wrapper) {
-                return new ResourceBundleModel((ResourceBundle) object, (DefaultObjectWrapper) wrapper);
-            }
-        };
 
     private Hashtable formats = null;
 
@@ -137,7 +128,7 @@ public class ResourceBundleModel
                 params[i] = unwrap((TemplateModel) it.next());
     
             // Invoke format
-            return new StringModel(format(key, params), wrapper);
+            return new BeanAndStringModel(format(key, params), wrapper);
         } catch (MissingResourceException e) {
             throw new TemplateModelException("No such key: " + key);
         } catch (Exception e) {

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/780b76a6/src/main/java/org/apache/freemarker/core/model/impl/SimpleMapModel.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/freemarker/core/model/impl/SimpleMapModel.java b/src/main/java/org/apache/freemarker/core/model/impl/SimpleMapModel.java
deleted file mode 100644
index cf9836e..0000000
--- a/src/main/java/org/apache/freemarker/core/model/impl/SimpleMapModel.java
+++ /dev/null
@@ -1,129 +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.impl;
-
-import java.util.List;
-import java.util.Map;
-
-import org.apache.freemarker.core.model.AdapterTemplateModel;
-import org.apache.freemarker.core.model.ObjectWrapper;
-import org.apache.freemarker.core.model.RichObjectWrapper;
-import org.apache.freemarker.core.model.TemplateCollectionModel;
-import org.apache.freemarker.core.model.TemplateHashModelEx2;
-import org.apache.freemarker.core.model.TemplateMethodModelEx;
-import org.apache.freemarker.core.model.TemplateModel;
-import org.apache.freemarker.core.model.TemplateModelException;
-import org.apache.freemarker.core.model.TemplateModelWithAPISupport;
-import org.apache.freemarker.core.model.WrapperTemplateModel;
-import org.apache.freemarker.core.model.WrappingTemplateModel;
-
-/**
- * Model used by {@link DefaultObjectWrapper} when <tt>simpleMapWrapper</tt>
- * mode is enabled. Provides a simple hash model interface to the
- * underlying map (does not copy like {@link org.apache.freemarker.core.model.impl.SimpleHash}),
- * and a method interface to non-string keys.
- */
-public class SimpleMapModel extends WrappingTemplateModel 
-implements TemplateHashModelEx2, TemplateMethodModelEx, AdapterTemplateModel, 
-WrapperTemplateModel, TemplateModelWithAPISupport {
-    static final ModelFactory FACTORY =
-        new ModelFactory()
-        {
-            @Override
-            public TemplateModel create(Object object, ObjectWrapper wrapper) {
-                return new SimpleMapModel((Map) object, (DefaultObjectWrapper) wrapper);
-            }
-        };
-
-    private final Map map;
-    
-    public SimpleMapModel(Map map, DefaultObjectWrapper wrapper) {
-        super(wrapper);
-        this.map = map;
-    }
-
-    @Override
-    public TemplateModel get(String key) throws TemplateModelException {
-        Object val = map.get(key);
-        if (val == null) {
-            if (key.length() == 1) {
-                // just check for Character key if this is a single-character string
-                Character charKey = Character.valueOf(key.charAt(0));
-                val = map.get(charKey);
-                if (val == null && !(map.containsKey(key) || map.containsKey(charKey))) {
-                    return null;
-                }
-            } else if (!map.containsKey(key)) {
-                return null;
-            }
-        }
-        return wrap(val);
-    }
-    
-    @Override
-    public Object exec(List args) throws TemplateModelException {
-        Object key = ((DefaultObjectWrapper) getObjectWrapper()).unwrap((TemplateModel) args.get(0));
-        Object value = map.get(key);
-        if (value == null && !map.containsKey(key)) {
-            return null;
-        }
-        return wrap(value);
-    }
-
-    @Override
-    public boolean isEmpty() {
-        return map.isEmpty();
-    }
-
-    @Override
-    public int size() {
-        return map.size();
-    }
-
-    @Override
-    public TemplateCollectionModel keys() {
-        return new CollectionAndSequence(new SimpleSequence(map.keySet(), getObjectWrapper()));
-    }
-
-    @Override
-    public TemplateCollectionModel values() {
-        return new CollectionAndSequence(new SimpleSequence(map.values(), getObjectWrapper()));
-    }
-    
-    @Override
-    public KeyValuePairIterator keyValuePairIterator() {
-        return new MapKeyValuePairIterator(map, getObjectWrapper());
-    }
-
-    @Override
-    public Object getAdaptedObject(Class hint) {
-        return map;
-    }
-    
-    @Override
-    public Object getWrappedObject() {
-        return map;
-    }
-
-    @Override
-    public TemplateModel getAPI() throws TemplateModelException {
-        return ((RichObjectWrapper) getObjectWrapper()).wrapAsAPI(map);
-    }
-}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/780b76a6/src/main/java/org/apache/freemarker/core/model/impl/SimpleObjectWrapper.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/freemarker/core/model/impl/SimpleObjectWrapper.java b/src/main/java/org/apache/freemarker/core/model/impl/SimpleObjectWrapper.java
index 281b8ac..57ab1b0 100644
--- a/src/main/java/org/apache/freemarker/core/model/impl/SimpleObjectWrapper.java
+++ b/src/main/java/org/apache/freemarker/core/model/impl/SimpleObjectWrapper.java
@@ -23,7 +23,6 @@ import org.apache.freemarker.core.Version;
 import org.apache.freemarker.core.model.TemplateHashModel;
 import org.apache.freemarker.core.model.TemplateModel;
 import org.apache.freemarker.core.model.TemplateModelException;
-import org.w3c.dom.Node;
 
 /**
  * A restricted object wrapper that will not expose arbitrary object, just those that directly correspond to the
@@ -41,23 +40,14 @@ public class SimpleObjectWrapper extends DefaultObjectWrapper {
         super(incompatibleImprovements);
     }
 
-    @Override
-    protected TemplateModel handW3CNode(Node node) throws TemplateModelException {
-        throw newUnhandledTypeException(node);
-    }
-
     /**
      * Called if a type other than the simple ones we know about is passed in. 
      * In this implementation, this just throws an exception.
      */
     @Override
-    protected TemplateModel handleUnknownType(Object obj) throws TemplateModelException {
-        throw newUnhandledTypeException(obj);
-    }
-
-    private TemplateModelException newUnhandledTypeException(Object obj) throws TemplateModelException {
-        return new TemplateModelException("SimpleObjectWrapper deliberately won't wrap this type: "
-                                         + obj.getClass().getName());
+    protected TemplateModel handleNonBasicTypes(Object obj) throws TemplateModelException {
+        throw new TemplateModelException("SimpleObjectWrapper deliberately won't wrap this type: "
+                + obj.getClass().getName());
     }
 
     @Override

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/780b76a6/src/main/java/org/apache/freemarker/core/model/impl/StringModel.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/freemarker/core/model/impl/StringModel.java b/src/main/java/org/apache/freemarker/core/model/impl/StringModel.java
deleted file mode 100644
index 499c291..0000000
--- a/src/main/java/org/apache/freemarker/core/model/impl/StringModel.java
+++ /dev/null
@@ -1,63 +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.impl;
-
-import org.apache.freemarker.core.model.ObjectWrapper;
-import org.apache.freemarker.core.model.TemplateModel;
-import org.apache.freemarker.core.model.TemplateScalarModel;
-
-/**
- * Subclass of {@link BeanModel} that exposes the return value of the {@link
- * java.lang.Object#toString()} method through the {@link TemplateScalarModel}
- * interface.
- */
-public class StringModel extends BeanModel
-implements TemplateScalarModel {
-    static final ModelFactory FACTORY =
-        new ModelFactory()
-        {
-            @Override
-            public TemplateModel create(Object object, ObjectWrapper wrapper) {
-                return new StringModel(object, (DefaultObjectWrapper) wrapper);
-            }
-        };
-
-    /**
-     * Creates a new model that wraps the specified object with BeanModel + scalar
-     * functionality.
-     * @param object the object to wrap into a model.
-     * @param wrapper the {@link DefaultObjectWrapper} associated with this model.
-     * Every model has to have an associated {@link DefaultObjectWrapper} instance. The
-     * model gains many attributes from its wrapper, including the caching 
-     * behavior, method exposure level, method-over-item shadowing policy etc.
-     */
-    public StringModel(Object object, DefaultObjectWrapper wrapper) {
-        super(object, wrapper);
-    }
-
-    /**
-     * Returns the result of calling {@link Object#toString()} on the wrapped
-     * object.
-     */
-    @Override
-    public String getAsString() {
-        return object.toString();
-    }
-}

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/780b76a6/src/main/java/org/apache/freemarker/core/model/impl/_EnumModels.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/freemarker/core/model/impl/_EnumModels.java b/src/main/java/org/apache/freemarker/core/model/impl/_EnumModels.java
deleted file mode 100644
index 19efe76..0000000
--- a/src/main/java/org/apache/freemarker/core/model/impl/_EnumModels.java
+++ /dev/null
@@ -1,54 +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.impl;
-
-import java.util.LinkedHashMap;
-import java.util.Map;
-
-import org.apache.freemarker.core.model.TemplateModel;
-
-/**
- * Don't use this class; it's only public to work around Google App Engine Java
- * compliance issues. FreeMarker developers only: treat this class as package-visible.
- */
-public class _EnumModels extends ClassBasedModelFactory {
-
-    public _EnumModels(DefaultObjectWrapper wrapper) {
-        super(wrapper);
-    }
-    
-    @Override
-    protected TemplateModel createModel(Class clazz) {
-        Object[] obj = clazz.getEnumConstants();
-        if (obj == null) {
-            // Return null - it'll manifest itself as undefined in the template.
-            // We're doing this rather than throw an exception as this way 
-            // people can use someEnumModel?default({}) to gracefully fall back 
-            // to an empty hash if they want to.
-            return null;
-        }
-        Map map = new LinkedHashMap();
-        for (Object anObj : obj) {
-            Enum value = (Enum) anObj;
-            map.put(value.name(), value);
-        }
-        return new SimpleMapModel(map, getWrapper());
-    }
-}

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/780b76a6/src/main/java/org/apache/freemarker/core/util/FTLUtil.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/freemarker/core/util/FTLUtil.java b/src/main/java/org/apache/freemarker/core/util/FTLUtil.java
index eba25f6..619eb48 100644
--- a/src/main/java/org/apache/freemarker/core/util/FTLUtil.java
+++ b/src/main/java/org/apache/freemarker/core/util/FTLUtil.java
@@ -43,17 +43,10 @@ import org.apache.freemarker.core.model.TemplateScalarModel;
 import org.apache.freemarker.core.model.TemplateSequenceModel;
 import org.apache.freemarker.core.model.TemplateTransformModel;
 import org.apache.freemarker.core.model.WrapperTemplateModel;
+import org.apache.freemarker.core.model.impl.BeanAndStringModel;
 import org.apache.freemarker.core.model.impl.BeanModel;
-import org.apache.freemarker.core.model.impl.BooleanModel;
-import org.apache.freemarker.core.model.impl.CollectionModel;
-import org.apache.freemarker.core.model.impl.DateModel;
-import org.apache.freemarker.core.model.impl.EnumerationModel;
-import org.apache.freemarker.core.model.impl.IteratorModel;
-import org.apache.freemarker.core.model.impl.MapModel;
-import org.apache.freemarker.core.model.impl.NumberModel;
 import org.apache.freemarker.core.model.impl.OverloadedMethodsModel;
 import org.apache.freemarker.core.model.impl.SimpleMethodModel;
-import org.apache.freemarker.core.model.impl.StringModel;
 
 /**
  * Static utility methods that perform tasks specific to the FreeMarker Template Language (FTL).
@@ -715,19 +708,7 @@ public final class FTLUtil {
      */
     private static Class getPrimaryTemplateModelInterface(TemplateModel tm) {
         if (tm instanceof BeanModel) {
-            if (tm instanceof CollectionModel) {
-                return TemplateSequenceModel.class;
-            } else if (tm instanceof IteratorModel || tm instanceof EnumerationModel) {
-                return TemplateCollectionModel.class;
-            } else if (tm instanceof MapModel) {
-                return TemplateHashModelEx.class;
-            } else if (tm instanceof NumberModel) {
-                return TemplateNumberModel.class;
-            } else if (tm instanceof BooleanModel) {
-                return TemplateBooleanModel.class;
-            } else if (tm instanceof DateModel) {
-                return TemplateDateModel.class;
-            } else if (tm instanceof StringModel) {
+            if (tm instanceof BeanAndStringModel) {
                 Object wrapped = ((BeanModel) tm).getWrappedObject();
                 return wrapped instanceof String
                         ? TemplateScalarModel.class