|
|
|
@ -17,9 +17,11 @@ |
|
|
|
package org.springframework.expression.spel.support; |
|
|
|
package org.springframework.expression.spel.support; |
|
|
|
|
|
|
|
|
|
|
|
import java.lang.reflect.Array; |
|
|
|
import java.lang.reflect.Array; |
|
|
|
|
|
|
|
import java.lang.reflect.Method; |
|
|
|
import java.util.ArrayList; |
|
|
|
import java.util.ArrayList; |
|
|
|
import java.util.List; |
|
|
|
import java.util.List; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
import org.springframework.core.MethodParameter; |
|
|
|
import org.springframework.core.convert.TypeDescriptor; |
|
|
|
import org.springframework.core.convert.TypeDescriptor; |
|
|
|
import org.springframework.expression.EvaluationException; |
|
|
|
import org.springframework.expression.EvaluationException; |
|
|
|
import org.springframework.expression.TypeConverter; |
|
|
|
import org.springframework.expression.TypeConverter; |
|
|
|
@ -29,7 +31,7 @@ import org.springframework.util.Assert; |
|
|
|
import org.springframework.util.ClassUtils; |
|
|
|
import org.springframework.util.ClassUtils; |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
/** |
|
|
|
* Utility methods used by the reflection resolver code to discover the appropriae |
|
|
|
* Utility methods used by the reflection resolver code to discover the appropriate |
|
|
|
* methods/constructors and fields that should be used in expressions. |
|
|
|
* methods/constructors and fields that should be used in expressions. |
|
|
|
* |
|
|
|
* |
|
|
|
* @author Andy Clement |
|
|
|
* @author Andy Clement |
|
|
|
@ -39,15 +41,15 @@ import org.springframework.util.ClassUtils; |
|
|
|
public class ReflectionHelper { |
|
|
|
public class ReflectionHelper { |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
/** |
|
|
|
* Compare argument arrays and return information about whether they match. A supplied type converter and |
|
|
|
* Compare argument arrays and return information about whether they match. A supplied type converter |
|
|
|
* conversionAllowed flag allow for matches to take into account that a type may be transformed into a different |
|
|
|
* and conversionAllowed flag allow for matches to take into account that a type may be transformed |
|
|
|
* type by the converter. |
|
|
|
* into a different type by the converter. |
|
|
|
* @param expectedArgTypes the array of types the method/constructor is expecting |
|
|
|
* @param expectedArgTypes the array of types the method/constructor is expecting |
|
|
|
* @param suppliedArgTypes the array of types that are being supplied at the point of invocation |
|
|
|
* @param suppliedArgTypes the array of types that are being supplied at the point of invocation |
|
|
|
* @param typeConverter a registered type converter |
|
|
|
* @param typeConverter a registered type converter |
|
|
|
* @return a MatchInfo object indicating what kind of match it was or null if it was not a match |
|
|
|
* @return a MatchInfo object indicating what kind of match it was or null if it was not a match |
|
|
|
*/ |
|
|
|
*/ |
|
|
|
public static ArgumentsMatchInfo compareArguments( |
|
|
|
static ArgumentsMatchInfo compareArguments( |
|
|
|
Class[] expectedArgTypes, Class[] suppliedArgTypes, TypeConverter typeConverter) { |
|
|
|
Class[] expectedArgTypes, Class[] suppliedArgTypes, TypeConverter typeConverter) { |
|
|
|
|
|
|
|
|
|
|
|
Assert.isTrue(expectedArgTypes.length == suppliedArgTypes.length, |
|
|
|
Assert.isTrue(expectedArgTypes.length == suppliedArgTypes.length, |
|
|
|
@ -110,11 +112,13 @@ public class ReflectionHelper { |
|
|
|
* @param typeConverter a registered type converter |
|
|
|
* @param typeConverter a registered type converter |
|
|
|
* @return a MatchInfo object indicating what kind of match it was or null if it was not a match |
|
|
|
* @return a MatchInfo object indicating what kind of match it was or null if it was not a match |
|
|
|
*/ |
|
|
|
*/ |
|
|
|
public static ArgumentsMatchInfo compareArgumentsVarargs( |
|
|
|
static ArgumentsMatchInfo compareArgumentsVarargs( |
|
|
|
Class[] expectedArgTypes, Class[] suppliedArgTypes, TypeConverter typeConverter) { |
|
|
|
Class[] expectedArgTypes, Class[] suppliedArgTypes, TypeConverter typeConverter) { |
|
|
|
|
|
|
|
|
|
|
|
Assert.isTrue(expectedArgTypes!=null && expectedArgTypes.length>0, "Expected arguments must at least include one array (the vargargs parameter)"); |
|
|
|
Assert.isTrue(expectedArgTypes != null && expectedArgTypes.length > 0, |
|
|
|
Assert.isTrue(expectedArgTypes[expectedArgTypes.length-1].isArray(), "Final expected argument should be array type (the varargs parameter)"); |
|
|
|
"Expected arguments must at least include one array (the vargargs parameter)"); |
|
|
|
|
|
|
|
Assert.isTrue(expectedArgTypes[expectedArgTypes.length - 1].isArray(), |
|
|
|
|
|
|
|
"Final expected argument should be array type (the varargs parameter)"); |
|
|
|
|
|
|
|
|
|
|
|
ArgsMatchKind match = ArgsMatchKind.EXACT; |
|
|
|
ArgsMatchKind match = ArgsMatchKind.EXACT; |
|
|
|
List<Integer> argsRequiringConversion = null; |
|
|
|
List<Integer> argsRequiringConversion = null; |
|
|
|
@ -214,76 +218,67 @@ public class ReflectionHelper { |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
/** |
|
|
|
* Takes an input set of argument values and, following the positions specified in the int array, it converts |
|
|
|
* Takes an input set of argument values and, following the positions specified in the int array, |
|
|
|
* them to the types specified as the required parameter types. The arguments are converted 'in-place' in the |
|
|
|
* it converts them to the types specified as the required parameter types. The arguments are |
|
|
|
* input array. |
|
|
|
* converted 'in-place' in the input array. |
|
|
|
* @param requiredParameterTypes the types that the caller would like to have |
|
|
|
|
|
|
|
* @param isVarargs whether the requiredParameterTypes is a varargs list |
|
|
|
|
|
|
|
* @param converter the type converter to use for attempting conversions |
|
|
|
* @param converter the type converter to use for attempting conversions |
|
|
|
* @param argumentsRequiringConversion details which of the input arguments need conversion |
|
|
|
|
|
|
|
* @param arguments the actual arguments that need conversion |
|
|
|
* @param arguments the actual arguments that need conversion |
|
|
|
|
|
|
|
* @param methodOrCtor the target Method or Constructor |
|
|
|
|
|
|
|
* @param argumentsRequiringConversion details which of the input arguments need conversion |
|
|
|
|
|
|
|
* @param varargsPosition the known position of the varargs argument, if any |
|
|
|
* @throws EvaluationException if a problem occurs during conversion |
|
|
|
* @throws EvaluationException if a problem occurs during conversion |
|
|
|
*/ |
|
|
|
*/ |
|
|
|
public static void convertArguments(Class[] requiredParameterTypes, boolean isVarargs, TypeConverter converter, |
|
|
|
static void convertArguments(TypeConverter converter, Object[] arguments, Object methodOrCtor, |
|
|
|
int[] argumentsRequiringConversion, Object[] arguments) throws EvaluationException { |
|
|
|
int[] argumentsRequiringConversion, Integer varargsPosition) throws EvaluationException { |
|
|
|
|
|
|
|
|
|
|
|
Assert.notNull(argumentsRequiringConversion,"should not be called if no conversions required"); |
|
|
|
for (int argPosition : argumentsRequiringConversion) { |
|
|
|
Assert.notNull(arguments,"should not be called if no conversions required"); |
|
|
|
TypeDescriptor targetType; |
|
|
|
|
|
|
|
if (varargsPosition != null && argPosition >= varargsPosition) { |
|
|
|
Class varargsType = null; |
|
|
|
MethodParameter methodParam = MethodParameter.forMethodOrConstructor(methodOrCtor, varargsPosition); |
|
|
|
if (isVarargs) { |
|
|
|
targetType = new TypeDescriptor(methodParam, methodParam.getParameterType().getComponentType()); |
|
|
|
Assert.isTrue(requiredParameterTypes[requiredParameterTypes.length-1].isArray(),"if varargs then last parameter type must be array"); |
|
|
|
|
|
|
|
varargsType = requiredParameterTypes[requiredParameterTypes.length - 1].getComponentType(); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
for (Integer argPosition : argumentsRequiringConversion) { |
|
|
|
|
|
|
|
Class<?> targetType = null; |
|
|
|
|
|
|
|
if (isVarargs && argPosition >= (requiredParameterTypes.length - 1)) { |
|
|
|
|
|
|
|
targetType = varargsType; |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
else { |
|
|
|
else { |
|
|
|
targetType = requiredParameterTypes[argPosition]; |
|
|
|
targetType = new TypeDescriptor(MethodParameter.forMethodOrConstructor(methodOrCtor, argPosition)); |
|
|
|
} |
|
|
|
} |
|
|
|
arguments[argPosition] = converter.convertValue(arguments[argPosition], TypeDescriptor.forObject(arguments[argPosition]), TypeDescriptor.valueOf(targetType)); |
|
|
|
arguments[argPosition] = converter.convertValue( |
|
|
|
|
|
|
|
arguments[argPosition], TypeDescriptor.forObject(arguments[argPosition]), targetType); |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
/** |
|
|
|
* Convert a supplied set of arguments into the requested types. If the parameterTypes are related to |
|
|
|
* 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 |
|
|
|
* a varargs method then the final entry in the parameterTypes array is going to be an array itself whose |
|
|
|
* component type should be used as the conversion target for extraneous arguments. (For example, if the |
|
|
|
* component type should be used as the conversion target for extraneous arguments. (For example, if the |
|
|
|
* parameterTypes are {Integer, String[]} and the input arguments are {Integer, boolean, float} then both |
|
|
|
* parameterTypes are {Integer, String[]} and the input arguments are {Integer, boolean, float} then both |
|
|
|
* the boolean and float must be converted to strings). This method does not repackage the arguments |
|
|
|
* the boolean and float must be converted to strings). This method does not repackage the arguments |
|
|
|
* into a form suitable for the varargs invocation |
|
|
|
* into a form suitable for the varargs invocation |
|
|
|
* @param parameterTypes the types to be converted to |
|
|
|
|
|
|
|
* @param isVarargs whether parameterTypes relates to a varargs method |
|
|
|
|
|
|
|
* @param converter the converter to use for type conversions |
|
|
|
* @param converter the converter to use for type conversions |
|
|
|
* @param arguments the arguments to convert to the requested parameter types |
|
|
|
* @param arguments the arguments to convert to the requested parameter types |
|
|
|
|
|
|
|
* @param method the target Method |
|
|
|
* @throws SpelEvaluationException if there is a problem with conversion |
|
|
|
* @throws SpelEvaluationException if there is a problem with conversion |
|
|
|
*/ |
|
|
|
*/ |
|
|
|
public static void convertAllArguments(Class[] parameterTypes, boolean isVarargs, TypeConverter converter, |
|
|
|
public static void convertAllArguments(TypeConverter converter, Object[] arguments, Method method) throws SpelEvaluationException { |
|
|
|
Object[] arguments) throws SpelEvaluationException { |
|
|
|
Integer varargsPosition = null; |
|
|
|
|
|
|
|
if (method.isVarArgs()) { |
|
|
|
Assert.notNull(arguments,"should not be called if nothing to convert"); |
|
|
|
Class[] paramTypes = method.getParameterTypes(); |
|
|
|
|
|
|
|
varargsPosition = paramTypes.length - 1; |
|
|
|
Class varargsType = null; |
|
|
|
|
|
|
|
if (isVarargs) { |
|
|
|
|
|
|
|
Assert.isTrue(parameterTypes[parameterTypes.length-1].isArray(),"if varargs then last parameter type must be array"); |
|
|
|
|
|
|
|
varargsType = parameterTypes[parameterTypes.length - 1].getComponentType(); |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
for (int i = 0; i < arguments.length; i++) { |
|
|
|
for (int argPosition = 0; argPosition < arguments.length; argPosition++) { |
|
|
|
Class<?> targetType = null; |
|
|
|
TypeDescriptor targetType; |
|
|
|
if (isVarargs && i >= (parameterTypes.length - 1)) { |
|
|
|
if (varargsPosition != null && argPosition >= varargsPosition) { |
|
|
|
targetType = varargsType; |
|
|
|
MethodParameter methodParam = new MethodParameter(method, varargsPosition); |
|
|
|
|
|
|
|
targetType = new TypeDescriptor(methodParam, methodParam.getParameterType().getComponentType()); |
|
|
|
} |
|
|
|
} |
|
|
|
else { |
|
|
|
else { |
|
|
|
targetType = parameterTypes[i]; |
|
|
|
targetType = new TypeDescriptor(new MethodParameter(method, argPosition)); |
|
|
|
} |
|
|
|
} |
|
|
|
try { |
|
|
|
try { |
|
|
|
if (arguments[i] != null && arguments[i].getClass() != targetType) { |
|
|
|
Object argument = arguments[argPosition]; |
|
|
|
|
|
|
|
if (argument != null && !targetType.getObjectType().isInstance(argument)) { |
|
|
|
if (converter == null) { |
|
|
|
if (converter == null) { |
|
|
|
throw new SpelEvaluationException(SpelMessage.TYPE_CONVERSION_ERROR, arguments[i].getClass().getName(),targetType); |
|
|
|
throw new SpelEvaluationException(SpelMessage.TYPE_CONVERSION_ERROR, argument.getClass().getName(), targetType); |
|
|
|
} |
|
|
|
} |
|
|
|
arguments[i] = converter.convertValue(arguments[i], TypeDescriptor.forObject(arguments[i]), TypeDescriptor.valueOf(targetType)); |
|
|
|
arguments[argPosition] = converter.convertValue(argument, TypeDescriptor.forObject(argument), targetType); |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
catch (EvaluationException ex) { |
|
|
|
catch (EvaluationException ex) { |
|
|
|
@ -292,7 +287,7 @@ public class ReflectionHelper { |
|
|
|
throw (SpelEvaluationException)ex; |
|
|
|
throw (SpelEvaluationException)ex; |
|
|
|
} |
|
|
|
} |
|
|
|
else { |
|
|
|
else { |
|
|
|
throw new SpelEvaluationException(ex, SpelMessage.TYPE_CONVERSION_ERROR,arguments[i].getClass().getName(),targetType); |
|
|
|
throw new SpelEvaluationException(ex, SpelMessage.TYPE_CONVERSION_ERROR,arguments[argPosition].getClass().getName(), targetType); |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
@ -313,14 +308,15 @@ public class ReflectionHelper { |
|
|
|
int argumentCount = args.length; |
|
|
|
int argumentCount = args.length; |
|
|
|
|
|
|
|
|
|
|
|
// Check if repackaging is needed:
|
|
|
|
// Check if repackaging is needed:
|
|
|
|
if (parameterCount != args.length || requiredParameterTypes[parameterCount - 1] != (args[argumentCount - 1] == null ? null : args[argumentCount - 1].getClass())) { |
|
|
|
if (parameterCount != args.length || |
|
|
|
|
|
|
|
requiredParameterTypes[parameterCount - 1] != |
|
|
|
|
|
|
|
(args[argumentCount - 1] == null ? null : args[argumentCount - 1].getClass())) { |
|
|
|
int arraySize = 0; // zero size array if nothing to pass as the varargs parameter
|
|
|
|
int arraySize = 0; // zero size array if nothing to pass as the varargs parameter
|
|
|
|
if (argumentCount >= parameterCount) { |
|
|
|
if (argumentCount >= parameterCount) { |
|
|
|
arraySize = argumentCount - (parameterCount - 1); |
|
|
|
arraySize = argumentCount - (parameterCount - 1); |
|
|
|
} |
|
|
|
} |
|
|
|
Object[] repackagedArguments = (Object[]) Array.newInstance(requiredParameterTypes[parameterCount - 1].getComponentType(), |
|
|
|
Object[] repackagedArguments = (Object[]) Array.newInstance(requiredParameterTypes[parameterCount - 1].getComponentType(), |
|
|
|
arraySize); |
|
|
|
arraySize); |
|
|
|
|
|
|
|
|
|
|
|
// Copy all but the varargs arguments
|
|
|
|
// Copy all but the varargs arguments
|
|
|
|
for (int i = 0; i < arraySize; i++) { |
|
|
|
for (int i = 0; i < arraySize; i++) { |
|
|
|
repackagedArguments[i] = args[parameterCount + i - 1]; |
|
|
|
repackagedArguments[i] = args[parameterCount + i - 1]; |
|
|
|
|