Polishing
This commit is contained in:
@@ -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
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|
||||||
|
|||||||
@@ -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.
|
||||||
|
|||||||
Reference in New Issue
Block a user