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/03 20:18:44 UTC

[1/2] incubator-freemarker git commit: Removed the global static final ObjectWrapper-s. It had a "few" consequences:

Repository: incubator-freemarker
Updated Branches:
  refs/heads/3 83b4b77ca -> b4dfe5d24


http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/b4dfe5d2/src/main/java/org/apache/freemarker/core/model/impl/SimpleSequence.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/freemarker/core/model/impl/SimpleSequence.java b/src/main/java/org/apache/freemarker/core/model/impl/SimpleSequence.java
index b16069c..17cd03a 100644
--- a/src/main/java/org/apache/freemarker/core/model/impl/SimpleSequence.java
+++ b/src/main/java/org/apache/freemarker/core/model/impl/SimpleSequence.java
@@ -26,7 +26,6 @@ import java.util.List;
 
 import org.apache.freemarker.core.model.ObjectWrapper;
 import org.apache.freemarker.core.model.RichObjectWrapper;
-import org.apache.freemarker.core.model.TemplateBooleanModel;
 import org.apache.freemarker.core.model.TemplateCollectionModel;
 import org.apache.freemarker.core.model.TemplateModel;
 import org.apache.freemarker.core.model.TemplateModelException;
@@ -77,70 +76,17 @@ import org.apache.freemarker.core.model.WrappingTemplateModel;
 public class SimpleSequence extends WrappingTemplateModel implements TemplateSequenceModel, Serializable {
 
     /**
-     * The {@link List} that stored the elements of this sequence. It migth contains both {@link TemplateModel} elements
+     * The {@link List} that stored the elements of this sequence. It might contains both {@link TemplateModel} elements
      * and non-{@link TemplateModel} elements.
      */
     protected final List list;
-    
-    private List unwrappedList;
-
-    /**
-     * Constructs an empty simple sequence that will use
-     * {@link _StaticObjectWrappers#DEFAULT_OBJECT_WRAPPER}.
-     * 
-     * @deprecated Use {@link #SimpleSequence(ObjectWrapper)} instead.
-     */
-    @Deprecated
-    public SimpleSequence() {
-        this((ObjectWrapper) null);
-    }
-
-    /**
-     * Constructs an empty simple sequence with preallocated capacity and using
-     * {@link _StaticObjectWrappers#DEFAULT_OBJECT_WRAPPER}.
-     * 
-     * @deprecated Use {@link #SimpleSequence(Collection, ObjectWrapper)}.
-     */
-    @Deprecated
-    public SimpleSequence(int capacity) {
-        list = new ArrayList(capacity);
-    }
-    
-    /**
-     * Constructs a simple sequence that will contain the elements
-     * from the specified {@link Collection} and will use {@link _StaticObjectWrappers#DEFAULT_OBJECT_WRAPPER}.
-     * @param collection the collection containing initial values. Note that a
-     * copy of the collection is made for internal use.
-     * 
-     * @deprecated Use {@link #SimpleSequence(Collection, ObjectWrapper)}.
-     */
-    @Deprecated
-    public SimpleSequence(Collection collection) {
-        this(collection, null);
-    }
-    
-    /**
-     * Constructs a simple sequence from the passed collection model, which shouldn't be added to later. The internal
-     * list will be build immediately (not lazily). The resulting sequence shouldn't be extended with
-     * {@link #add(Object)}, because the appropriate {@link ObjectWrapper} won't be available; use
-     * {@link #SimpleSequence(Collection, ObjectWrapper)} instead, if you need that.
-     */
-    public SimpleSequence(TemplateCollectionModel tcm) throws TemplateModelException {
-        ArrayList alist = new ArrayList();
-        for (TemplateModelIterator it = tcm.iterator(); it.hasNext(); ) {
-            alist.add(it.next());
-        }
-        alist.trimToSize();
-        list = alist;
-    }
 
     /**
      * Constructs an empty sequence using the specified object wrapper.
      * 
      * @param wrapper
-     *            The object wrapper to use to wrap the list items into {@link TemplateModel} instances. {@code null} is
-     *            allowed, but deprecated, and will cause the deprecated default object wrapper
-     *            {@link _StaticObjectWrappers#DEFAULT_OBJECT_WRAPPER} to be used.
+     *            The object wrapper to use to wrap the list items into {@link TemplateModel} instances. Not
+     *            {@code null}.
      */
     public SimpleSequence(ObjectWrapper wrapper) {
         super(wrapper);
@@ -187,55 +133,9 @@ public class SimpleSequence extends WrappingTemplateModel implements TemplateSeq
      */
     public void add(Object obj) {
         list.add(obj);
-        unwrappedList = null;
     }
 
     /**
-     * Adds a boolean value to the end of this sequence. The newly added boolean will be immediately converted into
-     * {@link TemplateBooleanModel#TRUE} or {@link TemplateBooleanModel#FALSE}, without using the {@link ObjectWrapper}.
-     *
-     * @param b
-     *            The boolean value to be added.
-     * 
-     * @deprecated Use {@link #add(Object)} instead, as this bypasses the {@link ObjectWrapper}.
-     */
-    @Deprecated
-    public void add(boolean b) {
-        add(b ? TemplateBooleanModel.TRUE : TemplateBooleanModel.FALSE);
-    }
-    
-    /**
-     * Builds a deep-copy of the underlying list, unwrapping any values that were already converted to
-     * {@link TemplateModel}-s. When called for the second time (or later), it just reuses the first result, unless the
-     * sequence was modified since then.
-     * 
-     * @deprecated No replacement exists; not a reliable way of getting back the original list elemnts.
-     */
-    @Deprecated
-    public List toList() throws TemplateModelException {
-        if (unwrappedList == null) {
-            Class listClass = list.getClass();
-            List result = null;
-            try {
-                result = (List) listClass.newInstance();
-            } catch (Exception e) {
-                throw new TemplateModelException("Error instantiating an object of type " + listClass.getName(),
-                        e);
-            }
-            DefaultObjectWrapper ow = _StaticObjectWrappers.DEFAULT_OBJECT_WRAPPER;
-            for (int i = 0; i < list.size(); i++) {
-                Object elem = list.get(i);
-                if (elem instanceof TemplateModel) {
-                    elem = ow.unwrap((TemplateModel) elem);
-                }
-                result.add(elem);
-            }
-            unwrappedList = result;
-        }
-        return unwrappedList;
-    }
-    
-    /**
      * Returns the item at the specified index of the list. If the item isn't yet an {@link TemplateModel}, it will wrap
      * it to one now, and writes it back into the backing list.
      */
@@ -259,47 +159,9 @@ public class SimpleSequence extends WrappingTemplateModel implements TemplateSeq
         return list.size();
     }
 
-    /**
-     * @return a synchronized wrapper for list.
-     */
-    public SimpleSequence synchronizedWrapper() {
-        return new SynchronizedSequence();
-    }
-    
     @Override
     public String toString() {
         return list.toString();
     }
 
-    private class SynchronizedSequence extends SimpleSequence {
-
-        @Override
-        public void add(Object obj) {
-            synchronized (SimpleSequence.this) {
-                SimpleSequence.this.add(obj);
-            }
-        }
-
-        @Override
-        public TemplateModel get(int i) throws TemplateModelException {
-            synchronized (SimpleSequence.this) {
-                return SimpleSequence.this.get(i);
-            }
-        }
-        
-        @Override
-        public int size() {
-            synchronized (SimpleSequence.this) {
-                return SimpleSequence.this.size();
-            }
-        }
-        
-        @Override
-        public List toList() throws TemplateModelException {
-            synchronized (SimpleSequence.this) {
-                return SimpleSequence.this.toList();
-            }
-        }
-    }
-    
 }
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/b4dfe5d2/src/main/java/org/apache/freemarker/core/model/impl/_StaticObjectWrappers.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/freemarker/core/model/impl/_StaticObjectWrappers.java b/src/main/java/org/apache/freemarker/core/model/impl/_StaticObjectWrappers.java
deleted file mode 100644
index 3be2da5..0000000
--- a/src/main/java/org/apache/freemarker/core/model/impl/_StaticObjectWrappers.java
+++ /dev/null
@@ -1,42 +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.Configuration;
-
-/**
- * For internal use only; don't depend on this, there's no backward compatibility guarantee at all!
- */
-// [FM3] This was added temporary until we get to cleaning up the parts that depend on a static ObjectWrapper. The
-// ObjectWrapper should always come from the Configuration, not from the statics here.
-public final class _StaticObjectWrappers {
-    
-    private _StaticObjectWrappers() {
-        //
-    }
-
-    public static final DefaultObjectWrapper DEFAULT_OBJECT_WRAPPER
-            = new DefaultObjectWrapperBuilder(Configuration.VERSION_3_0_0).build();
-
-    public static final RestrictedObjectWrapper RESTRICTED_OBJECT_WRAPPER
-            = new RestrictedObjectWrapper(Configuration.VERSION_3_0_0);
-    {
-        RESTRICTED_OBJECT_WRAPPER.writeProtect();
-    }    
-}

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/b4dfe5d2/src/main/java/org/apache/freemarker/dom/JaxenXPathSupport.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/freemarker/dom/JaxenXPathSupport.java b/src/main/java/org/apache/freemarker/dom/JaxenXPathSupport.java
index 0d4bd78..cd0724a 100644
--- a/src/main/java/org/apache/freemarker/dom/JaxenXPathSupport.java
+++ b/src/main/java/org/apache/freemarker/dom/JaxenXPathSupport.java
@@ -41,7 +41,6 @@ import org.apache.freemarker.core.model.TemplateModel;
 import org.apache.freemarker.core.model.TemplateModelException;
 import org.apache.freemarker.core.model.TemplateNumberModel;
 import org.apache.freemarker.core.model.TemplateScalarModel;
-import org.apache.freemarker.core.model.impl._StaticObjectWrappers;
 import org.apache.freemarker.core.util.UndeclaredThrowableException;
 import org.jaxen.BaseXPath;
 import org.jaxen.Function;
@@ -92,8 +91,7 @@ class JaxenXPathSupport implements XPathSupport {
             }
             List result = xpath.selectNodes(context != null ? context : EMPTY_ARRAYLIST);
             if (result.size() == 1) {
-                // [2.4] Use the proper object wrapper (argument in 2.4) 
-                return _StaticObjectWrappers.DEFAULT_OBJECT_WRAPPER.wrap(result.get(0));
+                return NodeQueryResultItemObjectWrapper.INSTANCE.wrap(result.get(0));
             }
             NodeListModel nlm = new NodeListModel(result, null);
             nlm.xpathSupport = this;

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/b4dfe5d2/src/main/java/org/apache/freemarker/dom/NodeListModel.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/freemarker/dom/NodeListModel.java b/src/main/java/org/apache/freemarker/dom/NodeListModel.java
index d119339..75b9e13 100644
--- a/src/main/java/org/apache/freemarker/dom/NodeListModel.java
+++ b/src/main/java/org/apache/freemarker/dom/NodeListModel.java
@@ -25,7 +25,6 @@ import java.util.List;
 import org.apache.freemarker.core.Configuration;
 import org.apache.freemarker.core.Environment;
 import org.apache.freemarker.core._UnexpectedTypeErrorExplainerTemplateModel;
-import org.apache.freemarker.core.model.ObjectWrapper;
 import org.apache.freemarker.core.model.TemplateBooleanModel;
 import org.apache.freemarker.core.model.TemplateDateModel;
 import org.apache.freemarker.core.model.TemplateHashModel;
@@ -56,27 +55,17 @@ class NodeListModel extends SimpleSequence implements TemplateHashModel, _Unexpe
     NodeModel contextNode;
     XPathSupport xpathSupport;
     
-    private static ObjectWrapper nodeWrapper = new ObjectWrapper() {
-        @Override
-        public TemplateModel wrap(Object obj) {
-            if (obj instanceof NodeModel) {
-                return (NodeModel) obj;
-            }
-            return NodeModel.wrap((Node) obj);
-        }
-    };
-    
     NodeListModel(Node contextNode) {
         this(NodeModel.wrap(contextNode));
     }
     
     NodeListModel(NodeModel contextNode) {
-        super(nodeWrapper);
+        super(NodeQueryResultItemObjectWrapper.INSTANCE);
         this.contextNode = contextNode;
     }
     
     NodeListModel(NodeList nodeList, NodeModel contextNode) {
-        super(nodeWrapper);
+        super(NodeQueryResultItemObjectWrapper.INSTANCE);
         for (int i = 0; i < nodeList.getLength(); i++) {
             list.add(nodeList.item(i));
         }
@@ -84,7 +73,7 @@ class NodeListModel extends SimpleSequence implements TemplateHashModel, _Unexpe
     }
     
     NodeListModel(NamedNodeMap nodeList, NodeModel contextNode) {
-        super(nodeWrapper);
+        super(NodeQueryResultItemObjectWrapper.INSTANCE);
         for (int i = 0; i < nodeList.getLength(); i++) {
             list.add(nodeList.item(i));
         }
@@ -92,7 +81,7 @@ class NodeListModel extends SimpleSequence implements TemplateHashModel, _Unexpe
     }
     
     NodeListModel(List list, NodeModel contextNode) {
-        super(list, nodeWrapper);
+        super(list, NodeQueryResultItemObjectWrapper.INSTANCE);
         this.contextNode = contextNode;
     }
     
@@ -227,5 +216,5 @@ class NodeListModel extends SimpleSequence implements TemplateHashModel, _Unexpe
                     : "multiple matches."
                 };
     }
-    
+
 }
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/b4dfe5d2/src/main/java/org/apache/freemarker/dom/NodeQueryResultItemObjectWrapper.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/freemarker/dom/NodeQueryResultItemObjectWrapper.java b/src/main/java/org/apache/freemarker/dom/NodeQueryResultItemObjectWrapper.java
new file mode 100644
index 0000000..e84e977
--- /dev/null
+++ b/src/main/java/org/apache/freemarker/dom/NodeQueryResultItemObjectWrapper.java
@@ -0,0 +1,92 @@
+/*
+ * 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.dom;
+
+import org.apache.freemarker.core.Environment;
+import org.apache.freemarker.core.model.ObjectWrapper;
+import org.apache.freemarker.core.model.TemplateBooleanModel;
+import org.apache.freemarker.core.model.TemplateDateModel;
+import org.apache.freemarker.core.model.TemplateModel;
+import org.apache.freemarker.core.model.TemplateModelAdapter;
+import org.apache.freemarker.core.model.TemplateModelException;
+import org.apache.freemarker.core.model.WrappingTemplateModel;
+import org.apache.freemarker.core.model.impl.SimpleDate;
+import org.apache.freemarker.core.model.impl.SimpleNumber;
+import org.apache.freemarker.core.model.impl.SimpleScalar;
+import org.w3c.dom.Node;
+
+/**
+ * Used for wrapping query result items (such as XPath query result items). Because {@link NodeModel} and such aren't
+ * {@link WrappingTemplateModel}-s, we can't use the actual {@link ObjectWrapper} from the {@link Environment}, also,
+ * even if we could, it might not be the right thing to do, because that  {@link ObjectWrapper} might not even wrap
+ * {@link Node}-s via {@link NodeModel}.
+ */
+class NodeQueryResultItemObjectWrapper implements ObjectWrapper {
+
+    static final NodeQueryResultItemObjectWrapper INSTANCE = new NodeQueryResultItemObjectWrapper();
+
+    private NodeQueryResultItemObjectWrapper() {
+        //
+    }
+
+    @Override
+    public TemplateModel wrap(Object obj) throws TemplateModelException {
+        if (obj instanceof NodeModel) {
+            return (NodeModel) obj;
+        }
+        if (obj instanceof Node) {
+            return NodeModel.wrap((Node) obj);
+        } else {
+            if (obj == null) {
+                return null;
+            }
+            if (obj instanceof TemplateModel) {
+                return (TemplateModel) obj;
+            }
+            if (obj instanceof TemplateModelAdapter) {
+                return ((TemplateModelAdapter) obj).getTemplateModel();
+            }
+
+            if (obj instanceof String) {
+                return new SimpleScalar((String) obj);
+            }
+            if (obj instanceof Number) {
+                return new SimpleNumber((Number) obj);
+            }
+            if (obj instanceof Boolean) {
+                return obj.equals(Boolean.TRUE) ? TemplateBooleanModel.TRUE : TemplateBooleanModel.FALSE;
+            }
+            if (obj instanceof java.util.Date) {
+                if (obj instanceof java.sql.Date) {
+                    return new SimpleDate((java.sql.Date) obj);
+                }
+                if (obj instanceof java.sql.Time) {
+                    return new SimpleDate((java.sql.Time) obj);
+                }
+                if (obj instanceof java.sql.Timestamp) {
+                    return new SimpleDate((java.sql.Timestamp) obj);
+                }
+                return new SimpleDate((java.util.Date) obj, TemplateDateModel.UNKNOWN);
+            }
+            throw new TemplateModelException("Don't know how to wrap a W3C DOM query result item of this type: "
+                    + obj.getClass().getName());
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/b4dfe5d2/src/main/java/org/apache/freemarker/servlet/FreemarkerServlet.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/freemarker/servlet/FreemarkerServlet.java b/src/main/java/org/apache/freemarker/servlet/FreemarkerServlet.java
index 9dbc701..95b139e 100644
--- a/src/main/java/org/apache/freemarker/servlet/FreemarkerServlet.java
+++ b/src/main/java/org/apache/freemarker/servlet/FreemarkerServlet.java
@@ -49,11 +49,14 @@ import org.apache.freemarker.core.Template;
 import org.apache.freemarker.core.TemplateException;
 import org.apache.freemarker.core.TemplateExceptionHandler;
 import org.apache.freemarker.core.TemplateNotFoundException;
+import org.apache.freemarker.core.Version;
 import org.apache.freemarker.core._CoreLogs;
 import org.apache.freemarker.core.model.ObjectWrapper;
+import org.apache.freemarker.core.model.ObjectWrapperAndUnwrapper;
 import org.apache.freemarker.core.model.TemplateModel;
 import org.apache.freemarker.core.model.TemplateModelException;
-import org.apache.freemarker.core.model.impl._StaticObjectWrappers;
+import org.apache.freemarker.core.model.impl.RestrictedObjectWrapper;
+import org.apache.freemarker.core.model.impl.SimpleHash;
 import org.apache.freemarker.core.outputformat.OutputFormat;
 import org.apache.freemarker.core.outputformat.impl.UndefinedOutputFormat;
 import org.apache.freemarker.core.templateresolver.TemplateLoader;
@@ -521,7 +524,7 @@ public class FreemarkerServlet extends HttpServlet {
     @SuppressFBWarnings(value="SE_BAD_FIELD", justification="Not investing into making this Servlet serializable")
     private Configuration config;
     @SuppressFBWarnings(value="SE_BAD_FIELD", justification="Not investing into making this Servlet serializable")
-    private ObjectWrapper wrapper;
+    private ObjectWrapperAndUnwrapper wrapper;
     private ContentType contentType;
     private OverrideResponseContentType overrideResponseContentType = initParamValueToEnum(
             getDefaultOverrideResponseContentType(), OverrideResponseContentType.values());
@@ -1004,7 +1007,7 @@ public class FreemarkerServlet extends HttpServlet {
         return config.getLocale();
     }
 
-    protected TemplateModel createModel(ObjectWrapper objectWrapper,
+    protected TemplateModel createModel(ObjectWrapperAndUnwrapper objectWrapper,
                                         ServletContext servletContext,
                                         final HttpServletRequest request,
                                         final HttpServletResponse response) throws TemplateModelException {
@@ -1289,7 +1292,7 @@ public class FreemarkerServlet extends HttpServlet {
      * @return The {@link ObjectWrapper} that will be used for adapting request, session, and servlet context attributes
      *         to {@link TemplateModel}-s, and also as the object wrapper setting of {@link Configuration}.
      */
-    protected ObjectWrapper createObjectWrapper() {
+    protected ObjectWrapperAndUnwrapper createObjectWrapper() {
         String wrapper = getServletConfig().getInitParameter(DEPR_INITPARAM_OBJECT_WRAPPER);
         if (wrapper != null) { // BC
             if (getInitParameter(Configurable.OBJECT_WRAPPER_KEY) != null) {
@@ -1298,7 +1301,7 @@ public class FreemarkerServlet extends HttpServlet {
                         + DEPR_INITPARAM_OBJECT_WRAPPER);
             }
             if (DEPR_INITPARAM_WRAPPER_RESTRICTED.equals(wrapper)) {
-                return _StaticObjectWrappers.RESTRICTED_OBJECT_WRAPPER;
+                return new RestrictedObjectWrapper(Configuration.VERSION_3_0_0);
             }
             return createDefaultObjectWrapper();
         } else {
@@ -1307,7 +1310,7 @@ public class FreemarkerServlet extends HttpServlet {
                 if (!config.isObjectWrapperExplicitlySet()) {
                     return createDefaultObjectWrapper();
                 } else {
-                    return config.getObjectWrapper();
+                    return asObjectWrapperAndUnwrapper(config.getObjectWrapper());
                 }
             } else {
                 try {
@@ -1315,11 +1318,20 @@ public class FreemarkerServlet extends HttpServlet {
                 } catch (ConfigurationException e) {
                     throw new RuntimeException("Failed to set " + Configurable.OBJECT_WRAPPER_KEY, e);
                 }
-                return config.getObjectWrapper();
+                return asObjectWrapperAndUnwrapper(config.getObjectWrapper());
             }
         }
     }
 
+    private ObjectWrapperAndUnwrapper asObjectWrapperAndUnwrapper(ObjectWrapper objectWrapper) {
+        if (!(objectWrapper instanceof ObjectWrapperAndUnwrapper)) {
+            throw new RuntimeException(FreemarkerServlet.class.getSimpleName() + " requires an ObjectWrapper that " +
+                    "implements " + ObjectWrapperAndUnwrapper.class.getName() + ", but this class doesn't do that: "
+                    + objectWrapper.getClass().getName());
+        }
+        return (ObjectWrapperAndUnwrapper) objectWrapper;
+    }
+
     /**
      * Override this to specify what the default {@link ObjectWrapper} will be when the
      * {@code object_wrapper} Servlet init-param wasn't specified. Note that this is called by
@@ -1328,13 +1340,13 @@ public class FreemarkerServlet extends HttpServlet {
      * won't be called, since then that has already specified the default.
      * 
      * <p>
-     * The default implementation calls {@link Configuration#getDefaultObjectWrapper(org.apache.freemarker.core.Version)}. You
+     * The default implementation calls {@link Configuration#getDefaultObjectWrapper(Version)}. You
      * should also pass in the version paramter when creating an {@link ObjectWrapper} that supports that. You can get
      * the version by calling {@link #getConfiguration()} and then {@link Configuration#getIncompatibleImprovements()}.
      * 
      * @since 2.3.22
      */
-    protected ObjectWrapper createDefaultObjectWrapper() {
+    protected ObjectWrapperAndUnwrapper createDefaultObjectWrapper() {
         return Configuration.getDefaultObjectWrapper(config.getIncompatibleImprovements());
     }
     
@@ -1358,7 +1370,7 @@ public class FreemarkerServlet extends HttpServlet {
     }
     
     protected HttpRequestParametersHashModel createRequestParametersHashModel(HttpServletRequest request) {
-        return new HttpRequestParametersHashModel(request);
+        return new HttpRequestParametersHashModel(request, getObjectWrapper());
     }
 
     /**
@@ -1407,13 +1419,11 @@ public class FreemarkerServlet extends HttpServlet {
      *            The template that will get executed
      * @param model
      *            The data model that will be passed to the template. By default this will be an
-     *            {@link AllHttpScopesHashModel} (which is a {@link org.apache.freemarker.core.model.impl.SimpleHash} subclass). Thus, you
-     *            can add new variables to the data-model with the
-     *            {@link org.apache.freemarker.core.model.impl.SimpleHash#put(String, Object)} subclass) method. However, to adjust the
-     *            data-model, overriding
-     *            {@link #createModel(ObjectWrapper, ServletContext, HttpServletRequest, HttpServletResponse)} is
-     *            probably a more appropriate place.
-     * 
+     *            {@link AllHttpScopesHashModel} (which is a {@link SimpleHash} subclass). Thus, you can add new
+     *            variables to the data-model with the {@link SimpleHash#put(String, Object)} subclass) method. However,
+     *            to adjust the data-model, overriding {@link #createModel(ObjectWrapperAndUnwrapper, ServletContext,
+     *            HttpServletRequest, HttpServletResponse)} is probably a more appropriate place.
+     *
      * @return true to process the template, false to suppress template processing.
      */
     protected boolean preTemplateProcess(

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/b4dfe5d2/src/main/java/org/apache/freemarker/servlet/HttpRequestHashModel.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/freemarker/servlet/HttpRequestHashModel.java b/src/main/java/org/apache/freemarker/servlet/HttpRequestHashModel.java
index 5124e01..a14bbc2 100644
--- a/src/main/java/org/apache/freemarker/servlet/HttpRequestHashModel.java
+++ b/src/main/java/org/apache/freemarker/servlet/HttpRequestHashModel.java
@@ -39,21 +39,18 @@ import org.apache.freemarker.core.model.impl.SimpleCollection;
 public final class HttpRequestHashModel implements TemplateHashModelEx {
     private final HttpServletRequest request;
     private final HttpServletResponse response;
-    private final ObjectWrapper wrapper;
+    private final ObjectWrapperAndUnwrapper wrapper;
 
     /**
      * @param wrapper
      *            Should be an {@link ObjectWrapperAndUnwrapper}, or else some features might won't work properly. (It's
      *            declared as {@link ObjectWrapper} only for backward compatibility.)
      */
-    public HttpRequestHashModel(
-        HttpServletRequest request, ObjectWrapper wrapper) {
+    public HttpRequestHashModel(HttpServletRequest request, ObjectWrapperAndUnwrapper wrapper) {
         this(request, null, wrapper);
     }
 
-    public HttpRequestHashModel(
-        HttpServletRequest request, HttpServletResponse response, 
-        ObjectWrapper wrapper) {
+    public HttpRequestHashModel(HttpServletRequest request, HttpServletResponse response, ObjectWrapperAndUnwrapper wrapper) {
         this.request = request;
         this.response = response;
         this.wrapper = wrapper;
@@ -85,7 +82,7 @@ public final class HttpRequestHashModel implements TemplateHashModelEx {
         for (Enumeration enumeration = request.getAttributeNames(); enumeration.hasMoreElements(); ) {
             keys.add(enumeration.nextElement());
         }
-        return new SimpleCollection(keys.iterator());
+        return new SimpleCollection(keys.iterator(), wrapper);
     }
     
     @Override
@@ -105,7 +102,7 @@ public final class HttpRequestHashModel implements TemplateHashModelEx {
         return response;
     }
     
-    public ObjectWrapper getObjectWrapper() {
+    public ObjectWrapperAndUnwrapper getObjectWrapper() {
         return wrapper;
     }
 }

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/b4dfe5d2/src/main/java/org/apache/freemarker/servlet/HttpRequestParametersHashModel.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/freemarker/servlet/HttpRequestParametersHashModel.java b/src/main/java/org/apache/freemarker/servlet/HttpRequestParametersHashModel.java
index b1aca6f..33e5817 100644
--- a/src/main/java/org/apache/freemarker/servlet/HttpRequestParametersHashModel.java
+++ b/src/main/java/org/apache/freemarker/servlet/HttpRequestParametersHashModel.java
@@ -26,6 +26,7 @@ import java.util.List;
 
 import javax.servlet.http.HttpServletRequest;
 
+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;
@@ -36,14 +37,14 @@ import org.apache.freemarker.core.model.impl.SimpleScalar;
  * TemplateHashModel wrapper for a HttpServletRequest parameters.
  */
 
-public class HttpRequestParametersHashModel
-    implements
-    TemplateHashModelEx {
+public class HttpRequestParametersHashModel implements TemplateHashModelEx {
     private final HttpServletRequest request;
+    private final ObjectWrapper objectWrapper;
     private List keys;
         
-    public HttpRequestParametersHashModel(HttpServletRequest request) {
+    public HttpRequestParametersHashModel(HttpServletRequest request, ObjectWrapper objectWrapper) {
         this.request = request;
+        this.objectWrapper = objectWrapper;
     }
 
     @Override
@@ -64,7 +65,7 @@ public class HttpRequestParametersHashModel
     
     @Override
     public TemplateCollectionModel keys() {
-        return new SimpleCollection(getKeys().iterator());
+        return new SimpleCollection(getKeys().iterator(), objectWrapper);
     }
     
     @Override
@@ -78,13 +79,13 @@ public class HttpRequestParametersHashModel
                 }
                 @Override
                 public Object next() {
-                    return request.getParameter((String) iter.next()); 
+                    return request.getParameter((String) iter.next());
                 }
                 @Override
                 public void remove() {
                     throw new UnsupportedOperationException();
                 }
-            });
+            }, objectWrapper);
     }
 
     protected String transcode(String string) {

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/b4dfe5d2/src/main/java/org/apache/freemarker/servlet/jsp/FreeMarkerPageContext.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/freemarker/servlet/jsp/FreeMarkerPageContext.java b/src/main/java/org/apache/freemarker/servlet/jsp/FreeMarkerPageContext.java
index 2b8b8eb..7d35203 100644
--- a/src/main/java/org/apache/freemarker/servlet/jsp/FreeMarkerPageContext.java
+++ b/src/main/java/org/apache/freemarker/servlet/jsp/FreeMarkerPageContext.java
@@ -46,18 +46,13 @@ import javax.servlet.jsp.PageContext;
 import javax.servlet.jsp.tagext.BodyContent;
 
 import org.apache.freemarker.core.Environment;
-import org.apache.freemarker.core.model.AdapterTemplateModel;
 import org.apache.freemarker.core.model.ObjectWrapper;
 import org.apache.freemarker.core.model.ObjectWrapperAndUnwrapper;
-import org.apache.freemarker.core.model.TemplateBooleanModel;
-import org.apache.freemarker.core.model.TemplateDateModel;
 import org.apache.freemarker.core.model.TemplateHashModelEx;
 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.TemplateNumberModel;
 import org.apache.freemarker.core.model.TemplateScalarModel;
-import org.apache.freemarker.core.model.WrapperTemplateModel;
 import org.apache.freemarker.core.util.UndeclaredThrowableException;
 import org.apache.freemarker.servlet.FreemarkerServlet;
 import org.apache.freemarker.servlet.HttpRequestHashModel;
@@ -75,8 +70,7 @@ abstract class FreeMarkerPageContext extends PageContext implements TemplateMode
     private HttpSession session;
     private final HttpServletRequest request;
     private final HttpServletResponse response;
-    private final ObjectWrapper wrapper;
-    private final ObjectWrapperAndUnwrapper unwrapper;
+    private final ObjectWrapperAndUnwrapper wrapper;
     private JspWriter jspOut;
     
     protected FreeMarkerPageContext() throws TemplateModelException {
@@ -109,9 +103,8 @@ abstract class FreeMarkerPageContext extends PageContext implements TemplateMode
             request = reqHash.getRequest();
             session = request.getSession(false);
             response = reqHash.getResponse();
-            wrapper = reqHash.getObjectWrapper();
-            unwrapper = wrapper instanceof ObjectWrapperAndUnwrapper
-                    ? (ObjectWrapperAndUnwrapper) wrapper : null;
+            ObjectWrapperAndUnwrapper ow = reqHash.getObjectWrapper();
+            wrapper = (ObjectWrapperAndUnwrapper) ow;
         } else {
             throw new  TemplateModelException("Could not find an instance of " + 
                     HttpRequestHashModel.class.getName() + 
@@ -131,7 +124,7 @@ abstract class FreeMarkerPageContext extends PageContext implements TemplateMode
         setAttribute(APPLICATION, servlet.getServletContext());
     }    
             
-    ObjectWrapper getObjectWrapper() {
+    ObjectWrapperAndUnwrapper getObjectWrapper() {
         return wrapper;
     }
     
@@ -191,32 +184,9 @@ abstract class FreeMarkerPageContext extends PageContext implements TemplateMode
         switch (scope) {
             case PAGE_SCOPE: {
                 try {
-                    final TemplateModel tm = environment.getGlobalNamespace().get(name);
-                    if (unwrapper != null) {
-                        return unwrapper.unwrap(tm);
-                    } else { // [FM3] Such unwrapping logic rather belongs to some core util
-                        if (tm instanceof AdapterTemplateModel) {
-                            return ((AdapterTemplateModel) tm).getAdaptedObject(OBJECT_CLASS);
-                        }
-                        if (tm instanceof WrapperTemplateModel) {
-                            return ((WrapperTemplateModel) tm).getWrappedObject();
-                        }
-                        if (tm instanceof TemplateScalarModel) {
-                            return ((TemplateScalarModel) tm).getAsString();
-                        }
-                        if (tm instanceof TemplateNumberModel) {
-                            return ((TemplateNumberModel) tm).getAsNumber();
-                        }
-                        if (tm instanceof TemplateBooleanModel) {
-                            return Boolean.valueOf(((TemplateBooleanModel) tm).getAsBoolean());
-                        }
-                        if (tm instanceof TemplateDateModel) {
-                            return ((TemplateDateModel) tm).getAsDate();
-                        }
-                        return tm;
-                    }
+                    return wrapper.unwrap(environment.getGlobalNamespace().get(name));
                 } catch (TemplateModelException e) {
-                    throw new UndeclaredThrowableException("Failed to unwrapp FTL global variable", e);
+                    throw new UndeclaredThrowableException("Failed to unwrap FTL global variable", e);
                 }
             }
             case REQUEST_SCOPE: {

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/b4dfe5d2/src/main/java/org/apache/freemarker/servlet/jsp/JspContextModel.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/freemarker/servlet/jsp/JspContextModel.java b/src/main/java/org/apache/freemarker/servlet/jsp/JspContextModel.java
deleted file mode 100644
index 1d02be6..0000000
--- a/src/main/java/org/apache/freemarker/servlet/jsp/JspContextModel.java
+++ /dev/null
@@ -1,56 +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.servlet.jsp;
-
-import javax.servlet.jsp.PageContext;
-
-import org.apache.freemarker.core.model.TemplateHashModel;
-import org.apache.freemarker.core.model.TemplateModel;
-import org.apache.freemarker.core.model.TemplateModelException;
-import org.apache.freemarker.core.model.impl._StaticObjectWrappers;
-
-class JspContextModel
-implements
-    TemplateHashModel {
-    public static final int ANY_SCOPE = -1;
-    public static final int PAGE_SCOPE = PageContext.PAGE_SCOPE;
-    public static final int REQUEST_SCOPE = PageContext.REQUEST_SCOPE;
-    public static final int SESSION_SCOPE = PageContext.SESSION_SCOPE;
-    public static final int APPLICATION_SCOPE = PageContext.APPLICATION_SCOPE;
-
-    private final PageContext pageContext;
-    private final int scope;
-
-    public JspContextModel(PageContext pageContext, int scope) {
-        this.pageContext = pageContext;
-        this.scope = scope;
-    }
-
-    @Override
-    public TemplateModel get(String key) throws TemplateModelException {
-        Object bean = scope == ANY_SCOPE ? pageContext.findAttribute(key) : pageContext.getAttribute(key, scope);
-        return _StaticObjectWrappers.DEFAULT_OBJECT_WRAPPER.wrap(bean);
-    }
-
-    @Override
-    public boolean isEmpty() {
-        return false;
-    }
-}

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/b4dfe5d2/src/main/java/org/apache/freemarker/servlet/jsp/JspTagModelBase.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/freemarker/servlet/jsp/JspTagModelBase.java b/src/main/java/org/apache/freemarker/servlet/jsp/JspTagModelBase.java
index 7f297dd..8c6fd3a 100644
--- a/src/main/java/org/apache/freemarker/servlet/jsp/JspTagModelBase.java
+++ b/src/main/java/org/apache/freemarker/servlet/jsp/JspTagModelBase.java
@@ -35,11 +35,9 @@ import org.apache.freemarker.core._DelayedJQuote;
 import org.apache.freemarker.core._DelayedShortClassName;
 import org.apache.freemarker.core._ErrorDescriptionBuilder;
 import org.apache.freemarker.core._TemplateModelException;
-import org.apache.freemarker.core.model.ObjectWrapper;
 import org.apache.freemarker.core.model.ObjectWrapperAndUnwrapper;
 import org.apache.freemarker.core.model.TemplateModel;
 import org.apache.freemarker.core.model.TemplateModelException;
-import org.apache.freemarker.core.model.impl._StaticObjectWrappers;
 import org.apache.freemarker.core.model.impl.DefaultObjectWrapper;
 import org.apache.freemarker.core.util._StringUtil;
 import org.apache.freemarker.servlet.jsp.SimpleTagDirectiveModel.TemplateExceptionWrapperJspException;
@@ -77,18 +75,15 @@ class JspTagModelBase {
         return tagClass.newInstance();
     }
     
-    void setupTag(Object tag, Map args, ObjectWrapper wrapper)
+    void setupTag(Object tag, Map args, ObjectWrapperAndUnwrapper wrapper)
     throws TemplateModelException, 
         InvocationTargetException, 
         IllegalAccessException {
         if (args != null && !args.isEmpty()) {
-            ObjectWrapperAndUnwrapper unwrapper = 
-                    wrapper instanceof ObjectWrapperAndUnwrapper ? (ObjectWrapperAndUnwrapper) wrapper
-                            : _StaticObjectWrappers.DEFAULT_OBJECT_WRAPPER;  // [2.4] Throw exception in this case
             final Object[] argArray = new Object[1];
             for (Iterator iter = args.entrySet().iterator(); iter.hasNext(); ) {
                 final Map.Entry entry = (Map.Entry) iter.next();
-                final Object arg = unwrapper.unwrap((TemplateModel) entry.getValue());
+                final Object arg = wrapper.unwrap((TemplateModel) entry.getValue());
                 argArray[0] = arg;
                 final Object paramName = entry.getKey();
                 Method setterMethod = (Method) propertySetters.get(paramName);

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/b4dfe5d2/src/manual/en_US/FM3-CHANGE-LOG.txt
----------------------------------------------------------------------
diff --git a/src/manual/en_US/FM3-CHANGE-LOG.txt b/src/manual/en_US/FM3-CHANGE-LOG.txt
index bb16fb8..3c66826 100644
--- a/src/manual/en_US/FM3-CHANGE-LOG.txt
+++ b/src/manual/en_US/FM3-CHANGE-LOG.txt
@@ -126,4 +126,16 @@ the FreeMarer 3 changelog here:
   obj.m(1), you could write obj.m[1]. This strange feature has led to some tricky cases, while almost nobody has
   utilized it.
 - SimpleObjectWrapper was renamed to RestrictedObjectWrapper, also the "simple" setting value was rename to
-  "restricted".
\ No newline at end of file
+  "restricted".
+- Removed the global static final ObjectWrapper-s. It had a "few" consequences:
+  - Standard TemplateModel implementations that can have an ObjectWrapper contrucor parameter don't allow null there anymore.
+    Also, any constructor overloads where you cold omit the ObjectWrapper were removed (these were deprecated in FM2 too).
+    In FM2, such overloads has used the global static default DefaltObjectWrapper, but that was removed.
+  - If the ObjectWrapper is not a DefaultObjectWrapper (or a subclass of it), `className?new(args)` will only accept 0 arguments.
+    (Earlier we have fallen back to using the global static default DefaultObjectWrapper instance to handle argument unwrapping
+    and overloaded constructors.) Note that ?new is only used to instantiate TemplateModel-s, typically, tempalte language
+    functions/directives implemented in Java, and so they hardly ever has an argument.
+  - FreemarkerServlet now requires that the ObjectWrapper it uses implements ObjectWrapperAndUnwrapper. (Thus, the return type
+    of FreemarerServlet.createDefaultObjectWrapper() has changed to ObjectWrapperAndUnwrapper.) The unwrapping functionality is
+    required when calling JSP custom tags/functions, and in FreeMarker 2 this was worked around with using the
+    global static default DefaultObjectWrapper when the ObjectWrapper wasn't an ObjectWrapperAndUnwrapper.

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/b4dfe5d2/src/test/java/org/apache/freemarker/core/ConfigurationTest.java
----------------------------------------------------------------------
diff --git a/src/test/java/org/apache/freemarker/core/ConfigurationTest.java b/src/test/java/org/apache/freemarker/core/ConfigurationTest.java
index 6889f8c..c1650ec 100644
--- a/src/test/java/org/apache/freemarker/core/ConfigurationTest.java
+++ b/src/test/java/org/apache/freemarker/core/ConfigurationTest.java
@@ -41,8 +41,8 @@ import org.apache.freemarker.core.model.TemplateModelException;
 import org.apache.freemarker.core.model.TemplateScalarModel;
 import org.apache.freemarker.core.model.impl.DefaultObjectWrapper;
 import org.apache.freemarker.core.model.impl.DefaultObjectWrapperBuilder;
+import org.apache.freemarker.core.model.impl.RestrictedObjectWrapper;
 import org.apache.freemarker.core.model.impl.SimpleScalar;
-import org.apache.freemarker.core.model.impl._StaticObjectWrappers;
 import org.apache.freemarker.core.outputformat.MarkupOutputFormat;
 import org.apache.freemarker.core.outputformat.OutputFormat;
 import org.apache.freemarker.core.outputformat.UnregisteredOutputFormatException;
@@ -107,16 +107,17 @@ public class ConfigurationTest extends TestCase {
         }
         
         assertFalse(cfg.isObjectWrapperExplicitlySet());
-        assertSame(_StaticObjectWrappers.DEFAULT_OBJECT_WRAPPER, cfg.getObjectWrapper());
+        assertSame(Configuration.getDefaultObjectWrapper(Configuration.VERSION_3_0_0), cfg.getObjectWrapper());
         //
-        cfg.setObjectWrapper(_StaticObjectWrappers.RESTRICTED_OBJECT_WRAPPER);
+        RestrictedObjectWrapper ow = new RestrictedObjectWrapper(Configuration.VERSION_3_0_0);
+        cfg.setObjectWrapper(ow);
         assertTrue(cfg.isObjectWrapperExplicitlySet());
-        assertSame(_StaticObjectWrappers.RESTRICTED_OBJECT_WRAPPER, cfg.getObjectWrapper());
+        assertSame(ow, cfg.getObjectWrapper());
         //
         for (int i = 0; i < 2; i++) {
             cfg.unsetObjectWrapper();
             assertFalse(cfg.isObjectWrapperExplicitlySet());
-            assertSame(_StaticObjectWrappers.DEFAULT_OBJECT_WRAPPER, cfg.getObjectWrapper());
+            assertSame(Configuration.getDefaultObjectWrapper(Configuration.VERSION_3_0_0), cfg.getObjectWrapper());
         }
         
         assertFalse(cfg.isTemplateExceptionHandlerExplicitlySet());
@@ -200,10 +201,6 @@ public class ConfigurationTest extends TestCase {
         }
     }
     
-    private void assertUsesLegacyObjectWrapper(Configuration cfg) {
-        assertSame(_StaticObjectWrappers.DEFAULT_OBJECT_WRAPPER, cfg.getObjectWrapper());
-    }
-
     private void assertUsesNewObjectWrapper(Configuration cfg) {
         assertEquals(
                 Configuration.VERSION_3_0_0,
@@ -641,14 +638,14 @@ public class ConfigurationTest extends TestCase {
         
         {
             cfg.setSetting(Configurable.OBJECT_WRAPPER_KEY, "defAult");
-            assertSame(_StaticObjectWrappers.DEFAULT_OBJECT_WRAPPER, cfg.getObjectWrapper());
+            assertSame(Configuration.getDefaultObjectWrapper(Configuration.VERSION_3_0_0), cfg.getObjectWrapper());
             DefaultObjectWrapper dow = (DefaultObjectWrapper) cfg.getObjectWrapper();
             assertEquals(Configuration.VERSION_3_0_0, dow.getIncompatibleImprovements());
         }
         
         {
             cfg.setSetting(Configurable.OBJECT_WRAPPER_KEY, "restricted");
-            assertSame(_StaticObjectWrappers.RESTRICTED_OBJECT_WRAPPER, cfg.getObjectWrapper());
+            assertThat(cfg.getObjectWrapper(), instanceOf(RestrictedObjectWrapper.class));
         }
     }
     

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/b4dfe5d2/src/test/java/org/apache/freemarker/core/NewBiObjectWrapperRestrictionTest.java
----------------------------------------------------------------------
diff --git a/src/test/java/org/apache/freemarker/core/NewBiObjectWrapperRestrictionTest.java b/src/test/java/org/apache/freemarker/core/NewBiObjectWrapperRestrictionTest.java
new file mode 100644
index 0000000..40dfe13
--- /dev/null
+++ b/src/test/java/org/apache/freemarker/core/NewBiObjectWrapperRestrictionTest.java
@@ -0,0 +1,51 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.freemarker.core;
+
+import java.io.IOException;
+
+import org.apache.freemarker.test.TemplateTest;
+import org.apache.freemarker.test.util.FullyCustomObjectWrapper;
+import org.junit.Test;
+
+public class NewBiObjectWrapperRestrictionTest extends TemplateTest {
+
+    @Override
+    protected Configuration createConfiguration() throws Exception {
+        Configuration cfg = super.createConfiguration();
+        cfg.setObjectWrapper(new FullyCustomObjectWrapper());
+        return cfg;
+    }
+
+    @Test
+    public void testPositive() throws IOException, TemplateException {
+        assertOutput(
+                "${'org.apache.freemarker.test.templatesuite.models.NewTestModel'?new()}",
+                "default constructor");
+    }
+
+    @Test
+    public void testNegative() {
+        assertErrorContains(
+                "${'org.apache.freemarker.test.templatesuite.models.NewTestModel'?new('s')}",
+                "only supports 0 argument");
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/b4dfe5d2/src/test/java/org/apache/freemarker/core/model/impl/DefaultObjectWrapperTest.java
----------------------------------------------------------------------
diff --git a/src/test/java/org/apache/freemarker/core/model/impl/DefaultObjectWrapperTest.java b/src/test/java/org/apache/freemarker/core/model/impl/DefaultObjectWrapperTest.java
index 2405a38..deefd32 100644
--- a/src/test/java/org/apache/freemarker/core/model/impl/DefaultObjectWrapperTest.java
+++ b/src/test/java/org/apache/freemarker/core/model/impl/DefaultObjectWrapperTest.java
@@ -491,18 +491,6 @@ public class DefaultObjectWrapperTest {
             assertFalse(coll.isEmpty());
             assertCollectionTMEquals(coll, "a", "b", "c");
 
-            assertTrue(coll.contains(OW.wrap("a")));
-            assertTrue(coll.contains(OW.wrap("b")));
-            assertTrue(coll.contains(OW.wrap("c")));
-            assertTrue(coll.contains(OW.wrap("c")));
-            assertFalse(coll.contains(OW.wrap("d")));
-            try {
-                assertFalse(coll.contains(OW.wrap(1)));
-                fail();
-            } catch (TemplateModelException e) {
-                assertThat(e.getMessage(), containsString("Integer"));
-            }
-
             assertRoundtrip(OW, set, DefaultNonListCollectionAdapter.class, TreeSet.class, "[a, b, c]");
             
             assertSizeThroughAPIModel(3, coll);
@@ -523,13 +511,6 @@ public class DefaultObjectWrapperTest {
             assertTrue(obj1 != null && obj1.equals(list) || obj2 != null && obj2.equals(list));
             assertTrue(tm1 instanceof DefaultListAdapter || tm2 instanceof DefaultListAdapter);
 
-            List similarList = new ArrayList();
-            similarList.add("b");
-            assertTrue(coll.contains(OW.wrap(similarList)));
-            assertTrue(coll.contains(OW.wrap(null)));
-            assertFalse(coll.contains(OW.wrap("a")));
-            assertFalse(coll.contains(OW.wrap(1)));
-
             assertRoundtrip(OW, set, DefaultNonListCollectionAdapter.class, HashSet.class, "[" + obj1 + ", "
                     + obj2 + "]");
         }

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/b4dfe5d2/src/test/java/org/apache/freemarker/servlet/jsp/RealServletContainertTest.java
----------------------------------------------------------------------
diff --git a/src/test/java/org/apache/freemarker/servlet/jsp/RealServletContainertTest.java b/src/test/java/org/apache/freemarker/servlet/jsp/RealServletContainertTest.java
index d3c1a14..4f87e2c 100644
--- a/src/test/java/org/apache/freemarker/servlet/jsp/RealServletContainertTest.java
+++ b/src/test/java/org/apache/freemarker/servlet/jsp/RealServletContainertTest.java
@@ -40,6 +40,7 @@ import javax.servlet.http.HttpServletResponse;
 import org.apache.freemarker.core.Configuration;
 import org.apache.freemarker.core.TemplateExceptionHandler;
 import org.apache.freemarker.core.model.ObjectWrapper;
+import org.apache.freemarker.core.model.ObjectWrapperAndUnwrapper;
 import org.apache.freemarker.core.model.impl.DefaultObjectWrapper;
 import org.apache.freemarker.core.model.impl.DefaultObjectWrapperBuilder;
 import org.apache.freemarker.core.model.impl.RestrictedObjectWrapper;
@@ -451,7 +452,7 @@ public class RealServletContainertTest extends WebAppTestCase {
         }
 
         @Override
-        protected ObjectWrapper createDefaultObjectWrapper() {
+        protected ObjectWrapperAndUnwrapper createDefaultObjectWrapper() {
             DefaultObjectWrapperBuilder bwb = new DefaultObjectWrapperBuilder(Configuration.VERSION_3_0_0);
             bwb.setUseModelCache(true);
             assertEquals(Configuration.VERSION_3_0_0, bwb.getIncompatibleImprovements());

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/b4dfe5d2/src/test/java/org/apache/freemarker/test/templatesuite/TemplateTestCase.java
----------------------------------------------------------------------
diff --git a/src/test/java/org/apache/freemarker/test/templatesuite/TemplateTestCase.java b/src/test/java/org/apache/freemarker/test/templatesuite/TemplateTestCase.java
index 0511490..347ae06 100644
--- a/src/test/java/org/apache/freemarker/test/templatesuite/TemplateTestCase.java
+++ b/src/test/java/org/apache/freemarker/test/templatesuite/TemplateTestCase.java
@@ -61,7 +61,6 @@ import org.apache.freemarker.core.model.impl.ResourceBundleModel;
 import org.apache.freemarker.core.model.impl.SimpleCollection;
 import org.apache.freemarker.core.model.impl.SimpleDate;
 import org.apache.freemarker.core.model.impl.SimpleNumber;
-import org.apache.freemarker.core.model.impl._StaticObjectWrappers;
 import org.apache.freemarker.core.templateresolver.impl.FileTemplateLoader;
 import org.apache.freemarker.core.util._NullArgumentException;
 import org.apache.freemarker.core.util._NullWriter;
@@ -210,8 +209,7 @@ public class TemplateTestCase extends FileTestCase {
             dataModel.put("obj", new org.apache.freemarker.test.templatesuite.models.BeanTestClass());
             dataModel.put("resourceBundle",
                     new ResourceBundleModel(ResourceBundle.getBundle(
-                            "org.apache.freemarker.test.templatesuite.models.BeansTestResources"),
-                            _StaticObjectWrappers.DEFAULT_OBJECT_WRAPPER));
+                            "org.apache.freemarker.test.templatesuite.models.BeansTestResources"), dow));
             dataModel.put("date", new GregorianCalendar(1974, 10, 14).getTime());
             dataModel.put("statics", dow.getStaticModels());
             dataModel.put("enums", dow.getEnumModels());
@@ -222,8 +220,8 @@ public class TemplateTestCase extends FileTestCase {
             dataModel.put( "boolean4", TemplateBooleanModel.TRUE);
             dataModel.put( "boolean5", TemplateBooleanModel.FALSE);
             
-            dataModel.put( "list1", new BooleanList1() );
-            dataModel.put( "list2", new BooleanList2() );
+            dataModel.put( "list1", new BooleanList1(dow) );
+            dataModel.put( "list2", new BooleanList2(dow) );
     
             dataModel.put( "hash1", new BooleanHash1() );
             dataModel.put( "hash2", new BooleanHash2() );
@@ -291,8 +289,8 @@ public class TemplateTestCase extends FileTestCase {
         } else if (simpleTestName.startsWith("type-builtins")) {
             dataModel.put("testmethod", new TestMethod());
             dataModel.put("testnode", new TestNode());
-            dataModel.put("testcollection", new SimpleCollection(new ArrayList<>()));
-            dataModel.put("testcollectionEx", DefaultNonListCollectionAdapter.adapt(new HashSet<>(), null));
+            dataModel.put("testcollection", new SimpleCollection(new ArrayList<>(), dow));
+            dataModel.put("testcollectionEx", DefaultNonListCollectionAdapter.adapt(new HashSet<>(), dow));
             dataModel.put("bean", new TestBean());
         } else if (simpleTestName.equals("date-type-builtins")) {
             GregorianCalendar cal = new GregorianCalendar(2003, 4 - 1, 5, 6, 7, 8);
@@ -353,7 +351,7 @@ public class TemplateTestCase extends FileTestCase {
             listWithNull.add(null);
             dataModel.put("listWithNullsOnly", listWithNullsOnly);
             
-            dataModel.put("abcCollection", new SimpleCollection(abcSet));
+            dataModel.put("abcCollection", new SimpleCollection(abcSet, dow));
             
             Set<String> set = new HashSet<>();
             set.add("a");

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/b4dfe5d2/src/test/java/org/apache/freemarker/test/templatesuite/models/BooleanList1.java
----------------------------------------------------------------------
diff --git a/src/test/java/org/apache/freemarker/test/templatesuite/models/BooleanList1.java b/src/test/java/org/apache/freemarker/test/templatesuite/models/BooleanList1.java
index 7c33c1e..3e0a69c 100644
--- a/src/test/java/org/apache/freemarker/test/templatesuite/models/BooleanList1.java
+++ b/src/test/java/org/apache/freemarker/test/templatesuite/models/BooleanList1.java
@@ -19,10 +19,12 @@
 
 package org.apache.freemarker.test.templatesuite.models;
 
+import org.apache.freemarker.core.model.ObjectWrapper;
 import org.apache.freemarker.core.model.TemplateBooleanModel;
 import org.apache.freemarker.core.model.TemplateModel;
 import org.apache.freemarker.core.model.TemplateModelException;
 import org.apache.freemarker.core.model.TemplateSequenceModel;
+import org.apache.freemarker.core.model.impl.SimpleSequence;
 
 /**
  * Model for testing the impact of isEmpty() on template list models. Every
@@ -30,11 +32,11 @@ import org.apache.freemarker.core.model.TemplateSequenceModel;
  */
 public class BooleanList1 implements TemplateSequenceModel {
 
-    private LegacyList  cList;
+    private SimpleSequence cList;
 
     /** Creates new BooleanList1 */
-    public BooleanList1() {
-        cList = new LegacyList();
+    public BooleanList1(ObjectWrapper ow) {
+        cList = new SimpleSequence(ow);
         cList.add( "false" );
         cList.add( "0" );
         cList.add(TemplateBooleanModel.FALSE);
@@ -45,27 +47,6 @@ public class BooleanList1 implements TemplateSequenceModel {
     }
 
     /**
-     * @return true if there is a next element.
-     */
-    public boolean hasNext() {
-        return cList.hasNext();
-    }
-
-    /**
-     * @return the next element in the list.
-     */
-    public TemplateModel next() throws TemplateModelException {
-        return cList.next();
-    }
-
-    /**
-     * @return true if the cursor is at the beginning of the list.
-     */
-    public boolean isRewound() {
-        return cList.isRewound();
-    }
-
-    /**
      * @return the specified index in the list
      */
     @Override
@@ -73,13 +54,6 @@ public class BooleanList1 implements TemplateSequenceModel {
         return cList.get(i);
     }
 
-    /**
-     * Resets the cursor to the beginning of the list.
-     */
-    public void rewind() {
-        cList.rewind();
-    }
-
     @Override
     public int size() {
         return cList.size();

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/b4dfe5d2/src/test/java/org/apache/freemarker/test/templatesuite/models/BooleanList2.java
----------------------------------------------------------------------
diff --git a/src/test/java/org/apache/freemarker/test/templatesuite/models/BooleanList2.java b/src/test/java/org/apache/freemarker/test/templatesuite/models/BooleanList2.java
index 4f4f3cb..939fb5f 100644
--- a/src/test/java/org/apache/freemarker/test/templatesuite/models/BooleanList2.java
+++ b/src/test/java/org/apache/freemarker/test/templatesuite/models/BooleanList2.java
@@ -19,9 +19,11 @@
 
 package org.apache.freemarker.test.templatesuite.models;
 
+import org.apache.freemarker.core.model.ObjectWrapper;
 import org.apache.freemarker.core.model.TemplateModel;
 import org.apache.freemarker.core.model.TemplateModelException;
 import org.apache.freemarker.core.model.TemplateSequenceModel;
+import org.apache.freemarker.core.model.impl.SimpleSequence;
 
 /**
  * Model for testing list models. Every
@@ -29,11 +31,11 @@ import org.apache.freemarker.core.model.TemplateSequenceModel;
  */
 public class BooleanList2 implements TemplateSequenceModel {
 
-    private LegacyList  cList;
+    private SimpleSequence cList;
 
     /** Creates new BooleanList2 */
-    public BooleanList2() {
-        cList = new LegacyList();
+    public BooleanList2(ObjectWrapper ow) {
+        cList = new SimpleSequence(ow);
     }
 
     /**

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/b4dfe5d2/src/test/java/org/apache/freemarker/test/templatesuite/models/LegacyList.java
----------------------------------------------------------------------
diff --git a/src/test/java/org/apache/freemarker/test/templatesuite/models/LegacyList.java b/src/test/java/org/apache/freemarker/test/templatesuite/models/LegacyList.java
deleted file mode 100644
index b369cfc..0000000
--- a/src/test/java/org/apache/freemarker/test/templatesuite/models/LegacyList.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.test.templatesuite.models;
-
-import java.util.Iterator;
-
-import org.apache.freemarker.core.model.TemplateModel;
-import org.apache.freemarker.core.model.TemplateModelException;
-import org.apache.freemarker.core.model.impl.SimpleSequence;
-
-/**
- * A little bridge class that subclasses the new SimpleList
- * and still implements the deprecated TemplateListModel
- */
-public class LegacyList extends SimpleSequence {
-
-    private Iterator iterator;
-
-    /**
-     * Resets the cursor to the beginning of the list.
-     */
-    public synchronized void rewind() {
-        iterator = null;
-    }
-
-    /**
-     * @return true if the cursor is at the beginning of the list.
-     */
-    public synchronized boolean isRewound() {
-        return (iterator == null);
-    }
-
-    /**
-     * @return true if there is a next element.
-     */
-    public synchronized boolean hasNext() {
-        if (iterator == null) {
-            iterator = list.listIterator();
-        }
-        return iterator.hasNext();
-    }
-
-    /**
-     * @return the next element in the list.
-     */
-    public synchronized TemplateModel next() throws TemplateModelException {
-        if (iterator == null) {
-            iterator = list.listIterator();
-        }
-        if (iterator.hasNext()) {
-            return (TemplateModel) iterator.next();
-        } else {
-            throw new TemplateModelException("No more elements.");
-        }
-    }
-}

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/b4dfe5d2/src/test/java/org/apache/freemarker/test/templatesuite/models/MultiModel1.java
----------------------------------------------------------------------
diff --git a/src/test/java/org/apache/freemarker/test/templatesuite/models/MultiModel1.java b/src/test/java/org/apache/freemarker/test/templatesuite/models/MultiModel1.java
index 35eea63..3c1adfb 100644
--- a/src/test/java/org/apache/freemarker/test/templatesuite/models/MultiModel1.java
+++ b/src/test/java/org/apache/freemarker/test/templatesuite/models/MultiModel1.java
@@ -19,11 +19,14 @@
 
 package org.apache.freemarker.test.templatesuite.models;
 
+import org.apache.freemarker.core.Configuration;
+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.TemplateModelException;
 import org.apache.freemarker.core.model.TemplateScalarModel;
 import org.apache.freemarker.core.model.TemplateSequenceModel;
+import org.apache.freemarker.core.model.impl.DefaultObjectWrapperBuilder;
 import org.apache.freemarker.core.model.impl.SimpleHash;
 import org.apache.freemarker.core.model.impl.SimpleScalar;
 import org.apache.freemarker.core.model.impl.SimpleSequence;
@@ -34,11 +37,13 @@ import org.apache.freemarker.core.model.impl.SimpleSequence;
 public class MultiModel1 implements TemplateHashModel,
         TemplateSequenceModel, TemplateScalarModel {
 
+    private ObjectWrapper ow = new DefaultObjectWrapperBuilder(Configuration.VERSION_3_0_0).build();
+
     private TemplateModel m_cSubModel = new MultiModel2();
-    private TemplateModel m_cListHashModel1 = new MultiModel4();
-    private TemplateModel m_cListHashModel2 = new MultiModel5();
-    private TemplateSequenceModel m_cListModel = new SimpleSequence();
-    private TemplateHashModel m_cHashModel = new SimpleHash();
+    private TemplateModel m_cListHashModel1 = new MultiModel4(ow);
+    private TemplateModel m_cListHashModel2 = new MultiModel5(ow);
+    private TemplateSequenceModel m_cListModel = new SimpleSequence(ow);
+    private TemplateHashModel m_cHashModel = new SimpleHash(ow);
 
     /** Creates new MultiModel1 */
     public MultiModel1() {

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/b4dfe5d2/src/test/java/org/apache/freemarker/test/templatesuite/models/MultiModel4.java
----------------------------------------------------------------------
diff --git a/src/test/java/org/apache/freemarker/test/templatesuite/models/MultiModel4.java b/src/test/java/org/apache/freemarker/test/templatesuite/models/MultiModel4.java
index 60a4a29..bdd11fc 100644
--- a/src/test/java/org/apache/freemarker/test/templatesuite/models/MultiModel4.java
+++ b/src/test/java/org/apache/freemarker/test/templatesuite/models/MultiModel4.java
@@ -19,18 +19,24 @@
 
 package org.apache.freemarker.test.templatesuite.models;
 
+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.TemplateModelException;
 import org.apache.freemarker.core.model.TemplateSequenceModel;
 import org.apache.freemarker.core.model.impl.SimpleScalar;
+import org.apache.freemarker.core.model.impl.SimpleSequence;
 
 /**
  * Testcase to see how FreeMarker deals with multiple Template models.
  */
 public class MultiModel4 implements TemplateSequenceModel, TemplateHashModel {
 
-    private LegacyList m_cList = new LegacyList();
+    private final SimpleSequence m_cList;
+
+    public MultiModel4(ObjectWrapper ow) {
+        this.m_cList = new SimpleSequence(ow);
+    }
 
     /**
      * @return the specified index in the list

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/b4dfe5d2/src/test/java/org/apache/freemarker/test/templatesuite/models/MultiModel5.java
----------------------------------------------------------------------
diff --git a/src/test/java/org/apache/freemarker/test/templatesuite/models/MultiModel5.java b/src/test/java/org/apache/freemarker/test/templatesuite/models/MultiModel5.java
index d6b2b35..01f7a3e 100644
--- a/src/test/java/org/apache/freemarker/test/templatesuite/models/MultiModel5.java
+++ b/src/test/java/org/apache/freemarker/test/templatesuite/models/MultiModel5.java
@@ -19,21 +19,24 @@
 
 package org.apache.freemarker.test.templatesuite.models;
 
+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.TemplateModelException;
 import org.apache.freemarker.core.model.TemplateSequenceModel;
 import org.apache.freemarker.core.model.impl.SimpleScalar;
+import org.apache.freemarker.core.model.impl.SimpleSequence;
 
 /**
  * Testcase to see how FreeMarker deals with multiple Template models.
  */
 public class MultiModel5 implements TemplateSequenceModel, TemplateHashModel {
 
-    private LegacyList  m_cList = new LegacyList();
+    private final SimpleSequence m_cList;
 
     /** Creates new MultiModel5 */
-    public MultiModel5() {
+    public MultiModel5(ObjectWrapper ow) {
+        this.m_cList = new SimpleSequence(ow);
         m_cList.add( new SimpleScalar( "Dummy to make list non-empty" ));
     }
 

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/b4dfe5d2/src/test/java/org/apache/freemarker/test/templatesuite/models/TransformHashWrapper.java
----------------------------------------------------------------------
diff --git a/src/test/java/org/apache/freemarker/test/templatesuite/models/TransformHashWrapper.java b/src/test/java/org/apache/freemarker/test/templatesuite/models/TransformHashWrapper.java
index c4f9212..cfdbe12 100644
--- a/src/test/java/org/apache/freemarker/test/templatesuite/models/TransformHashWrapper.java
+++ b/src/test/java/org/apache/freemarker/test/templatesuite/models/TransformHashWrapper.java
@@ -19,10 +19,13 @@
 
 package org.apache.freemarker.test.templatesuite.models;
 
+import org.apache.freemarker.core.Configuration;
+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.TemplateModelException;
 import org.apache.freemarker.core.model.TemplateScalarModel;
+import org.apache.freemarker.core.model.impl.DefaultObjectWrapperBuilder;
 import org.apache.freemarker.core.model.impl.SimpleHash;
 import org.apache.freemarker.core.util.HtmlEscape;
 import org.apache.freemarker.core.util.StandardCompress;
@@ -33,7 +36,8 @@ import org.apache.freemarker.core.util.StandardCompress;
 public class TransformHashWrapper implements TemplateHashModel,
         TemplateScalarModel {
 
-    private SimpleHash m_cHashModel = new SimpleHash();
+    private ObjectWrapper ow = new DefaultObjectWrapperBuilder(Configuration.VERSION_3_0_0).build();
+    private SimpleHash m_cHashModel = new SimpleHash(ow);
 
     /** Creates new TransformHashWrapper */
     public TransformHashWrapper() {

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/b4dfe5d2/src/test/java/org/apache/freemarker/test/util/FullyCustomObjectWrapper.java
----------------------------------------------------------------------
diff --git a/src/test/java/org/apache/freemarker/test/util/FullyCustomObjectWrapper.java b/src/test/java/org/apache/freemarker/test/util/FullyCustomObjectWrapper.java
new file mode 100644
index 0000000..31a1110
--- /dev/null
+++ b/src/test/java/org/apache/freemarker/test/util/FullyCustomObjectWrapper.java
@@ -0,0 +1,91 @@
+/*
+ * 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.test.util;
+
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Map;
+
+import org.apache.freemarker.core.model.ObjectWrapper;
+import org.apache.freemarker.core.model.TemplateBooleanModel;
+import org.apache.freemarker.core.model.TemplateDateModel;
+import org.apache.freemarker.core.model.TemplateModel;
+import org.apache.freemarker.core.model.TemplateModelAdapter;
+import org.apache.freemarker.core.model.TemplateModelException;
+import org.apache.freemarker.core.model.impl.DefaultObjectWrapper;
+import org.apache.freemarker.core.model.impl.SimpleDate;
+import org.apache.freemarker.core.model.impl.SimpleHash;
+import org.apache.freemarker.core.model.impl.SimpleNumber;
+import org.apache.freemarker.core.model.impl.SimpleScalar;
+import org.apache.freemarker.core.model.impl.SimpleSequence;
+
+/**
+ * An object wrapper that doesn't extend {@link DefaultObjectWrapper}.
+ */
+public class FullyCustomObjectWrapper implements ObjectWrapper {
+
+    @Override
+    public TemplateModel wrap(Object obj) throws TemplateModelException {
+        if (obj == null) {
+            return null;
+        }
+
+        if (obj instanceof TemplateModel) {
+            return (TemplateModel) obj;
+        }
+        if (obj instanceof TemplateModelAdapter) {
+            return ((TemplateModelAdapter) obj).getTemplateModel();
+        }
+
+        if (obj instanceof String) {
+            return new SimpleScalar((String) obj);
+        }
+        if (obj instanceof Number) {
+            return new SimpleNumber((Number) obj);
+        }
+        if (obj instanceof Boolean) {
+            return obj.equals(Boolean.TRUE) ? TemplateBooleanModel.TRUE : TemplateBooleanModel.FALSE;
+        }
+        if (obj instanceof java.util.Date) {
+            if (obj instanceof java.sql.Date) {
+                return new SimpleDate((java.sql.Date) obj);
+            }
+            if (obj instanceof java.sql.Time) {
+                return new SimpleDate((java.sql.Time) obj);
+            }
+            if (obj instanceof java.sql.Timestamp) {
+                return new SimpleDate((java.sql.Timestamp) obj);
+            }
+            return new SimpleDate((java.util.Date) obj, TemplateDateModel.UNKNOWN);
+        }
+
+        if (obj.getClass().isArray()) {
+            obj = Arrays.asList((Object[]) obj);
+        }
+        if (obj instanceof Collection) {
+            return new SimpleSequence((Collection<?>) obj, this);
+        }
+        if (obj instanceof Map) {
+            return new SimpleHash((Map<?, ?>) obj, this);
+        }
+
+        return null;
+    }
+}


[2/2] incubator-freemarker git commit: Removed the global static final ObjectWrapper-s. It had a "few" consequences:

Posted by dd...@apache.org.
Removed the global static final ObjectWrapper-s. It had a "few" consequences:

- Standard TemplateModel implementations that can have an ObjectWrapper contrucor parameter don't allow null there anymore. Also, any constructor overloads where you cold omit the ObjectWrapper were removed (these were deprecated in FM2 too). In FM2, such overloads has used the global static default DefaltObjectWrapper, but that was removed.
- If the ObjectWrapper is not a DefaultObjectWrapper (or a subclass of it), `className?new(args)` will only accept 0 arguments. (Earlier we have fallen back to using the global static default DefaultObjectWrapper instance to handle argument unwrapping and overloaded constructors.) Note that ?new is only used to instantiate TemplateModel-s, typically, tempalte language functions/directives implemented in Java, and so they hardly ever has an argument.
- FreemarkerServlet now requires that the ObjectWrapper it uses implements ObjectWrapperAndUnwrapper. (Thus, the return type of FreemarerServlet.createDefaultObjectWrapper() has changed to ObjectWrapperAndUnwrapper.) The unwrapping functionality is required when calling JSP custom tags/functions, and in FreeMarker 2 this was worked around with using the global static default DefaultObjectWrapper when the ObjectWrapper wasn't an ObjectWrapperAndUnwrapper.


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

Branch: refs/heads/3
Commit: b4dfe5d248275a96cf1e0c44fe0c92cdc14d3eca
Parents: 83b4b77
Author: ddekany <dd...@apache.org>
Authored: Wed Mar 1 21:56:37 2017 +0100
Committer: ddekany <dd...@apache.org>
Committed: Fri Mar 3 21:18:15 2017 +0100

----------------------------------------------------------------------
 .../apache/freemarker/core/ASTDirRecurse.java   |   3 +-
 .../org/apache/freemarker/core/ASTDirVisit.java |   3 +-
 .../org/apache/freemarker/core/ASTElement.java  |  28 ----
 .../freemarker/core/ASTExpAddOrConcat.java      |   7 +-
 .../apache/freemarker/core/ASTExpDefault.java   |  10 +-
 .../freemarker/core/ASTExpDynamicKeyName.java   |   9 +-
 .../freemarker/core/ASTExpHashLiteral.java      |   9 +-
 .../freemarker/core/ASTExpListLiteral.java      |  10 +-
 .../freemarker/core/BuiltInsForNodes.java       |  12 +-
 .../core/BuiltInsForStringsBasic.java           |   9 +-
 .../freemarker/core/BuiltInsForStringsMisc.java |  25 +++-
 .../core/BuiltInsForStringsRegexp.java          |  17 ++-
 .../apache/freemarker/core/Configurable.java    |  10 +-
 .../apache/freemarker/core/Configuration.java   |   7 +-
 .../org/apache/freemarker/core/Environment.java |  24 ++--
 .../freemarker/core/NativeCollectionEx.java     |  73 ++++++++++
 .../apache/freemarker/core/NativeHashEx2.java   | 105 ++++++++++++++
 .../apache/freemarker/core/NativeSequence.java  |  73 ++++++++++
 .../core/NativeStringArraySequence.java         |  53 +++++++
 .../NativeStringCollectionCollectionEx.java     |  79 ++++++++++
 .../core/NativeStringListSequence.java          |  56 ++++++++
 .../core/debug/RmiDebuggedEnvironmentImpl.java  |   9 +-
 .../apache/freemarker/core/model/Constants.java |  14 +-
 .../core/model/GeneralPurposeNothing.java       |  10 +-
 .../core/model/TemplateCollectionModelEx.java   |   8 --
 .../core/model/WrappingTemplateModel.java       |  21 +--
 .../core/model/impl/CollectionAndSequence.java  |   3 +
 .../impl/DefaultNonListCollectionAdapter.java   |  17 +--
 .../core/model/impl/DefaultObjectWrapper.java   |   9 +-
 .../core/model/impl/SimpleCollection.java       |  36 +----
 .../freemarker/core/model/impl/SimpleHash.java  | 136 +-----------------
 .../core/model/impl/SimpleSequence.java         | 144 +------------------
 .../core/model/impl/_StaticObjectWrappers.java  |  42 ------
 .../freemarker/dom/JaxenXPathSupport.java       |   4 +-
 .../apache/freemarker/dom/NodeListModel.java    |  21 +--
 .../dom/NodeQueryResultItemObjectWrapper.java   |  92 ++++++++++++
 .../freemarker/servlet/FreemarkerServlet.java   |  44 +++---
 .../servlet/HttpRequestHashModel.java           |  13 +-
 .../servlet/HttpRequestParametersHashModel.java |  15 +-
 .../servlet/jsp/FreeMarkerPageContext.java      |  42 +-----
 .../freemarker/servlet/jsp/JspContextModel.java |  56 --------
 .../freemarker/servlet/jsp/JspTagModelBase.java |   9 +-
 src/manual/en_US/FM3-CHANGE-LOG.txt             |  14 +-
 .../freemarker/core/ConfigurationTest.java      |  19 ++-
 .../core/NewBiObjectWrapperRestrictionTest.java |  51 +++++++
 .../model/impl/DefaultObjectWrapperTest.java    |  19 ---
 .../servlet/jsp/RealServletContainertTest.java  |   3 +-
 .../test/templatesuite/TemplateTestCase.java    |  14 +-
 .../test/templatesuite/models/BooleanList1.java |  36 +----
 .../test/templatesuite/models/BooleanList2.java |   8 +-
 .../test/templatesuite/models/LegacyList.java   |  73 ----------
 .../test/templatesuite/models/MultiModel1.java  |  13 +-
 .../test/templatesuite/models/MultiModel4.java  |   8 +-
 .../test/templatesuite/models/MultiModel5.java  |   7 +-
 .../models/TransformHashWrapper.java            |   6 +-
 .../test/util/FullyCustomObjectWrapper.java     |  91 ++++++++++++
 56 files changed, 912 insertions(+), 817 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/b4dfe5d2/src/main/java/org/apache/freemarker/core/ASTDirRecurse.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/freemarker/core/ASTDirRecurse.java b/src/main/java/org/apache/freemarker/core/ASTDirRecurse.java
index d335df5..b95b3fc 100644
--- a/src/main/java/org/apache/freemarker/core/ASTDirRecurse.java
+++ b/src/main/java/org/apache/freemarker/core/ASTDirRecurse.java
@@ -26,7 +26,6 @@ import org.apache.freemarker.core.model.TemplateModel;
 import org.apache.freemarker.core.model.TemplateNodeModel;
 import org.apache.freemarker.core.model.TemplateScalarModel;
 import org.apache.freemarker.core.model.TemplateSequenceModel;
-import org.apache.freemarker.core.model.impl.SimpleSequence;
 
 
 /**
@@ -56,7 +55,7 @@ final class ASTDirRecurse extends ASTDirective {
         }
         if (nss != null) {
             if (nss instanceof TemplateHashModel) {
-                SimpleSequence ss = new SimpleSequence(1);
+                NativeSequence ss = new NativeSequence(1);
                 ss.add(nss);
                 nss = ss;
             } else if (!(nss instanceof TemplateSequenceModel)) {

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/b4dfe5d2/src/main/java/org/apache/freemarker/core/ASTDirVisit.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/freemarker/core/ASTDirVisit.java b/src/main/java/org/apache/freemarker/core/ASTDirVisit.java
index 56b5710..4a4023b 100644
--- a/src/main/java/org/apache/freemarker/core/ASTDirVisit.java
+++ b/src/main/java/org/apache/freemarker/core/ASTDirVisit.java
@@ -25,7 +25,6 @@ import org.apache.freemarker.core.model.TemplateModel;
 import org.apache.freemarker.core.model.TemplateNodeModel;
 import org.apache.freemarker.core.model.TemplateScalarModel;
 import org.apache.freemarker.core.model.TemplateSequenceModel;
-import org.apache.freemarker.core.model.impl.SimpleSequence;
 
 
 /**
@@ -55,7 +54,7 @@ final class ASTDirVisit extends ASTDirective {
         }
         if (nss != null) {
             if (nss instanceof Environment.Namespace) {
-                SimpleSequence ss = new SimpleSequence(1);
+                NativeSequence ss = new NativeSequence(1);
                 ss.add(nss);
                 nss = ss;
             } else if (!(nss instanceof TemplateSequenceModel)) {

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/b4dfe5d2/src/main/java/org/apache/freemarker/core/ASTElement.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/freemarker/core/ASTElement.java b/src/main/java/org/apache/freemarker/core/ASTElement.java
index 6e2d0b3..a9cbfc0 100644
--- a/src/main/java/org/apache/freemarker/core/ASTElement.java
+++ b/src/main/java/org/apache/freemarker/core/ASTElement.java
@@ -23,9 +23,6 @@ import java.io.IOException;
 import java.util.Collections;
 import java.util.Enumeration;
 
-import org.apache.freemarker.core.model.TemplateNodeModel;
-import org.apache.freemarker.core.model.TemplateSequenceModel;
-import org.apache.freemarker.core.model.impl.SimpleSequence;
 import org.apache.freemarker.core.util._ArrayEnumeration;
 
 /**
@@ -144,31 +141,6 @@ abstract class ASTElement extends ASTNode {
 
     // Methods to implement TemplateNodeModel
 
-    public TemplateNodeModel getParentNode() {
-        // return parent;
-        return null;
-    }
-
-    public String getNodeNamespace() {
-        return null;
-    }
-
-    public String getNodeType() {
-        return "element";
-    }
-
-    public TemplateSequenceModel getChildNodes() {
-        if (childBuffer != null) {
-            final SimpleSequence seq = new SimpleSequence(childCount);
-            for (int i = 0; i < childCount; i++) {
-                seq.add(childBuffer[i]);
-            }
-            return seq;
-        } else {
-            return new SimpleSequence(0);
-        }
-    }
-
     public String getNodeName() {
         String className = getClass().getName();
         int shortNameOffset = className.lastIndexOf('.') + 1;

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/b4dfe5d2/src/main/java/org/apache/freemarker/core/ASTExpAddOrConcat.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/freemarker/core/ASTExpAddOrConcat.java b/src/main/java/org/apache/freemarker/core/ASTExpAddOrConcat.java
index 019cbf5..15e632a 100644
--- a/src/main/java/org/apache/freemarker/core/ASTExpAddOrConcat.java
+++ b/src/main/java/org/apache/freemarker/core/ASTExpAddOrConcat.java
@@ -36,7 +36,6 @@ import org.apache.freemarker.core.model.TemplateSequenceModel;
 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.SimpleSequence;
 
 /**
  * AST expression node: binary {@code +} operator. Note that this is treated separately from the other 4 arithmetic
@@ -275,7 +274,7 @@ final class ASTExpAddOrConcat extends ASTExpression {
         throws TemplateModelException {
             if (keys == null) {
                 HashSet keySet = new HashSet();
-                SimpleSequence keySeq = new SimpleSequence(32);
+                NativeSequence keySeq = new NativeSequence(32);
                 addKeys(keySet, keySeq, (TemplateHashModelEx) left);
                 addKeys(keySet, keySeq, (TemplateHashModelEx) right);
                 size = keySet.size();
@@ -283,7 +282,7 @@ final class ASTExpAddOrConcat extends ASTExpression {
             }
         }
 
-        private static void addKeys(Set set, SimpleSequence keySeq, TemplateHashModelEx hash)
+        private static void addKeys(Set set, NativeSequence keySeq, TemplateHashModelEx hash)
         throws TemplateModelException {
             TemplateModelIterator it = hash.keys().iterator();
             while (it.hasNext()) {
@@ -299,7 +298,7 @@ final class ASTExpAddOrConcat extends ASTExpression {
         private void initValues()
         throws TemplateModelException {
             if (values == null) {
-                SimpleSequence seq = new SimpleSequence(size());
+                NativeSequence seq = new NativeSequence(size());
                 // Note: size() invokes initKeys() if needed.
             
                 int ln = keys.size();

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/b4dfe5d2/src/main/java/org/apache/freemarker/core/ASTExpDefault.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/freemarker/core/ASTExpDefault.java b/src/main/java/org/apache/freemarker/core/ASTExpDefault.java
index 1015845..b891374 100755
--- a/src/main/java/org/apache/freemarker/core/ASTExpDefault.java
+++ b/src/main/java/org/apache/freemarker/core/ASTExpDefault.java
@@ -20,19 +20,17 @@
 package org.apache.freemarker.core;
 
 
+import org.apache.freemarker.core.model.Constants;
 import org.apache.freemarker.core.model.TemplateCollectionModel;
 import org.apache.freemarker.core.model.TemplateHashModelEx;
 import org.apache.freemarker.core.model.TemplateModel;
 import org.apache.freemarker.core.model.TemplateScalarModel;
 import org.apache.freemarker.core.model.TemplateSequenceModel;
-import org.apache.freemarker.core.model.impl.SimpleCollection;
 
 /** {@code exp!defExp}, {@code (exp)!defExp} and the same two with {@code (exp)!}. */
 class ASTExpDefault extends ASTExpression {
 	
-    private static final TemplateCollectionModel EMPTY_COLLECTION = new SimpleCollection(new java.util.ArrayList(0));
-    
-	static private class EmptyStringAndSequence 
+	static private class EmptyStringAndSequence
 	  implements TemplateScalarModel, TemplateSequenceModel, TemplateHashModelEx {
 		@Override
         public String getAsString() {
@@ -56,11 +54,11 @@ class ASTExpDefault extends ASTExpression {
 		}
 		@Override
         public TemplateCollectionModel keys() {
-			return EMPTY_COLLECTION;
+			return Constants.EMPTY_COLLECTION;
 		}
 		@Override
         public TemplateCollectionModel values() {
-			return EMPTY_COLLECTION;
+			return Constants.EMPTY_COLLECTION;
 		}
 		
 	}

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/b4dfe5d2/src/main/java/org/apache/freemarker/core/ASTExpDynamicKeyName.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/freemarker/core/ASTExpDynamicKeyName.java b/src/main/java/org/apache/freemarker/core/ASTExpDynamicKeyName.java
index dd9c45e..b904ce4 100644
--- a/src/main/java/org/apache/freemarker/core/ASTExpDynamicKeyName.java
+++ b/src/main/java/org/apache/freemarker/core/ASTExpDynamicKeyName.java
@@ -19,8 +19,6 @@
 
 package org.apache.freemarker.core;
 
-import java.util.ArrayList;
-
 import org.apache.freemarker.core.model.Constants;
 import org.apache.freemarker.core.model.TemplateHashModel;
 import org.apache.freemarker.core.model.TemplateModel;
@@ -28,7 +26,6 @@ import org.apache.freemarker.core.model.TemplateNumberModel;
 import org.apache.freemarker.core.model.TemplateScalarModel;
 import org.apache.freemarker.core.model.TemplateSequenceModel;
 import org.apache.freemarker.core.model.impl.SimpleScalar;
-import org.apache.freemarker.core.model.impl.SimpleSequence;
 
 /**
  * AST expression node: {@code target[keyExpression]}, where, in FM 2.3, {@code keyExpression} can be string, a number
@@ -211,14 +208,14 @@ final class ASTExpDynamicKeyName extends ASTExpression {
             return emptyResult(targetSeq != null);
         }
         if (targetSeq != null) {
-            ArrayList/*<TemplateModel>*/ list = new ArrayList(resultSize);
+            NativeSequence resultSeq = new NativeSequence(resultSize);
             int srcIdx = firstIdx;
             for (int i = 0; i < resultSize; i++) {
-                list.add(targetSeq.get(srcIdx));
+                resultSeq.add(targetSeq.get(srcIdx));
                 srcIdx += step;
             }
             // List items are already wrapped, so the wrapper will be null:
-            return new SimpleSequence(list, null);
+            return resultSeq;
         } else {
             final int exclEndIdx;
             if (step < 0 && resultSize > 1) {

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/b4dfe5d2/src/main/java/org/apache/freemarker/core/ASTExpHashLiteral.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/freemarker/core/ASTExpHashLiteral.java b/src/main/java/org/apache/freemarker/core/ASTExpHashLiteral.java
index 369637a..50f8851 100644
--- a/src/main/java/org/apache/freemarker/core/ASTExpHashLiteral.java
+++ b/src/main/java/org/apache/freemarker/core/ASTExpHashLiteral.java
@@ -30,7 +30,6 @@ 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.impl.CollectionAndSequence;
-import org.apache.freemarker.core.model.impl.SimpleSequence;
 
 /**
  * AST expression node: <tt>{ keyExp: valueExp, ... }</tt> 
@@ -109,11 +108,11 @@ final class ASTExpHashLiteral extends ASTExpression {
 
     private class SequenceHash implements TemplateHashModelEx2 {
 
-        private HashMap map; // maps keys to integer offset
+        private HashMap<String, TemplateModel> map;
         private TemplateCollectionModel keyCollection, valueCollection; // ordered lists of keys and values
 
         SequenceHash(Environment env) throws TemplateException {
-            map = new LinkedHashMap();
+            map = new LinkedHashMap<>();
             for (int i = 0; i < size; i++) {
                 ASTExpression keyExp = (ASTExpression) keys.get(i);
                 ASTExpression valExp = (ASTExpression) values.get(i);
@@ -132,7 +131,7 @@ final class ASTExpHashLiteral extends ASTExpression {
         @Override
         public TemplateCollectionModel keys() {
             if (keyCollection == null) {
-                keyCollection = new CollectionAndSequence(new SimpleSequence(map.keySet()));
+                keyCollection = new CollectionAndSequence(new NativeStringCollectionCollectionEx(map.keySet()));
             }
             return keyCollection;
         }
@@ -140,7 +139,7 @@ final class ASTExpHashLiteral extends ASTExpression {
         @Override
         public TemplateCollectionModel values() {
             if (valueCollection == null) {
-                valueCollection = new CollectionAndSequence(new SimpleSequence(map.values()));
+                valueCollection = new CollectionAndSequence(new NativeCollectionEx(map.values()));
             }
             return valueCollection;
         }

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/b4dfe5d2/src/main/java/org/apache/freemarker/core/ASTExpListLiteral.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/freemarker/core/ASTExpListLiteral.java b/src/main/java/org/apache/freemarker/core/ASTExpListLiteral.java
index b6af883..b3fba1f 100644
--- a/src/main/java/org/apache/freemarker/core/ASTExpListLiteral.java
+++ b/src/main/java/org/apache/freemarker/core/ASTExpListLiteral.java
@@ -22,7 +22,6 @@ package org.apache.freemarker.core;
 import java.io.IOException;
 import java.util.ArrayList;
 import java.util.Collections;
-import java.util.Iterator;
 import java.util.List;
 import java.util.ListIterator;
 
@@ -30,7 +29,6 @@ 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.TemplateSequenceModel;
-import org.apache.freemarker.core.model.impl.SimpleSequence;
 
 /**
  * AST expression node: {@code [ exp, ... ]} 
@@ -46,9 +44,9 @@ final class ASTExpListLiteral extends ASTExpression {
 
     @Override
     TemplateModel _eval(Environment env) throws TemplateException {
-        SimpleSequence list = new SimpleSequence(items.size());
-        for (Iterator it = items.iterator(); it.hasNext(); ) {
-            ASTExpression exp = (ASTExpression) it.next();
+        NativeSequence list = new NativeSequence(items.size());
+        for (Object item : items) {
+            ASTExpression exp = (ASTExpression) item;
             TemplateModel tm = exp.eval(env);
             exp.assertNonNull(tm, env);
             list.add(tm);
@@ -140,7 +138,7 @@ final class ASTExpListLiteral extends ASTExpression {
     // A hacky routine used by ASTDirVisit and ASTDirRecurse
     TemplateSequenceModel evaluateStringsToNamespaces(Environment env) throws TemplateException {
         TemplateSequenceModel val = (TemplateSequenceModel) eval(env);
-        SimpleSequence result = new SimpleSequence(val.size());
+        NativeSequence result = new NativeSequence(val.size());
         for (int i = 0; i < items.size(); i++) {
             Object itemExpr = items.get(i);
             if (itemExpr instanceof ASTExpStringLiteral) {

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/b4dfe5d2/src/main/java/org/apache/freemarker/core/BuiltInsForNodes.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/freemarker/core/BuiltInsForNodes.java b/src/main/java/org/apache/freemarker/core/BuiltInsForNodes.java
index b572deb..39bc546 100644
--- a/src/main/java/org/apache/freemarker/core/BuiltInsForNodes.java
+++ b/src/main/java/org/apache/freemarker/core/BuiltInsForNodes.java
@@ -27,11 +27,8 @@ import org.apache.freemarker.core.model.TemplateModelException;
 import org.apache.freemarker.core.model.TemplateNodeModel;
 import org.apache.freemarker.core.model.TemplateNodeModelEx;
 import org.apache.freemarker.core.model.impl.SimpleScalar;
-import org.apache.freemarker.core.model.impl.SimpleSequence;
 import org.apache.freemarker.core.util._StringUtil;
 
-import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
-
 /**
  * A holder for builtins that operate exclusively on (XML-)node left-hand value.
  */
@@ -117,13 +114,14 @@ class BuiltInsForNodes {
     // Can't be instantiated
     private BuiltInsForNodes() { }
 
-    static class AncestorSequence extends SimpleSequence implements TemplateMethodModel {
-        
-        @SuppressFBWarnings(value="SE_BAD_FIELD",
-                justification="Can't make this Serializable, and not extending SimpleSequence would be non-BC.")
+    static class AncestorSequence extends NativeSequence implements TemplateMethodModel {
+
+        private static final int INITIAL_CAPACITY = 12;
+
         private Environment env;
         
         AncestorSequence(Environment env) {
+            super(INITIAL_CAPACITY);
             this.env = env;
         }
         

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/b4dfe5d2/src/main/java/org/apache/freemarker/core/BuiltInsForStringsBasic.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/freemarker/core/BuiltInsForStringsBasic.java b/src/main/java/org/apache/freemarker/core/BuiltInsForStringsBasic.java
index 279ecb6..bcf00c4 100644
--- a/src/main/java/org/apache/freemarker/core/BuiltInsForStringsBasic.java
+++ b/src/main/java/org/apache/freemarker/core/BuiltInsForStringsBasic.java
@@ -19,6 +19,7 @@
 
 package org.apache.freemarker.core;
 
+import java.util.ArrayList;
 import java.util.List;
 import java.util.StringTokenizer;
 import java.util.regex.Matcher;
@@ -32,8 +33,6 @@ import org.apache.freemarker.core.model.TemplateModelException;
 import org.apache.freemarker.core.model.TemplateScalarModel;
 import org.apache.freemarker.core.model.impl.SimpleNumber;
 import org.apache.freemarker.core.model.impl.SimpleScalar;
-import org.apache.freemarker.core.model.impl.SimpleSequence;
-import org.apache.freemarker.core.model.impl._StaticObjectWrappers;
 import org.apache.freemarker.core.util._StringUtil;
 
 class BuiltInsForStringsBasic {
@@ -555,7 +554,7 @@ class BuiltInsForStringsBasic {
                     Pattern pattern = RegexpHelper.getPattern(splitString, (int) flags);
                     result = pattern.split(s);
                 } 
-                return _StaticObjectWrappers.DEFAULT_OBJECT_WRAPPER.wrap(result);
+                return new NativeStringArraySequence(result);
             }
         }
         
@@ -683,12 +682,12 @@ class BuiltInsForStringsBasic {
     static class word_listBI extends BuiltInForString {
         @Override
         TemplateModel calculateResult(String s, Environment env) {
-            SimpleSequence result = new SimpleSequence();
+            ArrayList<String> result = new ArrayList<>();
             StringTokenizer st = new StringTokenizer(s);
             while (st.hasMoreTokens()) {
                result.add(st.nextToken());
             }
-            return result;
+            return new  NativeStringListSequence(result);
         }
     }
 

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/b4dfe5d2/src/main/java/org/apache/freemarker/core/BuiltInsForStringsMisc.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/freemarker/core/BuiltInsForStringsMisc.java b/src/main/java/org/apache/freemarker/core/BuiltInsForStringsMisc.java
index f79f882..d0deade 100644
--- a/src/main/java/org/apache/freemarker/core/BuiltInsForStringsMisc.java
+++ b/src/main/java/org/apache/freemarker/core/BuiltInsForStringsMisc.java
@@ -33,10 +33,9 @@ import org.apache.freemarker.core.model.TemplateModelException;
 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.impl.SimpleNumber;
-import org.apache.freemarker.core.model.impl._StaticObjectWrappers;
 import org.apache.freemarker.core.model.impl.BeanModel;
 import org.apache.freemarker.core.model.impl.DefaultObjectWrapper;
+import org.apache.freemarker.core.model.impl.SimpleNumber;
 
 class BuiltInsForStringsMisc {
 
@@ -289,11 +288,23 @@ class BuiltInsForStringsMisc {
             @Override
             public Object exec(List arguments) throws TemplateModelException {
                 ObjectWrapper ow = env.getObjectWrapper();
-                DefaultObjectWrapper dow =
-                    ow instanceof DefaultObjectWrapper
-                    ? (DefaultObjectWrapper) ow
-                    : _StaticObjectWrappers.DEFAULT_OBJECT_WRAPPER;
-                return dow.newInstance(cl, arguments);
+                if (ow instanceof DefaultObjectWrapper) {
+                    return ((DefaultObjectWrapper) ow).newInstance(cl, arguments);
+                }
+
+                if (!arguments.isEmpty()) {
+                    throw new TemplateModelException(
+                            "className?new(args) only supports 0 arguments in the current configuration, because "
+                            + " the objectWrapper setting value is not a "
+                            + DefaultObjectWrapper.class.getName() +
+                            " (or its subclass).");
+                }
+                try {
+                    return cl.newInstance();
+                } catch (Exception e) {
+                    throw new TemplateModelException("Failed to instantiate "
+                            + cl.getName() + " with its parameterless constructor; see cause exception", e);
+                }
             }
         }
     }

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/b4dfe5d2/src/main/java/org/apache/freemarker/core/BuiltInsForStringsRegexp.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/freemarker/core/BuiltInsForStringsRegexp.java b/src/main/java/org/apache/freemarker/core/BuiltInsForStringsRegexp.java
index 5c2ec8c..0420d64 100644
--- a/src/main/java/org/apache/freemarker/core/BuiltInsForStringsRegexp.java
+++ b/src/main/java/org/apache/freemarker/core/BuiltInsForStringsRegexp.java
@@ -33,7 +33,6 @@ import org.apache.freemarker.core.model.TemplateModelIterator;
 import org.apache.freemarker.core.model.TemplateScalarModel;
 import org.apache.freemarker.core.model.TemplateSequenceModel;
 import org.apache.freemarker.core.model.impl.SimpleScalar;
-import org.apache.freemarker.core.model.impl.SimpleSequence;
 import org.apache.freemarker.core.util._StringUtil;
 
 
@@ -50,7 +49,8 @@ class BuiltInsForStringsRegexp {
             if (targetModel instanceof RegexMatchModel) {
                 return ((RegexMatchModel) targetModel).getGroups();
             } else if (targetModel instanceof RegexMatchModel.MatchWithGroups) {
-                return ((RegexMatchModel.MatchWithGroups) targetModel).groupsSeq;
+                return new NativeStringArraySequence(((RegexMatchModel.MatchWithGroups) targetModel).groups);
+
             } else {
                 throw new UnexpectedTypeException(target, targetModel,
                         "regular expression matcher",
@@ -138,14 +138,14 @@ class BuiltInsForStringsRegexp {
     implements TemplateBooleanModel, TemplateCollectionModel, TemplateSequenceModel {
         static class MatchWithGroups implements TemplateScalarModel {
             final String matchedInputPart;
-            final SimpleSequence groupsSeq;
-            
+            final String[] groups;
+
             MatchWithGroups(String input, Matcher matcher) {
                 matchedInputPart = input.substring(matcher.start(), matcher.end());
                 final int grpCount = matcher.groupCount() + 1;
-                groupsSeq = new SimpleSequence(grpCount);
+                groups = new String[grpCount];
                 for (int i = 0; i < grpCount; i++) {
-                    groupsSeq.add(matcher.group(i));
+                    groups[i] = matcher.group(i);
                 }
             }
             
@@ -199,6 +199,11 @@ class BuiltInsForStringsRegexp {
                     @Override
                     public TemplateModel get(int i) throws TemplateModelException {
                         try {
+                            // Avoid IndexOutOfBoundsException:
+                            if (i > firedEntireInputMatcher.groupCount()) {
+                                return null;
+                            }
+
                             return new SimpleScalar(firedEntireInputMatcher.group(i));
                         } catch (Exception e) {
                             throw new _TemplateModelException(e, "Failed to read match group");

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/b4dfe5d2/src/main/java/org/apache/freemarker/core/Configurable.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/freemarker/core/Configurable.java b/src/main/java/org/apache/freemarker/core/Configurable.java
index a750e3d..122a3c0 100644
--- a/src/main/java/org/apache/freemarker/core/Configurable.java
+++ b/src/main/java/org/apache/freemarker/core/Configurable.java
@@ -47,7 +47,6 @@ import org.apache.freemarker.core.model.TemplateModel;
 import org.apache.freemarker.core.model.impl.DefaultObjectWrapper;
 import org.apache.freemarker.core.model.impl.DefaultObjectWrapperBuilder;
 import org.apache.freemarker.core.model.impl.RestrictedObjectWrapper;
-import org.apache.freemarker.core.model.impl._StaticObjectWrappers;
 import org.apache.freemarker.core.outputformat.OutputFormat;
 import org.apache.freemarker.core.outputformat.impl.HTMLOutputFormat;
 import org.apache.freemarker.core.outputformat.impl.PlainTextOutputFormat;
@@ -1809,11 +1808,8 @@ public class Configurable {
      *       values: {@code "DefaultObjectWrapper(3.0.0)"},
      *       {@code "DefaultObjectWrapper(2.3.21, simpleMapWrapper=true)"}.
      *       <br>If the value does not contain dot, then it must be one of these special values (case insensitive):
-     *       {@code "default"} means the default of {@link Configuration} (the default depends on the
-     *       {@code Configuration#Configuration(Version) incompatible_improvements}, but a bug existed in 2.3.21 where
-     *       that was ignored),
-     *       {@code "default_2_3_0"} (means {@link _StaticObjectWrappers#DEFAULT_OBJECT_WRAPPER})
-     *       {@code "restricted"} (means the deprecated {@link _StaticObjectWrappers#RESTRICTED_OBJECT_WRAPPER}
+     *       {@code "default"} means the default of {@link Configuration},
+     *       {@code "restricted"} means the a {@link RestrictedObjectWrapper} instance.
      *
      *   <li><p>{@code "number_format"}: See {@link #setNumberFormat(String)}.
      *   
@@ -2213,7 +2209,7 @@ public class Configurable {
                         setObjectWrapper(Configuration.getDefaultObjectWrapper(Configuration.VERSION_3_0_0));
                     }
                 } else if ("restricted".equalsIgnoreCase(value)) {
-                    setObjectWrapper(_StaticObjectWrappers.RESTRICTED_OBJECT_WRAPPER);
+                    setObjectWrapper(new RestrictedObjectWrapper(Configuration.VERSION_3_0_0));
                 } else {
                     setObjectWrapper((ObjectWrapper) _ObjectBuilderSettingEvaluator.eval(
                                     value, ObjectWrapper.class, false, _SettingEvaluationEnvironment.getCurrent()));

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/b4dfe5d2/src/main/java/org/apache/freemarker/core/Configuration.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/freemarker/core/Configuration.java b/src/main/java/org/apache/freemarker/core/Configuration.java
index 6b256b6..4bf2eae 100644
--- a/src/main/java/org/apache/freemarker/core/Configuration.java
+++ b/src/main/java/org/apache/freemarker/core/Configuration.java
@@ -42,6 +42,7 @@ import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.ConcurrentMap;
 
 import org.apache.freemarker.core.model.ObjectWrapper;
+import org.apache.freemarker.core.model.ObjectWrapperAndUnwrapper;
 import org.apache.freemarker.core.model.TemplateHashModelEx;
 import org.apache.freemarker.core.model.TemplateMarkupOutputModel;
 import org.apache.freemarker.core.model.TemplateModel;
@@ -557,10 +558,6 @@ public class Configuration extends Configurable implements Cloneable, ParserConf
         return new DefaultSoftCacheStorage(); 
     }
     
-    static CacheStorage createDefaultCacheStorage(Version incompatibleImprovements) {
-        return createDefaultCacheStorage(incompatibleImprovements, null); 
-    }
-    
     private static class DefaultSoftCacheStorage extends SoftCacheStorage {
         // Nothing to override
     }
@@ -2709,7 +2706,7 @@ public class Configuration extends Configurable implements Cloneable, ParserConf
      * 
      * @since 2.3.21
      */
-    public static ObjectWrapper getDefaultObjectWrapper(Version incompatibleImprovements) {
+    public static ObjectWrapperAndUnwrapper getDefaultObjectWrapper(Version incompatibleImprovements) {
         return new DefaultObjectWrapperBuilder(incompatibleImprovements).build();
     }
 

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/b4dfe5d2/src/main/java/org/apache/freemarker/core/Environment.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/freemarker/core/Environment.java b/src/main/java/org/apache/freemarker/core/Environment.java
index 78e06db..d6cfa91 100644
--- a/src/main/java/org/apache/freemarker/core/Environment.java
+++ b/src/main/java/org/apache/freemarker/core/Environment.java
@@ -57,7 +57,6 @@ import org.apache.freemarker.core.model.TemplateSequenceModel;
 import org.apache.freemarker.core.model.TemplateTransformModel;
 import org.apache.freemarker.core.model.TransformControl;
 import org.apache.freemarker.core.model.impl.SimpleHash;
-import org.apache.freemarker.core.model.impl.SimpleSequence;
 import org.apache.freemarker.core.templateresolver.MalformedTemplateNameException;
 import org.apache.freemarker.core.templateresolver.TemplateNameFormat;
 import org.apache.freemarker.core.templateresolver.TemplateResolver;
@@ -577,9 +576,9 @@ public final class Environment extends Configurable {
     void invokeNodeHandlerFor(TemplateNodeModel node, TemplateSequenceModel namespaces)
             throws TemplateException, IOException {
         if (nodeNamespaces == null) {
-            SimpleSequence ss = new SimpleSequence(1);
-            ss.add(currentNamespace);
-            nodeNamespaces = ss;
+            NativeSequence seq = new NativeSequence(1);
+            seq.add(currentNamespace);
+            nodeNamespaces = seq;
         }
         int prevNodeNamespaceIndex = nodeNamespaceIndex;
         String prevNodeName = currentNodeName;
@@ -705,9 +704,9 @@ public final class Environment extends Configurable {
             final Map namedArgs, final List positionalArgs) throws TemplateException {
         String catchAllParamName = macro.getCatchAll();
         if (namedArgs != null) {
-            final SimpleHash catchAllParamValue;
+            final NativeHashEx2 catchAllParamValue;
             if (catchAllParamName != null) {
-                catchAllParamValue = new SimpleHash((ObjectWrapper) null);
+                catchAllParamValue = new NativeHashEx2();
                 macroCtx.setLocalVar(catchAllParamName, catchAllParamValue);
             } else {
                 catchAllParamValue = null;
@@ -731,9 +730,9 @@ public final class Environment extends Configurable {
                 }
             }
         } else if (positionalArgs != null) {
-            final SimpleSequence catchAllParamValue;
+            final NativeSequence catchAllParamValue;
             if (catchAllParamName != null) {
-                catchAllParamValue = new SimpleSequence((ObjectWrapper) null);
+                catchAllParamValue = new NativeSequence(8);
                 macroCtx.setLocalVar(catchAllParamName, catchAllParamValue);
             } else {
                 catchAllParamValue = null;
@@ -2743,10 +2742,11 @@ public final class Environment extends Configurable {
         private Template template;
 
         Namespace() {
-            template = Environment.this.getMainTemplate();
+            this(Environment.this.getMainTemplate());
         }
 
         Namespace(Template template) {
+            super(Environment.this.getObjectWrapper());
             this.template = template;
         }
 
@@ -2886,12 +2886,6 @@ public final class Environment extends Configurable {
         }
 
         @Override
-        public Map toMap() throws TemplateModelException {
-            ensureInitializedTME();
-            return super.toMap();
-        }
-
-        @Override
         public String toString() {
             ensureInitializedRTE();
             return super.toString();

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/b4dfe5d2/src/main/java/org/apache/freemarker/core/NativeCollectionEx.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/freemarker/core/NativeCollectionEx.java b/src/main/java/org/apache/freemarker/core/NativeCollectionEx.java
new file mode 100644
index 0000000..5afa98a
--- /dev/null
+++ b/src/main/java/org/apache/freemarker/core/NativeCollectionEx.java
@@ -0,0 +1,73 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.freemarker.core;
+
+import java.util.Collection;
+import java.util.Iterator;
+
+import org.apache.freemarker.core.model.ObjectWrapper;
+import org.apache.freemarker.core.model.TemplateCollectionModelEx;
+import org.apache.freemarker.core.model.TemplateModel;
+import org.apache.freemarker.core.model.TemplateModelException;
+import org.apache.freemarker.core.model.TemplateModelIterator;
+
+/**
+ * A collection where each items is already a {@link TemplateModel}, so no {@link ObjectWrapper} need to be specified.
+ */
+class NativeCollectionEx implements TemplateCollectionModelEx {
+
+    private final Collection<TemplateModel> collection;
+
+    public NativeCollectionEx(Collection<TemplateModel> collection) {
+        this.collection = collection;
+    }
+
+    @Override
+    public int size() {
+        return collection.size();
+    }
+
+    @Override
+    public boolean isEmpty() {
+        return collection.isEmpty();
+    }
+
+    @Override
+    public TemplateModelIterator iterator() throws TemplateModelException {
+        return new TemplateModelIterator() {
+
+            private final Iterator<TemplateModel> iterator = collection.iterator();
+
+            @Override
+            public TemplateModel next() throws TemplateModelException {
+                if (!iterator.hasNext()) {
+                    throw new TemplateModelException("The collection has no more items.");
+                }
+
+                return iterator.next();
+            }
+
+            @Override
+            public boolean hasNext() {
+                return iterator.hasNext();
+            }
+        };
+    }
+}

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

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/b4dfe5d2/src/main/java/org/apache/freemarker/core/NativeSequence.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/freemarker/core/NativeSequence.java b/src/main/java/org/apache/freemarker/core/NativeSequence.java
new file mode 100644
index 0000000..c27971a
--- /dev/null
+++ b/src/main/java/org/apache/freemarker/core/NativeSequence.java
@@ -0,0 +1,73 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.freemarker.core;
+
+import java.util.ArrayList;
+import java.util.Collection;
+
+import org.apache.freemarker.core.model.ObjectWrapper;
+import org.apache.freemarker.core.model.TemplateModel;
+import org.apache.freemarker.core.model.TemplateModelException;
+import org.apache.freemarker.core.model.TemplateSequenceModel;
+
+/**
+ * A sequence where each items is already a {@link TemplateModel}, so no {@link ObjectWrapper} need to be specified.
+ *
+ * <p>While this class allows adding items, doing so is not thread-safe, and thus only meant to be done during the
+ * initialization of the sequence.
+ */
+class NativeSequence implements TemplateSequenceModel {
+
+    private final ArrayList<TemplateModel> items;
+
+    public NativeSequence(int capacity) {
+        items = new ArrayList<>(capacity);
+    }
+
+    /**
+     * Copies the collection
+     */
+    public NativeSequence(Collection<TemplateModel> items) {
+        this.items = new ArrayList<>(items.size());
+        this.items.addAll(items);
+    }
+
+    public void add(TemplateModel tm) {
+        items.add(tm);
+    }
+
+    public void addAll(Collection<TemplateModel> items) {
+        this.items.addAll(items);
+    }
+
+    public void clear() {
+        items.clear();
+    }
+
+    @Override
+    public TemplateModel get(int index) throws TemplateModelException {
+        return items.get(index);
+    }
+
+    @Override
+    public int size() throws TemplateModelException {
+        return items.size();
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/b4dfe5d2/src/main/java/org/apache/freemarker/core/NativeStringArraySequence.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/freemarker/core/NativeStringArraySequence.java b/src/main/java/org/apache/freemarker/core/NativeStringArraySequence.java
new file mode 100644
index 0000000..96e9899
--- /dev/null
+++ b/src/main/java/org/apache/freemarker/core/NativeStringArraySequence.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;
+
+import org.apache.freemarker.core.model.ObjectWrapper;
+import org.apache.freemarker.core.model.TemplateModel;
+import org.apache.freemarker.core.model.TemplateModelException;
+import org.apache.freemarker.core.model.TemplateSequenceModel;
+import org.apache.freemarker.core.model.impl.DefaultArrayAdapter;
+import org.apache.freemarker.core.model.impl.SimpleScalar;
+
+/**
+ * Adapts (not copies) a {@link String} array with on-the-fly wrapping of the items to {@link SimpleScalar}-s. The
+ * important difference to {@link DefaultArrayAdapter} is that it doesn't depend on an {@link ObjectWrapper}, which is
+ * needed to guarantee the behavior of some template language constructs. The important difference to
+ * {@link NativeSequence} is that it doesn't need upfront conversion to {@link TemplateModel}-s (performance).
+ */
+class NativeStringArraySequence implements TemplateSequenceModel {
+
+    private final String[] items;
+
+    public NativeStringArraySequence(String[] items) {
+        this.items = items;
+    }
+
+    @Override
+    public TemplateModel get(int index) throws TemplateModelException {
+        return index < items.length ? new SimpleScalar(items[index]) : null;
+    }
+
+    @Override
+    public int size() throws TemplateModelException {
+        return items.length;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/b4dfe5d2/src/main/java/org/apache/freemarker/core/NativeStringCollectionCollectionEx.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/freemarker/core/NativeStringCollectionCollectionEx.java b/src/main/java/org/apache/freemarker/core/NativeStringCollectionCollectionEx.java
new file mode 100644
index 0000000..b2437e7
--- /dev/null
+++ b/src/main/java/org/apache/freemarker/core/NativeStringCollectionCollectionEx.java
@@ -0,0 +1,79 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.freemarker.core;
+
+import java.util.Collection;
+import java.util.Iterator;
+
+import org.apache.freemarker.core.model.ObjectWrapper;
+import org.apache.freemarker.core.model.TemplateCollectionModelEx;
+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.impl.DefaultNonListCollectionAdapter;
+import org.apache.freemarker.core.model.impl.SimpleScalar;
+
+/**
+ * Adapts (not copies) a {@link Collection} of {@link String}-s with on-the-fly wrapping of the items to {@link
+ * SimpleScalar}-s. The important difference to {@link DefaultNonListCollectionAdapter} is that it doesn't depend on an
+ * {@link ObjectWrapper}, which is needed to guarantee the behavior of some template language constructs. The important
+ * difference to {@link NativeCollectionEx} is that it doesn't need upfront conversion to {@link TemplateModel}-s
+ * (performance).
+ */
+class NativeStringCollectionCollectionEx implements TemplateCollectionModelEx {
+
+    private final Collection<String> collection;
+
+    public NativeStringCollectionCollectionEx(Collection<String> collection) {
+        this.collection = collection;
+    }
+
+    @Override
+    public int size() {
+        return collection.size();
+    }
+
+    @Override
+    public boolean isEmpty() {
+        return collection.isEmpty();
+    }
+
+    @Override
+    public TemplateModelIterator iterator() throws TemplateModelException {
+        return new TemplateModelIterator() {
+
+            private final Iterator<String> iterator = collection.iterator();
+
+            @Override
+            public TemplateModel next() throws TemplateModelException {
+                if (!iterator.hasNext()) {
+                    throw new TemplateModelException("The collection has no more items.");
+                }
+
+                return new SimpleScalar(iterator.next());
+            }
+
+            @Override
+            public boolean hasNext() {
+                return iterator.hasNext();
+            }
+        };
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/b4dfe5d2/src/main/java/org/apache/freemarker/core/NativeStringListSequence.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/freemarker/core/NativeStringListSequence.java b/src/main/java/org/apache/freemarker/core/NativeStringListSequence.java
new file mode 100644
index 0000000..7846fd3
--- /dev/null
+++ b/src/main/java/org/apache/freemarker/core/NativeStringListSequence.java
@@ -0,0 +1,56 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.freemarker.core;
+
+import java.util.List;
+
+import org.apache.freemarker.core.model.ObjectWrapper;
+import org.apache.freemarker.core.model.TemplateModel;
+import org.apache.freemarker.core.model.TemplateModelException;
+import org.apache.freemarker.core.model.TemplateSequenceModel;
+import org.apache.freemarker.core.model.impl.DefaultListAdapter;
+import org.apache.freemarker.core.model.impl.SimpleScalar;
+
+/**
+ * Adapts (not copies) a {@link List} of {@link String}-s with on-the-fly wrapping of the items to {@link
+ * SimpleScalar}-s. The important difference to {@link DefaultListAdapter} is that it doesn't depend on an {@link
+ * ObjectWrapper}, which is needed to guarantee the behavior of some template language constructs. The important
+ * difference to {@link NativeSequence} is that it doesn't need upfront conversion to {@link TemplateModel}-s
+ * (performance).
+ */
+class NativeStringListSequence implements TemplateSequenceModel {
+
+    private final List<String> items;
+
+    public NativeStringListSequence(List<String> items) {
+        this.items = items;
+    }
+
+    @Override
+    public TemplateModel get(int index) throws TemplateModelException {
+        return index < items.size() ? new SimpleScalar(items.get(index)) : null;
+    }
+
+    @Override
+    public int size() throws TemplateModelException {
+        return items.size();
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/b4dfe5d2/src/main/java/org/apache/freemarker/core/debug/RmiDebuggedEnvironmentImpl.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/freemarker/core/debug/RmiDebuggedEnvironmentImpl.java b/src/main/java/org/apache/freemarker/core/debug/RmiDebuggedEnvironmentImpl.java
index 3229c81..fbee3b1 100644
--- a/src/main/java/org/apache/freemarker/core/debug/RmiDebuggedEnvironmentImpl.java
+++ b/src/main/java/org/apache/freemarker/core/debug/RmiDebuggedEnvironmentImpl.java
@@ -40,6 +40,8 @@ import org.apache.freemarker.core.model.TemplateCollectionModel;
 import org.apache.freemarker.core.model.TemplateHashModelEx;
 import org.apache.freemarker.core.model.TemplateModel;
 import org.apache.freemarker.core.model.TemplateModelException;
+import org.apache.freemarker.core.model.impl.DefaultObjectWrapper;
+import org.apache.freemarker.core.model.impl.DefaultObjectWrapperBuilder;
 import org.apache.freemarker.core.model.impl.SimpleCollection;
 import org.apache.freemarker.core.model.impl.SimpleScalar;
 import org.apache.freemarker.core.util.UndeclaredThrowableException;
@@ -55,6 +57,9 @@ class RmiDebuggedEnvironmentImpl extends RmiDebugModelImpl implements DebuggedEn
     private static long nextId = 1;
     private static Set remotes = new HashSet();
 
+    private static final DefaultObjectWrapper OBJECT_WRAPPER = new DefaultObjectWrapperBuilder(Configuration
+            .VERSION_3_0_0)
+            .build();
     
     private boolean stopped = false;
     private final long id;
@@ -129,7 +134,7 @@ class RmiDebuggedEnvironmentImpl extends RmiDebugModelImpl implements DebuggedEn
 
         @Override
         public TemplateCollectionModel keys() {
-            return new SimpleCollection(keySet());
+            return new SimpleCollection(keySet(), OBJECT_WRAPPER);
         }
 
         @Override
@@ -140,7 +145,7 @@ class RmiDebuggedEnvironmentImpl extends RmiDebugModelImpl implements DebuggedEn
             for (Iterator it = keys.iterator(); it.hasNext(); ) {
                 list.add(get((String) it.next()));
             }
-            return new SimpleCollection(list);
+            return new SimpleCollection(list, OBJECT_WRAPPER);
         }
 
         @Override

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/b4dfe5d2/src/main/java/org/apache/freemarker/core/model/Constants.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/freemarker/core/model/Constants.java b/src/main/java/org/apache/freemarker/core/model/Constants.java
index bab1f8c..268188d 100644
--- a/src/main/java/org/apache/freemarker/core/model/Constants.java
+++ b/src/main/java/org/apache/freemarker/core/model/Constants.java
@@ -62,9 +62,19 @@ public class Constants {
         
     }
 
-    public static final TemplateCollectionModel EMPTY_COLLECTION = new EmptyCollectionModel();
+    public static final TemplateCollectionModelEx EMPTY_COLLECTION = new EmptyCollectionExModel();
     
-    private static class EmptyCollectionModel implements TemplateCollectionModel, Serializable {
+    private static class EmptyCollectionExModel implements TemplateCollectionModelEx, Serializable {
+
+        @Override
+        public int size() throws TemplateModelException {
+            return 0;
+        }
+
+        @Override
+        public boolean isEmpty() throws TemplateModelException {
+            return true;
+        }
 
         @Override
         public TemplateModelIterator iterator() throws TemplateModelException {

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/b4dfe5d2/src/main/java/org/apache/freemarker/core/model/GeneralPurposeNothing.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/freemarker/core/model/GeneralPurposeNothing.java b/src/main/java/org/apache/freemarker/core/model/GeneralPurposeNothing.java
index 7d105e9..da1c102 100644
--- a/src/main/java/org/apache/freemarker/core/model/GeneralPurposeNothing.java
+++ b/src/main/java/org/apache/freemarker/core/model/GeneralPurposeNothing.java
@@ -19,11 +19,8 @@
 
 package org.apache.freemarker.core.model;
 
-import java.util.ArrayList;
 import java.util.List;
 
-import org.apache.freemarker.core.model.impl.SimpleCollection;
-
 /**
  * Singleton object representing nothing, used by ?if_exists built-in.
  * It is meant to be interpreted in the most sensible way possible in various contexts.
@@ -35,8 +32,6 @@ implements TemplateBooleanModel, TemplateScalarModel, TemplateSequenceModel, Tem
 
     public static final TemplateModel INSTANCE = new GeneralPurposeNothing();
       
-    private static final TemplateCollectionModel EMPTY_COLLECTION = new SimpleCollection(new ArrayList(0));
-
     private GeneralPurposeNothing() {
     }
 
@@ -77,11 +72,12 @@ implements TemplateBooleanModel, TemplateScalarModel, TemplateSequenceModel, Tem
     
     @Override
     public TemplateCollectionModel keys() {
-        return EMPTY_COLLECTION;
+        return Constants.EMPTY_COLLECTION;
     }
 
     @Override
     public TemplateCollectionModel values() {
-        return EMPTY_COLLECTION;
+        return Constants.EMPTY_COLLECTION;
     }
+
 }

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/b4dfe5d2/src/main/java/org/apache/freemarker/core/model/TemplateCollectionModelEx.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/freemarker/core/model/TemplateCollectionModelEx.java b/src/main/java/org/apache/freemarker/core/model/TemplateCollectionModelEx.java
index 3de434c..92f0e3a 100644
--- a/src/main/java/org/apache/freemarker/core/model/TemplateCollectionModelEx.java
+++ b/src/main/java/org/apache/freemarker/core/model/TemplateCollectionModelEx.java
@@ -42,12 +42,4 @@ public interface TemplateCollectionModelEx extends TemplateCollectionModel {
      */
     boolean isEmpty() throws TemplateModelException;
 
-    /**
-     * Tells if a given value occurs in the collection, according the rules of the wrapped collection. As of 2.3.22,
-     * this interface is not yet utilized by FTL, and certainly it won't be earlier than 2.4.0. The usefulness of this
-     * method is questionable, as the equality rules of Java differs from that of FTL, hence, calling this won't be
-     * equivalent with {@code ?seq_contains(e)}.
-     */
-    boolean contains(TemplateModel item) throws TemplateModelException;
-
 }

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/b4dfe5d2/src/main/java/org/apache/freemarker/core/model/WrappingTemplateModel.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/freemarker/core/model/WrappingTemplateModel.java b/src/main/java/org/apache/freemarker/core/model/WrappingTemplateModel.java
index 1dd981e..206d9d4 100644
--- a/src/main/java/org/apache/freemarker/core/model/WrappingTemplateModel.java
+++ b/src/main/java/org/apache/freemarker/core/model/WrappingTemplateModel.java
@@ -19,7 +19,7 @@
 
 package org.apache.freemarker.core.model;
 
-import org.apache.freemarker.core.model.impl._StaticObjectWrappers;
+import org.apache.freemarker.core.util._NullArgumentException;
 
 /**
  * Convenience base-class for containers that wrap their contained arbitrary Java objects into {@link TemplateModel}
@@ -28,27 +28,16 @@ import org.apache.freemarker.core.model.impl._StaticObjectWrappers;
 abstract public class WrappingTemplateModel {
 
     private final ObjectWrapper objectWrapper;
-    
-    /**
-     * Protected constructor that creates a new wrapping template model using
-     * the default object wrapper.
-     * 
-     * @deprecated Use {@link #WrappingTemplateModel(ObjectWrapper)} instead; this method uses the deprecated.
-     */
-    @Deprecated
-    protected WrappingTemplateModel() {
-        this(null);
-    }
 
     /**
      * Protected constructor that creates a new wrapping template model using the specified object wrapper.
      * 
      * @param objectWrapper the wrapper to use. Passing {@code null} to it
-     *     is allowed but deprecated. If {@code null} is passed, the deprecated default object wrapper
-     *     is used.
+     *     is allowed but deprecated. Not {@code null}.
      */
     protected WrappingTemplateModel(ObjectWrapper objectWrapper) {
-        this.objectWrapper = objectWrapper != null ? objectWrapper : _StaticObjectWrappers.DEFAULT_OBJECT_WRAPPER;
+        _NullArgumentException.check("objectWrapper", objectWrapper);
+        this.objectWrapper = objectWrapper;
     }
     
     /**
@@ -67,7 +56,7 @@ abstract public class WrappingTemplateModel {
      * wrap the passed object.
      */
     protected final TemplateModel wrap(Object obj) throws TemplateModelException {
-        return objectWrapper.wrap(obj);
+            return objectWrapper.wrap(obj);
     }
     
 }

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/b4dfe5d2/src/main/java/org/apache/freemarker/core/model/impl/CollectionAndSequence.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/freemarker/core/model/impl/CollectionAndSequence.java b/src/main/java/org/apache/freemarker/core/model/impl/CollectionAndSequence.java
index 49e230f..7979981 100644
--- a/src/main/java/org/apache/freemarker/core/model/impl/CollectionAndSequence.java
+++ b/src/main/java/org/apache/freemarker/core/model/impl/CollectionAndSequence.java
@@ -22,6 +22,7 @@ package org.apache.freemarker.core.model.impl;
 import java.util.ArrayList;
 
 import org.apache.freemarker.core.model.TemplateCollectionModel;
+import org.apache.freemarker.core.model.TemplateCollectionModelEx;
 import org.apache.freemarker.core.model.TemplateModel;
 import org.apache.freemarker.core.model.TemplateModelException;
 import org.apache.freemarker.core.model.TemplateModelIterator;
@@ -68,6 +69,8 @@ final public class CollectionAndSequence implements TemplateCollectionModel, Tem
     public int size() throws TemplateModelException {
         if (sequence != null) {
             return sequence.size();
+        } else if (collection instanceof TemplateCollectionModelEx) {
+            return ((TemplateCollectionModelEx) collection).size();
         } else {
             initSequence();
             return data.size();

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/b4dfe5d2/src/main/java/org/apache/freemarker/core/model/impl/DefaultNonListCollectionAdapter.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/freemarker/core/model/impl/DefaultNonListCollectionAdapter.java b/src/main/java/org/apache/freemarker/core/model/impl/DefaultNonListCollectionAdapter.java
index 66fc933..3b128fd 100644
--- a/src/main/java/org/apache/freemarker/core/model/impl/DefaultNonListCollectionAdapter.java
+++ b/src/main/java/org/apache/freemarker/core/model/impl/DefaultNonListCollectionAdapter.java
@@ -23,8 +23,6 @@ import java.io.Serializable;
 import java.util.Collection;
 import java.util.List;
 
-import org.apache.freemarker.core._DelayedShortClassName;
-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.ObjectWrapperAndUnwrapper;
@@ -60,7 +58,7 @@ public class DefaultNonListCollectionAdapter extends WrappingTemplateModel imple
      * @param collection
      *            The collection to adapt; can't be {@code null}.
      * @param wrapper
-     *            The {@link ObjectWrapper} used to wrap the items in the array. Has to be
+     *            The {@link ObjectWrapper} used to wrap the items in the collection. Has to be
      *            {@link ObjectWrapperAndUnwrapper} because of planned future features.
      */
     public static DefaultNonListCollectionAdapter adapt(Collection collection, ObjectWrapperWithAPISupport wrapper) {
@@ -98,19 +96,6 @@ public class DefaultNonListCollectionAdapter extends WrappingTemplateModel imple
     }
 
     @Override
-    public boolean contains(TemplateModel item) throws TemplateModelException {
-        Object itemPojo = ((ObjectWrapperAndUnwrapper) getObjectWrapper()).unwrap(item);
-        try {
-            return collection.contains(itemPojo);
-        } catch (ClassCastException e) {
-            throw new _TemplateModelException(e,
-                    "Failed to check if the collection contains the item. Probably the item's Java type, ",
-                    itemPojo != null ? new _DelayedShortClassName(itemPojo.getClass()) : "Null",
-                    ", doesn't match the type of (some of) the collection items; see cause exception.");
-        }
-    }
-
-    @Override
     public TemplateModel getAPI() throws TemplateModelException {
         return ((ObjectWrapperWithAPISupport) getObjectWrapper()).wrapAsAPI(collection);
     }

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/b4dfe5d2/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 82b360a..f13caf5 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
@@ -84,7 +84,6 @@ import org.w3c.dom.Node;
  * JSR 133 and related literature). When used as part of {@link Configuration}, of course it's enough if that was safely
  * published and then left unmodified.
  */
-// TODO Get rid of unused stuff (including other clases in this package) coming from BeansWrapper.
 public class DefaultObjectWrapper implements RichObjectWrapper, WriteProtectable {
 
     private static final Logger LOG = _CoreLogs.OBJECT_WRAPPER;
@@ -168,7 +167,7 @@ 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...
+    @Deprecated // Only exists to keep some JUnit tests working... [FM3]
     private boolean useModelCache;
 
     private final Version incompatibleImprovements;
@@ -662,6 +661,9 @@ public class DefaultObjectWrapper implements RichObjectWrapper, WriteProtectable
         if (obj instanceof Number) {
             return new SimpleNumber((Number) obj);
         }
+        if (obj instanceof Boolean) {
+            return obj.equals(Boolean.TRUE) ? TemplateBooleanModel.TRUE : TemplateBooleanModel.FALSE;
+        }
         if (obj instanceof java.util.Date) {
             if (obj instanceof java.sql.Date) {
                 return new SimpleDate((java.sql.Date) obj);
@@ -686,9 +688,6 @@ public class DefaultObjectWrapper implements RichObjectWrapper, WriteProtectable
         if (obj instanceof Map) {
             return DefaultMapAdapter.adapt((Map<?, ?>) obj, this);
         }
-        if (obj instanceof Boolean) {
-            return obj.equals(Boolean.TRUE) ? TemplateBooleanModel.TRUE : TemplateBooleanModel.FALSE;
-        }
         if (obj instanceof Iterator) {
             return DefaultIteratorAdapter.adapt((Iterator<?>) obj, this);
         }

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/b4dfe5d2/src/main/java/org/apache/freemarker/core/model/impl/SimpleCollection.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/freemarker/core/model/impl/SimpleCollection.java b/src/main/java/org/apache/freemarker/core/model/impl/SimpleCollection.java
index ecae30e..cf399ab 100644
--- a/src/main/java/org/apache/freemarker/core/model/impl/SimpleCollection.java
+++ b/src/main/java/org/apache/freemarker/core/model/impl/SimpleCollection.java
@@ -52,24 +52,6 @@ implements TemplateCollectionModel, Serializable {
     private final Iterator iterator;
     private final Collection collection;
 
-    /**
-     * @deprecated Use {@link #SimpleCollection(Iterator, ObjectWrapper)}
-     */
-    @Deprecated
-    public SimpleCollection(Iterator iterator) {
-        this.iterator = iterator;
-        collection = null;
-    }
-
-    /**
-     * @deprecated Use {@link #SimpleCollection(Collection, ObjectWrapper)}
-     */
-    @Deprecated
-    public SimpleCollection(Collection collection) {
-        this.collection = collection;
-        iterator = null;
-    }
-
     public SimpleCollection(Iterator iterator, ObjectWrapper wrapper) {
         super(wrapper);
         this.iterator = iterator;
@@ -86,20 +68,16 @@ implements TemplateCollectionModel, Serializable {
      * Retrieves a template model iterator that is used to iterate over the elements in this collection.
      *  
      * <p>When you wrap an <tt>Iterator</tt> and you get <tt>TemplateModelIterator</tt> for multiple times,
-     * only on of the returned <tt>TemplateModelIterator</tt> instances can be really used. When you have called a
+     * only one of the returned <tt>TemplateModelIterator</tt> instances can be really used. When you have called a
      * method of a <tt>TemplateModelIterator</tt> instance, all other instance will throw a
      * <tt>TemplateModelException</tt> when you try to call their methods, since the wrapped <tt>Iterator</tt>
      * can't return the first element anymore.
      */
     @Override
     public TemplateModelIterator iterator() {
-        if (iterator != null) {
-            return new SimpleTemplateModelIterator(iterator, false);
-        } else {
-            synchronized (collection) {
-                return new SimpleTemplateModelIterator(collection.iterator(), true);
-            }
-        }
+        return iterator != null
+                ? new SimpleTemplateModelIterator(iterator, false)
+                : new SimpleTemplateModelIterator(collection.iterator(), true);
     }
     
     /**
@@ -122,7 +100,7 @@ implements TemplateCollectionModel, Serializable {
         public TemplateModel next() throws TemplateModelException {
             if (!iteratorOwnedByMe) { 
                 synchronized (SimpleCollection.this) {
-                    checkIteratorOwned();
+                    checkIteratorNotOwned();
                     iteratorOwned = true;
                     iteratorOwnedByMe = true;
                 }
@@ -141,14 +119,14 @@ implements TemplateCollectionModel, Serializable {
             // Calling hasNext may looks safe, but I have met sync. problems.
             if (!iteratorOwnedByMe) {
                 synchronized (SimpleCollection.this) {
-                    checkIteratorOwned();
+                    checkIteratorNotOwned();
                 }
             }
             
             return iterator.hasNext();
         }
         
-        private void checkIteratorOwned() throws TemplateModelException {
+        private void checkIteratorNotOwned() throws TemplateModelException {
             if (iteratorOwned) {
                 throw new TemplateModelException(
                         "This collection value wraps a java.util.Iterator, thus it can be listed only once.");

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/b4dfe5d2/src/main/java/org/apache/freemarker/core/model/impl/SimpleHash.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/freemarker/core/model/impl/SimpleHash.java b/src/main/java/org/apache/freemarker/core/model/impl/SimpleHash.java
index 45aa8bf..16ec685 100644
--- a/src/main/java/org/apache/freemarker/core/model/impl/SimpleHash.java
+++ b/src/main/java/org/apache/freemarker/core/model/impl/SimpleHash.java
@@ -85,34 +85,9 @@ public class SimpleHash extends WrappingTemplateModel implements TemplateHashMod
     private Map unwrappedMap;
 
     /**
-     * Constructs an empty hash that uses {@link _StaticObjectWrappers#DEFAULT_OBJECT_WRAPPER}.
-     * 
-     * @deprecated Use {@link #SimpleHash(ObjectWrapper)}
-     */
-    @Deprecated
-    public SimpleHash() {
-        this((ObjectWrapper) null);
-    }
-
-    /**
-     * Creates a new simple hash with the copy of the underlying map and the
-     * default wrapper {@link _StaticObjectWrappers#DEFAULT_OBJECT_WRAPPER}.
-     * @param map The Map to use for the key/value pairs. It makes a copy for 
-     * internal use. If the map implements the {@link SortedMap} interface, the
-     * internal copy will be a {@link TreeMap}, otherwise it will be a 
-     * {@link HashMap}.
-     * 
-     * @deprecated Use {@link #SimpleHash(Map, ObjectWrapper)}
-     */
-    @Deprecated
-    public SimpleHash(Map map) {
-        this(map, null);
-    }
-
-    /**
      * Creates an empty simple hash using the specified object wrapper.
      * @param wrapper The object wrapper to use to wrap objects into
-     * {@link TemplateModel} instances. If {@code null}, {@link _StaticObjectWrappers#DEFAULT_OBJECT_WRAPPER} is used.
+     * {@link TemplateModel} instances. Not {@code null}.
      */
     public SimpleHash(ObjectWrapper wrapper) {
         super(wrapper);
@@ -127,9 +102,8 @@ public class SimpleHash extends WrappingTemplateModel implements TemplateHashMod
      *            The Map to use for the key/value pairs. It makes a copy for internal use. If the map implements the
      *            {@link SortedMap} interface, the internal copy will be a {@link TreeMap}, otherwise it will be a
      * @param wrapper
-     *            The object wrapper to use to wrap contained objects into {@link TemplateModel} instances. Using
-     *            {@code null} is deprecated but allowed, in which case will be used
-     *            {@link _StaticObjectWrappers#DEFAULT_OBJECT_WRAPPER} is used.
+     *            The object wrapper to use to wrap contained objects into {@link TemplateModel} instances. Not
+     *            {@code null}.
      */
     public SimpleHash(Map map, ObjectWrapper wrapper) {
         super(wrapper);
@@ -288,39 +262,6 @@ public class SimpleHash extends WrappingTemplateModel implements TemplateHashMod
             put((String) entry.getKey(), entry.getValue());
         }
     }
-    
-    /**
-     * Note that this method creates and returns a deep-copy of the underlying hash used
-     * internally. This could be a gotcha for some people
-     * at some point who want to alter something in the data model,
-     * but we should maintain our immutability semantics (at least using default SimpleXXX wrappers) 
-     * for the data model. It will recursively unwrap the stuff in the underlying container. 
-     */
-    public Map toMap() throws TemplateModelException {
-        if (unwrappedMap == null) {
-            Class mapClass = map.getClass();
-            Map m = null;
-            try {
-                m = (Map) mapClass.newInstance();
-            } catch (Exception e) {
-                throw new TemplateModelException("Error instantiating map of type " + mapClass.getName() + "\n" + e.getMessage());
-            }
-            // Create a copy to maintain immutability semantics and
-            // Do nested unwrapping of elements if necessary.
-            DefaultObjectWrapper ow = _StaticObjectWrappers.DEFAULT_OBJECT_WRAPPER;
-            for (Iterator it = map.entrySet().iterator(); it.hasNext(); ) {
-                Map.Entry entry = (Map.Entry) it.next();
-                Object key = entry.getKey();
-                Object value = entry.getValue();
-                if (value instanceof TemplateModel) {
-                    value = ow.unwrap((TemplateModel) value);
-                }
-                m.put(key, value);
-            }
-            unwrappedMap = m;
-        }
-        return unwrappedMap;
-    }
 
     /**
      * Returns the {@code toString()} of the underlying {@link Map}.
@@ -354,75 +295,4 @@ public class SimpleHash extends WrappingTemplateModel implements TemplateHashMod
     public KeyValuePairIterator keyValuePairIterator() {
         return new MapKeyValuePairIterator(map, getObjectWrapper());
     }
-
-    public SimpleHash synchronizedWrapper() {
-        return new SynchronizedHash();
-    }
-    
-    private class SynchronizedHash extends SimpleHash {
-
-        @Override
-        public boolean isEmpty() {
-            synchronized (SimpleHash.this) {
-                return SimpleHash.this.isEmpty();
-            }
-        }
-        
-        @Override
-        public void put(String key, Object obj) {
-            synchronized (SimpleHash.this) {
-                SimpleHash.this.put(key, obj);
-            }
-        }
-
-        @Override
-        public TemplateModel get(String key) throws TemplateModelException {
-            synchronized (SimpleHash.this) {
-                return SimpleHash.this.get(key);
-            }
-        }
-
-        @Override
-        public void remove(String key) {
-            synchronized (SimpleHash.this) {
-                SimpleHash.this.remove(key);
-            }
-        }
-
-        @Override
-        public int size() {
-            synchronized (SimpleHash.this) {
-                return SimpleHash.this.size();
-            }
-        }
-
-        @Override
-        public TemplateCollectionModel keys() {
-            synchronized (SimpleHash.this) {
-                return SimpleHash.this.keys();
-            }
-        }
-
-        @Override
-        public TemplateCollectionModel values() {
-            synchronized (SimpleHash.this) {
-                return SimpleHash.this.values();
-            }
-        }
-
-        @Override
-        public KeyValuePairIterator keyValuePairIterator() {
-            synchronized (SimpleHash.this) {
-                return SimpleHash.this.keyValuePairIterator();
-            }
-        }
-        
-        @Override
-        public Map toMap() throws TemplateModelException {
-            synchronized (SimpleHash.this) {
-                return SimpleHash.this.toMap();
-            }
-        }
-    
-    }
 }