freemarker-notifications mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From ddek...@apache.org
Subject [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
Date Mon, 07 Aug 2017 22:41:32 GMT
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;
             }
         }
         


Mime
View raw message