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:27 UTC

[1/8] incubator-freemarker git commit: FREEMARKER-64: TemplateModel and TemplateModelEx changes (but they will be probably replaced by TemplateFunctionModel later): Removed FM2 `TemplateMethodModel`, then renamed `TemplateMethodModelEx` to `TemplateMetho

Repository: incubator-freemarker
Updated Branches:
  refs/heads/3 a3311d52e -> b867458aa


http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/1333a715/freemarker-core/src/main/java/org/apache/freemarker/core/BuiltInsForStringsBasic.java
----------------------------------------------------------------------
diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/BuiltInsForStringsBasic.java b/freemarker-core/src/main/java/org/apache/freemarker/core/BuiltInsForStringsBasic.java
index e8cd15c..ce3ff56 100644
--- a/freemarker-core/src/main/java/org/apache/freemarker/core/BuiltInsForStringsBasic.java
+++ b/freemarker-core/src/main/java/org/apache/freemarker/core/BuiltInsForStringsBasic.java
@@ -27,7 +27,6 @@ import java.util.regex.Pattern;
 
 import org.apache.freemarker.core.model.TemplateBooleanModel;
 import org.apache.freemarker.core.model.TemplateMethodModel;
-import org.apache.freemarker.core.model.TemplateMethodModelEx;
 import org.apache.freemarker.core.model.TemplateModel;
 import org.apache.freemarker.core.model.TemplateModelException;
 import org.apache.freemarker.core.model.TemplateScalarModel;
@@ -70,7 +69,7 @@ class BuiltInsForStringsBasic {
 
     static class containsBI extends ASTExpBuiltIn {
         
-        private class BIMethod implements TemplateMethodModelEx {
+        private class BIMethod implements TemplateMethodModel {
             
             private final String s;
     
@@ -79,9 +78,9 @@ class BuiltInsForStringsBasic {
             }
     
             @Override
-            public Object exec(List args) throws TemplateModelException {
+            public TemplateModel execute(List<? extends TemplateModel> args) throws TemplateModelException {
                 checkMethodArgCount(args, 1);
-                return s.indexOf(getStringMethodArg(args, 0)) != -1
+                return s.contains(getStringMethodArg(args, 0))
                         ? TemplateBooleanModel.TRUE : TemplateBooleanModel.FALSE;
             }
         }
@@ -95,7 +94,7 @@ class BuiltInsForStringsBasic {
 
     static class ends_withBI extends BuiltInForString {
     
-        private class BIMethod implements TemplateMethodModelEx {
+        private class BIMethod implements TemplateMethodModel {
             private String s;
     
             private BIMethod(String s) {
@@ -103,7 +102,7 @@ class BuiltInsForStringsBasic {
             }
     
             @Override
-            public Object exec(List args) throws TemplateModelException {
+            public TemplateModel execute(List<? extends TemplateModel> args) throws TemplateModelException {
                 checkMethodArgCount(args, 1);
                 return s.endsWith(getStringMethodArg(args, 0)) ?
                         TemplateBooleanModel.TRUE : TemplateBooleanModel.FALSE;
@@ -118,7 +117,7 @@ class BuiltInsForStringsBasic {
 
     static class ensure_ends_withBI extends BuiltInForString {
         
-        private class BIMethod implements TemplateMethodModelEx {
+        private class BIMethod implements TemplateMethodModel {
             private String s;
     
             private BIMethod(String s) {
@@ -126,7 +125,7 @@ class BuiltInsForStringsBasic {
             }
     
             @Override
-            public Object exec(List args) throws TemplateModelException {
+            public TemplateModel execute(List<? extends TemplateModel> args) throws TemplateModelException {
                 checkMethodArgCount(args, 1);
                 String suffix = getStringMethodArg(args, 0);
                 return new SimpleScalar(s.endsWith(suffix) ? s : s + suffix);
@@ -141,7 +140,7 @@ class BuiltInsForStringsBasic {
 
     static class ensure_starts_withBI extends BuiltInForString {
         
-        private class BIMethod implements TemplateMethodModelEx {
+        private class BIMethod implements TemplateMethodModel {
             private String s;
     
             private BIMethod(String s) {
@@ -149,7 +148,7 @@ class BuiltInsForStringsBasic {
             }
     
             @Override
-            public Object exec(List args) throws TemplateModelException {
+            public TemplateModel execute(List<? extends TemplateModel> args) throws TemplateModelException {
                 checkMethodArgCount(args, 1, 3);
                 
                 final String checkedPrefix = getStringMethodArg(args, 0);
@@ -190,7 +189,7 @@ class BuiltInsForStringsBasic {
 
     static class index_ofBI extends ASTExpBuiltIn {
         
-        private class BIMethod implements TemplateMethodModelEx {
+        private class BIMethod implements TemplateMethodModel {
             
             private final String s;
             
@@ -199,7 +198,7 @@ class BuiltInsForStringsBasic {
             }
             
             @Override
-            public Object exec(List args) throws TemplateModelException {
+            public TemplateModel execute(List<? extends TemplateModel> args) throws TemplateModelException {
                 int argCnt = args.size();
                 checkMethodArgCount(argCnt, 1, 2);
                 String subStr = getStringMethodArg(args, 0);
@@ -226,7 +225,7 @@ class BuiltInsForStringsBasic {
     }
     
     static class keep_afterBI extends BuiltInForString {
-        class KeepAfterMethod implements TemplateMethodModelEx {
+        class KeepAfterMethod implements TemplateMethodModel {
             private String s;
 
             KeepAfterMethod(String s) {
@@ -234,7 +233,7 @@ class BuiltInsForStringsBasic {
             }
 
             @Override
-            public Object exec(List args) throws TemplateModelException {
+            public TemplateModel execute(List<? extends TemplateModel> args) throws TemplateModelException {
                 int argCnt = args.size();
                 checkMethodArgCount(argCnt, 1, 2);
                 String separatorString = getStringMethodArg(args, 0);
@@ -272,7 +271,7 @@ class BuiltInsForStringsBasic {
     }
     
     static class keep_after_lastBI extends BuiltInForString {
-        class KeepAfterMethod implements TemplateMethodModelEx {
+        class KeepAfterMethod implements TemplateMethodModel {
             private String s;
 
             KeepAfterMethod(String s) {
@@ -280,7 +279,7 @@ class BuiltInsForStringsBasic {
             }
 
             @Override
-            public Object exec(List args) throws TemplateModelException {
+            public TemplateModel execute(List<? extends TemplateModel> args) throws TemplateModelException {
                 int argCnt = args.size();
                 checkMethodArgCount(argCnt, 1, 2);
                 String separatorString = getStringMethodArg(args, 0);
@@ -325,7 +324,7 @@ class BuiltInsForStringsBasic {
     }
     
     static class keep_beforeBI extends BuiltInForString {
-        class KeepUntilMethod implements TemplateMethodModelEx {
+        class KeepUntilMethod implements TemplateMethodModel {
             private String s;
 
             KeepUntilMethod(String s) {
@@ -333,7 +332,7 @@ class BuiltInsForStringsBasic {
             }
 
             @Override
-            public Object exec(List args) throws TemplateModelException {
+            public TemplateModel execute(List<? extends TemplateModel> args) throws TemplateModelException {
                 int argCnt = args.size();
                 checkMethodArgCount(argCnt, 1, 2);
                 String separatorString = getStringMethodArg(args, 0);
@@ -369,7 +368,7 @@ class BuiltInsForStringsBasic {
     
     // TODO
     static class keep_before_lastBI extends BuiltInForString {
-        class KeepUntilMethod implements TemplateMethodModelEx {
+        class KeepUntilMethod implements TemplateMethodModel {
             private String s;
 
             KeepUntilMethod(String s) {
@@ -377,7 +376,7 @@ class BuiltInsForStringsBasic {
             }
 
             @Override
-            public Object exec(List args) throws TemplateModelException {
+            public TemplateModel execute(List<? extends TemplateModel> args) throws TemplateModelException {
                 int argCnt = args.size();
                 checkMethodArgCount(argCnt, 1, 2);
                 String separatorString = getStringMethodArg(args, 0);
@@ -436,7 +435,7 @@ class BuiltInsForStringsBasic {
 
     static class padBI extends BuiltInForString {
         
-        private class BIMethod implements TemplateMethodModelEx {
+        private class BIMethod implements TemplateMethodModel {
             
             private final String s;
     
@@ -445,7 +444,7 @@ class BuiltInsForStringsBasic {
             }
     
             @Override
-            public Object exec(List args) throws TemplateModelException {
+            public TemplateModel execute(List<? extends TemplateModel> args) throws TemplateModelException {
                 int argCnt  = args.size();
                 checkMethodArgCount(argCnt, 1, 2);
     
@@ -487,7 +486,7 @@ class BuiltInsForStringsBasic {
     
     static class remove_beginningBI extends BuiltInForString {
         
-        private class BIMethod implements TemplateMethodModelEx {
+        private class BIMethod implements TemplateMethodModel {
             private String s;
     
             private BIMethod(String s) {
@@ -495,7 +494,7 @@ class BuiltInsForStringsBasic {
             }
     
             @Override
-            public Object exec(List args) throws TemplateModelException {
+            public TemplateModel execute(List<? extends TemplateModel> args) throws TemplateModelException {
                 checkMethodArgCount(args, 1);
                 String prefix = getStringMethodArg(args, 0);
                 return new SimpleScalar(s.startsWith(prefix) ? s.substring(prefix.length()) : s);
@@ -510,7 +509,7 @@ class BuiltInsForStringsBasic {
 
     static class remove_endingBI extends BuiltInForString {
     
-        private class BIMethod implements TemplateMethodModelEx {
+        private class BIMethod implements TemplateMethodModel {
             private String s;
     
             private BIMethod(String s) {
@@ -518,7 +517,7 @@ class BuiltInsForStringsBasic {
             }
     
             @Override
-            public Object exec(List args) throws TemplateModelException {
+            public TemplateModel execute(List<? extends TemplateModel> args) throws TemplateModelException {
                 checkMethodArgCount(args, 1);
                 String suffix = getStringMethodArg(args, 0);
                 return new SimpleScalar(s.endsWith(suffix) ? s.substring(0, s.length() - suffix.length()) : s);
@@ -540,12 +539,14 @@ class BuiltInsForStringsBasic {
             }
 
             @Override
-            public Object exec(List args) throws TemplateModelException {
+            public TemplateModel execute(List<? extends TemplateModel> args) throws TemplateException {
                 int argCnt = args.size();
                 checkMethodArgCount(argCnt, 1, 2);
-                String splitString = (String) args.get(0);
-                long flags = argCnt > 1 ? RegexpHelper.parseFlagString((String) args.get(1)) : 0;
-                String[] result = null;
+                String splitString = _CallableUtils.castArgToString(args, 0);
+                long flags = argCnt > 1
+                        ? RegexpHelper.parseFlagString(_CallableUtils.castArgToString(args, 1))
+                        : 0;
+                String[] result;
                 if ((flags & RegexpHelper.RE_FLAG_REGEXP) == 0) {
                     RegexpHelper.checkNonRegexpFlags("split", flags);
                     result = _StringUtil.split(s, splitString,
@@ -567,7 +568,7 @@ class BuiltInsForStringsBasic {
     
     static class starts_withBI extends BuiltInForString {
     
-        private class BIMethod implements TemplateMethodModelEx {
+        private class BIMethod implements TemplateMethodModel {
             private String s;
     
             private BIMethod(String s) {
@@ -575,7 +576,7 @@ class BuiltInsForStringsBasic {
             }
     
             @Override
-            public Object exec(List args) throws TemplateModelException {
+            public TemplateModel execute(List<? extends TemplateModel> args) throws TemplateModelException {
                 checkMethodArgCount(args, 1);
                 return s.startsWith(getStringMethodArg(args, 0)) ?
                         TemplateBooleanModel.TRUE : TemplateBooleanModel.FALSE;
@@ -592,10 +593,10 @@ class BuiltInsForStringsBasic {
         
         @Override
         TemplateModel calculateResult(final String s, final Environment env) throws TemplateException {
-            return new TemplateMethodModelEx() {
+            return new TemplateMethodModel() {
                 
                 @Override
-                public Object exec(java.util.List args) throws TemplateModelException {
+                public TemplateModel execute(List<? extends TemplateModel> args) throws TemplateModelException {
                     int argCount = args.size();
                     checkMethodArgCount(argCount, 1, 2);
     
@@ -618,9 +619,9 @@ class BuiltInsForStringsBasic {
                         }
                         if (beginIdx > endIdx) {
                             throw MessageUtil.newMethodArgsInvalidValueException("?" + key,
-                                    "The begin index argument, ", Integer.valueOf(beginIdx),
+                                    "The begin index argument, ", beginIdx,
                                     ", shouldn't be greater than the end index argument, ",
-                                    Integer.valueOf(endIdx), ".");
+                                    endIdx, ".");
                         }
                         return new SimpleScalar(s.substring(beginIdx, endIdx));
                     } else {
@@ -633,15 +634,14 @@ class BuiltInsForStringsBasic {
                     return MessageUtil.newMethodArgInvalidValueException(
                             "?" + key, argIdx,
                             "The index mustn't be greater than the length of the string, ",
-                            Integer.valueOf(len),
-                            ", but it was ", Integer.valueOf(idx), ".");
+                            len, ", but it was ", idx, ".");
                 }
     
                 private TemplateModelException newIndexLessThan0Exception(
                         int argIdx, int idx) throws TemplateModelException {
                     return MessageUtil.newMethodArgInvalidValueException(
                             "?" + key, argIdx,
-                            "The index must be at least 0, but was ", Integer.valueOf(idx), ".");
+                            "The index must be at least 0, but was ", idx, ".");
                 }
                 
             };

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/1333a715/freemarker-core/src/main/java/org/apache/freemarker/core/BuiltInsForStringsEncoding.java
----------------------------------------------------------------------
diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/BuiltInsForStringsEncoding.java b/freemarker-core/src/main/java/org/apache/freemarker/core/BuiltInsForStringsEncoding.java
index bfb44c6..cee1b49 100644
--- a/freemarker-core/src/main/java/org/apache/freemarker/core/BuiltInsForStringsEncoding.java
+++ b/freemarker-core/src/main/java/org/apache/freemarker/core/BuiltInsForStringsEncoding.java
@@ -148,11 +148,11 @@ class BuiltInsForStringsEncoding {
         protected abstract String encodeWithCharset(Charset charset) throws UnsupportedEncodingException;
     
         @Override
-        public Object exec(List args) throws TemplateModelException {
+        public TemplateModel execute(List<? extends TemplateModel> args) throws TemplateModelException {
             parent.checkMethodArgCount(args.size(), 1);
             try {
-                String charsetName = (String) args.get(0);
-                Charset charset = null;
+                String charsetName = _CallableUtils.castArgToString(args,0);
+                Charset charset;
                 try {
                     charset = Charset.forName(charsetName);
                 } catch (UnsupportedCharsetException e) {

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/1333a715/freemarker-core/src/main/java/org/apache/freemarker/core/BuiltInsForStringsMisc.java
----------------------------------------------------------------------
diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/BuiltInsForStringsMisc.java b/freemarker-core/src/main/java/org/apache/freemarker/core/BuiltInsForStringsMisc.java
index 2d50556..6bf7e2a 100644
--- a/freemarker-core/src/main/java/org/apache/freemarker/core/BuiltInsForStringsMisc.java
+++ b/freemarker-core/src/main/java/org/apache/freemarker/core/BuiltInsForStringsMisc.java
@@ -28,7 +28,7 @@ import org.apache.freemarker.core.model.ArgumentArrayLayout;
 import org.apache.freemarker.core.model.ObjectWrapper;
 import org.apache.freemarker.core.model.TemplateBooleanModel;
 import org.apache.freemarker.core.model.TemplateDirectiveModel;
-import org.apache.freemarker.core.model.TemplateMethodModelEx;
+import org.apache.freemarker.core.model.TemplateMethodModel;
 import org.apache.freemarker.core.model.TemplateModel;
 import org.apache.freemarker.core.model.TemplateModelException;
 import org.apache.freemarker.core.model.TemplateScalarModel;
@@ -72,7 +72,7 @@ class BuiltInsForStringsMisc {
         TemplateModel calculateResult(String s, Environment env) throws TemplateException {
             Template parentTemplate = getTemplate();
             
-            ASTExpression exp = null;
+            ASTExpression exp;
             try {
                 try {
                     ParsingConfiguration pCfg = parentTemplate.getParsingConfiguration();
@@ -151,9 +151,11 @@ class BuiltInsForStringsMisc {
             ASTExpression sourceExpr = null;
             String id = "anonymous_interpreted";
             if (model instanceof TemplateSequenceModel) {
-                sourceExpr = ((ASTExpression) new ASTExpDynamicKeyName(target, new ASTExpNumberLiteral(Integer.valueOf(0))).copyLocationFrom(target));
+                sourceExpr = ((ASTExpression) new ASTExpDynamicKeyName(target, new ASTExpNumberLiteral(0))
+                        .copyLocationFrom(target));
                 if (((TemplateSequenceModel) model).size() > 1) {
-                    id = ((ASTExpression) new ASTExpDynamicKeyName(target, new ASTExpNumberLiteral(Integer.valueOf(1))).copyLocationFrom(target)).evalAndCoerceToPlainText(env);
+                    id = ((ASTExpression) new ASTExpDynamicKeyName(target, new ASTExpNumberLiteral(1))
+                            .copyLocationFrom(target)).evalAndCoerceToPlainText(env);
                 }
             } else if (model instanceof TemplateScalarModel) {
                 sourceExpr = target;
@@ -252,12 +254,12 @@ class BuiltInsForStringsMisc {
             return new ConstructorFunction(target.evalAndCoerceToPlainText(env), env, target.getTemplate());
         }
 
-        class ConstructorFunction implements TemplateMethodModelEx {
+        class ConstructorFunction implements TemplateMethodModel {
 
             private final Class<?> cl;
             private final Environment env;
             
-            public ConstructorFunction(String classname, Environment env, Template template) throws TemplateException {
+            ConstructorFunction(String classname, Environment env, Template template) throws TemplateException {
                 this.env = env;
                 cl = env.getNewBuiltinClassResolver().resolve(classname, env, template);
                 if (!TemplateModel.class.isAssignableFrom(cl)) {
@@ -271,13 +273,13 @@ class BuiltInsForStringsMisc {
             }
 
             @Override
-            public Object exec(List arguments) throws TemplateModelException {
+            public TemplateModel execute(List<? extends TemplateModel> args) throws TemplateModelException {
                 ObjectWrapper ow = env.getObjectWrapper();
                 if (ow instanceof DefaultObjectWrapper) {
-                    return ((DefaultObjectWrapper) ow).newInstance(cl, arguments);
+                    return ow.wrap(((DefaultObjectWrapper) ow).newInstance(cl, args));
                 }
 
-                if (!arguments.isEmpty()) {
+                if (!args.isEmpty()) {
                     throw new TemplateModelException(
                             "className?new(args) only supports 0 arguments in the current configuration, because "
                             + " the objectWrapper setting value is not a "
@@ -285,7 +287,7 @@ class BuiltInsForStringsMisc {
                             " (or its subclass).");
                 }
                 try {
-                    return cl.newInstance();
+                    return ow.wrap(cl.newInstance());
                 } catch (Exception e) {
                     throw new TemplateModelException("Failed to instantiate "
                             + cl.getName() + " with its parameterless constructor; see cause exception", e);

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/1333a715/freemarker-core/src/main/java/org/apache/freemarker/core/BuiltInsForStringsRegexp.java
----------------------------------------------------------------------
diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/BuiltInsForStringsRegexp.java b/freemarker-core/src/main/java/org/apache/freemarker/core/BuiltInsForStringsRegexp.java
index 0420d64..025b599 100644
--- a/freemarker-core/src/main/java/org/apache/freemarker/core/BuiltInsForStringsRegexp.java
+++ b/freemarker-core/src/main/java/org/apache/freemarker/core/BuiltInsForStringsRegexp.java
@@ -70,12 +70,14 @@ class BuiltInsForStringsRegexp {
             }
             
             @Override
-            public Object exec(List args) throws TemplateModelException {
+            public TemplateModel execute(List<? extends TemplateModel> args) throws TemplateException {
                 int argCnt = args.size();
                 checkMethodArgCount(argCnt, 1, 2);
                 
-                String patternString = (String) args.get(0);
-                long flags = argCnt > 1 ? RegexpHelper.parseFlagString((String) args.get(1)) : 0;
+                String patternString = _CallableUtils.castArgToString(args, 0);
+                long flags = argCnt > 1
+                        ? RegexpHelper.parseFlagString(_CallableUtils.castArgToString(args, 1))
+                        : 0;
                 if ((flags & RegexpHelper.RE_FLAG_FIRST_ONLY) != 0) {
                     RegexpHelper.logFlagWarning("?" + key + " doesn't support the \"f\" flag.");
                 }
@@ -101,12 +103,14 @@ class BuiltInsForStringsRegexp {
             }
 
             @Override
-            public Object exec(List args) throws TemplateModelException {
+            public TemplateModel execute(List<? extends TemplateModel> args) throws TemplateException {
                 int argCnt = args.size();
                 checkMethodArgCount(argCnt, 2, 3);
-                String arg1 = (String) args.get(0);
-                String arg2 = (String) args.get(1);
-                long flags = argCnt > 2 ? RegexpHelper.parseFlagString((String) args.get(2)) : 0;
+                String arg1 = _CallableUtils.castArgToString(args, 0);
+                String arg2 = _CallableUtils.castArgToString(args, 1);
+                long flags = argCnt > 2
+                        ? RegexpHelper.parseFlagString(_CallableUtils.castArgToString(args, 2))
+                        : 0;
                 String result;
                 if ((flags & RegexpHelper.RE_FLAG_REGEXP) == 0) {
                     RegexpHelper.checkNonRegexpFlags("replace", flags);

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/1333a715/freemarker-core/src/main/java/org/apache/freemarker/core/Environment.java
----------------------------------------------------------------------
diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/Environment.java b/freemarker-core/src/main/java/org/apache/freemarker/core/Environment.java
index 25ea39d..ee212db 100644
--- a/freemarker-core/src/main/java/org/apache/freemarker/core/Environment.java
+++ b/freemarker-core/src/main/java/org/apache/freemarker/core/Environment.java
@@ -581,7 +581,7 @@ public final class Environment extends MutableProcessingConfiguration<Environmen
         try {
             TemplateDirectiveModel nodeProcessor = getNodeProcessor(node);
             if (nodeProcessor != null) {
-                _TemplateCallableModelUtils.executeWith0Arguments(
+                _CallableUtils.executeWith0Arguments(
                         nodeProcessor, NonTemplateCallPlace.INSTANCE, out, this);
             } else if (nodeProcessor == null) {
                 String nodeType = node.getNodeType();
@@ -640,7 +640,7 @@ public final class Environment extends MutableProcessingConfiguration<Environmen
     void fallback() throws TemplateException, IOException {
         TemplateDirectiveModel nodeProcessor = getNodeProcessor(currentNodeName, currentNodeNS, nodeNamespaceIndex);
         if (nodeProcessor != null) {
-            _TemplateCallableModelUtils.executeWith0Arguments(
+            _CallableUtils.executeWith0Arguments(
                     nodeProcessor, NonTemplateCallPlace.INSTANCE, out, this);
         }
     }

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/1333a715/freemarker-core/src/main/java/org/apache/freemarker/core/NonStringException.java
----------------------------------------------------------------------
diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/NonStringException.java b/freemarker-core/src/main/java/org/apache/freemarker/core/NonStringException.java
index c8cbc6d..6cf29f4 100644
--- a/freemarker-core/src/main/java/org/apache/freemarker/core/NonStringException.java
+++ b/freemarker-core/src/main/java/org/apache/freemarker/core/NonStringException.java
@@ -19,6 +19,8 @@
 
 package org.apache.freemarker.core;
 
+import java.io.Serializable;
+
 import org.apache.freemarker.core.model.TemplateBooleanModel;
 import org.apache.freemarker.core.model.TemplateDateModel;
 import org.apache.freemarker.core.model.TemplateModel;
@@ -37,7 +39,9 @@ public class NonStringException extends UnexpectedTypeException {
     static final Class[] STRING_COERCABLE_TYPES = new Class[] {
         TemplateScalarModel.class, TemplateNumberModel.class, TemplateDateModel.class, TemplateBooleanModel.class
     };
-    
+
+    private static final Class<?>[] EXPECTED_TYPES = { TemplateScalarModel.class };
+
     private static final String DEFAULT_DESCRIPTION
             = "Expecting " + NonStringException.STRING_COERCABLE_TYPES_DESC + " value here";
 
@@ -70,5 +74,11 @@ public class NonStringException extends UnexpectedTypeException {
             ASTExpression blamed, TemplateModel model, String[] tips, Environment env) throws InvalidReferenceException {
         super(blamed, model, NonStringException.STRING_COERCABLE_TYPES_DESC, STRING_COERCABLE_TYPES, tips, env);
     }
-        
+
+    NonStringException(
+            Serializable argumentNameOrIndex, TemplateModel model, String[] tips, Environment env)
+            throws InvalidReferenceException {
+        super(argumentNameOrIndex, model, "string", EXPECTED_TYPES, tips, env);
+    }
+
 }

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/1333a715/freemarker-core/src/main/java/org/apache/freemarker/core/_CallableUtils.java
----------------------------------------------------------------------
diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/_CallableUtils.java b/freemarker-core/src/main/java/org/apache/freemarker/core/_CallableUtils.java
new file mode 100644
index 0000000..0a69e51
--- /dev/null
+++ b/freemarker-core/src/main/java/org/apache/freemarker/core/_CallableUtils.java
@@ -0,0 +1,156 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.freemarker.core;
+
+import java.io.IOException;
+import java.io.Serializable;
+import java.io.Writer;
+import java.util.List;
+
+import org.apache.freemarker.core.model.ArgumentArrayLayout;
+import org.apache.freemarker.core.model.Constants;
+import org.apache.freemarker.core.model.TemplateCallableModel;
+import org.apache.freemarker.core.model.TemplateDirectiveModel;
+import org.apache.freemarker.core.model.TemplateFunctionModel;
+import org.apache.freemarker.core.model.TemplateModel;
+import org.apache.freemarker.core.model.TemplateNumberModel;
+import org.apache.freemarker.core.model.TemplateScalarModel;
+
+/**
+ * For internal use only; don't depend on this, there's no backward compatibility guarantee at all!
+ */
+// TODO [FM3] Most functionality here should be made public on some way. Also BuiltIn-s has some duplicates utiltity
+// methods for this functionality (checking arguments). Need to clean this up.
+public final class _CallableUtils {
+
+    private _CallableUtils() {
+        //
+    }
+
+    public static void executeWith0Arguments(
+            TemplateDirectiveModel directive, CallPlace callPlace, Writer out, Environment env)
+            throws IOException, TemplateException {
+        directive.execute(getArgumentArrayWithNoArguments(directive), callPlace, out, env);
+    }
+
+    public static TemplateModel executeWith0Arguments(
+            TemplateFunctionModel function, CallPlace callPlace, Environment env)
+            throws TemplateException {
+        return function.execute(getArgumentArrayWithNoArguments(function), callPlace, env);
+    }
+
+    private static TemplateModel[] getArgumentArrayWithNoArguments(TemplateCallableModel callable) {
+        ArgumentArrayLayout argsLayout = callable.getArgumentArrayLayout();
+        int totalLength = argsLayout.getTotalLength();
+        if (totalLength == 0) {
+            return Constants.EMPTY_TEMPLATE_MODEL_ARRAY;
+        } else {
+            TemplateModel[] args = new TemplateModel[totalLength];
+
+            int positionalVarargsArgumentIndex = argsLayout.getPositionalVarargsArgumentIndex();
+            if (positionalVarargsArgumentIndex != -1) {
+                args[positionalVarargsArgumentIndex] = Constants.EMPTY_SEQUENCE;
+            }
+
+            int namedVarargsArgumentIndex = argsLayout.getNamedVarargsArgumentIndex();
+            if (namedVarargsArgumentIndex != -1) {
+                args[namedVarargsArgumentIndex] = Constants.EMPTY_SEQUENCE;
+            }
+            
+            return args;
+        }
+    }
+
+    public static Number castArgToNumber(TemplateModel[] args, int argIndex, boolean allowNull)
+            throws TemplateException {
+        return castArgToNumber(args[argIndex], argIndex, allowNull);
+    }
+
+    public static Number castArgToNumber(TemplateModel argValue, int argIndex, boolean allowNull)
+            throws TemplateException {
+        return castArgToNumber(argValue, null, argIndex, allowNull);
+    }
+
+    public static Number castArgToNumber(TemplateModel argValue, String argName, boolean allowNull)
+            throws TemplateException {
+        return castArgToNumber(argValue, argName, -1, allowNull);
+    }
+
+    private static Number castArgToNumber(TemplateModel argValue, String argName, int argIndex, boolean allowNull)
+            throws TemplateException {
+        if (argValue instanceof TemplateNumberModel) {
+            return ((TemplateNumberModel) argValue).getAsNumber();
+        }
+        if (argValue == null) {
+            if (allowNull) {
+                return null;
+            }
+            throw new _MiscTemplateException(
+                    "The ", argName != null ? new _DelayedJQuote(argName) : new _DelayedOrdinal(argIndex + 1),
+                    " argument can't be null.");
+        }
+        throw new NonNumericalException((Serializable) argName != null ? argName : argIndex, argValue, null, null);
+    }
+    
+    //
+
+    public static String castArgToString(List<? extends TemplateModel> args, int argIndex) throws TemplateException {
+        return castArgToString(args, argIndex, false);
+    }
+
+    public static String castArgToString(List<? extends TemplateModel> args, int argIndex, boolean allowNull) throws
+            TemplateException {
+        return castArgToString(args.get(argIndex), argIndex, allowNull);
+    }
+
+    public static String castArgToString(TemplateModel[] args, int argIndex, boolean allowNull) throws TemplateException {
+        return castArgToString(args[argIndex], argIndex, allowNull);
+    }
+
+    public static String castArgToString(TemplateModel argValue, int argIndex) throws TemplateException {
+        return castArgToString(argValue, argIndex, false);
+    }
+
+    public static String castArgToString(TemplateModel argValue, int argIndex, boolean allowNull) throws TemplateException {
+        return castArgToString(argValue, null, argIndex, allowNull);
+    }
+
+    public static String castArgToString(TemplateModel argValue, String argName, boolean allowNull) throws TemplateException {
+        return castArgToString(argValue, argName, -1, allowNull);
+    }
+
+    private static String castArgToString(
+            TemplateModel argValue, String argName, int argIndex,
+            boolean allowNull) throws TemplateException {
+        if (argValue instanceof TemplateScalarModel) {
+            return _EvalUtil.modelToString((TemplateScalarModel) argValue, null, null);
+        }
+        if (argValue == null) {
+            if (allowNull) {
+                return null;
+            }
+            throw new _MiscTemplateException(
+                    "The ", argName != null ? new _DelayedJQuote(argName) : new _DelayedOrdinal(argIndex + 1),
+                    " argument can't be null.");
+        }
+        throw new NonStringException((Serializable) argName != null ? argName : argIndex, argValue, null, null);
+    }
+    
+}

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/1333a715/freemarker-core/src/main/java/org/apache/freemarker/core/_ObjectBuilderSettingEvaluator.java
----------------------------------------------------------------------
diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/_ObjectBuilderSettingEvaluator.java b/freemarker-core/src/main/java/org/apache/freemarker/core/_ObjectBuilderSettingEvaluator.java
index 3ff249a..d2fe617 100644
--- a/freemarker-core/src/main/java/org/apache/freemarker/core/_ObjectBuilderSettingEvaluator.java
+++ b/freemarker-core/src/main/java/org/apache/freemarker/core/_ObjectBuilderSettingEvaluator.java
@@ -36,7 +36,7 @@ import java.util.Map;
 import java.util.Properties;
 
 import org.apache.freemarker.core.model.TemplateHashModel;
-import org.apache.freemarker.core.model.TemplateMethodModelEx;
+import org.apache.freemarker.core.model.TemplateMethodModel;
 import org.apache.freemarker.core.model.TemplateModel;
 import org.apache.freemarker.core.model.TemplateModelException;
 import org.apache.freemarker.core.model.impl.DefaultObjectWrapper;
@@ -763,13 +763,13 @@ public class _ObjectBuilderSettingEvaluator {
                     throw new _ObjectBuilderSettingEvaluationException(
                             "Can't find " + beanPropSetter + " as FreeMarker method.");
                 }
-                if (!(m instanceof TemplateMethodModelEx)) {
+                if (!(m instanceof TemplateMethodModel)) {
                     throw new _ObjectBuilderSettingEvaluationException(
-                            _StringUtil.jQuote(beanPropSetter.getName()) + " wasn't a TemplateMethodModelEx.");
+                            _StringUtil.jQuote(beanPropSetter.getName()) + " wasn't a TemplateMethodModel.");
                 }
                 List/*TemplateModel*/ args = new ArrayList();
                 args.add(env.getObjectWrapper().wrap(namedParamValues.get(i)));
-                ((TemplateMethodModelEx) m).exec(args);
+                ((TemplateMethodModel) m).execute(args);
             } catch (Exception e) {
                 throw new _ObjectBuilderSettingEvaluationException(
                         "Failed to set " + _StringUtil.jQuote(name), e);

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/1333a715/freemarker-core/src/main/java/org/apache/freemarker/core/_TemplateCallableModelUtils.java
----------------------------------------------------------------------
diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/_TemplateCallableModelUtils.java b/freemarker-core/src/main/java/org/apache/freemarker/core/_TemplateCallableModelUtils.java
deleted file mode 100644
index 277dc1a..0000000
--- a/freemarker-core/src/main/java/org/apache/freemarker/core/_TemplateCallableModelUtils.java
+++ /dev/null
@@ -1,112 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *   http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-
-package org.apache.freemarker.core;
-
-import java.io.IOException;
-import java.io.Writer;
-
-import org.apache.freemarker.core.model.ArgumentArrayLayout;
-import org.apache.freemarker.core.model.Constants;
-import org.apache.freemarker.core.model.TemplateCallableModel;
-import org.apache.freemarker.core.model.TemplateDirectiveModel;
-import org.apache.freemarker.core.model.TemplateFunctionModel;
-import org.apache.freemarker.core.model.TemplateModel;
-import org.apache.freemarker.core.model.TemplateNumberModel;
-
-/**
- * For internal use only; don't depend on this, there's no backward compatibility guarantee at all!
- */
-// TODO [FM3] Most functionality here should be made public on some way
-public final class _TemplateCallableModelUtils {
-
-    private _TemplateCallableModelUtils() {
-        //
-    }
-
-    public static void executeWith0Arguments(
-            TemplateDirectiveModel directive, CallPlace callPlace, Writer out, Environment env)
-            throws IOException, TemplateException {
-        directive.execute(getArgumentArrayWithNoArguments(directive), callPlace, out, env);
-    }
-
-    public static TemplateModel executeWith0Arguments(
-            TemplateFunctionModel function, CallPlace callPlace, Environment env)
-            throws TemplateException {
-        return function.execute(getArgumentArrayWithNoArguments(function), callPlace, env);
-    }
-
-    private static TemplateModel[] getArgumentArrayWithNoArguments(TemplateCallableModel callable) {
-        ArgumentArrayLayout argsLayout = callable.getArgumentArrayLayout();
-        int totalLength = argsLayout.getTotalLength();
-        if (totalLength == 0) {
-            return Constants.EMPTY_TEMPLATE_MODEL_ARRAY;
-        } else {
-            TemplateModel[] args = new TemplateModel[totalLength];
-
-            int positionalVarargsArgumentIndex = argsLayout.getPositionalVarargsArgumentIndex();
-            if (positionalVarargsArgumentIndex != -1) {
-                args[positionalVarargsArgumentIndex] = Constants.EMPTY_SEQUENCE;
-            }
-
-            int namedVarargsArgumentIndex = argsLayout.getNamedVarargsArgumentIndex();
-            if (namedVarargsArgumentIndex != -1) {
-                args[namedVarargsArgumentIndex] = Constants.EMPTY_SEQUENCE;
-            }
-            
-            return args;
-        }
-    }
-
-    public static TemplateNumberModel castArgumentToNumber(TemplateModel[] args, int argIndex, boolean allowNull,
-            Environment env) throws TemplateException {
-        return getTemplateNumberModel(args[argIndex], argIndex, allowNull, env);
-    }
-
-    private static TemplateNumberModel getTemplateNumberModel(TemplateModel argValue, int argIndex, boolean allowNull,
-            Environment env) throws TemplateException {
-        if (argValue instanceof TemplateNumberModel) {
-            return (TemplateNumberModel) argValue;
-        }
-        if (argValue == null) {
-            if (allowNull) {
-                return null;
-            }
-            throw new _MiscTemplateException(env,
-                    "The ", new _DelayedOrdinal(argIndex + 1), " argument can't be null.");
-        }
-        throw new NonNumericalException(argIndex, argValue, null, env);
-    }
-
-    public static TemplateNumberModel castArgumentToNumber(TemplateModel argValue, String argName, boolean allowNull,
-            Environment env) throws TemplateException {
-        if (argValue instanceof TemplateNumberModel) {
-            return (TemplateNumberModel) argValue;
-        }
-        if (argValue == null) {
-            if (allowNull) {
-                return null;
-            }
-            throw new _MiscTemplateException(env,
-                    "The ", new _DelayedJQuote(argName), " argument can't be null.");
-        }
-        throw new NonNumericalException(argName, argValue, null, env);
-    }
-
-}

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/1333a715/freemarker-core/src/main/java/org/apache/freemarker/core/debug/DebugModel.java
----------------------------------------------------------------------
diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/debug/DebugModel.java b/freemarker-core/src/main/java/org/apache/freemarker/core/debug/DebugModel.java
index 0158bd9..1c6bc66 100644
--- a/freemarker-core/src/main/java/org/apache/freemarker/core/debug/DebugModel.java
+++ b/freemarker-core/src/main/java/org/apache/freemarker/core/debug/DebugModel.java
@@ -46,7 +46,6 @@ public interface DebugModel extends Remote {
     public static final int TYPE_HASH          =   64;
     public static final int TYPE_HASH_EX       =  128;
     public static final int TYPE_METHOD        =  256;
-    public static final int TYPE_METHOD_EX     =  512;
     public static final int TYPE_DIRECTIVE     = 1024;
     public static final int TYPE_ENVIRONMENT   = 2048;
     public static final int TYPE_TEMPLATE      = 4096;

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/1333a715/freemarker-core/src/main/java/org/apache/freemarker/core/debug/RmiDebugModelImpl.java
----------------------------------------------------------------------
diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/debug/RmiDebugModelImpl.java b/freemarker-core/src/main/java/org/apache/freemarker/core/debug/RmiDebugModelImpl.java
index e8e8cf1..7ab7ec7 100644
--- a/freemarker-core/src/main/java/org/apache/freemarker/core/debug/RmiDebugModelImpl.java
+++ b/freemarker-core/src/main/java/org/apache/freemarker/core/debug/RmiDebugModelImpl.java
@@ -32,7 +32,6 @@ import org.apache.freemarker.core.model.TemplateDirectiveModel;
 import org.apache.freemarker.core.model.TemplateHashModel;
 import org.apache.freemarker.core.model.TemplateHashModelEx;
 import org.apache.freemarker.core.model.TemplateMethodModel;
-import org.apache.freemarker.core.model.TemplateMethodModelEx;
 import org.apache.freemarker.core.model.TemplateModel;
 import org.apache.freemarker.core.model.TemplateModelException;
 import org.apache.freemarker.core.model.TemplateModelIterator;
@@ -155,9 +154,8 @@ class RmiDebugModelImpl extends UnicastRemoteObject implements DebugModel {
         if (model instanceof TemplateSequenceModel) type += TYPE_SEQUENCE;
         if (model instanceof TemplateCollectionModel) type += TYPE_COLLECTION;
         if (model instanceof TemplateHashModelEx) type += TYPE_HASH_EX;
-        else if (model instanceof TemplateHashModel) type += TYPE_HASH;
-        if (model instanceof TemplateMethodModelEx) type += TYPE_METHOD_EX;
-        else if (model instanceof TemplateMethodModel) type += TYPE_METHOD;
+        if (model instanceof TemplateHashModel) type += TYPE_HASH;
+        if (model instanceof TemplateMethodModel) type += TYPE_METHOD;
         if (model instanceof TemplateDirectiveModel) type += TYPE_DIRECTIVE;
         return type;
     }

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/1333a715/freemarker-core/src/main/java/org/apache/freemarker/core/model/GeneralPurposeNothing.java
----------------------------------------------------------------------
diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/model/GeneralPurposeNothing.java b/freemarker-core/src/main/java/org/apache/freemarker/core/model/GeneralPurposeNothing.java
index da1c102..92b0b58 100644
--- a/freemarker-core/src/main/java/org/apache/freemarker/core/model/GeneralPurposeNothing.java
+++ b/freemarker-core/src/main/java/org/apache/freemarker/core/model/GeneralPurposeNothing.java
@@ -28,7 +28,7 @@ import java.util.List;
  */
 
 final class GeneralPurposeNothing
-implements TemplateBooleanModel, TemplateScalarModel, TemplateSequenceModel, TemplateHashModelEx, TemplateMethodModelEx {
+implements TemplateBooleanModel, TemplateScalarModel, TemplateSequenceModel, TemplateHashModelEx, TemplateMethodModel {
 
     public static final TemplateModel INSTANCE = new GeneralPurposeNothing();
       
@@ -66,7 +66,7 @@ implements TemplateBooleanModel, TemplateScalarModel, TemplateSequenceModel, Tem
     }
 
     @Override
-    public Object exec(List args) {
+    public TemplateModel execute(List<? extends TemplateModel> args) {
         return null;
     }
     

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/1333a715/freemarker-core/src/main/java/org/apache/freemarker/core/model/TemplateMethodModel.java
----------------------------------------------------------------------
diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/model/TemplateMethodModel.java b/freemarker-core/src/main/java/org/apache/freemarker/core/model/TemplateMethodModel.java
index 5bfe7e3..c35a500 100644
--- a/freemarker-core/src/main/java/org/apache/freemarker/core/model/TemplateMethodModel.java
+++ b/freemarker-core/src/main/java/org/apache/freemarker/core/model/TemplateMethodModel.java
@@ -17,44 +17,37 @@
  * under the License.
  */
 
-/*
- * 22 October 1999: This class added by Holger Arendt.
- */
-
 package org.apache.freemarker.core.model;
 
 import java.util.List;
 
 import org.apache.freemarker.core.Environment;
+import org.apache.freemarker.core.TemplateException;
+import org.apache.freemarker.core.util.DeepUnwrap;
 
 /**
- * "method" template language data type: Objects that act like functions. The name comes from that their original
- * application was calling Java methods via {@link org.apache.freemarker.core.model.impl.DefaultObjectWrapper}.
- * 
- * <p>In templates they are used like {@code myMethod("foo", "bar")} or {@code myJavaObject.myJavaMethod("foo", "bar")}. 
+ * "method" template language data type: Objects that act like functions. Their main application is calling
+ * Java methods via {@link org.apache.freemarker.core.model.impl.DefaultObjectWrapper}, but you can implement this
+ * interface to invoke top-level functions too.
  * 
- * @deprecated Use {@link TemplateMethodModelEx} instead. This interface is from the old times when the only kind of
- *    value you could pass in was string.
+ * <p>In templates they are used like {@code myMethod(1, "foo")} or {@code myJavaObject.myJavaMethod(1, "foo")}.
  */
-@Deprecated
 public interface TemplateMethodModel extends TemplateModel {
 
     /**
-     * Executes the method call. All arguments passed to the method call are 
-     * coerced to strings before being passed, if the FreeMarker rules allow
-     * the coercion. If some of the passed arguments can not be coerced to a
-     * string, an exception will be raised in the engine and the method will 
-     * not be called. If your method would like to act on actual data model 
-     * objects instead of on their string representations, implement the 
-     * {@link TemplateMethodModelEx} instead.
-     * 
-     * @param arguments a <tt>List</tt> of <tt>String</tt> objects
-     *     containing the values of the arguments passed to the method.
+     * Executes the method call.
      *  
+     * @param args a {@link List} of {@link TemplateModel}-s,
+     *     containing the arguments passed to the method. If the implementation absolutely wants
+     *     to operate on POJOs, it can use the static utility methods in the {@link DeepUnwrap}
+     *     class to easily obtain them. However, unwrapping is not always possible (or not perfectly), and isn't always
+     *     efficient, so it's recommended to use the original {@link TemplateModel} value as much as possible.
+     *
      * @return the return value of the method, or {@code null}. If the returned value
      *     does not implement {@link TemplateModel}, it will be automatically 
-     *     wrapped using the {@link Environment#getObjectWrapper() environment 
+     *     wrapped using the {@link Environment#getObjectWrapper() environment's 
      *     object wrapper}.
      */
-    Object exec(List arguments) throws TemplateModelException;
-}
+    TemplateModel execute(List<? extends TemplateModel> args) throws TemplateException;
+    
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/1333a715/freemarker-core/src/main/java/org/apache/freemarker/core/model/TemplateMethodModelEx.java
----------------------------------------------------------------------
diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/model/TemplateMethodModelEx.java b/freemarker-core/src/main/java/org/apache/freemarker/core/model/TemplateMethodModelEx.java
deleted file mode 100644
index 2517d22..0000000
--- a/freemarker-core/src/main/java/org/apache/freemarker/core/model/TemplateMethodModelEx.java
+++ /dev/null
@@ -1,54 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *   http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-
-package org.apache.freemarker.core.model;
-
-import java.util.List;
-
-import org.apache.freemarker.core.Environment;
-import org.apache.freemarker.core.util.DeepUnwrap;
-
-/**
- * "extended method" template language data type: Objects that act like functions. Their main application is calling
- * Java methods via {@link org.apache.freemarker.core.model.impl.DefaultObjectWrapper}, but you can implement this interface to invoke
- * top-level functions too. They are "extended" compared to the deprecated {@link TemplateMethodModel}, which could only
- * accept string parameters.
- * 
- * <p>In templates they are used like {@code myMethod(1, "foo")} or {@code myJavaObject.myJavaMethod(1, "foo")}.
- */
-public interface TemplateMethodModelEx extends TemplateMethodModel {
-
-    /**
-     * Executes the method call.
-     *  
-     * @param arguments a {@link List} of {@link TemplateModel}-s,
-     *     containing the arguments passed to the method. If the implementation absolutely wants 
-     *     to operate on POJOs, it can use the static utility methods in the {@link DeepUnwrap} 
-     *     class to easily obtain them. However, unwrapping is not always possible (or not perfectly), and isn't always
-     *     efficient, so it's recommended to use the original {@link TemplateModel} value as much as possible.
-     *      
-     * @return the return value of the method, or {@code null}. If the returned value
-     *     does not implement {@link TemplateModel}, it will be automatically 
-     *     wrapped using the {@link Environment#getObjectWrapper() environment's 
-     *     object wrapper}.
-     */
-    @Override
-    Object exec(List arguments) throws TemplateModelException;
-    
-}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/1333a715/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/BeanModel.java
----------------------------------------------------------------------
diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/BeanModel.java b/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/BeanModel.java
index aa2f2f1..bc7ce67 100644
--- a/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/BeanModel.java
+++ b/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/BeanModel.java
@@ -38,6 +38,7 @@ import org.apache.freemarker.core._TemplateModelException;
 import org.apache.freemarker.core.model.AdapterTemplateModel;
 import org.apache.freemarker.core.model.TemplateCollectionModel;
 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.apache.freemarker.core.model.TemplateModelIterator;
@@ -105,7 +106,7 @@ public class BeanModel
     /**
      * Uses Beans introspection to locate a property or method with name
      * matching the key name. If a method or property is found, it's wrapped
-     * into {@link org.apache.freemarker.core.model.TemplateMethodModelEx} (for a method or
+     * into {@link TemplateMethodModel} (for a method or
      * indexed property), or evaluated on-the-fly and the return value wrapped
      * into appropriate model (for a simple property) Models for various
      * properties and methods are cached on a per-class basis, so the costly

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/1333a715/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 e4436e9..1a0e251 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
@@ -56,7 +56,7 @@ 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.TemplateHashModel;
-import org.apache.freemarker.core.model.TemplateMethodModelEx;
+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 +481,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 TemplateMethodModelEx} that selects an
+     * explicitly select a an overload, while otherwise you would get a {@link TemplateMethodModel} 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,7 +489,7 @@ 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 TemplateMethodModelEx wrap(Object object, Method method) {
+    public TemplateMethodModel wrap(Object object, Method method) {
         return new JavaMethodModel(object, method, method.getParameterTypes(), this);
     }
 
@@ -582,7 +582,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 TemplateMethodModelEx etc.
+        // passed as an argument to TemplateMethodModel etc.
         if (model instanceof AdapterTemplateModel) {
             Object wrapped = ((AdapterTemplateModel) model).getAdaptedObject(targetClass);
             if (targetClass == Object.class || targetClass.isInstance(wrapped)) {

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/1333a715/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 6408117..f89952e 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
@@ -24,14 +24,15 @@ import java.lang.reflect.Method;
 import java.util.List;
 
 import org.apache.freemarker.core._UnexpectedTypeErrorExplainerTemplateModel;
-import org.apache.freemarker.core.model.TemplateMethodModelEx;
+import org.apache.freemarker.core.model.TemplateMethodModel;
+import org.apache.freemarker.core.model.TemplateModel;
 import org.apache.freemarker.core.model.TemplateModelException;
 
 /**
- * Wraps a {@link Method} into the {@link TemplateMethodModelEx} interface. It is used by {@link BeanModel} to wrap
+ * Wraps a {@link Method} into the {@link TemplateMethodModel} interface. It is used by {@link BeanModel} to wrap
  * non-overloaded methods.
  */
-public final class JavaMethodModel extends SimpleMethod implements TemplateMethodModelEx,
+public final class JavaMethodModel extends SimpleMethod implements TemplateMethodModel,
         _UnexpectedTypeErrorExplainerTemplateModel {
     private final Object object;
     private final DefaultObjectWrapper wrapper;
@@ -53,10 +54,10 @@ public final class JavaMethodModel extends SimpleMethod implements TemplateMetho
      * Invokes the method, passing it the arguments from the list.
      */
     @Override
-    public Object exec(List arguments) throws TemplateModelException {
+    public TemplateModel execute(List<? extends TemplateModel> args) throws TemplateModelException {
         try {
             return wrapper.invokeMethod(object, (Method) getMember(), 
-                    unwrapArguments(arguments, wrapper));
+                    unwrapArguments(args, wrapper));
         } catch (TemplateModelException e) {
             throw e;
         } catch (Exception e) {

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/1333a715/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
index 9a66a6d..72ca642 100644
--- 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
@@ -23,14 +23,14 @@ package org.apache.freemarker.core.model.impl;
 import java.util.List;
 
 import org.apache.freemarker.core.model.TemplateMethodModel;
-import org.apache.freemarker.core.model.TemplateMethodModelEx;
+import org.apache.freemarker.core.model.TemplateModel;
 import org.apache.freemarker.core.model.TemplateModelException;
 
 /**
  * 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 TemplateMethodModelEx {
+class OverloadedMethodsModel implements TemplateMethodModel {
     private final Object object;
     private final OverloadedMethods overloadedMethods;
     private final DefaultObjectWrapper wrapper;
@@ -49,8 +49,8 @@ class OverloadedMethodsModel implements TemplateMethodModelEx {
      * unambiguously.
      */
     @Override
-    public Object exec(List arguments) throws TemplateModelException {
-        MemberAndArguments maa = overloadedMethods.getMemberAndArguments(arguments, wrapper);
+    public TemplateModel execute(List<? extends TemplateModel> args) throws TemplateModelException {
+        MemberAndArguments maa = overloadedMethods.getMemberAndArguments(args, wrapper);
         try {
             return maa.invokeMethod(wrapper, object);
         } catch (Exception e) {

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/1333a715/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 31af451..457c7ef 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
@@ -31,7 +31,7 @@ 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.TemplateMethodModel;
 import org.apache.freemarker.core.model.TemplateModel;
 import org.apache.freemarker.core.model.TemplateModelException;
 
@@ -54,9 +54,9 @@ public class ResourceBundleModel
     extends
     BeanModel
     implements
-    TemplateMethodModelEx {
+        TemplateMethodModel {
 
-    private Hashtable formats = null;
+    private Hashtable<String, MessageFormat> formats = null;
 
     public ResourceBundleModel(ResourceBundle bundle, DefaultObjectWrapper wrapper) {
         super(bundle, wrapper);
@@ -108,13 +108,13 @@ public class ResourceBundleModel
      * rest of the arguments. The created MessageFormats are cached for later reuse.
      */
     @Override
-    public Object exec(List arguments)
+    public TemplateModel execute(List<? extends TemplateModel> args)
         throws TemplateModelException {
         // Must have at least one argument - the key
-        if (arguments.size() < 1)
+        if (args.size() < 1)
             throw new TemplateModelException("No message key was specified");
         // Read it
-        Iterator it = arguments.iterator();
+        Iterator<? extends TemplateModel> it = args.iterator();
         String key = unwrap((TemplateModel) it.next()).toString();
         try {
             if (!it.hasNext()) {
@@ -122,10 +122,10 @@ public class ResourceBundleModel
             }
     
             // 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());
+            int paramsLen = args.size() - 1;
+            Object[] params = new Object[paramsLen];
+            for (int i = 0; i < paramsLen; ++i)
+                params[i] = unwrap(it.next());
     
             // Invoke format
             return new BeanAndStringModel(format(key, params), wrapper);

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/1333a715/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 8a7f082..9c4db4e 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
@@ -28,6 +28,7 @@ import java.util.Map;
 
 import org.apache.freemarker.core.model.TemplateCollectionModel;
 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;
@@ -37,7 +38,7 @@ 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 org.apache.freemarker.core.model.TemplateMethodModelEx} instance.
+ * 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}.
  */

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/1333a715/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 b049c63..2394394 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,13 +21,13 @@ package org.apache.freemarker.core.model.impl;
 
 import java.util.List;
 
-import org.apache.freemarker.core.model.TemplateMethodModelEx;
+import org.apache.freemarker.core.model.TemplateMethodModel;
 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
+ * list. It's mostly useful when implementing {@link TemplateMethodModel}-es that collect items from other
  * {@link TemplateModel}-s.
  */
 public class TemplateModelListSequence implements TemplateSequenceModel {

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/1333a715/freemarker-servlet/src/main/java/org/apache/freemarker/servlet/jsp/CustomTagAndELFunctionCombiner.java
----------------------------------------------------------------------
diff --git a/freemarker-servlet/src/main/java/org/apache/freemarker/servlet/jsp/CustomTagAndELFunctionCombiner.java b/freemarker-servlet/src/main/java/org/apache/freemarker/servlet/jsp/CustomTagAndELFunctionCombiner.java
index fb63309..ff9db12 100644
--- a/freemarker-servlet/src/main/java/org/apache/freemarker/servlet/jsp/CustomTagAndELFunctionCombiner.java
+++ b/freemarker-servlet/src/main/java/org/apache/freemarker/servlet/jsp/CustomTagAndELFunctionCombiner.java
@@ -28,7 +28,7 @@ import org.apache.freemarker.core._UnexpectedTypeErrorExplainerTemplateModel;
 import org.apache.freemarker.core.model.ArgumentArrayLayout;
 import org.apache.freemarker.core.CallPlace;
 import org.apache.freemarker.core.model.TemplateDirectiveModel;
-import org.apache.freemarker.core.model.TemplateMethodModelEx;
+import org.apache.freemarker.core.model.TemplateMethodModel;
 import org.apache.freemarker.core.model.TemplateModel;
 import org.apache.freemarker.core.model.TemplateModelException;
 import org.apache.freemarker.core.model.impl.JavaMethodModel;
@@ -47,7 +47,7 @@ class CustomTagAndELFunctionCombiner {
      * @param customTag
      *            A {@link TemplateDirectiveModel}.
      */
-    static TemplateModel combine(TemplateModel customTag, TemplateMethodModelEx elFunction) {
+    static TemplateModel combine(TemplateModel customTag, TemplateMethodModel elFunction) {
         if (customTag instanceof TemplateDirectiveModel) {
             return elFunction instanceof JavaMethodModel //
                     ? new TemplateDirectiveModelAndSimpleMethodModel( //
@@ -62,7 +62,7 @@ class CustomTagAndELFunctionCombiner {
 
     /**
      * Tells if the value can be used as the "custom tag" parameter to
-     * {@link #combine(TemplateModel, TemplateMethodModelEx)}.
+     * {@link #combine(TemplateModel, TemplateMethodModel)}.
      */
     static boolean canBeCombinedAsCustomTag(TemplateModel tm) {
         return (tm instanceof TemplateDirectiveModel) && !(tm instanceof CombinedTemplateModel);
@@ -70,10 +70,10 @@ class CustomTagAndELFunctionCombiner {
 
     /**
      * Tells if the value can be used as the "EL function" parameter to
-     * {@link #combine(TemplateModel, TemplateMethodModelEx)}.
+     * {@link #combine(TemplateModel, TemplateMethodModel)}.
      */
     static boolean canBeCombinedAsELFunction(TemplateModel tm) {
-        return tm instanceof TemplateMethodModelEx && !(tm instanceof CombinedTemplateModel);
+        return tm instanceof TemplateMethodModel && !(tm instanceof CombinedTemplateModel);
     }
 
     private static class CombinedTemplateModel {
@@ -81,7 +81,7 @@ class CustomTagAndELFunctionCombiner {
     }
 
     private static class TemplateDirectiveModelAndSimpleMethodModel extends CombinedTemplateModel
-            implements TemplateDirectiveModel, TemplateMethodModelEx,
+            implements TemplateDirectiveModel, TemplateMethodModel,
             _UnexpectedTypeErrorExplainerTemplateModel {
 
         private final TemplateDirectiveModel templateDirectiveModel;
@@ -94,8 +94,8 @@ class CustomTagAndELFunctionCombiner {
         }
 
         @Override
-        public Object exec(List arguments) throws TemplateModelException {
-            return simpleMethodModel.exec(arguments);
+        public TemplateModel execute(List<? extends TemplateModel> args) throws TemplateModelException {
+            return simpleMethodModel.execute(args);
         }
 
         @Override
@@ -121,20 +121,20 @@ class CustomTagAndELFunctionCombiner {
     }
 
     private static class TemplateDirectiveModelAndTemplateMethodModelEx extends CombinedTemplateModel
-            implements TemplateDirectiveModel, TemplateMethodModelEx {
+            implements TemplateDirectiveModel, TemplateMethodModel {
 
         private final TemplateDirectiveModel templateDirectiveModel;
-        private final TemplateMethodModelEx templateMethodModelEx;
+        private final TemplateMethodModel templateMethodModelEx;
 
         public TemplateDirectiveModelAndTemplateMethodModelEx( //
-                TemplateDirectiveModel templateDirectiveModel, TemplateMethodModelEx templateMethodModelEx) {
+                TemplateDirectiveModel templateDirectiveModel, TemplateMethodModel templateMethodModelEx) {
             this.templateDirectiveModel = templateDirectiveModel;
             this.templateMethodModelEx = templateMethodModelEx;
         }
 
         @Override
-        public Object exec(List arguments) throws TemplateModelException {
-            return templateMethodModelEx.exec(arguments);
+        public TemplateModel execute(List<? extends TemplateModel> args) throws TemplateException {
+            return templateMethodModelEx.execute(args);
         }
 
         @Override

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/1333a715/freemarker-servlet/src/main/java/org/apache/freemarker/servlet/jsp/TaglibFactory.java
----------------------------------------------------------------------
diff --git a/freemarker-servlet/src/main/java/org/apache/freemarker/servlet/jsp/TaglibFactory.java b/freemarker-servlet/src/main/java/org/apache/freemarker/servlet/jsp/TaglibFactory.java
index 606cef8..98634bd 100644
--- a/freemarker-servlet/src/main/java/org/apache/freemarker/servlet/jsp/TaglibFactory.java
+++ b/freemarker-servlet/src/main/java/org/apache/freemarker/servlet/jsp/TaglibFactory.java
@@ -66,7 +66,7 @@ import org.apache.freemarker.core.Environment;
 import org.apache.freemarker.core.model.ObjectWrapper;
 import org.apache.freemarker.core.model.TemplateDirectiveModel;
 import org.apache.freemarker.core.model.TemplateHashModel;
-import org.apache.freemarker.core.model.TemplateMethodModelEx;
+import org.apache.freemarker.core.model.TemplateMethodModel;
 import org.apache.freemarker.core.model.TemplateModel;
 import org.apache.freemarker.core.model.TemplateModelException;
 import org.apache.freemarker.core.model.impl.DefaultObjectWrapper;
@@ -233,7 +233,7 @@ public class TaglibFactory implements TemplateHashModel {
      * 
      * @return a {@link TemplateHashModel} representing the JSP taglib. Each element of this hash represents a single
      *         custom tag or EL function from the library, implemented as a {@link TemplateDirectiveModel} or
-     *         {@link TemplateMethodModelEx}, respectively.
+     *         {@link TemplateMethodModel}, respectively.
      */
     @Override
     public TemplateModel get(final String taglibUri) throws TemplateModelException {
@@ -1777,7 +1777,7 @@ public class TaglibFactory implements TemplateHashModel {
                     if (replacedTagOrFunction != null) {
                         if (CustomTagAndELFunctionCombiner.canBeCombinedAsELFunction(replacedTagOrFunction)) {
                             tagsAndFunctions.put(tagNameCData, CustomTagAndELFunctionCombiner.combine(
-                                    customTagModel, (TemplateMethodModelEx) replacedTagOrFunction));
+                                    customTagModel, (TemplateMethodModel) replacedTagOrFunction));
                         } else {
                             if (LOG.isWarnEnabled()) {
                                 LOG.warn("TLD contains multiple tags with name " + _StringUtil.jQuote(tagNameCData)
@@ -1816,7 +1816,7 @@ public class TaglibFactory implements TemplateHashModel {
                                 locator);
                     }
 
-                    final TemplateMethodModelEx elFunctionModel;
+                    final TemplateMethodModel elFunctionModel;
                     try {
                         elFunctionModel = defaultObjectWrapper.wrap(null, functionMethod);
                     } catch (Exception e) {

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/1333a715/freemarker-servlet/src/test/java/org/apache/freemarker/servlet/jsp/TLDParsingTest.java
----------------------------------------------------------------------
diff --git a/freemarker-servlet/src/test/java/org/apache/freemarker/servlet/jsp/TLDParsingTest.java b/freemarker-servlet/src/test/java/org/apache/freemarker/servlet/jsp/TLDParsingTest.java
index 38e69b5..43285c5 100644
--- a/freemarker-servlet/src/test/java/org/apache/freemarker/servlet/jsp/TLDParsingTest.java
+++ b/freemarker-servlet/src/test/java/org/apache/freemarker/servlet/jsp/TLDParsingTest.java
@@ -33,12 +33,10 @@ import javax.servlet.jsp.tagext.TagSupport;
 import javax.xml.parsers.SAXParserFactory;
 
 import org.apache.freemarker.core.Configuration;
-import org.apache.freemarker.core.model.TemplateMethodModelEx;
+import org.apache.freemarker.core.model.TemplateMethodModel;
 import org.apache.freemarker.core.model.TemplateScalarModel;
 import org.apache.freemarker.core.model.impl.DefaultObjectWrapper;
 import org.apache.freemarker.core.model.impl.SimpleScalar;
-import org.apache.freemarker.servlet.jsp.JspTagModelBase;
-import org.apache.freemarker.servlet.jsp.TaglibFactory;
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -84,13 +82,13 @@ public class TLDParsingTest {
         tag = (JspTagModelBase) tagsAndFunctions.get("setStringAttributeTag2");
         assertNotNull(tag);
 
-        TemplateMethodModelEx function = (TemplateMethodModelEx) tagsAndFunctions.get("toUpperCase");
+        TemplateMethodModel function = (TemplateMethodModel) tagsAndFunctions.get("toUpperCase");
         assertNotNull(function);
-        TemplateScalarModel result = (TemplateScalarModel) function.exec(Arrays.asList(new SimpleScalar("abc")));
+        TemplateScalarModel result = (TemplateScalarModel) function.execute(Arrays.asList(new SimpleScalar("abc")));
         assertEquals("ABC", result.getAsString());
-        function = (TemplateMethodModelEx) tagsAndFunctions.get("toUpperCase2");
+        function = (TemplateMethodModel) tagsAndFunctions.get("toUpperCase2");
         assertNotNull(function);
-        result = (TemplateScalarModel) function.exec(Arrays.asList(new SimpleScalar("abc")));
+        result = (TemplateScalarModel) function.execute(Arrays.asList(new SimpleScalar("abc")));
         assertEquals("ABC", result.getAsString());
     }
 


[2/8] incubator-freemarker git commit: FREEMARKER-64: TemplateModel and TemplateModelEx changes (but they will be probably replaced by TemplateFunctionModel later): Removed FM2 `TemplateMethodModel`, then renamed `TemplateMethodModelEx` to `TemplateMetho

Posted by dd...@apache.org.
FREEMARKER-64: TemplateModel and TemplateModelEx changes (but they will be probably replaced by TemplateFunctionModel later): Removed FM2 `TemplateMethodModel`, then renamed `TemplateMethodModelEx` to `TemplateMethodModel`. Then changed it further to return `TemplateModel` (instead of `Object`, which allowed returning a non-wrapped value by design in FM2), changed the parameter `List` to `List<? extends TemplateModel>` (which it was earlier too, but there were no generic paramters back then), and allowed throwing `TemplateException` instead of the more restrictive `TemplateModelException`.


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

Branch: refs/heads/3
Commit: 1333a715f2a305391907eefd74fcb8ad7a48db5f
Parents: 3cacd9e
Author: ddekany <dd...@apache.org>
Authored: Fri Aug 4 23:04:19 2017 +0200
Committer: ddekany <dd...@apache.org>
Committed: Fri Aug 4 23:33:34 2017 +0200

----------------------------------------------------------------------
 FM3-CHANGE-LOG.txt                              |  10 +-
 ...a8DefaultObjectWrapperBridgeMethodsTest.java |  23 ++-
 .../impl/Java8DefaultObjectWrapperTest.java     |  33 ++--
 .../impl/AbstractParallelIntrospectionTest.java |   7 +-
 .../model/impl/DefaultObjectWrapperTest.java    |  55 ++++---
 .../core/model/impl/EnumModelsTest.java         |   6 +-
 .../core/model/impl/ErrorMessagesTest.java      |  22 +--
 .../impl/FineTuneMethodAppearanceTest.java      |   4 +-
 .../core/model/impl/StaticModelsTest.java       |   6 +-
 .../core/templatesuite/models/MultiModel2.java  |  17 +-
 .../templatesuite/models/SimpleTestMethod.java  |  13 +-
 .../core/templatesuite/models/TestMethod.java   |   8 +-
 .../core/userpkg/AllFeaturesDirective.java      |  19 ++-
 .../core/userpkg/AllFeaturesFunction.java       |  20 ++-
 .../core/userpkg/TestTemplateCallableModel.java |  14 +-
 .../templatesuite/expected/stringbimethods.txt  |   2 -
 .../templatesuite/templates/stringbimethods.ftl |   4 -
 .../freemarker/core/ASTExpListLiteral.java      |  27 +---
 .../freemarker/core/ASTExpMethodCall.java       |   8 +-
 .../freemarker/core/BuiltInsForDates.java       |  25 ++-
 .../core/BuiltInsForExistenceHandling.java      |  14 +-
 .../core/BuiltInsForMultipleTypes.java          |  32 ++--
 .../BuiltInsForNestedContentParameters.java     |   6 +-
 .../freemarker/core/BuiltInsForNodes.java       |   4 +-
 .../freemarker/core/BuiltInsForSequences.java   |  62 ++++----
 .../core/BuiltInsForStringsBasic.java           |  78 +++++-----
 .../core/BuiltInsForStringsEncoding.java        |   6 +-
 .../freemarker/core/BuiltInsForStringsMisc.java |  22 +--
 .../core/BuiltInsForStringsRegexp.java          |  18 ++-
 .../org/apache/freemarker/core/Environment.java |   4 +-
 .../freemarker/core/NonStringException.java     |  14 +-
 .../apache/freemarker/core/_CallableUtils.java  | 156 +++++++++++++++++++
 .../core/_ObjectBuilderSettingEvaluator.java    |   8 +-
 .../core/_TemplateCallableModelUtils.java       | 112 -------------
 .../freemarker/core/debug/DebugModel.java       |   1 -
 .../core/debug/RmiDebugModelImpl.java           |   6 +-
 .../core/model/GeneralPurposeNothing.java       |   4 +-
 .../core/model/TemplateMethodModel.java         |  41 ++---
 .../core/model/TemplateMethodModelEx.java       |  54 -------
 .../freemarker/core/model/impl/BeanModel.java   |   3 +-
 .../core/model/impl/DefaultObjectWrapper.java   |   8 +-
 .../core/model/impl/JavaMethodModel.java        |  11 +-
 .../core/model/impl/OverloadedMethodsModel.java |   8 +-
 .../core/model/impl/ResourceBundleModel.java    |  20 +--
 .../freemarker/core/model/impl/StaticModel.java |   3 +-
 .../model/impl/TemplateModelListSequence.java   |   4 +-
 .../jsp/CustomTagAndELFunctionCombiner.java     |  26 ++--
 .../freemarker/servlet/jsp/TaglibFactory.java   |   8 +-
 .../freemarker/servlet/jsp/TLDParsingTest.java  |  12 +-
 49 files changed, 521 insertions(+), 547 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/1333a715/FM3-CHANGE-LOG.txt
----------------------------------------------------------------------
diff --git a/FM3-CHANGE-LOG.txt b/FM3-CHANGE-LOG.txt
index aab66b7..d38866b 100644
--- a/FM3-CHANGE-LOG.txt
+++ b/FM3-CHANGE-LOG.txt
@@ -135,7 +135,10 @@ Node: Changes already mentioned above aren't repeated here!
   - When parameter default expressions are evaluated, only the parameters defined earlier are already set.
     So `<#macro m a b=a>` works, but `<#macro m a=b b>` won't work anymore (unless there's `b` outside the
     parameter list), as `b` is not yet set when the default of `a` is calculated.
-
+- Built-ins don't convert their parameters to tring anymore; in FM some (not all) of them did, because they
+  were based on the TemplateMethod interface (deprecated even in FM2) that required String paramteters and
+  was auto-converted by FM2.
+    
   
 Java API changes
 ================
@@ -372,6 +375,11 @@ Core / Models and Object wrapping
   get method (`get(String)`) had priority over methods of similar name. The generic get method is only recognized from its
   name and parameter type, so it's a quite consfusing feature, and might will be removed alltogether.
 - Renamed TemplateDateModel.DATETIME to DATE_TIME (to be consistent with "dateTime" used elsewhere).
+- TemplateModel and TemplateModelEx changes (but they will be probably replaced by TemplateFunctionModel later):
+  Removed FM2 `TemplateMethodModel`, then renamed `TemplateMethodModelEx` to `TemplateMethodModel`. Then changed it further to return
+  `TemplateModel` (instead of `Object`, which allowed returning a non-wrapped value by design in FM2), changed the parameter `List` to
+  `List<? extends TemplateModel>` (which it was earlier too, but there were no generic paramters back then), and allowed throwing
+  `TemplateException` instead of the more restrictive `TemplateModelException`.
 
 Core / Template loading and caching
 ...................................

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/1333a715/freemarker-core-test-java8/src/test/java/org/apache/freemarker/core/model/impl/Java8DefaultObjectWrapperBridgeMethodsTest.java
----------------------------------------------------------------------
diff --git a/freemarker-core-test-java8/src/test/java/org/apache/freemarker/core/model/impl/Java8DefaultObjectWrapperBridgeMethodsTest.java b/freemarker-core-test-java8/src/test/java/org/apache/freemarker/core/model/impl/Java8DefaultObjectWrapperBridgeMethodsTest.java
index 495f3f9..abb373e 100644
--- a/freemarker-core-test-java8/src/test/java/org/apache/freemarker/core/model/impl/Java8DefaultObjectWrapperBridgeMethodsTest.java
+++ b/freemarker-core-test-java8/src/test/java/org/apache/freemarker/core/model/impl/Java8DefaultObjectWrapperBridgeMethodsTest.java
@@ -22,31 +22,30 @@ import static org.junit.Assert.*;
 
 import java.util.Collections;
 
-import org.junit.Test;
-
 import org.apache.freemarker.core.Configuration;
+import org.apache.freemarker.core.TemplateException;
 import org.apache.freemarker.core.model.TemplateHashModel;
-import org.apache.freemarker.core.model.TemplateMethodModelEx;
-import org.apache.freemarker.core.model.TemplateModelException;
+import org.apache.freemarker.core.model.TemplateMethodModel;
+import org.junit.Test;
 
 public class Java8DefaultObjectWrapperBridgeMethodsTest {
     
     @Test
-    public void testWithoutDefaultMethod() throws TemplateModelException {
+    public void testWithoutDefaultMethod() throws TemplateException {
         test(BridgeMethodsBean.class);
     }
 
     @Test
-    public void testWithDefaultMethod() throws TemplateModelException {
+    public void testWithDefaultMethod() throws TemplateException {
         test(Java8BridgeMethodsWithDefaultMethodBean.class);
     }
 
     @Test
-    public void testWithDefaultMethod2() throws TemplateModelException {
+    public void testWithDefaultMethod2() throws TemplateException {
         test(Java8BridgeMethodsWithDefaultMethodBean2.class);
     }
 
-    private void test(Class<?> pClass) throws TemplateModelException {
+    private void test(Class<?> pClass) throws TemplateException {
         DefaultObjectWrapper ow = new DefaultObjectWrapper.Builder(Configuration.VERSION_3_0_0).build();
         TemplateHashModel wrapped;
         try {
@@ -55,11 +54,11 @@ public class Java8DefaultObjectWrapperBridgeMethodsTest {
             throw new IllegalStateException(e);
         }
         
-        TemplateMethodModelEx m1 = (TemplateMethodModelEx) wrapped.get("m1");
-        assertEquals(BridgeMethodsBean.M1_RETURN_VALUE, "" + m1.exec(Collections.emptyList()));
+        TemplateMethodModel m1 = (TemplateMethodModel) wrapped.get("m1");
+        assertEquals(BridgeMethodsBean.M1_RETURN_VALUE, "" + m1.execute(Collections.emptyList()));
         
-        TemplateMethodModelEx m2 = (TemplateMethodModelEx) wrapped.get("m2");
-        assertNull(m2.exec(Collections.emptyList()));
+        TemplateMethodModel m2 = (TemplateMethodModel) wrapped.get("m2");
+        assertNull(m2.execute(Collections.emptyList()));
     }
     
 }

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/1333a715/freemarker-core-test-java8/src/test/java/org/apache/freemarker/core/model/impl/Java8DefaultObjectWrapperTest.java
----------------------------------------------------------------------
diff --git a/freemarker-core-test-java8/src/test/java/org/apache/freemarker/core/model/impl/Java8DefaultObjectWrapperTest.java b/freemarker-core-test-java8/src/test/java/org/apache/freemarker/core/model/impl/Java8DefaultObjectWrapperTest.java
index 905d536..08dc5b1 100644
--- a/freemarker-core-test-java8/src/test/java/org/apache/freemarker/core/model/impl/Java8DefaultObjectWrapperTest.java
+++ b/freemarker-core-test-java8/src/test/java/org/apache/freemarker/core/model/impl/Java8DefaultObjectWrapperTest.java
@@ -22,20 +22,19 @@ import static org.junit.Assert.*;
 
 import java.util.Collections;
 
-import org.junit.Test;
-
 import org.apache.freemarker.core.Configuration;
+import org.apache.freemarker.core.TemplateException;
 import org.apache.freemarker.core.model.TemplateHashModel;
-import org.apache.freemarker.core.model.TemplateMethodModelEx;
-import org.apache.freemarker.core.model.TemplateModelException;
+import org.apache.freemarker.core.model.TemplateMethodModel;
 import org.apache.freemarker.core.model.TemplateNumberModel;
 import org.apache.freemarker.core.model.TemplateScalarModel;
 import org.apache.freemarker.core.model.TemplateSequenceModel;
+import org.junit.Test;
 
 public class Java8DefaultObjectWrapperTest {
 
     @Test
-    public void testDefaultMethodRecognized() throws TemplateModelException {
+    public void testDefaultMethodRecognized() throws TemplateException {
         DefaultObjectWrapper.Builder owb = new DefaultObjectWrapper.Builder(Configuration.VERSION_3_0_0);
         DefaultObjectWrapper ow = owb.build();
         TemplateHashModel wrappedBean = (TemplateHashModel) ow.wrap(new Java8DefaultMethodsBean());
@@ -62,11 +61,11 @@ public class Java8DefaultObjectWrapperTest {
             // Has only indexed read method, so it's not exposed as a property
             assertNull(wrappedBean.get(Java8DefaultMethodsBeanBase.DEFAULT_METHOD_INDEXED_PROP));
 
-            TemplateMethodModelEx indexedReadMethod = (TemplateMethodModelEx) wrappedBean.get(
+            TemplateMethodModel indexedReadMethod = (TemplateMethodModel) wrappedBean.get(
                     Java8DefaultMethodsBeanBase.DEFAULT_METHOD_INDEXED_PROP_GETTER);
             assertNotNull(indexedReadMethod);
             assertEquals(Java8DefaultMethodsBeanBase.DEFAULT_METHOD_INDEXED_PROP_VALUE,
-                    ((TemplateScalarModel) indexedReadMethod.exec(Collections.singletonList(new SimpleNumber(0))))
+                    ((TemplateScalarModel) indexedReadMethod.execute(Collections.singletonList(new SimpleNumber(0))))
                             .getAsString
                             ());
         }
@@ -115,45 +114,45 @@ public class Java8DefaultObjectWrapperTest {
             // Has only indexed read method, so it's not exposed as a property
             assertNull(wrappedBean.get(Java8DefaultMethodsBean.INDEXED_PROP_4));
 
-            TemplateMethodModelEx indexedReadMethod = (TemplateMethodModelEx) wrappedBean.get(
+            TemplateMethodModel indexedReadMethod = (TemplateMethodModel) wrappedBean.get(
                     Java8DefaultMethodsBean.INDEXED_PROP_GETTER_4);
             assertNotNull(indexedReadMethod);
             assertEquals(Java8DefaultMethodsBean.INDEXED_PROP_4_VALUE,
-                    ((TemplateScalarModel) indexedReadMethod.exec(Collections.singletonList(new SimpleNumber(0))))
+                    ((TemplateScalarModel) indexedReadMethod.execute(Collections.singletonList(new SimpleNumber(0))))
                             .getAsString());
         }        
         {
-            TemplateMethodModelEx action = (TemplateMethodModelEx) wrappedBean.get(
+            TemplateMethodModel action = (TemplateMethodModel) wrappedBean.get(
                     Java8DefaultMethodsBean.NORMAL_ACTION);
             assertNotNull(action);
             assertEquals(
                     Java8DefaultMethodsBean.NORMAL_ACTION_RETURN_VALUE,
-                    ((TemplateScalarModel) action.exec(Collections.emptyList())).getAsString());
+                    ((TemplateScalarModel) action.execute(Collections.emptyList())).getAsString());
         }
         
         {
-            TemplateMethodModelEx action = (TemplateMethodModelEx) wrappedBean.get(
+            TemplateMethodModel action = (TemplateMethodModel) wrappedBean.get(
                     Java8DefaultMethodsBean.NORMAL_ACTION);
             assertNotNull(action);
             assertEquals(
                     Java8DefaultMethodsBean.NORMAL_ACTION_RETURN_VALUE,
-                    ((TemplateScalarModel) action.exec(Collections.emptyList())).getAsString());
+                    ((TemplateScalarModel) action.execute(Collections.emptyList())).getAsString());
         }
         {
-            TemplateMethodModelEx action = (TemplateMethodModelEx) wrappedBean.get(
+            TemplateMethodModel action = (TemplateMethodModel) wrappedBean.get(
                     Java8DefaultMethodsBean.DEFAULT_METHOD_ACTION);
             assertNotNull(action);
             assertEquals(
                     Java8DefaultMethodsBean.DEFAULT_METHOD_ACTION_RETURN_VALUE,
-                    ((TemplateScalarModel) action.exec(Collections.emptyList())).getAsString());
+                    ((TemplateScalarModel) action.execute(Collections.emptyList())).getAsString());
         }
         {
-            TemplateMethodModelEx action = (TemplateMethodModelEx) wrappedBean.get(
+            TemplateMethodModel action = (TemplateMethodModel) wrappedBean.get(
                     Java8DefaultMethodsBean.OVERRIDDEN_DEFAULT_METHOD_ACTION);
             assertNotNull(action);
             assertEquals(
                     Java8DefaultMethodsBean.OVERRIDDEN_DEFAULT_METHOD_ACTION_RETURN_VALUE,
-                    ((TemplateScalarModel) action.exec(Collections.emptyList())).getAsString());
+                    ((TemplateScalarModel) action.execute(Collections.emptyList())).getAsString());
         }
     }
 

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/1333a715/freemarker-core-test/src/test/java/org/apache/freemarker/core/model/impl/AbstractParallelIntrospectionTest.java
----------------------------------------------------------------------
diff --git a/freemarker-core-test/src/test/java/org/apache/freemarker/core/model/impl/AbstractParallelIntrospectionTest.java b/freemarker-core-test/src/test/java/org/apache/freemarker/core/model/impl/AbstractParallelIntrospectionTest.java
index bdb9a56..aac56b3 100644
--- a/freemarker-core-test/src/test/java/org/apache/freemarker/core/model/impl/AbstractParallelIntrospectionTest.java
+++ b/freemarker-core-test/src/test/java/org/apache/freemarker/core/model/impl/AbstractParallelIntrospectionTest.java
@@ -20,6 +20,7 @@
 package org.apache.freemarker.core.model.impl;
 
 import org.apache.freemarker.core.Configuration;
+import org.apache.freemarker.core.TemplateException;
 import org.apache.freemarker.core.model.TemplateHashModel;
 import org.apache.freemarker.core.model.TemplateMethodModel;
 import org.apache.freemarker.core.model.TemplateModelException;
@@ -111,12 +112,12 @@ public abstract class AbstractParallelIntrospectionTest extends TestCase {
         }
 
         private void testMethod(TemplateHashModel h, int objIdx, int mIdx)
-                throws TemplateModelException, AssertionError {
+                throws TemplateException, AssertionError {
             TemplateMethodModel pv = (TemplateMethodModel) h.get("m" + mIdx);
             final int expected = objIdx * 1000 + mIdx;
-            final int got = ((TemplateNumberModel) pv.exec(null)).getAsNumber().intValue();
+            final int got = ((TemplateNumberModel) pv.execute(null)).getAsNumber().intValue();
             if (got != expected) {
-                throw new AssertionError("Method assertation failed; " +
+                throw new AssertionError("Method assertion failed; " +
                         "expected " + expected + ", but got " + got);
             }
         }

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/1333a715/freemarker-core-test/src/test/java/org/apache/freemarker/core/model/impl/DefaultObjectWrapperTest.java
----------------------------------------------------------------------
diff --git a/freemarker-core-test/src/test/java/org/apache/freemarker/core/model/impl/DefaultObjectWrapperTest.java b/freemarker-core-test/src/test/java/org/apache/freemarker/core/model/impl/DefaultObjectWrapperTest.java
index 41acce3..dd1c841 100644
--- a/freemarker-core-test/src/test/java/org/apache/freemarker/core/model/impl/DefaultObjectWrapperTest.java
+++ b/freemarker-core-test/src/test/java/org/apache/freemarker/core/model/impl/DefaultObjectWrapperTest.java
@@ -52,7 +52,7 @@ import org.apache.freemarker.core.model.TemplateCollectionModel;
 import org.apache.freemarker.core.model.TemplateCollectionModelEx;
 import org.apache.freemarker.core.model.TemplateHashModel;
 import org.apache.freemarker.core.model.TemplateHashModelEx;
-import org.apache.freemarker.core.model.TemplateMethodModelEx;
+import org.apache.freemarker.core.model.TemplateMethodModel;
 import org.apache.freemarker.core.model.TemplateModel;
 import org.apache.freemarker.core.model.TemplateModelException;
 import org.apache.freemarker.core.model.TemplateModelIterator;
@@ -167,7 +167,7 @@ public class DefaultObjectWrapperTest {
     
     
     @Test
-    public void testCustomization() throws TemplateModelException {
+    public void testCustomization() throws TemplateException {
         CustomizedDefaultObjectWrapper ow = new CustomizedDefaultObjectWrapper(Configuration.VERSION_3_0_0);
         assertEquals(Configuration.VERSION_3_0_0, ow.getIncompatibleImprovements());
 
@@ -191,16 +191,15 @@ public class DefaultObjectWrapperTest {
         assertEquals(1, ow.unwrap(bean.get("x")));
         {
             // Check method calls, and also if the return value is wrapped with the overidden "wrap".
-            final TemplateModel mr = (TemplateModel) ((TemplateMethodModelEx) bean.get("m")).exec(Collections.emptyList());
-            assertEquals(
-                    Collections.singletonList(1),
-                    ow.unwrap(mr));
+            final TemplateModel mr = ((TemplateMethodModel) bean.get("m")).execute(
+                    Collections.<TemplateModel>emptyList());
+            assertEquals(Collections.singletonList(1), ow.unwrap(mr));
             assertTrue(DefaultListAdapter.class.isInstance(mr));
         }
         {
             // Check custom TM usage and round trip:
-            final TemplateModel mr = (TemplateModel) ((TemplateMethodModelEx) bean.get("incTupple"))
-                    .exec(Collections.singletonList(ow.wrap(new Tupple<>(1, 2))));
+            final TemplateModel mr = ((TemplateMethodModel) bean.get("incTupple"))
+                    .execute(Collections.singletonList(ow.wrap(new Tupple<>(1, 2))));
             assertEquals(new Tupple<>(2, 3), ow.unwrap(mr));
             assertTrue(TuppleAdapter.class.isInstance(mr));
         }
@@ -208,7 +207,7 @@ public class DefaultObjectWrapperTest {
 
     @SuppressWarnings("boxing")
     @Test
-    public void testCompositeValueWrapping() throws TemplateModelException, ClassNotFoundException {
+    public void testCompositeValueWrapping() throws TemplateException, ClassNotFoundException {
         DefaultObjectWrapper ow = new DefaultObjectWrapper.Builder(Configuration.VERSION_3_0_0).build();
 
         final Map hashMap = new HashMap();
@@ -246,7 +245,7 @@ public class DefaultObjectWrapperTest {
 
     @SuppressWarnings("boxing")
     @Test
-    public void testMapAdapter() throws TemplateModelException {
+    public void testMapAdapter() throws TemplateException {
         HashMap<String, Object> testMap = new LinkedHashMap<>();
         testMap.put("a", 1);
         testMap.put("b", null);
@@ -303,7 +302,7 @@ public class DefaultObjectWrapperTest {
 
     @SuppressWarnings("boxing")
     @Test
-    public void testListAdapter() throws TemplateModelException {
+    public void testListAdapter() throws TemplateException {
         {
             List testList = new ArrayList<>();
             testList.add(1);
@@ -326,7 +325,7 @@ public class DefaultObjectWrapperTest {
         }
 
         {
-            List testList = new LinkedList<>();
+            List<Object> testList = new LinkedList<>();
             testList.add(1);
             testList.add(null);
             testList.add("c");
@@ -442,29 +441,29 @@ public class DefaultObjectWrapperTest {
     private void assertRoundtrip(DefaultObjectWrapper dow, Object obj, Class expectedTMClass,
             Class expectedPojoClass,
             String expectedPojoToString)
-            throws TemplateModelException {
+            throws TemplateException {
         final TemplateModel objTM = dow.wrap(obj);
         assertThat(objTM.getClass(), typeCompatibleWith(expectedTMClass));
 
         final TemplateHashModel testBeanTM = (TemplateHashModel) dow.wrap(new RoundtripTesterBean());
 
         {
-            TemplateMethodModelEx getClassM = (TemplateMethodModelEx) testBeanTM.get("getClass");
-            Object r = getClassM.exec(Collections.singletonList(objTM));
+            TemplateMethodModel getClassM = (TemplateMethodModel) testBeanTM.get("getClass");
+            TemplateModel r = getClassM.execute(Collections.singletonList(objTM));
             final Class rClass = (Class) ((WrapperTemplateModel) r).getWrappedObject();
             assertThat(rClass, typeCompatibleWith(expectedPojoClass));
         }
 
         if (expectedPojoToString != null) {
-            TemplateMethodModelEx getToStringM = (TemplateMethodModelEx) testBeanTM.get("toString");
-            Object r = getToStringM.exec(Collections.singletonList(objTM));
+            TemplateMethodModel getToStringM = (TemplateMethodModel) testBeanTM.get("toString");
+            TemplateModel r = getToStringM.execute(Collections.singletonList(objTM));
             assertEquals(expectedPojoToString, ((TemplateScalarModel) r).getAsString());
         }
     }
 
     @SuppressWarnings("boxing")
     @Test
-    public void testCollectionAdapterBasics() throws TemplateModelException {
+    public void testCollectionAdapterBasics() throws TemplateException {
         {
             Set set = new TreeSet();
             set.add("a");
@@ -538,7 +537,7 @@ public class DefaultObjectWrapperTest {
     }
 
     @Test
-    public void testIteratorWrapping() throws TemplateModelException, ClassNotFoundException {
+    public void testIteratorWrapping() throws TemplateException, ClassNotFoundException {
         final List<String> list = ImmutableList.of("a", "b", "c");
         Iterator<String> it = list.iterator();
         TemplateCollectionModel coll = (TemplateCollectionModel) OW.wrap(it);
@@ -578,14 +577,14 @@ public class DefaultObjectWrapperTest {
     }
 
     @Test
-    public void testIteratorApiSupport() throws TemplateModelException {
+    public void testIteratorApiSupport() throws TemplateException {
         TemplateModel wrappedIterator = OW.wrap(Collections.emptyIterator());
         assertThat(wrappedIterator, instanceOf(DefaultIteratorAdapter.class));
         DefaultIteratorAdapter iteratorAdapter = (DefaultIteratorAdapter) wrappedIterator;
 
         TemplateHashModel api = (TemplateHashModel) iteratorAdapter.getAPI();
-        assertFalse(((TemplateBooleanModel) ((TemplateMethodModelEx)
-                api.get("hasNext")).exec(Collections.emptyList())).getAsBoolean());
+        assertFalse(((TemplateBooleanModel) ((TemplateMethodModel)
+                api.get("hasNext")).execute(Collections.<TemplateModel>emptyList())).getAsBoolean());
     }
 
     @SuppressWarnings("boxing")
@@ -651,7 +650,7 @@ public class DefaultObjectWrapperTest {
     }
 
     @Test
-    public void testEnumerationAdapter() throws TemplateModelException {
+    public void testEnumerationAdapter() throws TemplateException {
         Vector<String> vector = new Vector<String>();
         vector.add("a");
         vector.add("b");
@@ -674,8 +673,8 @@ public class DefaultObjectWrapperTest {
         }
 
         TemplateHashModel api = (TemplateHashModel) enumAdapter.getAPI();
-        assertFalse(((TemplateBooleanModel) ((TemplateMethodModelEx)
-                api.get("hasMoreElements")).exec(Collections.emptyList())).getAsBoolean());
+        assertFalse(((TemplateBooleanModel) ((TemplateMethodModel) api.get("hasMoreElements"))
+                .execute(Collections.<TemplateModel>emptyList())).getAsBoolean());
     }
 
     @Test
@@ -725,13 +724,13 @@ public class DefaultObjectWrapperTest {
                 .wrap(bean);
     }
 
-    private void assertSizeThroughAPIModel(int expectedSize, TemplateModel normalModel) throws TemplateModelException {
+    private void assertSizeThroughAPIModel(int expectedSize, TemplateModel normalModel) throws TemplateException {
         if (!(normalModel instanceof TemplateModelWithAPISupport)) {
             fail(); 
         }
         TemplateHashModel apiModel = (TemplateHashModel) ((TemplateModelWithAPISupport) normalModel).getAPI();
-        TemplateMethodModelEx sizeMethod = (TemplateMethodModelEx) apiModel.get("size");
-        TemplateNumberModel r = (TemplateNumberModel) sizeMethod.exec(Collections.emptyList());
+        TemplateMethodModel sizeMethod = (TemplateMethodModel) apiModel.get("size");
+        TemplateNumberModel r = (TemplateNumberModel) sizeMethod.execute(Collections.<TemplateModel>emptyList());
         assertEquals(expectedSize, r.getAsNumber().intValue());
     }
 

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/1333a715/freemarker-core-test/src/test/java/org/apache/freemarker/core/model/impl/EnumModelsTest.java
----------------------------------------------------------------------
diff --git a/freemarker-core-test/src/test/java/org/apache/freemarker/core/model/impl/EnumModelsTest.java b/freemarker-core-test/src/test/java/org/apache/freemarker/core/model/impl/EnumModelsTest.java
index fc19bb7..9373cf5 100644
--- a/freemarker-core-test/src/test/java/org/apache/freemarker/core/model/impl/EnumModelsTest.java
+++ b/freemarker-core-test/src/test/java/org/apache/freemarker/core/model/impl/EnumModelsTest.java
@@ -25,7 +25,7 @@ import java.util.ArrayList;
 
 import org.apache.freemarker.core.Configuration;
 import org.apache.freemarker.core.model.TemplateHashModel;
-import org.apache.freemarker.core.model.TemplateMethodModelEx;
+import org.apache.freemarker.core.model.TemplateMethodModel;
 import org.apache.freemarker.core.model.TemplateModel;
 import org.apache.freemarker.core.model.TemplateModelException;
 import org.apache.freemarker.core.model.TemplateScalarModel;
@@ -58,8 +58,8 @@ public class EnumModelsTest {
         assertTrue(a instanceof TemplateScalarModel);
         assertTrue(a instanceof TemplateHashModel);
         assertEquals(((TemplateScalarModel) a).getAsString(), "ts:A");
-        TemplateMethodModelEx nameMethod = (TemplateMethodModelEx) ((TemplateHashModel) a).get("name");
-        assertEquals(((TemplateScalarModel) nameMethod.exec(new ArrayList())).getAsString(), "A");
+        TemplateMethodModel nameMethod = (TemplateMethodModel) ((TemplateHashModel) a).get("name");
+        assertEquals(((TemplateScalarModel) nameMethod.execute(new ArrayList())).getAsString(), "A");
         
         assertSame(e, enums.get(E.class.getName()));
         

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/1333a715/freemarker-core-test/src/test/java/org/apache/freemarker/core/model/impl/ErrorMessagesTest.java
----------------------------------------------------------------------
diff --git a/freemarker-core-test/src/test/java/org/apache/freemarker/core/model/impl/ErrorMessagesTest.java b/freemarker-core-test/src/test/java/org/apache/freemarker/core/model/impl/ErrorMessagesTest.java
index 29e8b9b..dcb8cf6 100644
--- a/freemarker-core-test/src/test/java/org/apache/freemarker/core/model/impl/ErrorMessagesTest.java
+++ b/freemarker-core-test/src/test/java/org/apache/freemarker/core/model/impl/ErrorMessagesTest.java
@@ -27,7 +27,7 @@ import java.util.Date;
 
 import org.apache.freemarker.core.Configuration;
 import org.apache.freemarker.core.model.TemplateHashModel;
-import org.apache.freemarker.core.model.TemplateMethodModelEx;
+import org.apache.freemarker.core.model.TemplateMethodModel;
 import org.apache.freemarker.core.model.TemplateModelException;
 import org.apache.freemarker.core.model.TemplateScalarModel;
 import org.apache.freemarker.core.outputformat.impl.HTMLOutputFormat;
@@ -60,9 +60,9 @@ public class ErrorMessagesTest {
         TemplateHashModel thm = (TemplateHashModel) ow.wrap(new TestBean());
         
         {
-            TemplateMethodModelEx m = (TemplateMethodModelEx) thm.get("m1");
+            TemplateMethodModel m = (TemplateMethodModel) thm.get("m1");
             try {
-                m.exec(Collections.singletonList(html));
+                m.execute(Collections.singletonList(html));
                 fail();
             } catch (TemplateModelException e) {
                 assertThat(e.getMessage(), allOf(
@@ -72,9 +72,9 @@ public class ErrorMessagesTest {
         }
         
         {
-            TemplateMethodModelEx m = (TemplateMethodModelEx) thm.get("m2");
+            TemplateMethodModel m = (TemplateMethodModel) thm.get("m2");
             try {
-                m.exec(Collections.singletonList(html));
+                m.execute(Collections.singletonList(html));
                 fail();
             } catch (TemplateModelException e) {
                 assertThat(e.getMessage(), allOf(
@@ -84,9 +84,9 @@ public class ErrorMessagesTest {
         }
         
         for (String methodName : new String[] { "mOverloaded", "mOverloaded3" }) {
-            TemplateMethodModelEx m = (TemplateMethodModelEx) thm.get(methodName);
+            TemplateMethodModel m = (TemplateMethodModel) thm.get(methodName);
             try {
-                m.exec(Collections.singletonList(html));
+                m.execute(Collections.singletonList(html));
                 fail();
             } catch (TemplateModelException e) {
                 assertThat(e.getMessage(), allOf(
@@ -97,9 +97,9 @@ public class ErrorMessagesTest {
         }
         
         {
-            TemplateMethodModelEx m = (TemplateMethodModelEx) thm.get("mOverloaded2");
+            TemplateMethodModel m = (TemplateMethodModel) thm.get("mOverloaded2");
             try {
-                m.exec(Collections.singletonList(html));
+                m.execute(Collections.singletonList(html));
                 fail();
             } catch (TemplateModelException e) {
                 assertThat(e.getMessage(), allOf(
@@ -110,8 +110,8 @@ public class ErrorMessagesTest {
         }
         
         {
-            TemplateMethodModelEx m = (TemplateMethodModelEx) thm.get("mOverloaded4");
-            Object r = m.exec(Collections.singletonList(html));
+            TemplateMethodModel m = (TemplateMethodModel) thm.get("mOverloaded4");
+            Object r = m.execute(Collections.singletonList(html));
             if (r instanceof TemplateScalarModel) {
                 r = ((TemplateScalarModel) r).getAsString();
             }

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/1333a715/freemarker-core-test/src/test/java/org/apache/freemarker/core/model/impl/FineTuneMethodAppearanceTest.java
----------------------------------------------------------------------
diff --git a/freemarker-core-test/src/test/java/org/apache/freemarker/core/model/impl/FineTuneMethodAppearanceTest.java b/freemarker-core-test/src/test/java/org/apache/freemarker/core/model/impl/FineTuneMethodAppearanceTest.java
index 92f59fb..eb4d807 100644
--- a/freemarker-core-test/src/test/java/org/apache/freemarker/core/model/impl/FineTuneMethodAppearanceTest.java
+++ b/freemarker-core-test/src/test/java/org/apache/freemarker/core/model/impl/FineTuneMethodAppearanceTest.java
@@ -23,7 +23,7 @@ import static org.junit.Assert.*;
 
 import org.apache.freemarker.core.Configuration;
 import org.apache.freemarker.core.model.TemplateHashModel;
-import org.apache.freemarker.core.model.TemplateMethodModelEx;
+import org.apache.freemarker.core.model.TemplateMethodModel;
 import org.apache.freemarker.core.model.TemplateModel;
 import org.apache.freemarker.core.model.TemplateModelException;
 import org.apache.freemarker.core.model.TemplateScalarModel;
@@ -48,7 +48,7 @@ public class FineTuneMethodAppearanceTest {
         assertEquals("v1", ((TemplateScalarModel) thm.get("v1")).getAsString());
         assertEquals("v2()", ((TemplateScalarModel) thm.get("v2")).getAsString());
         assertEquals("getV3()", ((TemplateScalarModel) thm.get("v3")).getAsString());
-        assertTrue(thm.get("getV3") instanceof TemplateMethodModelEx);
+        assertTrue(thm.get("getV3") instanceof TemplateMethodModel);
     }
     
     static public class C {

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/1333a715/freemarker-core-test/src/test/java/org/apache/freemarker/core/model/impl/StaticModelsTest.java
----------------------------------------------------------------------
diff --git a/freemarker-core-test/src/test/java/org/apache/freemarker/core/model/impl/StaticModelsTest.java b/freemarker-core-test/src/test/java/org/apache/freemarker/core/model/impl/StaticModelsTest.java
index 609d632..44da42a 100644
--- a/freemarker-core-test/src/test/java/org/apache/freemarker/core/model/impl/StaticModelsTest.java
+++ b/freemarker-core-test/src/test/java/org/apache/freemarker/core/model/impl/StaticModelsTest.java
@@ -26,7 +26,7 @@ import java.util.ArrayList;
 
 import org.apache.freemarker.core.Configuration;
 import org.apache.freemarker.core.model.TemplateHashModel;
-import org.apache.freemarker.core.model.TemplateMethodModelEx;
+import org.apache.freemarker.core.model.TemplateMethodModel;
 import org.apache.freemarker.core.model.TemplateModel;
 import org.apache.freemarker.core.model.TemplateModelException;
 import org.apache.freemarker.core.model.TemplateScalarModel;
@@ -65,8 +65,8 @@ public class StaticModelsTest {
         assertEquals(((TemplateScalarModel) f).getAsString(), "F OK");
         
         TemplateModel m = s.get("m");
-        assertTrue(m instanceof TemplateMethodModelEx);
-        assertEquals(((TemplateScalarModel) ((TemplateMethodModelEx) m).exec(new ArrayList())).getAsString(), "m OK");
+        assertTrue(m instanceof TemplateMethodModel);
+        assertEquals(((TemplateScalarModel) ((TemplateMethodModel) m).execute(new ArrayList())).getAsString(), "m OK");
         
         assertSame(s, statics.get(S.class.getName()));
         

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/1333a715/freemarker-core-test/src/test/java/org/apache/freemarker/core/templatesuite/models/MultiModel2.java
----------------------------------------------------------------------
diff --git a/freemarker-core-test/src/test/java/org/apache/freemarker/core/templatesuite/models/MultiModel2.java b/freemarker-core-test/src/test/java/org/apache/freemarker/core/templatesuite/models/MultiModel2.java
index e264936..3223652 100644
--- a/freemarker-core-test/src/test/java/org/apache/freemarker/core/templatesuite/models/MultiModel2.java
+++ b/freemarker-core-test/src/test/java/org/apache/freemarker/core/templatesuite/models/MultiModel2.java
@@ -19,10 +19,12 @@
 
 package org.apache.freemarker.core.templatesuite.models;
 
-import java.util.Iterator;
 import java.util.List;
 
+import org.apache.freemarker.core.TemplateException;
+import org.apache.freemarker.core._CallableUtils;
 import org.apache.freemarker.core.model.TemplateMethodModel;
+import org.apache.freemarker.core.model.TemplateModel;
 import org.apache.freemarker.core.model.TemplateScalarModel;
 import org.apache.freemarker.core.model.impl.SimpleScalar;
 
@@ -44,18 +46,17 @@ public class MultiModel2 implements TemplateScalarModel, TemplateMethodModel {
     /**
      * Executes a method call.
      *
-     * @param arguments a <tt>List</tt> of <tt>String</tt> objects containing the values
+     * @param args a <tt>List</tt> of <tt>String</tt> objects containing the values
      * of the arguments passed to the method.
      * @return the <tt>TemplateModel</tt> produced by the method, or null.
      */
     @Override
-    public Object exec(List arguments) {
+    public TemplateModel execute(List<? extends TemplateModel> args) throws TemplateException {
         StringBuilder  aResults = new StringBuilder( "Arguments are:<br />" );
-        Iterator    iList = arguments.iterator();
-
-        while ( iList.hasNext() ) {
-            aResults.append( (String) iList.next() );
-            aResults.append( "<br />" );
+        for (int i = 0; i < args.size(); i++) {
+            TemplateModel arg = args.get(i);
+            aResults.append(_CallableUtils.castArgToString(arg, i));
+            aResults.append("<br />");
         }
 
         return new SimpleScalar( aResults.toString() );

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/1333a715/freemarker-core-test/src/test/java/org/apache/freemarker/core/templatesuite/models/SimpleTestMethod.java
----------------------------------------------------------------------
diff --git a/freemarker-core-test/src/test/java/org/apache/freemarker/core/templatesuite/models/SimpleTestMethod.java b/freemarker-core-test/src/test/java/org/apache/freemarker/core/templatesuite/models/SimpleTestMethod.java
index 21fc5c0..48352a0 100644
--- a/freemarker-core-test/src/test/java/org/apache/freemarker/core/templatesuite/models/SimpleTestMethod.java
+++ b/freemarker-core-test/src/test/java/org/apache/freemarker/core/templatesuite/models/SimpleTestMethod.java
@@ -22,6 +22,7 @@ package org.apache.freemarker.core.templatesuite.models;
 import java.util.List;
 
 import org.apache.freemarker.core.model.TemplateMethodModel;
+import org.apache.freemarker.core.model.TemplateModel;
 import org.apache.freemarker.core.model.impl.SimpleScalar;
 
 /**
@@ -32,18 +33,18 @@ public class SimpleTestMethod implements TemplateMethodModel {
     /**
      * Executes a method call.
      *
-     * @param arguments a <tt>List</tt> of <tt>String</tt> objects containing
+     * @param args a <tt>List</tt> of <tt>String</tt> objects containing
      * the values of the arguments passed to the method.
      * @return the <tt>TemplateModel</tt> produced by the method, or null.
      */
     @Override
-    public Object exec(List arguments) {
-        if ( arguments.size() == 0 ) {
+    public TemplateModel execute(List<? extends TemplateModel> args) {
+        if ( args.size() == 0 ) {
             return new SimpleScalar( "Empty list provided" );
-        } else if ( arguments.size() > 1 ) {
-            return new SimpleScalar( "Argument size is: " + arguments.size() );
+        } else if ( args.size() > 1 ) {
+            return new SimpleScalar( "Argument size is: " + args.size() );
         } else {
-            return new SimpleScalar( "Single argument value is: " + arguments.get(0) );
+            return new SimpleScalar( "Single argument value is: " + args.get(0) );
         }
     }
 }

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/1333a715/freemarker-core-test/src/test/java/org/apache/freemarker/core/templatesuite/models/TestMethod.java
----------------------------------------------------------------------
diff --git a/freemarker-core-test/src/test/java/org/apache/freemarker/core/templatesuite/models/TestMethod.java b/freemarker-core-test/src/test/java/org/apache/freemarker/core/templatesuite/models/TestMethod.java
index d9087a3..ed97f9e 100644
--- a/freemarker-core-test/src/test/java/org/apache/freemarker/core/templatesuite/models/TestMethod.java
+++ b/freemarker-core-test/src/test/java/org/apache/freemarker/core/templatesuite/models/TestMethod.java
@@ -3,10 +3,14 @@ package org.apache.freemarker.core.templatesuite.models;
 import java.util.List;
 
 import org.apache.freemarker.core.model.TemplateMethodModel;
+import org.apache.freemarker.core.model.TemplateModel;
+import org.apache.freemarker.core.model.impl.SimpleScalar;
 
 public class TestMethod implements TemplateMethodModel {
+
     @Override
-    public Object exec(List arguments) {
-        return "x";
+    public TemplateModel execute(List<? extends TemplateModel> args) {
+        return new SimpleScalar("x");
     }
+
 }

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/1333a715/freemarker-core-test/src/test/java/org/apache/freemarker/core/userpkg/AllFeaturesDirective.java
----------------------------------------------------------------------
diff --git a/freemarker-core-test/src/test/java/org/apache/freemarker/core/userpkg/AllFeaturesDirective.java b/freemarker-core-test/src/test/java/org/apache/freemarker/core/userpkg/AllFeaturesDirective.java
index 3509b78..d9bb394 100644
--- a/freemarker-core-test/src/test/java/org/apache/freemarker/core/userpkg/AllFeaturesDirective.java
+++ b/freemarker-core-test/src/test/java/org/apache/freemarker/core/userpkg/AllFeaturesDirective.java
@@ -19,19 +19,18 @@
 
 package org.apache.freemarker.core.userpkg;
 
-import static org.apache.freemarker.core._TemplateCallableModelUtils.*;
+import static org.apache.freemarker.core._CallableUtils.*;
 
 import java.io.IOException;
 import java.io.Writer;
 
+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.CallPlace;
 import org.apache.freemarker.core.model.TemplateDirectiveModel;
 import org.apache.freemarker.core.model.TemplateHashModelEx2;
 import org.apache.freemarker.core.model.TemplateModel;
-import org.apache.freemarker.core.model.TemplateNumberModel;
 import org.apache.freemarker.core.model.TemplateSequenceModel;
 import org.apache.freemarker.core.model.impl.SimpleNumber;
 import org.apache.freemarker.core.util.StringToIndexMap;
@@ -79,17 +78,17 @@ public class AllFeaturesDirective extends TestTemplateCallableModel implements T
     @Override
     public void execute(TemplateModel[] args, CallPlace callPlace, Writer out, Environment env)
             throws TemplateException, IOException {
-        execute(castArgumentToNumber(args, P1_ARG_IDX, p1AllowNull, env),
-                castArgumentToNumber(args, P2_ARG_IDX, p2AllowNull, env),
+        execute(castArgToNumber(args, P1_ARG_IDX, p1AllowNull),
+                castArgToNumber(args, P2_ARG_IDX, p2AllowNull),
                 (TemplateSequenceModel) args[P_VARARGS_ARG_IDX],
-                castArgumentToNumber(args[N1_ARG_IDX], N1_ARG_NAME, n1AllowNull, env),
-                castArgumentToNumber(args[N2_ARG_IDX], N2_ARG_NAME, n2AllowNull, env),
+                castArgToNumber(args[N1_ARG_IDX], N1_ARG_NAME, n1AllowNull),
+                castArgToNumber(args[N2_ARG_IDX], N2_ARG_NAME, n2AllowNull),
                 (TemplateHashModelEx2) args[N_VARARGS_ARG_IDX],
                 out, env, callPlace);
     }
 
-    private void execute(TemplateNumberModel p1, TemplateNumberModel p2, TemplateSequenceModel pOthers,
-            TemplateNumberModel n1, TemplateNumberModel n2, TemplateHashModelEx2 nOthers,
+    private void execute(Number p1, Number p2, TemplateSequenceModel pOthers,
+            Number n1, Number n2, TemplateHashModelEx2 nOthers,
             Writer out, Environment env, CallPlace callPlace) throws IOException, TemplateException {
         out.write("#a(");
         printParam("p1", p1, out, true);
@@ -107,7 +106,7 @@ public class AllFeaturesDirective extends TestTemplateCallableModel implements T
         if (callPlace.hasNestedContent()) {
             out.write(" {");
             if (p1 != null) {
-                int intP1 = p1.getAsNumber().intValue();
+                int intP1 = p1.intValue();
                 for (int i = 0; i < intP1; i++) {
                     // We dynamically set as many nested content parameters as many the caller has declared; this is
                     // unusual, and is for testing purposes only.

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/1333a715/freemarker-core-test/src/test/java/org/apache/freemarker/core/userpkg/AllFeaturesFunction.java
----------------------------------------------------------------------
diff --git a/freemarker-core-test/src/test/java/org/apache/freemarker/core/userpkg/AllFeaturesFunction.java b/freemarker-core-test/src/test/java/org/apache/freemarker/core/userpkg/AllFeaturesFunction.java
index 7f1f6c1..5825745 100644
--- a/freemarker-core-test/src/test/java/org/apache/freemarker/core/userpkg/AllFeaturesFunction.java
+++ b/freemarker-core-test/src/test/java/org/apache/freemarker/core/userpkg/AllFeaturesFunction.java
@@ -19,7 +19,7 @@
 
 package org.apache.freemarker.core.userpkg;
 
-import static org.apache.freemarker.core._TemplateCallableModelUtils.*;
+import static org.apache.freemarker.core._CallableUtils.*;
 
 import java.io.IOException;
 import java.io.StringWriter;
@@ -31,7 +31,6 @@ import org.apache.freemarker.core.model.ArgumentArrayLayout;
 import org.apache.freemarker.core.model.TemplateFunctionModel;
 import org.apache.freemarker.core.model.TemplateHashModelEx2;
 import org.apache.freemarker.core.model.TemplateModel;
-import org.apache.freemarker.core.model.TemplateNumberModel;
 import org.apache.freemarker.core.model.TemplateSequenceModel;
 import org.apache.freemarker.core.model.impl.SimpleScalar;
 import org.apache.freemarker.core.util.StringToIndexMap;
@@ -78,18 +77,17 @@ public class AllFeaturesFunction extends TestTemplateCallableModel implements Te
 
     @Override
     public TemplateModel execute(TemplateModel[] args, CallPlace callPlace, Environment env) throws TemplateException {
-        return execute(castArgumentToNumber(args, P1_ARG_IDX, p1AllowNull, env),
-                castArgumentToNumber(args, P2_ARG_IDX, p2AllowNull, env),
+        return execute(
+                castArgToNumber(args, P1_ARG_IDX, p1AllowNull),
+                castArgToNumber(args, P2_ARG_IDX, p2AllowNull),
                 (TemplateSequenceModel) args[P_VARARGS_ARG_IDX],
-                castArgumentToNumber(args[N1_ARG_IDX], N1_ARG_NAME, n1AllowNull, env),
-                castArgumentToNumber(args[N2_ARG_IDX], N2_ARG_NAME, n2AllowNull, env),
-                (TemplateHashModelEx2) args[N_VARARGS_ARG_IDX],
-                env);
+                castArgToNumber(args[N1_ARG_IDX], N1_ARG_NAME, n1AllowNull),
+                castArgToNumber(args[N2_ARG_IDX], N2_ARG_NAME, n2AllowNull),
+                (TemplateHashModelEx2) args[N_VARARGS_ARG_IDX]);
     }
 
-    private TemplateModel execute(TemplateNumberModel p1, TemplateNumberModel p2, TemplateSequenceModel pOthers,
-            TemplateNumberModel n1, TemplateNumberModel n2, TemplateHashModelEx2 nOthers,
-            Environment env) throws TemplateException {
+    private TemplateModel execute(Number p1, Number p2, TemplateSequenceModel pOthers,
+            Number n1, Number n2, TemplateHashModelEx2 nOthers) throws TemplateException {
         StringWriter out = new StringWriter();
         try {
             out.write("fa(");

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/1333a715/freemarker-core-test/src/test/java/org/apache/freemarker/core/userpkg/TestTemplateCallableModel.java
----------------------------------------------------------------------
diff --git a/freemarker-core-test/src/test/java/org/apache/freemarker/core/userpkg/TestTemplateCallableModel.java b/freemarker-core-test/src/test/java/org/apache/freemarker/core/userpkg/TestTemplateCallableModel.java
index 9dd34ba..f0a15d7 100644
--- a/freemarker-core-test/src/test/java/org/apache/freemarker/core/userpkg/TestTemplateCallableModel.java
+++ b/freemarker-core-test/src/test/java/org/apache/freemarker/core/userpkg/TestTemplateCallableModel.java
@@ -24,20 +24,20 @@ import java.io.Writer;
 
 import org.apache.freemarker.core.model.TemplateCallableModel;
 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.TemplateNumberModel;
 import org.apache.freemarker.core.model.TemplateScalarModel;
 import org.apache.freemarker.core.model.TemplateSequenceModel;
 import org.apache.freemarker.core.util.FTLUtil;
+import org.apache.freemarker.core.util._StringUtil;
 
 public abstract class TestTemplateCallableModel implements TemplateCallableModel {
 
-    protected void printParam(String name, TemplateModel value, Writer out) throws IOException, TemplateModelException {
+    protected void printParam(String name, Object value, Writer out) throws IOException, TemplateModelException {
         printParam(name, value, out, false);
     }
 
-    protected void printParam(String name, TemplateModel value, Writer out, boolean first)
+    protected void printParam(String name, Object value, Writer out, boolean first)
             throws IOException, TemplateModelException {
         if (!first) {
             out.write(", ");
@@ -47,7 +47,7 @@ public abstract class TestTemplateCallableModel implements TemplateCallableModel
         printValue(value, out);
     }
 
-    private void printValue(TemplateModel value, Writer out) throws IOException, TemplateModelException {
+    private void printValue(Object value, Writer out) throws IOException, TemplateModelException {
         if (value == null) {
             out.write("null");
         } else if (value instanceof TemplateNumberModel) {
@@ -79,6 +79,12 @@ public abstract class TestTemplateCallableModel implements TemplateCallableModel
                 }
             }
             out.write('}');
+        } else if (value instanceof String) {
+            out.write(_StringUtil.jQuote(value));
+        } else if (value instanceof Number) {
+            out.write(value.toString());
+        } else if (value instanceof Boolean) {
+            out.write(value.toString());
         } else {
             throw new IllegalArgumentException("Unsupported value class: " + value.getClass().getName());
         }

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/1333a715/freemarker-core-test/src/test/resources/org/apache/freemarker/core/templatesuite/expected/stringbimethods.txt
----------------------------------------------------------------------
diff --git a/freemarker-core-test/src/test/resources/org/apache/freemarker/core/templatesuite/expected/stringbimethods.txt b/freemarker-core-test/src/test/resources/org/apache/freemarker/core/templatesuite/expected/stringbimethods.txt
index 8ef415d..1d4a314 100644
--- a/freemarker-core-test/src/test/resources/org/apache/freemarker/core/templatesuite/expected/stringbimethods.txt
+++ b/freemarker-core-test/src/test/resources/org/apache/freemarker/core/templatesuite/expected/stringbimethods.txt
@@ -25,5 +25,3 @@ de
 true false
 yes no
 igen nem
-0.0 1.0
-true true
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/1333a715/freemarker-core-test/src/test/resources/org/apache/freemarker/core/templatesuite/templates/stringbimethods.ftl
----------------------------------------------------------------------
diff --git a/freemarker-core-test/src/test/resources/org/apache/freemarker/core/templatesuite/templates/stringbimethods.ftl b/freemarker-core-test/src/test/resources/org/apache/freemarker/core/templatesuite/templates/stringbimethods.ftl
index 94954f9..c3bd460 100644
--- a/freemarker-core-test/src/test/resources/org/apache/freemarker/core/templatesuite/templates/stringbimethods.ftl
+++ b/freemarker-core-test/src/test/resources/org/apache/freemarker/core/templatesuite/templates/stringbimethods.ftl
@@ -30,7 +30,3 @@ ${a?string} ${b?string}
 ${a?string("yes", "no")} ${b?string("yes", "no")}
 <#setting booleanFormat="igen,nem"/>
 ${a?string} ${b?string}
-<#setting numberFormat="0.0">
-${a?string(0, 1)} ${b?string(0, 1)}
-<#setting booleanFormat="true,false"/>
-${a?string(0, 1)?isString?string} ${b?string(0, 1)?isString?string}

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/1333a715/freemarker-core/src/main/java/org/apache/freemarker/core/ASTExpListLiteral.java
----------------------------------------------------------------------
diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/ASTExpListLiteral.java b/freemarker-core/src/main/java/org/apache/freemarker/core/ASTExpListLiteral.java
index e5dc679..0fc27da 100644
--- a/freemarker-core/src/main/java/org/apache/freemarker/core/ASTExpListLiteral.java
+++ b/freemarker-core/src/main/java/org/apache/freemarker/core/ASTExpListLiteral.java
@@ -26,7 +26,6 @@ import java.util.List;
 import java.util.ListIterator;
 
 import org.apache.freemarker.core.model.TemplateMethodModel;
-import org.apache.freemarker.core.model.TemplateMethodModelEx;
 import org.apache.freemarker.core.model.TemplateModel;
 import org.apache.freemarker.core.model.TemplateSequenceModel;
 
@@ -55,31 +54,7 @@ final class ASTExpListLiteral extends ASTExpression {
     }
 
     /**
-     * For {@link TemplateMethodModel} calls, but not for {@link TemplateMethodModelEx}-es, returns the list of
-     * arguments as {@link String}-s.
-     */
-    List/*<String>*/ getValueList(Environment env) throws TemplateException {
-        int size = items.size();
-        switch(size) {
-            case 0: {
-                return Collections.EMPTY_LIST;
-            }
-            case 1: {
-                return Collections.singletonList(((ASTExpression) items.get(0)).evalAndCoerceToPlainText(env));
-            }
-            default: {
-                List result = new ArrayList(items.size());
-                for (ListIterator iterator = items.listIterator(); iterator.hasNext(); ) {
-                    ASTExpression exp = (ASTExpression) iterator.next();
-                    result.add(exp.evalAndCoerceToPlainText(env));
-                }
-                return result;
-            }
-        }
-    }
-
-    /**
-     * For {@link TemplateMethodModelEx} calls, returns the list of arguments as {@link TemplateModel}-s.
+     * For {@link TemplateMethodModel} calls, returns the list of arguments as {@link TemplateModel}-s.
      */
     List<TemplateModel> getModelList(Environment env) throws TemplateException {
         int size = items.size();

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/1333a715/freemarker-core/src/main/java/org/apache/freemarker/core/ASTExpMethodCall.java
----------------------------------------------------------------------
diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/ASTExpMethodCall.java b/freemarker-core/src/main/java/org/apache/freemarker/core/ASTExpMethodCall.java
index dc8f3ff..581726e 100644
--- a/freemarker-core/src/main/java/org/apache/freemarker/core/ASTExpMethodCall.java
+++ b/freemarker-core/src/main/java/org/apache/freemarker/core/ASTExpMethodCall.java
@@ -32,7 +32,6 @@ import org.apache.freemarker.core.model.ArgumentArrayLayout;
 import org.apache.freemarker.core.model.Constants;
 import org.apache.freemarker.core.model.TemplateFunctionModel;
 import org.apache.freemarker.core.model.TemplateMethodModel;
-import org.apache.freemarker.core.model.TemplateMethodModelEx;
 import org.apache.freemarker.core.model.TemplateModel;
 import org.apache.freemarker.core.model.TemplateSequenceModel;
 import org.apache.freemarker.core.util.CommonSupplier;
@@ -61,11 +60,8 @@ final class ASTExpMethodCall extends ASTExpression implements CallPlace {
         TemplateModel targetModel = target.eval(env);
         if (targetModel instanceof TemplateMethodModel) {
             TemplateMethodModel targetMethod = (TemplateMethodModel) targetModel;
-            List argumentStrings = 
-            targetMethod instanceof TemplateMethodModelEx
-            ? arguments.getModelList(env)
-            : arguments.getValueList(env);
-            Object result = targetMethod.exec(argumentStrings);
+            List<TemplateModel> argumentStrings = arguments.getModelList(env);
+            Object result = targetMethod.execute(argumentStrings);
             return env.getObjectWrapper().wrap(result);
         } else if (targetModel instanceof TemplateFunctionModel) {
             TemplateFunctionModel func = (TemplateFunctionModel) targetModel;

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/1333a715/freemarker-core/src/main/java/org/apache/freemarker/core/BuiltInsForDates.java
----------------------------------------------------------------------
diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/BuiltInsForDates.java b/freemarker-core/src/main/java/org/apache/freemarker/core/BuiltInsForDates.java
index ad11b37..434578d 100644
--- a/freemarker-core/src/main/java/org/apache/freemarker/core/BuiltInsForDates.java
+++ b/freemarker-core/src/main/java/org/apache/freemarker/core/BuiltInsForDates.java
@@ -25,7 +25,7 @@ import java.util.TimeZone;
 
 import org.apache.freemarker.core.model.AdapterTemplateModel;
 import org.apache.freemarker.core.model.TemplateDateModel;
-import org.apache.freemarker.core.model.TemplateMethodModelEx;
+import org.apache.freemarker.core.model.TemplateMethodModel;
 import org.apache.freemarker.core.model.TemplateModel;
 import org.apache.freemarker.core.model.TemplateModelException;
 import org.apache.freemarker.core.model.TemplateScalarModel;
@@ -63,11 +63,6 @@ class BuiltInsForDates {
             }
         }
 
-        protected TemplateModel calculateResult(Date date, int dateType, Environment env) throws TemplateException {
-            // TODO Auto-generated method stub
-            return null;
-        }
-        
     }
     
     /**
@@ -75,7 +70,7 @@ class BuiltInsForDates {
      */
     static class iso_BI extends AbstractISOBI {
         
-        class Result implements TemplateMethodModelEx {
+        class Result implements TemplateMethodModel {
             private final Date date;
             private final int dateType;
             private final Environment env;
@@ -87,10 +82,10 @@ class BuiltInsForDates {
             }
 
             @Override
-            public Object exec(List args) throws TemplateModelException {
+            public TemplateModel execute(List<? extends TemplateModel> args) throws TemplateModelException {
                 checkMethodArgCount(args, 1);
                 
-                TemplateModel tzArgTM = (TemplateModel) args.get(0);
+                TemplateModel tzArgTM = args.get(0);
                 TimeZone tzArg; 
                 Object adaptedObj;
                 if (tzArgTM instanceof AdapterTemplateModel
@@ -178,15 +173,15 @@ class BuiltInsForDates {
     private BuiltInsForDates() { }
 
     static abstract class AbstractISOBI extends BuiltInForDate {
-        protected final Boolean showOffset;
-        protected final int accuracy;
+        final Boolean showOffset;
+        final int accuracy;
     
-        protected AbstractISOBI(Boolean showOffset, int accuracy) {
+        AbstractISOBI(Boolean showOffset, int accuracy) {
             this.showOffset = showOffset;
             this.accuracy = accuracy;
         }
         
-        protected void checkDateTypeNotUnknown(int dateType)
+        void checkDateTypeNotUnknown(int dateType)
         throws TemplateException {
             if (dateType == TemplateDateModel.UNKNOWN) {
                 throw new _MiscTemplateException(new _ErrorDescriptionBuilder(
@@ -196,11 +191,11 @@ class BuiltInsForDates {
             }
         }
     
-        protected boolean shouldShowOffset(Date date, int dateType, Environment env) {
+        boolean shouldShowOffset(Date date, int dateType, Environment env) {
             if (dateType == TemplateDateModel.DATE) {
                 return false;  // ISO 8061 doesn't allow zone for date-only values
             } else if (showOffset != null) {
-                return showOffset.booleanValue();
+                return showOffset;
             } else {
                 // java.sql.Time values meant to carry calendar field values only, so we don't show offset for them.
                 return !(date instanceof java.sql.Time);

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/1333a715/freemarker-core/src/main/java/org/apache/freemarker/core/BuiltInsForExistenceHandling.java
----------------------------------------------------------------------
diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/BuiltInsForExistenceHandling.java b/freemarker-core/src/main/java/org/apache/freemarker/core/BuiltInsForExistenceHandling.java
index 6e7cce0..c0d6164 100644
--- a/freemarker-core/src/main/java/org/apache/freemarker/core/BuiltInsForExistenceHandling.java
+++ b/freemarker-core/src/main/java/org/apache/freemarker/core/BuiltInsForExistenceHandling.java
@@ -22,7 +22,7 @@ package org.apache.freemarker.core;
 import java.util.List;
 
 import org.apache.freemarker.core.model.TemplateBooleanModel;
-import org.apache.freemarker.core.model.TemplateMethodModelEx;
+import org.apache.freemarker.core.model.TemplateMethodModel;
 import org.apache.freemarker.core.model.TemplateModel;
 import org.apache.freemarker.core.model.TemplateModelException;
 
@@ -63,7 +63,7 @@ class BuiltInsForExistenceHandling {
             return model == null ? FIRST_NON_NULL_METHOD : new ConstantMethod(model);
         }
 
-        private static class ConstantMethod implements TemplateMethodModelEx {
+        private static class ConstantMethod implements TemplateMethodModel {
             private final TemplateModel constant;
 
             ConstantMethod(TemplateModel constant) {
@@ -71,7 +71,7 @@ class BuiltInsForExistenceHandling {
             }
 
             @Override
-            public Object exec(List args) {
+            public TemplateModel execute(List<? extends TemplateModel> args) {
                 return constant;
             }
         }
@@ -80,14 +80,14 @@ class BuiltInsForExistenceHandling {
          * A method that goes through the arguments one by one and returns
          * the first one that is non-null. If all args are null, returns null.
          */
-        private static final TemplateMethodModelEx FIRST_NON_NULL_METHOD =
-            new TemplateMethodModelEx() {
+        private static final TemplateMethodModel FIRST_NON_NULL_METHOD =
+            new TemplateMethodModel() {
                 @Override
-                public Object exec(List args) throws TemplateModelException {
+                public TemplateModel execute(List<? extends TemplateModel> args) throws TemplateModelException {
                     int argCnt = args.size();
                     if (argCnt == 0) throw MessageUtil.newArgCntError("?default", argCnt, 1, Integer.MAX_VALUE);
                     for (int i = 0; i < argCnt; i++ ) {
-                        TemplateModel result = (TemplateModel) args.get(i);
+                        TemplateModel result = args.get(i);
                         if (result != null) return result;
                     }
                     return null;

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/1333a715/freemarker-core/src/main/java/org/apache/freemarker/core/BuiltInsForMultipleTypes.java
----------------------------------------------------------------------
diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/BuiltInsForMultipleTypes.java b/freemarker-core/src/main/java/org/apache/freemarker/core/BuiltInsForMultipleTypes.java
index 74f7c15..5e85262 100644
--- a/freemarker-core/src/main/java/org/apache/freemarker/core/BuiltInsForMultipleTypes.java
+++ b/freemarker-core/src/main/java/org/apache/freemarker/core/BuiltInsForMultipleTypes.java
@@ -19,6 +19,7 @@
 
 package org.apache.freemarker.core;
 
+import java.io.Serializable;
 import java.util.Date;
 import java.util.List;
 
@@ -110,7 +111,7 @@ class BuiltInsForMultipleTypes {
         private class DateParser
         implements
             TemplateDateModel,
-            TemplateMethodModel,
+                TemplateMethodModel,
             TemplateHashModel {
             private final String text;
             private final Environment env;
@@ -125,9 +126,9 @@ class BuiltInsForMultipleTypes {
             }
             
             @Override
-            public Object exec(List args) throws TemplateModelException {
+            public TemplateModel execute(List<? extends TemplateModel> args) throws TemplateException {
                 checkMethodArgCount(args, 0, 1);
-                return args.size() == 0 ? getAsDateModel() : get((String) args.get(0));
+                return args.size() == 0 ? getAsDateModel() : get(_CallableUtils.castArgToString(args, 0));
             }
             
             @Override
@@ -470,8 +471,8 @@ class BuiltInsForMultipleTypes {
         
         private class BooleanFormatter
         implements 
-            TemplateScalarModel, 
-            TemplateMethodModel {
+            TemplateScalarModel,
+                TemplateMethodModel {
             private final TemplateBooleanModel bool;
             private final Environment env;
             
@@ -481,9 +482,14 @@ class BuiltInsForMultipleTypes {
             }
     
             @Override
-            public Object exec(List args) throws TemplateModelException {
+            public TemplateModel execute(List<? extends TemplateModel> args) throws TemplateException {
                 checkMethodArgCount(args, 2);
-                return new SimpleScalar((String) args.get(bool.getAsBoolean() ? 0 : 1));
+                int argIdx = bool.getAsBoolean() ? 0 : 1;
+                TemplateModel result = args.get(argIdx);
+                if (!(result instanceof TemplateScalarModel)) {
+                    throw new NonStringException((Serializable) argIdx, result, null, null);
+                }
+                return result;
             }
     
             @Override
@@ -505,7 +511,7 @@ class BuiltInsForMultipleTypes {
         implements
             TemplateScalarModel,
             TemplateHashModel,
-            TemplateMethodModel {
+                TemplateMethodModel {
             private final TemplateDateModel dateModel;
             private final Environment env;
             private final TemplateDateFormat defaultFormat;
@@ -524,9 +530,9 @@ class BuiltInsForMultipleTypes {
             }
     
             @Override
-            public Object exec(List args) throws TemplateModelException {
+            public TemplateModel execute(List<? extends TemplateModel> args) throws TemplateException {
                 checkMethodArgCount(args, 1);
-                return formatWith((String) args.get(0));
+                return formatWith(_CallableUtils.castArgToString(args, 0));
             }
 
             @Override
@@ -580,7 +586,7 @@ class BuiltInsForMultipleTypes {
         implements
             TemplateScalarModel,
             TemplateHashModel,
-            TemplateMethodModel {
+                TemplateMethodModel {
             private final TemplateNumberModel numberModel;
             private final Number number;
             private final Environment env;
@@ -602,9 +608,9 @@ class BuiltInsForMultipleTypes {
             }
     
             @Override
-            public Object exec(List args) throws TemplateModelException {
+            public TemplateModel execute(List<? extends TemplateModel> args) throws TemplateException {
                 checkMethodArgCount(args, 1);
-                return get((String) args.get(0));
+                return get(_CallableUtils.castArgToString(args, 0));
             }
     
             @Override

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/1333a715/freemarker-core/src/main/java/org/apache/freemarker/core/BuiltInsForNestedContentParameters.java
----------------------------------------------------------------------
diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/BuiltInsForNestedContentParameters.java b/freemarker-core/src/main/java/org/apache/freemarker/core/BuiltInsForNestedContentParameters.java
index a4c4d8f..89593cd 100644
--- a/freemarker-core/src/main/java/org/apache/freemarker/core/BuiltInsForNestedContentParameters.java
+++ b/freemarker-core/src/main/java/org/apache/freemarker/core/BuiltInsForNestedContentParameters.java
@@ -22,7 +22,7 @@ import java.util.List;
 
 import org.apache.freemarker.core.ASTDirList.IterationContext;
 import org.apache.freemarker.core.model.TemplateBooleanModel;
-import org.apache.freemarker.core.model.TemplateMethodModelEx;
+import org.apache.freemarker.core.model.TemplateMethodModel;
 import org.apache.freemarker.core.model.TemplateModel;
 import org.apache.freemarker.core.model.TemplateModelException;
 import org.apache.freemarker.core.model.impl.SimpleNumber;
@@ -131,7 +131,7 @@ class BuiltInsForNestedContentParameters {
 
     static class item_cycleBI extends BuiltInForNestedContentParameter {
 
-        private class BIMethod implements TemplateMethodModelEx {
+        private class BIMethod implements TemplateMethodModel {
             
             private final IterationContext iterCtx;
     
@@ -140,7 +140,7 @@ class BuiltInsForNestedContentParameters {
             }
     
             @Override
-            public Object exec(List args) throws TemplateModelException {
+            public TemplateModel execute(List<? extends TemplateModel> args) throws TemplateModelException {
                 checkMethodArgCount(args, 1, Integer.MAX_VALUE);
                 return args.get(iterCtx.getIndex() % args.size());
             }

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/1333a715/freemarker-core/src/main/java/org/apache/freemarker/core/BuiltInsForNodes.java
----------------------------------------------------------------------
diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/BuiltInsForNodes.java b/freemarker-core/src/main/java/org/apache/freemarker/core/BuiltInsForNodes.java
index 39bc546..d5dfe93 100644
--- a/freemarker-core/src/main/java/org/apache/freemarker/core/BuiltInsForNodes.java
+++ b/freemarker-core/src/main/java/org/apache/freemarker/core/BuiltInsForNodes.java
@@ -126,7 +126,7 @@ class BuiltInsForNodes {
         }
         
         @Override
-        public Object exec(List names) throws TemplateModelException {
+        public TemplateModel execute(List<? extends TemplateModel> names) throws TemplateException {
             if (names == null || names.isEmpty()) {
                 return this;
             }
@@ -141,7 +141,7 @@ class BuiltInsForNodes {
                     }
                 } else {
                     for (int j = 0; j < names.size(); j++) {
-                        if (_StringUtil.matchesQName((String) names.get(j), nodeName, nsURI, env)) {
+                        if (_StringUtil.matchesQName(_CallableUtils.castArgToString(names, j), nodeName, nsURI, env)) {
                             result.add(tnm);
                             break;
                         }

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/1333a715/freemarker-core/src/main/java/org/apache/freemarker/core/BuiltInsForSequences.java
----------------------------------------------------------------------
diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/BuiltInsForSequences.java b/freemarker-core/src/main/java/org/apache/freemarker/core/BuiltInsForSequences.java
index e131406..9406d33 100644
--- a/freemarker-core/src/main/java/org/apache/freemarker/core/BuiltInsForSequences.java
+++ b/freemarker-core/src/main/java/org/apache/freemarker/core/BuiltInsForSequences.java
@@ -33,7 +33,7 @@ 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.TemplateHashModel;
-import org.apache.freemarker.core.model.TemplateMethodModelEx;
+import org.apache.freemarker.core.model.TemplateMethodModel;
 import org.apache.freemarker.core.model.TemplateModel;
 import org.apache.freemarker.core.model.TemplateModelException;
 import org.apache.freemarker.core.model.TemplateModelIterator;
@@ -54,7 +54,7 @@ class BuiltInsForSequences {
     
     static class chunkBI extends BuiltInForSequence {
 
-        private class BIMethod implements TemplateMethodModelEx {
+        private class BIMethod implements TemplateMethodModel {
             
             private final TemplateSequenceModel tsm;
 
@@ -63,7 +63,7 @@ class BuiltInsForSequences {
             }
 
             @Override
-            public Object exec(List args) throws TemplateModelException {
+            public TemplateModel execute(List<? extends TemplateModel> args) throws TemplateModelException {
                 checkMethodArgCount(args, 1, 2);
                 int chunkSize = getNumberMethodArg(args, 0).intValue();
                 
@@ -182,7 +182,7 @@ class BuiltInsForSequences {
 
     static class joinBI extends ASTExpBuiltIn {
         
-        private class BIMethodForCollection implements TemplateMethodModelEx {
+        private class BIMethodForCollection implements TemplateMethodModel {
             
             private final Environment env;
             private final TemplateCollectionModel coll;
@@ -193,7 +193,7 @@ class BuiltInsForSequences {
             }
 
             @Override
-            public Object exec(List args)
+            public TemplateModel execute(List<? extends TemplateModel> args)
                     throws TemplateModelException {
                 checkMethodArgCount(args, 1, 3);
                 final String separator = getStringMethodArg(args, 0);
@@ -218,7 +218,7 @@ class BuiltInsForSequences {
                             sb.append(_EvalUtil.coerceModelToStringOrUnsupportedMarkup(item, null, null, env));
                         } catch (TemplateException e) {
                             throw new _TemplateModelException(e,
-                                    "\"?", key, "\" failed at index ", Integer.valueOf(idx), " with this error:\n\n",
+                                    "\"?", key, "\" failed at index ", idx, " with this error:\n\n",
                                     MessageUtil.EMBEDDED_MESSAGE_BEGIN,
                                     new _DelayedGetMessageWithoutStackTop(e),
                                     MessageUtil.EMBEDDED_MESSAGE_END);
@@ -295,7 +295,7 @@ class BuiltInsForSequences {
     }
 
     static class seq_containsBI extends ASTExpBuiltIn {
-        private class BIMethodForCollection implements TemplateMethodModelEx {
+        private class BIMethodForCollection implements TemplateMethodModel {
             private TemplateCollectionModel m_coll;
             private Environment m_env;
 
@@ -305,10 +305,10 @@ class BuiltInsForSequences {
             }
 
             @Override
-            public Object exec(List args)
+            public TemplateModel execute(List<? extends TemplateModel> args)
                     throws TemplateModelException {
                 checkMethodArgCount(args, 1);
-                TemplateModel arg = (TemplateModel) args.get(0);
+                TemplateModel arg = args.get(0);
                 TemplateModelIterator it = m_coll.iterator();
                 int idx = 0;
                 while (it.hasNext()) {
@@ -321,7 +321,7 @@ class BuiltInsForSequences {
 
         }
 
-        private class BIMethodForSequence implements TemplateMethodModelEx {
+        private class BIMethodForSequence implements TemplateMethodModel {
             private TemplateSequenceModel m_seq;
             private Environment m_env;
 
@@ -331,10 +331,10 @@ class BuiltInsForSequences {
             }
 
             @Override
-            public Object exec(List args)
+            public TemplateModel execute(List<? extends TemplateModel> args)
                     throws TemplateModelException {
                 checkMethodArgCount(args, 1);
-                TemplateModel arg = (TemplateModel) args.get(0);
+                TemplateModel arg = args.get(0);
                 int size = m_seq.size();
                 for (int i = 0; i < size; i++) {
                     if (modelsEqual(i, m_seq.get(i), arg, m_env))
@@ -364,11 +364,11 @@ class BuiltInsForSequences {
     
     static class seq_index_ofBI extends ASTExpBuiltIn {
         
-        private class BIMethod implements TemplateMethodModelEx {
+        private class BIMethod implements TemplateMethodModel {
             
-            protected final TemplateSequenceModel m_seq;
-            protected final TemplateCollectionModel m_col;
-            protected final Environment m_env;
+            final TemplateSequenceModel m_seq;
+            final TemplateCollectionModel m_col;
+            final Environment m_env;
 
             private BIMethod(Environment env)
                     throws TemplateException {
@@ -395,12 +395,12 @@ class BuiltInsForSequences {
             }
 
             @Override
-            public final Object exec(List args)
+            public final TemplateModel execute(List<? extends TemplateModel> args)
                     throws TemplateModelException {
                 int argCnt = args.size();
                 checkMethodArgCount(argCnt, 1, 2);
                 
-                TemplateModel target = (TemplateModel) args.get(0);
+                TemplateModel target = args.get(0);
                 int foundAtIdx;
                 if (argCnt > 1) {
                     int startIndex = getNumberMethodArg(args, 1).intValue();
@@ -423,7 +423,7 @@ class BuiltInsForSequences {
                 return findInCol(target, 0, Integer.MAX_VALUE);
             }
             
-            protected int findInCol(TemplateModel target, int startIndex)
+            int findInCol(TemplateModel target, int startIndex)
                     throws TemplateModelException {
                 if (m_dir == 1) {
                     return findInCol(target, startIndex, Integer.MAX_VALUE);
@@ -432,7 +432,7 @@ class BuiltInsForSequences {
                 }
             }
         
-            protected int findInCol(TemplateModel target,
+            int findInCol(TemplateModel target,
                     final int allowedRangeStart, final int allowedRangeEnd)
                     throws TemplateModelException {
                 if (allowedRangeEnd < 0) return -1;
@@ -525,7 +525,7 @@ class BuiltInsForSequences {
     }
 
     static class sort_byBI extends sortBI {
-        class BIMethod implements TemplateMethodModelEx {
+        class BIMethod implements TemplateMethodModel {
             TemplateSequenceModel seq;
             
             BIMethod(TemplateSequenceModel seq) {
@@ -533,7 +533,7 @@ class BuiltInsForSequences {
             }
             
             @Override
-            public Object exec(List args)
+            public TemplateModel execute(List<? extends TemplateModel> args)
                     throws TemplateModelException {
                 // Should be:
                 // checkMethodArgCount(args, 1);
@@ -549,18 +549,14 @@ class BuiltInsForSequences {
                     int ln = seq.size();
                     subvars = new String[ln];
                     for (int i = 0; i < ln; i++) {
-                        Object item = seq.get(i);
-                        try {
-                            subvars[i] = ((TemplateScalarModel) item)
-                                    .getAsString();
-                        } catch (ClassCastException e) {
-                            if (!(item instanceof TemplateScalarModel)) {
-                                throw new _TemplateModelException(
-                                        "The argument to ?", key, "(key), when it's a sequence, must be a "
-                                        + "sequence of strings, but the item at index ", Integer.valueOf(i),
-                                        " is not a string.");
-                            }
+                        TemplateModel item = seq.get(i);
+                        if (!(item instanceof  TemplateScalarModel)) {
+                            throw new _TemplateModelException(
+                                    "The argument to ?", key, "(key), when it's a sequence, must be a "
+                                    + "sequence of strings, but the item at index ", i,
+                                    " is not a string.");
                         }
+                        subvars[i] = ((TemplateScalarModel) item).getAsString();
                     }
                 } else {
                     throw new _TemplateModelException(


[7/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

Posted by dd...@apache.org.
FREEMARKER-64: Removed TemplateMethodModel, using TemplateFunctionModel everywhere instead. Some refinement of existing TemplateCallableModel API-s, most importantly, the support for null argumenArrayLayout. Removed `?isMethod` (use `isFunction` instead) and `?isMacro` (use `isDirective` instead).


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

Branch: refs/heads/3
Commit: 8d5263f2aa0fca589ed67b6f6ccbbd8780ece5ef
Parents: 1333a71
Author: ddekany <dd...@apache.org>
Authored: Mon Aug 7 20:44:33 2017 +0200
Committer: ddekany <dd...@apache.org>
Committed: Tue Aug 8 00:11:19 2017 +0200

----------------------------------------------------------------------
 .gitignore                                      |   2 +
 FM3-CHANGE-LOG.txt                              |  19 +-
 .../core/FM2ASTToFM3SourceConverter.java        |   4 +
 .../converter/FM2ToFM3ConverterTest.java        |   4 +
 ...a8DefaultObjectWrapperBridgeMethodsTest.java |  19 +-
 .../impl/Java8DefaultObjectWrapperTest.java     |  43 +--
 .../freemarker/core/DirectiveCallPlaceTest.java |   6 +-
 .../EnvironmentGetTemplateVariantsTest.java     |   2 +-
 .../core/TheadInterruptingSupportTest.java      |   6 +-
 .../impl/AbstractParallelIntrospectionTest.java |   9 +-
 .../CommonSupertypeForUnwrappingHintTest.java   |   8 +-
 .../model/impl/DefaultObjectWrapperTest.java    |  34 ++-
 .../core/model/impl/EnumModelsTest.java         |  11 +-
 .../core/model/impl/ErrorMessagesTest.java      |  24 +-
 .../impl/FineTuneMethodAppearanceTest.java      |   3 +-
 .../core/model/impl/StaticModelsTest.java       |  11 +-
 .../templatesuite/CoreTemplateTestSuite.java    |   4 +-
 .../core/templatesuite/models/MultiModel2.java  |  33 +--
 .../models/SimpleTestFunction.java              |  44 +++
 .../templatesuite/models/SimpleTestMethod.java  |  50 ----
 .../core/templatesuite/models/TestMethod.java   |  16 --
 .../core/userpkg/AllFeaturesDirective.java      |   2 +-
 .../core/userpkg/AllFeaturesFunction.java       |   2 +-
 .../core/userpkg/NamedVarargsOnlyDirective.java |   2 +-
 .../userpkg/PositionalVarargsOnlyDirective.java |   2 +-
 .../userpkg/PositionalVarargsOnlyFunction.java  |   2 +-
 .../core/userpkg/TwoNamedParamsDirective.java   |   2 +-
 .../TwoNestedContentParamsDirective.java        |   2 +-
 .../userpkg/TwoPositionalParamsDirective.java   |   2 +-
 .../userpkg/TwoPositionalParamsFunction.java    |   2 +-
 .../core/userpkg/UpperCaseDirective.java        |   2 +-
 .../core/valueformat/NumberFormatTest.java      |   2 +-
 .../org/apache/freemarker/core/ast-builtins.ast |   4 +-
 .../org/apache/freemarker/core/ast-range.ast    |   6 +-
 .../templatesuite/expected/number-literal.txt   |  10 +-
 .../templatesuite/expected/type-builtins.txt    |  28 +-
 .../core/templatesuite/templates/list2.ftl      |   2 +-
 .../templatesuite/templates/number-literal.ftl  |   2 +-
 .../templates/string-builtins3.ftl              |  32 +--
 .../templatesuite/templates/type-builtins.ftl   |   7 +-
 .../freemarker/core/ASTDynamicTopLevelCall.java |  77 +++--
 .../apache/freemarker/core/ASTExpBuiltIn.java   |  68 +++--
 .../freemarker/core/ASTExpFunctionCall.java     | 229 +++++++++++++++
 .../freemarker/core/ASTExpListLiteral.java      |   5 +-
 .../freemarker/core/ASTExpMethodCall.java       | 226 ---------------
 .../freemarker/core/BuiltInsForDates.java       |  30 +-
 .../core/BuiltInsForExistenceHandling.java      |  49 ++--
 .../core/BuiltInsForMultipleTypes.java          | 114 ++++----
 .../BuiltInsForNestedContentParameters.java     |  20 +-
 .../freemarker/core/BuiltInsForNodes.java       |  38 ++-
 .../freemarker/core/BuiltInsForSequences.java   | 126 ++++----
 .../core/BuiltInsForStringsBasic.java           | 287 ++++++++++++-------
 .../core/BuiltInsForStringsEncoding.java        |  19 +-
 .../freemarker/core/BuiltInsForStringsMisc.java |  20 +-
 .../core/BuiltInsForStringsRegexp.java          |  51 ++--
 .../org/apache/freemarker/core/Environment.java |  21 +-
 .../org/apache/freemarker/core/MessageUtil.java |   2 +-
 .../freemarker/core/NonFunctionException.java   |  62 ++++
 .../freemarker/core/NonMethodException.java     |  62 ----
 .../apache/freemarker/core/_CallableUtils.java  |  61 ++--
 .../core/_ObjectBuilderSettingEvaluator.java    |  31 +-
 .../freemarker/core/debug/DebugModel.java       |   2 +-
 .../core/debug/RmiDebugModelImpl.java           |   4 +-
 .../core/model/ArgumentArrayLayout.java         |  18 ++
 .../core/model/GeneralPurposeNothing.java       |  18 +-
 .../core/model/TemplateCallableModel.java       |  11 +-
 .../core/model/TemplateDirectiveModel.java      |  45 ++-
 .../core/model/TemplateFunctionModel.java       |  10 +
 .../core/model/TemplateMethodModel.java         |  53 ----
 .../core/model/TemplateModelException.java      |   4 +-
 .../freemarker/core/model/impl/BeanModel.java   |  17 +-
 .../core/model/impl/DefaultObjectWrapper.java   |  29 +-
 .../core/model/impl/JavaMethodModel.java        |  94 ++----
 .../model/impl/OverloadedFixArgsMethods.java    |  24 +-
 .../model/impl/OverloadedJavaMethodModel.java   |  86 ++++++
 .../core/model/impl/OverloadedMethods.java      |  59 ++--
 .../core/model/impl/OverloadedMethodsModel.java |  65 -----
 .../model/impl/OverloadedMethodsSubset.java     |  52 ++--
 .../model/impl/OverloadedVarArgsMethods.java    |  57 ++--
 .../core/model/impl/ResourceBundleModel.java    |  40 +--
 .../core/model/impl/SimpleJavaMethodModel.java  | 123 ++++++++
 .../core/model/impl/SimpleMethod.java           |  86 +++---
 .../freemarker/core/model/impl/StaticModel.java |  16 +-
 .../model/impl/TemplateModelListSequence.java   |  13 +-
 .../freemarker/core/model/impl/_MethodUtil.java |  40 +--
 .../apache/freemarker/core/util/FTLUtil.java    |  11 +-
 freemarker-core/src/main/javacc/FTL.jj          |   4 +-
 .../apache/freemarker/servlet/IncludePage.java  |   2 +-
 .../jsp/CustomTagAndELFunctionCombiner.java     | 157 ----------
 .../freemarker/servlet/jsp/JspTagModelBase.java |  33 +--
 .../servlet/jsp/SimpleTagDirectiveModel.java    |   2 +-
 .../servlet/jsp/TagDirectiveModel.java          |   2 +-
 .../freemarker/servlet/jsp/TaglibFactory.java   |  22 +-
 ...eDirectiveModelAndTemplateFunctionModel.java |  77 +++++
 .../freemarker/servlet/jsp/TLDParsingTest.java  |  17 +-
 .../test/templateutil/AssertDirective.java      |   2 +-
 .../templateutil/AssertEqualsDirective.java     |   2 +-
 .../test/templateutil/AssertFailsDirective.java |   2 +-
 .../test/templateutil/NoOutputDirective.java    |   2 +-
 99 files changed, 1802 insertions(+), 1562 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/8d5263f2/.gitignore
----------------------------------------------------------------------
diff --git a/.gitignore b/.gitignore
index 0d9f1d4..5407d43 100644
--- a/.gitignore
+++ b/.gitignore
@@ -32,3 +32,5 @@
 
 .directory
 .Trash*
+
+**/adhoctest/
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/8d5263f2/FM3-CHANGE-LOG.txt
----------------------------------------------------------------------
diff --git a/FM3-CHANGE-LOG.txt b/FM3-CHANGE-LOG.txt
index d38866b..685160a 100644
--- a/FM3-CHANGE-LOG.txt
+++ b/FM3-CHANGE-LOG.txt
@@ -86,9 +86,6 @@ Node: Changes already mentioned above aren't repeated here!
   translated to `${b}`, while `#{x; m1M3}` is translated to `${x?string('0.0##')}`). The output should remain the same.
 - In `#macro` and `#function` the comma must only be used between by-position parameters (earlier the comma was
   optional).
-  TODO As of this writing, the by-position VS by-name feature isn't implemented yet, so
-  comma is to be used for `#function`, and comma must not be used for `#macro` (even though for now macros are always
-  allowed to be called with by-position parameters as well, just as in FM2).
 - In `#function`, parentheses are now required around parameter list (as in `<#function f(a, b)>`), even if there are
   zero parameters (as in `<#function f()>`). In `#macro`, parentheses are not allowed around parameter list anymore
   (so `#macro m(a b)` becomes to `#macro m a b`). This is to remain more consistent with the look-and-feel of the
@@ -120,8 +117,10 @@ Node: Changes already mentioned above aren't repeated here!
   - Renamed `DirectiveCallPlace` to `CallPlace`
   - Removed Environment.getDirectiveCallPlace(), as TemplateDirectiveModel-s now get the CallPlace as the
     parameter of the `execute` method.
-  - ?isTransform was removed (as there are no transforms anymore).
-    Converter note: The template converter tool replaces it with ?isDirective
+  - `?isTransform` was removed (as there are no transforms anymore); use `?isDirective` instead.
+  - `?isMacro` was removed; use `?isDirective` instead, which returns `true` both for macros and directives defined otherwise.
+  - `?isMethod` was removed; use `?isFunction` instead, which returns `true` both for Java methods and functions defined
+     otherwise (such as with `#function`).
 - The directive returned by `?interpret` doesn't allow nested content anymore. (It wasn't useful earlier either;
   the nested content was simply executed after the interpreted string.)
 - Changes in #macro/#functions
@@ -199,7 +198,6 @@ Major changes / features
     `TemplateDirectiveModel`, `TemplateTransformModel`). FM3 replaces them with only two new interfaces,
     `TemplateDirectiveModel` (differs from the interface with identical name in FM2) and `TemplateFunctionModel`.
     (These are both the subinterfaces of another new interface `TemplateCallableModel`.)
-    [TODO: TemplateMethodModel[Ex] wasn't yet replaced by TemplateFunctionModel.]
   - All callable TempalteModel-s support passing parameters by position and by name, even in the same call
     (e.g., `<@heading "Some title" icon="foo.jpg" />`, `sum(1, 2, 3, abs=true)`)
   - `#macro` now produces a `TemplateDirectiveModel` and `#function` produces a `TemplateFunctionModel`. (Earlier, the
@@ -375,11 +373,10 @@ Core / Models and Object wrapping
   get method (`get(String)`) had priority over methods of similar name. The generic get method is only recognized from its
   name and parameter type, so it's a quite consfusing feature, and might will be removed alltogether.
 - Renamed TemplateDateModel.DATETIME to DATE_TIME (to be consistent with "dateTime" used elsewhere).
-- TemplateModel and TemplateModelEx changes (but they will be probably replaced by TemplateFunctionModel later):
-  Removed FM2 `TemplateMethodModel`, then renamed `TemplateMethodModelEx` to `TemplateMethodModel`. Then changed it further to return
-  `TemplateModel` (instead of `Object`, which allowed returning a non-wrapped value by design in FM2), changed the parameter `List` to
-  `List<? extends TemplateModel>` (which it was earlier too, but there were no generic paramters back then), and allowed throwing
-  `TemplateException` instead of the more restrictive `TemplateModelException`.
+- `TemplateMethod` and `TemplateMethodEx` was removed, taken over by `TemplateFunctionModel`. `TemplateFunctionModel` is
+  the common interface bith for wrapped Java methods and functions defined in the templates, or on any other ways.
+  `OverloadedMethodsModel` and `SimpleMethodModel` were renamed to `OverloadJavaMethodModel` and  `SimpleJavaMethodModel`,
+  which of course extend `TemplateFunctionModel`, but allow inocations without `Environment` parameter.
 
 Core / Template loading and caching
 ...................................

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/8d5263f2/freemarker-converter/src/main/java/freemarker/core/FM2ASTToFM3SourceConverter.java
----------------------------------------------------------------------
diff --git a/freemarker-converter/src/main/java/freemarker/core/FM2ASTToFM3SourceConverter.java b/freemarker-converter/src/main/java/freemarker/core/FM2ASTToFM3SourceConverter.java
index 57a912f..6d9b25c 100644
--- a/freemarker-converter/src/main/java/freemarker/core/FM2ASTToFM3SourceConverter.java
+++ b/freemarker-converter/src/main/java/freemarker/core/FM2ASTToFM3SourceConverter.java
@@ -1971,6 +1971,10 @@ public class FM2ASTToFM3SourceConverter {
             .put("web_safe", "html")
             .put("is_transform", "isDirective")
             .put("isTransform", "isDirective")
+            .put("is_macro", "isDirective")
+            .put("isMacro", "isDirective")
+            .put("is_method", "isFunction")
+            .put("isMethod", "isFunction")
             .put("iso_utc_fz", "isoUtcFZ")
             .put("iso_utc_nz", "isoUtcNZ")
             .put("iso_utc_ms_nz", "isoUtcMsNZ")

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/8d5263f2/freemarker-converter/src/test/java/org/freemarker/converter/FM2ToFM3ConverterTest.java
----------------------------------------------------------------------
diff --git a/freemarker-converter/src/test/java/org/freemarker/converter/FM2ToFM3ConverterTest.java b/freemarker-converter/src/test/java/org/freemarker/converter/FM2ToFM3ConverterTest.java
index 63c2a92..0790518 100644
--- a/freemarker-converter/src/test/java/org/freemarker/converter/FM2ToFM3ConverterTest.java
+++ b/freemarker-converter/src/test/java/org/freemarker/converter/FM2ToFM3ConverterTest.java
@@ -475,6 +475,10 @@ public class FM2ToFM3ConverterTest extends ConverterTest {
         assertConverted("${s?html}", "${s?webSafe}");
         assertConverted("${s?isDirective}", "${s?is_transform}");
         assertConverted("${s?isDirective}", "${s?isTransform}");
+        assertConverted("${s?isDirective}", "${s?is_macro}");
+        assertConverted("${s?isDirective}", "${s?isMacro}");
+        assertConverted("${s?isFunction}", "${s?is_method}");
+        assertConverted("${s?isFunction}", "${s?isMethod}");
         assertConvertedSame("${s  ?   upperCase\t?\t\tleftPad(5)}");
         assertConvertedSame("${s <#--1--> ? <#--2--> upperCase}");
         // Runtime params:

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/8d5263f2/freemarker-core-test-java8/src/test/java/org/apache/freemarker/core/model/impl/Java8DefaultObjectWrapperBridgeMethodsTest.java
----------------------------------------------------------------------
diff --git a/freemarker-core-test-java8/src/test/java/org/apache/freemarker/core/model/impl/Java8DefaultObjectWrapperBridgeMethodsTest.java b/freemarker-core-test-java8/src/test/java/org/apache/freemarker/core/model/impl/Java8DefaultObjectWrapperBridgeMethodsTest.java
index abb373e..095dc37 100644
--- a/freemarker-core-test-java8/src/test/java/org/apache/freemarker/core/model/impl/Java8DefaultObjectWrapperBridgeMethodsTest.java
+++ b/freemarker-core-test-java8/src/test/java/org/apache/freemarker/core/model/impl/Java8DefaultObjectWrapperBridgeMethodsTest.java
@@ -20,12 +20,11 @@ package org.apache.freemarker.core.model.impl;
 
 import static org.junit.Assert.*;
 
-import java.util.Collections;
-
 import org.apache.freemarker.core.Configuration;
+import org.apache.freemarker.core.NonTemplateCallPlace;
 import org.apache.freemarker.core.TemplateException;
+import org.apache.freemarker.core.model.Constants;
 import org.apache.freemarker.core.model.TemplateHashModel;
-import org.apache.freemarker.core.model.TemplateMethodModel;
 import org.junit.Test;
 
 public class Java8DefaultObjectWrapperBridgeMethodsTest {
@@ -53,12 +52,14 @@ public class Java8DefaultObjectWrapperBridgeMethodsTest {
         } catch (Exception e) {
             throw new IllegalStateException(e);
         }
-        
-        TemplateMethodModel m1 = (TemplateMethodModel) wrapped.get("m1");
-        assertEquals(BridgeMethodsBean.M1_RETURN_VALUE, "" + m1.execute(Collections.emptyList()));
-        
-        TemplateMethodModel m2 = (TemplateMethodModel) wrapped.get("m2");
-        assertNull(m2.execute(Collections.emptyList()));
+
+        JavaMethodModel m1 = (JavaMethodModel) wrapped.get("m1");
+        assertEquals(
+                BridgeMethodsBean.M1_RETURN_VALUE,
+                "" + m1.execute(Constants.EMPTY_TEMPLATE_MODEL_ARRAY, NonTemplateCallPlace.INSTANCE));
+
+        JavaMethodModel m2 = (JavaMethodModel) wrapped.get("m2");
+        assertNull(m2.execute(Constants.EMPTY_TEMPLATE_MODEL_ARRAY, NonTemplateCallPlace.INSTANCE));
     }
     
 }

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/8d5263f2/freemarker-core-test-java8/src/test/java/org/apache/freemarker/core/model/impl/Java8DefaultObjectWrapperTest.java
----------------------------------------------------------------------
diff --git a/freemarker-core-test-java8/src/test/java/org/apache/freemarker/core/model/impl/Java8DefaultObjectWrapperTest.java b/freemarker-core-test-java8/src/test/java/org/apache/freemarker/core/model/impl/Java8DefaultObjectWrapperTest.java
index 08dc5b1..c2bfad5 100644
--- a/freemarker-core-test-java8/src/test/java/org/apache/freemarker/core/model/impl/Java8DefaultObjectWrapperTest.java
+++ b/freemarker-core-test-java8/src/test/java/org/apache/freemarker/core/model/impl/Java8DefaultObjectWrapperTest.java
@@ -20,12 +20,12 @@ package org.apache.freemarker.core.model.impl;
 
 import static org.junit.Assert.*;
 
-import java.util.Collections;
-
 import org.apache.freemarker.core.Configuration;
+import org.apache.freemarker.core.NonTemplateCallPlace;
 import org.apache.freemarker.core.TemplateException;
+import org.apache.freemarker.core.model.Constants;
 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.TemplateNumberModel;
 import org.apache.freemarker.core.model.TemplateScalarModel;
 import org.apache.freemarker.core.model.TemplateSequenceModel;
@@ -61,13 +61,13 @@ public class Java8DefaultObjectWrapperTest {
             // Has only indexed read method, so it's not exposed as a property
             assertNull(wrappedBean.get(Java8DefaultMethodsBeanBase.DEFAULT_METHOD_INDEXED_PROP));
 
-            TemplateMethodModel indexedReadMethod = (TemplateMethodModel) wrappedBean.get(
+            JavaMethodModel indexedReadMethod = (JavaMethodModel) wrappedBean.get(
                     Java8DefaultMethodsBeanBase.DEFAULT_METHOD_INDEXED_PROP_GETTER);
             assertNotNull(indexedReadMethod);
             assertEquals(Java8DefaultMethodsBeanBase.DEFAULT_METHOD_INDEXED_PROP_VALUE,
-                    ((TemplateScalarModel) indexedReadMethod.execute(Collections.singletonList(new SimpleNumber(0))))
-                            .getAsString
-                            ());
+                    ((TemplateScalarModel) indexedReadMethod.execute(
+                            new TemplateModel[] { new SimpleNumber(0) }, NonTemplateCallPlace.INSTANCE))
+                            .getAsString());
         }
         {
             // We see default method indexed read method, but it's invalidated by normal getter in the subclass
@@ -114,45 +114,54 @@ public class Java8DefaultObjectWrapperTest {
             // Has only indexed read method, so it's not exposed as a property
             assertNull(wrappedBean.get(Java8DefaultMethodsBean.INDEXED_PROP_4));
 
-            TemplateMethodModel indexedReadMethod = (TemplateMethodModel) wrappedBean.get(
+            JavaMethodModel indexedReadMethod = (JavaMethodModel) wrappedBean.get(
                     Java8DefaultMethodsBean.INDEXED_PROP_GETTER_4);
             assertNotNull(indexedReadMethod);
             assertEquals(Java8DefaultMethodsBean.INDEXED_PROP_4_VALUE,
-                    ((TemplateScalarModel) indexedReadMethod.execute(Collections.singletonList(new SimpleNumber(0))))
+                    ((TemplateScalarModel) indexedReadMethod.execute(
+                            new TemplateModel[] { new SimpleNumber(0) }, NonTemplateCallPlace.INSTANCE))
                             .getAsString());
         }        
         {
-            TemplateMethodModel action = (TemplateMethodModel) wrappedBean.get(
+            JavaMethodModel action = (JavaMethodModel) wrappedBean.get(
                     Java8DefaultMethodsBean.NORMAL_ACTION);
             assertNotNull(action);
             assertEquals(
                     Java8DefaultMethodsBean.NORMAL_ACTION_RETURN_VALUE,
-                    ((TemplateScalarModel) action.execute(Collections.emptyList())).getAsString());
+                    ((TemplateScalarModel) action.execute(
+                            Constants.EMPTY_TEMPLATE_MODEL_ARRAY, NonTemplateCallPlace.INSTANCE))
+                            .getAsString());
         }
         
         {
-            TemplateMethodModel action = (TemplateMethodModel) wrappedBean.get(
+            JavaMethodModel action = (JavaMethodModel) wrappedBean.get(
                     Java8DefaultMethodsBean.NORMAL_ACTION);
             assertNotNull(action);
             assertEquals(
                     Java8DefaultMethodsBean.NORMAL_ACTION_RETURN_VALUE,
-                    ((TemplateScalarModel) action.execute(Collections.emptyList())).getAsString());
+                    ((TemplateScalarModel) action.execute(
+                            Constants.EMPTY_TEMPLATE_MODEL_ARRAY, NonTemplateCallPlace.INSTANCE))
+                            .getAsString());
         }
         {
-            TemplateMethodModel action = (TemplateMethodModel) wrappedBean.get(
+            JavaMethodModel action = (JavaMethodModel) wrappedBean.get(
                     Java8DefaultMethodsBean.DEFAULT_METHOD_ACTION);
             assertNotNull(action);
             assertEquals(
                     Java8DefaultMethodsBean.DEFAULT_METHOD_ACTION_RETURN_VALUE,
-                    ((TemplateScalarModel) action.execute(Collections.emptyList())).getAsString());
+                    ((TemplateScalarModel) action.execute(
+                            Constants.EMPTY_TEMPLATE_MODEL_ARRAY, NonTemplateCallPlace.INSTANCE))
+                            .getAsString());
         }
         {
-            TemplateMethodModel action = (TemplateMethodModel) wrappedBean.get(
+            JavaMethodModel action = (JavaMethodModel) wrappedBean.get(
                     Java8DefaultMethodsBean.OVERRIDDEN_DEFAULT_METHOD_ACTION);
             assertNotNull(action);
             assertEquals(
                     Java8DefaultMethodsBean.OVERRIDDEN_DEFAULT_METHOD_ACTION_RETURN_VALUE,
-                    ((TemplateScalarModel) action.execute(Collections.emptyList())).getAsString());
+                    ((TemplateScalarModel) action.execute(
+                            Constants.EMPTY_TEMPLATE_MODEL_ARRAY, NonTemplateCallPlace.INSTANCE))
+                            .getAsString());
         }
     }
 

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/8d5263f2/freemarker-core-test/src/test/java/org/apache/freemarker/core/DirectiveCallPlaceTest.java
----------------------------------------------------------------------
diff --git a/freemarker-core-test/src/test/java/org/apache/freemarker/core/DirectiveCallPlaceTest.java b/freemarker-core-test/src/test/java/org/apache/freemarker/core/DirectiveCallPlaceTest.java
index 7ee42c2..25a05ac 100644
--- a/freemarker-core-test/src/test/java/org/apache/freemarker/core/DirectiveCallPlaceTest.java
+++ b/freemarker-core-test/src/test/java/org/apache/freemarker/core/DirectiveCallPlaceTest.java
@@ -137,7 +137,7 @@ public class DirectiveCallPlaceTest extends TemplateTest {
         }
 
         @Override
-        public ArgumentArrayLayout getArgumentArrayLayout() {
+        public ArgumentArrayLayout getDirectiveArgumentArrayLayout() {
             return ArgumentArrayLayout.PARAMETERLESS;
         }
 
@@ -207,7 +207,7 @@ public class DirectiveCallPlaceTest extends TemplateTest {
         }
 
         @Override
-        public ArgumentArrayLayout getArgumentArrayLayout() {
+        public ArgumentArrayLayout getDirectiveArgumentArrayLayout() {
             return ArgumentArrayLayout.PARAMETERLESS;
         }
 
@@ -245,7 +245,7 @@ public class DirectiveCallPlaceTest extends TemplateTest {
         }
 
         @Override
-        public ArgumentArrayLayout getArgumentArrayLayout() {
+        public ArgumentArrayLayout getDirectiveArgumentArrayLayout() {
             return ARGS_LAYOUT;
         }
 

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/8d5263f2/freemarker-core-test/src/test/java/org/apache/freemarker/core/EnvironmentGetTemplateVariantsTest.java
----------------------------------------------------------------------
diff --git a/freemarker-core-test/src/test/java/org/apache/freemarker/core/EnvironmentGetTemplateVariantsTest.java b/freemarker-core-test/src/test/java/org/apache/freemarker/core/EnvironmentGetTemplateVariantsTest.java
index a094ec6..d79e37a 100644
--- a/freemarker-core-test/src/test/java/org/apache/freemarker/core/EnvironmentGetTemplateVariantsTest.java
+++ b/freemarker-core-test/src/test/java/org/apache/freemarker/core/EnvironmentGetTemplateVariantsTest.java
@@ -207,7 +207,7 @@ public class EnvironmentGetTemplateVariantsTest extends TemplateTest {
             }
 
             @Override
-            public ArgumentArrayLayout getArgumentArrayLayout() {
+            public ArgumentArrayLayout getDirectiveArgumentArrayLayout() {
                 return ArgumentArrayLayout.PARAMETERLESS;
             }
 

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/8d5263f2/freemarker-core-test/src/test/java/org/apache/freemarker/core/TheadInterruptingSupportTest.java
----------------------------------------------------------------------
diff --git a/freemarker-core-test/src/test/java/org/apache/freemarker/core/TheadInterruptingSupportTest.java b/freemarker-core-test/src/test/java/org/apache/freemarker/core/TheadInterruptingSupportTest.java
index 61c5823..1468e59 100644
--- a/freemarker-core-test/src/test/java/org/apache/freemarker/core/TheadInterruptingSupportTest.java
+++ b/freemarker-core-test/src/test/java/org/apache/freemarker/core/TheadInterruptingSupportTest.java
@@ -129,7 +129,7 @@ public class TheadInterruptingSupportTest {
             }
 
             @Override
-            public ArgumentArrayLayout getArgumentArrayLayout() {
+            public ArgumentArrayLayout getDirectiveArgumentArrayLayout() {
                 return ArgumentArrayLayout.PARAMETERLESS;
             }
 
@@ -150,7 +150,7 @@ public class TheadInterruptingSupportTest {
             }
 
             @Override
-            public ArgumentArrayLayout getArgumentArrayLayout() {
+            public ArgumentArrayLayout getDirectiveArgumentArrayLayout() {
                 return ArgumentArrayLayout.PARAMETERLESS;
             }
 
@@ -173,7 +173,7 @@ public class TheadInterruptingSupportTest {
             }
 
             @Override
-            public ArgumentArrayLayout getArgumentArrayLayout() {
+            public ArgumentArrayLayout getDirectiveArgumentArrayLayout() {
                 return ArgumentArrayLayout.PARAMETERLESS;
             }
 

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/8d5263f2/freemarker-core-test/src/test/java/org/apache/freemarker/core/model/impl/AbstractParallelIntrospectionTest.java
----------------------------------------------------------------------
diff --git a/freemarker-core-test/src/test/java/org/apache/freemarker/core/model/impl/AbstractParallelIntrospectionTest.java b/freemarker-core-test/src/test/java/org/apache/freemarker/core/model/impl/AbstractParallelIntrospectionTest.java
index aac56b3..7d2b3fc 100644
--- a/freemarker-core-test/src/test/java/org/apache/freemarker/core/model/impl/AbstractParallelIntrospectionTest.java
+++ b/freemarker-core-test/src/test/java/org/apache/freemarker/core/model/impl/AbstractParallelIntrospectionTest.java
@@ -20,9 +20,10 @@
 package org.apache.freemarker.core.model.impl;
 
 import org.apache.freemarker.core.Configuration;
+import org.apache.freemarker.core.NonTemplateCallPlace;
 import org.apache.freemarker.core.TemplateException;
+import org.apache.freemarker.core.model.Constants;
 import org.apache.freemarker.core.model.TemplateHashModel;
-import org.apache.freemarker.core.model.TemplateMethodModel;
 import org.apache.freemarker.core.model.TemplateModelException;
 import org.apache.freemarker.core.model.TemplateNumberModel;
 
@@ -113,9 +114,11 @@ public abstract class AbstractParallelIntrospectionTest extends TestCase {
 
         private void testMethod(TemplateHashModel h, int objIdx, int mIdx)
                 throws TemplateException, AssertionError {
-            TemplateMethodModel pv = (TemplateMethodModel) h.get("m" + mIdx);
+            JavaMethodModel pv = (JavaMethodModel) h.get("m" + mIdx);
             final int expected = objIdx * 1000 + mIdx;
-            final int got = ((TemplateNumberModel) pv.execute(null)).getAsNumber().intValue();
+            final int got = ((TemplateNumberModel) pv.execute(
+                    Constants.EMPTY_TEMPLATE_MODEL_ARRAY, NonTemplateCallPlace.INSTANCE))
+                    .getAsNumber().intValue();
             if (got != expected) {
                 throw new AssertionError("Method assertion failed; " +
                         "expected " + expected + ", but got " + got);

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/8d5263f2/freemarker-core-test/src/test/java/org/apache/freemarker/core/model/impl/CommonSupertypeForUnwrappingHintTest.java
----------------------------------------------------------------------
diff --git a/freemarker-core-test/src/test/java/org/apache/freemarker/core/model/impl/CommonSupertypeForUnwrappingHintTest.java b/freemarker-core-test/src/test/java/org/apache/freemarker/core/model/impl/CommonSupertypeForUnwrappingHintTest.java
index ef15dae..c4e42ab 100644
--- a/freemarker-core-test/src/test/java/org/apache/freemarker/core/model/impl/CommonSupertypeForUnwrappingHintTest.java
+++ b/freemarker-core-test/src/test/java/org/apache/freemarker/core/model/impl/CommonSupertypeForUnwrappingHintTest.java
@@ -22,6 +22,7 @@ package org.apache.freemarker.core.model.impl;
 import java.io.Serializable;
 import java.util.List;
 
+import org.apache.freemarker.core.model.TemplateModel;
 import org.apache.freemarker.core.model.TemplateModelException;
 
 import junit.framework.TestCase;
@@ -110,17 +111,18 @@ public class CommonSupertypeForUnwrappingHintTest extends TestCase {
         }
 
         @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 w) throws TemplateModelException {
+        MaybeEmptyMemberAndArguments getMemberAndArguments(TemplateModel[] tmArgs, DefaultObjectWrapper w) throws
+                TemplateModelException {
             throw new RuntimeException("Not implemented in this dummy.");
         }
         

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/8d5263f2/freemarker-core-test/src/test/java/org/apache/freemarker/core/model/impl/DefaultObjectWrapperTest.java
----------------------------------------------------------------------
diff --git a/freemarker-core-test/src/test/java/org/apache/freemarker/core/model/impl/DefaultObjectWrapperTest.java b/freemarker-core-test/src/test/java/org/apache/freemarker/core/model/impl/DefaultObjectWrapperTest.java
index dd1c841..9ac550c 100644
--- a/freemarker-core-test/src/test/java/org/apache/freemarker/core/model/impl/DefaultObjectWrapperTest.java
+++ b/freemarker-core-test/src/test/java/org/apache/freemarker/core/model/impl/DefaultObjectWrapperTest.java
@@ -41,18 +41,19 @@ import java.util.TreeSet;
 import java.util.Vector;
 
 import org.apache.freemarker.core.Configuration;
+import org.apache.freemarker.core.NonTemplateCallPlace;
 import org.apache.freemarker.core.Template;
 import org.apache.freemarker.core.TemplateException;
 import org.apache.freemarker.core.Version;
 import org.apache.freemarker.core._CoreAPI;
 import org.apache.freemarker.core.model.AdapterTemplateModel;
+import org.apache.freemarker.core.model.Constants;
 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.TemplateCollectionModelEx;
 import org.apache.freemarker.core.model.TemplateHashModel;
 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.apache.freemarker.core.model.TemplateModelIterator;
@@ -191,15 +192,16 @@ public class DefaultObjectWrapperTest {
         assertEquals(1, ow.unwrap(bean.get("x")));
         {
             // Check method calls, and also if the return value is wrapped with the overidden "wrap".
-            final TemplateModel mr = ((TemplateMethodModel) bean.get("m")).execute(
-                    Collections.<TemplateModel>emptyList());
+            final TemplateModel mr = ((JavaMethodModel) bean.get("m")).execute(
+                    Constants.EMPTY_TEMPLATE_MODEL_ARRAY, NonTemplateCallPlace.INSTANCE);
             assertEquals(Collections.singletonList(1), ow.unwrap(mr));
             assertTrue(DefaultListAdapter.class.isInstance(mr));
         }
         {
             // Check custom TM usage and round trip:
-            final TemplateModel mr = ((TemplateMethodModel) bean.get("incTupple"))
-                    .execute(Collections.singletonList(ow.wrap(new Tupple<>(1, 2))));
+            final TemplateModel mr = ((JavaMethodModel) bean.get("incTupple"))
+                    .execute(new TemplateModel[] { ow.wrap(new Tupple<>(1, 2)) },
+                            NonTemplateCallPlace.INSTANCE);
             assertEquals(new Tupple<>(2, 3), ow.unwrap(mr));
             assertTrue(TuppleAdapter.class.isInstance(mr));
         }
@@ -448,15 +450,15 @@ public class DefaultObjectWrapperTest {
         final TemplateHashModel testBeanTM = (TemplateHashModel) dow.wrap(new RoundtripTesterBean());
 
         {
-            TemplateMethodModel getClassM = (TemplateMethodModel) testBeanTM.get("getClass");
-            TemplateModel r = getClassM.execute(Collections.singletonList(objTM));
+            JavaMethodModel getClassM = (JavaMethodModel) testBeanTM.get("getClass");
+            TemplateModel r = getClassM.execute(new TemplateModel[] { objTM }, NonTemplateCallPlace.INSTANCE);
             final Class rClass = (Class) ((WrapperTemplateModel) r).getWrappedObject();
             assertThat(rClass, typeCompatibleWith(expectedPojoClass));
         }
 
         if (expectedPojoToString != null) {
-            TemplateMethodModel getToStringM = (TemplateMethodModel) testBeanTM.get("toString");
-            TemplateModel r = getToStringM.execute(Collections.singletonList(objTM));
+            JavaMethodModel getToStringM = (JavaMethodModel) testBeanTM.get("toString");
+            TemplateModel r = getToStringM.execute(new TemplateModel[] { objTM }, NonTemplateCallPlace.INSTANCE);
             assertEquals(expectedPojoToString, ((TemplateScalarModel) r).getAsString());
         }
     }
@@ -583,8 +585,9 @@ public class DefaultObjectWrapperTest {
         DefaultIteratorAdapter iteratorAdapter = (DefaultIteratorAdapter) wrappedIterator;
 
         TemplateHashModel api = (TemplateHashModel) iteratorAdapter.getAPI();
-        assertFalse(((TemplateBooleanModel) ((TemplateMethodModel)
-                api.get("hasNext")).execute(Collections.<TemplateModel>emptyList())).getAsBoolean());
+        assertFalse(((TemplateBooleanModel) ((JavaMethodModel)
+                api.get("hasNext")).execute(Constants.EMPTY_TEMPLATE_MODEL_ARRAY, NonTemplateCallPlace.INSTANCE))
+                .getAsBoolean());
     }
 
     @SuppressWarnings("boxing")
@@ -673,8 +676,8 @@ public class DefaultObjectWrapperTest {
         }
 
         TemplateHashModel api = (TemplateHashModel) enumAdapter.getAPI();
-        assertFalse(((TemplateBooleanModel) ((TemplateMethodModel) api.get("hasMoreElements"))
-                .execute(Collections.<TemplateModel>emptyList())).getAsBoolean());
+        assertFalse(((TemplateBooleanModel) ((JavaMethodModel) api.get("hasMoreElements"))
+                .execute(Constants.EMPTY_TEMPLATE_MODEL_ARRAY, NonTemplateCallPlace.INSTANCE)).getAsBoolean());
     }
 
     @Test
@@ -729,8 +732,9 @@ public class DefaultObjectWrapperTest {
             fail(); 
         }
         TemplateHashModel apiModel = (TemplateHashModel) ((TemplateModelWithAPISupport) normalModel).getAPI();
-        TemplateMethodModel sizeMethod = (TemplateMethodModel) apiModel.get("size");
-        TemplateNumberModel r = (TemplateNumberModel) sizeMethod.execute(Collections.<TemplateModel>emptyList());
+        JavaMethodModel sizeMethod = (JavaMethodModel) apiModel.get("size");
+        TemplateNumberModel r = (TemplateNumberModel) sizeMethod.execute(
+                Constants.EMPTY_TEMPLATE_MODEL_ARRAY, NonTemplateCallPlace.INSTANCE);
         assertEquals(expectedSize, r.getAsNumber().intValue());
     }
 

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/8d5263f2/freemarker-core-test/src/test/java/org/apache/freemarker/core/model/impl/EnumModelsTest.java
----------------------------------------------------------------------
diff --git a/freemarker-core-test/src/test/java/org/apache/freemarker/core/model/impl/EnumModelsTest.java b/freemarker-core-test/src/test/java/org/apache/freemarker/core/model/impl/EnumModelsTest.java
index 9373cf5..9336388 100644
--- a/freemarker-core-test/src/test/java/org/apache/freemarker/core/model/impl/EnumModelsTest.java
+++ b/freemarker-core-test/src/test/java/org/apache/freemarker/core/model/impl/EnumModelsTest.java
@@ -21,11 +21,10 @@ package org.apache.freemarker.core.model.impl;
 
 import static org.junit.Assert.*;
 
-import java.util.ArrayList;
-
 import org.apache.freemarker.core.Configuration;
+import org.apache.freemarker.core.NonTemplateCallPlace;
+import org.apache.freemarker.core.model.Constants;
 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.TemplateModelException;
 import org.apache.freemarker.core.model.TemplateScalarModel;
@@ -58,8 +57,10 @@ public class EnumModelsTest {
         assertTrue(a instanceof TemplateScalarModel);
         assertTrue(a instanceof TemplateHashModel);
         assertEquals(((TemplateScalarModel) a).getAsString(), "ts:A");
-        TemplateMethodModel nameMethod = (TemplateMethodModel) ((TemplateHashModel) a).get("name");
-        assertEquals(((TemplateScalarModel) nameMethod.execute(new ArrayList())).getAsString(), "A");
+        JavaMethodModel nameMethod = (JavaMethodModel) ((TemplateHashModel) a).get("name");
+        assertEquals(((TemplateScalarModel) nameMethod.execute(
+                Constants.EMPTY_TEMPLATE_MODEL_ARRAY, NonTemplateCallPlace.INSTANCE)).getAsString(),
+                "A");
         
         assertSame(e, enums.get(E.class.getName()));
         

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/8d5263f2/freemarker-core-test/src/test/java/org/apache/freemarker/core/model/impl/ErrorMessagesTest.java
----------------------------------------------------------------------
diff --git a/freemarker-core-test/src/test/java/org/apache/freemarker/core/model/impl/ErrorMessagesTest.java b/freemarker-core-test/src/test/java/org/apache/freemarker/core/model/impl/ErrorMessagesTest.java
index dcb8cf6..89b06be 100644
--- a/freemarker-core-test/src/test/java/org/apache/freemarker/core/model/impl/ErrorMessagesTest.java
+++ b/freemarker-core-test/src/test/java/org/apache/freemarker/core/model/impl/ErrorMessagesTest.java
@@ -22,12 +22,12 @@ package org.apache.freemarker.core.model.impl;
 import static org.hamcrest.Matchers.*;
 import static org.junit.Assert.*;
 
-import java.util.Collections;
 import java.util.Date;
 
 import org.apache.freemarker.core.Configuration;
+import org.apache.freemarker.core.NonTemplateCallPlace;
 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.TemplateModelException;
 import org.apache.freemarker.core.model.TemplateScalarModel;
 import org.apache.freemarker.core.outputformat.impl.HTMLOutputFormat;
@@ -60,9 +60,9 @@ public class ErrorMessagesTest {
         TemplateHashModel thm = (TemplateHashModel) ow.wrap(new TestBean());
         
         {
-            TemplateMethodModel m = (TemplateMethodModel) thm.get("m1");
+            JavaMethodModel m = (JavaMethodModel) thm.get("m1");
             try {
-                m.execute(Collections.singletonList(html));
+                m.execute(new TemplateModel[] { html }, NonTemplateCallPlace.INSTANCE);
                 fail();
             } catch (TemplateModelException e) {
                 assertThat(e.getMessage(), allOf(
@@ -72,9 +72,9 @@ public class ErrorMessagesTest {
         }
         
         {
-            TemplateMethodModel m = (TemplateMethodModel) thm.get("m2");
+            JavaMethodModel m = (JavaMethodModel) thm.get("m2");
             try {
-                m.execute(Collections.singletonList(html));
+                m.execute(new TemplateModel[] { html }, NonTemplateCallPlace.INSTANCE);
                 fail();
             } catch (TemplateModelException e) {
                 assertThat(e.getMessage(), allOf(
@@ -84,9 +84,9 @@ public class ErrorMessagesTest {
         }
         
         for (String methodName : new String[] { "mOverloaded", "mOverloaded3" }) {
-            TemplateMethodModel m = (TemplateMethodModel) thm.get(methodName);
+            JavaMethodModel m = (JavaMethodModel)thm.get(methodName);
             try {
-                m.execute(Collections.singletonList(html));
+                m.execute(new TemplateModel[] { html }, NonTemplateCallPlace.INSTANCE);
                 fail();
             } catch (TemplateModelException e) {
                 assertThat(e.getMessage(), allOf(
@@ -97,9 +97,9 @@ public class ErrorMessagesTest {
         }
         
         {
-            TemplateMethodModel m = (TemplateMethodModel) thm.get("mOverloaded2");
+            JavaMethodModel m = (JavaMethodModel)thm.get("mOverloaded2");
             try {
-                m.execute(Collections.singletonList(html));
+                m.execute(new TemplateModel[] { html }, NonTemplateCallPlace.INSTANCE);
                 fail();
             } catch (TemplateModelException e) {
                 assertThat(e.getMessage(), allOf(
@@ -110,8 +110,8 @@ public class ErrorMessagesTest {
         }
         
         {
-            TemplateMethodModel m = (TemplateMethodModel) thm.get("mOverloaded4");
-            Object r = m.execute(Collections.singletonList(html));
+            JavaMethodModel m = (JavaMethodModel)thm.get("mOverloaded4");
+            Object r = m.execute(new TemplateModel[] { html }, NonTemplateCallPlace.INSTANCE);
             if (r instanceof TemplateScalarModel) {
                 r = ((TemplateScalarModel) r).getAsString();
             }

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/8d5263f2/freemarker-core-test/src/test/java/org/apache/freemarker/core/model/impl/FineTuneMethodAppearanceTest.java
----------------------------------------------------------------------
diff --git a/freemarker-core-test/src/test/java/org/apache/freemarker/core/model/impl/FineTuneMethodAppearanceTest.java b/freemarker-core-test/src/test/java/org/apache/freemarker/core/model/impl/FineTuneMethodAppearanceTest.java
index eb4d807..e5366f6 100644
--- a/freemarker-core-test/src/test/java/org/apache/freemarker/core/model/impl/FineTuneMethodAppearanceTest.java
+++ b/freemarker-core-test/src/test/java/org/apache/freemarker/core/model/impl/FineTuneMethodAppearanceTest.java
@@ -23,7 +23,6 @@ import static org.junit.Assert.*;
 
 import org.apache.freemarker.core.Configuration;
 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.TemplateModelException;
 import org.apache.freemarker.core.model.TemplateScalarModel;
@@ -48,7 +47,7 @@ public class FineTuneMethodAppearanceTest {
         assertEquals("v1", ((TemplateScalarModel) thm.get("v1")).getAsString());
         assertEquals("v2()", ((TemplateScalarModel) thm.get("v2")).getAsString());
         assertEquals("getV3()", ((TemplateScalarModel) thm.get("v3")).getAsString());
-        assertTrue(thm.get("getV3") instanceof TemplateMethodModel);
+        assertTrue(thm.get("getV3") instanceof JavaMethodModel);
     }
     
     static public class C {

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/8d5263f2/freemarker-core-test/src/test/java/org/apache/freemarker/core/model/impl/StaticModelsTest.java
----------------------------------------------------------------------
diff --git a/freemarker-core-test/src/test/java/org/apache/freemarker/core/model/impl/StaticModelsTest.java b/freemarker-core-test/src/test/java/org/apache/freemarker/core/model/impl/StaticModelsTest.java
index 44da42a..f888b2c 100644
--- a/freemarker-core-test/src/test/java/org/apache/freemarker/core/model/impl/StaticModelsTest.java
+++ b/freemarker-core-test/src/test/java/org/apache/freemarker/core/model/impl/StaticModelsTest.java
@@ -22,11 +22,10 @@ package org.apache.freemarker.core.model.impl;
 import static org.hamcrest.Matchers.*;
 import static org.junit.Assert.*;
 
-import java.util.ArrayList;
-
 import org.apache.freemarker.core.Configuration;
+import org.apache.freemarker.core.NonTemplateCallPlace;
+import org.apache.freemarker.core.model.Constants;
 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.TemplateModelException;
 import org.apache.freemarker.core.model.TemplateScalarModel;
@@ -65,8 +64,10 @@ public class StaticModelsTest {
         assertEquals(((TemplateScalarModel) f).getAsString(), "F OK");
         
         TemplateModel m = s.get("m");
-        assertTrue(m instanceof TemplateMethodModel);
-        assertEquals(((TemplateScalarModel) ((TemplateMethodModel) m).execute(new ArrayList())).getAsString(), "m OK");
+        assertTrue(m instanceof JavaMethodModel);
+        assertEquals(((TemplateScalarModel) ((JavaMethodModel) m).execute(
+                Constants.EMPTY_TEMPLATE_MODEL_ARRAY, NonTemplateCallPlace.INSTANCE)).getAsString(),
+                "m OK");
         
         assertSame(s, statics.get(S.class.getName()));
         

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/8d5263f2/freemarker-core-test/src/test/java/org/apache/freemarker/core/templatesuite/CoreTemplateTestSuite.java
----------------------------------------------------------------------
diff --git a/freemarker-core-test/src/test/java/org/apache/freemarker/core/templatesuite/CoreTemplateTestSuite.java b/freemarker-core-test/src/test/java/org/apache/freemarker/core/templatesuite/CoreTemplateTestSuite.java
index 0b21c75..6beb834 100644
--- a/freemarker-core-test/src/test/java/org/apache/freemarker/core/templatesuite/CoreTemplateTestSuite.java
+++ b/freemarker-core-test/src/test/java/org/apache/freemarker/core/templatesuite/CoreTemplateTestSuite.java
@@ -36,9 +36,9 @@ import org.apache.freemarker.core.templatesuite.models.JavaObjectInfo;
 import org.apache.freemarker.core.templatesuite.models.Listables;
 import org.apache.freemarker.core.templatesuite.models.MultiModel1;
 import org.apache.freemarker.core.templatesuite.models.OverloadedMethods2;
+import org.apache.freemarker.core.templatesuite.models.SimpleTestFunction;
 import org.apache.freemarker.core.templatesuite.models.TestBean;
 import org.apache.freemarker.core.templatesuite.models.TestBoolean;
-import org.apache.freemarker.core.templatesuite.models.TestMethod;
 import org.apache.freemarker.core.templatesuite.models.TestNode;
 import org.apache.freemarker.core.templatesuite.models.VarArgTestModel;
 import org.apache.freemarker.test.TemplateTestSuite;
@@ -158,7 +158,7 @@ public class CoreTemplateTestSuite extends TemplateTestSuite {
         } else if (simpleTestName.equals("stringbimethods")) {
             dataModel.put("multi", new TestBoolean());
         } else if (simpleTestName.startsWith("type-builtins")) {
-            dataModel.put("testmethod", new TestMethod());
+            dataModel.put("testfunction", new SimpleTestFunction());
             dataModel.put("testnode", new TestNode());
             dataModel.put("testcollection", new SimpleCollection(new ArrayList<>(), dow));
             dataModel.put("testcollectionEx", DefaultNonListCollectionAdapter.adapt(new HashSet<>(), dow));

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/8d5263f2/freemarker-core-test/src/test/java/org/apache/freemarker/core/templatesuite/models/MultiModel2.java
----------------------------------------------------------------------
diff --git a/freemarker-core-test/src/test/java/org/apache/freemarker/core/templatesuite/models/MultiModel2.java b/freemarker-core-test/src/test/java/org/apache/freemarker/core/templatesuite/models/MultiModel2.java
index 3223652..ad4cbe3 100644
--- a/freemarker-core-test/src/test/java/org/apache/freemarker/core/templatesuite/models/MultiModel2.java
+++ b/freemarker-core-test/src/test/java/org/apache/freemarker/core/templatesuite/models/MultiModel2.java
@@ -19,11 +19,12 @@
 
 package org.apache.freemarker.core.templatesuite.models;
 
-import java.util.List;
-
+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.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.TemplateScalarModel;
 import org.apache.freemarker.core.model.impl.SimpleScalar;
@@ -31,34 +32,26 @@ import org.apache.freemarker.core.model.impl.SimpleScalar;
 /**
  * Testcase to see how FreeMarker deals with multiple Template models.
  */
-public class MultiModel2 implements TemplateScalarModel, TemplateMethodModel {
+public class MultiModel2 implements TemplateScalarModel, TemplateFunctionModel {
 
-    /**
-     * Returns the scalar's value as a String.
-     *
-     * @return the String value of this scalar.
-     */
     @Override
     public String getAsString() {
         return "Model2 is alive!";
     }
 
-    /**
-     * Executes a method call.
-     *
-     * @param args a <tt>List</tt> of <tt>String</tt> objects containing the values
-     * of the arguments passed to the method.
-     * @return the <tt>TemplateModel</tt> produced by the method, or null.
-     */
     @Override
-    public TemplateModel execute(List<? extends TemplateModel> args) throws TemplateException {
+    public TemplateModel execute(TemplateModel[] args, CallPlace callPlace, Environment env) throws TemplateException {
         StringBuilder  aResults = new StringBuilder( "Arguments are:<br />" );
-        for (int i = 0; i < args.size(); i++) {
-            TemplateModel arg = args.get(i);
-            aResults.append(_CallableUtils.castArgToString(arg, i));
+        for (int i = 0; i < args.length; i++) {
+            aResults.append(_CallableUtils.castArgToString(args, i));
             aResults.append("<br />");
         }
 
         return new SimpleScalar( aResults.toString() );
     }
+
+    @Override
+    public ArgumentArrayLayout getFunctionArgumentArrayLayout() {
+        return null;
+    }
 }

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/8d5263f2/freemarker-core-test/src/test/java/org/apache/freemarker/core/templatesuite/models/SimpleTestFunction.java
----------------------------------------------------------------------
diff --git a/freemarker-core-test/src/test/java/org/apache/freemarker/core/templatesuite/models/SimpleTestFunction.java b/freemarker-core-test/src/test/java/org/apache/freemarker/core/templatesuite/models/SimpleTestFunction.java
new file mode 100644
index 0000000..0f6f579
--- /dev/null
+++ b/freemarker-core-test/src/test/java/org/apache/freemarker/core/templatesuite/models/SimpleTestFunction.java
@@ -0,0 +1,44 @@
+/*
+ * 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.templatesuite.models;
+
+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.impl.SimpleScalar;
+
+/**
+ * A simple method model used as a test bed.
+ */
+public class SimpleTestFunction implements TemplateFunctionModel {
+
+    @Override
+    public TemplateModel execute(TemplateModel[] args, CallPlace callPlace, Environment env) throws TemplateException {
+        return new SimpleScalar("Argument value is: " + args[0]);
+    }
+
+    @Override
+    public ArgumentArrayLayout getFunctionArgumentArrayLayout() {
+        return ArgumentArrayLayout.SINGLE_POSITIONAL_PARAMETER;
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/8d5263f2/freemarker-core-test/src/test/java/org/apache/freemarker/core/templatesuite/models/SimpleTestMethod.java
----------------------------------------------------------------------
diff --git a/freemarker-core-test/src/test/java/org/apache/freemarker/core/templatesuite/models/SimpleTestMethod.java b/freemarker-core-test/src/test/java/org/apache/freemarker/core/templatesuite/models/SimpleTestMethod.java
deleted file mode 100644
index 48352a0..0000000
--- a/freemarker-core-test/src/test/java/org/apache/freemarker/core/templatesuite/models/SimpleTestMethod.java
+++ /dev/null
@@ -1,50 +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.templatesuite.models;
-
-import java.util.List;
-
-import org.apache.freemarker.core.model.TemplateMethodModel;
-import org.apache.freemarker.core.model.TemplateModel;
-import org.apache.freemarker.core.model.impl.SimpleScalar;
-
-/**
- * A simple method model used as a test bed.
- */
-public class SimpleTestMethod implements TemplateMethodModel {
-
-    /**
-     * Executes a method call.
-     *
-     * @param args a <tt>List</tt> of <tt>String</tt> objects containing
-     * the values of the arguments passed to the method.
-     * @return the <tt>TemplateModel</tt> produced by the method, or null.
-     */
-    @Override
-    public TemplateModel execute(List<? extends TemplateModel> args) {
-        if ( args.size() == 0 ) {
-            return new SimpleScalar( "Empty list provided" );
-        } else if ( args.size() > 1 ) {
-            return new SimpleScalar( "Argument size is: " + args.size() );
-        } else {
-            return new SimpleScalar( "Single argument value is: " + args.get(0) );
-        }
-    }
-}

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/8d5263f2/freemarker-core-test/src/test/java/org/apache/freemarker/core/templatesuite/models/TestMethod.java
----------------------------------------------------------------------
diff --git a/freemarker-core-test/src/test/java/org/apache/freemarker/core/templatesuite/models/TestMethod.java b/freemarker-core-test/src/test/java/org/apache/freemarker/core/templatesuite/models/TestMethod.java
deleted file mode 100644
index ed97f9e..0000000
--- a/freemarker-core-test/src/test/java/org/apache/freemarker/core/templatesuite/models/TestMethod.java
+++ /dev/null
@@ -1,16 +0,0 @@
-package org.apache.freemarker.core.templatesuite.models;
-
-import java.util.List;
-
-import org.apache.freemarker.core.model.TemplateMethodModel;
-import org.apache.freemarker.core.model.TemplateModel;
-import org.apache.freemarker.core.model.impl.SimpleScalar;
-
-public class TestMethod implements TemplateMethodModel {
-
-    @Override
-    public TemplateModel execute(List<? extends TemplateModel> args) {
-        return new SimpleScalar("x");
-    }
-
-}

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/8d5263f2/freemarker-core-test/src/test/java/org/apache/freemarker/core/userpkg/AllFeaturesDirective.java
----------------------------------------------------------------------
diff --git a/freemarker-core-test/src/test/java/org/apache/freemarker/core/userpkg/AllFeaturesDirective.java b/freemarker-core-test/src/test/java/org/apache/freemarker/core/userpkg/AllFeaturesDirective.java
index d9bb394..27a42d9 100644
--- a/freemarker-core-test/src/test/java/org/apache/freemarker/core/userpkg/AllFeaturesDirective.java
+++ b/freemarker-core-test/src/test/java/org/apache/freemarker/core/userpkg/AllFeaturesDirective.java
@@ -122,7 +122,7 @@ public class AllFeaturesDirective extends TestTemplateCallableModel implements T
     }
 
     @Override
-    public ArgumentArrayLayout getArgumentArrayLayout() {
+    public ArgumentArrayLayout getDirectiveArgumentArrayLayout() {
         return ARGS_LAYOUT;
     }
 

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/8d5263f2/freemarker-core-test/src/test/java/org/apache/freemarker/core/userpkg/AllFeaturesFunction.java
----------------------------------------------------------------------
diff --git a/freemarker-core-test/src/test/java/org/apache/freemarker/core/userpkg/AllFeaturesFunction.java b/freemarker-core-test/src/test/java/org/apache/freemarker/core/userpkg/AllFeaturesFunction.java
index 5825745..f468be7 100644
--- a/freemarker-core-test/src/test/java/org/apache/freemarker/core/userpkg/AllFeaturesFunction.java
+++ b/freemarker-core-test/src/test/java/org/apache/freemarker/core/userpkg/AllFeaturesFunction.java
@@ -105,7 +105,7 @@ public class AllFeaturesFunction extends TestTemplateCallableModel implements Te
     }
 
     @Override
-    public ArgumentArrayLayout getArgumentArrayLayout() {
+    public ArgumentArrayLayout getFunctionArgumentArrayLayout() {
         return ARGS_LAYOUT;
     }
 

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/8d5263f2/freemarker-core-test/src/test/java/org/apache/freemarker/core/userpkg/NamedVarargsOnlyDirective.java
----------------------------------------------------------------------
diff --git a/freemarker-core-test/src/test/java/org/apache/freemarker/core/userpkg/NamedVarargsOnlyDirective.java b/freemarker-core-test/src/test/java/org/apache/freemarker/core/userpkg/NamedVarargsOnlyDirective.java
index a087188..268c7fc 100644
--- a/freemarker-core-test/src/test/java/org/apache/freemarker/core/userpkg/NamedVarargsOnlyDirective.java
+++ b/freemarker-core-test/src/test/java/org/apache/freemarker/core/userpkg/NamedVarargsOnlyDirective.java
@@ -52,7 +52,7 @@ public class NamedVarargsOnlyDirective extends TestTemplateCallableModel impleme
     }
 
     @Override
-    public ArgumentArrayLayout getArgumentArrayLayout() {
+    public ArgumentArrayLayout getDirectiveArgumentArrayLayout() {
         return ARGS_LAYOUT;
     }
 

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/8d5263f2/freemarker-core-test/src/test/java/org/apache/freemarker/core/userpkg/PositionalVarargsOnlyDirective.java
----------------------------------------------------------------------
diff --git a/freemarker-core-test/src/test/java/org/apache/freemarker/core/userpkg/PositionalVarargsOnlyDirective.java b/freemarker-core-test/src/test/java/org/apache/freemarker/core/userpkg/PositionalVarargsOnlyDirective.java
index 2f9532e..9bccfe5 100644
--- a/freemarker-core-test/src/test/java/org/apache/freemarker/core/userpkg/PositionalVarargsOnlyDirective.java
+++ b/freemarker-core-test/src/test/java/org/apache/freemarker/core/userpkg/PositionalVarargsOnlyDirective.java
@@ -46,7 +46,7 @@ public class PositionalVarargsOnlyDirective extends TestTemplateCallableModel im
     }
 
     @Override
-    public ArgumentArrayLayout getArgumentArrayLayout() {
+    public ArgumentArrayLayout getDirectiveArgumentArrayLayout() {
         return ArgumentArrayLayout.POSITIONAL_VARARGS_PARAMETER_ONLY;
     }
 

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/8d5263f2/freemarker-core-test/src/test/java/org/apache/freemarker/core/userpkg/PositionalVarargsOnlyFunction.java
----------------------------------------------------------------------
diff --git a/freemarker-core-test/src/test/java/org/apache/freemarker/core/userpkg/PositionalVarargsOnlyFunction.java b/freemarker-core-test/src/test/java/org/apache/freemarker/core/userpkg/PositionalVarargsOnlyFunction.java
index eb8a3eb..1c0c82c 100644
--- a/freemarker-core-test/src/test/java/org/apache/freemarker/core/userpkg/PositionalVarargsOnlyFunction.java
+++ b/freemarker-core-test/src/test/java/org/apache/freemarker/core/userpkg/PositionalVarargsOnlyFunction.java
@@ -56,7 +56,7 @@ public class PositionalVarargsOnlyFunction extends TestTemplateCallableModel imp
     }
 
     @Override
-    public ArgumentArrayLayout getArgumentArrayLayout() {
+    public ArgumentArrayLayout getFunctionArgumentArrayLayout() {
         return ARGS_LAYOUT;
     }
 }

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/8d5263f2/freemarker-core-test/src/test/java/org/apache/freemarker/core/userpkg/TwoNamedParamsDirective.java
----------------------------------------------------------------------
diff --git a/freemarker-core-test/src/test/java/org/apache/freemarker/core/userpkg/TwoNamedParamsDirective.java b/freemarker-core-test/src/test/java/org/apache/freemarker/core/userpkg/TwoNamedParamsDirective.java
index 83b5435..3b3ed1c 100644
--- a/freemarker-core-test/src/test/java/org/apache/freemarker/core/userpkg/TwoNamedParamsDirective.java
+++ b/freemarker-core-test/src/test/java/org/apache/freemarker/core/userpkg/TwoNamedParamsDirective.java
@@ -62,7 +62,7 @@ public class TwoNamedParamsDirective extends TestTemplateCallableModel implement
     }
 
     @Override
-    public ArgumentArrayLayout getArgumentArrayLayout() {
+    public ArgumentArrayLayout getDirectiveArgumentArrayLayout() {
         return ARGS_LAYOUT;
     }
 

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/8d5263f2/freemarker-core-test/src/test/java/org/apache/freemarker/core/userpkg/TwoNestedContentParamsDirective.java
----------------------------------------------------------------------
diff --git a/freemarker-core-test/src/test/java/org/apache/freemarker/core/userpkg/TwoNestedContentParamsDirective.java b/freemarker-core-test/src/test/java/org/apache/freemarker/core/userpkg/TwoNestedContentParamsDirective.java
index 6f768f4..e2d7ffb 100644
--- a/freemarker-core-test/src/test/java/org/apache/freemarker/core/userpkg/TwoNestedContentParamsDirective.java
+++ b/freemarker-core-test/src/test/java/org/apache/freemarker/core/userpkg/TwoNestedContentParamsDirective.java
@@ -48,7 +48,7 @@ public class TwoNestedContentParamsDirective extends TestTemplateCallableModel i
     }
 
     @Override
-    public ArgumentArrayLayout getArgumentArrayLayout() {
+    public ArgumentArrayLayout getDirectiveArgumentArrayLayout() {
         return ArgumentArrayLayout.PARAMETERLESS;
     }
 

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/8d5263f2/freemarker-core-test/src/test/java/org/apache/freemarker/core/userpkg/TwoPositionalParamsDirective.java
----------------------------------------------------------------------
diff --git a/freemarker-core-test/src/test/java/org/apache/freemarker/core/userpkg/TwoPositionalParamsDirective.java b/freemarker-core-test/src/test/java/org/apache/freemarker/core/userpkg/TwoPositionalParamsDirective.java
index 008d8c8..a057bc3 100644
--- a/freemarker-core-test/src/test/java/org/apache/freemarker/core/userpkg/TwoPositionalParamsDirective.java
+++ b/freemarker-core-test/src/test/java/org/apache/freemarker/core/userpkg/TwoPositionalParamsDirective.java
@@ -51,7 +51,7 @@ public class TwoPositionalParamsDirective extends TestTemplateCallableModel impl
     }
 
     @Override
-    public ArgumentArrayLayout getArgumentArrayLayout() {
+    public ArgumentArrayLayout getDirectiveArgumentArrayLayout() {
         return ARGS_LAYOUT;
     }
 

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/8d5263f2/freemarker-core-test/src/test/java/org/apache/freemarker/core/userpkg/TwoPositionalParamsFunction.java
----------------------------------------------------------------------
diff --git a/freemarker-core-test/src/test/java/org/apache/freemarker/core/userpkg/TwoPositionalParamsFunction.java b/freemarker-core-test/src/test/java/org/apache/freemarker/core/userpkg/TwoPositionalParamsFunction.java
index 58e292b..e406c99 100644
--- a/freemarker-core-test/src/test/java/org/apache/freemarker/core/userpkg/TwoPositionalParamsFunction.java
+++ b/freemarker-core-test/src/test/java/org/apache/freemarker/core/userpkg/TwoPositionalParamsFunction.java
@@ -57,7 +57,7 @@ public class TwoPositionalParamsFunction extends TestTemplateCallableModel imple
     }
 
     @Override
-    public ArgumentArrayLayout getArgumentArrayLayout() {
+    public ArgumentArrayLayout getFunctionArgumentArrayLayout() {
         return ARGS_LAYOUT;
     }
 }

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/8d5263f2/freemarker-core-test/src/test/java/org/apache/freemarker/core/userpkg/UpperCaseDirective.java
----------------------------------------------------------------------
diff --git a/freemarker-core-test/src/test/java/org/apache/freemarker/core/userpkg/UpperCaseDirective.java b/freemarker-core-test/src/test/java/org/apache/freemarker/core/userpkg/UpperCaseDirective.java
index 05e5d6d..3936502 100644
--- a/freemarker-core-test/src/test/java/org/apache/freemarker/core/userpkg/UpperCaseDirective.java
+++ b/freemarker-core-test/src/test/java/org/apache/freemarker/core/userpkg/UpperCaseDirective.java
@@ -47,7 +47,7 @@ public class UpperCaseDirective implements TemplateDirectiveModel {
     }
 
     @Override
-    public ArgumentArrayLayout getArgumentArrayLayout() {
+    public ArgumentArrayLayout getDirectiveArgumentArrayLayout() {
         return ArgumentArrayLayout.PARAMETERLESS;
     }
 

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/8d5263f2/freemarker-core-test/src/test/java/org/apache/freemarker/core/valueformat/NumberFormatTest.java
----------------------------------------------------------------------
diff --git a/freemarker-core-test/src/test/java/org/apache/freemarker/core/valueformat/NumberFormatTest.java b/freemarker-core-test/src/test/java/org/apache/freemarker/core/valueformat/NumberFormatTest.java
index 9bdea77..685ce5a 100644
--- a/freemarker-core-test/src/test/java/org/apache/freemarker/core/valueformat/NumberFormatTest.java
+++ b/freemarker-core-test/src/test/java/org/apache/freemarker/core/valueformat/NumberFormatTest.java
@@ -186,7 +186,7 @@ public class NumberFormatTest extends TemplateTest {
             }
 
             @Override
-            public ArgumentArrayLayout getArgumentArrayLayout() {
+            public ArgumentArrayLayout getDirectiveArgumentArrayLayout() {
                 return ArgumentArrayLayout.PARAMETERLESS;
             }
 

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/8d5263f2/freemarker-core-test/src/test/resources/org/apache/freemarker/core/ast-builtins.ast
----------------------------------------------------------------------
diff --git a/freemarker-core-test/src/test/resources/org/apache/freemarker/core/ast-builtins.ast b/freemarker-core-test/src/test/resources/org/apache/freemarker/core/ast-builtins.ast
index 538dd05..0b0a8f8 100644
--- a/freemarker-core-test/src/test/resources/org/apache/freemarker/core/ast-builtins.ast
+++ b/freemarker-core-test/src/test/resources/org/apache/freemarker/core/ast-builtins.ast
@@ -24,7 +24,7 @@
     #text  // o.a.f.c.ASTStaticText
         - content: "\n"  // String
     ${...}  // o.a.f.c.ASTDollarInterpolation
-        - content: ...(...)  // o.a.f.c.ASTExpMethodCall
+        - content: ...(...)  // o.a.f.c.ASTExpFunctionCall
             - callee: ?leftPad  // o.a.f.c.BuiltInsForStringsBasic$padBI
                 - left-hand operand: x  // o.a.f.c.ASTExpVariable
                 - right-hand operand: "leftPad"  // String
@@ -32,7 +32,7 @@
     #text  // o.a.f.c.ASTStaticText
         - content: "\n"  // String
     ${...}  // o.a.f.c.ASTDollarInterpolation
-        - content: ...(...)  // o.a.f.c.ASTExpMethodCall
+        - content: ...(...)  // o.a.f.c.ASTExpFunctionCall
             - callee: ?leftPad  // o.a.f.c.BuiltInsForStringsBasic$padBI
                 - left-hand operand: x  // o.a.f.c.ASTExpVariable
                 - right-hand operand: "leftPad"  // String

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/8d5263f2/freemarker-core-test/src/test/resources/org/apache/freemarker/core/ast-range.ast
----------------------------------------------------------------------
diff --git a/freemarker-core-test/src/test/resources/org/apache/freemarker/core/ast-range.ast b/freemarker-core-test/src/test/resources/org/apache/freemarker/core/ast-range.ast
index 9d9bb4b..3d66e58 100644
--- a/freemarker-core-test/src/test/resources/org/apache/freemarker/core/ast-range.ast
+++ b/freemarker-core-test/src/test/resources/org/apache/freemarker/core/ast-range.ast
@@ -115,12 +115,12 @@
         - assignment target: "x"  // String
         - assignment operator: "="  // String
         - assignment source: ..  // o.a.f.c.ASTExpRange
-            - left-hand operand: ...(...)  // o.a.f.c.ASTExpMethodCall
+            - left-hand operand: ...(...)  // o.a.f.c.ASTExpFunctionCall
                 - callee: ?indexOf  // o.a.f.c.BuiltInsForStringsBasic$index_ofBI
                     - left-hand operand: n  // o.a.f.c.ASTExpVariable
                     - right-hand operand: "indexOf"  // String
                 - argument value: "x"  // o.a.f.c.ASTExpStringLiteral
-            - right-hand operand: ...(...)  // o.a.f.c.ASTExpMethodCall
+            - right-hand operand: ...(...)  // o.a.f.c.ASTExpFunctionCall
                 - callee: ?indexOf  // o.a.f.c.BuiltInsForStringsBasic$index_ofBI
                     - left-hand operand: m  // o.a.f.c.ASTExpVariable
                     - right-hand operand: "indexOf"  // String
@@ -236,7 +236,7 @@
     #text  // o.a.f.c.ASTStaticText
         - content: "\n"  // String
     ${...}  // o.a.f.c.ASTDollarInterpolation
-        - content: ...(...)  // o.a.f.c.ASTExpMethodCall
+        - content: ...(...)  // o.a.f.c.ASTExpFunctionCall
             - callee: f  // o.a.f.c.ASTExpVariable
             - argument value: ..  // o.a.f.c.ASTExpRange
                 - left-hand operand: m  // o.a.f.c.ASTExpVariable

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/8d5263f2/freemarker-core-test/src/test/resources/org/apache/freemarker/core/templatesuite/expected/number-literal.txt
----------------------------------------------------------------------
diff --git a/freemarker-core-test/src/test/resources/org/apache/freemarker/core/templatesuite/expected/number-literal.txt b/freemarker-core-test/src/test/resources/org/apache/freemarker/core/templatesuite/expected/number-literal.txt
index b80bc85..18311bd 100644
--- a/freemarker-core-test/src/test/resources/org/apache/freemarker/core/templatesuite/expected/number-literal.txt
+++ b/freemarker-core-test/src/test/resources/org/apache/freemarker/core/templatesuite/expected/number-literal.txt
@@ -69,11 +69,11 @@ twelve
 
 <p>Numbers in method calls:</p>
 
-Single argument value is: 1
-Single argument value is: 1
-Single argument value is: 12
-Single argument value is: 3
-Single argument value is: 3
+Argument value is: 1
+Argument value is: 1
+Argument value is: 12
+Argument value is: 3
+Argument value is: 3
 
 </body>
 </html>

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/8d5263f2/freemarker-core-test/src/test/resources/org/apache/freemarker/core/templatesuite/expected/type-builtins.txt
----------------------------------------------------------------------
diff --git a/freemarker-core-test/src/test/resources/org/apache/freemarker/core/templatesuite/expected/type-builtins.txt b/freemarker-core-test/src/test/resources/org/apache/freemarker/core/templatesuite/expected/type-builtins.txt
index eeb191e..4d48327 100644
--- a/freemarker-core-test/src/test/resources/org/apache/freemarker/core/templatesuite/expected/type-builtins.txt
+++ b/freemarker-core-test/src/test/resources/org/apache/freemarker/core/templatesuite/expected/type-builtins.txt
@@ -16,17 +16,17 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-StNuBoMeMaHaHxSeCoCxEnInDiNo
-1 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 1 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 1 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 1 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 1 0 0 0 0 0 0 0 1 0
-0 0 0 0 0 1 1 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 1 0 0 1 1 0 0
-0 0 0 0 0 0 0 0 1 0 1 0 0 0
-0 0 0 0 0 0 0 0 1 1 1 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 1
-1 0 0 0 0 1 1 0 0 0 0 0 0 0
-0 0 0 1 0 0 0 0 0 0 0 0 0 0
-0 0 0 1 0 0 0 0 0 0 0 0 0 0
+StNuBoHaHxSeCoCxEnInFuDiNo
+1 0 0 0 0 0 0 0 0 0 0 0 0
+0 1 0 0 0 0 0 0 0 0 0 0 0
+0 0 1 0 0 0 0 0 0 0 0 0 0
+0 0 0 0 0 0 0 0 0 0 1 0 0
+0 0 0 0 0 0 0 0 0 0 0 1 0
+0 0 0 1 1 0 0 0 0 0 0 0 0
+0 0 0 0 0 1 0 0 1 1 0 0 0
+0 0 0 0 0 0 1 0 1 0 0 0 0
+0 0 0 0 0 0 1 1 1 0 0 0 0
+0 0 0 0 0 0 0 0 0 0 0 0 1
+1 0 0 1 1 0 0 0 0 0 0 0 0
+0 0 0 0 0 0 0 0 0 0 1 0 0
+0 0 0 0 0 0 0 0 0 0 1 0 0

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/8d5263f2/freemarker-core-test/src/test/resources/org/apache/freemarker/core/templatesuite/templates/list2.ftl
----------------------------------------------------------------------
diff --git a/freemarker-core-test/src/test/resources/org/apache/freemarker/core/templatesuite/templates/list2.ftl b/freemarker-core-test/src/test/resources/org/apache/freemarker/core/templatesuite/templates/list2.ftl
index 9893c1b..7d563ed 100644
--- a/freemarker-core-test/src/test/resources/org/apache/freemarker/core/templatesuite/templates/list2.ftl
+++ b/freemarker-core-test/src/test/resources/org/apache/freemarker/core/templatesuite/templates/list2.ftl
@@ -82,7 +82,7 @@
 
 <#function resolve(xs)>
     <#assign resolveCallCnt = (resolveCallCnt!0) + 1>
-    <#if xs?isMethod>
+    <#if xs?isFunction>
         <#return xs()>
     <#else>
         <#return xs>

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/8d5263f2/freemarker-core-test/src/test/resources/org/apache/freemarker/core/templatesuite/templates/number-literal.ftl
----------------------------------------------------------------------
diff --git a/freemarker-core-test/src/test/resources/org/apache/freemarker/core/templatesuite/templates/number-literal.ftl b/freemarker-core-test/src/test/resources/org/apache/freemarker/core/templatesuite/templates/number-literal.ftl
index 334841d..348d33b 100644
--- a/freemarker-core-test/src/test/resources/org/apache/freemarker/core/templatesuite/templates/number-literal.ftl
+++ b/freemarker-core-test/src/test/resources/org/apache/freemarker/core/templatesuite/templates/number-literal.ftl
@@ -44,7 +44,7 @@
           "12" = "twelve",
           "2one" = "two-one",
           one2 = "one-two",
-          call = "org.apache.freemarker.core.templatesuite.models.SimpleTestMethod"?new()
+          call = "org.apache.freemarker.core.templatesuite.models.SimpleTestFunction"?new()
 >
 
 <p>A simple test follows:</p>

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/8d5263f2/freemarker-core-test/src/test/resources/org/apache/freemarker/core/templatesuite/templates/string-builtins3.ftl
----------------------------------------------------------------------
diff --git a/freemarker-core-test/src/test/resources/org/apache/freemarker/core/templatesuite/templates/string-builtins3.ftl b/freemarker-core-test/src/test/resources/org/apache/freemarker/core/templatesuite/templates/string-builtins3.ftl
index 6442c63..ece74f9 100644
--- a/freemarker-core-test/src/test/resources/org/apache/freemarker/core/templatesuite/templates/string-builtins3.ftl
+++ b/freemarker-core-test/src/test/resources/org/apache/freemarker/core/templatesuite/templates/string-builtins3.ftl
@@ -35,10 +35,10 @@
 <@assertFails message='"m" flag'>
     ${'x'?keepBefore('x', 'm')}
 </...@assertFails>
-<@assertFails message='3'>
+<@assertFails message='Too many arguments'>
     ${'x'?keepBefore('x', 'i', 'x')}
 </...@assertFails>
-<@assertFails message='none'>
+<@assertFails message='argument'>
     ${'x'?keepBefore()}
 </...@assertFails>
 
@@ -61,10 +61,10 @@
 <@assertFails message='"m" flag'>
     ${'x'?keepBeforeLast('x', 'm')}
 </...@assertFails>
-<@assertFails message='3'>
+<@assertFails message='Too many arguments'>
     ${'x'?keepBeforeLast('x', 'i', 'x')}
 </...@assertFails>
-<@assertFails message='none'>
+<@assertFails message='argument'>
     ${'x'?keepBeforeLast()}
 </...@assertFails>
 
@@ -87,10 +87,10 @@
 <@assertFails message='"m" flag'>
     ${'x'?keepAfter('x', 'm')}
 </...@assertFails>
-<@assertFails message='3'>
+<@assertFails message='Too many arguments'>
     ${'x'?keepAfter('x', 'i', 'x')}
 </...@assertFails>
-<@assertFails message='none'>
+<@assertFails message='argument'>
     ${'x'?keepAfter()}
 </...@assertFails>
 
@@ -115,10 +115,10 @@
 <@assertFails message='"m" flag'>
     ${'x'?keepAfterLast('x', 'm')}
 </...@assertFails>
-<@assertFails message='3'>
+<@assertFails message='Too many arguments'>
     ${'x'?keepAfterLast('x', 'i', 'x')}
 </...@assertFails>
-<@assertFails message='none'>
+<@assertFails message='argument'>
     ${'x'?keepAfterLast()}
 </...@assertFails>
 
@@ -129,10 +129,10 @@
 <@assertEquals expected='o' actual='foo'?removeBeginning('fo') />
 <@assertEquals expected='' actual='foo'?removeBeginning('foo') />
 <@assertEquals expected='foo' actual='foo'?removeBeginning('') />
-<@assertFails message='2'>
+<@assertFails message='Too many arguments'>
     ${'x'?removeBeginning('x', 'x')}
 </...@assertFails>
-<@assertFails message='none'>
+<@assertFails message='argument'>
     ${'x'?removeBeginning()}
 </...@assertFails>
 
@@ -143,10 +143,10 @@
 <@assertEquals expected='b' actual='bar'?removeEnding('ar') />
 <@assertEquals expected='' actual='bar'?removeEnding('bar') />
 <@assertEquals expected='bar' actual='bar'?removeEnding('') />
-<@assertFails message='2'>
+<@assertFails message='Too many arguments'>
     ${'x'?removeEnding('x', 'x')}
 </...@assertFails>
-<@assertFails message='none'>
+<@assertFails message='argument'>
     ${'x'?removeEnding()}
 </...@assertFails>
 
@@ -171,10 +171,10 @@
 <@assertEquals expected='https://example.com' actual="https://example.com"?ensureStartsWith("[a-z]+://", "http://") />
 <@assertEquals expected='http://HTTP://example.com' actual="HTTP://example.com"?ensureStartsWith("[a-z]+://", "http://") />
 <@assertEquals expected='HTTP://example.com' actual="HTTP://example.com"?ensureStartsWith("[a-z]+://", "http://", "ir") />
-<@assertFails message='4'>
+<@assertFails message='Too many arguments'>
     ${'x'?ensureStartsWith('x', 'x', 'x', 'x')}
 </...@assertFails>
-<@assertFails message='none'>
+<@assertFails message='argument'>
     ${'x'?ensureStartsWith()}
 </...@assertFails>
 
@@ -185,10 +185,10 @@
 <@assertEquals expected='foo' actual='foo'?ensureEndsWith('') />
 <@assertEquals expected='x' actual=''?ensureEndsWith('x') />
 <@assertEquals expected='' actual=''?ensureEndsWith('') />
-<@assertFails message='2'>
+<@assertFails message='Too many arguments'>
     ${'x'?ensureEndsWith('x', 'x')}
 </...@assertFails>
-<@assertFails message='none'>
+<@assertFails message='argument'>
     ${'x'?ensureEndsWith()}
 </...@assertFails>
 

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/8d5263f2/freemarker-core-test/src/test/resources/org/apache/freemarker/core/templatesuite/templates/type-builtins.ftl
----------------------------------------------------------------------
diff --git a/freemarker-core-test/src/test/resources/org/apache/freemarker/core/templatesuite/templates/type-builtins.ftl b/freemarker-core-test/src/test/resources/org/apache/freemarker/core/templatesuite/templates/type-builtins.ftl
index 695bd76..3442824 100644
--- a/freemarker-core-test/src/test/resources/org/apache/freemarker/core/templatesuite/templates/type-builtins.ftl
+++ b/freemarker-core-test/src/test/resources/org/apache/freemarker/core/templatesuite/templates/type-builtins.ftl
@@ -17,10 +17,10 @@
   under the License.
 -->
 <#setting booleanFormat="1,0">
-StNuBoMeMaHaHxSeCoCxEnInDiNo
+StNuBoHaHxSeCoCxEnInFuDiNo
 <#list [
   "a", 1, false,
-  testmethod, testmacro,
+  testfunction, testmacro,
   {"a":1}, [1], testcollection, testcollectionEx,
   testnode,
   bean, bean.m, bean.mOverloaded
@@ -28,8 +28,6 @@ StNuBoMeMaHaHxSeCoCxEnInDiNo
   ${x?isString} <#t>
   ${x?isNumber} <#t>
   ${x?isBoolean} <#t>
-  ${x?isMethod} <#t>
-  ${x?isMacro} <#t>
   ${x?isHash} <#t>
   ${x?isHashEx} <#t>
   ${x?isSequence} <#t>
@@ -37,6 +35,7 @@ StNuBoMeMaHaHxSeCoCxEnInDiNo
   ${x?isCollectionEx} <#t>
   ${x?isEnumerable} <#t>
   ${x?isIndexable} <#t>
+  ${x?isFunction} <#t>
   ${x?isDirective} <#t>
   ${x?isNode}<#lt>
 </#list>


[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

Posted by dd...@apache.org.
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;
         }



[8/8] incubator-freemarker git commit: FREEMARKER-64: Merged: FM3 Unification and improvement of function-like constructs

Posted by dd...@apache.org.
FREEMARKER-64: Merged: FM3 Unification and improvement of function-like constructs

Merge commit 'refs/pull/32/head' of https://github.com/apache/incubator-freemarker into 3


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

Branch: refs/heads/3
Commit: b867458aa2dd937aeec20ab8cf764d2040815ac8
Parents: a3311d5 8d5263f
Author: ddekany <dd...@apache.org>
Authored: Tue Aug 8 00:40:09 2017 +0200
Committer: ddekany <dd...@apache.org>
Committed: Tue Aug 8 00:40:57 2017 +0200

----------------------------------------------------------------------
 .gitignore                                      |   2 +
 FM3-CHANGE-LOG.txt                              |  19 +-
 .../core/FM2ASTToFM3SourceConverter.java        |   4 +
 .../converter/FM2ToFM3ConverterTest.java        |   4 +
 ...a8DefaultObjectWrapperBridgeMethodsTest.java |  32 +-
 .../impl/Java8DefaultObjectWrapperTest.java     |  50 ++--
 .../freemarker/core/DirectiveCallPlaceTest.java |   6 +-
 .../EnvironmentGetTemplateVariantsTest.java     |   2 +-
 .../core/TheadInterruptingSupportTest.java      |   6 +-
 .../impl/AbstractParallelIntrospectionTest.java |  14 +-
 .../CommonSupertypeForUnwrappingHintTest.java   |   8 +-
 .../model/impl/DefaultObjectWrapperTest.java    |  59 ++--
 .../core/model/impl/EnumModelsTest.java         |  11 +-
 .../core/model/impl/ErrorMessagesTest.java      |  24 +-
 .../impl/FineTuneMethodAppearanceTest.java      |   3 +-
 .../core/model/impl/StaticModelsTest.java       |  11 +-
 .../templatesuite/CoreTemplateTestSuite.java    |   4 +-
 .../core/templatesuite/models/MultiModel2.java  |  40 ++-
 .../models/SimpleTestFunction.java              |  44 +++
 .../templatesuite/models/SimpleTestMethod.java  |  49 ---
 .../core/templatesuite/models/TestMethod.java   |  12 -
 .../core/userpkg/AllFeaturesDirective.java      |  21 +-
 .../core/userpkg/AllFeaturesFunction.java       |  22 +-
 .../core/userpkg/NamedVarargsOnlyDirective.java |   2 +-
 .../userpkg/PositionalVarargsOnlyDirective.java |   2 +-
 .../userpkg/PositionalVarargsOnlyFunction.java  |   2 +-
 .../core/userpkg/TestTemplateCallableModel.java |  14 +-
 .../core/userpkg/TwoNamedParamsDirective.java   |   2 +-
 .../TwoNestedContentParamsDirective.java        |   2 +-
 .../userpkg/TwoPositionalParamsDirective.java   |   2 +-
 .../userpkg/TwoPositionalParamsFunction.java    |   2 +-
 .../core/userpkg/UpperCaseDirective.java        |   2 +-
 .../core/valueformat/NumberFormatTest.java      |   2 +-
 .../org/apache/freemarker/core/ast-builtins.ast |   4 +-
 .../org/apache/freemarker/core/ast-range.ast    |   6 +-
 .../templatesuite/expected/number-literal.txt   |  10 +-
 .../templatesuite/expected/stringbimethods.txt  |   2 -
 .../templatesuite/expected/type-builtins.txt    |  28 +-
 .../core/templatesuite/templates/list2.ftl      |   2 +-
 .../templatesuite/templates/number-literal.ftl  |   2 +-
 .../templates/string-builtins3.ftl              |  32 +-
 .../templatesuite/templates/stringbimethods.ftl |   4 -
 .../templatesuite/templates/type-builtins.ftl   |   7 +-
 .../freemarker/core/ASTDynamicTopLevelCall.java |  77 +++--
 .../apache/freemarker/core/ASTExpBuiltIn.java   |  68 +++--
 .../freemarker/core/ASTExpFunctionCall.java     | 229 ++++++++++++++
 .../freemarker/core/ASTExpListLiteral.java      |  30 +-
 .../freemarker/core/ASTExpMethodCall.java       | 230 --------------
 .../freemarker/core/BuiltInsForDates.java       |  47 ++-
 .../core/BuiltInsForExistenceHandling.java      |  49 +--
 .../core/BuiltInsForMultipleTypes.java          | 120 ++++----
 .../BuiltInsForNestedContentParameters.java     |  20 +-
 .../freemarker/core/BuiltInsForNodes.java       |  38 ++-
 .../freemarker/core/BuiltInsForSequences.java   | 156 +++++-----
 .../core/BuiltInsForStringsBasic.java           | 299 ++++++++++++-------
 .../core/BuiltInsForStringsEncoding.java        |  23 +-
 .../freemarker/core/BuiltInsForStringsMisc.java |  32 +-
 .../core/BuiltInsForStringsRegexp.java          |  51 ++--
 .../org/apache/freemarker/core/Environment.java |  25 +-
 .../org/apache/freemarker/core/MessageUtil.java |   2 +-
 .../freemarker/core/NonFunctionException.java   |  62 ++++
 .../freemarker/core/NonMethodException.java     |  62 ----
 .../freemarker/core/NonStringException.java     |  14 +-
 .../apache/freemarker/core/_CallableUtils.java  | 169 +++++++++++
 .../core/_ObjectBuilderSettingEvaluator.java    |  31 +-
 .../core/_TemplateCallableModelUtils.java       | 112 -------
 .../freemarker/core/debug/DebugModel.java       |   3 +-
 .../core/debug/RmiDebugModelImpl.java           |   8 +-
 .../core/model/ArgumentArrayLayout.java         |  18 ++
 .../core/model/GeneralPurposeNothing.java       |  18 +-
 .../core/model/TemplateCallableModel.java       |  11 +-
 .../core/model/TemplateDirectiveModel.java      |  45 ++-
 .../core/model/TemplateFunctionModel.java       |  10 +
 .../core/model/TemplateMethodModel.java         |  60 ----
 .../core/model/TemplateMethodModelEx.java       |  54 ----
 .../core/model/TemplateModelException.java      |   4 +-
 .../freemarker/core/model/impl/BeanModel.java   |  16 +-
 .../core/model/impl/DefaultObjectWrapper.java   |  29 +-
 .../core/model/impl/JavaMethodModel.java        |  95 ++----
 .../model/impl/OverloadedFixArgsMethods.java    |  24 +-
 .../model/impl/OverloadedJavaMethodModel.java   |  86 ++++++
 .../core/model/impl/OverloadedMethods.java      |  59 ++--
 .../core/model/impl/OverloadedMethodsModel.java |  65 ----
 .../model/impl/OverloadedMethodsSubset.java     |  52 ++--
 .../model/impl/OverloadedVarArgsMethods.java    |  57 ++--
 .../core/model/impl/ResourceBundleModel.java    |  46 +--
 .../core/model/impl/SimpleJavaMethodModel.java  | 123 ++++++++
 .../core/model/impl/SimpleMethod.java           |  86 +++---
 .../freemarker/core/model/impl/StaticModel.java |  15 +-
 .../model/impl/TemplateModelListSequence.java   |  13 +-
 .../freemarker/core/model/impl/_MethodUtil.java |  40 +--
 .../apache/freemarker/core/util/FTLUtil.java    |  11 +-
 freemarker-core/src/main/javacc/FTL.jj          |   4 +-
 .../apache/freemarker/servlet/IncludePage.java  |   2 +-
 .../jsp/CustomTagAndELFunctionCombiner.java     | 157 ----------
 .../freemarker/servlet/jsp/JspTagModelBase.java |  33 +-
 .../servlet/jsp/SimpleTagDirectiveModel.java    |   2 +-
 .../servlet/jsp/TagDirectiveModel.java          |   2 +-
 .../freemarker/servlet/jsp/TaglibFactory.java   |  22 +-
 ...eDirectiveModelAndTemplateFunctionModel.java |  77 +++++
 .../freemarker/servlet/jsp/TLDParsingTest.java  |  19 +-
 .../test/templateutil/AssertDirective.java      |   2 +-
 .../templateutil/AssertEqualsDirective.java     |   2 +-
 .../test/templateutil/AssertFailsDirective.java |   2 +-
 .../test/templateutil/NoOutputDirective.java    |   2 +-
 105 files changed, 2055 insertions(+), 1841 deletions(-)
----------------------------------------------------------------------



[5/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

Posted by dd...@apache.org.
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/8d5263f2/freemarker-core/src/main/java/org/apache/freemarker/core/BuiltInsForStringsBasic.java
----------------------------------------------------------------------
diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/BuiltInsForStringsBasic.java b/freemarker-core/src/main/java/org/apache/freemarker/core/BuiltInsForStringsBasic.java
index ce3ff56..9311626 100644
--- a/freemarker-core/src/main/java/org/apache/freemarker/core/BuiltInsForStringsBasic.java
+++ b/freemarker-core/src/main/java/org/apache/freemarker/core/BuiltInsForStringsBasic.java
@@ -20,13 +20,13 @@
 package org.apache.freemarker.core;
 
 import java.util.ArrayList;
-import java.util.List;
 import java.util.StringTokenizer;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
 
+import org.apache.freemarker.core.model.ArgumentArrayLayout;
 import org.apache.freemarker.core.model.TemplateBooleanModel;
-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.TemplateModelException;
 import org.apache.freemarker.core.model.TemplateScalarModel;
@@ -69,20 +69,25 @@ class BuiltInsForStringsBasic {
 
     static class containsBI extends ASTExpBuiltIn {
         
-        private class BIMethod implements TemplateMethodModel {
+        private class BIMethod implements TemplateFunctionModel {
             
             private final String s;
     
             private BIMethod(String s) {
                 this.s = s;
             }
-    
+
             @Override
-            public TemplateModel execute(List<? extends TemplateModel> args) throws TemplateModelException {
-                checkMethodArgCount(args, 1);
-                return s.contains(getStringMethodArg(args, 0))
+            public TemplateModel execute(TemplateModel[] args, CallPlace callPlace, Environment env)
+                    throws TemplateException {
+                return s.contains(_CallableUtils.castArgToString(args, 0))
                         ? TemplateBooleanModel.TRUE : TemplateBooleanModel.FALSE;
             }
+
+            @Override
+            public ArgumentArrayLayout getFunctionArgumentArrayLayout() {
+                return ArgumentArrayLayout.SINGLE_POSITIONAL_PARAMETER;
+            }
         }
     
         @Override
@@ -94,19 +99,24 @@ class BuiltInsForStringsBasic {
 
     static class ends_withBI extends BuiltInForString {
     
-        private class BIMethod implements TemplateMethodModel {
+        private class BIMethod implements TemplateFunctionModel {
             private String s;
     
             private BIMethod(String s) {
                 this.s = s;
             }
-    
+
             @Override
-            public TemplateModel execute(List<? extends TemplateModel> args) throws TemplateModelException {
-                checkMethodArgCount(args, 1);
+            public TemplateModel execute(TemplateModel[] args, CallPlace callPlace, Environment env)
+                    throws TemplateException {
                 return s.endsWith(getStringMethodArg(args, 0)) ?
                         TemplateBooleanModel.TRUE : TemplateBooleanModel.FALSE;
             }
+
+            @Override
+            public ArgumentArrayLayout getFunctionArgumentArrayLayout() {
+                return ArgumentArrayLayout.SINGLE_POSITIONAL_PARAMETER;
+            }
         }
     
         @Override
@@ -117,19 +127,24 @@ class BuiltInsForStringsBasic {
 
     static class ensure_ends_withBI extends BuiltInForString {
         
-        private class BIMethod implements TemplateMethodModel {
+        private class BIMethod implements TemplateFunctionModel {
             private String s;
     
             private BIMethod(String s) {
                 this.s = s;
             }
-    
+
             @Override
-            public TemplateModel execute(List<? extends TemplateModel> args) throws TemplateModelException {
-                checkMethodArgCount(args, 1);
+            public TemplateModel execute(TemplateModel[] args, CallPlace callPlace, Environment env)
+                    throws TemplateException {
                 String suffix = getStringMethodArg(args, 0);
                 return new SimpleScalar(s.endsWith(suffix) ? s : s + suffix);
             }
+
+            @Override
+            public ArgumentArrayLayout getFunctionArgumentArrayLayout() {
+                return ArgumentArrayLayout.SINGLE_POSITIONAL_PARAMETER;
+            }
         }
     
         @Override
@@ -140,27 +155,28 @@ class BuiltInsForStringsBasic {
 
     static class ensure_starts_withBI extends BuiltInForString {
         
-        private class BIMethod implements TemplateMethodModel {
+        private class BIMethod implements TemplateFunctionModel {
             private String s;
     
             private BIMethod(String s) {
                 this.s = s;
             }
-    
+
             @Override
-            public TemplateModel execute(List<? extends TemplateModel> args) throws TemplateModelException {
-                checkMethodArgCount(args, 1, 3);
-                
+            public TemplateModel execute(TemplateModel[] args, CallPlace callPlace, Environment env)
+                    throws TemplateException {
                 final String checkedPrefix = getStringMethodArg(args, 0);
-                
+
                 final boolean startsWithPrefix;
-                final String addedPrefix; 
-                if (args.size() > 1) {
-                    addedPrefix = getStringMethodArg(args, 1);
-                    long flags = args.size() > 2
-                            ? RegexpHelper.parseFlagString(getStringMethodArg(args, 2))
+                final String addedPrefix;
+                String addedPrefixArg = getStringMethodArg(args, 1, true);
+                String flagsArg = getStringMethodArg(args, 2, true);
+                if (addedPrefixArg != null) {
+                    addedPrefix = addedPrefixArg;
+                    long flags = flagsArg != null
+                            ? RegexpHelper.parseFlagString(flagsArg)
                             : RegexpHelper.RE_FLAG_REGEXP;
-                  
+
                     if ((flags & RegexpHelper.RE_FLAG_REGEXP) == 0) {
                         RegexpHelper.checkOnlyHasNonRegexpFlags(key, flags, true);
                         if ((flags & RegexpHelper.RE_FLAG_CASE_INSENSITIVE) == 0) {
@@ -172,13 +188,22 @@ class BuiltInsForStringsBasic {
                         Pattern pattern = RegexpHelper.getPattern(checkedPrefix, (int) flags);
                         final Matcher matcher = pattern.matcher(s);
                         startsWithPrefix = matcher.lookingAt();
-                    } 
+                    }
                 } else {
+                    if (flagsArg != null) {
+                        throw new _MiscTemplateException(
+                                "The 2nd parameter must be non-null when the 3rd parameter is non-null");
+                    }
                     startsWithPrefix = s.startsWith(checkedPrefix);
                     addedPrefix = checkedPrefix;
                 }
                 return new SimpleScalar(startsWithPrefix ? s : addedPrefix + s);
             }
+
+            @Override
+            public ArgumentArrayLayout getFunctionArgumentArrayLayout() {
+                return ArgumentArrayLayout.THREE_POSITIONAL_PARAMETERS;
+            }
         }
     
         @Override
@@ -189,26 +214,31 @@ class BuiltInsForStringsBasic {
 
     static class index_ofBI extends ASTExpBuiltIn {
         
-        private class BIMethod implements TemplateMethodModel {
+        private class BIMethod implements TemplateFunctionModel {
             
             private final String s;
             
             private BIMethod(String s) {
                 this.s = s;
             }
-            
+
             @Override
-            public TemplateModel execute(List<? extends TemplateModel> args) throws TemplateModelException {
-                int argCnt = args.size();
-                checkMethodArgCount(argCnt, 1, 2);
+            public TemplateModel execute(TemplateModel[] args, CallPlace callPlace, Environment env)
+                    throws TemplateException {
                 String subStr = getStringMethodArg(args, 0);
-                if (argCnt > 1) {
-                    int startIdx = getNumberMethodArg(args, 1).intValue();
+                Number indexModel = getNumberMethodArg(args, 1, true);
+                if (indexModel != null) {
+                    int startIdx = indexModel.intValue();
                     return new SimpleNumber(findLast ? s.lastIndexOf(subStr, startIdx) : s.indexOf(subStr, startIdx));
                 } else {
                     return new SimpleNumber(findLast ? s.lastIndexOf(subStr) : s.indexOf(subStr));
                 }
             }
+
+            @Override
+            public ArgumentArrayLayout getFunctionArgumentArrayLayout() {
+                return ArgumentArrayLayout.TWO_POSITIONAL_PARAMETERS;
+            }
         }
         
         private final boolean findLast;
@@ -225,7 +255,7 @@ class BuiltInsForStringsBasic {
     }
     
     static class keep_afterBI extends BuiltInForString {
-        class KeepAfterMethod implements TemplateMethodModel {
+        class KeepAfterMethod implements TemplateFunctionModel {
             private String s;
 
             KeepAfterMethod(String s) {
@@ -233,12 +263,13 @@ class BuiltInsForStringsBasic {
             }
 
             @Override
-            public TemplateModel execute(List<? extends TemplateModel> args) throws TemplateModelException {
-                int argCnt = args.size();
-                checkMethodArgCount(argCnt, 1, 2);
+            public TemplateModel execute(TemplateModel[] args, CallPlace callPlace, Environment env)
+                    throws TemplateException {
                 String separatorString = getStringMethodArg(args, 0);
-                long flags = argCnt > 1 ? RegexpHelper.parseFlagString(getStringMethodArg(args, 1)) : 0;
-                
+
+                String flagsStr = getStringMethodArg(args, 1, true);
+                long flags = flagsStr != null ? RegexpHelper.parseFlagString(flagsStr) : 0;
+
                 int startIndex;
                 if ((flags & RegexpHelper.RE_FLAG_REGEXP) == 0) {
                     RegexpHelper.checkOnlyHasNonRegexpFlags(key, flags, true);
@@ -258,9 +289,14 @@ class BuiltInsForStringsBasic {
                     } else {
                         startIndex = -1;
                     }
-                } 
+                }
                 return startIndex == -1 ? TemplateScalarModel.EMPTY_STRING : new SimpleScalar(s.substring(startIndex));
             }
+
+            @Override
+            public ArgumentArrayLayout getFunctionArgumentArrayLayout() {
+                return ArgumentArrayLayout.TWO_POSITIONAL_PARAMETERS;
+            }
         }
         
         @Override
@@ -271,7 +307,7 @@ class BuiltInsForStringsBasic {
     }
     
     static class keep_after_lastBI extends BuiltInForString {
-        class KeepAfterMethod implements TemplateMethodModel {
+        class KeepAfterMethod implements TemplateFunctionModel {
             private String s;
 
             KeepAfterMethod(String s) {
@@ -279,12 +315,12 @@ class BuiltInsForStringsBasic {
             }
 
             @Override
-            public TemplateModel execute(List<? extends TemplateModel> args) throws TemplateModelException {
-                int argCnt = args.size();
-                checkMethodArgCount(argCnt, 1, 2);
+            public TemplateModel execute(TemplateModel[] args, CallPlace callPlace, Environment env)
+                    throws TemplateException {
                 String separatorString = getStringMethodArg(args, 0);
-                long flags = argCnt > 1 ? RegexpHelper.parseFlagString(getStringMethodArg(args, 1)) : 0;
-                
+                String flagString = getStringMethodArg(args, 1, true);
+                long flags = flagString != null ? RegexpHelper.parseFlagString(flagString) : 0;
+
                 int startIndex;
                 if ((flags & RegexpHelper.RE_FLAG_REGEXP) == 0) {
                     RegexpHelper.checkOnlyHasNonRegexpFlags(key, flags, true);
@@ -311,9 +347,14 @@ class BuiltInsForStringsBasic {
                             startIndex = -1;
                         }
                     }
-                } 
+                }
                 return startIndex == -1 ? TemplateScalarModel.EMPTY_STRING : new SimpleScalar(s.substring(startIndex));
             }
+
+            @Override
+            public ArgumentArrayLayout getFunctionArgumentArrayLayout() {
+                return ArgumentArrayLayout.TWO_POSITIONAL_PARAMETERS;
+            }
         }
         
         @Override
@@ -324,7 +365,7 @@ class BuiltInsForStringsBasic {
     }
     
     static class keep_beforeBI extends BuiltInForString {
-        class KeepUntilMethod implements TemplateMethodModel {
+        class KeepUntilMethod implements TemplateFunctionModel {
             private String s;
 
             KeepUntilMethod(String s) {
@@ -332,12 +373,12 @@ class BuiltInsForStringsBasic {
             }
 
             @Override
-            public TemplateModel execute(List<? extends TemplateModel> args) throws TemplateModelException {
-                int argCnt = args.size();
-                checkMethodArgCount(argCnt, 1, 2);
+            public TemplateModel execute(TemplateModel[] args, CallPlace callPlace, Environment env)
+                    throws TemplateException {
                 String separatorString = getStringMethodArg(args, 0);
-                long flags = argCnt > 1 ? RegexpHelper.parseFlagString(getStringMethodArg(args, 1)) : 0;
-                
+                String flagString = getStringMethodArg(args, 1, true);
+                long flags = flagString != null ? RegexpHelper.parseFlagString(flagString) : 0;
+
                 int stopIndex;
                 if ((flags & RegexpHelper.RE_FLAG_REGEXP) == 0) {
                     RegexpHelper.checkOnlyHasNonRegexpFlags(key, flags, true);
@@ -354,9 +395,14 @@ class BuiltInsForStringsBasic {
                     } else {
                         stopIndex = -1;
                     }
-                } 
+                }
                 return stopIndex == -1 ? new SimpleScalar(s) : new SimpleScalar(s.substring(0, stopIndex));
             }
+
+            @Override
+            public ArgumentArrayLayout getFunctionArgumentArrayLayout() {
+                return ArgumentArrayLayout.TWO_POSITIONAL_PARAMETERS;
+            }
         }
         
         @Override
@@ -368,7 +414,7 @@ class BuiltInsForStringsBasic {
     
     // TODO
     static class keep_before_lastBI extends BuiltInForString {
-        class KeepUntilMethod implements TemplateMethodModel {
+        class KeepUntilMethod implements TemplateFunctionModel {
             private String s;
 
             KeepUntilMethod(String s) {
@@ -376,12 +422,12 @@ class BuiltInsForStringsBasic {
             }
 
             @Override
-            public TemplateModel execute(List<? extends TemplateModel> args) throws TemplateModelException {
-                int argCnt = args.size();
-                checkMethodArgCount(argCnt, 1, 2);
+            public TemplateModel execute(TemplateModel[] args, CallPlace callPlace, Environment env)
+                    throws TemplateException {
                 String separatorString = getStringMethodArg(args, 0);
-                long flags = argCnt > 1 ? RegexpHelper.parseFlagString(getStringMethodArg(args, 1)) : 0;
-                
+                String flagString = getStringMethodArg(args, 1, true);
+                long flags = flagString != null ? RegexpHelper.parseFlagString(flagString) : 0;
+
                 int stopIndex;
                 if ((flags & RegexpHelper.RE_FLAG_REGEXP) == 0) {
                     RegexpHelper.checkOnlyHasNonRegexpFlags(key, flags, true);
@@ -405,9 +451,14 @@ class BuiltInsForStringsBasic {
                             stopIndex = -1;
                         }
                     }
-                } 
+                }
                 return stopIndex == -1 ? new SimpleScalar(s) : new SimpleScalar(s.substring(0, stopIndex));
             }
+
+            @Override
+            public ArgumentArrayLayout getFunctionArgumentArrayLayout() {
+                return ArgumentArrayLayout.TWO_POSITIONAL_PARAMETERS;
+            }
         }
         
         @Override
@@ -435,23 +486,21 @@ class BuiltInsForStringsBasic {
 
     static class padBI extends BuiltInForString {
         
-        private class BIMethod implements TemplateMethodModel {
+        private class BIMethod implements TemplateFunctionModel {
             
             private final String s;
     
             private BIMethod(String s) {
                 this.s = s;
             }
-    
+
             @Override
-            public TemplateModel execute(List<? extends TemplateModel> args) throws TemplateModelException {
-                int argCnt  = args.size();
-                checkMethodArgCount(argCnt, 1, 2);
-    
+            public TemplateModel execute(TemplateModel[] args, CallPlace callPlace, Environment env)
+                    throws TemplateException {
                 int width = getNumberMethodArg(args, 0).intValue();
-    
-                if (argCnt > 1) {
-                    String filling = getStringMethodArg(args, 1);
+
+                String filling = getStringMethodArg(args, 1, true);
+                if (filling != null) {
                     try {
                         return new SimpleScalar(
                                 leftPadder
@@ -470,6 +519,11 @@ class BuiltInsForStringsBasic {
                     return new SimpleScalar(leftPadder ? _StringUtil.leftPad(s, width) : _StringUtil.rightPad(s, width));
                 }
             }
+
+            @Override
+            public ArgumentArrayLayout getFunctionArgumentArrayLayout() {
+                return ArgumentArrayLayout.TWO_POSITIONAL_PARAMETERS;
+            }
         }
     
         private final boolean leftPadder;
@@ -486,19 +540,24 @@ class BuiltInsForStringsBasic {
     
     static class remove_beginningBI extends BuiltInForString {
         
-        private class BIMethod implements TemplateMethodModel {
+        private class BIMethod implements TemplateFunctionModel {
             private String s;
     
             private BIMethod(String s) {
                 this.s = s;
             }
-    
+
             @Override
-            public TemplateModel execute(List<? extends TemplateModel> args) throws TemplateModelException {
-                checkMethodArgCount(args, 1);
+            public TemplateModel execute(TemplateModel[] args, CallPlace callPlace, Environment env)
+                    throws TemplateException {
                 String prefix = getStringMethodArg(args, 0);
                 return new SimpleScalar(s.startsWith(prefix) ? s.substring(prefix.length()) : s);
             }
+
+            @Override
+            public ArgumentArrayLayout getFunctionArgumentArrayLayout() {
+                return ArgumentArrayLayout.SINGLE_POSITIONAL_PARAMETER;
+            }
         }
     
         @Override
@@ -509,19 +568,24 @@ class BuiltInsForStringsBasic {
 
     static class remove_endingBI extends BuiltInForString {
     
-        private class BIMethod implements TemplateMethodModel {
+        private class BIMethod implements TemplateFunctionModel {
             private String s;
     
             private BIMethod(String s) {
                 this.s = s;
             }
-    
+
             @Override
-            public TemplateModel execute(List<? extends TemplateModel> args) throws TemplateModelException {
-                checkMethodArgCount(args, 1);
+            public TemplateModel execute(TemplateModel[] args, CallPlace callPlace, Environment env)
+                    throws TemplateException {
                 String suffix = getStringMethodArg(args, 0);
                 return new SimpleScalar(s.endsWith(suffix) ? s.substring(0, s.length() - suffix.length()) : s);
             }
+
+            @Override
+            public ArgumentArrayLayout getFunctionArgumentArrayLayout() {
+                return ArgumentArrayLayout.SINGLE_POSITIONAL_PARAMETER;
+            }
         }
     
         @Override
@@ -531,7 +595,7 @@ class BuiltInsForStringsBasic {
     }
     
     static class split_BI extends BuiltInForString {
-        class SplitMethod implements TemplateMethodModel {
+        class SplitMethod implements TemplateFunctionModel {
             private String s;
 
             SplitMethod(String s) {
@@ -539,12 +603,13 @@ class BuiltInsForStringsBasic {
             }
 
             @Override
-            public TemplateModel execute(List<? extends TemplateModel> args) throws TemplateException {
-                int argCnt = args.size();
-                checkMethodArgCount(argCnt, 1, 2);
-                String splitString = _CallableUtils.castArgToString(args, 0);
-                long flags = argCnt > 1
-                        ? RegexpHelper.parseFlagString(_CallableUtils.castArgToString(args, 1))
+            public TemplateModel execute(TemplateModel[] args, CallPlace callPlace, Environment env)
+                    throws TemplateException {
+                int argCnt = args.length;
+                String splitString = getStringMethodArg(args, 0);
+                TemplateModel arg2 = args[1];
+                long flags = arg2 != null
+                        ? RegexpHelper.parseFlagString(getStringMethodArg(args, 1))
                         : 0;
                 String[] result;
                 if ((flags & RegexpHelper.RE_FLAG_REGEXP) == 0) {
@@ -554,9 +619,14 @@ class BuiltInsForStringsBasic {
                 } else {
                     Pattern pattern = RegexpHelper.getPattern(splitString, (int) flags);
                     result = pattern.split(s);
-                } 
+                }
                 return new NativeStringArraySequence(result);
             }
+
+            @Override
+            public ArgumentArrayLayout getFunctionArgumentArrayLayout() {
+                return ArgumentArrayLayout.TWO_POSITIONAL_PARAMETERS;
+            }
         }
         
         @Override
@@ -568,19 +638,24 @@ class BuiltInsForStringsBasic {
     
     static class starts_withBI extends BuiltInForString {
     
-        private class BIMethod implements TemplateMethodModel {
+        private class BIMethod implements TemplateFunctionModel {
             private String s;
     
             private BIMethod(String s) {
                 this.s = s;
             }
-    
+
             @Override
-            public TemplateModel execute(List<? extends TemplateModel> args) throws TemplateModelException {
-                checkMethodArgCount(args, 1);
+            public TemplateModel execute(TemplateModel[] args, CallPlace callPlace, Environment env)
+                    throws TemplateException {
                 return s.startsWith(getStringMethodArg(args, 0)) ?
                         TemplateBooleanModel.TRUE : TemplateBooleanModel.FALSE;
             }
+
+            @Override
+            public ArgumentArrayLayout getFunctionArgumentArrayLayout() {
+                return ArgumentArrayLayout.SINGLE_POSITIONAL_PARAMETER;
+            }
         }
     
         @Override
@@ -593,25 +668,24 @@ class BuiltInsForStringsBasic {
         
         @Override
         TemplateModel calculateResult(final String s, final Environment env) throws TemplateException {
-            return new TemplateMethodModel() {
-                
+            return new TemplateFunctionModel() {
+
                 @Override
-                public TemplateModel execute(List<? extends TemplateModel> args) throws TemplateModelException {
-                    int argCount = args.size();
-                    checkMethodArgCount(argCount, 1, 2);
-    
+                public TemplateModel execute(TemplateModel[] args, CallPlace callPlace, Environment env)
+                        throws TemplateException {
                     int beginIdx = getNumberMethodArg(args, 0).intValue();
-    
+
                     final int len = s.length();
-    
+
                     if (beginIdx < 0) {
                         throw newIndexLessThan0Exception(0, beginIdx);
                     } else if (beginIdx > len) {
                         throw newIndexGreaterThanLengthException(0, beginIdx, len);
                     }
-    
-                    if (argCount > 1) {
-                        int endIdx = getNumberMethodArg(args, 1).intValue();
+
+                    Number endIdxNumber = getNumberMethodArg(args, 1, true);
+                    if (endIdxNumber != null) {
+                        int endIdx = endIdxNumber.intValue();
                         if (endIdx < 0) {
                             throw newIndexLessThan0Exception(1, endIdx);
                         } else if (endIdx > len) {
@@ -628,7 +702,12 @@ class BuiltInsForStringsBasic {
                         return new SimpleScalar(s.substring(beginIdx));
                     }
                 }
-    
+
+                @Override
+                public ArgumentArrayLayout getFunctionArgumentArrayLayout() {
+                    return ArgumentArrayLayout.TWO_POSITIONAL_PARAMETERS;
+                }
+
                 private TemplateModelException newIndexGreaterThanLengthException(
                         int argIdx, int idx, final int len) throws TemplateModelException {
                     return MessageUtil.newMethodArgInvalidValueException(

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/8d5263f2/freemarker-core/src/main/java/org/apache/freemarker/core/BuiltInsForStringsEncoding.java
----------------------------------------------------------------------
diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/BuiltInsForStringsEncoding.java b/freemarker-core/src/main/java/org/apache/freemarker/core/BuiltInsForStringsEncoding.java
index cee1b49..794fe94 100644
--- a/freemarker-core/src/main/java/org/apache/freemarker/core/BuiltInsForStringsEncoding.java
+++ b/freemarker-core/src/main/java/org/apache/freemarker/core/BuiltInsForStringsEncoding.java
@@ -22,9 +22,9 @@ package org.apache.freemarker.core;
 import java.io.UnsupportedEncodingException;
 import java.nio.charset.Charset;
 import java.nio.charset.UnsupportedCharsetException;
-import java.util.List;
 
-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;
 import org.apache.freemarker.core.model.TemplateScalarModel;
@@ -132,7 +132,7 @@ class BuiltInsForStringsEncoding {
     private BuiltInsForStringsEncoding() { }
 
     static abstract class AbstractUrlBIResult implements
-    TemplateScalarModel, TemplateMethodModel {
+    TemplateScalarModel, TemplateFunctionModel {
         
         protected final ASTExpBuiltIn parent;
         protected final String targetAsString;
@@ -146,10 +146,10 @@ class BuiltInsForStringsEncoding {
         }
         
         protected abstract String encodeWithCharset(Charset charset) throws UnsupportedEncodingException;
-    
+
         @Override
-        public TemplateModel execute(List<? extends TemplateModel> args) throws TemplateModelException {
-            parent.checkMethodArgCount(args.size(), 1);
+        public TemplateModel execute(TemplateModel[] args, CallPlace callPlace, Environment env)
+                throws TemplateException {
             try {
                 String charsetName = _CallableUtils.castArgToString(args,0);
                 Charset charset;
@@ -164,7 +164,12 @@ class BuiltInsForStringsEncoding {
                 throw new _TemplateModelException(e, "Failed to execute URL encoding.");
             }
         }
-        
+
+        @Override
+        public ArgumentArrayLayout getFunctionArgumentArrayLayout() {
+            return ArgumentArrayLayout.SINGLE_POSITIONAL_PARAMETER;
+        }
+
         @Override
         public String getAsString() throws TemplateModelException {
             if (cachedResult == null) {

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/8d5263f2/freemarker-core/src/main/java/org/apache/freemarker/core/BuiltInsForStringsMisc.java
----------------------------------------------------------------------
diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/BuiltInsForStringsMisc.java b/freemarker-core/src/main/java/org/apache/freemarker/core/BuiltInsForStringsMisc.java
index 6bf7e2a..1bcd592 100644
--- a/freemarker-core/src/main/java/org/apache/freemarker/core/BuiltInsForStringsMisc.java
+++ b/freemarker-core/src/main/java/org/apache/freemarker/core/BuiltInsForStringsMisc.java
@@ -22,13 +22,12 @@ package org.apache.freemarker.core;
 import java.io.IOException;
 import java.io.StringReader;
 import java.io.Writer;
-import java.util.List;
 
 import org.apache.freemarker.core.model.ArgumentArrayLayout;
 import org.apache.freemarker.core.model.ObjectWrapper;
 import org.apache.freemarker.core.model.TemplateBooleanModel;
 import org.apache.freemarker.core.model.TemplateDirectiveModel;
-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.TemplateModelException;
 import org.apache.freemarker.core.model.TemplateScalarModel;
@@ -219,7 +218,7 @@ class BuiltInsForStringsMisc {
             }
 
             @Override
-            public ArgumentArrayLayout getArgumentArrayLayout() {
+            public ArgumentArrayLayout getDirectiveArgumentArrayLayout() {
                 return ArgumentArrayLayout.PARAMETERLESS;
             }
 
@@ -254,7 +253,7 @@ class BuiltInsForStringsMisc {
             return new ConstructorFunction(target.evalAndCoerceToPlainText(env), env, target.getTemplate());
         }
 
-        class ConstructorFunction implements TemplateMethodModel {
+        class ConstructorFunction implements TemplateFunctionModel {
 
             private final Class<?> cl;
             private final Environment env;
@@ -273,13 +272,14 @@ class BuiltInsForStringsMisc {
             }
 
             @Override
-            public TemplateModel execute(List<? extends TemplateModel> args) throws TemplateModelException {
+            public TemplateModel execute(TemplateModel[] args, CallPlace callPlace, Environment env)
+                    throws TemplateModelException {
                 ObjectWrapper ow = env.getObjectWrapper();
                 if (ow instanceof DefaultObjectWrapper) {
-                    return ow.wrap(((DefaultObjectWrapper) ow).newInstance(cl, args));
+                    return ow.wrap(((DefaultObjectWrapper) ow).newInstance(cl, args, callPlace));
                 }
 
-                if (!args.isEmpty()) {
+                if (args.length != 0) {
                     throw new TemplateModelException(
                             "className?new(args) only supports 0 arguments in the current configuration, because "
                             + " the objectWrapper setting value is not a "
@@ -293,6 +293,12 @@ class BuiltInsForStringsMisc {
                             + cl.getName() + " with its parameterless constructor; see cause exception", e);
                 }
             }
+
+            @Override
+            public ArgumentArrayLayout getFunctionArgumentArrayLayout() {
+                return null;
+            }
+
         }
     }
     

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/8d5263f2/freemarker-core/src/main/java/org/apache/freemarker/core/BuiltInsForStringsRegexp.java
----------------------------------------------------------------------
diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/BuiltInsForStringsRegexp.java b/freemarker-core/src/main/java/org/apache/freemarker/core/BuiltInsForStringsRegexp.java
index 025b599..ef73e5f 100644
--- a/freemarker-core/src/main/java/org/apache/freemarker/core/BuiltInsForStringsRegexp.java
+++ b/freemarker-core/src/main/java/org/apache/freemarker/core/BuiltInsForStringsRegexp.java
@@ -20,13 +20,13 @@
 package org.apache.freemarker.core;
 
 import java.util.ArrayList;
-import java.util.List;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
 
+import org.apache.freemarker.core.model.ArgumentArrayLayout;
 import org.apache.freemarker.core.model.TemplateBooleanModel;
 import org.apache.freemarker.core.model.TemplateCollectionModel;
-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.TemplateModelException;
 import org.apache.freemarker.core.model.TemplateModelIterator;
@@ -61,29 +61,35 @@ class BuiltInsForStringsRegexp {
     }
     
     static class matchesBI extends BuiltInForString {
-        class MatcherBuilder implements TemplateMethodModel {
+        class MatcherBuilder implements TemplateFunctionModel {
             
             String matchString;
             
             MatcherBuilder(String matchString) throws TemplateModelException {
                 this.matchString = matchString;
             }
-            
+
+
             @Override
-            public TemplateModel execute(List<? extends TemplateModel> args) throws TemplateException {
-                int argCnt = args.size();
-                checkMethodArgCount(argCnt, 1, 2);
-                
-                String patternString = _CallableUtils.castArgToString(args, 0);
-                long flags = argCnt > 1
-                        ? RegexpHelper.parseFlagString(_CallableUtils.castArgToString(args, 1))
+            public TemplateModel execute(TemplateModel[] args, CallPlace callPlace, Environment env)
+                    throws TemplateException {
+                String patternString = getStringMethodArg(args, 0);
+                String flagString = getStringMethodArg(args, 1, true);
+                long flags = flagString != null
+                        ? RegexpHelper.parseFlagString(flagString)
                         : 0;
                 if ((flags & RegexpHelper.RE_FLAG_FIRST_ONLY) != 0) {
+                    // TODO [FM3] Should be an error?
                     RegexpHelper.logFlagWarning("?" + key + " doesn't support the \"f\" flag.");
                 }
                 Pattern pattern = RegexpHelper.getPattern(patternString, (int) flags);
                 return new RegexMatchModel(pattern, matchString);
             }
+
+            @Override
+            public ArgumentArrayLayout getFunctionArgumentArrayLayout() {
+                return ArgumentArrayLayout.TWO_POSITIONAL_PARAMETERS;
+            }
         }
         
         @Override
@@ -95,7 +101,7 @@ class BuiltInsForStringsRegexp {
     
     static class replace_reBI extends BuiltInForString {
         
-        class ReplaceMethod implements TemplateMethodModel {
+        class ReplaceMethod implements TemplateFunctionModel {
             private String s;
 
             ReplaceMethod(String s) {
@@ -103,13 +109,13 @@ class BuiltInsForStringsRegexp {
             }
 
             @Override
-            public TemplateModel execute(List<? extends TemplateModel> args) throws TemplateException {
-                int argCnt = args.size();
-                checkMethodArgCount(argCnt, 2, 3);
-                String arg1 = _CallableUtils.castArgToString(args, 0);
-                String arg2 = _CallableUtils.castArgToString(args, 1);
-                long flags = argCnt > 2
-                        ? RegexpHelper.parseFlagString(_CallableUtils.castArgToString(args, 2))
+            public TemplateModel execute(TemplateModel[] args, CallPlace callPlace, Environment env)
+                    throws TemplateException {
+                String arg1 = getStringMethodArg(args, 0);
+                String arg2 = getStringMethodArg(args, 1);
+                String flagString = getStringMethodArg(args, 2, true);
+                long flags = flagString != null
+                        ? RegexpHelper.parseFlagString(flagString)
                         : 0;
                 String result;
                 if ((flags & RegexpHelper.RE_FLAG_REGEXP) == 0) {
@@ -123,10 +129,15 @@ class BuiltInsForStringsRegexp {
                     result = (flags & RegexpHelper.RE_FLAG_FIRST_ONLY) != 0
                             ? matcher.replaceFirst(arg2)
                             : matcher.replaceAll(arg2);
-                } 
+                }
                 return new SimpleScalar(result);
             }
 
+            @Override
+            public ArgumentArrayLayout getFunctionArgumentArrayLayout() {
+                return ArgumentArrayLayout.THREE_POSITIONAL_PARAMETERS;
+            }
+
         }
         
         @Override

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/8d5263f2/freemarker-core/src/main/java/org/apache/freemarker/core/Environment.java
----------------------------------------------------------------------
diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/Environment.java b/freemarker-core/src/main/java/org/apache/freemarker/core/Environment.java
index ee212db..f697309 100644
--- a/freemarker-core/src/main/java/org/apache/freemarker/core/Environment.java
+++ b/freemarker-core/src/main/java/org/apache/freemarker/core/Environment.java
@@ -2929,7 +2929,7 @@ public final class Environment extends MutableProcessingConfiguration<Environmen
      * Superclass of {@link TemplateCallableModel}-s implemented in the template language.
      */
     abstract class TemplateLanguageCallable implements TemplateCallableModel {
-        private final ASTDirMacroOrFunction callableDefinition;
+        final ASTDirMacroOrFunction callableDefinition;
         private final Namespace namespace;
 
         public TemplateLanguageCallable(ASTDirMacroOrFunction callableDefinition, Namespace namespace) {
@@ -2993,7 +2993,8 @@ public final class Environment extends MutableProcessingConfiguration<Environmen
                                 new _ErrorDescriptionBuilder(
                                         "When calling macro ", new _DelayedJQuote(callableDefinition.getName()),
                                         ", required parameter ", new _DelayedJQuote(paramDef.getName()),
-                                        (argIdx < getArgumentArrayLayout().getPredefinedPositionalArgumentCount()
+                                        (argIdx < callableDefinition.getArgumentArrayLayout()
+                                                        .getPredefinedPositionalArgumentCount()
                                                 ? new Object[] { " (parameter #", (argIdx + 1), ")" }
                                                 : ""),
                                         " was either not specified, or had null/missing value.")
@@ -3013,11 +3014,6 @@ public final class Environment extends MutableProcessingConfiguration<Environmen
             }
         }
 
-        @Override
-        public ArgumentArrayLayout getArgumentArrayLayout() {
-            return callableDefinition.getArgumentArrayLayout();
-        }
-
         ASTDirMacroOrFunction getCallableDefinition() {
             return callableDefinition;
         }
@@ -3053,6 +3049,11 @@ public final class Environment extends MutableProcessingConfiguration<Environmen
             return true;
         }
 
+        @Override
+        public ArgumentArrayLayout getDirectiveArgumentArrayLayout() {
+            return callableDefinition.getArgumentArrayLayout();
+        }
+
     }
 
     /**
@@ -3077,6 +3078,12 @@ public final class Environment extends MutableProcessingConfiguration<Environmen
             }
             return env.getLastReturnValue();
         }
+
+        @Override
+        public ArgumentArrayLayout getFunctionArgumentArrayLayout() {
+            return callableDefinition.getArgumentArrayLayout();
+        }
+
     }
 
 }

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/8d5263f2/freemarker-core/src/main/java/org/apache/freemarker/core/MessageUtil.java
----------------------------------------------------------------------
diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/MessageUtil.java b/freemarker-core/src/main/java/org/apache/freemarker/core/MessageUtil.java
index a1858b1..8749fd4 100644
--- a/freemarker-core/src/main/java/org/apache/freemarker/core/MessageUtil.java
+++ b/freemarker-core/src/main/java/org/apache/freemarker/core/MessageUtil.java
@@ -169,7 +169,7 @@ class MessageUtil {
                 && !(argExp instanceof ASTExpVariable)
                 && !(argExp instanceof ASTExpDot)
                 && !(argExp instanceof ASTExpDynamicKeyName)
-                && !(argExp instanceof ASTExpMethodCall)
+                && !(argExp instanceof ASTExpFunctionCall)
                 && !(argExp instanceof ASTExpBuiltIn);
         if (needParen) sb.append('(');
         sb.append(argExp.getCanonicalForm());

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/8d5263f2/freemarker-core/src/main/java/org/apache/freemarker/core/NonFunctionException.java
----------------------------------------------------------------------
diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/NonFunctionException.java b/freemarker-core/src/main/java/org/apache/freemarker/core/NonFunctionException.java
new file mode 100644
index 0000000..2ae7fdd
--- /dev/null
+++ b/freemarker-core/src/main/java/org/apache/freemarker/core/NonFunctionException.java
@@ -0,0 +1,62 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.freemarker.core;
+
+import org.apache.freemarker.core.model.TemplateFunctionModel;
+import org.apache.freemarker.core.model.TemplateModel;
+
+/**
+ * Indicates that a {@link TemplateFunctionModel} value was expected, but the value had a different type.
+ */
+public class NonFunctionException extends UnexpectedTypeException {
+
+    private static final Class[] EXPECTED_TYPES = new Class[] { TemplateFunctionModel.class };
+    
+    public NonFunctionException(Environment env) {
+        super(env, "Expecting method value here");
+    }
+
+    public NonFunctionException(String description, Environment env) {
+        super(env, description);
+    }
+
+    NonFunctionException(Environment env, _ErrorDescriptionBuilder description) {
+        super(env, description);
+    }
+
+    NonFunctionException(
+            ASTExpression blamed, TemplateModel model, Environment env)
+            throws InvalidReferenceException {
+        super(blamed, model, "function", EXPECTED_TYPES, env);
+    }
+
+    NonFunctionException(
+            ASTExpression blamed, TemplateModel model, String tip,
+            Environment env)
+            throws InvalidReferenceException {
+        super(blamed, model, "function", EXPECTED_TYPES, tip, env);
+    }
+
+    NonFunctionException(
+            ASTExpression blamed, TemplateModel model, String[] tips, Environment env) throws InvalidReferenceException {
+        super(blamed, model, "function", EXPECTED_TYPES, tips, env);
+    }    
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/8d5263f2/freemarker-core/src/main/java/org/apache/freemarker/core/NonMethodException.java
----------------------------------------------------------------------
diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/NonMethodException.java b/freemarker-core/src/main/java/org/apache/freemarker/core/NonMethodException.java
deleted file mode 100644
index 6a51f4c..0000000
--- a/freemarker-core/src/main/java/org/apache/freemarker/core/NonMethodException.java
+++ /dev/null
@@ -1,62 +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;
-
-import org.apache.freemarker.core.model.TemplateMethodModel;
-import org.apache.freemarker.core.model.TemplateModel;
-
-/**
- * Indicates that a {@link TemplateMethodModel} value was expected, but the value had a different type.
- */
-public class NonMethodException extends UnexpectedTypeException {
-
-    private static final Class[] EXPECTED_TYPES = new Class[] { TemplateMethodModel.class };
-    
-    public NonMethodException(Environment env) {
-        super(env, "Expecting method value here");
-    }
-
-    public NonMethodException(String description, Environment env) {
-        super(env, description);
-    }
-
-    NonMethodException(Environment env, _ErrorDescriptionBuilder description) {
-        super(env, description);
-    }
-
-    NonMethodException(
-            ASTExpression blamed, TemplateModel model, Environment env)
-            throws InvalidReferenceException {
-        super(blamed, model, "method", EXPECTED_TYPES, env);
-    }
-
-    NonMethodException(
-            ASTExpression blamed, TemplateModel model, String tip,
-            Environment env)
-            throws InvalidReferenceException {
-        super(blamed, model, "method", EXPECTED_TYPES, tip, env);
-    }
-
-    NonMethodException(
-            ASTExpression blamed, TemplateModel model, String[] tips, Environment env) throws InvalidReferenceException {
-        super(blamed, model, "method", EXPECTED_TYPES, tips, env);
-    }    
-
-}

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/8d5263f2/freemarker-core/src/main/java/org/apache/freemarker/core/_CallableUtils.java
----------------------------------------------------------------------
diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/_CallableUtils.java b/freemarker-core/src/main/java/org/apache/freemarker/core/_CallableUtils.java
index 0a69e51..4ab59b4 100644
--- a/freemarker-core/src/main/java/org/apache/freemarker/core/_CallableUtils.java
+++ b/freemarker-core/src/main/java/org/apache/freemarker/core/_CallableUtils.java
@@ -26,7 +26,6 @@ import java.util.List;
 
 import org.apache.freemarker.core.model.ArgumentArrayLayout;
 import org.apache.freemarker.core.model.Constants;
-import org.apache.freemarker.core.model.TemplateCallableModel;
 import org.apache.freemarker.core.model.TemplateDirectiveModel;
 import org.apache.freemarker.core.model.TemplateFunctionModel;
 import org.apache.freemarker.core.model.TemplateModel;
@@ -47,18 +46,19 @@ public final class _CallableUtils {
     public static void executeWith0Arguments(
             TemplateDirectiveModel directive, CallPlace callPlace, Writer out, Environment env)
             throws IOException, TemplateException {
-        directive.execute(getArgumentArrayWithNoArguments(directive), callPlace, out, env);
+        directive.execute(
+                getArgumentArrayWithNoArguments(directive.getDirectiveArgumentArrayLayout()), callPlace, out, env);
     }
 
     public static TemplateModel executeWith0Arguments(
             TemplateFunctionModel function, CallPlace callPlace, Environment env)
             throws TemplateException {
-        return function.execute(getArgumentArrayWithNoArguments(function), callPlace, env);
+        return function.execute(
+                getArgumentArrayWithNoArguments(function.getFunctionArgumentArrayLayout()), callPlace, env);
     }
 
-    private static TemplateModel[] getArgumentArrayWithNoArguments(TemplateCallableModel callable) {
-        ArgumentArrayLayout argsLayout = callable.getArgumentArrayLayout();
-        int totalLength = argsLayout.getTotalLength();
+    private static TemplateModel[] getArgumentArrayWithNoArguments(ArgumentArrayLayout argsLayout) {
+        int totalLength = argsLayout != null ? argsLayout.getTotalLength() : 0;
         if (totalLength == 0) {
             return Constants.EMPTY_TEMPLATE_MODEL_ARRAY;
         } else {
@@ -78,28 +78,37 @@ public final class _CallableUtils {
         }
     }
 
-    public static Number castArgToNumber(TemplateModel[] args, int argIndex, boolean allowNull)
+    public static Number castArgToNumber(TemplateModel[] args, int argIndex) throws TemplateException {
+        return castArgToNumber(args, argIndex, false);
+    }
+
+    public static Number castArgToNumber(TemplateModel[] args, int argIndex, boolean optional)
+            throws TemplateException {
+        return castArgToNumber(args[argIndex], argIndex, optional);
+    }
+
+    public static Number castArgToNumber(TemplateModel argValue, int argIndex)
             throws TemplateException {
-        return castArgToNumber(args[argIndex], argIndex, allowNull);
+        return castArgToNumber(argValue, argIndex, false);
     }
 
-    public static Number castArgToNumber(TemplateModel argValue, int argIndex, boolean allowNull)
+    public static Number castArgToNumber(TemplateModel argValue, int argIndex, boolean optional)
             throws TemplateException {
-        return castArgToNumber(argValue, null, argIndex, allowNull);
+        return castArgToNumber(argValue, null, argIndex, optional);
     }
 
-    public static Number castArgToNumber(TemplateModel argValue, String argName, boolean allowNull)
+    public static Number castArgToNumber(TemplateModel argValue, String argName, boolean optional)
             throws TemplateException {
-        return castArgToNumber(argValue, argName, -1, allowNull);
+        return castArgToNumber(argValue, argName, -1, optional);
     }
 
-    private static Number castArgToNumber(TemplateModel argValue, String argName, int argIndex, boolean allowNull)
+    private static Number castArgToNumber(TemplateModel argValue, String argName, int argIndex, boolean optional)
             throws TemplateException {
         if (argValue instanceof TemplateNumberModel) {
             return ((TemplateNumberModel) argValue).getAsNumber();
         }
         if (argValue == null) {
-            if (allowNull) {
+            if (optional) {
                 return null;
             }
             throw new _MiscTemplateException(
@@ -115,35 +124,39 @@ public final class _CallableUtils {
         return castArgToString(args, argIndex, false);
     }
 
-    public static String castArgToString(List<? extends TemplateModel> args, int argIndex, boolean allowNull) throws
+    public static String castArgToString(List<? extends TemplateModel> args, int argIndex, boolean optional) throws
             TemplateException {
-        return castArgToString(args.get(argIndex), argIndex, allowNull);
+        return castArgToString(args.get(argIndex), argIndex, optional);
+    }
+
+    public static String castArgToString(TemplateModel[] args, int argIndex) throws TemplateException {
+        return castArgToString(args, argIndex, false);
     }
 
-    public static String castArgToString(TemplateModel[] args, int argIndex, boolean allowNull) throws TemplateException {
-        return castArgToString(args[argIndex], argIndex, allowNull);
+    public static String castArgToString(TemplateModel[] args, int argIndex, boolean optional) throws TemplateException {
+        return castArgToString(args[argIndex], argIndex, optional);
     }
 
     public static String castArgToString(TemplateModel argValue, int argIndex) throws TemplateException {
         return castArgToString(argValue, argIndex, false);
     }
 
-    public static String castArgToString(TemplateModel argValue, int argIndex, boolean allowNull) throws TemplateException {
-        return castArgToString(argValue, null, argIndex, allowNull);
+    public static String castArgToString(TemplateModel argValue, int argIndex, boolean optional) throws TemplateException {
+        return castArgToString(argValue, null, argIndex, optional);
     }
 
-    public static String castArgToString(TemplateModel argValue, String argName, boolean allowNull) throws TemplateException {
-        return castArgToString(argValue, argName, -1, allowNull);
+    public static String castArgToString(TemplateModel argValue, String argName, boolean optional) throws TemplateException {
+        return castArgToString(argValue, argName, -1, optional);
     }
 
     private static String castArgToString(
             TemplateModel argValue, String argName, int argIndex,
-            boolean allowNull) throws TemplateException {
+            boolean optional) throws TemplateException {
         if (argValue instanceof TemplateScalarModel) {
             return _EvalUtil.modelToString((TemplateScalarModel) argValue, null, null);
         }
         if (argValue == null) {
-            if (allowNull) {
+            if (optional) {
                 return null;
             }
             throw new _MiscTemplateException(

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/8d5263f2/freemarker-core/src/main/java/org/apache/freemarker/core/_ObjectBuilderSettingEvaluator.java
----------------------------------------------------------------------
diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/_ObjectBuilderSettingEvaluator.java b/freemarker-core/src/main/java/org/apache/freemarker/core/_ObjectBuilderSettingEvaluator.java
index d2fe617..89a6fe8 100644
--- a/freemarker-core/src/main/java/org/apache/freemarker/core/_ObjectBuilderSettingEvaluator.java
+++ b/freemarker-core/src/main/java/org/apache/freemarker/core/_ObjectBuilderSettingEvaluator.java
@@ -36,10 +36,10 @@ import java.util.Map;
 import java.util.Properties;
 
 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.TemplateModelException;
 import org.apache.freemarker.core.model.impl.DefaultObjectWrapper;
+import org.apache.freemarker.core.model.impl.JavaMethodModel;
 import org.apache.freemarker.core.model.impl.RestrictedObjectWrapper;
 import org.apache.freemarker.core.outputformat.impl.HTMLOutputFormat;
 import org.apache.freemarker.core.outputformat.impl.PlainTextOutputFormat;
@@ -712,17 +712,17 @@ public class _ObjectBuilderSettingEvaluator {
     }
 
     private void setJavaBeanProperties(Object bean,
-            List/*<String>*/ namedParamNames, List/*<Object>*/ namedParamValues)
+            List<String> namedParamNames, List<Object> namedParamValues)
             throws _ObjectBuilderSettingEvaluationException {
         if (namedParamNames.isEmpty()) {
             return;
         }
         
-        final Class cl = bean.getClass();
-        Map/*<String,Method>*/ beanPropSetters;
+        final Class<?> cl = bean.getClass();
+        Map<String, Method> beanPropSetters;
         try {
             PropertyDescriptor[] propDescs = Introspector.getBeanInfo(cl).getPropertyDescriptors();
-            beanPropSetters = new HashMap(propDescs.length * 4 / 3, 1.0f);
+            beanPropSetters = new HashMap<>(propDescs.length * 4 / 3, 1.0f);
             for (PropertyDescriptor propDesc : propDescs) {
                 final Method writeMethod = propDesc.getWriteMethod();
                 if (writeMethod != null) {
@@ -735,14 +735,14 @@ public class _ObjectBuilderSettingEvaluator {
 
         TemplateHashModel beanTM = null;
         for (int i = 0; i < namedParamNames.size(); i++) {
-            String name = (String) namedParamNames.get(i);
+            String name = namedParamNames.get(i);
             if (!beanPropSetters.containsKey(name)) {
                 throw new _ObjectBuilderSettingEvaluationException(
                         "The " + cl.getName() + " class has no writeable JavaBeans property called "
                         + _StringUtil.jQuote(name) + ".");
             }
             
-            Method beanPropSetter = (Method) beanPropSetters.put(name, null);
+            Method beanPropSetter = beanPropSetters.put(name, null);
             if (beanPropSetter == null) {
                     throw new _ObjectBuilderSettingEvaluationException(
                             "JavaBeans property " + _StringUtil.jQuote(name) + " is set twice.");
@@ -757,19 +757,20 @@ public class _ObjectBuilderSettingEvaluator {
                     }
                     beanTM = (TemplateHashModel) wrappedObj;
                 }
-                
+
                 TemplateModel m = beanTM.get(beanPropSetter.getName());
                 if (m == null) {
                     throw new _ObjectBuilderSettingEvaluationException(
                             "Can't find " + beanPropSetter + " as FreeMarker method.");
                 }
-                if (!(m instanceof TemplateMethodModel)) {
+                if (!(m instanceof JavaMethodModel)) {
                     throw new _ObjectBuilderSettingEvaluationException(
-                            _StringUtil.jQuote(beanPropSetter.getName()) + " wasn't a TemplateMethodModel.");
+                            _StringUtil.jQuote(beanPropSetter.getName()) + " wasn't a JavaMethodModel.");
                 }
                 List/*TemplateModel*/ args = new ArrayList();
-                args.add(env.getObjectWrapper().wrap(namedParamValues.get(i)));
-                ((TemplateMethodModel) m).execute(args);
+                ((JavaMethodModel) m).execute(
+                        new TemplateModel[] { env.getObjectWrapper().wrap(namedParamValues.get(i)) },
+                        NonTemplateCallPlace.INSTANCE);
             } catch (Exception e) {
                 throw new _ObjectBuilderSettingEvaluationException(
                         "Failed to set " + _StringUtil.jQuote(name), e);
@@ -992,16 +993,16 @@ public class _ObjectBuilderSettingEvaluator {
                 }
             } else {
                 DefaultObjectWrapper ow = env.getObjectWrapper();
-                List/*<TemplateModel>*/ tmArgs = new ArrayList(positionalParamValues.size());
+                TemplateModel[] tmArgs = new TemplateModel[positionalParamValues.size()];
                 for (int i = 0; i < positionalParamValues.size(); i++) {
                     try {
-                        tmArgs.add(ow.wrap(positionalParamValues.get(i)));
+                        tmArgs[i] = ow.wrap(positionalParamValues.get(i));
                     } catch (TemplateModelException e) {
                         throw new _ObjectBuilderSettingEvaluationException("Failed to wrap arg #" + (i + 1), e);
                     }
                 }
                 try {
-                    return ow.newInstance(cl, tmArgs);
+                    return ow.newInstance(cl, tmArgs, NonTemplateCallPlace.INSTANCE);
                 } catch (Exception e) {
                     throw new _ObjectBuilderSettingEvaluationException(
                             "Failed to call " + cl.getName() + " constructor", e);

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/8d5263f2/freemarker-core/src/main/java/org/apache/freemarker/core/debug/DebugModel.java
----------------------------------------------------------------------
diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/debug/DebugModel.java b/freemarker-core/src/main/java/org/apache/freemarker/core/debug/DebugModel.java
index 1c6bc66..5f4c473 100644
--- a/freemarker-core/src/main/java/org/apache/freemarker/core/debug/DebugModel.java
+++ b/freemarker-core/src/main/java/org/apache/freemarker/core/debug/DebugModel.java
@@ -45,7 +45,7 @@ public interface DebugModel extends Remote {
     public static final int TYPE_COLLECTION    =   32;
     public static final int TYPE_HASH          =   64;
     public static final int TYPE_HASH_EX       =  128;
-    public static final int TYPE_METHOD        =  256;
+    public static final int TYPE_FUNCTION      =  256;
     public static final int TYPE_DIRECTIVE     = 1024;
     public static final int TYPE_ENVIRONMENT   = 2048;
     public static final int TYPE_TEMPLATE      = 4096;

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/8d5263f2/freemarker-core/src/main/java/org/apache/freemarker/core/debug/RmiDebugModelImpl.java
----------------------------------------------------------------------
diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/debug/RmiDebugModelImpl.java b/freemarker-core/src/main/java/org/apache/freemarker/core/debug/RmiDebugModelImpl.java
index 7ab7ec7..20e9f50 100644
--- a/freemarker-core/src/main/java/org/apache/freemarker/core/debug/RmiDebugModelImpl.java
+++ b/freemarker-core/src/main/java/org/apache/freemarker/core/debug/RmiDebugModelImpl.java
@@ -29,9 +29,9 @@ 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.TemplateDirectiveModel;
+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.TemplateMethodModel;
 import org.apache.freemarker.core.model.TemplateModel;
 import org.apache.freemarker.core.model.TemplateModelException;
 import org.apache.freemarker.core.model.TemplateModelIterator;
@@ -155,7 +155,7 @@ class RmiDebugModelImpl extends UnicastRemoteObject implements DebugModel {
         if (model instanceof TemplateCollectionModel) type += TYPE_COLLECTION;
         if (model instanceof TemplateHashModelEx) type += TYPE_HASH_EX;
         if (model instanceof TemplateHashModel) type += TYPE_HASH;
-        if (model instanceof TemplateMethodModel) type += TYPE_METHOD;
+        if (model instanceof TemplateFunctionModel) type += TYPE_FUNCTION;
         if (model instanceof TemplateDirectiveModel) type += TYPE_DIRECTIVE;
         return type;
     }

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/8d5263f2/freemarker-core/src/main/java/org/apache/freemarker/core/model/ArgumentArrayLayout.java
----------------------------------------------------------------------
diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/model/ArgumentArrayLayout.java b/freemarker-core/src/main/java/org/apache/freemarker/core/model/ArgumentArrayLayout.java
index 4e1149c..e974570 100644
--- a/freemarker-core/src/main/java/org/apache/freemarker/core/model/ArgumentArrayLayout.java
+++ b/freemarker-core/src/main/java/org/apache/freemarker/core/model/ArgumentArrayLayout.java
@@ -25,6 +25,8 @@ import org.apache.freemarker.core.util.StringToIndexMap;
  * {@link TemplateCallableModel} subinterfaces define a method called {@code execute}, which has an argument array
  * parameter, whose layout this class describes. The layout specifies the (minimum) array length, what's the index
  * of which parameters, and if there are varargs parameters, in which case they must not be left {@code null}.
+ * (Note that a {@link TemplateCallableModel} may have {@code null} layout; see the documentation of {@code execute}
+ * for more.)
  * <p>
  * Each parameter has a constant index in this array, which is the same for all invocations of the same
  * {@link TemplateCallableModel} object (regardless if there are omitted optional parameters). Thus, the argument
@@ -87,6 +89,22 @@ public final class ArgumentArrayLayout {
             null, false);
 
     /**
+     * Constant to be used when the {@link TemplateCallableModel} has 2 positional parameter, and no others.
+     * (The argument array index of the positional parameters will be 0 and 1.)
+     */
+    public static final ArgumentArrayLayout TWO_POSITIONAL_PARAMETERS = new ArgumentArrayLayout(
+            2, false,
+            null, false);
+
+    /**
+     * Constant to be used when the {@link TemplateCallableModel} has 3 positional parameter, and no others.
+     * (The argument array index of the positional parameters will be 0, 1, and 2.)
+     */
+    public static final ArgumentArrayLayout THREE_POSITIONAL_PARAMETERS = new ArgumentArrayLayout(
+            3, false,
+            null, false);
+
+    /**
      * Constant to be used when the {@link TemplateCallableModel} has 1 positional varargs parameter, and no others.
      * (The argument array index of the positional varargs parameter will be 0.)
      *  */

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/8d5263f2/freemarker-core/src/main/java/org/apache/freemarker/core/model/GeneralPurposeNothing.java
----------------------------------------------------------------------
diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/model/GeneralPurposeNothing.java b/freemarker-core/src/main/java/org/apache/freemarker/core/model/GeneralPurposeNothing.java
index 92b0b58..b19b912 100644
--- a/freemarker-core/src/main/java/org/apache/freemarker/core/model/GeneralPurposeNothing.java
+++ b/freemarker-core/src/main/java/org/apache/freemarker/core/model/GeneralPurposeNothing.java
@@ -19,7 +19,9 @@
 
 package org.apache.freemarker.core.model;
 
-import java.util.List;
+import org.apache.freemarker.core.CallPlace;
+import org.apache.freemarker.core.Environment;
+import org.apache.freemarker.core.TemplateException;
 
 /**
  * Singleton object representing nothing, used by ?if_exists built-in.
@@ -28,7 +30,8 @@ import java.util.List;
  */
 
 final class GeneralPurposeNothing
-implements TemplateBooleanModel, TemplateScalarModel, TemplateSequenceModel, TemplateHashModelEx, TemplateMethodModel {
+implements TemplateBooleanModel, TemplateScalarModel, TemplateSequenceModel, TemplateHashModelEx,
+        TemplateFunctionModel {
 
     public static final TemplateModel INSTANCE = new GeneralPurposeNothing();
       
@@ -66,10 +69,17 @@ implements TemplateBooleanModel, TemplateScalarModel, TemplateSequenceModel, Tem
     }
 
     @Override
-    public TemplateModel execute(List<? extends TemplateModel> args) {
+    public TemplateModel execute(TemplateModel[] args, CallPlace callPlace, Environment env) throws TemplateException {
         return null;
     }
-    
+
+    @Override
+    public ArgumentArrayLayout getFunctionArgumentArrayLayout() {
+        return ArgumentArrayLayout.create(
+                0, true,
+                null, true);
+    }
+
     @Override
     public TemplateCollectionModel keys() {
         return Constants.EMPTY_COLLECTION;

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/8d5263f2/freemarker-core/src/main/java/org/apache/freemarker/core/model/TemplateCallableModel.java
----------------------------------------------------------------------
diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/model/TemplateCallableModel.java b/freemarker-core/src/main/java/org/apache/freemarker/core/model/TemplateCallableModel.java
index 6aa9445..caa612a 100644
--- a/freemarker-core/src/main/java/org/apache/freemarker/core/model/TemplateCallableModel.java
+++ b/freemarker-core/src/main/java/org/apache/freemarker/core/model/TemplateCallableModel.java
@@ -20,14 +20,9 @@
 package org.apache.freemarker.core.model;
 
 /**
- * Super interface of {@link TemplateFunctionModel} and {@link TemplateDirectiveModel}; don' extended (or implement) it
- * yourself!
+ * Super interface (marker interface) of {@link TemplateFunctionModel} and {@link TemplateDirectiveModel}; don' extended
+ * (or implement) it yourself!
  */
 public interface TemplateCallableModel extends TemplateModel {
-
-    /**
-     * Returns the argument array layout to use when calling the {@code execute} method.
-     */
-    ArgumentArrayLayout getArgumentArrayLayout();
-
+    //
 }

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/8d5263f2/freemarker-core/src/main/java/org/apache/freemarker/core/model/TemplateDirectiveModel.java
----------------------------------------------------------------------
diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/model/TemplateDirectiveModel.java b/freemarker-core/src/main/java/org/apache/freemarker/core/model/TemplateDirectiveModel.java
index 95678d6..be06cba 100644
--- a/freemarker-core/src/main/java/org/apache/freemarker/core/model/TemplateDirectiveModel.java
+++ b/freemarker-core/src/main/java/org/apache/freemarker/core/model/TemplateDirectiveModel.java
@@ -5,7 +5,9 @@ import java.io.Writer;
 
 import org.apache.freemarker.core.CallPlace;
 import org.apache.freemarker.core.Environment;
+import org.apache.freemarker.core.NonTemplateCallPlace;
 import org.apache.freemarker.core.TemplateException;
+import org.apache.freemarker.core.model.impl.JavaMethodModel;
 
 /**
  * A {@link TemplateCallableModel} that progressively writes it result into the {@code out} object, instead of
@@ -26,25 +28,29 @@ public interface TemplateDirectiveModel extends TemplateCallableModel {
      * Invokes the directive.
      *
      * @param args
-     *         The of argument values. Not {@code null}. If a parameter was omitted on the caller side, the
-     *         corresponding array element will be {@code null}. The length of the array and the indexes
-     *         correspont to the {@link ArgumentArrayLayout} returned by {@link #getArgumentArrayLayout()}.
-     *         If the caller doesn't keep argument layout rules (such as the array is shorter than
-     *         {@link ArgumentArrayLayout#getTotalLength()}, or the type of the values at
-     *         {@link ArgumentArrayLayout#getPositionalVarargsArgumentIndex()} or at
-     *         {@link ArgumentArrayLayout#getNamedVarargsArgumentIndex()} is improper), this method may
-     *         throws {@link IndexOutOfBoundsException} or {@link ClassCastException}. Thus, user Java code
-     *         that wishes to call {@link TemplateCallableModel}-s is responsible to ensure that the argument array
-     *         follows the layout described be {@link ArgumentArrayLayout}, as the {@code execute} method
-     *         isn't meant to do validations on that level.
+     *         The array of argument values. Not {@code null}. If a parameter was omitted on the caller side, the
+     *         corresponding array element will be {@code null}. The length of the array and the indexes correspond to
+     *         the {@link ArgumentArrayLayout} returned by {@link #getDirectiveArgumentArrayLayout()}. {@link
+     *         ArgumentArrayLayout} os not {@code null}m and the caller doesn't keep argument layout rules (such as the
+     *         array is shorter than {@link ArgumentArrayLayout#getTotalLength()}, or the type of the values at {@link
+     *         ArgumentArrayLayout#getPositionalVarargsArgumentIndex()} or at
+     *         {@link ArgumentArrayLayout#getNamedVarargsArgumentIndex()}
+     *         is improper), this method may throws {@link IndexOutOfBoundsException} or {@link ClassCastException}.
+     *         Thus, user Java code that wishes to call {@link TemplateCallableModel}-s is responsible to ensure that
+     *         the argument array follows the layout described be {@link ArgumentArrayLayout}, as the {@code execute}
+     *         method isn't meant to do validations on that level.
      * @param callPlace
-     *         The place (in a template, normally) where this directive was called from. Not {@code null}. Note that
-     *         {@link CallPlace#executeNestedContent(TemplateModel[], Writer, Environment)} can be used to execute the
-     *         nested content.
+     *         The place (in a template, normally) where this directive was called from. Not {@code null}; in case the
+     *         call is not from a template, you can use {@link NonTemplateCallPlace#INSTANCE} (or another {@link
+     *         NonTemplateCallPlace} instance). Note that {@link CallPlace#executeNestedContent(TemplateModel[], Writer,
+     *         Environment)} can be used to execute the nested content (even if there's no nested content; then simply
+     *         nothing happens).
      * @param out
      *         Print the output here (if there's any)
      * @param env
-     *         The current processing environment. Not {@code null}.
+     *         The current processing environment. Not {@code null} in general, though certain implementations may
+     *         specifically allow that, typically, implementations that are just adapters towards FreeMarker-unaware
+     *         callables (for example, {@link JavaMethodModel} is like that).
      *
      * @throws TemplateException
      *         If any problem occurs that's not an {@link IOException} during writing the template output.
@@ -67,4 +73,13 @@ public interface TemplateDirectiveModel extends TemplateCallableModel {
      */
     boolean isNestedContentSupported();
 
+    /**
+     * Returns the argument array layout to use when calling the {@code {@link #execute(TemplateModel[], CallPlace,
+     * Writer, Environment)}} method, or rarely {@code null}. If it's {@code null} then there can only be positional
+     * arguments, any number of them (though of course the {@code execute} method implementation itself may restricts
+     * the acceptable argument count), and the argument array will be simply as long as the number of arguments
+     * specified at the call place.
+     */
+    ArgumentArrayLayout getDirectiveArgumentArrayLayout();
+
 }

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/8d5263f2/freemarker-core/src/main/java/org/apache/freemarker/core/model/TemplateFunctionModel.java
----------------------------------------------------------------------
diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/model/TemplateFunctionModel.java b/freemarker-core/src/main/java/org/apache/freemarker/core/model/TemplateFunctionModel.java
index 6b42550..70824ec 100644
--- a/freemarker-core/src/main/java/org/apache/freemarker/core/model/TemplateFunctionModel.java
+++ b/freemarker-core/src/main/java/org/apache/freemarker/core/model/TemplateFunctionModel.java
@@ -5,6 +5,7 @@ import java.io.Writer;
 import org.apache.freemarker.core.CallPlace;
 import org.apache.freemarker.core.Environment;
 import org.apache.freemarker.core.TemplateException;
+import org.apache.freemarker.core.model.impl.JavaMethodModel;
 
 /**
  * A {@link TemplateCallableModel}, which returns its result as a {@link TemplateModel} at the end of its execution.
@@ -35,4 +36,13 @@ public interface TemplateFunctionModel extends TemplateCallableModel {
      */
     TemplateModel execute(TemplateModel[] args, CallPlace callPlace, Environment env) throws TemplateException;
 
+    /**
+     * Returns the argument array layout to use when calling the {@code {@link #execute(TemplateModel[], CallPlace,
+     * Environment)}} method, or rarely {@code null}. If it's {@code null} then there can only be positional
+     * arguments, any number of them (though of course the {@code execute} method implementation itself may restricts
+     * the acceptable argument count), and the argument array will be simply as long as the number of arguments
+     * specified at the call place. This layoutless mode is for example used by {@link JavaMethodModel}-s.
+     */
+    ArgumentArrayLayout getFunctionArgumentArrayLayout();
+
 }

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/8d5263f2/freemarker-core/src/main/java/org/apache/freemarker/core/model/TemplateMethodModel.java
----------------------------------------------------------------------
diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/model/TemplateMethodModel.java b/freemarker-core/src/main/java/org/apache/freemarker/core/model/TemplateMethodModel.java
deleted file mode 100644
index c35a500..0000000
--- a/freemarker-core/src/main/java/org/apache/freemarker/core/model/TemplateMethodModel.java
+++ /dev/null
@@ -1,53 +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;
-
-import java.util.List;
-
-import org.apache.freemarker.core.Environment;
-import org.apache.freemarker.core.TemplateException;
-import org.apache.freemarker.core.util.DeepUnwrap;
-
-/**
- * "method" template language data type: Objects that act like functions. Their main application is calling
- * Java methods via {@link org.apache.freemarker.core.model.impl.DefaultObjectWrapper}, but you can implement this
- * interface to invoke top-level functions too.
- * 
- * <p>In templates they are used like {@code myMethod(1, "foo")} or {@code myJavaObject.myJavaMethod(1, "foo")}.
- */
-public interface TemplateMethodModel extends TemplateModel {
-
-    /**
-     * Executes the method call.
-     *  
-     * @param args a {@link List} of {@link TemplateModel}-s,
-     *     containing the arguments passed to the method. If the implementation absolutely wants
-     *     to operate on POJOs, it can use the static utility methods in the {@link DeepUnwrap}
-     *     class to easily obtain them. However, unwrapping is not always possible (or not perfectly), and isn't always
-     *     efficient, so it's recommended to use the original {@link TemplateModel} value as much as possible.
-     *
-     * @return the return value of the method, or {@code null}. If the returned value
-     *     does not implement {@link TemplateModel}, it will be automatically 
-     *     wrapped using the {@link Environment#getObjectWrapper() environment's 
-     *     object wrapper}.
-     */
-    TemplateModel execute(List<? extends TemplateModel> args) throws TemplateException;
-    
-}
\ 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/TemplateModelException.java
----------------------------------------------------------------------
diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/model/TemplateModelException.java b/freemarker-core/src/main/java/org/apache/freemarker/core/model/TemplateModelException.java
index d38faa4..c9d6aa9 100644
--- a/freemarker-core/src/main/java/org/apache/freemarker/core/model/TemplateModelException.java
+++ b/freemarker-core/src/main/java/org/apache/freemarker/core/model/TemplateModelException.java
@@ -24,7 +24,9 @@ import org.apache.freemarker.core.TemplateException;
 import org.apache.freemarker.core._ErrorDescriptionBuilder;
 
 /**
- * {@link TemplateModel} methods throw this exception if the requested data can't be retrieved.  
+ * {@link ObjectWrapper}-s may throw this when wrapping/unwrapping fails, or {@link TemplateModel} methods throw this
+ * if the requested data can't be retrieved. {@link TemplateCallableModel}-s should typically throw
+ * {@link TemplateException} instead (like when a required argument is missing).
  */
 public class TemplateModelException extends TemplateException {
 

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/8d5263f2/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/BeanModel.java
----------------------------------------------------------------------
diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/BeanModel.java b/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/BeanModel.java
index bc7ce67..33d5e86 100644
--- a/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/BeanModel.java
+++ b/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/BeanModel.java
@@ -37,8 +37,8 @@ import org.apache.freemarker.core._DelayedJQuote;
 import org.apache.freemarker.core._TemplateModelException;
 import org.apache.freemarker.core.model.AdapterTemplateModel;
 import org.apache.freemarker.core.model.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.apache.freemarker.core.model.TemplateModelIterator;
@@ -104,12 +104,11 @@ public class BeanModel
     }
     
     /**
-     * Uses Beans introspection to locate a property or method with name
-     * matching the key name. If a method or property is found, it's wrapped
-     * into {@link TemplateMethodModel} (for a method or
-     * indexed property), or evaluated on-the-fly and the return value wrapped
-     * into appropriate model (for a simple property) Models for various
-     * properties and methods are cached on a per-class basis, so the costly
+     * Uses Beans introspection to locate a JavaBean property or method with name
+     * matching the key name. If a method is found, it's wrapped
+     * into {@link TemplateFunctionModel} (a {@link JavaMethodModel} more specifically).
+     * If a JavaBean property is found, its value is returned. Introspection results
+     * for various properties and methods are cached on a per-class basis, so the costly
      * introspection is performed only once per property or method of a class.
      * (Side-note: this also implies that any class whose method has been called
      * will be strongly referred to by the framework and will not become
@@ -212,10 +211,10 @@ public class BeanModel
             // cachedModel remains null, as we don't cache these
         } else if (desc instanceof Method) {
             Method method = (Method) desc;
-            resultModel = cachedModel = new JavaMethodModel(
+            resultModel = cachedModel = new SimpleJavaMethodModel(
                     object, method, ClassIntrospector.getArgTypes(classInfo, method), wrapper);
         } else if (desc instanceof OverloadedMethods) {
-            resultModel = cachedModel = new OverloadedMethodsModel(
+            resultModel = cachedModel = new OverloadedJavaMethodModel(
                     object, (OverloadedMethods) desc, wrapper);
         }
         


[6/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

Posted by dd...@apache.org.
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/8d5263f2/freemarker-core/src/main/java/org/apache/freemarker/core/ASTDynamicTopLevelCall.java
----------------------------------------------------------------------
diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/ASTDynamicTopLevelCall.java b/freemarker-core/src/main/java/org/apache/freemarker/core/ASTDynamicTopLevelCall.java
index 3fe5f74..f579f6f 100644
--- a/freemarker-core/src/main/java/org/apache/freemarker/core/ASTDynamicTopLevelCall.java
+++ b/freemarker-core/src/main/java/org/apache/freemarker/core/ASTDynamicTopLevelCall.java
@@ -101,6 +101,7 @@ class ASTDynamicTopLevelCall extends ASTDirective implements CallPlace  {
         TemplateCallableModel callableValue;
         TemplateDirectiveModel directive;
         TemplateFunctionModel function;
+        ArgumentArrayLayout argsLayout;
         boolean nestedContentSupported;
         {
             TemplateModel callableValueTM = callableValueExp._eval(env);
@@ -108,6 +109,7 @@ class ASTDynamicTopLevelCall extends ASTDirective implements CallPlace  {
                 callableValue = (TemplateCallableModel) callableValueTM;
                 directive = (TemplateDirectiveModel) callableValueTM;
                 function = null;
+                argsLayout = directive.getDirectiveArgumentArrayLayout();
                 nestedContentSupported = directive.isNestedContentSupported();
             } else if (callableValueTM instanceof TemplateFunctionModel) {
                 if (!allowCallingFunctions) {
@@ -117,6 +119,7 @@ class ASTDynamicTopLevelCall extends ASTDirective implements CallPlace  {
                 callableValue = (TemplateCallableModel) callableValueTM;
                 directive = null;
                 function = (TemplateFunctionModel) callableValue;
+                argsLayout = function.getFunctionArgumentArrayLayout();
                 nestedContentSupported = false;
             } else if (callableValueTM == null) {
                 throw InvalidReferenceException.getInstance(callableValueExp, env);
@@ -129,7 +132,39 @@ class ASTDynamicTopLevelCall extends ASTDirective implements CallPlace  {
             throw new _MiscTemplateException(env, "Nested content is not supported by this directive.");
         }
 
-        ArgumentArrayLayout argsLayout = callableValue.getArgumentArrayLayout();
+        TemplateModel[] execArgs = argsLayout != null
+                ? getExecuteArgsBasedOnLayout(argsLayout, callableValue, env)
+                : getExecuteArgsWithoutLayout(callableValue, env);
+
+        if (directive != null) {
+            directive.execute(execArgs, this, env.getOut(), env);
+        } else {
+            TemplateModel result = function.execute(execArgs, this, env);
+            if (result == null) {
+                throw new _MiscTemplateException(env, "Function has returned no value (or null)");
+            }
+            // TODO [FM3] Implement it when we have a such language... it should work like `${f()}`.
+            throw new BugException("Top-level function call not yet implemented");
+        }
+
+        return null;
+    }
+
+    private TemplateModel[] getExecuteArgsWithoutLayout(TemplateCallableModel callableValue, Environment env)
+            throws TemplateException {
+        if (namedArgs != null) {
+            throw new _MiscTemplateException(env, getNamedArgumentsNotSupportedMessage(callableValue, namedArgs[0]));
+        }
+        TemplateModel[] execArgs = new TemplateModel[positionalArgs.length];
+        for (int i = 0; i < positionalArgs.length; i++) {
+            ASTExpression positionalArg = positionalArgs[i];
+            execArgs[i] = positionalArg.eval(env);
+        }
+        return execArgs;
+    }
+
+    private TemplateModel[] getExecuteArgsBasedOnLayout(ArgumentArrayLayout argsLayout, TemplateCallableModel callableValue,
+            Environment env) throws TemplateException {
         int predefPosArgCnt = argsLayout.getPredefinedPositionalArgumentCount();
         int posVarargsArgIdx = argsLayout.getPositionalVarargsArgumentIndex();
 
@@ -157,7 +192,7 @@ class ASTDynamicTopLevelCall extends ASTDirective implements CallPlace  {
             }
             execArgs[posVarargsArgIdx] = varargsSeq;
         } else if (positionalArgs != null && positionalArgs.length > predefPosArgCnt) {
-            checkSupportsAnyParameters(callableValue, env);
+            checkSupportsAnyParameters(callableValue, argsLayout, env);
             List<String> validPredefNames = argsLayout.getPredefinedNamedArgumentsMap().getKeys();
             _ErrorDescriptionBuilder errorDesc = new _ErrorDescriptionBuilder(
                     "The target ", FTLUtil.getCallableTypeName(callableValue), " ",
@@ -194,16 +229,11 @@ class ASTDynamicTopLevelCall extends ASTDirective implements CallPlace  {
                 } else {
                     if (namedVarargsHash == null) {
                         if (namedVarargsArgumentIndex == -1) {
-                            checkSupportsAnyParameters(callableValue, env);
+                            checkSupportsAnyParameters(callableValue, argsLayout, env);
                             Collection<String> validNames = predefNamedArgsMap.getKeys();
                             throw new _MiscTemplateException(env,
                                     validNames == null || validNames.isEmpty()
-                                    ? new Object[] {
-                                            "The called ", FTLUtil.getCallableTypeName(callableValue),
-                                            " can't have arguments that are passed by name (like ",
-                                            new _DelayedJQuote(namedArg.name), "). Try to pass arguments by position "
-                                            + "(i.e, without name, as in ", "<@example 1, 2, 3 />" ,  ")."
-                                    }
+                                    ? getNamedArgumentsNotSupportedMessage(callableValue, namedArg)
                                     : new Object[] {
                                             "The called ", FTLUtil.getCallableTypeName(callableValue),
                                             " has no parameter that's passed by name and is called ",
@@ -221,24 +251,23 @@ class ASTDynamicTopLevelCall extends ASTDirective implements CallPlace  {
         if (namedVarargsArgumentIndex != -1) {
             execArgs[namedVarargsArgumentIndex] = namedVarargsHash != null ? namedVarargsHash : Constants.EMPTY_HASH;
         }
+        return execArgs;
+    }
 
-        if (directive != null) {
-            directive.execute(execArgs, this, env.getOut(), env);
-        } else {
-            TemplateModel result = function.execute(execArgs, this, env);
-            if (result == null) {
-                throw new _MiscTemplateException(env, "Function has returned no value (or null)");
-            }
-            // TODO [FM3] Implement it when we have a such language... it should work like `${f()}`.
-            throw new BugException("Top-level function call not yet implemented");
-        }
-
-        return null;
+    private Object[] getNamedArgumentsNotSupportedMessage(TemplateCallableModel callableValue,
+            NamedArgument namedArg) {
+        return new Object[] {
+                "The called ", FTLUtil.getCallableTypeName(callableValue),
+                " can't have arguments that are passed by name (like ",
+                new _DelayedJQuote(namedArg.name), "). Try to pass arguments by position "
+                + "(i.e, without name, as in ", "<@example 1, 2, 3 />" ,  ")."
+        };
     }
 
-    private void checkSupportsAnyParameters(TemplateCallableModel callableValue, Environment env)
-            throws _MiscTemplateException {
-        if (callableValue.getArgumentArrayLayout().getTotalLength() == 0) {
+    private void checkSupportsAnyParameters(
+            TemplateCallableModel callableValue, ArgumentArrayLayout argsLayout, Environment env)
+            throws TemplateException {
+        if (argsLayout.getTotalLength() == 0) {
             throw new _MiscTemplateException(env,
                     "The called ", FTLUtil.getCallableTypeName(callableValue), " doesn't support any parameters.");
         }

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/8d5263f2/freemarker-core/src/main/java/org/apache/freemarker/core/ASTExpBuiltIn.java
----------------------------------------------------------------------
diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/ASTExpBuiltIn.java b/freemarker-core/src/main/java/org/apache/freemarker/core/ASTExpBuiltIn.java
index a74f83f..c69c297 100644
--- a/freemarker-core/src/main/java/org/apache/freemarker/core/ASTExpBuiltIn.java
+++ b/freemarker-core/src/main/java/org/apache/freemarker/core/ASTExpBuiltIn.java
@@ -140,9 +140,8 @@ abstract class ASTExpBuiltIn extends ASTExpression implements Cloneable {
         putBI("isHash", new BuiltInsForMultipleTypes.is_hashBI());
         putBI("isInfinite", new is_infiniteBI());
         putBI("isIndexable", new BuiltInsForMultipleTypes.is_indexableBI());
-        putBI("isMacro", new BuiltInsForMultipleTypes.is_macroBI());
         putBI("isMarkupOutput", new BuiltInsForMultipleTypes.is_markup_outputBI());
-        putBI("isMethod", new BuiltInsForMultipleTypes.is_methodBI());
+        putBI("isFunction", new BuiltInsForMultipleTypes.is_functionBI());
         putBI("isNan", new is_nanBI());
         putBI("isNode", new BuiltInsForMultipleTypes.is_nodeBI());
         putBI("isNumber", new BuiltInsForMultipleTypes.is_numberBI());
@@ -391,7 +390,11 @@ abstract class ASTExpBuiltIn extends ASTExpression implements Cloneable {
     protected final void checkMethodArgCount(List args, int expectedCnt) throws TemplateModelException {
         checkMethodArgCount(args.size(), expectedCnt);
     }
-    
+
+    protected final void checkMethodArgCount(TemplateModel[] args, int expectedCnt) throws TemplateModelException {
+        checkMethodArgCount(args.length, expectedCnt);
+    }
+
     protected final void checkMethodArgCount(int argCnt, int expectedCnt) throws TemplateModelException {
         if (argCnt != expectedCnt) {
             throw MessageUtil.newArgCntError("?" + key, argCnt, expectedCnt);
@@ -401,48 +404,79 @@ abstract class ASTExpBuiltIn extends ASTExpression implements Cloneable {
     protected final void checkMethodArgCount(List args, int minCnt, int maxCnt) throws TemplateModelException {
         checkMethodArgCount(args.size(), minCnt, maxCnt);
     }
-    
+
+    protected final void checkMethodArgCount(TemplateModel[] args, int minCnt, int maxCnt) throws
+            TemplateModelException {
+        checkMethodArgCount(args.length, minCnt, maxCnt);
+    }
+
     protected final void checkMethodArgCount(int argCnt, int minCnt, int maxCnt) throws TemplateModelException {
         if (argCnt < minCnt || argCnt > maxCnt) {
             throw MessageUtil.newArgCntError("?" + key, argCnt, minCnt, maxCnt);
         }
     }
 
-    /**
-     * Same as {@link #getStringMethodArg}, but checks if {@code args} is big enough, and returns {@code null} if it
-     * isn't.
-     */
-    protected final String getOptStringMethodArg(List args, int argIdx)
-            throws TemplateModelException {
-        return args.size() > argIdx ? getStringMethodArg(args, argIdx) : null;
+    protected final String getStringMethodArg(TemplateModel[] args, int argIdx) throws TemplateModelException {
+        return getStringMethodArg(args, argIdx, false);
     }
-    
+
     /**
      * Gets a method argument and checks if it's a string; it does NOT check if {@code args} is big enough.
      */
-    protected final String getStringMethodArg(List args, int argIdx)
+    protected final String getStringMethodArg(TemplateModel[] args, int argIdx, boolean optional)
+            throws TemplateModelException {
+        TemplateModel arg = args[argIdx];
+        return getStringMethodArg(arg, argIdx, optional);
+    }
+
+    protected String getStringMethodArg(TemplateModel arg, int argIdx)
+            throws TemplateModelException {
+        return getStringMethodArg(arg, argIdx, false);
+    }
+
+    protected String getStringMethodArg(TemplateModel arg, int argIdx, boolean optional)
             throws TemplateModelException {
-        TemplateModel arg = (TemplateModel) args.get(argIdx);
         if (!(arg instanceof TemplateScalarModel)) {
+            if (optional && arg == null) {
+                return null;
+            }
             throw MessageUtil.newMethodArgMustBeStringException("?" + key, argIdx, arg);
         } else {
             return _EvalUtil.modelToString((TemplateScalarModel) arg, null, null);
         }
     }
 
+    protected final Number getNumberMethodArg(TemplateModel[] args, int argIdx)
+            throws TemplateModelException {
+        return getNumberMethodArg(args, argIdx, false);
+    }
+
     /**
      * Gets a method argument and checks if it's a number; it does NOT check if {@code args} is big enough.
      */
-    protected final Number getNumberMethodArg(List args, int argIdx)
+    protected final Number getNumberMethodArg(TemplateModel[] args, int argIdx, boolean optional)
+            throws TemplateModelException {
+        TemplateModel arg = args[argIdx];
+        return getNumberMethodArg(arg, argIdx, optional);
+    }
+
+    protected Number getNumberMethodArg(TemplateModel arg, int argIdx)
+            throws TemplateModelException {
+        return getNumberMethodArg(arg, argIdx, false);
+    }
+
+    protected Number getNumberMethodArg(TemplateModel arg, int argIdx, boolean optional)
             throws TemplateModelException {
-        TemplateModel arg = (TemplateModel) args.get(argIdx);
         if (!(arg instanceof TemplateNumberModel)) {
+            if (optional && arg == null) {
+                return null;
+            }
             throw MessageUtil.newMethodArgMustBeNumberException("?" + key, argIdx, arg);
         } else {
             return _EvalUtil.modelToNumber((TemplateNumberModel) arg, null);
         }
     }
-    
+
     protected final TemplateModelException newMethodArgInvalidValueException(int argIdx, Object[] details) {
         return MessageUtil.newMethodArgInvalidValueException("?" + key, argIdx, details);
     }

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/8d5263f2/freemarker-core/src/main/java/org/apache/freemarker/core/ASTExpFunctionCall.java
----------------------------------------------------------------------
diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/ASTExpFunctionCall.java b/freemarker-core/src/main/java/org/apache/freemarker/core/ASTExpFunctionCall.java
new file mode 100644
index 0000000..40dc6e3
--- /dev/null
+++ b/freemarker-core/src/main/java/org/apache/freemarker/core/ASTExpFunctionCall.java
@@ -0,0 +1,229 @@
+/*
+ * 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.
+ */
+
+/*
+ * 22 October 1999: This class added by Holger Arendt.
+ */
+
+package org.apache.freemarker.core;
+
+import java.io.IOException;
+import java.io.Writer;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.freemarker.core.model.ArgumentArrayLayout;
+import org.apache.freemarker.core.model.Constants;
+import org.apache.freemarker.core.model.TemplateFunctionModel;
+import org.apache.freemarker.core.model.TemplateModel;
+import org.apache.freemarker.core.model.TemplateSequenceModel;
+import org.apache.freemarker.core.util.CommonSupplier;
+import org.apache.freemarker.core.util.FTLUtil;
+
+
+/**
+ * AST expression node: {@code exp(args)}.
+ */
+final class ASTExpFunctionCall extends ASTExpression implements CallPlace {
+
+    private final ASTExpression target;
+    private final ASTExpListLiteral arguments;
+
+    ASTExpFunctionCall(ASTExpression target, ArrayList arguments) {
+        this(target, new ASTExpListLiteral(arguments));
+    }
+
+    private ASTExpFunctionCall(ASTExpression target, ASTExpListLiteral arguments) {
+        this.target = target;
+        this.arguments = arguments;
+    }
+
+    @Override
+    TemplateModel _eval(Environment env) throws TemplateException {
+        TemplateModel targetModel = target.eval(env);
+
+        if (!(targetModel instanceof TemplateFunctionModel)) {
+            throw new NonFunctionException(target, targetModel, env);
+        }
+        TemplateFunctionModel func = (TemplateFunctionModel) targetModel;
+
+        ArgumentArrayLayout arrayLayout = func.getFunctionArgumentArrayLayout();
+
+        // TODO [FM3] This is just temporary, until we support named args. Then the logic in ASTDynamicTopLevelCall
+        // should be reused.
+
+        TemplateModel[] args;
+        if (arrayLayout != null) {
+            int posVarargsLength;
+            int callArgCnt = arguments.size();
+            int predefPosArgCnt = arrayLayout.getPredefinedPositionalArgumentCount();
+            int posVarargsIdx = arrayLayout.getPositionalVarargsArgumentIndex();
+            if (callArgCnt > predefPosArgCnt) {
+                if (posVarargsIdx == -1) {
+                    throw new _MiscTemplateException(env,
+                            "Too many arguments; the target ", FTLUtil.getCallableTypeName(func),
+                            " only has ", predefPosArgCnt, " parameters.");
+                }
+            }
+
+            List<TemplateModel> callArgList = arguments.getModelList(env);
+
+            args = new TemplateModel[arrayLayout.getTotalLength()];
+            int callPredefArgCnt = Math.min(callArgCnt, predefPosArgCnt);
+            for (int argIdx = 0; argIdx < callPredefArgCnt; argIdx++) {
+                args[argIdx] = callArgList.get(argIdx);
+            }
+
+            if (posVarargsIdx != -1) {
+                TemplateSequenceModel varargsSeq;
+                posVarargsLength = callArgCnt - predefPosArgCnt;
+                if (posVarargsLength <= 0) {
+                    varargsSeq = Constants.EMPTY_SEQUENCE;
+                } else {
+                    NativeSequence nativeSeq = new NativeSequence(posVarargsLength);
+                    varargsSeq = nativeSeq;
+                    for (int posVarargIdx = 0; posVarargIdx < posVarargsLength; posVarargIdx++) {
+                        nativeSeq.add(callArgList.get(predefPosArgCnt + posVarargIdx));
+                    }
+                }
+                args[posVarargsIdx] = varargsSeq;
+            }
+
+            int namedVarargsArgIdx = arrayLayout.getNamedVarargsArgumentIndex();
+            if (namedVarargsArgIdx != -1) {
+                args[namedVarargsArgIdx] = Constants.EMPTY_HASH;
+            }
+        } else {
+            List<TemplateModel> callArgList = arguments.getModelList(env);
+            args = new TemplateModel[callArgList.size()];
+            for (int i = 0; i < callArgList.size(); i++) {
+                args[i] = callArgList.get(i);
+            }
+        }
+
+        return func.execute(args, this, env);
+    }
+
+    @Override
+    public String getCanonicalForm() {
+        StringBuilder buf = new StringBuilder();
+        buf.append(target.getCanonicalForm());
+        buf.append("(");
+        String list = arguments.getCanonicalForm();
+        buf.append(list.substring(1, list.length() - 1));
+        buf.append(")");
+        return buf.toString();
+    }
+
+    @Override
+    String getASTNodeDescriptor() {
+        return "...(...)";
+    }
+    
+    TemplateModel getConstantValue() {
+        return null;
+    }
+
+    @Override
+    boolean isLiteral() {
+        return false;
+    }
+
+    @Override
+    protected ASTExpression deepCloneWithIdentifierReplaced_inner(
+            String replacedIdentifier, ASTExpression replacement, ReplacemenetState replacementState) {
+        return new ASTExpFunctionCall(
+                target.deepCloneWithIdentifierReplaced(replacedIdentifier, replacement, replacementState),
+                (ASTExpListLiteral) arguments.deepCloneWithIdentifierReplaced(replacedIdentifier, replacement, replacementState));
+    }
+
+    @Override
+    int getParameterCount() {
+        return 1 + arguments.items.size();
+    }
+
+    @Override
+    Object getParameterValue(int idx) {
+        if (idx == 0) {
+            return target;
+        } else if (idx < getParameterCount()) {
+            return arguments.items.get(idx - 1);
+        } else {
+            throw new IndexOutOfBoundsException();
+        }
+    }
+
+    @Override
+    ParameterRole getParameterRole(int idx) {
+        if (idx == 0) {
+            return ParameterRole.CALLEE;
+        } else if (idx < getParameterCount()) {
+            return ParameterRole.ARGUMENT_VALUE;
+        } else {
+            throw new IndexOutOfBoundsException();
+        }
+    }
+
+    // -----------------------------------------------------------------------------------------------------------------
+    // CallPlace API
+
+    @Override
+    public boolean hasNestedContent() {
+        return false;
+    }
+
+    @Override
+    public int getNestedContentParameterCount() {
+        return 0;
+    }
+
+    @Override
+    public void executeNestedContent(TemplateModel[] nestedContentArgs, Writer out, Environment env)
+            throws TemplateException, IOException {
+        // Do nothing
+    }
+
+    @Override
+    public Object getOrCreateCustomData(Object providerIdentity, CommonSupplier<?> supplier)
+            throws CallPlaceCustomDataInitializationException {
+        throw new UnsupportedOperationException("Expression call places don't store custom data");
+    }
+
+    @Override
+    public boolean isCustomDataSupported() {
+        return false;
+    }
+
+    @Override
+    public boolean isNestedOutputCacheable() {
+        return false;
+    }
+
+    @Override
+    public int getFirstTargetJavaParameterTypeIndex() {
+        // TODO [FM3]
+        return -1;
+    }
+
+    @Override
+    public Class<?> getTargetJavaParameterType(int argIndex) {
+        // TODO [FM3]
+        return null;
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/8d5263f2/freemarker-core/src/main/java/org/apache/freemarker/core/ASTExpListLiteral.java
----------------------------------------------------------------------
diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/ASTExpListLiteral.java b/freemarker-core/src/main/java/org/apache/freemarker/core/ASTExpListLiteral.java
index 0fc27da..1ddf63c 100644
--- a/freemarker-core/src/main/java/org/apache/freemarker/core/ASTExpListLiteral.java
+++ b/freemarker-core/src/main/java/org/apache/freemarker/core/ASTExpListLiteral.java
@@ -25,7 +25,7 @@ import java.util.Collections;
 import java.util.List;
 import java.util.ListIterator;
 
-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;
 
@@ -54,8 +54,9 @@ final class ASTExpListLiteral extends ASTExpression {
     }
 
     /**
-     * For {@link TemplateMethodModel} calls, returns the list of arguments as {@link TemplateModel}-s.
+     * For {@link TemplateFunctionModel} calls, returns the list of arguments as {@link TemplateModel}-s.
      */
+    // TODO [FM3][CF] This will be removed
     List<TemplateModel> getModelList(Environment env) throws TemplateException {
         int size = items.size();
         switch(size) {

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/8d5263f2/freemarker-core/src/main/java/org/apache/freemarker/core/ASTExpMethodCall.java
----------------------------------------------------------------------
diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/ASTExpMethodCall.java b/freemarker-core/src/main/java/org/apache/freemarker/core/ASTExpMethodCall.java
deleted file mode 100644
index 581726e..0000000
--- a/freemarker-core/src/main/java/org/apache/freemarker/core/ASTExpMethodCall.java
+++ /dev/null
@@ -1,226 +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.
- */
-
-/*
- * 22 October 1999: This class added by Holger Arendt.
- */
-
-package org.apache.freemarker.core;
-
-import java.io.IOException;
-import java.io.Writer;
-import java.util.ArrayList;
-import java.util.List;
-
-import org.apache.freemarker.core.model.ArgumentArrayLayout;
-import org.apache.freemarker.core.model.Constants;
-import org.apache.freemarker.core.model.TemplateFunctionModel;
-import org.apache.freemarker.core.model.TemplateMethodModel;
-import org.apache.freemarker.core.model.TemplateModel;
-import org.apache.freemarker.core.model.TemplateSequenceModel;
-import org.apache.freemarker.core.util.CommonSupplier;
-import org.apache.freemarker.core.util.FTLUtil;
-
-
-/**
- * AST expression node: {@code exp(args)}.
- */
-final class ASTExpMethodCall extends ASTExpression implements CallPlace {
-
-    private final ASTExpression target;
-    private final ASTExpListLiteral arguments;
-
-    ASTExpMethodCall(ASTExpression target, ArrayList arguments) {
-        this(target, new ASTExpListLiteral(arguments));
-    }
-
-    private ASTExpMethodCall(ASTExpression target, ASTExpListLiteral arguments) {
-        this.target = target;
-        this.arguments = arguments;
-    }
-
-    @Override
-    TemplateModel _eval(Environment env) throws TemplateException {
-        TemplateModel targetModel = target.eval(env);
-        if (targetModel instanceof TemplateMethodModel) {
-            TemplateMethodModel targetMethod = (TemplateMethodModel) targetModel;
-            List<TemplateModel> argumentStrings = arguments.getModelList(env);
-            Object result = targetMethod.execute(argumentStrings);
-            return env.getObjectWrapper().wrap(result);
-        } else if (targetModel instanceof TemplateFunctionModel) {
-            TemplateFunctionModel func = (TemplateFunctionModel) targetModel;
-
-            ArgumentArrayLayout arrayLayout = func.getArgumentArrayLayout();
-
-            // TODO [FM3] This is just temporary, until we support named args. Then the logic in ASTDynamicTopLevelCall
-            // should be reused.
-
-            int posVarargsLength;
-            int callArgCnt = arguments.size();
-            int predefPosArgCnt = arrayLayout.getPredefinedPositionalArgumentCount();
-            int posVarargsIdx = arrayLayout.getPositionalVarargsArgumentIndex();
-            if (callArgCnt > predefPosArgCnt) {
-                if (posVarargsIdx == -1) {
-                    throw new _MiscTemplateException(env,
-                            "Too many arguments; the target ", FTLUtil.getCallableTypeName(func),
-                            " has ", predefPosArgCnt, " arguments.");
-                }
-            }
-
-            List<TemplateModel> callArgList = arguments.getModelList(env);
-
-            TemplateModel[] args = new TemplateModel[arrayLayout.getTotalLength()];
-            int callPredefArgCnt = Math.min(callArgCnt, predefPosArgCnt);
-            for (int argIdx = 0; argIdx < callPredefArgCnt; argIdx++) {
-                args[argIdx] = callArgList.get(argIdx);
-            }
-
-            if (posVarargsIdx != -1) {
-                TemplateSequenceModel varargsSeq;
-                posVarargsLength = callArgCnt - predefPosArgCnt;
-                if (posVarargsLength <= 0) {
-                    varargsSeq = Constants.EMPTY_SEQUENCE;
-                } else {
-                    NativeSequence nativeSeq = new NativeSequence(posVarargsLength);
-                    varargsSeq = nativeSeq;
-                    for (int posVarargIdx = 0; posVarargIdx < posVarargsLength; posVarargIdx++) {
-                        nativeSeq.add(callArgList.get(predefPosArgCnt + posVarargIdx));
-                    }
-                }
-                args[posVarargsIdx] = varargsSeq;
-            }
-
-            int namedVarargsArgIdx = arrayLayout.getNamedVarargsArgumentIndex();
-            if (namedVarargsArgIdx != -1) {
-                args[namedVarargsArgIdx] = Constants.EMPTY_HASH;
-            }
-
-            return func.execute(args, this, env);
-        } else {
-            throw new NonMethodException(target, targetModel, env);
-        }
-    }
-
-    @Override
-    public String getCanonicalForm() {
-        StringBuilder buf = new StringBuilder();
-        buf.append(target.getCanonicalForm());
-        buf.append("(");
-        String list = arguments.getCanonicalForm();
-        buf.append(list.substring(1, list.length() - 1));
-        buf.append(")");
-        return buf.toString();
-    }
-
-    @Override
-    String getASTNodeDescriptor() {
-        return "...(...)";
-    }
-    
-    TemplateModel getConstantValue() {
-        return null;
-    }
-
-    @Override
-    boolean isLiteral() {
-        return false;
-    }
-
-    @Override
-    protected ASTExpression deepCloneWithIdentifierReplaced_inner(
-            String replacedIdentifier, ASTExpression replacement, ReplacemenetState replacementState) {
-        return new ASTExpMethodCall(
-                target.deepCloneWithIdentifierReplaced(replacedIdentifier, replacement, replacementState),
-                (ASTExpListLiteral) arguments.deepCloneWithIdentifierReplaced(replacedIdentifier, replacement, replacementState));
-    }
-
-    @Override
-    int getParameterCount() {
-        return 1 + arguments.items.size();
-    }
-
-    @Override
-    Object getParameterValue(int idx) {
-        if (idx == 0) {
-            return target;
-        } else if (idx < getParameterCount()) {
-            return arguments.items.get(idx - 1);
-        } else {
-            throw new IndexOutOfBoundsException();
-        }
-    }
-
-    @Override
-    ParameterRole getParameterRole(int idx) {
-        if (idx == 0) {
-            return ParameterRole.CALLEE;
-        } else if (idx < getParameterCount()) {
-            return ParameterRole.ARGUMENT_VALUE;
-        } else {
-            throw new IndexOutOfBoundsException();
-        }
-    }
-
-    // -----------------------------------------------------------------------------------------------------------------
-    // CallPlace API
-
-    @Override
-    public boolean hasNestedContent() {
-        return false;
-    }
-
-    @Override
-    public int getNestedContentParameterCount() {
-        return 0;
-    }
-
-    @Override
-    public void executeNestedContent(TemplateModel[] nestedContentArgs, Writer out, Environment env)
-            throws TemplateException, IOException {
-        // Do nothing
-    }
-
-    @Override
-    public Object getOrCreateCustomData(Object providerIdentity, CommonSupplier<?> supplier)
-            throws CallPlaceCustomDataInitializationException {
-        throw new UnsupportedOperationException("Expression call places don't store custom data");
-    }
-
-    @Override
-    public boolean isCustomDataSupported() {
-        return false;
-    }
-
-    @Override
-    public boolean isNestedOutputCacheable() {
-        return false;
-    }
-
-    @Override
-    public int getFirstTargetJavaParameterTypeIndex() {
-        // TODO [FM3]
-        return -1;
-    }
-
-    @Override
-    public Class<?> getTargetJavaParameterType(int argIndex) {
-        // TODO [FM3]
-        return null;
-    }
-}

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/8d5263f2/freemarker-core/src/main/java/org/apache/freemarker/core/BuiltInsForDates.java
----------------------------------------------------------------------
diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/BuiltInsForDates.java b/freemarker-core/src/main/java/org/apache/freemarker/core/BuiltInsForDates.java
index 434578d..3760c77 100644
--- a/freemarker-core/src/main/java/org/apache/freemarker/core/BuiltInsForDates.java
+++ b/freemarker-core/src/main/java/org/apache/freemarker/core/BuiltInsForDates.java
@@ -20,14 +20,13 @@
 package org.apache.freemarker.core;
 
 import java.util.Date;
-import java.util.List;
 import java.util.TimeZone;
 
 import org.apache.freemarker.core.model.AdapterTemplateModel;
+import org.apache.freemarker.core.model.ArgumentArrayLayout;
 import org.apache.freemarker.core.model.TemplateDateModel;
-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.TemplateModelException;
 import org.apache.freemarker.core.model.TemplateScalarModel;
 import org.apache.freemarker.core.model.impl.SimpleDate;
 import org.apache.freemarker.core.model.impl.SimpleScalar;
@@ -70,7 +69,7 @@ class BuiltInsForDates {
      */
     static class iso_BI extends AbstractISOBI {
         
-        class Result implements TemplateMethodModel {
+        class Result implements TemplateFunctionModel {
             private final Date date;
             private final int dateType;
             private final Environment env;
@@ -81,19 +80,19 @@ class BuiltInsForDates {
                 this.env = env;
             }
 
+
             @Override
-            public TemplateModel execute(List<? extends TemplateModel> args) throws TemplateModelException {
-                checkMethodArgCount(args, 1);
-                
-                TemplateModel tzArgTM = args.get(0);
-                TimeZone tzArg; 
+            public TemplateModel execute(TemplateModel[] args, CallPlace callPlace, Environment env)
+                    throws TemplateException {
+                TemplateModel tzArgTM = args[0];
+                TimeZone tzArg;
                 Object adaptedObj;
                 if (tzArgTM instanceof AdapterTemplateModel
                         && (adaptedObj =
                                 ((AdapterTemplateModel) tzArgTM)
                                 .getAdaptedObject(TimeZone.class))
                             instanceof TimeZone) {
-                    tzArg = (TimeZone) adaptedObj;                    
+                    tzArg = (TimeZone) adaptedObj;
                 } else if (tzArgTM instanceof TemplateScalarModel) {
                     String tzName = _EvalUtil.modelToString((TemplateScalarModel) tzArgTM, null, null);
                     try {
@@ -108,17 +107,22 @@ class BuiltInsForDates {
                     throw MessageUtil.newMethodArgUnexpectedTypeException(
                             "?" + key, 0, "string or java.util.TimeZone", tzArgTM);
                 }
-                
+
                 return new SimpleScalar(_DateUtil.dateToISO8601String(
                         date,
                         dateType != TemplateDateModel.TIME,
                         dateType != TemplateDateModel.DATE,
                         shouldShowOffset(date, dateType, env),
                         accuracy,
-                        tzArg, 
+                        tzArg,
                         env.getISOBuiltInCalendarFactory()));
             }
-            
+
+            @Override
+            public ArgumentArrayLayout getFunctionArgumentArrayLayout() {
+                return ArgumentArrayLayout.SINGLE_POSITIONAL_PARAMETER;
+            }
+
         }
 
         iso_BI(Boolean showOffset, int accuracy) {

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/8d5263f2/freemarker-core/src/main/java/org/apache/freemarker/core/BuiltInsForExistenceHandling.java
----------------------------------------------------------------------
diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/BuiltInsForExistenceHandling.java b/freemarker-core/src/main/java/org/apache/freemarker/core/BuiltInsForExistenceHandling.java
index c0d6164..dfa7c46 100644
--- a/freemarker-core/src/main/java/org/apache/freemarker/core/BuiltInsForExistenceHandling.java
+++ b/freemarker-core/src/main/java/org/apache/freemarker/core/BuiltInsForExistenceHandling.java
@@ -19,12 +19,10 @@
 
 package org.apache.freemarker.core;
 
-import java.util.List;
-
+import org.apache.freemarker.core.model.ArgumentArrayLayout;
 import org.apache.freemarker.core.model.TemplateBooleanModel;
-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.TemplateModelException;
 
 /**
  * A holder for builtins that deal with null left-hand values.
@@ -56,14 +54,14 @@ class BuiltInsForExistenceHandling {
     }
     
     static class defaultBI extends BuiltInsForExistenceHandling.ExistenceBuiltIn {
-        
+
         @Override
         TemplateModel _eval(final Environment env) throws TemplateException {
             TemplateModel model = evalMaybeNonexistentTarget(env);
             return model == null ? FIRST_NON_NULL_METHOD : new ConstantMethod(model);
         }
 
-        private static class ConstantMethod implements TemplateMethodModel {
+        private static class ConstantMethod implements TemplateFunctionModel {
             private final TemplateModel constant;
 
             ConstantMethod(TemplateModel constant) {
@@ -71,28 +69,43 @@ class BuiltInsForExistenceHandling {
             }
 
             @Override
-            public TemplateModel execute(List<? extends TemplateModel> args) {
+            public TemplateModel execute(TemplateModel[] args, CallPlace callPlace, Environment env)
+                    throws TemplateException {
                 return constant;
             }
+
+            @Override
+            public ArgumentArrayLayout getFunctionArgumentArrayLayout() {
+                return null;
+            }
+
         }
 
         /**
          * A method that goes through the arguments one by one and returns
          * the first one that is non-null. If all args are null, returns null.
          */
-        private static final TemplateMethodModel FIRST_NON_NULL_METHOD =
-            new TemplateMethodModel() {
-                @Override
-                public TemplateModel execute(List<? extends TemplateModel> args) throws TemplateModelException {
-                    int argCnt = args.size();
-                    if (argCnt == 0) throw MessageUtil.newArgCntError("?default", argCnt, 1, Integer.MAX_VALUE);
-                    for (int i = 0; i < argCnt; i++ ) {
-                        TemplateModel result = args.get(i);
-                        if (result != null) return result;
+        private static final TemplateFunctionModel FIRST_NON_NULL_METHOD = new TemplateFunctionModel() {
+
+            @Override
+            public TemplateModel execute(TemplateModel[] args, CallPlace callPlace, Environment env)
+                    throws TemplateException {
+                int argsLen = args.length;
+                for (int i = 0; i < argsLen; i++ ) {
+                    TemplateModel result = args[i];
+                    if (result != null) {
+                        return result;
                     }
-                    return null;
                 }
-            };
+                return null;
+            }
+
+            @Override
+            public ArgumentArrayLayout getFunctionArgumentArrayLayout() {
+                return null;
+            }
+
+        };
     }
     
     static class existsBI extends BuiltInsForExistenceHandling.ExistenceBuiltIn {

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/8d5263f2/freemarker-core/src/main/java/org/apache/freemarker/core/BuiltInsForMultipleTypes.java
----------------------------------------------------------------------
diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/BuiltInsForMultipleTypes.java b/freemarker-core/src/main/java/org/apache/freemarker/core/BuiltInsForMultipleTypes.java
index 5e85262..b1bd1dd 100644
--- a/freemarker-core/src/main/java/org/apache/freemarker/core/BuiltInsForMultipleTypes.java
+++ b/freemarker-core/src/main/java/org/apache/freemarker/core/BuiltInsForMultipleTypes.java
@@ -19,19 +19,18 @@
 
 package org.apache.freemarker.core;
 
-import java.io.Serializable;
 import java.util.Date;
-import java.util.List;
 
+import org.apache.freemarker.core.model.ArgumentArrayLayout;
 import org.apache.freemarker.core.model.TemplateBooleanModel;
 import org.apache.freemarker.core.model.TemplateCollectionModel;
 import org.apache.freemarker.core.model.TemplateCollectionModelEx;
 import org.apache.freemarker.core.model.TemplateDateModel;
 import org.apache.freemarker.core.model.TemplateDirectiveModel;
+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.TemplateModelException;
 import org.apache.freemarker.core.model.TemplateModelWithAPISupport;
@@ -108,29 +107,30 @@ class BuiltInsForMultipleTypes {
     }
 
     static class dateBI extends ASTExpBuiltIn {
-        private class DateParser
-        implements
-            TemplateDateModel,
-                TemplateMethodModel,
-            TemplateHashModel {
+        private class DateParser implements TemplateDateModel, TemplateFunctionModel, TemplateHashModel {
             private final String text;
             private final Environment env;
             private final TemplateDateFormat defaultFormat;
             private TemplateDateModel cachedValue;
             
-            DateParser(String text, Environment env)
-            throws TemplateException {
+            DateParser(String text, Environment env) throws TemplateException {
                 this.text = text;
                 this.env = env;
                 defaultFormat = env.getTemplateDateFormat(dateType, Date.class, target, false);
             }
-            
+
             @Override
-            public TemplateModel execute(List<? extends TemplateModel> args) throws TemplateException {
-                checkMethodArgCount(args, 0, 1);
-                return args.size() == 0 ? getAsDateModel() : get(_CallableUtils.castArgToString(args, 0));
+            public TemplateModel execute(TemplateModel[] args, CallPlace callPlace, Environment env)
+                    throws TemplateException {
+                TemplateModel arg1 = args[0];
+                return arg1 == null ? getAsDateModel() : get(_CallableUtils.castArgToString(arg1, 0));
             }
-            
+
+            @Override
+            public ArgumentArrayLayout getFunctionArgumentArrayLayout() {
+                return ArgumentArrayLayout.SINGLE_POSITIONAL_PARAMETER;
+            }
+
             @Override
             public TemplateModel get(String pattern) throws TemplateModelException {
                 TemplateDateFormat format;
@@ -258,7 +258,7 @@ class BuiltInsForMultipleTypes {
         TemplateModel _eval(Environment env) throws TemplateException {
             TemplateModel tm = target.eval(env);
             target.assertNonNull(tm, env);
-            return (tm instanceof TemplateBooleanModel)  ?
+            return (tm instanceof TemplateBooleanModel) ?
                 TemplateBooleanModel.TRUE : TemplateBooleanModel.FALSE;
         }
     }
@@ -286,7 +286,7 @@ class BuiltInsForMultipleTypes {
         TemplateModel _eval(Environment env) throws TemplateException {
             TemplateModel tm = target.eval(env);
             target.assertNonNull(tm, env);
-            return (tm instanceof TemplateDateModel)  ?
+            return (tm instanceof TemplateDateModel) ?
                 TemplateBooleanModel.TRUE : TemplateBooleanModel.FALSE;
         }
     }
@@ -356,32 +356,22 @@ class BuiltInsForMultipleTypes {
         }
     }
 
-    static class is_macroBI extends ASTExpBuiltIn {
-        @Override
-        TemplateModel _eval(Environment env) throws TemplateException {
-            TemplateModel tm = target.eval(env);
-            target.assertNonNull(tm, env);
-            return (tm instanceof Environment.TemplateLanguageDirective)  ?
-                TemplateBooleanModel.TRUE : TemplateBooleanModel.FALSE;
-        }
-    }
-
     static class is_markup_outputBI extends ASTExpBuiltIn {
         @Override
         TemplateModel _eval(Environment env) throws TemplateException {
             TemplateModel tm = target.eval(env);
             target.assertNonNull(tm, env);
-            return (tm instanceof TemplateMarkupOutputModel)  ?
+            return (tm instanceof TemplateMarkupOutputModel) ?
                 TemplateBooleanModel.TRUE : TemplateBooleanModel.FALSE;
         }
     }
     
-    static class is_methodBI extends ASTExpBuiltIn {
+    static class is_functionBI extends ASTExpBuiltIn {
         @Override
         TemplateModel _eval(Environment env) throws TemplateException {
             TemplateModel tm = target.eval(env);
             target.assertNonNull(tm, env);
-            return (tm instanceof TemplateMethodModel)  ?
+            return (tm instanceof TemplateFunctionModel) ?
                 TemplateBooleanModel.TRUE : TemplateBooleanModel.FALSE;
         }
     }
@@ -391,7 +381,7 @@ class BuiltInsForMultipleTypes {
         TemplateModel _eval(Environment env) throws TemplateException {
             TemplateModel tm = target.eval(env);
             target.assertNonNull(tm, env);
-            return (tm instanceof TemplateNodeModel)  ?
+            return (tm instanceof TemplateNodeModel) ?
                 TemplateBooleanModel.TRUE : TemplateBooleanModel.FALSE;
         }
     }
@@ -401,7 +391,7 @@ class BuiltInsForMultipleTypes {
         TemplateModel _eval(Environment env) throws TemplateException {
             TemplateModel tm = target.eval(env);
             target.assertNonNull(tm, env);
-            return (tm instanceof TemplateNumberModel)  ?
+            return (tm instanceof TemplateNumberModel) ?
                 TemplateBooleanModel.TRUE : TemplateBooleanModel.FALSE;
         }
     }
@@ -421,7 +411,7 @@ class BuiltInsForMultipleTypes {
         TemplateModel _eval(Environment env) throws TemplateException {
             TemplateModel tm = target.eval(env);
             target.assertNonNull(tm, env);
-            return (tm instanceof TemplateScalarModel)  ?
+            return (tm instanceof TemplateScalarModel) ?
                 TemplateBooleanModel.TRUE : TemplateBooleanModel.FALSE;
         }
     }
@@ -469,10 +459,7 @@ class BuiltInsForMultipleTypes {
     
     static class stringBI extends ASTExpBuiltIn {
         
-        private class BooleanFormatter
-        implements 
-            TemplateScalarModel,
-                TemplateMethodModel {
+        private class BooleanFormatter implements TemplateScalarModel, TemplateFunctionModel {
             private final TemplateBooleanModel bool;
             private final Environment env;
             
@@ -480,18 +467,23 @@ class BuiltInsForMultipleTypes {
                 this.bool = bool;
                 this.env = env;
             }
-    
+
             @Override
-            public TemplateModel execute(List<? extends TemplateModel> args) throws TemplateException {
-                checkMethodArgCount(args, 2);
+            public TemplateModel execute(TemplateModel[] args, CallPlace callPlace, Environment env)
+                    throws TemplateException {
                 int argIdx = bool.getAsBoolean() ? 0 : 1;
-                TemplateModel result = args.get(argIdx);
+                TemplateModel result = args[argIdx];
                 if (!(result instanceof TemplateScalarModel)) {
-                    throw new NonStringException((Serializable) argIdx, result, null, null);
+                    throw new NonStringException(argIdx, result, null, null);
                 }
                 return result;
             }
-    
+
+            @Override
+            public ArgumentArrayLayout getFunctionArgumentArrayLayout() {
+                return ArgumentArrayLayout.TWO_POSITIONAL_PARAMETERS;
+            }
+
             @Override
             public String getAsString() throws TemplateModelException {
                 // Boolean should have come first... but that change would be non-BC. 
@@ -507,11 +499,7 @@ class BuiltInsForMultipleTypes {
             }
         }
     
-        private class DateFormatter
-        implements
-            TemplateScalarModel,
-            TemplateHashModel,
-                TemplateMethodModel {
+        private class DateFormatter implements TemplateScalarModel, TemplateHashModel, TemplateFunctionModel {
             private final TemplateDateModel dateModel;
             private final Environment env;
             private final TemplateDateFormat defaultFormat;
@@ -528,14 +516,19 @@ class BuiltInsForMultipleTypes {
                         : env.getTemplateDateFormat(
                                 dateType, _EvalUtil.modelToDate(dateModel, target).getClass(), target, true);
             }
-    
+
             @Override
-            public TemplateModel execute(List<? extends TemplateModel> args) throws TemplateException {
-                checkMethodArgCount(args, 1);
+            public TemplateModel execute(TemplateModel[] args, CallPlace callPlace, Environment env)
+                    throws TemplateException {
                 return formatWith(_CallableUtils.castArgToString(args, 0));
             }
 
             @Override
+            public ArgumentArrayLayout getFunctionArgumentArrayLayout() {
+                return ArgumentArrayLayout.SINGLE_POSITIONAL_PARAMETER;
+            }
+
+            @Override
             public TemplateModel get(String key)
             throws TemplateModelException {
                 return formatWith(key);
@@ -582,11 +575,7 @@ class BuiltInsForMultipleTypes {
             }
         }
         
-        private class NumberFormatter
-        implements
-            TemplateScalarModel,
-            TemplateHashModel,
-                TemplateMethodModel {
+        private class NumberFormatter implements TemplateScalarModel, TemplateHashModel, TemplateFunctionModel {
             private final TemplateNumberModel numberModel;
             private final Number number;
             private final Environment env;
@@ -606,13 +595,18 @@ class BuiltInsForMultipleTypes {
                     throw _CoreAPI.ensureIsTemplateModelException("Failed to get default number format", e); 
                 }
             }
-    
+
             @Override
-            public TemplateModel execute(List<? extends TemplateModel> args) throws TemplateException {
-                checkMethodArgCount(args, 1);
+            public TemplateModel execute(TemplateModel[] args, CallPlace callPlace, Environment env)
+                    throws TemplateException {
                 return get(_CallableUtils.castArgToString(args, 0));
             }
-    
+
+            @Override
+            public ArgumentArrayLayout getFunctionArgumentArrayLayout() {
+                return ArgumentArrayLayout.SINGLE_POSITIONAL_PARAMETER;
+            }
+
             @Override
             public TemplateModel get(String key) throws TemplateModelException {
                 TemplateNumberFormat format;

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/8d5263f2/freemarker-core/src/main/java/org/apache/freemarker/core/BuiltInsForNestedContentParameters.java
----------------------------------------------------------------------
diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/BuiltInsForNestedContentParameters.java b/freemarker-core/src/main/java/org/apache/freemarker/core/BuiltInsForNestedContentParameters.java
index 89593cd..bc4b9dc 100644
--- a/freemarker-core/src/main/java/org/apache/freemarker/core/BuiltInsForNestedContentParameters.java
+++ b/freemarker-core/src/main/java/org/apache/freemarker/core/BuiltInsForNestedContentParameters.java
@@ -18,13 +18,11 @@
  */
 package org.apache.freemarker.core;
 
-import java.util.List;
-
 import org.apache.freemarker.core.ASTDirList.IterationContext;
+import org.apache.freemarker.core.model.ArgumentArrayLayout;
 import org.apache.freemarker.core.model.TemplateBooleanModel;
-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.TemplateModelException;
 import org.apache.freemarker.core.model.impl.SimpleNumber;
 import org.apache.freemarker.core.model.impl.SimpleScalar;
 
@@ -131,18 +129,24 @@ class BuiltInsForNestedContentParameters {
 
     static class item_cycleBI extends BuiltInForNestedContentParameter {
 
-        private class BIMethod implements TemplateMethodModel {
+        private class BIMethod implements TemplateFunctionModel {
             
             private final IterationContext iterCtx;
     
             private BIMethod(IterationContext iterCtx) {
                 this.iterCtx = iterCtx;
             }
-    
+
             @Override
-            public TemplateModel execute(List<? extends TemplateModel> args) throws TemplateModelException {
+            public TemplateModel execute(TemplateModel[] args, CallPlace callPlace, Environment env)
+                    throws TemplateException {
                 checkMethodArgCount(args, 1, Integer.MAX_VALUE);
-                return args.get(iterCtx.getIndex() % args.size());
+                return args[iterCtx.getIndex() % args.length];
+            }
+
+            @Override
+            public ArgumentArrayLayout getFunctionArgumentArrayLayout() {
+                return null;
             }
         }
         

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/8d5263f2/freemarker-core/src/main/java/org/apache/freemarker/core/BuiltInsForNodes.java
----------------------------------------------------------------------
diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/BuiltInsForNodes.java b/freemarker-core/src/main/java/org/apache/freemarker/core/BuiltInsForNodes.java
index d5dfe93..5390dc4 100644
--- a/freemarker-core/src/main/java/org/apache/freemarker/core/BuiltInsForNodes.java
+++ b/freemarker-core/src/main/java/org/apache/freemarker/core/BuiltInsForNodes.java
@@ -19,9 +19,8 @@
 
 package org.apache.freemarker.core;
 
-import java.util.List;
-
-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;
 import org.apache.freemarker.core.model.TemplateNodeModel;
@@ -114,7 +113,7 @@ class BuiltInsForNodes {
     // Can't be instantiated
     private BuiltInsForNodes() { }
 
-    static class AncestorSequence extends NativeSequence implements TemplateMethodModel {
+    static class AncestorSequence extends NativeSequence implements TemplateFunctionModel {
 
         private static final int INITIAL_CAPACITY = 12;
 
@@ -124,24 +123,30 @@ class BuiltInsForNodes {
             super(INITIAL_CAPACITY);
             this.env = env;
         }
-        
+
         @Override
-        public TemplateModel execute(List<? extends TemplateModel> names) throws TemplateException {
-            if (names == null || names.isEmpty()) {
+        public TemplateModel execute(TemplateModel[] args, CallPlace callPlace, Environment env)
+                throws TemplateException {
+            if (args.length == 0) {
                 return this;
             }
             AncestorSequence result = new AncestorSequence(env);
-            for (int i = 0; i < size(); i++) {
-                TemplateNodeModel tnm = (TemplateNodeModel) get(i);
+            for (int seqIdx = 0; seqIdx < size(); seqIdx++) {
+                TemplateNodeModel tnm = (TemplateNodeModel) get(seqIdx);
                 String nodeName = tnm.getNodeName();
                 String nsURI = tnm.getNodeNamespace();
                 if (nsURI == null) {
-                    if (names.contains(nodeName)) {
-                        result.add(tnm);
+                    for (int argIdx = 0; argIdx < args.length; argIdx++) {
+                        String name = _CallableUtils.castArgToString(args, argIdx);
+                        if (name.equals(nodeName)) {
+                            result.add(tnm);
+                            break;
+                        }
                     }
                 } else {
-                    for (int j = 0; j < names.size(); j++) {
-                        if (_StringUtil.matchesQName(_CallableUtils.castArgToString(names, j), nodeName, nsURI, env)) {
+                    for (int argIdx = 0; argIdx < args.length; argIdx++) {
+                        if (_StringUtil.matchesQName(
+                                _CallableUtils.castArgToString(args, argIdx), nodeName, nsURI, env)) {
                             result.add(tnm);
                             break;
                         }
@@ -150,5 +155,10 @@ class BuiltInsForNodes {
             }
             return result;
         }
-    }    
+
+        @Override
+        public ArgumentArrayLayout getFunctionArgumentArrayLayout() {
+            return null;
+        }
+    }
 }
\ 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/BuiltInsForSequences.java
----------------------------------------------------------------------
diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/BuiltInsForSequences.java b/freemarker-core/src/main/java/org/apache/freemarker/core/BuiltInsForSequences.java
index 9406d33..d8cfc91 100644
--- a/freemarker-core/src/main/java/org/apache/freemarker/core/BuiltInsForSequences.java
+++ b/freemarker-core/src/main/java/org/apache/freemarker/core/BuiltInsForSequences.java
@@ -25,15 +25,15 @@ import java.util.ArrayList;
 import java.util.Collections;
 import java.util.Comparator;
 import java.util.Date;
-import java.util.List;
 
 import org.apache.freemarker.core.arithmetic.ArithmeticEngine;
+import org.apache.freemarker.core.model.ArgumentArrayLayout;
 import org.apache.freemarker.core.model.Constants;
 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.TemplateModelException;
 import org.apache.freemarker.core.model.TemplateModelIterator;
@@ -54,7 +54,7 @@ class BuiltInsForSequences {
     
     static class chunkBI extends BuiltInForSequence {
 
-        private class BIMethod implements TemplateMethodModel {
+        private class BIMethod implements TemplateFunctionModel {
             
             private final TemplateSequenceModel tsm;
 
@@ -63,14 +63,16 @@ class BuiltInsForSequences {
             }
 
             @Override
-            public TemplateModel execute(List<? extends TemplateModel> args) throws TemplateModelException {
-                checkMethodArgCount(args, 1, 2);
+            public TemplateModel execute(TemplateModel[] args, CallPlace callPlace, Environment env)
+                    throws TemplateException {
                 int chunkSize = getNumberMethodArg(args, 0).intValue();
-                
-                return new ChunkedSequence(
-                        tsm,
-                        chunkSize,
-                        args.size() > 1 ? (TemplateModel) args.get(1) : null);
+
+                return new ChunkedSequence(tsm, chunkSize, args[1]);
+            }
+
+            @Override
+            public ArgumentArrayLayout getFunctionArgumentArrayLayout() {
+                return ArgumentArrayLayout.TWO_POSITIONAL_PARAMETERS;
             }
         }
 
@@ -182,7 +184,7 @@ class BuiltInsForSequences {
 
     static class joinBI extends ASTExpBuiltIn {
         
-        private class BIMethodForCollection implements TemplateMethodModel {
+        private class BIMethodForCollection implements TemplateFunctionModel {
             
             private final Environment env;
             private final TemplateCollectionModel coll;
@@ -193,17 +195,16 @@ class BuiltInsForSequences {
             }
 
             @Override
-            public TemplateModel execute(List<? extends TemplateModel> args)
-                    throws TemplateModelException {
-                checkMethodArgCount(args, 1, 3);
+            public TemplateModel execute(TemplateModel[] args, CallPlace callPlace, Environment env)
+                    throws TemplateException {
                 final String separator = getStringMethodArg(args, 0);
-                final String whenEmpty = getOptStringMethodArg(args, 1);
-                final String afterLast = getOptStringMethodArg(args, 2);
-                
+                final String whenEmpty = getStringMethodArg(args, 1, true);
+                final String afterLast = getStringMethodArg(args, 2, true);
+
                 StringBuilder sb = new StringBuilder();
-                
+
                 TemplateModelIterator it = coll.iterator();
-                
+
                 int idx = 0;
                 boolean hadItem = false;
                 while (it.hasNext()) {
@@ -232,7 +233,12 @@ class BuiltInsForSequences {
                     if (whenEmpty != null) sb.append(whenEmpty);
                 }
                 return new SimpleScalar(sb.toString());
-           }
+            }
+
+            @Override
+            public ArgumentArrayLayout getFunctionArgumentArrayLayout() {
+                return ArgumentArrayLayout.THREE_POSITIONAL_PARAMETERS;
+            }
 
         }
 
@@ -295,7 +301,7 @@ class BuiltInsForSequences {
     }
 
     static class seq_containsBI extends ASTExpBuiltIn {
-        private class BIMethodForCollection implements TemplateMethodModel {
+        private class BIMethodForCollection implements TemplateFunctionModel {
             private TemplateCollectionModel m_coll;
             private Environment m_env;
 
@@ -305,10 +311,9 @@ class BuiltInsForSequences {
             }
 
             @Override
-            public TemplateModel execute(List<? extends TemplateModel> args)
-                    throws TemplateModelException {
-                checkMethodArgCount(args, 1);
-                TemplateModel arg = args.get(0);
+            public TemplateModel execute(TemplateModel[] args, CallPlace callPlace, Environment env)
+                    throws TemplateException {
+                TemplateModel arg = args[0];
                 TemplateModelIterator it = m_coll.iterator();
                 int idx = 0;
                 while (it.hasNext()) {
@@ -319,9 +324,14 @@ class BuiltInsForSequences {
                 return TemplateBooleanModel.FALSE;
             }
 
+            @Override
+            public ArgumentArrayLayout getFunctionArgumentArrayLayout() {
+                return ArgumentArrayLayout.SINGLE_POSITIONAL_PARAMETER;
+            }
+
         }
 
-        private class BIMethodForSequence implements TemplateMethodModel {
+        private class BIMethodForSequence implements TemplateFunctionModel {
             private TemplateSequenceModel m_seq;
             private Environment m_env;
 
@@ -331,10 +341,9 @@ class BuiltInsForSequences {
             }
 
             @Override
-            public TemplateModel execute(List<? extends TemplateModel> args)
-                    throws TemplateModelException {
-                checkMethodArgCount(args, 1);
-                TemplateModel arg = args.get(0);
+            public TemplateModel execute(TemplateModel[] args, CallPlace callPlace, Environment env)
+                    throws TemplateException {
+                TemplateModel arg = args[0];
                 int size = m_seq.size();
                 for (int i = 0; i < size; i++) {
                     if (modelsEqual(i, m_seq.get(i), arg, m_env))
@@ -343,6 +352,11 @@ class BuiltInsForSequences {
                 return TemplateBooleanModel.FALSE;
             }
 
+            @Override
+            public ArgumentArrayLayout getFunctionArgumentArrayLayout() {
+                return ArgumentArrayLayout.SINGLE_POSITIONAL_PARAMETER;
+            }
+
         }
     
         @Override
@@ -364,7 +378,7 @@ class BuiltInsForSequences {
     
     static class seq_index_ofBI extends ASTExpBuiltIn {
         
-        private class BIMethod implements TemplateMethodModel {
+        private class BIMethod implements TemplateFunctionModel {
             
             final TemplateSequenceModel m_seq;
             final TemplateCollectionModel m_col;
@@ -395,21 +409,20 @@ class BuiltInsForSequences {
             }
 
             @Override
-            public final TemplateModel execute(List<? extends TemplateModel> args)
-                    throws TemplateModelException {
-                int argCnt = args.size();
-                checkMethodArgCount(argCnt, 1, 2);
-                
-                TemplateModel target = args.get(0);
+            public TemplateModel execute(TemplateModel[] args, CallPlace callPlace, Environment env)
+                    throws TemplateException {
+                TemplateModel target = args[0];
+                Number startIndex = getNumberMethodArg(args, 1, true);
                 int foundAtIdx;
-                if (argCnt > 1) {
-                    int startIndex = getNumberMethodArg(args, 1).intValue();
+                if (startIndex != null) {
+                    // TODO [FM3] Prefer Col?
                     // In 2.3.x only, we prefer TemplateSequenceModel for
                     // backward compatibility:
                     foundAtIdx = m_seq != null
-                            ? findInSeq(target, startIndex)
-                            : findInCol(target, startIndex);
+                            ? findInSeq(target, startIndex.intValue())
+                            : findInCol(target, startIndex.intValue());
                 } else {
+                    // TODO [FM3] Prefer Col?
                     // In 2.3.x only, we prefer TemplateSequenceModel for
                     // backward compatibility:
                     foundAtIdx = m_seq != null
@@ -418,7 +431,12 @@ class BuiltInsForSequences {
                 }
                 return foundAtIdx == -1 ? Constants.MINUS_ONE : new SimpleNumber(foundAtIdx);
             }
-            
+
+            @Override
+            public ArgumentArrayLayout getFunctionArgumentArrayLayout() {
+                return ArgumentArrayLayout.TWO_POSITIONAL_PARAMETERS;
+            }
+
             int findInCol(TemplateModel target) throws TemplateModelException {
                 return findInCol(target, 0, Integer.MAX_VALUE);
             }
@@ -525,25 +543,20 @@ class BuiltInsForSequences {
     }
 
     static class sort_byBI extends sortBI {
-        class BIMethod implements TemplateMethodModel {
+        class BIMethod implements TemplateFunctionModel {
             TemplateSequenceModel seq;
             
             BIMethod(TemplateSequenceModel seq) {
                 this.seq = seq;
             }
-            
+
             @Override
-            public TemplateModel execute(List<? extends TemplateModel> args)
-                    throws TemplateModelException {
-                // Should be:
-                // checkMethodArgCount(args, 1);
-                // But for BC:
-                if (args.size() < 1) throw MessageUtil.newArgCntError("?" + key, args.size(), 1);
-                
+            public TemplateModel execute(TemplateModel[] args, CallPlace callPlace, Environment env)
+                    throws TemplateException {
                 String[] subvars;
-                Object obj = args.get(0);
+                TemplateModel obj = args[0];
                 if (obj instanceof TemplateScalarModel) {
-                    subvars = new String[]{((TemplateScalarModel) obj).getAsString()};
+                    subvars = new String[] { ((TemplateScalarModel) obj).getAsString() };
                 } else if (obj instanceof TemplateSequenceModel) {
                     TemplateSequenceModel seq = (TemplateSequenceModel) obj;
                     int ln = seq.size();
@@ -563,7 +576,12 @@ class BuiltInsForSequences {
                             "The argument to ?", key, "(key) must be a string (the name of the subvariable), or a "
                             + "sequence of strings (the \"path\" to the subvariable).");
                 }
-                return sort(seq, subvars); 
+                return sort(seq, subvars);
+            }
+
+            @Override
+            public ArgumentArrayLayout getFunctionArgumentArrayLayout() {
+                return ArgumentArrayLayout.SINGLE_POSITIONAL_PARAMETER;
             }
         }
         


[3/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

Posted by dd...@apache.org.
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/8d5263f2/freemarker-servlet/src/main/java/org/apache/freemarker/servlet/IncludePage.java
----------------------------------------------------------------------
diff --git a/freemarker-servlet/src/main/java/org/apache/freemarker/servlet/IncludePage.java b/freemarker-servlet/src/main/java/org/apache/freemarker/servlet/IncludePage.java
index c500c8c..0f2a061 100644
--- a/freemarker-servlet/src/main/java/org/apache/freemarker/servlet/IncludePage.java
+++ b/freemarker-servlet/src/main/java/org/apache/freemarker/servlet/IncludePage.java
@@ -185,7 +185,7 @@ public class IncludePage implements TemplateDirectiveModel {
     }
 
     @Override
-    public ArgumentArrayLayout getArgumentArrayLayout() {
+    public ArgumentArrayLayout getDirectiveArgumentArrayLayout() {
         return ARGS_LAYOUT;
     }
 

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/8d5263f2/freemarker-servlet/src/main/java/org/apache/freemarker/servlet/jsp/CustomTagAndELFunctionCombiner.java
----------------------------------------------------------------------
diff --git a/freemarker-servlet/src/main/java/org/apache/freemarker/servlet/jsp/CustomTagAndELFunctionCombiner.java b/freemarker-servlet/src/main/java/org/apache/freemarker/servlet/jsp/CustomTagAndELFunctionCombiner.java
deleted file mode 100644
index ff9db12..0000000
--- a/freemarker-servlet/src/main/java/org/apache/freemarker/servlet/jsp/CustomTagAndELFunctionCombiner.java
+++ /dev/null
@@ -1,157 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *   http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.apache.freemarker.servlet.jsp;
-
-import java.io.IOException;
-import java.io.Writer;
-import java.util.List;
-
-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.CallPlace;
-import org.apache.freemarker.core.model.TemplateDirectiveModel;
-import org.apache.freemarker.core.model.TemplateMethodModel;
-import org.apache.freemarker.core.model.TemplateModel;
-import org.apache.freemarker.core.model.TemplateModelException;
-import org.apache.freemarker.core.model.impl.JavaMethodModel;
-import org.apache.freemarker.core.util.BugException;
-import org.apache.freemarker.core.util._ClassUtil;
-
-/**
- * Used when a custom JSP tag and an EL function uses the same name in a tag library, to invoke a single FTL value from
- * the two. As FTL as no separate namespace for "tags" and functions, both aspect has to be implemented by the same
- * value.
- */
-@SuppressWarnings("rawtypes")
-class CustomTagAndELFunctionCombiner {
-
-    /**
-     * @param customTag
-     *            A {@link TemplateDirectiveModel}.
-     */
-    static TemplateModel combine(TemplateModel customTag, TemplateMethodModel elFunction) {
-        if (customTag instanceof TemplateDirectiveModel) {
-            return elFunction instanceof JavaMethodModel //
-                    ? new TemplateDirectiveModelAndSimpleMethodModel( //
-                            (TemplateDirectiveModel) customTag, (JavaMethodModel) elFunction) //
-                    : new TemplateDirectiveModelAndTemplateMethodModelEx( //
-                            (TemplateDirectiveModel) customTag, elFunction);
-        } else {
-            throw new BugException(
-                    "Unexpected custom JSP tag class: " + _ClassUtil.getShortClassNameOfObject(customTag));
-        }
-    }
-
-    /**
-     * Tells if the value can be used as the "custom tag" parameter to
-     * {@link #combine(TemplateModel, TemplateMethodModel)}.
-     */
-    static boolean canBeCombinedAsCustomTag(TemplateModel tm) {
-        return (tm instanceof TemplateDirectiveModel) && !(tm instanceof CombinedTemplateModel);
-    }
-
-    /**
-     * Tells if the value can be used as the "EL function" parameter to
-     * {@link #combine(TemplateModel, TemplateMethodModel)}.
-     */
-    static boolean canBeCombinedAsELFunction(TemplateModel tm) {
-        return tm instanceof TemplateMethodModel && !(tm instanceof CombinedTemplateModel);
-    }
-
-    private static class CombinedTemplateModel {
-        // Marker only
-    }
-
-    private static class TemplateDirectiveModelAndSimpleMethodModel extends CombinedTemplateModel
-            implements TemplateDirectiveModel, TemplateMethodModel,
-            _UnexpectedTypeErrorExplainerTemplateModel {
-
-        private final TemplateDirectiveModel templateDirectiveModel;
-        private final JavaMethodModel simpleMethodModel;
-
-        public TemplateDirectiveModelAndSimpleMethodModel( //
-                TemplateDirectiveModel templateDirectiveModel, JavaMethodModel simpleMethodModel) {
-            this.templateDirectiveModel = templateDirectiveModel;
-            this.simpleMethodModel = simpleMethodModel;
-        }
-
-        @Override
-        public TemplateModel execute(List<? extends TemplateModel> args) throws TemplateModelException {
-            return simpleMethodModel.execute(args);
-        }
-
-        @Override
-        public Object[] explainTypeError(Class[] expectedClasses) {
-            return simpleMethodModel.explainTypeError(expectedClasses);
-        }
-
-        @Override
-        public void execute(TemplateModel[] args, CallPlace callPlace, Writer out, Environment env)
-                throws TemplateException, IOException {
-            templateDirectiveModel.execute(args, callPlace, out, env);
-        }
-
-        @Override
-        public ArgumentArrayLayout getArgumentArrayLayout() {
-            return templateDirectiveModel.getArgumentArrayLayout();
-        }
-
-        @Override
-        public boolean isNestedContentSupported() {
-            return templateDirectiveModel.isNestedContentSupported();
-        }
-    }
-
-    private static class TemplateDirectiveModelAndTemplateMethodModelEx extends CombinedTemplateModel
-            implements TemplateDirectiveModel, TemplateMethodModel {
-
-        private final TemplateDirectiveModel templateDirectiveModel;
-        private final TemplateMethodModel templateMethodModelEx;
-
-        public TemplateDirectiveModelAndTemplateMethodModelEx( //
-                TemplateDirectiveModel templateDirectiveModel, TemplateMethodModel templateMethodModelEx) {
-            this.templateDirectiveModel = templateDirectiveModel;
-            this.templateMethodModelEx = templateMethodModelEx;
-        }
-
-        @Override
-        public TemplateModel execute(List<? extends TemplateModel> args) throws TemplateException {
-            return templateMethodModelEx.execute(args);
-        }
-
-        @Override
-        public void execute(TemplateModel[] args, CallPlace callPlace, Writer out, Environment env)
-                throws TemplateException, IOException {
-            templateDirectiveModel.execute(args, callPlace, out, env);
-        }
-
-        @Override
-        public ArgumentArrayLayout getArgumentArrayLayout() {
-            return templateDirectiveModel.getArgumentArrayLayout();
-        }
-
-        @Override
-        public boolean isNestedContentSupported() {
-            return templateDirectiveModel.isNestedContentSupported();
-        }
-    }
-
-}

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/8d5263f2/freemarker-servlet/src/main/java/org/apache/freemarker/servlet/jsp/JspTagModelBase.java
----------------------------------------------------------------------
diff --git a/freemarker-servlet/src/main/java/org/apache/freemarker/servlet/jsp/JspTagModelBase.java b/freemarker-servlet/src/main/java/org/apache/freemarker/servlet/jsp/JspTagModelBase.java
index 99e8272..01418ba 100644
--- a/freemarker-servlet/src/main/java/org/apache/freemarker/servlet/jsp/JspTagModelBase.java
+++ b/freemarker-servlet/src/main/java/org/apache/freemarker/servlet/jsp/JspTagModelBase.java
@@ -29,10 +29,16 @@ import java.math.BigDecimal;
 import java.util.HashMap;
 import java.util.Map;
 
+import javax.servlet.jsp.JspException;
+import javax.servlet.jsp.tagext.DynamicAttributes;
+import javax.servlet.jsp.tagext.JspTag;
+
 import org.apache.freemarker.core.Template;
+import org.apache.freemarker.core.TemplateException;
 import org.apache.freemarker.core._DelayedJQuote;
 import org.apache.freemarker.core._DelayedShortClassName;
 import org.apache.freemarker.core._ErrorDescriptionBuilder;
+import org.apache.freemarker.core._MiscTemplateException;
 import org.apache.freemarker.core._TemplateModelException;
 import org.apache.freemarker.core.model.ObjectWrapperAndUnwrapper;
 import org.apache.freemarker.core.model.TemplateHashModelEx2;
@@ -45,7 +51,6 @@ import org.apache.freemarker.servlet.jsp.SimpleTagDirectiveModel.TemplateExcepti
 class JspTagModelBase {
     protected final String tagName;
     private final Class tagClass;
-    private final Method dynaSetter;
     private final Map propertySetters = new HashMap();
     
     protected JspTagModelBase(String tagName, Class tagClass) throws IntrospectionException {
@@ -59,24 +64,14 @@ class JspTagModelBase {
                 propertySetters.put(pd.getName(), m);
             }
         }
-        // Check to see if the tag implements the JSP2.0 DynamicAttributes
-        // interface, to allow setting of arbitrary attributes
-        Method dynaSetter;
-        try {
-            dynaSetter = tagClass.getMethod("setDynamicAttribute",
-                    String.class, String.class, Object.class);
-        } catch (NoSuchMethodException nsme) {
-            dynaSetter = null;
-        }
-        this.dynaSetter = dynaSetter;
     }
     
     Object getTagInstance() throws IllegalAccessException, InstantiationException {
         return tagClass.newInstance();
     }
     
-    void setupTag(Object tag, TemplateHashModelEx2 args, ObjectWrapperAndUnwrapper wrapper)
-            throws TemplateModelException,
+    void setupTag(JspTag tag, TemplateHashModelEx2 args, ObjectWrapperAndUnwrapper wrapper)
+            throws TemplateException,
         InvocationTargetException, 
         IllegalAccessException {
         if (args != null && !args.isEmpty()) {
@@ -88,12 +83,18 @@ class JspTagModelBase {
                 final String paramName = ((TemplateScalarModel) entry.getKey()).getAsString();
                 Method setterMethod = (Method) propertySetters.get(paramName);
                 if (setterMethod == null) {
-                    if (dynaSetter == null) {
+                    if (tag instanceof DynamicAttributes) {
+                        try {
+                            ((DynamicAttributes) tag).setDynamicAttribute(null, paramName, argArray[0]);
+                        } catch (JspException e) {
+                            throw new _MiscTemplateException(
+                                    "Failed to set JSP tag dynamic attribute ", new _DelayedJQuote(paramName), ".",
+                                    e);
+                        }
+                    } else {
                         throw new TemplateModelException("Unknown property "
                                 + _StringUtil.jQuote(paramName.toString())
                                 + " on instance of " + tagClass.getName());
-                    } else {
-                        dynaSetter.invoke(tag, null, paramName, argArray[0]);
                     }
                 } else {
                     if (arg instanceof BigDecimal) {

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/8d5263f2/freemarker-servlet/src/main/java/org/apache/freemarker/servlet/jsp/SimpleTagDirectiveModel.java
----------------------------------------------------------------------
diff --git a/freemarker-servlet/src/main/java/org/apache/freemarker/servlet/jsp/SimpleTagDirectiveModel.java b/freemarker-servlet/src/main/java/org/apache/freemarker/servlet/jsp/SimpleTagDirectiveModel.java
index 6035a1f..b4f4a15 100644
--- a/freemarker-servlet/src/main/java/org/apache/freemarker/servlet/jsp/SimpleTagDirectiveModel.java
+++ b/freemarker-servlet/src/main/java/org/apache/freemarker/servlet/jsp/SimpleTagDirectiveModel.java
@@ -107,7 +107,7 @@ class SimpleTagDirectiveModel extends JspTagModelBase implements TemplateDirecti
     }
 
     @Override
-    public ArgumentArrayLayout getArgumentArrayLayout() {
+    public ArgumentArrayLayout getDirectiveArgumentArrayLayout() {
         return ARGS_LAYOUT;
     }
 

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/8d5263f2/freemarker-servlet/src/main/java/org/apache/freemarker/servlet/jsp/TagDirectiveModel.java
----------------------------------------------------------------------
diff --git a/freemarker-servlet/src/main/java/org/apache/freemarker/servlet/jsp/TagDirectiveModel.java b/freemarker-servlet/src/main/java/org/apache/freemarker/servlet/jsp/TagDirectiveModel.java
index ff5b435..15db96d 100644
--- a/freemarker-servlet/src/main/java/org/apache/freemarker/servlet/jsp/TagDirectiveModel.java
+++ b/freemarker-servlet/src/main/java/org/apache/freemarker/servlet/jsp/TagDirectiveModel.java
@@ -117,7 +117,7 @@ class TagDirectiveModel extends JspTagModelBase implements TemplateDirectiveMode
     }
 
     @Override
-    public ArgumentArrayLayout getArgumentArrayLayout() {
+    public ArgumentArrayLayout getDirectiveArgumentArrayLayout() {
         return ARGS_LAYOUT;
     }
 

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/8d5263f2/freemarker-servlet/src/main/java/org/apache/freemarker/servlet/jsp/TaglibFactory.java
----------------------------------------------------------------------
diff --git a/freemarker-servlet/src/main/java/org/apache/freemarker/servlet/jsp/TaglibFactory.java b/freemarker-servlet/src/main/java/org/apache/freemarker/servlet/jsp/TaglibFactory.java
index 98634bd..a1525af 100644
--- a/freemarker-servlet/src/main/java/org/apache/freemarker/servlet/jsp/TaglibFactory.java
+++ b/freemarker-servlet/src/main/java/org/apache/freemarker/servlet/jsp/TaglibFactory.java
@@ -65,8 +65,8 @@ import org.apache.freemarker.core.ConfigurationException;
 import org.apache.freemarker.core.Environment;
 import org.apache.freemarker.core.model.ObjectWrapper;
 import org.apache.freemarker.core.model.TemplateDirectiveModel;
+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.TemplateModelException;
 import org.apache.freemarker.core.model.impl.DefaultObjectWrapper;
@@ -233,7 +233,7 @@ public class TaglibFactory implements TemplateHashModel {
      * 
      * @return a {@link TemplateHashModel} representing the JSP taglib. Each element of this hash represents a single
      *         custom tag or EL function from the library, implemented as a {@link TemplateDirectiveModel} or
-     *         {@link TemplateMethodModel}, respectively.
+     *         {@link TemplateFunctionModel}, respectively.
      */
     @Override
     public TemplateModel get(final String taglibUri) throws TemplateModelException {
@@ -1759,7 +1759,7 @@ public class TaglibFactory implements TemplateHashModel {
 
                     final Class<?> tagClass = resoveClassFromTLD(tagClassCData, "custom tag", tagNameCData);
 
-                    final TemplateModel customTagModel;
+                    final TemplateDirectiveModel customTagModel;
                     try {
                         if (Tag.class.isAssignableFrom(tagClass)) {
                             customTagModel = new TagDirectiveModel(tagNameCData, tagClass);
@@ -1775,9 +1775,10 @@ public class TaglibFactory implements TemplateHashModel {
 
                     TemplateModel replacedTagOrFunction = tagsAndFunctions.put(tagNameCData, customTagModel);
                     if (replacedTagOrFunction != null) {
-                        if (CustomTagAndELFunctionCombiner.canBeCombinedAsELFunction(replacedTagOrFunction)) {
-                            tagsAndFunctions.put(tagNameCData, CustomTagAndELFunctionCombiner.combine(
-                                    customTagModel, (TemplateMethodModel) replacedTagOrFunction));
+                        if (replacedTagOrFunction instanceof TemplateFunctionModel
+                                && !(replacedTagOrFunction instanceof  TemplateDirectiveModelAndTemplateFunctionModel)) {
+                            tagsAndFunctions.put(tagNameCData, new TemplateDirectiveModelAndTemplateFunctionModel(
+                                    customTagModel, (TemplateFunctionModel) replacedTagOrFunction));
                         } else {
                             if (LOG.isWarnEnabled()) {
                                 LOG.warn("TLD contains multiple tags with name " + _StringUtil.jQuote(tagNameCData)
@@ -1816,7 +1817,7 @@ public class TaglibFactory implements TemplateHashModel {
                                 locator);
                     }
 
-                    final TemplateMethodModel elFunctionModel;
+                    final TemplateFunctionModel elFunctionModel;
                     try {
                         elFunctionModel = defaultObjectWrapper.wrap(null, functionMethod);
                     } catch (Exception e) {
@@ -1827,9 +1828,10 @@ public class TaglibFactory implements TemplateHashModel {
 
                     TemplateModel replacedTagOrFunction = tagsAndFunctions.put(functionNameCData, elFunctionModel);
                     if (replacedTagOrFunction != null) {
-                        if (CustomTagAndELFunctionCombiner.canBeCombinedAsCustomTag(replacedTagOrFunction)) {
-                            tagsAndFunctions.put(functionNameCData, CustomTagAndELFunctionCombiner.combine(
-                                    replacedTagOrFunction, elFunctionModel));
+                        if (replacedTagOrFunction instanceof  TemplateDirectiveModel
+                                && !(replacedTagOrFunction instanceof  TemplateDirectiveModelAndTemplateFunctionModel)) {
+                            tagsAndFunctions.put(functionNameCData, new TemplateDirectiveModelAndTemplateFunctionModel(
+                                    (TemplateDirectiveModel) replacedTagOrFunction, elFunctionModel));
                         } else {
                             if (LOG.isWarnEnabled()) {
                                 LOG.warn("TLD contains multiple functions with name "

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/8d5263f2/freemarker-servlet/src/main/java/org/apache/freemarker/servlet/jsp/TemplateDirectiveModelAndTemplateFunctionModel.java
----------------------------------------------------------------------
diff --git a/freemarker-servlet/src/main/java/org/apache/freemarker/servlet/jsp/TemplateDirectiveModelAndTemplateFunctionModel.java b/freemarker-servlet/src/main/java/org/apache/freemarker/servlet/jsp/TemplateDirectiveModelAndTemplateFunctionModel.java
new file mode 100644
index 0000000..e4d74d1
--- /dev/null
+++ b/freemarker-servlet/src/main/java/org/apache/freemarker/servlet/jsp/TemplateDirectiveModelAndTemplateFunctionModel.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.servlet.jsp;
+
+import java.io.IOException;
+import java.io.Writer;
+
+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.TemplateDirectiveModel;
+import org.apache.freemarker.core.model.TemplateFunctionModel;
+import org.apache.freemarker.core.model.TemplateModel;
+
+/**
+ * Used when a custom JSP tag and an EL function uses the same name in a tag library, to invoke a single FTL value from
+ * the two. As FTL as no separate namespace for "tags" and functions, both aspect has to be implemented by the same
+ * value.
+ */
+class TemplateDirectiveModelAndTemplateFunctionModel
+        implements TemplateDirectiveModel, TemplateFunctionModel {
+
+    private final TemplateDirectiveModel templateDirectiveModel;
+    private final TemplateFunctionModel templateFunctionModel;
+
+    TemplateDirectiveModelAndTemplateFunctionModel( //
+            TemplateDirectiveModel templateDirectiveModel, TemplateFunctionModel templateMethodModelEx) {
+        this.templateDirectiveModel = templateDirectiveModel;
+        this.templateFunctionModel = templateMethodModelEx;
+    }
+
+    @Override
+    public void execute(TemplateModel[] args, CallPlace callPlace, Writer out, Environment env)
+            throws TemplateException, IOException {
+        templateDirectiveModel.execute(args, callPlace, out, env);
+    }
+
+    @Override
+    public ArgumentArrayLayout getDirectiveArgumentArrayLayout() {
+        return templateDirectiveModel.getDirectiveArgumentArrayLayout();
+    }
+
+    @Override
+    public boolean isNestedContentSupported() {
+        return templateDirectiveModel.isNestedContentSupported();
+    }
+
+    @Override
+    public ArgumentArrayLayout getFunctionArgumentArrayLayout() {
+        return templateFunctionModel.getFunctionArgumentArrayLayout();
+    }
+
+    @Override
+    public TemplateModel execute(TemplateModel[] args, CallPlace callPlace, Environment env)
+            throws TemplateException {
+        return templateFunctionModel.execute(args, callPlace, env);
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/8d5263f2/freemarker-servlet/src/test/java/org/apache/freemarker/servlet/jsp/TLDParsingTest.java
----------------------------------------------------------------------
diff --git a/freemarker-servlet/src/test/java/org/apache/freemarker/servlet/jsp/TLDParsingTest.java b/freemarker-servlet/src/test/java/org/apache/freemarker/servlet/jsp/TLDParsingTest.java
index 43285c5..3cc6993 100644
--- a/freemarker-servlet/src/test/java/org/apache/freemarker/servlet/jsp/TLDParsingTest.java
+++ b/freemarker-servlet/src/test/java/org/apache/freemarker/servlet/jsp/TLDParsingTest.java
@@ -23,7 +23,6 @@ import static org.junit.Assert.*;
 
 import java.io.InputStream;
 import java.net.URL;
-import java.util.Arrays;
 import java.util.Map;
 
 import javax.servlet.ServletContextEvent;
@@ -33,9 +32,11 @@ import javax.servlet.jsp.tagext.TagSupport;
 import javax.xml.parsers.SAXParserFactory;
 
 import org.apache.freemarker.core.Configuration;
-import org.apache.freemarker.core.model.TemplateMethodModel;
+import org.apache.freemarker.core.NonTemplateCallPlace;
+import org.apache.freemarker.core.model.TemplateModel;
 import org.apache.freemarker.core.model.TemplateScalarModel;
 import org.apache.freemarker.core.model.impl.DefaultObjectWrapper;
+import org.apache.freemarker.core.model.impl.JavaMethodModel;
 import org.apache.freemarker.core.model.impl.SimpleScalar;
 import org.junit.Before;
 import org.junit.Test;
@@ -74,7 +75,7 @@ public class TLDParsingTest {
         assertEquals(1, tldParser.getListeners().size());
         assertTrue(tldParser.getListeners().get(0) instanceof ExampleContextListener);
 
-        Map tagsAndFunctions = tldParser.getTagsAndFunctions();
+        Map<String, TemplateModel> tagsAndFunctions = tldParser.getTagsAndFunctions();
         assertEquals(4, tagsAndFunctions.size());
 
         JspTagModelBase tag = (JspTagModelBase) tagsAndFunctions.get("setStringAttributeTag");
@@ -82,13 +83,15 @@ public class TLDParsingTest {
         tag = (JspTagModelBase) tagsAndFunctions.get("setStringAttributeTag2");
         assertNotNull(tag);
 
-        TemplateMethodModel function = (TemplateMethodModel) tagsAndFunctions.get("toUpperCase");
+        JavaMethodModel function = (JavaMethodModel) tagsAndFunctions.get("toUpperCase");
         assertNotNull(function);
-        TemplateScalarModel result = (TemplateScalarModel) function.execute(Arrays.asList(new SimpleScalar("abc")));
+        TemplateScalarModel result = (TemplateScalarModel) function.execute(
+                new TemplateModel[] { new SimpleScalar("abc") }, NonTemplateCallPlace.INSTANCE);
         assertEquals("ABC", result.getAsString());
-        function = (TemplateMethodModel) tagsAndFunctions.get("toUpperCase2");
+        function = (JavaMethodModel) tagsAndFunctions.get("toUpperCase2");
         assertNotNull(function);
-        result = (TemplateScalarModel) function.execute(Arrays.asList(new SimpleScalar("abc")));
+        result = (TemplateScalarModel) function.execute(
+                new TemplateModel[] { new SimpleScalar("abc") }, NonTemplateCallPlace.INSTANCE);
         assertEquals("ABC", result.getAsString());
     }
 

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/8d5263f2/freemarker-test-utils/src/main/java/org/apache/freemarker/test/templateutil/AssertDirective.java
----------------------------------------------------------------------
diff --git a/freemarker-test-utils/src/main/java/org/apache/freemarker/test/templateutil/AssertDirective.java b/freemarker-test-utils/src/main/java/org/apache/freemarker/test/templateutil/AssertDirective.java
index 2e7ecfc..eb6930b 100644
--- a/freemarker-test-utils/src/main/java/org/apache/freemarker/test/templateutil/AssertDirective.java
+++ b/freemarker-test-utils/src/main/java/org/apache/freemarker/test/templateutil/AssertDirective.java
@@ -56,7 +56,7 @@ public class AssertDirective implements TemplateDirectiveModel {
     }
 
     @Override
-    public ArgumentArrayLayout getArgumentArrayLayout() {
+    public ArgumentArrayLayout getDirectiveArgumentArrayLayout() {
         return ArgumentArrayLayout.SINGLE_POSITIONAL_PARAMETER;
     }
 

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/8d5263f2/freemarker-test-utils/src/main/java/org/apache/freemarker/test/templateutil/AssertEqualsDirective.java
----------------------------------------------------------------------
diff --git a/freemarker-test-utils/src/main/java/org/apache/freemarker/test/templateutil/AssertEqualsDirective.java b/freemarker-test-utils/src/main/java/org/apache/freemarker/test/templateutil/AssertEqualsDirective.java
index 6734fbc..51baeb4 100644
--- a/freemarker-test-utils/src/main/java/org/apache/freemarker/test/templateutil/AssertEqualsDirective.java
+++ b/freemarker-test-utils/src/main/java/org/apache/freemarker/test/templateutil/AssertEqualsDirective.java
@@ -78,7 +78,7 @@ public class AssertEqualsDirective implements TemplateDirectiveModel {
     }
 
     @Override
-    public ArgumentArrayLayout getArgumentArrayLayout() {
+    public ArgumentArrayLayout getDirectiveArgumentArrayLayout() {
         return ARGS_LAYOUT;
     }
 

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/8d5263f2/freemarker-test-utils/src/main/java/org/apache/freemarker/test/templateutil/AssertFailsDirective.java
----------------------------------------------------------------------
diff --git a/freemarker-test-utils/src/main/java/org/apache/freemarker/test/templateutil/AssertFailsDirective.java b/freemarker-test-utils/src/main/java/org/apache/freemarker/test/templateutil/AssertFailsDirective.java
index e8120fc..6fe1505 100644
--- a/freemarker-test-utils/src/main/java/org/apache/freemarker/test/templateutil/AssertFailsDirective.java
+++ b/freemarker-test-utils/src/main/java/org/apache/freemarker/test/templateutil/AssertFailsDirective.java
@@ -136,7 +136,7 @@ public class AssertFailsDirective implements TemplateDirectiveModel {
     }
 
     @Override
-    public ArgumentArrayLayout getArgumentArrayLayout() {
+    public ArgumentArrayLayout getDirectiveArgumentArrayLayout() {
         return ARGS_LAYOUT;
     }
 

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/8d5263f2/freemarker-test-utils/src/main/java/org/apache/freemarker/test/templateutil/NoOutputDirective.java
----------------------------------------------------------------------
diff --git a/freemarker-test-utils/src/main/java/org/apache/freemarker/test/templateutil/NoOutputDirective.java b/freemarker-test-utils/src/main/java/org/apache/freemarker/test/templateutil/NoOutputDirective.java
index a8a7bee..1e33dab 100644
--- a/freemarker-test-utils/src/main/java/org/apache/freemarker/test/templateutil/NoOutputDirective.java
+++ b/freemarker-test-utils/src/main/java/org/apache/freemarker/test/templateutil/NoOutputDirective.java
@@ -45,7 +45,7 @@ public class NoOutputDirective implements TemplateDirectiveModel {
     }
 
     @Override
-    public ArgumentArrayLayout getArgumentArrayLayout() {
+    public ArgumentArrayLayout getDirectiveArgumentArrayLayout() {
         return ArgumentArrayLayout.PARAMETERLESS;
     }