Skip to content

Commit 874a2a9

Browse files
committed
Backwards-compatible handling of generic and raw collection converters
Issue: SPR-11369
1 parent e6f4796 commit 874a2a9

File tree

3 files changed

+88
-25
lines changed

3 files changed

+88
-25
lines changed

spring-core/src/main/java/org/springframework/core/convert/converter/ConditionalGenericConverter.java

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2012 the original author or authors.
2+
* Copyright 2002-2014 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -19,17 +19,16 @@
1919
import org.springframework.core.convert.TypeDescriptor;
2020

2121
/**
22-
* A {@link GenericConverter} that may conditionally execute based on attributes of the
23-
* {@code source} and {@code target} {@link TypeDescriptor}. See
24-
* {@link ConditionalConverter} for details.
22+
* A {@link GenericConverter} that may conditionally execute based on attributes
23+
* of the {@code source} and {@code target} {@link TypeDescriptor}.
24+
* See {@link ConditionalConverter} for details.
2525
*
2626
* @author Keith Donald
2727
* @author Phillip Webb
2828
* @since 3.0
2929
* @see GenericConverter
3030
* @see ConditionalConverter
3131
*/
32-
public interface ConditionalGenericConverter extends GenericConverter,
33-
ConditionalConverter {
32+
public interface ConditionalGenericConverter extends GenericConverter, ConditionalConverter {
3433

3534
}

spring-core/src/main/java/org/springframework/core/convert/support/GenericConversionService.java

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -331,18 +331,18 @@ public Set<ConvertiblePair> getConvertibleTypes() {
331331

332332
@Override
333333
public boolean matches(TypeDescriptor sourceType, TypeDescriptor targetType) {
334-
ResolvableType rt = targetType.getResolvableType();
335-
if (!rt.isAssignableFrom(this.targetType)) {
336-
// Generic type structure not fully assignable -> try lenient fallback if
337-
// unresolvable generics remain, just requiring the raw type to match then
338-
if (!rt.hasUnresolvableGenerics() || !this.typeInfo.getTargetType().equals(targetType.getObjectType())) {
339-
return false;
340-
}
334+
// Check raw type first...
335+
if (!this.typeInfo.getTargetType().equals(targetType.getObjectType())) {
336+
return false;
341337
}
342-
if (this.converter instanceof ConditionalConverter) {
343-
return ((ConditionalConverter) this.converter).matches(sourceType, targetType);
338+
// Full check for complex generic type match required?
339+
ResolvableType rt = targetType.getResolvableType();
340+
if (!(rt.getType() instanceof Class) && !rt.isAssignableFrom(this.targetType) &&
341+
!this.targetType.hasUnresolvableGenerics()) {
342+
return false;
344343
}
345-
return true;
344+
return !(this.converter instanceof ConditionalConverter) ||
345+
((ConditionalConverter) this.converter).matches(sourceType, targetType);
346346
}
347347

348348
@Override

spring-core/src/test/java/org/springframework/core/convert/support/GenericConversionServiceTests.java

Lines changed: 73 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -619,7 +619,7 @@ public void convertPrimitiveArray() {
619619
GenericConversionService conversionService = new DefaultConversionService();
620620
byte[] byteArray = new byte[] { 1, 2, 3 };
621621
Byte[] converted = conversionService.convert(byteArray, Byte[].class);
622-
assertTrue(Arrays.equals(converted, new Byte[] { 1, 2, 3 }));
622+
assertTrue(Arrays.equals(converted, new Byte[] {1, 2, 3}));
623623
}
624624

625625
@Test
@@ -739,7 +739,7 @@ public Byte convert(Byte source) {
739739
byte[] byteArray = new byte[] { 1, 2, 3 };
740740
byte[] converted = conversionService.convert(byteArray, byte[].class);
741741
assertNotSame(byteArray, converted);
742-
assertTrue(Arrays.equals(new byte[] { 2, 3, 4 }, converted));
742+
assertTrue(Arrays.equals(new byte[] {2, 3, 4}, converted));
743743
}
744744

745745
@Test
@@ -769,8 +769,11 @@ public void convertNullAnnotatedStringToString() throws Exception {
769769

770770
@Test
771771
public void multipleCollectionTypesFromSameSourceType() throws Exception {
772+
conversionService.addConverter(new MyStringToRawCollectionConverter());
773+
conversionService.addConverter(new MyStringToGenericCollectionConverter());
772774
conversionService.addConverter(new MyStringToStringCollectionConverter());
773775
conversionService.addConverter(new MyStringToIntegerCollectionConverter());
776+
774777
assertEquals(Collections.singleton("testX"),
775778
conversionService.convert("test", TypeDescriptor.valueOf(String.class), new TypeDescriptor(getClass().getField("stringCollection"))));
776779
assertEquals(Collections.singleton(4),
@@ -788,6 +791,7 @@ public void multipleCollectionTypesFromSameSourceType() throws Exception {
788791
@Test
789792
public void adaptedCollectionTypesFromSameSourceType() throws Exception {
790793
conversionService.addConverter(new MyStringToStringCollectionConverter());
794+
791795
assertEquals(Collections.singleton("testX"),
792796
conversionService.convert("test", TypeDescriptor.valueOf(String.class), new TypeDescriptor(getClass().getField("stringCollection"))));
793797
assertEquals(Collections.singleton("testX"),
@@ -800,6 +804,46 @@ public void adaptedCollectionTypesFromSameSourceType() throws Exception {
800804
conversionService.convert("test", TypeDescriptor.valueOf(String.class), new TypeDescriptor(getClass().getField("stringCollection"))));
801805
assertEquals(Collections.singleton("testX"),
802806
conversionService.convert("test", TypeDescriptor.valueOf(String.class), new TypeDescriptor(getClass().getField("rawCollection"))));
807+
808+
try {
809+
conversionService.convert("test", TypeDescriptor.valueOf(String.class), new TypeDescriptor(getClass().getField("integerCollection")));
810+
fail("Should have thrown ConverterNotFoundException");
811+
}
812+
catch (ConverterNotFoundException ex) {
813+
// expected
814+
}
815+
}
816+
817+
@Test
818+
public void genericCollectionAsSource() throws Exception {
819+
conversionService.addConverter(new MyStringToGenericCollectionConverter());
820+
821+
assertEquals(Collections.singleton("testX"),
822+
conversionService.convert("test", TypeDescriptor.valueOf(String.class), new TypeDescriptor(getClass().getField("stringCollection"))));
823+
assertEquals(Collections.singleton("testX"),
824+
conversionService.convert("test", TypeDescriptor.valueOf(String.class), new TypeDescriptor(getClass().getField("genericCollection"))));
825+
assertEquals(Collections.singleton("testX"),
826+
conversionService.convert("test", TypeDescriptor.valueOf(String.class), new TypeDescriptor(getClass().getField("rawCollection"))));
827+
828+
// The following is unpleasant but a consequence of the generic collection converter above...
829+
assertEquals(Collections.singleton("testX"),
830+
conversionService.convert("test", TypeDescriptor.valueOf(String.class), new TypeDescriptor(getClass().getField("integerCollection"))));
831+
}
832+
833+
@Test
834+
public void rawCollectionAsSource() throws Exception {
835+
conversionService.addConverter(new MyStringToRawCollectionConverter());
836+
837+
assertEquals(Collections.singleton("testX"),
838+
conversionService.convert("test", TypeDescriptor.valueOf(String.class), new TypeDescriptor(getClass().getField("stringCollection"))));
839+
assertEquals(Collections.singleton("testX"),
840+
conversionService.convert("test", TypeDescriptor.valueOf(String.class), new TypeDescriptor(getClass().getField("genericCollection"))));
841+
assertEquals(Collections.singleton("testX"),
842+
conversionService.convert("test", TypeDescriptor.valueOf(String.class), new TypeDescriptor(getClass().getField("rawCollection"))));
843+
844+
// The following is unpleasant but a consequence of the raw collection converter above...
845+
assertEquals(Collections.singleton("testX"),
846+
conversionService.convert("test", TypeDescriptor.valueOf(String.class), new TypeDescriptor(getClass().getField("integerCollection"))));
803847
}
804848

805849

@@ -810,6 +854,7 @@ public void adaptedCollectionTypesFromSameSourceType() throws Exception {
810854
public static @interface ExampleAnnotation {
811855
}
812856

857+
813858
private static class MyConditionalConverter implements Converter<String, Color>, ConditionalConverter {
814859

815860
private int matchAttempts = 0;
@@ -830,8 +875,8 @@ public int getMatchAttempts() {
830875
}
831876
}
832877

833-
private static class MyConditionalGenericConverter implements GenericConverter,
834-
ConditionalConverter {
878+
879+
private static class MyConditionalGenericConverter implements GenericConverter, ConditionalConverter {
835880

836881
private List<TypeDescriptor> sourceTypes = new ArrayList<TypeDescriptor>();
837882

@@ -857,8 +902,8 @@ public List<TypeDescriptor> getSourceTypes() {
857902
}
858903
}
859904

860-
private static class MyConditionalConverterFactory implements
861-
ConverterFactory<String, Color>, ConditionalConverter {
905+
906+
private static class MyConditionalConverterFactory implements ConverterFactory<String, Color>, ConditionalConverter {
862907

863908
private MyConditionalConverter converter = new MyConditionalConverter();
864909

@@ -885,6 +930,7 @@ public int getNestedMatchAttempts() {
885930
}
886931
}
887932

933+
888934
interface MyEnumInterface {
889935

890936
String getCode();
@@ -900,6 +946,23 @@ public String getCode() {
900946
}
901947
}
902948

949+
950+
public static class MyStringToRawCollectionConverter implements Converter<String, Collection> {
951+
952+
@Override
953+
public Collection convert(String source) {
954+
return Collections.singleton(source + "X");
955+
}
956+
}
957+
958+
public static class MyStringToGenericCollectionConverter implements Converter<String, Collection<?>> {
959+
960+
@Override
961+
public Collection<?> convert(String source) {
962+
return Collections.singleton(source + "X");
963+
}
964+
}
965+
903966
private static class MyEnumInterfaceToStringConverter<T extends MyEnumInterface> implements Converter<T, String> {
904967

905968
@Override
@@ -924,12 +987,13 @@ public Collection<Integer> convert(String source) {
924987
}
925988
}
926989

927-
public Collection<String> stringCollection;
928-
929-
public Collection<Integer> integerCollection;
930990

931991
public Collection rawCollection;
932992

933993
public Collection<?> genericCollection;
934994

995+
public Collection<String> stringCollection;
996+
997+
public Collection<Integer> integerCollection;
998+
935999
}

0 commit comments

Comments
 (0)