Skip to content

Commit a1a7c32

Browse files
author
Keith Donald
committed
string to collection and array converters now are conditional and apply target element type match
1 parent 38041ac commit a1a7c32

File tree

5 files changed

+50
-12
lines changed

5 files changed

+50
-12
lines changed

org.springframework.core/src/main/java/org/springframework/core/convert/ConversionService.java

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,12 @@ public interface ConversionService {
2727

2828
/**
2929
* Returns true if objects of sourceType can be converted to targetType.
30-
* @param sourceType the source type to convert from (required)
30+
* If this method returns true, it means {@link #convert(Object, Class)} is capable of converting an instance of sourceType to targetType.
31+
* Special note on collections, arrays, and maps types:
32+
* For conversion between collection, array, and map types, this method will return 'true'
33+
* even though a convert invocation may still generate a {@link ConversionException} if the underlying elements are not convertible.
34+
* Callers are expected to handle this exceptional case when working with collections and maps.
35+
* @param sourceType the source type to convert from (may be null if source is null)
3136
* @param targetType the target type to convert to (required)
3237
* @return true if a conversion can be performed, false if not
3338
* @throws IllegalArgumentException if targetType is null
@@ -36,12 +41,16 @@ public interface ConversionService {
3641

3742
/**
3843
* Returns true if objects of sourceType can be converted to the targetType.
39-
* The TypeDescriptors provide additional context about the source and target locations where conversion would occur, often object property locations.
40-
* @param sourceType context about the source type to convert from (required)
44+
* The TypeDescriptors provide additional context about the source and target locations where conversion would occur, often object fields or property locations.
45+
* If this method returns true, it means {@link #convert(Object, TypeDescriptor, TypeDescriptor)} is capable of converting an instance of sourceType to targetType.
46+
* Special note on collections, arrays, and maps types:
47+
* For conversion between collection, array, and map types, this method will return 'true'
48+
* even though a convert invocation may still generate a {@link ConversionException} if the underlying elements are not convertible.
49+
* Callers are expected to handle this exceptional case when working with collections and maps.
50+
* @param sourceType context about the source type to convert from (may be null if source is null)
4151
* @param targetType context about the target type to convert to (required)
4252
* @return true if a conversion can be performed between the source and target types, false if not
4353
* @throws IllegalArgumentException if targetType is null
44-
* @see TypeDescriptor#forObject(Object)
4554
*/
4655
boolean canConvert(TypeDescriptor sourceType, TypeDescriptor targetType);
4756

@@ -57,15 +66,14 @@ public interface ConversionService {
5766

5867
/**
5968
* Convert the source to targetType.
60-
* The TypeDescriptors provide additional context about the source and target locations where conversion will occur, often object property locations.
69+
* The TypeDescriptors provide additional context about the source and target locations where conversion will occur, often object fields or property locations.
6170
* @param source the source object to convert (may be null)
6271
* @param sourceType context about the source type converting from (may be null if source is null)
6372
* @param targetType context about the target type to convert to (required)
6473
* @return the converted object, an instance of {@link TypeDescriptor#getObjectType() targetType}</code>
6574
* @throws ConversionException if a conversion exception occurred
6675
* @throws IllegalArgumentException if targetType is null
6776
* @throws IllegalArgumentException if sourceType is null but source is not null
68-
* @see TypeDescriptor#forObject(Object)
6977
*/
7078
Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType);
7179

org.springframework.core/src/main/java/org/springframework/core/convert/support/StringToArrayConverter.java

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,16 +22,17 @@
2222

2323
import org.springframework.core.convert.ConversionService;
2424
import org.springframework.core.convert.TypeDescriptor;
25-
import org.springframework.core.convert.converter.GenericConverter;
25+
import org.springframework.core.convert.converter.ConditionalGenericConverter;
2626
import org.springframework.util.StringUtils;
2727

2828
/**
2929
* Converts a comma-delimited String to an Array.
30-
*
30+
* Only matches if String.class can be converted to the target array element type.
31+
*
3132
* @author Keith Donald
3233
* @since 3.0
3334
*/
34-
final class StringToArrayConverter implements GenericConverter {
35+
final class StringToArrayConverter implements ConditionalGenericConverter {
3536

3637
private final ConversionService conversionService;
3738

@@ -43,6 +44,10 @@ public Set<ConvertiblePair> getConvertibleTypes() {
4344
return Collections.singleton(new ConvertiblePair(String.class, Object[].class));
4445
}
4546

47+
public boolean matches(TypeDescriptor sourceType, TypeDescriptor targetType) {
48+
return this.conversionService.canConvert(sourceType, targetType.getElementType());
49+
}
50+
4651
public Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType) {
4752
if (source == null) {
4853
return null;

org.springframework.core/src/main/java/org/springframework/core/convert/support/StringToCollectionConverter.java

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828

2929
/**
3030
* Converts a comma-delimited String to a Collection.
31+
* If the target collection element type is declared, only matches if String.class can be converted to it.
3132
*
3233
* @author Keith Donald
3334
* @since 3.0
@@ -45,10 +46,11 @@ public Set<ConvertiblePair> getConvertibleTypes() {
4546
}
4647

4748
public boolean matches(TypeDescriptor sourceType, TypeDescriptor targetType) {
48-
if (targetType.getElementType() == null) {
49+
if (targetType.getElementType() != null) {
50+
return this.conversionService.canConvert(sourceType, targetType.getElementType());
51+
} else {
4952
return true;
5053
}
51-
return this.conversionService.canConvert(sourceType, targetType.getElementType());
5254
}
5355

5456
@SuppressWarnings("unchecked")

org.springframework.core/src/test/java/org/springframework/core/convert/support/DefaultConversionTests.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -759,4 +759,5 @@ public static TestEntity findTestEntity(Long id) {
759759
return new TestEntity(id);
760760
}
761761
}
762+
762763
}

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

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,16 +16,17 @@
1616

1717
package org.springframework.core.convert.support;
1818

19+
import static junit.framework.Assert.assertTrue;
1920
import static org.junit.Assert.assertEquals;
2021
import static org.junit.Assert.assertFalse;
2122
import static org.junit.Assert.assertNotNull;
2223
import static org.junit.Assert.assertNull;
2324
import static org.junit.Assert.assertSame;
24-
import static org.junit.Assert.assertTrue;
2525
import static org.junit.Assert.fail;
2626

2727
import java.util.ArrayList;
2828
import java.util.Arrays;
29+
import java.util.Collection;
2930
import java.util.HashMap;
3031
import java.util.LinkedHashMap;
3132
import java.util.LinkedList;
@@ -528,4 +529,25 @@ public WithCopyConstructor(WithCopyConstructor value) {
528529

529530
public static Map<String, ?> wildcardMap;
530531

532+
@Test
533+
public void stringToArrayCanConvert() {
534+
conversionService.addConverter(new StringToArrayConverter(conversionService));
535+
assertFalse(conversionService.canConvert(String.class, Integer[].class));
536+
conversionService.addConverterFactory(new StringToNumberConverterFactory());
537+
assertTrue(conversionService.canConvert(String.class, Integer[].class));
538+
}
539+
540+
@Test
541+
public void stringToCollectionCanConvert() throws Exception {
542+
conversionService.addConverter(new StringToCollectionConverter(conversionService));
543+
assertTrue(conversionService.canConvert(String.class, Collection.class));
544+
TypeDescriptor targetType = new TypeDescriptor(getClass().getField("stringToCollection"));
545+
assertFalse(conversionService.canConvert(TypeDescriptor.valueOf(String.class), targetType));
546+
conversionService.addConverterFactory(new StringToNumberConverterFactory());
547+
assertTrue(conversionService.canConvert(TypeDescriptor.valueOf(String.class), targetType));
548+
}
549+
550+
public Collection<Integer> stringToCollection;
551+
552+
531553
}

0 commit comments

Comments
 (0)