diff --git a/org.springframework.expression/src/main/java/org/springframework/expression/spel/ast/FunctionReference.java b/org.springframework.expression/src/main/java/org/springframework/expression/spel/ast/FunctionReference.java index 3a53c9e0af3..2b326e895a3 100644 --- a/org.springframework.expression/src/main/java/org/springframework/expression/spel/ast/FunctionReference.java +++ b/org.springframework.expression/src/main/java/org/springframework/expression/spel/ast/FunctionReference.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2009 the original author or authors. + * Copyright 2002-2010 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,7 +16,6 @@ package org.springframework.expression.spel.ast; -import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.lang.reflect.Modifier; @@ -37,9 +36,10 @@ import org.springframework.util.ReflectionUtils; * function definition in an expression: "(#max = {|x,y|$x>$y?$x:$y};max(2,3))" Calling context defined function: * "#isEven(37)". Functions may also be static java methods, registered in the context prior to invocation of the * expression. - * - * Functions are very simplistic, the arguments are not part of the definition (right now), so the names must be unique. - * + * + *

Functions are very simplistic, the arguments are not part of the definition (right now), + * so the names must be unique. + * * @author Andy Clement * @since 3.0 */ @@ -65,7 +65,8 @@ public class FunctionReference extends SpelNodeImpl { } try { return executeFunctionJLRMethod(state, (Method) o.getValue()); - } catch (SpelEvaluationException se) { + } + catch (SpelEvaluationException se) { se.setPosition(getStartPosition()); throw se; } @@ -79,42 +80,37 @@ public class FunctionReference extends SpelNodeImpl { * @return the return value of the invoked Java method * @throws EvaluationException if there is any problem invoking the method */ - private TypedValue executeFunctionJLRMethod(ExpressionState state, Method m) throws EvaluationException { + private TypedValue executeFunctionJLRMethod(ExpressionState state, Method method) throws EvaluationException { Object[] functionArgs = getArguments(state); - if (!m.isVarArgs() && m.getParameterTypes().length != functionArgs.length) { - throw new SpelEvaluationException(SpelMessage.INCORRECT_NUMBER_OF_ARGUMENTS_TO_FUNCTION, functionArgs.length, m - .getParameterTypes().length); + if (!method.isVarArgs() && method.getParameterTypes().length != functionArgs.length) { + throw new SpelEvaluationException(SpelMessage.INCORRECT_NUMBER_OF_ARGUMENTS_TO_FUNCTION, + functionArgs.length, method.getParameterTypes().length); } // Only static methods can be called in this way - if (!Modifier.isStatic(m.getModifiers())) { - throw new SpelEvaluationException(getStartPosition(), SpelMessage.FUNCTION_MUST_BE_STATIC, m + if (!Modifier.isStatic(method.getModifiers())) { + throw new SpelEvaluationException(getStartPosition(), SpelMessage.FUNCTION_MUST_BE_STATIC, method .getDeclaringClass().getName() - + "." + m.getName(), name); + + "." + method.getName(), name); } // Convert arguments if necessary and remap them for varargs if required if (functionArgs != null) { TypeConverter converter = state.getEvaluationContext().getTypeConverter(); - ReflectionHelper.convertAllArguments(m.getParameterTypes(), m.isVarArgs(), converter, functionArgs); + ReflectionHelper.convertAllArguments(converter, functionArgs, method); } - if (m.isVarArgs()) { - functionArgs = ReflectionHelper.setupArgumentsForVarargsInvocation(m.getParameterTypes(), functionArgs); + if (method.isVarArgs()) { + functionArgs = ReflectionHelper.setupArgumentsForVarargsInvocation(method.getParameterTypes(), functionArgs); } try { - ReflectionUtils.makeAccessible(m); - Object result = m.invoke(m.getClass(), functionArgs); - return new TypedValue(result, new TypeDescriptor(new MethodParameter(m,-1))); - } catch (IllegalArgumentException e) { - throw new SpelEvaluationException(getStartPosition(), e, SpelMessage.EXCEPTION_DURING_FUNCTION_CALL, name, e - .getMessage()); - } catch (IllegalAccessException e) { - throw new SpelEvaluationException(getStartPosition(), e, SpelMessage.EXCEPTION_DURING_FUNCTION_CALL, name, e - .getMessage()); - } catch (InvocationTargetException e) { - throw new SpelEvaluationException(getStartPosition(), e, SpelMessage.EXCEPTION_DURING_FUNCTION_CALL, name, e - .getMessage()); + ReflectionUtils.makeAccessible(method); + Object result = method.invoke(method.getClass(), functionArgs); + return new TypedValue(result, new TypeDescriptor(new MethodParameter(method,-1))); + } + catch (Exception ex) { + throw new SpelEvaluationException(getStartPosition(), ex, SpelMessage.EXCEPTION_DURING_FUNCTION_CALL, + this.name, ex.getMessage()); } } diff --git a/org.springframework.expression/src/main/java/org/springframework/expression/spel/support/ReflectionHelper.java b/org.springframework.expression/src/main/java/org/springframework/expression/spel/support/ReflectionHelper.java index ecbca38ebcd..0db58662145 100644 --- a/org.springframework.expression/src/main/java/org/springframework/expression/spel/support/ReflectionHelper.java +++ b/org.springframework.expression/src/main/java/org/springframework/expression/spel/support/ReflectionHelper.java @@ -17,9 +17,11 @@ package org.springframework.expression.spel.support; import java.lang.reflect.Array; +import java.lang.reflect.Method; import java.util.ArrayList; import java.util.List; +import org.springframework.core.MethodParameter; import org.springframework.core.convert.TypeDescriptor; import org.springframework.expression.EvaluationException; import org.springframework.expression.TypeConverter; @@ -29,7 +31,7 @@ import org.springframework.util.Assert; 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. * * @author Andy Clement @@ -39,15 +41,15 @@ import org.springframework.util.ClassUtils; public class ReflectionHelper { /** - * Compare argument arrays and return information about whether they match. A supplied type converter and - * conversionAllowed flag allow for matches to take into account that a type may be transformed into a different - * type by the converter. + * Compare argument arrays and return information about whether they match. A supplied type converter + * and conversionAllowed flag allow for matches to take into account that a type may be transformed + * into a different type by the converter. * @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 typeConverter a registered type converter * @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) { Assert.isTrue(expectedArgTypes.length == suppliedArgTypes.length, @@ -110,11 +112,13 @@ public class ReflectionHelper { * @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 */ - public static ArgumentsMatchInfo compareArgumentsVarargs( + static ArgumentsMatchInfo compareArgumentsVarargs( 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[expectedArgTypes.length-1].isArray(), "Final expected argument should be array type (the varargs parameter)"); + Assert.isTrue(expectedArgTypes != null && expectedArgTypes.length > 0, + "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; List 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 - * them to the types specified as the required parameter types. The arguments are 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 + * Takes an input set of argument values and, following the positions specified in the int array, + * it converts them to the types specified as the required parameter types. The arguments are + * converted 'in-place' in the input array. * @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 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 */ - public static void convertArguments(Class[] requiredParameterTypes, boolean isVarargs, TypeConverter converter, - int[] argumentsRequiringConversion, Object[] arguments) throws EvaluationException { - - Assert.notNull(argumentsRequiringConversion,"should not be called if no conversions required"); - Assert.notNull(arguments,"should not be called if no conversions required"); - - Class varargsType = null; - if (isVarargs) { - 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; + static void convertArguments(TypeConverter converter, Object[] arguments, Object methodOrCtor, + int[] argumentsRequiringConversion, Integer varargsPosition) throws EvaluationException { + + for (int argPosition : argumentsRequiringConversion) { + TypeDescriptor targetType; + if (varargsPosition != null && argPosition >= varargsPosition) { + MethodParameter methodParam = MethodParameter.forMethodOrConstructor(methodOrCtor, varargsPosition); + targetType = new TypeDescriptor(methodParam, methodParam.getParameterType().getComponentType()); } 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 * 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 - * 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 - * @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 arguments the arguments to convert to the requested parameter types + * @param method the target Method * @throws SpelEvaluationException if there is a problem with conversion */ - public static void convertAllArguments(Class[] parameterTypes, boolean isVarargs, TypeConverter converter, - Object[] arguments) throws SpelEvaluationException { - - Assert.notNull(arguments,"should not be called if nothing to convert"); - - 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(); + public static void convertAllArguments(TypeConverter converter, Object[] arguments, Method method) throws SpelEvaluationException { + Integer varargsPosition = null; + if (method.isVarArgs()) { + Class[] paramTypes = method.getParameterTypes(); + varargsPosition = paramTypes.length - 1; } - for (int i = 0; i < arguments.length; i++) { - Class targetType = null; - if (isVarargs && i >= (parameterTypes.length - 1)) { - targetType = varargsType; + for (int argPosition = 0; argPosition < arguments.length; argPosition++) { + TypeDescriptor targetType; + if (varargsPosition != null && argPosition >= varargsPosition) { + MethodParameter methodParam = new MethodParameter(method, varargsPosition); + targetType = new TypeDescriptor(methodParam, methodParam.getParameterType().getComponentType()); } else { - targetType = parameterTypes[i]; + targetType = new TypeDescriptor(new MethodParameter(method, argPosition)); } try { - if (arguments[i] != null && arguments[i].getClass() != targetType) { + Object argument = arguments[argPosition]; + if (argument != null && !targetType.getObjectType().isInstance(argument)) { 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) { @@ -292,7 +287,7 @@ public class ReflectionHelper { throw (SpelEvaluationException)ex; } 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; // 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 if (argumentCount >= parameterCount) { arraySize = argumentCount - (parameterCount - 1); } Object[] repackagedArguments = (Object[]) Array.newInstance(requiredParameterTypes[parameterCount - 1].getComponentType(), arraySize); - // Copy all but the varargs arguments for (int i = 0; i < arraySize; i++) { repackagedArguments[i] = args[parameterCount + i - 1]; diff --git a/org.springframework.expression/src/main/java/org/springframework/expression/spel/support/ReflectiveConstructorExecutor.java b/org.springframework.expression/src/main/java/org/springframework/expression/spel/support/ReflectiveConstructorExecutor.java index 08d2b7dfa9c..2e050192594 100644 --- a/org.springframework.expression/src/main/java/org/springframework/expression/spel/support/ReflectiveConstructorExecutor.java +++ b/org.springframework.expression/src/main/java/org/springframework/expression/spel/support/ReflectiveConstructorExecutor.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2009 the original author or authors. + * Copyright 2002-2010 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -29,12 +29,15 @@ import org.springframework.util.ReflectionUtils; * A simple ConstructorExecutor implementation that runs a constructor using reflective invocation. * * @author Andy Clement + * @author Juergen Hoeller * @since 3.0 */ class ReflectiveConstructorExecutor implements ConstructorExecutor { private final Constructor ctor; + private final Integer varargsPosition; + // When the constructor was found, we will have determined if arguments need to be converted for it // to be invoked. Conversion won't be cheap so let's only do it if necessary. private final int[] argsRequiringConversion; @@ -42,23 +45,27 @@ class ReflectiveConstructorExecutor implements ConstructorExecutor { public ReflectiveConstructorExecutor(Constructor ctor, int[] argsRequiringConversion) { this.ctor = ctor; + if (ctor.isVarArgs()) { + Class[] paramTypes = ctor.getParameterTypes(); + this.varargsPosition = paramTypes.length - 1; + } + else { + this.varargsPosition = null; + } this.argsRequiringConversion = argsRequiringConversion; } public TypedValue execute(EvaluationContext context, Object... arguments) throws AccessException { try { - if (argsRequiringConversion != null && arguments != null) { - ReflectionHelper.convertArguments(this.ctor.getParameterTypes(), - this.ctor.isVarArgs(), context.getTypeConverter(), - this.argsRequiringConversion, arguments); + if (this.argsRequiringConversion != null && arguments != null) { + ReflectionHelper.convertArguments(context.getTypeConverter(), arguments, + this.ctor, this.argsRequiringConversion, this.varargsPosition); } if (this.ctor.isVarArgs()) { - arguments = ReflectionHelper.setupArgumentsForVarargsInvocation( - this.ctor.getParameterTypes(), arguments); + arguments = ReflectionHelper.setupArgumentsForVarargsInvocation(this.ctor.getParameterTypes(), arguments); } ReflectionUtils.makeAccessible(this.ctor); - return new TypedValue(this.ctor.newInstance(arguments), - TypeDescriptor.valueOf(this.ctor.getDeclaringClass())); + return new TypedValue(this.ctor.newInstance(arguments), TypeDescriptor.valueOf(this.ctor.getDeclaringClass())); } catch (Exception ex) { throw new AccessException("Problem invoking constructor: " + this.ctor, ex); diff --git a/org.springframework.expression/src/main/java/org/springframework/expression/spel/support/ReflectiveMethodExecutor.java b/org.springframework.expression/src/main/java/org/springframework/expression/spel/support/ReflectiveMethodExecutor.java index 4cdf47346aa..89b51f0ca03 100644 --- a/org.springframework.expression/src/main/java/org/springframework/expression/spel/support/ReflectiveMethodExecutor.java +++ b/org.springframework.expression/src/main/java/org/springframework/expression/spel/support/ReflectiveMethodExecutor.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2009 the original author or authors. + * Copyright 2002-2010 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -28,12 +28,15 @@ import org.springframework.util.ReflectionUtils; /** * @author Andy Clement + * @author Juergen Hoeller * @since 3.0 */ class ReflectiveMethodExecutor implements MethodExecutor { private final Method method; + private final Integer varargsPosition; + // When the method was found, we will have determined if arguments need to be converted for it // to be invoked. Conversion won't be cheap so let's only do it if necessary. private final int[] argsRequiringConversion; @@ -41,6 +44,13 @@ class ReflectiveMethodExecutor implements MethodExecutor { public ReflectiveMethodExecutor(Method theMethod, int[] argumentsRequiringConversion) { this.method = theMethod; + if (theMethod.isVarArgs()) { + Class[] paramTypes = theMethod.getParameterTypes(); + this.varargsPosition = paramTypes.length - 1; + } + else { + this.varargsPosition = null; + } this.argsRequiringConversion = argumentsRequiringConversion; } @@ -48,15 +58,17 @@ class ReflectiveMethodExecutor implements MethodExecutor { public TypedValue execute(EvaluationContext context, Object target, Object... arguments) throws AccessException { try { if (this.argsRequiringConversion != null && arguments != null) { - ReflectionHelper.convertArguments(this.method.getParameterTypes(), this.method.isVarArgs(), - context.getTypeConverter(), this.argsRequiringConversion, arguments); + ReflectionHelper.convertArguments( + context.getTypeConverter(), arguments, this.method, + this.argsRequiringConversion, this.varargsPosition); } if (this.method.isVarArgs()) { arguments = ReflectionHelper.setupArgumentsForVarargsInvocation(this.method.getParameterTypes(), arguments); } ReflectionUtils.makeAccessible(this.method); - return new TypedValue(this.method.invoke(target, arguments), new TypeDescriptor(new MethodParameter(method,-1))); - } catch (Exception ex) { + return new TypedValue(this.method.invoke(target, arguments), new TypeDescriptor(new MethodParameter(this.method, -1))); + } + catch (Exception ex) { throw new AccessException("Problem invoking method: " + this.method, ex); } } diff --git a/org.springframework.expression/src/test/java/org/springframework/expression/spel/ExpressionTestsUsingCoreConversionService.java b/org.springframework.expression/src/test/java/org/springframework/expression/spel/ExpressionTestsUsingCoreConversionService.java index 8e7d7cf80fd..396849d29ee 100644 --- a/org.springframework.expression/src/test/java/org/springframework/expression/spel/ExpressionTestsUsingCoreConversionService.java +++ b/org.springframework.expression/src/test/java/org/springframework/expression/spel/ExpressionTestsUsingCoreConversionService.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2009 the original author or authors. + * Copyright 2002-2010 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -17,12 +17,14 @@ package org.springframework.expression.spel; import java.util.ArrayList; +import java.util.Collection; import java.util.List; -import junit.framework.Assert; - +import static junit.framework.Assert.*; import org.junit.Before; import org.junit.Test; + +import org.springframework.core.MethodParameter; import org.springframework.core.convert.ConversionService; import org.springframework.core.convert.TypeDescriptor; import org.springframework.core.convert.support.ConversionServiceFactory; @@ -32,9 +34,11 @@ import org.springframework.expression.TypeConverter; import org.springframework.expression.spel.support.StandardEvaluationContext; /** - * Expression evaluation where the TypeConverter plugged in is the {@link org.springframework.core.convert.support.GenericConversionService} - * + * Expression evaluation where the TypeConverter plugged in is the + * {@link org.springframework.core.convert.support.GenericConversionService}. + * * @author Andy Clement + * @author Dave Syer */ public class ExpressionTestsUsingCoreConversionService extends ExpressionTestCase { @@ -68,31 +72,66 @@ public class ExpressionTestsUsingCoreConversionService extends ExpressionTestCas // ArrayList containing List to List Class clazz = typeDescriptorForListOfString.getElementType(); - Assert.assertEquals(String.class,clazz); + assertEquals(String.class,clazz); List l = (List) tcs.convertValue(listOfInteger, typeDescriptorForListOfString); - Assert.assertNotNull(l); + assertNotNull(l); // ArrayList containing List to List clazz = typeDescriptorForListOfInteger.getElementType(); - Assert.assertEquals(Integer.class,clazz); + assertEquals(Integer.class,clazz); l = (List) tcs.convertValue(listOfString, typeDescriptorForListOfString); - Assert.assertNotNull(l); + assertNotNull(l); } @Test public void testSetParameterizedList() throws Exception { StandardEvaluationContext context = TestScenarioCreator.getTestEvaluationContext(); Expression e = parser.parseExpression("listOfInteger.size()"); - Assert.assertEquals(0,e.getValue(context,Integer.class).intValue()); + assertEquals(0,e.getValue(context,Integer.class).intValue()); context.setTypeConverter(new TypeConvertorUsingConversionService()); // Assign a List to the List field - the component elements should be converted parser.parseExpression("listOfInteger").setValue(context,listOfString); - Assert.assertEquals(3,e.getValue(context,Integer.class).intValue()); // size now 3 + assertEquals(3,e.getValue(context,Integer.class).intValue()); // size now 3 Class clazz = parser.parseExpression("listOfInteger[1].getClass()").getValue(context,Class.class); // element type correctly Integer - Assert.assertEquals(Integer.class,clazz); + assertEquals(Integer.class,clazz); } - + + @Test + public void testCoercionToCollectionOfPrimitive() throws Exception { + + class TestTarget { + @SuppressWarnings("unused") + public int sum(Collection numbers) { + int total = 0; + for (int i : numbers) { + total += i; + } + return total; + } + } + + StandardEvaluationContext evaluationContext = new StandardEvaluationContext(); + + TypeDescriptor collectionType = new TypeDescriptor(new MethodParameter(TestTarget.class.getDeclaredMethod( + "sum", Collection.class), 0)); + // The type conversion is possible + assertTrue(evaluationContext.getTypeConverter() + .canConvert(TypeDescriptor.valueOf(String.class), collectionType)); + // ... and it can be done successfully + assertEquals("[1, 2, 3, 4]", evaluationContext.getTypeConverter().convertValue("1,2,3,4", + TypeDescriptor.valueOf(String.class), collectionType).toString()); + + + evaluationContext.setVariable("target", new TestTarget()); + + // OK up to here, so the evaluation should be fine... + // ... but this fails + int result = (Integer) parser.parseExpression("#target.sum(#root)").getValue(evaluationContext, "1,2,3,4"); + assertEquals("Wrong result: " + result, 10, result); + + } + /** * Type converter that uses the core conversion service. diff --git a/org.springframework.expression/src/test/java/org/springframework/expression/spel/ScenariosForSpringSecurity.java b/org.springframework.expression/src/test/java/org/springframework/expression/spel/ScenariosForSpringSecurity.java index be5d749839e..b73d9c94b7f 100644 --- a/org.springframework.expression/src/test/java/org/springframework/expression/spel/ScenariosForSpringSecurity.java +++ b/org.springframework.expression/src/test/java/org/springframework/expression/spel/ScenariosForSpringSecurity.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2009 the original author or authors. + * Copyright 2002-2010 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -19,8 +19,8 @@ package org.springframework.expression.spel; import java.lang.reflect.Method; import junit.framework.Assert; - import org.junit.Test; + import org.springframework.core.MethodParameter; import org.springframework.core.convert.TypeDescriptor; import org.springframework.expression.AccessException; @@ -285,7 +285,7 @@ public class ScenariosForSpringSecurity extends ExpressionTestCase { Method m = HasRoleExecutor.class.getMethod("hasRole", String[].class); Object[] args = arguments; if (args != null) { - ReflectionHelper.convertAllArguments(m.getParameterTypes(), m.isVarArgs(), tc, args); + ReflectionHelper.convertAllArguments(tc, args, m); } if (m.isVarArgs()) { args = ReflectionHelper.setupArgumentsForVarargsInvocation(m.getParameterTypes(), args); diff --git a/org.springframework.expression/src/test/java/org/springframework/expression/spel/HelperTests.java b/org.springframework.expression/src/test/java/org/springframework/expression/spel/support/ReflectionHelperTests.java similarity index 92% rename from org.springframework.expression/src/test/java/org/springframework/expression/spel/HelperTests.java rename to org.springframework.expression/src/test/java/org/springframework/expression/spel/support/ReflectionHelperTests.java index 19fb78e0394..9cd60fdf94d 100644 --- a/org.springframework.expression/src/test/java/org/springframework/expression/spel/HelperTests.java +++ b/org.springframework.expression/src/test/java/org/springframework/expression/spel/support/ReflectionHelperTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2009 the original author or authors. + * Copyright 2002-2010 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,27 +13,28 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.springframework.expression.spel; + +package org.springframework.expression.spel.support; import java.io.ByteArrayOutputStream; import java.io.PrintStream; +import java.lang.reflect.Method; import java.util.ArrayList; import java.util.List; import junit.framework.Assert; - import org.junit.Test; + import org.springframework.expression.EvaluationContext; -import org.springframework.expression.EvaluationException; import org.springframework.expression.ParseException; import org.springframework.expression.PropertyAccessor; import org.springframework.expression.TypedValue; +import org.springframework.expression.spel.ExpressionTestCase; +import org.springframework.expression.spel.SpelEvaluationException; +import org.springframework.expression.spel.SpelMessage; +import org.springframework.expression.spel.SpelUtilities; import org.springframework.expression.spel.ast.FormatHelper; import org.springframework.expression.spel.standard.SpelExpression; -import org.springframework.expression.spel.support.ReflectionHelper; -import org.springframework.expression.spel.support.ReflectivePropertyAccessor; -import org.springframework.expression.spel.support.StandardEvaluationContext; -import org.springframework.expression.spel.support.StandardTypeConverter; import org.springframework.expression.spel.support.ReflectionHelper.ArgsMatchKind; /** @@ -41,7 +42,7 @@ import org.springframework.expression.spel.support.ReflectionHelper.ArgsMatchKin * * @author Andy Clement */ -public class HelperTests extends ExpressionTestCase { +public class ReflectionHelperTests extends ExpressionTestCase { @Test public void testFormatHelperForClassName() { @@ -199,63 +200,68 @@ public class HelperTests extends ExpressionTestCase { checkMatch2(new Class[]{Integer.class,Integer.class,Integer.class},new Class[]{Integer.class,String[].class},tc,ArgsMatchKind.REQUIRES_CONVERSION,1,2); // what happens on (Integer,String) passed to (Integer[]) ? } - + @Test public void testConvertArguments() throws Exception { StandardTypeConverter tc = new StandardTypeConverter(); + Method oneArg = TestInterface.class.getMethod("oneArg", String.class); + Method twoArg = TestInterface.class.getMethod("twoArg", String.class, String[].class); // basic conversion int>String Object[] args = new Object[]{3}; - ReflectionHelper.convertArguments(new Class[]{String.class}, false, tc, new int[]{0}, args); + ReflectionHelper.convertArguments(tc, args, oneArg, new int[]{0}, null); checkArguments(args, "3"); // varargs but nothing to convert args = new Object[]{3}; - ReflectionHelper.convertArguments(new Class[]{String.class,String[].class}, false, tc, new int[]{0}, args); + ReflectionHelper.convertArguments(tc, args, twoArg, new int[]{0}, 1); checkArguments(args, "3"); // varargs with nothing needing conversion args = new Object[]{3,"abc","abc"}; - ReflectionHelper.convertArguments(new Class[]{String.class,String[].class}, true, tc, new int[]{0,1,2}, args); + ReflectionHelper.convertArguments(tc, args, twoArg, new int[]{0,1,2}, 1); checkArguments(args, "3","abc","abc"); // varargs with conversion required args = new Object[]{3,false,3.0d}; - ReflectionHelper.convertArguments(new Class[]{String.class,String[].class}, true, tc, new int[]{0,1,2}, args); + ReflectionHelper.convertArguments(tc, args, twoArg, new int[]{0,1,2}, 1); checkArguments(args, "3","false","3.0"); } @Test - public void testConvertArguments2() throws EvaluationException { + public void testConvertArguments2() throws Exception { StandardTypeConverter tc = new StandardTypeConverter(); + Method oneArg = TestInterface.class.getMethod("oneArg", String.class); + Method twoArg = TestInterface.class.getMethod("twoArg", String.class, String[].class); // Simple conversion: int to string Object[] args = new Object[]{3}; - ReflectionHelper.convertAllArguments(new Class[]{String.class}, false, tc, args); + ReflectionHelper.convertAllArguments(tc, args, oneArg); checkArguments(args,"3"); // varargs conversion args = new Object[]{3,false,3.0f}; - ReflectionHelper.convertAllArguments(new Class[]{String.class,String[].class}, true, tc, args); + ReflectionHelper.convertAllArguments(tc, args, twoArg); checkArguments(args,"3","false","3.0"); // varargs conversion but no varargs args = new Object[]{3}; - ReflectionHelper.convertAllArguments(new Class[]{String.class,String[].class}, true, tc, args); + ReflectionHelper.convertAllArguments(tc, args, twoArg); checkArguments(args,"3"); // missing converter args = new Object[]{3,false,3.0f}; try { - ReflectionHelper.convertAllArguments(new Class[]{String.class,String[].class}, true, null, args); + ReflectionHelper.convertAllArguments(null, args, twoArg); Assert.fail("Should have failed because no converter supplied"); - } catch (SpelEvaluationException se) { + } + catch (SpelEvaluationException se) { Assert.assertEquals(SpelMessage.TYPE_CONVERSION_ERROR,se.getMessageCode()); } // null value args = new Object[]{3,null,3.0f}; - ReflectionHelper.convertAllArguments(new Class[]{String.class,String[].class}, true, tc, args); + ReflectionHelper.convertAllArguments(tc, args, twoArg); checkArguments(args,"3",null,"3.0"); } @@ -486,4 +492,13 @@ public class HelperTests extends ExpressionTestCase { private void checkArgument(Object expected, Object actual) { Assert.assertEquals(expected,actual); } + + + public interface TestInterface { + + void oneArg(String arg1); + + void twoArg(String arg1, String... arg2); + } + }