|
|
|
@ -40,15 +40,22 @@ import org.springframework.expression.spel.support.ReflectionHelper.ArgumentsMat |
|
|
|
|
|
|
|
|
|
|
|
import static org.assertj.core.api.Assertions.assertThat; |
|
|
|
import static org.assertj.core.api.Assertions.assertThat; |
|
|
|
import static org.assertj.core.api.Assertions.assertThatExceptionOfType; |
|
|
|
import static org.assertj.core.api.Assertions.assertThatExceptionOfType; |
|
|
|
|
|
|
|
import static org.springframework.expression.spel.support.ReflectionHelper.ArgumentsMatchKind.CLOSE; |
|
|
|
|
|
|
|
import static org.springframework.expression.spel.support.ReflectionHelper.ArgumentsMatchKind.EXACT; |
|
|
|
|
|
|
|
import static org.springframework.expression.spel.support.ReflectionHelper.ArgumentsMatchKind.REQUIRES_CONVERSION; |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
/** |
|
|
|
* Tests for reflection helper code. |
|
|
|
* Tests for {@link ReflectionHelper}, {@link SpelUtilities}, {@link TypedValue}, |
|
|
|
|
|
|
|
* {@link ReflectivePropertyAccessor}, ... |
|
|
|
* |
|
|
|
* |
|
|
|
* @author Andy Clement |
|
|
|
* @author Andy Clement |
|
|
|
* @author Sam Brannen |
|
|
|
* @author Sam Brannen |
|
|
|
*/ |
|
|
|
*/ |
|
|
|
class ReflectionHelperTests extends AbstractExpressionTests { |
|
|
|
class ReflectionHelperTests extends AbstractExpressionTests { |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private final StandardTypeConverter tc = new StandardTypeConverter(); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@Test |
|
|
|
@Test |
|
|
|
void utilities() throws ParseException { |
|
|
|
void utilities() throws ParseException { |
|
|
|
SpelExpression expr = (SpelExpression)parser.parseExpression("3+4+5+6+7-2"); |
|
|
|
SpelExpression expr = (SpelExpression)parser.parseExpression("3+4+5+6+7-2"); |
|
|
|
@ -100,44 +107,38 @@ class ReflectionHelperTests extends AbstractExpressionTests { |
|
|
|
|
|
|
|
|
|
|
|
@Test |
|
|
|
@Test |
|
|
|
void reflectionHelperCompareArguments_ExactMatching() { |
|
|
|
void reflectionHelperCompareArguments_ExactMatching() { |
|
|
|
StandardTypeConverter tc = new StandardTypeConverter(); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Calling foo(String) with (String) is exact match
|
|
|
|
// Calling foo(String) with (String) is exact match
|
|
|
|
checkMatch(new Class<?>[] {String.class}, new Class<?>[] {String.class}, tc, ReflectionHelper.ArgumentsMatchKind.EXACT); |
|
|
|
checkMatch(new Class<?>[] {String.class}, new Class<?>[] {String.class}, tc, EXACT); |
|
|
|
|
|
|
|
|
|
|
|
// Calling foo(String,Integer) with (String,Integer) is exact match
|
|
|
|
// Calling foo(String,Integer) with (String,Integer) is exact match
|
|
|
|
checkMatch(new Class<?>[] {String.class, Integer.class}, new Class<?>[] {String.class, Integer.class}, tc, ArgumentsMatchKind.EXACT); |
|
|
|
checkMatch(new Class<?>[] {String.class, Integer.class}, new Class<?>[] {String.class, Integer.class}, tc, EXACT); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
@Test |
|
|
|
@Test |
|
|
|
void reflectionHelperCompareArguments_CloseMatching() { |
|
|
|
void reflectionHelperCompareArguments_CloseMatching() { |
|
|
|
StandardTypeConverter tc = new StandardTypeConverter(); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Calling foo(List) with (ArrayList) is close match (no conversion required)
|
|
|
|
// Calling foo(List) with (ArrayList) is close match (no conversion required)
|
|
|
|
checkMatch(new Class<?>[] {ArrayList.class}, new Class<?>[] {List.class}, tc, ArgumentsMatchKind.CLOSE); |
|
|
|
checkMatch(new Class<?>[] {ArrayList.class}, new Class<?>[] {List.class}, tc, CLOSE); |
|
|
|
|
|
|
|
|
|
|
|
// Passing (Sub,String) on call to foo(Super,String) is close match
|
|
|
|
// Passing (Sub,String) on call to foo(Super,String) is close match
|
|
|
|
checkMatch(new Class<?>[] {Sub.class, String.class}, new Class<?>[] {Super.class, String.class}, tc, ArgumentsMatchKind.CLOSE); |
|
|
|
checkMatch(new Class<?>[] {Sub.class, String.class}, new Class<?>[] {Super.class, String.class}, tc, CLOSE); |
|
|
|
|
|
|
|
|
|
|
|
// Passing (String,Sub) on call to foo(String,Super) is close match
|
|
|
|
// Passing (String,Sub) on call to foo(String,Super) is close match
|
|
|
|
checkMatch(new Class<?>[] {String.class, Sub.class}, new Class<?>[] {String.class, Super.class}, tc, ArgumentsMatchKind.CLOSE); |
|
|
|
checkMatch(new Class<?>[] {String.class, Sub.class}, new Class<?>[] {String.class, Super.class}, tc, CLOSE); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
@Test |
|
|
|
@Test |
|
|
|
void reflectionHelperCompareArguments_RequiresConversionMatching() { |
|
|
|
void reflectionHelperCompareArguments_CloseMatching_WithAutoBoxing() { |
|
|
|
StandardTypeConverter tc = new StandardTypeConverter(); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Calling foo(String,int) with (String,Integer) requires boxing conversion of argument one
|
|
|
|
// Calling foo(String,int) with (String,Integer) requires boxing conversion of argument one
|
|
|
|
checkMatch(new Class<?>[] {String.class, int.class}, new Class<?>[] {String.class,Integer.class},tc, ArgumentsMatchKind.CLOSE); |
|
|
|
checkMatch(new Class<?>[] {String.class, int.class}, new Class<?>[] {String.class, Integer.class},tc, CLOSE); |
|
|
|
|
|
|
|
|
|
|
|
// Passing (int,String) on call to foo(Integer,String) requires boxing conversion of argument zero
|
|
|
|
// Passing (int,String) on call to foo(Integer,String) requires boxing conversion of argument zero
|
|
|
|
checkMatch(new Class<?>[] {int.class, String.class}, new Class<?>[] {Integer.class, String.class},tc, ArgumentsMatchKind.CLOSE); |
|
|
|
checkMatch(new Class<?>[] {int.class, String.class}, new Class<?>[] {Integer.class, String.class},tc, CLOSE); |
|
|
|
|
|
|
|
|
|
|
|
// Passing (int,Sub) on call to foo(Integer,Super) requires boxing conversion of argument zero
|
|
|
|
// Passing (int,Sub) on call to foo(Integer,Super) requires boxing conversion of argument zero
|
|
|
|
checkMatch(new Class<?>[] {int.class, Sub.class}, new Class<?>[] {Integer.class, Super.class}, tc, ArgumentsMatchKind.CLOSE); |
|
|
|
checkMatch(new Class<?>[] {int.class, Sub.class}, new Class<?>[] {Integer.class, Super.class}, tc, CLOSE); |
|
|
|
|
|
|
|
|
|
|
|
// Passing (int,Sub,boolean) on call to foo(Integer,Super,Boolean) requires boxing conversion of arguments zero and two
|
|
|
|
// Passing (int,Sub,boolean) on call to foo(Integer,Super,Boolean) requires boxing conversion of arguments zero and two
|
|
|
|
// TODO checkMatch(new Class<?>[] {int.class, Sub.class, boolean.class}, new Class<?>[] {Integer.class, Super.class, Boolean.class}, tc, ArgsMatchKind.REQUIRES_CONVERSION);
|
|
|
|
checkMatch(new Class<?>[] {int.class, Sub.class, boolean.class}, new Class<?>[] {Integer.class, Super.class, Boolean.class}, tc, CLOSE); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
@Test |
|
|
|
@Test |
|
|
|
@ -149,59 +150,56 @@ class ReflectionHelperTests extends AbstractExpressionTests { |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
@Test |
|
|
|
@Test |
|
|
|
void reflectionHelperCompareArguments_Varargs_ExactMatching() { |
|
|
|
void reflectionHelperCompareArguments_Varargs() { |
|
|
|
StandardTypeConverter tc = new StandardTypeConverter(); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Passing (String[]) on call to (String[]) is exact match
|
|
|
|
// Passing (String[]) on call to (String[]) is exact match
|
|
|
|
checkMatch2(new Class<?>[] {String[].class}, new Class<?>[] {String[].class}, tc, ArgumentsMatchKind.EXACT); |
|
|
|
checkMatchVarargs(new Class<?>[] {String[].class}, new Class<?>[] {String[].class}, tc, EXACT); |
|
|
|
|
|
|
|
|
|
|
|
// Passing (Integer, String[]) on call to (Integer, String[]) is exact match
|
|
|
|
// Passing (Integer, String[]) on call to (Integer, String[]) is exact match
|
|
|
|
checkMatch2(new Class<?>[] {Integer.class, String[].class}, new Class<?>[] {Integer.class, String[].class}, tc, ArgumentsMatchKind.EXACT); |
|
|
|
checkMatchVarargs(new Class<?>[] {Integer.class, String[].class}, new Class<?>[] {Integer.class, String[].class}, tc, EXACT); |
|
|
|
|
|
|
|
|
|
|
|
// Passing (String, Integer, String[]) on call to (String, String, String[]) is exact match
|
|
|
|
// Passing (String, Integer, String[]) on call to (String, String, String[]) is exact match
|
|
|
|
checkMatch2(new Class<?>[] {String.class, Integer.class, String[].class}, new Class<?>[] {String.class,Integer.class, String[].class}, tc, ArgumentsMatchKind.EXACT); |
|
|
|
checkMatchVarargs(new Class<?>[] {String.class, Integer.class, String[].class}, new Class<?>[] {String.class,Integer.class, String[].class}, tc, EXACT); |
|
|
|
|
|
|
|
|
|
|
|
// Passing (Sub, String[]) on call to (Super, String[]) is exact match
|
|
|
|
// Passing (Sub, String[]) on call to (Super, String[]) is exact match
|
|
|
|
checkMatch2(new Class<?>[] {Sub.class, String[].class}, new Class<?>[] {Super.class,String[].class}, tc, ArgumentsMatchKind.CLOSE); |
|
|
|
checkMatchVarargs(new Class<?>[] {Sub.class, String[].class}, new Class<?>[] {Super.class,String[].class}, tc, CLOSE); |
|
|
|
|
|
|
|
|
|
|
|
// Passing (Integer, String[]) on call to (String, String[]) is exact match
|
|
|
|
// Passing (Integer, String[]) on call to (String, String[]) is exact match
|
|
|
|
checkMatch2(new Class<?>[] {Integer.class, String[].class}, new Class<?>[] {String.class, String[].class}, tc, ArgumentsMatchKind.REQUIRES_CONVERSION); |
|
|
|
checkMatchVarargs(new Class<?>[] {Integer.class, String[].class}, new Class<?>[] {String.class, String[].class}, tc, REQUIRES_CONVERSION); |
|
|
|
|
|
|
|
|
|
|
|
// Passing (Integer, Sub, String[]) on call to (String, Super, String[]) is exact match
|
|
|
|
// Passing (Integer, Sub, String[]) on call to (String, Super, String[]) is exact match
|
|
|
|
checkMatch2(new Class<?>[] {Integer.class, Sub.class, String[].class}, new Class<?>[] {String.class, Super.class, String[].class}, tc, ArgumentsMatchKind.REQUIRES_CONVERSION); |
|
|
|
checkMatchVarargs(new Class<?>[] {Integer.class, Sub.class, String[].class}, new Class<?>[] {String.class, Super.class, String[].class}, tc, REQUIRES_CONVERSION); |
|
|
|
|
|
|
|
|
|
|
|
// Passing (String) on call to (String[]) is exact match
|
|
|
|
// Passing (String) on call to (String[]) is exact match
|
|
|
|
checkMatch2(new Class<?>[] {String.class}, new Class<?>[] {String[].class}, tc, ArgumentsMatchKind.EXACT); |
|
|
|
checkMatchVarargs(new Class<?>[] {String.class}, new Class<?>[] {String[].class}, tc, EXACT); |
|
|
|
|
|
|
|
|
|
|
|
// Passing (Integer,String) on call to (Integer,String[]) is exact match
|
|
|
|
// Passing (Integer,String) on call to (Integer,String[]) is exact match
|
|
|
|
checkMatch2(new Class<?>[] {Integer.class, String.class}, new Class<?>[] {Integer.class, String[].class}, tc, ArgumentsMatchKind.EXACT); |
|
|
|
checkMatchVarargs(new Class<?>[] {Integer.class, String.class}, new Class<?>[] {Integer.class, String[].class}, tc, EXACT); |
|
|
|
|
|
|
|
|
|
|
|
// Passing (String) on call to (Integer[]) is conversion match (String to Integer)
|
|
|
|
// Passing (String) on call to (Integer[]) is conversion match (String to Integer)
|
|
|
|
checkMatch2(new Class<?>[] {String.class}, new Class<?>[] {Integer[].class}, tc, ArgumentsMatchKind.REQUIRES_CONVERSION); |
|
|
|
checkMatchVarargs(new Class<?>[] {String.class}, new Class<?>[] {Integer[].class}, tc, REQUIRES_CONVERSION); |
|
|
|
|
|
|
|
|
|
|
|
// Passing (Sub) on call to (Super[]) is close match
|
|
|
|
// Passing (Sub) on call to (Super[]) is close match
|
|
|
|
checkMatch2(new Class<?>[] {Sub.class}, new Class<?>[] {Super[].class}, tc, ArgumentsMatchKind.CLOSE); |
|
|
|
checkMatchVarargs(new Class<?>[] {Sub.class}, new Class<?>[] {Super[].class}, tc, CLOSE); |
|
|
|
|
|
|
|
|
|
|
|
// Passing (Super) on call to (Sub[]) is not a match
|
|
|
|
// Passing (Super) on call to (Sub[]) is not a match
|
|
|
|
checkMatch2(new Class<?>[] {Super.class}, new Class<?>[] {Sub[].class}, tc, null); |
|
|
|
checkMatchVarargs(new Class<?>[] {Super.class}, new Class<?>[] {Sub[].class}, tc, null); |
|
|
|
|
|
|
|
|
|
|
|
checkMatch2(new Class<?>[] {Unconvertable.class, String.class}, new Class<?>[] {Sub.class, Super[].class}, tc, null); |
|
|
|
checkMatchVarargs(new Class<?>[] {Unconvertable.class, String.class}, new Class<?>[] {Sub.class, Super[].class}, tc, null); |
|
|
|
|
|
|
|
|
|
|
|
checkMatch2(new Class<?>[] {Integer.class, Integer.class, String.class}, new Class<?>[] {String.class, String.class, Super[].class}, tc, null); |
|
|
|
checkMatchVarargs(new Class<?>[] {Integer.class, Integer.class, String.class}, new Class<?>[] {String.class, String.class, Super[].class}, tc, null); |
|
|
|
|
|
|
|
|
|
|
|
checkMatch2(new Class<?>[] {Unconvertable.class, String.class}, new Class<?>[] {Sub.class, Super[].class}, tc, null); |
|
|
|
checkMatchVarargs(new Class<?>[] {Unconvertable.class, String.class}, new Class<?>[] {Sub.class, Super[].class}, tc, null); |
|
|
|
|
|
|
|
|
|
|
|
checkMatch2(new Class<?>[] {Integer.class, Integer.class, String.class}, new Class<?>[] {String.class, String.class, Super[].class}, tc, null); |
|
|
|
checkMatchVarargs(new Class<?>[] {Integer.class, Integer.class, String.class}, new Class<?>[] {String.class, String.class, Super[].class}, tc, null); |
|
|
|
|
|
|
|
|
|
|
|
checkMatch2(new Class<?>[] {Integer.class, Integer.class, Sub.class}, new Class<?>[] {String.class, String.class, Super[].class}, tc, ArgumentsMatchKind.REQUIRES_CONVERSION); |
|
|
|
checkMatchVarargs(new Class<?>[] {Integer.class, Integer.class, Sub.class}, new Class<?>[] {String.class, String.class, Super[].class}, tc, REQUIRES_CONVERSION); |
|
|
|
|
|
|
|
|
|
|
|
checkMatch2(new Class<?>[] {Integer.class, Integer.class, Integer.class}, new Class<?>[] {Integer.class, String[].class}, tc, ArgumentsMatchKind.REQUIRES_CONVERSION); |
|
|
|
checkMatchVarargs(new Class<?>[] {Integer.class, Integer.class, Integer.class}, new Class<?>[] {Integer.class, String[].class}, tc, REQUIRES_CONVERSION); |
|
|
|
// what happens on (Integer,String) passed to (Integer[]) ?
|
|
|
|
// what happens on (Integer,String) passed to (Integer[]) ?
|
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
@Test |
|
|
|
@Test |
|
|
|
void convertArguments() throws Exception { |
|
|
|
void convertArguments() throws Exception { |
|
|
|
StandardTypeConverter tc = new StandardTypeConverter(); |
|
|
|
|
|
|
|
Method oneArg = TestInterface.class.getMethod("oneArg", String.class); |
|
|
|
Method oneArg = TestInterface.class.getMethod("oneArg", String.class); |
|
|
|
Method twoArg = TestInterface.class.getMethod("twoArg", String.class, String[].class); |
|
|
|
Method twoArg = TestInterface.class.getMethod("twoArg", String.class, String[].class); |
|
|
|
|
|
|
|
|
|
|
|
@ -227,8 +225,7 @@ class ReflectionHelperTests extends AbstractExpressionTests { |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
@Test |
|
|
|
@Test |
|
|
|
void convertArguments2() throws Exception { |
|
|
|
void convertAllArguments() throws Exception { |
|
|
|
StandardTypeConverter tc = new StandardTypeConverter(); |
|
|
|
|
|
|
|
Method oneArg = TestInterface.class.getMethod("oneArg", String.class); |
|
|
|
Method oneArg = TestInterface.class.getMethod("oneArg", String.class); |
|
|
|
Method twoArg = TestInterface.class.getMethod("twoArg", String.class, String[].class); |
|
|
|
Method twoArg = TestInterface.class.getMethod("twoArg", String.class, String[].class); |
|
|
|
|
|
|
|
|
|
|
|
@ -254,7 +251,7 @@ class ReflectionHelperTests extends AbstractExpressionTests { |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
@Test |
|
|
|
@Test |
|
|
|
void setupArguments() { |
|
|
|
void setupArgumentsForVarargsInvocation() { |
|
|
|
Object[] newArray = ReflectionHelper.setupArgumentsForVarargsInvocation( |
|
|
|
Object[] newArray = ReflectionHelper.setupArgumentsForVarargsInvocation( |
|
|
|
new Class<?>[] {String[].class}, "a", "b", "c"); |
|
|
|
new Class<?>[] {String[].class}, "a", "b", "c"); |
|
|
|
|
|
|
|
|
|
|
|
@ -402,21 +399,21 @@ class ReflectionHelperTests extends AbstractExpressionTests { |
|
|
|
assertThat(matchInfo).as("Should not be a null match").isNotNull(); |
|
|
|
assertThat(matchInfo).as("Should not be a null match").isNotNull(); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
if (expectedMatchKind == ArgumentsMatchKind.EXACT) { |
|
|
|
if (expectedMatchKind == EXACT) { |
|
|
|
assertThat(matchInfo.isExactMatch()).isTrue(); |
|
|
|
assertThat(matchInfo.isExactMatch()).isTrue(); |
|
|
|
} |
|
|
|
} |
|
|
|
else if (expectedMatchKind == ArgumentsMatchKind.CLOSE) { |
|
|
|
else if (expectedMatchKind == CLOSE) { |
|
|
|
assertThat(matchInfo.isCloseMatch()).isTrue(); |
|
|
|
assertThat(matchInfo.isCloseMatch()).isTrue(); |
|
|
|
} |
|
|
|
} |
|
|
|
else if (expectedMatchKind == ArgumentsMatchKind.REQUIRES_CONVERSION) { |
|
|
|
else if (expectedMatchKind == REQUIRES_CONVERSION) { |
|
|
|
assertThat(matchInfo.isMatchRequiringConversion()).as("expected to be a match requiring conversion, but was " + matchInfo).isTrue(); |
|
|
|
assertThat(matchInfo.isMatchRequiringConversion()).as("expected to be a match requiring conversion, but was " + matchInfo).isTrue(); |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
/** |
|
|
|
* Used to validate the match returned from a compareArguments call. |
|
|
|
* Used to validate the match returned from a compareArgumentsVarargs call. |
|
|
|
*/ |
|
|
|
*/ |
|
|
|
private static void checkMatch2(Class<?>[] inputTypes, Class<?>[] expectedTypes, |
|
|
|
private static void checkMatchVarargs(Class<?>[] inputTypes, Class<?>[] expectedTypes, |
|
|
|
StandardTypeConverter typeConverter, ArgumentsMatchKind expectedMatchKind) { |
|
|
|
StandardTypeConverter typeConverter, ArgumentsMatchKind expectedMatchKind) { |
|
|
|
|
|
|
|
|
|
|
|
ReflectionHelper.ArgumentsMatchInfo matchInfo = |
|
|
|
ReflectionHelper.ArgumentsMatchInfo matchInfo = |
|
|
|
@ -438,14 +435,10 @@ class ReflectionHelperTests extends AbstractExpressionTests { |
|
|
|
private static void checkArguments(Object[] args, Object... expected) { |
|
|
|
private static void checkArguments(Object[] args, Object... expected) { |
|
|
|
assertThat(args).hasSize(expected.length); |
|
|
|
assertThat(args).hasSize(expected.length); |
|
|
|
for (int i = 0; i < expected.length; i++) { |
|
|
|
for (int i = 0; i < expected.length; i++) { |
|
|
|
checkArgument(expected[i], args[i]); |
|
|
|
assertThat(args[i]).isEqualTo(expected[i]); |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
private static void checkArgument(Object expected, Object actual) { |
|
|
|
|
|
|
|
assertThat(actual).isEqualTo(expected); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private static List<TypeDescriptor> typeDescriptors(Class<?>... types) { |
|
|
|
private static List<TypeDescriptor> typeDescriptors(Class<?>... types) { |
|
|
|
return Arrays.stream(types).map(TypeDescriptor::valueOf).toList(); |
|
|
|
return Arrays.stream(types).map(TypeDescriptor::valueOf).toList(); |
|
|
|
} |
|
|
|
} |
|
|
|
|