From 387c8a8181e2332b1978c231f339576db6266ffd Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Fri, 18 Dec 2015 22:10:50 +0100 Subject: [PATCH] Differentiate between TypeDescriptors with same annotations but different attributes Issue: SPR-13714 --- .../core/convert/TypeDescriptor.java | 2 +- .../core/convert/TypeDescriptorTests.java | 574 +++++++++--------- .../GenericConversionServiceTests.java | 121 +++- 3 files changed, 399 insertions(+), 298 deletions(-) diff --git a/spring-core/src/main/java/org/springframework/core/convert/TypeDescriptor.java b/spring-core/src/main/java/org/springframework/core/convert/TypeDescriptor.java index b55df08308..931cb28891 100644 --- a/spring-core/src/main/java/org/springframework/core/convert/TypeDescriptor.java +++ b/spring-core/src/main/java/org/springframework/core/convert/TypeDescriptor.java @@ -468,7 +468,7 @@ public class TypeDescriptor implements Serializable { return false; } for (Annotation ann : getAnnotations()) { - if (!other.hasAnnotation(ann.annotationType())) { + if (!ann.equals(other.getAnnotation(ann.annotationType()))) { return false; } } diff --git a/spring-core/src/test/java/org/springframework/core/convert/TypeDescriptorTests.java b/spring-core/src/test/java/org/springframework/core/convert/TypeDescriptorTests.java index 61f30c3143..39184c2c77 100644 --- a/spring-core/src/test/java/org/springframework/core/convert/TypeDescriptorTests.java +++ b/spring-core/src/test/java/org/springframework/core/convert/TypeDescriptorTests.java @@ -52,27 +52,11 @@ import static org.junit.Assert.*; * @author Andy Clement * @author Phillip Webb * @author Sam Brannen + * @author Nathan Piper */ @SuppressWarnings("rawtypes") public class TypeDescriptorTests { - public List listOfString; - - public List> listOfListOfString = new ArrayList>(); - - public List listOfListOfUnknown = new ArrayList(); - - public int[] intArray; - - public List[] arrayOfListOfString; - - public List listField = new ArrayList(); - - public Map mapField = new HashMap(); - - public Map> nestedMapField = new HashMap>(); - - @Test public void parameterPrimitive() throws Exception { TypeDescriptor desc = new TypeDescriptor(new MethodParameter(getClass().getMethod("testParameterPrimitive", int.class), 0)); @@ -86,10 +70,6 @@ public class TypeDescriptorTests { assertFalse(desc.isMap()); } - public void testParameterPrimitive(int primitive) { - - } - @Test public void parameterScalar() throws Exception { TypeDescriptor desc = new TypeDescriptor(new MethodParameter(getClass().getMethod("testParameterScalar", String.class), 0)); @@ -104,10 +84,6 @@ public class TypeDescriptorTests { assertFalse(desc.isMap()); } - public void testParameterScalar(String value) { - - } - @Test public void parameterList() throws Exception { MethodParameter methodParameter = new MethodParameter(getClass().getMethod("testParameterList", List.class), 0); @@ -129,10 +105,6 @@ public class TypeDescriptorTests { assertFalse(desc.isMap()); } - public void testParameterList(List>>> list) { - - } - @Test public void parameterListNoParamTypes() throws Exception { MethodParameter methodParameter = new MethodParameter(getClass().getMethod("testParameterListNoParamTypes", List.class), 0); @@ -149,10 +121,6 @@ public class TypeDescriptorTests { assertFalse(desc.isMap()); } - public void testParameterListNoParamTypes(List list) { - - } - @Test public void parameterArray() throws Exception { MethodParameter methodParameter = new MethodParameter(getClass().getMethod("testParameterArray", Integer[].class), 0); @@ -170,10 +138,6 @@ public class TypeDescriptorTests { assertFalse(desc.isMap()); } - public void testParameterArray(Integer[] array) { - - } - @Test public void parameterMap() throws Exception { MethodParameter methodParameter = new MethodParameter(getClass().getMethod("testParameterMap", Map.class), 0); @@ -194,10 +158,6 @@ public class TypeDescriptorTests { assertEquals(String.class, desc.getMapValueTypeDescriptor().getElementTypeDescriptor().getType()); } - public void testParameterMap(Map> map) { - - } - @Test public void parameterAnnotated() throws Exception { TypeDescriptor t1 = new TypeDescriptor(new MethodParameter(getClass().getMethod("testAnnotatedMethod", String.class), 0)); @@ -208,36 +168,20 @@ public class TypeDescriptorTests { assertEquals(123, t1.getAnnotation(ParameterAnnotation.class).value()); } - @Target({ElementType.PARAMETER}) - @Retention(RetentionPolicy.RUNTIME) - public @interface ParameterAnnotation { - int value(); - } - - public void testAnnotatedMethod(@ParameterAnnotation(123) String parameter) { - - } - @Test public void propertyComplex() throws Exception { - Property property = new Property(getClass(), getClass().getMethod("getComplexProperty"), getClass().getMethod("setComplexProperty", Map.class)); + Property property = new Property(getClass(), getClass().getMethod("getComplexProperty"), + getClass().getMethod("setComplexProperty", Map.class)); TypeDescriptor desc = new TypeDescriptor(property); assertEquals(String.class, desc.getMapKeyTypeDescriptor().getType()); assertEquals(Integer.class, desc.getMapValueTypeDescriptor().getElementTypeDescriptor().getElementTypeDescriptor().getType()); } - public Map>> getComplexProperty() { - return null; - } - - public void setComplexProperty(Map>> complexProperty) { - - } - @Test public void propertyGenericType() throws Exception { GenericType genericBean = new IntegerType(); - Property property = new Property(getClass(), genericBean.getClass().getMethod("getProperty"), genericBean.getClass().getMethod("setProperty", Integer.class)); + Property property = new Property(getClass(), genericBean.getClass().getMethod("getProperty"), + genericBean.getClass().getMethod("setProperty", Integer.class)); TypeDescriptor desc = new TypeDescriptor(property); assertEquals(Integer.class, desc.getType()); } @@ -245,7 +189,8 @@ public class TypeDescriptorTests { @Test public void propertyTypeCovariance() throws Exception { GenericType genericBean = new NumberType(); - Property property = new Property(getClass(), genericBean.getClass().getMethod("getProperty"), genericBean.getClass().getMethod("setProperty", Number.class)); + Property property = new Property(getClass(), genericBean.getClass().getMethod("getProperty"), + genericBean.getClass().getMethod("setProperty", Number.class)); TypeDescriptor desc = new TypeDescriptor(property); assertEquals(Integer.class, desc.getType()); } @@ -253,69 +198,18 @@ public class TypeDescriptorTests { @Test public void propertyGenericTypeList() throws Exception { GenericType genericBean = new IntegerType(); - Property property = new Property(getClass(), genericBean.getClass().getMethod("getListProperty"), genericBean.getClass().getMethod("setListProperty", List.class)); + Property property = new Property(getClass(), genericBean.getClass().getMethod("getListProperty"), + genericBean.getClass().getMethod("setListProperty", List.class)); TypeDescriptor desc = new TypeDescriptor(property); assertEquals(List.class, desc.getType()); assertEquals(Integer.class, desc.getElementTypeDescriptor().getType()); } - public interface GenericType { - T getProperty(); - - void setProperty(T t); - - List getListProperty(); - - void setListProperty(List t); - - } - - public class IntegerType implements GenericType { - - @Override - public Integer getProperty() { - return null; - } - - @Override - public void setProperty(Integer t) { - } - - @Override - public List getListProperty() { - return null; - } - - @Override - public void setListProperty(List t) { - } - } - - public class NumberType implements GenericType { - - @Override - public Integer getProperty() { - return null; - } - - @Override - public void setProperty(Number t) { - } - - @Override - public List getListProperty() { - return null; - } - - @Override - public void setListProperty(List t) { - } - } - @Test public void propertyGenericClassList() throws Exception { IntegerClass genericBean = new IntegerClass(); - Property property = new Property(genericBean.getClass(), genericBean.getClass().getMethod("getListProperty"), genericBean.getClass().getMethod("setListProperty", List.class)); + Property property = new Property(genericBean.getClass(), genericBean.getClass().getMethod("getListProperty"), + genericBean.getClass().getMethod("setListProperty", List.class)); TypeDescriptor desc = new TypeDescriptor(property); assertEquals(List.class, desc.getType()); assertEquals(Integer.class, desc.getElementTypeDescriptor().getType()); @@ -323,32 +217,10 @@ public class TypeDescriptorTests { assertTrue(desc.hasAnnotation(MethodAnnotation1.class)); } - public static class GenericClass { - - public T getProperty() { - return null; - } - - public void setProperty(T t) { - } - - @MethodAnnotation1 - public List getListProperty() { - return null; - } - - public void setListProperty(List t) { - } - - } - - public static class IntegerClass extends GenericClass { - - } - @Test public void property() throws Exception { - Property property = new Property(getClass(), getClass().getMethod("getProperty"), getClass().getMethod("setProperty", Map.class)); + Property property = new Property( + getClass(), getClass().getMethod("getProperty"), getClass().getMethod("setProperty", Map.class)); TypeDescriptor desc = new TypeDescriptor(property); assertEquals(Map.class, desc.getType()); assertEquals(Integer.class, desc.getMapKeyTypeDescriptor().getElementTypeDescriptor().getType()); @@ -358,60 +230,6 @@ public class TypeDescriptorTests { assertNotNull(desc.getAnnotation(MethodAnnotation3.class)); } - @MethodAnnotation1 - public Map, List> getProperty() { - return property; - } - - @MethodAnnotation2 - public void setProperty(Map, List> property) { - this.property = property; - } - - @MethodAnnotation3 - private Map, List> property; - - - @Target({ ElementType.METHOD, ElementType.ANNOTATION_TYPE }) - @Retention(RetentionPolicy.RUNTIME) - public @interface MethodAnnotation1 { - } - - @Target({ElementType.METHOD}) - @Retention(RetentionPolicy.RUNTIME) - public @interface MethodAnnotation2 { - } - - @Target({ElementType.FIELD}) - @Retention(RetentionPolicy.RUNTIME) - public @interface MethodAnnotation3 { - } - - @MethodAnnotation1 - @Target({ ElementType.METHOD, ElementType.ANNOTATION_TYPE }) - @Retention(RetentionPolicy.RUNTIME) - public @interface ComposedMethodAnnotation1 {} - - @ComposedMethodAnnotation1 - @Target(ElementType.METHOD) - @Retention(RetentionPolicy.RUNTIME) - public @interface ComposedComposedMethodAnnotation1 {} - - @MethodAnnotation1 - public void methodWithLocalAnnotation() {} - - @ComposedMethodAnnotation1 - public void methodWithComposedAnnotation() {} - - @ComposedComposedMethodAnnotation1 - public void methodWithComposedComposedAnnotation() {} - - private void assertAnnotationFoundOnMethod(Class annotationType, String methodName) throws Exception { - TypeDescriptor typeDescriptor = new TypeDescriptor(new MethodParameter(getClass().getMethod(methodName), -1)); - assertNotNull("Should have found @" + annotationType.getSimpleName() + " on " + methodName + ".", - typeDescriptor.getAnnotation(annotationType)); - } - @Test public void getAnnotationOnMethodThatIsLocallyAnnotated() throws Exception { assertAnnotationFoundOnMethod(MethodAnnotation1.class, "methodWithLocalAnnotation"); @@ -427,6 +245,12 @@ public class TypeDescriptorTests { assertAnnotationFoundOnMethod(MethodAnnotation1.class, "methodWithComposedComposedAnnotation"); } + private void assertAnnotationFoundOnMethod(Class annotationType, String methodName) throws Exception { + TypeDescriptor typeDescriptor = new TypeDescriptor(new MethodParameter(getClass().getMethod(methodName), -1)); + assertNotNull("Should have found @" + annotationType.getSimpleName() + " on " + methodName + ".", + typeDescriptor.getAnnotation(annotationType)); + } + @Test public void fieldScalar() throws Exception { TypeDescriptor typeDescriptor = new TypeDescriptor(getClass().getField("fieldScalar")); @@ -438,8 +262,6 @@ public class TypeDescriptorTests { assertEquals(Integer.class, typeDescriptor.getObjectType()); } - public Integer fieldScalar; - @Test public void fieldList() throws Exception { TypeDescriptor typeDescriptor = new TypeDescriptor(TypeDescriptorTests.class.getDeclaredField("listOfString")); @@ -504,8 +326,6 @@ public class TypeDescriptorTests { assertEquals(Long.class, desc.getMapValueTypeDescriptor().getElementTypeDescriptor().getType()); } - public Map, List> fieldMap; - @Test public void fieldAnnotated() throws Exception { TypeDescriptor typeDescriptor = new TypeDescriptor(getClass().getField("fieldAnnotated")); @@ -513,15 +333,6 @@ public class TypeDescriptorTests { assertNotNull(typeDescriptor.getAnnotation(FieldAnnotation.class)); } - @FieldAnnotation - public List fieldAnnotated; - - @Target({ElementType.FIELD}) - @Retention(RetentionPolicy.RUNTIME) - public @interface FieldAnnotation { - - } - @Test public void valueOfScalar() { TypeDescriptor typeDescriptor = TypeDescriptor.valueOf(Integer.class); @@ -592,7 +403,7 @@ public class TypeDescriptorTests { assertEquals(String.class, t1.getType()); } - @Test(expected=IllegalArgumentException.class) + @Test(expected = IllegalArgumentException.class) public void nestedMethodParameterNot1NestedLevel() throws Exception { TypeDescriptor.nested(new MethodParameter(getClass().getMethod("test4", List.class), 0, 2), 2); } @@ -609,31 +420,11 @@ public class TypeDescriptorTests { assertNull(t1); } - @Test(expected=IllegalArgumentException.class) + @Test(expected = IllegalArgumentException.class) public void nestedMethodParameterTypeInvalidNestingLevel() throws Exception { TypeDescriptor.nested(new MethodParameter(getClass().getMethod("test5", String.class), 0, 2), 2); } - public void test1(List param1) { - - } - - public void test2(List> param1) { - - } - - public void test3(Map param1) { - - } - - public void test4(List> param1) { - - } - - public void test5(String param1) { - - } - @Test public void nestedNotParameterized() throws Exception { TypeDescriptor t1 = TypeDescriptor.nested(new MethodParameter(getClass().getMethod("test6", List.class), 0), 1); @@ -643,18 +434,12 @@ public class TypeDescriptorTests { assertNull(t2); } - public void test6(List param1) { - - } - @Test public void nestedFieldTypeMapTwoLevels() throws Exception { TypeDescriptor t1 = TypeDescriptor.nested(getClass().getField("test4"), 2); assertEquals(String.class, t1.getType()); } - public List> test4; - @Test public void nestedPropertyTypeMapTwoLevels() throws Exception { Property property = new Property(getClass(), getClass().getMethod("getTest4"), getClass().getMethod("setTest4", List.class)); @@ -662,14 +447,6 @@ public class TypeDescriptorTests { assertEquals(String.class, t1.getType()); } - public List> getTest4() { - return null; - } - - public void setTest4(List> test4) { - - } - @Test public void collection() { TypeDescriptor desc = TypeDescriptor.collection(List.class, TypeDescriptor.valueOf(Integer.class)); @@ -762,9 +539,6 @@ public class TypeDescriptorTests { assertNotNull(desc.getAnnotation(FieldAnnotation.class)); } - @FieldAnnotation - public List> listPreserveContext; - @Test public void mapKeyType() { TypeDescriptor desc = TypeDescriptor.valueOf(Map.class); @@ -783,9 +557,6 @@ public class TypeDescriptorTests { assertNotNull(desc.getAnnotation(FieldAnnotation.class)); } - @FieldAnnotation - public Map, List> mapPreserveContext; - @Test public void mapValueType() { TypeDescriptor desc = TypeDescriptor.valueOf(Map.class); @@ -805,7 +576,7 @@ public class TypeDescriptorTests { } @Test - public void equals() throws Exception { + public void equality() throws Exception { TypeDescriptor t1 = TypeDescriptor.valueOf(String.class); TypeDescriptor t2 = TypeDescriptor.valueOf(String.class); TypeDescriptor t3 = TypeDescriptor.valueOf(Date.class); @@ -826,6 +597,18 @@ public class TypeDescriptorTests { TypeDescriptor t11 = new TypeDescriptor(getClass().getField("mapField")); TypeDescriptor t12 = new TypeDescriptor(getClass().getField("mapField")); assertEquals(t11, t12); + + TypeDescriptor t13 = new TypeDescriptor(new MethodParameter(getClass().getMethod("testAnnotatedMethod", String.class), 0)); + TypeDescriptor t14 = new TypeDescriptor(new MethodParameter(getClass().getMethod("testAnnotatedMethod", String.class), 0)); + assertEquals(t13, t14); + + TypeDescriptor t15 = new TypeDescriptor(new MethodParameter(getClass().getMethod("testAnnotatedMethod", String.class), 0)); + TypeDescriptor t16 = new TypeDescriptor(new MethodParameter(getClass().getMethod("testAnnotatedMethodDifferentAnnotationValue", String.class), 0)); + assertNotEquals(t15, t16); + + TypeDescriptor t17 = new TypeDescriptor(new MethodParameter(getClass().getMethod("testAnnotatedMethod", String.class), 0)); + TypeDescriptor t18 = new TypeDescriptor(new MethodParameter(getClass().getMethod("test5", String.class), 0)); + assertNotEquals(t17, t18); } @Test @@ -844,10 +627,6 @@ public class TypeDescriptorTests { assertTrue(TypeDescriptor.valueOf(List.class).isAssignableTo(new TypeDescriptor(getClass().getField("listField")))); } - public List notGenericList; - - public List isAssignableElementTypes; - @Test public void isAssignableMapKeyValueTypes() throws Exception { assertTrue(new TypeDescriptor(getClass().getField("mapField")).isAssignableTo(new TypeDescriptor(getClass().getField("mapField")))); @@ -857,10 +636,6 @@ public class TypeDescriptorTests { assertTrue(TypeDescriptor.valueOf(Map.class).isAssignableTo(new TypeDescriptor(getClass().getField("mapField")))); } - public Map notGenericMap; - - public Map isAssignableMapKeyValueTypes; - @Test public void multiValueMap() throws Exception { TypeDescriptor td = new TypeDescriptor(getClass().getField("multiValueMap")); @@ -871,8 +646,6 @@ public class TypeDescriptorTests { td.getMapValueTypeDescriptor().getElementTypeDescriptor().getType()); } - public MultiValueMap multiValueMap = new LinkedMultiValueMap(); - @Test public void passDownGeneric() throws Exception { TypeDescriptor td = new TypeDescriptor(getClass().getField("passDownGeneric")); @@ -881,12 +654,6 @@ public class TypeDescriptorTests { assertEquals(Integer.class, td.getElementTypeDescriptor().getElementTypeDescriptor().getElementTypeDescriptor().getType()); } - public PassDownGeneric passDownGeneric = new PassDownGeneric(); - - @SuppressWarnings("serial") - public static class PassDownGeneric extends ArrayList>> { - } - @Test public void testUpCast() throws Exception { Property property = new Property(getClass(), getClass().getMethod("getProperty"), @@ -904,8 +671,9 @@ public class TypeDescriptorTests { try { typeDescriptor.upcast(Collection.class); fail("Did not throw"); - } catch(IllegalArgumentException e) { - assertEquals("interface java.util.Map is not assignable to interface java.util.Collection", e.getMessage()); + } + catch (IllegalArgumentException ex) { + assertEquals("interface java.util.Map is not assignable to interface java.util.Collection", ex.getMessage()); } } @@ -933,13 +701,13 @@ public class TypeDescriptorTests { @Test public void createMapArray() throws Exception { - TypeDescriptor mapType = TypeDescriptor.map(LinkedHashMap.class, TypeDescriptor.valueOf(String.class), TypeDescriptor.valueOf(Integer.class)); + TypeDescriptor mapType = TypeDescriptor.map( + LinkedHashMap.class, TypeDescriptor.valueOf(String.class), TypeDescriptor.valueOf(Integer.class)); TypeDescriptor arrayType = TypeDescriptor.array(mapType); assertEquals(arrayType.getType(), LinkedHashMap[].class); assertEquals(arrayType.getElementTypeDescriptor(), mapType); } - @Test public void createStringArray() throws Exception { TypeDescriptor arrayType = TypeDescriptor.array(TypeDescriptor.valueOf(String.class)); @@ -985,4 +753,268 @@ public class TypeDescriptorTests { assertThat(TypeDescriptor.valueOf(Integer.class).getSource(), equalTo((Object) Integer.class)); } + + // Methods designed for test introspection + + public void testParameterPrimitive(int primitive) { + } + + public void testParameterScalar(String value) { + } + + public void testParameterList(List>>> list) { + } + + public void testParameterListNoParamTypes(List list) { + } + + public void testParameterArray(Integer[] array) { + } + + public void testParameterMap(Map> map) { + } + + public void test1(List param1) { + } + + public void test2(List> param1) { + } + + public void test3(Map param1) { + } + + public void test4(List> param1) { + } + + public void test5(String param1) { + } + + public void test6(List param1) { + } + + public List> getTest4() { + return null; + } + + public void setTest4(List> test4) { + } + + public Map>> getComplexProperty() { + return null; + } + + @MethodAnnotation1 + public Map, List> getProperty() { + return property; + } + + @MethodAnnotation2 + public void setProperty(Map, List> property) { + this.property = property; + } + + @MethodAnnotation1 + public void methodWithLocalAnnotation() { + } + + @ComposedMethodAnnotation1 + public void methodWithComposedAnnotation() { + } + + @ComposedComposedMethodAnnotation1 + public void methodWithComposedComposedAnnotation() { + } + + public void setComplexProperty(Map>> complexProperty) { + } + + public void testAnnotatedMethod(@ParameterAnnotation(123) String parameter) { + } + + public void testAnnotatedMethodDifferentAnnotationValue(@ParameterAnnotation(567) String parameter) { + } + + + // Fields designed for test introspection + + public Integer fieldScalar; + + public List listOfString; + + public List> listOfListOfString = new ArrayList>(); + + public List listOfListOfUnknown = new ArrayList(); + + public int[] intArray; + + public List[] arrayOfListOfString; + + public List listField = new ArrayList(); + + public Map mapField = new HashMap(); + + public Map> nestedMapField = new HashMap>(); + + public Map, List> fieldMap; + + public List> test4; + + @FieldAnnotation + public List fieldAnnotated; + + @FieldAnnotation + public List> listPreserveContext; + + @FieldAnnotation + public Map, List> mapPreserveContext; + + @MethodAnnotation3 + private Map, List> property; + + public List notGenericList; + + public List isAssignableElementTypes; + + public Map notGenericMap; + + public Map isAssignableMapKeyValueTypes; + + public MultiValueMap multiValueMap = new LinkedMultiValueMap(); + + public PassDownGeneric passDownGeneric = new PassDownGeneric(); + + + // Classes designed for test introspection + + @SuppressWarnings("serial") + public static class PassDownGeneric extends ArrayList>> { + } + + + public static class GenericClass { + + public T getProperty() { + return null; + } + + public void setProperty(T t) { + } + + @MethodAnnotation1 + public List getListProperty() { + return null; + } + + public void setListProperty(List t) { + } + } + + + public static class IntegerClass extends GenericClass { + } + + + public interface GenericType { + + T getProperty(); + + void setProperty(T t); + + List getListProperty(); + + void setListProperty(List t); + } + + + public class IntegerType implements GenericType { + + @Override + public Integer getProperty() { + return null; + } + + @Override + public void setProperty(Integer t) { + } + + @Override + public List getListProperty() { + return null; + } + + @Override + public void setListProperty(List t) { + } + } + + + public class NumberType implements GenericType { + + @Override + public Integer getProperty() { + return null; + } + + @Override + public void setProperty(Number t) { + } + + @Override + public List getListProperty() { + return null; + } + + @Override + public void setListProperty(List t) { + } + } + + + // Annotations used on tested elements + + @Target({ElementType.PARAMETER}) + @Retention(RetentionPolicy.RUNTIME) + public @interface ParameterAnnotation { + + int value(); + } + + + @Target({ElementType.FIELD}) + @Retention(RetentionPolicy.RUNTIME) + public @interface FieldAnnotation { + } + + + @Target({ElementType.METHOD, ElementType.ANNOTATION_TYPE}) + @Retention(RetentionPolicy.RUNTIME) + public @interface MethodAnnotation1 { + } + + + @Target({ElementType.METHOD}) + @Retention(RetentionPolicy.RUNTIME) + public @interface MethodAnnotation2 { + } + + + @Target({ElementType.FIELD}) + @Retention(RetentionPolicy.RUNTIME) + public @interface MethodAnnotation3 { + } + + + @MethodAnnotation1 + @Target({ElementType.METHOD, ElementType.ANNOTATION_TYPE}) + @Retention(RetentionPolicy.RUNTIME) + public @interface ComposedMethodAnnotation1 { + } + + + @ComposedMethodAnnotation1 + @Target(ElementType.METHOD) + @Retention(RetentionPolicy.RUNTIME) + public @interface ComposedComposedMethodAnnotation1 { + } + } diff --git a/spring-core/src/test/java/org/springframework/core/convert/support/GenericConversionServiceTests.java b/spring-core/src/test/java/org/springframework/core/convert/support/GenericConversionServiceTests.java index 6b16aefe5a..4b3183b65b 100644 --- a/spring-core/src/test/java/org/springframework/core/convert/support/GenericConversionServiceTests.java +++ b/spring-core/src/test/java/org/springframework/core/convert/support/GenericConversionServiceTests.java @@ -478,6 +478,21 @@ public class GenericConversionServiceTests { assertTrue(converter.getNestedMatchAttempts() > 0); } + @Test + public void conditionalConverterCachingForDifferentAnnotationAttributes() throws Exception { + conversionService.addConverter(new ColorConverter()); + conversionService.addConverter(new MyConditionalColorConverter()); + + assertEquals(Color.BLACK, conversionService.convert("000000xxxx", + new TypeDescriptor(getClass().getField("activeColor")))); + assertEquals(Color.BLACK, conversionService.convert(" #000000 ", + new TypeDescriptor(getClass().getField("inactiveColor")))); + assertEquals(Color.BLACK, conversionService.convert("000000yyyy", + new TypeDescriptor(getClass().getField("activeColor")))); + assertEquals(Color.BLACK, conversionService.convert(" #000000 ", + new TypeDescriptor(getClass().getField("inactiveColor")))); + } + @Test public void shouldNotSupportNullConvertibleTypesFromNonConditionalGenericConverter() { GenericConverter converter = new NonConditionalGenericConverter(); @@ -629,14 +644,49 @@ public class GenericConversionServiceTests { } + @ExampleAnnotation(active = true) + public String annotatedString; + + @ExampleAnnotation(active = true) + public Color activeColor; + + @ExampleAnnotation(active = false) + public Color inactiveColor; + + public List list; + + public Map map; + + public Map wildcardMap; + + @SuppressWarnings("rawtypes") + public Collection rawCollection; + + public Collection genericCollection; + + public Collection stringCollection; + + public Collection integerCollection; + + @Retention(RetentionPolicy.RUNTIME) - private static @interface ExampleAnnotation {} + private @interface ExampleAnnotation { + + boolean active(); + } + + + private interface MyBaseInterface { + } - private static interface MyBaseInterface {} - private static interface MyInterface extends MyBaseInterface {} + private interface MyInterface extends MyBaseInterface { + } + + + private static class MyInterfaceImplementer implements MyInterface { + } - private static class MyInterfaceImplementer implements MyInterface {} private static class MyBaseInterfaceToStringConverter implements Converter { @@ -646,6 +696,7 @@ public class GenericConversionServiceTests { } } + private static class MyStringArrayToResourceArrayConverter implements Converter { @Override @@ -654,6 +705,7 @@ public class GenericConversionServiceTests { } } + private static class MyStringArrayToIntegerArrayConverter implements Converter { @Override @@ -662,6 +714,7 @@ public class GenericConversionServiceTests { } } + private static class MyStringToIntegerArrayConverter implements Converter { @Override @@ -671,6 +724,7 @@ public class GenericConversionServiceTests { } } + private static class WithCopyConstructor { WithCopyConstructor() {} @@ -679,6 +733,7 @@ public class GenericConversionServiceTests { WithCopyConstructor(WithCopyConstructor value) {} } + private static class MyConditionalConverter implements Converter, ConditionalConverter { private int matchAttempts = 0; @@ -699,6 +754,7 @@ public class GenericConversionServiceTests { } } + private static class NonConditionalGenericConverter implements GenericConverter { @Override @@ -712,6 +768,7 @@ public class GenericConversionServiceTests { } } + private static class MyConditionalGenericConverter implements GenericConverter, ConditionalConverter { private final List sourceTypes = new ArrayList(); @@ -737,6 +794,7 @@ public class GenericConversionServiceTests { } } + private static class MyConditionalConverterFactory implements ConverterFactory, ConditionalConverter { private MyConditionalConverter converter = new MyConditionalConverter(); @@ -768,11 +826,13 @@ public class GenericConversionServiceTests { String getBaseCode(); } - private static interface MyEnumInterface extends MyEnumBaseInterface { + + private interface MyEnumInterface extends MyEnumBaseInterface { String getCode(); } - private static enum MyEnum implements MyEnumInterface { + + private enum MyEnum implements MyEnumInterface { A("1"), B("2"), @@ -795,7 +855,8 @@ public class GenericConversionServiceTests { } } - private static enum EnumWithSubclass { + + private enum EnumWithSubclass { FIRST { @Override @@ -805,6 +866,7 @@ public class GenericConversionServiceTests { } } + @SuppressWarnings("rawtypes") private static class MyStringToRawCollectionConverter implements Converter { @@ -814,6 +876,7 @@ public class GenericConversionServiceTests { } } + private static class MyStringToGenericCollectionConverter implements Converter> { @Override @@ -822,6 +885,7 @@ public class GenericConversionServiceTests { } } + private static class MyEnumInterfaceToStringConverter implements Converter { @Override @@ -830,14 +894,16 @@ public class GenericConversionServiceTests { } } + private static class StringToMyEnumInterfaceConverterFactory implements ConverterFactory { - @SuppressWarnings({ "unchecked", "rawtypes" }) + @SuppressWarnings({"unchecked", "rawtypes"}) public Converter getConverter(Class targetType) { return new StringToMyEnumInterfaceConverter(targetType); } private static class StringToMyEnumInterfaceConverter & MyEnumInterface> implements Converter { + private final Class enumType; public StringToMyEnumInterfaceConverter(Class enumType) { @@ -855,9 +921,10 @@ public class GenericConversionServiceTests { } } + private static class StringToMyEnumBaseInterfaceConverterFactory implements ConverterFactory { - @SuppressWarnings({ "unchecked", "rawtypes" }) + @SuppressWarnings({"unchecked", "rawtypes"}) public Converter getConverter(Class targetType) { return new StringToMyEnumBaseInterfaceConverter(targetType); } @@ -881,6 +948,7 @@ public class GenericConversionServiceTests { } } + private static class MyStringToStringCollectionConverter implements Converter> { @Override @@ -889,6 +957,7 @@ public class GenericConversionServiceTests { } } + private static class MyStringToIntegerCollectionConverter implements Converter> { @Override @@ -897,6 +966,7 @@ public class GenericConversionServiceTests { } } + @SuppressWarnings("rawtypes") private static class UntypedConverter implements Converter { @@ -906,28 +976,27 @@ public class GenericConversionServiceTests { } } + private static class ColorConverter implements Converter { + @Override - public Color convert(String source) { if (!source.startsWith("#")) source = "#" + source; return Color.decode(source); } + public Color convert(String source) { + return Color.decode(source.trim()); + } } - @ExampleAnnotation - public String annotatedString; - - public List list; - - public Map map; - - public Map wildcardMap; - - @SuppressWarnings("rawtypes") - public Collection rawCollection; - - public Collection genericCollection; - - public Collection stringCollection; + private static class MyConditionalColorConverter implements Converter, ConditionalConverter { - public Collection integerCollection; + @Override + public boolean matches(TypeDescriptor sourceType, TypeDescriptor targetType) { + ExampleAnnotation ann = targetType.getAnnotation(ExampleAnnotation.class); + return (ann != null && ann.active()); + } + @Override + public Color convert(String source) { + return Color.decode(source.substring(0, 6)); + } + } } -- GitLab