提交 0ef0ff60 编写于 作者: K Keith Donald

super converter implementation

上级 b0400935
......@@ -16,7 +16,7 @@
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
* converter.
*
......@@ -25,20 +25,19 @@ package org.springframework.core.convert;
public interface ConversionExecutor<S, T> {
/**
* Returns the source class of conversions performed by this executor.
* @return the source class
* The type this executor converts from.
*/
public Class<S> getSourceClass();
/**
* Returns the target class of conversions performed by this executor.
* @return the target class
*/
* The type this executor converts to.
s */
public Class<T> getTargetClass();
/**
* Execute the conversion for the provided source object.
* @param source the source object to convert
* Convert the source to T.
* @param source the source to convert
* @throws ConversionExecutionException if an exception occurs during type conversion
*/
public T execute(S source) throws ConversionExecutionException;
......
......@@ -26,7 +26,7 @@ package org.springframework.core.convert;
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 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
......@@ -39,7 +39,7 @@ public interface ConversionService {
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
* capable of converting to the targetClass
* @param source the source to convert from (may be null)
......@@ -54,7 +54,7 @@ public interface ConversionService {
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.
* @param sourceClass the source class to convert from (required)
* @param targetClass the target class to convert to (required)
......@@ -65,9 +65,8 @@ public interface ConversionService {
throws ConversionExecutorNotFoundException;
/**
* Get a ConversionExecutor that uses a custom converter to capable convert objects from <code>sourceClass</code> to
* <code>targetClass</code>. The returned ConversionExecutor is thread-safe and may safely be cached for use in
* client code.
* Get a ConversionExecutor that that converts objects from S to T with a custom converter.
* The returned ConversionExecutor is thread-safe and may safely be cached for use in client code.
* @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)
* @param sourceClass the source class to convert from (required)
......@@ -79,9 +78,8 @@ public interface ConversionService {
Class<T> targetClass) throws ConversionExecutorNotFoundException;
/**
* Lookup a class by its well-known alias. For example, <code>long</code> for <code>java.lang.Long</code>
* @param alias the class alias
* @return the class, or <code>null</code> if no alias exists
* Get a class by its alias.
* @return the class, or <code>null</code> if no such alias exists
*/
public Class<?> getClassForAlias(String alias);
......
......@@ -19,7 +19,7 @@ import org.springframework.core.convert.ConversionException;
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>
* Implementations of this interface are thread-safe and can be shared. Converters are typically registered with and
* accessed through a {@link ConversionService}.
......@@ -29,7 +29,7 @@ import org.springframework.core.convert.ConversionService;
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
* @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
......@@ -39,7 +39,7 @@ public interface Converter<S, T> {
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
* @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
......
......@@ -37,12 +37,8 @@ import org.springframework.util.NumberUtils;
*/
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);
}
public Number convertBack(Number target) throws Exception {
return target;
}
}
\ No newline at end of file
......@@ -16,19 +16,19 @@
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
*/
@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);
}
public String convertBack(Enum target) throws Exception {
return target.name();
public <RS extends String> RS convertBack(Enum target, Class<RS> sourceClass) throws Exception {
return (RS) target.name();
}
}
\ No newline at end of file
......@@ -19,8 +19,7 @@ import org.springframework.core.convert.ConversionException;
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
* 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.
* A super converter converts a source object of type S to a target of class hierarchy T.
* <p>
* Implementations of this interface are thread-safe and can be shared. Converters are typically registered with and
* accessed through a {@link ConversionService}.
......@@ -30,25 +29,14 @@ import org.springframework.core.convert.ConversionService;
public interface SuperConverter<S, T> {
/**
* Convert the source S to an instance of AT.
* @param source the source object to convert, which must be an instance of S
* @param actualTargetClass the actual target class to convert to (AT), which must be equal to or a specialization
* of T.
* @return the converted object, which must be an instance of AT
* Convert the source of type S to an instance of type RT.
* @param source the source object to convert, whose class must be equal to or a subclass of S
* @param targetClass the requested target class to convert to (RT), which must be equal to T or extend from T
* @return the converted object, which must be an instance of RT
* @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 <AT extends T> AT convert(S source, Class<AT> actualTargetClass) 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;
public <RT extends T> RT convert(S source, Class<RT> targetClass) throws Exception;
}
\ No newline at end of file
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;
}
\ No newline at end of file
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);
}
}
......@@ -53,18 +53,7 @@ class ArrayToArray implements SuperConverter {
this.elementConverter = elementConverter;
}
public Class getSourceClass() {
return Object[].class;
}
public Class getSuperTargetClass() {
return Object[].class;
}
public Object convert(Object source, Class targetClass) throws Exception {
if (source == null) {
return null;
}
Class sourceComponentType = source.getClass().getComponentType();
Class targetComponentType = targetClass.getComponentType();
int length = Array.getLength(source);
......@@ -77,10 +66,6 @@ class ArrayToArray implements SuperConverter {
return targetArray;
}
public Object convertBack(Object target) throws Exception {
throw new UnsupportedOperationException("Not supported");
}
private ConversionExecutor getElementConverter(Class sourceComponentType, Class targetComponentType) {
if (elementConverter != null) {
return elementConverter;
......
......@@ -17,19 +17,13 @@ package org.springframework.core.convert.service;
import java.lang.reflect.Array;
import java.lang.reflect.Constructor;
import java.util.ArrayList;
import java.util.Collection;
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.convert.ConversionExecutor;
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
......@@ -45,7 +39,7 @@ import org.springframework.core.convert.converter.SuperConverter;
* @author Keith Donald
*/
@SuppressWarnings("unchecked")
class ArrayToCollection implements SuperConverter {
class ArrayToCollection implements SuperTwoWayConverter {
private ConversionService conversionService;
......@@ -69,11 +63,8 @@ class ArrayToCollection implements SuperConverter {
}
public Object convert(Object source, Class targetClass) throws Exception {
if (source == null) {
return null;
}
Class collectionImplClass = getCollectionImplClass(targetClass);
Constructor constructor = collectionImplClass.getConstructor((Class[]) null);
Class implClass = CollectionConversionUtils.getImpl(targetClass);
Constructor constructor = implClass.getConstructor((Class[]) null);
Collection collection = (Collection) constructor.newInstance((Object[]) null);
ConversionExecutor converter = getArrayElementConverter(source, targetClass);
int length = Array.getLength(source);
......@@ -87,16 +78,10 @@ class ArrayToCollection implements SuperConverter {
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 {
if (target == null) {
return null;
}
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;
for (Iterator it = collection.iterator(); it.hasNext(); i++) {
Object value = it.next();
......@@ -105,8 +90,7 @@ class ArrayToCollection implements SuperConverter {
if (elementConverter != null) {
converter = elementConverter;
} else {
converter = conversionService.getConversionExecutor(value.getClass(), sourceClass
.getComponentType());
converter = conversionService.getConversionExecutor(value.getClass(), elementType);
}
value = converter.execute(value);
}
......@@ -115,22 +99,6 @@ class ArrayToCollection implements SuperConverter {
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) {
if (elementConverter != null) {
return elementConverter;
......
/*
* 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;
}
}
}
......@@ -15,14 +15,8 @@
*/
package org.springframework.core.convert.service;
import java.util.ArrayList;
import java.util.Collection;
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.convert.ConversionExecutor;
......@@ -35,7 +29,7 @@ import org.springframework.core.convert.converter.SuperConverter;
* @author Keith Donald
*/
@SuppressWarnings("unchecked")
class CollectionToCollection implements SuperConverter {
class CollectionToCollection implements SuperConverter<Collection, Collection> {
private ConversionService conversionService;
......@@ -58,21 +52,10 @@ class CollectionToCollection implements SuperConverter {
this.elementConverter = elementConverter;
}
public Class getSourceClass() {
return Collection.class;
}
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);
public Collection convert(Collection source, Class targetClass) throws Exception {
Class implClass = CollectionConversionUtils.getImpl(targetClass);
Collection targetCollection = (Collection) implClass.getConstructor((Class[]) null)
.newInstance((Object[]) null);
ConversionExecutor elementConverter = getElementConverter(source, targetClass);
Collection sourceCollection = (Collection) source;
Iterator it = sourceCollection.iterator();
......@@ -86,27 +69,6 @@ class CollectionToCollection implements SuperConverter {
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) {
if (elementConverter != null) {
return elementConverter;
......
......@@ -35,7 +35,7 @@ import org.springframework.core.convert.converter.StringToLong;
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.
*
* @author Keith Donald
......@@ -67,6 +67,7 @@ public class DefaultConversionService extends GenericConversionService {
addConverter(new StringToLocale());
addConverter(new StringToEnum());
addConverter(new NumberToNumber());
// TODO probably don't allow these to be customized, or at least make public
addConverter(new ObjectToCollection(this));
addConverter(new CollectionToCollection(this));
}
......
......@@ -34,11 +34,14 @@ 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.SuperConverter;
import org.springframework.core.convert.converter.SuperTwoWayConverter;
import org.springframework.util.Assert;
/**
* 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
*/
@SuppressWarnings("unchecked")
......@@ -50,6 +53,12 @@ public class GenericConversionService implements ConversionService {
*/
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.
*/
......@@ -95,7 +104,17 @@ public class GenericConversionService implements ConversionService {
* @param converter the super converter to register
*/
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,
ConversionException {
if (source != null) {
ConversionExecutor conversionExecutor = getConversionExecutor(source.getClass(), targetClass);
return conversionExecutor.execute(source);
} else {
return null;
}
ConversionExecutor conversionExecutor = getConversionExecutor(source.getClass(), targetClass);
return conversionExecutor.execute(source);
}
public Object executeConversion(String converterId, Object source, Class targetClass)
throws ConversionExecutorNotFoundException, ConversionException {
if (source != null) {
ConversionExecutor conversionExecutor = getConversionExecutor(converterId, source.getClass(), targetClass);
return conversionExecutor.execute(source);
} else {
return null;
}
ConversionExecutor conversionExecutor = getConversionExecutor(converterId, source.getClass(), targetClass);
return conversionExecutor.execute(source);
}
public ConversionExecutor getConversionExecutor(Class sourceClass, Class targetClass)
......@@ -151,7 +162,8 @@ public class GenericConversionService implements ConversionService {
}
if (targetClass.isArray()) {
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 {
return new StaticSuperConversionExecutor(sourceClass, targetClass, new ObjectToArray(this));
}
......@@ -161,6 +173,10 @@ public class GenericConversionService implements ConversionService {
// we found a converter
return new StaticConversionExecutor(sourceClass, targetClass, converter);
} else {
SuperConverter superConverter = findRegisteredSuperConverter(sourceClass, targetClass);
if (superConverter != null) {
return new StaticSuperConversionExecutor(sourceClass, targetClass, superConverter);
}
if (parent != null) {
// try the parent
return parent.getConversionExecutor(sourceClass, targetClass);
......@@ -192,7 +208,7 @@ public class GenericConversionService implements ConversionService {
// internal helpers
private List getTypeInfo(Converter converter) {
private List getTypeInfo(Object converter) {
List typeInfo = new ArrayList(2);
Class classToIntrospect = converter.getClass();
while (classToIntrospect != null) {
......@@ -200,7 +216,8 @@ public class GenericConversionService implements ConversionService {
for (Type genericInterface : genericInterfaces) {
if (genericInterface instanceof ParameterizedType) {
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 t = parameterizedInterface.getActualTypeArguments()[1];
typeInfo.add(getParameterClass(s, converter.getClass()));
......@@ -213,12 +230,12 @@ public class GenericConversionService implements ConversionService {
return typeInfo;
}
private Class<?> getParameterClass(Type parameterType, Class<?> converterClass) {
private Class getParameterClass(Type parameterType, Class converterClass) {
if (parameterType instanceof TypeVariable) {
parameterType = GenericTypeResolver.resolveTypeVariable((TypeVariable<?>) parameterType, converterClass);
parameterType = GenericTypeResolver.resolveTypeVariable((TypeVariable) parameterType, converterClass);
}
if (parameterType instanceof Class) {
return (Class<?>) parameterType;
return (Class) parameterType;
}
// when would this happen?
return null;
......@@ -227,12 +244,21 @@ public class GenericConversionService implements ConversionService {
private Map getSourceMap(Class sourceClass) {
Map sourceMap = (Map) sourceClassConverters.get(sourceClass);
if (sourceMap == null) {
sourceMap = new HashMap<Class<?>, Converter<?, ?>>();
sourceMap = new HashMap();
sourceClassConverters.put(sourceClass, 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) {
if (targetType.isPrimitive()) {
if (targetType.equals(int.class)) {
......@@ -265,8 +291,57 @@ public class GenericConversionService implements ConversionService {
classQueue.addFirst(sourceClass);
while (!classQueue.isEmpty()) {
Class currentClass = (Class) classQueue.removeLast();
Map<Class, Converter> sourceTargetConverters = findConvertersForSource(currentClass);
Converter converter = findTargetConverter(sourceTargetConverters, targetClass);
Map converters = getConvertersForSource(currentClass);
Converter converter = getConverter(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 = getConvertersForSource(Object.class);
return getConverter(objectConverters, targetClass);
} else {
LinkedList classQueue = new LinkedList();
classQueue.addFirst(sourceClass);
while (!classQueue.isEmpty()) {
Class currentClass = (Class) classQueue.removeLast();
Map converters = getConvertersForSource(currentClass);
Converter converter = getConverter(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;
}
}
private Map getConvertersForSource(Class sourceClass) {
Map converters = (Map) sourceClassConverters.get(sourceClass);
return converters != null ? converters : Collections.emptyMap();
}
private Converter getConverter(Map converters, Class targetClass) {
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;
}
......@@ -275,15 +350,15 @@ public class GenericConversionService implements ConversionService {
classQueue.addFirst(interfaces[i]);
}
}
Map objectConverters = findConvertersForSource(Object.class);
return findTargetConverter(objectConverters, targetClass);
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 sourceTargetConverters = findConvertersForSource(currentClass);
Converter converter = findTargetConverter(sourceTargetConverters, targetClass);
Map converters = getSuperConvertersForSource(currentClass);
SuperConverter converter = findSuperConverter(converters, targetClass);
if (converter != null) {
return converter;
}
......@@ -299,15 +374,49 @@ public class GenericConversionService implements ConversionService {
}
}
private Map findConvertersForSource(Class sourceClass) {
Map sourceConverters = (Map) sourceClassConverters.get(sourceClass);
return sourceConverters != null ? sourceConverters : Collections.emptyMap();
private Map getSuperConvertersForSource(Class sourceClass) {
Map converters = (Map) sourceClassSuperConverters.get(sourceClass);
return converters != null ? converters : Collections.emptyMap();
}
private Converter findTargetConverter(Map sourceTargetConverters, Class targetClass) {
if (sourceTargetConverters.isEmpty()) {
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;
}
return (Converter) sourceTargetConverters.get(targetClass);
}
}
\ No newline at end of file
......@@ -17,11 +17,6 @@ package org.springframework.core.convert.service;
import org.springframework.core.convert.converter.Converter;
/**
* Package private converter that is a "no op".
*
* @author Keith Donald
*/
@SuppressWarnings("unchecked")
class NoOpConverter implements Converter {
......
......@@ -22,7 +22,7 @@ import org.springframework.core.convert.ConversionService;
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.
*
* @author Keith Donald
......@@ -51,14 +51,6 @@ class ObjectToArray implements SuperConverter {
this.elementConverter = elementConverter;
}
public Class getSourceClass() {
return Object.class;
}
public Class getSuperTargetClass() {
return Object[].class;
}
public Object convert(Object source, Class targetClass) throws Exception {
Class componentType = targetClass.getComponentType();
Object array = Array.newInstance(componentType, 1);
......@@ -72,8 +64,4 @@ class ObjectToArray implements SuperConverter {
return array;
}
public Object convertBack(Object target) throws Exception {
throw new UnsupportedOperationException("Not supported");
}
}
\ No newline at end of file
......@@ -16,13 +16,7 @@
package org.springframework.core.convert.service;
import java.lang.reflect.Constructor;
import java.util.ArrayList;
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.convert.ConversionExecutor;
......@@ -36,7 +30,7 @@ import org.springframework.core.convert.converter.SuperConverter;
* @author Keith Donald
*/
@SuppressWarnings("unchecked")
class ObjectToCollection implements SuperConverter {
class ObjectToCollection implements SuperConverter<Object, Collection> {
private ConversionService conversionService;
......@@ -59,20 +53,9 @@ class ObjectToCollection implements SuperConverter {
this.elementConverter = elementConverter;
}
public Class getSourceClass() {
return Object.class;
}
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);
public Collection convert(Object source, Class targetClass) throws Exception {
Class implClass = CollectionConversionUtils.getImpl(targetClass);
Constructor constructor = implClass.getConstructor((Class[]) null);
Collection collection = (Collection) constructor.newInstance((Object[]) null);
ConversionExecutor converter = getElementConverter(source, targetClass);
Object value;
......@@ -85,27 +68,6 @@ class ObjectToCollection implements SuperConverter {
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) {
if (elementConverter != null) {
return elementConverter;
......
/*
* 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);
}
}
/*
* 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");
* 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.util.Assert;
@SuppressWarnings("unchecked")
class StaticConversionExecutor implements ConversionExecutor {
class StaticConversionExecutor<S, T> implements ConversionExecutor<S, T> {
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(targetClass, "The target class is required");
Assert.notNull(converter, "The converter is required");
......@@ -39,15 +38,15 @@ class StaticConversionExecutor implements ConversionExecutor {
this.converter = converter;
}
public Class getSourceClass() {
public Class<S> getSourceClass() {
return sourceClass;
}
public Class getTargetClass() {
public Class<T> getTargetClass() {
return targetClass;
}
public Object execute(Object source) throws ConversionExecutionException {
public T execute(S source) throws ConversionExecutionException {
if (source == null) {
return null;
}
......@@ -66,7 +65,7 @@ class StaticConversionExecutor implements ConversionExecutor {
if (!(o instanceof StaticConversionExecutor)) {
return false;
}
StaticConversionExecutor other = (StaticConversionExecutor) o;
StaticConversionExecutor<?, ?> other = (StaticConversionExecutor<?, ?>) o;
return sourceClass.equals(other.sourceClass) && targetClass.equals(other.targetClass);
}
......
/*
* 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");
* 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.util.Assert;
@SuppressWarnings("unchecked")
class StaticSuperConversionExecutor implements ConversionExecutor {
class StaticSuperConversionExecutor<S, T> implements ConversionExecutor<S, T> {
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(targetClass, "The target class is required");
Assert.notNull(converter, "The super converter is required");
......@@ -39,15 +38,15 @@ class StaticSuperConversionExecutor implements ConversionExecutor {
this.converter = converter;
}
public Class getSourceClass() {
public Class<S> getSourceClass() {
return sourceClass;
}
public Class getTargetClass() {
public Class<T> getTargetClass() {
return targetClass;
}
public Object execute(Object source) throws ConversionExecutionException {
public T execute(S source) throws ConversionExecutionException {
if (source == null) {
return null;
}
......@@ -66,7 +65,7 @@ class StaticSuperConversionExecutor implements ConversionExecutor {
if (!(o instanceof StaticSuperConversionExecutor)) {
return false;
}
StaticSuperConversionExecutor other = (StaticSuperConversionExecutor) o;
StaticSuperConversionExecutor<?, ?> other = (StaticSuperConversionExecutor<?, ?>) o;
return sourceClass.equals(other.sourceClass) && targetClass.equals(other.targetClass);
}
......
/*
* 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.ConversionExecutionException;
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;
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;
/**
* Test case for the default conversion service.
*
* @author Keith Donald
*/
public class DefaultConversionServiceTests extends TestCase {
public class GenericConversionServiceTests extends TestCase {
ConversionService service = new DefaultConversionService();
private GenericConversionService service = new GenericConversionService();
public void testConversionForwardIndex() {
ConversionExecutor<String, Integer> executor = service.getConversionExecutor(String.class, Integer.class);
Integer three = executor.execute("3");
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 testConversionReverseIndex() {
ConversionExecutor<Integer, String> executor = service.getConversionExecutor(Integer.class, String.class);
String threeString = executor.execute(new Integer(3));
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 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 testConversionExecutorNullArgument() {
service.addConverter(new StringToInteger());
ConversionExecutor executor = service.getConversionExecutor(String.class, Integer.class);
assertNull(executor.execute(null));
}
public void testConverterLookupTargetClassNotSupported() {
public void testConversionExecutorWrongTypeArgument() {
service.addConverter(new StringToInteger());
ConversionExecutor executor = service.getConversionExecutor(Integer.class, String.class);
try {
service.getConversionExecutor(String.class, HashMap.class);
fail("Should have thrown an exception");
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 testConversionToPrimitive() {
DefaultConversionService service = new DefaultConversionService();
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]);
......@@ -87,6 +120,7 @@ public class DefaultConversionServiceTests extends TestCase {
}
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]);
......@@ -94,7 +128,7 @@ public class DefaultConversionServiceTests extends TestCase {
assertEquals(3, result[2]);
}
public void testConversionArrayToList() {
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));
......@@ -102,7 +136,23 @@ public class DefaultConversionServiceTests extends TestCase {
assertEquals("3", result.get(2));
}
public void testConversionToArray() {
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");
......@@ -115,6 +165,7 @@ public class DefaultConversionServiceTests extends TestCase {
}
public void testConversionListToArrayWithComponentConversion() {
service.addConverter(new StringToInteger());
ConversionExecutor executor = service.getConversionExecutor(Collection.class, Integer[].class);
List list = new ArrayList();
list.add("1");
......@@ -126,34 +177,77 @@ public class DefaultConversionServiceTests extends TestCase {
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() {
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 testConversionStringToArrayWithElementConversion() {
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) {
}
}
}
\ No newline at end of file
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册