custom converters
This commit is contained in:
parent
0ef0ff60f0
commit
d9f5a7a6c8
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright 2004-2009 the original author or authors.
|
* Copyright 2004-2008 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.
|
||||||
|
|
@ -40,7 +40,7 @@ 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
|
* TODO auto-conversion of generic collection elements
|
||||||
*
|
*
|
||||||
* @author Keith Donald
|
* @author Keith Donald
|
||||||
*/
|
*/
|
||||||
|
|
@ -54,11 +54,17 @@ 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
|
* An indexed map of SuperConverters. Each Map.Entry key is a source class (S) that can be converted from. Each
|
||||||
* value is a Map that defines the targetClass-to-Converter mappings for that source.
|
* Map.Entry value is a Map that defines the targetClass-to-SuperConverter mappings for that source.
|
||||||
*/
|
*/
|
||||||
private final Map sourceClassSuperConverters = new HashMap();
|
private final Map sourceClassSuperConverters = new HashMap();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A map of custom converters. Custom converters are assigned a unique identifier that can be used to lookup the
|
||||||
|
* converter. This allows multiple converters for the same source->target class to be registered.
|
||||||
|
*/
|
||||||
|
private final Map customConverters = new HashMap();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Indexes classes by well-known aliases.
|
* Indexes classes by well-known aliases.
|
||||||
*/
|
*/
|
||||||
|
|
@ -88,7 +94,7 @@ public class GenericConversionService implements ConversionService {
|
||||||
* @param converter the converter to register
|
* @param converter the converter to register
|
||||||
*/
|
*/
|
||||||
public void addConverter(Converter converter) {
|
public void addConverter(Converter converter) {
|
||||||
List typeInfo = getTypeInfo(converter);
|
List typeInfo = getRequiredTypeInfo(converter);
|
||||||
Class sourceClass = (Class) typeInfo.get(0);
|
Class sourceClass = (Class) typeInfo.get(0);
|
||||||
Class targetClass = (Class) typeInfo.get(1);
|
Class targetClass = (Class) typeInfo.get(1);
|
||||||
// index forward
|
// index forward
|
||||||
|
|
@ -104,7 +110,7 @@ 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) {
|
||||||
List typeInfo = getTypeInfo(converter);
|
List typeInfo = getRequiredTypeInfo(converter);
|
||||||
Class sourceClass = (Class) typeInfo.get(0);
|
Class sourceClass = (Class) typeInfo.get(0);
|
||||||
Class targetClass = (Class) typeInfo.get(1);
|
Class targetClass = (Class) typeInfo.get(1);
|
||||||
// index forward
|
// index forward
|
||||||
|
|
@ -117,6 +123,31 @@ public class GenericConversionService implements ConversionService {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Register the converter as a custom converter with this conversion service.
|
||||||
|
* @param id the id to assign the converter
|
||||||
|
* @param converter the converter to use a custom converter
|
||||||
|
*/
|
||||||
|
public void addConverter(String id, Converter converter) {
|
||||||
|
customConverters.put(id, converter);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adapts a {@link SuperTwoWayConverter} that converts between BS and BT class hierarchies to a {@link Converter}
|
||||||
|
* that converts between the specific BS/BT sub types S and T.
|
||||||
|
*
|
||||||
|
* TODO - I think this is going to force indexing on a getSourceClass/getTargetclass prop instead generic args
|
||||||
|
*
|
||||||
|
* @param sourceClass the source class S to convert from, which must be equal or extend BS
|
||||||
|
* @param targetClass the target type T to convert to, which must equal or extend BT
|
||||||
|
* @param converter the super two way converter
|
||||||
|
* @return a converter that converts from S to T by delegating to the super converter
|
||||||
|
*/
|
||||||
|
public static <S, T> Converter<S, T> converterFor(Class<S> sourceClass, Class<T> targetClass,
|
||||||
|
SuperTwoWayConverter converter) {
|
||||||
|
return new SuperTwoWayConverterConverter<S, T>(converter, sourceClass, targetClass);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Add a convenient alias for the target type. {@link #getClassForAlias(String)} can then be used to lookup the type
|
* Add a convenient alias for the target type. {@link #getClassForAlias(String)} can then be used to lookup the type
|
||||||
* given the alias.
|
* given the alias.
|
||||||
|
|
@ -126,16 +157,16 @@ public class GenericConversionService implements ConversionService {
|
||||||
aliasMap.put(alias, targetType);
|
aliasMap.put(alias, targetType);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// implementing ConversionService
|
||||||
|
|
||||||
public Object executeConversion(Object source, Class targetClass) throws ConversionExecutorNotFoundException,
|
public Object executeConversion(Object source, Class targetClass) throws ConversionExecutorNotFoundException,
|
||||||
ConversionException {
|
ConversionException {
|
||||||
ConversionExecutor conversionExecutor = getConversionExecutor(source.getClass(), targetClass);
|
return getConversionExecutor(source.getClass(), targetClass).execute(source);
|
||||||
return conversionExecutor.execute(source);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public Object executeConversion(String converterId, Object source, Class targetClass)
|
public Object executeConversion(String converterId, Object source, Class targetClass)
|
||||||
throws ConversionExecutorNotFoundException, ConversionException {
|
throws ConversionExecutorNotFoundException, ConversionException {
|
||||||
ConversionExecutor conversionExecutor = getConversionExecutor(converterId, source.getClass(), targetClass);
|
return getConversionExecutor(converterId, source.getClass(), targetClass).execute(source);
|
||||||
return conversionExecutor.execute(source);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public ConversionExecutor getConversionExecutor(Class sourceClass, Class targetClass)
|
public ConversionExecutor getConversionExecutor(Class sourceClass, Class targetClass)
|
||||||
|
|
@ -188,9 +219,156 @@ public class GenericConversionService implements ConversionService {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public ConversionExecutor getConversionExecutor(String converterId, Class sourceClass, Class targetClass)
|
public ConversionExecutor getConversionExecutor(String id, Class sourceClass, Class targetClass)
|
||||||
throws ConversionExecutorNotFoundException {
|
throws ConversionExecutorNotFoundException {
|
||||||
throw new UnsupportedOperationException("Not yet implemented");
|
Assert.hasText(id, "The id of the custom converter is required");
|
||||||
|
Assert.notNull(sourceClass, "The source class to convert from is required");
|
||||||
|
Assert.notNull(targetClass, "The target class to convert to is required");
|
||||||
|
Converter converter = (Converter) customConverters.get(id);
|
||||||
|
if (converter == null) {
|
||||||
|
if (parent != null) {
|
||||||
|
return parent.getConversionExecutor(id, sourceClass, targetClass);
|
||||||
|
} else {
|
||||||
|
throw new ConversionExecutorNotFoundException(sourceClass, targetClass,
|
||||||
|
"No custom Converter found with id '" + id + "' for converting from sourceClass ["
|
||||||
|
+ sourceClass.getName() + "] to targetClass [" + targetClass.getName() + "]");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
sourceClass = convertToWrapperClassIfNecessary(sourceClass);
|
||||||
|
targetClass = convertToWrapperClassIfNecessary(targetClass);
|
||||||
|
// TODO Not optimal getting this each time
|
||||||
|
List typeInfo = getRequiredTypeInfo(converter);
|
||||||
|
Class converterSourceClass = (Class) typeInfo.get(0);
|
||||||
|
Class converterTargetClass = (Class) typeInfo.get(1);
|
||||||
|
if (sourceClass.isArray()) {
|
||||||
|
Class sourceComponentType = sourceClass.getComponentType();
|
||||||
|
if (targetClass.isArray()) {
|
||||||
|
Class targetComponentType = targetClass.getComponentType();
|
||||||
|
if (converterSourceClass.isAssignableFrom(sourceComponentType)) {
|
||||||
|
if (!converterTargetClass.equals(targetComponentType)) {
|
||||||
|
throw new ConversionExecutorNotFoundException(sourceClass, targetClass,
|
||||||
|
"Custom Converter with id '" + id
|
||||||
|
+ "' cannot convert from an array storing elements of type ["
|
||||||
|
+ sourceComponentType.getName() + "] to an array of storing elements of type ["
|
||||||
|
+ targetComponentType.getName() + "]");
|
||||||
|
}
|
||||||
|
ConversionExecutor elementConverter = new StaticConversionExecutor(sourceComponentType,
|
||||||
|
targetComponentType, converter);
|
||||||
|
return new StaticSuperConversionExecutor(sourceClass, targetClass, new ArrayToArray(
|
||||||
|
elementConverter));
|
||||||
|
} else if (converterTargetClass.isAssignableFrom(sourceComponentType)) {
|
||||||
|
if (!converterSourceClass.equals(targetComponentType)) {
|
||||||
|
throw new ConversionExecutorNotFoundException(sourceClass, targetClass,
|
||||||
|
"Custom Converter with id '" + id
|
||||||
|
+ "' cannot convert from an array storing elements of type ["
|
||||||
|
+ sourceComponentType.getName() + "] to an array of storing elements of type ["
|
||||||
|
+ targetComponentType.getName() + "]");
|
||||||
|
}
|
||||||
|
ConversionExecutor elementConverter = new StaticConversionExecutor(sourceComponentType,
|
||||||
|
targetComponentType, new ReverseConverter(converter));
|
||||||
|
return new StaticSuperConversionExecutor(sourceClass, targetClass, new ArrayToArray(
|
||||||
|
elementConverter));
|
||||||
|
} else {
|
||||||
|
throw new ConversionExecutorNotFoundException(sourceClass, targetClass,
|
||||||
|
"Custom Converter with id '" + id
|
||||||
|
+ "' cannot convert from an array storing elements of type ["
|
||||||
|
+ sourceComponentType.getName() + "] to an array storing elements of type ["
|
||||||
|
+ targetComponentType.getName() + "]");
|
||||||
|
}
|
||||||
|
} else if (Collection.class.isAssignableFrom(targetClass)) {
|
||||||
|
if (!targetClass.isInterface() && Modifier.isAbstract(targetClass.getModifiers())) {
|
||||||
|
throw new IllegalArgumentException("Conversion target class [" + targetClass.getName()
|
||||||
|
+ "] is invalid; cannot convert to abstract collection types--"
|
||||||
|
+ "request an interface or concrete implementation instead");
|
||||||
|
}
|
||||||
|
if (converterSourceClass.isAssignableFrom(sourceComponentType)) {
|
||||||
|
// type erasure has prevented us from getting the concrete type, this is best we can do for now
|
||||||
|
ConversionExecutor elementConverter = new StaticConversionExecutor(sourceComponentType,
|
||||||
|
converterTargetClass, converter);
|
||||||
|
return new StaticSuperConversionExecutor(sourceClass, targetClass, new ArrayToCollection(
|
||||||
|
elementConverter));
|
||||||
|
} else if (converterTargetClass.isAssignableFrom(sourceComponentType)) {
|
||||||
|
ConversionExecutor elementConverter = new StaticConversionExecutor(sourceComponentType,
|
||||||
|
converterSourceClass, new ReverseConverter(converter));
|
||||||
|
return new StaticSuperConversionExecutor(sourceClass, targetClass, new ArrayToCollection(
|
||||||
|
elementConverter));
|
||||||
|
} else {
|
||||||
|
throw new ConversionExecutorNotFoundException(sourceClass, targetClass,
|
||||||
|
"Custom Converter with id '" + id
|
||||||
|
+ "' cannot convert from array an storing elements type ["
|
||||||
|
+ sourceComponentType.getName() + "] to a collection of type ["
|
||||||
|
+ targetClass.getName() + "]");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (targetClass.isArray()) {
|
||||||
|
Class targetComponentType = targetClass.getComponentType();
|
||||||
|
if (Collection.class.isAssignableFrom(sourceClass)) {
|
||||||
|
// type erasure limits us here as well
|
||||||
|
if (converterTargetClass.equals(targetComponentType)) {
|
||||||
|
ConversionExecutor elementConverter = new StaticConversionExecutor(converterSourceClass,
|
||||||
|
targetComponentType, converter);
|
||||||
|
SuperConverter collectionToArray = new ReverseSuperConverter(
|
||||||
|
new ArrayToCollection(elementConverter));
|
||||||
|
return new StaticSuperConversionExecutor(sourceClass, targetClass, collectionToArray);
|
||||||
|
} else if (converterSourceClass.equals(targetComponentType)) {
|
||||||
|
ConversionExecutor elementConverter = new StaticConversionExecutor(converterTargetClass,
|
||||||
|
targetComponentType, new ReverseConverter(converter));
|
||||||
|
SuperConverter collectionToArray = new ReverseSuperConverter(
|
||||||
|
new ArrayToCollection(elementConverter));
|
||||||
|
return new StaticSuperConversionExecutor(sourceClass, targetClass, collectionToArray);
|
||||||
|
} else {
|
||||||
|
throw new ConversionExecutorNotFoundException(sourceClass, targetClass,
|
||||||
|
"Custom Converter with id '" + id + "' cannot convert from collection of type ["
|
||||||
|
+ sourceClass.getName() + "] to an array storing elements of type ["
|
||||||
|
+ targetComponentType.getName() + "]");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (converterSourceClass.isAssignableFrom(sourceClass)) {
|
||||||
|
if (!converterTargetClass.equals(targetComponentType)) {
|
||||||
|
throw new ConversionExecutorNotFoundException(sourceClass, targetClass,
|
||||||
|
"Custom Converter with id '" + id + "' cannot convert from sourceClass ["
|
||||||
|
+ sourceClass.getName() + "] to array holding elements of type ["
|
||||||
|
+ targetComponentType.getName() + "]");
|
||||||
|
}
|
||||||
|
ConversionExecutor elementConverter = new StaticConversionExecutor(sourceClass,
|
||||||
|
targetComponentType, converter);
|
||||||
|
return new StaticSuperConversionExecutor(sourceClass, targetClass, new ObjectToArray(
|
||||||
|
elementConverter));
|
||||||
|
} else if (converterTargetClass.isAssignableFrom(sourceClass)) {
|
||||||
|
if (!converterSourceClass.equals(targetComponentType)) {
|
||||||
|
throw new ConversionExecutorNotFoundException(sourceClass, targetClass,
|
||||||
|
"Custom Converter with id '" + id + "' cannot convert from sourceClass ["
|
||||||
|
+ sourceClass.getName() + "] to array holding elements of type ["
|
||||||
|
+ targetComponentType.getName() + "]");
|
||||||
|
}
|
||||||
|
ConversionExecutor elementConverter = new StaticConversionExecutor(sourceClass,
|
||||||
|
targetComponentType, new ReverseConverter(converter));
|
||||||
|
return new StaticSuperConversionExecutor(sourceClass, targetClass, new ObjectToArray(
|
||||||
|
elementConverter));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// TODO look to factor some of this duplicated code here and above out a bit
|
||||||
|
if (converterSourceClass.isAssignableFrom(sourceClass)) {
|
||||||
|
if (!converterTargetClass.equals(targetClass)) {
|
||||||
|
throw new ConversionExecutorNotFoundException(sourceClass, targetClass, "Custom Converter with id '"
|
||||||
|
+ id + "' cannot convert from sourceClass [" + sourceClass.getName() + "] to targetClass ["
|
||||||
|
+ targetClass.getName() + "]");
|
||||||
|
}
|
||||||
|
return new StaticConversionExecutor(sourceClass, targetClass, converter);
|
||||||
|
} else if (converterTargetClass.isAssignableFrom(sourceClass)) {
|
||||||
|
if (!converterSourceClass.equals(targetClass)) {
|
||||||
|
throw new ConversionExecutorNotFoundException(sourceClass, targetClass, "Custom Converter with id '"
|
||||||
|
+ id + "' cannot convert from sourceClass [" + sourceClass.getName() + "] to targetClass ["
|
||||||
|
+ targetClass.getName() + "]");
|
||||||
|
}
|
||||||
|
return new StaticConversionExecutor(sourceClass, targetClass, new ReverseConverter(converter));
|
||||||
|
} else {
|
||||||
|
throw new ConversionExecutorNotFoundException(sourceClass, targetClass, "Custom Converter with id '" + id
|
||||||
|
+ "' cannot convert from sourceClass [" + sourceClass.getName() + "] to targetClass ["
|
||||||
|
+ targetClass.getName() + "]");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public Class getClassForAlias(String name) throws IllegalArgumentException {
|
public Class getClassForAlias(String name) throws IllegalArgumentException {
|
||||||
|
|
@ -208,25 +386,32 @@ public class GenericConversionService implements ConversionService {
|
||||||
|
|
||||||
// internal helpers
|
// internal helpers
|
||||||
|
|
||||||
private List getTypeInfo(Object converter) {
|
private List getRequiredTypeInfo(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) {
|
||||||
Type[] genericInterfaces = converter.getClass().getGenericInterfaces();
|
Type[] genericInterfaces = classToIntrospect.getGenericInterfaces();
|
||||||
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())) {
|
|| SuperConverter.class.isAssignableFrom((Class) parameterizedInterface.getRawType())) {
|
||||||
Type s = parameterizedInterface.getActualTypeArguments()[0];
|
Class s = getParameterClass(parameterizedInterface.getActualTypeArguments()[0], converter
|
||||||
Type t = parameterizedInterface.getActualTypeArguments()[1];
|
.getClass());
|
||||||
|
Class t = getParameterClass(parameterizedInterface.getActualTypeArguments()[1], converter
|
||||||
|
.getClass());
|
||||||
typeInfo.add(getParameterClass(s, converter.getClass()));
|
typeInfo.add(getParameterClass(s, converter.getClass()));
|
||||||
typeInfo.add(getParameterClass(t, converter.getClass()));
|
typeInfo.add(getParameterClass(t, converter.getClass()));
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
classToIntrospect = classToIntrospect.getSuperclass();
|
classToIntrospect = classToIntrospect.getSuperclass();
|
||||||
}
|
}
|
||||||
|
if (typeInfo.size() != 2) {
|
||||||
|
throw new IllegalArgumentException("Unable to extract source and target class arguments from Converter ["
|
||||||
|
+ converter.getClass().getName() + "]; does the Converter specify the <S, T> generic types?");
|
||||||
|
}
|
||||||
return typeInfo;
|
return typeInfo;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -237,8 +422,8 @@ public class GenericConversionService implements ConversionService {
|
||||||
if (parameterType instanceof Class) {
|
if (parameterType instanceof Class) {
|
||||||
return (Class) parameterType;
|
return (Class) parameterType;
|
||||||
}
|
}
|
||||||
// when would this happen?
|
throw new IllegalArgumentException("Unable to obtain the java.lang.Class for parameterType [" + parameterType
|
||||||
return null;
|
+ "] on Converter [" + converterClass.getName() + "]");
|
||||||
}
|
}
|
||||||
|
|
||||||
private Map getSourceMap(Class sourceClass) {
|
private Map getSourceMap(Class sourceClass) {
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,15 @@
|
||||||
package org.springframework.core.convert.converter;
|
package org.springframework.core.convert.service;
|
||||||
|
|
||||||
|
import org.springframework.core.convert.converter.Converter;
|
||||||
|
import org.springframework.core.convert.converter.SuperConverter;
|
||||||
|
import org.springframework.core.convert.converter.SuperTwoWayConverter;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Adapts a {@link SuperTwoWayConverter} to the {@link Converter} interface in a type safe way. This adapter is useful
|
* 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.
|
* for applying more general {@link SuperConverter} logic to a specific source/target class pair.
|
||||||
*/
|
*/
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
public class SuperTwoWayConverterConverter<S, T> implements Converter<S, T> {
|
class SuperTwoWayConverterConverter<S, T> implements Converter<S, T> {
|
||||||
|
|
||||||
private SuperTwoWayConverter superConverter;
|
private SuperTwoWayConverter superConverter;
|
||||||
|
|
||||||
|
|
@ -13,8 +17,7 @@ public class SuperTwoWayConverterConverter<S, T> implements Converter<S, T> {
|
||||||
|
|
||||||
private Class targetClass;
|
private Class targetClass;
|
||||||
|
|
||||||
public <SCS, SCT> SuperTwoWayConverterConverter(SuperTwoWayConverter<SCS, SCT> superConverter,
|
public SuperTwoWayConverterConverter(SuperTwoWayConverter superConverter, Class sourceClass, Class targetClass) {
|
||||||
Class<? extends SCS> sourceClass, Class<? extends SCT> targetClass) {
|
|
||||||
this.superConverter = superConverter;
|
this.superConverter = superConverter;
|
||||||
this.sourceClass = sourceClass;
|
this.sourceClass = sourceClass;
|
||||||
this.targetClass = targetClass;
|
this.targetClass = targetClass;
|
||||||
|
|
@ -1,5 +1,21 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2004-2008 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;
|
package org.springframework.core.convert.service;
|
||||||
|
|
||||||
|
import java.security.Principal;
|
||||||
import java.util.AbstractList;
|
import java.util.AbstractList;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
|
|
@ -8,6 +24,7 @@ import java.util.List;
|
||||||
|
|
||||||
import junit.framework.TestCase;
|
import junit.framework.TestCase;
|
||||||
|
|
||||||
|
import org.springframework.core.convert.ConversionException;
|
||||||
import org.springframework.core.convert.ConversionExecutionException;
|
import org.springframework.core.convert.ConversionExecutionException;
|
||||||
import org.springframework.core.convert.ConversionExecutor;
|
import org.springframework.core.convert.ConversionExecutor;
|
||||||
import org.springframework.core.convert.ConversionExecutorNotFoundException;
|
import org.springframework.core.convert.ConversionExecutorNotFoundException;
|
||||||
|
|
@ -15,7 +32,6 @@ import org.springframework.core.convert.converter.Converter;
|
||||||
import org.springframework.core.convert.converter.NumberToNumber;
|
import org.springframework.core.convert.converter.NumberToNumber;
|
||||||
import org.springframework.core.convert.converter.StringToEnum;
|
import org.springframework.core.convert.converter.StringToEnum;
|
||||||
import org.springframework.core.convert.converter.StringToInteger;
|
import org.springframework.core.convert.converter.StringToInteger;
|
||||||
import org.springframework.core.convert.service.GenericConversionService;
|
|
||||||
|
|
||||||
public class GenericConversionServiceTests extends TestCase {
|
public class GenericConversionServiceTests extends TestCase {
|
||||||
|
|
||||||
|
|
@ -250,4 +266,257 @@ public class GenericConversionServiceTests extends TestCase {
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void testAddConverterNoSourceTargetClassInfoAvailable() {
|
||||||
|
try {
|
||||||
|
service.addConverter(new Converter() {
|
||||||
|
public Object convert(Object source) throws Exception {
|
||||||
|
return source;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Object convertBack(Object target) throws Exception {
|
||||||
|
return target;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
fail("Should have failed");
|
||||||
|
} catch (IllegalArgumentException e) {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testCustomConverterConversionForwardIndex() {
|
||||||
|
service.addConverter("princy", new CustomTwoWayConverter());
|
||||||
|
ConversionExecutor executor = service.getConversionExecutor("princy", String.class, Principal.class);
|
||||||
|
assertEquals("keith", ((Principal) executor.execute("keith")).getName());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testCustomConverterConversionReverseIndex() {
|
||||||
|
service.addConverter("princy", new CustomTwoWayConverter());
|
||||||
|
ConversionExecutor executor = service.getConversionExecutor("princy", Principal.class, String.class);
|
||||||
|
assertEquals("keith", executor.execute(new Principal() {
|
||||||
|
public String getName() {
|
||||||
|
return "keith";
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testCustomConverterConversionForSameType() {
|
||||||
|
service.addConverter("trimmer", new Trimmer());
|
||||||
|
ConversionExecutor executor = service.getConversionExecutor("trimmer", String.class, String.class);
|
||||||
|
assertEquals("a string", executor.execute("a string "));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testCustomConverterLookupNotCompatibleSource() {
|
||||||
|
service.addConverter("trimmer", new Trimmer());
|
||||||
|
try {
|
||||||
|
service.getConversionExecutor("trimmer", Object.class, String.class);
|
||||||
|
fail("Should have failed");
|
||||||
|
} catch (ConversionException e) {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testCustomConverterLookupNotCompatibleTarget() {
|
||||||
|
service.addConverter("trimmer", new Trimmer());
|
||||||
|
try {
|
||||||
|
service.getConversionExecutor("trimmer", String.class, Object.class);
|
||||||
|
} catch (ConversionException e) {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testCustomConverterLookupNotCompatibleTargetReverse() {
|
||||||
|
service.addConverter("princy", new CustomTwoWayConverter());
|
||||||
|
try {
|
||||||
|
service.getConversionExecutor("princy", Principal.class, Integer.class);
|
||||||
|
} catch (ConversionException e) {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testCustomConverterConversionArrayToArray() {
|
||||||
|
service.addConverter("princy", new CustomTwoWayConverter());
|
||||||
|
ConversionExecutor executor = service.getConversionExecutor("princy", String[].class, Principal[].class);
|
||||||
|
Principal[] p = (Principal[]) executor.execute(new String[] { "princy1", "princy2" });
|
||||||
|
assertEquals("princy1", p[0].getName());
|
||||||
|
assertEquals("princy2", p[1].getName());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testCustomConverterConversionArrayToArrayReverse() {
|
||||||
|
service.addConverter("princy", new CustomTwoWayConverter());
|
||||||
|
ConversionExecutor executor = service.getConversionExecutor("princy", Principal[].class, String[].class);
|
||||||
|
final Principal princy1 = new Principal() {
|
||||||
|
public String getName() {
|
||||||
|
return "princy1";
|
||||||
|
}
|
||||||
|
};
|
||||||
|
final Principal princy2 = new Principal() {
|
||||||
|
public String getName() {
|
||||||
|
return "princy2";
|
||||||
|
}
|
||||||
|
};
|
||||||
|
String[] p = (String[]) executor.execute(new Principal[] { princy1, princy2 });
|
||||||
|
assertEquals("princy1", p[0]);
|
||||||
|
assertEquals("princy2", p[1]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testCustomConverterLookupArrayToArrayBogusSource() {
|
||||||
|
service.addConverter("princy", new CustomTwoWayConverter());
|
||||||
|
try {
|
||||||
|
service.getConversionExecutor("princy", Integer[].class, Principal[].class);
|
||||||
|
fail("Should have failed");
|
||||||
|
} catch (ConversionExecutorNotFoundException e) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testCustomConverterLookupArrayToArrayBogusTarget() {
|
||||||
|
service.addConverter("princy", new CustomTwoWayConverter());
|
||||||
|
try {
|
||||||
|
service.getConversionExecutor("princy", Principal[].class, Integer[].class);
|
||||||
|
} catch (ConversionExecutorNotFoundException e) {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testCustomConverterConversionArrayToCollection() {
|
||||||
|
service.addConverter("princy", new CustomTwoWayConverter());
|
||||||
|
ConversionExecutor executor = service.getConversionExecutor("princy", String[].class, List.class);
|
||||||
|
List list = (List) executor.execute(new String[] { "princy1", "princy2" });
|
||||||
|
assertEquals("princy1", ((Principal) list.get(0)).getName());
|
||||||
|
assertEquals("princy2", ((Principal) list.get(1)).getName());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testCustomConverterConversionArrayToCollectionReverse() {
|
||||||
|
service.addConverter("princy", new CustomTwoWayConverter());
|
||||||
|
ConversionExecutor executor = service.getConversionExecutor("princy", Principal[].class, List.class);
|
||||||
|
final Principal princy1 = new Principal() {
|
||||||
|
public String getName() {
|
||||||
|
return "princy1";
|
||||||
|
}
|
||||||
|
};
|
||||||
|
final Principal princy2 = new Principal() {
|
||||||
|
public String getName() {
|
||||||
|
return "princy2";
|
||||||
|
}
|
||||||
|
};
|
||||||
|
List p = (List) executor.execute(new Principal[] { princy1, princy2 });
|
||||||
|
assertEquals("princy1", p.get(0));
|
||||||
|
assertEquals("princy2", p.get(1));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testCustomConverterLookupArrayToCollectionBogusSource() {
|
||||||
|
service.addConverter("princy", new CustomTwoWayConverter());
|
||||||
|
try {
|
||||||
|
service.getConversionExecutor("princy", Integer[].class, List.class);
|
||||||
|
fail("Should have failed");
|
||||||
|
} catch (ConversionExecutorNotFoundException e) {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testCustomConverterLookupCollectionToArray() {
|
||||||
|
service.addConverter("princy", new CustomTwoWayConverter());
|
||||||
|
ConversionExecutor executor = service.getConversionExecutor("princy", List.class, Principal[].class);
|
||||||
|
List princyList = new ArrayList();
|
||||||
|
princyList.add("princy1");
|
||||||
|
princyList.add("princy2");
|
||||||
|
Principal[] p = (Principal[]) executor.execute(princyList);
|
||||||
|
assertEquals("princy1", p[0].getName());
|
||||||
|
assertEquals("princy2", p[1].getName());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testCustomConverterLookupCollectionToArrayReverse() {
|
||||||
|
service.addConverter("princy", new CustomTwoWayConverter());
|
||||||
|
ConversionExecutor executor = service.getConversionExecutor("princy", List.class, String[].class);
|
||||||
|
final Principal princy1 = new Principal() {
|
||||||
|
public String getName() {
|
||||||
|
return "princy1";
|
||||||
|
}
|
||||||
|
};
|
||||||
|
final Principal princy2 = new Principal() {
|
||||||
|
public String getName() {
|
||||||
|
return "princy2";
|
||||||
|
}
|
||||||
|
};
|
||||||
|
List princyList = new ArrayList();
|
||||||
|
princyList.add(princy1);
|
||||||
|
princyList.add(princy2);
|
||||||
|
String[] p = (String[]) executor.execute(princyList);
|
||||||
|
assertEquals("princy1", p[0]);
|
||||||
|
assertEquals("princy2", p[1]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testtestCustomConverterLookupCollectionToArrayBogusTarget() {
|
||||||
|
service.addConverter("princy", new CustomTwoWayConverter());
|
||||||
|
try {
|
||||||
|
service.getConversionExecutor("princy", List.class, Integer[].class);
|
||||||
|
fail("Should have failed");
|
||||||
|
} catch (ConversionExecutorNotFoundException e) {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testCustomConverterConversionObjectToArray() {
|
||||||
|
service.addConverter("princy", new CustomTwoWayConverter());
|
||||||
|
ConversionExecutor executor = service.getConversionExecutor("princy", String.class, Principal[].class);
|
||||||
|
Principal[] p = (Principal[]) executor.execute("princy1");
|
||||||
|
assertEquals("princy1", p[0].getName());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testCustomConverterConversionObjectToArrayReverse() {
|
||||||
|
service.addConverter("princy", new CustomTwoWayConverter());
|
||||||
|
ConversionExecutor executor = service.getConversionExecutor("princy", Principal.class, String[].class);
|
||||||
|
final Principal princy1 = new Principal() {
|
||||||
|
public String getName() {
|
||||||
|
return "princy1";
|
||||||
|
}
|
||||||
|
};
|
||||||
|
String[] p = (String[]) executor.execute(princy1);
|
||||||
|
assertEquals("princy1", p[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testCustomConverterLookupObjectToArrayBogusSource() {
|
||||||
|
service.addConverter("princy", new CustomTwoWayConverter());
|
||||||
|
try {
|
||||||
|
service.getConversionExecutor("princy", Integer.class, Principal[].class);
|
||||||
|
fail("Should have failed");
|
||||||
|
} catch (ConversionExecutorNotFoundException e) {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class CustomTwoWayConverter implements Converter<String, Principal> {
|
||||||
|
|
||||||
|
public Principal convert(final String source) throws Exception {
|
||||||
|
return new Principal() {
|
||||||
|
public String getName() {
|
||||||
|
return (String) source;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public String convertBack(Principal target) throws Exception {
|
||||||
|
return ((Principal) target).getName();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class Trimmer implements Converter<String, String> {
|
||||||
|
|
||||||
|
public String convert(String source) throws Exception {
|
||||||
|
return ((String) source).trim();
|
||||||
|
}
|
||||||
|
|
||||||
|
public String convertBack(String target) throws Exception {
|
||||||
|
throw new UnsupportedOperationException("Will never run");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testSuperTwoWayConverterConverterAdaption() {
|
||||||
|
// this fails at the moment
|
||||||
|
//service.addConverter(GenericConversionService.converterFor(String.class, FooEnum.class, new StringToEnum()));
|
||||||
|
//assertEquals(FooEnum.BAR, service.executeConversion("BAR", FooEnum.class));
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
Loading…
Reference in New Issue