You are viewing a plain text version of this content. The canonical link for it is here.
Posted to notifications@freemarker.apache.org by dd...@apache.org on 2017/08/07 22:41:30 UTC

[4/8] incubator-freemarker git commit: FREEMARKER-64: Removed TemplateMethodModel, using TemplateFunctionModel everywhere instead. Some refinement of existing TemplateCallableModel API-s, most importantly, the support for null argumenArrayLayout. Removed

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/8d5263f2/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/DefaultObjectWrapper.java
----------------------------------------------------------------------
diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/DefaultObjectWrapper.java b/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/DefaultObjectWrapper.java
index 1a0e251..9d797ce 100644
--- a/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/DefaultObjectWrapper.java
+++ b/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/DefaultObjectWrapper.java
@@ -42,7 +42,9 @@ import java.util.ResourceBundle;
 import java.util.Set;
 import java.util.WeakHashMap;
 
+import org.apache.freemarker.core.CallPlace;
 import org.apache.freemarker.core.Configuration;
+import org.apache.freemarker.core.NonTemplateCallPlace;
 import org.apache.freemarker.core.Version;
 import org.apache.freemarker.core._CoreAPI;
 import org.apache.freemarker.core._DelayedFTLTypeDescription;
@@ -55,8 +57,8 @@ 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.TemplateDateModel;
+import org.apache.freemarker.core.model.TemplateFunctionModel;
 import org.apache.freemarker.core.model.TemplateHashModel;
-import org.apache.freemarker.core.model.TemplateMethodModel;
 import org.apache.freemarker.core.model.TemplateModel;
 import org.apache.freemarker.core.model.TemplateModelAdapter;
 import org.apache.freemarker.core.model.TemplateModelException;
@@ -481,7 +483,7 @@ public class DefaultObjectWrapper implements RichObjectWrapper {
      * Wraps a Java method so that it can be called from templates, without wrapping its parent ("this") object. The
      * result is almost the same as that you would get by wrapping the parent object then getting the method from the
      * resulting {@link TemplateHashModel} by name. Except, if the wrapped method is overloaded, with this method you
-     * explicitly select a an overload, while otherwise you would get a {@link TemplateMethodModel} that selects an
+     * explicitly select an overload, while otherwise you would get a {@link OverloadedJavaMethodModel} that selects an
      * overload each time it's called based on the argument values.
      *
      * @param object The object whose method will be called, or {@code null} if {@code method} is a static method.
@@ -489,8 +491,8 @@ public class DefaultObjectWrapper implements RichObjectWrapper {
      * @param method The method to call, which must be an (inherited) member of the class of {@code object}, as
      *          described by {@link Method#invoke(Object, Object...)}
      */
-    public TemplateMethodModel wrap(Object object, Method method) {
-        return new JavaMethodModel(object, method, method.getParameterTypes(), this);
+    public TemplateFunctionModel wrap(Object object, Method method) {
+        return new SimpleJavaMethodModel(object, method, method.getParameterTypes(), this);
     }
 
     /**
@@ -582,7 +584,7 @@ public class DefaultObjectWrapper implements RichObjectWrapper {
         // This is for transparent interop with other wrappers (and ourselves)
         // Passing the targetClass allows e.g. a Jython-aware method that declares a
         // PyObject as its argument to receive a PyObject from a Jython-aware TemplateModel
-        // passed as an argument to TemplateMethodModel etc.
+        // passed as an argument to TemplateFunctionModel etc.
         if (model instanceof AdapterTemplateModel) {
             Object wrapped = ((AdapterTemplateModel) model).getAdaptedObject(targetClass);
             if (targetClass == Object.class || targetClass.isInstance(wrapped)) {
@@ -1040,10 +1042,13 @@ public class DefaultObjectWrapper implements RichObjectWrapper {
      * constructor. Overloaded constructors and varargs are supported. Only public constructors will be called.
      *
      * @param clazz The class whose constructor we will call.
-     * @param arguments The list of {@link TemplateModel}-s to pass to the constructor after unwrapping them
+     * @param args The list of {@link TemplateModel}-s to pass to the constructor after unwrapping them
+     * @param callPlace Where the constructor is called from (which may contains information useful for overloaded
+     *                  constructor selection); you may want to use {@link NonTemplateCallPlace#INSTANCE}.
+     *                 if you call this from Java code.
      * @return The instance created; it's not wrapped into {@link TemplateModel}.
      */
-    public Object newInstance(Class<?> clazz, List/*<? extends TemplateModel>*/ arguments)
+    public Object newInstance(Class<?> clazz, TemplateModel[] args, CallPlace callPlace)
             throws TemplateModelException {
         try {
             Object ctors = classIntrospector.get(clazz).get(ClassIntrospector.CONSTRUCTORS_KEY);
@@ -1052,19 +1057,19 @@ public class DefaultObjectWrapper implements RichObjectWrapper {
                         " has no public constructors.");
             }
             Constructor<?> ctor = null;
-            Object[] objargs;
+            Object[] pojoArgs;
             if (ctors instanceof SimpleMethod) {
                 SimpleMethod sm = (SimpleMethod) ctors;
                 ctor = (Constructor<?>) sm.getMember();
-                objargs = sm.unwrapArguments(arguments, this);
+                pojoArgs = sm.unwrapArguments(args, this);
                 try {
-                    return ctor.newInstance(objargs);
+                    return ctor.newInstance(pojoArgs);
                 } catch (Exception e) {
-                    if (e instanceof TemplateModelException) throw (TemplateModelException) e;
                     throw _MethodUtil.newInvocationTemplateModelException(null, ctor, e);
                 }
             } else if (ctors instanceof OverloadedMethods) {
-                final MemberAndArguments mma = ((OverloadedMethods) ctors).getMemberAndArguments(arguments, this);
+                // TODO [FM3] Utilize optional java type info in callPlace for overloaded method selection
+                final MemberAndArguments mma = ((OverloadedMethods) ctors).getMemberAndArguments(args, this);
                 try {
                     return mma.invokeConstructor(this);
                 } catch (Exception e) {

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/8d5263f2/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/JavaMethodModel.java
----------------------------------------------------------------------
diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/JavaMethodModel.java b/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/JavaMethodModel.java
index f89952e..6fb04c6 100644
--- a/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/JavaMethodModel.java
+++ b/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/JavaMethodModel.java
@@ -19,88 +19,34 @@
 
 package org.apache.freemarker.core.model.impl;
 
-import java.lang.reflect.Member;
-import java.lang.reflect.Method;
-import java.util.List;
-
-import org.apache.freemarker.core._UnexpectedTypeErrorExplainerTemplateModel;
-import org.apache.freemarker.core.model.TemplateMethodModel;
+import org.apache.freemarker.core.CallPlace;
+import org.apache.freemarker.core.Environment;
+import org.apache.freemarker.core.TemplateException;
+import org.apache.freemarker.core.model.ArgumentArrayLayout;
+import org.apache.freemarker.core.model.TemplateFunctionModel;
 import org.apache.freemarker.core.model.TemplateModel;
-import org.apache.freemarker.core.model.TemplateModelException;
 
 /**
- * Wraps a {@link Method} into the {@link TemplateMethodModel} interface. It is used by {@link BeanModel} to wrap
- * non-overloaded methods.
+ * Common super interface (marker interface) for {@link TemplateFunctionModel}-s that stand for Java methods; do not
+ * implement it yourself! It meant to be implemented inside FreeMarker only.
  */
-public final class JavaMethodModel extends SimpleMethod implements TemplateMethodModel,
-        _UnexpectedTypeErrorExplainerTemplateModel {
-    private final Object object;
-    private final DefaultObjectWrapper wrapper;
-
-    /**
-     * Creates a model for a specific method on a specific object.
-     * @param object the object to call the method on, or {@code null} for a static method.
-     * @param method the method that will be invoked.
-     * @param argTypes Either pass in {@code Method#getParameterTypes() method.getParameterTypes()} here,
-     *          or reuse an earlier result of that call (for speed). Not {@code null}.
-     */
-    JavaMethodModel(Object object, Method method, Class[] argTypes, DefaultObjectWrapper wrapper) {
-        super(method, argTypes);
-        this.object = object;
-        this.wrapper = wrapper;
-    }
+public interface JavaMethodModel extends TemplateFunctionModel {
 
     /**
-     * Invokes the method, passing it the arguments from the list.
+     * Calls {@link #execute(TemplateModel[], CallPlace, Environment)}, but it emphasizes that the
+     * {@link Environment} parameters is ignored, and passes {@code null} for it.
+     *
+     * @param args As {@link #getFunctionArgumentArrayLayout()} always return {@code null} in
+     *             {@link JavaMethodModel}-s, the length of this array corresponds to the number of actual arguments
+     *             specified on the call site, and all parameters will be positional.
+     *
+     * @param callPlace Same as with {@link #execute(TemplateModel[], CallPlace, Environment)}.
      */
-    @Override
-    public TemplateModel execute(List<? extends TemplateModel> args) throws TemplateModelException {
-        try {
-            return wrapper.invokeMethod(object, (Method) getMember(), 
-                    unwrapArguments(args, wrapper));
-        } catch (TemplateModelException e) {
-            throw e;
-        } catch (Exception e) {
-            throw _MethodUtil.newInvocationTemplateModelException(object, getMember(), e);
-        }
-    }
-
-    @Override
-    public String toString() {
-        return getMember().toString();
-    }
+    TemplateModel execute(TemplateModel[] args, CallPlace callPlace) throws TemplateException;
 
     /**
-     * Implementation of experimental interface; don't use it, no backward compatibility guarantee!
+     * Always returns {@code null} for {@link JavaMethodModel}-s; hence, only positional parameters are supported.
      */
     @Override
-    public Object[] explainTypeError(Class[] expectedClasses) {
-        final Member member = getMember();
-        if (!(member instanceof Method)) {
-            return null;  // This shouldn't occur
-        }
-        Method m = (Method) member;
-        
-        final Class returnType = m.getReturnType();
-        if (returnType == null || returnType == void.class || returnType == Void.class) {
-            return null;  // Calling it won't help
-        }
-        
-        String mName = m.getName();
-        if (mName.startsWith("get") && mName.length() > 3 && Character.isUpperCase(mName.charAt(3))
-                && (m.getParameterTypes().length == 0)) {
-            return new Object[] {
-                    "Maybe using obj.something instead of obj.getSomething will yield the desired value." };
-        } else if (mName.startsWith("is") && mName.length() > 2 && Character.isUpperCase(mName.charAt(2))
-                && (m.getParameterTypes().length == 0)) {
-            return new Object[] {
-                    "Maybe using obj.something instead of obj.isSomething will yield the desired value." };
-        } else {
-            return new Object[] {
-                    "Maybe using obj.something(",
-                    (m.getParameterTypes().length != 0 ? "params" : ""),
-                    ") instead of obj.something will yield the desired value" };
-        }
-    }
-    
-}
\ No newline at end of file
+    ArgumentArrayLayout getFunctionArgumentArrayLayout();
+}

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/8d5263f2/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/OverloadedFixArgsMethods.java
----------------------------------------------------------------------
diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/OverloadedFixArgsMethods.java b/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/OverloadedFixArgsMethods.java
index bff717d..4a8b69b 100644
--- a/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/OverloadedFixArgsMethods.java
+++ b/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/OverloadedFixArgsMethods.java
@@ -18,10 +18,7 @@
  */
 package org.apache.freemarker.core.model.impl;
 
-import java.util.Collections;
-import java.util.Iterator;
-import java.util.List;
-
+import org.apache.freemarker.core.model.Constants;
 import org.apache.freemarker.core.model.ObjectWrapperAndUnwrapper;
 import org.apache.freemarker.core.model.TemplateModel;
 import org.apache.freemarker.core.model.TemplateModelException;
@@ -36,28 +33,28 @@ class OverloadedFixArgsMethods extends OverloadedMethodsSubset {
     }
 
     @Override
-    Class[] preprocessParameterTypes(CallableMemberDescriptor memberDesc) {
+    Class<?>[] preprocessParameterTypes(CallableMemberDescriptor memberDesc) {
         return memberDesc.getParamTypes();
     }
     
     @Override
-    void afterWideningUnwrappingHints(Class[] paramTypes, int[] paramNumericalTypes) {
+    void afterWideningUnwrappingHints(Class<?>[] paramTypes, int[] paramNumericalTypes) {
         // Do nothing
     }
 
     @Override
-    MaybeEmptyMemberAndArguments getMemberAndArguments(List tmArgs, DefaultObjectWrapper unwrapper)
+    MaybeEmptyMemberAndArguments getMemberAndArguments(TemplateModel[] tmArgs, DefaultObjectWrapper unwrapper)
     throws TemplateModelException {
         if (tmArgs == null) {
             // null is treated as empty args
-            tmArgs = Collections.EMPTY_LIST;
+            tmArgs = Constants.EMPTY_TEMPLATE_MODEL_ARRAY;
         }
-        final int argCount = tmArgs.size();
-        final Class[][] unwrappingHintsByParamCount = getUnwrappingHintsByParamCount();
+        final int argCount = tmArgs.length;
+        final Class<?>[][] unwrappingHintsByParamCount = getUnwrappingHintsByParamCount();
         if (unwrappingHintsByParamCount.length <= argCount) {
             return EmptyMemberAndArguments.WRONG_NUMBER_OF_ARGUMENTS;
         }
-        Class[] unwarppingHints = unwrappingHintsByParamCount[argCount];
+        Class<?>[] unwarppingHints = unwrappingHintsByParamCount[argCount];
         if (unwarppingHints == null) {
             return EmptyMemberAndArguments.WRONG_NUMBER_OF_ARGUMENTS;
         }
@@ -69,10 +66,9 @@ class OverloadedFixArgsMethods extends OverloadedMethodsSubset {
             typeFlags = null;
         }
 
-        Iterator it = tmArgs.iterator();
-        for (int i = 0; i < argCount; ++i) {
+        for (int i = 0; i < argCount; i++) {
             Object pojo = unwrapper.tryUnwrapTo(
-                    (TemplateModel) it.next(),
+                    tmArgs[i],
                     unwarppingHints[i],
                     typeFlags != null ? typeFlags[i] : 0);
             if (pojo == ObjectWrapperAndUnwrapper.CANT_UNWRAP_TO_TARGET_CLASS) {

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/8d5263f2/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/OverloadedJavaMethodModel.java
----------------------------------------------------------------------
diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/OverloadedJavaMethodModel.java b/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/OverloadedJavaMethodModel.java
new file mode 100644
index 0000000..1877687
--- /dev/null
+++ b/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/OverloadedJavaMethodModel.java
@@ -0,0 +1,86 @@
+/*
+ * 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.CallPlace;
+import org.apache.freemarker.core.Environment;
+import org.apache.freemarker.core.TemplateException;
+import org.apache.freemarker.core.model.ArgumentArrayLayout;
+import org.apache.freemarker.core.model.TemplateFunctionModel;
+import org.apache.freemarker.core.model.TemplateModel;
+import org.apache.freemarker.core.model.TemplateModelException;
+
+/**
+ * Wraps a set of same-name overloaded methods behind {@link TemplateFunctionModel} interface,
+ * like if it was a single method; chooses among them behind the scenes on call-time based on the argument values
+ * (and the {@link CallPlace}).
+ *
+ * @see SimpleJavaMethodModel
+ */
+class OverloadedJavaMethodModel implements JavaMethodModel {
+
+    private final Object object;
+    private final OverloadedMethods overloadedMethods;
+    private final DefaultObjectWrapper wrapper;
+    
+    OverloadedJavaMethodModel(Object object, OverloadedMethods overloadedMethods, DefaultObjectWrapper wrapper) {
+        this.object = object;
+        this.overloadedMethods = overloadedMethods;
+        this.wrapper = wrapper;
+    }
+
+    @Override
+    public TemplateModel execute(TemplateModel[] args, CallPlace callPlace) throws TemplateException {
+        return execute(args, callPlace, null);
+    }
+
+    /**
+     * See {@link #execute(TemplateModel[], CallPlace)}; the {@link Environment} parameter can be {@code null} in this
+     * implementation. The actual method to call from several overloaded methods will be chosen based on the classes of
+     * the arguments.
+     *
+     * @throws TemplateModelException
+     *         if the method cannot be chosen unambiguously.
+     */
+    @Override
+    public TemplateModel execute(TemplateModel[] args, CallPlace callPlace, Environment envUnused)
+            throws TemplateModelException {
+        // TODO [FM3] Utilize optional java type info in callPlace for overloaded method selection
+        MemberAndArguments maa = overloadedMethods.getMemberAndArguments(args, wrapper);
+        try {
+            return maa.invokeMethod(wrapper, object);
+        } catch (Exception e) {
+            if (e instanceof TemplateModelException) throw (TemplateModelException) e;
+            
+            throw _MethodUtil.newInvocationTemplateModelException(
+                    object,
+                    maa.getCallableMemberDescriptor(),
+                    e);
+        }
+    }
+
+    @Override
+    public ArgumentArrayLayout getFunctionArgumentArrayLayout() {
+        // Required to return null! See inherited JavaDoc.
+        return null;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/8d5263f2/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/OverloadedMethods.java
----------------------------------------------------------------------
diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/OverloadedMethods.java b/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/OverloadedMethods.java
index 1ba1a56..fad854d 100644
--- a/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/OverloadedMethods.java
+++ b/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/OverloadedMethods.java
@@ -23,7 +23,6 @@ import java.lang.reflect.Constructor;
 import java.lang.reflect.Method;
 import java.util.HashSet;
 import java.util.Iterator;
-import java.util.List;
 
 import org.apache.freemarker.core._DelayedConversionToString;
 import org.apache.freemarker.core._ErrorDescriptionBuilder;
@@ -52,12 +51,12 @@ final class OverloadedMethods {
     }
     
     void addMethod(Method method) {
-        final Class[] paramTypes = method.getParameterTypes();
+        final Class<?>[] paramTypes = method.getParameterTypes();
         addCallableMemberDescriptor(new ReflectionCallableMemberDescriptor(method, paramTypes));
     }
 
-    void addConstructor(Constructor constr) {
-        final Class[] paramTypes = constr.getParameterTypes();
+    void addConstructor(Constructor<?> constr) {
+        final Class<?>[] paramTypes = constr.getParameterTypes();
         addCallableMemberDescriptor(new ReflectionCallableMemberDescriptor(constr, paramTypes));
     }
     
@@ -72,7 +71,7 @@ final class OverloadedMethods {
         }
     }
     
-    MemberAndArguments getMemberAndArguments(List/*<TemplateModel>*/ tmArgs, DefaultObjectWrapper unwrapper)
+    MemberAndArguments getMemberAndArguments(TemplateModel[] tmArgs, DefaultObjectWrapper unwrapper)
     throws TemplateModelException {
         // Try to find a oms args match:
         MaybeEmptyMemberAndArguments fixArgsRes = fixArgMethods.getMemberAndArguments(tmArgs, unwrapper);
@@ -104,7 +103,7 @@ final class OverloadedMethods {
 
     private Object[] toCompositeErrorMessage(
             final EmptyMemberAndArguments fixArgsEmptyRes, final EmptyMemberAndArguments varargsEmptyRes,
-            List tmArgs) {
+            TemplateModel[] tmArgs) {
         final Object[] argsErrorMsg;
         if (varargsEmptyRes != null) {
             if (fixArgsEmptyRes == null || fixArgsEmptyRes.isNumberOfArgumentsWrong()) {
@@ -123,7 +122,7 @@ final class OverloadedMethods {
         return argsErrorMsg;
     }
 
-    private Object[] toErrorMessage(EmptyMemberAndArguments res, List/*<TemplateModel>*/ tmArgs) {
+    private Object[] toErrorMessage(EmptyMemberAndArguments res, TemplateModel[] tmArgs) {
         final Object[] unwrappedArgs = res.getUnwrappedArguments();
         return new Object[] {
                 res.getErrorDescription(),
@@ -143,23 +142,26 @@ final class OverloadedMethods {
             
             @Override
             protected String doConversion(Object obj) {
-                final Iterator fixArgMethodsIter = fixArgMethods.getMemberDescriptors();
-                final Iterator varargMethodsIter = varargMethods != null ? varargMethods.getMemberDescriptors() : null;
+                final Iterator<ReflectionCallableMemberDescriptor> fixArgMethodsIter
+                        = fixArgMethods.getMemberDescriptors();
+                final Iterator<ReflectionCallableMemberDescriptor> varargMethodsIter
+                        = varargMethods != null ? varargMethods.getMemberDescriptors() : null;
                 
-                boolean hasMethods = fixArgMethodsIter.hasNext() || (varargMethodsIter != null && varargMethodsIter.hasNext()); 
+                boolean hasMethods = fixArgMethodsIter.hasNext()
+                        || (varargMethodsIter != null && varargMethodsIter.hasNext());
                 if (hasMethods) {
                     StringBuilder sb = new StringBuilder();
-                    HashSet fixArgMethods = new HashSet();
+                    HashSet<CallableMemberDescriptor> fixArgMethods = new HashSet<>();
                     while (fixArgMethodsIter.hasNext()) {
                         if (sb.length() != 0) sb.append(",\n");
                         sb.append("    ");
-                        CallableMemberDescriptor callableMemberDesc = (CallableMemberDescriptor) fixArgMethodsIter.next();
+                        CallableMemberDescriptor callableMemberDesc = fixArgMethodsIter.next();
                         fixArgMethods.add(callableMemberDesc);
                         sb.append(callableMemberDesc.getDeclaration());
                     }
                     if (varargMethodsIter != null) {
                         while (varargMethodsIter.hasNext()) {
-                            CallableMemberDescriptor callableMemberDesc = (CallableMemberDescriptor) varargMethodsIter.next();
+                            CallableMemberDescriptor callableMemberDesc = varargMethodsIter.next();
                             if (!fixArgMethods.contains(callableMemberDesc)) {
                                 if (sb.length() != 0) sb.append(",\n");
                                 sb.append("    ");
@@ -181,15 +183,16 @@ final class OverloadedMethods {
      * allows finding a matching overload. 
      */
     private void addMarkupBITipAfterNoNoMarchIfApplicable(_ErrorDescriptionBuilder edb,
-            List tmArgs) {
-        for (int argIdx = 0; argIdx < tmArgs.size(); argIdx++) {
-            Object tmArg = tmArgs.get(argIdx);
+            TemplateModel[] tmArgs) {
+        for (int argIdx = 0; argIdx < tmArgs.length; argIdx++) {
+            TemplateModel tmArg = tmArgs[argIdx];
             if (tmArg instanceof TemplateMarkupOutputModel) {
-                for (Iterator membDescs = fixArgMethods.getMemberDescriptors(); membDescs.hasNext();) {
-                    CallableMemberDescriptor membDesc = (CallableMemberDescriptor) membDescs.next();
-                    Class[] paramTypes = membDesc.getParamTypes();
+                for (Iterator<ReflectionCallableMemberDescriptor> membDescs = fixArgMethods.getMemberDescriptors();
+                        membDescs.hasNext(); ) {
+                    CallableMemberDescriptor membDesc = membDescs.next();
+                    Class<?>[] paramTypes = membDesc.getParamTypes();
                     
-                    Class paramType = null;
+                    Class<?> paramType = null;
                     if (membDesc.isVarargs() && argIdx >= paramTypes.length - 1) {
                         paramType = paramTypes[paramTypes.length - 1];
                         if (paramType.isArray()) {
@@ -201,7 +204,7 @@ final class OverloadedMethods {
                     }
                     if (paramType != null) {
                         if (paramType.isAssignableFrom(String.class) && !paramType.isAssignableFrom(tmArg.getClass())) {
-                            edb.tip(JavaMethodModel.MARKUP_OUTPUT_TO_STRING_TIP);
+                            edb.tip(SimpleJavaMethodModel.MARKUP_OUTPUT_TO_STRING_TIP);
                             return;
                         }
                     }
@@ -210,10 +213,10 @@ final class OverloadedMethods {
         }
     }
 
-    private _DelayedConversionToString getTMActualParameterTypes(List arguments) {
-        final String[] argumentTypeDescs = new String[arguments.size()];
-        for (int i = 0; i < arguments.size(); i++) {
-            argumentTypeDescs[i] = FTLUtil.getTypeDescription((TemplateModel) arguments.get(i));
+    private _DelayedConversionToString getTMActualParameterTypes(TemplateModel[] args) {
+        final String[] argumentTypeDescs = new String[args.length];
+        for (int i = 0; i < args.length; i++) {
+            argumentTypeDescs[i] = FTLUtil.getTypeDescription(args[i]);
         }
         
         return new DelayedCallSignatureToString(argumentTypeDescs) {
@@ -227,7 +230,7 @@ final class OverloadedMethods {
     }
     
     private Object getUnwrappedActualParameterTypes(Object[] unwrappedArgs) {
-        final Class[] argumentTypes = new Class[unwrappedArgs.length];
+        final Class<?>[] argumentTypes = new Class<?>[unwrappedArgs.length];
         for (int i = 0; i < unwrappedArgs.length; i++) {
             Object unwrappedArg = unwrappedArgs[i];
             argumentTypes[i] = unwrappedArg != null ? unwrappedArg.getClass() : null;
@@ -238,7 +241,7 @@ final class OverloadedMethods {
             @Override
             String argumentToString(Object argType) {
                 return argType != null
-                        ? _ClassUtil.getShortClassName((Class) argType)
+                        ? _ClassUtil.getShortClassName((Class<?>) argType)
                         : _ClassUtil.getShortClassNameOfObject(null);
             }
             
@@ -247,7 +250,7 @@ final class OverloadedMethods {
     
     private abstract class DelayedCallSignatureToString extends _DelayedConversionToString {
 
-        public DelayedCallSignatureToString(Object[] argTypeArray) {
+        DelayedCallSignatureToString(Object[] argTypeArray) {
             super(argTypeArray);
         }
 

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/8d5263f2/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/OverloadedMethodsModel.java
----------------------------------------------------------------------
diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/OverloadedMethodsModel.java b/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/OverloadedMethodsModel.java
deleted file mode 100644
index 72ca642..0000000
--- a/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/OverloadedMethodsModel.java
+++ /dev/null
@@ -1,65 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *   http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-
-package org.apache.freemarker.core.model.impl;
-
-
-import java.util.List;
-
-import org.apache.freemarker.core.model.TemplateMethodModel;
-import org.apache.freemarker.core.model.TemplateModel;
-import org.apache.freemarker.core.model.TemplateModelException;
-
-/**
- * Wraps a set of same-name overloaded methods behind {@link TemplateMethodModel} interface,
- * like if it was a single method, chooses among them behind the scenes on call-time based on the argument values.
- */
-class OverloadedMethodsModel implements TemplateMethodModel {
-    private final Object object;
-    private final OverloadedMethods overloadedMethods;
-    private final DefaultObjectWrapper wrapper;
-    
-    OverloadedMethodsModel(Object object, OverloadedMethods overloadedMethods, DefaultObjectWrapper wrapper) {
-        this.object = object;
-        this.overloadedMethods = overloadedMethods;
-        this.wrapper = wrapper;
-    }
-
-    /**
-     * Invokes the method, passing it the arguments from the list. The actual
-     * method to call from several overloaded methods will be chosen based
-     * on the classes of the arguments.
-     * @throws TemplateModelException if the method cannot be chosen
-     * unambiguously.
-     */
-    @Override
-    public TemplateModel execute(List<? extends TemplateModel> args) throws TemplateModelException {
-        MemberAndArguments maa = overloadedMethods.getMemberAndArguments(args, wrapper);
-        try {
-            return maa.invokeMethod(wrapper, object);
-        } catch (Exception e) {
-            if (e instanceof TemplateModelException) throw (TemplateModelException) e;
-            
-            throw _MethodUtil.newInvocationTemplateModelException(
-                    object,
-                    maa.getCallableMemberDescriptor(),
-                    e);
-        }
-    }
-}

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/8d5263f2/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/OverloadedMethodsSubset.java
----------------------------------------------------------------------
diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/OverloadedMethodsSubset.java b/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/OverloadedMethodsSubset.java
index e783af8..aab6d43 100644
--- a/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/OverloadedMethodsSubset.java
+++ b/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/OverloadedMethodsSubset.java
@@ -27,6 +27,7 @@ import java.util.Map;
 import java.util.Set;
 import java.util.concurrent.ConcurrentHashMap;
 
+import org.apache.freemarker.core.model.TemplateModel;
 import org.apache.freemarker.core.model.TemplateModelException;
 import org.apache.freemarker.core.util._ClassUtil;
 import org.apache.freemarker.core.util._NullArgumentException;
@@ -52,7 +53,7 @@ abstract class OverloadedMethodsSubset {
         ZERO_PARAM_COUNT_TYPE_FLAGS_ARRAY[0] = ALL_ZEROS_ARRAY;
     }
 
-    private Class[/*number of args*/][/*arg index*/] unwrappingHintsByParamCount;
+    private Class<?>[/*number of args*/][/*arg index*/] unwrappingHintsByParamCount;
     
     /**
      * Tells what types occur at a given parameter position with a bit field. See {@link TypeFlags}.
@@ -61,10 +62,10 @@ abstract class OverloadedMethodsSubset {
     
     // TODO: This can cause memory-leak when classes are re-loaded. However, first the genericClassIntrospectionCache
     // and such need to be oms in this regard. 
-    private final Map/*<ArgumentTypes, MaybeEmptyCallableMemberDescriptor>*/ argTypesToMemberDescCache
-            = new ConcurrentHashMap(6, 0.75f, 1);
+    private final Map<ArgumentTypes, MaybeEmptyCallableMemberDescriptor> argTypesToMemberDescCache
+            = new ConcurrentHashMap<>(6, 0.75f, 1);
     
-    private final List/*<ReflectionCallableMemberDescriptor>*/ memberDescs = new LinkedList();
+    private final List<ReflectionCallableMemberDescriptor> memberDescs = new LinkedList<>();
     
     OverloadedMethodsSubset() {
         //
@@ -75,7 +76,7 @@ abstract class OverloadedMethodsSubset {
         
         // Warning: Do not modify this array, or put it into unwrappingHintsByParamCount by reference, as the arrays
         // inside that are modified!
-        final Class[] prepedParamTypes = preprocessParameterTypes(memberDesc);
+        final Class<?>[] prepedParamTypes = preprocessParameterTypes(memberDesc);
         final int paramCount = prepedParamTypes.length;  // Must be the same as the length of the original param list
         
         // Merge these unwrapping hints with the existing table of hints:
@@ -83,13 +84,13 @@ abstract class OverloadedMethodsSubset {
             unwrappingHintsByParamCount = new Class[paramCount + 1][];
             unwrappingHintsByParamCount[paramCount] = prepedParamTypes.clone();
         } else if (unwrappingHintsByParamCount.length <= paramCount) {
-            Class[][] newUnwrappingHintsByParamCount = new Class[paramCount + 1][];
+            Class<?>[][] newUnwrappingHintsByParamCount = new Class[paramCount + 1][];
             System.arraycopy(unwrappingHintsByParamCount, 0, newUnwrappingHintsByParamCount, 0,
                     unwrappingHintsByParamCount.length);
             unwrappingHintsByParamCount = newUnwrappingHintsByParamCount;
             unwrappingHintsByParamCount[paramCount] = prepedParamTypes.clone();
         } else {
-            Class[] unwrappingHints = unwrappingHintsByParamCount[paramCount]; 
+            Class<?>[] unwrappingHints = unwrappingHintsByParamCount[paramCount];
             if (unwrappingHints == null) {
                 unwrappingHintsByParamCount[paramCount] = prepedParamTypes.clone();
             } else {
@@ -123,7 +124,7 @@ abstract class OverloadedMethodsSubset {
         afterWideningUnwrappingHints(prepedParamTypes, typeFlagsByParamIdx);
     }
     
-    Class[][] getUnwrappingHintsByParamCount() {
+    Class<?>[][] getUnwrappingHintsByParamCount() {
         return unwrappingHintsByParamCount;
     }
     
@@ -131,12 +132,11 @@ abstract class OverloadedMethodsSubset {
             justification="Locks for member descriptor creation only")
     final MaybeEmptyCallableMemberDescriptor getMemberDescriptorForArgs(Object[] args, boolean varArg) {
         ArgumentTypes argTypes = new ArgumentTypes(args);
-        MaybeEmptyCallableMemberDescriptor memberDesc
-                = (MaybeEmptyCallableMemberDescriptor) argTypesToMemberDescCache.get(argTypes);
+        MaybeEmptyCallableMemberDescriptor memberDesc = argTypesToMemberDescCache.get(argTypes);
         if (memberDesc == null) {
             // Synchronized so that we won't unnecessarily invoke the same member desc. for multiple times in parallel.
             synchronized (argTypesToMemberDescCache) {
-                memberDesc = (MaybeEmptyCallableMemberDescriptor) argTypesToMemberDescCache.get(argTypes);
+                memberDesc = argTypesToMemberDescCache.get(argTypes);
                 if (memberDesc == null) {
                     memberDesc = argTypes.getMostSpecific(memberDescs, varArg);
                     argTypesToMemberDescCache.put(argTypes, memberDesc);
@@ -146,14 +146,14 @@ abstract class OverloadedMethodsSubset {
         return memberDesc;
     }
     
-    Iterator/*<ReflectionCallableMemberDescriptor>*/ getMemberDescriptors() {
+    Iterator<ReflectionCallableMemberDescriptor> getMemberDescriptors() {
         return memberDescs.iterator();
     }
     
-    abstract Class[] preprocessParameterTypes(CallableMemberDescriptor memberDesc);
-    abstract void afterWideningUnwrappingHints(Class[] paramTypes, int[] paramNumericalTypes);
+    abstract Class<?>[] preprocessParameterTypes(CallableMemberDescriptor memberDesc);
+    abstract void afterWideningUnwrappingHints(Class<?>[] paramTypes, int[] paramNumericalTypes);
     
-    abstract MaybeEmptyMemberAndArguments getMemberAndArguments(List/*<TemplateModel>*/ tmArgs, 
+    abstract MaybeEmptyMemberAndArguments getMemberAndArguments(TemplateModel[] tmArgs,
             DefaultObjectWrapper unwrapper) throws TemplateModelException;
 
     /**
@@ -168,7 +168,7 @@ abstract class OverloadedMethodsSubset {
      * @param c1 Parameter type 1
      * @param c2 Parameter type 2
      */
-    protected Class getCommonSupertypeForUnwrappingHint(Class c1, Class c2) {
+    Class<?> getCommonSupertypeForUnwrappingHint(Class<?> c1, Class<?> c2) {
         if (c1 == c2) return c1;
         // This also means that the hint for (Integer, Integer) will be Integer, not just Number. This is consistent
         // with how non-overloaded method hints work.
@@ -214,7 +214,7 @@ abstract class OverloadedMethodsSubset {
         // - One of classes was a primitive type
         // - One of classes was a numerical type (either boxing type or primitive)
         
-        Set commonTypes = _MethodUtil.getAssignables(c1, c2);
+        Set<Class<?>> commonTypes = _MethodUtil.getAssignables(c1, c2);
         commonTypes.retainAll(_MethodUtil.getAssignables(c2, c1));
         if (commonTypes.isEmpty()) {
             // Can happen when at least one of the arguments is an interface, as
@@ -226,11 +226,11 @@ abstract class OverloadedMethodsSubset {
         // because of interfaces. I.e., if you call this method for String.class 
         // and Number.class, you'll have Comparable, Serializable, and Object as 
         // maximal elements. 
-        List max = new ArrayList();
-        listCommonTypes:  for (Iterator commonTypesIter = commonTypes.iterator(); commonTypesIter.hasNext(); ) {
-            Class clazz = (Class) commonTypesIter.next();
-            for (Iterator maxIter = max.iterator(); maxIter.hasNext(); ) {
-                Class maxClazz = (Class) maxIter.next();
+        List<Class<?>> max = new ArrayList<>();
+        listCommonTypes:
+        for (Class<?> clazz : commonTypes) {
+            for (Iterator<Class<?>> maxIter = max.iterator(); maxIter.hasNext(); ) {
+                Class<?> maxClazz = maxIter.next();
                 if (_MethodUtil.isMoreOrSameSpecificParameterType(maxClazz, clazz, false /*bugfixed [1]*/, 0) != 0) {
                     // clazz can't be maximal, if there's already a more specific or equal maximal than it.
                     continue listCommonTypes;
@@ -251,8 +251,8 @@ abstract class OverloadedMethodsSubset {
         
         if (max.size() > 1) {  // we have an ambiguity
             // Find the non-interface class
-            for (Iterator it = max.iterator(); it.hasNext(); ) {
-                Class maxCl = (Class) it.next();
+            for (Iterator<Class<?>> it = max.iterator(); it.hasNext(); ) {
+                Class<?> maxCl = it.next();
                 if (!maxCl.isInterface()) {
                     if (maxCl != Object.class) {  // This actually shouldn't ever happen, but to be sure...
                         // If it's not Object, we use it as the most specific
@@ -278,7 +278,7 @@ abstract class OverloadedMethodsSubset {
             }
         }
         
-        return (Class) max.get(0);
+        return max.get(0);
     }
     
     /**
@@ -286,7 +286,7 @@ abstract class OverloadedMethodsSubset {
      * count or if we are in pre-2.3.21 mode, or {@link #ALL_ZEROS_ARRAY} if there were no parameters that turned
      * on a flag. The returned {@code int}-s are one or more {@link TypeFlags} constants binary "or"-ed together.  
      */
-    final protected int[] getTypeFlags(int paramCount) {
+    final int[] getTypeFlags(int paramCount) {
         return typeFlagsByParamCount != null && typeFlagsByParamCount.length > paramCount
                 ? typeFlagsByParamCount[paramCount]
                 : null;

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/8d5263f2/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/OverloadedVarArgsMethods.java
----------------------------------------------------------------------
diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/OverloadedVarArgsMethods.java b/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/OverloadedVarArgsMethods.java
index 6547923..8085693 100644
--- a/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/OverloadedVarArgsMethods.java
+++ b/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/OverloadedVarArgsMethods.java
@@ -19,10 +19,8 @@
 package org.apache.freemarker.core.model.impl;
 
 import java.lang.reflect.Array;
-import java.util.Collections;
-import java.util.Iterator;
-import java.util.List;
 
+import org.apache.freemarker.core.model.Constants;
 import org.apache.freemarker.core.model.ObjectWrapperAndUnwrapper;
 import org.apache.freemarker.core.model.TemplateModel;
 import org.apache.freemarker.core.model.TemplateModelException;
@@ -41,10 +39,10 @@ class OverloadedVarArgsMethods extends OverloadedMethodsSubset {
      * Replaces the last parameter type with the array component type of it.
      */
     @Override
-    Class[] preprocessParameterTypes(CallableMemberDescriptor memberDesc) {
-        final Class[] preprocessedParamTypes = memberDesc.getParamTypes().clone();
+    Class<?>[] preprocessParameterTypes(CallableMemberDescriptor memberDesc) {
+        final Class<?>[] preprocessedParamTypes = memberDesc.getParamTypes().clone();
         int ln = preprocessedParamTypes.length;
-        final Class varArgsCompType = preprocessedParamTypes[ln - 1].getComponentType();
+        final Class<?> varArgsCompType = preprocessedParamTypes[ln - 1].getComponentType();
         if (varArgsCompType == null) {
             throw new BugException("Only varargs methods should be handled here");
         }
@@ -53,7 +51,7 @@ class OverloadedVarArgsMethods extends OverloadedMethodsSubset {
     }
 
     @Override
-    void afterWideningUnwrappingHints(Class[] paramTypes, int[] paramNumericalTypes) {
+    void afterWideningUnwrappingHints(Class<?>[] paramTypes, int[] paramNumericalTypes) {
         // Overview
         // --------
         //
@@ -67,7 +65,7 @@ class OverloadedVarArgsMethods extends OverloadedMethodsSubset {
         // So we only update the already existing hints. Remember that we already have m(t1, t2) there.
         
         final int paramCount = paramTypes.length;
-        final Class[][] unwrappingHintsByParamCount = getUnwrappingHintsByParamCount();
+        final Class<?>[][] unwrappingHintsByParamCount = getUnwrappingHintsByParamCount();
         
         // The case of e(t1, t2), e(t1, t2, t2), e(t1, t2, t2, t2), ..., where e is an *earlierly* added method.
         // When that was added, this method wasn't added yet, so it had no chance updating the hints of this method,
@@ -75,7 +73,7 @@ class OverloadedVarArgsMethods extends OverloadedMethodsSubset {
         // FIXME: Only needed if m(t1, t2) was filled an empty slot, otherwise whatever was there was already
         // widened by the preceding hints, so this will be a no-op.
         for (int i = paramCount - 1; i >= 0; i--) {
-            final Class[] previousHints = unwrappingHintsByParamCount[i];
+            final Class<?>[] previousHints = unwrappingHintsByParamCount[i];
             if (previousHints != null) {
                 widenHintsToCommonSupertypes(
                         paramCount,
@@ -88,7 +86,7 @@ class OverloadedVarArgsMethods extends OverloadedMethodsSubset {
         // so we do that now:
         // FIXME: Same as above; it's often unnecessary.
         if (paramCount + 1 < unwrappingHintsByParamCount.length) {
-            Class[] oneLongerHints = unwrappingHintsByParamCount[paramCount + 1];
+            Class<?>[] oneLongerHints = unwrappingHintsByParamCount[paramCount + 1];
             if (oneLongerHints != null) {
                 widenHintsToCommonSupertypes(
                         paramCount,
@@ -114,8 +112,8 @@ class OverloadedVarArgsMethods extends OverloadedMethodsSubset {
     }
     
     private void widenHintsToCommonSupertypes(
-            int paramCountOfWidened, Class[] wideningTypes, int[] wideningTypeFlags) {
-        final Class[] typesToWiden = getUnwrappingHintsByParamCount()[paramCountOfWidened];
+            int paramCountOfWidened, Class<?>[] wideningTypes, int[] wideningTypeFlags) {
+        final Class<?>[] typesToWiden = getUnwrappingHintsByParamCount()[paramCountOfWidened];
         if (typesToWiden == null) { 
             return;  // no such overload exists; nothing to widen
         }
@@ -127,7 +125,7 @@ class OverloadedVarArgsMethods extends OverloadedMethodsSubset {
             typesToWiden[i] = getCommonSupertypeForUnwrappingHint(typesToWiden[i], wideningTypes[i]);
         }
         if (typesToWidenLen > wideningTypesLen) {
-            Class varargsComponentType = wideningTypes[wideningTypesLen - 1];
+            Class<?> varargsComponentType = wideningTypes[wideningTypesLen - 1];
             for (int i = wideningTypesLen; i < typesToWidenLen; ++i) {
                 typesToWiden[i] = getCommonSupertypeForUnwrappingHint(typesToWiden[i], varargsComponentType);
             }
@@ -137,20 +135,20 @@ class OverloadedVarArgsMethods extends OverloadedMethodsSubset {
     }
     
     @Override
-    MaybeEmptyMemberAndArguments getMemberAndArguments(List tmArgs, DefaultObjectWrapper unwrapper) 
+    MaybeEmptyMemberAndArguments getMemberAndArguments(TemplateModel[] tmArgs, DefaultObjectWrapper unwrapper)
     throws TemplateModelException {
         if (tmArgs == null) {
             // null is treated as empty args
-            tmArgs = Collections.EMPTY_LIST;
+            tmArgs = Constants.EMPTY_TEMPLATE_MODEL_ARRAY;
         }
-        final int argsLen = tmArgs.size();
-        final Class[][] unwrappingHintsByParamCount = getUnwrappingHintsByParamCount();
+        final int argsLen = tmArgs.length;
+        final Class<?>[][] unwrappingHintsByParamCount = getUnwrappingHintsByParamCount();
         final Object[] pojoArgs = new Object[argsLen];
         int[] typesFlags = null;
         // Going down starting from methods with args.length + 1 parameters, because we must try to match against a case
         // where all specified args are fixargs, and we have 0 varargs.
         outer: for (int paramCount = Math.min(argsLen + 1, unwrappingHintsByParamCount.length - 1); paramCount >= 0; --paramCount) {
-            Class[] unwarappingHints = unwrappingHintsByParamCount[paramCount];
+            Class<?>[] unwarappingHints = unwrappingHintsByParamCount[paramCount];
             if (unwarappingHints == null) {
                 if (paramCount == 0) {
                     return EmptyMemberAndArguments.WRONG_NUMBER_OF_ARGUMENTS;
@@ -164,11 +162,10 @@ class OverloadedVarArgsMethods extends OverloadedMethodsSubset {
             }
             
             // Try to unwrap the arguments
-            Iterator it = tmArgs.iterator();
-            for (int i = 0; i < argsLen; ++i) {
+            for (int i = 0; i < argsLen; i++) {
                 int paramIdx = i < paramCount ? i : paramCount - 1;
                 Object pojo = unwrapper.tryUnwrapTo(
-                        (TemplateModel) it.next(),
+                        tmArgs[i],
                         unwarappingHints[paramIdx],
                         typesFlags != null ? typesFlags[paramIdx] : 0);
                 if (pojo == ObjectWrapperAndUnwrapper.CANT_UNWRAP_TO_TARGET_CLASS) {
@@ -187,7 +184,7 @@ class OverloadedVarArgsMethods extends OverloadedMethodsSubset {
             if (argsOrErrorIdx instanceof Object[]) {
                 pojoArgsWithArray = (Object[]) argsOrErrorIdx;
             } else {
-                return EmptyMemberAndArguments.noCompatibleOverload(((Integer) argsOrErrorIdx).intValue());
+                return EmptyMemberAndArguments.noCompatibleOverload((Integer) argsOrErrorIdx);
             }
             if (typesFlags != null) {
                 // Note that overloaded method selection has already accounted for overflow errors when the method
@@ -210,11 +207,11 @@ class OverloadedVarArgsMethods extends OverloadedMethodsSubset {
      *    order (1-based index) of the argument that couldn't be unwrapped. 
      */
     private Object replaceVarargsSectionWithArray(
-            Object[] args, List modelArgs, CallableMemberDescriptor memberDesc, DefaultObjectWrapper unwrapper) 
-    throws TemplateModelException {
-        final Class[] paramTypes = memberDesc.getParamTypes();
+            Object[] args, TemplateModel[] tmArgs, CallableMemberDescriptor memberDesc, DefaultObjectWrapper unwrapper)
+            throws TemplateModelException {
+        final Class<?>[] paramTypes = memberDesc.getParamTypes();
         final int paramCount = paramTypes.length;
-        final Class varArgsCompType = paramTypes[paramCount - 1].getComponentType(); 
+        final Class<?> varArgsCompType = paramTypes[paramCount - 1].getComponentType();
         final int totalArgCount = args.length;
         final int fixArgCount = paramCount - 1;
         if (args.length != paramCount) {
@@ -222,18 +219,18 @@ class OverloadedVarArgsMethods extends OverloadedMethodsSubset {
             System.arraycopy(args, 0, packedArgs, 0, fixArgCount);
             Object varargs = Array.newInstance(varArgsCompType, totalArgCount - fixArgCount);
             for (int i = fixArgCount; i < totalArgCount; ++i) {
-                Object val = unwrapper.tryUnwrapTo((TemplateModel) modelArgs.get(i), varArgsCompType);
+                Object val = unwrapper.tryUnwrapTo(tmArgs[i], varArgsCompType);
                 if (val == ObjectWrapperAndUnwrapper.CANT_UNWRAP_TO_TARGET_CLASS) {
-                    return Integer.valueOf(i + 1);
+                    return i + 1;
                 }
                 Array.set(varargs, i - fixArgCount, val);
             }
             packedArgs[fixArgCount] = varargs;
             return packedArgs;
         } else {
-            Object val = unwrapper.tryUnwrapTo((TemplateModel) modelArgs.get(fixArgCount), varArgsCompType);
+            Object val = unwrapper.tryUnwrapTo(tmArgs[fixArgCount], varArgsCompType);
             if (val == ObjectWrapperAndUnwrapper.CANT_UNWRAP_TO_TARGET_CLASS) {
-                return Integer.valueOf(fixArgCount + 1);
+                return fixArgCount + 1;
             }
             Object array = Array.newInstance(varArgsCompType, 1);
             Array.set(array, 0, val);

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/8d5263f2/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
index 457c7ef..b4cca30 100644
--- 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
@@ -22,16 +22,19 @@ 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.CallPlace;
+import org.apache.freemarker.core.Environment;
+import org.apache.freemarker.core.TemplateException;
+import org.apache.freemarker.core._CallableUtils;
 import org.apache.freemarker.core._DelayedJQuote;
 import org.apache.freemarker.core._TemplateModelException;
-import org.apache.freemarker.core.model.TemplateMethodModel;
+import org.apache.freemarker.core.model.ArgumentArrayLayout;
+import org.apache.freemarker.core.model.TemplateFunctionModel;
 import org.apache.freemarker.core.model.TemplateModel;
 import org.apache.freemarker.core.model.TemplateModelException;
 
@@ -50,11 +53,7 @@ import org.apache.freemarker.core.model.TemplateModelException;
  * for MessageFormat with arguments arg1, arg2 and arg3</li>
  * </ul>
  */
-public class ResourceBundleModel
-    extends
-    BeanModel
-    implements
-        TemplateMethodModel {
+public class ResourceBundleModel extends BeanModel implements TemplateFunctionModel {
 
     private Hashtable<String, MessageFormat> formats = null;
 
@@ -108,25 +107,23 @@ public class ResourceBundleModel
      * rest of the arguments. The created MessageFormats are cached for later reuse.
      */
     @Override
-    public TemplateModel execute(List<? extends TemplateModel> args)
-        throws TemplateModelException {
+    public TemplateModel execute(TemplateModel[] args, CallPlace callPlace, Environment env) throws TemplateException {
         // Must have at least one argument - the key
-        if (args.size() < 1)
-            throw new TemplateModelException("No message key was specified");
+        if (args.length < 1)
+            throw new TemplateException("No message key was specified", env);
         // Read it
-        Iterator<? extends TemplateModel> it = args.iterator();
-        String key = unwrap((TemplateModel) it.next()).toString();
+        String key = _CallableUtils.castArgToString(args, 0);
         try {
-            if (!it.hasNext()) {
+            if (args.length == 1) {
                 return wrap(((ResourceBundle) object).getObject(key));
             }
-    
+
             // Copy remaining arguments into an Object[]
-            int paramsLen = args.size() - 1;
+            int paramsLen = args.length - 1;
             Object[] params = new Object[paramsLen];
             for (int i = 0; i < paramsLen; ++i)
-                params[i] = unwrap(it.next());
-    
+                params[i] = unwrap(args[1 + i]);
+
             // Invoke format
             return new BeanAndStringModel(format(key, params), wrapper);
         } catch (MissingResourceException e) {
@@ -136,6 +133,11 @@ public class ResourceBundleModel
         }
     }
 
+    @Override
+    public ArgumentArrayLayout getFunctionArgumentArrayLayout() {
+        return null;
+    }
+
     /**
      * Provides direct access to caching format engine from code (instead of from script).
      */

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/8d5263f2/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/SimpleJavaMethodModel.java
----------------------------------------------------------------------
diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/SimpleJavaMethodModel.java b/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/SimpleJavaMethodModel.java
new file mode 100644
index 0000000..d27d131
--- /dev/null
+++ b/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/SimpleJavaMethodModel.java
@@ -0,0 +1,123 @@
+/*
+ * 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.Member;
+import java.lang.reflect.Method;
+
+import org.apache.freemarker.core.CallPlace;
+import org.apache.freemarker.core.Environment;
+import org.apache.freemarker.core.TemplateException;
+import org.apache.freemarker.core._UnexpectedTypeErrorExplainerTemplateModel;
+import org.apache.freemarker.core.model.ArgumentArrayLayout;
+import org.apache.freemarker.core.model.TemplateFunctionModel;
+import org.apache.freemarker.core.model.TemplateModel;
+import org.apache.freemarker.core.model.TemplateModelException;
+
+/**
+ * Wraps a {@link Method} into the {@link TemplateFunctionModel} interface. It is used by {@link BeanModel} to wrap
+ * non-overloaded methods.
+ *
+ * @see OverloadedJavaMethodModel
+ */
+public final class SimpleJavaMethodModel extends SimpleMethod implements JavaMethodModel,
+        _UnexpectedTypeErrorExplainerTemplateModel {
+    private final Object object;
+    private final DefaultObjectWrapper wrapper;
+
+    /**
+     * Creates a model for a specific method on a specific object.
+     * @param object the object to call the method on, or {@code null} for a static method.
+     * @param method the method that will be invoked.
+     * @param argTypes Either pass in {@code Method#getParameterTypes() method.getParameterTypes()} here,
+     *          or reuse an earlier result of that call (for speed). Not {@code null}.
+     */
+    SimpleJavaMethodModel(Object object, Method method, Class[] argTypes, DefaultObjectWrapper wrapper) {
+        super(method, argTypes);
+        this.object = object;
+        this.wrapper = wrapper;
+    }
+
+    @Override
+    public TemplateModel execute(TemplateModel[] args, CallPlace callPlace) throws TemplateException {
+        return execute(args, callPlace, null);
+    }
+
+    /**
+     * See {@link #execute(TemplateModel[], CallPlace)}; the {@link Environment} parameter can be {@code null} in
+     * this implementation.
+     */
+    @Override
+    public TemplateModel execute(TemplateModel[] args, CallPlace callPlace, Environment envUnused) throws
+            TemplateException {
+        try {
+            return wrapper.invokeMethod(object, (Method) getMember(), unwrapArguments(args, wrapper));
+        } catch (TemplateModelException e) {
+            throw e;
+        } catch (Exception e) {
+            throw _MethodUtil.newInvocationTemplateModelException(object, getMember(), e);
+        }
+    }
+
+    @Override
+    public ArgumentArrayLayout getFunctionArgumentArrayLayout() {
+        // Required to return null! See inherited JavaDoc.
+        return null;
+    }
+
+    @Override
+    public String toString() {
+        return getMember().toString();
+    }
+
+    /**
+     * Implementation of experimental interface; don't use it, no backward compatibility guarantee!
+     */
+    @Override
+    public Object[] explainTypeError(Class[] expectedClasses) {
+        final Member member = getMember();
+        if (!(member instanceof Method)) {
+            return null;  // This shouldn't occur
+        }
+        Method m = (Method) member;
+        
+        final Class returnType = m.getReturnType();
+        if (returnType == null || returnType == void.class || returnType == Void.class) {
+            return null;  // Calling it won't help
+        }
+        
+        String mName = m.getName();
+        if (mName.startsWith("get") && mName.length() > 3 && Character.isUpperCase(mName.charAt(3))
+                && (m.getParameterTypes().length == 0)) {
+            return new Object[] {
+                    "Maybe using obj.something instead of obj.getSomething will yield the desired value." };
+        } else if (mName.startsWith("is") && mName.length() > 2 && Character.isUpperCase(mName.charAt(2))
+                && (m.getParameterTypes().length == 0)) {
+            return new Object[] {
+                    "Maybe using obj.something instead of obj.isSomething will yield the desired value." };
+        } else {
+            return new Object[] {
+                    "Maybe using obj.something(",
+                    (m.getParameterTypes().length != 0 ? "params" : ""),
+                    ") instead of obj.something will yield the desired value" };
+        }
+    }
+    
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/8d5263f2/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
index d891190..678b9ed 100644
--- 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
@@ -20,14 +20,12 @@ 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.Constants;
 import org.apache.freemarker.core.model.ObjectWrapperAndUnwrapper;
 import org.apache.freemarker.core.model.TemplateMarkupOutputModel;
 import org.apache.freemarker.core.model.TemplateModel;
@@ -35,7 +33,7 @@ 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.
+ * This class is used as a base for non-overloaded method models and for constructors.
  * (For overloaded methods and constructors see {@link OverloadedMethods}.)
  */
 class SimpleMethod {
@@ -45,101 +43,101 @@ class SimpleMethod {
               + "But consider if the Java method whose argument it will be can handle markup strings properly.";
     
     private final Member member;
-    private final Class[] argTypes;
+    private final Class<?>[] argTypes;
     
-    protected SimpleMethod(Member member, Class[] argTypes) {
+    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;
+    Object[] unwrapArguments(TemplateModel[] args, DefaultObjectWrapper wrapper) throws TemplateModelException {
+        if (args == null) {
+            args = Constants.EMPTY_TEMPLATE_MODEL_ARRAY;
         }
         boolean isVarArg = _MethodUtil.isVarargs(member);
         int typesLen = argTypes.length;
         if (isVarArg) {
-            if (typesLen - 1 > arguments.size()) {
+            if (typesLen - 1 > args.length) {
                 throw new _TemplateModelException(
                         _MethodUtil.invocationErrorMessageStart(member),
-                        " takes at least ", Integer.valueOf(typesLen - 1),
+                        " takes at least ", typesLen - 1,
                         typesLen - 1 == 1 ? " argument" : " arguments", ", but ",
-                        Integer.valueOf(arguments.size()), " was given.");
+                        args.length, " was given.");
             }
-        } else if (typesLen != arguments.size()) {
+        } else if (typesLen != args.length) {
             throw new _TemplateModelException(
                     _MethodUtil.invocationErrorMessageStart(member), 
-                    " takes ", Integer.valueOf(typesLen), typesLen == 1 ? " argument" : " arguments", ", but ",
-                    Integer.valueOf(arguments.size()), " was given.");
+                    " takes ", typesLen, typesLen == 1 ? " argument" : " arguments", ", but ",
+                    args.length, " was given.");
         }
          
-        return unwrapArguments(arguments, argTypes, isVarArg, wrapper);
+        return unwrapArguments(args, argTypes, isVarArg, wrapper);
     }
 
-    private Object[] unwrapArguments(List args, Class[] argTypes, boolean isVarargs,
+    private Object[] unwrapArguments(TemplateModel[] args, Class<?>[] argTypes, boolean isVarargs,
             DefaultObjectWrapper w)
     throws TemplateModelException {
         if (args == null) return null;
         
         int typesLen = argTypes.length;
-        int argsLen = args.size();
+        int argsLen = args.length;
         
         Object[] unwrappedArgs = new Object[typesLen];
         
         // Unwrap arguments:
-        Iterator it = args.iterator();
+        int argsIdx = 0;
         int normalArgCnt = isVarargs ? typesLen - 1 : typesLen; 
-        int argIdx = 0;
-        while (argIdx < normalArgCnt) {
-            Class argType = argTypes[argIdx];
-            TemplateModel argVal = (TemplateModel) it.next();
+        int unwrappedArgsIdx = 0;
+        while (unwrappedArgsIdx < normalArgCnt) {
+            Class<?> argType = argTypes[unwrappedArgsIdx];
+            TemplateModel argVal = args[argsIdx++];
             Object unwrappedArgVal = w.tryUnwrapTo(argVal, argType);
             if (unwrappedArgVal == ObjectWrapperAndUnwrapper.CANT_UNWRAP_TO_TARGET_CLASS) {
-                throw createArgumentTypeMismarchException(argIdx, argVal, argType);
+                throw createArgumentTypeMismatchException(unwrappedArgsIdx, argVal, argType);
             }
             if (unwrappedArgVal == null && argType.isPrimitive()) {
-                throw createNullToPrimitiveArgumentException(argIdx, argType); 
+                throw createNullToPrimitiveArgumentException(unwrappedArgsIdx, argType);
             }
             
-            unwrappedArgs[argIdx++] = unwrappedArgVal;
+            unwrappedArgs[unwrappedArgsIdx++] = 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);
+            Class<?> varargType = argTypes[typesLen - 1];
+            Class<?> varargItemType = varargType.getComponentType();
+            if (argsIdx >= args.length) {
+                unwrappedArgs[unwrappedArgsIdx] = Array.newInstance(varargItemType, 0);
             } else {
-                TemplateModel argVal = (TemplateModel) it.next();
+                TemplateModel argVal = args[argsIdx++];
                 
                 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
+                if (argsLen - unwrappedArgsIdx == 1
                         && (unwrappedArgVal = w.tryUnwrapTo(argVal, varargType))
                             != ObjectWrapperAndUnwrapper.CANT_UNWRAP_TO_TARGET_CLASS) {
                     // It was a vararg array.
-                    unwrappedArgs[argIdx++] = unwrappedArgVal;
+                    unwrappedArgs[unwrappedArgsIdx] = 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;
+                    int varargArrayLen = argsLen - unwrappedArgsIdx;
                     Object varargArray = Array.newInstance(varargItemType, varargArrayLen);
                     for (int varargIdx = 0; varargIdx < varargArrayLen; varargIdx++) {
-                        TemplateModel varargVal = (TemplateModel) (varargIdx == 0 ? argVal : it.next());
+                        TemplateModel varargVal = varargIdx == 0 ? argVal : args[argsIdx++];
                         Object unwrappedVarargVal = w.tryUnwrapTo(varargVal, varargItemType);
                         if (unwrappedVarargVal == ObjectWrapperAndUnwrapper.CANT_UNWRAP_TO_TARGET_CLASS) {
-                            throw createArgumentTypeMismarchException(
-                                    argIdx + varargIdx, varargVal, varargItemType);
+                            throw createArgumentTypeMismatchException(
+                                    unwrappedArgsIdx + varargIdx, varargVal, varargItemType);
                         }
                         
                         if (unwrappedVarargVal == null && varargItemType.isPrimitive()) {
-                            throw createNullToPrimitiveArgumentException(argIdx + varargIdx, varargItemType); 
+                            throw createNullToPrimitiveArgumentException(unwrappedArgsIdx + varargIdx, varargItemType);
                         }
                         Array.set(varargArray, varargIdx, unwrappedVarargVal);
                     }
-                    unwrappedArgs[argIdx++] = varargArray;
+                    unwrappedArgs[unwrappedArgsIdx] = varargArray;
                 }
             }
         }
@@ -147,11 +145,11 @@ class SimpleMethod {
         return unwrappedArgs;
     }
 
-    private TemplateModelException createArgumentTypeMismarchException(
-            int argIdx, TemplateModel argVal, Class targetType) {
+    private TemplateModelException createArgumentTypeMismatchException(
+            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)),
+                new _DelayedOrdinal(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))) {
@@ -160,10 +158,10 @@ class SimpleMethod {
         return new _TemplateModelException(desc);
     }
 
-    private TemplateModelException createNullToPrimitiveArgumentException(int argIdx, Class targetType) {
+    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)),
+                new _DelayedOrdinal(argIdx + 1),
                 " argument was null, but the target Java parameter type (", _ClassUtil.getShortClassName(targetType),
                 ") is primitive and so can't store null.");
     }

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/8d5263f2/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
index 9c4db4e..fc32348 100644
--- 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
@@ -27,20 +27,18 @@ import java.util.Iterator;
 import java.util.Map;
 
 import org.apache.freemarker.core.model.TemplateCollectionModel;
+import org.apache.freemarker.core.model.TemplateFunctionModel;
 import org.apache.freemarker.core.model.TemplateHashModelEx;
-import org.apache.freemarker.core.model.TemplateMethodModel;
 import org.apache.freemarker.core.model.TemplateModel;
 import org.apache.freemarker.core.model.TemplateModelException;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 /**
- * 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 TemplateMethodModel} instance.
- * Unfortunately, there is currently no support for bean property-style
- * calls of static methods, similar to that in {@link BeanModel}.
+ * 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 TemplateFunctionModel} instance. There is currently no support for bean property-style calls of static
+ * methods, similar to that in {@link BeanModel} (as it's not part for the JavaBeans specification).
  */
 final class StaticModel implements TemplateHashModelEx {
     
@@ -167,10 +165,10 @@ final class StaticModel implements TemplateHashModelEx {
                 Object value = entry.getValue();
                 if (value instanceof Method) {
                     Method method = (Method) value;
-                    entry.setValue(new JavaMethodModel(null, method,
+                    entry.setValue(new SimpleJavaMethodModel(null, method,
                             method.getParameterTypes(), wrapper));
                 } else if (value instanceof OverloadedMethods) {
-                    entry.setValue(new OverloadedMethodsModel(null, (OverloadedMethods) value, wrapper));
+                    entry.setValue(new OverloadedJavaMethodModel(null, (OverloadedMethods) value, wrapper));
                 }
             }
         }

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/8d5263f2/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
index 2394394..a93df33 100644
--- 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
@@ -21,26 +21,29 @@ package org.apache.freemarker.core.model.impl;
 
 import java.util.List;
 
-import org.apache.freemarker.core.model.TemplateMethodModel;
+import org.apache.freemarker.core.model.TemplateFunctionModel;
 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 TemplateMethodModel}-es that collect items from other
+ * list. It's mostly useful when implementing {@link TemplateFunctionModel}-es that collect items from other
  * {@link TemplateModel}-s.
  */
 public class TemplateModelListSequence implements TemplateSequenceModel {
     
-    private List/*<TemplateModel>*/ list;
+    private final List<? extends TemplateModel> list;
 
-    public TemplateModelListSequence(List list) {
+    /**
+     * @param list The list of items; will not be copied, will not be modified.
+     */
+    public TemplateModelListSequence(List<? extends TemplateModel> list) {
         this.list = list;
     }
 
     @Override
     public TemplateModel get(int index) {
-        return (TemplateModel) list.get(index);
+        return list.get(index);
     }
 
     @Override

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/8d5263f2/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/_MethodUtil.java
----------------------------------------------------------------------
diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/_MethodUtil.java b/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/_MethodUtil.java
index 82da455..004ebaa 100644
--- a/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/_MethodUtil.java
+++ b/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/_MethodUtil.java
@@ -64,7 +64,7 @@ public final class _MethodUtil {
      *         </ul> 
      */
     // TODO Seems that we don't use the full functionality of this anymore, so we could simplify this. See usages.
-    public static int isMoreOrSameSpecificParameterType(final Class specific, final Class generic, boolean bugfixed,
+    static int isMoreOrSameSpecificParameterType(final Class<?> specific, final Class<?> generic, boolean bugfixed,
             int ifHigherThan) {
         if (ifHigherThan >= 4) return 0;
         if (generic.isAssignableFrom(specific)) {
@@ -79,7 +79,7 @@ public final class _MethodUtil {
                     return isWideningPrimitiveNumberConversion(specific, generic) ? 3 : 0;
                 } else {  // => specificIsPrim && !genericIsPrim
                     if (bugfixed) {
-                        final Class specificAsBoxed = _ClassUtil.primitiveClassToBoxingClass(specific);
+                        final Class<?> specificAsBoxed = _ClassUtil.primitiveClassToBoxingClass(specific);
                         if (specificAsBoxed == generic) {
                             // A primitive class is more specific than its boxing class, because it can't store null
                             return 2;
@@ -110,7 +110,7 @@ public final class _MethodUtil {
         }  // of: !generic.isAssignableFrom(specific) 
     }
 
-    private static boolean isWideningPrimitiveNumberConversion(final Class source, final Class target) {
+    private static boolean isWideningPrimitiveNumberConversion(final Class<?> source, final Class<?> target) {
         if (target == Short.TYPE && (source == Byte.TYPE)) {
             return true;
         } else if (target == Integer.TYPE && 
@@ -134,7 +134,7 @@ public final class _MethodUtil {
         }
     }
 
-    private static boolean isWideningBoxedNumberConversion(final Class source, final Class target) {
+    private static boolean isWideningBoxedNumberConversion(final Class<?> source, final Class<?> target) {
         if (target == Short.class && source == Byte.class) {
             return true;
         } else if (target == Integer.class && 
@@ -161,42 +161,42 @@ public final class _MethodUtil {
     /**
      * Attention, this doesn't handle primitive classes correctly, nor numerical conversions.
      */
-    public static Set getAssignables(Class c1, Class c2) {
-        Set s = new HashSet();
+    static Set<Class<?>> getAssignables(Class<?> c1, Class<?> c2) {
+        Set<Class<?>> s = new HashSet<>();
         collectAssignables(c1, c2, s);
         return s;
     }
     
-    private static void collectAssignables(Class c1, Class c2, Set s) {
+    private static void collectAssignables(Class<?> c1, Class<?> c2, Set<Class<?>> s) {
         if (c1.isAssignableFrom(c2)) {
             s.add(c1);
         }
-        Class sc = c1.getSuperclass();
+        Class<?> sc = c1.getSuperclass();
         if (sc != null) {
             collectAssignables(sc, c2, s);
         }
-        Class[] itf = c1.getInterfaces();
-        for (Class anItf : itf) {
+        Class<?>[] itf = c1.getInterfaces();
+        for (Class<?> anItf : itf) {
             collectAssignables(anItf, c2, s);
         }
     }
 
-    public static Class[] getParameterTypes(Member member) {
+    public static Class<?>[] getParameterTypes(Member member) {
         if (member instanceof Method) {
             return ((Method) member).getParameterTypes();
         }
-        if (member instanceof Constructor) {
-            return ((Constructor) member).getParameterTypes();
+        if (member instanceof Constructor<?>) {
+            return ((Constructor<?>) member).getParameterTypes();
         }
         throw new IllegalArgumentException("\"member\" must be Method or Constructor");
     }
 
-    public static boolean isVarargs(Member member) {
+    static boolean isVarargs(Member member) {
         if (member instanceof Method) { 
             return ((Method) member).isVarArgs();
         }
         if (member instanceof Constructor) {
-            return ((Constructor) member).isVarArgs();
+            return ((Constructor<?>) member).isVarArgs();
         }
         throw new BugException();
     }
@@ -223,7 +223,7 @@ public final class _MethodUtil {
         sb.append(member.getName());
 
         sb.append('(');
-        Class[] paramTypes = _MethodUtil.getParameterTypes(member);
+        Class<?>[] paramTypes = _MethodUtil.getParameterTypes(member);
         for (int i = 0; i < paramTypes.length; i++) {
             if (i != 0) sb.append(", ");
             String paramTypeDecl = _ClassUtil.getShortClassName(paramTypes[i]);
@@ -239,7 +239,7 @@ public final class _MethodUtil {
         return sb.toString();
     }
 
-    public static Object[] invocationErrorMessageStart(Member member) {
+    static Object[] invocationErrorMessageStart(Member member) {
         return invocationErrorMessageStart(member, member instanceof Constructor);
     }
     
@@ -247,7 +247,7 @@ public final class _MethodUtil {
         return new Object[] { "Java ", isConstructor ? "constructor " : "method ", new _DelayedJQuote(member) };
     }
 
-    public static TemplateModelException newInvocationTemplateModelException(Object object, Member member, Throwable e) {
+    static TemplateModelException newInvocationTemplateModelException(Object object, Member member, Throwable e) {
         return newInvocationTemplateModelException(
                 object,
                 member,
@@ -256,7 +256,7 @@ public final class _MethodUtil {
                 e);
     }
 
-    public static TemplateModelException newInvocationTemplateModelException(Object object, CallableMemberDescriptor callableMemberDescriptor, Throwable e) {
+    static TemplateModelException newInvocationTemplateModelException(Object object, CallableMemberDescriptor callableMemberDescriptor, Throwable e) {
         return newInvocationTemplateModelException(
                 object,
                 new _DelayedConversionToString(callableMemberDescriptor) {
@@ -294,7 +294,7 @@ public final class _MethodUtil {
      * Extracts the JavaBeans property from a reader method name, or returns {@code null} if the method name doesn't
      * look like a reader method name.
      */
-    public static String getBeanPropertyNameFromReaderMethodName(String name, Class<?> returnType) {
+    static String getBeanPropertyNameFromReaderMethodName(String name, Class<?> returnType) {
         int start;
         if (name.startsWith("get")) {
             start = 3;

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/8d5263f2/freemarker-core/src/main/java/org/apache/freemarker/core/util/FTLUtil.java
----------------------------------------------------------------------
diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/util/FTLUtil.java b/freemarker-core/src/main/java/org/apache/freemarker/core/util/FTLUtil.java
index 2923647..7c58284 100644
--- a/freemarker-core/src/main/java/org/apache/freemarker/core/util/FTLUtil.java
+++ b/freemarker-core/src/main/java/org/apache/freemarker/core/util/FTLUtil.java
@@ -34,7 +34,6 @@ import org.apache.freemarker.core.model.TemplateFunctionModel;
 import org.apache.freemarker.core.model.TemplateHashModel;
 import org.apache.freemarker.core.model.TemplateHashModelEx;
 import org.apache.freemarker.core.model.TemplateMarkupOutputModel;
-import org.apache.freemarker.core.model.TemplateMethodModel;
 import org.apache.freemarker.core.model.TemplateModel;
 import org.apache.freemarker.core.model.TemplateModelIterator;
 import org.apache.freemarker.core.model.TemplateNodeModel;
@@ -45,6 +44,7 @@ import org.apache.freemarker.core.model.TemplateSequenceModel;
 import org.apache.freemarker.core.model.WrapperTemplateModel;
 import org.apache.freemarker.core.model.impl.BeanAndStringModel;
 import org.apache.freemarker.core.model.impl.BeanModel;
+import org.apache.freemarker.core.model.impl.JavaMethodModel;
 
 /**
  * Static utility methods that perform tasks specific to the FreeMarker Template Language (FTL).
@@ -760,7 +760,7 @@ public final class FTLUtil {
         }
 
         if (callable instanceof TemplateFunctionModel) {
-            String f = "function"; // TODO [FM3][CF] Should say "method" sometimes
+            String f = callable instanceof JavaMethodModel ? "method" : "function";
             result = d == null ? f : d + "+" + f;
         }
 
@@ -799,7 +799,8 @@ public final class FTLUtil {
                 appendTypeName(sb, typeNamesAppended, _CoreAPI.isMacro(cl) ? "macro" : "directive");
             }
             if (TemplateFunctionModel.class.isAssignableFrom(cl)) {
-                appendTypeName(sb, typeNamesAppended, "function"); // TODO [FM3][CF] should say "method" sometimes
+                appendTypeName(sb, typeNamesAppended,
+                        JavaMethodModel.class.isAssignableFrom(cl) ? "method" : "function");
             }
         }
 
@@ -812,10 +813,6 @@ public final class FTLUtil {
             appendTypeName(sb, typeNamesAppended, "iterator");
         }
 
-        if (TemplateMethodModel.class.isAssignableFrom(cl)) {
-            appendTypeName(sb, typeNamesAppended, "method");
-        }
-
         if (Environment.Namespace.class.isAssignableFrom(cl)) {
             appendTypeName(sb, typeNamesAppended, "namespace");
         } else if (TemplateHashModelEx.class.isAssignableFrom(cl)) {

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/8d5263f2/freemarker-core/src/main/javacc/FTL.jj
----------------------------------------------------------------------
diff --git a/freemarker-core/src/main/javacc/FTL.jj b/freemarker-core/src/main/javacc/FTL.jj
index 5d1b354..4201d84 100644
--- a/freemarker-core/src/main/javacc/FTL.jj
+++ b/freemarker-core/src/main/javacc/FTL.jj
@@ -2040,7 +2040,7 @@ ASTExpression DynamicKey(ASTExpression exp) :
 /**
  * production for an arglist part of a method invocation.
  */
-ASTExpMethodCall MethodArgs(ASTExpression exp) :
+ASTExpFunctionCall MethodArgs(ASTExpression exp) :
 {
         ArrayList args = new ArrayList();
         Token end;
@@ -2051,7 +2051,7 @@ ASTExpMethodCall MethodArgs(ASTExpression exp) :
         end = <CLOSE_PAREN>
         {
             args.trimToSize();
-            ASTExpMethodCall result = new ASTExpMethodCall(exp, args);
+            ASTExpFunctionCall result = new ASTExpFunctionCall(exp, args);
             result.setLocation(template, exp, end);
             return result;
         }