freemarker-notifications mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From ddek...@apache.org
Subject [1/2] incubator-freemarker git commit: Removed the global static final ObjectWrapper-s. It had a "few" consequences:
Date Fri, 03 Mar 2017 20:18:44 GMT
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;
+    }
+}


Mime
View raw message