http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/3fd56062/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/PrimtiveArrayBackedReadOnlyList.java
----------------------------------------------------------------------
diff --git 
a/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/PrimtiveArrayBackedReadOnlyList.java
 
b/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/PrimtiveArrayBackedReadOnlyList.java
new file mode 100644
index 0000000..0cf7d80
--- /dev/null
+++ 
b/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/PrimtiveArrayBackedReadOnlyList.java
@@ -0,0 +1,47 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.freemarker.core.model.impl;
+
+import java.lang.reflect.Array;
+import java.util.AbstractList;
+
+/**
+ * Similar to {@link NonPrimitiveArrayBackedReadOnlyList}, but uses reflection 
so that it works with primitive arrays
+ * too. 
+ */
+class PrimtiveArrayBackedReadOnlyList extends AbstractList {
+    
+    private final Object array;
+    
+    PrimtiveArrayBackedReadOnlyList(Object array) {
+        this.array = array;
+    }
+
+    @Override
+    public Object get(int index) {
+        return Array.get(array, index);
+    }
+
+    @Override
+    public int size() {
+        return Array.getLength(array);
+    }
+
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/3fd56062/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/ReflectionCallableMemberDescriptor.java
----------------------------------------------------------------------
diff --git 
a/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/ReflectionCallableMemberDescriptor.java
 
b/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/ReflectionCallableMemberDescriptor.java
new file mode 100644
index 0000000..eb2ade5
--- /dev/null
+++ 
b/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/ReflectionCallableMemberDescriptor.java
@@ -0,0 +1,95 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.freemarker.core.model.impl;
+
+import java.lang.reflect.Constructor;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Member;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+
+import org.apache.freemarker.core.model.TemplateModel;
+import org.apache.freemarker.core.model.TemplateModelException;
+
+/**
+ * The most commonly used {@link CallableMemberDescriptor} implementation. 
+ */
+final class ReflectionCallableMemberDescriptor extends 
CallableMemberDescriptor {
+
+    private final Member/*Method|Constructor*/ member;
+    
+    /**
+     * Don't modify this array!
+     */
+    final Class[] paramTypes;
+    
+    ReflectionCallableMemberDescriptor(Method member, Class[] paramTypes) {
+        this.member = member;
+        this.paramTypes = paramTypes;
+    }
+
+    ReflectionCallableMemberDescriptor(Constructor member, Class[] paramTypes) 
{
+        this.member = member;
+        this.paramTypes = paramTypes;
+    }
+
+    @Override
+    TemplateModel invokeMethod(DefaultObjectWrapper ow, Object obj, Object[] 
args)
+            throws TemplateModelException, InvocationTargetException, 
IllegalAccessException {
+        return ow.invokeMethod(obj, (Method) member, args);
+    }
+
+    @Override
+    Object invokeConstructor(DefaultObjectWrapper ow, Object[] args)
+            throws IllegalArgumentException, InstantiationException, 
IllegalAccessException, InvocationTargetException {
+        return ((Constructor) member).newInstance(args);
+    }
+
+    @Override
+    String getDeclaration() {
+        return _MethodUtil.toString(member);
+    }
+    
+    @Override
+    boolean isConstructor() {
+        return member instanceof Constructor;
+    }
+    
+    @Override
+    boolean isStatic() {
+        return (member.getModifiers() & Modifier.STATIC) != 0;
+    }
+
+    @Override
+    boolean isVarargs() {
+        return _MethodUtil.isVarargs(member);
+    }
+
+    @Override
+    Class[] getParamTypes() {
+        return paramTypes;
+    }
+
+    @Override
+    String getName() {
+        return member.getName();
+    }
+    
+}

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/3fd56062/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/ResourceBundleModel.java
----------------------------------------------------------------------
diff --git 
a/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/ResourceBundleModel.java
 
b/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/ResourceBundleModel.java
new file mode 100644
index 0000000..31af451
--- /dev/null
+++ 
b/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/ResourceBundleModel.java
@@ -0,0 +1,181 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.freemarker.core.model.impl;
+
+import java.text.MessageFormat;
+import java.util.Enumeration;
+import java.util.Hashtable;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.MissingResourceException;
+import java.util.ResourceBundle;
+import java.util.Set;
+
+import org.apache.freemarker.core._DelayedJQuote;
+import org.apache.freemarker.core._TemplateModelException;
+import org.apache.freemarker.core.model.TemplateMethodModelEx;
+import org.apache.freemarker.core.model.TemplateModel;
+import org.apache.freemarker.core.model.TemplateModelException;
+
+/**
+ * <p>A hash model that wraps a resource bundle. Makes it convenient to store
+ * localized content in the data model. It also acts as a method model that 
will
+ * take a resource key and arbitrary number of arguments and will apply
+ * {@link MessageFormat} with arguments on the string represented by the 
key.</p>
+ *
+ * <p>Typical usages:</p>
+ * <ul>
+ * <li><tt>bundle.resourceKey</tt> will retrieve the object from resource 
bundle
+ * with key <tt>resourceKey</tt></li>
+ * <li><tt>bundle("patternKey", arg1, arg2, arg3)</tt> will retrieve the string
+ * from resource bundle with key <tt>patternKey</tt>, and will use it as a 
pattern
+ * for MessageFormat with arguments arg1, arg2 and arg3</li>
+ * </ul>
+ */
+public class ResourceBundleModel
+    extends
+    BeanModel
+    implements
+    TemplateMethodModelEx {
+
+    private Hashtable formats = null;
+
+    public ResourceBundleModel(ResourceBundle bundle, DefaultObjectWrapper 
wrapper) {
+        super(bundle, wrapper);
+    }
+
+    /**
+     * Overridden to invoke the get method of the resource bundle.
+     */
+    @Override
+    protected TemplateModel invokeGenericGet(Map keyMap, Class clazz, String 
key)
+    throws TemplateModelException {
+        try {
+            return wrap(((ResourceBundle) object).getObject(key));
+        } catch (MissingResourceException e) {
+            throw new _TemplateModelException(e,
+                    "No ", new _DelayedJQuote(key), " key in the 
ResourceBundle. "
+                    + "Note that conforming to the ResourceBundle Java API, 
this is an error and not just "
+                    + "a missing sub-variable (a null).");
+        }
+    }
+
+    /**
+     * Returns true if this bundle contains no objects.
+     */
+    @Override
+    public boolean isEmpty() {
+        return !((ResourceBundle) object).getKeys().hasMoreElements() &&
+            super.isEmpty();
+    }
+
+    @Override
+    public int size() {
+        return keySet().size();
+    }
+
+    @Override
+    protected Set keySet() {
+        Set set = super.keySet();
+        Enumeration e = ((ResourceBundle) object).getKeys();
+        while (e.hasMoreElements()) {
+            set.add(e.nextElement());
+        }
+        return set;
+    }
+
+    /**
+     * Takes first argument as a resource key, looks up a string in resource 
bundle
+     * with this key, then applies a MessageFormat.format on the string with 
the
+     * rest of the arguments. The created MessageFormats are cached for later 
reuse.
+     */
+    @Override
+    public Object exec(List arguments)
+        throws TemplateModelException {
+        // Must have at least one argument - the key
+        if (arguments.size() < 1)
+            throw new TemplateModelException("No message key was specified");
+        // Read it
+        Iterator it = arguments.iterator();
+        String key = unwrap((TemplateModel) it.next()).toString();
+        try {
+            if (!it.hasNext()) {
+                return wrap(((ResourceBundle) object).getObject(key));
+            }
+    
+            // Copy remaining arguments into an Object[]
+            int args = arguments.size() - 1;
+            Object[] params = new Object[args];
+            for (int i = 0; i < args; ++i)
+                params[i] = unwrap((TemplateModel) it.next());
+    
+            // Invoke format
+            return new BeanAndStringModel(format(key, params), wrapper);
+        } catch (MissingResourceException e) {
+            throw new TemplateModelException("No such key: " + key);
+        } catch (Exception e) {
+            throw new TemplateModelException(e.getMessage());
+        }
+    }
+
+    /**
+     * Provides direct access to caching format engine from code (instead of 
from script).
+     */
+    public String format(String key, Object[] params)
+        throws MissingResourceException {
+        // Check to see if we already have a cache for message formats
+        // and construct it if we don't
+        // NOTE: this block statement should be synchronized. However
+        // concurrent creation of two caches will have no harmful
+        // consequences, and we avoid a performance hit.
+        /* synchronized(this) */
+        {
+            if (formats == null)
+                formats = new Hashtable();
+        }
+
+        MessageFormat format = null;
+        // Check to see if we already have a requested MessageFormat cached
+        // and construct it if we don't
+        // NOTE: this block statement should be synchronized. However
+        // concurrent creation of two formats will have no harmful
+        // consequences, and we avoid a performance hit.
+        /* synchronized(formats) */
+        {
+            format = (MessageFormat) formats.get(key);
+            if (format == null) {
+                format = new MessageFormat(((ResourceBundle) 
object).getString(key));
+                format.setLocale(getBundle().getLocale());
+                formats.put(key, format);
+            }
+        }
+
+        // Perform the formatting. We synchronize on it in case it
+        // contains date formatting, which is not thread-safe.
+        synchronized (format) {
+            return format.format(params);
+        }
+    }
+
+    public ResourceBundle getBundle() {
+        return (ResourceBundle) object;
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/3fd56062/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/RestrictedObjectWrapper.java
----------------------------------------------------------------------
diff --git 
a/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/RestrictedObjectWrapper.java
 
b/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/RestrictedObjectWrapper.java
new file mode 100644
index 0000000..e456dc6
--- /dev/null
+++ 
b/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/RestrictedObjectWrapper.java
@@ -0,0 +1,98 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.freemarker.core.model.impl;
+
+import java.lang.ref.ReferenceQueue;
+import java.lang.ref.WeakReference;
+import java.util.Map;
+import java.util.WeakHashMap;
+
+import org.apache.freemarker.core.Version;
+import org.apache.freemarker.core.model.TemplateHashModel;
+import org.apache.freemarker.core.model.TemplateModel;
+import org.apache.freemarker.core.model.TemplateModelException;
+
+/**
+ * A restricted version of {@link DefaultObjectWrapper} that doesn't expose 
arbitrary object, just those that directly
+ * correspond to the {@link TemplateModel} sub-interfaces ({@code String}, 
{@code Map} and such). If it had to wrap
+ * other kind of objects, it will throw exception. It will also block {@code 
?api} calls on the values it wraps.
+ */
+public class RestrictedObjectWrapper extends DefaultObjectWrapper {
+
+    protected RestrictedObjectWrapper(Builder builder, boolean 
finalizeConstruction) {
+        super(builder, finalizeConstruction);
+    }
+
+    /**
+     * Called if a type other than the simple ones we know about is passed in. 
+     * In this implementation, this just throws an exception.
+     */
+    @Override
+    protected TemplateModel handleNonBasicTypes(Object obj) throws 
TemplateModelException {
+        throw new TemplateModelException("RestrictedObjectWrapper deliberately 
won't wrap this type: "
+                + obj.getClass().getName());
+    }
+
+    @Override
+    public TemplateHashModel wrapAsAPI(Object obj) throws 
TemplateModelException {
+        throw new TemplateModelException("RestrictedObjectWrapper deliberately 
doesn't allow ?api.");
+    }
+
+    protected static abstract class ExtendableBuilder<
+            ProductT extends RestrictedObjectWrapper, SelfT extends 
ExtendableBuilder<ProductT,
+            SelfT>> extends DefaultObjectWrapper.ExtendableBuilder<ProductT, 
SelfT> {
+
+        protected ExtendableBuilder(Version incompatibleImprovements, boolean 
isIncompImprsAlreadyNormalized) {
+            super(incompatibleImprovements, isIncompImprsAlreadyNormalized);
+        }
+
+    }
+
+    public static final class Builder extends 
ExtendableBuilder<RestrictedObjectWrapper, Builder> {
+
+        private final static Map<ClassLoader, Map<Builder, 
WeakReference<RestrictedObjectWrapper>>>
+                INSTANCE_CACHE = new WeakHashMap<>();
+
+        private final static ReferenceQueue<RestrictedObjectWrapper> 
INSTANCE_CACHE_REF_QUEUE = new ReferenceQueue<>();
+
+        public Builder(Version incompatibleImprovements) {
+            super(incompatibleImprovements, false);
+        }
+
+        @Override
+        public RestrictedObjectWrapper build() {
+            return DefaultObjectWrapperTCCLSingletonUtil.getSingleton(
+                    this, INSTANCE_CACHE, INSTANCE_CACHE_REF_QUEUE, 
ConstructorInvoker.INSTANCE);
+        }
+
+        private static class ConstructorInvoker
+                implements 
DefaultObjectWrapperTCCLSingletonUtil._ConstructorInvoker<RestrictedObjectWrapper,
 Builder> {
+
+            private static final ConstructorInvoker INSTANCE = new 
ConstructorInvoker();
+
+            @Override
+            public RestrictedObjectWrapper invoke(Builder builder) {
+                return new RestrictedObjectWrapper(builder, true);
+            }
+        }
+
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/3fd56062/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/SequenceAdapter.java
----------------------------------------------------------------------
diff --git 
a/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/SequenceAdapter.java
 
b/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/SequenceAdapter.java
new file mode 100644
index 0000000..e9b6156
--- /dev/null
+++ 
b/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/SequenceAdapter.java
@@ -0,0 +1,68 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.freemarker.core.model.impl;
+
+import java.util.AbstractList;
+
+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.TemplateSequenceModel;
+import org.apache.freemarker.core.util.UndeclaredThrowableException;
+
+/**
+ */
+class SequenceAdapter extends AbstractList implements TemplateModelAdapter {
+    private final DefaultObjectWrapper wrapper;
+    private final TemplateSequenceModel model;
+    
+    SequenceAdapter(TemplateSequenceModel model, DefaultObjectWrapper wrapper) 
{
+        this.model = model;
+        this.wrapper = wrapper;
+    }
+    
+    @Override
+    public TemplateModel getTemplateModel() {
+        return model;
+    }
+    
+    @Override
+    public int size() {
+        try {
+            return model.size();
+        } catch (TemplateModelException e) {
+            throw new UndeclaredThrowableException(e);
+        }
+    }
+    
+    @Override
+    public Object get(int index) {
+        try {
+            return wrapper.unwrap(model.get(index));
+        } catch (TemplateModelException e) {
+            throw new UndeclaredThrowableException(e);
+        }
+    }
+    
+    public TemplateSequenceModel getTemplateSequenceModel() {
+        return model;
+    }
+    
+}

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/3fd56062/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/SetAdapter.java
----------------------------------------------------------------------
diff --git 
a/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/SetAdapter.java
 
b/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/SetAdapter.java
new file mode 100644
index 0000000..0975ac4
--- /dev/null
+++ 
b/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/SetAdapter.java
@@ -0,0 +1,32 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.freemarker.core.model.impl;
+
+import java.util.Set;
+
+import org.apache.freemarker.core.model.TemplateCollectionModel;
+
+/**
+ */
+class SetAdapter extends CollectionAdapter implements Set {
+    SetAdapter(TemplateCollectionModel model, DefaultObjectWrapper wrapper) {
+        super(model, wrapper);
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/3fd56062/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/SimpleCollection.java
----------------------------------------------------------------------
diff --git 
a/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/SimpleCollection.java
 
b/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/SimpleCollection.java
new file mode 100644
index 0000000..cf399ab
--- /dev/null
+++ 
b/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/SimpleCollection.java
@@ -0,0 +1,138 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.freemarker.core.model.impl;
+
+import java.io.Serializable;
+import java.util.Collection;
+import java.util.Iterator;
+
+import org.apache.freemarker.core.model.ObjectWrapper;
+import org.apache.freemarker.core.model.TemplateCollectionModel;
+import org.apache.freemarker.core.model.TemplateModel;
+import org.apache.freemarker.core.model.TemplateModelException;
+import org.apache.freemarker.core.model.TemplateModelIterator;
+import org.apache.freemarker.core.model.WrappingTemplateModel;
+
+/**
+ * A simple implementation of {@link TemplateCollectionModel}.
+ * It's able to wrap <tt>java.util.Iterator</tt>-s and 
<tt>java.util.Collection</tt>-s.
+ * If you wrap an <tt>Iterator</tt>, the variable can be &lt;#list&gt;-ed only 
once!
+ *
+ * <p>Consider using {@link SimpleSequence} instead of this class if you want 
to wrap <tt>Iterator</tt>s.
+ * <tt>SimpleSequence</tt> will read all elements of the <tt>Iterator</tt>, 
and store them in a <tt>List</tt>
+ * (this may cause too high resource consumption in some applications), so you 
can list the variable
+ * for unlimited times. Also, if you want to wrap <tt>Collection</tt>s, and 
then list the resulting
+ * variable for many times, <tt>SimpleSequence</tt> may gives better 
performance, as the
+ * wrapping of non-<tt>TemplateModel</tt> objects happens only once.
+ *
+ * <p>This class is thread-safe. The returned {@link TemplateModelIterator}-s
+ * are <em>not</em> thread-safe.
+ */
+public class SimpleCollection extends WrappingTemplateModel
+implements TemplateCollectionModel, Serializable {
+    
+    private boolean iteratorOwned;
+    private final Iterator iterator;
+    private final Collection collection;
+
+    public SimpleCollection(Iterator iterator, ObjectWrapper wrapper) {
+        super(wrapper);
+        this.iterator = iterator;
+        collection = null;
+    }
+
+    public SimpleCollection(Collection collection, ObjectWrapper wrapper) {
+        super(wrapper);
+        this.collection = collection;
+        iterator = null;
+    }
+
+    /**
+     * 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 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() {
+        return iterator != null
+                ? new SimpleTemplateModelIterator(iterator, false)
+                : new SimpleTemplateModelIterator(collection.iterator(), true);
+    }
+    
+    /**
+     * Wraps an {@link Iterator}; not thread-safe. The encapsulated {@link 
Iterator} may be accessible from multiple
+     * threads (as multiple {@link SimpleTemplateModelIterator} instance can 
wrap the same {@link Iterator} instance),
+     * but if the {@link Iterator} was marked in the constructor as shared, 
the first thread which uses the
+     * {@link Iterator} will monopolize that.
+     */
+    private class SimpleTemplateModelIterator implements TemplateModelIterator 
{
+        
+        private final Iterator iterator;
+        private boolean iteratorOwnedByMe;
+            
+        SimpleTemplateModelIterator(Iterator iterator, boolean 
iteratorOwnedByMe) {
+            this.iterator = iterator;
+            this.iteratorOwnedByMe = iteratorOwnedByMe;
+        }
+
+        @Override
+        public TemplateModel next() throws TemplateModelException {
+            if (!iteratorOwnedByMe) { 
+                synchronized (SimpleCollection.this) {
+                    checkIteratorNotOwned();
+                    iteratorOwned = true;
+                    iteratorOwnedByMe = true;
+                }
+            }
+            
+            if (!iterator.hasNext()) {
+                throw new TemplateModelException("The collection has no more 
items.");
+            }
+            
+            Object value  = iterator.next();
+            return value instanceof TemplateModel ? (TemplateModel) value : 
wrap(value);
+        }
+
+        @Override
+        public boolean hasNext() throws TemplateModelException {
+            // Calling hasNext may looks safe, but I have met sync. problems.
+            if (!iteratorOwnedByMe) {
+                synchronized (SimpleCollection.this) {
+                    checkIteratorNotOwned();
+                }
+            }
+            
+            return iterator.hasNext();
+        }
+        
+        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/3fd56062/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/SimpleDate.java
----------------------------------------------------------------------
diff --git 
a/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/SimpleDate.java
 
b/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/SimpleDate.java
new file mode 100644
index 0000000..cf6e753
--- /dev/null
+++ 
b/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/SimpleDate.java
@@ -0,0 +1,85 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.freemarker.core.model.impl;
+
+import java.io.Serializable;
+
+import org.apache.freemarker.core.model.TemplateDateModel;
+
+/**
+ * A simple implementation of the <tt>TemplateDateModel</tt>
+ * interface. Note that this class is immutable.
+ * <p>This class is thread-safe.
+ */
+public class SimpleDate implements TemplateDateModel, Serializable {
+    private final java.util.Date date;
+    private final int type;
+    
+    /**
+     * Creates a new date model wrapping the specified date object and
+     * having DATE type.
+     */
+    public SimpleDate(java.sql.Date date) {
+        this(date, DATE);
+    }
+    
+    /**
+     * Creates a new date model wrapping the specified time object and
+     * having TIME type.
+     */
+    public SimpleDate(java.sql.Time time) {
+        this(time, TIME);
+    }
+    
+    /**
+     * Creates a new date model wrapping the specified time object and
+     * having DATETIME type.
+     */
+    public SimpleDate(java.sql.Timestamp datetime) {
+        this(datetime, DATETIME);
+    }
+    
+    /**
+     * Creates a new date model wrapping the specified date object and
+     * having the specified type.
+     */
+    public SimpleDate(java.util.Date date, int type) {
+        if (date == null) {
+            throw new IllegalArgumentException("date == null");
+        }
+        this.date = date;
+        this.type = type;
+    }
+    
+    @Override
+    public java.util.Date getAsDate() {
+        return date;
+    }
+
+    @Override
+    public int getDateType() {
+        return type;
+    }
+    
+    @Override
+    public String toString() {
+        return date.toString();
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/3fd56062/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/SimpleHash.java
----------------------------------------------------------------------
diff --git 
a/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/SimpleHash.java
 
b/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/SimpleHash.java
new file mode 100644
index 0000000..f520c3d
--- /dev/null
+++ 
b/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/SimpleHash.java
@@ -0,0 +1,296 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.freemarker.core.model.impl;
+
+import java.io.Serializable;
+import java.util.ConcurrentModificationException;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.SortedMap;
+import java.util.TreeMap;
+
+import org.apache.freemarker.core._DelayedJQuote;
+import org.apache.freemarker.core._TemplateModelException;
+import org.apache.freemarker.core.model.ObjectWrapper;
+import org.apache.freemarker.core.model.TemplateBooleanModel;
+import org.apache.freemarker.core.model.TemplateCollectionModel;
+import org.apache.freemarker.core.model.TemplateHashModelEx;
+import org.apache.freemarker.core.model.TemplateHashModelEx2;
+import org.apache.freemarker.core.model.TemplateModel;
+import org.apache.freemarker.core.model.TemplateModelException;
+import org.apache.freemarker.core.model.WrappingTemplateModel;
+
+/**
+ * A simple implementation of the {@link TemplateHashModelEx} interface, using 
its own underlying {@link Map} or
+ * {@link SortedMap} for storing the hash entries. If you are wrapping an 
already existing {@link Map}, you should
+ * certainly use {@link DefaultMapAdapter} instead (see comparison below).
+ *
+ * <p>
+ * This class is thread-safe if you don't call modifying methods (like {@link 
#put(String, Object)},
+ * {@link #remove(String)}, etc.) after you have made the object available for 
multiple threads (assuming you have
+ * published it safely to the other threads; see JSR-133 Java Memory Model). 
These methods aren't called by FreeMarker,
+ * so it's usually not a concern.
+ * 
+ * <p>
+ * <b>{@link SimpleHash} VS {@link DefaultMapAdapter} - Which to use when?</b>
+ * 
+ * <p>
+ * For a {@link Map} that exists regardless of FreeMarker, only you need to 
access it from templates,
+ * {@link DefaultMapAdapter} should be the default choice, as it reflects the 
exact behavior of the underlying
+ * {@link Map} (no surprises), can be unwrapped to the originally wrapped 
object (important when passing it to Java
+ * methods from the template), and has more predictable performance (no 
spikes).
+ * 
+ * <p>
+ * For a hash that's made specifically to be used from templates, creating an 
empty {@link SimpleHash} then filling it
+ * with {@link SimpleHash#put(String, Object)} is usually the way to go, as 
the resulting hash is significantly faster
+ * to read from templates than a {@link DefaultMapAdapter} (though it's 
somewhat slower to read from a plain Java method
+ * to which it had to be passed adapted to a {@link Map}).
+ * 
+ * <p>
+ * It also matters if for how many times will the <em>same</em> {@link Map} 
entry be read from the template(s) later, on
+ * average. If, on average, you read each entry for more than 4 times, {@link 
SimpleHash} will be most certainly faster,
+ * but if for 2 times or less (and especially if not at all) then {@link 
DefaultMapAdapter} will be faster. Before
+ * choosing based on performance though, pay attention to the behavioral 
differences; {@link SimpleHash} will
+ * shallow-copy the original {@link Map} at construction time, so key order 
will be lost in some cases, and it won't
+ * reflect {@link Map} content changes after the {@link SimpleHash} 
construction, also {@link SimpleHash} can't be
+ * unwrapped to the original {@link Map} instance.
+ *
+ * @see DefaultMapAdapter
+ * @see TemplateHashModelEx
+ */
+public class SimpleHash extends WrappingTemplateModel implements 
TemplateHashModelEx2, Serializable {
+
+    private final Map map;
+    private boolean putFailed;
+    private Map unwrappedMap;
+
+    /**
+     * Creates an empty simple hash using the specified object wrapper.
+     * @param wrapper The object wrapper to use to wrap objects into
+     * {@link TemplateModel} instances. Not {@code null}.
+     */
+    public SimpleHash(ObjectWrapper wrapper) {
+        super(wrapper);
+        map = new HashMap();
+    }
+
+    /**
+     * Creates a new hash by shallow-coping (possibly cloning) the underlying 
map; in many applications you should use
+     * {@link DefaultMapAdapter} instead.
+     *
+     * @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
+     * @param wrapper
+     *            The object wrapper to use to wrap contained objects into 
{@link TemplateModel} instances. Not
+     *            {@code null}.
+     */
+    public SimpleHash(Map map, ObjectWrapper wrapper) {
+        super(wrapper);
+        Map mapCopy;
+        try {
+            mapCopy = copyMap(map);
+        } catch (ConcurrentModificationException cme) {
+            //This will occur extremely rarely.
+            //If it does, we just wait 5 ms and try again. If 
+            // the ConcurrentModificationException
+            // is thrown again, we just let it bubble up this time.
+            // TODO: Maybe we should log here.
+            try {
+                Thread.sleep(5);
+            } catch (InterruptedException ie) {
+                // Ignored
+            }
+            synchronized (map) {
+                mapCopy = copyMap(map);
+            }
+        }
+        this.map = mapCopy;
+    }
+
+    protected Map copyMap(Map map) {
+        if (map instanceof HashMap) {
+            return (Map) ((HashMap) map).clone();
+        }
+        if (map instanceof SortedMap) {
+            if (map instanceof TreeMap) {
+                return (Map) ((TreeMap) map).clone();
+            } else {
+                return new TreeMap((SortedMap) map);
+            }
+        } 
+        return new HashMap(map);
+    }
+
+    /**
+     * Adds a key-value entry to this hash.
+     *
+     * @param key
+     *            The name by which the object is identified in the template.
+     * @param value
+     *            The value to which the name will be associated. This will 
only be wrapped to {@link TemplateModel}
+     *            lazily when it's first read.
+     */
+    public void put(String key, Object value) {
+        map.put(key, value);
+        unwrappedMap = null;
+    }
+
+    /**
+     * Puts a boolean in the map
+     *
+     * @param key the name by which the resulting <tt>TemplateModel</tt>
+     * is identified in the template.
+     * @param b the boolean to store.
+     */
+    public void put(String key, boolean b) {
+        put(key, b ? TemplateBooleanModel.TRUE : TemplateBooleanModel.FALSE);
+    }
+
+    @Override
+    public TemplateModel get(String key) throws TemplateModelException {
+        Object result;
+        try {
+            result = map.get(key);
+        } catch (ClassCastException e) {
+            throw new _TemplateModelException(e,
+                    "ClassCastException while getting Map entry with String 
key ",
+                    new _DelayedJQuote(key));
+        } catch (NullPointerException e) {
+            throw new _TemplateModelException(e,
+                    "NullPointerException while getting Map entry with String 
key ",
+                    new _DelayedJQuote(key));
+        }
+        // The key to use for putting -- it's the key that already exists in
+        // the map (either key or charKey below). This way, we'll never put a 
+        // new key in the map, avoiding spurious 
ConcurrentModificationException
+        // from another thread iterating over the map, see bug #1939742 in 
+        // SourceForge tracker.
+        Object putKey = null;
+        if (result == null) {
+            // Check for Character key if this is a single-character string.
+            // In SortedMap-s, however, we can't do that safely, as it can 
cause ClassCastException.
+            if (key.length() == 1 && !(map instanceof SortedMap)) {
+                Character charKey = Character.valueOf(key.charAt(0));
+                try {
+                    result = map.get(charKey);
+                    if (result != null || map.containsKey(charKey)) {
+                        putKey = charKey;
+                    }
+                } catch (ClassCastException e) {
+                    throw new _TemplateModelException(e,
+                            "ClassCastException while getting Map entry with 
Character key ",
+                            new _DelayedJQuote(key));
+                } catch (NullPointerException e) {
+                    throw new _TemplateModelException(e,
+                            "NullPointerException while getting Map entry with 
Character key ",
+                            new _DelayedJQuote(key));
+                }
+            }
+            if (putKey == null) {
+                if (!map.containsKey(key)) {
+                    return null;
+                } else {
+                    putKey = key;
+                }
+            }
+        } else {
+            putKey = key;
+        }
+        
+        if (result instanceof TemplateModel) {
+            return (TemplateModel) result;
+        }
+        
+        TemplateModel tm = wrap(result);
+        if (!putFailed) {
+            try {
+                map.put(putKey, tm);
+            } catch (Exception e) {
+                // If it's immutable or something, we just keep going.
+                putFailed = true;
+            }
+        }
+        return tm;
+    }
+
+    /**
+     * Tells if the map contains a key or not, regardless if the associated 
value is {@code null} or not.
+     * @since 2.3.20
+     */
+    public boolean containsKey(String key) {
+        return map.containsKey(key);
+    }
+
+    /**
+     * Removes the given key from the underlying map.
+     *
+     * @param key the key to be removed
+     */
+    public void remove(String key) {
+        map.remove(key);
+    }
+
+    /**
+     * Adds all the key/value entries in the map
+     * @param m the map with the entries to add, the keys are assumed to be 
strings.
+     */
+
+    public void putAll(Map m) {
+        for (Iterator it = m.entrySet().iterator(); it.hasNext(); ) {
+            Map.Entry entry = (Map.Entry) it.next();
+            put((String) entry.getKey(), entry.getValue());
+        }
+    }
+
+    /**
+     * Returns the {@code toString()} of the underlying {@link Map}.
+     */
+    @Override
+    public String toString() {
+        return map.toString();
+    }
+
+    @Override
+    public int size() {
+        return map.size();
+    }
+
+    @Override
+    public boolean isEmpty() {
+        return map == null || map.isEmpty();
+    }
+
+    @Override
+    public TemplateCollectionModel keys() {
+        return new SimpleCollection(map.keySet(), getObjectWrapper());
+    }
+
+    @Override
+    public TemplateCollectionModel values() {
+        return new SimpleCollection(map.values(), getObjectWrapper());
+    }
+
+    @Override
+    public KeyValuePairIterator keyValuePairIterator() {
+        return new MapKeyValuePairIterator(map, getObjectWrapper());
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/3fd56062/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/SimpleMethod.java
----------------------------------------------------------------------
diff --git 
a/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/SimpleMethod.java
 
b/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/SimpleMethod.java
new file mode 100644
index 0000000..ae5c531
--- /dev/null
+++ 
b/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/SimpleMethod.java
@@ -0,0 +1,174 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.freemarker.core.model.impl;
+
+import java.lang.reflect.Array;
+import java.lang.reflect.Member;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.List;
+
+import org.apache.freemarker.core._DelayedFTLTypeDescription;
+import org.apache.freemarker.core._DelayedOrdinal;
+import org.apache.freemarker.core._ErrorDescriptionBuilder;
+import org.apache.freemarker.core._TemplateModelException;
+import org.apache.freemarker.core.model.ObjectWrapperAndUnwrapper;
+import org.apache.freemarker.core.model.TemplateMarkupOutputModel;
+import org.apache.freemarker.core.model.TemplateModel;
+import org.apache.freemarker.core.model.TemplateModelException;
+import org.apache.freemarker.core.util._ClassUtil;
+
+/**
+ * This class is used for as a base for non-overloaded method models and for 
constructors.
+ * (For overloaded methods and constructors see {@link OverloadedMethods}.)
+ */
+class SimpleMethod {
+    
+    static final String MARKUP_OUTPUT_TO_STRING_TIP
+            = "A markup output value can be converted to markup string like 
value?markup_string. "
+              + "But consider if the Java method whose argument it will be can 
handle markup strings properly.";
+    
+    private final Member member;
+    private final Class[] argTypes;
+    
+    protected SimpleMethod(Member member, Class[] argTypes) {
+        this.member = member;
+        this.argTypes = argTypes;
+    }
+    
+    Object[] unwrapArguments(List arguments, DefaultObjectWrapper wrapper) 
throws TemplateModelException {
+        if (arguments == null) {
+            arguments = Collections.EMPTY_LIST;
+        }
+        boolean isVarArg = _MethodUtil.isVarargs(member);
+        int typesLen = argTypes.length;
+        if (isVarArg) {
+            if (typesLen - 1 > arguments.size()) {
+                throw new _TemplateModelException(
+                        _MethodUtil.invocationErrorMessageStart(member),
+                        " takes at least ", Integer.valueOf(typesLen - 1),
+                        typesLen - 1 == 1 ? " argument" : " arguments", ", but 
",
+                        Integer.valueOf(arguments.size()), " was given.");
+            }
+        } else if (typesLen != arguments.size()) {
+            throw new _TemplateModelException(
+                    _MethodUtil.invocationErrorMessageStart(member), 
+                    " takes ", Integer.valueOf(typesLen), typesLen == 1 ? " 
argument" : " arguments", ", but ",
+                    Integer.valueOf(arguments.size()), " was given.");
+        }
+         
+        return unwrapArguments(arguments, argTypes, isVarArg, wrapper);
+    }
+
+    private Object[] unwrapArguments(List args, Class[] argTypes, boolean 
isVarargs,
+            DefaultObjectWrapper w)
+    throws TemplateModelException {
+        if (args == null) return null;
+        
+        int typesLen = argTypes.length;
+        int argsLen = args.size();
+        
+        Object[] unwrappedArgs = new Object[typesLen];
+        
+        // Unwrap arguments:
+        Iterator it = args.iterator();
+        int normalArgCnt = isVarargs ? typesLen - 1 : typesLen; 
+        int argIdx = 0;
+        while (argIdx < normalArgCnt) {
+            Class argType = argTypes[argIdx];
+            TemplateModel argVal = (TemplateModel) it.next();
+            Object unwrappedArgVal = w.tryUnwrapTo(argVal, argType);
+            if (unwrappedArgVal == 
ObjectWrapperAndUnwrapper.CANT_UNWRAP_TO_TARGET_CLASS) {
+                throw createArgumentTypeMismarchException(argIdx, argVal, 
argType);
+            }
+            if (unwrappedArgVal == null && argType.isPrimitive()) {
+                throw createNullToPrimitiveArgumentException(argIdx, argType); 
+            }
+            
+            unwrappedArgs[argIdx++] = unwrappedArgVal;
+        }
+        if (isVarargs) {
+            // The last argType, which is the vararg type, wasn't processed 
yet.
+            
+            Class varargType = argTypes[typesLen - 1];
+            Class varargItemType = varargType.getComponentType();
+            if (!it.hasNext()) {
+                unwrappedArgs[argIdx++] = Array.newInstance(varargItemType, 0);
+            } else {
+                TemplateModel argVal = (TemplateModel) it.next();
+                
+                Object unwrappedArgVal;
+                // We first try to treat the last argument as a vararg *array*.
+                // This is consistent to what OverloadedVarArgMethod does.
+                if (argsLen - argIdx == 1
+                        && (unwrappedArgVal = w.tryUnwrapTo(argVal, 
varargType))
+                            != 
ObjectWrapperAndUnwrapper.CANT_UNWRAP_TO_TARGET_CLASS) {
+                    // It was a vararg array.
+                    unwrappedArgs[argIdx++] = unwrappedArgVal;
+                } else {
+                    // It wasn't a vararg array, so we assume it's a vararg
+                    // array *item*, possibly followed by further ones.
+                    int varargArrayLen = argsLen - argIdx;
+                    Object varargArray = Array.newInstance(varargItemType, 
varargArrayLen);
+                    for (int varargIdx = 0; varargIdx < varargArrayLen; 
varargIdx++) {
+                        TemplateModel varargVal = (TemplateModel) (varargIdx 
== 0 ? argVal : it.next());
+                        Object unwrappedVarargVal = w.tryUnwrapTo(varargVal, 
varargItemType);
+                        if (unwrappedVarargVal == 
ObjectWrapperAndUnwrapper.CANT_UNWRAP_TO_TARGET_CLASS) {
+                            throw createArgumentTypeMismarchException(
+                                    argIdx + varargIdx, varargVal, 
varargItemType);
+                        }
+                        
+                        if (unwrappedVarargVal == null && 
varargItemType.isPrimitive()) {
+                            throw 
createNullToPrimitiveArgumentException(argIdx + varargIdx, varargItemType); 
+                        }
+                        Array.set(varargArray, varargIdx, unwrappedVarargVal);
+                    }
+                    unwrappedArgs[argIdx++] = varargArray;
+                }
+            }
+        }
+        
+        return unwrappedArgs;
+    }
+
+    private TemplateModelException createArgumentTypeMismarchException(
+            int argIdx, TemplateModel argVal, Class targetType) {
+        _ErrorDescriptionBuilder desc = new _ErrorDescriptionBuilder(
+                _MethodUtil.invocationErrorMessageStart(member), " couldn't be 
called: Can't convert the ",
+                new _DelayedOrdinal(Integer.valueOf(argIdx + 1)),
+                " argument's value to the target Java type, ", 
_ClassUtil.getShortClassName(targetType),
+                ". The type of the actual value was: ", new 
_DelayedFTLTypeDescription(argVal));
+        if (argVal instanceof TemplateMarkupOutputModel && 
(targetType.isAssignableFrom(String.class))) {
+            desc.tip(MARKUP_OUTPUT_TO_STRING_TIP);
+        }
+        return new _TemplateModelException(desc);
+    }
+
+    private TemplateModelException createNullToPrimitiveArgumentException(int 
argIdx, Class targetType) {
+        return new _TemplateModelException(
+                _MethodUtil.invocationErrorMessageStart(member), " couldn't be 
called: The value of the ",
+                new _DelayedOrdinal(Integer.valueOf(argIdx + 1)),
+                " argument was null, but the target Java parameter type (", 
_ClassUtil.getShortClassName(targetType),
+                ") is primitive and so can't store null.");
+    }
+    
+    protected Member getMember() {
+        return member;
+    }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/3fd56062/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/SimpleNumber.java
----------------------------------------------------------------------
diff --git 
a/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/SimpleNumber.java
 
b/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/SimpleNumber.java
new file mode 100644
index 0000000..6f588b6
--- /dev/null
+++ 
b/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/SimpleNumber.java
@@ -0,0 +1,77 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.freemarker.core.model.impl;
+
+import java.io.Serializable;
+
+import org.apache.freemarker.core.model.TemplateNumberModel;
+
+
+/**
+ * A simple implementation of the <tt>TemplateNumberModel</tt>
+ * interface. Note that this class is immutable.
+ *
+ * <p>This class is thread-safe.
+ */
+public final class SimpleNumber implements TemplateNumberModel, Serializable {
+
+    /**
+     * @serial the value of this <tt>SimpleNumber</tt> 
+     */
+    private final Number value;
+
+    public SimpleNumber(Number value) {
+        this.value = value;
+    }
+
+    public SimpleNumber(byte val) {
+        value = Byte.valueOf(val);
+    }
+
+    public SimpleNumber(short val) {
+        value = Short.valueOf(val);
+    }
+
+    public SimpleNumber(int val) {
+        value = Integer.valueOf(val);
+    }
+
+    public SimpleNumber(long val) {
+        value = Long.valueOf(val);
+    }
+
+    public SimpleNumber(float val) {
+        value = Float.valueOf(val);
+    }
+    
+    public SimpleNumber(double val) {
+        value = Double.valueOf(val);
+    }
+
+    @Override
+    public Number getAsNumber() {
+        return value;
+    }
+
+    @Override
+    public String toString() {
+        return value.toString();
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/3fd56062/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/SimpleScalar.java
----------------------------------------------------------------------
diff --git 
a/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/SimpleScalar.java
 
b/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/SimpleScalar.java
new file mode 100644
index 0000000..79a8820
--- /dev/null
+++ 
b/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/SimpleScalar.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.model.impl;
+
+import java.io.Serializable;
+
+import org.apache.freemarker.core.model.TemplateScalarModel;
+
+/**
+ * A simple implementation of the <tt>TemplateScalarModel</tt>
+ * interface, using a <tt>String</tt>.
+ * As of version 2.0 this object is immutable.
+ *
+ * <p>This class is thread-safe.
+ *
+ * @see SimpleSequence
+ * @see SimpleHash
+ */
+public final class SimpleScalar 
+implements TemplateScalarModel, Serializable {
+    
+    /**
+     * @serial the value of this <tt>SimpleScalar</tt> if it wraps a
+     * <tt>String</tt>.
+     */
+    private final String value;
+
+    /**
+     * Constructs a <tt>SimpleScalar</tt> containing a string value.
+     * @param value the string value. If this is {@code null}, its value in 
FTL will be {@code ""}.
+     */
+    public SimpleScalar(String value) {
+        this.value = value;
+    }
+
+    @Override
+    public String getAsString() {
+        return (value == null) ? "" : value;
+    }
+
+    @Override
+    public String toString() {
+        // [2.4] Shouldn't return null
+        return value;
+    }
+    
+    /**
+     * Same as calling the constructor, except that for a {@code null} 
parameter it returns null. 
+     * 
+     * @since 2.3.23
+     */
+    public static SimpleScalar newInstanceOrNull(String s) {
+        return s != null ? new SimpleScalar(s) : null;
+    }
+    
+}

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/3fd56062/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/SimpleSequence.java
----------------------------------------------------------------------
diff --git 
a/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/SimpleSequence.java
 
b/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/SimpleSequence.java
new file mode 100644
index 0000000..1b949f1
--- /dev/null
+++ 
b/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/SimpleSequence.java
@@ -0,0 +1,162 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.freemarker.core.model.impl;
+
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.Collection;
+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.WrappingTemplateModel;
+
+/**
+ * A simple implementation of the {@link TemplateSequenceModel} interface, 
using its own underlying {@link List} for
+ * storing the list items. If you are wrapping an already existing {@link 
List} or {@code array}, you should certainly
+ * use {@link DefaultMapAdapter} or {@link DefaultArrayAdapter} (see 
comparison below).
+ * 
+ * <p>
+ * This class is thread-safe if you don't call modifying methods (like {@link 
#add(Object)}) after you have made the
+ * object available for multiple threads (assuming you have published it 
safely to the other threads; see JSR-133 Java
+ * Memory Model). These methods aren't called by FreeMarker, so it's usually 
not a concern.
+ * 
+ * <p>
+ * <b>{@link SimpleSequence} VS {@link DefaultListAdapter}/{@link 
DefaultArrayAdapter} - Which to use when?</b>
+ * </p>
+ * 
+ * <p>
+ * For a {@link List} or {@code array} that exists regardless of FreeMarker, 
only you need to access it from templates,
+ * {@link DefaultMapAdapter} should be the default choice, as it can be 
unwrapped to the originally wrapped object
+ * (important when passing it to Java methods from the template). It also has 
more predictable performance (no spikes).
+ * 
+ * <p>
+ * For a sequence that's made specifically to be used from templates, creating 
an empty {@link SimpleSequence} then
+ * filling it with {@link SimpleSequence#add(Object)} is usually the way to 
go, as the resulting sequence is
+ * significantly faster to read from templates than a {@link 
DefaultListAdapter} (though it's somewhat slower to read
+ * from a plain Java method to which it had to be passed adapted to a {@link 
List}).
+ * 
+ * <p>
+ * It also matters if for how many times will the <em>same</em> {@link List} 
entry be read from the template(s) later,
+ * on average. If, on average, you read each entry for more than 4 times, 
{@link SimpleSequence} will be most
+ * certainly faster, but if for 2 times or less (and especially if not at all) 
then {@link DefaultMapAdapter} will
+ * be faster. Before choosing based on performance though, pay attention to 
the behavioral differences;
+ * {@link SimpleSequence} will shallow-copy the original {@link List} at 
construction time, so it won't reflect
+ * {@link List} content changes after the {@link SimpleSequence} construction, 
also {@link SimpleSequence} can't be
+ * unwrapped to the original wrapped instance.
+ *
+ * @see DefaultListAdapter
+ * @see DefaultArrayAdapter
+ * @see TemplateSequenceModel
+ */
+public class SimpleSequence extends WrappingTemplateModel implements 
TemplateSequenceModel, Serializable {
+
+    /**
+     * 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;
+
+    /**
+     * 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. Not
+     *            {@code null}.
+     */
+    public SimpleSequence(ObjectWrapper wrapper) {
+        super(wrapper);
+        list = new ArrayList();
+    }
+    
+    /**
+     * Constructs an empty simple sequence with preallocated capacity.
+     * 
+     * @param wrapper
+     *            See the similar parameter of {@link 
SimpleSequence#SimpleSequence(ObjectWrapper)}.
+     * 
+     * @since 2.3.21
+     */
+    public SimpleSequence(int capacity, ObjectWrapper wrapper) {
+        super(wrapper);
+        list = new ArrayList(capacity);
+    }    
+    
+    /**
+     * Constructs a simple sequence that will contain the elements from the 
specified {@link Collection}; consider
+     * using {@link DefaultListAdapter} instead.
+     * 
+     * @param collection
+     *            The collection containing the initial items of the sequence. 
A shallow copy of this collection is made
+     *            immediately for internal use (thus, later modification on 
the parameter collection won't be visible in
+     *            the resulting sequence). The items however, will be only 
wrapped with the {@link ObjectWrapper}
+     *            lazily, when first needed.
+     * @param wrapper
+     *            See the similar parameter of {@link 
SimpleSequence#SimpleSequence(ObjectWrapper)}.
+     */
+    public SimpleSequence(Collection collection, ObjectWrapper wrapper) {
+        super(wrapper);
+        list = new ArrayList(collection);
+    }
+
+    /**
+     * Adds an arbitrary object to the end of this sequence. If the newly 
added object does not implement the
+     * {@link TemplateModel} interface, it will be wrapped into the 
appropriate {@link TemplateModel} interface when
+     * it's first read (lazily).
+     *
+     * @param obj
+     *            The object to be added.
+     */
+    public void add(Object obj) {
+        list.add(obj);
+    }
+
+    /**
+     * 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.
+     */
+    @Override
+    public TemplateModel get(int index) throws TemplateModelException {
+        try {
+            Object value = list.get(index);
+            if (value instanceof TemplateModel) {
+                return (TemplateModel) value;
+            }
+            TemplateModel tm = wrap(value);
+            list.set(index, tm);
+            return tm;
+        } catch (IndexOutOfBoundsException e) {
+            return null;
+        }
+    }
+
+    @Override
+    public int size() {
+        return list.size();
+    }
+
+    @Override
+    public String toString() {
+        return list.toString();
+    }
+
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/3fd56062/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/SingletonCustomizer.java
----------------------------------------------------------------------
diff --git 
a/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/SingletonCustomizer.java
 
b/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/SingletonCustomizer.java
new file mode 100644
index 0000000..e4a0e5a
--- /dev/null
+++ 
b/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/SingletonCustomizer.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.model.impl;
+
+import org.apache.freemarker.core.model.ObjectWrapper;
+
+/**
+ * Marker interface useful when used together with {@link 
MethodAppearanceFineTuner} and such customizer objects, to
+ * indicate that it <b>doesn't contain reference to the {@link 
ObjectWrapper}</b> (so beware with non-static inner
+ * classes) and can be and should be used in call introspection cache keys. 
This also implies that you won't
+ * invoke many instances of the class, rather just reuse the same (or same 
few) instances over and over. Furthermore,
+ * the instances must be thread-safe. The typical pattern in which this 
instance should be used is like this:
+ * 
+ * <pre>static class MyMethodAppearanceFineTuner implements 
MethodAppearanceFineTuner, SingletonCustomizer {
+ *      
+ *    // This is the singleton:
+ *    static final MyMethodAppearanceFineTuner INSTANCE = new 
MyMethodAppearanceFineTuner();
+ *     
+ *    // Private, so it can't be constructed from outside this class.
+ *    private MyMethodAppearanceFineTuner() { }
+ *
+ *    &#64;Override
+ *    public void fineTuneMethodAppearance(...) {
+ *       // Do something here, only using the parameters and maybe some other 
singletons. 
+ *       ...
+ *    }
+ *     
+ * }</pre>
+ *
+ * @since 2.3.21
+ */
+public interface SingletonCustomizer {
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/3fd56062/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/StaticModel.java
----------------------------------------------------------------------
diff --git 
a/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/StaticModel.java
 
b/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/StaticModel.java
new file mode 100644
index 0000000..fbee788
--- /dev/null
+++ 
b/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/StaticModel.java
@@ -0,0 +1,177 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.freemarker.core.model.impl;
+
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+
+import org.apache.freemarker.core._CoreLogs;
+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.slf4j.Logger;
+
+/**
+ * Wraps the static fields and methods of a class in a
+ * {@link org.apache.freemarker.core.model.TemplateHashModel}.
+ * Fields are wrapped using {@link DefaultObjectWrapper#wrap(Object)}, and
+ * methods are wrapped into an appropriate {@link 
org.apache.freemarker.core.model.TemplateMethodModelEx} instance.
+ * Unfortunately, there is currently no support for bean property-style
+ * calls of static methods, similar to that in {@link BeanModel}.
+ */
+final class StaticModel implements TemplateHashModelEx {
+    
+    private static final Logger LOG = _CoreLogs.OBJECT_WRAPPER;
+    
+    private final Class clazz;
+    private final DefaultObjectWrapper wrapper;
+    private final Map map = new HashMap();
+
+    StaticModel(Class clazz, DefaultObjectWrapper wrapper) throws 
TemplateModelException {
+        this.clazz = clazz;
+        this.wrapper = wrapper;
+        populate();
+    }
+
+    /**
+     * Returns the field or method named by the <tt>key</tt>
+     * parameter.
+     */
+    @Override
+    public TemplateModel get(String key) throws TemplateModelException {
+        Object model = map.get(key);
+        // Simple method, overloaded method or final field -- these have 
cached 
+        // template models
+        if (model instanceof TemplateModel)
+            return (TemplateModel) model;
+        // Non-final field; this must be evaluated on each call.
+        if (model instanceof Field) {
+            try {
+                return wrapper.getOuterIdentity().wrap(((Field) 
model).get(null));
+            } catch (IllegalAccessException e) {
+                throw new TemplateModelException(
+                    "Illegal access for field " + key + " of class " + 
clazz.getName());
+            }
+        }
+
+        throw new TemplateModelException(
+            "No such key: " + key + " in class " + clazz.getName());
+    }
+
+    /**
+     * Returns true if there is at least one public static
+     * field or method in the underlying class.
+     */
+    @Override
+    public boolean isEmpty() {
+        return map.isEmpty();
+    }
+
+    @Override
+    public int size() {
+        return map.size();
+    }
+    
+    @Override
+    public TemplateCollectionModel keys() throws TemplateModelException {
+        return (TemplateCollectionModel) 
wrapper.getOuterIdentity().wrap(map.keySet());
+    }
+    
+    @Override
+    public TemplateCollectionModel values() throws TemplateModelException {
+        return (TemplateCollectionModel) 
wrapper.getOuterIdentity().wrap(map.values());
+    }
+
+    private void populate() throws TemplateModelException {
+        if (!Modifier.isPublic(clazz.getModifiers())) {
+            throw new TemplateModelException(
+                "Can't wrap the non-public class " + clazz.getName());
+        }
+        
+        if (wrapper.getExposureLevel() == DefaultObjectWrapper.EXPOSE_NOTHING) 
{
+            return;
+        }
+
+        Field[] fields = clazz.getFields();
+        for (Field field : fields) {
+            int mod = field.getModifiers();
+            if (Modifier.isPublic(mod) && Modifier.isStatic(mod)) {
+                if (Modifier.isFinal(mod))
+                    try {
+                        // public static final fields are evaluated once and
+                        // stored in the map
+                        map.put(field.getName(), 
wrapper.getOuterIdentity().wrap(field.get(null)));
+                    } catch (IllegalAccessException e) {
+                        // Intentionally ignored
+                    }
+                else
+                    // This is a special flagging value: Field in the map means
+                    // that this is a non-final field, and it must be evaluated
+                    // on each get() call.
+                    map.put(field.getName(), field);
+            }
+        }
+        if (wrapper.getExposureLevel() < 
DefaultObjectWrapper.EXPOSE_PROPERTIES_ONLY) {
+            Method[] methods = clazz.getMethods();
+            for (Method method : methods) {
+                int mod = method.getModifiers();
+                if (Modifier.isPublic(mod) && Modifier.isStatic(mod)
+                        && 
wrapper.getClassIntrospector().isAllowedToExpose(method)) {
+                    String name = method.getName();
+                    Object obj = map.get(name);
+                    if (obj instanceof Method) {
+                        OverloadedMethods overloadedMethods = new 
OverloadedMethods();
+                        overloadedMethods.addMethod((Method) obj);
+                        overloadedMethods.addMethod(method);
+                        map.put(name, overloadedMethods);
+                    } else if (obj instanceof OverloadedMethods) {
+                        OverloadedMethods overloadedMethods = 
(OverloadedMethods) obj;
+                        overloadedMethods.addMethod(method);
+                    } else {
+                        if (obj != null) {
+                            if (LOG.isInfoEnabled()) {
+                                LOG.info("Overwriting value [" + obj + "] for 
" +
+                                        " key '" + name + "' with [" + method +
+                                        "] in static model for " + 
clazz.getName());
+                            }
+                        }
+                        map.put(name, method);
+                    }
+                }
+            }
+            for (Iterator entries = map.entrySet().iterator(); 
entries.hasNext(); ) {
+                Map.Entry entry = (Map.Entry) entries.next();
+                Object value = entry.getValue();
+                if (value instanceof Method) {
+                    Method method = (Method) value;
+                    entry.setValue(new JavaMethodModel(null, method,
+                            method.getParameterTypes(), wrapper));
+                } else if (value instanceof OverloadedMethods) {
+                    entry.setValue(new OverloadedMethodsModel(null, 
(OverloadedMethods) value, wrapper));
+                }
+            }
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/3fd56062/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/StaticModels.java
----------------------------------------------------------------------
diff --git 
a/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/StaticModels.java
 
b/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/StaticModels.java
new file mode 100644
index 0000000..15b45bc
--- /dev/null
+++ 
b/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/StaticModels.java
@@ -0,0 +1,43 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.freemarker.core.model.impl;
+
+import org.apache.freemarker.core.model.TemplateModel;
+import org.apache.freemarker.core.model.TemplateModelException;
+
+/**
+ * Utility class for instantiating {@link StaticModel} instances from
+ * templates. If your template's data model contains an instance of
+ * StaticModels (named, say <tt>StaticModels</tt>), then you can
+ * instantiate an arbitrary StaticModel using get syntax (i.e.
+ * <tt>StaticModels["java.lang.System"].currentTimeMillis()</tt>).
+ */
+class StaticModels extends ClassBasedModelFactory {
+    
+    StaticModels(DefaultObjectWrapper wrapper) {
+        super(wrapper);
+    }
+
+    @Override
+    protected TemplateModel createModel(Class clazz) 
+    throws TemplateModelException {
+        return new StaticModel(clazz, getWrapper());
+    }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/3fd56062/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/TemplateModelListSequence.java
----------------------------------------------------------------------
diff --git 
a/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/TemplateModelListSequence.java
 
b/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/TemplateModelListSequence.java
new file mode 100644
index 0000000..b049c63
--- /dev/null
+++ 
b/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/TemplateModelListSequence.java
@@ -0,0 +1,58 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.freemarker.core.model.impl;
+
+import java.util.List;
+
+import org.apache.freemarker.core.model.TemplateMethodModelEx;
+import org.apache.freemarker.core.model.TemplateModel;
+import org.apache.freemarker.core.model.TemplateSequenceModel;
+
+/**
+ * A sequence that wraps a {@link List} of {@link TemplateModel}-s. It does 
not copy the original
+ * list. It's mostly useful when implementing {@link TemplateMethodModelEx}-es 
that collect items from other
+ * {@link TemplateModel}-s.
+ */
+public class TemplateModelListSequence implements TemplateSequenceModel {
+    
+    private List/*<TemplateModel>*/ list;
+
+    public TemplateModelListSequence(List list) {
+        this.list = list;
+    }
+
+    @Override
+    public TemplateModel get(int index) {
+        return (TemplateModel) list.get(index);
+    }
+
+    @Override
+    public int size() {
+        return list.size();
+    }
+
+    /**
+     * Returns the original {@link List} of {@link TemplateModel}-s, so it's 
not a fully unwrapped value.
+     */
+    public Object getWrappedObject() {
+        return list;
+    }
+    
+}

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/3fd56062/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/TypeFlags.java
----------------------------------------------------------------------
diff --git 
a/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/TypeFlags.java
 
b/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/TypeFlags.java
new file mode 100644
index 0000000..6a5579b
--- /dev/null
+++ 
b/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/TypeFlags.java
@@ -0,0 +1,130 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.freemarker.core.model.impl;
+
+import java.math.BigDecimal;
+import java.math.BigInteger;
+import java.util.Date;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * Flag values and masks for "type flags". "Type flags" is a set of bits that 
store information about the possible
+ * destination types at a parameter position of overloaded methods. 
+ */
+class TypeFlags {
+
+    /**
+     * Indicates that the unwrapping hint will not be a specific numerical 
type; it must not be set if there's no
+     * numerical type at the given parameter index.
+     */
+    static final int WIDENED_NUMERICAL_UNWRAPPING_HINT = 1;
+    
+    static final int BYTE = 4;
+    static final int SHORT = 8;
+    static final int INTEGER = 16;
+    static final int LONG = 32;
+    static final int FLOAT = 64;
+    static final int DOUBLE = 128;
+    static final int BIG_INTEGER = 256;
+    static final int BIG_DECIMAL = 512;
+    static final int UNKNOWN_NUMERICAL_TYPE = 1024;
+
+    static final int ACCEPTS_NUMBER = 0x800;
+    static final int ACCEPTS_DATE = 0x1000;
+    static final int ACCEPTS_STRING = 0x2000;
+    static final int ACCEPTS_BOOLEAN = 0x4000;
+    static final int ACCEPTS_MAP = 0x8000;
+    static final int ACCEPTS_LIST = 0x10000;
+    static final int ACCEPTS_SET = 0x20000;
+    static final int ACCEPTS_ARRAY = 0x40000;
+    
+    /**
+     * Indicates the presence of the char or Character type
+     */
+    static final int CHARACTER = 0x80000;
+    
+    static final int ACCEPTS_ANY_OBJECT = ACCEPTS_NUMBER | ACCEPTS_DATE | 
ACCEPTS_STRING | ACCEPTS_BOOLEAN
+            | ACCEPTS_MAP | ACCEPTS_LIST | ACCEPTS_SET | ACCEPTS_ARRAY;
+    
+    static final int MASK_KNOWN_INTEGERS = BYTE | SHORT | INTEGER | LONG | 
BIG_INTEGER;
+    static final int MASK_KNOWN_NONINTEGERS = FLOAT | DOUBLE | BIG_DECIMAL;
+    static final int MASK_ALL_KNOWN_NUMERICALS = MASK_KNOWN_INTEGERS | 
MASK_KNOWN_NONINTEGERS;
+    static final int MASK_ALL_NUMERICALS = MASK_ALL_KNOWN_NUMERICALS | 
UNKNOWN_NUMERICAL_TYPE;
+    
+    static int classToTypeFlags(Class pClass) {
+        // We start with the most frequent cases  
+        if (pClass == Object.class) {
+            return ACCEPTS_ANY_OBJECT;
+        } else if (pClass == String.class) {
+            return ACCEPTS_STRING;
+        } else if (pClass.isPrimitive()) {
+            if (pClass == Integer.TYPE) return INTEGER | ACCEPTS_NUMBER;
+            else if (pClass == Long.TYPE) return LONG | ACCEPTS_NUMBER;
+            else if (pClass == Double.TYPE) return DOUBLE | ACCEPTS_NUMBER;
+            else if (pClass == Float.TYPE) return FLOAT | ACCEPTS_NUMBER;
+            else if (pClass == Byte.TYPE) return BYTE | ACCEPTS_NUMBER;
+            else if (pClass == Short.TYPE) return SHORT | ACCEPTS_NUMBER;
+            else if (pClass == Character.TYPE) return CHARACTER;
+            else if (pClass == Boolean.TYPE) return ACCEPTS_BOOLEAN;
+            else return 0;
+        } else if (Number.class.isAssignableFrom(pClass)) {
+            if (pClass == Integer.class) return INTEGER | ACCEPTS_NUMBER;
+            else if (pClass == Long.class) return LONG | ACCEPTS_NUMBER;
+            else if (pClass == Double.class) return DOUBLE | ACCEPTS_NUMBER;
+            else if (pClass == Float.class) return FLOAT | ACCEPTS_NUMBER;
+            else if (pClass == Byte.class) return BYTE | ACCEPTS_NUMBER;
+            else if (pClass == Short.class) return SHORT | ACCEPTS_NUMBER;
+            else if (BigDecimal.class.isAssignableFrom(pClass)) return 
BIG_DECIMAL | ACCEPTS_NUMBER;
+            else if (BigInteger.class.isAssignableFrom(pClass)) return 
BIG_INTEGER | ACCEPTS_NUMBER;
+            else return UNKNOWN_NUMERICAL_TYPE | ACCEPTS_NUMBER;
+        } else if (pClass.isArray()) {
+            return ACCEPTS_ARRAY;
+        } else {
+            int flags = 0;
+            if (pClass.isAssignableFrom(String.class)) {
+                flags |= ACCEPTS_STRING;
+            }
+            if (pClass.isAssignableFrom(Date.class)) {
+                flags |= ACCEPTS_DATE;
+            }
+            if (pClass.isAssignableFrom(Boolean.class)) {
+                flags |= ACCEPTS_BOOLEAN;
+            }
+            if (pClass.isAssignableFrom(Map.class)) {
+                flags |= ACCEPTS_MAP;
+            }
+            if (pClass.isAssignableFrom(List.class)) {
+                flags |= ACCEPTS_LIST;
+            }
+            if (pClass.isAssignableFrom(Set.class)) {
+                flags |= ACCEPTS_SET;
+            }
+            
+            if (pClass == Character.class) {
+                flags |= CHARACTER;
+            }
+            
+            return flags;
+        } 
+    }
+
+}


Reply via email to