freemarker-notifications mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From ddek...@apache.org
Subject [20/20] incubator-freemarker git commit: 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.
Date Tue, 28 Feb 2017 22:57:54 GMT
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 <ddekany@apache.org>
Authored: Mon Feb 27 10:36:49 2017 +0100
Committer: ddekany <ddekany@apache.org>
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="&#xA;" />
   <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;
+    }
+}


Mime
View raw message