Polish SpEL Javadocs and internals

This commit is contained in:
Sam Brannen
2024-02-10 15:03:02 +01:00
parent 1080c145e3
commit 888e50175d
13 changed files with 194 additions and 172 deletions

View File

@@ -112,6 +112,24 @@ class ParsingTests {
parseCheck("#var1='value1'");
}
@Test
void indexing() {
parseCheck("#var[2]");
parseCheck("person['name']");
parseCheck("person[name]");
parseCheck("array[2]");
parseCheck("array[2][3]");
parseCheck("func()[2]");
parseCheck("#func()[2]");
parseCheck("'abc'[2]");
parseCheck("\"abc\"[2]", "'abc'[2]");
parseCheck("{1,2,3}[2]");
parseCheck("{'k':'v'}['k']");
parseCheck("{'k':'v'}[k]");
parseCheck("{'k1':'v1','k2':'v2'}['k2']");
parseCheck("{'k1':'v1','k2':'v2'}[k2]");
}
@Test
void projection() {
parseCheck("{1,2,3,4,5,6,7,8,9,10}.![#isEven()]");

View File

@@ -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.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 Sam Brannen
*/
class ReflectionHelperTests extends AbstractExpressionTests {
private final StandardTypeConverter tc = new StandardTypeConverter();
@Test
void utilities() throws ParseException {
SpelExpression expr = (SpelExpression)parser.parseExpression("3+4+5+6+7-2");
@@ -100,44 +107,38 @@ class ReflectionHelperTests extends AbstractExpressionTests {
@Test
void reflectionHelperCompareArguments_ExactMatching() {
StandardTypeConverter tc = new StandardTypeConverter();
// 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
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
void reflectionHelperCompareArguments_CloseMatching() {
StandardTypeConverter tc = new StandardTypeConverter();
// 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
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
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
void reflectionHelperCompareArguments_RequiresConversionMatching() {
StandardTypeConverter tc = new StandardTypeConverter();
void reflectionHelperCompareArguments_CloseMatching_WithAutoBoxing() {
// 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
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
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
// 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
@@ -149,59 +150,56 @@ class ReflectionHelperTests extends AbstractExpressionTests {
}
@Test
void reflectionHelperCompareArguments_Varargs_ExactMatching() {
StandardTypeConverter tc = new StandardTypeConverter();
void reflectionHelperCompareArguments_Varargs() {
// 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
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
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
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
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
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
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
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)
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
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
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[]) ?
}
@Test
void convertArguments() throws Exception {
StandardTypeConverter tc = new StandardTypeConverter();
Method oneArg = TestInterface.class.getMethod("oneArg", String.class);
Method twoArg = TestInterface.class.getMethod("twoArg", String.class, String[].class);
@@ -227,8 +225,7 @@ class ReflectionHelperTests extends AbstractExpressionTests {
}
@Test
void convertArguments2() throws Exception {
StandardTypeConverter tc = new StandardTypeConverter();
void convertAllArguments() throws Exception {
Method oneArg = TestInterface.class.getMethod("oneArg", String.class);
Method twoArg = TestInterface.class.getMethod("twoArg", String.class, String[].class);
@@ -254,7 +251,7 @@ class ReflectionHelperTests extends AbstractExpressionTests {
}
@Test
void setupArguments() {
void setupArgumentsForVarargsInvocation() {
Object[] newArray = ReflectionHelper.setupArgumentsForVarargsInvocation(
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();
}
if (expectedMatchKind == ArgumentsMatchKind.EXACT) {
if (expectedMatchKind == EXACT) {
assertThat(matchInfo.isExactMatch()).isTrue();
}
else if (expectedMatchKind == ArgumentsMatchKind.CLOSE) {
else if (expectedMatchKind == CLOSE) {
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();
}
}
/**
* 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) {
ReflectionHelper.ArgumentsMatchInfo matchInfo =
@@ -438,14 +435,10 @@ class ReflectionHelperTests extends AbstractExpressionTests {
private static void checkArguments(Object[] args, Object... expected) {
assertThat(args).hasSize(expected.length);
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) {
return Arrays.stream(types).map(TypeDescriptor::valueOf).toList();
}