http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/8d5263f2/freemarker-core/src/main/java/org/apache/freemarker/core/BuiltInsForStringsBasic.java ---------------------------------------------------------------------- diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/BuiltInsForStringsBasic.java b/freemarker-core/src/main/java/org/apache/freemarker/core/BuiltInsForStringsBasic.java index ce3ff56..9311626 100644 --- a/freemarker-core/src/main/java/org/apache/freemarker/core/BuiltInsForStringsBasic.java +++ b/freemarker-core/src/main/java/org/apache/freemarker/core/BuiltInsForStringsBasic.java @@ -20,13 +20,13 @@ package org.apache.freemarker.core; import java.util.ArrayList; -import java.util.List; import java.util.StringTokenizer; import java.util.regex.Matcher; import java.util.regex.Pattern; +import org.apache.freemarker.core.model.ArgumentArrayLayout; import org.apache.freemarker.core.model.TemplateBooleanModel; -import org.apache.freemarker.core.model.TemplateMethodModel; +import org.apache.freemarker.core.model.TemplateFunctionModel; import org.apache.freemarker.core.model.TemplateModel; import org.apache.freemarker.core.model.TemplateModelException; import org.apache.freemarker.core.model.TemplateScalarModel; @@ -69,20 +69,25 @@ class BuiltInsForStringsBasic { static class containsBI extends ASTExpBuiltIn { - private class BIMethod implements TemplateMethodModel { + private class BIMethod implements TemplateFunctionModel { private final String s; private BIMethod(String s) { this.s = s; } - + @Override - public TemplateModel execute(List<? extends TemplateModel> args) throws TemplateModelException { - checkMethodArgCount(args, 1); - return s.contains(getStringMethodArg(args, 0)) + public TemplateModel execute(TemplateModel[] args, CallPlace callPlace, Environment env) + throws TemplateException { + return s.contains(_CallableUtils.castArgToString(args, 0)) ? TemplateBooleanModel.TRUE : TemplateBooleanModel.FALSE; } + + @Override + public ArgumentArrayLayout getFunctionArgumentArrayLayout() { + return ArgumentArrayLayout.SINGLE_POSITIONAL_PARAMETER; + } } @Override @@ -94,19 +99,24 @@ class BuiltInsForStringsBasic { static class ends_withBI extends BuiltInForString { - private class BIMethod implements TemplateMethodModel { + private class BIMethod implements TemplateFunctionModel { private String s; private BIMethod(String s) { this.s = s; } - + @Override - public TemplateModel execute(List<? extends TemplateModel> args) throws TemplateModelException { - checkMethodArgCount(args, 1); + public TemplateModel execute(TemplateModel[] args, CallPlace callPlace, Environment env) + throws TemplateException { return s.endsWith(getStringMethodArg(args, 0)) ? TemplateBooleanModel.TRUE : TemplateBooleanModel.FALSE; } + + @Override + public ArgumentArrayLayout getFunctionArgumentArrayLayout() { + return ArgumentArrayLayout.SINGLE_POSITIONAL_PARAMETER; + } } @Override @@ -117,19 +127,24 @@ class BuiltInsForStringsBasic { static class ensure_ends_withBI extends BuiltInForString { - private class BIMethod implements TemplateMethodModel { + private class BIMethod implements TemplateFunctionModel { private String s; private BIMethod(String s) { this.s = s; } - + @Override - public TemplateModel execute(List<? extends TemplateModel> args) throws TemplateModelException { - checkMethodArgCount(args, 1); + public TemplateModel execute(TemplateModel[] args, CallPlace callPlace, Environment env) + throws TemplateException { String suffix = getStringMethodArg(args, 0); return new SimpleScalar(s.endsWith(suffix) ? s : s + suffix); } + + @Override + public ArgumentArrayLayout getFunctionArgumentArrayLayout() { + return ArgumentArrayLayout.SINGLE_POSITIONAL_PARAMETER; + } } @Override @@ -140,27 +155,28 @@ class BuiltInsForStringsBasic { static class ensure_starts_withBI extends BuiltInForString { - private class BIMethod implements TemplateMethodModel { + private class BIMethod implements TemplateFunctionModel { private String s; private BIMethod(String s) { this.s = s; } - + @Override - public TemplateModel execute(List<? extends TemplateModel> args) throws TemplateModelException { - checkMethodArgCount(args, 1, 3); - + public TemplateModel execute(TemplateModel[] args, CallPlace callPlace, Environment env) + throws TemplateException { final String checkedPrefix = getStringMethodArg(args, 0); - + final boolean startsWithPrefix; - final String addedPrefix; - if (args.size() > 1) { - addedPrefix = getStringMethodArg(args, 1); - long flags = args.size() > 2 - ? RegexpHelper.parseFlagString(getStringMethodArg(args, 2)) + final String addedPrefix; + String addedPrefixArg = getStringMethodArg(args, 1, true); + String flagsArg = getStringMethodArg(args, 2, true); + if (addedPrefixArg != null) { + addedPrefix = addedPrefixArg; + long flags = flagsArg != null + ? RegexpHelper.parseFlagString(flagsArg) : RegexpHelper.RE_FLAG_REGEXP; - + if ((flags & RegexpHelper.RE_FLAG_REGEXP) == 0) { RegexpHelper.checkOnlyHasNonRegexpFlags(key, flags, true); if ((flags & RegexpHelper.RE_FLAG_CASE_INSENSITIVE) == 0) { @@ -172,13 +188,22 @@ class BuiltInsForStringsBasic { Pattern pattern = RegexpHelper.getPattern(checkedPrefix, (int) flags); final Matcher matcher = pattern.matcher(s); startsWithPrefix = matcher.lookingAt(); - } + } } else { + if (flagsArg != null) { + throw new _MiscTemplateException( + "The 2nd parameter must be non-null when the 3rd parameter is non-null"); + } startsWithPrefix = s.startsWith(checkedPrefix); addedPrefix = checkedPrefix; } return new SimpleScalar(startsWithPrefix ? s : addedPrefix + s); } + + @Override + public ArgumentArrayLayout getFunctionArgumentArrayLayout() { + return ArgumentArrayLayout.THREE_POSITIONAL_PARAMETERS; + } } @Override @@ -189,26 +214,31 @@ class BuiltInsForStringsBasic { static class index_ofBI extends ASTExpBuiltIn { - private class BIMethod implements TemplateMethodModel { + private class BIMethod implements TemplateFunctionModel { private final String s; private BIMethod(String s) { this.s = s; } - + @Override - public TemplateModel execute(List<? extends TemplateModel> args) throws TemplateModelException { - int argCnt = args.size(); - checkMethodArgCount(argCnt, 1, 2); + public TemplateModel execute(TemplateModel[] args, CallPlace callPlace, Environment env) + throws TemplateException { String subStr = getStringMethodArg(args, 0); - if (argCnt > 1) { - int startIdx = getNumberMethodArg(args, 1).intValue(); + Number indexModel = getNumberMethodArg(args, 1, true); + if (indexModel != null) { + int startIdx = indexModel.intValue(); return new SimpleNumber(findLast ? s.lastIndexOf(subStr, startIdx) : s.indexOf(subStr, startIdx)); } else { return new SimpleNumber(findLast ? s.lastIndexOf(subStr) : s.indexOf(subStr)); } } + + @Override + public ArgumentArrayLayout getFunctionArgumentArrayLayout() { + return ArgumentArrayLayout.TWO_POSITIONAL_PARAMETERS; + } } private final boolean findLast; @@ -225,7 +255,7 @@ class BuiltInsForStringsBasic { } static class keep_afterBI extends BuiltInForString { - class KeepAfterMethod implements TemplateMethodModel { + class KeepAfterMethod implements TemplateFunctionModel { private String s; KeepAfterMethod(String s) { @@ -233,12 +263,13 @@ class BuiltInsForStringsBasic { } @Override - public TemplateModel execute(List<? extends TemplateModel> args) throws TemplateModelException { - int argCnt = args.size(); - checkMethodArgCount(argCnt, 1, 2); + public TemplateModel execute(TemplateModel[] args, CallPlace callPlace, Environment env) + throws TemplateException { String separatorString = getStringMethodArg(args, 0); - long flags = argCnt > 1 ? RegexpHelper.parseFlagString(getStringMethodArg(args, 1)) : 0; - + + String flagsStr = getStringMethodArg(args, 1, true); + long flags = flagsStr != null ? RegexpHelper.parseFlagString(flagsStr) : 0; + int startIndex; if ((flags & RegexpHelper.RE_FLAG_REGEXP) == 0) { RegexpHelper.checkOnlyHasNonRegexpFlags(key, flags, true); @@ -258,9 +289,14 @@ class BuiltInsForStringsBasic { } else { startIndex = -1; } - } + } return startIndex == -1 ? TemplateScalarModel.EMPTY_STRING : new SimpleScalar(s.substring(startIndex)); } + + @Override + public ArgumentArrayLayout getFunctionArgumentArrayLayout() { + return ArgumentArrayLayout.TWO_POSITIONAL_PARAMETERS; + } } @Override @@ -271,7 +307,7 @@ class BuiltInsForStringsBasic { } static class keep_after_lastBI extends BuiltInForString { - class KeepAfterMethod implements TemplateMethodModel { + class KeepAfterMethod implements TemplateFunctionModel { private String s; KeepAfterMethod(String s) { @@ -279,12 +315,12 @@ class BuiltInsForStringsBasic { } @Override - public TemplateModel execute(List<? extends TemplateModel> args) throws TemplateModelException { - int argCnt = args.size(); - checkMethodArgCount(argCnt, 1, 2); + public TemplateModel execute(TemplateModel[] args, CallPlace callPlace, Environment env) + throws TemplateException { String separatorString = getStringMethodArg(args, 0); - long flags = argCnt > 1 ? RegexpHelper.parseFlagString(getStringMethodArg(args, 1)) : 0; - + String flagString = getStringMethodArg(args, 1, true); + long flags = flagString != null ? RegexpHelper.parseFlagString(flagString) : 0; + int startIndex; if ((flags & RegexpHelper.RE_FLAG_REGEXP) == 0) { RegexpHelper.checkOnlyHasNonRegexpFlags(key, flags, true); @@ -311,9 +347,14 @@ class BuiltInsForStringsBasic { startIndex = -1; } } - } + } return startIndex == -1 ? TemplateScalarModel.EMPTY_STRING : new SimpleScalar(s.substring(startIndex)); } + + @Override + public ArgumentArrayLayout getFunctionArgumentArrayLayout() { + return ArgumentArrayLayout.TWO_POSITIONAL_PARAMETERS; + } } @Override @@ -324,7 +365,7 @@ class BuiltInsForStringsBasic { } static class keep_beforeBI extends BuiltInForString { - class KeepUntilMethod implements TemplateMethodModel { + class KeepUntilMethod implements TemplateFunctionModel { private String s; KeepUntilMethod(String s) { @@ -332,12 +373,12 @@ class BuiltInsForStringsBasic { } @Override - public TemplateModel execute(List<? extends TemplateModel> args) throws TemplateModelException { - int argCnt = args.size(); - checkMethodArgCount(argCnt, 1, 2); + public TemplateModel execute(TemplateModel[] args, CallPlace callPlace, Environment env) + throws TemplateException { String separatorString = getStringMethodArg(args, 0); - long flags = argCnt > 1 ? RegexpHelper.parseFlagString(getStringMethodArg(args, 1)) : 0; - + String flagString = getStringMethodArg(args, 1, true); + long flags = flagString != null ? RegexpHelper.parseFlagString(flagString) : 0; + int stopIndex; if ((flags & RegexpHelper.RE_FLAG_REGEXP) == 0) { RegexpHelper.checkOnlyHasNonRegexpFlags(key, flags, true); @@ -354,9 +395,14 @@ class BuiltInsForStringsBasic { } else { stopIndex = -1; } - } + } return stopIndex == -1 ? new SimpleScalar(s) : new SimpleScalar(s.substring(0, stopIndex)); } + + @Override + public ArgumentArrayLayout getFunctionArgumentArrayLayout() { + return ArgumentArrayLayout.TWO_POSITIONAL_PARAMETERS; + } } @Override @@ -368,7 +414,7 @@ class BuiltInsForStringsBasic { // TODO static class keep_before_lastBI extends BuiltInForString { - class KeepUntilMethod implements TemplateMethodModel { + class KeepUntilMethod implements TemplateFunctionModel { private String s; KeepUntilMethod(String s) { @@ -376,12 +422,12 @@ class BuiltInsForStringsBasic { } @Override - public TemplateModel execute(List<? extends TemplateModel> args) throws TemplateModelException { - int argCnt = args.size(); - checkMethodArgCount(argCnt, 1, 2); + public TemplateModel execute(TemplateModel[] args, CallPlace callPlace, Environment env) + throws TemplateException { String separatorString = getStringMethodArg(args, 0); - long flags = argCnt > 1 ? RegexpHelper.parseFlagString(getStringMethodArg(args, 1)) : 0; - + String flagString = getStringMethodArg(args, 1, true); + long flags = flagString != null ? RegexpHelper.parseFlagString(flagString) : 0; + int stopIndex; if ((flags & RegexpHelper.RE_FLAG_REGEXP) == 0) { RegexpHelper.checkOnlyHasNonRegexpFlags(key, flags, true); @@ -405,9 +451,14 @@ class BuiltInsForStringsBasic { stopIndex = -1; } } - } + } return stopIndex == -1 ? new SimpleScalar(s) : new SimpleScalar(s.substring(0, stopIndex)); } + + @Override + public ArgumentArrayLayout getFunctionArgumentArrayLayout() { + return ArgumentArrayLayout.TWO_POSITIONAL_PARAMETERS; + } } @Override @@ -435,23 +486,21 @@ class BuiltInsForStringsBasic { static class padBI extends BuiltInForString { - private class BIMethod implements TemplateMethodModel { + private class BIMethod implements TemplateFunctionModel { private final String s; private BIMethod(String s) { this.s = s; } - + @Override - public TemplateModel execute(List<? extends TemplateModel> args) throws TemplateModelException { - int argCnt = args.size(); - checkMethodArgCount(argCnt, 1, 2); - + public TemplateModel execute(TemplateModel[] args, CallPlace callPlace, Environment env) + throws TemplateException { int width = getNumberMethodArg(args, 0).intValue(); - - if (argCnt > 1) { - String filling = getStringMethodArg(args, 1); + + String filling = getStringMethodArg(args, 1, true); + if (filling != null) { try { return new SimpleScalar( leftPadder @@ -470,6 +519,11 @@ class BuiltInsForStringsBasic { return new SimpleScalar(leftPadder ? _StringUtil.leftPad(s, width) : _StringUtil.rightPad(s, width)); } } + + @Override + public ArgumentArrayLayout getFunctionArgumentArrayLayout() { + return ArgumentArrayLayout.TWO_POSITIONAL_PARAMETERS; + } } private final boolean leftPadder; @@ -486,19 +540,24 @@ class BuiltInsForStringsBasic { static class remove_beginningBI extends BuiltInForString { - private class BIMethod implements TemplateMethodModel { + private class BIMethod implements TemplateFunctionModel { private String s; private BIMethod(String s) { this.s = s; } - + @Override - public TemplateModel execute(List<? extends TemplateModel> args) throws TemplateModelException { - checkMethodArgCount(args, 1); + public TemplateModel execute(TemplateModel[] args, CallPlace callPlace, Environment env) + throws TemplateException { String prefix = getStringMethodArg(args, 0); return new SimpleScalar(s.startsWith(prefix) ? s.substring(prefix.length()) : s); } + + @Override + public ArgumentArrayLayout getFunctionArgumentArrayLayout() { + return ArgumentArrayLayout.SINGLE_POSITIONAL_PARAMETER; + } } @Override @@ -509,19 +568,24 @@ class BuiltInsForStringsBasic { static class remove_endingBI extends BuiltInForString { - private class BIMethod implements TemplateMethodModel { + private class BIMethod implements TemplateFunctionModel { private String s; private BIMethod(String s) { this.s = s; } - + @Override - public TemplateModel execute(List<? extends TemplateModel> args) throws TemplateModelException { - checkMethodArgCount(args, 1); + public TemplateModel execute(TemplateModel[] args, CallPlace callPlace, Environment env) + throws TemplateException { String suffix = getStringMethodArg(args, 0); return new SimpleScalar(s.endsWith(suffix) ? s.substring(0, s.length() - suffix.length()) : s); } + + @Override + public ArgumentArrayLayout getFunctionArgumentArrayLayout() { + return ArgumentArrayLayout.SINGLE_POSITIONAL_PARAMETER; + } } @Override @@ -531,7 +595,7 @@ class BuiltInsForStringsBasic { } static class split_BI extends BuiltInForString { - class SplitMethod implements TemplateMethodModel { + class SplitMethod implements TemplateFunctionModel { private String s; SplitMethod(String s) { @@ -539,12 +603,13 @@ class BuiltInsForStringsBasic { } @Override - public TemplateModel execute(List<? extends TemplateModel> args) throws TemplateException { - int argCnt = args.size(); - checkMethodArgCount(argCnt, 1, 2); - String splitString = _CallableUtils.castArgToString(args, 0); - long flags = argCnt > 1 - ? RegexpHelper.parseFlagString(_CallableUtils.castArgToString(args, 1)) + public TemplateModel execute(TemplateModel[] args, CallPlace callPlace, Environment env) + throws TemplateException { + int argCnt = args.length; + String splitString = getStringMethodArg(args, 0); + TemplateModel arg2 = args[1]; + long flags = arg2 != null + ? RegexpHelper.parseFlagString(getStringMethodArg(args, 1)) : 0; String[] result; if ((flags & RegexpHelper.RE_FLAG_REGEXP) == 0) { @@ -554,9 +619,14 @@ class BuiltInsForStringsBasic { } else { Pattern pattern = RegexpHelper.getPattern(splitString, (int) flags); result = pattern.split(s); - } + } return new NativeStringArraySequence(result); } + + @Override + public ArgumentArrayLayout getFunctionArgumentArrayLayout() { + return ArgumentArrayLayout.TWO_POSITIONAL_PARAMETERS; + } } @Override @@ -568,19 +638,24 @@ class BuiltInsForStringsBasic { static class starts_withBI extends BuiltInForString { - private class BIMethod implements TemplateMethodModel { + private class BIMethod implements TemplateFunctionModel { private String s; private BIMethod(String s) { this.s = s; } - + @Override - public TemplateModel execute(List<? extends TemplateModel> args) throws TemplateModelException { - checkMethodArgCount(args, 1); + public TemplateModel execute(TemplateModel[] args, CallPlace callPlace, Environment env) + throws TemplateException { return s.startsWith(getStringMethodArg(args, 0)) ? TemplateBooleanModel.TRUE : TemplateBooleanModel.FALSE; } + + @Override + public ArgumentArrayLayout getFunctionArgumentArrayLayout() { + return ArgumentArrayLayout.SINGLE_POSITIONAL_PARAMETER; + } } @Override @@ -593,25 +668,24 @@ class BuiltInsForStringsBasic { @Override TemplateModel calculateResult(final String s, final Environment env) throws TemplateException { - return new TemplateMethodModel() { - + return new TemplateFunctionModel() { + @Override - public TemplateModel execute(List<? extends TemplateModel> args) throws TemplateModelException { - int argCount = args.size(); - checkMethodArgCount(argCount, 1, 2); - + public TemplateModel execute(TemplateModel[] args, CallPlace callPlace, Environment env) + throws TemplateException { int beginIdx = getNumberMethodArg(args, 0).intValue(); - + final int len = s.length(); - + if (beginIdx < 0) { throw newIndexLessThan0Exception(0, beginIdx); } else if (beginIdx > len) { throw newIndexGreaterThanLengthException(0, beginIdx, len); } - - if (argCount > 1) { - int endIdx = getNumberMethodArg(args, 1).intValue(); + + Number endIdxNumber = getNumberMethodArg(args, 1, true); + if (endIdxNumber != null) { + int endIdx = endIdxNumber.intValue(); if (endIdx < 0) { throw newIndexLessThan0Exception(1, endIdx); } else if (endIdx > len) { @@ -628,7 +702,12 @@ class BuiltInsForStringsBasic { return new SimpleScalar(s.substring(beginIdx)); } } - + + @Override + public ArgumentArrayLayout getFunctionArgumentArrayLayout() { + return ArgumentArrayLayout.TWO_POSITIONAL_PARAMETERS; + } + private TemplateModelException newIndexGreaterThanLengthException( int argIdx, int idx, final int len) throws TemplateModelException { return MessageUtil.newMethodArgInvalidValueException(
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/8d5263f2/freemarker-core/src/main/java/org/apache/freemarker/core/BuiltInsForStringsEncoding.java ---------------------------------------------------------------------- diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/BuiltInsForStringsEncoding.java b/freemarker-core/src/main/java/org/apache/freemarker/core/BuiltInsForStringsEncoding.java index cee1b49..794fe94 100644 --- a/freemarker-core/src/main/java/org/apache/freemarker/core/BuiltInsForStringsEncoding.java +++ b/freemarker-core/src/main/java/org/apache/freemarker/core/BuiltInsForStringsEncoding.java @@ -22,9 +22,9 @@ package org.apache.freemarker.core; import java.io.UnsupportedEncodingException; import java.nio.charset.Charset; import java.nio.charset.UnsupportedCharsetException; -import java.util.List; -import org.apache.freemarker.core.model.TemplateMethodModel; +import org.apache.freemarker.core.model.ArgumentArrayLayout; +import org.apache.freemarker.core.model.TemplateFunctionModel; import org.apache.freemarker.core.model.TemplateModel; import org.apache.freemarker.core.model.TemplateModelException; import org.apache.freemarker.core.model.TemplateScalarModel; @@ -132,7 +132,7 @@ class BuiltInsForStringsEncoding { private BuiltInsForStringsEncoding() { } static abstract class AbstractUrlBIResult implements - TemplateScalarModel, TemplateMethodModel { + TemplateScalarModel, TemplateFunctionModel { protected final ASTExpBuiltIn parent; protected final String targetAsString; @@ -146,10 +146,10 @@ class BuiltInsForStringsEncoding { } protected abstract String encodeWithCharset(Charset charset) throws UnsupportedEncodingException; - + @Override - public TemplateModel execute(List<? extends TemplateModel> args) throws TemplateModelException { - parent.checkMethodArgCount(args.size(), 1); + public TemplateModel execute(TemplateModel[] args, CallPlace callPlace, Environment env) + throws TemplateException { try { String charsetName = _CallableUtils.castArgToString(args,0); Charset charset; @@ -164,7 +164,12 @@ class BuiltInsForStringsEncoding { throw new _TemplateModelException(e, "Failed to execute URL encoding."); } } - + + @Override + public ArgumentArrayLayout getFunctionArgumentArrayLayout() { + return ArgumentArrayLayout.SINGLE_POSITIONAL_PARAMETER; + } + @Override public String getAsString() throws TemplateModelException { if (cachedResult == null) { http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/8d5263f2/freemarker-core/src/main/java/org/apache/freemarker/core/BuiltInsForStringsMisc.java ---------------------------------------------------------------------- diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/BuiltInsForStringsMisc.java b/freemarker-core/src/main/java/org/apache/freemarker/core/BuiltInsForStringsMisc.java index 6bf7e2a..1bcd592 100644 --- a/freemarker-core/src/main/java/org/apache/freemarker/core/BuiltInsForStringsMisc.java +++ b/freemarker-core/src/main/java/org/apache/freemarker/core/BuiltInsForStringsMisc.java @@ -22,13 +22,12 @@ package org.apache.freemarker.core; import java.io.IOException; import java.io.StringReader; import java.io.Writer; -import java.util.List; import org.apache.freemarker.core.model.ArgumentArrayLayout; import org.apache.freemarker.core.model.ObjectWrapper; import org.apache.freemarker.core.model.TemplateBooleanModel; import org.apache.freemarker.core.model.TemplateDirectiveModel; -import org.apache.freemarker.core.model.TemplateMethodModel; +import org.apache.freemarker.core.model.TemplateFunctionModel; import org.apache.freemarker.core.model.TemplateModel; import org.apache.freemarker.core.model.TemplateModelException; import org.apache.freemarker.core.model.TemplateScalarModel; @@ -219,7 +218,7 @@ class BuiltInsForStringsMisc { } @Override - public ArgumentArrayLayout getArgumentArrayLayout() { + public ArgumentArrayLayout getDirectiveArgumentArrayLayout() { return ArgumentArrayLayout.PARAMETERLESS; } @@ -254,7 +253,7 @@ class BuiltInsForStringsMisc { return new ConstructorFunction(target.evalAndCoerceToPlainText(env), env, target.getTemplate()); } - class ConstructorFunction implements TemplateMethodModel { + class ConstructorFunction implements TemplateFunctionModel { private final Class<?> cl; private final Environment env; @@ -273,13 +272,14 @@ class BuiltInsForStringsMisc { } @Override - public TemplateModel execute(List<? extends TemplateModel> args) throws TemplateModelException { + public TemplateModel execute(TemplateModel[] args, CallPlace callPlace, Environment env) + throws TemplateModelException { ObjectWrapper ow = env.getObjectWrapper(); if (ow instanceof DefaultObjectWrapper) { - return ow.wrap(((DefaultObjectWrapper) ow).newInstance(cl, args)); + return ow.wrap(((DefaultObjectWrapper) ow).newInstance(cl, args, callPlace)); } - if (!args.isEmpty()) { + if (args.length != 0) { throw new TemplateModelException( "className?new(args) only supports 0 arguments in the current configuration, because " + " the objectWrapper setting value is not a " @@ -293,6 +293,12 @@ class BuiltInsForStringsMisc { + cl.getName() + " with its parameterless constructor; see cause exception", e); } } + + @Override + public ArgumentArrayLayout getFunctionArgumentArrayLayout() { + return null; + } + } } http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/8d5263f2/freemarker-core/src/main/java/org/apache/freemarker/core/BuiltInsForStringsRegexp.java ---------------------------------------------------------------------- diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/BuiltInsForStringsRegexp.java b/freemarker-core/src/main/java/org/apache/freemarker/core/BuiltInsForStringsRegexp.java index 025b599..ef73e5f 100644 --- a/freemarker-core/src/main/java/org/apache/freemarker/core/BuiltInsForStringsRegexp.java +++ b/freemarker-core/src/main/java/org/apache/freemarker/core/BuiltInsForStringsRegexp.java @@ -20,13 +20,13 @@ package org.apache.freemarker.core; import java.util.ArrayList; -import java.util.List; import java.util.regex.Matcher; import java.util.regex.Pattern; +import org.apache.freemarker.core.model.ArgumentArrayLayout; import org.apache.freemarker.core.model.TemplateBooleanModel; import org.apache.freemarker.core.model.TemplateCollectionModel; -import org.apache.freemarker.core.model.TemplateMethodModel; +import org.apache.freemarker.core.model.TemplateFunctionModel; import org.apache.freemarker.core.model.TemplateModel; import org.apache.freemarker.core.model.TemplateModelException; import org.apache.freemarker.core.model.TemplateModelIterator; @@ -61,29 +61,35 @@ class BuiltInsForStringsRegexp { } static class matchesBI extends BuiltInForString { - class MatcherBuilder implements TemplateMethodModel { + class MatcherBuilder implements TemplateFunctionModel { String matchString; MatcherBuilder(String matchString) throws TemplateModelException { this.matchString = matchString; } - + + @Override - public TemplateModel execute(List<? extends TemplateModel> args) throws TemplateException { - int argCnt = args.size(); - checkMethodArgCount(argCnt, 1, 2); - - String patternString = _CallableUtils.castArgToString(args, 0); - long flags = argCnt > 1 - ? RegexpHelper.parseFlagString(_CallableUtils.castArgToString(args, 1)) + public TemplateModel execute(TemplateModel[] args, CallPlace callPlace, Environment env) + throws TemplateException { + String patternString = getStringMethodArg(args, 0); + String flagString = getStringMethodArg(args, 1, true); + long flags = flagString != null + ? RegexpHelper.parseFlagString(flagString) : 0; if ((flags & RegexpHelper.RE_FLAG_FIRST_ONLY) != 0) { + // TODO [FM3] Should be an error? RegexpHelper.logFlagWarning("?" + key + " doesn't support the \"f\" flag."); } Pattern pattern = RegexpHelper.getPattern(patternString, (int) flags); return new RegexMatchModel(pattern, matchString); } + + @Override + public ArgumentArrayLayout getFunctionArgumentArrayLayout() { + return ArgumentArrayLayout.TWO_POSITIONAL_PARAMETERS; + } } @Override @@ -95,7 +101,7 @@ class BuiltInsForStringsRegexp { static class replace_reBI extends BuiltInForString { - class ReplaceMethod implements TemplateMethodModel { + class ReplaceMethod implements TemplateFunctionModel { private String s; ReplaceMethod(String s) { @@ -103,13 +109,13 @@ class BuiltInsForStringsRegexp { } @Override - public TemplateModel execute(List<? extends TemplateModel> args) throws TemplateException { - int argCnt = args.size(); - checkMethodArgCount(argCnt, 2, 3); - String arg1 = _CallableUtils.castArgToString(args, 0); - String arg2 = _CallableUtils.castArgToString(args, 1); - long flags = argCnt > 2 - ? RegexpHelper.parseFlagString(_CallableUtils.castArgToString(args, 2)) + public TemplateModel execute(TemplateModel[] args, CallPlace callPlace, Environment env) + throws TemplateException { + String arg1 = getStringMethodArg(args, 0); + String arg2 = getStringMethodArg(args, 1); + String flagString = getStringMethodArg(args, 2, true); + long flags = flagString != null + ? RegexpHelper.parseFlagString(flagString) : 0; String result; if ((flags & RegexpHelper.RE_FLAG_REGEXP) == 0) { @@ -123,10 +129,15 @@ class BuiltInsForStringsRegexp { result = (flags & RegexpHelper.RE_FLAG_FIRST_ONLY) != 0 ? matcher.replaceFirst(arg2) : matcher.replaceAll(arg2); - } + } return new SimpleScalar(result); } + @Override + public ArgumentArrayLayout getFunctionArgumentArrayLayout() { + return ArgumentArrayLayout.THREE_POSITIONAL_PARAMETERS; + } + } @Override http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/8d5263f2/freemarker-core/src/main/java/org/apache/freemarker/core/Environment.java ---------------------------------------------------------------------- diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/Environment.java b/freemarker-core/src/main/java/org/apache/freemarker/core/Environment.java index ee212db..f697309 100644 --- a/freemarker-core/src/main/java/org/apache/freemarker/core/Environment.java +++ b/freemarker-core/src/main/java/org/apache/freemarker/core/Environment.java @@ -2929,7 +2929,7 @@ public final class Environment extends MutableProcessingConfiguration<Environmen * Superclass of {@link TemplateCallableModel}-s implemented in the template language. */ abstract class TemplateLanguageCallable implements TemplateCallableModel { - private final ASTDirMacroOrFunction callableDefinition; + final ASTDirMacroOrFunction callableDefinition; private final Namespace namespace; public TemplateLanguageCallable(ASTDirMacroOrFunction callableDefinition, Namespace namespace) { @@ -2993,7 +2993,8 @@ public final class Environment extends MutableProcessingConfiguration<Environmen new _ErrorDescriptionBuilder( "When calling macro ", new _DelayedJQuote(callableDefinition.getName()), ", required parameter ", new _DelayedJQuote(paramDef.getName()), - (argIdx < getArgumentArrayLayout().getPredefinedPositionalArgumentCount() + (argIdx < callableDefinition.getArgumentArrayLayout() + .getPredefinedPositionalArgumentCount() ? new Object[] { " (parameter #", (argIdx + 1), ")" } : ""), " was either not specified, or had null/missing value.") @@ -3013,11 +3014,6 @@ public final class Environment extends MutableProcessingConfiguration<Environmen } } - @Override - public ArgumentArrayLayout getArgumentArrayLayout() { - return callableDefinition.getArgumentArrayLayout(); - } - ASTDirMacroOrFunction getCallableDefinition() { return callableDefinition; } @@ -3053,6 +3049,11 @@ public final class Environment extends MutableProcessingConfiguration<Environmen return true; } + @Override + public ArgumentArrayLayout getDirectiveArgumentArrayLayout() { + return callableDefinition.getArgumentArrayLayout(); + } + } /** @@ -3077,6 +3078,12 @@ public final class Environment extends MutableProcessingConfiguration<Environmen } return env.getLastReturnValue(); } + + @Override + public ArgumentArrayLayout getFunctionArgumentArrayLayout() { + return callableDefinition.getArgumentArrayLayout(); + } + } } http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/8d5263f2/freemarker-core/src/main/java/org/apache/freemarker/core/MessageUtil.java ---------------------------------------------------------------------- diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/MessageUtil.java b/freemarker-core/src/main/java/org/apache/freemarker/core/MessageUtil.java index a1858b1..8749fd4 100644 --- a/freemarker-core/src/main/java/org/apache/freemarker/core/MessageUtil.java +++ b/freemarker-core/src/main/java/org/apache/freemarker/core/MessageUtil.java @@ -169,7 +169,7 @@ class MessageUtil { && !(argExp instanceof ASTExpVariable) && !(argExp instanceof ASTExpDot) && !(argExp instanceof ASTExpDynamicKeyName) - && !(argExp instanceof ASTExpMethodCall) + && !(argExp instanceof ASTExpFunctionCall) && !(argExp instanceof ASTExpBuiltIn); if (needParen) sb.append('('); sb.append(argExp.getCanonicalForm()); http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/8d5263f2/freemarker-core/src/main/java/org/apache/freemarker/core/NonFunctionException.java ---------------------------------------------------------------------- diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/NonFunctionException.java b/freemarker-core/src/main/java/org/apache/freemarker/core/NonFunctionException.java new file mode 100644 index 0000000..2ae7fdd --- /dev/null +++ b/freemarker-core/src/main/java/org/apache/freemarker/core/NonFunctionException.java @@ -0,0 +1,62 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.freemarker.core; + +import org.apache.freemarker.core.model.TemplateFunctionModel; +import org.apache.freemarker.core.model.TemplateModel; + +/** + * Indicates that a {@link TemplateFunctionModel} value was expected, but the value had a different type. + */ +public class NonFunctionException extends UnexpectedTypeException { + + private static final Class[] EXPECTED_TYPES = new Class[] { TemplateFunctionModel.class }; + + public NonFunctionException(Environment env) { + super(env, "Expecting method value here"); + } + + public NonFunctionException(String description, Environment env) { + super(env, description); + } + + NonFunctionException(Environment env, _ErrorDescriptionBuilder description) { + super(env, description); + } + + NonFunctionException( + ASTExpression blamed, TemplateModel model, Environment env) + throws InvalidReferenceException { + super(blamed, model, "function", EXPECTED_TYPES, env); + } + + NonFunctionException( + ASTExpression blamed, TemplateModel model, String tip, + Environment env) + throws InvalidReferenceException { + super(blamed, model, "function", EXPECTED_TYPES, tip, env); + } + + NonFunctionException( + ASTExpression blamed, TemplateModel model, String[] tips, Environment env) throws InvalidReferenceException { + super(blamed, model, "function", EXPECTED_TYPES, tips, env); + } + +} http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/8d5263f2/freemarker-core/src/main/java/org/apache/freemarker/core/NonMethodException.java ---------------------------------------------------------------------- diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/NonMethodException.java b/freemarker-core/src/main/java/org/apache/freemarker/core/NonMethodException.java deleted file mode 100644 index 6a51f4c..0000000 --- a/freemarker-core/src/main/java/org/apache/freemarker/core/NonMethodException.java +++ /dev/null @@ -1,62 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -package org.apache.freemarker.core; - -import org.apache.freemarker.core.model.TemplateMethodModel; -import org.apache.freemarker.core.model.TemplateModel; - -/** - * Indicates that a {@link TemplateMethodModel} value was expected, but the value had a different type. - */ -public class NonMethodException extends UnexpectedTypeException { - - private static final Class[] EXPECTED_TYPES = new Class[] { TemplateMethodModel.class }; - - public NonMethodException(Environment env) { - super(env, "Expecting method value here"); - } - - public NonMethodException(String description, Environment env) { - super(env, description); - } - - NonMethodException(Environment env, _ErrorDescriptionBuilder description) { - super(env, description); - } - - NonMethodException( - ASTExpression blamed, TemplateModel model, Environment env) - throws InvalidReferenceException { - super(blamed, model, "method", EXPECTED_TYPES, env); - } - - NonMethodException( - ASTExpression blamed, TemplateModel model, String tip, - Environment env) - throws InvalidReferenceException { - super(blamed, model, "method", EXPECTED_TYPES, tip, env); - } - - NonMethodException( - ASTExpression blamed, TemplateModel model, String[] tips, Environment env) throws InvalidReferenceException { - super(blamed, model, "method", EXPECTED_TYPES, tips, env); - } - -} http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/8d5263f2/freemarker-core/src/main/java/org/apache/freemarker/core/_CallableUtils.java ---------------------------------------------------------------------- diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/_CallableUtils.java b/freemarker-core/src/main/java/org/apache/freemarker/core/_CallableUtils.java index 0a69e51..4ab59b4 100644 --- a/freemarker-core/src/main/java/org/apache/freemarker/core/_CallableUtils.java +++ b/freemarker-core/src/main/java/org/apache/freemarker/core/_CallableUtils.java @@ -26,7 +26,6 @@ import java.util.List; import org.apache.freemarker.core.model.ArgumentArrayLayout; import org.apache.freemarker.core.model.Constants; -import org.apache.freemarker.core.model.TemplateCallableModel; import org.apache.freemarker.core.model.TemplateDirectiveModel; import org.apache.freemarker.core.model.TemplateFunctionModel; import org.apache.freemarker.core.model.TemplateModel; @@ -47,18 +46,19 @@ public final class _CallableUtils { public static void executeWith0Arguments( TemplateDirectiveModel directive, CallPlace callPlace, Writer out, Environment env) throws IOException, TemplateException { - directive.execute(getArgumentArrayWithNoArguments(directive), callPlace, out, env); + directive.execute( + getArgumentArrayWithNoArguments(directive.getDirectiveArgumentArrayLayout()), callPlace, out, env); } public static TemplateModel executeWith0Arguments( TemplateFunctionModel function, CallPlace callPlace, Environment env) throws TemplateException { - return function.execute(getArgumentArrayWithNoArguments(function), callPlace, env); + return function.execute( + getArgumentArrayWithNoArguments(function.getFunctionArgumentArrayLayout()), callPlace, env); } - private static TemplateModel[] getArgumentArrayWithNoArguments(TemplateCallableModel callable) { - ArgumentArrayLayout argsLayout = callable.getArgumentArrayLayout(); - int totalLength = argsLayout.getTotalLength(); + private static TemplateModel[] getArgumentArrayWithNoArguments(ArgumentArrayLayout argsLayout) { + int totalLength = argsLayout != null ? argsLayout.getTotalLength() : 0; if (totalLength == 0) { return Constants.EMPTY_TEMPLATE_MODEL_ARRAY; } else { @@ -78,28 +78,37 @@ public final class _CallableUtils { } } - public static Number castArgToNumber(TemplateModel[] args, int argIndex, boolean allowNull) + public static Number castArgToNumber(TemplateModel[] args, int argIndex) throws TemplateException { + return castArgToNumber(args, argIndex, false); + } + + public static Number castArgToNumber(TemplateModel[] args, int argIndex, boolean optional) + throws TemplateException { + return castArgToNumber(args[argIndex], argIndex, optional); + } + + public static Number castArgToNumber(TemplateModel argValue, int argIndex) throws TemplateException { - return castArgToNumber(args[argIndex], argIndex, allowNull); + return castArgToNumber(argValue, argIndex, false); } - public static Number castArgToNumber(TemplateModel argValue, int argIndex, boolean allowNull) + public static Number castArgToNumber(TemplateModel argValue, int argIndex, boolean optional) throws TemplateException { - return castArgToNumber(argValue, null, argIndex, allowNull); + return castArgToNumber(argValue, null, argIndex, optional); } - public static Number castArgToNumber(TemplateModel argValue, String argName, boolean allowNull) + public static Number castArgToNumber(TemplateModel argValue, String argName, boolean optional) throws TemplateException { - return castArgToNumber(argValue, argName, -1, allowNull); + return castArgToNumber(argValue, argName, -1, optional); } - private static Number castArgToNumber(TemplateModel argValue, String argName, int argIndex, boolean allowNull) + private static Number castArgToNumber(TemplateModel argValue, String argName, int argIndex, boolean optional) throws TemplateException { if (argValue instanceof TemplateNumberModel) { return ((TemplateNumberModel) argValue).getAsNumber(); } if (argValue == null) { - if (allowNull) { + if (optional) { return null; } throw new _MiscTemplateException( @@ -115,35 +124,39 @@ public final class _CallableUtils { return castArgToString(args, argIndex, false); } - public static String castArgToString(List<? extends TemplateModel> args, int argIndex, boolean allowNull) throws + public static String castArgToString(List<? extends TemplateModel> args, int argIndex, boolean optional) throws TemplateException { - return castArgToString(args.get(argIndex), argIndex, allowNull); + return castArgToString(args.get(argIndex), argIndex, optional); + } + + public static String castArgToString(TemplateModel[] args, int argIndex) throws TemplateException { + return castArgToString(args, argIndex, false); } - public static String castArgToString(TemplateModel[] args, int argIndex, boolean allowNull) throws TemplateException { - return castArgToString(args[argIndex], argIndex, allowNull); + public static String castArgToString(TemplateModel[] args, int argIndex, boolean optional) throws TemplateException { + return castArgToString(args[argIndex], argIndex, optional); } public static String castArgToString(TemplateModel argValue, int argIndex) throws TemplateException { return castArgToString(argValue, argIndex, false); } - public static String castArgToString(TemplateModel argValue, int argIndex, boolean allowNull) throws TemplateException { - return castArgToString(argValue, null, argIndex, allowNull); + public static String castArgToString(TemplateModel argValue, int argIndex, boolean optional) throws TemplateException { + return castArgToString(argValue, null, argIndex, optional); } - public static String castArgToString(TemplateModel argValue, String argName, boolean allowNull) throws TemplateException { - return castArgToString(argValue, argName, -1, allowNull); + public static String castArgToString(TemplateModel argValue, String argName, boolean optional) throws TemplateException { + return castArgToString(argValue, argName, -1, optional); } private static String castArgToString( TemplateModel argValue, String argName, int argIndex, - boolean allowNull) throws TemplateException { + boolean optional) throws TemplateException { if (argValue instanceof TemplateScalarModel) { return _EvalUtil.modelToString((TemplateScalarModel) argValue, null, null); } if (argValue == null) { - if (allowNull) { + if (optional) { return null; } throw new _MiscTemplateException( http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/8d5263f2/freemarker-core/src/main/java/org/apache/freemarker/core/_ObjectBuilderSettingEvaluator.java ---------------------------------------------------------------------- diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/_ObjectBuilderSettingEvaluator.java b/freemarker-core/src/main/java/org/apache/freemarker/core/_ObjectBuilderSettingEvaluator.java index d2fe617..89a6fe8 100644 --- a/freemarker-core/src/main/java/org/apache/freemarker/core/_ObjectBuilderSettingEvaluator.java +++ b/freemarker-core/src/main/java/org/apache/freemarker/core/_ObjectBuilderSettingEvaluator.java @@ -36,10 +36,10 @@ import java.util.Map; import java.util.Properties; import org.apache.freemarker.core.model.TemplateHashModel; -import org.apache.freemarker.core.model.TemplateMethodModel; import org.apache.freemarker.core.model.TemplateModel; import org.apache.freemarker.core.model.TemplateModelException; import org.apache.freemarker.core.model.impl.DefaultObjectWrapper; +import org.apache.freemarker.core.model.impl.JavaMethodModel; import org.apache.freemarker.core.model.impl.RestrictedObjectWrapper; import org.apache.freemarker.core.outputformat.impl.HTMLOutputFormat; import org.apache.freemarker.core.outputformat.impl.PlainTextOutputFormat; @@ -712,17 +712,17 @@ public class _ObjectBuilderSettingEvaluator { } private void setJavaBeanProperties(Object bean, - List/*<String>*/ namedParamNames, List/*<Object>*/ namedParamValues) + List<String> namedParamNames, List<Object> namedParamValues) throws _ObjectBuilderSettingEvaluationException { if (namedParamNames.isEmpty()) { return; } - final Class cl = bean.getClass(); - Map/*<String,Method>*/ beanPropSetters; + final Class<?> cl = bean.getClass(); + Map<String, Method> beanPropSetters; try { PropertyDescriptor[] propDescs = Introspector.getBeanInfo(cl).getPropertyDescriptors(); - beanPropSetters = new HashMap(propDescs.length * 4 / 3, 1.0f); + beanPropSetters = new HashMap<>(propDescs.length * 4 / 3, 1.0f); for (PropertyDescriptor propDesc : propDescs) { final Method writeMethod = propDesc.getWriteMethod(); if (writeMethod != null) { @@ -735,14 +735,14 @@ public class _ObjectBuilderSettingEvaluator { TemplateHashModel beanTM = null; for (int i = 0; i < namedParamNames.size(); i++) { - String name = (String) namedParamNames.get(i); + String name = namedParamNames.get(i); if (!beanPropSetters.containsKey(name)) { throw new _ObjectBuilderSettingEvaluationException( "The " + cl.getName() + " class has no writeable JavaBeans property called " + _StringUtil.jQuote(name) + "."); } - Method beanPropSetter = (Method) beanPropSetters.put(name, null); + Method beanPropSetter = beanPropSetters.put(name, null); if (beanPropSetter == null) { throw new _ObjectBuilderSettingEvaluationException( "JavaBeans property " + _StringUtil.jQuote(name) + " is set twice."); @@ -757,19 +757,20 @@ public class _ObjectBuilderSettingEvaluator { } beanTM = (TemplateHashModel) wrappedObj; } - + TemplateModel m = beanTM.get(beanPropSetter.getName()); if (m == null) { throw new _ObjectBuilderSettingEvaluationException( "Can't find " + beanPropSetter + " as FreeMarker method."); } - if (!(m instanceof TemplateMethodModel)) { + if (!(m instanceof JavaMethodModel)) { throw new _ObjectBuilderSettingEvaluationException( - _StringUtil.jQuote(beanPropSetter.getName()) + " wasn't a TemplateMethodModel."); + _StringUtil.jQuote(beanPropSetter.getName()) + " wasn't a JavaMethodModel."); } List/*TemplateModel*/ args = new ArrayList(); - args.add(env.getObjectWrapper().wrap(namedParamValues.get(i))); - ((TemplateMethodModel) m).execute(args); + ((JavaMethodModel) m).execute( + new TemplateModel[] { env.getObjectWrapper().wrap(namedParamValues.get(i)) }, + NonTemplateCallPlace.INSTANCE); } catch (Exception e) { throw new _ObjectBuilderSettingEvaluationException( "Failed to set " + _StringUtil.jQuote(name), e); @@ -992,16 +993,16 @@ public class _ObjectBuilderSettingEvaluator { } } else { DefaultObjectWrapper ow = env.getObjectWrapper(); - List/*<TemplateModel>*/ tmArgs = new ArrayList(positionalParamValues.size()); + TemplateModel[] tmArgs = new TemplateModel[positionalParamValues.size()]; for (int i = 0; i < positionalParamValues.size(); i++) { try { - tmArgs.add(ow.wrap(positionalParamValues.get(i))); + tmArgs[i] = ow.wrap(positionalParamValues.get(i)); } catch (TemplateModelException e) { throw new _ObjectBuilderSettingEvaluationException("Failed to wrap arg #" + (i + 1), e); } } try { - return ow.newInstance(cl, tmArgs); + return ow.newInstance(cl, tmArgs, NonTemplateCallPlace.INSTANCE); } catch (Exception e) { throw new _ObjectBuilderSettingEvaluationException( "Failed to call " + cl.getName() + " constructor", e); http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/8d5263f2/freemarker-core/src/main/java/org/apache/freemarker/core/debug/DebugModel.java ---------------------------------------------------------------------- diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/debug/DebugModel.java b/freemarker-core/src/main/java/org/apache/freemarker/core/debug/DebugModel.java index 1c6bc66..5f4c473 100644 --- a/freemarker-core/src/main/java/org/apache/freemarker/core/debug/DebugModel.java +++ b/freemarker-core/src/main/java/org/apache/freemarker/core/debug/DebugModel.java @@ -45,7 +45,7 @@ public interface DebugModel extends Remote { public static final int TYPE_COLLECTION = 32; public static final int TYPE_HASH = 64; public static final int TYPE_HASH_EX = 128; - public static final int TYPE_METHOD = 256; + public static final int TYPE_FUNCTION = 256; public static final int TYPE_DIRECTIVE = 1024; public static final int TYPE_ENVIRONMENT = 2048; public static final int TYPE_TEMPLATE = 4096; http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/8d5263f2/freemarker-core/src/main/java/org/apache/freemarker/core/debug/RmiDebugModelImpl.java ---------------------------------------------------------------------- diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/debug/RmiDebugModelImpl.java b/freemarker-core/src/main/java/org/apache/freemarker/core/debug/RmiDebugModelImpl.java index 7ab7ec7..20e9f50 100644 --- a/freemarker-core/src/main/java/org/apache/freemarker/core/debug/RmiDebugModelImpl.java +++ b/freemarker-core/src/main/java/org/apache/freemarker/core/debug/RmiDebugModelImpl.java @@ -29,9 +29,9 @@ import org.apache.freemarker.core.model.TemplateBooleanModel; import org.apache.freemarker.core.model.TemplateCollectionModel; import org.apache.freemarker.core.model.TemplateDateModel; import org.apache.freemarker.core.model.TemplateDirectiveModel; +import org.apache.freemarker.core.model.TemplateFunctionModel; import org.apache.freemarker.core.model.TemplateHashModel; import org.apache.freemarker.core.model.TemplateHashModelEx; -import org.apache.freemarker.core.model.TemplateMethodModel; import org.apache.freemarker.core.model.TemplateModel; import org.apache.freemarker.core.model.TemplateModelException; import org.apache.freemarker.core.model.TemplateModelIterator; @@ -155,7 +155,7 @@ class RmiDebugModelImpl extends UnicastRemoteObject implements DebugModel { if (model instanceof TemplateCollectionModel) type += TYPE_COLLECTION; if (model instanceof TemplateHashModelEx) type += TYPE_HASH_EX; if (model instanceof TemplateHashModel) type += TYPE_HASH; - if (model instanceof TemplateMethodModel) type += TYPE_METHOD; + if (model instanceof TemplateFunctionModel) type += TYPE_FUNCTION; if (model instanceof TemplateDirectiveModel) type += TYPE_DIRECTIVE; return type; } http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/8d5263f2/freemarker-core/src/main/java/org/apache/freemarker/core/model/ArgumentArrayLayout.java ---------------------------------------------------------------------- diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/model/ArgumentArrayLayout.java b/freemarker-core/src/main/java/org/apache/freemarker/core/model/ArgumentArrayLayout.java index 4e1149c..e974570 100644 --- a/freemarker-core/src/main/java/org/apache/freemarker/core/model/ArgumentArrayLayout.java +++ b/freemarker-core/src/main/java/org/apache/freemarker/core/model/ArgumentArrayLayout.java @@ -25,6 +25,8 @@ import org.apache.freemarker.core.util.StringToIndexMap; * {@link TemplateCallableModel} subinterfaces define a method called {@code execute}, which has an argument array * parameter, whose layout this class describes. The layout specifies the (minimum) array length, what's the index * of which parameters, and if there are varargs parameters, in which case they must not be left {@code null}. + * (Note that a {@link TemplateCallableModel} may have {@code null} layout; see the documentation of {@code execute} + * for more.) * <p> * Each parameter has a constant index in this array, which is the same for all invocations of the same * {@link TemplateCallableModel} object (regardless if there are omitted optional parameters). Thus, the argument @@ -87,6 +89,22 @@ public final class ArgumentArrayLayout { null, false); /** + * Constant to be used when the {@link TemplateCallableModel} has 2 positional parameter, and no others. + * (The argument array index of the positional parameters will be 0 and 1.) + */ + public static final ArgumentArrayLayout TWO_POSITIONAL_PARAMETERS = new ArgumentArrayLayout( + 2, false, + null, false); + + /** + * Constant to be used when the {@link TemplateCallableModel} has 3 positional parameter, and no others. + * (The argument array index of the positional parameters will be 0, 1, and 2.) + */ + public static final ArgumentArrayLayout THREE_POSITIONAL_PARAMETERS = new ArgumentArrayLayout( + 3, false, + null, false); + + /** * Constant to be used when the {@link TemplateCallableModel} has 1 positional varargs parameter, and no others. * (The argument array index of the positional varargs parameter will be 0.) * */ http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/8d5263f2/freemarker-core/src/main/java/org/apache/freemarker/core/model/GeneralPurposeNothing.java ---------------------------------------------------------------------- diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/model/GeneralPurposeNothing.java b/freemarker-core/src/main/java/org/apache/freemarker/core/model/GeneralPurposeNothing.java index 92b0b58..b19b912 100644 --- a/freemarker-core/src/main/java/org/apache/freemarker/core/model/GeneralPurposeNothing.java +++ b/freemarker-core/src/main/java/org/apache/freemarker/core/model/GeneralPurposeNothing.java @@ -19,7 +19,9 @@ package org.apache.freemarker.core.model; -import java.util.List; +import org.apache.freemarker.core.CallPlace; +import org.apache.freemarker.core.Environment; +import org.apache.freemarker.core.TemplateException; /** * Singleton object representing nothing, used by ?if_exists built-in. @@ -28,7 +30,8 @@ import java.util.List; */ final class GeneralPurposeNothing -implements TemplateBooleanModel, TemplateScalarModel, TemplateSequenceModel, TemplateHashModelEx, TemplateMethodModel { +implements TemplateBooleanModel, TemplateScalarModel, TemplateSequenceModel, TemplateHashModelEx, + TemplateFunctionModel { public static final TemplateModel INSTANCE = new GeneralPurposeNothing(); @@ -66,10 +69,17 @@ implements TemplateBooleanModel, TemplateScalarModel, TemplateSequenceModel, Tem } @Override - public TemplateModel execute(List<? extends TemplateModel> args) { + public TemplateModel execute(TemplateModel[] args, CallPlace callPlace, Environment env) throws TemplateException { return null; } - + + @Override + public ArgumentArrayLayout getFunctionArgumentArrayLayout() { + return ArgumentArrayLayout.create( + 0, true, + null, true); + } + @Override public TemplateCollectionModel keys() { return Constants.EMPTY_COLLECTION; http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/8d5263f2/freemarker-core/src/main/java/org/apache/freemarker/core/model/TemplateCallableModel.java ---------------------------------------------------------------------- diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/model/TemplateCallableModel.java b/freemarker-core/src/main/java/org/apache/freemarker/core/model/TemplateCallableModel.java index 6aa9445..caa612a 100644 --- a/freemarker-core/src/main/java/org/apache/freemarker/core/model/TemplateCallableModel.java +++ b/freemarker-core/src/main/java/org/apache/freemarker/core/model/TemplateCallableModel.java @@ -20,14 +20,9 @@ package org.apache.freemarker.core.model; /** - * Super interface of {@link TemplateFunctionModel} and {@link TemplateDirectiveModel}; don' extended (or implement) it - * yourself! + * Super interface (marker interface) of {@link TemplateFunctionModel} and {@link TemplateDirectiveModel}; don' extended + * (or implement) it yourself! */ public interface TemplateCallableModel extends TemplateModel { - - /** - * Returns the argument array layout to use when calling the {@code execute} method. - */ - ArgumentArrayLayout getArgumentArrayLayout(); - + // } http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/8d5263f2/freemarker-core/src/main/java/org/apache/freemarker/core/model/TemplateDirectiveModel.java ---------------------------------------------------------------------- diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/model/TemplateDirectiveModel.java b/freemarker-core/src/main/java/org/apache/freemarker/core/model/TemplateDirectiveModel.java index 95678d6..be06cba 100644 --- a/freemarker-core/src/main/java/org/apache/freemarker/core/model/TemplateDirectiveModel.java +++ b/freemarker-core/src/main/java/org/apache/freemarker/core/model/TemplateDirectiveModel.java @@ -5,7 +5,9 @@ import java.io.Writer; import org.apache.freemarker.core.CallPlace; import org.apache.freemarker.core.Environment; +import org.apache.freemarker.core.NonTemplateCallPlace; import org.apache.freemarker.core.TemplateException; +import org.apache.freemarker.core.model.impl.JavaMethodModel; /** * A {@link TemplateCallableModel} that progressively writes it result into the {@code out} object, instead of @@ -26,25 +28,29 @@ public interface TemplateDirectiveModel extends TemplateCallableModel { * Invokes the directive. * * @param args - * The of argument values. Not {@code null}. If a parameter was omitted on the caller side, the - * corresponding array element will be {@code null}. The length of the array and the indexes - * correspont to the {@link ArgumentArrayLayout} returned by {@link #getArgumentArrayLayout()}. - * If the caller doesn't keep argument layout rules (such as the array is shorter than - * {@link ArgumentArrayLayout#getTotalLength()}, or the type of the values at - * {@link ArgumentArrayLayout#getPositionalVarargsArgumentIndex()} or at - * {@link ArgumentArrayLayout#getNamedVarargsArgumentIndex()} is improper), this method may - * throws {@link IndexOutOfBoundsException} or {@link ClassCastException}. Thus, user Java code - * that wishes to call {@link TemplateCallableModel}-s is responsible to ensure that the argument array - * follows the layout described be {@link ArgumentArrayLayout}, as the {@code execute} method - * isn't meant to do validations on that level. + * The array of argument values. Not {@code null}. If a parameter was omitted on the caller side, the + * corresponding array element will be {@code null}. The length of the array and the indexes correspond to + * the {@link ArgumentArrayLayout} returned by {@link #getDirectiveArgumentArrayLayout()}. {@link + * ArgumentArrayLayout} os not {@code null}m and the caller doesn't keep argument layout rules (such as the + * array is shorter than {@link ArgumentArrayLayout#getTotalLength()}, or the type of the values at {@link + * ArgumentArrayLayout#getPositionalVarargsArgumentIndex()} or at + * {@link ArgumentArrayLayout#getNamedVarargsArgumentIndex()} + * is improper), this method may throws {@link IndexOutOfBoundsException} or {@link ClassCastException}. + * Thus, user Java code that wishes to call {@link TemplateCallableModel}-s is responsible to ensure that + * the argument array follows the layout described be {@link ArgumentArrayLayout}, as the {@code execute} + * method isn't meant to do validations on that level. * @param callPlace - * The place (in a template, normally) where this directive was called from. Not {@code null}. Note that - * {@link CallPlace#executeNestedContent(TemplateModel[], Writer, Environment)} can be used to execute the - * nested content. + * The place (in a template, normally) where this directive was called from. Not {@code null}; in case the + * call is not from a template, you can use {@link NonTemplateCallPlace#INSTANCE} (or another {@link + * NonTemplateCallPlace} instance). Note that {@link CallPlace#executeNestedContent(TemplateModel[], Writer, + * Environment)} can be used to execute the nested content (even if there's no nested content; then simply + * nothing happens). * @param out * Print the output here (if there's any) * @param env - * The current processing environment. Not {@code null}. + * The current processing environment. Not {@code null} in general, though certain implementations may + * specifically allow that, typically, implementations that are just adapters towards FreeMarker-unaware + * callables (for example, {@link JavaMethodModel} is like that). * * @throws TemplateException * If any problem occurs that's not an {@link IOException} during writing the template output. @@ -67,4 +73,13 @@ public interface TemplateDirectiveModel extends TemplateCallableModel { */ boolean isNestedContentSupported(); + /** + * Returns the argument array layout to use when calling the {@code {@link #execute(TemplateModel[], CallPlace, + * Writer, Environment)}} method, or rarely {@code null}. If it's {@code null} then there can only be positional + * arguments, any number of them (though of course the {@code execute} method implementation itself may restricts + * the acceptable argument count), and the argument array will be simply as long as the number of arguments + * specified at the call place. + */ + ArgumentArrayLayout getDirectiveArgumentArrayLayout(); + } http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/8d5263f2/freemarker-core/src/main/java/org/apache/freemarker/core/model/TemplateFunctionModel.java ---------------------------------------------------------------------- diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/model/TemplateFunctionModel.java b/freemarker-core/src/main/java/org/apache/freemarker/core/model/TemplateFunctionModel.java index 6b42550..70824ec 100644 --- a/freemarker-core/src/main/java/org/apache/freemarker/core/model/TemplateFunctionModel.java +++ b/freemarker-core/src/main/java/org/apache/freemarker/core/model/TemplateFunctionModel.java @@ -5,6 +5,7 @@ import java.io.Writer; import org.apache.freemarker.core.CallPlace; import org.apache.freemarker.core.Environment; import org.apache.freemarker.core.TemplateException; +import org.apache.freemarker.core.model.impl.JavaMethodModel; /** * A {@link TemplateCallableModel}, which returns its result as a {@link TemplateModel} at the end of its execution. @@ -35,4 +36,13 @@ public interface TemplateFunctionModel extends TemplateCallableModel { */ TemplateModel execute(TemplateModel[] args, CallPlace callPlace, Environment env) throws TemplateException; + /** + * Returns the argument array layout to use when calling the {@code {@link #execute(TemplateModel[], CallPlace, + * Environment)}} method, or rarely {@code null}. If it's {@code null} then there can only be positional + * arguments, any number of them (though of course the {@code execute} method implementation itself may restricts + * the acceptable argument count), and the argument array will be simply as long as the number of arguments + * specified at the call place. This layoutless mode is for example used by {@link JavaMethodModel}-s. + */ + ArgumentArrayLayout getFunctionArgumentArrayLayout(); + } http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/8d5263f2/freemarker-core/src/main/java/org/apache/freemarker/core/model/TemplateMethodModel.java ---------------------------------------------------------------------- diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/model/TemplateMethodModel.java b/freemarker-core/src/main/java/org/apache/freemarker/core/model/TemplateMethodModel.java deleted file mode 100644 index c35a500..0000000 --- a/freemarker-core/src/main/java/org/apache/freemarker/core/model/TemplateMethodModel.java +++ /dev/null @@ -1,53 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -package org.apache.freemarker.core.model; - -import java.util.List; - -import org.apache.freemarker.core.Environment; -import org.apache.freemarker.core.TemplateException; -import org.apache.freemarker.core.util.DeepUnwrap; - -/** - * "method" template language data type: Objects that act like functions. Their main application is calling - * Java methods via {@link org.apache.freemarker.core.model.impl.DefaultObjectWrapper}, but you can implement this - * interface to invoke top-level functions too. - * - * <p>In templates they are used like {@code myMethod(1, "foo")} or {@code myJavaObject.myJavaMethod(1, "foo")}. - */ -public interface TemplateMethodModel extends TemplateModel { - - /** - * Executes the method call. - * - * @param args a {@link List} of {@link TemplateModel}-s, - * containing the arguments passed to the method. If the implementation absolutely wants - * to operate on POJOs, it can use the static utility methods in the {@link DeepUnwrap} - * class to easily obtain them. However, unwrapping is not always possible (or not perfectly), and isn't always - * efficient, so it's recommended to use the original {@link TemplateModel} value as much as possible. - * - * @return the return value of the method, or {@code null}. If the returned value - * does not implement {@link TemplateModel}, it will be automatically - * wrapped using the {@link Environment#getObjectWrapper() environment's - * object wrapper}. - */ - TemplateModel execute(List<? extends TemplateModel> args) throws TemplateException; - -} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/8d5263f2/freemarker-core/src/main/java/org/apache/freemarker/core/model/TemplateModelException.java ---------------------------------------------------------------------- diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/model/TemplateModelException.java b/freemarker-core/src/main/java/org/apache/freemarker/core/model/TemplateModelException.java index d38faa4..c9d6aa9 100644 --- a/freemarker-core/src/main/java/org/apache/freemarker/core/model/TemplateModelException.java +++ b/freemarker-core/src/main/java/org/apache/freemarker/core/model/TemplateModelException.java @@ -24,7 +24,9 @@ import org.apache.freemarker.core.TemplateException; import org.apache.freemarker.core._ErrorDescriptionBuilder; /** - * {@link TemplateModel} methods throw this exception if the requested data can't be retrieved. + * {@link ObjectWrapper}-s may throw this when wrapping/unwrapping fails, or {@link TemplateModel} methods throw this + * if the requested data can't be retrieved. {@link TemplateCallableModel}-s should typically throw + * {@link TemplateException} instead (like when a required argument is missing). */ public class TemplateModelException extends TemplateException { http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/8d5263f2/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/BeanModel.java ---------------------------------------------------------------------- diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/BeanModel.java b/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/BeanModel.java index bc7ce67..33d5e86 100644 --- a/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/BeanModel.java +++ b/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/BeanModel.java @@ -37,8 +37,8 @@ import org.apache.freemarker.core._DelayedJQuote; import org.apache.freemarker.core._TemplateModelException; import org.apache.freemarker.core.model.AdapterTemplateModel; import org.apache.freemarker.core.model.TemplateCollectionModel; +import org.apache.freemarker.core.model.TemplateFunctionModel; import org.apache.freemarker.core.model.TemplateHashModelEx; -import org.apache.freemarker.core.model.TemplateMethodModel; import org.apache.freemarker.core.model.TemplateModel; import org.apache.freemarker.core.model.TemplateModelException; import org.apache.freemarker.core.model.TemplateModelIterator; @@ -104,12 +104,11 @@ public class BeanModel } /** - * Uses Beans introspection to locate a property or method with name - * matching the key name. If a method or property is found, it's wrapped - * into {@link TemplateMethodModel} (for a method or - * indexed property), or evaluated on-the-fly and the return value wrapped - * into appropriate model (for a simple property) Models for various - * properties and methods are cached on a per-class basis, so the costly + * Uses Beans introspection to locate a JavaBean property or method with name + * matching the key name. If a method is found, it's wrapped + * into {@link TemplateFunctionModel} (a {@link JavaMethodModel} more specifically). + * If a JavaBean property is found, its value is returned. Introspection results + * for various properties and methods are cached on a per-class basis, so the costly * introspection is performed only once per property or method of a class. * (Side-note: this also implies that any class whose method has been called * will be strongly referred to by the framework and will not become @@ -212,10 +211,10 @@ public class BeanModel // cachedModel remains null, as we don't cache these } else if (desc instanceof Method) { Method method = (Method) desc; - resultModel = cachedModel = new JavaMethodModel( + resultModel = cachedModel = new SimpleJavaMethodModel( object, method, ClassIntrospector.getArgTypes(classInfo, method), wrapper); } else if (desc instanceof OverloadedMethods) { - resultModel = cachedModel = new OverloadedMethodsModel( + resultModel = cachedModel = new OverloadedJavaMethodModel( object, (OverloadedMethods) desc, wrapper); }
