freemarker-notifications mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From ddek...@apache.org
Subject [28/54] [partial] incubator-freemarker git commit: Top level package name change to org.apache.freemarker, and some of of the internal package structure changes. Other smaller cleanup. To be continued...
Date Thu, 16 Feb 2017 23:08:53 GMT
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/ecb4e230/src/main/java/freemarker/ext/beans/ArgumentTypes.java
----------------------------------------------------------------------
diff --git a/src/main/java/freemarker/ext/beans/ArgumentTypes.java b/src/main/java/freemarker/ext/beans/ArgumentTypes.java
deleted file mode 100644
index e383259..0000000
--- a/src/main/java/freemarker/ext/beans/ArgumentTypes.java
+++ /dev/null
@@ -1,735 +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 freemarker.ext.beans;
-
-import java.lang.reflect.InvocationTargetException;
-import java.math.BigDecimal;
-import java.util.Collection;
-import java.util.Iterator;
-import java.util.LinkedList;
-import java.util.List;
-
-import freemarker.core.BugException;
-import freemarker.template.TemplateModel;
-import freemarker.template.TemplateModelException;
-import freemarker.template.Version;
-import freemarker.template.utility.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: Java reflection API will 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;
-    
-    private final boolean bugfixed;
-    
-    /**
-     * @param args The actual arguments. A varargs argument should be present exploded, no as an array.
-     * @param bugfixed Introduced in 2.3.21, sets this object to a mode that works well with {@link BeansWrapper}-s
-     *      created with {@link Version} 2.3.21 or higher.
-     */
-    ArgumentTypes(Object[] args, boolean bugfixed) {
-        int ln = args.length;
-        Class<?>[] typesTmp = new Class[ln];
-        for (int i = 0; i < ln; ++i) {
-            Object arg = args[i];
-            typesTmp[i] = arg == null
-                    ? (bugfixed ? Null.class : Object.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;  
-        this.bugfixed = bugfixed;
-    }
-    
-    @Override
-    public int hashCode() {
-        int hash = 0;
-        for (int i = 0; i < types.length; ++i) {
-            hash ^= types[i].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<CallableMemberDescriptor>();
-        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>This method behaves differently in {@code bugfixed}-mode (used when a {@link BeansWrapper} is created with
-     * incompatible improvements set to 2.3.21 or higher). Below we describe the bugfixed behavior only. 
-     *  
-     * <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;
-        
-        if (bugfixed) {
-            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 fixed parameters wins:
-                        return paramTypes1Len - paramTypes2Len;
-                    }
-                } else {
-                    return 0;
-                }
-            }
-        } else { // non-bugfixed (backward-compatible) mode
-            boolean paramTypes1HasAMoreSpecific = false;
-            boolean paramTypes2HasAMoreSpecific = false;
-            for (int i = 0; i < paramTypes1Len; ++i) {
-                Class<?> paramType1 = getParamType(paramTypes1, paramTypes1Len, i, varArg);
-                Class<?> paramType2 = getParamType(paramTypes2, paramTypes2Len, i, varArg);
-                if (paramType1 != paramType2) {
-                    paramTypes1HasAMoreSpecific = 
-                        paramTypes1HasAMoreSpecific
-                        || _MethodUtil.isMoreOrSameSpecificParameterType(paramType1, paramType2, false, 0) != 0;
-                    paramTypes2HasAMoreSpecific = 
-                        paramTypes2HasAMoreSpecific
-                        || _MethodUtil.isMoreOrSameSpecificParameterType(paramType2, paramType1, false, 0) != 0;
-                }
-            }
-            
-            if (paramTypes1HasAMoreSpecific) {
-                return paramTypes2HasAMoreSpecific ? 0 : 1;
-            } else if (paramTypes2HasAMoreSpecific) {
-                return -1;
-            } 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<CallableMemberDescriptor>();
-        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 if (bugfixed) {
-            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;
-            }
-        } else { // if !bugfixed
-            // This non-bugfixed (backward-compatible, pre-2.3.21) branch:
-            // - Doesn't convert *to* non-primitive numerical types (unless the argument is a BigDecimal).
-            //   (This is like in Java language, which also doesn't coerce to non-primitive numerical types.) 
-            // - Doesn't support BigInteger conversions
-            // - Doesn't support NumberWithFallbackType-s and CharacterOrString-s. Those are only produced in bugfixed
-            //   mode anyway.
-            // - Doesn't support conversion between array and List
-            if (formal.isPrimitive()) {
-                // Check for boxing with widening primitive conversion. Note that 
-                // actual parameters are never primitives.
-                // It doesn't do the same with boxing types... that was a bug.
-                if (formal == Boolean.TYPE) {
-                    return actual == Boolean.class
-                            ? CONVERSION_DIFFICULTY_REFLECTION : CONVERSION_DIFFICULTY_IMPOSSIBLE;
-                } else if (formal == Double.TYPE && 
-                        (actual == Double.class || actual == Float.class || 
-                         actual == Long.class || actual == Integer.class || 
-                         actual == Short.class || actual == Byte.class)) {
-                     return CONVERSION_DIFFICULTY_REFLECTION;
-                } else if (formal == Integer.TYPE && 
-                        (actual == Integer.class || actual == Short.class || 
-                         actual == Byte.class)) {
-                     return CONVERSION_DIFFICULTY_REFLECTION;
-                } else if (formal == Long.TYPE && 
-                        (actual == Long.class || actual == Integer.class || 
-                         actual == Short.class || actual == Byte.class)) {
-                     return CONVERSION_DIFFICULTY_REFLECTION;
-                } else if (formal == Float.TYPE && 
-                        (actual == Float.class || actual == Long.class || 
-                         actual == Integer.class || actual == Short.class || 
-                         actual == Byte.class)) {
-                     return CONVERSION_DIFFICULTY_REFLECTION;
-                } else if (formal == Character.TYPE) {
-                    return actual == Character.class
-                            ? CONVERSION_DIFFICULTY_REFLECTION : CONVERSION_DIFFICULTY_IMPOSSIBLE;
-                } else if (formal == Byte.TYPE && actual == Byte.class) {
-                    return CONVERSION_DIFFICULTY_REFLECTION;
-                } else if (formal == Short.TYPE &&
-                   (actual == Short.class || actual == Byte.class)) {
-                    return CONVERSION_DIFFICULTY_REFLECTION;
-                } else if (BigDecimal.class.isAssignableFrom(actual) && ClassUtil.isNumerical(formal)) {
-                    // Special case for BigDecimals as we deem BigDecimal to be
-                    // convertible to any numeric type - either object or primitive.
-                    // This can actually cause us trouble as this is a narrowing 
-                    // conversion, not widening. 
-                    return CONVERSION_DIFFICULTY_REFLECTION;
-                } else {
-                    return CONVERSION_DIFFICULTY_IMPOSSIBLE;
-                }
-            } 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/ecb4e230/src/main/java/freemarker/ext/beans/ArrayModel.java
----------------------------------------------------------------------
diff --git a/src/main/java/freemarker/ext/beans/ArrayModel.java b/src/main/java/freemarker/ext/beans/ArrayModel.java
deleted file mode 100644
index 5884c9a..0000000
--- a/src/main/java/freemarker/ext/beans/ArrayModel.java
+++ /dev/null
@@ -1,120 +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 freemarker.ext.beans;
-
-import java.lang.reflect.Array;
-
-import freemarker.ext.util.ModelFactory;
-import freemarker.template.ObjectWrapper;
-import freemarker.template.TemplateCollectionModel;
-import freemarker.template.TemplateModel;
-import freemarker.template.TemplateModelException;
-import freemarker.template.TemplateModelIterator;
-import freemarker.template.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()
-        {
-            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);
-    }
-
-
-    public TemplateModelIterator iterator() {
-        return new Iterator();
-    }
-
-    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;
-
-        public boolean hasNext() {
-            return position < length;
-        }
-
-        public TemplateModel get(int index)
-        throws TemplateModelException {
-            return ArrayModel.this.get(index);
-        }
-
-        public TemplateModel next()
-        throws TemplateModelException {
-            return position < length ? get(position++) : null;
-        }
-
-        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/ecb4e230/src/main/java/freemarker/ext/beans/BeanModel.java
----------------------------------------------------------------------
diff --git a/src/main/java/freemarker/ext/beans/BeanModel.java b/src/main/java/freemarker/ext/beans/BeanModel.java
deleted file mode 100644
index 1d6062f..0000000
--- a/src/main/java/freemarker/ext/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 freemarker.ext.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.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import freemarker.core.CollectionAndSequence;
-import freemarker.core._DelayedFTLTypeDescription;
-import freemarker.core._DelayedJQuote;
-import freemarker.core._TemplateModelException;
-import freemarker.ext.util.ModelFactory;
-import freemarker.ext.util.WrapperTemplateModel;
-import freemarker.template.AdapterTemplateModel;
-import freemarker.template.ObjectWrapper;
-import freemarker.template.SimpleScalar;
-import freemarker.template.SimpleSequence;
-import freemarker.template.TemplateCollectionModel;
-import freemarker.template.TemplateHashModelEx;
-import freemarker.template.TemplateModel;
-import freemarker.template.TemplateModelException;
-import freemarker.template.TemplateModelIterator;
-import freemarker.template.TemplateModelWithAPISupport;
-import freemarker.template.TemplateScalarModel;
-import freemarker.template.utility.StringUtil;
-
-/**
- * A class that will wrap an arbitrary object into {@link freemarker.template.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 = LoggerFactory.getLogger("freemarker.beans");
-    
-    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 freemarker.template.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<Object, TemplateModel>();
-                }
-                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 empty. It is empty if either the wrapped 
-     * object is null, or it's a Boolean with false value.
-     */
-    @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 && wrapper.is2324Bugfixed()) {
-            return !((Iterator<?>) object).hasNext();
-        }
-        if (object instanceof Map) {
-            return ((Map<?,?>) object).isEmpty();
-        }
-        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<Object>(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/ecb4e230/src/main/java/freemarker/ext/beans/BeansModelCache.java
----------------------------------------------------------------------
diff --git a/src/main/java/freemarker/ext/beans/BeansModelCache.java b/src/main/java/freemarker/ext/beans/BeansModelCache.java
deleted file mode 100644
index 659fe1b..0000000
--- a/src/main/java/freemarker/ext/beans/BeansModelCache.java
+++ /dev/null
@@ -1,74 +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 freemarker.ext.beans;
-
-import java.util.HashSet;
-import java.util.Map;
-import java.util.Set;
-import java.util.concurrent.ConcurrentHashMap;
-
-import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
-import freemarker.ext.util.ModelCache;
-import freemarker.ext.util.ModelFactory;
-import freemarker.template.TemplateModel;
-
-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