Skip to content

Commit a40f658

Browse files
author
Keith Donald
committed
added convert(Object, TypeDescriptor) convenience method; collection and map tests
1 parent 8c68906 commit a40f658

File tree

5 files changed

+88
-46
lines changed

5 files changed

+88
-46
lines changed

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

Lines changed: 13 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -34,20 +34,9 @@ public interface ConversionService {
3434
*/
3535
boolean canConvert(Class<?> sourceType, Class<?> targetType);
3636

37-
/**
38-
* Convert the source to targetType.
39-
* @param source the source object to convert (may be null)
40-
* @param targetType the target type to convert to (required)
41-
* @return the converted object, an instance of targetType
42-
* @throws ConversionException if an exception occurred
43-
* @throws IllegalArgumentException if targetType is null
44-
*/
45-
<T> T convert(Object source, Class<T> targetType);
46-
4737
/**
4838
* Returns true if objects of sourceType can be converted to the targetType.
49-
* The TypeDescriptors provide additional context about the field locations where conversion would occur, often object property locations.
50-
* This flavor of the canConvert operation exists mainly for use by a general purpose data mapping framework, and not for use by user code.
39+
* The TypeDescriptors provide additional context about the source and target locations where conversion would occur, often object property locations.
5140
* @param sourceType context about the source type to convert from (required)
5241
* @param targetType context about the target type to convert to (required)
5342
* @return true if a conversion can be performed between the source and target types, false if not
@@ -58,13 +47,22 @@ public interface ConversionService {
5847

5948
/**
6049
* Convert the source to targetType.
61-
* The TypeDescriptors provide additional context about the field locations where conversion will occur, often object property locations.
62-
* This flavor of the convert operation exists mainly for use by a general purpose data mapping framework, and not for use by user code.
50+
* @param source the source object to convert (may be null)
51+
* @param targetType the target type to convert to (required)
52+
* @return the converted object, an instance of targetType
53+
* @throws ConversionException if a conversion exception occurred
54+
* @throws IllegalArgumentException if targetType is null
55+
*/
56+
<T> T convert(Object source, Class<T> targetType);
57+
58+
/**
59+
* 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.
6361
* @param source the source object to convert (may be null)
6462
* @param sourceType context about the source type converting from (may be null if source is null)
6563
* @param targetType context about the target type to convert to (required)
6664
* @return the converted object, an instance of {@link TypeDescriptor#getObjectType() targetType}</code>
67-
* @throws ConversionException if an exception occurred
65+
* @throws ConversionException if a conversion exception occurred
6866
* @throws IllegalArgumentException if targetType is null
6967
* @throws IllegalArgumentException if sourceType is null but source is not null
7068
* @see TypeDescriptor#forObject(Object)

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

Lines changed: 23 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -70,35 +70,35 @@ public class TypeDescriptor {
7070

7171
/**
7272
* Create a new type descriptor from a {@link MethodParameter}.
73-
* Use this constructor when a conversion point is a constructor parameter, method parameter, or method return value.
73+
* Use this constructor when a source or target conversion point is a constructor parameter, method parameter, or method return value.
7474
* @param methodParameter the method parameter
7575
*/
7676
public TypeDescriptor(MethodParameter methodParameter) {
7777
this(new ParameterDescriptor(methodParameter));
7878
}
7979

8080
/**
81-
* Create a new type descriptor for a field.
82-
* Use this constructor when a conversion point is a field.
81+
* Create a new type descriptor from a {@link Field}.
82+
* Use this constructor when source or target conversion point is a field.
8383
* @param field the field
8484
*/
8585
public TypeDescriptor(Field field) {
8686
this(new FieldDescriptor(field));
8787
}
8888

8989
/**
90-
* Create a new type descriptor for a bean property.
91-
* Use this constructor when a target conversion point is a property on a Java class.
90+
* Create a new type descriptor from a {@link Property}.
91+
* Use this constructor when a source or target conversion point is a property on a Java class.
9292
* @param property the property
9393
*/
9494
public TypeDescriptor(Property property) {
9595
this(new BeanPropertyDescriptor(property));
9696
}
9797

9898
/**
99-
* Create a new type descriptor for the given class.
100-
* Use this to instruct the conversion system to convert to an object to a specific target type, when no type location such as a method parameter or field is available to provide additional conversion context.
101-
* Generally prefer use of {@link #forObject(Object)} for constructing source type descriptors for source objects.
99+
* Create a new type descriptor from the given type.
100+
* Use this to instruct the conversion system to convert an object to a specific target type, when no type location such as a method parameter or field is available to provide additional conversion context.
101+
* Generally prefer use of {@link #forObject(Object)} for constructing type descriptors from source objects, as it handles the null object case.
102102
* @param type the class
103103
* @return the type descriptor
104104
*/
@@ -108,9 +108,10 @@ public static TypeDescriptor valueOf(Class<?> type) {
108108
}
109109

110110
/**
111-
* Create a new type descriptor for a java.util.Collection class.
112-
* Useful for supporting conversion of source Collection objects to other types.
113-
* Serves as an alternative to {@link #forObject(Object)} to be used when you cannot rely on Collection element introspection to resolve the element type.
111+
* Create a new type descriptor from a java.util.Collection type.
112+
* Useful for converting to typed Collections.
113+
* For example, a List&lt;String&gt; could be converted to a List&lt;EmailAddress&gt; by converting to a targetType built with this method.
114+
* The method call to construct such a TypeDescriptor would look something like: collection(List.class, TypeDescriptor.valueOf(EmailAddress.class));
114115
* @param collectionType the collection type, which must implement {@link Collection}.
115116
* @param elementType the collection's element type, used to convert collection elements
116117
* @return the collection type descriptor
@@ -123,9 +124,10 @@ public static TypeDescriptor collection(Class<?> collectionType, TypeDescriptor
123124
}
124125

125126
/**
126-
* Create a new type descriptor for a java.util.Map class.
127-
* Useful for supporting the conversion of source Map objects to other types.
128-
* Serves as an alternative to {@link #forObject(Object)} to be used when you cannot rely on Map entry introspection to resolve the key and value type.
127+
* Create a new type descriptor from a java.util.Map type.
128+
* Useful for Converting to typed Maps.
129+
* For example, a Map&lt;String, String&gt; could be converted to a Map&lt;Id, EmailAddress&gt; by converting to a targetType built with this method:
130+
* The method call to construct such a TypeDescriptor would look something like: map(Map.class, TypeDescriptor.valueOf(Id.class), TypeDescriptor.valueOf(EmailAddress.class));
129131
* @param mapType the map type, which must implement {@link Map}.
130132
* @param keyType the map's key type, used to convert map keys
131133
* @param valueType the map's value type, used to convert map values
@@ -201,16 +203,19 @@ public static TypeDescriptor forObject(Object source) {
201203
}
202204

203205
/**
204-
* Determine the declared (non-generic) type of the wrapped parameter/field.
205-
* @return the declared type, or <code>null</code> if this is {@link TypeDescriptor#NULL}
206+
* The type of the backing class, method parameter, field, or property described by this TypeDescriptor.
207+
* Returns primitive types as-is.
208+
* See {@link #getObjectType()} for a variation of this operation that resolves primitive types to their corresponding Object types if necessary.
209+
* @return the type, or <code>null</code> if this is {@link TypeDescriptor#NULL}
210+
* @see #getObjectType()
206211
*/
207212
public Class<?> getType() {
208213
return type;
209214
}
210215

211216
/**
212-
* Determine the declared type of the wrapped parameter/field.
213-
* Returns the Object wrapper type if the underlying type is a primitive.
217+
* Variation of {@link #getType()} that accounts for a primitive type by returning its object wrapper type.
218+
* This is useful for conversion service implementations that wish to normalize to object-based types and not work with primitive types directly.
214219
*/
215220
public Class<?> getObjectType() {
216221
return ClassUtils.resolvePrimitiveIfNecessary(getType());

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

Lines changed: 23 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
import org.apache.commons.logging.Log;
3333
import org.apache.commons.logging.LogFactory;
3434
import org.springframework.core.GenericTypeResolver;
35+
import org.springframework.core.convert.ConversionException;
3536
import org.springframework.core.convert.ConversionFailedException;
3637
import org.springframework.core.convert.ConversionService;
3738
import org.springframework.core.convert.ConverterNotFoundException;
@@ -139,14 +140,6 @@ public boolean canConvert(Class<?> sourceType, Class<?> targetType) {
139140
return canConvert(sourceType != null ? TypeDescriptor.valueOf(sourceType) : null, TypeDescriptor.valueOf(targetType));
140141
}
141142

142-
@SuppressWarnings("unchecked")
143-
public <T> T convert(Object source, Class<T> targetType) {
144-
if (targetType == null) {
145-
throw new IllegalArgumentException("The targetType to convert to cannot be null");
146-
}
147-
return (T) convert(source, TypeDescriptor.forObject(source), TypeDescriptor.valueOf(targetType));
148-
}
149-
150143
public boolean canConvert(TypeDescriptor sourceType, TypeDescriptor targetType) {
151144
if (targetType == null) {
152145
throw new IllegalArgumentException("The targetType to convert to cannot be null");
@@ -168,6 +161,14 @@ public boolean canConvert(TypeDescriptor sourceType, TypeDescriptor targetType)
168161
}
169162
}
170163

164+
@SuppressWarnings("unchecked")
165+
public <T> T convert(Object source, Class<T> targetType) {
166+
if (targetType == null) {
167+
throw new IllegalArgumentException("The targetType to convert to cannot be null");
168+
}
169+
return (T) convert(source, TypeDescriptor.forObject(source), TypeDescriptor.valueOf(targetType));
170+
}
171+
171172
public Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType) {
172173
if (targetType == null) {
173174
throw new IllegalArgumentException("The targetType to convert to cannot be null");
@@ -190,6 +191,20 @@ public Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor t
190191
}
191192
}
192193

194+
/**
195+
* Convenience operation for converting a source object to the specified targetType, where the targetType is a descriptor that provides additional conversion context.
196+
* Simply delegates to {@link #convert(Object, TypeDescriptor, TypeDescriptor)} and encapsulates the construction of the sourceType descriptor using {@link TypeDescriptor#forObject(Object)}.
197+
* @param source the source object
198+
* @param targetType the target type
199+
* @return the converted value
200+
* @throws ConversionException if a conversion exception occurred
201+
* @throws IllegalArgumentException if targetType is null
202+
* @throws IllegalArgumentException if sourceType is null but source is not null
203+
*/
204+
public Object convert(Object source, TypeDescriptor targetType) {
205+
return convert(source, TypeDescriptor.forObject(source), targetType);
206+
}
207+
193208
public String toString() {
194209
List<String> converterStrings = new ArrayList<String>();
195210
for (Map<Class<?>, MatchableConverters> targetConverters : this.converters.values()) {

org.springframework.core/src/test/java/org/springframework/core/convert/TypeDescriptorTests.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@
3636
import org.junit.Ignore;
3737
import org.junit.Test;
3838
import org.springframework.core.MethodParameter;
39+
import org.springframework.core.convert.support.DefaultConversionService;
3940

4041
/**
4142
* @author Keith Donald

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

Lines changed: 28 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,13 @@
1616

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

19-
import java.awt.*;
19+
import static junit.framework.Assert.assertEquals;
20+
import static junit.framework.Assert.assertTrue;
21+
import static org.junit.Assert.assertFalse;
22+
import static org.junit.Assert.assertNull;
23+
import static org.junit.Assert.assertSame;
24+
25+
import java.awt.Color;
2026
import java.math.BigDecimal;
2127
import java.math.BigInteger;
2228
import java.util.AbstractList;
@@ -34,12 +40,9 @@
3440
import java.util.Properties;
3541
import java.util.Set;
3642

37-
import static org.junit.Assert.*;
3843
import org.junit.Test;
39-
4044
import org.springframework.core.MethodParameter;
4145
import org.springframework.core.convert.ConversionFailedException;
42-
import org.springframework.core.convert.ConversionService;
4346
import org.springframework.core.convert.ConverterNotFoundException;
4447
import org.springframework.core.convert.TypeDescriptor;
4548
import org.springframework.core.convert.converter.Converter;
@@ -51,7 +54,7 @@
5154
*/
5255
public class DefaultConversionTests {
5356

54-
private ConversionService conversionService = new DefaultConversionService();
57+
private DefaultConversionService conversionService = new DefaultConversionService();
5558

5659
@Test
5760
public void testStringToCharacter() {
@@ -574,6 +577,16 @@ public void convertCollectionToCollectionSpecialCaseSourceImpl() throws Exceptio
574577
assertEquals(new Integer(2), bar.get(1));
575578
assertEquals(new Integer(3), bar.get(2));
576579
}
580+
581+
@Test
582+
public void collection() {
583+
List<String> strings = new ArrayList<String>();
584+
strings.add("3");
585+
strings.add("9");
586+
List<Integer> integers = (List<Integer>) conversionService.convert(strings, TypeDescriptor.collection(List.class, TypeDescriptor.valueOf(Integer.class)));
587+
assertEquals(new Integer(3), integers.get(0));
588+
assertEquals(new Integer(9), integers.get(1));
589+
}
577590

578591
public Map<Integer, FooEnum> genericMap = new HashMap<Integer, FooEnum>();
579592

@@ -588,6 +601,16 @@ public void convertMapToMap() throws Exception {
588601
assertEquals(FooEnum.BAZ, map.get(2));
589602
}
590603

604+
@Test
605+
public void map() {
606+
Map<String, String> strings = new HashMap<String, String>();
607+
strings.put("3", "9");
608+
strings.put("6", "31");
609+
Map<Integer, Integer> integers = (Map<Integer, Integer>) conversionService.convert(strings, TypeDescriptor.map(Map.class, TypeDescriptor.valueOf(Integer.class), TypeDescriptor.valueOf(Integer.class)));
610+
assertEquals(new Integer(9), integers.get(3));
611+
assertEquals(new Integer(31), integers.get(6));
612+
}
613+
591614
@Test
592615
public void convertPropertiesToString() {
593616
Properties foo = new Properties();

0 commit comments

Comments
 (0)