Polishing

This commit is contained in:
Sam Brannen
2023-03-15 15:00:03 +01:00
parent 2a5eab4b89
commit df4e7d1929
3 changed files with 51 additions and 48 deletions

View File

@@ -522,7 +522,7 @@ public class TypeDescriptor implements Serializable {
/** /**
* Create a new type descriptor for an object. * Create a new type descriptor for an object.
* <p>Use this factory method to introspect a source object before asking the * <p>Use this factory method to introspect a source object before asking the
* conversion system to convert it to some another type. * conversion system to convert it to some other type.
* <p>If the provided object is {@code null}, returns {@code null}, else calls * <p>If the provided object is {@code null}, returns {@code null}, else calls
* {@link #valueOf(Class)} to build a TypeDescriptor from the object's class. * {@link #valueOf(Class)} to build a TypeDescriptor from the object's class.
* @param source the source object * @param source the source object

View File

@@ -1,5 +1,5 @@
/* /*
* Copyright 2002-2022 the original author or authors. * Copyright 2002-2023 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@@ -21,6 +21,7 @@ import java.io.PrintStream;
import java.lang.reflect.Method; import java.lang.reflect.Method;
import java.lang.reflect.Proxy; import java.lang.reflect.Proxy;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays;
import java.util.List; import java.util.List;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
@@ -44,11 +45,12 @@ import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
* Tests for reflection helper code. * Tests for reflection helper code.
* *
* @author Andy Clement * @author Andy Clement
* @author Sam Brannen
*/ */
public class ReflectionHelperTests extends AbstractExpressionTests { class ReflectionHelperTests extends AbstractExpressionTests {
@Test @Test
public void testUtilities() 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");
ByteArrayOutputStream baos = new ByteArrayOutputStream(); ByteArrayOutputStream baos = new ByteArrayOutputStream();
PrintStream ps = new PrintStream(baos); PrintStream ps = new PrintStream(baos);
@@ -74,12 +76,12 @@ public class ReflectionHelperTests extends AbstractExpressionTests {
// CompoundExpression value:2 // CompoundExpression value:2
// IntLiteral value:2 // IntLiteral value:2
// ===> Expression '3+4+5+6+7-2' - AST end // ===> Expression '3+4+5+6+7-2' - AST end
assertThat(s.contains("===> Expression '3+4+5+6+7-2' - AST start")).isTrue(); assertThat(s).contains("===> Expression '3+4+5+6+7-2' - AST start");
assertThat(s.contains(" OpPlus value:((((3 + 4) + 5) + 6) + 7) #children:2")).isTrue(); assertThat(s).contains(" OpPlus value:((((3 + 4) + 5) + 6) + 7) #children:2");
} }
@Test @Test
public void testTypedValue() { void typedValue() {
TypedValue tv1 = new TypedValue("hello"); TypedValue tv1 = new TypedValue("hello");
TypedValue tv2 = new TypedValue("hello"); TypedValue tv2 = new TypedValue("hello");
TypedValue tv3 = new TypedValue("bye"); TypedValue tv3 = new TypedValue("bye");
@@ -91,13 +93,13 @@ public class ReflectionHelperTests extends AbstractExpressionTests {
assertThat(tv3).isNotEqualTo(tv2); assertThat(tv3).isNotEqualTo(tv2);
assertThat(tv1).isNotEqualTo(tv3); assertThat(tv1).isNotEqualTo(tv3);
assertThat(tv2).isNotEqualTo(tv3); assertThat(tv2).isNotEqualTo(tv3);
assertThat(tv2.hashCode()).isEqualTo(tv1.hashCode()); assertThat(tv2).hasSameHashCodeAs(tv1);
assertThat(tv3.hashCode()).isNotEqualTo(tv1.hashCode()); assertThat(tv3).doesNotHaveSameHashCodeAs(tv1);
assertThat(tv3.hashCode()).isNotEqualTo(tv2.hashCode()); assertThat(tv3).doesNotHaveSameHashCodeAs(tv2);
} }
@Test @Test
public void testReflectionHelperCompareArguments_ExactMatching() { void reflectionHelperCompareArguments_ExactMatching() {
StandardTypeConverter tc = new StandardTypeConverter(); StandardTypeConverter tc = new StandardTypeConverter();
// Calling foo(String) with (String) is exact match // Calling foo(String) with (String) is exact match
@@ -108,7 +110,7 @@ public class ReflectionHelperTests extends AbstractExpressionTests {
} }
@Test @Test
public void testReflectionHelperCompareArguments_CloseMatching() { void reflectionHelperCompareArguments_CloseMatching() {
StandardTypeConverter tc = new StandardTypeConverter(); 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)
@@ -122,7 +124,7 @@ public class ReflectionHelperTests extends AbstractExpressionTests {
} }
@Test @Test
public void testReflectionHelperCompareArguments_RequiresConversionMatching() { void reflectionHelperCompareArguments_RequiresConversionMatching() {
StandardTypeConverter tc = new StandardTypeConverter(); 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
@@ -139,7 +141,7 @@ public class ReflectionHelperTests extends AbstractExpressionTests {
} }
@Test @Test
public void testReflectionHelperCompareArguments_NotAMatch() { void reflectionHelperCompareArguments_NotAMatch() {
StandardTypeConverter typeConverter = new StandardTypeConverter(); StandardTypeConverter typeConverter = new StandardTypeConverter();
// Passing (Super,String) on call to foo(Sub,String) is not a match // Passing (Super,String) on call to foo(Sub,String) is not a match
@@ -147,7 +149,7 @@ public class ReflectionHelperTests extends AbstractExpressionTests {
} }
@Test @Test
public void testReflectionHelperCompareArguments_Varargs_ExactMatching() { void reflectionHelperCompareArguments_Varargs_ExactMatching() {
StandardTypeConverter tc = new StandardTypeConverter(); StandardTypeConverter tc = new StandardTypeConverter();
// Passing (String[]) on call to (String[]) is exact match // Passing (String[]) on call to (String[]) is exact match
@@ -198,7 +200,7 @@ public class ReflectionHelperTests extends AbstractExpressionTests {
} }
@Test @Test
public void testConvertArguments() throws Exception { void convertArguments() throws Exception {
StandardTypeConverter tc = new StandardTypeConverter(); 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);
@@ -225,7 +227,7 @@ public class ReflectionHelperTests extends AbstractExpressionTests {
} }
@Test @Test
public void testConvertArguments2() throws Exception { void convertArguments2() throws Exception {
StandardTypeConverter tc = new StandardTypeConverter(); 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);
@@ -252,7 +254,7 @@ public class ReflectionHelperTests extends AbstractExpressionTests {
} }
@Test @Test
public void testSetupArguments() { void setupArguments() {
Object[] newArray = ReflectionHelper.setupArgumentsForVarargsInvocation( Object[] newArray = ReflectionHelper.setupArgumentsForVarargsInvocation(
new Class<?>[] {String[].class}, "a", "b", "c"); new Class<?>[] {String[].class}, "a", "b", "c");
@@ -260,14 +262,11 @@ public class ReflectionHelperTests extends AbstractExpressionTests {
Object firstParam = newArray[0]; Object firstParam = newArray[0];
assertThat(firstParam.getClass().getComponentType()).isEqualTo(String.class); assertThat(firstParam.getClass().getComponentType()).isEqualTo(String.class);
Object[] firstParamArray = (Object[]) firstParam; Object[] firstParamArray = (Object[]) firstParam;
assertThat(firstParamArray).hasSize(3); assertThat(firstParamArray).containsExactly("a", "b", "c");
assertThat(firstParamArray[0]).isEqualTo("a");
assertThat(firstParamArray[1]).isEqualTo("b");
assertThat(firstParamArray[2]).isEqualTo("c");
} }
@Test @Test
public void testReflectivePropertyAccessor() throws Exception { void reflectivePropertyAccessor() throws Exception {
ReflectivePropertyAccessor rpa = new ReflectivePropertyAccessor(); ReflectivePropertyAccessor rpa = new ReflectivePropertyAccessor();
Tester t = new Tester(); Tester t = new Tester();
t.setProperty("hello"); t.setProperty("hello");
@@ -328,7 +327,7 @@ public class ReflectionHelperTests extends AbstractExpressionTests {
} }
@Test @Test
public void testOptimalReflectivePropertyAccessor() throws Exception { void optimalReflectivePropertyAccessor() throws Exception {
ReflectivePropertyAccessor reflective = new ReflectivePropertyAccessor(); ReflectivePropertyAccessor reflective = new ReflectivePropertyAccessor();
Tester tester = new Tester(); Tester tester = new Tester();
tester.setProperty("hello"); tester.setProperty("hello");
@@ -374,19 +373,28 @@ public class ReflectionHelperTests extends AbstractExpressionTests {
MethodResolver resolver = new ReflectiveMethodResolver(); MethodResolver resolver = new ReflectiveMethodResolver();
StandardEvaluationContext evaluationContext = new StandardEvaluationContext(); StandardEvaluationContext evaluationContext = new StandardEvaluationContext();
// Nonexistent method
MethodExecutor bogus = resolver.resolve(evaluationContext, proxy, "bogus", List.of()); MethodExecutor bogus = resolver.resolve(evaluationContext, proxy, "bogus", List.of());
assertThat(bogus).as("MethodExecutor for bogus()").isNull(); assertThat(bogus).as("MethodExecutor for bogus()").isNull();
// Method in interface
MethodExecutor run = resolver.resolve(evaluationContext, proxy, "run", List.of());
assertThat(run).as("MethodExecutor for run()").isNotNull();
// Methods in Object
MethodExecutor toString = resolver.resolve(evaluationContext, proxy, "toString", List.of()); MethodExecutor toString = resolver.resolve(evaluationContext, proxy, "toString", List.of());
assertThat(toString).as("MethodExecutor for toString()").isNotNull(); assertThat(toString).as("MethodExecutor for toString()").isNotNull();
MethodExecutor hashCode = resolver.resolve(evaluationContext, proxy, "hashCode", List.of()); MethodExecutor hashCode = resolver.resolve(evaluationContext, proxy, "hashCode", List.of());
assertThat(hashCode).as("MethodExecutor for hashCode()").isNotNull(); assertThat(hashCode).as("MethodExecutor for hashCode()").isNotNull();
MethodExecutor equals = resolver.resolve(evaluationContext, proxy, "equals", typeDescriptors(Object.class));
assertThat(equals).as("MethodExecutor for equals()").isNotNull();
} }
/** /**
* Used to validate the match returned from a compareArguments call. * Used to validate the match returned from a compareArguments call.
*/ */
private void checkMatch(Class<?>[] inputTypes, Class<?>[] expectedTypes, StandardTypeConverter typeConverter, ArgumentsMatchKind expectedMatchKind) { private void checkMatch(Class<?>[] inputTypes, Class<?>[] expectedTypes, StandardTypeConverter typeConverter, ArgumentsMatchKind expectedMatchKind) {
ReflectionHelper.ArgumentsMatchInfo matchInfo = ReflectionHelper.compareArguments(getTypeDescriptors(expectedTypes), getTypeDescriptors(inputTypes), typeConverter); ReflectionHelper.ArgumentsMatchInfo matchInfo = ReflectionHelper.compareArguments(typeDescriptors(expectedTypes), typeDescriptors(inputTypes), typeConverter);
if (expectedMatchKind == null) { if (expectedMatchKind == null) {
assertThat(matchInfo).as("Did not expect them to match in any way").isNull(); assertThat(matchInfo).as("Did not expect them to match in any way").isNull();
} }
@@ -408,47 +416,42 @@ public class ReflectionHelperTests extends AbstractExpressionTests {
/** /**
* Used to validate the match returned from a compareArguments call. * Used to validate the match returned from a compareArguments call.
*/ */
private void checkMatch2(Class<?>[] inputTypes, Class<?>[] expectedTypes, StandardTypeConverter typeConverter, ArgumentsMatchKind expectedMatchKind) { private static void checkMatch2(Class<?>[] inputTypes, Class<?>[] expectedTypes,
ReflectionHelper.ArgumentsMatchInfo matchInfo = ReflectionHelper.compareArgumentsVarargs(getTypeDescriptors(expectedTypes), getTypeDescriptors(inputTypes), typeConverter); StandardTypeConverter typeConverter, ArgumentsMatchKind expectedMatchKind) {
ReflectionHelper.ArgumentsMatchInfo matchInfo =
ReflectionHelper.compareArgumentsVarargs(typeDescriptors(expectedTypes), typeDescriptors(inputTypes), typeConverter);
if (expectedMatchKind == null) { if (expectedMatchKind == null) {
assertThat(matchInfo).as("Did not expect them to match in any way: " + matchInfo).isNull(); assertThat(matchInfo).as("Did not expect them to match in any way: " + matchInfo).isNull();
} }
else { else {
assertThat(matchInfo).as("Should not be a null match").isNotNull(); assertThat(matchInfo).as("Should not be a null match").isNotNull();
} switch (expectedMatchKind) {
case EXACT -> assertThat(matchInfo.isExactMatch()).isTrue();
if (expectedMatchKind == ArgumentsMatchKind.EXACT) { case CLOSE -> assertThat(matchInfo.isCloseMatch()).isTrue();
assertThat(matchInfo.isExactMatch()).isTrue(); case REQUIRES_CONVERSION -> assertThat(matchInfo.isMatchRequiringConversion())
} .as("expected to be a match requiring conversion, but was " + matchInfo).isTrue();
else if (expectedMatchKind == ArgumentsMatchKind.CLOSE) { }
assertThat(matchInfo.isCloseMatch()).isTrue();
}
else if (expectedMatchKind == ArgumentsMatchKind.REQUIRES_CONVERSION) {
assertThat(matchInfo.isMatchRequiringConversion()).as("expected to be a match requiring conversion, but was " + matchInfo).isTrue();
} }
} }
private 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]); checkArgument(expected[i], args[i]);
} }
} }
private void checkArgument(Object expected, Object actual) { private static void checkArgument(Object expected, Object actual) {
assertThat(actual).isEqualTo(expected); assertThat(actual).isEqualTo(expected);
} }
private List<TypeDescriptor> getTypeDescriptors(Class<?>... types) { private static List<TypeDescriptor> typeDescriptors(Class<?>... types) {
List<TypeDescriptor> typeDescriptors = new ArrayList<>(types.length); return Arrays.stream(types).map(TypeDescriptor::valueOf).toList();
for (Class<?> type : types) {
typeDescriptors.add(TypeDescriptor.valueOf(type));
}
return typeDescriptors;
} }
public interface TestInterface { interface TestInterface {
void oneArg(String arg1); void oneArg(String arg1);

View File

@@ -1,5 +1,5 @@
/* /*
* Copyright 2002-2022 the original author or authors. * Copyright 2002-2023 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.