Removed BeansWrapper, merging it into DefaultObjectWrapper (the o.a.f.core.model.impl.beans packageis gone now). It works, but there's a lot of unused classes and logic now, which will have to be removed.
Project: http://git-wip-us.apache.org/repos/asf/incubator-freemarker/repo Commit: http://git-wip-us.apache.org/repos/asf/incubator-freemarker/commit/051a0822 Tree: http://git-wip-us.apache.org/repos/asf/incubator-freemarker/tree/051a0822 Diff: http://git-wip-us.apache.org/repos/asf/incubator-freemarker/diff/051a0822 Branch: refs/heads/3 Commit: 051a0822329aae44642b491fb1a10e75ad4ea778 Parents: 3187a09 Author: ddekany <[email protected]> Authored: Mon Feb 27 10:36:49 2017 +0100 Committer: ddekany <[email protected]> Committed: Tue Feb 28 23:57:22 2017 +0100 ---------------------------------------------------------------------- .../Java-code-style-FreeMarker.xml | 20 +- .../apache/freemarker/core/ASTExpression.java | 2 +- .../core/BuiltInsForMultipleTypes.java | 4 +- .../freemarker/core/BuiltInsForSequences.java | 2 +- .../freemarker/core/BuiltInsForStringsMisc.java | 14 +- .../apache/freemarker/core/Configurable.java | 14 +- .../org/apache/freemarker/core/_CoreLogs.java | 12 +- .../core/_ErrorDescriptionBuilder.java | 2 +- .../core/_ObjectBuilderSettingEvaluator.java | 6 +- .../core/_SettingEvaluationEnvironment.java | 8 +- .../freemarker/core/model/ObjectWrapper.java | 8 +- .../core/model/TemplateMethodModel.java | 2 +- .../core/model/TemplateMethodModelEx.java | 2 +- .../freemarker/core/model/impl/APIModel.java | 45 + .../core/model/impl/ArgumentTypes.java | 647 +++++++ .../freemarker/core/model/impl/ArrayModel.java | 126 ++ .../freemarker/core/model/impl/BeanModel.java | 362 ++++ .../core/model/impl/BeansModelCache.java | 73 + .../core/model/impl/BooleanModel.java | 40 + .../model/impl/CallableMemberDescriptor.java | 56 + .../core/model/impl/CharacterOrString.java | 45 + .../core/model/impl/ClassBasedModelFactory.java | 148 ++ .../core/model/impl/ClassChangeNotifier.java | 32 + .../core/model/impl/ClassIntrospector.java | 807 +++++++++ .../model/impl/ClassIntrospectorBuilder.java | 190 ++ .../core/model/impl/CollectionAdapter.java | 88 + .../core/model/impl/CollectionModel.java | 109 ++ .../freemarker/core/model/impl/DateModel.java | 76 + .../model/impl/DefaultEnumerationAdapter.java | 121 ++ .../core/model/impl/DefaultObjectWrapper.java | 1474 +++++++++++++++- .../model/impl/DefaultObjectWrapperBuilder.java | 100 +- .../impl/DefaultObjectWrapperConfiguration.java | 187 +- .../impl/EmptyCallableMemberDescriptor.java | 35 + .../model/impl/EmptyMemberAndArguments.java | 93 + .../core/model/impl/EnumerationModel.java | 108 ++ .../freemarker/core/model/impl/HashAdapter.java | 180 ++ .../model/impl/InvalidPropertyException.java | 34 + .../core/model/impl/IteratorModel.java | 112 ++ .../model/impl/JRebelClassChangeNotifier.java | 58 + .../freemarker/core/model/impl/MapModel.java | 120 ++ .../MaybeEmptyCallableMemberDescriptor.java | 25 + .../impl/MaybeEmptyMemberAndArguments.java | 22 + .../core/model/impl/MemberAndArguments.java | 64 + .../model/impl/MethodAppearanceFineTuner.java | 156 ++ .../core/model/impl/MethodSorter.java | 32 + .../freemarker/core/model/impl/ModelCache.java | 143 ++ .../core/model/impl/ModelFactory.java | 34 + .../NonPrimitiveArrayBackedReadOnlyList.java | 42 + .../freemarker/core/model/impl/NumberModel.java | 60 + .../model/impl/OverloadedFixArgsMethods.java | 99 ++ .../core/model/impl/OverloadedMethods.java | 271 +++ .../core/model/impl/OverloadedMethodsModel.java | 82 + .../model/impl/OverloadedMethodsSubset.java | 402 +++++ .../core/model/impl/OverloadedNumberUtil.java | 1289 ++++++++++++++ .../model/impl/OverloadedVarArgsMethods.java | 245 +++ .../impl/PrimtiveArrayBackedReadOnlyList.java | 47 + .../ReflectionCallableMemberDescriptor.java | 95 + .../core/model/impl/ResourceBundleModel.java | 190 ++ .../core/model/impl/SequenceAdapter.java | 68 + .../freemarker/core/model/impl/SetAdapter.java | 32 + .../freemarker/core/model/impl/SimpleHash.java | 5 +- .../core/model/impl/SimpleMapModel.java | 129 ++ .../core/model/impl/SimpleMethod.java | 174 ++ .../core/model/impl/SimpleMethodModel.java | 133 ++ .../core/model/impl/SimpleObjectWrapper.java | 19 +- .../core/model/impl/SimpleSequence.java | 5 +- .../core/model/impl/SingletonCustomizer.java | 51 + .../freemarker/core/model/impl/StaticModel.java | 177 ++ .../core/model/impl/StaticModels.java | 43 + .../freemarker/core/model/impl/StringModel.java | 63 + .../freemarker/core/model/impl/TypeFlags.java | 130 ++ .../core/model/impl/UnsafeMethods.java | 112 ++ .../freemarker/core/model/impl/_EnumModels.java | 54 + .../freemarker/core/model/impl/_MethodUtil.java | 293 ++++ .../freemarker/core/model/impl/_ModelAPI.java | 220 +++ .../core/model/impl/_ModelImplApi.java | 37 - .../core/model/impl/_StaticObjectWrappers.java | 5 - .../core/model/impl/beans/APIModel.java | 45 - .../core/model/impl/beans/ArgumentTypes.java | 647 ------- .../core/model/impl/beans/ArrayModel.java | 126 -- .../core/model/impl/beans/BeanModel.java | 365 ---- .../core/model/impl/beans/BeansModelCache.java | 73 - .../core/model/impl/beans/BeansWrapper.java | 1644 ------------------ .../model/impl/beans/BeansWrapperBuilder.java | 159 -- .../impl/beans/BeansWrapperConfiguration.java | 232 --- .../core/model/impl/beans/BooleanModel.java | 40 - .../impl/beans/CallableMemberDescriptor.java | 56 - .../model/impl/beans/CharacterOrString.java | 45 - .../impl/beans/ClassBasedModelFactory.java | 148 -- .../model/impl/beans/ClassChangeNotifier.java | 32 - .../model/impl/beans/ClassIntrospector.java | 808 --------- .../impl/beans/ClassIntrospectorBuilder.java | 190 -- .../model/impl/beans/CollectionAdapter.java | 88 - .../core/model/impl/beans/CollectionModel.java | 109 -- .../core/model/impl/beans/DateModel.java | 79 - .../beans/EmptyCallableMemberDescriptor.java | 35 - .../impl/beans/EmptyMemberAndArguments.java | 93 - .../core/model/impl/beans/EnumerationModel.java | 108 -- .../core/model/impl/beans/HashAdapter.java | 180 -- .../impl/beans/InvalidPropertyException.java | 34 - .../core/model/impl/beans/IteratorModel.java | 112 -- .../impl/beans/JRebelClassChangeNotifier.java | 58 - .../core/model/impl/beans/MapModel.java | 120 -- .../MaybeEmptyCallableMemberDescriptor.java | 25 - .../beans/MaybeEmptyMemberAndArguments.java | 22 - .../model/impl/beans/MemberAndArguments.java | 64 - .../impl/beans/MethodAppearanceFineTuner.java | 94 - .../core/model/impl/beans/MethodSorter.java | 32 - .../core/model/impl/beans/ModelCache.java | 143 -- .../core/model/impl/beans/ModelFactory.java | 34 - .../NonPrimitiveArrayBackedReadOnlyList.java | 42 - .../core/model/impl/beans/NumberModel.java | 60 - .../impl/beans/OverloadedFixArgsMethods.java | 99 -- .../model/impl/beans/OverloadedMethods.java | 271 --- .../impl/beans/OverloadedMethodsModel.java | 83 - .../impl/beans/OverloadedMethodsSubset.java | 402 ----- .../model/impl/beans/OverloadedNumberUtil.java | 1289 -------------- .../impl/beans/OverloadedVarArgsMethods.java | 245 --- .../beans/PrimtiveArrayBackedReadOnlyList.java | 47 - .../ReflectionCallableMemberDescriptor.java | 95 - .../model/impl/beans/ResourceBundleModel.java | 190 -- .../core/model/impl/beans/SequenceAdapter.java | 68 - .../core/model/impl/beans/SetAdapter.java | 32 - .../core/model/impl/beans/SimpleMapModel.java | 132 -- .../core/model/impl/beans/SimpleMethod.java | 174 -- .../model/impl/beans/SimpleMethodModel.java | 134 -- .../model/impl/beans/SingletonCustomizer.java | 51 - .../core/model/impl/beans/StaticModel.java | 177 -- .../core/model/impl/beans/StaticModels.java | 43 - .../core/model/impl/beans/StringModel.java | 63 - .../core/model/impl/beans/TypeFlags.java | 130 -- .../core/model/impl/beans/UnsafeMethods.java | 112 -- .../core/model/impl/beans/_BeansAPI.java | 220 --- .../core/model/impl/beans/_EnumModels.java | 54 - .../core/model/impl/beans/_MethodUtil.java | 293 ---- .../core/model/impl/beans/package.html | 39 - .../freemarker/core/model/impl/package.html | 39 + .../apache/freemarker/core/util/FTLUtil.java | 22 +- .../apache/freemarker/core/util/_ClassUtil.java | 2 +- .../freemarker/servlet/FreemarkerServlet.java | 4 - .../jsp/CustomTagAndELFunctionCombiner.java | 2 +- .../freemarker/servlet/jsp/JspContextModel.java | 2 +- .../freemarker/servlet/jsp/JspTagModelBase.java | 6 +- .../freemarker/servlet/jsp/TaglibFactory.java | 17 +- .../model/impl/beans/unsafeMethods.properties | 98 -- .../core/model/impl/unsafeMethods.properties | 98 ++ src/manual/en_US/FM3-CHANGE-LOG.txt | 6 +- .../freemarker/core/ConfigurationTest.java | 7 +- .../freemarker/core/IteratorIssuesTest.java | 43 +- .../core/ObjectBuilderSettingsTest.java | 40 +- .../impl/AbstractParallelIntrospectionTest.java | 125 ++ .../model/impl/AlphabeticalMethodSorter.java | 49 + .../CommonSupertypeForUnwrappingHintTest.java | 129 ++ .../model/impl/DefaultObjectWrapperDesc.java | 30 + .../model/impl/DefaultObjectWrapperInc.java | 30 + ...jectWrapperModelFactoryRegistrationTest.java | 94 + .../impl/DefaultObjectWrapperReadOnlyTest.java | 87 + .../DefaultObjectWrapperSingletonsTest.java | 689 ++++++++ .../model/impl/DefaultObjectWrapperTest.java | 191 +- .../DefaultObjectWrapperWithShortedMethods.java | 41 + .../DefaultObjectWrapperWithSortedMethods.java | 40 + .../core/model/impl/EnumModelsTest.java | 84 + .../core/model/impl/ErrorMessagesTest.java | 170 ++ .../impl/FineTuneMethodAppearanceTest.java | 64 + .../GetlessMethodsAsPropertyGettersRule.java | 67 + .../core/model/impl/IsApplicableTest.java | 171 ++ .../impl/IsMoreSpecificParameterTypeTest.java | 98 ++ .../Java7MembersOnlyDefaultObjectWrapper.java | 102 ++ .../impl/ManyObjectsOfDifferentClasses.java | 249 +++ .../impl/ManyStaticsOfDifferentClasses.java | 236 +++ .../model/impl/MiscNumericalOperationsTest.java | 111 ++ .../model/impl/ModelAPINewInstanceTest.java | 134 ++ .../core/model/impl/ModelCacheTest.java | 69 + .../model/impl/OverloadedNumberUtilTest.java | 585 +++++++ .../impl/ParameterListPreferabilityTest.java | 445 +++++ .../impl/PrallelObjectIntrospectionTest.java | 43 + .../impl/PrallelStaticIntrospectionTest.java | 47 + .../core/model/impl/RationalNumber.java | 90 + .../core/model/impl/StaticModelsTest.java | 90 + .../core/model/impl/TypeFlagsTest.java | 671 +++++++ .../AbstractParallelIntrospectionTest.java | 125 -- .../impl/beans/AlphabeticalMethodSorter.java | 49 - .../impl/beans/BeansAPINewInstanceTest.java | 134 -- .../model/impl/beans/BeansWrapperBasics.java | 62 - .../impl/beans/BeansWrapperCachesTest.java | 99 -- .../core/model/impl/beans/BeansWrapperDesc.java | 30 - .../core/model/impl/beans/BeansWrapperInc.java | 30 - .../model/impl/beans/BeansWrapperMiscTest.java | 57 - .../impl/beans/BeansWrapperReadOnlyTest.java | 95 - .../impl/beans/BeansWrapperSingletonsTest.java | 743 -------- .../beans/BeansWrapperWithShortedMethods.java | 41 - .../CommonSupertypeForUnwrappingHintTest.java | 129 -- .../impl/beans/DefaultObjectWrapperDesc.java | 30 - .../impl/beans/DefaultObjectWrapperInc.java | 30 - .../DefaultObjectWrapperWithSortedMethods.java | 41 - .../core/model/impl/beans/EnumModelsTest.java | 84 - .../model/impl/beans/ErrorMessagesTest.java | 170 -- .../beans/FineTuneMethodAppearanceTest.java | 65 - .../GetlessMethodsAsPropertyGettersRule.java | 70 - .../core/model/impl/beans/IsApplicableTest.java | 171 -- .../beans/IsMoreSpecificParameterTypeTest.java | 98 -- .../beans/Java7MembersOnlyBeansWrapper.java | 102 -- .../beans/ManyObjectsOfDifferentClasses.java | 249 --- .../beans/ManyStaticsOfDifferentClasses.java | 236 --- .../impl/beans/MiscNumericalOperationsTest.java | 111 -- .../core/model/impl/beans/ModelCacheTest.java | 65 - .../impl/beans/OverloadedNumberUtilTest.java | 585 ------- .../beans/ParameterListPreferabilityTest.java | 445 ----- .../beans/PrallelObjectIntrospectionTest.java | 43 - .../beans/PrallelStaticIntrospectionTest.java | 47 - .../core/model/impl/beans/RationalNumber.java | 90 - .../core/model/impl/beans/StaticModelsTest.java | 90 - .../core/model/impl/beans/TypeFlagsTest.java | 671 ------- .../servlet/jsp/RealServletContainertTest.java | 19 +- .../freemarker/servlet/jsp/TLDParsingTest.java | 8 +- .../test/templatesuite/TemplateTestCase.java | 63 +- .../test/templatesuite/models/Listables.java | 5 - .../models/OverloadedMethods2.java | 2 +- .../SimpleMapAndCollectionObjectWrapper.java | 3 +- .../expected/attributes-modernModels.txt | 4 +- .../basic/WEB-INF/expected/attributes.txt | 4 +- .../test/templatesuite/expected/bean-maps.txt | 115 -- .../test/templatesuite/expected/beans.txt | 56 - .../expected/default-object-wrapper.txt | 56 + .../test/templatesuite/expected/listhash.txt | 44 - .../templatesuite/templates/api-builtins.ftl | 8 +- .../test/templatesuite/templates/bean-maps.ftl | 58 - .../test/templatesuite/templates/beans.ftl | 60 - .../templates/default-object-wrapper.ftl | 60 + .../templates/overloaded-methods.ftl | 4 +- .../freemarker/test/templatesuite/testcases.xml | 53 +- 231 files changed, 15741 insertions(+), 16580 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/051a0822/src/ide-settings/IntelliJ-IDEA/Java-code-style-FreeMarker.xml ---------------------------------------------------------------------- diff --git a/src/ide-settings/IntelliJ-IDEA/Java-code-style-FreeMarker.xml b/src/ide-settings/IntelliJ-IDEA/Java-code-style-FreeMarker.xml index 9615559..ae87ca3 100644 --- a/src/ide-settings/IntelliJ-IDEA/Java-code-style-FreeMarker.xml +++ b/src/ide-settings/IntelliJ-IDEA/Java-code-style-FreeMarker.xml @@ -1,3 +1,21 @@ +<!-- + 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. +--> <code_scheme name="FreeMarker"> <option name="LINE_SEPARATOR" value="
" /> <option name="CLASS_COUNT_TO_USE_IMPORT_ON_DEMAND" value="99" /> @@ -27,7 +45,7 @@ <option name="JD_PARAM_DESCRIPTION_ON_NEW_LINE" value="true" /> <option name="WRAP_COMMENTS" value="true" /> <JavaCodeStyleSettings> - <option name="CLASS_NAMES_IN_JAVADOC" value="2" /> + <option name="CLASS_NAMES_IN_JAVADOC" value="3" /> </JavaCodeStyleSettings> <codeStyleSettings language="JAVA"> <option name="RIGHT_MARGIN" value="120" /> http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/051a0822/src/main/java/org/apache/freemarker/core/ASTExpression.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/apache/freemarker/core/ASTExpression.java b/src/main/java/org/apache/freemarker/core/ASTExpression.java index 6635e35..af08ccd 100644 --- a/src/main/java/org/apache/freemarker/core/ASTExpression.java +++ b/src/main/java/org/apache/freemarker/core/ASTExpression.java @@ -29,7 +29,7 @@ import org.apache.freemarker.core.model.TemplateModelException; import org.apache.freemarker.core.model.TemplateNumberModel; import org.apache.freemarker.core.model.TemplateScalarModel; import org.apache.freemarker.core.model.TemplateSequenceModel; -import org.apache.freemarker.core.model.impl.beans.BeanModel; +import org.apache.freemarker.core.model.impl.BeanModel; /** * AST expression node superclass http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/051a0822/src/main/java/org/apache/freemarker/core/BuiltInsForMultipleTypes.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/apache/freemarker/core/BuiltInsForMultipleTypes.java b/src/main/java/org/apache/freemarker/core/BuiltInsForMultipleTypes.java index b7bd7f6..d7f0249 100644 --- a/src/main/java/org/apache/freemarker/core/BuiltInsForMultipleTypes.java +++ b/src/main/java/org/apache/freemarker/core/BuiltInsForMultipleTypes.java @@ -42,8 +42,8 @@ import org.apache.freemarker.core.model.TemplateTransformModel; import org.apache.freemarker.core.model.impl.SimpleDate; import org.apache.freemarker.core.model.impl.SimpleNumber; import org.apache.freemarker.core.model.impl.SimpleScalar; -import org.apache.freemarker.core.model.impl.beans.OverloadedMethodsModel; -import org.apache.freemarker.core.model.impl.beans.SimpleMethodModel; +import org.apache.freemarker.core.model.impl.OverloadedMethodsModel; +import org.apache.freemarker.core.model.impl.SimpleMethodModel; import org.apache.freemarker.core.util.BugException; import org.apache.freemarker.core.valueformat.TemplateDateFormat; import org.apache.freemarker.core.valueformat.TemplateNumberFormat; http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/051a0822/src/main/java/org/apache/freemarker/core/BuiltInsForSequences.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/apache/freemarker/core/BuiltInsForSequences.java b/src/main/java/org/apache/freemarker/core/BuiltInsForSequences.java index 92c59b8..c54328c 100644 --- a/src/main/java/org/apache/freemarker/core/BuiltInsForSequences.java +++ b/src/main/java/org/apache/freemarker/core/BuiltInsForSequences.java @@ -44,7 +44,7 @@ import org.apache.freemarker.core.model.impl.CollectionAndSequence; import org.apache.freemarker.core.model.impl.SimpleNumber; import org.apache.freemarker.core.model.impl.SimpleScalar; import org.apache.freemarker.core.model.impl.TemplateModelListSequence; -import org.apache.freemarker.core.model.impl.beans.CollectionModel; +import org.apache.freemarker.core.model.impl.CollectionModel; import org.apache.freemarker.core.util.BugException; import org.apache.freemarker.core.util._StringUtil; http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/051a0822/src/main/java/org/apache/freemarker/core/BuiltInsForStringsMisc.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/apache/freemarker/core/BuiltInsForStringsMisc.java b/src/main/java/org/apache/freemarker/core/BuiltInsForStringsMisc.java index 12e1207..712ff76 100644 --- a/src/main/java/org/apache/freemarker/core/BuiltInsForStringsMisc.java +++ b/src/main/java/org/apache/freemarker/core/BuiltInsForStringsMisc.java @@ -35,8 +35,8 @@ import org.apache.freemarker.core.model.TemplateSequenceModel; import org.apache.freemarker.core.model.TemplateTransformModel; import org.apache.freemarker.core.model.impl.SimpleNumber; import org.apache.freemarker.core.model.impl._StaticObjectWrappers; -import org.apache.freemarker.core.model.impl.beans.BeanModel; -import org.apache.freemarker.core.model.impl.beans.BeansWrapper; +import org.apache.freemarker.core.model.impl.BeanModel; +import org.apache.freemarker.core.model.impl.DefaultObjectWrapper; class BuiltInsForStringsMisc { @@ -289,11 +289,11 @@ class BuiltInsForStringsMisc { @Override public Object exec(List arguments) throws TemplateModelException { ObjectWrapper ow = env.getObjectWrapper(); - BeansWrapper bw = - ow instanceof BeansWrapper - ? (BeansWrapper) ow - : _StaticObjectWrappers.BEANS_WRAPPER; - return bw.newInstance(cl, arguments); + DefaultObjectWrapper dow = + ow instanceof DefaultObjectWrapper + ? (DefaultObjectWrapper) ow + : _StaticObjectWrappers.DEFAULT_OBJECT_WRAPPER; + return dow.newInstance(cl, arguments); } } } http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/051a0822/src/main/java/org/apache/freemarker/core/Configurable.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/apache/freemarker/core/Configurable.java b/src/main/java/org/apache/freemarker/core/Configurable.java index 78cd125..c1252d5 100644 --- a/src/main/java/org/apache/freemarker/core/Configurable.java +++ b/src/main/java/org/apache/freemarker/core/Configurable.java @@ -48,7 +48,6 @@ import org.apache.freemarker.core.model.impl.DefaultObjectWrapper; import org.apache.freemarker.core.model.impl.DefaultObjectWrapperBuilder; import org.apache.freemarker.core.model.impl.SimpleObjectWrapper; import org.apache.freemarker.core.model.impl._StaticObjectWrappers; -import org.apache.freemarker.core.model.impl.beans.BeansWrapper; import org.apache.freemarker.core.outputformat.OutputFormat; import org.apache.freemarker.core.outputformat.impl.HTMLOutputFormat; import org.apache.freemarker.core.outputformat.impl.PlainTextOutputFormat; @@ -1805,18 +1804,17 @@ public class Configurable { * <li><p>{@code "object_wrapper"}: * See {@link #setObjectWrapper(ObjectWrapper)}. * <br>String value: If the value contains dot, then it's interpreted as an <a href="#fm_obe">object builder - * expression</a>, with the addition that {@link BeansWrapper}, {@link DefaultObjectWrapper} and + * expression</a>, with the addition that {@link DefaultObjectWrapper}, {@link DefaultObjectWrapper} and * {@link SimpleObjectWrapper} can be referred without package name. For example, these strings are valid * values: {@code "DefaultObjectWrapper(3.0.0)"}, - * {@code "BeansWrapper(2.3.21, simpleMapWrapper=true)"}. + * {@code "DefaultObjectWrapper(2.3.21, simpleMapWrapper=true)"}. * <br>If the value does not contain dot, then it must be one of these special values (case insensitive): * {@code "default"} means the default of {@link Configuration} (the default depends on the * {@code Configuration#Configuration(Version) incompatible_improvements}, but a bug existed in 2.3.21 where * that was ignored), * {@code "default_2_3_0"} (means {@link _StaticObjectWrappers#DEFAULT_OBJECT_WRAPPER}) - * {@code "simple"} (means the deprecated {@link _StaticObjectWrappers#SIMPLE_OBJECT_WRAPPER}, - * {@code "beans"} (means {@link _StaticObjectWrappers#BEANS_WRAPPER}) - * + * {@code "simple"} (means the deprecated {@link _StaticObjectWrappers#SIMPLE_OBJECT_WRAPPER} + * * <li><p>{@code "number_format"}: See {@link #setNumberFormat(String)}. * * <li><p>{@code "boolean_format"}: See {@link #setBooleanFormat(String)} . @@ -2116,7 +2114,7 @@ public class Configurable { * </li> * <li> * <p>The following classes can be referred to with simple (unqualified) name instead of fully qualified name: - * {@link DefaultObjectWrapper}, {@link BeansWrapper}, {@link SimpleObjectWrapper}, {@link Locale}, + * {@link DefaultObjectWrapper}, {@link DefaultObjectWrapper}, {@link SimpleObjectWrapper}, {@link Locale}, * {@link TemplateConfiguration}, {@link PathGlobMatcher}, {@link FileNameGlobMatcher}, {@link PathRegexMatcher}, * {@link AndMatcher}, {@link OrMatcher}, {@link NotMatcher}, {@link ConditionalTemplateConfigurationFactory}, * {@link MergingTemplateConfigurationFactory}, {@link FirstMatchTemplateConfigurationFactory}, @@ -2216,8 +2214,6 @@ public class Configurable { } } else if ("simple".equalsIgnoreCase(value)) { setObjectWrapper(_StaticObjectWrappers.SIMPLE_OBJECT_WRAPPER); - } else if ("beans".equalsIgnoreCase(value)) { - setObjectWrapper(_StaticObjectWrappers.BEANS_WRAPPER); } else { setObjectWrapper((ObjectWrapper) _ObjectBuilderSettingEvaluator.eval( value, ObjectWrapper.class, false, _SettingEvaluationEnvironment.getCurrent())); http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/051a0822/src/main/java/org/apache/freemarker/core/_CoreLogs.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/apache/freemarker/core/_CoreLogs.java b/src/main/java/org/apache/freemarker/core/_CoreLogs.java index 1ccb915..3523255 100644 --- a/src/main/java/org/apache/freemarker/core/_CoreLogs.java +++ b/src/main/java/org/apache/freemarker/core/_CoreLogs.java @@ -27,11 +27,13 @@ import org.slf4j.LoggerFactory; * access things inside this package that users shouldn't. */ public final class _CoreLogs { - - public static final Logger RUNTIME = LoggerFactory.getLogger("org.apache.freemarker.core.runtime"); - public static final Logger ATTEMPT = LoggerFactory.getLogger("org.apache.freemarker.core.runtime.attempt"); - public static final Logger SECURITY = LoggerFactory.getLogger("org.apache.freemarker.core.security"); - public static final Logger BEANS_WRAPPER = LoggerFactory.getLogger("org.apache.freemarker.core.model.impl.beans"); + + // [FM3] Why "Runtime"? "TemplateProcessing" maybe? + public static final Logger RUNTIME = LoggerFactory.getLogger("org.apache.freemarker.core.Runtime"); + public static final Logger ATTEMPT = LoggerFactory.getLogger("org.apache.freemarker.core.Runtime.Attempt"); + public static final Logger SECURITY = LoggerFactory.getLogger("org.apache.freemarker.core.Security"); + public static final Logger OBJECT_WRAPPER = LoggerFactory.getLogger("org.apache.freemarker.core.model" + + ".ObjectWrapper"); public static final Logger TEMPLATE_RESOLVER = LoggerFactory.getLogger( "org.apache.freemarker.core.templateresolver"); public static final Logger DEBUG_SERVER = LoggerFactory.getLogger("org.apache.freemarker.core.debug.server"); http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/051a0822/src/main/java/org/apache/freemarker/core/_ErrorDescriptionBuilder.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/apache/freemarker/core/_ErrorDescriptionBuilder.java b/src/main/java/org/apache/freemarker/core/_ErrorDescriptionBuilder.java index 812aa5a..93ae5ac 100644 --- a/src/main/java/org/apache/freemarker/core/_ErrorDescriptionBuilder.java +++ b/src/main/java/org/apache/freemarker/core/_ErrorDescriptionBuilder.java @@ -23,7 +23,7 @@ import java.lang.reflect.Constructor; import java.lang.reflect.Member; import java.lang.reflect.Method; -import org.apache.freemarker.core.model.impl.beans._MethodUtil; +import org.apache.freemarker.core.model.impl._MethodUtil; import org.apache.freemarker.core.util._ClassUtil; import org.apache.freemarker.core.util._StringUtil; import org.slf4j.Logger; http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/051a0822/src/main/java/org/apache/freemarker/core/_ObjectBuilderSettingEvaluator.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/apache/freemarker/core/_ObjectBuilderSettingEvaluator.java b/src/main/java/org/apache/freemarker/core/_ObjectBuilderSettingEvaluator.java index 9c18db0..97f0ce5 100644 --- a/src/main/java/org/apache/freemarker/core/_ObjectBuilderSettingEvaluator.java +++ b/src/main/java/org/apache/freemarker/core/_ObjectBuilderSettingEvaluator.java @@ -41,7 +41,7 @@ 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.SimpleObjectWrapper; -import org.apache.freemarker.core.model.impl.beans.BeansWrapper; +import org.apache.freemarker.core.model.impl.DefaultObjectWrapper; import org.apache.freemarker.core.outputformat.impl.HTMLOutputFormat; import org.apache.freemarker.core.outputformat.impl.PlainTextOutputFormat; import org.apache.freemarker.core.outputformat.impl.RTFOutputFormat; @@ -658,7 +658,7 @@ public class _ObjectBuilderSettingEvaluator { SHORTHANDS = new HashMap/*<String,String>*/(); addWithSimpleName(SHORTHANDS, DefaultObjectWrapper.class); - addWithSimpleName(SHORTHANDS, BeansWrapper.class); + addWithSimpleName(SHORTHANDS, DefaultObjectWrapper.class); addWithSimpleName(SHORTHANDS, SimpleObjectWrapper.class); addWithSimpleName(SHORTHANDS, TemplateConfiguration.class); @@ -981,7 +981,7 @@ public class _ObjectBuilderSettingEvaluator { "Failed to call " + cl.getName() + " 0-argument constructor", e); } } else { - BeansWrapper ow = env.getObjectWrapper(); + DefaultObjectWrapper ow = env.getObjectWrapper(); List/*<TemplateModel>*/ tmArgs = new ArrayList(positionalParamValues.size()); for (int i = 0; i < positionalParamValues.size(); i++) { try { http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/051a0822/src/main/java/org/apache/freemarker/core/_SettingEvaluationEnvironment.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/apache/freemarker/core/_SettingEvaluationEnvironment.java b/src/main/java/org/apache/freemarker/core/_SettingEvaluationEnvironment.java index 7e9fb37..f095467 100644 --- a/src/main/java/org/apache/freemarker/core/_SettingEvaluationEnvironment.java +++ b/src/main/java/org/apache/freemarker/core/_SettingEvaluationEnvironment.java @@ -21,7 +21,7 @@ package org.apache.freemarker.core; import java.util.Properties; -import org.apache.freemarker.core.model.impl.beans.BeansWrapper; +import org.apache.freemarker.core.model.impl.DefaultObjectWrapper; /** * Don't use this; used internally by FreeMarker, might changes without notice. @@ -31,7 +31,7 @@ public class _SettingEvaluationEnvironment { private static final ThreadLocal CURRENT = new ThreadLocal(); - private BeansWrapper objectWrapper; + private DefaultObjectWrapper objectWrapper; public static _SettingEvaluationEnvironment getCurrent() { Object r = CURRENT.get(); @@ -51,9 +51,9 @@ public class _SettingEvaluationEnvironment { CURRENT.set(previous); } - public BeansWrapper getObjectWrapper() { + public DefaultObjectWrapper getObjectWrapper() { if (objectWrapper == null) { - objectWrapper = new BeansWrapper(Configuration.VERSION_3_0_0); + objectWrapper = new DefaultObjectWrapper(Configuration.VERSION_3_0_0); } return objectWrapper; } http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/051a0822/src/main/java/org/apache/freemarker/core/model/ObjectWrapper.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/apache/freemarker/core/model/ObjectWrapper.java b/src/main/java/org/apache/freemarker/core/model/ObjectWrapper.java index a321742..19b40cb 100644 --- a/src/main/java/org/apache/freemarker/core/model/ObjectWrapper.java +++ b/src/main/java/org/apache/freemarker/core/model/ObjectWrapper.java @@ -22,15 +22,15 @@ package org.apache.freemarker.core.model; import java.util.Map; import org.apache.freemarker.core.Configuration; -import org.apache.freemarker.core.model.impl.beans.BeansWrapper; +import org.apache.freemarker.core.model.impl.DefaultObjectWrapper; /** * Maps Java objects to the type-system of FreeMarker Template Language (see the {@link TemplateModel} * interfaces). Thus this is what decides what parts of the Java objects will be accessible in the templates and how. * - * <p>For example, with a {@link BeansWrapper} both the items of {@link Map} and the JavaBean properties (the getters) + * <p>For example, with a {@link DefaultObjectWrapper} both the items of {@link Map} and the JavaBean properties (the getters) * of an object are accessible in template uniformly with the {@code myObject.foo} syntax, where "foo" is the map key or - * the property name. This is because both kind of object is wrapped by {@link BeansWrapper} into a + * the property name. This is because both kind of object is wrapped by {@link DefaultObjectWrapper} into a * {@link TemplateHashModel} implementation that will call {@link Map#get(Object)} or the getter method, transparently * to the template language. * @@ -44,7 +44,7 @@ public interface ObjectWrapper { * * @param obj The object to wrap into a {@link TemplateModel}. If it already implements {@link TemplateModel}, * it should just return the object as is. If it's {@code null}, the method should return {@code null} - * (however, {@link BeansWrapper}, has a legacy option for returning a null model object instead, but it's not + * (however, {@link DefaultObjectWrapper}, has a legacy option for returning a null model object instead, but it's not * a good idea). * * @return a {@link TemplateModel} wrapper of the object passed in. To support un-wrapping, you may consider the http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/051a0822/src/main/java/org/apache/freemarker/core/model/TemplateMethodModel.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/apache/freemarker/core/model/TemplateMethodModel.java b/src/main/java/org/apache/freemarker/core/model/TemplateMethodModel.java index d624ede..85adfcb 100644 --- a/src/main/java/org/apache/freemarker/core/model/TemplateMethodModel.java +++ b/src/main/java/org/apache/freemarker/core/model/TemplateMethodModel.java @@ -29,7 +29,7 @@ import org.apache.freemarker.core.Environment; /** * "method" template language data type: Objects that act like functions. The name comes from that their original - * application was calling Java methods via {@link org.apache.freemarker.core.model.impl.beans.BeansWrapper}. + * application was calling Java methods via {@link org.apache.freemarker.core.model.impl.DefaultObjectWrapper}. * * <p>In templates they are used like {@code myMethod("foo", "bar")} or {@code myJavaObject.myJavaMethod("foo", "bar")}. * http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/051a0822/src/main/java/org/apache/freemarker/core/model/TemplateMethodModelEx.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/apache/freemarker/core/model/TemplateMethodModelEx.java b/src/main/java/org/apache/freemarker/core/model/TemplateMethodModelEx.java index fdf7ec0..d06da21 100644 --- a/src/main/java/org/apache/freemarker/core/model/TemplateMethodModelEx.java +++ b/src/main/java/org/apache/freemarker/core/model/TemplateMethodModelEx.java @@ -26,7 +26,7 @@ import org.apache.freemarker.core.util.DeepUnwrap; /** * "extended 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.beans.BeansWrapper}, but you can implement this interface to create + * Java methods via {@link org.apache.freemarker.core.model.impl.DefaultObjectWrapper}, but you can implement this interface to create * top-level functions too. They are "extended" compared to the deprecated {@link TemplateMethodModel}, which could only * accept string parameters. * http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/051a0822/src/main/java/org/apache/freemarker/core/model/impl/APIModel.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/apache/freemarker/core/model/impl/APIModel.java b/src/main/java/org/apache/freemarker/core/model/impl/APIModel.java new file mode 100644 index 0000000..f5f686d --- /dev/null +++ b/src/main/java/org/apache/freemarker/core/model/impl/APIModel.java @@ -0,0 +1,45 @@ +/* + * 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; + +/** + * Exposes the Java API (and properties) of an object. + * + * <p> + * Notes: + * <ul> + * <li>The exposion level is inherited from the {@link DefaultObjectWrapper}</li> + * <li>But methods will always shadow properties and fields with identical name, regardless of {@link DefaultObjectWrapper} + * settings</li> + * </ul> + * + * @since 2.3.22 + */ +final class APIModel extends BeanModel { + + APIModel(Object object, DefaultObjectWrapper wrapper) { + super(object, wrapper, false); + } + + protected boolean isMethodsShadowItems() { + return true; + } + +} http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/051a0822/src/main/java/org/apache/freemarker/core/model/impl/ArgumentTypes.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/apache/freemarker/core/model/impl/ArgumentTypes.java b/src/main/java/org/apache/freemarker/core/model/impl/ArgumentTypes.java new file mode 100644 index 0000000..6e16ecd --- /dev/null +++ b/src/main/java/org/apache/freemarker/core/model/impl/ArgumentTypes.java @@ -0,0 +1,647 @@ +/* + * 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.InvocationTargetException; +import java.util.Collection; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; + +import org.apache.freemarker.core.model.TemplateModel; +import org.apache.freemarker.core.model.TemplateModelException; +import org.apache.freemarker.core.util.BugException; +import org.apache.freemarker.core.util._ClassUtil; + +/** + * The argument types of a method call; usable as cache key. + */ +final class ArgumentTypes { + + /** + * Conversion difficulty: Lowest; Java Reflection will do it automatically. + */ + private static final int CONVERSION_DIFFICULTY_REFLECTION = 0; + + /** + * Conversion difficulty: Medium: Java reflection API won't convert it, FreeMarker has to do it. + */ + private static final int CONVERSION_DIFFICULTY_FREEMARKER = 1; + + /** + * Conversion difficulty: Highest; conversion is not possible. + */ + private static final int CONVERSION_DIFFICULTY_IMPOSSIBLE = 2; + + /** + * The types of the arguments; for varags this contains the exploded list (not the array). + */ + private final Class<?>[] types; + + /** + * @param args The actual arguments. A varargs argument should be present exploded, no as an array. + */ + ArgumentTypes(Object[] args) { + int ln = args.length; + Class<?>[] typesTmp = new Class[ln]; + for (int i = 0; i < ln; ++i) { + Object arg = args[i]; + typesTmp[i] = arg == null + ? Null.class + : arg.getClass(); + } + + // `typesTmp` is used so the array is only modified before it's stored in the final `types` field (see JSR-133) + types = typesTmp; + } + + @Override + public int hashCode() { + int hash = 0; + for (Class<?> type : types) { + hash ^= type.hashCode(); + } + return hash; + } + + @Override + public boolean equals(Object o) { + if (o instanceof ArgumentTypes) { + ArgumentTypes cs = (ArgumentTypes) o; + if (cs.types.length != types.length) { + return false; + } + for (int i = 0; i < types.length; ++i) { + if (cs.types[i] != types[i]) { + return false; + } + } + return true; + } + return false; + } + + /** + * @return Possibly {@link EmptyCallableMemberDescriptor#NO_SUCH_METHOD} or + * {@link EmptyCallableMemberDescriptor#AMBIGUOUS_METHOD}. + */ + MaybeEmptyCallableMemberDescriptor getMostSpecific( + List<ReflectionCallableMemberDescriptor> memberDescs, boolean varArg) { + LinkedList<CallableMemberDescriptor> applicables = getApplicables(memberDescs, varArg); + if (applicables.isEmpty()) { + return EmptyCallableMemberDescriptor.NO_SUCH_METHOD; + } + if (applicables.size() == 1) { + return applicables.getFirst(); + } + + LinkedList<CallableMemberDescriptor> maximals = new LinkedList<>(); + for (CallableMemberDescriptor applicable : applicables) { + boolean lessSpecific = false; + for (Iterator<CallableMemberDescriptor> maximalsIter = maximals.iterator(); + maximalsIter.hasNext(); ) { + CallableMemberDescriptor maximal = maximalsIter.next(); + final int cmpRes = compareParameterListPreferability( + applicable.getParamTypes(), maximal.getParamTypes(), varArg); + if (cmpRes > 0) { + maximalsIter.remove(); + } else if (cmpRes < 0) { + lessSpecific = true; + } + } + if (!lessSpecific) { + maximals.addLast(applicable); + } + } + if (maximals.size() > 1) { + return EmptyCallableMemberDescriptor.AMBIGUOUS_METHOD; + } + return maximals.getFirst(); + } + + /** + * Tells if among the parameter list of two methods, which one fits this argument list better. + * This method assumes that the parameter lists are applicable to this argument lists; if that's not ensured, + * what the result will be is undefined. + * + * <p>The decision is made by comparing the preferability of each parameter types of the same position in a loop. + * At the end, the parameter list with the more preferred parameters will be the preferred one. If both parameter + * lists has the same amount of preferred parameters, the one that has the first (lower index) preferred parameter + * is the preferred one. Otherwise the two parameter list are considered to be equal in terms of preferability. + * + * <p>If there's no numerical conversion involved, the preferability of two parameter types is decided on how + * specific their types are. For example, {@code String} is more specific than {@link Object} (because + * {@code Object.class.isAssignableFrom(String.class)}-s), and so {@code String} is preferred. Primitive + * types are considered to be more specific than the corresponding boxing class (like {@code boolean} is more + * specific than {@code Boolean}, because the former can't store {@code null}). The preferability decision gets + * trickier when there's a possibility of numerical conversion from the actual argument type to the type of some of + * the parameters. If such conversion is only possible for one of the competing parameter types, that parameter + * automatically wins. If it's possible for both, {@link OverloadedNumberUtil#getArgumentConversionPrice} will + * be used to calculate the conversion "price", and the parameter type with lowest price wins. There are also + * a twist with array-to-list and list-to-array conversions; we try to avoid those, so the parameter where such + * conversion isn't needed will always win. + * + * @param paramTypes1 The parameter types of one of the competing methods + * @param paramTypes2 The parameter types of the other competing method + * @param varArg Whether these competing methods are varargs methods. + * @return More than 0 if the first parameter list is preferred, less then 0 if the other is preferred, + * 0 if there's no decision + */ + int compareParameterListPreferability(Class<?>[] paramTypes1, Class<?>[] paramTypes2, boolean varArg) { + final int argTypesLen = types.length; + final int paramTypes1Len = paramTypes1.length; + final int paramTypes2Len = paramTypes2.length; + //assert varArg || paramTypes1Len == paramTypes2Len; + + int paramList1WeakWinCnt = 0; + int paramList2WeakWinCnt = 0; + int paramList1WinCnt = 0; + int paramList2WinCnt = 0; + int paramList1StrongWinCnt = 0; + int paramList2StrongWinCnt = 0; + int paramList1VeryStrongWinCnt = 0; + int paramList2VeryStrongWinCnt = 0; + int firstWinerParamList = 0; + for (int i = 0; i < argTypesLen; i++) { + final Class<?> paramType1 = getParamType(paramTypes1, paramTypes1Len, i, varArg); + final Class<?> paramType2 = getParamType(paramTypes2, paramTypes2Len, i, varArg); + + final int winerParam; // 1 => paramType1; -1 => paramType2; 0 => draw + if (paramType1 == paramType2) { + winerParam = 0; + } else { + final Class<?> argType = types[i]; + final boolean argIsNum = Number.class.isAssignableFrom(argType); + + final int numConvPrice1; + if (argIsNum && _ClassUtil.isNumerical(paramType1)) { + final Class<?> nonPrimParamType1 = paramType1.isPrimitive() + ? _ClassUtil.primitiveClassToBoxingClass(paramType1) : paramType1; + numConvPrice1 = OverloadedNumberUtil.getArgumentConversionPrice(argType, nonPrimParamType1); + } else { + numConvPrice1 = Integer.MAX_VALUE; + } + // numConvPrice1 is Integer.MAX_VALUE if either: + // - argType and paramType1 aren't both numerical + // - FM doesn't know some of the numerical types, or the conversion between them is not allowed + + final int numConvPrice2; + if (argIsNum && _ClassUtil.isNumerical(paramType2)) { + final Class<?> nonPrimParamType2 = paramType2.isPrimitive() + ? _ClassUtil.primitiveClassToBoxingClass(paramType2) : paramType2; + numConvPrice2 = OverloadedNumberUtil.getArgumentConversionPrice(argType, nonPrimParamType2); + } else { + numConvPrice2 = Integer.MAX_VALUE; + } + + if (numConvPrice1 == Integer.MAX_VALUE) { + if (numConvPrice2 == Integer.MAX_VALUE) { // No numerical conversions anywhere + // List to array conversions (unwrapping sometimes makes a List instead of an array) + if (List.class.isAssignableFrom(argType) + && (paramType1.isArray() || paramType2.isArray())) { + if (paramType1.isArray()) { + if (paramType2.isArray()) { // both paramType1 and paramType2 are arrays + int r = compareParameterListPreferability_cmpTypeSpecificty( + paramType1.getComponentType(), paramType2.getComponentType()); + // Because we don't know if the List items are instances of the component + // type or not, we prefer the safer choice, which is the more generic array: + if (r > 0) { + winerParam = 2; + paramList2StrongWinCnt++; + } else if (r < 0) { + winerParam = 1; + paramList1StrongWinCnt++; + } else { + winerParam = 0; + } + } else { // paramType1 is array, paramType2 isn't + // Avoid List to array conversion if the other way makes any sense: + if (Collection.class.isAssignableFrom(paramType2)) { + winerParam = 2; + paramList2StrongWinCnt++; + } else { + winerParam = 1; + paramList1WeakWinCnt++; + } + } + } else { // paramType2 is array, paramType1 isn't + // Avoid List to array conversion if the other way makes any sense: + if (Collection.class.isAssignableFrom(paramType1)) { + winerParam = 1; + paramList1StrongWinCnt++; + } else { + winerParam = 2; + paramList2WeakWinCnt++; + } + } + } else if (argType.isArray() + && (List.class.isAssignableFrom(paramType1) + || List.class.isAssignableFrom(paramType2))) { + // Array to List conversions (unwrapping sometimes makes an array instead of a List) + if (List.class.isAssignableFrom(paramType1)) { + if (List.class.isAssignableFrom(paramType2)) { + // Both paramType1 and paramType2 extends List + winerParam = 0; + } else { + // Only paramType1 extends List + winerParam = 2; + paramList2VeryStrongWinCnt++; + } + } else { + // Only paramType2 extends List + winerParam = 1; + paramList1VeryStrongWinCnt++; + } + } else { // No list to/from array conversion + final int r = compareParameterListPreferability_cmpTypeSpecificty( + paramType1, paramType2); + if (r > 0) { + winerParam = 1; + if (r > 1) { + paramList1WinCnt++; + } else { + paramList1WeakWinCnt++; + } + } else if (r < 0) { + winerParam = -1; + if (r < -1) { + paramList2WinCnt++; + } else { + paramList2WeakWinCnt++; + } + } else { + winerParam = 0; + } + } + } else { // No num. conv. of param1, num. conv. of param2 + winerParam = -1; + paramList2WinCnt++; + } + } else if (numConvPrice2 == Integer.MAX_VALUE) { // Num. conv. of param1, not of param2 + winerParam = 1; + paramList1WinCnt++; + } else { // Num. conv. of both param1 and param2 + if (numConvPrice1 != numConvPrice2) { + if (numConvPrice1 < numConvPrice2) { + winerParam = 1; + if (numConvPrice1 < OverloadedNumberUtil.BIG_MANTISSA_LOSS_PRICE + && numConvPrice2 > OverloadedNumberUtil.BIG_MANTISSA_LOSS_PRICE) { + paramList1StrongWinCnt++; + } else { + paramList1WinCnt++; + } + } else { + winerParam = -1; + if (numConvPrice2 < OverloadedNumberUtil.BIG_MANTISSA_LOSS_PRICE + && numConvPrice1 > OverloadedNumberUtil.BIG_MANTISSA_LOSS_PRICE) { + paramList2StrongWinCnt++; + } else { + paramList2WinCnt++; + } + } + } else { + winerParam = (paramType1.isPrimitive() ? 1 : 0) - (paramType2.isPrimitive() ? 1 : 0); + if (winerParam == 1) paramList1WeakWinCnt++; + else if (winerParam == -1) paramList2WeakWinCnt++; + } + } + } // when paramType1 != paramType2 + + if (firstWinerParamList == 0 && winerParam != 0) { + firstWinerParamList = winerParam; + } + } // for each parameter types + + if (paramList1VeryStrongWinCnt != paramList2VeryStrongWinCnt) { + return paramList1VeryStrongWinCnt - paramList2VeryStrongWinCnt; + } else if (paramList1StrongWinCnt != paramList2StrongWinCnt) { + return paramList1StrongWinCnt - paramList2StrongWinCnt; + } else if (paramList1WinCnt != paramList2WinCnt) { + return paramList1WinCnt - paramList2WinCnt; + } else if (paramList1WeakWinCnt != paramList2WeakWinCnt) { + return paramList1WeakWinCnt - paramList2WeakWinCnt; + } else if (firstWinerParamList != 0) { // paramList1WinCnt == paramList2WinCnt + return firstWinerParamList; + } else { // still undecided + if (varArg) { + if (paramTypes1Len == paramTypes2Len) { + // If we had a 0-length varargs array in both methods, we also compare the types at the + // index of the varargs parameter, like if we had a single varargs argument. However, this + // time we don't have an argument type, so we can only decide based on type specificity: + if (argTypesLen == paramTypes1Len - 1) { + Class<?> paramType1 = getParamType(paramTypes1, paramTypes1Len, argTypesLen, true); + Class<?> paramType2 = getParamType(paramTypes2, paramTypes2Len, argTypesLen, true); + if (_ClassUtil.isNumerical(paramType1) && _ClassUtil.isNumerical(paramType2)) { + int r = OverloadedNumberUtil.compareNumberTypeSpecificity(paramType1, paramType2); + if (r != 0) return r; + // falls through + } + return compareParameterListPreferability_cmpTypeSpecificty(paramType1, paramType2); + } else { + return 0; + } + } else { + // The method with more oms parameters wins: + return paramTypes1Len - paramTypes2Len; + } + } else { + return 0; + } + } + } + + /** + * Trivial comparison of type specificities; unaware of numerical conversions. + * + * @return Less-than-0, 0, or more-than-0 depending on which side is more specific. The absolute value is 1 if + * the difference is only in primitive VS non-primitive, more otherwise. + */ + private int compareParameterListPreferability_cmpTypeSpecificty( + final Class<?> paramType1, final Class<?> paramType2) { + // The more specific (smaller) type wins. + + final Class<?> nonPrimParamType1 = paramType1.isPrimitive() + ? _ClassUtil.primitiveClassToBoxingClass(paramType1) : paramType1; + final Class<?> nonPrimParamType2 = paramType2.isPrimitive() + ? _ClassUtil.primitiveClassToBoxingClass(paramType2) : paramType2; + + if (nonPrimParamType1 == nonPrimParamType2) { + if (nonPrimParamType1 != paramType1) { + if (nonPrimParamType2 != paramType2) { + return 0; // identical prim. types; shouldn't ever be reached + } else { + return 1; // param1 is prim., param2 is non prim. + } + } else if (nonPrimParamType2 != paramType2) { + return -1; // param1 is non-prim., param2 is prim. + } else { + return 0; // identical non-prim. types + } + } else if (nonPrimParamType2.isAssignableFrom(nonPrimParamType1)) { + return 2; + } else if (nonPrimParamType1.isAssignableFrom(nonPrimParamType2)) { + return -2; + } if (nonPrimParamType1 == Character.class && nonPrimParamType2.isAssignableFrom(String.class)) { + return 2; // A character is a 1 long string in FTL, so we pretend that it's a String subtype. + } if (nonPrimParamType2 == Character.class && nonPrimParamType1.isAssignableFrom(String.class)) { + return -2; + } else { + return 0; // unrelated types + } + } + + private static Class<?> getParamType(Class<?>[] paramTypes, int paramTypesLen, int i, boolean varArg) { + return varArg && i >= paramTypesLen - 1 + ? paramTypes[paramTypesLen - 1].getComponentType() + : paramTypes[i]; + } + + /** + * Returns all methods that are applicable to actual + * parameter types represented by this ArgumentTypes object. + */ + LinkedList<CallableMemberDescriptor> getApplicables( + List<ReflectionCallableMemberDescriptor> memberDescs, boolean varArg) { + LinkedList<CallableMemberDescriptor> applicables = new LinkedList<>(); + for (ReflectionCallableMemberDescriptor memberDesc : memberDescs) { + int difficulty = isApplicable(memberDesc, varArg); + if (difficulty != CONVERSION_DIFFICULTY_IMPOSSIBLE) { + if (difficulty == CONVERSION_DIFFICULTY_REFLECTION) { + applicables.add(memberDesc); + } else if (difficulty == CONVERSION_DIFFICULTY_FREEMARKER) { + applicables.add(new SpecialConversionCallableMemberDescriptor(memberDesc)); + } else { + throw new BugException(); + } + } + } + return applicables; + } + + /** + * Returns if the supplied method is applicable to actual + * parameter types represented by this ArgumentTypes object, also tells + * how difficult that conversion is. + * + * @return One of the <tt>CONVERSION_DIFFICULTY_...</tt> constants. + */ + private int isApplicable(ReflectionCallableMemberDescriptor memberDesc, boolean varArg) { + final Class<?>[] paramTypes = memberDesc.getParamTypes(); + final int cl = types.length; + final int fl = paramTypes.length - (varArg ? 1 : 0); + if (varArg) { + if (cl < fl) { + return CONVERSION_DIFFICULTY_IMPOSSIBLE; + } + } else { + if (cl != fl) { + return CONVERSION_DIFFICULTY_IMPOSSIBLE; + } + } + + int maxDifficulty = 0; + for (int i = 0; i < fl; ++i) { + int difficulty = isMethodInvocationConvertible(paramTypes[i], types[i]); + if (difficulty == CONVERSION_DIFFICULTY_IMPOSSIBLE) { + return CONVERSION_DIFFICULTY_IMPOSSIBLE; + } + if (maxDifficulty < difficulty) { + maxDifficulty = difficulty; + } + } + if (varArg) { + Class<?> varArgParamType = paramTypes[fl].getComponentType(); + for (int i = fl; i < cl; ++i) { + int difficulty = isMethodInvocationConvertible(varArgParamType, types[i]); + if (difficulty == CONVERSION_DIFFICULTY_IMPOSSIBLE) { + return CONVERSION_DIFFICULTY_IMPOSSIBLE; + } + if (maxDifficulty < difficulty) { + maxDifficulty = difficulty; + } + } + } + return maxDifficulty; + } + + /** + * Determines whether a type is convertible to another type via + * method invocation conversion, and if so, what kind of conversion is needed. + * It treates the object type counterpart of primitive types as if they were the primitive types + * (that is, a Boolean actual parameter type matches boolean primitive formal type). This behavior + * is because this method is used to determine applicable methods for + * an actual parameter list, and primitive types are represented by + * their object duals in reflective method calls. + * @param formal the parameter type to which the actual + * parameter type should be convertible; possibly a primitive type + * @param actual the argument type; not a primitive type, maybe {@link Null}. + * + * @return One of the <tt>CONVERSION_DIFFICULTY_...</tt> constants. + */ + private int isMethodInvocationConvertible(final Class<?> formal, final Class<?> actual) { + // Check for identity or widening reference conversion + if (formal.isAssignableFrom(actual) && actual != CharacterOrString.class) { + return CONVERSION_DIFFICULTY_REFLECTION; + } else { + final Class<?> formalNP; + if (formal.isPrimitive()) { + if (actual == Null.class) { + return CONVERSION_DIFFICULTY_IMPOSSIBLE; + } + + formalNP = _ClassUtil.primitiveClassToBoxingClass(formal); + if (actual == formalNP) { + // Character and char, etc. + return CONVERSION_DIFFICULTY_REFLECTION; + } + } else { // formal is non-primitive + if (actual == Null.class) { + return CONVERSION_DIFFICULTY_REFLECTION; + } + + formalNP = formal; + } + if (Number.class.isAssignableFrom(actual) && Number.class.isAssignableFrom(formalNP)) { + return OverloadedNumberUtil.getArgumentConversionPrice(actual, formalNP) == Integer.MAX_VALUE + ? CONVERSION_DIFFICULTY_IMPOSSIBLE : CONVERSION_DIFFICULTY_REFLECTION; + } else if (formal.isArray()) { + // DefaultObjectWrapper method/constructor calls convert from List to array automatically + return List.class.isAssignableFrom(actual) + ? CONVERSION_DIFFICULTY_FREEMARKER : CONVERSION_DIFFICULTY_IMPOSSIBLE; + } else if (actual.isArray() && formal.isAssignableFrom(List.class)) { + // DefaultObjectWrapper method/constructor calls convert from array to List automatically + return CONVERSION_DIFFICULTY_FREEMARKER; + } else if (actual == CharacterOrString.class + && (formal.isAssignableFrom(String.class) + || formal.isAssignableFrom(Character.class) || formal == char.class)) { + return CONVERSION_DIFFICULTY_FREEMARKER; + } else { + return CONVERSION_DIFFICULTY_IMPOSSIBLE; + } + } + } + + /** + * Symbolizes the class of null (it's missing from Java). + */ + private static class Null { + + // Can't be instantiated + private Null() { } + + } + + /** + * Used instead of {@link ReflectionCallableMemberDescriptor} when the method is only applicable + * ({@link #isApplicable}) with conversion that Java reflection won't do. It delegates to a + * {@link ReflectionCallableMemberDescriptor}, but it adds the necessary conversions to the invocation methods. + */ + private static final class SpecialConversionCallableMemberDescriptor extends CallableMemberDescriptor { + + private final ReflectionCallableMemberDescriptor callableMemberDesc; + + SpecialConversionCallableMemberDescriptor(ReflectionCallableMemberDescriptor callableMemberDesc) { + this.callableMemberDesc = callableMemberDesc; + } + + @Override + TemplateModel invokeMethod(DefaultObjectWrapper ow, Object obj, Object[] args) throws TemplateModelException, + InvocationTargetException, IllegalAccessException { + convertArgsToReflectionCompatible(ow, args); + return callableMemberDesc.invokeMethod(ow, obj, args); + } + + @Override + Object invokeConstructor(DefaultObjectWrapper ow, Object[] args) throws IllegalArgumentException, + InstantiationException, IllegalAccessException, InvocationTargetException, TemplateModelException { + convertArgsToReflectionCompatible(ow, args); + return callableMemberDesc.invokeConstructor(ow, args); + } + + @Override + String getDeclaration() { + return callableMemberDesc.getDeclaration(); + } + + @Override + boolean isConstructor() { + return callableMemberDesc.isConstructor(); + } + + @Override + boolean isStatic() { + return callableMemberDesc.isStatic(); + } + + @Override + boolean isVarargs() { + return callableMemberDesc.isVarargs(); + } + + @Override + Class<?>[] getParamTypes() { + return callableMemberDesc.getParamTypes(); + } + + @Override + String getName() { + return callableMemberDesc.getName(); + } + + private void convertArgsToReflectionCompatible(DefaultObjectWrapper ow, Object[] args) throws TemplateModelException { + Class<?>[] paramTypes = callableMemberDesc.getParamTypes(); + int ln = paramTypes.length; + for (int i = 0; i < ln; i++) { + Class<?> paramType = paramTypes[i]; + final Object arg = args[i]; + if (arg == null) continue; + + // Handle conversion between List and array types, in both directions. Java reflection won't do such + // conversion, so we have to. + // Most reflection-incompatible conversions were already addressed by the unwrapping. The reason + // this one isn't is that for overloaded methods the hint of a given parameter position is often vague, + // so we may end up with a List even if some parameter types at that position are arrays (remember, we + // have to chose one unwrapping target type, despite that we have many possible overloaded methods), or + // the other way around (that happens when AdapterTemplateMoldel returns an array). + // Later, the overloaded method selection will assume that a List argument is applicable to an array + // parameter, and that an array argument is applicable to a List parameter, so we end up with this + // situation. + if (paramType.isArray() && arg instanceof List) { + args[i] = ow.listToArray((List<?>) arg, paramType, null); + } + if (arg.getClass().isArray() && paramType.isAssignableFrom(List.class)) { + args[i] = ow.arrayToList(arg); + } + + // Handle the conversion from CharacterOrString to Character or String: + if (arg instanceof CharacterOrString) { + if (paramType == Character.class || paramType == char.class + || (!paramType.isAssignableFrom(String.class) + && paramType.isAssignableFrom(Character.class))) { + args[i] = Character.valueOf(((CharacterOrString) arg).getAsChar()); + } else { + args[i] = ((CharacterOrString) arg).getAsString(); + } + } + } + } + + } + +} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/051a0822/src/main/java/org/apache/freemarker/core/model/impl/ArrayModel.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/apache/freemarker/core/model/impl/ArrayModel.java b/src/main/java/org/apache/freemarker/core/model/impl/ArrayModel.java new file mode 100644 index 0000000..12ef9f0 --- /dev/null +++ b/src/main/java/org/apache/freemarker/core/model/impl/ArrayModel.java @@ -0,0 +1,126 @@ +/* + * 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.Array; + +import org.apache.freemarker.core.model.ObjectWrapper; +import org.apache.freemarker.core.model.TemplateCollectionModel; +import org.apache.freemarker.core.model.TemplateModel; +import org.apache.freemarker.core.model.TemplateModelException; +import org.apache.freemarker.core.model.TemplateModelIterator; +import org.apache.freemarker.core.model.TemplateSequenceModel; + +/** + * <p>A class that will wrap an arbitrary array into {@link TemplateCollectionModel} + * and {@link TemplateSequenceModel} interfaces. It supports element retrieval through the <tt>array[index]</tt> + * syntax and can be iterated as a list. + */ +public class ArrayModel +extends + BeanModel +implements + TemplateCollectionModel, + TemplateSequenceModel { + static final ModelFactory FACTORY = + new ModelFactory() + { + @Override + public TemplateModel create(Object object, ObjectWrapper wrapper) { + return new ArrayModel(object, (DefaultObjectWrapper) wrapper); + } + }; + + // Cached length of the array + private final int length; + + /** + * Creates a new model that wraps the specified array object. + * @param array the array object to wrap into a model. + * @param wrapper the {@link DefaultObjectWrapper} associated with this model. + * Every model has to have an associated {@link DefaultObjectWrapper} instance. The + * model gains many attributes from its wrapper, including the caching + * behavior, method exposure level, method-over-item shadowing policy etc. + * @throws IllegalArgumentException if the passed object is not a Java array. + */ + public ArrayModel(Object array, DefaultObjectWrapper wrapper) { + super(array, wrapper); + Class clazz = array.getClass(); + if (!clazz.isArray()) + throw new IllegalArgumentException("Object is not an array, it's " + array.getClass().getName()); + length = Array.getLength(array); + } + + + @Override + public TemplateModelIterator iterator() { + return new Iterator(); + } + + @Override + public TemplateModel get(int index) + throws TemplateModelException { + try { + return wrap(Array.get(object, index)); + } catch (IndexOutOfBoundsException e) { + return null; +// throw new TemplateModelException("Index out of bounds: " + index); + } + } + + private class Iterator + implements + TemplateSequenceModel, + TemplateModelIterator { + private int position = 0; + + @Override + public boolean hasNext() { + return position < length; + } + + @Override + public TemplateModel get(int index) + throws TemplateModelException { + return ArrayModel.this.get(index); + } + + @Override + public TemplateModel next() + throws TemplateModelException { + return position < length ? get(position++) : null; + } + + @Override + public int size() { + return ArrayModel.this.size(); + } + } + + @Override + public int size() { + return length; + } + + @Override + public boolean isEmpty() { + return length == 0; + } +}
