http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/8d5263f2/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/DefaultObjectWrapper.java ---------------------------------------------------------------------- diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/DefaultObjectWrapper.java b/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/DefaultObjectWrapper.java index 1a0e251..9d797ce 100644 --- a/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/DefaultObjectWrapper.java +++ b/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/DefaultObjectWrapper.java @@ -42,7 +42,9 @@ import java.util.ResourceBundle; import java.util.Set; import java.util.WeakHashMap; +import org.apache.freemarker.core.CallPlace; import org.apache.freemarker.core.Configuration; +import org.apache.freemarker.core.NonTemplateCallPlace; import org.apache.freemarker.core.Version; import org.apache.freemarker.core._CoreAPI; import org.apache.freemarker.core._DelayedFTLTypeDescription; @@ -55,8 +57,8 @@ import org.apache.freemarker.core.model.RichObjectWrapper; import org.apache.freemarker.core.model.TemplateBooleanModel; import org.apache.freemarker.core.model.TemplateCollectionModel; import org.apache.freemarker.core.model.TemplateDateModel; +import org.apache.freemarker.core.model.TemplateFunctionModel; import org.apache.freemarker.core.model.TemplateHashModel; -import org.apache.freemarker.core.model.TemplateMethodModel; import org.apache.freemarker.core.model.TemplateModel; import org.apache.freemarker.core.model.TemplateModelAdapter; import org.apache.freemarker.core.model.TemplateModelException; @@ -481,7 +483,7 @@ public class DefaultObjectWrapper implements RichObjectWrapper { * Wraps a Java method so that it can be called from templates, without wrapping its parent ("this") object. The * result is almost the same as that you would get by wrapping the parent object then getting the method from the * resulting {@link TemplateHashModel} by name. Except, if the wrapped method is overloaded, with this method you - * explicitly select a an overload, while otherwise you would get a {@link TemplateMethodModel} that selects an + * explicitly select an overload, while otherwise you would get a {@link OverloadedJavaMethodModel} that selects an * overload each time it's called based on the argument values. * * @param object The object whose method will be called, or {@code null} if {@code method} is a static method. @@ -489,8 +491,8 @@ public class DefaultObjectWrapper implements RichObjectWrapper { * @param method The method to call, which must be an (inherited) member of the class of {@code object}, as * described by {@link Method#invoke(Object, Object...)} */ - public TemplateMethodModel wrap(Object object, Method method) { - return new JavaMethodModel(object, method, method.getParameterTypes(), this); + public TemplateFunctionModel wrap(Object object, Method method) { + return new SimpleJavaMethodModel(object, method, method.getParameterTypes(), this); } /** @@ -582,7 +584,7 @@ public class DefaultObjectWrapper implements RichObjectWrapper { // This is for transparent interop with other wrappers (and ourselves) // Passing the targetClass allows e.g. a Jython-aware method that declares a // PyObject as its argument to receive a PyObject from a Jython-aware TemplateModel - // passed as an argument to TemplateMethodModel etc. + // passed as an argument to TemplateFunctionModel etc. if (model instanceof AdapterTemplateModel) { Object wrapped = ((AdapterTemplateModel) model).getAdaptedObject(targetClass); if (targetClass == Object.class || targetClass.isInstance(wrapped)) { @@ -1040,10 +1042,13 @@ public class DefaultObjectWrapper implements RichObjectWrapper { * constructor. Overloaded constructors and varargs are supported. Only public constructors will be called. * * @param clazz The class whose constructor we will call. - * @param arguments The list of {@link TemplateModel}-s to pass to the constructor after unwrapping them + * @param args The list of {@link TemplateModel}-s to pass to the constructor after unwrapping them + * @param callPlace Where the constructor is called from (which may contains information useful for overloaded + * constructor selection); you may want to use {@link NonTemplateCallPlace#INSTANCE}. + * if you call this from Java code. * @return The instance created; it's not wrapped into {@link TemplateModel}. */ - public Object newInstance(Class<?> clazz, List/*<? extends TemplateModel>*/ arguments) + public Object newInstance(Class<?> clazz, TemplateModel[] args, CallPlace callPlace) throws TemplateModelException { try { Object ctors = classIntrospector.get(clazz).get(ClassIntrospector.CONSTRUCTORS_KEY); @@ -1052,19 +1057,19 @@ public class DefaultObjectWrapper implements RichObjectWrapper { " has no public constructors."); } Constructor<?> ctor = null; - Object[] objargs; + Object[] pojoArgs; if (ctors instanceof SimpleMethod) { SimpleMethod sm = (SimpleMethod) ctors; ctor = (Constructor<?>) sm.getMember(); - objargs = sm.unwrapArguments(arguments, this); + pojoArgs = sm.unwrapArguments(args, this); try { - return ctor.newInstance(objargs); + return ctor.newInstance(pojoArgs); } catch (Exception e) { - if (e instanceof TemplateModelException) throw (TemplateModelException) e; throw _MethodUtil.newInvocationTemplateModelException(null, ctor, e); } } else if (ctors instanceof OverloadedMethods) { - final MemberAndArguments mma = ((OverloadedMethods) ctors).getMemberAndArguments(arguments, this); + // TODO [FM3] Utilize optional java type info in callPlace for overloaded method selection + final MemberAndArguments mma = ((OverloadedMethods) ctors).getMemberAndArguments(args, this); try { return mma.invokeConstructor(this); } catch (Exception e) {
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/8d5263f2/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/JavaMethodModel.java ---------------------------------------------------------------------- diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/JavaMethodModel.java b/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/JavaMethodModel.java index f89952e..6fb04c6 100644 --- a/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/JavaMethodModel.java +++ b/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/JavaMethodModel.java @@ -19,88 +19,34 @@ package org.apache.freemarker.core.model.impl; -import java.lang.reflect.Member; -import java.lang.reflect.Method; -import java.util.List; - -import org.apache.freemarker.core._UnexpectedTypeErrorExplainerTemplateModel; -import org.apache.freemarker.core.model.TemplateMethodModel; +import org.apache.freemarker.core.CallPlace; +import org.apache.freemarker.core.Environment; +import org.apache.freemarker.core.TemplateException; +import org.apache.freemarker.core.model.ArgumentArrayLayout; +import org.apache.freemarker.core.model.TemplateFunctionModel; import org.apache.freemarker.core.model.TemplateModel; -import org.apache.freemarker.core.model.TemplateModelException; /** - * Wraps a {@link Method} into the {@link TemplateMethodModel} interface. It is used by {@link BeanModel} to wrap - * non-overloaded methods. + * Common super interface (marker interface) for {@link TemplateFunctionModel}-s that stand for Java methods; do not + * implement it yourself! It meant to be implemented inside FreeMarker only. */ -public final class JavaMethodModel extends SimpleMethod implements TemplateMethodModel, - _UnexpectedTypeErrorExplainerTemplateModel { - private final Object object; - private final DefaultObjectWrapper wrapper; - - /** - * Creates a model for a specific method on a specific object. - * @param object the object to call the method on, or {@code null} for a static method. - * @param method the method that will be invoked. - * @param argTypes Either pass in {@code Method#getParameterTypes() method.getParameterTypes()} here, - * or reuse an earlier result of that call (for speed). Not {@code null}. - */ - JavaMethodModel(Object object, Method method, Class[] argTypes, DefaultObjectWrapper wrapper) { - super(method, argTypes); - this.object = object; - this.wrapper = wrapper; - } +public interface JavaMethodModel extends TemplateFunctionModel { /** - * Invokes the method, passing it the arguments from the list. + * Calls {@link #execute(TemplateModel[], CallPlace, Environment)}, but it emphasizes that the + * {@link Environment} parameters is ignored, and passes {@code null} for it. + * + * @param args As {@link #getFunctionArgumentArrayLayout()} always return {@code null} in + * {@link JavaMethodModel}-s, the length of this array corresponds to the number of actual arguments + * specified on the call site, and all parameters will be positional. + * + * @param callPlace Same as with {@link #execute(TemplateModel[], CallPlace, Environment)}. */ - @Override - public TemplateModel execute(List<? extends TemplateModel> args) throws TemplateModelException { - try { - return wrapper.invokeMethod(object, (Method) getMember(), - unwrapArguments(args, wrapper)); - } catch (TemplateModelException e) { - throw e; - } catch (Exception e) { - throw _MethodUtil.newInvocationTemplateModelException(object, getMember(), e); - } - } - - @Override - public String toString() { - return getMember().toString(); - } + TemplateModel execute(TemplateModel[] args, CallPlace callPlace) throws TemplateException; /** - * Implementation of experimental interface; don't use it, no backward compatibility guarantee! + * Always returns {@code null} for {@link JavaMethodModel}-s; hence, only positional parameters are supported. */ @Override - public Object[] explainTypeError(Class[] expectedClasses) { - final Member member = getMember(); - if (!(member instanceof Method)) { - return null; // This shouldn't occur - } - Method m = (Method) member; - - final Class returnType = m.getReturnType(); - if (returnType == null || returnType == void.class || returnType == Void.class) { - return null; // Calling it won't help - } - - String mName = m.getName(); - if (mName.startsWith("get") && mName.length() > 3 && Character.isUpperCase(mName.charAt(3)) - && (m.getParameterTypes().length == 0)) { - return new Object[] { - "Maybe using obj.something instead of obj.getSomething will yield the desired value." }; - } else if (mName.startsWith("is") && mName.length() > 2 && Character.isUpperCase(mName.charAt(2)) - && (m.getParameterTypes().length == 0)) { - return new Object[] { - "Maybe using obj.something instead of obj.isSomething will yield the desired value." }; - } else { - return new Object[] { - "Maybe using obj.something(", - (m.getParameterTypes().length != 0 ? "params" : ""), - ") instead of obj.something will yield the desired value" }; - } - } - -} \ No newline at end of file + ArgumentArrayLayout getFunctionArgumentArrayLayout(); +} http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/8d5263f2/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/OverloadedFixArgsMethods.java ---------------------------------------------------------------------- diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/OverloadedFixArgsMethods.java b/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/OverloadedFixArgsMethods.java index bff717d..4a8b69b 100644 --- a/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/OverloadedFixArgsMethods.java +++ b/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/OverloadedFixArgsMethods.java @@ -18,10 +18,7 @@ */ package org.apache.freemarker.core.model.impl; -import java.util.Collections; -import java.util.Iterator; -import java.util.List; - +import org.apache.freemarker.core.model.Constants; import org.apache.freemarker.core.model.ObjectWrapperAndUnwrapper; import org.apache.freemarker.core.model.TemplateModel; import org.apache.freemarker.core.model.TemplateModelException; @@ -36,28 +33,28 @@ class OverloadedFixArgsMethods extends OverloadedMethodsSubset { } @Override - Class[] preprocessParameterTypes(CallableMemberDescriptor memberDesc) { + Class<?>[] preprocessParameterTypes(CallableMemberDescriptor memberDesc) { return memberDesc.getParamTypes(); } @Override - void afterWideningUnwrappingHints(Class[] paramTypes, int[] paramNumericalTypes) { + void afterWideningUnwrappingHints(Class<?>[] paramTypes, int[] paramNumericalTypes) { // Do nothing } @Override - MaybeEmptyMemberAndArguments getMemberAndArguments(List tmArgs, DefaultObjectWrapper unwrapper) + MaybeEmptyMemberAndArguments getMemberAndArguments(TemplateModel[] tmArgs, DefaultObjectWrapper unwrapper) throws TemplateModelException { if (tmArgs == null) { // null is treated as empty args - tmArgs = Collections.EMPTY_LIST; + tmArgs = Constants.EMPTY_TEMPLATE_MODEL_ARRAY; } - final int argCount = tmArgs.size(); - final Class[][] unwrappingHintsByParamCount = getUnwrappingHintsByParamCount(); + final int argCount = tmArgs.length; + final Class<?>[][] unwrappingHintsByParamCount = getUnwrappingHintsByParamCount(); if (unwrappingHintsByParamCount.length <= argCount) { return EmptyMemberAndArguments.WRONG_NUMBER_OF_ARGUMENTS; } - Class[] unwarppingHints = unwrappingHintsByParamCount[argCount]; + Class<?>[] unwarppingHints = unwrappingHintsByParamCount[argCount]; if (unwarppingHints == null) { return EmptyMemberAndArguments.WRONG_NUMBER_OF_ARGUMENTS; } @@ -69,10 +66,9 @@ class OverloadedFixArgsMethods extends OverloadedMethodsSubset { typeFlags = null; } - Iterator it = tmArgs.iterator(); - for (int i = 0; i < argCount; ++i) { + for (int i = 0; i < argCount; i++) { Object pojo = unwrapper.tryUnwrapTo( - (TemplateModel) it.next(), + tmArgs[i], unwarppingHints[i], typeFlags != null ? typeFlags[i] : 0); if (pojo == ObjectWrapperAndUnwrapper.CANT_UNWRAP_TO_TARGET_CLASS) { http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/8d5263f2/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/OverloadedJavaMethodModel.java ---------------------------------------------------------------------- diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/OverloadedJavaMethodModel.java b/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/OverloadedJavaMethodModel.java new file mode 100644 index 0000000..1877687 --- /dev/null +++ b/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/OverloadedJavaMethodModel.java @@ -0,0 +1,86 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.freemarker.core.model.impl; + + +import org.apache.freemarker.core.CallPlace; +import org.apache.freemarker.core.Environment; +import org.apache.freemarker.core.TemplateException; +import org.apache.freemarker.core.model.ArgumentArrayLayout; +import org.apache.freemarker.core.model.TemplateFunctionModel; +import org.apache.freemarker.core.model.TemplateModel; +import org.apache.freemarker.core.model.TemplateModelException; + +/** + * Wraps a set of same-name overloaded methods behind {@link TemplateFunctionModel} interface, + * like if it was a single method; chooses among them behind the scenes on call-time based on the argument values + * (and the {@link CallPlace}). + * + * @see SimpleJavaMethodModel + */ +class OverloadedJavaMethodModel implements JavaMethodModel { + + private final Object object; + private final OverloadedMethods overloadedMethods; + private final DefaultObjectWrapper wrapper; + + OverloadedJavaMethodModel(Object object, OverloadedMethods overloadedMethods, DefaultObjectWrapper wrapper) { + this.object = object; + this.overloadedMethods = overloadedMethods; + this.wrapper = wrapper; + } + + @Override + public TemplateModel execute(TemplateModel[] args, CallPlace callPlace) throws TemplateException { + return execute(args, callPlace, null); + } + + /** + * See {@link #execute(TemplateModel[], CallPlace)}; the {@link Environment} parameter can be {@code null} in this + * implementation. The actual method to call from several overloaded methods will be chosen based on the classes of + * the arguments. + * + * @throws TemplateModelException + * if the method cannot be chosen unambiguously. + */ + @Override + public TemplateModel execute(TemplateModel[] args, CallPlace callPlace, Environment envUnused) + throws TemplateModelException { + // TODO [FM3] Utilize optional java type info in callPlace for overloaded method selection + MemberAndArguments maa = overloadedMethods.getMemberAndArguments(args, wrapper); + try { + return maa.invokeMethod(wrapper, object); + } catch (Exception e) { + if (e instanceof TemplateModelException) throw (TemplateModelException) e; + + throw _MethodUtil.newInvocationTemplateModelException( + object, + maa.getCallableMemberDescriptor(), + e); + } + } + + @Override + public ArgumentArrayLayout getFunctionArgumentArrayLayout() { + // Required to return null! See inherited JavaDoc. + return null; + } + +} http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/8d5263f2/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/OverloadedMethods.java ---------------------------------------------------------------------- diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/OverloadedMethods.java b/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/OverloadedMethods.java index 1ba1a56..fad854d 100644 --- a/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/OverloadedMethods.java +++ b/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/OverloadedMethods.java @@ -23,7 +23,6 @@ import java.lang.reflect.Constructor; import java.lang.reflect.Method; import java.util.HashSet; import java.util.Iterator; -import java.util.List; import org.apache.freemarker.core._DelayedConversionToString; import org.apache.freemarker.core._ErrorDescriptionBuilder; @@ -52,12 +51,12 @@ final class OverloadedMethods { } void addMethod(Method method) { - final Class[] paramTypes = method.getParameterTypes(); + final Class<?>[] paramTypes = method.getParameterTypes(); addCallableMemberDescriptor(new ReflectionCallableMemberDescriptor(method, paramTypes)); } - void addConstructor(Constructor constr) { - final Class[] paramTypes = constr.getParameterTypes(); + void addConstructor(Constructor<?> constr) { + final Class<?>[] paramTypes = constr.getParameterTypes(); addCallableMemberDescriptor(new ReflectionCallableMemberDescriptor(constr, paramTypes)); } @@ -72,7 +71,7 @@ final class OverloadedMethods { } } - MemberAndArguments getMemberAndArguments(List/*<TemplateModel>*/ tmArgs, DefaultObjectWrapper unwrapper) + MemberAndArguments getMemberAndArguments(TemplateModel[] tmArgs, DefaultObjectWrapper unwrapper) throws TemplateModelException { // Try to find a oms args match: MaybeEmptyMemberAndArguments fixArgsRes = fixArgMethods.getMemberAndArguments(tmArgs, unwrapper); @@ -104,7 +103,7 @@ final class OverloadedMethods { private Object[] toCompositeErrorMessage( final EmptyMemberAndArguments fixArgsEmptyRes, final EmptyMemberAndArguments varargsEmptyRes, - List tmArgs) { + TemplateModel[] tmArgs) { final Object[] argsErrorMsg; if (varargsEmptyRes != null) { if (fixArgsEmptyRes == null || fixArgsEmptyRes.isNumberOfArgumentsWrong()) { @@ -123,7 +122,7 @@ final class OverloadedMethods { return argsErrorMsg; } - private Object[] toErrorMessage(EmptyMemberAndArguments res, List/*<TemplateModel>*/ tmArgs) { + private Object[] toErrorMessage(EmptyMemberAndArguments res, TemplateModel[] tmArgs) { final Object[] unwrappedArgs = res.getUnwrappedArguments(); return new Object[] { res.getErrorDescription(), @@ -143,23 +142,26 @@ final class OverloadedMethods { @Override protected String doConversion(Object obj) { - final Iterator fixArgMethodsIter = fixArgMethods.getMemberDescriptors(); - final Iterator varargMethodsIter = varargMethods != null ? varargMethods.getMemberDescriptors() : null; + final Iterator<ReflectionCallableMemberDescriptor> fixArgMethodsIter + = fixArgMethods.getMemberDescriptors(); + final Iterator<ReflectionCallableMemberDescriptor> varargMethodsIter + = varargMethods != null ? varargMethods.getMemberDescriptors() : null; - boolean hasMethods = fixArgMethodsIter.hasNext() || (varargMethodsIter != null && varargMethodsIter.hasNext()); + boolean hasMethods = fixArgMethodsIter.hasNext() + || (varargMethodsIter != null && varargMethodsIter.hasNext()); if (hasMethods) { StringBuilder sb = new StringBuilder(); - HashSet fixArgMethods = new HashSet(); + HashSet<CallableMemberDescriptor> fixArgMethods = new HashSet<>(); while (fixArgMethodsIter.hasNext()) { if (sb.length() != 0) sb.append(",\n"); sb.append(" "); - CallableMemberDescriptor callableMemberDesc = (CallableMemberDescriptor) fixArgMethodsIter.next(); + CallableMemberDescriptor callableMemberDesc = fixArgMethodsIter.next(); fixArgMethods.add(callableMemberDesc); sb.append(callableMemberDesc.getDeclaration()); } if (varargMethodsIter != null) { while (varargMethodsIter.hasNext()) { - CallableMemberDescriptor callableMemberDesc = (CallableMemberDescriptor) varargMethodsIter.next(); + CallableMemberDescriptor callableMemberDesc = varargMethodsIter.next(); if (!fixArgMethods.contains(callableMemberDesc)) { if (sb.length() != 0) sb.append(",\n"); sb.append(" "); @@ -181,15 +183,16 @@ final class OverloadedMethods { * allows finding a matching overload. */ private void addMarkupBITipAfterNoNoMarchIfApplicable(_ErrorDescriptionBuilder edb, - List tmArgs) { - for (int argIdx = 0; argIdx < tmArgs.size(); argIdx++) { - Object tmArg = tmArgs.get(argIdx); + TemplateModel[] tmArgs) { + for (int argIdx = 0; argIdx < tmArgs.length; argIdx++) { + TemplateModel tmArg = tmArgs[argIdx]; if (tmArg instanceof TemplateMarkupOutputModel) { - for (Iterator membDescs = fixArgMethods.getMemberDescriptors(); membDescs.hasNext();) { - CallableMemberDescriptor membDesc = (CallableMemberDescriptor) membDescs.next(); - Class[] paramTypes = membDesc.getParamTypes(); + for (Iterator<ReflectionCallableMemberDescriptor> membDescs = fixArgMethods.getMemberDescriptors(); + membDescs.hasNext(); ) { + CallableMemberDescriptor membDesc = membDescs.next(); + Class<?>[] paramTypes = membDesc.getParamTypes(); - Class paramType = null; + Class<?> paramType = null; if (membDesc.isVarargs() && argIdx >= paramTypes.length - 1) { paramType = paramTypes[paramTypes.length - 1]; if (paramType.isArray()) { @@ -201,7 +204,7 @@ final class OverloadedMethods { } if (paramType != null) { if (paramType.isAssignableFrom(String.class) && !paramType.isAssignableFrom(tmArg.getClass())) { - edb.tip(JavaMethodModel.MARKUP_OUTPUT_TO_STRING_TIP); + edb.tip(SimpleJavaMethodModel.MARKUP_OUTPUT_TO_STRING_TIP); return; } } @@ -210,10 +213,10 @@ final class OverloadedMethods { } } - private _DelayedConversionToString getTMActualParameterTypes(List arguments) { - final String[] argumentTypeDescs = new String[arguments.size()]; - for (int i = 0; i < arguments.size(); i++) { - argumentTypeDescs[i] = FTLUtil.getTypeDescription((TemplateModel) arguments.get(i)); + private _DelayedConversionToString getTMActualParameterTypes(TemplateModel[] args) { + final String[] argumentTypeDescs = new String[args.length]; + for (int i = 0; i < args.length; i++) { + argumentTypeDescs[i] = FTLUtil.getTypeDescription(args[i]); } return new DelayedCallSignatureToString(argumentTypeDescs) { @@ -227,7 +230,7 @@ final class OverloadedMethods { } private Object getUnwrappedActualParameterTypes(Object[] unwrappedArgs) { - final Class[] argumentTypes = new Class[unwrappedArgs.length]; + final Class<?>[] argumentTypes = new Class<?>[unwrappedArgs.length]; for (int i = 0; i < unwrappedArgs.length; i++) { Object unwrappedArg = unwrappedArgs[i]; argumentTypes[i] = unwrappedArg != null ? unwrappedArg.getClass() : null; @@ -238,7 +241,7 @@ final class OverloadedMethods { @Override String argumentToString(Object argType) { return argType != null - ? _ClassUtil.getShortClassName((Class) argType) + ? _ClassUtil.getShortClassName((Class<?>) argType) : _ClassUtil.getShortClassNameOfObject(null); } @@ -247,7 +250,7 @@ final class OverloadedMethods { private abstract class DelayedCallSignatureToString extends _DelayedConversionToString { - public DelayedCallSignatureToString(Object[] argTypeArray) { + DelayedCallSignatureToString(Object[] argTypeArray) { super(argTypeArray); } http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/8d5263f2/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/OverloadedMethodsModel.java ---------------------------------------------------------------------- diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/OverloadedMethodsModel.java b/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/OverloadedMethodsModel.java deleted file mode 100644 index 72ca642..0000000 --- a/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/OverloadedMethodsModel.java +++ /dev/null @@ -1,65 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -package org.apache.freemarker.core.model.impl; - - -import java.util.List; - -import org.apache.freemarker.core.model.TemplateMethodModel; -import org.apache.freemarker.core.model.TemplateModel; -import org.apache.freemarker.core.model.TemplateModelException; - -/** - * Wraps a set of same-name overloaded methods behind {@link TemplateMethodModel} interface, - * like if it was a single method, chooses among them behind the scenes on call-time based on the argument values. - */ -class OverloadedMethodsModel implements TemplateMethodModel { - private final Object object; - private final OverloadedMethods overloadedMethods; - private final DefaultObjectWrapper wrapper; - - OverloadedMethodsModel(Object object, OverloadedMethods overloadedMethods, DefaultObjectWrapper wrapper) { - this.object = object; - this.overloadedMethods = overloadedMethods; - this.wrapper = wrapper; - } - - /** - * Invokes the method, passing it the arguments from the list. The actual - * method to call from several overloaded methods will be chosen based - * on the classes of the arguments. - * @throws TemplateModelException if the method cannot be chosen - * unambiguously. - */ - @Override - public TemplateModel execute(List<? extends TemplateModel> args) throws TemplateModelException { - MemberAndArguments maa = overloadedMethods.getMemberAndArguments(args, wrapper); - try { - return maa.invokeMethod(wrapper, object); - } catch (Exception e) { - if (e instanceof TemplateModelException) throw (TemplateModelException) e; - - throw _MethodUtil.newInvocationTemplateModelException( - object, - maa.getCallableMemberDescriptor(), - e); - } - } -} http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/8d5263f2/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/OverloadedMethodsSubset.java ---------------------------------------------------------------------- diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/OverloadedMethodsSubset.java b/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/OverloadedMethodsSubset.java index e783af8..aab6d43 100644 --- a/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/OverloadedMethodsSubset.java +++ b/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/OverloadedMethodsSubset.java @@ -27,6 +27,7 @@ import java.util.Map; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; +import org.apache.freemarker.core.model.TemplateModel; import org.apache.freemarker.core.model.TemplateModelException; import org.apache.freemarker.core.util._ClassUtil; import org.apache.freemarker.core.util._NullArgumentException; @@ -52,7 +53,7 @@ abstract class OverloadedMethodsSubset { ZERO_PARAM_COUNT_TYPE_FLAGS_ARRAY[0] = ALL_ZEROS_ARRAY; } - private Class[/*number of args*/][/*arg index*/] unwrappingHintsByParamCount; + private Class<?>[/*number of args*/][/*arg index*/] unwrappingHintsByParamCount; /** * Tells what types occur at a given parameter position with a bit field. See {@link TypeFlags}. @@ -61,10 +62,10 @@ abstract class OverloadedMethodsSubset { // TODO: This can cause memory-leak when classes are re-loaded. However, first the genericClassIntrospectionCache // and such need to be oms in this regard. - private final Map/*<ArgumentTypes, MaybeEmptyCallableMemberDescriptor>*/ argTypesToMemberDescCache - = new ConcurrentHashMap(6, 0.75f, 1); + private final Map<ArgumentTypes, MaybeEmptyCallableMemberDescriptor> argTypesToMemberDescCache + = new ConcurrentHashMap<>(6, 0.75f, 1); - private final List/*<ReflectionCallableMemberDescriptor>*/ memberDescs = new LinkedList(); + private final List<ReflectionCallableMemberDescriptor> memberDescs = new LinkedList<>(); OverloadedMethodsSubset() { // @@ -75,7 +76,7 @@ abstract class OverloadedMethodsSubset { // Warning: Do not modify this array, or put it into unwrappingHintsByParamCount by reference, as the arrays // inside that are modified! - final Class[] prepedParamTypes = preprocessParameterTypes(memberDesc); + final Class<?>[] prepedParamTypes = preprocessParameterTypes(memberDesc); final int paramCount = prepedParamTypes.length; // Must be the same as the length of the original param list // Merge these unwrapping hints with the existing table of hints: @@ -83,13 +84,13 @@ abstract class OverloadedMethodsSubset { unwrappingHintsByParamCount = new Class[paramCount + 1][]; unwrappingHintsByParamCount[paramCount] = prepedParamTypes.clone(); } else if (unwrappingHintsByParamCount.length <= paramCount) { - Class[][] newUnwrappingHintsByParamCount = new Class[paramCount + 1][]; + Class<?>[][] newUnwrappingHintsByParamCount = new Class[paramCount + 1][]; System.arraycopy(unwrappingHintsByParamCount, 0, newUnwrappingHintsByParamCount, 0, unwrappingHintsByParamCount.length); unwrappingHintsByParamCount = newUnwrappingHintsByParamCount; unwrappingHintsByParamCount[paramCount] = prepedParamTypes.clone(); } else { - Class[] unwrappingHints = unwrappingHintsByParamCount[paramCount]; + Class<?>[] unwrappingHints = unwrappingHintsByParamCount[paramCount]; if (unwrappingHints == null) { unwrappingHintsByParamCount[paramCount] = prepedParamTypes.clone(); } else { @@ -123,7 +124,7 @@ abstract class OverloadedMethodsSubset { afterWideningUnwrappingHints(prepedParamTypes, typeFlagsByParamIdx); } - Class[][] getUnwrappingHintsByParamCount() { + Class<?>[][] getUnwrappingHintsByParamCount() { return unwrappingHintsByParamCount; } @@ -131,12 +132,11 @@ abstract class OverloadedMethodsSubset { justification="Locks for member descriptor creation only") final MaybeEmptyCallableMemberDescriptor getMemberDescriptorForArgs(Object[] args, boolean varArg) { ArgumentTypes argTypes = new ArgumentTypes(args); - MaybeEmptyCallableMemberDescriptor memberDesc - = (MaybeEmptyCallableMemberDescriptor) argTypesToMemberDescCache.get(argTypes); + MaybeEmptyCallableMemberDescriptor memberDesc = argTypesToMemberDescCache.get(argTypes); if (memberDesc == null) { // Synchronized so that we won't unnecessarily invoke the same member desc. for multiple times in parallel. synchronized (argTypesToMemberDescCache) { - memberDesc = (MaybeEmptyCallableMemberDescriptor) argTypesToMemberDescCache.get(argTypes); + memberDesc = argTypesToMemberDescCache.get(argTypes); if (memberDesc == null) { memberDesc = argTypes.getMostSpecific(memberDescs, varArg); argTypesToMemberDescCache.put(argTypes, memberDesc); @@ -146,14 +146,14 @@ abstract class OverloadedMethodsSubset { return memberDesc; } - Iterator/*<ReflectionCallableMemberDescriptor>*/ getMemberDescriptors() { + Iterator<ReflectionCallableMemberDescriptor> getMemberDescriptors() { return memberDescs.iterator(); } - abstract Class[] preprocessParameterTypes(CallableMemberDescriptor memberDesc); - abstract void afterWideningUnwrappingHints(Class[] paramTypes, int[] paramNumericalTypes); + abstract Class<?>[] preprocessParameterTypes(CallableMemberDescriptor memberDesc); + abstract void afterWideningUnwrappingHints(Class<?>[] paramTypes, int[] paramNumericalTypes); - abstract MaybeEmptyMemberAndArguments getMemberAndArguments(List/*<TemplateModel>*/ tmArgs, + abstract MaybeEmptyMemberAndArguments getMemberAndArguments(TemplateModel[] tmArgs, DefaultObjectWrapper unwrapper) throws TemplateModelException; /** @@ -168,7 +168,7 @@ abstract class OverloadedMethodsSubset { * @param c1 Parameter type 1 * @param c2 Parameter type 2 */ - protected Class getCommonSupertypeForUnwrappingHint(Class c1, Class c2) { + Class<?> getCommonSupertypeForUnwrappingHint(Class<?> c1, Class<?> c2) { if (c1 == c2) return c1; // This also means that the hint for (Integer, Integer) will be Integer, not just Number. This is consistent // with how non-overloaded method hints work. @@ -214,7 +214,7 @@ abstract class OverloadedMethodsSubset { // - One of classes was a primitive type // - One of classes was a numerical type (either boxing type or primitive) - Set commonTypes = _MethodUtil.getAssignables(c1, c2); + Set<Class<?>> commonTypes = _MethodUtil.getAssignables(c1, c2); commonTypes.retainAll(_MethodUtil.getAssignables(c2, c1)); if (commonTypes.isEmpty()) { // Can happen when at least one of the arguments is an interface, as @@ -226,11 +226,11 @@ abstract class OverloadedMethodsSubset { // because of interfaces. I.e., if you call this method for String.class // and Number.class, you'll have Comparable, Serializable, and Object as // maximal elements. - List max = new ArrayList(); - listCommonTypes: for (Iterator commonTypesIter = commonTypes.iterator(); commonTypesIter.hasNext(); ) { - Class clazz = (Class) commonTypesIter.next(); - for (Iterator maxIter = max.iterator(); maxIter.hasNext(); ) { - Class maxClazz = (Class) maxIter.next(); + List<Class<?>> max = new ArrayList<>(); + listCommonTypes: + for (Class<?> clazz : commonTypes) { + for (Iterator<Class<?>> maxIter = max.iterator(); maxIter.hasNext(); ) { + Class<?> maxClazz = maxIter.next(); if (_MethodUtil.isMoreOrSameSpecificParameterType(maxClazz, clazz, false /*bugfixed [1]*/, 0) != 0) { // clazz can't be maximal, if there's already a more specific or equal maximal than it. continue listCommonTypes; @@ -251,8 +251,8 @@ abstract class OverloadedMethodsSubset { if (max.size() > 1) { // we have an ambiguity // Find the non-interface class - for (Iterator it = max.iterator(); it.hasNext(); ) { - Class maxCl = (Class) it.next(); + for (Iterator<Class<?>> it = max.iterator(); it.hasNext(); ) { + Class<?> maxCl = it.next(); if (!maxCl.isInterface()) { if (maxCl != Object.class) { // This actually shouldn't ever happen, but to be sure... // If it's not Object, we use it as the most specific @@ -278,7 +278,7 @@ abstract class OverloadedMethodsSubset { } } - return (Class) max.get(0); + return max.get(0); } /** @@ -286,7 +286,7 @@ abstract class OverloadedMethodsSubset { * count or if we are in pre-2.3.21 mode, or {@link #ALL_ZEROS_ARRAY} if there were no parameters that turned * on a flag. The returned {@code int}-s are one or more {@link TypeFlags} constants binary "or"-ed together. */ - final protected int[] getTypeFlags(int paramCount) { + final int[] getTypeFlags(int paramCount) { return typeFlagsByParamCount != null && typeFlagsByParamCount.length > paramCount ? typeFlagsByParamCount[paramCount] : null; http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/8d5263f2/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/OverloadedVarArgsMethods.java ---------------------------------------------------------------------- diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/OverloadedVarArgsMethods.java b/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/OverloadedVarArgsMethods.java index 6547923..8085693 100644 --- a/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/OverloadedVarArgsMethods.java +++ b/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/OverloadedVarArgsMethods.java @@ -19,10 +19,8 @@ package org.apache.freemarker.core.model.impl; import java.lang.reflect.Array; -import java.util.Collections; -import java.util.Iterator; -import java.util.List; +import org.apache.freemarker.core.model.Constants; import org.apache.freemarker.core.model.ObjectWrapperAndUnwrapper; import org.apache.freemarker.core.model.TemplateModel; import org.apache.freemarker.core.model.TemplateModelException; @@ -41,10 +39,10 @@ class OverloadedVarArgsMethods extends OverloadedMethodsSubset { * Replaces the last parameter type with the array component type of it. */ @Override - Class[] preprocessParameterTypes(CallableMemberDescriptor memberDesc) { - final Class[] preprocessedParamTypes = memberDesc.getParamTypes().clone(); + Class<?>[] preprocessParameterTypes(CallableMemberDescriptor memberDesc) { + final Class<?>[] preprocessedParamTypes = memberDesc.getParamTypes().clone(); int ln = preprocessedParamTypes.length; - final Class varArgsCompType = preprocessedParamTypes[ln - 1].getComponentType(); + final Class<?> varArgsCompType = preprocessedParamTypes[ln - 1].getComponentType(); if (varArgsCompType == null) { throw new BugException("Only varargs methods should be handled here"); } @@ -53,7 +51,7 @@ class OverloadedVarArgsMethods extends OverloadedMethodsSubset { } @Override - void afterWideningUnwrappingHints(Class[] paramTypes, int[] paramNumericalTypes) { + void afterWideningUnwrappingHints(Class<?>[] paramTypes, int[] paramNumericalTypes) { // Overview // -------- // @@ -67,7 +65,7 @@ class OverloadedVarArgsMethods extends OverloadedMethodsSubset { // So we only update the already existing hints. Remember that we already have m(t1, t2) there. final int paramCount = paramTypes.length; - final Class[][] unwrappingHintsByParamCount = getUnwrappingHintsByParamCount(); + final Class<?>[][] unwrappingHintsByParamCount = getUnwrappingHintsByParamCount(); // The case of e(t1, t2), e(t1, t2, t2), e(t1, t2, t2, t2), ..., where e is an *earlierly* added method. // When that was added, this method wasn't added yet, so it had no chance updating the hints of this method, @@ -75,7 +73,7 @@ class OverloadedVarArgsMethods extends OverloadedMethodsSubset { // FIXME: Only needed if m(t1, t2) was filled an empty slot, otherwise whatever was there was already // widened by the preceding hints, so this will be a no-op. for (int i = paramCount - 1; i >= 0; i--) { - final Class[] previousHints = unwrappingHintsByParamCount[i]; + final Class<?>[] previousHints = unwrappingHintsByParamCount[i]; if (previousHints != null) { widenHintsToCommonSupertypes( paramCount, @@ -88,7 +86,7 @@ class OverloadedVarArgsMethods extends OverloadedMethodsSubset { // so we do that now: // FIXME: Same as above; it's often unnecessary. if (paramCount + 1 < unwrappingHintsByParamCount.length) { - Class[] oneLongerHints = unwrappingHintsByParamCount[paramCount + 1]; + Class<?>[] oneLongerHints = unwrappingHintsByParamCount[paramCount + 1]; if (oneLongerHints != null) { widenHintsToCommonSupertypes( paramCount, @@ -114,8 +112,8 @@ class OverloadedVarArgsMethods extends OverloadedMethodsSubset { } private void widenHintsToCommonSupertypes( - int paramCountOfWidened, Class[] wideningTypes, int[] wideningTypeFlags) { - final Class[] typesToWiden = getUnwrappingHintsByParamCount()[paramCountOfWidened]; + int paramCountOfWidened, Class<?>[] wideningTypes, int[] wideningTypeFlags) { + final Class<?>[] typesToWiden = getUnwrappingHintsByParamCount()[paramCountOfWidened]; if (typesToWiden == null) { return; // no such overload exists; nothing to widen } @@ -127,7 +125,7 @@ class OverloadedVarArgsMethods extends OverloadedMethodsSubset { typesToWiden[i] = getCommonSupertypeForUnwrappingHint(typesToWiden[i], wideningTypes[i]); } if (typesToWidenLen > wideningTypesLen) { - Class varargsComponentType = wideningTypes[wideningTypesLen - 1]; + Class<?> varargsComponentType = wideningTypes[wideningTypesLen - 1]; for (int i = wideningTypesLen; i < typesToWidenLen; ++i) { typesToWiden[i] = getCommonSupertypeForUnwrappingHint(typesToWiden[i], varargsComponentType); } @@ -137,20 +135,20 @@ class OverloadedVarArgsMethods extends OverloadedMethodsSubset { } @Override - MaybeEmptyMemberAndArguments getMemberAndArguments(List tmArgs, DefaultObjectWrapper unwrapper) + MaybeEmptyMemberAndArguments getMemberAndArguments(TemplateModel[] tmArgs, DefaultObjectWrapper unwrapper) throws TemplateModelException { if (tmArgs == null) { // null is treated as empty args - tmArgs = Collections.EMPTY_LIST; + tmArgs = Constants.EMPTY_TEMPLATE_MODEL_ARRAY; } - final int argsLen = tmArgs.size(); - final Class[][] unwrappingHintsByParamCount = getUnwrappingHintsByParamCount(); + final int argsLen = tmArgs.length; + final Class<?>[][] unwrappingHintsByParamCount = getUnwrappingHintsByParamCount(); final Object[] pojoArgs = new Object[argsLen]; int[] typesFlags = null; // Going down starting from methods with args.length + 1 parameters, because we must try to match against a case // where all specified args are fixargs, and we have 0 varargs. outer: for (int paramCount = Math.min(argsLen + 1, unwrappingHintsByParamCount.length - 1); paramCount >= 0; --paramCount) { - Class[] unwarappingHints = unwrappingHintsByParamCount[paramCount]; + Class<?>[] unwarappingHints = unwrappingHintsByParamCount[paramCount]; if (unwarappingHints == null) { if (paramCount == 0) { return EmptyMemberAndArguments.WRONG_NUMBER_OF_ARGUMENTS; @@ -164,11 +162,10 @@ class OverloadedVarArgsMethods extends OverloadedMethodsSubset { } // Try to unwrap the arguments - Iterator it = tmArgs.iterator(); - for (int i = 0; i < argsLen; ++i) { + for (int i = 0; i < argsLen; i++) { int paramIdx = i < paramCount ? i : paramCount - 1; Object pojo = unwrapper.tryUnwrapTo( - (TemplateModel) it.next(), + tmArgs[i], unwarappingHints[paramIdx], typesFlags != null ? typesFlags[paramIdx] : 0); if (pojo == ObjectWrapperAndUnwrapper.CANT_UNWRAP_TO_TARGET_CLASS) { @@ -187,7 +184,7 @@ class OverloadedVarArgsMethods extends OverloadedMethodsSubset { if (argsOrErrorIdx instanceof Object[]) { pojoArgsWithArray = (Object[]) argsOrErrorIdx; } else { - return EmptyMemberAndArguments.noCompatibleOverload(((Integer) argsOrErrorIdx).intValue()); + return EmptyMemberAndArguments.noCompatibleOverload((Integer) argsOrErrorIdx); } if (typesFlags != null) { // Note that overloaded method selection has already accounted for overflow errors when the method @@ -210,11 +207,11 @@ class OverloadedVarArgsMethods extends OverloadedMethodsSubset { * order (1-based index) of the argument that couldn't be unwrapped. */ private Object replaceVarargsSectionWithArray( - Object[] args, List modelArgs, CallableMemberDescriptor memberDesc, DefaultObjectWrapper unwrapper) - throws TemplateModelException { - final Class[] paramTypes = memberDesc.getParamTypes(); + Object[] args, TemplateModel[] tmArgs, CallableMemberDescriptor memberDesc, DefaultObjectWrapper unwrapper) + throws TemplateModelException { + final Class<?>[] paramTypes = memberDesc.getParamTypes(); final int paramCount = paramTypes.length; - final Class varArgsCompType = paramTypes[paramCount - 1].getComponentType(); + final Class<?> varArgsCompType = paramTypes[paramCount - 1].getComponentType(); final int totalArgCount = args.length; final int fixArgCount = paramCount - 1; if (args.length != paramCount) { @@ -222,18 +219,18 @@ class OverloadedVarArgsMethods extends OverloadedMethodsSubset { System.arraycopy(args, 0, packedArgs, 0, fixArgCount); Object varargs = Array.newInstance(varArgsCompType, totalArgCount - fixArgCount); for (int i = fixArgCount; i < totalArgCount; ++i) { - Object val = unwrapper.tryUnwrapTo((TemplateModel) modelArgs.get(i), varArgsCompType); + Object val = unwrapper.tryUnwrapTo(tmArgs[i], varArgsCompType); if (val == ObjectWrapperAndUnwrapper.CANT_UNWRAP_TO_TARGET_CLASS) { - return Integer.valueOf(i + 1); + return i + 1; } Array.set(varargs, i - fixArgCount, val); } packedArgs[fixArgCount] = varargs; return packedArgs; } else { - Object val = unwrapper.tryUnwrapTo((TemplateModel) modelArgs.get(fixArgCount), varArgsCompType); + Object val = unwrapper.tryUnwrapTo(tmArgs[fixArgCount], varArgsCompType); if (val == ObjectWrapperAndUnwrapper.CANT_UNWRAP_TO_TARGET_CLASS) { - return Integer.valueOf(fixArgCount + 1); + return fixArgCount + 1; } Object array = Array.newInstance(varArgsCompType, 1); Array.set(array, 0, val); http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/8d5263f2/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/ResourceBundleModel.java ---------------------------------------------------------------------- diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/ResourceBundleModel.java b/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/ResourceBundleModel.java index 457c7ef..b4cca30 100644 --- a/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/ResourceBundleModel.java +++ b/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/ResourceBundleModel.java @@ -22,16 +22,19 @@ package org.apache.freemarker.core.model.impl; import java.text.MessageFormat; import java.util.Enumeration; import java.util.Hashtable; -import java.util.Iterator; -import java.util.List; import java.util.Map; import java.util.MissingResourceException; import java.util.ResourceBundle; import java.util.Set; +import org.apache.freemarker.core.CallPlace; +import org.apache.freemarker.core.Environment; +import org.apache.freemarker.core.TemplateException; +import org.apache.freemarker.core._CallableUtils; import org.apache.freemarker.core._DelayedJQuote; import org.apache.freemarker.core._TemplateModelException; -import org.apache.freemarker.core.model.TemplateMethodModel; +import org.apache.freemarker.core.model.ArgumentArrayLayout; +import org.apache.freemarker.core.model.TemplateFunctionModel; import org.apache.freemarker.core.model.TemplateModel; import org.apache.freemarker.core.model.TemplateModelException; @@ -50,11 +53,7 @@ import org.apache.freemarker.core.model.TemplateModelException; * for MessageFormat with arguments arg1, arg2 and arg3</li> * </ul> */ -public class ResourceBundleModel - extends - BeanModel - implements - TemplateMethodModel { +public class ResourceBundleModel extends BeanModel implements TemplateFunctionModel { private Hashtable<String, MessageFormat> formats = null; @@ -108,25 +107,23 @@ public class ResourceBundleModel * rest of the arguments. The created MessageFormats are cached for later reuse. */ @Override - public TemplateModel execute(List<? extends TemplateModel> args) - throws TemplateModelException { + public TemplateModel execute(TemplateModel[] args, CallPlace callPlace, Environment env) throws TemplateException { // Must have at least one argument - the key - if (args.size() < 1) - throw new TemplateModelException("No message key was specified"); + if (args.length < 1) + throw new TemplateException("No message key was specified", env); // Read it - Iterator<? extends TemplateModel> it = args.iterator(); - String key = unwrap((TemplateModel) it.next()).toString(); + String key = _CallableUtils.castArgToString(args, 0); try { - if (!it.hasNext()) { + if (args.length == 1) { return wrap(((ResourceBundle) object).getObject(key)); } - + // Copy remaining arguments into an Object[] - int paramsLen = args.size() - 1; + int paramsLen = args.length - 1; Object[] params = new Object[paramsLen]; for (int i = 0; i < paramsLen; ++i) - params[i] = unwrap(it.next()); - + params[i] = unwrap(args[1 + i]); + // Invoke format return new BeanAndStringModel(format(key, params), wrapper); } catch (MissingResourceException e) { @@ -136,6 +133,11 @@ public class ResourceBundleModel } } + @Override + public ArgumentArrayLayout getFunctionArgumentArrayLayout() { + return null; + } + /** * Provides direct access to caching format engine from code (instead of from script). */ http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/8d5263f2/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/SimpleJavaMethodModel.java ---------------------------------------------------------------------- diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/SimpleJavaMethodModel.java b/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/SimpleJavaMethodModel.java new file mode 100644 index 0000000..d27d131 --- /dev/null +++ b/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/SimpleJavaMethodModel.java @@ -0,0 +1,123 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.freemarker.core.model.impl; + +import java.lang.reflect.Member; +import java.lang.reflect.Method; + +import org.apache.freemarker.core.CallPlace; +import org.apache.freemarker.core.Environment; +import org.apache.freemarker.core.TemplateException; +import org.apache.freemarker.core._UnexpectedTypeErrorExplainerTemplateModel; +import org.apache.freemarker.core.model.ArgumentArrayLayout; +import org.apache.freemarker.core.model.TemplateFunctionModel; +import org.apache.freemarker.core.model.TemplateModel; +import org.apache.freemarker.core.model.TemplateModelException; + +/** + * Wraps a {@link Method} into the {@link TemplateFunctionModel} interface. It is used by {@link BeanModel} to wrap + * non-overloaded methods. + * + * @see OverloadedJavaMethodModel + */ +public final class SimpleJavaMethodModel extends SimpleMethod implements JavaMethodModel, + _UnexpectedTypeErrorExplainerTemplateModel { + private final Object object; + private final DefaultObjectWrapper wrapper; + + /** + * Creates a model for a specific method on a specific object. + * @param object the object to call the method on, or {@code null} for a static method. + * @param method the method that will be invoked. + * @param argTypes Either pass in {@code Method#getParameterTypes() method.getParameterTypes()} here, + * or reuse an earlier result of that call (for speed). Not {@code null}. + */ + SimpleJavaMethodModel(Object object, Method method, Class[] argTypes, DefaultObjectWrapper wrapper) { + super(method, argTypes); + this.object = object; + this.wrapper = wrapper; + } + + @Override + public TemplateModel execute(TemplateModel[] args, CallPlace callPlace) throws TemplateException { + return execute(args, callPlace, null); + } + + /** + * See {@link #execute(TemplateModel[], CallPlace)}; the {@link Environment} parameter can be {@code null} in + * this implementation. + */ + @Override + public TemplateModel execute(TemplateModel[] args, CallPlace callPlace, Environment envUnused) throws + TemplateException { + try { + return wrapper.invokeMethod(object, (Method) getMember(), unwrapArguments(args, wrapper)); + } catch (TemplateModelException e) { + throw e; + } catch (Exception e) { + throw _MethodUtil.newInvocationTemplateModelException(object, getMember(), e); + } + } + + @Override + public ArgumentArrayLayout getFunctionArgumentArrayLayout() { + // Required to return null! See inherited JavaDoc. + return null; + } + + @Override + public String toString() { + return getMember().toString(); + } + + /** + * Implementation of experimental interface; don't use it, no backward compatibility guarantee! + */ + @Override + public Object[] explainTypeError(Class[] expectedClasses) { + final Member member = getMember(); + if (!(member instanceof Method)) { + return null; // This shouldn't occur + } + Method m = (Method) member; + + final Class returnType = m.getReturnType(); + if (returnType == null || returnType == void.class || returnType == Void.class) { + return null; // Calling it won't help + } + + String mName = m.getName(); + if (mName.startsWith("get") && mName.length() > 3 && Character.isUpperCase(mName.charAt(3)) + && (m.getParameterTypes().length == 0)) { + return new Object[] { + "Maybe using obj.something instead of obj.getSomething will yield the desired value." }; + } else if (mName.startsWith("is") && mName.length() > 2 && Character.isUpperCase(mName.charAt(2)) + && (m.getParameterTypes().length == 0)) { + return new Object[] { + "Maybe using obj.something instead of obj.isSomething will yield the desired value." }; + } else { + return new Object[] { + "Maybe using obj.something(", + (m.getParameterTypes().length != 0 ? "params" : ""), + ") instead of obj.something will yield the desired value" }; + } + } + +} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/8d5263f2/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/SimpleMethod.java ---------------------------------------------------------------------- diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/SimpleMethod.java b/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/SimpleMethod.java index d891190..678b9ed 100644 --- a/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/SimpleMethod.java +++ b/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/SimpleMethod.java @@ -20,14 +20,12 @@ package org.apache.freemarker.core.model.impl; import java.lang.reflect.Array; import java.lang.reflect.Member; -import java.util.Collections; -import java.util.Iterator; -import java.util.List; import org.apache.freemarker.core._DelayedFTLTypeDescription; import org.apache.freemarker.core._DelayedOrdinal; import org.apache.freemarker.core._ErrorDescriptionBuilder; import org.apache.freemarker.core._TemplateModelException; +import org.apache.freemarker.core.model.Constants; import org.apache.freemarker.core.model.ObjectWrapperAndUnwrapper; import org.apache.freemarker.core.model.TemplateMarkupOutputModel; import org.apache.freemarker.core.model.TemplateModel; @@ -35,7 +33,7 @@ import org.apache.freemarker.core.model.TemplateModelException; import org.apache.freemarker.core.util._ClassUtil; /** - * This class is used for as a base for non-overloaded method models and for constructors. + * This class is used as a base for non-overloaded method models and for constructors. * (For overloaded methods and constructors see {@link OverloadedMethods}.) */ class SimpleMethod { @@ -45,101 +43,101 @@ class SimpleMethod { + "But consider if the Java method whose argument it will be can handle markup strings properly."; private final Member member; - private final Class[] argTypes; + private final Class<?>[] argTypes; - protected SimpleMethod(Member member, Class[] argTypes) { + SimpleMethod(Member member, Class<?>[] argTypes) { this.member = member; this.argTypes = argTypes; } - Object[] unwrapArguments(List arguments, DefaultObjectWrapper wrapper) throws TemplateModelException { - if (arguments == null) { - arguments = Collections.EMPTY_LIST; + Object[] unwrapArguments(TemplateModel[] args, DefaultObjectWrapper wrapper) throws TemplateModelException { + if (args == null) { + args = Constants.EMPTY_TEMPLATE_MODEL_ARRAY; } boolean isVarArg = _MethodUtil.isVarargs(member); int typesLen = argTypes.length; if (isVarArg) { - if (typesLen - 1 > arguments.size()) { + if (typesLen - 1 > args.length) { throw new _TemplateModelException( _MethodUtil.invocationErrorMessageStart(member), - " takes at least ", Integer.valueOf(typesLen - 1), + " takes at least ", typesLen - 1, typesLen - 1 == 1 ? " argument" : " arguments", ", but ", - Integer.valueOf(arguments.size()), " was given."); + args.length, " was given."); } - } else if (typesLen != arguments.size()) { + } else if (typesLen != args.length) { throw new _TemplateModelException( _MethodUtil.invocationErrorMessageStart(member), - " takes ", Integer.valueOf(typesLen), typesLen == 1 ? " argument" : " arguments", ", but ", - Integer.valueOf(arguments.size()), " was given."); + " takes ", typesLen, typesLen == 1 ? " argument" : " arguments", ", but ", + args.length, " was given."); } - return unwrapArguments(arguments, argTypes, isVarArg, wrapper); + return unwrapArguments(args, argTypes, isVarArg, wrapper); } - private Object[] unwrapArguments(List args, Class[] argTypes, boolean isVarargs, + private Object[] unwrapArguments(TemplateModel[] args, Class<?>[] argTypes, boolean isVarargs, DefaultObjectWrapper w) throws TemplateModelException { if (args == null) return null; int typesLen = argTypes.length; - int argsLen = args.size(); + int argsLen = args.length; Object[] unwrappedArgs = new Object[typesLen]; // Unwrap arguments: - Iterator it = args.iterator(); + int argsIdx = 0; int normalArgCnt = isVarargs ? typesLen - 1 : typesLen; - int argIdx = 0; - while (argIdx < normalArgCnt) { - Class argType = argTypes[argIdx]; - TemplateModel argVal = (TemplateModel) it.next(); + int unwrappedArgsIdx = 0; + while (unwrappedArgsIdx < normalArgCnt) { + Class<?> argType = argTypes[unwrappedArgsIdx]; + TemplateModel argVal = args[argsIdx++]; Object unwrappedArgVal = w.tryUnwrapTo(argVal, argType); if (unwrappedArgVal == ObjectWrapperAndUnwrapper.CANT_UNWRAP_TO_TARGET_CLASS) { - throw createArgumentTypeMismarchException(argIdx, argVal, argType); + throw createArgumentTypeMismatchException(unwrappedArgsIdx, argVal, argType); } if (unwrappedArgVal == null && argType.isPrimitive()) { - throw createNullToPrimitiveArgumentException(argIdx, argType); + throw createNullToPrimitiveArgumentException(unwrappedArgsIdx, argType); } - unwrappedArgs[argIdx++] = unwrappedArgVal; + unwrappedArgs[unwrappedArgsIdx++] = unwrappedArgVal; } if (isVarargs) { // The last argType, which is the vararg type, wasn't processed yet. - Class varargType = argTypes[typesLen - 1]; - Class varargItemType = varargType.getComponentType(); - if (!it.hasNext()) { - unwrappedArgs[argIdx++] = Array.newInstance(varargItemType, 0); + Class<?> varargType = argTypes[typesLen - 1]; + Class<?> varargItemType = varargType.getComponentType(); + if (argsIdx >= args.length) { + unwrappedArgs[unwrappedArgsIdx] = Array.newInstance(varargItemType, 0); } else { - TemplateModel argVal = (TemplateModel) it.next(); + TemplateModel argVal = args[argsIdx++]; Object unwrappedArgVal; // We first try to treat the last argument as a vararg *array*. // This is consistent to what OverloadedVarArgMethod does. - if (argsLen - argIdx == 1 + if (argsLen - unwrappedArgsIdx == 1 && (unwrappedArgVal = w.tryUnwrapTo(argVal, varargType)) != ObjectWrapperAndUnwrapper.CANT_UNWRAP_TO_TARGET_CLASS) { // It was a vararg array. - unwrappedArgs[argIdx++] = unwrappedArgVal; + unwrappedArgs[unwrappedArgsIdx] = unwrappedArgVal; } else { // It wasn't a vararg array, so we assume it's a vararg // array *item*, possibly followed by further ones. - int varargArrayLen = argsLen - argIdx; + int varargArrayLen = argsLen - unwrappedArgsIdx; Object varargArray = Array.newInstance(varargItemType, varargArrayLen); for (int varargIdx = 0; varargIdx < varargArrayLen; varargIdx++) { - TemplateModel varargVal = (TemplateModel) (varargIdx == 0 ? argVal : it.next()); + TemplateModel varargVal = varargIdx == 0 ? argVal : args[argsIdx++]; Object unwrappedVarargVal = w.tryUnwrapTo(varargVal, varargItemType); if (unwrappedVarargVal == ObjectWrapperAndUnwrapper.CANT_UNWRAP_TO_TARGET_CLASS) { - throw createArgumentTypeMismarchException( - argIdx + varargIdx, varargVal, varargItemType); + throw createArgumentTypeMismatchException( + unwrappedArgsIdx + varargIdx, varargVal, varargItemType); } if (unwrappedVarargVal == null && varargItemType.isPrimitive()) { - throw createNullToPrimitiveArgumentException(argIdx + varargIdx, varargItemType); + throw createNullToPrimitiveArgumentException(unwrappedArgsIdx + varargIdx, varargItemType); } Array.set(varargArray, varargIdx, unwrappedVarargVal); } - unwrappedArgs[argIdx++] = varargArray; + unwrappedArgs[unwrappedArgsIdx] = varargArray; } } } @@ -147,11 +145,11 @@ class SimpleMethod { return unwrappedArgs; } - private TemplateModelException createArgumentTypeMismarchException( - int argIdx, TemplateModel argVal, Class targetType) { + private TemplateModelException createArgumentTypeMismatchException( + int argIdx, TemplateModel argVal, Class<?> targetType) { _ErrorDescriptionBuilder desc = new _ErrorDescriptionBuilder( _MethodUtil.invocationErrorMessageStart(member), " couldn't be called: Can't convert the ", - new _DelayedOrdinal(Integer.valueOf(argIdx + 1)), + new _DelayedOrdinal(argIdx + 1), " argument's value to the target Java type, ", _ClassUtil.getShortClassName(targetType), ". The type of the actual value was: ", new _DelayedFTLTypeDescription(argVal)); if (argVal instanceof TemplateMarkupOutputModel && (targetType.isAssignableFrom(String.class))) { @@ -160,10 +158,10 @@ class SimpleMethod { return new _TemplateModelException(desc); } - private TemplateModelException createNullToPrimitiveArgumentException(int argIdx, Class targetType) { + private TemplateModelException createNullToPrimitiveArgumentException(int argIdx, Class<?> targetType) { return new _TemplateModelException( _MethodUtil.invocationErrorMessageStart(member), " couldn't be called: The value of the ", - new _DelayedOrdinal(Integer.valueOf(argIdx + 1)), + new _DelayedOrdinal(argIdx + 1), " argument was null, but the target Java parameter type (", _ClassUtil.getShortClassName(targetType), ") is primitive and so can't store null."); } http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/8d5263f2/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/StaticModel.java ---------------------------------------------------------------------- diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/StaticModel.java b/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/StaticModel.java index 9c4db4e..fc32348 100644 --- a/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/StaticModel.java +++ b/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/StaticModel.java @@ -27,20 +27,18 @@ import java.util.Iterator; import java.util.Map; import org.apache.freemarker.core.model.TemplateCollectionModel; +import org.apache.freemarker.core.model.TemplateFunctionModel; import org.apache.freemarker.core.model.TemplateHashModelEx; -import org.apache.freemarker.core.model.TemplateMethodModel; import org.apache.freemarker.core.model.TemplateModel; import org.apache.freemarker.core.model.TemplateModelException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** - * Wraps the static fields and methods of a class in a - * {@link org.apache.freemarker.core.model.TemplateHashModel}. - * Fields are wrapped using {@link DefaultObjectWrapper#wrap(Object)}, and - * methods are wrapped into an appropriate {@link TemplateMethodModel} instance. - * Unfortunately, there is currently no support for bean property-style - * calls of static methods, similar to that in {@link BeanModel}. + * Wraps the static fields and methods of a class in a {@link org.apache.freemarker.core.model.TemplateHashModel}. + * Fields are wrapped using {@link DefaultObjectWrapper#wrap(Object)}, and methods are wrapped into an appropriate + * {@link TemplateFunctionModel} instance. There is currently no support for bean property-style calls of static + * methods, similar to that in {@link BeanModel} (as it's not part for the JavaBeans specification). */ final class StaticModel implements TemplateHashModelEx { @@ -167,10 +165,10 @@ final class StaticModel implements TemplateHashModelEx { Object value = entry.getValue(); if (value instanceof Method) { Method method = (Method) value; - entry.setValue(new JavaMethodModel(null, method, + entry.setValue(new SimpleJavaMethodModel(null, method, method.getParameterTypes(), wrapper)); } else if (value instanceof OverloadedMethods) { - entry.setValue(new OverloadedMethodsModel(null, (OverloadedMethods) value, wrapper)); + entry.setValue(new OverloadedJavaMethodModel(null, (OverloadedMethods) value, wrapper)); } } } http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/8d5263f2/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/TemplateModelListSequence.java ---------------------------------------------------------------------- diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/TemplateModelListSequence.java b/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/TemplateModelListSequence.java index 2394394..a93df33 100644 --- a/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/TemplateModelListSequence.java +++ b/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/TemplateModelListSequence.java @@ -21,26 +21,29 @@ package org.apache.freemarker.core.model.impl; import java.util.List; -import org.apache.freemarker.core.model.TemplateMethodModel; +import org.apache.freemarker.core.model.TemplateFunctionModel; import org.apache.freemarker.core.model.TemplateModel; import org.apache.freemarker.core.model.TemplateSequenceModel; /** * A sequence that wraps a {@link List} of {@link TemplateModel}-s. It does not copy the original - * list. It's mostly useful when implementing {@link TemplateMethodModel}-es that collect items from other + * list. It's mostly useful when implementing {@link TemplateFunctionModel}-es that collect items from other * {@link TemplateModel}-s. */ public class TemplateModelListSequence implements TemplateSequenceModel { - private List/*<TemplateModel>*/ list; + private final List<? extends TemplateModel> list; - public TemplateModelListSequence(List list) { + /** + * @param list The list of items; will not be copied, will not be modified. + */ + public TemplateModelListSequence(List<? extends TemplateModel> list) { this.list = list; } @Override public TemplateModel get(int index) { - return (TemplateModel) list.get(index); + return list.get(index); } @Override http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/8d5263f2/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/_MethodUtil.java ---------------------------------------------------------------------- diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/_MethodUtil.java b/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/_MethodUtil.java index 82da455..004ebaa 100644 --- a/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/_MethodUtil.java +++ b/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/_MethodUtil.java @@ -64,7 +64,7 @@ public final class _MethodUtil { * </ul> */ // TODO Seems that we don't use the full functionality of this anymore, so we could simplify this. See usages. - public static int isMoreOrSameSpecificParameterType(final Class specific, final Class generic, boolean bugfixed, + static int isMoreOrSameSpecificParameterType(final Class<?> specific, final Class<?> generic, boolean bugfixed, int ifHigherThan) { if (ifHigherThan >= 4) return 0; if (generic.isAssignableFrom(specific)) { @@ -79,7 +79,7 @@ public final class _MethodUtil { return isWideningPrimitiveNumberConversion(specific, generic) ? 3 : 0; } else { // => specificIsPrim && !genericIsPrim if (bugfixed) { - final Class specificAsBoxed = _ClassUtil.primitiveClassToBoxingClass(specific); + final Class<?> specificAsBoxed = _ClassUtil.primitiveClassToBoxingClass(specific); if (specificAsBoxed == generic) { // A primitive class is more specific than its boxing class, because it can't store null return 2; @@ -110,7 +110,7 @@ public final class _MethodUtil { } // of: !generic.isAssignableFrom(specific) } - private static boolean isWideningPrimitiveNumberConversion(final Class source, final Class target) { + private static boolean isWideningPrimitiveNumberConversion(final Class<?> source, final Class<?> target) { if (target == Short.TYPE && (source == Byte.TYPE)) { return true; } else if (target == Integer.TYPE && @@ -134,7 +134,7 @@ public final class _MethodUtil { } } - private static boolean isWideningBoxedNumberConversion(final Class source, final Class target) { + private static boolean isWideningBoxedNumberConversion(final Class<?> source, final Class<?> target) { if (target == Short.class && source == Byte.class) { return true; } else if (target == Integer.class && @@ -161,42 +161,42 @@ public final class _MethodUtil { /** * Attention, this doesn't handle primitive classes correctly, nor numerical conversions. */ - public static Set getAssignables(Class c1, Class c2) { - Set s = new HashSet(); + static Set<Class<?>> getAssignables(Class<?> c1, Class<?> c2) { + Set<Class<?>> s = new HashSet<>(); collectAssignables(c1, c2, s); return s; } - private static void collectAssignables(Class c1, Class c2, Set s) { + private static void collectAssignables(Class<?> c1, Class<?> c2, Set<Class<?>> s) { if (c1.isAssignableFrom(c2)) { s.add(c1); } - Class sc = c1.getSuperclass(); + Class<?> sc = c1.getSuperclass(); if (sc != null) { collectAssignables(sc, c2, s); } - Class[] itf = c1.getInterfaces(); - for (Class anItf : itf) { + Class<?>[] itf = c1.getInterfaces(); + for (Class<?> anItf : itf) { collectAssignables(anItf, c2, s); } } - public static Class[] getParameterTypes(Member member) { + public static Class<?>[] getParameterTypes(Member member) { if (member instanceof Method) { return ((Method) member).getParameterTypes(); } - if (member instanceof Constructor) { - return ((Constructor) member).getParameterTypes(); + if (member instanceof Constructor<?>) { + return ((Constructor<?>) member).getParameterTypes(); } throw new IllegalArgumentException("\"member\" must be Method or Constructor"); } - public static boolean isVarargs(Member member) { + static boolean isVarargs(Member member) { if (member instanceof Method) { return ((Method) member).isVarArgs(); } if (member instanceof Constructor) { - return ((Constructor) member).isVarArgs(); + return ((Constructor<?>) member).isVarArgs(); } throw new BugException(); } @@ -223,7 +223,7 @@ public final class _MethodUtil { sb.append(member.getName()); sb.append('('); - Class[] paramTypes = _MethodUtil.getParameterTypes(member); + Class<?>[] paramTypes = _MethodUtil.getParameterTypes(member); for (int i = 0; i < paramTypes.length; i++) { if (i != 0) sb.append(", "); String paramTypeDecl = _ClassUtil.getShortClassName(paramTypes[i]); @@ -239,7 +239,7 @@ public final class _MethodUtil { return sb.toString(); } - public static Object[] invocationErrorMessageStart(Member member) { + static Object[] invocationErrorMessageStart(Member member) { return invocationErrorMessageStart(member, member instanceof Constructor); } @@ -247,7 +247,7 @@ public final class _MethodUtil { return new Object[] { "Java ", isConstructor ? "constructor " : "method ", new _DelayedJQuote(member) }; } - public static TemplateModelException newInvocationTemplateModelException(Object object, Member member, Throwable e) { + static TemplateModelException newInvocationTemplateModelException(Object object, Member member, Throwable e) { return newInvocationTemplateModelException( object, member, @@ -256,7 +256,7 @@ public final class _MethodUtil { e); } - public static TemplateModelException newInvocationTemplateModelException(Object object, CallableMemberDescriptor callableMemberDescriptor, Throwable e) { + static TemplateModelException newInvocationTemplateModelException(Object object, CallableMemberDescriptor callableMemberDescriptor, Throwable e) { return newInvocationTemplateModelException( object, new _DelayedConversionToString(callableMemberDescriptor) { @@ -294,7 +294,7 @@ public final class _MethodUtil { * Extracts the JavaBeans property from a reader method name, or returns {@code null} if the method name doesn't * look like a reader method name. */ - public static String getBeanPropertyNameFromReaderMethodName(String name, Class<?> returnType) { + static String getBeanPropertyNameFromReaderMethodName(String name, Class<?> returnType) { int start; if (name.startsWith("get")) { start = 3; http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/8d5263f2/freemarker-core/src/main/java/org/apache/freemarker/core/util/FTLUtil.java ---------------------------------------------------------------------- diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/util/FTLUtil.java b/freemarker-core/src/main/java/org/apache/freemarker/core/util/FTLUtil.java index 2923647..7c58284 100644 --- a/freemarker-core/src/main/java/org/apache/freemarker/core/util/FTLUtil.java +++ b/freemarker-core/src/main/java/org/apache/freemarker/core/util/FTLUtil.java @@ -34,7 +34,6 @@ import org.apache.freemarker.core.model.TemplateFunctionModel; import org.apache.freemarker.core.model.TemplateHashModel; import org.apache.freemarker.core.model.TemplateHashModelEx; import org.apache.freemarker.core.model.TemplateMarkupOutputModel; -import org.apache.freemarker.core.model.TemplateMethodModel; import org.apache.freemarker.core.model.TemplateModel; import org.apache.freemarker.core.model.TemplateModelIterator; import org.apache.freemarker.core.model.TemplateNodeModel; @@ -45,6 +44,7 @@ import org.apache.freemarker.core.model.TemplateSequenceModel; import org.apache.freemarker.core.model.WrapperTemplateModel; import org.apache.freemarker.core.model.impl.BeanAndStringModel; import org.apache.freemarker.core.model.impl.BeanModel; +import org.apache.freemarker.core.model.impl.JavaMethodModel; /** * Static utility methods that perform tasks specific to the FreeMarker Template Language (FTL). @@ -760,7 +760,7 @@ public final class FTLUtil { } if (callable instanceof TemplateFunctionModel) { - String f = "function"; // TODO [FM3][CF] Should say "method" sometimes + String f = callable instanceof JavaMethodModel ? "method" : "function"; result = d == null ? f : d + "+" + f; } @@ -799,7 +799,8 @@ public final class FTLUtil { appendTypeName(sb, typeNamesAppended, _CoreAPI.isMacro(cl) ? "macro" : "directive"); } if (TemplateFunctionModel.class.isAssignableFrom(cl)) { - appendTypeName(sb, typeNamesAppended, "function"); // TODO [FM3][CF] should say "method" sometimes + appendTypeName(sb, typeNamesAppended, + JavaMethodModel.class.isAssignableFrom(cl) ? "method" : "function"); } } @@ -812,10 +813,6 @@ public final class FTLUtil { appendTypeName(sb, typeNamesAppended, "iterator"); } - if (TemplateMethodModel.class.isAssignableFrom(cl)) { - appendTypeName(sb, typeNamesAppended, "method"); - } - if (Environment.Namespace.class.isAssignableFrom(cl)) { appendTypeName(sb, typeNamesAppended, "namespace"); } else if (TemplateHashModelEx.class.isAssignableFrom(cl)) { http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/8d5263f2/freemarker-core/src/main/javacc/FTL.jj ---------------------------------------------------------------------- diff --git a/freemarker-core/src/main/javacc/FTL.jj b/freemarker-core/src/main/javacc/FTL.jj index 5d1b354..4201d84 100644 --- a/freemarker-core/src/main/javacc/FTL.jj +++ b/freemarker-core/src/main/javacc/FTL.jj @@ -2040,7 +2040,7 @@ ASTExpression DynamicKey(ASTExpression exp) : /** * production for an arglist part of a method invocation. */ -ASTExpMethodCall MethodArgs(ASTExpression exp) : +ASTExpFunctionCall MethodArgs(ASTExpression exp) : { ArrayList args = new ArrayList(); Token end; @@ -2051,7 +2051,7 @@ ASTExpMethodCall MethodArgs(ASTExpression exp) : end = <CLOSE_PAREN> { args.trimToSize(); - ASTExpMethodCall result = new ASTExpMethodCall(exp, args); + ASTExpFunctionCall result = new ASTExpFunctionCall(exp, args); result.setLocation(template, exp, end); return result; }
