more converters; since 3.0

This commit is contained in:
Keith Donald 2009-07-12 17:47:33 +00:00
parent 29139dfd1a
commit 1344a6d4d0
41 changed files with 256 additions and 228 deletions

View File

@ -750,6 +750,7 @@ public class GenericBinder implements Binder {
if (cause instanceof SpelEvaluationException
&& ((SpelEvaluationException) cause).getMessageCode() == SpelMessage.TYPE_CONVERSION_ERROR) {
// TODO this could be a ConverterExecutorNotFoundException if no suitable converter was found
cause.getCause().printStackTrace();
ConversionFailedException failure = (ConversionFailedException) cause.getCause();
MessageBuilder builder = new MessageBuilder(messageSource);
builder.code("conversionFailed");

View File

@ -280,6 +280,29 @@ public class GenericBinderTests {
assertEquals("12345", bean.addresses.get(2).zip);
}
@Test
@Ignore
public void bindToListSingleStringNoListFormatter() {
binder.addBinding("addresses");
//binder.registerFormatter(new GenericCollectionPropertyType(List.class, Address.class), new AddressListFormatter());
Map<String, String> values = new LinkedHashMap<String, String>();
values.put("addresses", "4655 Macy Lane:Melbourne:FL:35452,1234 Rostock Circle:Palm Bay:FL:32901,1977 Bel Aire Estates:Coker:AL:12345");
BindingResults results = binder.bind(values);
Assert.assertEquals(3, bean.addresses.size());
assertEquals("4655 Macy Lane", bean.addresses.get(0).street);
assertEquals("Melbourne", bean.addresses.get(0).city);
assertEquals("FL", bean.addresses.get(0).state);
assertEquals("35452", bean.addresses.get(0).zip);
assertEquals("1234 Rostock Circle", bean.addresses.get(1).street);
assertEquals("Palm Bay", bean.addresses.get(1).city);
assertEquals("FL", bean.addresses.get(1).state);
assertEquals("32901", bean.addresses.get(1).zip);
assertEquals("1977 Bel Aire Estates", bean.addresses.get(2).street);
assertEquals("Coker", bean.addresses.get(2).city);
assertEquals("AL", bean.addresses.get(2).state);
assertEquals("12345", bean.addresses.get(2).zip);
}
@Test
public void getListAsSingleString() {
binder.addBinding("addresses");
@ -348,7 +371,6 @@ public class GenericBinderTests {
Map<String, String[]> values = new LinkedHashMap<String, String[]>();
values.put("favoriteFoodsByGroup", new String[] { "DAIRY=Milk", "FRUIT=Peaches", "MEAT=Ham" });
BindingResults results = binder.bind(values);
System.out.println(results);
Assert.assertEquals(3, bean.favoriteFoodsByGroup.size());
assertEquals("Milk", bean.favoriteFoodsByGroup.get(FoodGroup.DAIRY));
assertEquals("Peaches", bean.favoriteFoodsByGroup.get(FoodGroup.FRUIT));
@ -363,8 +385,6 @@ public class GenericBinderTests {
values.put("favoriteFoodsByGroup['FRUIT']", "Peaches");
values.put("favoriteFoodsByGroup['MEAT']", "Ham");
BindingResults results = binder.bind(values);
System.out.println(results);
System.out.println(results);
Assert.assertEquals(3, bean.favoriteFoodsByGroup.size());
assertEquals("Milk", bean.favoriteFoodsByGroup.get(FoodGroup.DAIRY));
assertEquals("Peaches", bean.favoriteFoodsByGroup.get(FoodGroup.FRUIT));
@ -377,7 +397,6 @@ public class GenericBinderTests {
Map<String, String> values = new LinkedHashMap<String, String>();
values.put("favoriteFoodsByGroup", "DAIRY=Milk FRUIT=Peaches MEAT=Ham");
BindingResults results = binder.bind(values);
System.out.println(results);
Assert.assertEquals(3, bean.favoriteFoodsByGroup.size());
assertEquals("Milk", bean.favoriteFoodsByGroup.get(FoodGroup.DAIRY));
assertEquals("Peaches", bean.favoriteFoodsByGroup.get(FoodGroup.FRUIT));

View File

@ -19,8 +19,8 @@ import org.springframework.core.style.StylerUtils;
/**
* Thrown when an attempt to execute a type conversion fails.
*
* @author Keith Donald
* @since 3.0
*/
public class ConversionFailedException extends ConvertException {

View File

@ -17,8 +17,8 @@ package org.springframework.core.convert;
/**
* Base class for exceptions thrown by the convert system.
*
* @author Keith Donald
* @since 3.0
*/
public abstract class ConvertException extends RuntimeException {

View File

@ -17,8 +17,8 @@ package org.springframework.core.convert;
/**
* Thrown when a suitable converter could not be found in a conversion service.
*
* @author Keith Donald
* @since 3.0
*/
public class ConverterNotFoundException extends ConvertException {

View File

@ -20,8 +20,8 @@ package org.springframework.core.convert;
* <p>
* Call {@link #convert(Object, Class)} to perform a thread-safe type conversion using this system.<br>
* Call {@link #convert(Object, TypeDescriptor)} to perform a conversion with additional context about the targetType to convert to.
*
* @author Keith Donald
* @since 3.0
*/
public interface TypeConverter {

View File

@ -25,11 +25,11 @@ import org.springframework.core.GenericCollectionTypeResolver;
import org.springframework.core.MethodParameter;
import org.springframework.util.Assert;
// TODO doesn't support more than depth of one (eg. Map<String,List<Foo>> or List<String>[])
/**
* Context about a type to convert to.
* @author Keith Donald
* @author Andy Clement
* @since 3.0
*/
public class TypeDescriptor<T> {

View File

@ -25,6 +25,7 @@ import org.springframework.core.convert.TypeConverter;
* invoked behind a {@link TypeConverter}. They typically should not be called directly.
* </p>
* @author Keith Donald
* @since 3.0
*/
public interface Converter<S, T> {

View File

@ -18,6 +18,7 @@ package org.springframework.core.convert.converter;
/**
* A factory for "ranged" converters that can convert objects from S to subtypes of R.
* @author Keith Donald
* @since 3.0
* @param <S> The source type converters created by this factory can convert from
* @param <R> The target range (or base) type converters created by this factory can convert to;
* for example {@link Number} for a set of number subtypes.

View File

@ -19,9 +19,12 @@ import org.springframework.core.convert.TypeConverter;
/**
* A meta interface a Converter may implement to describe what types he can convert between.
* Implementing this interface is required for converters that do not declare their parameterized types S and T and expect to be registered with a {@link TypeConverter}.
* Implementing this interface is required when registering converters that do not declare their parameterized types S and T with a {@link TypeConverter}.
* Such Converters are often dynamically created by a {@link ConverterFactory}.
* @author Keith Donald
* @since 3.0
* @see Converter
* @see SuperConverter
* @see ConverterFactory
*/
public interface ConverterInfo {

View File

@ -18,6 +18,7 @@ package org.springframework.core.convert.converter;
/**
* For registering converters with a type conversion system.
* @author Keith Donald
* @since 3.0
*/
public interface ConverterRegistry {

View File

@ -21,7 +21,9 @@ import org.springframework.core.convert.TypeDescriptor;
/**
* Base class for converters that convert to and from collection types (arrays and java.util.Collection types)
* @author Keith Donald
* @since 3.0
*/
@SuppressWarnings("unchecked")
abstract class AbstractCollectionConverter implements ConversionExecutor {
private GenericTypeConverter conversionService;

View File

@ -24,9 +24,10 @@ import org.springframework.core.convert.TypeDescriptor;
* Special one-way converter that converts from a source array to a target array. Supports type conversion of the
* individual array elements; for example, the ability to convert a String[] to an Integer[]. Mainly used internally by
* {@link TypeConverter} implementations.
*
* @author Keith Donald
* @since 3.0
*/
@SuppressWarnings("unchecked")
class ArrayToArray extends AbstractCollectionConverter {
public ArrayToArray(TypeDescriptor sourceArrayType, TypeDescriptor targetArrayType, GenericTypeConverter conversionService) {

View File

@ -24,9 +24,10 @@ import org.springframework.core.convert.TypeDescriptor;
* Special converter that converts from a source array to a target collection. Supports the selection of an
* "approximate" collection implementation when a target collection interface such as <code>List.class</code> is
* specified. Supports type conversion of array elements when a parameterized target collection type descriptor is provided.
*
* @author Keith Donald
* @since 3.0
*/
@SuppressWarnings("unchecked")
class ArrayToCollection extends AbstractCollectionConverter {
public ArrayToCollection(TypeDescriptor sourceArrayType, TypeDescriptor targetCollectionType,
@ -35,9 +36,8 @@ class ArrayToCollection extends AbstractCollectionConverter {
}
@Override
@SuppressWarnings("unchecked")
protected Object doExecute(Object sourceArray) throws Exception {
Class implClass = CollectionConversionUtils.getImpl(getTargetCollectionType());
Class implClass = ConversionUtils.getCollectionImpl((Class<? extends Collection>) getTargetCollectionType());
Collection collection = (Collection) implClass.newInstance();
int length = Array.getLength(sourceArray);
ConversionExecutor elementConverter = getElementConverter();

View File

@ -21,10 +21,11 @@ import org.springframework.util.NumberUtils;
/**
* Converts from a Character to any JDK-standard Number implementation.
*
* <p>
* Support Number classes including Byte, Short, Integer, Float, Double, Long, BigInteger, BigDecimal. This class
* delegates to {@link NumberUtils#convertNumberToTargetClass(Number, Class)} to perform the conversion.
*
* @author Keith Donald
* @since 3.0
* @see java.lang.Byte
* @see java.lang.Short
* @see java.lang.Integer
@ -33,9 +34,7 @@ import org.springframework.util.NumberUtils;
* @see java.lang.Float
* @see java.lang.Double
* @see java.math.BigDecimal
* @see NumberUtils
*
* @author Keith Donald
* @see NumberUtils
*/
public class CharacterToNumberFactory implements ConverterFactory<Character, Number> {

View File

@ -23,14 +23,15 @@ import org.springframework.core.convert.TypeDescriptor;
/**
* Special converter that converts from target collection to a source array.
*
* @author Keith Donald
* @since 3.0
*/
@SuppressWarnings("unchecked")
class CollectionToArray extends AbstractCollectionConverter {
public CollectionToArray(TypeDescriptor sourceArrayType, TypeDescriptor targetCollectionType,
public CollectionToArray(TypeDescriptor sourceCollectionType, TypeDescriptor targetArrayType,
GenericTypeConverter conversionService) {
super(sourceArrayType, targetCollectionType, conversionService);
super(sourceCollectionType, targetArrayType, conversionService);
}
@Override

View File

@ -22,9 +22,10 @@ import org.springframework.core.convert.TypeDescriptor;
/**
* A converter that can convert from one collection type to another.
*
* @author Keith Donald
* @since 3.0
*/
@SuppressWarnings("unchecked")
class CollectionToCollection extends AbstractCollectionConverter {
public CollectionToCollection(TypeDescriptor sourceCollectionType, TypeDescriptor targetCollectionType,
@ -33,10 +34,9 @@ class CollectionToCollection extends AbstractCollectionConverter {
}
@Override
@SuppressWarnings("unchecked")
protected Object doExecute(Object source) throws Exception {
Collection sourceCollection = (Collection) source;
Class implClass = CollectionConversionUtils.getImpl(getTargetCollectionType());
Class implClass = ConversionUtils.getCollectionImpl((Class<? extends Collection>) getTargetCollectionType());
Collection targetCollection = (Collection) implClass.newInstance();
ConversionExecutor elementConverter = getElementConverter(sourceCollection);
Iterator it = sourceCollection.iterator();

View File

@ -21,8 +21,8 @@ import org.springframework.core.convert.ConversionFailedException;
* A command parameterized with the information necessary to perform a conversion of a source input to a
* target output. Encapsulates knowledge about how to convert source objects to a specific target type using a specific
* converter.
*
* @author Keith Donald
* @since 3.0
*/
public interface ConversionExecutor {

View File

@ -15,12 +15,11 @@
*/
package org.springframework.core.convert.support;
/**
* Default implementation of a conversion service. Will automatically register <i>from string</i> converters for
* a number of standard Java types like Class, Number, Boolean and so on.
*
* @author Keith Donald
* @since 3.0
*/
public class DefaultTypeConverter extends GenericTypeConverter {

View File

@ -26,9 +26,9 @@ import java.util.List;
import java.util.Map;
import org.springframework.core.GenericTypeResolver;
import org.springframework.core.convert.TypeDescriptor;
import org.springframework.core.convert.ConverterNotFoundException;
import org.springframework.core.convert.TypeConverter;
import org.springframework.core.convert.TypeDescriptor;
import org.springframework.core.convert.converter.Converter;
import org.springframework.core.convert.converter.ConverterFactory;
import org.springframework.core.convert.converter.ConverterInfo;
@ -36,9 +36,12 @@ import org.springframework.core.convert.converter.ConverterRegistry;
import org.springframework.util.Assert;
/**
* Base implementation of a conversion service. Initially empty, e.g. no converters are registered by default.
* TODO - object to collection/map converters
* Base implementation of a conversion service.
* Initially empty, e.g. no converters are registered by default.
* @author Keith Donald
* @see #add(Converter)
* @see #add(ConverterFactory)
* @since 3.0
*/
@SuppressWarnings("unchecked")
public class GenericTypeConverter implements TypeConverter, ConverterRegistry {
@ -69,6 +72,9 @@ public class GenericTypeConverter implements TypeConverter, ConverterRegistry {
public void add(Converter converter) {
List typeInfo = getRequiredTypeInfo(converter);
if (typeInfo == null) {
throw new IllegalArgumentException("Unable to the determine sourceType <S> and targetType <T> your Converter<S, T> converts between");
}
Class sourceType = (Class) typeInfo.get(0);
Class targetType = (Class) typeInfo.get(1);
Map sourceMap = getSourceMap(sourceType);
@ -77,6 +83,9 @@ public class GenericTypeConverter implements TypeConverter, ConverterRegistry {
public void add(ConverterFactory<?, ?> converterFactory) {
List typeInfo = getRequiredTypeInfo(converterFactory);
if (typeInfo == null) {
throw new IllegalArgumentException("Unable to the determine sourceType <S> and targetType <T> your ConverterFactory<S, T> creates Converters to convert between");
}
Class sourceType = (Class) typeInfo.get(0);
Class targetType = (Class) typeInfo.get(1);
Map sourceMap = getSourceMap(sourceType);
@ -145,9 +154,9 @@ public class GenericTypeConverter implements TypeConverter, ConverterRegistry {
Assert.notNull(sourceClass, "The sourceType to convert from is required");
Assert.notNull(targetType, "The targetType to convert to is required");
if (targetType.getType() == null) {
// TODO for Andy - is this correct way to handle the Null TypedValue?
return NoOpConversionExecutor.INSTANCE;
}
// TODO clean this if/else code up
TypeDescriptor sourceType = TypeDescriptor.valueOf(sourceClass);
if (sourceType.isArray()) {
if (targetType.isArray()) {
@ -161,14 +170,19 @@ public class GenericTypeConverter implements TypeConverter, ConverterRegistry {
return new ArrayToCollection(sourceType, targetType, this);
} else if (targetType.isMap()) {
if (sourceType.getElementType().equals(String.class)) {
// string array to map; with string element values in format foo=bar
return new StringArrayToMap(sourceType, targetType, this);
} else {
// array to map
return null;
}
} else {
// array to object
return null;
if (targetType.equals(String.class)) {
// array to string;
return null;
} else {
// array to object
return null;
}
}
}
if (sourceType.isCollection()) {
@ -178,14 +192,19 @@ public class GenericTypeConverter implements TypeConverter, ConverterRegistry {
return new CollectionToArray(sourceType, targetType, this);
} else if (targetType.isMap()) {
if (sourceType.getElementType().equals(String.class)) {
// string collection to map; with string element values in format foo=bar
return null;
return new StringCollectionToMap(sourceType, targetType, this);
} else {
// object collection to map
return null;
}
} else {
// collection to object
return null;
if (targetType.equals(String.class)) {
// collection to string;
return null;
} else {
// collection to object
return null;
}
}
}
if (sourceType.isMap()) {
@ -193,16 +212,16 @@ public class GenericTypeConverter implements TypeConverter, ConverterRegistry {
return new MapToMap(sourceType, targetType, this);
} else if (targetType.isArray()) {
if (targetType.getElementType().equals(String.class)) {
// map to string array; with string element values in format foo=bar
return null;
return new MapToStringArray(sourceType, targetType, this);
} else {
// map to object array
return null;
}
} else if (targetType.isCollection()) {
if (targetType.getElementType().equals(String.class)) {
// map to string collection; with string element values in format foo=bar
return null;
return new MapToStringCollection(sourceType, targetType, this);
} else {
// map to object collection
return null;
}
} else {
@ -211,18 +230,24 @@ public class GenericTypeConverter implements TypeConverter, ConverterRegistry {
}
}
if (targetType.isArray()) {
// object to array
return null;
if (sourceType.getType().equals(String.class)) {
return new StringToArray(sourceType, targetType, this);
} else {
return new ObjectToArray(sourceType, targetType, this);
}
}
if (targetType.isCollection()) {
// object to collection
return null;
if (sourceType.getType().equals(String.class)) {
return new StringToCollection(sourceType, targetType, this);
} else {
return new ObjectToCollection(sourceType, targetType, this);
}
}
if (targetType.isMap()) {
// object to map
if (sourceType.getType().equals(String.class)) {
return new StringToMap(sourceType, targetType, this);
} else {
// object to map
return null;
}
}
@ -246,43 +271,52 @@ public class GenericTypeConverter implements TypeConverter, ConverterRegistry {
typeInfo.add(info.getSourceType());
typeInfo.add(info.getTargetType());
return typeInfo;
} else {
return getConverterTypeInfo(converter.getClass());
}
Class classToIntrospect = converter.getClass();
}
private List getConverterTypeInfo(Class converterClass) {
Class classToIntrospect = converterClass;
while (classToIntrospect != null) {
Type[] genericInterfaces = classToIntrospect.getGenericInterfaces();
for (Type genericInterface : genericInterfaces) {
if (genericInterface instanceof ParameterizedType) {
ParameterizedType pInterface = (ParameterizedType) genericInterface;
if (Converter.class.isAssignableFrom((Class) pInterface.getRawType())
|| ConverterFactory.class.isAssignableFrom((Class) pInterface.getRawType())) {
Class s = getParameterClass(pInterface.getActualTypeArguments()[0], converter.getClass());
Class t = getParameterClass(pInterface.getActualTypeArguments()[1], converter.getClass());
typeInfo.add(getParameterClass(s, converter.getClass()));
typeInfo.add(getParameterClass(t, converter.getClass()));
break;
Type[] ifcs = classToIntrospect.getGenericInterfaces();
for (Type ifc : ifcs) {
if (ifc instanceof ParameterizedType) {
ParameterizedType paramIfc = (ParameterizedType) ifc;
Type rawType = paramIfc.getRawType();
if (Converter.class.equals(rawType) || ConverterFactory.class.equals(rawType)) {
List typeInfo = new ArrayList(2);
Type arg1 = paramIfc.getActualTypeArguments()[0];
if (arg1 instanceof TypeVariable) {
arg1 = GenericTypeResolver.resolveTypeVariable((TypeVariable) arg1, converterClass);
}
if (arg1 instanceof Class) {
typeInfo.add((Class) arg1);
}
Type arg2 = paramIfc.getActualTypeArguments()[1];
if (arg2 instanceof TypeVariable) {
arg2 = GenericTypeResolver.resolveTypeVariable((TypeVariable) arg2, converterClass);
}
if (arg2 instanceof Class) {
typeInfo.add((Class) arg2);
}
if (typeInfo.size() == 2) {
return typeInfo;
}
}
else if (Converter.class.isAssignableFrom((Class) rawType)) {
return getConverterTypeInfo((Class) rawType);
}
}
else if (Converter.class.isAssignableFrom((Class) ifc)) {
return getConverterTypeInfo((Class) ifc);
}
}
classToIntrospect = classToIntrospect.getSuperclass();
}
if (typeInfo.size() != 2) {
throw new IllegalArgumentException("Unable to extract source and target class arguments from Converter ["
+ converter.getClass().getName() + "]; does the Converter specify the <S, T> generic types?");
}
return typeInfo;
return null;
}
private Class getParameterClass(Type parameterType, Class converterClass) {
if (parameterType instanceof TypeVariable) {
parameterType = GenericTypeResolver.resolveTypeVariable((TypeVariable) parameterType, converterClass);
}
if (parameterType instanceof Class) {
return (Class) parameterType;
}
throw new IllegalArgumentException("Unable to obtain the java.lang.Class for parameterType [" + parameterType
+ "] on Converter [" + converterClass.getName() + "]");
}
private Map getSourceMap(Class sourceType) {
Map sourceMap = (Map) sourceTypeConverters.get(sourceType);
if (sourceMap == null) {

View File

@ -15,11 +15,8 @@
*/
package org.springframework.core.convert.support;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.SortedMap;
import java.util.TreeMap;
import org.springframework.core.convert.ConversionFailedException;
import org.springframework.core.convert.TypeDescriptor;
@ -27,6 +24,7 @@ import org.springframework.core.convert.TypeDescriptor;
/**
* Converts from one map to another map, with support for converting individual map elements based on generic type information.
* @author Keith Donald
* @since 3.0
*/
@SuppressWarnings("unchecked")
class MapToMap implements ConversionExecutor {
@ -37,7 +35,7 @@ class MapToMap implements ConversionExecutor {
private GenericTypeConverter conversionService;
private EntryConverter entryConverter;
private MapEntryConverter entryConverter;
/**
* Creates a new map-to-map converter
@ -52,23 +50,23 @@ class MapToMap implements ConversionExecutor {
this.entryConverter = createEntryConverter();
}
private EntryConverter createEntryConverter() {
private MapEntryConverter createEntryConverter() {
if (sourceType.isMapEntryTypeKnown() && targetType.isMapEntryTypeKnown()) {
ConversionExecutor keyConverter = conversionService.getConversionExecutor(sourceType.getMapKeyType(),
TypeDescriptor.valueOf(targetType.getMapKeyType()));
ConversionExecutor valueConverter = conversionService.getConversionExecutor(sourceType.getMapValueType(),
TypeDescriptor.valueOf(targetType.getMapValueType()));
return new EntryConverter(keyConverter, valueConverter);
return new MapEntryConverter(keyConverter, valueConverter);
} else {
return EntryConverter.NO_OP_INSTANCE;
return MapEntryConverter.NO_OP_INSTANCE;
}
}
public Object execute(Object source) throws ConversionFailedException {
try {
Map map = (Map) source;
Map targetMap = (Map) getImpl(targetType.getType()).newInstance();
EntryConverter converter = getEntryConverter(map);
Map targetMap = (Map) ConversionUtils.getMapImpl(targetType.getType()).newInstance();
MapEntryConverter converter = getEntryConverter(map);
Iterator<Map.Entry<?, ?>> it = map.entrySet().iterator();
while (it.hasNext()) {
Map.Entry entry = it.next();
@ -80,9 +78,9 @@ class MapToMap implements ConversionExecutor {
}
}
private EntryConverter getEntryConverter(Map<?, ?> map) {
EntryConverter entryConverter = this.entryConverter;
if (entryConverter == EntryConverter.NO_OP_INSTANCE) {
private MapEntryConverter getEntryConverter(Map<?, ?> map) {
MapEntryConverter entryConverter = this.entryConverter;
if (entryConverter == MapEntryConverter.NO_OP_INSTANCE) {
Class<?> targetKeyType = targetType.getMapKeyType();
Class<?> targetValueType = targetType.getMapValueType();
if (targetKeyType != null && targetValueType != null) {
@ -105,59 +103,10 @@ class MapToMap implements ConversionExecutor {
break;
}
}
entryConverter = new EntryConverter(keyConverter, valueConverter);
entryConverter = new MapEntryConverter(keyConverter, valueConverter);
}
}
return entryConverter;
}
static Class<?> getImpl(Class<?> targetClass) {
if (targetClass.isInterface()) {
if (Map.class.equals(targetClass)) {
return HashMap.class;
} else if (SortedMap.class.equals(targetClass)) {
return TreeMap.class;
} else {
throw new IllegalArgumentException("Unsupported Map interface [" + targetClass.getName() + "]");
}
} else {
return targetClass;
}
}
private static class EntryConverter {
public static final EntryConverter NO_OP_INSTANCE = new EntryConverter();
private ConversionExecutor keyConverter;
private ConversionExecutor valueConverter;
private EntryConverter() {
}
public EntryConverter(ConversionExecutor keyConverter, ConversionExecutor valueConverter) {
this.keyConverter = keyConverter;
this.valueConverter = valueConverter;
}
public Object convertKey(Object key) {
if (keyConverter != null) {
return keyConverter.execute(key);
} else {
return key;
}
}
public Object convertValue(Object value) {
if (valueConverter != null) {
return valueConverter.execute(value);
} else {
return value;
}
}
}
}

View File

@ -18,18 +18,20 @@ package org.springframework.core.convert.support;
import org.springframework.core.convert.ConversionFailedException;
/**
* Conversion executor that does nothing. Access singleton at {@link #INSTANCE}.s
* Conversion executor that does nothing.
* Access singleton using {@link #INSTANCE}.
* @since 3.0
*/
class NoOpConversionExecutor implements ConversionExecutor {
public static final ConversionExecutor INSTANCE = new NoOpConversionExecutor();
private NoOpConversionExecutor() {
}
public Object execute(Object source) throws ConversionFailedException {
// does nothing
return source;
}
private NoOpConversionExecutor() {
}
}

View File

@ -20,7 +20,7 @@ import org.springframework.util.NumberUtils;
/**
* Converts from any JDK-standard Number implementation to a Character.
*
* @author Keith Donald
* @see java.lang.Character
* @see java.lang.Short
* @see java.lang.Integer
@ -30,8 +30,7 @@ import org.springframework.util.NumberUtils;
* @see java.lang.Double
* @see java.math.BigDecimal
* @see NumberUtils
*
* @author Keith Donald
* @since 3.0
*/
public class NumberToCharacter implements Converter<Number, Character> {
public Character convert(Number source) {

View File

@ -21,10 +21,10 @@ import org.springframework.util.NumberUtils;
/**
* Converts from any JDK-standard Number implementation to any other JDK-standard Number implementation.
*
* <p>
* Support Number classes including Byte, Short, Integer, Float, Double, Long, BigInteger, BigDecimal. This class
* delegates to {@link NumberUtils#convertNumberToTargetClass(Number, Class)} to perform the conversion.
*
* @author Keith Donald
* @see java.lang.Byte
* @see java.lang.Short
* @see java.lang.Integer
@ -34,8 +34,7 @@ import org.springframework.util.NumberUtils;
* @see java.lang.Double
* @see java.math.BigDecimal
* @see NumberUtils
*
* @author Keith Donald
* @since 3.0
*/
public class NumberToNumberFactory implements ConverterFactory<Number, Number> {

View File

@ -1,3 +1,18 @@
/*
* Copyright 2004-2009 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.core.convert.support;
import org.springframework.core.convert.converter.Converter;
@ -6,6 +21,7 @@ import org.springframework.core.convert.converter.Converter;
* Simply calls {@link Object#toString()} to convert any object to a string.
* Used by the {@link DefaultTypeConverter} as a fallback if there are no other explicit to string converters registered.
* @author Keith Donald
* @since 3.0
*/
public class ObjectToString implements Converter<Object, String> {
public String convert(Object source) {

View File

@ -23,6 +23,7 @@ import org.springframework.core.style.ToStringCreator;
/**
* Default conversion executor implementation for converters.
* @author Keith Donald
* @since 3.0
*/
@SuppressWarnings("unchecked")
class StaticConversionExecutor implements ConversionExecutor {

View File

@ -15,14 +15,18 @@
*/
package org.springframework.core.convert.support;
import java.util.HashMap;
import java.lang.reflect.Array;
import java.util.Map;
import java.util.SortedMap;
import java.util.TreeMap;
import org.springframework.core.convert.ConversionFailedException;
import org.springframework.core.convert.TypeDescriptor;
/**
* Converts a String array to a Map.
* Each element in the array must be formatted as key=value.
* @author Keith Donald
* @since 3.0
*/
@SuppressWarnings("unchecked")
class StringArrayToMap implements ConversionExecutor {
@ -32,7 +36,7 @@ class StringArrayToMap implements ConversionExecutor {
private GenericTypeConverter conversionService;
private EntryConverter entryConverter;
private MapEntryConverter entryConverter;
public StringArrayToMap(TypeDescriptor sourceType, TypeDescriptor targetType, GenericTypeConverter conversionService) {
this.sourceType = sourceType;
@ -41,24 +45,25 @@ class StringArrayToMap implements ConversionExecutor {
this.entryConverter = createEntryConverter();
}
private EntryConverter createEntryConverter() {
private MapEntryConverter createEntryConverter() {
if (targetType.isMapEntryTypeKnown()) {
ConversionExecutor keyConverter = conversionService.getConversionExecutor(String.class,
TypeDescriptor.valueOf(targetType.getMapKeyType()));
ConversionExecutor valueConverter = conversionService.getConversionExecutor(String.class,
TypeDescriptor.valueOf(targetType.getMapValueType()));
return new EntryConverter(keyConverter, valueConverter);
return new MapEntryConverter(keyConverter, valueConverter);
} else {
return EntryConverter.NO_OP_INSTANCE;
return MapEntryConverter.NO_OP_INSTANCE;
}
}
public Object execute(Object source) throws ConversionFailedException {
try {
Map targetMap = (Map) getImpl(targetType.getType()).newInstance();
String[] array = (String[]) source;
for (String string : array) {
String[] fields = string.split("=");
Map targetMap = (Map) ConversionUtils.getMapImpl(targetType.getType()).newInstance();
int length = Array.getLength(source);
for (int i = 0; i < length; i++) {
String property = (String) Array.get(source, i);
String[] fields = property.split("=");
String key = fields[0];
String value = fields[1];
targetMap.put(entryConverter.convertKey(key), entryConverter.convertValue(value));
@ -69,53 +74,4 @@ class StringArrayToMap implements ConversionExecutor {
}
}
static Class<?> getImpl(Class<?> targetClass) {
if (targetClass.isInterface()) {
if (Map.class.equals(targetClass)) {
return HashMap.class;
} else if (SortedMap.class.equals(targetClass)) {
return TreeMap.class;
} else {
throw new IllegalArgumentException("Unsupported Map interface [" + targetClass.getName() + "]");
}
} else {
return targetClass;
}
}
private static class EntryConverter {
public static final EntryConverter NO_OP_INSTANCE = new EntryConverter();
private ConversionExecutor keyConverter;
private ConversionExecutor valueConverter;
private EntryConverter() {
}
public EntryConverter(ConversionExecutor keyConverter, ConversionExecutor valueConverter) {
this.keyConverter = keyConverter;
this.valueConverter = valueConverter;
}
public Object convertKey(Object key) {
if (keyConverter != null) {
return keyConverter.execute(key);
} else {
return key;
}
}
public Object convertValue(Object value) {
if (valueConverter != null) {
return valueConverter.execute(value);
} else {
return value;
}
}
}
}

View File

@ -21,8 +21,8 @@ import org.springframework.core.convert.converter.Converter;
/**
* Converts a String to a BigDecimal using {@link BigDecimal#BigDecimal(String).
*
* @author Keith Donald
* @since 3.0
*/
public class StringToBigDecimal implements Converter<String, BigDecimal> {
public BigDecimal convert(String source) {

View File

@ -21,8 +21,8 @@ import org.springframework.core.convert.converter.Converter;
/**
* Converts a String to a BigInteger using {@link BigInteger#BigInteger(String)}.
*
* @author Keith Donald
* @since 3.0
*/
public class StringToBigInteger implements Converter<String, BigInteger> {
public BigInteger convert(String source) {

View File

@ -20,9 +20,9 @@ import org.springframework.core.convert.converter.Converter;
/**
* Converts String to a Boolean. The trueString and falseStrings are configurable.
*
* @see #StringToBoolean(String, String)
* @author Keith Donald
* @see #StringToBoolean(String, String)
* @since 3.0
*/
public class StringToBoolean implements Converter<String, Boolean> {

View File

@ -19,8 +19,8 @@ import org.springframework.core.convert.converter.Converter;
/**
* Converts a String to a Byte and back.
*
* @author Keith Donald
* @since 3.0
*/
public class StringToByte implements Converter<String, Byte> {
public Byte convert(String source) {

View File

@ -19,8 +19,8 @@ import org.springframework.core.convert.converter.Converter;
/**
* Converts a String to a Character and back.
*
* @author Keith Donald
* @since 3.0
*/
public class StringToCharacter implements Converter<String, Character> {
public Character convert(String source) {

View File

@ -19,8 +19,8 @@ import org.springframework.core.convert.converter.Converter;
/**
* Converts a String to a Double using {@link Double#valueOf(String)}.
*
* @author Keith Donald
* @since 3.0
*/
public class StringToDouble implements Converter<String, Double> {
public Double convert(String source) {

View File

@ -1,8 +1,29 @@
/*
* Copyright 2004-2009 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.core.convert.support;
import org.springframework.core.convert.converter.Converter;
import org.springframework.core.convert.converter.ConverterFactory;
import org.springframework.core.convert.converter.ConverterInfo;
/**
* A factory for String to enum converters.
* @author Keith Donald
* @since 3.0
*/
@SuppressWarnings("unchecked")
public class StringToEnumFactory implements ConverterFactory<String, Enum> {
@ -10,14 +31,22 @@ public class StringToEnumFactory implements ConverterFactory<String, Enum> {
return new StringToEnum(targetType);
}
class StringToEnum<T extends Enum> implements Converter<String, T> {
class StringToEnum<T extends Enum> implements Converter<String, T>, ConverterInfo {
private Class<T> enumType;
public StringToEnum(Class<T> enumType) {
this.enumType = enumType;
}
public Class<String> getSourceType() {
return String.class;
}
public Class<T> getTargetType() {
return enumType;
}
public T convert(String source) throws Exception {
return (T) Enum.valueOf(enumType, source);
}

View File

@ -19,8 +19,8 @@ import org.springframework.core.convert.converter.Converter;
/**
* Converts a String to Float using {@link Float#valueOf(String)}.
*
* @author Keith Donald
* @since 3.0
*/
public class StringToFloat implements Converter<String, Float> {
public Float convert(String source) {

View File

@ -20,6 +20,7 @@ import org.springframework.core.convert.converter.Converter;
/**
* Converts a String to an Integer using {@link Integer#valueOf(String)}.
* @author Keith Donald
* @since 3.0
*/
public class StringToInteger implements Converter<String, Integer> {
public Integer convert(String source) {

View File

@ -23,6 +23,7 @@ import org.springframework.util.StringUtils;
/**
* Converts a String to a Locale using {@link StringUtils#parseLocaleString(String)}.
* @author Keith Donald
* @since 3.0
*/
public class StringToLocale implements Converter<String, Locale> {
public Locale convert(String source) {

View File

@ -20,6 +20,7 @@ import org.springframework.core.convert.converter.Converter;
/**
* Converts a String to a Long using {@link Long#valueOf(String)}.
* @author Keith Donald
* @since 3.0
*/
public class StringToLong implements Converter<String, Long> {
public Long convert(String source) {

View File

@ -18,6 +18,18 @@ package org.springframework.core.convert.support;
import org.springframework.core.convert.ConversionFailedException;
import org.springframework.core.convert.TypeDescriptor;
/**
* Converts a String to a map.
* The String should be in the format:
* <pre>
* key=value
* key=value
* key=value
* key=value
* </pre>
* @author Keith Donald
* @since 3.0
*/
@SuppressWarnings("unchecked")
class StringToMap implements ConversionExecutor {

View File

@ -20,6 +20,7 @@ import org.springframework.core.convert.converter.Converter;
/**
* Converts a String to a Short using {@link Short#valueOf(String)}.
* @author Keith Donald
* @since 3.0
*/
public class StringToShort implements Converter<String, Short> {
public Short convert(String source) {

View File

@ -219,7 +219,6 @@ public class GenericTypeConverterTests {
public Map<Integer, FooEnum> genericMap = new HashMap<Integer, FooEnum>();
@Test
@Ignore
public void convertMapToMap() throws Exception {
Map<String, String> foo = new HashMap<String, String>();
foo.put("1", "BAR");
@ -231,15 +230,15 @@ public class GenericTypeConverterTests {
assertEquals(map.get(2), FooEnum.BAZ);
}
@Ignore
@Test
public void convertObjectToArray() {
String[] result = (String[]) converter.convert("1,2,3", String[].class);
assertEquals(1, result.length);
assertEquals("1,2,3", result[0]);
assertEquals(3, result.length);
assertEquals("1", result[0]);
assertEquals("2", result[1]);
assertEquals("3", result[2]);
}
@Ignore
@Test
public void convertObjectToArrayWithElementConversion() {
converter.add(new StringToInteger());