super converter implementation

This commit is contained in:
Keith Donald 2009-03-08 08:47:10 +00:00
parent b04009357a
commit 0ef0ff60f0
22 changed files with 598 additions and 420 deletions

View File

@ -16,7 +16,7 @@
package org.springframework.core.convert; package org.springframework.core.convert;
/** /**
* A command object that is parameterized with the information necessary to perform a conversion of a source input to a * 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 * target output. Encapsulates knowledge about how to convert source objects to a specific target type using a specific
* converter. * converter.
* *
@ -25,20 +25,19 @@ package org.springframework.core.convert;
public interface ConversionExecutor<S, T> { public interface ConversionExecutor<S, T> {
/** /**
* Returns the source class of conversions performed by this executor. * The type this executor converts from.
* @return the source class
*/ */
public Class<S> getSourceClass(); public Class<S> getSourceClass();
/** /**
* Returns the target class of conversions performed by this executor. * The type this executor converts to.
* @return the target class s */
*/
public Class<T> getTargetClass(); public Class<T> getTargetClass();
/** /**
* Execute the conversion for the provided source object. * Convert the source to T.
* @param source the source object to convert * @param source the source to convert
* @throws ConversionExecutionException if an exception occurs during type conversion
*/ */
public T execute(S source) throws ConversionExecutionException; public T execute(S source) throws ConversionExecutionException;

View File

@ -26,7 +26,7 @@ package org.springframework.core.convert;
public interface ConversionService { public interface ConversionService {
/** /**
* Convert the source object to <code>targetClass</code> * Convert the source to target class T.
* @param source the source to convert from (may be null) * @param source the source to convert from (may be null)
* @param targetClass the target class to convert to * @param targetClass the target class to convert to
* @return the converted object, an instance of the <code>targetClass</code>, or <code>null</code> if a null source * @return the converted object, an instance of the <code>targetClass</code>, or <code>null</code> if a null source
@ -39,7 +39,7 @@ public interface ConversionService {
ConversionException; ConversionException;
/** /**
* Convert the source object to <code>targetClass</code> using a custom converter. * Convert the source to target class T with a custom converter.
* @param converterId the id of the custom converter, which must be registered with this conversion service and * @param converterId the id of the custom converter, which must be registered with this conversion service and
* capable of converting to the targetClass * capable of converting to the targetClass
* @param source the source to convert from (may be null) * @param source the source to convert from (may be null)
@ -54,7 +54,7 @@ public interface ConversionService {
throws ConversionExecutorNotFoundException, ConversionException; throws ConversionExecutorNotFoundException, ConversionException;
/** /**
* Get a ConversionExecutor capable of converting objects from <code>sourceClass</code> to <code>targetClass</code>. * Get a ConversionExecutor that converts objects from S to T.
* The returned ConversionExecutor is thread-safe and may safely be cached for later use by client code. * The returned ConversionExecutor is thread-safe and may safely be cached for later use by client code.
* @param sourceClass the source class to convert from (required) * @param sourceClass the source class to convert from (required)
* @param targetClass the target class to convert to (required) * @param targetClass the target class to convert to (required)
@ -65,9 +65,8 @@ public interface ConversionService {
throws ConversionExecutorNotFoundException; throws ConversionExecutorNotFoundException;
/** /**
* Get a ConversionExecutor that uses a custom converter to capable convert objects from <code>sourceClass</code> to * Get a ConversionExecutor that that converts objects from S to T with a custom converter.
* <code>targetClass</code>. The returned ConversionExecutor is thread-safe and may safely be cached for use in * The returned ConversionExecutor is thread-safe and may safely be cached for use in client code.
* client code.
* @param converterId the id of the custom converter, which must be registered with this conversion service and * @param converterId the id of the custom converter, which must be registered with this conversion service and
* capable of converting from sourceClass to targetClass (required) * capable of converting from sourceClass to targetClass (required)
* @param sourceClass the source class to convert from (required) * @param sourceClass the source class to convert from (required)
@ -79,9 +78,8 @@ public interface ConversionService {
Class<T> targetClass) throws ConversionExecutorNotFoundException; Class<T> targetClass) throws ConversionExecutorNotFoundException;
/** /**
* Lookup a class by its well-known alias. For example, <code>long</code> for <code>java.lang.Long</code> * Get a class by its alias.
* @param alias the class alias * @return the class, or <code>null</code> if no such alias exists
* @return the class, or <code>null</code> if no alias exists
*/ */
public Class<?> getClassForAlias(String alias); public Class<?> getClassForAlias(String alias);

View File

@ -19,7 +19,7 @@ import org.springframework.core.convert.ConversionException;
import org.springframework.core.convert.ConversionService; import org.springframework.core.convert.ConversionService;
/** /**
* A converter converts a source object of type S to a target type of type T and back. * A converter converts a source object of type S to a target of type T and back.
* <p> * <p>
* Implementations of this interface are thread-safe and can be shared. Converters are typically registered with and * Implementations of this interface are thread-safe and can be shared. Converters are typically registered with and
* accessed through a {@link ConversionService}. * accessed through a {@link ConversionService}.
@ -29,7 +29,7 @@ import org.springframework.core.convert.ConversionService;
public interface Converter<S, T> { public interface Converter<S, T> {
/** /**
* Convert the source S to target type T. * Convert the source of type S to target type T.
* @param source the source object to convert, which must be an instance of S * @param source the source object to convert, which must be an instance of S
* @return the converted object, which must be an instance of T * @return the converted object, which must be an instance of T
* @throws Exception an exception occurred performing the conversion; may be any checked exception, the conversion * @throws Exception an exception occurred performing the conversion; may be any checked exception, the conversion
@ -39,7 +39,7 @@ public interface Converter<S, T> {
public T convert(S source) throws Exception; public T convert(S source) throws Exception;
/** /**
* Convert the target T back to source type S. * Convert the target of type T back to source type S.
* @param target the target object to convert, which must be an instance of T * @param target the target object to convert, which must be an instance of T
* @return the converted object, which must be an instance of S * @return the converted object, which must be an instance of S
* @throws Exception an exception occurred performing the conversion; may be any checked exception, the conversion * @throws Exception an exception occurred performing the conversion; may be any checked exception, the conversion

View File

@ -37,12 +37,8 @@ import org.springframework.util.NumberUtils;
*/ */
public class NumberToNumber implements SuperConverter<Number, Number> { public class NumberToNumber implements SuperConverter<Number, Number> {
public <N extends Number> N convert(Number source, Class<N> targetClass) throws Exception { public <RT extends Number> RT convert(Number source, Class<RT> targetClass) throws Exception {
return NumberUtils.convertNumberToTargetClass(source, targetClass); return NumberUtils.convertNumberToTargetClass(source, targetClass);
} }
public Number convertBack(Number target) throws Exception {
return target;
}
} }

View File

@ -16,19 +16,19 @@
package org.springframework.core.convert.converter; package org.springframework.core.convert.converter;
/** /**
* Converts from a String to Enum using {@link Enum#valueOf(Class, String)}. * Converts a String to a Enum using {@link Enum#valueOf(Class, String)}.
* *
* @author Keith Donald * @author Keith Donald
*/ */
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
public class StringToEnum implements SuperConverter<String, Enum> { public class StringToEnum implements SuperTwoWayConverter<String, Enum> {
public <E extends Enum> E convert(String source, Class<E> targetClass) throws Exception { public <RT extends Enum> RT convert(String source, Class<RT> targetClass) throws Exception {
return Enum.valueOf(targetClass, source); return Enum.valueOf(targetClass, source);
} }
public String convertBack(Enum target) throws Exception { public <RS extends String> RS convertBack(Enum target, Class<RS> sourceClass) throws Exception {
return target.name(); return (RS) target.name();
} }
} }

View File

@ -19,8 +19,7 @@ import org.springframework.core.convert.ConversionException;
import org.springframework.core.convert.ConversionService; import org.springframework.core.convert.ConversionService;
/** /**
* A super converter converts a source object of type S to a target type of type AT and back, where AT is equal to or a * A super converter converts a source object of type S to a target of class hierarchy T.
* subclass of T, T being the "super" target type. This allows a single converter to convert to objects that are part of a common class hierarchy.
* <p> * <p>
* Implementations of this interface are thread-safe and can be shared. Converters are typically registered with and * Implementations of this interface are thread-safe and can be shared. Converters are typically registered with and
* accessed through a {@link ConversionService}. * accessed through a {@link ConversionService}.
@ -30,25 +29,14 @@ import org.springframework.core.convert.ConversionService;
public interface SuperConverter<S, T> { public interface SuperConverter<S, T> {
/** /**
* Convert the source S to an instance of AT. * Convert the source of type S to an instance of type RT.
* @param source the source object to convert, which must be an instance of S * @param source the source object to convert, whose class must be equal to or a subclass of S
* @param actualTargetClass the actual target class to convert to (AT), which must be equal to or a specialization * @param targetClass the requested target class to convert to (RT), which must be equal to T or extend from T
* of T. * @return the converted object, which must be an instance of RT
* @return the converted object, which must be an instance of AT
* @throws Exception an exception occurred performing the conversion; may be any checked exception, the conversion * @throws Exception an exception occurred performing the conversion; may be any checked exception, the conversion
* system will handle wrapping the failure in a {@link ConversionException} that provides a consistent type * system will handle wrapping the failure in a {@link ConversionException} that provides a consistent type
* conversion error context * conversion error context
*/ */
public <AT extends T> AT convert(S source, Class<AT> actualTargetClass) throws Exception; public <RT extends T> RT convert(S source, Class<RT> targetClass) throws Exception;
/**
* Convert the target T to an instance of S.
* @param target the target object to convert, which must be an instance of T.
* @return the converted object, which must be an instance of S.
* @throws Exception an exception occurred performing the conversion; may be any checked exception, the conversion
* system will handle wrapping the failure in a {@link ConversionException} that provides a consistent type
* conversion error context
*/
public S convertBack(T target) throws Exception;
} }

View File

@ -0,0 +1,27 @@
package org.springframework.core.convert.converter;
import org.springframework.core.convert.ConversionException;
import org.springframework.core.convert.ConversionService;
/**
* A super converter that can also convert a target object of type T to a source of class hierarchy S.
* <p>
* Implementations of this interface are thread-safe and can be shared. Converters are typically registered with and
* accessed through a {@link ConversionService}.
* </p>
* @author Keith Donald
*/
public interface SuperTwoWayConverter<S, T> extends SuperConverter<S, T> {
/**
* Convert the target of type T to an instance of S.
* @param target the target object to convert, whose class must be equal to or a subclass of T
* @param sourceClass the requested source class to convert to, which must be equal to S or extend from S
* @return the converted object, which must be an instance of S
* @throws Exception an exception occurred performing the conversion; may be any checked exception, the conversion
* system will handle wrapping the failure in a {@link ConversionException} that provides a consistent type
* conversion error context
*/
public <RS extends S> RS convertBack(T target, Class<RS> sourceClass) throws Exception;
}

View File

@ -0,0 +1,31 @@
package org.springframework.core.convert.converter;
/**
* Adapts a {@link SuperTwoWayConverter} to the {@link Converter} interface in a type safe way. This adapter is useful
* for applying more general {@link SuperConverter} logic to a specific source/target class pair.
*/
@SuppressWarnings("unchecked")
public class SuperTwoWayConverterConverter<S, T> implements Converter<S, T> {
private SuperTwoWayConverter superConverter;
private Class sourceClass;
private Class targetClass;
public <SCS, SCT> SuperTwoWayConverterConverter(SuperTwoWayConverter<SCS, SCT> superConverter,
Class<? extends SCS> sourceClass, Class<? extends SCT> targetClass) {
this.superConverter = superConverter;
this.sourceClass = sourceClass;
this.targetClass = targetClass;
}
public T convert(S source) throws Exception {
return (T) superConverter.convert(source, targetClass);
}
public S convertBack(T target) throws Exception {
return (S) superConverter.convertBack(target, sourceClass);
}
}

View File

@ -53,18 +53,7 @@ class ArrayToArray implements SuperConverter {
this.elementConverter = elementConverter; this.elementConverter = elementConverter;
} }
public Class getSourceClass() {
return Object[].class;
}
public Class getSuperTargetClass() {
return Object[].class;
}
public Object convert(Object source, Class targetClass) throws Exception { public Object convert(Object source, Class targetClass) throws Exception {
if (source == null) {
return null;
}
Class sourceComponentType = source.getClass().getComponentType(); Class sourceComponentType = source.getClass().getComponentType();
Class targetComponentType = targetClass.getComponentType(); Class targetComponentType = targetClass.getComponentType();
int length = Array.getLength(source); int length = Array.getLength(source);
@ -77,10 +66,6 @@ class ArrayToArray implements SuperConverter {
return targetArray; return targetArray;
} }
public Object convertBack(Object target) throws Exception {
throw new UnsupportedOperationException("Not supported");
}
private ConversionExecutor getElementConverter(Class sourceComponentType, Class targetComponentType) { private ConversionExecutor getElementConverter(Class sourceComponentType, Class targetComponentType) {
if (elementConverter != null) { if (elementConverter != null) {
return elementConverter; return elementConverter;

View File

@ -17,19 +17,13 @@ package org.springframework.core.convert.service;
import java.lang.reflect.Array; import java.lang.reflect.Array;
import java.lang.reflect.Constructor; import java.lang.reflect.Constructor;
import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
import java.util.Iterator; import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;
import java.util.SortedSet;
import java.util.TreeSet;
import org.springframework.core.GenericCollectionTypeResolver; import org.springframework.core.GenericCollectionTypeResolver;
import org.springframework.core.convert.ConversionExecutor; import org.springframework.core.convert.ConversionExecutor;
import org.springframework.core.convert.ConversionService; import org.springframework.core.convert.ConversionService;
import org.springframework.core.convert.converter.SuperConverter; import org.springframework.core.convert.converter.SuperTwoWayConverter;
/** /**
* Special converter that converts from a source array to a target collection. Supports the selection of an * Special converter that converts from a source array to a target collection. Supports the selection of an
@ -45,7 +39,7 @@ import org.springframework.core.convert.converter.SuperConverter;
* @author Keith Donald * @author Keith Donald
*/ */
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
class ArrayToCollection implements SuperConverter { class ArrayToCollection implements SuperTwoWayConverter {
private ConversionService conversionService; private ConversionService conversionService;
@ -69,11 +63,8 @@ class ArrayToCollection implements SuperConverter {
} }
public Object convert(Object source, Class targetClass) throws Exception { public Object convert(Object source, Class targetClass) throws Exception {
if (source == null) { Class implClass = CollectionConversionUtils.getImpl(targetClass);
return null; Constructor constructor = implClass.getConstructor((Class[]) null);
}
Class collectionImplClass = getCollectionImplClass(targetClass);
Constructor constructor = collectionImplClass.getConstructor((Class[]) null);
Collection collection = (Collection) constructor.newInstance((Object[]) null); Collection collection = (Collection) constructor.newInstance((Object[]) null);
ConversionExecutor converter = getArrayElementConverter(source, targetClass); ConversionExecutor converter = getArrayElementConverter(source, targetClass);
int length = Array.getLength(source); int length = Array.getLength(source);
@ -87,16 +78,10 @@ class ArrayToCollection implements SuperConverter {
return collection; return collection;
} }
public Object convertBack(Object target) throws Exception {
throw new UnsupportedOperationException("Should never be called");
}
public Object convertBack(Object target, Class sourceClass) throws Exception { public Object convertBack(Object target, Class sourceClass) throws Exception {
if (target == null) {
return null;
}
Collection collection = (Collection) target; Collection collection = (Collection) target;
Object array = Array.newInstance(sourceClass.getComponentType(), collection.size()); Class elementType = sourceClass.getComponentType();
Object array = Array.newInstance(elementType, collection.size());
int i = 0; int i = 0;
for (Iterator it = collection.iterator(); it.hasNext(); i++) { for (Iterator it = collection.iterator(); it.hasNext(); i++) {
Object value = it.next(); Object value = it.next();
@ -105,8 +90,7 @@ class ArrayToCollection implements SuperConverter {
if (elementConverter != null) { if (elementConverter != null) {
converter = elementConverter; converter = elementConverter;
} else { } else {
converter = conversionService.getConversionExecutor(value.getClass(), sourceClass converter = conversionService.getConversionExecutor(value.getClass(), elementType);
.getComponentType());
} }
value = converter.execute(value); value = converter.execute(value);
} }
@ -115,22 +99,6 @@ class ArrayToCollection implements SuperConverter {
return array; return array;
} }
private Class getCollectionImplClass(Class targetClass) {
if (targetClass.isInterface()) {
if (List.class.equals(targetClass)) {
return ArrayList.class;
} else if (Set.class.equals(targetClass)) {
return LinkedHashSet.class;
} else if (SortedSet.class.equals(targetClass)) {
return TreeSet.class;
} else {
throw new IllegalArgumentException("Unsupported collection interface [" + targetClass.getName() + "]");
}
} else {
return targetClass;
}
}
private ConversionExecutor getArrayElementConverter(Object source, Class targetClass) { private ConversionExecutor getArrayElementConverter(Object source, Class targetClass) {
if (elementConverter != null) { if (elementConverter != null) {
return elementConverter; return elementConverter;

View File

@ -0,0 +1,43 @@
/*
* 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.service;
import java.util.ArrayList;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;
import java.util.SortedSet;
import java.util.TreeSet;
class CollectionConversionUtils {
static Class<?> getImpl(Class<?> targetClass) {
if (targetClass.isInterface()) {
if (List.class.equals(targetClass)) {
return ArrayList.class;
} else if (Set.class.equals(targetClass)) {
return LinkedHashSet.class;
} else if (SortedSet.class.equals(targetClass)) {
return TreeSet.class;
} else {
throw new IllegalArgumentException("Unsupported collection interface [" + targetClass.getName() + "]");
}
} else {
return targetClass;
}
}
}

View File

@ -15,14 +15,8 @@
*/ */
package org.springframework.core.convert.service; package org.springframework.core.convert.service;
import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
import java.util.Iterator; import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;
import java.util.SortedSet;
import java.util.TreeSet;
import org.springframework.core.GenericCollectionTypeResolver; import org.springframework.core.GenericCollectionTypeResolver;
import org.springframework.core.convert.ConversionExecutor; import org.springframework.core.convert.ConversionExecutor;
@ -35,7 +29,7 @@ import org.springframework.core.convert.converter.SuperConverter;
* @author Keith Donald * @author Keith Donald
*/ */
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
class CollectionToCollection implements SuperConverter { class CollectionToCollection implements SuperConverter<Collection, Collection> {
private ConversionService conversionService; private ConversionService conversionService;
@ -58,21 +52,10 @@ class CollectionToCollection implements SuperConverter {
this.elementConverter = elementConverter; this.elementConverter = elementConverter;
} }
public Class getSourceClass() { public Collection convert(Collection source, Class targetClass) throws Exception {
return Collection.class; Class implClass = CollectionConversionUtils.getImpl(targetClass);
} Collection targetCollection = (Collection) implClass.getConstructor((Class[]) null)
.newInstance((Object[]) null);
public Class getSuperTargetClass() {
return Collection.class;
}
public Object convert(Object source, Class targetClass) throws Exception {
if (source == null) {
return null;
}
Class targetCollectionImpl = getCollectionImplClass(targetClass);
Collection targetCollection = (Collection) targetCollectionImpl.getConstructor((Class[]) null).newInstance(
(Object[]) null);
ConversionExecutor elementConverter = getElementConverter(source, targetClass); ConversionExecutor elementConverter = getElementConverter(source, targetClass);
Collection sourceCollection = (Collection) source; Collection sourceCollection = (Collection) source;
Iterator it = sourceCollection.iterator(); Iterator it = sourceCollection.iterator();
@ -86,27 +69,6 @@ class CollectionToCollection implements SuperConverter {
return targetCollection; return targetCollection;
} }
public Object convertBack(Object target) throws Exception {
throw new UnsupportedOperationException("Not supported");
}
// this code is duplicated in ArrayToCollection.java and ObjectToCollection too
private Class getCollectionImplClass(Class targetClass) {
if (targetClass.isInterface()) {
if (List.class.equals(targetClass)) {
return ArrayList.class;
} else if (Set.class.equals(targetClass)) {
return LinkedHashSet.class;
} else if (SortedSet.class.equals(targetClass)) {
return TreeSet.class;
} else {
throw new IllegalArgumentException("Unsupported collection interface [" + targetClass.getName() + "]");
}
} else {
return targetClass;
}
}
private ConversionExecutor getElementConverter(Object source, Class targetClass) { private ConversionExecutor getElementConverter(Object source, Class targetClass) {
if (elementConverter != null) { if (elementConverter != null) {
return elementConverter; return elementConverter;

View File

@ -35,7 +35,7 @@ import org.springframework.core.convert.converter.StringToLong;
import org.springframework.core.convert.converter.StringToShort; import org.springframework.core.convert.converter.StringToShort;
/** /**
* Default, local implementation of a conversion service. Will automatically register <i>from string</i> converters for * 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. * a number of standard Java types like Class, Number, Boolean and so on.
* *
* @author Keith Donald * @author Keith Donald
@ -67,6 +67,7 @@ public class DefaultConversionService extends GenericConversionService {
addConverter(new StringToLocale()); addConverter(new StringToLocale());
addConverter(new StringToEnum()); addConverter(new StringToEnum());
addConverter(new NumberToNumber()); addConverter(new NumberToNumber());
// TODO probably don't allow these to be customized, or at least make public
addConverter(new ObjectToCollection(this)); addConverter(new ObjectToCollection(this));
addConverter(new CollectionToCollection(this)); addConverter(new CollectionToCollection(this));
} }

View File

@ -34,11 +34,14 @@ import org.springframework.core.convert.ConversionExecutorNotFoundException;
import org.springframework.core.convert.ConversionService; import org.springframework.core.convert.ConversionService;
import org.springframework.core.convert.converter.Converter; import org.springframework.core.convert.converter.Converter;
import org.springframework.core.convert.converter.SuperConverter; import org.springframework.core.convert.converter.SuperConverter;
import org.springframework.core.convert.converter.SuperTwoWayConverter;
import org.springframework.util.Assert; import org.springframework.util.Assert;
/** /**
* Base implementation of a conversion service. Initially empty, e.g. no converters are registered by default. * Base implementation of a conversion service. Initially empty, e.g. no converters are registered by default.
* *
* TODO custom converters TODO handle case where no Converter/SuperConverter generic param info is available
*
* @author Keith Donald * @author Keith Donald
*/ */
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
@ -50,6 +53,12 @@ public class GenericConversionService implements ConversionService {
*/ */
private final Map sourceClassConverters = new HashMap(); private final Map sourceClassConverters = new HashMap();
/**
* An indexed map of Converters. Each Map.Entry key is a source class (S) that can be converted from. Each Map.Entry
* value is a Map that defines the targetClass-to-Converter mappings for that source.
*/
private final Map sourceClassSuperConverters = new HashMap();
/** /**
* Indexes classes by well-known aliases. * Indexes classes by well-known aliases.
*/ */
@ -95,7 +104,17 @@ public class GenericConversionService implements ConversionService {
* @param converter the super converter to register * @param converter the super converter to register
*/ */
public void addConverter(SuperConverter converter) { public void addConverter(SuperConverter converter) {
// TODO List typeInfo = getTypeInfo(converter);
Class sourceClass = (Class) typeInfo.get(0);
Class targetClass = (Class) typeInfo.get(1);
// index forward
Map sourceMap = getSourceSuperConverterMap(sourceClass);
sourceMap.put(targetClass, converter);
if (converter instanceof SuperTwoWayConverter) {
// index reverse
sourceMap = getSourceSuperConverterMap(targetClass);
sourceMap.put(sourceClass, new ReverseSuperConverter((SuperTwoWayConverter) converter));
}
} }
/** /**
@ -109,22 +128,14 @@ public class GenericConversionService implements ConversionService {
public Object executeConversion(Object source, Class targetClass) throws ConversionExecutorNotFoundException, public Object executeConversion(Object source, Class targetClass) throws ConversionExecutorNotFoundException,
ConversionException { ConversionException {
if (source != null) { ConversionExecutor conversionExecutor = getConversionExecutor(source.getClass(), targetClass);
ConversionExecutor conversionExecutor = getConversionExecutor(source.getClass(), targetClass); return conversionExecutor.execute(source);
return conversionExecutor.execute(source);
} else {
return null;
}
} }
public Object executeConversion(String converterId, Object source, Class targetClass) public Object executeConversion(String converterId, Object source, Class targetClass)
throws ConversionExecutorNotFoundException, ConversionException { throws ConversionExecutorNotFoundException, ConversionException {
if (source != null) { ConversionExecutor conversionExecutor = getConversionExecutor(converterId, source.getClass(), targetClass);
ConversionExecutor conversionExecutor = getConversionExecutor(converterId, source.getClass(), targetClass); return conversionExecutor.execute(source);
return conversionExecutor.execute(source);
} else {
return null;
}
} }
public ConversionExecutor getConversionExecutor(Class sourceClass, Class targetClass) public ConversionExecutor getConversionExecutor(Class sourceClass, Class targetClass)
@ -151,7 +162,8 @@ public class GenericConversionService implements ConversionService {
} }
if (targetClass.isArray()) { if (targetClass.isArray()) {
if (Collection.class.isAssignableFrom(sourceClass)) { if (Collection.class.isAssignableFrom(sourceClass)) {
return new StaticSuperConversionExecutor(sourceClass, targetClass, new CollectionToArray(this)); SuperConverter collectionToArray = new ReverseSuperConverter(new ArrayToCollection(this));
return new StaticSuperConversionExecutor(sourceClass, targetClass, collectionToArray);
} else { } else {
return new StaticSuperConversionExecutor(sourceClass, targetClass, new ObjectToArray(this)); return new StaticSuperConversionExecutor(sourceClass, targetClass, new ObjectToArray(this));
} }
@ -161,6 +173,10 @@ public class GenericConversionService implements ConversionService {
// we found a converter // we found a converter
return new StaticConversionExecutor(sourceClass, targetClass, converter); return new StaticConversionExecutor(sourceClass, targetClass, converter);
} else { } else {
SuperConverter superConverter = findRegisteredSuperConverter(sourceClass, targetClass);
if (superConverter != null) {
return new StaticSuperConversionExecutor(sourceClass, targetClass, superConverter);
}
if (parent != null) { if (parent != null) {
// try the parent // try the parent
return parent.getConversionExecutor(sourceClass, targetClass); return parent.getConversionExecutor(sourceClass, targetClass);
@ -192,7 +208,7 @@ public class GenericConversionService implements ConversionService {
// internal helpers // internal helpers
private List getTypeInfo(Converter converter) { private List getTypeInfo(Object converter) {
List typeInfo = new ArrayList(2); List typeInfo = new ArrayList(2);
Class classToIntrospect = converter.getClass(); Class classToIntrospect = converter.getClass();
while (classToIntrospect != null) { while (classToIntrospect != null) {
@ -200,7 +216,8 @@ public class GenericConversionService implements ConversionService {
for (Type genericInterface : genericInterfaces) { for (Type genericInterface : genericInterfaces) {
if (genericInterface instanceof ParameterizedType) { if (genericInterface instanceof ParameterizedType) {
ParameterizedType parameterizedInterface = (ParameterizedType) genericInterface; ParameterizedType parameterizedInterface = (ParameterizedType) genericInterface;
if (Converter.class.equals(parameterizedInterface.getRawType())) { if (Converter.class.equals(parameterizedInterface.getRawType())
|| SuperConverter.class.isAssignableFrom((Class) parameterizedInterface.getRawType())) {
Type s = parameterizedInterface.getActualTypeArguments()[0]; Type s = parameterizedInterface.getActualTypeArguments()[0];
Type t = parameterizedInterface.getActualTypeArguments()[1]; Type t = parameterizedInterface.getActualTypeArguments()[1];
typeInfo.add(getParameterClass(s, converter.getClass())); typeInfo.add(getParameterClass(s, converter.getClass()));
@ -213,12 +230,12 @@ public class GenericConversionService implements ConversionService {
return typeInfo; return typeInfo;
} }
private Class<?> getParameterClass(Type parameterType, Class<?> converterClass) { private Class getParameterClass(Type parameterType, Class converterClass) {
if (parameterType instanceof TypeVariable) { if (parameterType instanceof TypeVariable) {
parameterType = GenericTypeResolver.resolveTypeVariable((TypeVariable<?>) parameterType, converterClass); parameterType = GenericTypeResolver.resolveTypeVariable((TypeVariable) parameterType, converterClass);
} }
if (parameterType instanceof Class) { if (parameterType instanceof Class) {
return (Class<?>) parameterType; return (Class) parameterType;
} }
// when would this happen? // when would this happen?
return null; return null;
@ -227,12 +244,21 @@ public class GenericConversionService implements ConversionService {
private Map getSourceMap(Class sourceClass) { private Map getSourceMap(Class sourceClass) {
Map sourceMap = (Map) sourceClassConverters.get(sourceClass); Map sourceMap = (Map) sourceClassConverters.get(sourceClass);
if (sourceMap == null) { if (sourceMap == null) {
sourceMap = new HashMap<Class<?>, Converter<?, ?>>(); sourceMap = new HashMap();
sourceClassConverters.put(sourceClass, sourceMap); sourceClassConverters.put(sourceClass, sourceMap);
} }
return sourceMap; return sourceMap;
} }
private Map getSourceSuperConverterMap(Class sourceClass) {
Map sourceMap = (Map) sourceClassSuperConverters.get(sourceClass);
if (sourceMap == null) {
sourceMap = new HashMap();
sourceClassSuperConverters.put(sourceClass, sourceMap);
}
return sourceMap;
}
private Class convertToWrapperClassIfNecessary(Class targetType) { private Class convertToWrapperClassIfNecessary(Class targetType) {
if (targetType.isPrimitive()) { if (targetType.isPrimitive()) {
if (targetType.equals(int.class)) { if (targetType.equals(int.class)) {
@ -265,8 +291,8 @@ public class GenericConversionService implements ConversionService {
classQueue.addFirst(sourceClass); classQueue.addFirst(sourceClass);
while (!classQueue.isEmpty()) { while (!classQueue.isEmpty()) {
Class currentClass = (Class) classQueue.removeLast(); Class currentClass = (Class) classQueue.removeLast();
Map<Class, Converter> sourceTargetConverters = findConvertersForSource(currentClass); Map converters = getConvertersForSource(currentClass);
Converter converter = findTargetConverter(sourceTargetConverters, targetClass); Converter converter = getConverter(converters, targetClass);
if (converter != null) { if (converter != null) {
return converter; return converter;
} }
@ -275,15 +301,15 @@ public class GenericConversionService implements ConversionService {
classQueue.addFirst(interfaces[i]); classQueue.addFirst(interfaces[i]);
} }
} }
Map objectConverters = findConvertersForSource(Object.class); Map objectConverters = getConvertersForSource(Object.class);
return findTargetConverter(objectConverters, targetClass); return getConverter(objectConverters, targetClass);
} else { } else {
LinkedList classQueue = new LinkedList(); LinkedList classQueue = new LinkedList();
classQueue.addFirst(sourceClass); classQueue.addFirst(sourceClass);
while (!classQueue.isEmpty()) { while (!classQueue.isEmpty()) {
Class currentClass = (Class) classQueue.removeLast(); Class currentClass = (Class) classQueue.removeLast();
Map sourceTargetConverters = findConvertersForSource(currentClass); Map converters = getConvertersForSource(currentClass);
Converter converter = findTargetConverter(sourceTargetConverters, targetClass); Converter converter = getConverter(converters, targetClass);
if (converter != null) { if (converter != null) {
return converter; return converter;
} }
@ -299,15 +325,98 @@ public class GenericConversionService implements ConversionService {
} }
} }
private Map findConvertersForSource(Class sourceClass) { private Map getConvertersForSource(Class sourceClass) {
Map sourceConverters = (Map) sourceClassConverters.get(sourceClass); Map converters = (Map) sourceClassConverters.get(sourceClass);
return sourceConverters != null ? sourceConverters : Collections.emptyMap(); return converters != null ? converters : Collections.emptyMap();
} }
private Converter findTargetConverter(Map sourceTargetConverters, Class targetClass) { private Converter getConverter(Map converters, Class targetClass) {
if (sourceTargetConverters.isEmpty()) { return (Converter) converters.get(targetClass);
}
private SuperConverter findRegisteredSuperConverter(Class sourceClass, Class targetClass) {
if (sourceClass.isInterface()) {
LinkedList classQueue = new LinkedList();
classQueue.addFirst(sourceClass);
while (!classQueue.isEmpty()) {
Class currentClass = (Class) classQueue.removeLast();
Map converters = getSuperConvertersForSource(currentClass);
SuperConverter converter = findSuperConverter(converters, targetClass);
if (converter != null) {
return converter;
}
Class[] interfaces = currentClass.getInterfaces();
for (int i = 0; i < interfaces.length; i++) {
classQueue.addFirst(interfaces[i]);
}
}
Map objectConverters = getSuperConvertersForSource(Object.class);
return findSuperConverter(objectConverters, targetClass);
} else {
LinkedList classQueue = new LinkedList();
classQueue.addFirst(sourceClass);
while (!classQueue.isEmpty()) {
Class currentClass = (Class) classQueue.removeLast();
Map converters = getSuperConvertersForSource(currentClass);
SuperConverter converter = findSuperConverter(converters, targetClass);
if (converter != null) {
return converter;
}
if (currentClass.getSuperclass() != null) {
classQueue.addFirst(currentClass.getSuperclass());
}
Class[] interfaces = currentClass.getInterfaces();
for (int i = 0; i < interfaces.length; i++) {
classQueue.addFirst(interfaces[i]);
}
}
return null; return null;
} }
return (Converter) sourceTargetConverters.get(targetClass);
} }
private Map getSuperConvertersForSource(Class sourceClass) {
Map converters = (Map) sourceClassSuperConverters.get(sourceClass);
return converters != null ? converters : Collections.emptyMap();
}
private SuperConverter findSuperConverter(Map converters, Class targetClass) {
if (converters.isEmpty()) {
return null;
}
if (targetClass.isInterface()) {
LinkedList classQueue = new LinkedList();
classQueue.addFirst(targetClass);
while (!classQueue.isEmpty()) {
Class currentClass = (Class) classQueue.removeLast();
SuperConverter converter = (SuperConverter) converters.get(currentClass);
if (converter != null) {
return converter;
}
Class[] interfaces = currentClass.getInterfaces();
for (int i = 0; i < interfaces.length; i++) {
classQueue.addFirst(interfaces[i]);
}
}
return (SuperConverter) converters.get(Object.class);
} else {
LinkedList classQueue = new LinkedList();
classQueue.addFirst(targetClass);
while (!classQueue.isEmpty()) {
Class currentClass = (Class) classQueue.removeLast();
SuperConverter converter = (SuperConverter) converters.get(currentClass);
if (converter != null) {
return converter;
}
if (currentClass.getSuperclass() != null) {
classQueue.addFirst(currentClass.getSuperclass());
}
Class[] interfaces = currentClass.getInterfaces();
for (int i = 0; i < interfaces.length; i++) {
classQueue.addFirst(interfaces[i]);
}
}
return null;
}
}
} }

View File

@ -17,11 +17,6 @@ package org.springframework.core.convert.service;
import org.springframework.core.convert.converter.Converter; import org.springframework.core.convert.converter.Converter;
/**
* Package private converter that is a "no op".
*
* @author Keith Donald
*/
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
class NoOpConverter implements Converter { class NoOpConverter implements Converter {

View File

@ -22,7 +22,7 @@ import org.springframework.core.convert.ConversionService;
import org.springframework.core.convert.converter.SuperConverter; import org.springframework.core.convert.converter.SuperConverter;
/** /**
* Special two-way converter that converts an object to an single-element array. Mainly used internally by * Special converter that converts an object to an single-element array. Mainly used internally by
* {@link ConversionService} implementations. * {@link ConversionService} implementations.
* *
* @author Keith Donald * @author Keith Donald
@ -51,14 +51,6 @@ class ObjectToArray implements SuperConverter {
this.elementConverter = elementConverter; this.elementConverter = elementConverter;
} }
public Class getSourceClass() {
return Object.class;
}
public Class getSuperTargetClass() {
return Object[].class;
}
public Object convert(Object source, Class targetClass) throws Exception { public Object convert(Object source, Class targetClass) throws Exception {
Class componentType = targetClass.getComponentType(); Class componentType = targetClass.getComponentType();
Object array = Array.newInstance(componentType, 1); Object array = Array.newInstance(componentType, 1);
@ -72,8 +64,4 @@ class ObjectToArray implements SuperConverter {
return array; return array;
} }
public Object convertBack(Object target) throws Exception {
throw new UnsupportedOperationException("Not supported");
}
} }

View File

@ -16,13 +16,7 @@
package org.springframework.core.convert.service; package org.springframework.core.convert.service;
import java.lang.reflect.Constructor; import java.lang.reflect.Constructor;
import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;
import java.util.SortedSet;
import java.util.TreeSet;
import org.springframework.core.GenericCollectionTypeResolver; import org.springframework.core.GenericCollectionTypeResolver;
import org.springframework.core.convert.ConversionExecutor; import org.springframework.core.convert.ConversionExecutor;
@ -36,7 +30,7 @@ import org.springframework.core.convert.converter.SuperConverter;
* @author Keith Donald * @author Keith Donald
*/ */
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
class ObjectToCollection implements SuperConverter { class ObjectToCollection implements SuperConverter<Object, Collection> {
private ConversionService conversionService; private ConversionService conversionService;
@ -59,20 +53,9 @@ class ObjectToCollection implements SuperConverter {
this.elementConverter = elementConverter; this.elementConverter = elementConverter;
} }
public Class getSourceClass() { public Collection convert(Object source, Class targetClass) throws Exception {
return Object.class; Class implClass = CollectionConversionUtils.getImpl(targetClass);
} Constructor constructor = implClass.getConstructor((Class[]) null);
public Class getSuperTargetClass() {
return Collection.class;
}
public Object convert(Object source, Class targetClass) throws Exception {
if (source == null) {
return null;
}
Class collectionImplClass = getCollectionImplClass(targetClass);
Constructor constructor = collectionImplClass.getConstructor((Class[]) null);
Collection collection = (Collection) constructor.newInstance((Object[]) null); Collection collection = (Collection) constructor.newInstance((Object[]) null);
ConversionExecutor converter = getElementConverter(source, targetClass); ConversionExecutor converter = getElementConverter(source, targetClass);
Object value; Object value;
@ -85,27 +68,6 @@ class ObjectToCollection implements SuperConverter {
return collection; return collection;
} }
public Object convertBack(Object target) throws Exception {
throw new UnsupportedOperationException("Not supported");
}
// this code is duplicated in ArrayToCollection and CollectionToCollection
private Class getCollectionImplClass(Class targetClass) {
if (targetClass.isInterface()) {
if (List.class.equals(targetClass)) {
return ArrayList.class;
} else if (Set.class.equals(targetClass)) {
return LinkedHashSet.class;
} else if (SortedSet.class.equals(targetClass)) {
return TreeSet.class;
} else {
throw new IllegalArgumentException("Unsupported collection interface [" + targetClass.getName() + "]");
}
} else {
return targetClass;
}
}
private ConversionExecutor getElementConverter(Object source, Class targetClass) { private ConversionExecutor getElementConverter(Object source, Class targetClass) {
if (elementConverter != null) { if (elementConverter != null) {
return elementConverter; return elementConverter;

View File

@ -0,0 +1,34 @@
/*
* 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.service;
import org.springframework.core.convert.converter.SuperConverter;
import org.springframework.core.convert.converter.SuperTwoWayConverter;
@SuppressWarnings("unchecked")
class ReverseSuperConverter implements SuperConverter {
private SuperTwoWayConverter converter;
public ReverseSuperConverter(SuperTwoWayConverter converter) {
this.converter = converter;
}
public Object convert(Object source, Class targetClass) throws Exception {
return converter.convertBack(source, targetClass);
}
}

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2004-2008 the original author or authors. * Copyright 2004-2009 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -21,16 +21,15 @@ import org.springframework.core.convert.converter.Converter;
import org.springframework.core.style.ToStringCreator; import org.springframework.core.style.ToStringCreator;
import org.springframework.util.Assert; import org.springframework.util.Assert;
@SuppressWarnings("unchecked") class StaticConversionExecutor<S, T> implements ConversionExecutor<S, T> {
class StaticConversionExecutor implements ConversionExecutor {
private final Class sourceClass; private final Class<S> sourceClass;
private final Class targetClass; private final Class<T> targetClass;
private final Converter converter; private final Converter<S, T> converter;
public StaticConversionExecutor(Class sourceClass, Class targetClass, Converter converter) { public StaticConversionExecutor(Class<S> sourceClass, Class<T> targetClass, Converter<S, T> converter) {
Assert.notNull(sourceClass, "The source class is required"); Assert.notNull(sourceClass, "The source class is required");
Assert.notNull(targetClass, "The target class is required"); Assert.notNull(targetClass, "The target class is required");
Assert.notNull(converter, "The converter is required"); Assert.notNull(converter, "The converter is required");
@ -39,15 +38,15 @@ class StaticConversionExecutor implements ConversionExecutor {
this.converter = converter; this.converter = converter;
} }
public Class getSourceClass() { public Class<S> getSourceClass() {
return sourceClass; return sourceClass;
} }
public Class getTargetClass() { public Class<T> getTargetClass() {
return targetClass; return targetClass;
} }
public Object execute(Object source) throws ConversionExecutionException { public T execute(S source) throws ConversionExecutionException {
if (source == null) { if (source == null) {
return null; return null;
} }
@ -66,7 +65,7 @@ class StaticConversionExecutor implements ConversionExecutor {
if (!(o instanceof StaticConversionExecutor)) { if (!(o instanceof StaticConversionExecutor)) {
return false; return false;
} }
StaticConversionExecutor other = (StaticConversionExecutor) o; StaticConversionExecutor<?, ?> other = (StaticConversionExecutor<?, ?>) o;
return sourceClass.equals(other.sourceClass) && targetClass.equals(other.targetClass); return sourceClass.equals(other.sourceClass) && targetClass.equals(other.targetClass);
} }

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2004-2008 the original author or authors. * Copyright 2004-2009 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -21,16 +21,15 @@ import org.springframework.core.convert.converter.SuperConverter;
import org.springframework.core.style.ToStringCreator; import org.springframework.core.style.ToStringCreator;
import org.springframework.util.Assert; import org.springframework.util.Assert;
@SuppressWarnings("unchecked") class StaticSuperConversionExecutor<S, T> implements ConversionExecutor<S, T> {
class StaticSuperConversionExecutor implements ConversionExecutor {
private final Class sourceClass; private final Class<S> sourceClass;
private final Class targetClass; private final Class<T> targetClass;
private final SuperConverter converter; private final SuperConverter<S, T> converter;
public StaticSuperConversionExecutor(Class sourceClass, Class targetClass, SuperConverter converter) { public StaticSuperConversionExecutor(Class<S> sourceClass, Class<T> targetClass, SuperConverter<S, T> converter) {
Assert.notNull(sourceClass, "The source class is required"); Assert.notNull(sourceClass, "The source class is required");
Assert.notNull(targetClass, "The target class is required"); Assert.notNull(targetClass, "The target class is required");
Assert.notNull(converter, "The super converter is required"); Assert.notNull(converter, "The super converter is required");
@ -39,15 +38,15 @@ class StaticSuperConversionExecutor implements ConversionExecutor {
this.converter = converter; this.converter = converter;
} }
public Class getSourceClass() { public Class<S> getSourceClass() {
return sourceClass; return sourceClass;
} }
public Class getTargetClass() { public Class<T> getTargetClass() {
return targetClass; return targetClass;
} }
public Object execute(Object source) throws ConversionExecutionException { public T execute(S source) throws ConversionExecutionException {
if (source == null) { if (source == null) {
return null; return null;
} }
@ -66,7 +65,7 @@ class StaticSuperConversionExecutor implements ConversionExecutor {
if (!(o instanceof StaticSuperConversionExecutor)) { if (!(o instanceof StaticSuperConversionExecutor)) {
return false; return false;
} }
StaticSuperConversionExecutor other = (StaticSuperConversionExecutor) o; StaticSuperConversionExecutor<?, ?> other = (StaticSuperConversionExecutor<?, ?>) o;
return sourceClass.equals(other.sourceClass) && targetClass.equals(other.targetClass); return sourceClass.equals(other.sourceClass) && targetClass.equals(other.targetClass);
} }

View File

@ -1,159 +0,0 @@
/*
* 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.service;
import java.util.AbstractList;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import junit.framework.TestCase;
import org.springframework.core.convert.ConversionExecutor;
import org.springframework.core.convert.ConversionExecutorNotFoundException;
import org.springframework.core.convert.ConversionService;
import org.springframework.core.convert.converter.Converter;
import org.springframework.core.convert.converter.StringToBoolean;
/**
* Test case for the default conversion service.
*
* @author Keith Donald
*/
public class DefaultConversionServiceTests extends TestCase {
ConversionService service = new DefaultConversionService();
public void testConversionForwardIndex() {
ConversionExecutor<String, Integer> executor = service.getConversionExecutor(String.class, Integer.class);
Integer three = executor.execute("3");
assertEquals(3, three.intValue());
}
public void testConversionReverseIndex() {
ConversionExecutor<Integer, String> executor = service.getConversionExecutor(Integer.class, String.class);
String threeString = executor.execute(new Integer(3));
assertEquals("3", threeString);
}
public void testConversionCompatibleTypes() {
ArrayList source = new ArrayList();
assertSame(source, service.getConversionExecutor(ArrayList.class, List.class).execute(source));
}
public void testConversionOverrideDefaultConverter() {
Converter<String, Boolean> customConverter = new StringToBoolean("ja", "nee");
((GenericConversionService) service).addConverter(customConverter);
ConversionExecutor<String, Boolean> executor = service.getConversionExecutor(String.class, Boolean.class);
assertTrue(executor.execute("ja").booleanValue());
}
public void testConverterLookupTargetClassNotSupported() {
try {
service.getConversionExecutor(String.class, HashMap.class);
fail("Should have thrown an exception");
} catch (ConversionExecutorNotFoundException e) {
}
}
public void testConversionToPrimitive() {
DefaultConversionService service = new DefaultConversionService();
ConversionExecutor executor = service.getConversionExecutor(String.class, int.class);
Integer three = (Integer) executor.execute("3");
assertEquals(3, three.intValue());
}
public void testConversionArrayToArray() {
ConversionExecutor executor = service.getConversionExecutor(String[].class, Integer[].class);
Integer[] result = (Integer[]) executor.execute(new String[] { "1", "2", "3" });
assertEquals(new Integer(1), result[0]);
assertEquals(new Integer(2), result[1]);
assertEquals(new Integer(3), result[2]);
}
public void testConversionArrayToPrimitiveArray() {
ConversionExecutor executor = service.getConversionExecutor(String[].class, int[].class);
int[] result = (int[]) executor.execute(new String[] { "1", "2", "3" });
assertEquals(1, result[0]);
assertEquals(2, result[1]);
assertEquals(3, result[2]);
}
public void testConversionArrayToList() {
ConversionExecutor executor = service.getConversionExecutor(String[].class, List.class);
List result = (List) executor.execute(new String[] { "1", "2", "3" });
assertEquals("1", result.get(0));
assertEquals("2", result.get(1));
assertEquals("3", result.get(2));
}
public void testConversionToArray() {
ConversionExecutor executor = service.getConversionExecutor(Collection.class, String[].class);
List list = new ArrayList();
list.add("1");
list.add("2");
list.add("3");
String[] result = (String[]) executor.execute(list);
assertEquals("1", result[0]);
assertEquals("2", result[1]);
assertEquals("3", result[2]);
}
public void testConversionListToArrayWithComponentConversion() {
ConversionExecutor executor = service.getConversionExecutor(Collection.class, Integer[].class);
List list = new ArrayList();
list.add("1");
list.add("2");
list.add("3");
Integer[] result = (Integer[]) executor.execute(list);
assertEquals(new Integer(1), result[0]);
assertEquals(new Integer(2), result[1]);
assertEquals(new Integer(3), result[2]);
}
public void testConversionArrayToConcreteList() {
ConversionExecutor executor = service.getConversionExecutor(String[].class, LinkedList.class);
LinkedList result = (LinkedList) executor.execute(new String[] { "1", "2", "3" });
assertEquals("1", result.get(0));
assertEquals("2", result.get(1));
assertEquals("3", result.get(2));
}
public void testConversionArrayToAbstractList() {
try {
service.getConversionExecutor(String[].class, AbstractList.class);
} catch (IllegalArgumentException e) {
}
}
public void testConversionStringToArray() {
ConversionExecutor executor = service.getConversionExecutor(String.class, String[].class);
String[] result = (String[]) executor.execute("1,2,3");
assertEquals(1, result.length);
assertEquals("1,2,3", result[0]);
}
public void testConversionStringToArrayWithElementConversion() {
ConversionExecutor executor = service.getConversionExecutor(String.class, Integer[].class);
Integer[] result = (Integer[]) executor.execute("123");
assertEquals(1, result.length);
assertEquals(new Integer(123), result[0]);
}
}

View File

@ -0,0 +1,253 @@
package org.springframework.core.convert.service;
import java.util.AbstractList;
import java.util.ArrayList;
import java.util.Collection;
import java.util.LinkedList;
import java.util.List;
import junit.framework.TestCase;
import org.springframework.core.convert.ConversionExecutionException;
import org.springframework.core.convert.ConversionExecutor;
import org.springframework.core.convert.ConversionExecutorNotFoundException;
import org.springframework.core.convert.converter.Converter;
import org.springframework.core.convert.converter.NumberToNumber;
import org.springframework.core.convert.converter.StringToEnum;
import org.springframework.core.convert.converter.StringToInteger;
import org.springframework.core.convert.service.GenericConversionService;
public class GenericConversionServiceTests extends TestCase {
private GenericConversionService service = new GenericConversionService();
public void testExecuteConversion() {
service.addConverter(new StringToInteger());
assertEquals(new Integer(3), service.executeConversion("3", Integer.class));
}
public void testConverterConversionForwardIndex() {
service.addConverter(new StringToInteger());
ConversionExecutor executor = service.getConversionExecutor(String.class, Integer.class);
Integer three = (Integer) executor.execute("3");
assertEquals(3, three.intValue());
}
public void testConverterConversionReverseIndex() {
service.addConverter(new StringToInteger());
ConversionExecutor executor = service.getConversionExecutor(Integer.class, String.class);
String threeString = (String) executor.execute(new Integer(3));
assertEquals("3", threeString);
}
public void testConversionExecutorNotFound() {
try {
service.getConversionExecutor(String.class, Integer.class);
fail("Should have thrown an exception");
} catch (ConversionExecutorNotFoundException e) {
}
}
public void testConversionCompatibleTypes() {
ArrayList source = new ArrayList();
assertSame(source, service.getConversionExecutor(ArrayList.class, List.class).execute(source));
}
public void testConversionExecutorNullArgument() {
service.addConverter(new StringToInteger());
ConversionExecutor executor = service.getConversionExecutor(String.class, Integer.class);
assertNull(executor.execute(null));
}
public void testConversionExecutorWrongTypeArgument() {
service.addConverter(new StringToInteger());
ConversionExecutor executor = service.getConversionExecutor(Integer.class, String.class);
try {
executor.execute("BOGUS");
fail("Should have failed");
} catch (ConversionExecutionException e) {
}
}
public void testConverterConversionSuperSourceType() {
service.addConverter(new Converter<CharSequence, Integer>() {
public Integer convert(CharSequence source) throws Exception {
return Integer.valueOf(source.toString());
}
public CharSequence convertBack(Integer target) throws Exception {
return target.toString();
}
});
ConversionExecutor executor = service.getConversionExecutor(String.class, Integer.class);
Integer result = (Integer) executor.execute("3");
assertEquals(new Integer(3), result);
}
public void testConverterConversionNoSuperTargetType() {
service.addConverter(new Converter<CharSequence, Number>() {
public Integer convert(CharSequence source) throws Exception {
return Integer.valueOf(source.toString());
}
public CharSequence convertBack(Number target) throws Exception {
return target.toString();
}
});
try {
ConversionExecutor executor = service.getConversionExecutor(String.class, Integer.class);
fail("Should have failed");
} catch (ConversionExecutorNotFoundException e) {
}
}
public void testConversionObjectToPrimitive() {
service.addConverter(new StringToInteger());
ConversionExecutor executor = service.getConversionExecutor(String.class, int.class);
Integer three = (Integer) executor.execute("3");
assertEquals(3, three.intValue());
}
public void testConversionArrayToArray() {
service.addConverter(new StringToInteger());
ConversionExecutor executor = service.getConversionExecutor(String[].class, Integer[].class);
Integer[] result = (Integer[]) executor.execute(new String[] { "1", "2", "3" });
assertEquals(new Integer(1), result[0]);
assertEquals(new Integer(2), result[1]);
assertEquals(new Integer(3), result[2]);
}
public void testConversionArrayToPrimitiveArray() {
service.addConverter(new StringToInteger());
ConversionExecutor executor = service.getConversionExecutor(String[].class, int[].class);
int[] result = (int[]) executor.execute(new String[] { "1", "2", "3" });
assertEquals(1, result[0]);
assertEquals(2, result[1]);
assertEquals(3, result[2]);
}
public void testConversionArrayToListInterface() {
ConversionExecutor executor = service.getConversionExecutor(String[].class, List.class);
List result = (List) executor.execute(new String[] { "1", "2", "3" });
assertEquals("1", result.get(0));
assertEquals("2", result.get(1));
assertEquals("3", result.get(2));
}
public void testConversionArrayToListImpl() {
ConversionExecutor executor = service.getConversionExecutor(String[].class, LinkedList.class);
LinkedList result = (LinkedList) executor.execute(new String[] { "1", "2", "3" });
assertEquals("1", result.get(0));
assertEquals("2", result.get(1));
assertEquals("3", result.get(2));
}
public void testConversionArrayToAbstractList() {
try {
service.getConversionExecutor(String[].class, AbstractList.class);
} catch (IllegalArgumentException e) {
}
}
public void testConversionListToArray() {
ConversionExecutor executor = service.getConversionExecutor(Collection.class, String[].class);
List list = new ArrayList();
list.add("1");
list.add("2");
list.add("3");
String[] result = (String[]) executor.execute(list);
assertEquals("1", result[0]);
assertEquals("2", result[1]);
assertEquals("3", result[2]);
}
public void testConversionListToArrayWithComponentConversion() {
service.addConverter(new StringToInteger());
ConversionExecutor executor = service.getConversionExecutor(Collection.class, Integer[].class);
List list = new ArrayList();
list.add("1");
list.add("2");
list.add("3");
Integer[] result = (Integer[]) executor.execute(list);
assertEquals(new Integer(1), result[0]);
assertEquals(new Integer(2), result[1]);
assertEquals(new Integer(3), result[2]);
}
public void testConversionObjectToArray() {
ConversionExecutor executor = service.getConversionExecutor(String.class, String[].class);
String[] result = (String[]) executor.execute("1,2,3");
assertEquals(1, result.length);
assertEquals("1,2,3", result[0]);
}
public void testConversionObjectToArrayWithElementConversion() {
service.addConverter(new StringToInteger());
ConversionExecutor executor = service.getConversionExecutor(String.class, Integer[].class);
Integer[] result = (Integer[]) executor.execute("123");
assertEquals(1, result.length);
assertEquals(new Integer(123), result[0]);
}
public static enum FooEnum {
BAR
}
public void testSuperConverterConversionForwardIndex() {
service.addConverter(new StringToEnum());
ConversionExecutor executor = service.getConversionExecutor(String.class, FooEnum.class);
assertEquals(FooEnum.BAR, executor.execute("BAR"));
}
public void testSuperTwoWayConverterConversionReverseIndex() {
service.addConverter(new StringToEnum());
ConversionExecutor executor = service.getConversionExecutor(FooEnum.class, String.class);
assertEquals("BAR", executor.execute(FooEnum.BAR));
}
public void testSuperConverterConversionNotConvertibleAbstractType() {
service.addConverter(new StringToEnum());
ConversionExecutor executor = service.getConversionExecutor(String.class, Enum.class);
try {
executor.execute("WHATEV");
fail("Should have failed");
} catch (ConversionExecutionException e) {
}
}
public void testSuperConverterConversionNotConvertibleAbstractType2() {
service.addConverter(new NumberToNumber());
Number customNumber = new Number() {
@Override
public double doubleValue() {
return 0;
}
@Override
public float floatValue() {
return 0;
}
@Override
public int intValue() {
return 0;
}
@Override
public long longValue() {
return 0;
}
};
ConversionExecutor executor = service.getConversionExecutor(Integer.class, customNumber.getClass());
try {
executor.execute(3);
fail("Should have failed");
} catch (ConversionExecutionException e) {
}
}
}