You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@velocity.apache.org by cb...@apache.org on 2018/10/17 11:42:10 UTC

svn commit: r1844087 - in /velocity/engine/branches/VELOCITY-892/velocity-engine-core/src: main/java/org/apache/velocity/util/introspection/ main/resources/org/apache/velocity/runtime/defaults/ test/java/org/apache/velocity/test/util/introspection/

Author: cbrisson
Date: Wed Oct 17 11:42:10 2018
New Revision: 1844087

URL: http://svn.apache.org/viewvc?rev=1844087&view=rev
Log:
[VELOCITY-892] Handling conversions towards Type (work in progress)

Added:
    velocity/engine/branches/VELOCITY-892/velocity-engine-core/src/main/java/org/apache/velocity/util/introspection/TypeConversionHandler.java
    velocity/engine/branches/VELOCITY-892/velocity-engine-core/src/main/java/org/apache/velocity/util/introspection/TypeConversionHandlerImpl.java
Removed:
    velocity/engine/branches/VELOCITY-892/velocity-engine-core/src/main/java/org/apache/velocity/util/introspection/ConversionHandlerImpl.java
Modified:
    velocity/engine/branches/VELOCITY-892/velocity-engine-core/src/main/java/org/apache/velocity/util/introspection/ClassMap.java
    velocity/engine/branches/VELOCITY-892/velocity-engine-core/src/main/java/org/apache/velocity/util/introspection/ConversionHandler.java
    velocity/engine/branches/VELOCITY-892/velocity-engine-core/src/main/java/org/apache/velocity/util/introspection/IntrospectionUtils.java
    velocity/engine/branches/VELOCITY-892/velocity-engine-core/src/main/java/org/apache/velocity/util/introspection/Introspector.java
    velocity/engine/branches/VELOCITY-892/velocity-engine-core/src/main/java/org/apache/velocity/util/introspection/IntrospectorBase.java
    velocity/engine/branches/VELOCITY-892/velocity-engine-core/src/main/java/org/apache/velocity/util/introspection/IntrospectorCache.java
    velocity/engine/branches/VELOCITY-892/velocity-engine-core/src/main/java/org/apache/velocity/util/introspection/MethodMap.java
    velocity/engine/branches/VELOCITY-892/velocity-engine-core/src/main/java/org/apache/velocity/util/introspection/UberspectImpl.java
    velocity/engine/branches/VELOCITY-892/velocity-engine-core/src/main/resources/org/apache/velocity/runtime/defaults/velocity.properties
    velocity/engine/branches/VELOCITY-892/velocity-engine-core/src/test/java/org/apache/velocity/test/util/introspection/ConversionHandlerTestCase.java

Modified: velocity/engine/branches/VELOCITY-892/velocity-engine-core/src/main/java/org/apache/velocity/util/introspection/ClassMap.java
URL: http://svn.apache.org/viewvc/velocity/engine/branches/VELOCITY-892/velocity-engine-core/src/main/java/org/apache/velocity/util/introspection/ClassMap.java?rev=1844087&r1=1844086&r2=1844087&view=diff
==============================================================================
--- velocity/engine/branches/VELOCITY-892/velocity-engine-core/src/main/java/org/apache/velocity/util/introspection/ClassMap.java (original)
+++ velocity/engine/branches/VELOCITY-892/velocity-engine-core/src/main/java/org/apache/velocity/util/introspection/ClassMap.java Wed Oct 17 11:42:10 2018
@@ -73,7 +73,7 @@ public class ClassMap
      * @param conversionHandler conversion handler
      * @since 2.0
      */
-    public ClassMap(final Class clazz, final Logger log, final ConversionHandler conversionHandler)
+    public ClassMap(final Class clazz, final Logger log, final TypeConversionHandler conversionHandler)
     {
         this.clazz = clazz;
         this.log = log;
@@ -121,7 +121,7 @@ public class ClassMap
      * are taken from all the public methods
      * that our class, its parents and their implemented interfaces provide.
      */
-    private MethodCache createMethodCache(ConversionHandler conversionHandler)
+    private MethodCache createMethodCache(TypeConversionHandler conversionHandler)
     {
         MethodCache methodCache = new MethodCache(log, conversionHandler);
 	//
@@ -231,7 +231,7 @@ public class ClassMap
         /** Map of methods that are searchable according to method parameters to find a match */
         private final MethodMap methodMap;
 
-        private MethodCache(Logger log, ConversionHandler conversionHandler)
+        private MethodCache(Logger log, TypeConversionHandler conversionHandler)
         {
             this.log = log;
             methodMap = new MethodMap(conversionHandler);

Modified: velocity/engine/branches/VELOCITY-892/velocity-engine-core/src/main/java/org/apache/velocity/util/introspection/ConversionHandler.java
URL: http://svn.apache.org/viewvc/velocity/engine/branches/VELOCITY-892/velocity-engine-core/src/main/java/org/apache/velocity/util/introspection/ConversionHandler.java?rev=1844087&r1=1844086&r2=1844087&view=diff
==============================================================================
--- velocity/engine/branches/VELOCITY-892/velocity-engine-core/src/main/java/org/apache/velocity/util/introspection/ConversionHandler.java (original)
+++ velocity/engine/branches/VELOCITY-892/velocity-engine-core/src/main/java/org/apache/velocity/util/introspection/ConversionHandler.java Wed Oct 17 11:42:10 2018
@@ -27,13 +27,17 @@ package org.apache.velocity.util.introsp
  *
  * @author <a href="mailto:claude.brisson@gmail.com">Claude Brisson</a>
  * @version $Id: ConversionHandler.java $
+ * @deprecated use {@link TypeConversionHandler}
+ * @see TypeConversionHandler
  * @since 2.0
  */
 
+@Deprecated
 public interface ConversionHandler
 {
     /**
      * Check to see if the conversion can be done using an explicit conversion
+     *
      * @param formal expected formal type
      * @param actual provided argument type
      * @return null if no conversion is needed, or the appropriate Converter object
@@ -55,10 +59,11 @@ public interface ConversionHandler
     /**
      * Add the given converter to the handler. Implementation should be thread-safe.
      *
-     * @param formal expected formal type
-     * @param actual provided argument type
+     * @param formal    expected formal type
+     * @param actual    provided argument type
      * @param converter converter
      * @since 2.0
      */
     void addConverter(Class formal, Class actual, Converter converter);
 }
+

Modified: velocity/engine/branches/VELOCITY-892/velocity-engine-core/src/main/java/org/apache/velocity/util/introspection/IntrospectionUtils.java
URL: http://svn.apache.org/viewvc/velocity/engine/branches/VELOCITY-892/velocity-engine-core/src/main/java/org/apache/velocity/util/introspection/IntrospectionUtils.java?rev=1844087&r1=1844086&r2=1844087&view=diff
==============================================================================
--- velocity/engine/branches/VELOCITY-892/velocity-engine-core/src/main/java/org/apache/velocity/util/introspection/IntrospectionUtils.java (original)
+++ velocity/engine/branches/VELOCITY-892/velocity-engine-core/src/main/java/org/apache/velocity/util/introspection/IntrospectionUtils.java Wed Oct 17 11:42:10 2018
@@ -19,6 +19,9 @@ package org.apache.velocity.util.introsp
  * under the License.
  */
 
+import org.apache.commons.lang3.reflect.TypeUtils;
+
+import java.lang.reflect.Type;
 import java.util.HashMap;
 import java.util.Map;
 import java.util.Set;
@@ -87,7 +90,6 @@ public class IntrospectionUtils
      *
      */
 
-
     /**
      * Determines whether a type represented by a class object is
      * convertible to another type represented by a class object using a
@@ -108,92 +110,100 @@ public class IntrospectionUtils
      * type or an object type of a primitive type that can be converted to
      * the formal type.
      */
-    public static boolean isMethodInvocationConvertible(Class formal,
+    public static boolean isMethodInvocationConvertible(Type formal,
                                                         Class actual,
                                                         boolean possibleVarArg)
     {
-        /* if it's a null, it means the arg was null */
-        if (actual == null)
+        if (formal instanceof Class)
         {
-            return !formal.isPrimitive();
-        }
+            Class formalClass = (Class)formal;
+            /* if it's a null, it means the arg was null */
+            if (actual == null)
+            {
+                return !formalClass.isPrimitive();
+            }
 
-        /* Check for identity or widening reference conversion */
-        if (formal.isAssignableFrom(actual))
-        {
-            return true;
-        }
+            /* Check for identity or widening reference conversion */
+            if (formalClass.isAssignableFrom(actual))
+            {
+                return true;
+            }
 
-        /* 2.0: Since MethodMap's comparison functions now use this method with potentially reversed arguments order,
-         * actual can be a primitive type. */
+            /* 2.0: Since MethodMap's comparison functions now use this method with potentially reversed arguments order,
+             * actual can be a primitive type. */
 
-        /* Check for boxing */
-        if (!formal.isPrimitive() && actual.isPrimitive())
-        {
-            Class boxed = boxingMap.get(actual);
-            if (boxed != null && boxed == formal || formal.isAssignableFrom(boxed)) return true;
-        }
+            /* Check for boxing */
+            if (!formalClass.isPrimitive() && actual.isPrimitive())
+            {
+                Class boxed = boxingMap.get(actual);
+                if (boxed != null && boxed == formalClass || formalClass.isAssignableFrom(boxed)) return true;
+            }
 
-        if (formal.isPrimitive())
-        {
-            if (actual.isPrimitive())
+            if (formalClass.isPrimitive())
             {
-                /* check for widening primitive conversion */
-                if (formal == Short.TYPE && actual == Byte.TYPE)
-                    return true;
-                if (formal == Integer.TYPE && (
+                if (actual.isPrimitive())
+                {
+                    /* check for widening primitive conversion */
+                    if (formalClass == Short.TYPE && actual == Byte.TYPE)
+                        return true;
+                    if (formalClass == Integer.TYPE && (
                         actual == Byte.TYPE || actual == Short.TYPE))
-                    return true;
-                if (formal == Long.TYPE && (
+                        return true;
+                    if (formalClass == Long.TYPE && (
                         actual == Byte.TYPE || actual == Short.TYPE || actual == Integer.TYPE))
-                    return true;
-                if (formal == Float.TYPE && (
+                        return true;
+                    if (formalClass == Float.TYPE && (
                         actual == Byte.TYPE || actual == Short.TYPE || actual == Integer.TYPE ||
-                                actual == Long.TYPE))
-                    return true;
-                if (formal == Double.TYPE && (
+                            actual == Long.TYPE))
+                        return true;
+                    if (formalClass == Double.TYPE && (
                         actual == Byte.TYPE || actual == Short.TYPE || actual == Integer.TYPE ||
-                                actual == Long.TYPE || actual == Float.TYPE))
-                    return true;
-            }
-            else
-            {
-                /* Check for unboxing with widening primitive conversion. */
-                if (formal == Boolean.TYPE && actual == Boolean.class)
-                    return true;
-                if (formal == Character.TYPE && actual == Character.class)
-                    return true;
-                if (formal == Byte.TYPE && actual == Byte.class)
-                    return true;
-                if (formal == Short.TYPE && (actual == Short.class || actual == Byte.class))
-                    return true;
-                if (formal == Integer.TYPE && (actual == Integer.class || actual == Short.class ||
+                            actual == Long.TYPE || actual == Float.TYPE))
+                        return true;
+                } else
+                {
+                    /* Check for unboxing with widening primitive conversion. */
+                    if (formalClass == Boolean.TYPE && actual == Boolean.class)
+                        return true;
+                    if (formalClass == Character.TYPE && actual == Character.class)
+                        return true;
+                    if (formalClass == Byte.TYPE && actual == Byte.class)
+                        return true;
+                    if (formalClass == Short.TYPE && (actual == Short.class || actual == Byte.class))
+                        return true;
+                    if (formalClass == Integer.TYPE && (actual == Integer.class || actual == Short.class ||
                         actual == Byte.class))
-                    return true;
-                if (formal == Long.TYPE && (actual == Long.class || actual == Integer.class ||
+                        return true;
+                    if (formalClass == Long.TYPE && (actual == Long.class || actual == Integer.class ||
                         actual == Short.class || actual == Byte.class))
-                    return true;
-                if (formal == Float.TYPE && (actual == Float.class || actual == Long.class ||
+                        return true;
+                    if (formalClass == Float.TYPE && (actual == Float.class || actual == Long.class ||
                         actual == Integer.class || actual == Short.class || actual == Byte.class))
-                    return true;
-                if (formal == Double.TYPE && (actual == Double.class || actual == Float.class ||
+                        return true;
+                    if (formalClass == Double.TYPE && (actual == Double.class || actual == Float.class ||
                         actual == Long.class || actual == Integer.class || actual == Short.class ||
                         actual == Byte.class))
-                    return true;
+                        return true;
+                }
             }
-        }
 
-        /* Check for vararg conversion. */
-        if (possibleVarArg && formal.isArray())
-        {
-            if (actual.isArray())
+            /* Check for vararg conversion. */
+            if (possibleVarArg && formalClass.isArray())
             {
-                actual = actual.getComponentType();
+                if (actual.isArray())
+                {
+                    actual = actual.getComponentType();
+                }
+                return isMethodInvocationConvertible(formalClass.getComponentType(),
+                    actual, false);
             }
-            return isMethodInvocationConvertible(formal.getComponentType(),
-                                                 actual, false);
+            return false;
+        }
+        else
+        {
+            // no distinction between strict and implicit, not a big deal in this case
+            return TypeUtils.isAssignable(actual, formal);
         }
-        return false;
     }
 
     /**
@@ -212,55 +222,64 @@ public class IntrospectionUtils
      * or formal and actual are both primitive types and actual can be
      * subject to widening conversion to formal.
      */
-    public static boolean isStrictMethodInvocationConvertible(Class formal,
+    public static boolean isStrictMethodInvocationConvertible(Type formal,
                                                               Class actual,
                                                               boolean possibleVarArg)
     {
-        /* Check for nullity */
-        if (actual == null)
-        {
-            return !formal.isPrimitive();
-        }
-
-        /* Check for identity or widening reference conversion */
-        if(formal.isAssignableFrom(actual))
+        if (formal instanceof Class)
         {
-            return true;
-        }
+            Class formalClass = (Class) formal;
+            /* Check for nullity */
+            if (actual == null)
+            {
+                return !formalClass.isPrimitive();
+            }
 
-        /* Check for widening primitive conversion. */
-        if(formal.isPrimitive())
-        {
-            if(formal == Short.TYPE && (actual == Byte.TYPE))
-                return true;
-            if(formal == Integer.TYPE &&
-               (actual == Short.TYPE || actual == Byte.TYPE))
-                return true;
-            if(formal == Long.TYPE &&
-               (actual == Integer.TYPE || actual == Short.TYPE ||
-                actual == Byte.TYPE))
-                return true;
-            if(formal == Float.TYPE &&
-               (actual == Long.TYPE || actual == Integer.TYPE ||
-                actual == Short.TYPE || actual == Byte.TYPE))
-                return true;
-            if(formal == Double.TYPE &&
-               (actual == Float.TYPE || actual == Long.TYPE ||
-                actual == Integer.TYPE || actual == Short.TYPE ||
-                actual == Byte.TYPE))
+            /* Check for identity or widening reference conversion */
+            if (formalClass.isAssignableFrom(actual))
+            {
                 return true;
-        }
+            }
 
-        /* Check for vararg conversion. */
-        if (possibleVarArg && formal.isArray())
-        {
-            if (actual.isArray())
+            /* Check for widening primitive conversion. */
+            if (formalClass.isPrimitive())
+            {
+                if (formal == Short.TYPE && (actual == Byte.TYPE))
+                    return true;
+                if (formal == Integer.TYPE &&
+                    (actual == Short.TYPE || actual == Byte.TYPE))
+                    return true;
+                if (formal == Long.TYPE &&
+                    (actual == Integer.TYPE || actual == Short.TYPE ||
+                        actual == Byte.TYPE))
+                    return true;
+                if (formal == Float.TYPE &&
+                    (actual == Long.TYPE || actual == Integer.TYPE ||
+                        actual == Short.TYPE || actual == Byte.TYPE))
+                    return true;
+                if (formal == Double.TYPE &&
+                    (actual == Float.TYPE || actual == Long.TYPE ||
+                        actual == Integer.TYPE || actual == Short.TYPE ||
+                        actual == Byte.TYPE))
+                    return true;
+            }
+
+            /* Check for vararg conversion. */
+            if (possibleVarArg && formalClass.isArray())
             {
-                actual = actual.getComponentType();
+                if (actual.isArray())
+                {
+                    actual = actual.getComponentType();
+                }
+                return isStrictMethodInvocationConvertible(formalClass.getComponentType(),
+                    actual, false);
             }
-            return isStrictMethodInvocationConvertible(formal.getComponentType(),
-                                                       actual, false);
+            return false;
+        }
+        else
+        {
+            // no distinction between strict and implicit, not a big deal in this case
+            return TypeUtils.isAssignable(actual, formal);
         }
-        return false;
     }
 }

Modified: velocity/engine/branches/VELOCITY-892/velocity-engine-core/src/main/java/org/apache/velocity/util/introspection/Introspector.java
URL: http://svn.apache.org/viewvc/velocity/engine/branches/VELOCITY-892/velocity-engine-core/src/main/java/org/apache/velocity/util/introspection/Introspector.java?rev=1844087&r1=1844086&r2=1844087&view=diff
==============================================================================
--- velocity/engine/branches/VELOCITY-892/velocity-engine-core/src/main/java/org/apache/velocity/util/introspection/Introspector.java (original)
+++ velocity/engine/branches/VELOCITY-892/velocity-engine-core/src/main/java/org/apache/velocity/util/introspection/Introspector.java Wed Oct 17 11:42:10 2018
@@ -68,7 +68,7 @@ public class Introspector extends Intros
      * @param conversionHandler conversion handler
      * @since 2.0
      */
-    public Introspector(final Logger log, ConversionHandler conversionHandler)
+    public Introspector(final Logger log, TypeConversionHandler conversionHandler)
     {
         super(log, conversionHandler);
     }

Modified: velocity/engine/branches/VELOCITY-892/velocity-engine-core/src/main/java/org/apache/velocity/util/introspection/IntrospectorBase.java
URL: http://svn.apache.org/viewvc/velocity/engine/branches/VELOCITY-892/velocity-engine-core/src/main/java/org/apache/velocity/util/introspection/IntrospectorBase.java?rev=1844087&r1=1844086&r2=1844087&view=diff
==============================================================================
--- velocity/engine/branches/VELOCITY-892/velocity-engine-core/src/main/java/org/apache/velocity/util/introspection/IntrospectorBase.java (original)
+++ velocity/engine/branches/VELOCITY-892/velocity-engine-core/src/main/java/org/apache/velocity/util/introspection/IntrospectorBase.java Wed Oct 17 11:42:10 2018
@@ -62,17 +62,13 @@ public abstract class IntrospectorBase
     /** The Introspector Cache */
     private final IntrospectorCache introspectorCache;
 
-    /** The Conversion handler */
-    private final ConversionHandler conversionHandler;
-
     /**
      * C'tor.
      */
-    protected IntrospectorBase(final Logger log, final ConversionHandler conversionHandler)
+    protected IntrospectorBase(final Logger log, final TypeConversionHandler conversionHandler)
     {
         this.log = log;
         introspectorCache = new IntrospectorCache(log, conversionHandler);
-        this.conversionHandler = conversionHandler;
     }
 
     /**

Modified: velocity/engine/branches/VELOCITY-892/velocity-engine-core/src/main/java/org/apache/velocity/util/introspection/IntrospectorCache.java
URL: http://svn.apache.org/viewvc/velocity/engine/branches/VELOCITY-892/velocity-engine-core/src/main/java/org/apache/velocity/util/introspection/IntrospectorCache.java?rev=1844087&r1=1844086&r2=1844087&view=diff
==============================================================================
--- velocity/engine/branches/VELOCITY-892/velocity-engine-core/src/main/java/org/apache/velocity/util/introspection/IntrospectorCache.java (original)
+++ velocity/engine/branches/VELOCITY-892/velocity-engine-core/src/main/java/org/apache/velocity/util/introspection/IntrospectorCache.java Wed Oct 17 11:42:10 2018
@@ -68,12 +68,12 @@ public final class IntrospectorCache
     /**
      * Conversion handler
      */
-    private final ConversionHandler conversionHandler;
+    private final TypeConversionHandler conversionHandler;
 
     /**
      * C'tor
      */
-    public IntrospectorCache(final Logger log, final ConversionHandler conversionHandler)
+    public IntrospectorCache(final Logger log, final TypeConversionHandler conversionHandler)
     {
         this.log = log;
         this.conversionHandler = conversionHandler;

Modified: velocity/engine/branches/VELOCITY-892/velocity-engine-core/src/main/java/org/apache/velocity/util/introspection/MethodMap.java
URL: http://svn.apache.org/viewvc/velocity/engine/branches/VELOCITY-892/velocity-engine-core/src/main/java/org/apache/velocity/util/introspection/MethodMap.java?rev=1844087&r1=1844086&r2=1844087&view=diff
==============================================================================
--- velocity/engine/branches/VELOCITY-892/velocity-engine-core/src/main/java/org/apache/velocity/util/introspection/MethodMap.java (original)
+++ velocity/engine/branches/VELOCITY-892/velocity-engine-core/src/main/java/org/apache/velocity/util/introspection/MethodMap.java Wed Oct 17 11:42:10 2018
@@ -19,9 +19,11 @@ package org.apache.velocity.util.introsp
  * under the License.
  */
 
+import org.apache.commons.lang3.reflect.TypeUtils;
 import org.apache.velocity.exception.VelocityException;
 
 import java.lang.reflect.Method;
+import java.lang.reflect.Type;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.LinkedList;
@@ -54,7 +56,7 @@ public class MethodMap
     private static final int IMPLCITLY_CONVERTIBLE = 2;
     private static final int STRICTLY_CONVERTIBLE = 3;
 
-    ConversionHandler conversionHandler;
+    TypeConversionHandler conversionHandler;
 
     /**
      * Default constructor
@@ -69,7 +71,7 @@ public class MethodMap
      * @param conversionHandler conversion handler
      * @since 2.0
      */
-    public MethodMap(ConversionHandler conversionHandler)
+    public MethodMap(TypeConversionHandler conversionHandler)
     {
         this.conversionHandler = conversionHandler;
     }
@@ -173,7 +175,7 @@ public class MethodMap
         Method method;
 
         /* cache arguments classes array */
-        Class[] methodTypes;
+        Type[] methodTypes;
 
         /* specificity: how does the best match compare to provided arguments
          * one one LESS_SPECIFIC, MORE_SPECIFIC or INCOMPARABLE */
@@ -190,9 +192,9 @@ public class MethodMap
         {
             this.method = method;
             this.applicability = applicability;
-            this.methodTypes = method.getParameterTypes();
+            this.methodTypes = method.getGenericParameterTypes();
             this.specificity = compare(methodTypes, unboxedArgs);
-            this.varargs = methodTypes.length > 0 && methodTypes[methodTypes.length - 1].isArray();
+            this.varargs = methodTypes.length > 0 && (methodTypes[methodTypes.length - 1] instanceof Class) && ((Class)methodTypes[methodTypes.length - 1]).isArray();
         }
     }
 
@@ -322,74 +324,74 @@ public class MethodMap
     /**
      * Determines which method signature (represented by a class array) is more
      * specific. This defines a partial ordering on the method signatures.
-     * @param c1 first signature to compare
-     * @param c2 second signature to compare
+     * @param t1 first signature to compare
+     * @param t2 second signature to compare
      * @return MORE_SPECIFIC if c1 is more specific than c2, LESS_SPECIFIC if
      * c1 is less specific than c2, INCOMPARABLE if they are incomparable.
      */
-    private int compare(Class[] c1, Class[] c2)
+    private int compare(Type[] t1, Type[] t2)
     {
-        boolean c1IsVararag = false;
-        boolean c2IsVararag = false;
+        boolean t1IsVararag = false;
+        boolean t2IsVararag = false;
         boolean fixedLengths = false;
 
         // compare lengths to handle comparisons where the size of the arrays
         // doesn't match, but the methods are both applicable due to the fact
         // that one is a varargs method
-        if (c1.length > c2.length)
+        if (t1.length > t2.length)
         {
-            int l2 = c2.length;
+            int l2 = t2.length;
             if (l2 == 0)
             {
                 return MORE_SPECIFIC;
             }
-            c2 = Arrays.copyOf(c2, c1.length);
-            Class itemClass = c2[l2 - 1].getComponentType();
+            t2 = Arrays.copyOf(t2, t1.length);
+            Type itemType = t2[l2 - 1] instanceof Class ? ((Class)t2[l2 - 1]).getComponentType() : null;
             /* if item class is null, then it implies the vaarg is #1
              * (and receives an empty array)
              */
-            if (itemClass == null)
+            if (itemType == null)
             {
                 /* by construct, we have c1.length = l2 + 1 */
-                c1IsVararag = true;
-                c2[c1.length - 1] = null;
+                t1IsVararag = true;
+                t2[t1.length - 1] = null;
             }
             else
             {
-                c2IsVararag = true;
-                for (int i = l2 - 1; i < c1.length; ++i)
+                t2IsVararag = true;
+                for (int i = l2 - 1; i < t1.length; ++i)
                 {
                 /* also overwrite the vaargs itself */
-                    c2[i] = itemClass;
+                    t2[i] = itemType;
                 }
             }
             fixedLengths = true;
         }
-        else if (c2.length > c1.length)
+        else if (t2.length > t1.length)
         {
-            int l1 = c1.length;
+            int l1 = t1.length;
             if (l1 == 0)
             {
                 return LESS_SPECIFIC;
             }
-            c1 = Arrays.copyOf(c1, c2.length);
-            Class itemClass = c1[l1 - 1].getComponentType();
+            t1 = Arrays.copyOf(t1, t2.length);
+            Type itemType = t1[l1 - 1] instanceof Class ? ((Class)t1[l1 - 1]).getComponentType() : null;
             /* if item class is null, then it implies the vaarg is #2
              * (and receives an empty array)
              */
-            if (itemClass == null)
+            if (itemType == null)
             {
                 /* by construct, we have c2.length = l1 + 1 */
-                c2IsVararag = true;
-                c1[c2.length - 1] = null;
+                t2IsVararag = true;
+                t1[t2.length - 1] = null;
             }
             else
             {
-                c1IsVararag = true;
-                for (int i = l1 - 1; i < c2.length; ++i)
+                t1IsVararag = true;
+                for (int i = l1 - 1; i < t2.length; ++i)
                 {
                 /* also overwrite the vaargs itself */
-                    c1[i] = itemClass;
+                    t1[i] = itemType;
                 }
             }
             fixedLengths = true;
@@ -398,52 +400,72 @@ public class MethodMap
         /* ok, move on and compare those of equal lengths */
         int fromC1toC2 = STRICTLY_CONVERTIBLE;
         int fromC2toC1 = STRICTLY_CONVERTIBLE;
-        for(int i = 0; i < c1.length; ++i)
+        for(int i = 0; i < t1.length; ++i)
         {
-            boolean last = !fixedLengths && (i == c1.length - 1);
-            if (c1[i] != c2[i])
+            boolean last = !fixedLengths && (i == t1.length - 1);
+            if (t1[i] != t2[i])
             {
-                if (c1[i] == null)
+                if (t1[i] == null)
                 {
                     fromC2toC1 = NOT_CONVERTIBLE;
-                    if (c2[i].isPrimitive())
+                    if ((t2[i] instanceof Class) && ((Class)t2[i]).isPrimitive())
                     {
                         fromC1toC2 = NOT_CONVERTIBLE;
                     }
                 }
-                else if (c2[i] == null)
+                else if (t2[i] == null)
                 {
                     fromC1toC2 = NOT_CONVERTIBLE;
-                    if (c1[i].isPrimitive())
+                    if ((t1[i] instanceof Class) && ((Class)t1[i]).isPrimitive())
                     {
                         fromC2toC1 = NOT_CONVERTIBLE;
                     }
                 }
                 else
                 {
-                    switch (fromC1toC2)
+                    if (t1[i] instanceof Class)
                     {
-                        case STRICTLY_CONVERTIBLE:
-                            if (isStrictConvertible(c2[i], c1[i], last)) break;
-                            fromC1toC2 = IMPLCITLY_CONVERTIBLE;
-                        case IMPLCITLY_CONVERTIBLE:
-                            if (isConvertible(c2[i], c1[i], last)) break;
-                            fromC1toC2 = EXPLICITLY_CONVERTIBLE;
-                        case EXPLICITLY_CONVERTIBLE:
-                            if (isExplicitlyConvertible(c2[i], c1[i], last)) break;
-                            fromC1toC2 = NOT_CONVERTIBLE;
-                    }
-                    switch (fromC2toC1)
-                    {
-                        case STRICTLY_CONVERTIBLE:
-                            if (isStrictConvertible(c1[i], c2[i], last)) break;
-                            fromC2toC1 = IMPLCITLY_CONVERTIBLE;
-                        case IMPLCITLY_CONVERTIBLE:
-                            if (isConvertible(c1[i], c2[i], last)) break;
-                            fromC2toC1 = EXPLICITLY_CONVERTIBLE;
-                        case EXPLICITLY_CONVERTIBLE:
-                            if (isExplicitlyConvertible(c1[i], c2[i], last)) break;
-                            fromC2toC1 = NOT_CONVERTIBLE;
+                        Class c1 = (Class)t1[i];
+                        switch (fromC1toC2)
+                        {
+                            case STRICTLY_CONVERTIBLE:
+                                if (isStrictConvertible(t2[i], c1, last)) break;
+                                fromC1toC2 = IMPLCITLY_CONVERTIBLE;
+                            case IMPLCITLY_CONVERTIBLE:
+                                if (isConvertible(t2[i], c1, last)) break;
+                                fromC1toC2 = EXPLICITLY_CONVERTIBLE;
+                            case EXPLICITLY_CONVERTIBLE:
+                                if (isExplicitlyConvertible(t2[i], c1, last)) break;
+                                fromC1toC2 = NOT_CONVERTIBLE;
+                        }
+                    }
+                    else if (fromC1toC2 > NOT_CONVERTIBLE)
+                    {
+                        fromC1toC2 = TypeUtils.isAssignable(t1[i], t2[i]) ?
+                            Math.min(fromC1toC2, IMPLCITLY_CONVERTIBLE) :
+                            NOT_CONVERTIBLE;
+                    }
+                    if (t2[i] instanceof Class)
+                    {
+                        Class c2 = (Class)t2[i];
+                        switch (fromC2toC1)
+                        {
+                            case STRICTLY_CONVERTIBLE:
+                                if (isStrictConvertible(t1[i], c2, last)) break;
+                                fromC2toC1 = IMPLCITLY_CONVERTIBLE;
+                            case IMPLCITLY_CONVERTIBLE:
+                                if (isConvertible(t1[i], c2, last)) break;
+                                fromC2toC1 = EXPLICITLY_CONVERTIBLE;
+                            case EXPLICITLY_CONVERTIBLE:
+                                if (isExplicitlyConvertible(t1[i], c2, last)) break;
+                                fromC2toC1 = NOT_CONVERTIBLE;
+                        }
+                    }
+                    else if (fromC2toC1 > NOT_CONVERTIBLE)
+                    {
+                        fromC2toC1 = TypeUtils.isAssignable(t2[i], t1[i]) ?
+                            Math.min(fromC2toC1, IMPLCITLY_CONVERTIBLE) :
+                            NOT_CONVERTIBLE;
                     }
                 }
             }
@@ -472,8 +494,8 @@ public class MethodMap
              * If one method accepts varargs and the other does not,
              * call the non-vararg one more specific.
              */
-            boolean last1Array = c1IsVararag || !fixedLengths && c1[c1.length - 1].isArray();
-            boolean last2Array = c2IsVararag || !fixedLengths && c2[c2.length - 1].isArray();
+            boolean last1Array = t1IsVararag || !fixedLengths && (t1[t1.length - 1] instanceof Class) && ((Class)t1[t1.length - 1]).isArray();
+            boolean last2Array = t2IsVararag || !fixedLengths && (t2[t2.length - 1] instanceof Class) && ((Class)t2[t2.length - 1]).isArray();
             if (last1Array && !last2Array)
             {
                 return LESS_SPECIFIC;
@@ -499,14 +521,15 @@ public class MethodMap
      */
     private int getApplicability(Method method, Class[] classes)
     {
-        Class[] methodArgs = method.getParameterTypes();
+        Type[] methodArgs = method.getGenericParameterTypes();
         int ret = STRICTLY_CONVERTIBLE;
         if (methodArgs.length > classes.length)
         {
             // if there's just one more methodArg than class arg
             // and the last methodArg is an array, then treat it as a vararg
             if (methodArgs.length == classes.length + 1 &&
-                methodArgs[methodArgs.length - 1].isArray())
+                (methodArgs[methodArgs.length - 1] instanceof Class) &&
+                ((Class)methodArgs[methodArgs.length - 1]).isArray())
             {
                 // all the args preceding the vararg must match
                 for (int i = 0; i < classes.length; i++)
@@ -541,13 +564,14 @@ public class MethodMap
             // (e.g. String when the method is expecting String...)
             for(int i = 0; i < classes.length; ++i)
             {
-                if (!isStrictConvertible(methodArgs[i], classes[i], i == classes.length - 1 && methodArgs[i].isArray()))
+                boolean possibleVararg = i == classes.length - 1 && (methodArgs[i] instanceof Class) && ((Class)methodArgs[i]).isArray();
+                if (!isStrictConvertible(methodArgs[i], classes[i], possibleVararg))
                 {
-                    if (isConvertible(methodArgs[i], classes[i], i == classes.length - 1 && methodArgs[i].isArray()))
+                    if (isConvertible(methodArgs[i], classes[i], possibleVararg))
                     {
                         ret = Math.min(ret, IMPLCITLY_CONVERTIBLE);
                     }
-                    else if (isExplicitlyConvertible(methodArgs[i], classes[i], i == classes.length - 1 && methodArgs[i].isArray()))
+                    else if (isExplicitlyConvertible(methodArgs[i], classes[i], possibleVararg))
                     {
                         ret = Math.min(ret, EXPLICITLY_CONVERTIBLE);
                     }
@@ -562,8 +586,8 @@ public class MethodMap
         else if (methodArgs.length > 0) // more arguments given than the method accepts; check for varargs
         {
             // check that the last methodArg is an array
-            Class lastarg = methodArgs[methodArgs.length - 1];
-            if (!lastarg.isArray())
+            Type lastarg = methodArgs[methodArgs.length - 1];
+            if (!(lastarg instanceof Class) || !((Class)lastarg).isArray())
             {
                 return NOT_CONVERTIBLE;
             }
@@ -589,7 +613,7 @@ public class MethodMap
             }
 
             // check that all remaining arguments are convertible to the vararg type
-            Class vararg = lastarg.getComponentType();
+            Class vararg = ((Class)lastarg).getComponentType();
             for (int i = methodArgs.length - 1; i < classes.length; ++i)
             {
                 if (!isStrictConvertible(vararg, classes[i], false))
@@ -621,7 +645,7 @@ public class MethodMap
      * @param possibleVarArg
      * @return convertible
      */
-    private boolean isConvertible(Class formal, Class actual, boolean possibleVarArg)
+    private boolean isConvertible(Type formal, Class actual, boolean possibleVarArg)
     {
         return IntrospectionUtils.
             isMethodInvocationConvertible(formal, actual, possibleVarArg);
@@ -636,7 +660,7 @@ public class MethodMap
      * @param possibleVarArg
      * @return convertible
      */
-    private static boolean isStrictConvertible(Class formal, Class actual, boolean possibleVarArg)
+    private static boolean isStrictConvertible(Type formal, Class actual, boolean possibleVarArg)
     {
         return IntrospectionUtils.
             isStrictMethodInvocationConvertible(formal, actual, possibleVarArg);
@@ -650,7 +674,7 @@ public class MethodMap
      * @param possibleVarArg
      * @return
      */
-    private boolean isExplicitlyConvertible(Class formal, Class actual, boolean possibleVarArg)
+    private boolean isExplicitlyConvertible(Type formal, Class actual, boolean possibleVarArg)
     {
         return conversionHandler != null && conversionHandler.isExplicitlyConvertible(formal, actual, possibleVarArg);
     }

Added: velocity/engine/branches/VELOCITY-892/velocity-engine-core/src/main/java/org/apache/velocity/util/introspection/TypeConversionHandler.java
URL: http://svn.apache.org/viewvc/velocity/engine/branches/VELOCITY-892/velocity-engine-core/src/main/java/org/apache/velocity/util/introspection/TypeConversionHandler.java?rev=1844087&view=auto
==============================================================================
--- velocity/engine/branches/VELOCITY-892/velocity-engine-core/src/main/java/org/apache/velocity/util/introspection/TypeConversionHandler.java (added)
+++ velocity/engine/branches/VELOCITY-892/velocity-engine-core/src/main/java/org/apache/velocity/util/introspection/TypeConversionHandler.java Wed Oct 17 11:42:10 2018
@@ -0,0 +1,67 @@
+package org.apache.velocity.util.introspection;
+
+/*
+ * 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.
+ */
+
+import java.lang.reflect.Type;
+
+/**
+ * A conversion handler adds admissible conversions between Java types whenever Velocity introspection has to map
+ * VTL methods and property accessors to Java methods.
+ * Both methods must be consistent: <code>getNeededConverter</code> must not return <code>null</code> whenever
+ * <code>isExplicitlyConvertible</code> returned true with the same arguments.
+ *
+ * @author <a href="mailto:claude.brisson@gmail.com">Claude Brisson</a>
+ * @version $Id: ConversionHandler.java $
+ * @since 2.1
+ */
+
+public interface TypeConversionHandler
+{
+    /**
+     * Check to see if the conversion can be done using an explicit conversion
+     * @param formal expected formal type
+     * @param actual provided argument type
+     * @return null if no conversion is needed, or the appropriate Converter object
+     * @since 2.1
+     */
+    boolean isExplicitlyConvertible(Type formal, Class actual, boolean possibleVarArg);
+
+    /**
+     * Returns the appropriate Converter object needed for an explicit conversion
+     * Returns null if no conversion is needed.
+     *
+     * @param formal expected formal type
+     * @param actual provided argument type
+     * @return null if no conversion is needed, or the appropriate Converter object
+     * @since 2.1
+     */
+    Converter getNeededConverter(Type formal, Class actual);
+
+    /**
+     * Add the given converter to the handler. Implementation should be thread-safe.
+     *
+     * @param formal expected formal type
+     * @param actual provided argument type
+     * @param converter converter
+     * @since 2.1
+     */
+    void addConverter(Type formal, Class actual, Converter converter);
+
+}

Added: velocity/engine/branches/VELOCITY-892/velocity-engine-core/src/main/java/org/apache/velocity/util/introspection/TypeConversionHandlerImpl.java
URL: http://svn.apache.org/viewvc/velocity/engine/branches/VELOCITY-892/velocity-engine-core/src/main/java/org/apache/velocity/util/introspection/TypeConversionHandlerImpl.java?rev=1844087&view=auto
==============================================================================
--- velocity/engine/branches/VELOCITY-892/velocity-engine-core/src/main/java/org/apache/velocity/util/introspection/TypeConversionHandlerImpl.java (added)
+++ velocity/engine/branches/VELOCITY-892/velocity-engine-core/src/main/java/org/apache/velocity/util/introspection/TypeConversionHandlerImpl.java Wed Oct 17 11:42:10 2018
@@ -0,0 +1,618 @@
+package org.apache.velocity.util.introspection;
+
+/*
+ * 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.
+ */
+
+import org.apache.velocity.util.Pair;
+
+import java.lang.reflect.Type;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+/**
+ * A conversion handler adds admissible conversions between Java types whenever Velocity introspection has to map
+ * VTL methods and property accessors to Java methods. This implementation is the default Conversion Handler
+ * for Velocity.
+ *
+ * @author <a href="mailto:claude.brisson@gmail.com">Claude Brisson</a>
+ * @version $Id: TypeConversionHandlerImpl.java $
+ * @since 2.0
+ */
+
+public class TypeConversionHandlerImpl implements TypeConversionHandler
+{
+    /**
+     * standard narrowing and string parsing conversions.
+     */
+    static Map<Pair<? extends Type, ? extends Class>, Converter> standardConverterMap;
+
+    /**
+     * basic toString converter
+     */
+    static Converter toString;
+
+    /**
+     * cache miss converter
+     */
+    static Converter cacheMiss;
+
+    /**
+     * min/max byte/short/int values as long
+     */
+    static final long minByte = Byte.MIN_VALUE, maxByte = Byte.MAX_VALUE,
+        minShort = Short.MIN_VALUE, maxShort = Short.MAX_VALUE,
+        minInt = Integer.MIN_VALUE, maxInt = Integer.MAX_VALUE;
+
+    /**
+     * min/max long values as double
+     */
+    static final double minLong = Long.MIN_VALUE, maxLong = Long.MAX_VALUE;
+
+    /**
+     * a converters cache map, initialized with the standard narrowing and string parsing conversions.
+     */
+    Map<Pair<? extends Type, ? extends Class>, Converter> converterCacheMap;
+
+    static
+    {
+        standardConverterMap = new HashMap<>();
+
+        cacheMiss = new Converter<Object>()
+        {
+            @Override
+            public Object convert(Object o)
+            {
+                return o;
+            }
+        };
+
+        /* number -> boolean */
+        Converter<Boolean> numberToBool = new Converter<Boolean>()
+        {
+            @Override
+            public Boolean convert(Object o)
+            {
+                return o == null ? null : ((Number) o).intValue() != 0;
+            }
+        };
+        standardConverterMap.put(new Pair<>(Boolean.class, Byte.class), numberToBool);
+        standardConverterMap.put(new Pair<>(Boolean.class, Short.class), numberToBool);
+        standardConverterMap.put(new Pair<>(Boolean.class, Integer.class), numberToBool);
+        standardConverterMap.put(new Pair<>(Boolean.class, Long.class), numberToBool);
+        standardConverterMap.put(new Pair<>(Boolean.class, Float.class), numberToBool);
+        standardConverterMap.put(new Pair<>(Boolean.class, Double.class), numberToBool);
+        standardConverterMap.put(new Pair<>(Boolean.class, Number.class), numberToBool);
+        standardConverterMap.put(new Pair<>(Boolean.class, Byte.TYPE), numberToBool);
+        standardConverterMap.put(new Pair<>(Boolean.class, Short.TYPE), numberToBool);
+        standardConverterMap.put(new Pair<>(Boolean.class, Integer.TYPE), numberToBool);
+        standardConverterMap.put(new Pair<>(Boolean.class, Long.TYPE), numberToBool);
+        standardConverterMap.put(new Pair<>(Boolean.class, Float.TYPE), numberToBool);
+        standardConverterMap.put(new Pair<>(Boolean.class, Double.TYPE), numberToBool);
+        standardConverterMap.put(new Pair<>(Boolean.TYPE, Byte.class), numberToBool);
+        standardConverterMap.put(new Pair<>(Boolean.TYPE, Short.class), numberToBool);
+        standardConverterMap.put(new Pair<>(Boolean.TYPE, Integer.class), numberToBool);
+        standardConverterMap.put(new Pair<>(Boolean.TYPE, Long.class), numberToBool);
+        standardConverterMap.put(new Pair<>(Boolean.TYPE, Float.class), numberToBool);
+        standardConverterMap.put(new Pair<>(Boolean.TYPE, Double.class), numberToBool);
+        standardConverterMap.put(new Pair<>(Boolean.TYPE, Number.class), numberToBool);
+        standardConverterMap.put(new Pair<>(Boolean.TYPE, Byte.TYPE), numberToBool);
+        standardConverterMap.put(new Pair<>(Boolean.TYPE, Short.TYPE), numberToBool);
+        standardConverterMap.put(new Pair<>(Boolean.TYPE, Integer.TYPE), numberToBool);
+        standardConverterMap.put(new Pair<>(Boolean.TYPE, Long.TYPE), numberToBool);
+        standardConverterMap.put(new Pair<>(Boolean.TYPE, Float.TYPE), numberToBool);
+        standardConverterMap.put(new Pair<>(Boolean.TYPE, Double.TYPE), numberToBool);
+
+        /* character -> boolean */
+        Converter<Boolean> charToBoolean = new Converter<Boolean>()
+        {
+            @Override
+            public Boolean convert(Object o)
+            {
+                return o == null ? null : (Character) o != 0;
+            }
+        };
+        standardConverterMap.put(new Pair<>(Boolean.class, Character.class), charToBoolean);
+        standardConverterMap.put(new Pair<>(Boolean.class, Character.TYPE), charToBoolean);
+        standardConverterMap.put(new Pair<>(Boolean.TYPE, Character.class), charToBoolean);
+        standardConverterMap.put(new Pair<>(Boolean.TYPE, Character.TYPE), charToBoolean);
+
+        /* string -> boolean */
+        Converter<Boolean> stringToBoolean = new Converter<Boolean>()
+        {
+            @Override
+            public Boolean convert(Object o)
+            {
+                return Boolean.valueOf(String.valueOf(o));
+            }
+        };
+        standardConverterMap.put(new Pair<>(Boolean.class, String.class), stringToBoolean);
+        standardConverterMap.put(new Pair<>(Boolean.TYPE, String.class), stringToBoolean);
+
+        /* narrowing towards byte */
+        Converter<Byte> narrowingToByte = new Converter<Byte>()
+        {
+            @Override
+            public Byte convert(Object o)
+            {
+                if (o == null) return null;
+                long l = ((Number)o).longValue();
+                if (l < minByte || l > maxByte)
+                {
+                    throw new NumberFormatException("value out of range for byte type: " + l);
+                }
+                return ((Number) o).byteValue();
+            }
+        };
+        standardConverterMap.put(new Pair<>(Byte.class, Short.class), narrowingToByte);
+        standardConverterMap.put(new Pair<>(Byte.class, Integer.class), narrowingToByte);
+        standardConverterMap.put(new Pair<>(Byte.class, Long.class), narrowingToByte);
+        standardConverterMap.put(new Pair<>(Byte.class, Float.class), narrowingToByte);
+        standardConverterMap.put(new Pair<>(Byte.class, Double.class), narrowingToByte);
+        standardConverterMap.put(new Pair<>(Byte.class, Number.class), narrowingToByte);
+        standardConverterMap.put(new Pair<>(Byte.class, Short.TYPE), narrowingToByte);
+        standardConverterMap.put(new Pair<>(Byte.class, Integer.TYPE), narrowingToByte);
+        standardConverterMap.put(new Pair<>(Byte.class, Long.TYPE), narrowingToByte);
+        standardConverterMap.put(new Pair<>(Byte.class, Float.TYPE), narrowingToByte);
+        standardConverterMap.put(new Pair<>(Byte.class, Double.TYPE), narrowingToByte);
+        standardConverterMap.put(new Pair<>(Byte.TYPE, Short.class), narrowingToByte);
+        standardConverterMap.put(new Pair<>(Byte.TYPE, Integer.class), narrowingToByte);
+        standardConverterMap.put(new Pair<>(Byte.TYPE, Long.class), narrowingToByte);
+        standardConverterMap.put(new Pair<>(Byte.TYPE, Float.class), narrowingToByte);
+        standardConverterMap.put(new Pair<>(Byte.TYPE, Double.class), narrowingToByte);
+        standardConverterMap.put(new Pair<>(Byte.TYPE, Number.class), narrowingToByte);
+        standardConverterMap.put(new Pair<>(Byte.TYPE, Short.TYPE), narrowingToByte);
+        standardConverterMap.put(new Pair<>(Byte.TYPE, Integer.TYPE), narrowingToByte);
+        standardConverterMap.put(new Pair<>(Byte.TYPE, Long.TYPE), narrowingToByte);
+        standardConverterMap.put(new Pair<>(Byte.TYPE, Float.TYPE), narrowingToByte);
+        standardConverterMap.put(new Pair<>(Byte.TYPE, Double.TYPE), narrowingToByte);
+
+        /* string to byte */
+        Converter<Byte> stringToByte = new Converter<Byte>()
+        {
+            @Override
+            public Byte convert(Object o)
+            {
+                return Byte.valueOf(String.valueOf(o));
+            }
+        };
+        standardConverterMap.put(new Pair<>(Byte.class, String.class), stringToByte);
+        standardConverterMap.put(new Pair<>(Byte.TYPE, String.class), stringToByte);
+
+        /* narrowing towards short */
+        Converter<Short> narrowingToShort = new Converter<Short>()
+        {
+            @Override
+            public Short convert(Object o)
+            {
+                if (o == null) return null;
+                long l = ((Number)o).longValue();
+                if (l < minShort || l > maxShort)
+                {
+                    throw new NumberFormatException("value out of range for short type: " + l);
+                }
+                return ((Number) o).shortValue();
+            }
+        };
+        standardConverterMap.put(new Pair<>(Short.class, Integer.class), narrowingToShort);
+        standardConverterMap.put(new Pair<>(Short.class, Long.class), narrowingToShort);
+        standardConverterMap.put(new Pair<>(Short.class, Float.class), narrowingToShort);
+        standardConverterMap.put(new Pair<>(Short.class, Double.class), narrowingToShort);
+        standardConverterMap.put(new Pair<>(Short.class, Number.class), narrowingToShort);
+        standardConverterMap.put(new Pair<>(Short.class, Integer.TYPE), narrowingToShort);
+        standardConverterMap.put(new Pair<>(Short.class, Long.TYPE), narrowingToShort);
+        standardConverterMap.put(new Pair<>(Short.class, Float.TYPE), narrowingToShort);
+        standardConverterMap.put(new Pair<>(Short.class, Double.TYPE), narrowingToShort);
+        standardConverterMap.put(new Pair<>(Short.TYPE, Integer.class), narrowingToShort);
+        standardConverterMap.put(new Pair<>(Short.TYPE, Long.class), narrowingToShort);
+        standardConverterMap.put(new Pair<>(Short.TYPE, Float.class), narrowingToShort);
+        standardConverterMap.put(new Pair<>(Short.TYPE, Double.class), narrowingToShort);
+        standardConverterMap.put(new Pair<>(Short.TYPE, Number.class), narrowingToShort);
+        standardConverterMap.put(new Pair<>(Short.TYPE, Integer.TYPE), narrowingToShort);
+        standardConverterMap.put(new Pair<>(Short.TYPE, Long.TYPE), narrowingToShort);
+        standardConverterMap.put(new Pair<>(Short.TYPE, Float.TYPE), narrowingToShort);
+        standardConverterMap.put(new Pair<>(Short.TYPE, Double.TYPE), narrowingToShort);
+
+        /* string to short */
+        Converter<Short> stringToShort = new Converter<Short>()
+        {
+            @Override
+            public Short convert(Object o)
+            {
+                return Short.valueOf(String.valueOf(o));
+            }
+        };
+        standardConverterMap.put(new Pair<>(Short.class, String.class), stringToShort);
+        standardConverterMap.put(new Pair<>(Short.TYPE, String.class), stringToShort);
+
+        /* narrowing towards int */
+        Converter<Integer> narrowingToInteger = new Converter<Integer>()
+        {
+            @Override
+            public Integer convert(Object o)
+            {
+                if (o == null) return null;
+                long l = ((Number)o).longValue();
+                if (l < minInt || l > maxInt)
+                {
+                    throw new NumberFormatException("value out of range for integer type: " + l);
+                }
+                return ((Number) o).intValue();
+            }
+        };
+        standardConverterMap.put(new Pair<>(Integer.class, Long.class), narrowingToInteger);
+        standardConverterMap.put(new Pair<>(Integer.class, Float.class), narrowingToInteger);
+        standardConverterMap.put(new Pair<>(Integer.class, Double.class), narrowingToInteger);
+        standardConverterMap.put(new Pair<>(Integer.class, Number.class), narrowingToInteger);
+        standardConverterMap.put(new Pair<>(Integer.class, Long.TYPE), narrowingToInteger);
+        standardConverterMap.put(new Pair<>(Integer.class, Float.TYPE), narrowingToInteger);
+        standardConverterMap.put(new Pair<>(Integer.class, Double.TYPE), narrowingToInteger);
+        standardConverterMap.put(new Pair<>(Integer.TYPE, Long.class), narrowingToInteger);
+        standardConverterMap.put(new Pair<>(Integer.TYPE, Float.class), narrowingToInteger);
+        standardConverterMap.put(new Pair<>(Integer.TYPE, Double.class), narrowingToInteger);
+        standardConverterMap.put(new Pair<>(Integer.TYPE, Number.class), narrowingToInteger);
+        standardConverterMap.put(new Pair<>(Integer.TYPE, Long.TYPE), narrowingToInteger);
+        standardConverterMap.put(new Pair<>(Integer.TYPE, Float.TYPE), narrowingToInteger);
+        standardConverterMap.put(new Pair<>(Integer.TYPE, Double.TYPE), narrowingToInteger);
+
+        /* widening towards Integer */
+        Converter<Integer> wideningToInteger = new Converter<Integer>()
+        {
+            @Override
+            public Integer convert(Object o)
+            {
+                if (o == null) return null;
+                return ((Number) o).intValue();
+            }
+        };
+        standardConverterMap.put(new Pair<>(Integer.class, Short.class), wideningToInteger);
+        standardConverterMap.put(new Pair<>(Integer.class, Short.TYPE), wideningToInteger);
+
+        /* string to int */
+        Converter<Integer> stringToInteger = new Converter<Integer>()
+        {
+            @Override
+            public Integer convert(Object o)
+            {
+                return Integer.valueOf(String.valueOf(o));
+            }
+        };
+        standardConverterMap.put(new Pair<>(Integer.class, String.class), stringToInteger);
+        standardConverterMap.put(new Pair<>(Integer.TYPE, String.class), stringToInteger);
+
+        /* narrowing towards long */
+        Converter<Long> narrowingToLong = new Converter<Long>()
+        {
+            @Override
+            public Long convert(Object o)
+            {
+                if (o == null) return null;
+                double d = ((Number)o).doubleValue();
+                if (d < minLong || d > maxLong)
+                {
+                    throw new NumberFormatException("value out of range for long type: " + d);
+                }
+                return ((Number) o).longValue();
+            }
+        };
+        standardConverterMap.put(new Pair<>(Long.class, Float.class), narrowingToLong);
+        standardConverterMap.put(new Pair<>(Long.class, Double.class), narrowingToLong);
+        standardConverterMap.put(new Pair<>(Long.class, Number.class), narrowingToLong);
+        standardConverterMap.put(new Pair<>(Long.class, Float.TYPE), narrowingToLong);
+        standardConverterMap.put(new Pair<>(Long.class, Double.TYPE), narrowingToLong);
+        standardConverterMap.put(new Pair<>(Long.TYPE, Float.class), narrowingToLong);
+        standardConverterMap.put(new Pair<>(Long.TYPE, Double.class), narrowingToLong);
+        standardConverterMap.put(new Pair<>(Long.TYPE, Number.class), narrowingToLong);
+        standardConverterMap.put(new Pair<>(Long.TYPE, Float.TYPE), narrowingToLong);
+        standardConverterMap.put(new Pair<>(Long.TYPE, Double.TYPE), narrowingToLong);
+
+        /* widening towards Long */
+        Converter<Long> wideningToLong = new Converter<Long>()
+        {
+            @Override
+            public Long convert(Object o)
+            {
+                if (o == null) return null;
+                return ((Number) o).longValue();
+            }
+        };
+        standardConverterMap.put(new Pair<>(Long.class, Short.class), wideningToLong);
+        standardConverterMap.put(new Pair<>(Long.class, Integer.class), wideningToLong);
+        standardConverterMap.put(new Pair<>(Long.class, Short.TYPE), wideningToLong);
+        standardConverterMap.put(new Pair<>(Long.class, Integer.TYPE), wideningToLong);
+
+        /* string to long */
+        Converter<Long> stringToLong = new Converter<Long>()
+        {
+            @Override
+            public Long convert(Object o)
+            {
+                return Long.valueOf(String.valueOf(o));
+            }
+        };
+        standardConverterMap.put(new Pair<>(Long.class, String.class), stringToLong);
+        standardConverterMap.put(new Pair<>(Long.TYPE, String.class), stringToLong);
+
+        /* narrowing towards float */
+        Converter<Float> narrowingToFloat = new Converter<Float>()
+        {
+            @Override
+            public Float convert(Object o)
+            {
+                return o == null ? null : ((Number) o).floatValue();
+            }
+        };
+        standardConverterMap.put(new Pair<>(Float.class, Double.class), narrowingToFloat);
+        standardConverterMap.put(new Pair<>(Float.class, Number.class), narrowingToFloat);
+        standardConverterMap.put(new Pair<>(Float.class, Double.TYPE), narrowingToFloat);
+        standardConverterMap.put(new Pair<>(Float.TYPE, Double.class), narrowingToFloat);
+        standardConverterMap.put(new Pair<>(Float.TYPE, Number.class), narrowingToFloat);
+        standardConverterMap.put(new Pair<>(Float.TYPE, Double.TYPE), narrowingToFloat);
+
+        /* exact towards Float */
+        Converter<Float> toFloat = new Converter<Float>()
+        {
+            @Override
+            public Float convert(Object o)
+            {
+                if (o == null) return null;
+                return ((Number) o).floatValue();
+            }
+        };
+        standardConverterMap.put(new Pair<>(Float.class, Short.class), toFloat);
+        standardConverterMap.put(new Pair<>(Float.class, Integer.class), toFloat);
+        standardConverterMap.put(new Pair<>(Float.class, Long.class), toFloat);
+        standardConverterMap.put(new Pair<>(Float.class, Short.TYPE), toFloat);
+        standardConverterMap.put(new Pair<>(Float.class, Integer.TYPE), toFloat);
+        standardConverterMap.put(new Pair<>(Float.class, Long.TYPE), toFloat);
+
+        /* string to float */
+        Converter<Float> stringToFloat = new Converter<Float>()
+        {
+            @Override
+            public Float convert(Object o)
+            {
+                return Float.valueOf(String.valueOf(o));
+            }
+        };
+        standardConverterMap.put(new Pair<>(Float.class, String.class), stringToFloat);
+        standardConverterMap.put(new Pair<>(Float.TYPE, String.class), stringToFloat);
+
+        /* exact or widening towards Double */
+        Converter<Double> toDouble = new Converter<Double>()
+        {
+            @Override
+            public Double convert(Object o)
+            {
+                if (o == null) return null;
+                return ((Number) o).doubleValue();
+            }
+        };
+        standardConverterMap.put(new Pair<>(Double.class, Short.class), toDouble);
+        standardConverterMap.put(new Pair<>(Double.class, Integer.class), toDouble);
+        standardConverterMap.put(new Pair<>(Double.class, Long.class), toDouble);
+        standardConverterMap.put(new Pair<>(Double.class, Float.class), toDouble);
+        standardConverterMap.put(new Pair<>(Double.class, Number.class), toDouble);
+        standardConverterMap.put(new Pair<>(Double.class, Short.TYPE), toDouble);
+        standardConverterMap.put(new Pair<>(Double.class, Integer.TYPE), toDouble);
+        standardConverterMap.put(new Pair<>(Double.class, Long.TYPE), toDouble);
+        standardConverterMap.put(new Pair<>(Double.class, Float.TYPE), toDouble);
+        standardConverterMap.put(new Pair<>(Double.TYPE, Number.class), toDouble);
+
+        /* string to double */
+        Converter<Double> stringToDouble = new Converter<Double>()
+        {
+            @Override
+            public Double convert(Object o)
+            {
+                return Double.valueOf(String.valueOf(o));
+            }
+        };
+        standardConverterMap.put(new Pair<>(Double.class, String.class), stringToDouble);
+        standardConverterMap.put(new Pair<>(Double.TYPE, String.class), stringToDouble);
+
+        /* boolean to byte */
+        Converter<Byte> booleanToByte = new Converter<Byte>()
+        {
+            @Override
+            public Byte convert(Object o)
+            {
+                return o == null ? null : (Boolean) o ? (byte)1 : (byte)0;
+            }
+        };
+        standardConverterMap.put(new Pair<>(Byte.class, Boolean.class), booleanToByte);
+        standardConverterMap.put(new Pair<>(Byte.class, Boolean.TYPE), booleanToByte);
+        standardConverterMap.put(new Pair<>(Byte.TYPE, Boolean.class), booleanToByte);
+        standardConverterMap.put(new Pair<>(Byte.TYPE, Boolean.TYPE), booleanToByte);
+
+        /* boolean to short */
+        Converter<Short> booleanToShort = new Converter<Short>()
+        {
+            @Override
+            public Short convert(Object o)
+            {
+                return o == null ? null : (Boolean) o ? (short)1 : (short)0;
+            }
+        };
+        standardConverterMap.put(new Pair<>(Short.class, Boolean.class), booleanToShort);
+        standardConverterMap.put(new Pair<>(Short.class, Boolean.TYPE), booleanToShort);
+        standardConverterMap.put(new Pair<>(Short.TYPE, Boolean.class), booleanToShort);
+        standardConverterMap.put(new Pair<>(Short.TYPE, Boolean.TYPE), booleanToShort);
+
+        /* boolean to integer */
+        Converter<Integer> booleanToInteger = new Converter<Integer>()
+        {
+            @Override
+            public Integer convert(Object o)
+            {
+                return o == null ? null : (Boolean) o ? (Integer)1 : (Integer)0;
+            }
+        };
+        standardConverterMap.put(new Pair<>(Integer.class, Boolean.class), booleanToInteger);
+        standardConverterMap.put(new Pair<>(Integer.class, Boolean.TYPE), booleanToInteger);
+        standardConverterMap.put(new Pair<>(Integer.TYPE, Boolean.class), booleanToInteger);
+        standardConverterMap.put(new Pair<>(Integer.TYPE, Boolean.TYPE), booleanToInteger);
+
+        /* boolean to lonf */
+        Converter<Long> booleanToLong = new Converter<Long>()
+        {
+            @Override
+            public Long convert(Object o)
+            {
+                return o == null ? null : (Boolean) o ? 1L : 0L;
+            }
+        };
+        standardConverterMap.put(new Pair<>(Long.class, Boolean.class), booleanToLong);
+        standardConverterMap.put(new Pair<>(Long.class, Boolean.TYPE), booleanToLong);
+        standardConverterMap.put(new Pair<>(Long.TYPE, Boolean.class), booleanToLong);
+        standardConverterMap.put(new Pair<>(Long.TYPE, Boolean.TYPE), booleanToLong);
+
+        /* to string */
+        toString = new Converter<String>()
+        {
+            @Override
+            public String convert(Object o)
+            {
+                return String.valueOf(o);
+            }
+        };
+    }
+
+    /**
+     * Constructor
+     */
+    public TypeConversionHandlerImpl()
+    {
+        converterCacheMap = new ConcurrentHashMap<>();
+    }
+
+    /**
+     * Check to see if the conversion can be done using an explicit conversion
+     * @param actual found argument type
+     * @param formal expected formal type
+     * @return true if actual class can be explicitely converted to expected formal type
+     * @since 2.1
+     */
+    @Override
+    public boolean isExplicitlyConvertible(Type formal, Class actual, boolean possibleVarArg)
+    {
+        /*
+         * for consistency, we also have to check standard implicit convertibility
+         * since it may not have been checked before by the calling code
+         */
+        if ((formal instanceof Class) && (Class)formal == actual ||
+            IntrospectionUtils.isMethodInvocationConvertible(formal, actual, possibleVarArg) ||
+            getNeededConverter(formal, actual) != null)
+        {
+            return true;
+        }
+
+        /* Check var arg */
+        if (possibleVarArg && (formal instanceof Class) && ((Class)formal).isArray())
+        {
+            if (actual.isArray())
+            {
+                actual = actual.getComponentType();
+            }
+            return isExplicitlyConvertible(((Class)formal).getComponentType(), actual, false);
+        }
+        return false;
+    }
+
+
+    /**
+     * Returns the appropriate Converter object needed for an explicit conversion
+     * Returns null if no conversion is needed.
+     *
+     * @param actual found argument type
+     * @param formal expected formal type
+     * @return null if no conversion is needed, or the appropriate Converter object
+     * @since 2.1
+     */
+    @Override
+    public Converter getNeededConverter(Type formal, Class actual)
+    {
+        Pair<Type, Class> key = new Pair<>(formal, actual);
+
+        /* first check for a standard conversion */
+        Converter converter = standardConverterMap.get(key);
+        if (converter == null)
+        {
+            /* then the converters cache map */
+            converter = converterCacheMap.get(key);
+            if (converter == null)
+            {
+                /* check for conversion towards string */
+                if (formal == String.class)
+                {
+                    converter = toString;
+                }
+                /* check for String -> Enum constant conversion */
+                else if ((formal instanceof Class) && ((Class)formal).isEnum() && actual == String.class)
+                {
+                    final Class<Enum> enumClass = (Class<Enum>)formal;
+                    converter = new Converter()
+                    {
+                        @Override
+                        public Object convert(Object o)
+                        {
+                            return Enum.valueOf(enumClass, (String) o);
+                        }
+                    };
+                }
+
+                converterCacheMap.put(key, converter == null ? cacheMiss : converter);
+            }
+        }
+        return converter == cacheMiss ? null : converter;
+    }
+
+    /**
+     * Add the given converter to the handler.
+     *
+     * @param formal expected formal type
+     * @param actual provided argument type
+     * @param converter converter
+     * @since 2.1
+     */
+    @Override
+    public void addConverter(Type formal, Class actual, Converter converter)
+    {
+        Pair<Type, Class> key = new Pair<>(formal, actual);
+        converterCacheMap.put(key, converter);
+        if (formal instanceof Class)
+        {
+            Class formalClass = (Class)formal;
+            if (formalClass.isPrimitive())
+            {
+                key = new Pair<>((Type)IntrospectionUtils.getBoxedClass(formalClass), actual);
+                converterCacheMap.put(key, converter);
+            }
+            else
+            {
+                Class unboxedFormal = IntrospectionUtils.getUnboxedClass(formalClass);
+                if (unboxedFormal != formalClass)
+                {
+                    key = new Pair<>((Type)unboxedFormal, actual);
+                    converterCacheMap.put(key, converter);
+                }
+            }
+        }
+    }
+}

Modified: velocity/engine/branches/VELOCITY-892/velocity-engine-core/src/main/java/org/apache/velocity/util/introspection/UberspectImpl.java
URL: http://svn.apache.org/viewvc/velocity/engine/branches/VELOCITY-892/velocity-engine-core/src/main/java/org/apache/velocity/util/introspection/UberspectImpl.java?rev=1844087&r1=1844086&r2=1844087&view=diff
==============================================================================
--- velocity/engine/branches/VELOCITY-892/velocity-engine-core/src/main/java/org/apache/velocity/util/introspection/UberspectImpl.java (original)
+++ velocity/engine/branches/VELOCITY-892/velocity-engine-core/src/main/java/org/apache/velocity/util/introspection/UberspectImpl.java Wed Oct 17 11:42:10 2018
@@ -19,6 +19,7 @@ package org.apache.velocity.util.introsp
  * under the License.
  */
 
+import org.apache.commons.lang3.Conversion;
 import org.apache.velocity.exception.VelocityException;
 import org.apache.velocity.runtime.RuntimeConstants;
 import org.apache.velocity.runtime.RuntimeServices;
@@ -42,6 +43,7 @@ import org.slf4j.Logger;
 import java.lang.reflect.Array;
 import java.lang.reflect.InvocationTargetException;
 import java.lang.reflect.Method;
+import java.lang.reflect.Type;
 import java.util.Enumeration;
 import java.util.Iterator;
 import java.util.Map;
@@ -69,7 +71,7 @@ public class UberspectImpl implements Ub
     /**
      * the conversion handler
      */
-    protected ConversionHandler conversionHandler;
+    protected TypeConversionHandler conversionHandler;
 
     /**
      * runtime services
@@ -86,7 +88,7 @@ public class UberspectImpl implements Ub
         introspector = new Introspector(log, conversionHandler);
     }
 
-    public ConversionHandler getConversionHandler()
+    public TypeConversionHandler getConversionHandler()
     {
         return conversionHandler;
     }
@@ -95,6 +97,7 @@ public class UberspectImpl implements Ub
      * sets the runtime services
      * @param rs runtime services
      */
+    @SuppressWarnings("deprecation")
     public void setRuntimeServices(RuntimeServices rs)
     {
         rsvc = rs;
@@ -129,17 +132,45 @@ public class UberspectImpl implements Ub
                 throw new VelocityException("Cannot access class '" + conversionHandlerClass + "'", ae);
             }
 
-            if (!(o instanceof ConversionHandler))
+            if (o instanceof ConversionHandler)
+            {
+                log.warn("The ConversionHandler interface is deprecated - see the TypeConversionHandler interface");
+                final ConversionHandler ch = (ConversionHandler)o;
+                conversionHandler = new TypeConversionHandler()
+                {
+                    @Override
+                    public boolean isExplicitlyConvertible(Type formal, Class actual, boolean possibleVarArg)
+                    {
+                        if (formal instanceof Class) return ch.isExplicitlyConvertible((Class)formal, actual, possibleVarArg);
+                        else throw new UnsupportedOperationException("This conversion handler doesn't handle Types which aren't Classes");
+                    }
+
+                    @Override
+                    public Converter getNeededConverter(Type formal, Class actual)
+                    {
+                        if (formal instanceof Class) return ch.getNeededConverter((Class)formal, actual);
+                        else throw new UnsupportedOperationException("This conversion handler doesn't handle Types which aren't Classes");
+                    }
+
+                    @Override
+                    public void addConverter(Type formal, Class actual, Converter converter)
+                    {
+                        if (formal instanceof Class) ch.addConverter((Class)formal, actual, converter);
+                        else throw new UnsupportedOperationException("This conversion handler doesn't handle Types which aren't Classes");
+                    }
+                };
+            }
+            else if (!(o instanceof TypeConversionHandler))
             {
                 String err = "The specified class for ResourceManager (" + conversionHandlerClass
-                        + ") does not implement " + ConversionHandler.class.getName()
+                        + ") does not implement " + TypeConversionHandler.class.getName()
                         + "; Velocity is not initialized correctly.";
 
                 log.error(err);
                 throw new VelocityException(err);
             }
 
-            conversionHandler = (ConversionHandler) o;
+            conversionHandler = (TypeConversionHandler) o;
         }
     }
 
@@ -256,7 +287,7 @@ public class UberspectImpl implements Ub
         Method m = introspector.getMethod(obj.getClass(), methodName, args);
         if (m != null)
         {
-            return new VelMethodImpl(m, false, getNeededConverters(m.getParameterTypes(), args));
+            return new VelMethodImpl(m, false, getNeededConverters(m.getGenericParameterTypes(), args));
         }
 
         Class cls = obj.getClass();
@@ -269,7 +300,7 @@ public class UberspectImpl implements Ub
             {
                 // and create a method that knows to wrap the value
                 // before invoking the method
-                return new VelMethodImpl(m, true, getNeededConverters(m.getParameterTypes(), args));
+                return new VelMethodImpl(m, true, getNeededConverters(m.getGenericParameterTypes(), args));
             }
         }
         // watch for classes, to allow calling their static methods (VELOCITY-102)
@@ -278,7 +309,7 @@ public class UberspectImpl implements Ub
             m = introspector.getMethod((Class)obj, methodName, args);
             if (m != null)
             {
-                return new VelMethodImpl(m, false, getNeededConverters(m.getParameterTypes(), args));
+                return new VelMethodImpl(m, false, getNeededConverters(m.getGenericParameterTypes(), args));
             }
         }
         return null;
@@ -288,7 +319,7 @@ public class UberspectImpl implements Ub
      * get the list of needed converters to adapt passed argument types to method types
      * @return null if not conversion needed, otherwise an array containing needed converters
      */
-    private Converter[] getNeededConverters(Class[] expected, Object[] provided)
+    private Converter[] getNeededConverters(Type[] expected, Object[] provided)
     {
         if (conversionHandler == null) return null;
         // var args are not handled here - CB TODO

Modified: velocity/engine/branches/VELOCITY-892/velocity-engine-core/src/main/resources/org/apache/velocity/runtime/defaults/velocity.properties
URL: http://svn.apache.org/viewvc/velocity/engine/branches/VELOCITY-892/velocity-engine-core/src/main/resources/org/apache/velocity/runtime/defaults/velocity.properties?rev=1844087&r1=1844086&r2=1844087&view=diff
==============================================================================
--- velocity/engine/branches/VELOCITY-892/velocity-engine-core/src/main/resources/org/apache/velocity/runtime/defaults/velocity.properties (original)
+++ velocity/engine/branches/VELOCITY-892/velocity-engine-core/src/main/resources/org/apache/velocity/runtime/defaults/velocity.properties Wed Oct 17 11:42:10 2018
@@ -210,7 +210,7 @@ runtime.introspector.uberspect = org.apa
 # Sets the data types Conversion Handler used by the default uberspector
 # ----------------------------------------------------------------------------
 
-runtime.conversion.handler.class = org.apache.velocity.util.introspection.ConversionHandlerImpl
+runtime.conversion.handler.class = org.apache.velocity.util.introspection.TypeConversionHandlerImpl
 
 
 # ----------------------------------------------------------------------------

Modified: velocity/engine/branches/VELOCITY-892/velocity-engine-core/src/test/java/org/apache/velocity/test/util/introspection/ConversionHandlerTestCase.java
URL: http://svn.apache.org/viewvc/velocity/engine/branches/VELOCITY-892/velocity-engine-core/src/test/java/org/apache/velocity/test/util/introspection/ConversionHandlerTestCase.java?rev=1844087&r1=1844086&r2=1844087&view=diff
==============================================================================
--- velocity/engine/branches/VELOCITY-892/velocity-engine-core/src/test/java/org/apache/velocity/test/util/introspection/ConversionHandlerTestCase.java (original)
+++ velocity/engine/branches/VELOCITY-892/velocity-engine-core/src/test/java/org/apache/velocity/test/util/introspection/ConversionHandlerTestCase.java Wed Oct 17 11:42:10 2018
@@ -28,11 +28,11 @@ import org.apache.velocity.context.Conte
 import org.apache.velocity.runtime.RuntimeConstants;
 import org.apache.velocity.runtime.RuntimeInstance;
 import org.apache.velocity.test.BaseTestCase;
-import org.apache.velocity.util.introspection.ConversionHandler;
-import org.apache.velocity.util.introspection.ConversionHandlerImpl;
+import org.apache.velocity.util.introspection.TypeConversionHandlerImpl;
 import org.apache.velocity.util.introspection.Converter;
 import org.apache.velocity.util.introspection.Info;
 import org.apache.velocity.util.introspection.IntrospectionUtils;
+import org.apache.velocity.util.introspection.TypeConversionHandler;
 import org.apache.velocity.util.introspection.Uberspect;
 import org.apache.velocity.util.introspection.UberspectImpl;
 
@@ -113,7 +113,7 @@ public class ConversionHandlerTestCase e
         Uberspect uberspect = ve.getUberspect();
         assertTrue(uberspect instanceof UberspectImpl);
         UberspectImpl ui = (UberspectImpl)uberspect;
-        ConversionHandler ch = ui.getConversionHandler();
+        TypeConversionHandler ch = ui.getConversionHandler();
         assertTrue(ch != null);
         ch.addConverter(Float.class, Obj.class, new Converter<Float>()
         {
@@ -295,10 +295,10 @@ public class ConversionHandlerTestCase e
 
     public static class Introspect
     {
-        private ConversionHandler handler;
+        private TypeConversionHandler handler;
         public Introspect()
         {
-            handler = new ConversionHandlerImpl();
+            handler = new TypeConversionHandlerImpl();
         }
         public boolean isStrictlyConvertible(Class expected, Class provided)
         {