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

[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

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);
         }