freemarker-notifications mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From ddek...@apache.org
Subject [14/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:48 GMT
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/051a0822/src/main/java/org/apache/freemarker/core/model/impl/_MethodUtil.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/freemarker/core/model/impl/_MethodUtil.java b/src/main/java/org/apache/freemarker/core/model/impl/_MethodUtil.java
new file mode 100644
index 0000000..633687d
--- /dev/null
+++ b/src/main/java/org/apache/freemarker/core/model/impl/_MethodUtil.java
@@ -0,0 +1,293 @@
+/*
+ * 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.Constructor;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Member;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.util.HashSet;
+import java.util.Set;
+
+import org.apache.freemarker.core._DelayedConversionToString;
+import org.apache.freemarker.core._DelayedJQuote;
+import org.apache.freemarker.core._TemplateModelException;
+import org.apache.freemarker.core.model.TemplateModelException;
+import org.apache.freemarker.core.util.BugException;
+import org.apache.freemarker.core.util._ClassUtil;
+
+/**
+ * For internal use only; don't depend on this, there's no backward compatibility guarantee at all!
+ */
+public final class _MethodUtil {
+    
+    private _MethodUtil() {
+        // Not meant to be instantiated
+    }
+
+    /**
+     * Determines whether the type given as the 1st argument is convertible to the type given as the 2nd argument
+     * for method call argument conversion. This follows the rules of the Java reflection-based method call, except
+     * that since we don't have the value here, a boxed class is never seen as convertible to a primitive type. 
+     * 
+     * @return 0 means {@code false}, non-0 means {@code true}.
+     *         That is, 0 is returned less specificity or incomparable specificity, also when if
+     *         then method was aborted because of {@code ifHigherThan}.
+     *         The absolute value of the returned non-0 number symbolizes how more specific it is:
+     *         <ul>
+     *           <li>1: The two classes are identical</li>
+     *           <li>2: The 1st type is primitive, the 2nd type is the corresponding boxing class</li>
+     *           <li>3: Both classes are numerical, and one is convertible into the other with widening conversion.
+     *                  E.g., {@code int} is convertible to {@code long} and {#code double}, hence {@code int} is more
+     *                  specific.
+     *                  This ignores primitive VS boxed mismatches, except that a boxed class is never seen as
+     *                  convertible to a primitive class.</li>
+     *           <li>4: One class is {@code instanceof} of the other, but they aren't identical.
+     *               But unlike in Java, primitive numerical types are {@code instanceof} {@link Number} here.</li>
+     *         </ul> 
+     */
+    // TODO Seems that we don't use the full functionality of this anymore, so we could simplify this. See usages.
+    public static int isMoreOrSameSpecificParameterType(final Class specific, final Class generic, boolean bugfixed,
+            int ifHigherThan) {
+        if (ifHigherThan >= 4) return 0;
+        if (generic.isAssignableFrom(specific)) {
+            // Identity or widening reference conversion:
+            return generic == specific ? 1 : 4;
+        } else {
+            final boolean specificIsPrim = specific.isPrimitive(); 
+            final boolean genericIsPrim = generic.isPrimitive();
+            if (specificIsPrim) {
+                if (genericIsPrim) {
+                    if (ifHigherThan >= 3) return 0;
+                    return isWideningPrimitiveNumberConversion(specific, generic) ? 3 : 0;
+                } else {  // => specificIsPrim && !genericIsPrim
+                    if (bugfixed) {
+                        final Class specificAsBoxed = _ClassUtil.primitiveClassToBoxingClass(specific);
+                        if (specificAsBoxed == generic) {
+                            // A primitive class is more specific than its boxing class, because it can't store null
+                            return 2;
+                        } else if (generic.isAssignableFrom(specificAsBoxed)) {
+                            // Note: This only occurs if `specific` is a primitive numerical, and `generic == Number`
+                            return 4;
+                        } else if (ifHigherThan >= 3) {
+                            return 0;
+                        } else if (Number.class.isAssignableFrom(specificAsBoxed)
+                                && Number.class.isAssignableFrom(generic)) {
+                            return isWideningBoxedNumberConversion(specificAsBoxed, generic) ? 3 : 0;
+                        } else {
+                            return 0;
+                        }
+                    } else {
+                        return 0;
+                    }
+                }
+            } else {  // => !specificIsPrim
+                if (ifHigherThan >= 3) return 0;
+                if (bugfixed && !genericIsPrim
+                        && Number.class.isAssignableFrom(specific) && Number.class.isAssignableFrom(generic)) {
+                    return isWideningBoxedNumberConversion(specific, generic) ? 3 : 0;
+                } else {
+                    return 0;
+                }
+            }
+        }  // of: !generic.isAssignableFrom(specific) 
+    }
+
+    private static boolean isWideningPrimitiveNumberConversion(final Class source, final Class target) {
+        if (target == Short.TYPE && (source == Byte.TYPE)) {
+            return true;
+        } else if (target == Integer.TYPE && 
+           (source == Short.TYPE || source == Byte.TYPE)) {
+            return true;
+        } else if (target == Long.TYPE && 
+           (source == Integer.TYPE || source == Short.TYPE || 
+            source == Byte.TYPE)) {
+            return true;
+        } else if (target == Float.TYPE && 
+           (source == Long.TYPE || source == Integer.TYPE || 
+            source == Short.TYPE || source == Byte.TYPE)) {
+            return true;
+        } else if (target == Double.TYPE && 
+           (source == Float.TYPE || source == Long.TYPE || 
+            source == Integer.TYPE || source == Short.TYPE || 
+            source == Byte.TYPE)) {
+            return true; 
+        } else {
+            return false;
+        }
+    }
+
+    private static boolean isWideningBoxedNumberConversion(final Class source, final Class target) {
+        if (target == Short.class && source == Byte.class) {
+            return true;
+        } else if (target == Integer.class && 
+           (source == Short.class || source == Byte.class)) {
+            return true;
+        } else if (target == Long.class && 
+           (source == Integer.class || source == Short.class || 
+            source == Byte.class)) {
+            return true;
+        } else if (target == Float.class && 
+           (source == Long.class || source == Integer.class || 
+            source == Short.class || source == Byte.class)) {
+            return true;
+        } else if (target == Double.class && 
+           (source == Float.class || source == Long.class || 
+            source == Integer.class || source == Short.class || 
+            source == Byte.class)) {
+            return true; 
+        } else {
+            return false;
+        }
+    }
+
+    /**
+     * Attention, this doesn't handle primitive classes correctly, nor numerical conversions.
+     */
+    public static Set getAssignables(Class c1, Class c2) {
+        Set s = new HashSet();
+        collectAssignables(c1, c2, s);
+        return s;
+    }
+    
+    private static void collectAssignables(Class c1, Class c2, Set s) {
+        if (c1.isAssignableFrom(c2)) {
+            s.add(c1);
+        }
+        Class sc = c1.getSuperclass();
+        if (sc != null) {
+            collectAssignables(sc, c2, s);
+        }
+        Class[] itf = c1.getInterfaces();
+        for (Class anItf : itf) {
+            collectAssignables(anItf, c2, s);
+        }
+    }
+
+    public static Class[] getParameterTypes(Member member) {
+        if (member instanceof Method) {
+            return ((Method) member).getParameterTypes();
+        }
+        if (member instanceof Constructor) {
+            return ((Constructor) member).getParameterTypes();
+        }
+        throw new IllegalArgumentException("\"member\" must be Method or Constructor");
+    }
+
+    public static boolean isVarargs(Member member) {
+        if (member instanceof Method) { 
+            return ((Method) member).isVarArgs();
+        }
+        if (member instanceof Constructor) {
+            return ((Constructor) member).isVarArgs();
+        }
+        throw new BugException();
+    }
+
+    /**
+     * Returns a more streamlined method or constructor description than {@code Member.toString()} does.
+     */
+    public static String toString(Member member) {
+        if (!(member instanceof Method || member instanceof Constructor)) {
+            throw new IllegalArgumentException("\"member\" must be a Method or Constructor");
+        }
+        
+        StringBuilder sb = new StringBuilder();
+        
+        if ((member.getModifiers() & Modifier.STATIC) != 0) {
+            sb.append("static ");
+        }
+        
+        String className = _ClassUtil.getShortClassName(member.getDeclaringClass());
+        if (className != null) {
+            sb.append(className);
+            sb.append('.');
+        }
+        sb.append(member.getName());
+
+        sb.append('(');
+        Class[] paramTypes = _MethodUtil.getParameterTypes(member);
+        for (int i = 0; i < paramTypes.length; i++) {
+            if (i != 0) sb.append(", ");
+            String paramTypeDecl = _ClassUtil.getShortClassName(paramTypes[i]);
+            if (i == paramTypes.length - 1 && paramTypeDecl.endsWith("[]") && _MethodUtil.isVarargs(member)) {
+                sb.append(paramTypeDecl.substring(0, paramTypeDecl.length() - 2));
+                sb.append("...");
+            } else {
+                sb.append(paramTypeDecl);
+            }
+        }
+        sb.append(')');
+        
+        return sb.toString();
+    }
+
+    public static Object[] invocationErrorMessageStart(Member member) {
+        return invocationErrorMessageStart(member, member instanceof Constructor);
+    }
+    
+    private static Object[] invocationErrorMessageStart(Object member, boolean isConstructor) {
+        return new Object[] { "Java ", isConstructor ? "constructor " : "method ", new _DelayedJQuote(member) };
+    }
+
+    public static TemplateModelException newInvocationTemplateModelException(Object object, Member member, Throwable e) {
+        return newInvocationTemplateModelException(
+                object,
+                member,
+                (member.getModifiers() & Modifier.STATIC) != 0,
+                member instanceof Constructor,
+                e);
+    }
+
+    public static TemplateModelException newInvocationTemplateModelException(Object object, CallableMemberDescriptor callableMemberDescriptor, Throwable e) {
+        return newInvocationTemplateModelException(
+                object,
+                new _DelayedConversionToString(callableMemberDescriptor) {
+                    @Override
+                    protected String doConversion(Object callableMemberDescriptor) {
+                        return ((CallableMemberDescriptor) callableMemberDescriptor).getDeclaration();
+                    }
+                },
+                callableMemberDescriptor.isStatic(),
+                callableMemberDescriptor.isConstructor(),
+                e);
+    }
+    
+    private static TemplateModelException newInvocationTemplateModelException(
+            Object parentObject, Object member, boolean isStatic, boolean isConstructor, Throwable e) {
+        while (e instanceof InvocationTargetException) {
+            Throwable cause = ((InvocationTargetException) e).getTargetException();
+            if (cause != null) {
+                e = cause;
+            } else {
+                break;
+            }
+        }
+
+        return new _TemplateModelException(e,
+                invocationErrorMessageStart(member, isConstructor),
+                " threw an exception",
+                isStatic || isConstructor ? "" : new Object[] {
+                    " when invoked on ", parentObject.getClass(), " object ", new _DelayedJQuote(parentObject) 
+                },
+                "; see cause exception in the Java stack trace.");
+    }
+
+}
\ 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/_ModelAPI.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/freemarker/core/model/impl/_ModelAPI.java b/src/main/java/org/apache/freemarker/core/model/impl/_ModelAPI.java
new file mode 100644
index 0000000..660173a
--- /dev/null
+++ b/src/main/java/org/apache/freemarker/core/model/impl/_ModelAPI.java
@@ -0,0 +1,220 @@
+/*
+ * 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.ref.Reference;
+import java.lang.ref.ReferenceQueue;
+import java.lang.ref.WeakReference;
+import java.lang.reflect.Array;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.InvocationTargetException;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.freemarker.core.model.TemplateModelException;
+import org.apache.freemarker.core.util.BugException;
+import org.apache.freemarker.core.util._CollectionUtil;
+
+/**
+ * For internal use only; don't depend on this, there's no backward compatibility guarantee at all!
+ * This class is to work around the lack of module system in Java, i.e., so that other FreeMarker packages can
+ * access things inside this package that users shouldn't. 
+ */ 
+public class _ModelAPI {
+
+    private _ModelAPI() { }
+    
+    public static Object newInstance(Class<?> pClass, Object[] args, DefaultObjectWrapper ow)
+            throws NoSuchMethodException, IllegalArgumentException, InstantiationException,
+            IllegalAccessException, InvocationTargetException, TemplateModelException {
+        return newInstance(getConstructorDescriptor(pClass, args), args, ow);
+    }
+    
+    /**
+     * Gets the constructor that matches the types of the arguments the best. So this is more
+     * than what the Java reflection API provides in that it can handle overloaded constructors. This re-uses the
+     * overloaded method selection logic of {@link DefaultObjectWrapper}.
+     */
+    private static CallableMemberDescriptor getConstructorDescriptor(Class<?> pClass, Object[] args)
+            throws NoSuchMethodException {
+        if (args == null) args = _CollectionUtil.EMPTY_OBJECT_ARRAY;
+        
+        final ArgumentTypes argTypes = new ArgumentTypes(args);
+        final List<ReflectionCallableMemberDescriptor> fixedArgMemberDescs
+                = new ArrayList<>();
+        final List<ReflectionCallableMemberDescriptor> varArgsMemberDescs
+                = new ArrayList<>();
+        for (Constructor<?> constr : pClass.getConstructors()) {
+            ReflectionCallableMemberDescriptor memberDesc = new ReflectionCallableMemberDescriptor(constr, constr.getParameterTypes());
+            if (!_MethodUtil.isVarargs(constr)) {
+                fixedArgMemberDescs.add(memberDesc);
+            } else {
+                varArgsMemberDescs.add(memberDesc);
+            }
+        }
+        
+        MaybeEmptyCallableMemberDescriptor contrDesc = argTypes.getMostSpecific(fixedArgMemberDescs, false);
+        if (contrDesc == EmptyCallableMemberDescriptor.NO_SUCH_METHOD) {
+            contrDesc = argTypes.getMostSpecific(varArgsMemberDescs, true);
+        }
+        
+        if (contrDesc instanceof EmptyCallableMemberDescriptor) {
+            if (contrDesc == EmptyCallableMemberDescriptor.NO_SUCH_METHOD) {
+                throw new NoSuchMethodException(
+                        "There's no public " + pClass.getName()
+                        + " constructor with compatible parameter list.");
+            } else if (contrDesc == EmptyCallableMemberDescriptor.AMBIGUOUS_METHOD) {
+                throw new NoSuchMethodException(
+                        "There are multiple public " + pClass.getName()
+                        + " constructors that match the compatible parameter list with the same preferability.");
+            } else {
+                throw new NoSuchMethodException();
+            }
+        } else {
+            return (CallableMemberDescriptor) contrDesc;
+        }
+    }
+    
+    private static Object newInstance(CallableMemberDescriptor constrDesc, Object[] args, DefaultObjectWrapper ow)
+            throws InstantiationException, IllegalAccessException, InvocationTargetException, IllegalArgumentException,
+            TemplateModelException {
+        if (args == null) args = _CollectionUtil.EMPTY_OBJECT_ARRAY;
+        
+        final Object[] packedArgs;
+        if (constrDesc.isVarargs()) {
+            // We have to put all the varargs arguments into a single array argument.
+
+            final Class<?>[] paramTypes = constrDesc.getParamTypes();
+            final int fixedArgCnt = paramTypes.length - 1;
+            
+            packedArgs = new Object[fixedArgCnt + 1]; 
+            for (int i = 0; i < fixedArgCnt; i++) {
+                packedArgs[i] = args[i];
+            }
+            
+            final Class<?> compType = paramTypes[fixedArgCnt].getComponentType();
+            final int varArgCnt = args.length - fixedArgCnt;
+            final Object varArgsArray = Array.newInstance(compType, varArgCnt);
+            for (int i = 0; i < varArgCnt; i++) {
+                Array.set(varArgsArray, i, args[fixedArgCnt + i]);
+            }
+            packedArgs[fixedArgCnt] = varArgsArray;
+        } else {
+            packedArgs = args;
+        }
+        
+        return constrDesc.invokeConstructor(ow, packedArgs);
+    }
+    
+    /**
+     * Contains the common parts of the singleton management for {@link DefaultObjectWrapper} and {@link DefaultObjectWrapper}.  
+     *  
+     * @param dowSubclassFactory Creates a <em>new</em> read-only object wrapper of the desired
+     *     {@link DefaultObjectWrapper} subclass. 
+     */
+    public static <OW extends DefaultObjectWrapper, OWC extends DefaultObjectWrapperConfiguration> OW
+    getDefaultObjectWrapperSubclassSingleton(
+            OWC settings,
+            Map<ClassLoader, Map<OWC, WeakReference<OW>>> instanceCache,
+            ReferenceQueue<OW> instanceCacheRefQue,
+            _DefaultObjectWrapperSubclassFactory<OW, OWC> dowSubclassFactory) {
+        // DefaultObjectWrapper can't be cached across different Thread Context Class Loaders (TCCL), because the result of
+        // a class name (String) to Class mappings depends on it, and the staticModels and enumModels need that.
+        // (The ClassIntrospector doesn't have to consider the TCCL, as it only works with Class-es, not class
+        // names.)
+        ClassLoader tccl = Thread.currentThread().getContextClassLoader();
+        
+        Reference<OW> instanceRef;
+        Map<OWC, WeakReference<OW>> tcclScopedCache;
+        synchronized (instanceCache) {
+            tcclScopedCache = instanceCache.get(tccl);
+            if (tcclScopedCache == null) {
+                tcclScopedCache = new HashMap<>();
+                instanceCache.put(tccl, tcclScopedCache);
+                instanceRef = null;
+            } else {
+                instanceRef = tcclScopedCache.get(settings);
+            }
+        }
+
+        OW instance = instanceRef != null ? instanceRef.get() : null;
+        if (instance != null) {  // cache hit
+            return instance;
+        }
+        // cache miss
+        
+        settings = clone(settings);  // prevent any aliasing issues 
+        instance = dowSubclassFactory.create(settings);
+        if (!instance.isWriteProtected()) {
+            throw new BugException();
+        }
+        
+        synchronized (instanceCache) {
+            instanceRef = tcclScopedCache.get(settings);
+            OW concurrentInstance = instanceRef != null ? instanceRef.get() : null;
+            if (concurrentInstance == null) {
+                tcclScopedCache.put(settings, new WeakReference<>(instance, instanceCacheRefQue));
+            } else {
+                instance = concurrentInstance;
+            }
+        }
+        
+        removeClearedReferencesFromCache(instanceCache, instanceCacheRefQue);
+        
+        return instance;
+    }
+
+    @SuppressWarnings("unchecked")
+    private static <BWC extends DefaultObjectWrapperConfiguration> BWC clone(BWC settings) {
+        return (BWC) settings.clone(true);
+    }
+    
+    private static <BW extends DefaultObjectWrapper, BWC extends DefaultObjectWrapperConfiguration>
+            void removeClearedReferencesFromCache(
+                    Map<ClassLoader, Map<BWC, WeakReference<BW>>> instanceCache,
+                    ReferenceQueue<BW> instanceCacheRefQue) {
+        Reference<? extends BW> clearedRef;
+        while ((clearedRef = instanceCacheRefQue.poll()) != null) {
+            synchronized (instanceCache) {
+                findClearedRef: for (Map<BWC, WeakReference<BW>> tcclScopedCache : instanceCache.values()) {
+                    for (Iterator<WeakReference<BW>> it2 = tcclScopedCache.values().iterator(); it2.hasNext(); ) {
+                        if (it2.next() == clearedRef) {
+                            it2.remove();
+                            break findClearedRef;
+                        }
+                    }
+                }
+            } // sync
+        } // while poll
+    }
+    
+    /**
+     * For internal use only; don't depend on this, there's no backward compatibility guarantee at all!
+     */
+    public interface _DefaultObjectWrapperSubclassFactory<BW extends DefaultObjectWrapper, BWC extends DefaultObjectWrapperConfiguration> {
+        
+        /** Creates a new read-only {@link DefaultObjectWrapper}; used for {@link DefaultObjectWrapperBuilder} and such. */
+        BW create(BWC sa);
+    }
+    
+}

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/051a0822/src/main/java/org/apache/freemarker/core/model/impl/_ModelImplApi.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/freemarker/core/model/impl/_ModelImplApi.java b/src/main/java/org/apache/freemarker/core/model/impl/_ModelImplApi.java
deleted file mode 100644
index 20dd1b7..0000000
--- a/src/main/java/org/apache/freemarker/core/model/impl/_ModelImplApi.java
+++ /dev/null
@@ -1,37 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- * 
- *   http://www.apache.org/licenses/LICENSE-2.0
- * 
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.apache.freemarker.core.model.impl;
-
-/**
- * For internal use only; don't depend on this, there's no backward compatibility guarantee at all!
- * This class is to work around the lack of module system in Java, i.e., so that other FreeMarker packages can
- * access things inside this package that users shouldn't. 
- */ 
-public class _ModelImplApi {
-
-    private _ModelImplApi() {
-        // Not to be instantiated
-    }
-    
-    /** For unit testing only */
-    public static void DefaultObjectWrapperFactory_clearInstanceCache() {
-        DefaultObjectWrapperBuilder.clearInstanceCache();
-    }
-
-}

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/051a0822/src/main/java/org/apache/freemarker/core/model/impl/_StaticObjectWrappers.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/freemarker/core/model/impl/_StaticObjectWrappers.java b/src/main/java/org/apache/freemarker/core/model/impl/_StaticObjectWrappers.java
index 5f52b10..7c2a035 100644
--- a/src/main/java/org/apache/freemarker/core/model/impl/_StaticObjectWrappers.java
+++ b/src/main/java/org/apache/freemarker/core/model/impl/_StaticObjectWrappers.java
@@ -19,8 +19,6 @@
 package org.apache.freemarker.core.model.impl;
 
 import org.apache.freemarker.core.Configuration;
-import org.apache.freemarker.core.model.impl.beans.BeansWrapper;
-import org.apache.freemarker.core.model.impl.beans.BeansWrapperBuilder;
 
 /**
  * For internal use only; don't depend on this, there's no backward compatibility guarantee at all!
@@ -36,9 +34,6 @@ public final class _StaticObjectWrappers {
     public static final DefaultObjectWrapper DEFAULT_OBJECT_WRAPPER
             = new DefaultObjectWrapperBuilder(Configuration.VERSION_3_0_0).build();
 
-    public static final BeansWrapper BEANS_WRAPPER
-            = new BeansWrapperBuilder(Configuration.VERSION_3_0_0).build();
-    
     public static final SimpleObjectWrapper SIMPLE_OBJECT_WRAPPER
             = new SimpleObjectWrapper(Configuration.VERSION_3_0_0);
     {

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/051a0822/src/main/java/org/apache/freemarker/core/model/impl/beans/APIModel.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/freemarker/core/model/impl/beans/APIModel.java b/src/main/java/org/apache/freemarker/core/model/impl/beans/APIModel.java
deleted file mode 100644
index 3550fe8..0000000
--- a/src/main/java/org/apache/freemarker/core/model/impl/beans/APIModel.java
+++ /dev/null
@@ -1,45 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- * 
- *   http://www.apache.org/licenses/LICENSE-2.0
- * 
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-
-package org.apache.freemarker.core.model.impl.beans;
-
-/**
- * Exposes the Java API (and properties) of an object.
- * 
- * <p>
- * Notes:
- * <ul>
- * <li>The exposing level is inherited from the {@link BeansWrapper}</li>
- * <li>But methods will always shadow properties and fields with identical name, regardless of {@link BeansWrapper}
- * settings</li>
- * </ul>
- * 
- * @since 2.3.22
- */
-final class APIModel extends BeanModel {
-
-    APIModel(Object object, BeansWrapper 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/beans/ArgumentTypes.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/freemarker/core/model/impl/beans/ArgumentTypes.java b/src/main/java/org/apache/freemarker/core/model/impl/beans/ArgumentTypes.java
deleted file mode 100644
index abac620..0000000
--- a/src/main/java/org/apache/freemarker/core/model/impl/beans/ArgumentTypes.java
+++ /dev/null
@@ -1,647 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- * 
- *   http://www.apache.org/licenses/LICENSE-2.0
- * 
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.apache.freemarker.core.model.impl.beans;
-
-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()) {
-                // BeansWrapper 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)) {
-                // BeansWrapper 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(BeansWrapper bw, Object obj, Object[] args) throws TemplateModelException,
-                InvocationTargetException, IllegalAccessException {
-            convertArgsToReflectionCompatible(bw, args);
-            return callableMemberDesc.invokeMethod(bw, obj, args);
-        }
-
-        @Override
-        Object invokeConstructor(BeansWrapper bw, Object[] args) throws IllegalArgumentException,
-                InstantiationException, IllegalAccessException, InvocationTargetException, TemplateModelException {
-            convertArgsToReflectionCompatible(bw, args);
-            return callableMemberDesc.invokeConstructor(bw, 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(BeansWrapper bw, 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] = bw.listToArray((List<?>) arg, paramType, null);
-                }
-                if (arg.getClass().isArray() && paramType.isAssignableFrom(List.class)) {
-                    args[i] = bw.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/beans/ArrayModel.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/freemarker/core/model/impl/beans/ArrayModel.java b/src/main/java/org/apache/freemarker/core/model/impl/beans/ArrayModel.java
deleted file mode 100644
index 8be341c..0000000
--- a/src/main/java/org/apache/freemarker/core/model/impl/beans/ArrayModel.java
+++ /dev/null
@@ -1,126 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- * 
- *   http://www.apache.org/licenses/LICENSE-2.0
- * 
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-
-package org.apache.freemarker.core.model.impl.beans;
-
-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, (BeansWrapper) 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 BeansWrapper} associated with this model.
-     * Every model has to have an associated {@link BeansWrapper} 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, BeansWrapper 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;
-    }
-}

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/051a0822/src/main/java/org/apache/freemarker/core/model/impl/beans/BeanModel.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/freemarker/core/model/impl/beans/BeanModel.java b/src/main/java/org/apache/freemarker/core/model/impl/beans/BeanModel.java
deleted file mode 100644
index 5c2db2b..0000000
--- a/src/main/java/org/apache/freemarker/core/model/impl/beans/BeanModel.java
+++ /dev/null
@@ -1,365 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- * 
- *   http://www.apache.org/licenses/LICENSE-2.0
- * 
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-
-package org.apache.freemarker.core.model.impl.beans;
-
-import java.beans.IndexedPropertyDescriptor;
-import java.beans.PropertyDescriptor;
-import java.lang.reflect.Field;
-import java.lang.reflect.InvocationTargetException;
-import java.lang.reflect.Method;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.HashMap;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-
-import org.apache.freemarker.core._CoreLogs;
-import org.apache.freemarker.core._DelayedFTLTypeDescription;
-import org.apache.freemarker.core._DelayedJQuote;
-import org.apache.freemarker.core._TemplateModelException;
-import org.apache.freemarker.core.model.AdapterTemplateModel;
-import org.apache.freemarker.core.model.ObjectWrapper;
-import org.apache.freemarker.core.model.TemplateCollectionModel;
-import org.apache.freemarker.core.model.TemplateHashModelEx;
-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.TemplateModelWithAPISupport;
-import org.apache.freemarker.core.model.TemplateScalarModel;
-import org.apache.freemarker.core.model.WrapperTemplateModel;
-import org.apache.freemarker.core.model.impl.CollectionAndSequence;
-import org.apache.freemarker.core.model.impl.SimpleScalar;
-import org.apache.freemarker.core.model.impl.SimpleSequence;
-import org.apache.freemarker.core.util._StringUtil;
-import org.slf4j.Logger;
-
-/**
- * A class that will wrap an arbitrary object into {@link org.apache.freemarker.core.model.TemplateHashModel}
- * interface allowing calls to arbitrary property getters and invocation of
- * accessible methods on the object from a template using the
- * <tt>object.foo</tt> to access properties and <tt>object.bar(arg1, arg2)</tt> to
- * invoke methods on it. You can also use the <tt>object.foo[index]</tt> syntax to
- * access indexed properties. It uses Beans {@link java.beans.Introspector}
- * to dynamically discover the properties and methods. 
- */
-
-public class BeanModel
-implements
-    TemplateHashModelEx, AdapterTemplateModel, WrapperTemplateModel, TemplateModelWithAPISupport {
-    
-    private static final Logger LOG = _CoreLogs.BEANS_WRAPPER;
-    
-    protected final Object object;
-    protected final BeansWrapper wrapper;
-    
-    // We use this to represent an unknown value as opposed to known value of null (JR)
-    static final TemplateModel UNKNOWN = new SimpleScalar("UNKNOWN");
-    
-    static final ModelFactory FACTORY =
-        new ModelFactory()
-        {
-            @Override
-            public TemplateModel create(Object object, ObjectWrapper wrapper) {
-                return new BeanModel(object, (BeansWrapper) wrapper);
-            }
-        };
-
-    // I've tried to use a volatile ConcurrentHashMap field instead of HashMap + synchronized(this), but oddly it was
-    // a bit slower, at least on Java 8 u66. 
-    private HashMap<Object, TemplateModel> memberCache;
-
-    /**
-     * Creates a new model that wraps the specified object. Note that there are
-     * specialized subclasses of this class for wrapping arrays, collections,
-     * enumeration, iterators, and maps. Note also that the superclass can be
-     * used to wrap String objects if only scalar functionality is needed. You
-     * can also choose to delegate the choice over which model class is used for
-     * wrapping to {@link BeansWrapper#wrap(Object)}.
-     * @param object the object to wrap into a model.
-     * @param wrapper the {@link BeansWrapper} associated with this model.
-     * Every model has to have an associated {@link BeansWrapper} instance. The
-     * model gains many attributes from its wrapper, including the caching 
-     * behavior, method exposure level, method-over-item shadowing policy etc.
-     */
-    public BeanModel(Object object, BeansWrapper wrapper) {
-        // [2.4]: All models were introspected here, then the results was discareded, and get() will just do the
-        // introspection again. So is this necessary? (The inrospectNow parameter was added in 2.3.21 to allow
-        // lazy-introspecting BeansWrapper.trueModel|falseModel.)
-        this(object, wrapper, true);
-    }
-
-    /** @since 2.3.21 */
-    BeanModel(Object object, BeansWrapper wrapper, boolean inrospectNow) {
-        this.object = object;
-        this.wrapper = wrapper;
-        if (inrospectNow && object != null) {
-            // [2.4]: Could this be removed?
-            wrapper.getClassIntrospector().get(object.getClass());
-        }
-    }
-    
-    /**
-     * Uses Beans introspection to locate a property or method with name
-     * matching the key name. If a method or property is found, it's wrapped
-     * into {@link org.apache.freemarker.core.model.TemplateMethodModelEx} (for a method or
-     * indexed property), or evaluated on-the-fly and the return value wrapped
-     * into appropriate model (for a simple property) Models for various
-     * properties and methods are cached on a per-class basis, so the costly
-     * introspection is performed only once per property or method of a class.
-     * (Side-note: this also implies that any class whose method has been called
-     * will be strongly referred to by the framework and will not become
-     * unloadable until this class has been unloaded first. Normally this is not
-     * an issue, but can be in a rare scenario where you create many classes on-
-     * the-fly. Also, as the cache grows with new classes and methods introduced
-     * to the framework, it may appear as if it were leaking memory. The
-     * framework does, however detect class reloads (if you happen to be in an
-     * environment that does this kind of things--servlet containers do it when
-     * they reload a web application) and flushes the cache. If no method or
-     * property matching the key is found, the framework will try to invoke
-     * methods with signature
-     * <tt>non-void-return-type get(java.lang.String)</tt>,
-     * then <tt>non-void-return-type get(java.lang.Object)</tt>, or 
-     * alternatively (if the wrapped object is a resource bundle) 
-     * <tt>Object getObject(java.lang.String)</tt>.
-     * @throws TemplateModelException if there was no property nor method nor
-     * a generic <tt>get</tt> method to invoke.
-     */
-    @Override
-    public TemplateModel get(String key)
-        throws TemplateModelException {
-        Class<?> clazz = object.getClass();
-        Map<Object, Object> classInfo = wrapper.getClassIntrospector().get(clazz);
-        TemplateModel retval = null;
-        
-        try {
-            if (wrapper.isMethodsShadowItems()) {
-                Object fd = classInfo.get(key);
-                if (fd != null) {
-                    retval = invokeThroughDescriptor(fd, classInfo);
-                } else {
-                    retval = invokeGenericGet(classInfo, clazz, key);
-                }
-            } else {
-                TemplateModel model = invokeGenericGet(classInfo, clazz, key);
-                final TemplateModel nullModel = wrapper.wrap(null);
-                if (model != nullModel && model != UNKNOWN) {
-                    return model;
-                }
-                Object fd = classInfo.get(key);
-                if (fd != null) {
-                    retval = invokeThroughDescriptor(fd, classInfo);
-                    if (retval == UNKNOWN && model == nullModel) {
-                        // This is the (somewhat subtle) case where the generic get() returns null
-                        // and we have no bean info, so we respect the fact that
-                        // the generic get() returns null and return null. (JR)
-                        retval = nullModel;
-                    }
-                }
-            }
-            if (retval == UNKNOWN) {
-                if (wrapper.isStrict()) {
-                    throw new InvalidPropertyException("No such bean property: " + key);
-                } else {
-                    logNoSuchKey(key, classInfo);
-                }
-                retval = wrapper.wrap(null);
-            }
-            return retval;
-        } catch (TemplateModelException e) {
-            throw e;
-        } catch (Exception e) {
-            throw new _TemplateModelException(e,
-                    "An error has occurred when reading existing sub-variable ", new _DelayedJQuote(key),
-                    "; see cause exception! The type of the containing value was: ",
-                    new _DelayedFTLTypeDescription(this)
-            );
-        }
-    }
-
-    private void logNoSuchKey(String key, Map<?, ?> keyMap) {
-        if (LOG.isDebugEnabled()) {
-            LOG.debug("Key " + _StringUtil.jQuoteNoXSS(key) + " was not found on instance of " + 
-                object.getClass().getName() + ". Introspection information for " +
-                "the class is: " + keyMap);
-        }
-    }
-    
-    /**
-     * Whether the model has a plain get(String) or get(Object) method
-     */
-    
-    protected boolean hasPlainGetMethod() {
-        return wrapper.getClassIntrospector().get(object.getClass()).get(ClassIntrospector.GENERIC_GET_KEY) != null;
-    }
-    
-    private TemplateModel invokeThroughDescriptor(Object desc, Map<Object, Object> classInfo)
-            throws IllegalAccessException, InvocationTargetException, TemplateModelException {
-        // See if this particular instance has a cached implementation for the requested feature descriptor
-        TemplateModel cachedModel;
-        synchronized (this) {
-            cachedModel = memberCache != null ? memberCache.get(desc) : null;
-        }
-
-        if (cachedModel != null) {
-            return cachedModel;
-        }
-
-        TemplateModel resultModel = UNKNOWN;
-        if (desc instanceof IndexedPropertyDescriptor) {
-            Method readMethod = ((IndexedPropertyDescriptor) desc).getIndexedReadMethod(); 
-            resultModel = cachedModel = 
-                new SimpleMethodModel(object, readMethod, 
-                        ClassIntrospector.getArgTypes(classInfo, readMethod), wrapper);
-        } else if (desc instanceof PropertyDescriptor) {
-            PropertyDescriptor pd = (PropertyDescriptor) desc;
-            resultModel = wrapper.invokeMethod(object, pd.getReadMethod(), null);
-            // cachedModel remains null, as we don't cache these
-        } else if (desc instanceof Field) {
-            resultModel = wrapper.wrap(((Field) desc).get(object));
-            // cachedModel remains null, as we don't cache these
-        } else if (desc instanceof Method) {
-            Method method = (Method) desc;
-            resultModel = cachedModel = new SimpleMethodModel(
-                    object, method, ClassIntrospector.getArgTypes(classInfo, method), wrapper);
-        } else if (desc instanceof OverloadedMethods) {
-            resultModel = cachedModel = new OverloadedMethodsModel(
-                    object, (OverloadedMethods) desc, wrapper);
-        }
-        
-        // If new cachedModel was created, cache it
-        if (cachedModel != null) {
-            synchronized (this) {
-                if (memberCache == null) {
-                    memberCache = new HashMap<>();
-                }
-                memberCache.put(desc, cachedModel);
-            }
-        }
-        return resultModel;
-    }
-    
-    void clearMemberCache() {
-        synchronized (this) {
-            memberCache = null;
-        }
-    }
-
-    protected TemplateModel invokeGenericGet(Map/*<Object, Object>*/ classInfo, Class<?> clazz, String key)
-            throws IllegalAccessException, InvocationTargetException,
-        TemplateModelException {
-        Method genericGet = (Method) classInfo.get(ClassIntrospector.GENERIC_GET_KEY);
-        if (genericGet == null) {
-            return UNKNOWN;
-        }
-
-        return wrapper.invokeMethod(object, genericGet, new Object[] { key });
-    }
-
-    protected TemplateModel wrap(Object obj)
-    throws TemplateModelException {
-        return wrapper.getOuterIdentity().wrap(obj);
-    }
-    
-    protected Object unwrap(TemplateModel model)
-    throws TemplateModelException {
-        return wrapper.unwrap(model);
-    }
-
-    /**
-     * Tells whether the model is considered to be empty.
-     * It is empty if the wrapped object is a 0 length {@link String}, or an empty {@link Collection} or and empty
-     * {@link Map}, or an {@link Iterator} that has no more items, or a {@link Boolean#FALSE}, or {@code null}. 
-     */
-    @Override
-    public boolean isEmpty() {
-        if (object instanceof String) {
-            return ((String) object).length() == 0;
-        }
-        if (object instanceof Collection) {
-            return ((Collection<?>) object).isEmpty();
-        }
-        if (object instanceof Iterator) {
-            return !((Iterator<?>) object).hasNext();
-        }
-        if (object instanceof Map) {
-            return ((Map<?,?>) object).isEmpty();
-        }
-        // [FM3] Why's FALSE empty? 
-        return object == null || Boolean.FALSE.equals(object);
-    }
-    
-    /**
-     * Returns the same as {@link #getWrappedObject()}; to ensure that, this method will be final starting from 2.4.
-     * This behavior of {@link BeanModel} is assumed by some FreeMarker code. 
-     */
-    @Override
-    public Object getAdaptedObject(Class<?> hint) {
-        return object;  // return getWrappedObject(); starting from 2.4
-    }
-
-    @Override
-    public Object getWrappedObject() {
-        return object;
-    }
-    
-    @Override
-    public int size() {
-        return wrapper.getClassIntrospector().keyCount(object.getClass());
-    }
-
-    @Override
-    public TemplateCollectionModel keys() {
-        return new CollectionAndSequence(new SimpleSequence(keySet(), wrapper));
-    }
-
-    @Override
-    public TemplateCollectionModel values() throws TemplateModelException {
-        List<Object> values = new ArrayList<>(size());
-        TemplateModelIterator it = keys().iterator();
-        while (it.hasNext()) {
-            String key = ((TemplateScalarModel) it.next()).getAsString();
-            values.add(get(key));
-        }
-        return new CollectionAndSequence(new SimpleSequence(values, wrapper));
-    }
-    
-    @Override
-    public String toString() {
-        return object.toString();
-    }
-
-    /**
-     * Helper method to support TemplateHashModelEx. Returns the Set of
-     * Strings which are available via the TemplateHashModel
-     * interface. Subclasses that override <tt>invokeGenericGet</tt> to
-     * provide additional hash keys should also override this method.
-     */
-    protected Set/*<Object>*/ keySet() {
-        return wrapper.getClassIntrospector().keySet(object.getClass());
-    }
-
-    @Override
-    public TemplateModel getAPI() throws TemplateModelException {
-        return wrapper.wrapAsAPI(object);
-    }
-    
-}
\ 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/beans/BeansModelCache.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/freemarker/core/model/impl/beans/BeansModelCache.java b/src/main/java/org/apache/freemarker/core/model/impl/beans/BeansModelCache.java
deleted file mode 100644
index 52db93d..0000000
--- a/src/main/java/org/apache/freemarker/core/model/impl/beans/BeansModelCache.java
+++ /dev/null
@@ -1,73 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- * 
- *   http://www.apache.org/licenses/LICENSE-2.0
- * 
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-
-package org.apache.freemarker.core.model.impl.beans;
-
-import java.util.HashSet;
-import java.util.Map;
-import java.util.Set;
-import java.util.concurrent.ConcurrentHashMap;
-
-import org.apache.freemarker.core.model.TemplateModel;
-
-import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
-
-public class BeansModelCache extends ModelCache {
-    private final Map classToFactory = new ConcurrentHashMap();
-    private final Set mappedClassNames = new HashSet();
-
-    private final BeansWrapper wrapper;
-    
-    BeansModelCache(BeansWrapper wrapper) {
-        this.wrapper = wrapper;
-    }
-    
-    @Override
-    protected boolean isCacheable(Object object) {
-        return object.getClass() != Boolean.class; 
-    }
-    
-    @Override
-    @SuppressFBWarnings(value="JLM_JSR166_UTILCONCURRENT_MONITORENTER", justification="Locks for factory creation only")
-    protected TemplateModel create(Object object) {
-        Class clazz = object.getClass();
-        
-        ModelFactory factory = (ModelFactory) classToFactory.get(clazz);
-        
-        if (factory == null) {
-            // Synchronized so that we won't unnecessarily create the same factory for multiple times in parallel.
-            synchronized (classToFactory) {
-                factory = (ModelFactory) classToFactory.get(clazz);
-                if (factory == null) {
-                    String className = clazz.getName();
-                    // clear mappings when class reloading is detected
-                    if (!mappedClassNames.add(className)) {
-                        classToFactory.clear();
-                        mappedClassNames.clear();
-                        mappedClassNames.add(className);
-                    }
-                    factory = wrapper.getModelFactory(clazz);
-                    classToFactory.put(clazz, factory);
-                }
-            }
-        }
-        
-        return factory.create(object, wrapper);
-    }
-}


Mime
View raw message