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