Fix SpEL varargs handling and usage of other getValue() methods
Building on the initial work for SPR-12326, this commit addresses three problems: Firstly the ReflectiveMethodResolver is modified to consider a direct parameter match more important than a varargs match. Also in that same type when there are a number of close matches, the first one is taken rather than the last one. Secondly more testcases and better support have been added for the case of passing a single parameter to a varargs accepting method. Finally it is possible to set the root context object indirectly and not pass it on getValue() calls to the expression objects but not all variants of getValue() were handling that. This is now fixed. Issue: SPR-12326
This commit is contained in:
@@ -109,7 +109,8 @@ public class SpelExpression implements Expression {
|
||||
Object result;
|
||||
if (this.compiledAst != null) {
|
||||
try {
|
||||
return this.compiledAst.getValue(null,null);
|
||||
TypedValue contextRoot = evaluationContext == null ? null : evaluationContext.getRootObject();
|
||||
return this.compiledAst.getValue(contextRoot == null ? null : contextRoot.getValue(), evaluationContext);
|
||||
}
|
||||
catch (Throwable ex) {
|
||||
// If running in mixed mode, revert to interpreted
|
||||
@@ -134,7 +135,7 @@ public class SpelExpression implements Expression {
|
||||
Object result;
|
||||
if (this.compiledAst != null) {
|
||||
try {
|
||||
return this.compiledAst.getValue(rootObject,null);
|
||||
return this.compiledAst.getValue(rootObject, evaluationContext);
|
||||
}
|
||||
catch (Throwable ex) {
|
||||
// If running in mixed mode, revert to interpreted
|
||||
@@ -159,7 +160,8 @@ public class SpelExpression implements Expression {
|
||||
public <T> T getValue(Class<T> expectedResultType) throws EvaluationException {
|
||||
if (this.compiledAst != null) {
|
||||
try {
|
||||
Object result = this.compiledAst.getValue(null,null);
|
||||
TypedValue contextRoot = evaluationContext == null ? null : evaluationContext.getRootObject();
|
||||
Object result = this.compiledAst.getValue(contextRoot == null ? null : contextRoot.getValue(), evaluationContext);
|
||||
if (expectedResultType == null) {
|
||||
return (T)result;
|
||||
}
|
||||
|
||||
@@ -253,8 +253,11 @@ public class ReflectionHelper {
|
||||
if (varargsPosition == arguments.length - 1) {
|
||||
TypeDescriptor targetType = new TypeDescriptor(methodParam);
|
||||
Object argument = arguments[varargsPosition];
|
||||
arguments[varargsPosition] = converter.convertValue(argument, TypeDescriptor.forObject(argument), targetType);
|
||||
conversionOccurred |= (argument != arguments[varargsPosition]);
|
||||
TypeDescriptor sourceType = TypeDescriptor.forObject(argument);
|
||||
arguments[varargsPosition] = converter.convertValue(argument, sourceType, targetType);
|
||||
if (!looksLikeSimpleArrayPackaging(sourceType, targetType)) {
|
||||
conversionOccurred |= (argument != arguments[varargsPosition]);
|
||||
}
|
||||
}
|
||||
else {
|
||||
TypeDescriptor targetType = new TypeDescriptor(methodParam).getElementTypeDescriptor();
|
||||
@@ -268,6 +271,80 @@ public class ReflectionHelper {
|
||||
return conversionOccurred;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the target type simply represents the array (possibly boxed/unboxed) form of sourceType.
|
||||
* @param sourceType the type of the original argument
|
||||
* @param actualType the type of the converted argument
|
||||
* @return
|
||||
*/
|
||||
private static boolean looksLikeSimpleArrayPackaging(TypeDescriptor sourceType, TypeDescriptor targetType) {
|
||||
TypeDescriptor td = targetType.getElementTypeDescriptor();
|
||||
if (td != null) {
|
||||
if (td.equals(sourceType)) {
|
||||
return true;
|
||||
}
|
||||
else { // check for boxing
|
||||
if (td.isPrimitive() || sourceType.isPrimitive()) {
|
||||
Class<?> targetElementClass = td.getType();
|
||||
Class<?> sourceElementClass = sourceType.getType();
|
||||
if (targetElementClass.isPrimitive()) {
|
||||
if (targetElementClass == Boolean.TYPE) {
|
||||
return sourceElementClass == Boolean.class;
|
||||
}
|
||||
else if (targetElementClass == Double.TYPE) {
|
||||
return sourceElementClass == Double.class;
|
||||
}
|
||||
else if (targetElementClass == Float.TYPE) {
|
||||
return sourceElementClass == Float.class;
|
||||
}
|
||||
else if (targetElementClass == Integer.TYPE) {
|
||||
return sourceElementClass == Integer.class;
|
||||
}
|
||||
else if (targetElementClass == Long.TYPE) {
|
||||
return sourceElementClass == Long.class;
|
||||
}
|
||||
else if (targetElementClass == Short.TYPE) {
|
||||
return sourceElementClass == Short.class;
|
||||
}
|
||||
else if (targetElementClass == Character.TYPE) {
|
||||
return sourceElementClass == Character.class;
|
||||
}
|
||||
else if (targetElementClass == Byte.TYPE) {
|
||||
return sourceElementClass == Byte.class;
|
||||
}
|
||||
}
|
||||
else if (sourceElementClass.isPrimitive()) {
|
||||
if (sourceElementClass == Boolean.TYPE) {
|
||||
return targetElementClass == Boolean.class;
|
||||
}
|
||||
else if (sourceElementClass == Double.TYPE) {
|
||||
return targetElementClass == Double.class;
|
||||
}
|
||||
else if (sourceElementClass == Float.TYPE) {
|
||||
return targetElementClass == Float.class;
|
||||
}
|
||||
else if (sourceElementClass == Integer.TYPE) {
|
||||
return targetElementClass == Integer.class;
|
||||
}
|
||||
else if (sourceElementClass == Long.TYPE) {
|
||||
return targetElementClass == Long.class;
|
||||
}
|
||||
else if (sourceElementClass == Character.TYPE) {
|
||||
return targetElementClass == Character.class;
|
||||
}
|
||||
else if (sourceElementClass == Short.TYPE) {
|
||||
return targetElementClass == Short.class;
|
||||
}
|
||||
else if (sourceElementClass == Byte.TYPE) {
|
||||
return targetElementClass == Byte.class;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a supplied set of arguments into the requested types. If the parameterTypes are related to
|
||||
* a varargs method then the final entry in the parameterTypes array is going to be an array itself whose
|
||||
|
||||
@@ -123,6 +123,18 @@ public class ReflectiveMethodResolver implements MethodResolver {
|
||||
public int compare(Method m1, Method m2) {
|
||||
int m1pl = m1.getParameterTypes().length;
|
||||
int m2pl = m2.getParameterTypes().length;
|
||||
// varargs methods go last
|
||||
if (m1pl == m2pl) {
|
||||
if (!m1.isVarArgs() && m2.isVarArgs()) {
|
||||
return -1;
|
||||
}
|
||||
else if (m1.isVarArgs() && !m2.isVarArgs()) {
|
||||
return 1;
|
||||
}
|
||||
else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
return (new Integer(m1pl)).compareTo(m2pl);
|
||||
}
|
||||
});
|
||||
@@ -163,7 +175,10 @@ public class ReflectiveMethodResolver implements MethodResolver {
|
||||
}
|
||||
else if (matchInfo.isCloseMatch()) {
|
||||
if (!this.useDistance) {
|
||||
closeMatch = method;
|
||||
// Take this as a close match if there isn't one already
|
||||
if (closeMatch == null) {
|
||||
closeMatch = method;
|
||||
}
|
||||
}
|
||||
else {
|
||||
int matchDistance = ReflectionHelper.getTypeDifferenceWeight(paramDescriptors, argumentTypes);
|
||||
|
||||
Reference in New Issue
Block a user