From 90bb4c2256c2dd9dc58ac65a549ecb9cd64cab94 Mon Sep 17 00:00:00 2001 From: Keith Donald Date: Sun, 5 Jun 2011 18:36:58 +0000 Subject: [PATCH] added convert(Object, TypeDescriptor) convenience method; collection and map tests git-svn-id: https://src.springframework.org/svn/spring-framework/trunk@4460 50f2f4bb-b051-0410-bef5-90022cba6387 --- .../core/convert/ConversionService.java | 28 ++++++------- .../core/convert/TypeDescriptor.java | 41 +++++++++++-------- .../support/GenericConversionService.java | 31 ++++++++++---- .../core/convert/TypeDescriptorTests.java | 1 + .../support/DefaultConversionTests.java | 33 ++++++++++++--- 5 files changed, 88 insertions(+), 46 deletions(-) diff --git a/org.springframework.core/src/main/java/org/springframework/core/convert/ConversionService.java b/org.springframework.core/src/main/java/org/springframework/core/convert/ConversionService.java index 58166818fdd..5767534a133 100644 --- a/org.springframework.core/src/main/java/org/springframework/core/convert/ConversionService.java +++ b/org.springframework.core/src/main/java/org/springframework/core/convert/ConversionService.java @@ -34,20 +34,9 @@ public interface ConversionService { */ boolean canConvert(Class sourceType, Class targetType); - /** - * Convert the source to targetType. - * @param source the source object to convert (may be null) - * @param targetType the target type to convert to (required) - * @return the converted object, an instance of targetType - * @throws ConversionException if an exception occurred - * @throws IllegalArgumentException if targetType is null - */ - T convert(Object source, Class targetType); - /** * Returns true if objects of sourceType can be converted to the targetType. - * The TypeDescriptors provide additional context about the field locations where conversion would occur, often object property locations. - * This flavor of the canConvert operation exists mainly for use by a general purpose data mapping framework, and not for use by user code. + * The TypeDescriptors provide additional context about the source and target locations where conversion would occur, often object property locations. * @param sourceType context about the source type to convert from (required) * @param targetType context about the target type to convert to (required) * @return true if a conversion can be performed between the source and target types, false if not @@ -58,13 +47,22 @@ public interface ConversionService { /** * Convert the source to targetType. - * The TypeDescriptors provide additional context about the field locations where conversion will occur, often object property locations. - * This flavor of the convert operation exists mainly for use by a general purpose data mapping framework, and not for use by user code. + * @param source the source object to convert (may be null) + * @param targetType the target type to convert to (required) + * @return the converted object, an instance of targetType + * @throws ConversionException if a conversion exception occurred + * @throws IllegalArgumentException if targetType is null + */ + T convert(Object source, Class targetType); + + /** + * Convert the source to targetType. + * The TypeDescriptors provide additional context about the source and target locations where conversion will occur, often object property locations. * @param source the source object to convert (may be null) * @param sourceType context about the source type converting from (may be null if source is null) * @param targetType context about the target type to convert to (required) * @return the converted object, an instance of {@link TypeDescriptor#getObjectType() targetType} - * @throws ConversionException if an exception occurred + * @throws ConversionException if a conversion exception occurred * @throws IllegalArgumentException if targetType is null * @throws IllegalArgumentException if sourceType is null but source is not null * @see TypeDescriptor#forObject(Object) diff --git a/org.springframework.core/src/main/java/org/springframework/core/convert/TypeDescriptor.java b/org.springframework.core/src/main/java/org/springframework/core/convert/TypeDescriptor.java index bc609aa75dc..b4c64986297 100644 --- a/org.springframework.core/src/main/java/org/springframework/core/convert/TypeDescriptor.java +++ b/org.springframework.core/src/main/java/org/springframework/core/convert/TypeDescriptor.java @@ -70,7 +70,7 @@ public class TypeDescriptor { /** * Create a new type descriptor from a {@link MethodParameter}. - * Use this constructor when a conversion point is a constructor parameter, method parameter, or method return value. + * Use this constructor when a source or target conversion point is a constructor parameter, method parameter, or method return value. * @param methodParameter the method parameter */ public TypeDescriptor(MethodParameter methodParameter) { @@ -78,8 +78,8 @@ public class TypeDescriptor { } /** - * Create a new type descriptor for a field. - * Use this constructor when a conversion point is a field. + * Create a new type descriptor from a {@link Field}. + * Use this constructor when source or target conversion point is a field. * @param field the field */ public TypeDescriptor(Field field) { @@ -87,8 +87,8 @@ public class TypeDescriptor { } /** - * Create a new type descriptor for a bean property. - * Use this constructor when a target conversion point is a property on a Java class. + * Create a new type descriptor from a {@link Property}. + * Use this constructor when a source or target conversion point is a property on a Java class. * @param property the property */ public TypeDescriptor(Property property) { @@ -96,9 +96,9 @@ public class TypeDescriptor { } /** - * Create a new type descriptor for the given class. - * 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. - * Generally prefer use of {@link #forObject(Object)} for constructing source type descriptors for source objects. + * Create a new type descriptor from the given type. + * 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. + * Generally prefer use of {@link #forObject(Object)} for constructing type descriptors from source objects, as it handles the null object case. * @param type the class * @return the type descriptor */ @@ -108,9 +108,10 @@ public class TypeDescriptor { } /** - * Create a new type descriptor for a java.util.Collection class. - * Useful for supporting conversion of source Collection objects to other types. - * Serves as an alternative to {@link #forObject(Object)} to be used when you cannot rely on Collection element introspection to resolve the element type. + * Create a new type descriptor from a java.util.Collection type. + * Useful for converting to typed Collections. + * For example, a List<String> could be converted to a List<EmailAddress> by converting to a targetType built with this method. + * The method call to construct such a TypeDescriptor would look something like: collection(List.class, TypeDescriptor.valueOf(EmailAddress.class)); * @param collectionType the collection type, which must implement {@link Collection}. * @param elementType the collection's element type, used to convert collection elements * @return the collection type descriptor @@ -123,9 +124,10 @@ public class TypeDescriptor { } /** - * Create a new type descriptor for a java.util.Map class. - * Useful for supporting the conversion of source Map objects to other types. - * 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. + * Create a new type descriptor from a java.util.Map type. + * Useful for Converting to typed Maps. + * For example, a Map<String, String> could be converted to a Map<Id, EmailAddress> by converting to a targetType built with this method: + * The method call to construct such a TypeDescriptor would look something like: map(Map.class, TypeDescriptor.valueOf(Id.class), TypeDescriptor.valueOf(EmailAddress.class)); * @param mapType the map type, which must implement {@link Map}. * @param keyType the map's key type, used to convert map keys * @param valueType the map's value type, used to convert map values @@ -201,16 +203,19 @@ public class TypeDescriptor { } /** - * Determine the declared (non-generic) type of the wrapped parameter/field. - * @return the declared type, or null if this is {@link TypeDescriptor#NULL} + * The type of the backing class, method parameter, field, or property described by this TypeDescriptor. + * Returns primitive types as-is. + * See {@link #getObjectType()} for a variation of this operation that resolves primitive types to their corresponding Object types if necessary. + * @return the type, or null if this is {@link TypeDescriptor#NULL} + * @see #getObjectType() */ public Class getType() { return type; } /** - * Determine the declared type of the wrapped parameter/field. - * Returns the Object wrapper type if the underlying type is a primitive. + * Variation of {@link #getType()} that accounts for a primitive type by returning its object wrapper type. + * This is useful for conversion service implementations that wish to normalize to object-based types and not work with primitive types directly. */ public Class getObjectType() { return ClassUtils.resolvePrimitiveIfNecessary(getType()); diff --git a/org.springframework.core/src/main/java/org/springframework/core/convert/support/GenericConversionService.java b/org.springframework.core/src/main/java/org/springframework/core/convert/support/GenericConversionService.java index d8322c5405f..7b6e7c4ce14 100644 --- a/org.springframework.core/src/main/java/org/springframework/core/convert/support/GenericConversionService.java +++ b/org.springframework.core/src/main/java/org/springframework/core/convert/support/GenericConversionService.java @@ -32,6 +32,7 @@ import java.util.concurrent.ConcurrentHashMap; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.springframework.core.GenericTypeResolver; +import org.springframework.core.convert.ConversionException; import org.springframework.core.convert.ConversionFailedException; import org.springframework.core.convert.ConversionService; import org.springframework.core.convert.ConverterNotFoundException; @@ -139,14 +140,6 @@ public class GenericConversionService implements ConfigurableConversionService { return canConvert(sourceType != null ? TypeDescriptor.valueOf(sourceType) : null, TypeDescriptor.valueOf(targetType)); } - @SuppressWarnings("unchecked") - public T convert(Object source, Class targetType) { - if (targetType == null) { - throw new IllegalArgumentException("The targetType to convert to cannot be null"); - } - return (T) convert(source, TypeDescriptor.forObject(source), TypeDescriptor.valueOf(targetType)); - } - public boolean canConvert(TypeDescriptor sourceType, TypeDescriptor targetType) { if (targetType == null) { throw new IllegalArgumentException("The targetType to convert to cannot be null"); @@ -168,6 +161,14 @@ public class GenericConversionService implements ConfigurableConversionService { } } + @SuppressWarnings("unchecked") + public T convert(Object source, Class targetType) { + if (targetType == null) { + throw new IllegalArgumentException("The targetType to convert to cannot be null"); + } + return (T) convert(source, TypeDescriptor.forObject(source), TypeDescriptor.valueOf(targetType)); + } + public Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType) { if (targetType == null) { throw new IllegalArgumentException("The targetType to convert to cannot be null"); @@ -190,6 +191,20 @@ public class GenericConversionService implements ConfigurableConversionService { } } + /** + * Convenience operation for converting a source object to the specified targetType, where the targetType is a descriptor that provides additional conversion context. + * Simply delegates to {@link #convert(Object, TypeDescriptor, TypeDescriptor)} and encapsulates the construction of the sourceType descriptor using {@link TypeDescriptor#forObject(Object)}. + * @param source the source object + * @param targetType the target type + * @return the converted value + * @throws ConversionException if a conversion exception occurred + * @throws IllegalArgumentException if targetType is null + * @throws IllegalArgumentException if sourceType is null but source is not null + */ + public Object convert(Object source, TypeDescriptor targetType) { + return convert(source, TypeDescriptor.forObject(source), targetType); + } + public String toString() { List converterStrings = new ArrayList(); for (Map, MatchableConverters> targetConverters : this.converters.values()) { diff --git a/org.springframework.core/src/test/java/org/springframework/core/convert/TypeDescriptorTests.java b/org.springframework.core/src/test/java/org/springframework/core/convert/TypeDescriptorTests.java index d6bbceb9879..5774fa41422 100644 --- a/org.springframework.core/src/test/java/org/springframework/core/convert/TypeDescriptorTests.java +++ b/org.springframework.core/src/test/java/org/springframework/core/convert/TypeDescriptorTests.java @@ -36,6 +36,7 @@ import java.util.Map; import org.junit.Ignore; import org.junit.Test; import org.springframework.core.MethodParameter; +import org.springframework.core.convert.support.DefaultConversionService; /** * @author Keith Donald diff --git a/org.springframework.core/src/test/java/org/springframework/core/convert/support/DefaultConversionTests.java b/org.springframework.core/src/test/java/org/springframework/core/convert/support/DefaultConversionTests.java index 4f8b5fa8bfe..db9ce5a47f1 100644 --- a/org.springframework.core/src/test/java/org/springframework/core/convert/support/DefaultConversionTests.java +++ b/org.springframework.core/src/test/java/org/springframework/core/convert/support/DefaultConversionTests.java @@ -16,7 +16,13 @@ package org.springframework.core.convert.support; -import java.awt.*; +import static junit.framework.Assert.assertEquals; +import static junit.framework.Assert.assertTrue; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertSame; + +import java.awt.Color; import java.math.BigDecimal; import java.math.BigInteger; import java.util.AbstractList; @@ -34,12 +40,9 @@ import java.util.Map; import java.util.Properties; import java.util.Set; -import static org.junit.Assert.*; import org.junit.Test; - import org.springframework.core.MethodParameter; import org.springframework.core.convert.ConversionFailedException; -import org.springframework.core.convert.ConversionService; import org.springframework.core.convert.ConverterNotFoundException; import org.springframework.core.convert.TypeDescriptor; import org.springframework.core.convert.converter.Converter; @@ -51,7 +54,7 @@ import org.springframework.core.convert.converter.ConverterRegistry; */ public class DefaultConversionTests { - private ConversionService conversionService = new DefaultConversionService(); + private DefaultConversionService conversionService = new DefaultConversionService(); @Test public void testStringToCharacter() { @@ -574,6 +577,16 @@ public class DefaultConversionTests { assertEquals(new Integer(2), bar.get(1)); assertEquals(new Integer(3), bar.get(2)); } + + @Test + public void collection() { + List strings = new ArrayList(); + strings.add("3"); + strings.add("9"); + List integers = (List) conversionService.convert(strings, TypeDescriptor.collection(List.class, TypeDescriptor.valueOf(Integer.class))); + assertEquals(new Integer(3), integers.get(0)); + assertEquals(new Integer(9), integers.get(1)); + } public Map genericMap = new HashMap(); @@ -588,6 +601,16 @@ public class DefaultConversionTests { assertEquals(FooEnum.BAZ, map.get(2)); } + @Test + public void map() { + Map strings = new HashMap(); + strings.put("3", "9"); + strings.put("6", "31"); + Map integers = (Map) conversionService.convert(strings, TypeDescriptor.map(Map.class, TypeDescriptor.valueOf(Integer.class), TypeDescriptor.valueOf(Integer.class))); + assertEquals(new Integer(9), integers.get(3)); + assertEquals(new Integer(31), integers.get(6)); + } + @Test public void convertPropertiesToString() { Properties foo = new Properties();