aboutsummaryrefslogtreecommitdiff
path: root/velocity-engine-core/src/main/java/org/apache/velocity/util/introspection/MethodMap.java
diff options
context:
space:
mode:
Diffstat (limited to 'velocity-engine-core/src/main/java/org/apache/velocity/util/introspection/MethodMap.java')
-rw-r--r--velocity-engine-core/src/main/java/org/apache/velocity/util/introspection/MethodMap.java164
1 files changed, 93 insertions, 71 deletions
diff --git a/velocity-engine-core/src/main/java/org/apache/velocity/util/introspection/MethodMap.java b/velocity-engine-core/src/main/java/org/apache/velocity/util/introspection/MethodMap.java
index 3fcdccee..f9f1e000 100644
--- a/velocity-engine-core/src/main/java/org/apache/velocity/util/introspection/MethodMap.java
+++ b/velocity-engine-core/src/main/java/org/apache/velocity/util/introspection/MethodMap.java
@@ -19,9 +19,11 @@ package org.apache.velocity.util.introspection;
* 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 && TypeUtils.isArrayType(methodTypes[methodTypes.length - 1]);
}
}
@@ -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 = TypeUtils.getArrayComponentType(t2[l2 - 1]);
/* 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 = TypeUtils.getArrayComponentType(t1[l1 - 1]);
/* 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])
+ Class c1 = t1[i] == null ? null : IntrospectionUtils.getTypeClass(t1[i]);
+ Class c2 = t2[i] == null ? null : IntrospectionUtils.getTypeClass(t2[i]);
+ boolean last = !fixedLengths && (i == t1.length - 1);
+ if (t1[i] == null && t2[i] != null || t1[i] != null && t2[i] == null || !t1[i].equals(t2[i]))
{
- if (c1[i] == null)
+ if (t1[i] == null)
{
fromC2toC1 = NOT_CONVERTIBLE;
- if (c2[i].isPrimitive())
+ if (c2 != null && c2.isPrimitive())
{
fromC1toC2 = NOT_CONVERTIBLE;
}
}
- else if (c2[i] == null)
+ else if (t2[i] == null)
{
fromC1toC2 = NOT_CONVERTIBLE;
- if (c1[i].isPrimitive())
+ if (c1 != null && c1.isPrimitive())
{
fromC2toC1 = NOT_CONVERTIBLE;
}
}
else
{
- switch (fromC1toC2)
+ if (c1 != null)
{
- 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 (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 (c2 != null)
+ {
+ 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;
+ }
}
- switch (fromC2toC1)
+ else if (fromC2toC1 > NOT_CONVERTIBLE)
{
- 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;
+ 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 && TypeUtils.isArrayType (t1[t1.length - 1]);
+ boolean last2Array = t2IsVararag || !fixedLengths && TypeUtils.isArrayType(t2[t2.length - 1]);
if (last1Array && !last2Array)
{
return LESS_SPECIFIC;
@@ -499,14 +521,13 @@ 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())
+ if (methodArgs.length == classes.length + 1 && TypeUtils.isArrayType(methodArgs[methodArgs.length - 1]))
{
// all the args preceding the vararg must match
for (int i = 0; i < classes.length; i++)
@@ -541,13 +562,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 && TypeUtils.isArrayType(methodArgs[i]);
+ 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 +584,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 (!TypeUtils.isArrayType(lastarg))
{
return NOT_CONVERTIBLE;
}
@@ -589,7 +611,7 @@ public class MethodMap
}
// check that all remaining arguments are convertible to the vararg type
- Class vararg = lastarg.getComponentType();
+ Type vararg = TypeUtils.getArrayComponentType(lastarg);
for (int i = methodArgs.length - 1; i < classes.length; ++i)
{
if (!isStrictConvertible(vararg, classes[i], false))
@@ -621,7 +643,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 +658,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 +672,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);
}