freemarker-notifications mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From ddek...@apache.org
Subject [8/9] incubator-freemarker git commit: Removed support for incompatibleImprovements below 3.0.0.
Date Mon, 20 Feb 2017 10:14:31 GMT
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/8ba7883e/src/main/java/org/apache/freemarker/core/model/impl/DefaultObjectWrapperConfiguration.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/freemarker/core/model/impl/DefaultObjectWrapperConfiguration.java b/src/main/java/org/apache/freemarker/core/model/impl/DefaultObjectWrapperConfiguration.java
index c69b498..a32f7c8 100644
--- a/src/main/java/org/apache/freemarker/core/model/impl/DefaultObjectWrapperConfiguration.java
+++ b/src/main/java/org/apache/freemarker/core/model/impl/DefaultObjectWrapperConfiguration.java
@@ -20,7 +20,6 @@
 package org.apache.freemarker.core.model.impl;
 
 import org.apache.freemarker.core.Version;
-import org.apache.freemarker.core._TemplateAPI;
 import org.apache.freemarker.core.model.ObjectWrapper;
 import org.apache.freemarker.core.model.impl.beans.BeansWrapperConfiguration;
 
@@ -35,14 +34,12 @@ import org.apache.freemarker.core.model.impl.beans.BeansWrapperConfiguration;
  */
 public abstract class DefaultObjectWrapperConfiguration extends BeansWrapperConfiguration {
     
-    private boolean useAdaptersForContainers;
-    private boolean forceLegacyNonListCollections;
+    private boolean useAdaptersForContainers = true;
+    private boolean forceLegacyNonListCollections = true; // [FM3] [2.4]: = IcI < _TemplateAPI.VERSION_INT_2_4_0;
     private boolean iterableSupport;
 
     protected DefaultObjectWrapperConfiguration(Version incompatibleImprovements) {
         super(DefaultObjectWrapper.normalizeIncompatibleImprovementsVersion(incompatibleImprovements), true);
-        useAdaptersForContainers = getIncompatibleImprovements().intValue() >= _TemplateAPI.VERSION_INT_2_3_22;
-        forceLegacyNonListCollections = true; // [2.4]: = IcI < _TemplateAPI.VERSION_INT_2_4_0;
     }
 
     /** See {@link DefaultObjectWrapper#getUseAdaptersForContainers()}. */

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/8ba7883e/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 d891f3b..5f52b10 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
@@ -34,13 +34,13 @@ public final class _StaticObjectWrappers {
     }
 
     public static final DefaultObjectWrapper DEFAULT_OBJECT_WRAPPER
-            = new DefaultObjectWrapperBuilder(Configuration.VERSION_2_3_0).build();
+            = new DefaultObjectWrapperBuilder(Configuration.VERSION_3_0_0).build();
 
     public static final BeansWrapper BEANS_WRAPPER
-            = new BeansWrapperBuilder(Configuration.VERSION_2_3_0).build();
+            = new BeansWrapperBuilder(Configuration.VERSION_3_0_0).build();
     
     public static final SimpleObjectWrapper SIMPLE_OBJECT_WRAPPER
-            = new SimpleObjectWrapper(Configuration.VERSION_2_3_0);
+            = new SimpleObjectWrapper(Configuration.VERSION_3_0_0);
     {
         SIMPLE_OBJECT_WRAPPER.writeProtect();
     }    

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/8ba7883e/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
index de31367..2bb82a2 100644
--- 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
@@ -19,13 +19,11 @@
 package org.apache.freemarker.core.model.impl.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 org.apache.freemarker.core.Version;
 import org.apache.freemarker.core.ast.BugException;
 import org.apache.freemarker.core.model.TemplateModel;
 import org.apache.freemarker.core.model.TemplateModelException;
@@ -42,7 +40,7 @@ final class ArgumentTypes {
     private static final int CONVERSION_DIFFICULTY_REFLECTION = 0;
 
     /**
-     * Conversion difficulty: Java reflection API will won't convert it, FreeMarker has to do it.
+     * Conversion difficulty: Medium: Java reflection API won't convert it, FreeMarker has to do it.
      */
     private static final int CONVERSION_DIFFICULTY_FREEMARKER = 1;
     
@@ -56,26 +54,21 @@ final class ArgumentTypes {
      */
     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) {
+    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
-                    ? (bugfixed ? Null.class : Object.class)
+                    ? 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;  
-        this.bugfixed = bugfixed;
     }
     
     @Override
@@ -147,9 +140,6 @@ final class ArgumentTypes {
      * 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
@@ -179,222 +169,197 @@ final class ArgumentTypes {
         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);
+        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 winerParam;  // 1 => paramType1; -1 => paramType2; 0 => draw
-                if (paramType1 == paramType2) {
-                    winerParam = 0;
+                final int numConvPrice1;
+                if (argIsNum && _ClassUtil.isNumerical(paramType1)) {
+                    final Class<?> nonPrimParamType1 = paramType1.isPrimitive()
+                            ? _ClassUtil.primitiveClassToBoxingClass(paramType1) : paramType1;
+                    numConvPrice1 = OverloadedNumberUtil.getArgumentConversionPrice(argType, nonPrimParamType1);
                 } 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)) {
+                    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 = 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 {  // 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 if (r < 0) {
-                                    winerParam = -1;
-                                    if (r < -1) {
-                                        paramList2WinCnt++;
-                                    } else {
-                                        paramList2WeakWinCnt++;
-                                    }
+                                }
+                            } 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 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) {
+                        } else {  // No list to/from array conversion
+                            final int r = compareParameterListPreferability_cmpTypeSpecificty(
+                                    paramType1, paramType2);
+                            if (r > 0) {
                                 winerParam = 1;
-                                if (numConvPrice1 < OverloadedNumberUtil.BIG_MANTISSA_LOSS_PRICE
-                                        && numConvPrice2 > OverloadedNumberUtil.BIG_MANTISSA_LOSS_PRICE) {
-                                    paramList1StrongWinCnt++;
-                                } else {
+                                if (r > 1) {
                                     paramList1WinCnt++;
+                                } else {
+                                    paramList1WeakWinCnt++;
                                 }
-                            } else {
+                            } else if (r < 0) {
                                 winerParam = -1;
-                                if (numConvPrice2 < OverloadedNumberUtil.BIG_MANTISSA_LOSS_PRICE
-                                        && numConvPrice1 > OverloadedNumberUtil.BIG_MANTISSA_LOSS_PRICE) {
-                                    paramList2StrongWinCnt++;
-                                } else {
+                                if (r < -1) {
                                     paramList2WinCnt++;
+                                } else {
+                                    paramList2WeakWinCnt++;
                                 }
+                            } else {
+                                winerParam = 0;
                             }
-                        } else {
-                            winerParam = (paramType1.isPrimitive() ? 1 : 0) - (paramType2.isPrimitive() ? 1 : 0);
-                            if (winerParam == 1) paramList1WeakWinCnt++;
-                            else if (winerParam == -1) paramList2WeakWinCnt++;
                         }
+                    } else {  // No num. conv. of param1, num. conv. of param2
+                        winerParam = -1;
+                        paramList2WinCnt++;
                     }
-                }  // 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
+                } 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++;
                             }
-                            return compareParameterListPreferability_cmpTypeSpecificty(paramType1, paramType2);
                         } else {
-                            return 0;
+                            winerParam = -1;
+                            if (numConvPrice2 < OverloadedNumberUtil.BIG_MANTISSA_LOSS_PRICE
+                                    && numConvPrice1 > OverloadedNumberUtil.BIG_MANTISSA_LOSS_PRICE) {
+                                paramList2StrongWinCnt++;
+                            } else {
+                                paramList2WinCnt++;
+                            }
                         }
                     } else {
-                        // The method with more fixed parameters wins:
-                        return paramTypes1Len - paramTypes2Len;
+                        winerParam = (paramType1.isPrimitive() ? 1 : 0) - (paramType2.isPrimitive() ? 1 : 0);
+                        if (winerParam == 1) paramList1WeakWinCnt++;
+                        else if (winerParam == -1) paramList2WeakWinCnt++;
                     }
-                } else {
-                    return 0;
                 }
+            }  // when paramType1 != paramType2
+            
+            if (firstWinerParamList == 0 && winerParam != 0) {
+                firstWinerParamList = winerParam; 
             }
-        } 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;
+        }  // 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;
                 }
-            }
-            
-            if (paramTypes1HasAMoreSpecific) {
-                return paramTypes2HasAMoreSpecific ? 0 : 1;
-            } else if (paramTypes2HasAMoreSpecific) {
-                return -1;
             } else {
                 return 0;
             }
@@ -533,7 +498,7 @@ final class ArgumentTypes {
         // Check for identity or widening reference conversion
         if (formal.isAssignableFrom(actual) && actual != CharacterOrString.class) {
             return CONVERSION_DIFFICULTY_REFLECTION;
-        } else if (bugfixed) {
+        } else {
             final Class<?> formalNP;
             if (formal.isPrimitive()) {
                 if (actual == Null.class) {
@@ -569,59 +534,6 @@ final class ArgumentTypes {
             } 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;
-            }
         }
     }
     

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/8ba7883e/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
index 9ef62d0..d800b0c 100644
--- 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
@@ -285,8 +285,9 @@ implements
     }
 
     /**
-     * Tells whether the model is empty. It is empty if either the wrapped 
-     * object is null, or it's a Boolean with false value.
+     * 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() {
@@ -296,12 +297,13 @@ implements
         if (object instanceof Collection) {
             return ((Collection<?>) object).isEmpty();
         }
-        if (object instanceof Iterator && wrapper.is2324Bugfixed()) {
+        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);
     }
     

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/8ba7883e/src/main/java/org/apache/freemarker/core/model/impl/beans/BeansWrapper.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/freemarker/core/model/impl/beans/BeansWrapper.java b/src/main/java/org/apache/freemarker/core/model/impl/beans/BeansWrapper.java
index 26e8ef9..3d1f841 100644
--- a/src/main/java/org/apache/freemarker/core/model/impl/beans/BeansWrapper.java
+++ b/src/main/java/org/apache/freemarker/core/model/impl/beans/BeansWrapper.java
@@ -49,6 +49,7 @@ import org.apache.freemarker.core.ast._TemplateModelException;
 import org.apache.freemarker.core.model.AdapterTemplateModel;
 import org.apache.freemarker.core.model.ObjectWrapper;
 import org.apache.freemarker.core.model.ObjectWrapperAndUnwrapper;
+import org.apache.freemarker.core.model.RichObjectWrapper;
 import org.apache.freemarker.core.model.TemplateBooleanModel;
 import org.apache.freemarker.core.model.TemplateCollectionModel;
 import org.apache.freemarker.core.model.TemplateDateModel;
@@ -63,10 +64,9 @@ import org.apache.freemarker.core.model.TemplateSequenceModel;
 import org.apache.freemarker.core.model.WrapperTemplateModel;
 import org.apache.freemarker.core.model.impl.DefaultObjectWrapper;
 import org.apache.freemarker.core.model.impl.SimpleObjectWrapper;
-import org.apache.freemarker.core.util._ClassUtil;
-import org.apache.freemarker.core.model.RichObjectWrapper;
 import org.apache.freemarker.core.util.UndeclaredThrowableException;
 import org.apache.freemarker.core.util.WriteProtectable;
+import org.apache.freemarker.core.util._ClassUtil;
 import org.slf4j.Logger;
 
 /**
@@ -227,11 +227,11 @@ public class BeansWrapper implements RichObjectWrapper, WriteProtectable {
      *     </li>
      *     <li>
      *       <p>2.3.21 (or higher):
-     *       Several glitches were fixed in <em>overloaded</em> method selection. This usually just gets
+     *       Several glitches were oms in <em>overloaded</em> method selection. This usually just gets
      *       rid of errors (like ambiguity exceptions and numerical precision loses due to bad overloaded method
      *       choices), still, as in some cases the method chosen can be a different one now (that was the point of
      *       the reworking after all), it can mean a change in the behavior of the application. The most important
-     *       change is that the treatment of {@code null} arguments were fixed, as earlier they were only seen
+     *       change is that the treatment of {@code null} arguments were oms, as earlier they were only seen
      *       applicable to parameters of type {@code Object}. Now {@code null}-s are seen to be applicable to any
      *       non-primitive parameters, and among those the one with the most specific type will be preferred (just
      *       like in Java), which is hence never the one with the {@code Object} parameter type. For more details
@@ -354,7 +354,7 @@ public class BeansWrapper implements RichObjectWrapper, WriteProtectable {
         staticModels = new StaticModels(BeansWrapper.this);
         enumModels = createEnumModels(BeansWrapper.this);
         modelCache = new BeansModelCache(BeansWrapper.this);
-        setUseCache(bwConf.getUseModelCache());
+        setUseModelCache(bwConf.getUseModelCache());
 
         finalizeConstruction(writeProtected);
     }
@@ -492,7 +492,7 @@ public class BeansWrapper implements RichObjectWrapper, WriteProtectable {
      * (Technical note: {@link Map}-s will be wrapped into {@link SimpleMapModel} in this case.)
      * 
      * <p>*: For historical reasons, FreeMarker 2.3.X doesn't support non-string keys with the {@code []} operator,
-     *       hence the workarounds. This will be likely fixed in FreeMarker 2.4.0. Also note that the method- and
+     *       hence the workarounds. This will be likely oms in FreeMarker 2.4.0. Also note that the method- and
      *       the "field"-namespaces aren't separate in FreeMarker, hence {@code myMap.get} can return the {@code get}
      *       method.
      */
@@ -747,7 +747,7 @@ public class BeansWrapper implements RichObjectWrapper, WriteProtectable {
      * the same object will likely return the same model (although there is
      * no guarantee as the cache items can be cleared any time).
      */
-    public void setUseCache(boolean useCache) {
+    public void setUseModelCache(boolean useCache) {
         checkModifiable();
         modelCache.setUseCache(useCache);
     }
@@ -755,7 +755,7 @@ public class BeansWrapper implements RichObjectWrapper, WriteProtectable {
     /**
      * @since 2.3.21
      */
-    public boolean getUseCache() {
+    public boolean getUseModelCache() {
         return modelCache.getUseCache();
     }
     
@@ -784,39 +784,13 @@ public class BeansWrapper implements RichObjectWrapper, WriteProtectable {
         return incompatibleImprovements;
     }
     
-    boolean is2321Bugfixed() {
-        return is2321Bugfixed(getIncompatibleImprovements());
-    }
-
-    static boolean is2321Bugfixed(Version version) {
-        return version.intValue() >= _TemplateAPI.VERSION_INT_2_3_21;
-    }
-
-    boolean is2324Bugfixed() {
-        return is2324Bugfixed(getIncompatibleImprovements());
-    }
-
-    static boolean is2324Bugfixed(Version version) {
-        return version.intValue() >= _TemplateAPI.VERSION_INT_2_3_24;
-    }
-
-    private static boolean is300Bugfixed(Version incompatibleImprovements) {
-        return incompatibleImprovements.intValue() >= Configuration.VERSION_3_0_0.intValue();
-    }
-    
     /** 
      * Returns the lowest version number that is equivalent with the parameter version.
      * @since 2.3.21
      */
     protected static Version normalizeIncompatibleImprovementsVersion(Version incompatibleImprovements) {
         _TemplateAPI.checkVersionNotNullAndSupported(incompatibleImprovements);
-        if (incompatibleImprovements.intValue() < _TemplateAPI.VERSION_INT_2_3_0) {
-            throw new IllegalArgumentException("Version must be at least 2.3.0.");
-        }
-        return is300Bugfixed(incompatibleImprovements) ? Configuration.VERSION_3_0_0 
-                : is2324Bugfixed(incompatibleImprovements) ? Configuration.VERSION_2_3_24
-                : is2321Bugfixed(incompatibleImprovements) ? Configuration.VERSION_2_3_21
-                : Configuration.VERSION_2_3_0;
+        return Configuration.VERSION_3_0_0;
     }
 
     /**
@@ -986,13 +960,12 @@ public class BeansWrapper implements RichObjectWrapper, WriteProtectable {
     
     /**
      * @param typeFlags
-     *            Used when unwrapping for overloaded methods and so the {@code targetClass} is possibly too generic.
-     *            Must be 0 when unwrapping parameter values for non-overloaded methods, also if
-     *            {@link #is2321Bugfixed()} is {@code false}.
+     *            Used when unwrapping for overloaded methods and so the {@code targetClass} is possibly too generic
+     *            (as it's the most specific common superclass). Must be 0 when unwrapping parameter values for
+     *            non-overloaded methods.
      * @return {@link ObjectWrapperAndUnwrapper#CANT_UNWRAP_TO_TARGET_CLASS} or the unwrapped object.
      */
-    Object tryUnwrapTo(TemplateModel model, Class<?> targetClass, int typeFlags) 
-    throws TemplateModelException {
+    Object tryUnwrapTo(TemplateModel model, Class<?> targetClass, int typeFlags) throws TemplateModelException {
         Object res = tryUnwrapTo(model, targetClass, typeFlags, null);
         if ((typeFlags & TypeFlags.WIDENED_NUMERICAL_UNWRAPPING_HINT) != 0
                 && res instanceof Number) {
@@ -1012,9 +985,7 @@ public class BeansWrapper implements RichObjectWrapper, WriteProtectable {
             return null;
         }
         
-        final boolean is2321Bugfixed = is2321Bugfixed();
-        
-        if (is2321Bugfixed && targetClass.isPrimitive()) {
+        if (targetClass.isPrimitive()) {
             targetClass = _ClassUtil.primitiveClassToBoxingClass(targetClass);
         }
         
@@ -1030,7 +1001,7 @@ public class BeansWrapper implements RichObjectWrapper, WriteProtectable {
             
             // Attempt numeric conversion: 
             if (targetClass != Object.class && (wrapped instanceof Number && _ClassUtil.isNumerical(targetClass))) {
-                Number number = forceUnwrappedNumberToType((Number) wrapped, targetClass, is2321Bugfixed);
+                Number number = forceUnwrappedNumberToType((Number) wrapped, targetClass);
                 if (number != null) return number;
             }
         }
@@ -1043,7 +1014,7 @@ public class BeansWrapper implements RichObjectWrapper, WriteProtectable {
             
             // Attempt numeric conversion: 
             if (targetClass != Object.class && (wrapped instanceof Number && _ClassUtil.isNumerical(targetClass))) {
-                Number number = forceUnwrappedNumberToType((Number) wrapped, targetClass, is2321Bugfixed);
+                Number number = forceUnwrappedNumberToType((Number) wrapped, targetClass);
                 if (number != null) {
                     return number;
                 }
@@ -1069,7 +1040,7 @@ public class BeansWrapper implements RichObjectWrapper, WriteProtectable {
             if (_ClassUtil.isNumerical(targetClass)) {
                 if (model instanceof TemplateNumberModel) {
                     Number number = forceUnwrappedNumberToType(
-                            ((TemplateNumberModel) model).getAsNumber(), targetClass, is2321Bugfixed);
+                            ((TemplateNumberModel) model).getAsNumber(), targetClass);
                     if (number != null) {
                         return number;
                     }
@@ -1204,9 +1175,6 @@ public class BeansWrapper implements RichObjectWrapper, WriteProtectable {
                 return new SetAdapter((TemplateCollectionModel) model, this);
             }
             
-            // In 2.3.21 bugfixed mode only, List-s are convertible to arrays on invocation time. Only overloaded
-            // methods need this. As itf will be 0 in non-bugfixed mode and for non-overloaded method calls, it's
-            // enough to check if the TypeFlags.ACCEPTS_ARRAY bit is 1:
             if ((itf & TypeFlags.ACCEPTS_ARRAY) != 0
                     && model instanceof TemplateSequenceModel) {
                 return new SequenceAdapter((TemplateSequenceModel) model, this);
@@ -1306,7 +1274,7 @@ public class BeansWrapper implements RichObjectWrapper, WriteProtectable {
                         isComponentTypeExamined = true;
                     }
                     if (isComponentTypeNumerical && listItem instanceof Number) {
-                        listItem = forceUnwrappedNumberToType((Number) listItem, componentType, true);
+                        listItem = forceUnwrappedNumberToType((Number) listItem, componentType);
                     } else if (componentType == String.class && listItem instanceof Character) {
                         listItem = String.valueOf(((Character) listItem).charValue());
                     } else if ((componentType == Character.class || componentType == char.class)
@@ -1362,7 +1330,7 @@ public class BeansWrapper implements RichObjectWrapper, WriteProtectable {
      * @param n Non-{@code null}
      * @return {@code null} if the conversion has failed.
      */
-    static Number forceUnwrappedNumberToType(final Number n, final Class<?> targetType, final boolean bugfixed) {
+    static Number forceUnwrappedNumberToType(final Number n, final Class<?> targetType) {
         // We try to order the conditions by decreasing probability.
         if (targetType == n.getClass()) {
             return n;
@@ -1392,7 +1360,7 @@ public class BeansWrapper implements RichObjectWrapper, WriteProtectable {
         } else if (targetType == BigInteger.class) {
             if (n instanceof BigInteger) {
                 return n;
-            } else if (bugfixed) {
+            } else {
                 if (n instanceof OverloadedNumberUtil.IntegerBigDecimal) {
                     return ((OverloadedNumberUtil.IntegerBigDecimal) n).bigIntegerValue();
                 } else if (n instanceof BigDecimal) {
@@ -1400,9 +1368,6 @@ public class BeansWrapper implements RichObjectWrapper, WriteProtectable {
                 } else {
                     return BigInteger.valueOf(n.longValue()); 
                 }
-            } else {
-                // This is wrong, because something like "123.4" will cause NumberFormatException instead of flooring.
-                return new BigInteger(n.toString());
             }
         } else {
             final Number oriN = n instanceof OverloadedNumberUtil.NumberWithFallbackType

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/8ba7883e/src/main/java/org/apache/freemarker/core/model/impl/beans/BeansWrapperBuilder.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/freemarker/core/model/impl/beans/BeansWrapperBuilder.java b/src/main/java/org/apache/freemarker/core/model/impl/beans/BeansWrapperBuilder.java
index ffd403e..2c0dcc0 100644
--- a/src/main/java/org/apache/freemarker/core/model/impl/beans/BeansWrapperBuilder.java
+++ b/src/main/java/org/apache/freemarker/core/model/impl/beans/BeansWrapperBuilder.java
@@ -96,7 +96,7 @@ import org.apache.freemarker.core.model.impl.DefaultObjectWrapperBuilder;
  *         these caches aren't unused.
  *       </li>
  *       <li><p>
- *         Instance model cache: By default off (see {@link BeansWrapper#setUseCache(boolean)}). Caches the
+ *         Instance model cache: By default off (see {@link BeansWrapper#setUseModelCache(boolean)}). Caches the
  *         {@link TemplateModel}-s for all Java objects that were accessed from templates.
  *       </li>
  *     </ul>

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/8ba7883e/src/main/java/org/apache/freemarker/core/model/impl/beans/BeansWrapperConfiguration.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/freemarker/core/model/impl/beans/BeansWrapperConfiguration.java b/src/main/java/org/apache/freemarker/core/model/impl/beans/BeansWrapperConfiguration.java
index 7a504f8..327cbc5 100644
--- a/src/main/java/org/apache/freemarker/core/model/impl/beans/BeansWrapperConfiguration.java
+++ b/src/main/java/org/apache/freemarker/core/model/impl/beans/BeansWrapperConfiguration.java
@@ -183,7 +183,7 @@ public abstract class BeansWrapperConfiguration implements Cloneable {
         return useModelCache;
     }
 
-    /** See {@link BeansWrapper#setUseCache(boolean)} (it means the same). */
+    /** See {@link BeansWrapper#setUseModelCache(boolean)} (it means the same). */
     public void setUseModelCache(boolean useModelCache) {
         this.useModelCache = useModelCache;
     }

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/8ba7883e/src/main/java/org/apache/freemarker/core/model/impl/beans/ClassIntrospector.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/freemarker/core/model/impl/beans/ClassIntrospector.java b/src/main/java/org/apache/freemarker/core/model/impl/beans/ClassIntrospector.java
index 279fe31..2b33fb4 100644
--- a/src/main/java/org/apache/freemarker/core/model/impl/beans/ClassIntrospector.java
+++ b/src/main/java/org/apache/freemarker/core/model/impl/beans/ClassIntrospector.java
@@ -128,7 +128,6 @@ class ClassIntrospector {
     final boolean exposeFields;
     final MethodAppearanceFineTuner methodAppearanceFineTuner;
     final MethodSorter methodSorter;
-    final boolean bugfixed;
 
     /** See {@link #getHasSharedInstanceRestrictons()} */
     final private boolean hasSharedInstanceRestrictons;
@@ -177,7 +176,6 @@ class ClassIntrospector {
         exposeFields = builder.getExposeFields();
         methodAppearanceFineTuner = builder.getMethodAppearanceFineTuner();
         methodSorter = builder.getMethodSorter();
-        bugfixed = builder.isBugfixed();
 
         this.sharedLock = sharedLock;
 
@@ -345,7 +343,7 @@ class ClassIntrospector {
                             Object previous = introspData.get(methodKey);
                             if (previous instanceof Method) {
                                 // Overloaded method - replace Method with a OverloadedMethods
-                                OverloadedMethods overloadedMethods = new OverloadedMethods(bugfixed);
+                                OverloadedMethods overloadedMethods = new OverloadedMethods();
                                 overloadedMethods.addMethod((Method) previous);
                                 overloadedMethods.addMethod(method);
                                 introspData.put(methodKey, overloadedMethods);
@@ -433,7 +431,7 @@ class ClassIntrospector {
                 Constructor<?> ctor = ctors[0];
                 introspData.put(CONSTRUCTORS_KEY, new SimpleMethod(ctor, ctor.getParameterTypes()));
             } else if (ctors.length > 1) {
-                OverloadedMethods overloadedCtors = new OverloadedMethods(bugfixed);
+                OverloadedMethods overloadedCtors = new OverloadedMethods();
                 for (Constructor<?> ctor : ctors) {
                     overloadedCtors.addConstructor(ctor);
                 }

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/8ba7883e/src/main/java/org/apache/freemarker/core/model/impl/beans/ClassIntrospectorBuilder.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/freemarker/core/model/impl/beans/ClassIntrospectorBuilder.java b/src/main/java/org/apache/freemarker/core/model/impl/beans/ClassIntrospectorBuilder.java
index e1e6984..8716a3b 100644
--- a/src/main/java/org/apache/freemarker/core/model/impl/beans/ClassIntrospectorBuilder.java
+++ b/src/main/java/org/apache/freemarker/core/model/impl/beans/ClassIntrospectorBuilder.java
@@ -27,10 +27,9 @@ import java.util.Iterator;
 import java.util.Map;
 
 import org.apache.freemarker.core.Version;
+import org.apache.freemarker.core.util._NullArgumentException;
 
 final class ClassIntrospectorBuilder implements Cloneable {
-    
-    private final boolean bugfixed;
 
     private static final Map/*<PropertyAssignments, Reference<ClassIntrospector>>*/ INSTANCE_CACHE = new HashMap();
     private static final ReferenceQueue INSTANCE_CACHE_REF_QUEUE = new ReferenceQueue(); 
@@ -47,7 +46,6 @@ final class ClassIntrospectorBuilder implements Cloneable {
     // - If you add a new field, review all methods in this class, also the ClassIntrospector constructor
     
     ClassIntrospectorBuilder(ClassIntrospector ci) {
-        bugfixed = ci.bugfixed;
         exposureLevel = ci.exposureLevel;
         exposeFields = ci.exposeFields;
         methodAppearanceFineTuner = ci.methodAppearanceFineTuner;
@@ -57,8 +55,9 @@ final class ClassIntrospectorBuilder implements Cloneable {
     ClassIntrospectorBuilder(Version incompatibleImprovements) {
         // Warning: incompatibleImprovements must not affect this object at versions increments where there's no
         // change in the BeansWrapper.normalizeIncompatibleImprovements results. That is, this class may don't react
-        // to some version changes that affects BeansWrapper, but not the other way around. 
-        bugfixed = BeansWrapper.is2321Bugfixed(incompatibleImprovements);
+        // to some version changes that affects BeansWrapper, but not the other way around.
+        _NullArgumentException.check(incompatibleImprovements);
+        // Currently nothing depends on incompatibleImprovements
     }
     
     @Override
@@ -74,7 +73,6 @@ final class ClassIntrospectorBuilder implements Cloneable {
     public int hashCode() {
         final int prime = 31;
         int result = 1;
-        result = prime * result + (bugfixed ? 1231 : 1237);
         result = prime * result + (exposeFields ? 1231 : 1237);
         result = prime * result + exposureLevel;
         result = prime * result + System.identityHashCode(methodAppearanceFineTuner);
@@ -89,7 +87,6 @@ final class ClassIntrospectorBuilder implements Cloneable {
         if (getClass() != obj.getClass()) return false;
         ClassIntrospectorBuilder other = (ClassIntrospectorBuilder) obj;
         
-        if (bugfixed != other.bugfixed) return false;
         if (exposeFields != other.exposeFields) return false;
         if (exposureLevel != other.exposureLevel) return false;
         if (methodAppearanceFineTuner != other.methodAppearanceFineTuner) return false;
@@ -191,9 +188,5 @@ final class ClassIntrospectorBuilder implements Cloneable {
             return new ClassIntrospector(this, new Object(), true, false);
         }
     }
-
-    public boolean isBugfixed() {
-        return bugfixed;
-    }
     
 }
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/8ba7883e/src/main/java/org/apache/freemarker/core/model/impl/beans/OverloadedFixArgsMethods.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/freemarker/core/model/impl/beans/OverloadedFixArgsMethods.java b/src/main/java/org/apache/freemarker/core/model/impl/beans/OverloadedFixArgsMethods.java
index 672a69a..a1de28b 100644
--- a/src/main/java/org/apache/freemarker/core/model/impl/beans/OverloadedFixArgsMethods.java
+++ b/src/main/java/org/apache/freemarker/core/model/impl/beans/OverloadedFixArgsMethods.java
@@ -31,8 +31,8 @@ import org.apache.freemarker.core.model.TemplateModelException;
  */
 class OverloadedFixArgsMethods extends OverloadedMethodsSubset {
     
-    OverloadedFixArgsMethods(boolean bugfixed) {
-        super(bugfixed);
+    OverloadedFixArgsMethods() {
+        super();
     }
 
     @Override
@@ -84,15 +84,11 @@ class OverloadedFixArgsMethods extends OverloadedMethodsSubset {
         MaybeEmptyCallableMemberDescriptor maybeEmtpyMemberDesc = getMemberDescriptorForArgs(pojoArgs, false);
         if (maybeEmtpyMemberDesc instanceof CallableMemberDescriptor) {
             CallableMemberDescriptor memberDesc = (CallableMemberDescriptor) maybeEmtpyMemberDesc;
-            if (bugfixed) {
-                if (typeFlags != null) {
-                    // Note that overloaded method selection has already accounted for overflow errors when the method
-                    // was selected. So this forced conversion shouldn't cause such corruption. Except, conversion from
-                    // BigDecimal is allowed to overflow for backward-compatibility.
-                    forceNumberArgumentsToParameterTypes(pojoArgs, memberDesc.getParamTypes(), typeFlags);
-                }
-            } else {
-                BeansWrapper.coerceBigDecimals(memberDesc.getParamTypes(), pojoArgs);
+            if (typeFlags != null) {
+                // Note that overloaded method selection has already accounted for overflow errors when the method
+                // was selected. So this forced conversion shouldn't cause such corruption. Except, conversion from
+                // BigDecimal is allowed to overflow for backward-compatibility.
+                forceNumberArgumentsToParameterTypes(pojoArgs, memberDesc.getParamTypes(), typeFlags);
             }
             return new MemberAndArguments(memberDesc, pojoArgs);
         } else {

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/8ba7883e/src/main/java/org/apache/freemarker/core/model/impl/beans/OverloadedMethods.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/freemarker/core/model/impl/beans/OverloadedMethods.java b/src/main/java/org/apache/freemarker/core/model/impl/beans/OverloadedMethods.java
index 3ceebe1..978e9d6 100644
--- a/src/main/java/org/apache/freemarker/core/model/impl/beans/OverloadedMethods.java
+++ b/src/main/java/org/apache/freemarker/core/model/impl/beans/OverloadedMethods.java
@@ -46,11 +46,9 @@ final class OverloadedMethods {
 
     private final OverloadedMethodsSubset fixArgMethods;
     private OverloadedMethodsSubset varargMethods;
-    private final boolean bugfixed;
     
-    OverloadedMethods(boolean bugfixed) {
-        this.bugfixed = bugfixed;
-        fixArgMethods = new OverloadedFixArgsMethods(bugfixed);
+    OverloadedMethods() {
+        fixArgMethods = new OverloadedFixArgsMethods();
     }
     
     void addMethod(Method method) {
@@ -64,11 +62,11 @@ final class OverloadedMethods {
     }
     
     private void addCallableMemberDescriptor(ReflectionCallableMemberDescriptor memberDesc) {
-        // Note: "varargs" methods are always callable as fixed args, with a sequence (array) as the last parameter.
+        // Note: "varargs" methods are always callable as oms args, with a sequence (array) as the last parameter.
         fixArgMethods.addCallableMemberDescriptor(memberDesc);
         if (memberDesc.isVarargs()) {
             if (varargMethods == null) {
-                varargMethods = new OverloadedVarArgsMethods(bugfixed);
+                varargMethods = new OverloadedVarArgsMethods();
             }
             varargMethods.addCallableMemberDescriptor(memberDesc);
         }
@@ -76,7 +74,7 @@ final class OverloadedMethods {
     
     MemberAndArguments getMemberAndArguments(List/*<TemplateModel>*/ tmArgs, BeansWrapper unwrapper) 
     throws TemplateModelException {
-        // Try to find a fixed args match:
+        // Try to find a oms args match:
         MaybeEmptyMemberAndArguments fixArgsRes = fixArgMethods.getMemberAndArguments(tmArgs, unwrapper);
         if (fixArgsRes instanceof MemberAndArguments) {
             return (MemberAndArguments) fixArgsRes;
@@ -100,10 +98,6 @@ final class OverloadedMethods {
                         tmArgs),
                 "\nThe matching overload was searched among these members:\n",
                 memberListToString());
-        if (!bugfixed) {
-            edb.tip("You seem to use BeansWrapper with incompatibleImprovements set below 2.3.21. If you think this "
-                    + "error is unfounded, enabling 2.3.21 fixes may helps. See version history for more.");
-        }
         addMarkupBITipAfterNoNoMarchIfApplicable(edb, tmArgs);
         throw new _TemplateModelException(edb);
     }

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/8ba7883e/src/main/java/org/apache/freemarker/core/model/impl/beans/OverloadedMethodsSubset.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/freemarker/core/model/impl/beans/OverloadedMethodsSubset.java b/src/main/java/org/apache/freemarker/core/model/impl/beans/OverloadedMethodsSubset.java
index 3960d3c..8f41200 100644
--- a/src/main/java/org/apache/freemarker/core/model/impl/beans/OverloadedMethodsSubset.java
+++ b/src/main/java/org/apache/freemarker/core/model/impl/beans/OverloadedMethodsSubset.java
@@ -60,17 +60,14 @@ abstract class OverloadedMethodsSubset {
     private int[/*number of args*/][/*arg index*/] typeFlagsByParamCount;
     
     // TODO: This can cause memory-leak when classes are re-loaded. However, first the genericClassIntrospectionCache
-    // and such need to be fixed in this regard. 
+    // and such need to be oms in this regard. 
     private final Map/*<ArgumentTypes, MaybeEmptyCallableMemberDescriptor>*/ argTypesToMemberDescCache
             = new ConcurrentHashMap(6, 0.75f, 1);
     
     private final List/*<ReflectionCallableMemberDescriptor>*/ memberDescs = new LinkedList();
     
-    /** Enables 2.3.21 {@link BeansWrapper} incompatibleImprovements */
-    protected final boolean bugfixed;
-    
-    OverloadedMethodsSubset(boolean bugfixed) {
-        this.bugfixed = bugfixed;
+    OverloadedMethodsSubset() {
+        //
     }
     
     void addCallableMemberDescriptor(ReflectionCallableMemberDescriptor memberDesc) {
@@ -111,23 +108,19 @@ abstract class OverloadedMethodsSubset {
         }
 
         int[] typeFlagsByParamIdx = ALL_ZEROS_ARRAY;
-        if (bugfixed) {
-            // Fill typeFlagsByParamCount (if necessary)
-            for (int paramIdx = 0; paramIdx < paramCount; paramIdx++) {
-                final int typeFlags = TypeFlags.classToTypeFlags(prepedParamTypes[paramIdx]);
-                if (typeFlags != 0) {
-                    if (typeFlagsByParamIdx == ALL_ZEROS_ARRAY) {
-                        typeFlagsByParamIdx = new int[paramCount];
-                    }
-                    typeFlagsByParamIdx[paramIdx] = typeFlags;
+        // Fill typeFlagsByParamCount (if necessary)
+        for (int paramIdx = 0; paramIdx < paramCount; paramIdx++) {
+            final int typeFlags = TypeFlags.classToTypeFlags(prepedParamTypes[paramIdx]);
+            if (typeFlags != 0) {
+                if (typeFlagsByParamIdx == ALL_ZEROS_ARRAY) {
+                    typeFlagsByParamIdx = new int[paramCount];
                 }
+                typeFlagsByParamIdx[paramIdx] = typeFlags;
             }
-            mergeInTypesFlags(paramCount, typeFlagsByParamIdx);
         }
+        mergeInTypesFlags(paramCount, typeFlagsByParamIdx);
         
-        afterWideningUnwrappingHints(
-                bugfixed ? prepedParamTypes : unwrappingHintsByParamCount[paramCount],
-                typeFlagsByParamIdx);
+        afterWideningUnwrappingHints(prepedParamTypes, typeFlagsByParamIdx);
     }
     
     Class[][] getUnwrappingHintsByParamCount() {
@@ -137,7 +130,7 @@ abstract class OverloadedMethodsSubset {
     @SuppressFBWarnings(value="JLM_JSR166_UTILCONCURRENT_MONITORENTER",
             justification="Locks for member descriptor creation only")
     final MaybeEmptyCallableMemberDescriptor getMemberDescriptorForArgs(Object[] args, boolean varArg) {
-        ArgumentTypes argTypes = new ArgumentTypes(args, bugfixed);
+        ArgumentTypes argTypes = new ArgumentTypes(args);
         MaybeEmptyCallableMemberDescriptor memberDesc
                 = (MaybeEmptyCallableMemberDescriptor) argTypesToMemberDescCache.get(argTypes);
         if (memberDesc == null) {
@@ -180,54 +173,41 @@ abstract class OverloadedMethodsSubset {
         // This also means that the hint for (Integer, Integer) will be Integer, not just Number. This is consistent
         // with how non-overloaded method hints work.
         
-        if (bugfixed) {
-            // c1 primitive class to boxing class:
-            final boolean c1WasPrim; 
-            if (c1.isPrimitive()) {
-                c1 = _ClassUtil.primitiveClassToBoxingClass(c1);
-                c1WasPrim = true;
-            } else {
-                c1WasPrim = false;
-            }
-            
-            // c2 primitive class to boxing class:
-            final boolean c2WasPrim; 
-            if (c2.isPrimitive()) {
-                c2 = _ClassUtil.primitiveClassToBoxingClass(c2);
-                c2WasPrim = true;
-            } else {
-                c2WasPrim = false;
-            }
-    
-            if (c1 == c2) {
-                // If it was like int and Integer, boolean and Boolean, etc., we return the boxing type (as that's the
-                // less specific, because it allows null.)
-                // (If it was two equivalent primitives, we don't get here, because of the 1st line of the method.) 
-                return c1;
-            } else if (Number.class.isAssignableFrom(c1) && Number.class.isAssignableFrom(c2)) {
-                // We don't want the unwrapper to convert to a numerical super-type [*] as it's not yet known what the
-                // actual number type of the chosen method will be. We will postpone the actual numerical conversion
-                // until that, especially as some conversions (like fixed point to floating point) can be lossy.
-                // * Numerical super-type: Like long > int > short > byte.  
-                return Number.class;
-            } else if (c1WasPrim || c2WasPrim) {
-                // At this point these all stand:
-                // - At least one of them was primitive
-                // - No more than one of them was numerical
-                // - They don't have the same wrapper (boxing) class
-                return Object.class;
-            }
-            // Falls through
-        } else {  // old buggy behavior
-            if (c2.isPrimitive()) {
-                if (c2 == Byte.TYPE) c2 = Byte.class;
-                else if (c2 == Short.TYPE) c2 = Short.class;
-                else if (c2 == Character.TYPE) c2 = Character.class;
-                else if (c2 == Integer.TYPE) c2 = Integer.class;
-                else if (c2 == Float.TYPE) c2 = Float.class;
-                else if (c2 == Long.TYPE) c2 = Long.class;
-                else if (c2 == Double.TYPE) c2 = Double.class;
-            }
+        // c1 primitive class to boxing class:
+        final boolean c1WasPrim; 
+        if (c1.isPrimitive()) {
+            c1 = _ClassUtil.primitiveClassToBoxingClass(c1);
+            c1WasPrim = true;
+        } else {
+            c1WasPrim = false;
+        }
+        
+        // c2 primitive class to boxing class:
+        final boolean c2WasPrim; 
+        if (c2.isPrimitive()) {
+            c2 = _ClassUtil.primitiveClassToBoxingClass(c2);
+            c2WasPrim = true;
+        } else {
+            c2WasPrim = false;
+        }
+
+        if (c1 == c2) {
+            // If it was like int and Integer, boolean and Boolean, etc., we return the boxing type (as that's the
+            // less specific, because it allows null.)
+            // (If it was two equivalent primitives, we don't get here, because of the 1st line of the method.) 
+            return c1;
+        } else if (Number.class.isAssignableFrom(c1) && Number.class.isAssignableFrom(c2)) {
+            // We don't want the unwrapper to convert to a numerical super-type [*] as it's not yet known what the
+            // actual number type of the chosen method will be. We will postpone the actual numerical conversion
+            // until that, especially as some conversions (like oms point to floating point) can be lossy.
+            // * Numerical super-type: Like long > int > short > byte.  
+            return Number.class;
+        } else if (c1WasPrim || c2WasPrim) {
+            // At this point these all stand:
+            // - At least one of them was primitive
+            // - No more than one of them was numerical
+            // - They don't have the same wrapper (boxing) class
+            return Object.class;
         }
         
         // We never get to this point if buxfixed is true and any of these stands:
@@ -243,7 +223,7 @@ abstract class OverloadedMethodsSubset {
         }
         
         // Gather maximally specific elements. Yes, there can be more than one 
-        // thank to interfaces. I.e., if you call this method for String.class 
+        // because of interfaces. I.e., if you call this method for String.class 
         // and Number.class, you'll have Comparable, Serializable, and Object as 
         // maximal elements. 
         List max = new ArrayList();
@@ -270,35 +250,31 @@ abstract class OverloadedMethodsSubset {
         }
         
         if (max.size() > 1) {  // we have an ambiguity
-            if (bugfixed) {
-                // Find the non-interface class
-                for (Iterator it = max.iterator(); it.hasNext(); ) {
-                    Class maxCl = (Class) it.next();
-                    if (!maxCl.isInterface()) {
-                        if (maxCl != Object.class) {  // This actually shouldn't ever happen, but to be sure...
-                            // If it's not Object, we use it as the most specific
-                            return maxCl;
-                        } else {
-                            // Otherwise remove Object, and we will try with the interfaces 
-                            it.remove();
-                        }
+            // Find the non-interface class
+            for (Iterator it = max.iterator(); it.hasNext(); ) {
+                Class maxCl = (Class) it.next();
+                if (!maxCl.isInterface()) {
+                    if (maxCl != Object.class) {  // This actually shouldn't ever happen, but to be sure...
+                        // If it's not Object, we use it as the most specific
+                        return maxCl;
+                    } else {
+                        // Otherwise remove Object, and we will try with the interfaces 
+                        it.remove();
                     }
                 }
-                
-                // At this point we only have interfaces left.
-                // Try removing interfaces about which we know that they are useless as unwrapping hints:
-                max.remove(Cloneable.class);
-                if (max.size() > 1) {  // Still have an ambiguity...
-                    max.remove(Serializable.class);
-                    if (max.size() > 1) {  // Still had an ambiguity...
-                        max.remove(Comparable.class);
-                        if (max.size() > 1) {
-                            return Object.class; // Still had an ambiguity... no luck.
-                        }
+            }
+            
+            // At this point we only have interfaces left.
+            // Try removing interfaces about which we know that they are useless as unwrapping hints:
+            max.remove(Cloneable.class);
+            if (max.size() > 1) {  // Still have an ambiguity...
+                max.remove(Serializable.class);
+                if (max.size() > 1) {  // Still had an ambiguity...
+                    max.remove(Comparable.class);
+                    if (max.size() > 1) {
+                        return Object.class; // Still had an ambiguity... no luck.
                     }
                 }
-            } else {
-                return Object.class;
             }
         }
         
@@ -318,7 +294,6 @@ abstract class OverloadedMethodsSubset {
 
     /**
      * Updates the content of the {@link #typeFlagsByParamCount} field with the parameter type flags of a method.
-     * Don't call this when {@link #bugfixed} is {@code false}! 
      * 
      * @param dstParamCount The parameter count for which we want to merge in the type flags 
      * @param srcTypeFlagsByParamIdx If shorter than {@code dstParamCount}, its last item will be repeated until
@@ -415,8 +390,7 @@ abstract class OverloadedMethodsSubset {
                 // If arg isn't a number, we can't do any conversions anyway, regardless of the param type.
                 if (arg instanceof Number) {
                     final Class targetType = paramTypes[paramTypeIdx];
-                    final Number convertedArg = BeansWrapper.forceUnwrappedNumberToType(
-                            (Number) arg, targetType, bugfixed);
+                    final Number convertedArg = BeansWrapper.forceUnwrappedNumberToType((Number) arg, targetType);
                     if (convertedArg != null) {
                         args[argIdx] = convertedArg;
                     }

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/8ba7883e/src/main/java/org/apache/freemarker/core/model/impl/beans/OverloadedVarArgsMethods.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/freemarker/core/model/impl/beans/OverloadedVarArgsMethods.java b/src/main/java/org/apache/freemarker/core/model/impl/beans/OverloadedVarArgsMethods.java
index e5453a0..f4fa207 100644
--- a/src/main/java/org/apache/freemarker/core/model/impl/beans/OverloadedVarArgsMethods.java
+++ b/src/main/java/org/apache/freemarker/core/model/impl/beans/OverloadedVarArgsMethods.java
@@ -33,8 +33,8 @@ import org.apache.freemarker.core.model.TemplateModelException;
  */
 class OverloadedVarArgsMethods extends OverloadedMethodsSubset {
 
-    OverloadedVarArgsMethods(boolean bugfixed) {
-        super(bugfixed);
+    OverloadedVarArgsMethods() {
+        super();
     }
     
     /**
@@ -133,9 +133,7 @@ class OverloadedVarArgsMethods extends OverloadedMethodsSubset {
             }
         }
         
-        if (bugfixed) {
-            mergeInTypesFlags(paramCountOfWidened, wideningTypeFlags);
-        }
+        mergeInTypesFlags(paramCountOfWidened, wideningTypeFlags);
     }
     
     @Override
@@ -191,15 +189,11 @@ class OverloadedVarArgsMethods extends OverloadedMethodsSubset {
             } else {
                 return EmptyMemberAndArguments.noCompatibleOverload(((Integer) argsOrErrorIdx).intValue());
             }
-            if (bugfixed) {
-                if (typesFlags != null) {
-                    // Note that overloaded method selection has already accounted for overflow errors when the method
-                    // was selected. So this forced conversion shouldn't cause such corruption. Except, conversion from
-                    // BigDecimal is allowed to overflow for backward-compatibility.
-                    forceNumberArgumentsToParameterTypes(pojoArgsWithArray, memberDesc.getParamTypes(), typesFlags);
-                }
-            } else {
-                BeansWrapper.coerceBigDecimals(memberDesc.getParamTypes(), pojoArgsWithArray);
+            if (typesFlags != null) {
+                // Note that overloaded method selection has already accounted for overflow errors when the method
+                // was selected. So this forced conversion shouldn't cause such corruption. Except, conversion from
+                // BigDecimal is allowed to overflow for backward-compatibility.
+                forceNumberArgumentsToParameterTypes(pojoArgsWithArray, memberDesc.getParamTypes(), typesFlags);
             }
             return new MemberAndArguments(memberDesc, pojoArgsWithArray);
         } else {

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/8ba7883e/src/main/java/org/apache/freemarker/core/model/impl/beans/StaticModel.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/freemarker/core/model/impl/beans/StaticModel.java b/src/main/java/org/apache/freemarker/core/model/impl/beans/StaticModel.java
index 024e454..a302d8b 100644
--- a/src/main/java/org/apache/freemarker/core/model/impl/beans/StaticModel.java
+++ b/src/main/java/org/apache/freemarker/core/model/impl/beans/StaticModel.java
@@ -142,7 +142,7 @@ final class StaticModel implements TemplateHashModelEx {
                     String name = method.getName();
                     Object obj = map.get(name);
                     if (obj instanceof Method) {
-                        OverloadedMethods overloadedMethods = new OverloadedMethods(wrapper.is2321Bugfixed());
+                        OverloadedMethods overloadedMethods = new OverloadedMethods();
                         overloadedMethods.addMethod((Method) obj);
                         overloadedMethods.addMethod(method);
                         map.put(name, overloadedMethods);

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/8ba7883e/src/main/java/org/apache/freemarker/core/model/impl/beans/_BeansAPI.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/freemarker/core/model/impl/beans/_BeansAPI.java b/src/main/java/org/apache/freemarker/core/model/impl/beans/_BeansAPI.java
index a8c6680..9bc4880 100644
--- a/src/main/java/org/apache/freemarker/core/model/impl/beans/_BeansAPI.java
+++ b/src/main/java/org/apache/freemarker/core/model/impl/beans/_BeansAPI.java
@@ -60,7 +60,7 @@ public class _BeansAPI {
             throws NoSuchMethodException {
         if (args == null) args = _CollectionUtil.EMPTY_OBJECT_ARRAY;
         
-        final ArgumentTypes argTypes = new ArgumentTypes(args, true);
+        final ArgumentTypes argTypes = new ArgumentTypes(args);
         final List<ReflectionCallableMemberDescriptor> fixedArgMemberDescs
                 = new ArrayList<>();
         final List<ReflectionCallableMemberDescriptor> varArgsMemberDescs

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/8ba7883e/src/main/java/org/apache/freemarker/core/model/impl/beans/_MethodUtil.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/freemarker/core/model/impl/beans/_MethodUtil.java b/src/main/java/org/apache/freemarker/core/model/impl/beans/_MethodUtil.java
index 98b8dec..1dd995c 100644
--- a/src/main/java/org/apache/freemarker/core/model/impl/beans/_MethodUtil.java
+++ b/src/main/java/org/apache/freemarker/core/model/impl/beans/_MethodUtil.java
@@ -63,6 +63,7 @@ public final class _MethodUtil {
      *               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;

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/8ba7883e/src/main/java/org/apache/freemarker/core/templateresolver/impl/DefaultTemplateNameFormat.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/freemarker/core/templateresolver/impl/DefaultTemplateNameFormat.java b/src/main/java/org/apache/freemarker/core/templateresolver/impl/DefaultTemplateNameFormat.java
index c3fda79..9f366f1 100644
--- a/src/main/java/org/apache/freemarker/core/templateresolver/impl/DefaultTemplateNameFormat.java
+++ b/src/main/java/org/apache/freemarker/core/templateresolver/impl/DefaultTemplateNameFormat.java
@@ -64,7 +64,7 @@ import org.apache.freemarker.core.util._StringUtil;
  * {@code foo//bar///baaz.ftl} is normalized to {@code foo/bar/baaz.ftl}. (In general, 0 long step names aren't
  * possible anymore.)</li>
  * 
- * <li>The {@code ".."} bugs of the legacy normalizer are fixed: {@code ".."} steps has removed the preceding
+ * <li>The {@code ".."} bugs of the legacy normalizer are oms: {@code ".."} steps has removed the preceding
  * {@code "."} or {@code "*"} or scheme steps, not treating them specially as they should be. Now these work as
  * expected. Examples: {@code "a/./../c"} has become to {@code "a/c"}, now it will be {@code "c"}; {@code "a/b/*}
  * {@code /../c"} has become to {@code "a/b/c"}, now it will be {@code "a/*}{@code /c"}; {@code "scheme://.."} has

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/8ba7883e/src/main/java/org/apache/freemarker/core/templateresolver/impl/DefaultTemplateResolver.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/freemarker/core/templateresolver/impl/DefaultTemplateResolver.java b/src/main/java/org/apache/freemarker/core/templateresolver/impl/DefaultTemplateResolver.java
index 3763d35..97e02cb 100644
--- a/src/main/java/org/apache/freemarker/core/templateresolver/impl/DefaultTemplateResolver.java
+++ b/src/main/java/org/apache/freemarker/core/templateresolver/impl/DefaultTemplateResolver.java
@@ -35,7 +35,6 @@ import org.apache.freemarker.core.Configuration;
 import org.apache.freemarker.core.Template;
 import org.apache.freemarker.core.TemplateNotFoundException;
 import org.apache.freemarker.core._CoreLogs;
-import org.apache.freemarker.core._TemplateAPI;
 import org.apache.freemarker.core.ast.BugException;
 import org.apache.freemarker.core.ast.MarkReleaserTemplateSpecifiedEncodingHandler;
 import org.apache.freemarker.core.ast.TemplateConfiguration;
@@ -99,30 +98,6 @@ public class DefaultTemplateResolver extends TemplateResolver {
     private boolean localizedLookup = true;
 
     private Configuration config;
-
-    /**
-     * Same as {@link #DefaultTemplateResolver(TemplateLoader, CacheStorage, Configuration)} with a new {@link SoftCacheStorage}
-     * as the 2nd parameter.
-     * 
-     * @since 2.3.21
-     */
-    public DefaultTemplateResolver(TemplateLoader templateLoader, Configuration config) {
-        this(templateLoader, _TemplateAPI.createDefaultCacheStorage(Configuration.VERSION_2_3_0), config);
-    }
-    
-    /**
-     * Same as
-     * {@link #DefaultTemplateResolver(TemplateLoader, CacheStorage, TemplateLookupStrategy, TemplateNameFormat, Configuration)}
-     * with {@link DefaultTemplateLookupStrategy#INSTANCE} and {@link DefaultTemplateNameFormatFM2#INSTANCE}.
-     * 
-     * @since 2.3.21
-     */
-    public DefaultTemplateResolver(TemplateLoader templateLoader, CacheStorage cacheStorage, Configuration config) {
-        this(templateLoader, cacheStorage,
-                _TemplateAPI.getDefaultTemplateLookupStrategy(Configuration.VERSION_2_3_0),
-                _TemplateAPI.getDefaultTemplateNameFormat(Configuration.VERSION_2_3_0),
-                config);
-    }
     
     /**
      * Same as
@@ -255,16 +230,7 @@ public class DefaultTemplateResolver extends TemplateResolver {
         _NullArgumentException.check("locale", locale);
         _NullArgumentException.check("encoding", encoding);
         
-        try {
-            name = templateNameFormat.normalizeRootBasedName(name);
-        } catch (MalformedTemplateNameException e) {
-            // If we don't have to emulate backward compatible behavior, then just rethrow it: 
-            if (templateNameFormat != DefaultTemplateNameFormatFM2.INSTANCE
-                    || config.getIncompatibleImprovements().intValue() >= _TemplateAPI.VERSION_INT_2_4_0) {
-                throw e;
-            }
-            return new GetTemplateResult(null, e);
-        }
+        name = templateNameFormat.normalizeRootBasedName(name);
         
         if (templateLoader == null) {
             return new GetTemplateResult(name, "The TemplateLoader (and TemplateLoader2) was null.");

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/8ba7883e/src/main/java/org/apache/freemarker/servlet/InitParamParser.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/freemarker/servlet/InitParamParser.java b/src/main/java/org/apache/freemarker/servlet/InitParamParser.java
index ee2b78d..2bfcba3 100644
--- a/src/main/java/org/apache/freemarker/servlet/InitParamParser.java
+++ b/src/main/java/org/apache/freemarker/servlet/InitParamParser.java
@@ -28,7 +28,6 @@ import java.util.regex.Pattern;
 import javax.servlet.ServletContext;
 
 import org.apache.freemarker.core.Configuration;
-import org.apache.freemarker.core._TemplateAPI;
 import org.apache.freemarker.core.ast._ObjectBuilderSettingEvaluator;
 import org.apache.freemarker.core.ast._SettingEvaluationEnvironment;
 import org.apache.freemarker.core.templateresolver.TemplateLoader;
@@ -80,10 +79,9 @@ final class InitParamParser {
         } else if (pureTemplatePath.startsWith(TEMPLATE_PATH_PREFIX_FILE)) {
             String filePath = pureTemplatePath.substring(TEMPLATE_PATH_PREFIX_FILE.length());
             templateLoader = new FileTemplateLoader(new File(filePath));
-        } else if (pureTemplatePath.startsWith("[")
-                && cfg.getIncompatibleImprovements().intValue() >= _TemplateAPI.VERSION_INT_2_3_22) {
+        } else if (pureTemplatePath.startsWith("[")) {
             if (!pureTemplatePath.endsWith("]")) {
-                // B.C. constraint: Can't throw any checked exceptions.
+                // B.C. constraint: Can't throw any checked exceptions. [FM3] Fix it?
                 throw new TemplatePathParsingException("Failed to parse template path; closing \"]\" is missing.");
             }
             String commaSepItems = pureTemplatePath.substring(1, pureTemplatePath.length() - 1).trim();
@@ -94,9 +92,9 @@ final class InitParamParser {
                 templateLoaders[i] = createTemplateLoader(pathItem, cfg, classLoaderClass, srvCtx);
             }
             templateLoader = new MultiTemplateLoader(templateLoaders);
-        } else if (pureTemplatePath.startsWith("{")
-                && cfg.getIncompatibleImprovements().intValue() >= _TemplateAPI.VERSION_INT_2_3_22) {
-            throw new TemplatePathParsingException("Template paths starting with \"{\" are reseved for future purposes");
+        } else if (pureTemplatePath.startsWith("{")) {
+            throw new TemplatePathParsingException(
+                    "Template paths starting with \"{\" are reseved for future purposes");
         } else {
             templateLoader = new WebAppTemplateLoader(srvCtx, pureTemplatePath);
         }



Mime
View raw message