improved conversion system logging, collection converter simplification/polish, several optimizations, annotation-driven formatting caching

git-svn-id: https://src.springframework.org/svn/spring-framework/trunk@3255 50f2f4bb-b051-0410-bef5-90022cba6387
This commit is contained in:
Keith Donald 2010-04-17 04:43:28 +00:00
parent efc8a513d1
commit eca3e5d0b8
23 changed files with 263 additions and 379 deletions

View File

@ -17,12 +17,14 @@
package org.springframework.format.support;
import java.lang.annotation.Annotation;
import java.text.ParseException;
import java.util.Collections;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import org.springframework.context.i18n.LocaleContextHolder;
import org.springframework.core.GenericTypeResolver;
import org.springframework.core.convert.ConversionFailedException;
import org.springframework.core.convert.ConversionService;
import org.springframework.core.convert.TypeDescriptor;
import org.springframework.core.convert.converter.ConditionalGenericConverter;
@ -66,6 +68,9 @@ public class FormattingConversionService extends GenericConversionService implem
}
Set<Class<?>> fieldTypes = annotationFormatterFactory.getFieldTypes();
final Map<FieldFormatterKey, GenericConverter> cachedPrinters = new ConcurrentHashMap<FieldFormatterKey, GenericConverter>();
final Map<FieldFormatterKey, GenericConverter> cachedParsers = new ConcurrentHashMap<FieldFormatterKey, GenericConverter>();
for (final Class<?> fieldType : fieldTypes) {
addConverter(new ConditionalGenericConverter() {
public Set<ConvertiblePair> getConvertibleTypes() {
@ -75,8 +80,14 @@ public class FormattingConversionService extends GenericConversionService implem
return (sourceType.getAnnotation(annotationType) != null);
}
public Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType) {
Printer<?> printer = annotationFormatterFactory.getPrinter(sourceType.getAnnotation(annotationType), sourceType.getType());
return new PrinterConverter(fieldType, printer, FormattingConversionService.this).convert(source, sourceType, targetType);
FieldFormatterKey key = new FieldFormatterKey(sourceType.getAnnotation(annotationType), fieldType);
GenericConverter converter = cachedPrinters.get(key);
if (converter == null) {
Printer<?> printer = annotationFormatterFactory.getPrinter(key.getAnnotation(), key.getFieldType());
converter = new PrinterConverter(fieldType, printer, FormattingConversionService.this);
cachedPrinters.put(key, converter);
}
return converter.convert(source, sourceType, targetType);
}
public String toString() {
return "@" + annotationType.getName() + " " + fieldType.getName() + " -> " +
@ -91,8 +102,14 @@ public class FormattingConversionService extends GenericConversionService implem
return (targetType.getAnnotation(annotationType) != null);
}
public Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType) {
Parser<?> parser = annotationFormatterFactory.getParser(targetType.getAnnotation(annotationType), targetType.getType());
return new ParserConverter(fieldType, parser, FormattingConversionService.this).convert(source, sourceType, targetType);
FieldFormatterKey key = new FieldFormatterKey(targetType.getAnnotation(annotationType), fieldType);
GenericConverter converter = cachedParsers.get(key);
if (converter == null) {
Parser<?> printer = annotationFormatterFactory.getParser(key.getAnnotation(), key.getFieldType());
converter = new ParserConverter(fieldType, printer, FormattingConversionService.this);
cachedParsers.put(key, converter);
}
return converter.convert(source, sourceType, targetType);
}
public String toString() {
return String.class.getName() + " -> @" + annotationType.getName() + " " +
@ -102,6 +119,38 @@ public class FormattingConversionService extends GenericConversionService implem
}
}
private static final class FieldFormatterKey {
private final Annotation annotation;
private final Class<?> fieldType;
public FieldFormatterKey(Annotation annotation, Class<?> fieldType) {
this.annotation = annotation;
this.fieldType = fieldType;
}
public Annotation getAnnotation() {
return annotation;
}
public Class<?> getFieldType() {
return fieldType;
}
public boolean equals(Object o) {
if (!(o instanceof FieldFormatterKey)) {
return false;
}
FieldFormatterKey key = (FieldFormatterKey) o;
return this.annotation.equals(key.annotation) && this.fieldType.equals(key.fieldType);
}
public int hashCode() {
return this.annotation.hashCode() + this.fieldType.hashCode();
}
}
private static class PrinterConverter implements GenericConverter {
@ -142,7 +191,6 @@ public class FormattingConversionService extends GenericConversionService implem
}
}
private static class ParserConverter implements GenericConverter {
private Class<?> fieldType;
@ -166,17 +214,17 @@ public class FormattingConversionService extends GenericConversionService implem
if (text == null || text.length() == 0) {
return null;
}
Object result;
try {
Object result = this.parser.parse(text, LocaleContextHolder.getLocale());
TypeDescriptor resultType = TypeDescriptor.valueOf(result.getClass());
if (!resultType.isAssignableTo(targetType)) {
result = this.conversionService.convert(result, resultType, targetType);
}
return result;
result = this.parser.parse(text, LocaleContextHolder.getLocale());
} catch (ParseException e) {
throw new IllegalArgumentException("Unable to parse '" + text + "'", e);
}
catch (Exception ex) {
throw new ConversionFailedException(sourceType, targetType, source, ex);
TypeDescriptor resultType = TypeDescriptor.valueOf(result.getClass());
if (!resultType.isAssignableTo(targetType)) {
result = this.conversionService.convert(result, resultType, targetType);
}
return result;
}
public String toString() {

View File

@ -231,7 +231,7 @@ public class TypeDescriptor {
* Is this type a {@link Collection} type?
*/
public boolean isCollection() {
return isTypeAssignableTo(Collection.class);
return Collection.class.isAssignableFrom(getType());
}
/**
@ -257,6 +257,15 @@ public class TypeDescriptor {
return TypeDescriptor.valueOf(getElementType());
}
/**
* Return the element type as a type descriptor; if the element type is null (cannot be determined), the type descriptor is derived from the element argument.
* @param element the element
* @return the element type descriptor
*/
public TypeDescriptor getElementTypeDescriptor(Object element) {
return getElementType() != null ? getElementTypeDescriptor() : TypeDescriptor.forObject(element);
}
/**
* Create a copy of this type descriptor, preserving the context information
* but exposing the specified element type (e.g. an array/collection element).
@ -280,7 +289,7 @@ public class TypeDescriptor {
* Is this type a {@link Map} type?
*/
public boolean isMap() {
return isTypeAssignableTo(Map.class);
return Map.class.isAssignableFrom(getType());
}
/**
@ -355,6 +364,15 @@ public class TypeDescriptor {
return TypeDescriptor.valueOf(getMapKeyType());
}
/**
* Return the map key type as a type descriptor; if the key type is null (cannot be determined), the type descriptor is derived from the key argument.
* @param key the key
* @return the map key type descriptor
*/
public TypeDescriptor getMapKeyTypeDescriptor(Object key) {
return getMapKeyType() != null ? getMapKeyTypeDescriptor() : TypeDescriptor.forObject(key);
}
/**
* Returns map value type as a type descriptor.
*/
@ -362,6 +380,15 @@ public class TypeDescriptor {
return TypeDescriptor.valueOf(getMapValueType());
}
/**
* Return the map value type as a type descriptor; if the value type is null (cannot be determined), the type descriptor is derived from the value argument.
* @param value the value
* @return the map value type descriptor
*/
public TypeDescriptor getMapValueTypeDescriptor(Object value) {
return getMapValueType() != null ? getMapValueTypeDescriptor() : TypeDescriptor.forObject(value);
}
/**
* Obtain the annotations associated with the wrapped parameter/field, if any.
*/
@ -374,8 +401,6 @@ public class TypeDescriptor {
}
else if (this.methodParameter != null) {
if (this.methodParameter.getParameterIndex() < 0) {
// The best we can do for return type metadata is to expose
// method-level annotations when the target is the return type...
return this.methodParameter.getMethodAnnotations();
}
else {
@ -400,76 +425,22 @@ public class TypeDescriptor {
}
/**
* Returns true if an object this type can be assigned to a reference of given targetType.
* Returns true if an object of this type can be assigned to a reference of given targetType.
* @param targetType the target type
* @return true if this type is assignable to the target
*/
public boolean isAssignableTo(TypeDescriptor targetType) {
Class targetClass = targetType.getType();
if (!isTypeAssignableTo(targetClass)) {
return false;
if (this == TypeDescriptor.NULL || targetType == TypeDescriptor.NULL) {
return true;
}
if (targetClass != null) {
if (Collection.class.isAssignableFrom(targetClass)) {
Class<?> elementType = targetType.getCollectionElementType();
if (elementType != null) {
Class<?> sourceElementType = getCollectionElementType();
if (sourceElementType == null || !elementType.isAssignableFrom(sourceElementType)) {
return false;
}
}
}
else if (Map.class.isAssignableFrom(targetClass)) {
Class<?> keyType = targetType.getMapKeyType();
if (keyType != null) {
Class<?> sourceKeyType = getMapKeyType();
if (sourceKeyType == null || !keyType.isAssignableFrom(sourceKeyType)) {
return false;
}
}
Class<?> valueType = targetType.getMapValueType();
if (valueType != null) {
Class<?> sourceValueType = getMapValueType();
if (sourceValueType == null || !valueType.isAssignableFrom(sourceValueType)) {
return false;
}
}
}
}
return true;
return targetType.getObjectType().isAssignableFrom(getObjectType());
}
/**
* A textual representation of the type descriptor (eg. Map<String,Foo>) for use in messages
*/
public String asString() {
StringBuffer stringValue = new StringBuffer();
if (isArray()) {
// TODO should properly handle multi dimensional arrays
stringValue.append(getArrayComponentType().getName()).append("[]");
}
else {
Class<?> clazz = getType();
if (clazz == null) {
return "null";
}
stringValue.append(clazz.getName());
if (isCollection()) {
Class<?> collectionType = getCollectionElementType();
if (collectionType != null) {
stringValue.append("<").append(collectionType.getName()).append(">");
}
}
else if (isMap()) {
Class<?> keyType = getMapKeyType();
Class<?> valType = getMapValueType();
if (keyType != null && valType != null) {
stringValue.append("<").append(keyType.getName()).append(",");
stringValue.append(valType).append(">");
}
}
}
return stringValue.toString();
return toString();
}
public String toString() {
@ -484,6 +455,14 @@ public class TypeDescriptor {
builder.append("@").append(ann.annotationType().getName()).append(' ');
}
builder.append(ClassUtils.getQualifiedName(getType()));
if (isMap()) {
Class<?> mapKeyType = getMapKeyType();
Class<?> valueKeyType = getMapValueType();
builder.append("<").append(mapKeyType != null ? ClassUtils.getQualifiedName(mapKeyType) : "?").append(", ").append(valueKeyType != null ? ClassUtils.getQualifiedName(valueKeyType) : "?").append(">");
} else if (isCollection()) {
Class<?> elementType = getElementType();
builder.append("<").append(elementType != null ? ClassUtils.getQualifiedName(elementType) : "?").append(">");
}
builder.append("]");
return builder.toString();
}
@ -521,12 +500,6 @@ public class TypeDescriptor {
}
}
private boolean isTypeAssignableTo(Class<?> clazz) {
Class<?> type = getType();
return (type != null && (clazz == null || ClassUtils.isAssignable(clazz, type)));
}
// static factory methods
/**
@ -551,7 +524,7 @@ public class TypeDescriptor {
if (object == null) {
return NULL;
}
else if (object instanceof Collection || object instanceof Map) {
else if (object instanceof Collection<?> || object instanceof Map<?, ?>) {
return new TypeDescriptor(object);
}
else {

View File

@ -20,6 +20,7 @@ import java.util.Arrays;
import java.util.Collections;
import java.util.Set;
import org.springframework.core.convert.ConversionService;
import org.springframework.core.convert.TypeDescriptor;
import org.springframework.core.convert.converter.GenericConverter;
import org.springframework.util.ObjectUtils;
@ -35,7 +36,7 @@ final class ArrayToArrayConverter implements GenericConverter {
private final CollectionToArrayConverter helperConverter;
public ArrayToArrayConverter(GenericConversionService conversionService) {
public ArrayToArrayConverter(ConversionService conversionService) {
this.helperConverter = new CollectionToArrayConverter(conversionService);
}

View File

@ -22,27 +22,25 @@ import java.util.Collections;
import java.util.Set;
import org.springframework.core.CollectionFactory;
import org.springframework.core.convert.ConverterNotFoundException;
import org.springframework.core.convert.ConversionService;
import org.springframework.core.convert.TypeDescriptor;
import org.springframework.core.convert.converter.ConditionalGenericConverter;
import org.springframework.core.convert.converter.GenericConverter;
/**
* Converts an Array to a Collection.
*
* <p>First, creates a new Collection of the requested targetType.
* Then adds each array element to the target collection.
* Will perform an element conversion from the source component type
* to the collection's parameterized type if necessary.
* Will perform an element conversion from the source component type to the collection's parameterized type if necessary.
*
* @author Keith Donald
* @since 3.0
*/
final class ArrayToCollectionConverter implements ConditionalGenericConverter {
private final GenericConversionService conversionService;
private final ConversionService conversionService;
public ArrayToCollectionConverter(GenericConversionService conversionService) {
public ArrayToCollectionConverter(ConversionService conversionService) {
this.conversionService = conversionService;
}
@ -57,30 +55,16 @@ final class ArrayToCollectionConverter implements ConditionalGenericConverter {
@SuppressWarnings("unchecked")
public Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType) {
if (source == null) {
return this.conversionService.convertNullSource(sourceType, targetType);
return null;
}
int length = Array.getLength(source);
Collection collection = CollectionFactory.createCollection(targetType.getType(), length);
TypeDescriptor sourceElementType = sourceType.getElementTypeDescriptor();
TypeDescriptor targetElementType = targetType.getElementTypeDescriptor();
if (targetElementType == TypeDescriptor.NULL || sourceElementType.isAssignableTo(targetElementType)) {
for (int i = 0; i < length; i++) {
collection.add(Array.get(source, i));
}
Collection target = CollectionFactory.createCollection(targetType.getType(), length);
for (int i = 0; i < length; i++) {
Object sourceElement = Array.get(source, i);
Object targetElement = this.conversionService.convert(sourceElement, sourceType.getElementTypeDescriptor(), targetType.getElementTypeDescriptor(sourceElement));
target.add(targetElement);
}
else {
GenericConverter converter = this.conversionService.getConverter(sourceElementType, targetElementType);
if (converter == null) {
throw new ConverterNotFoundException(sourceElementType, targetElementType);
}
for (int i = 0; i < length; i++) {
Object sourceElement = Array.get(source, i);
Object targetElement = ConversionUtils.invokeConverter(
converter, sourceElement, sourceElementType, targetElementType);
collection.add(targetElement);
}
}
return collection;
return target;
}
}

View File

@ -20,6 +20,7 @@ import java.util.Arrays;
import java.util.Collections;
import java.util.Set;
import org.springframework.core.convert.ConversionService;
import org.springframework.core.convert.TypeDescriptor;
import org.springframework.core.convert.converter.ConditionalGenericConverter;
import org.springframework.util.ObjectUtils;
@ -35,7 +36,7 @@ final class ArrayToObjectConverter implements ConditionalGenericConverter {
private final CollectionToObjectConverter helperConverter;
public ArrayToObjectConverter(GenericConversionService conversionService) {
public ArrayToObjectConverter(ConversionService conversionService) {
this.helperConverter = new CollectionToObjectConverter(conversionService);
}

View File

@ -20,6 +20,7 @@ import java.util.Arrays;
import java.util.Collections;
import java.util.Set;
import org.springframework.core.convert.ConversionService;
import org.springframework.core.convert.TypeDescriptor;
import org.springframework.core.convert.converter.ConditionalGenericConverter;
import org.springframework.util.ObjectUtils;
@ -35,7 +36,7 @@ final class ArrayToStringConverter implements ConditionalGenericConverter {
private final CollectionToStringConverter helperConverter;
public ArrayToStringConverter(GenericConversionService conversionService) {
public ArrayToStringConverter(ConversionService conversionService) {
this.helperConverter = new CollectionToStringConverter(conversionService);
}

View File

@ -19,13 +19,11 @@ package org.springframework.core.convert.support;
import java.lang.reflect.Array;
import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
import java.util.Set;
import org.springframework.core.convert.ConverterNotFoundException;
import org.springframework.core.convert.ConversionService;
import org.springframework.core.convert.TypeDescriptor;
import org.springframework.core.convert.converter.ConditionalGenericConverter;
import org.springframework.core.convert.converter.GenericConverter;
/**
* Converts a Collection to an array.
@ -40,9 +38,9 @@ import org.springframework.core.convert.converter.GenericConverter;
*/
final class CollectionToArrayConverter implements ConditionalGenericConverter {
private final GenericConversionService conversionService;
private final ConversionService conversionService;
public CollectionToArrayConverter(GenericConversionService conversionService) {
public CollectionToArrayConverter(ConversionService conversionService) {
this.conversionService = conversionService;
}
@ -56,32 +54,14 @@ final class CollectionToArrayConverter implements ConditionalGenericConverter {
public Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType) {
if (source == null) {
return this.conversionService.convertNullSource(sourceType, targetType);
}
return null;
}
Collection<?> sourceCollection = (Collection<?>) source;
TypeDescriptor sourceElementType = sourceType.getElementTypeDescriptor();
if (sourceElementType == TypeDescriptor.NULL) {
sourceElementType = ConversionUtils.getElementType(sourceCollection);
}
TypeDescriptor targetElementType = targetType.getElementTypeDescriptor();
Object array = Array.newInstance(targetElementType.getType(), sourceCollection.size());
Object array = Array.newInstance(targetType.getElementType(), sourceCollection.size());
int i = 0;
if (sourceElementType == TypeDescriptor.NULL || sourceElementType.isAssignableTo(targetElementType)) {
for (Iterator<?> it = sourceCollection.iterator(); it.hasNext(); i++) {
Array.set(array, i, it.next());
}
}
else {
GenericConverter converter = this.conversionService.getConverter(sourceElementType, targetElementType);
if (converter == null) {
throw new ConverterNotFoundException(sourceElementType, targetElementType);
}
for (Iterator<?> it = sourceCollection.iterator(); it.hasNext(); i++) {
Object sourceElement = it.next();
Object targetElement = ConversionUtils.invokeConverter(
converter, sourceElement, sourceElementType, targetElementType);
Array.set(array, i, targetElement);
}
for (Object sourceElement : sourceCollection) {
Object targetElement = this.conversionService.convert(sourceElement, sourceType.getElementTypeDescriptor(sourceElement), targetType.getElementTypeDescriptor());
Array.set(array, i++, targetElement);
}
return array;
}

View File

@ -21,10 +21,9 @@ import java.util.Collections;
import java.util.Set;
import org.springframework.core.CollectionFactory;
import org.springframework.core.convert.ConverterNotFoundException;
import org.springframework.core.convert.ConversionService;
import org.springframework.core.convert.TypeDescriptor;
import org.springframework.core.convert.converter.ConditionalGenericConverter;
import org.springframework.core.convert.converter.GenericConverter;
/**
* Converts from a Collection to another Collection.
@ -39,9 +38,9 @@ import org.springframework.core.convert.converter.GenericConverter;
*/
final class CollectionToCollectionConverter implements ConditionalGenericConverter {
private final GenericConversionService conversionService;
private final ConversionService conversionService;
public CollectionToCollectionConverter(GenericConversionService conversionService) {
public CollectionToCollectionConverter(ConversionService conversionService) {
this.conversionService = conversionService;
}
@ -56,32 +55,13 @@ final class CollectionToCollectionConverter implements ConditionalGenericConvert
@SuppressWarnings("unchecked")
public Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType) {
if (source == null) {
return this.conversionService.convertNullSource(sourceType, targetType);
return null;
}
Collection<?> sourceCollection = (Collection<?>) source;
TypeDescriptor sourceElementType = sourceType.getElementTypeDescriptor();
if (sourceElementType == TypeDescriptor.NULL) {
sourceElementType = ConversionUtils.getElementType(sourceCollection);
}
TypeDescriptor targetElementType = targetType.getElementTypeDescriptor();
if (sourceElementType == TypeDescriptor.NULL || targetElementType == TypeDescriptor.NULL
|| sourceElementType.isAssignableTo(targetElementType)) {
if (sourceType.isAssignableTo(targetType)) {
return sourceCollection;
}
else {
Collection target = CollectionFactory.createCollection(targetType.getType(), sourceCollection.size());
target.addAll(sourceCollection);
return target;
}
}
Collection target = CollectionFactory.createCollection(targetType.getType(), sourceCollection.size());
GenericConverter converter = this.conversionService.getConverter(sourceElementType, targetElementType);
if (converter == null) {
throw new ConverterNotFoundException(sourceElementType, targetElementType);
}
for (Object element : sourceCollection) {
target.add(ConversionUtils.invokeConverter(converter, element, sourceElementType, targetElementType));
for (Object sourceElement : sourceCollection) {
Object targetElement = this.conversionService.convert(sourceElement, sourceType.getElementTypeDescriptor(sourceElement), targetType.getElementTypeDescriptor(sourceElement));
target.add(targetElement);
}
return target;
}

View File

@ -20,10 +20,9 @@ import java.util.Collection;
import java.util.Collections;
import java.util.Set;
import org.springframework.core.convert.ConverterNotFoundException;
import org.springframework.core.convert.ConversionService;
import org.springframework.core.convert.TypeDescriptor;
import org.springframework.core.convert.converter.ConditionalGenericConverter;
import org.springframework.core.convert.converter.GenericConverter;
/**
* Converts a Collection to an Object by returning the first collection element after converting it to the desired targetType.
@ -33,9 +32,9 @@ import org.springframework.core.convert.converter.GenericConverter;
*/
final class CollectionToObjectConverter implements ConditionalGenericConverter {
private final GenericConversionService conversionService;
private final ConversionService conversionService;
public CollectionToObjectConverter(GenericConversionService conversionService) {
public CollectionToObjectConverter(ConversionService conversionService) {
this.conversionService = conversionService;
}
@ -49,29 +48,14 @@ final class CollectionToObjectConverter implements ConditionalGenericConverter {
public Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType) {
if (source == null) {
return this.conversionService.convertNullSource(sourceType, targetType);
return null;
}
Collection<?> sourceCollection = (Collection<?>) source;
if (sourceCollection.size() == 0) {
return null;
}
else {
Object firstElement = sourceCollection.iterator().next();
TypeDescriptor sourceElementType = sourceType.getElementTypeDescriptor();
if (sourceElementType == TypeDescriptor.NULL && firstElement != null) {
sourceElementType = TypeDescriptor.valueOf(firstElement.getClass());
}
if (sourceElementType == TypeDescriptor.NULL || sourceElementType.isAssignableTo(targetType)) {
return firstElement;
}
else {
GenericConverter converter = this.conversionService.getConverter(sourceElementType, targetType);
if (converter == null) {
throw new ConverterNotFoundException(sourceElementType, targetType);
}
return ConversionUtils.invokeConverter(converter, firstElement, sourceElementType, targetType);
}
}
Object firstElement = sourceCollection.iterator().next();
return this.conversionService.convert(firstElement, sourceType.getElementTypeDescriptor(firstElement), targetType);
}
}
}

View File

@ -20,10 +20,9 @@ import java.util.Collection;
import java.util.Collections;
import java.util.Set;
import org.springframework.core.convert.ConverterNotFoundException;
import org.springframework.core.convert.ConversionService;
import org.springframework.core.convert.TypeDescriptor;
import org.springframework.core.convert.converter.ConditionalGenericConverter;
import org.springframework.core.convert.converter.GenericConverter;
/**
* Converts a Collection to a comma-delimited String.
@ -35,9 +34,9 @@ final class CollectionToStringConverter implements ConditionalGenericConverter {
private static final String DELIMITER = ",";
private final GenericConversionService conversionService;
private final ConversionService conversionService;
public CollectionToStringConverter(GenericConversionService conversionService) {
public CollectionToStringConverter(ConversionService conversionService) {
this.conversionService = conversionService;
}
@ -51,48 +50,23 @@ final class CollectionToStringConverter implements ConditionalGenericConverter {
public Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType) {
if (source == null) {
return this.conversionService.convertNullSource(sourceType, targetType);
return null;
}
Collection<?> sourceCollection = (Collection<?>) source;
if (sourceCollection.size() == 0) {
return "";
}
else {
TypeDescriptor sourceElementType = sourceType.getElementTypeDescriptor();
if (sourceElementType == TypeDescriptor.NULL) {
sourceElementType = ConversionUtils.getElementType(sourceCollection);
}
if (sourceElementType == TypeDescriptor.NULL || sourceElementType.isAssignableTo(targetType)) {
StringBuilder string = new StringBuilder();
int i = 0;
for (Object element : sourceCollection) {
if (i > 0) {
string.append(DELIMITER);
}
string.append(element);
i++;
}
return string.toString();
}
else {
GenericConverter converter = this.conversionService.getConverter(sourceElementType, targetType);
if (converter == null) {
throw new ConverterNotFoundException(sourceElementType, targetType);
}
StringBuilder string = new StringBuilder();
int i = 0;
for (Object sourceElement : sourceCollection) {
if (i > 0) {
string.append(DELIMITER);
}
Object targetElement = ConversionUtils.invokeConverter(
converter, sourceElement, sourceElementType, targetType);
string.append(targetElement);
i++;
}
return string.toString();
StringBuilder string = new StringBuilder();
int i = 0;
for (Object sourceElement : sourceCollection) {
if (i > 0) {
string.append(DELIMITER);
}
Object targetElement = this.conversionService.convert(sourceElement, sourceType.getElementTypeDescriptor(sourceElement), targetType);
string.append(targetElement);
i++;
}
return string.toString();
}
}

View File

@ -30,6 +30,7 @@ import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.core.GenericTypeResolver;
import org.springframework.core.convert.ConversionException;
import org.springframework.core.convert.ConversionFailedException;
import org.springframework.core.convert.ConversionService;
import org.springframework.core.convert.ConverterNotFoundException;
@ -39,6 +40,7 @@ import org.springframework.core.convert.converter.Converter;
import org.springframework.core.convert.converter.ConverterFactory;
import org.springframework.core.convert.converter.ConverterRegistry;
import org.springframework.core.convert.converter.GenericConverter;
import org.springframework.core.style.StylerUtils;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
@ -113,29 +115,68 @@ public class GenericConversionService implements ConversionService, ConverterReg
public boolean canConvert(TypeDescriptor sourceType, TypeDescriptor targetType) {
assertNotNull(sourceType, targetType);
if (logger.isDebugEnabled()) {
logger.debug("Checking if I can convert " + sourceType + " to " + targetType);
}
if (sourceType == TypeDescriptor.NULL || targetType == TypeDescriptor.NULL) {
if (logger.isDebugEnabled()) {
logger.debug("Yes, I can convert");
}
return true;
}
return getConverter(sourceType, targetType) != null;
GenericConverter converter = getConverter(sourceType, targetType);
if (converter != null) {
if (logger.isDebugEnabled()) {
logger.debug("Yes, I can convert");
}
return true;
} else {
if (logger.isDebugEnabled()) {
logger.debug("No, I cannot convert");
}
return false;
}
}
public Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType) {
assertNotNull(sourceType, targetType);
if (logger.isDebugEnabled()) {
logger.debug("Converting value of " + sourceType + " to " + targetType);
logger.debug("Converting value " + StylerUtils.style(source) + " of " + sourceType + " to " + targetType);
}
if (sourceType == TypeDescriptor.NULL) {
Assert.isTrue(source == null, "The source must be null if sourceType == TypeDescriptor.NULL");
return convertNullSource(sourceType, targetType);
Assert.isTrue(source == null, "The value must be null if sourceType == TypeDescriptor.NULL");
Object result = convertNullSource(sourceType, targetType);
if (logger.isDebugEnabled()) {
logger.debug("Converted to " + StylerUtils.style(result));
}
return result;
}
if (targetType == TypeDescriptor.NULL) {
if (logger.isDebugEnabled()) {
logger.debug("Converted to null");
}
return null;
}
GenericConverter converter = getConverter(sourceType, targetType);
if (converter == null) {
throw new ConverterNotFoundException(sourceType, targetType);
ConverterNotFoundException e = new ConverterNotFoundException(sourceType, targetType);
if (logger.isDebugEnabled()) {
logger.debug(e);
}
throw e;
}
try {
Object result = ConversionUtils.invokeConverter(converter, source, sourceType, targetType);
if (logger.isDebugEnabled()) {
logger.debug("Converted to " + StylerUtils.style(result));
}
return result;
} catch (ConversionException e) {
if (logger.isDebugEnabled()) {
logger.debug(e);
}
throw e;
}
return ConversionUtils.invokeConverter(converter, source, sourceType, targetType);
}
public String toString() {
@ -197,7 +238,7 @@ public class GenericConversionService implements ConversionService, ConverterReg
/**
* Return the default converter if no converter is found for the given sourceType/targetType pair.
* Returns a NO_OP Converter if the sourceType is assignalbe to the targetType.
* Returns a NO_OP Converter if the sourceType is assignable to the targetType.
* Returns <code>null</code> otherwise, indicating no suitable converter could be found.
* Subclasses may override.
* @param sourceType the source type to convert from
@ -366,7 +407,6 @@ public class GenericConversionService implements ConversionService, ConverterReg
private GenericConverter matchConverter(
MatchableConverters matchable, TypeDescriptor sourceFieldType, TypeDescriptor targetFieldType) {
return (matchable != null ? matchable.matchConverter(sourceFieldType, targetFieldType) : null);
}
@ -453,19 +493,19 @@ public class GenericConversionService implements ConversionService, ConverterReg
for (ConditionalGenericConverter conditional : this.conditionalConverters) {
if (conditional.matches(sourceType, targetType)) {
if (logger.isDebugEnabled()) {
logger.debug("Conditional converter lookup [MATCHED] " + conditional);
logger.debug("[MATCHED] converter " + conditional);
}
return conditional;
}
else {
if (logger.isDebugEnabled()) {
logger.debug("Conditional converter lookup [DID NOT MATCH] " + conditional);
logger.debug("[DID NOT MATCH] converter " + conditional);
}
}
}
}
if (this.defaultConverter != null && logger.isDebugEnabled()) {
logger.debug("Default converter lookup [MATCHED] " + this.defaultConverter);
logger.debug("[MATCHED] converter " + this.defaultConverter);
}
return this.defaultConverter;
}

View File

@ -21,6 +21,7 @@ import java.util.Map;
import java.util.Set;
import org.springframework.core.CollectionFactory;
import org.springframework.core.convert.ConversionService;
import org.springframework.core.convert.TypeDescriptor;
import org.springframework.core.convert.converter.ConditionalGenericConverter;
@ -37,9 +38,9 @@ import org.springframework.core.convert.converter.ConditionalGenericConverter;
*/
final class MapToMapConverter implements ConditionalGenericConverter {
private final GenericConversionService conversionService;
private final ConversionService conversionService;
public MapToMapConverter(GenericConversionService conversionService) {
public MapToMapConverter(ConversionService conversionService) {
this.conversionService = conversionService;
}
@ -55,57 +56,19 @@ final class MapToMapConverter implements ConditionalGenericConverter {
@SuppressWarnings("unchecked")
public Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType) {
if (source == null) {
return this.conversionService.convertNullSource(sourceType, targetType);
return null;
}
Map<?, ?> sourceMap = (Map<?, ?>) source;
TypeDescriptor targetKeyType = targetType.getMapKeyTypeDescriptor();
TypeDescriptor targetValueType = targetType.getMapValueTypeDescriptor();
if (targetKeyType == TypeDescriptor.NULL && targetValueType == TypeDescriptor.NULL) {
return compatibleMapWithoutEntryConversion(sourceMap, targetType);
}
TypeDescriptor sourceKeyType = sourceType.getMapKeyTypeDescriptor();
TypeDescriptor sourceValueType = sourceType.getMapValueTypeDescriptor();
if (sourceKeyType == TypeDescriptor.NULL || sourceValueType == TypeDescriptor.NULL) {
TypeDescriptor[] sourceEntryTypes = ConversionUtils.getMapEntryTypes(sourceMap);
sourceKeyType = sourceEntryTypes[0];
sourceValueType = sourceEntryTypes[1];
}
if (sourceKeyType == TypeDescriptor.NULL && sourceValueType == TypeDescriptor.NULL) {
return compatibleMapWithoutEntryConversion(sourceMap, targetType);
}
boolean keysCompatible = false;
if (sourceKeyType != TypeDescriptor.NULL && sourceKeyType.isAssignableTo(targetKeyType)) {
keysCompatible = true;
}
boolean valuesCompatible = false;
if (sourceValueType != TypeDescriptor.NULL && sourceValueType.isAssignableTo(targetValueType)) {
valuesCompatible = true;
}
if (keysCompatible && valuesCompatible) {
return compatibleMapWithoutEntryConversion(sourceMap, targetType);
}
Map targetMap = CollectionFactory.createMap(targetType.getType(), sourceMap.size());
MapEntryConverter converter = new MapEntryConverter(sourceKeyType, sourceValueType, targetKeyType,
targetValueType, keysCompatible, valuesCompatible, this.conversionService);
for (Object entry : sourceMap.entrySet()) {
Map.Entry sourceMapEntry = (Map.Entry) entry;
Object targetKey = converter.convertKey(sourceMapEntry.getKey());
Object targetValue = converter.convertValue(sourceMapEntry.getValue());
Object sourceKey = sourceMapEntry.getKey();
Object sourceValue = sourceMapEntry.getValue();
Object targetKey = this.conversionService.convert(sourceKey, sourceType.getMapKeyTypeDescriptor(sourceKey), targetType.getMapKeyTypeDescriptor(sourceKey));
Object targetValue = this.conversionService.convert(sourceValue, sourceType.getMapValueTypeDescriptor(sourceValue), targetType.getMapValueTypeDescriptor(sourceValue));
targetMap.put(targetKey, targetValue);
}
return targetMap;
}
@SuppressWarnings("unchecked")
private Map<?, ?> compatibleMapWithoutEntryConversion(Map<?, ?> source, TypeDescriptor targetType) {
if (targetType.getType().isInstance(source)) {
return source;
}
else {
Map target = CollectionFactory.createMap(targetType.getType(), source.size());
target.putAll(source);
return target;
}
}
}

View File

@ -20,10 +20,9 @@ import java.lang.reflect.Array;
import java.util.Collections;
import java.util.Set;
import org.springframework.core.convert.ConverterNotFoundException;
import org.springframework.core.convert.ConversionService;
import org.springframework.core.convert.TypeDescriptor;
import org.springframework.core.convert.converter.ConditionalGenericConverter;
import org.springframework.core.convert.converter.GenericConverter;
/**
* Converts an Object to a single-element Array containing the Object.
@ -34,9 +33,9 @@ import org.springframework.core.convert.converter.GenericConverter;
*/
final class ObjectToArrayConverter implements ConditionalGenericConverter {
private final GenericConversionService conversionService;
private final ConversionService conversionService;
public ObjectToArrayConverter(GenericConversionService conversionService) {
public ObjectToArrayConverter(ConversionService conversionService) {
this.conversionService = conversionService;
}
@ -50,20 +49,11 @@ final class ObjectToArrayConverter implements ConditionalGenericConverter {
public Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType) {
if (source == null) {
return this.conversionService.convertNullSource(sourceType, targetType);
}
TypeDescriptor targetElementType = targetType.getElementTypeDescriptor();
Object target = Array.newInstance(targetElementType.getType(), 1);
if (sourceType.isAssignableTo(targetElementType)) {
Array.set(target, 0, source);
}
else {
GenericConverter converter = this.conversionService.getConverter(sourceType, targetElementType);
if (converter == null) {
throw new ConverterNotFoundException(sourceType, targetElementType);
}
Array.set(target, 0, ConversionUtils.invokeConverter(converter, source, sourceType, targetElementType));
return null;
}
Object target = Array.newInstance(targetType.getElementType(), 1);
Object targetElement = this.conversionService.convert(source, sourceType, targetType.getElementTypeDescriptor());
Array.set(target, 0, targetElement);
return target;
}

View File

@ -21,10 +21,9 @@ import java.util.Collections;
import java.util.Set;
import org.springframework.core.CollectionFactory;
import org.springframework.core.convert.ConverterNotFoundException;
import org.springframework.core.convert.ConversionService;
import org.springframework.core.convert.TypeDescriptor;
import org.springframework.core.convert.converter.ConditionalGenericConverter;
import org.springframework.core.convert.converter.GenericConverter;
/**
* Converts an Object to a single-element Collection containing the Object.
@ -35,9 +34,9 @@ import org.springframework.core.convert.converter.GenericConverter;
*/
final class ObjectToCollectionConverter implements ConditionalGenericConverter {
private final GenericConversionService conversionService;
private final ConversionService conversionService;
public ObjectToCollectionConverter(GenericConversionService conversionService) {
public ObjectToCollectionConverter(ConversionService conversionService) {
this.conversionService = conversionService;
}
@ -52,20 +51,11 @@ final class ObjectToCollectionConverter implements ConditionalGenericConverter {
@SuppressWarnings("unchecked")
public Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType) {
if (source == null) {
return this.conversionService.convertNullSource(sourceType, targetType);
return null;
}
Collection target = CollectionFactory.createCollection(targetType.getType(), 1);
TypeDescriptor targetElementType = targetType.getElementTypeDescriptor();
if (targetElementType == TypeDescriptor.NULL || sourceType.isAssignableTo(targetElementType)) {
target.add(source);
}
else {
GenericConverter converter = this.conversionService.getConverter(sourceType, targetElementType);
if (converter == null) {
throw new ConverterNotFoundException(sourceType, targetElementType);
}
target.add(ConversionUtils.invokeConverter(converter, source, sourceType, targetElementType));
}
Object targetElement = this.conversionService.convert(source, sourceType, targetType.getElementTypeDescriptor(source));
target.add(targetElement);
return target;
}

View File

@ -52,9 +52,6 @@ final class ObjectToObjectConverter implements ConditionalGenericConverter {
}
public Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType) {
if (sourceType.isAssignableTo(targetType)) {
return source;
}
Class<?> sourceClass = sourceType.getObjectType();
Class<?> targetClass = targetType.getObjectType();
Object target;

View File

@ -43,10 +43,10 @@ final class ObjectToStringConverter implements ConditionalGenericConverter {
public boolean matches(TypeDescriptor sourceType, TypeDescriptor targetType) {
Class<?> sourceClass = sourceType.getObjectType();
return Number.class.isAssignableFrom(sourceClass) || Boolean.class.equals(sourceClass) ||
Character.class.equals(sourceClass) || CharSequence.class.isAssignableFrom(sourceClass) ||
StringWriter.class.isAssignableFrom(sourceClass) || sourceClass.isEnum() ||
ObjectToObjectConverter.hasValueOfMethodOrConstructor(sourceClass, String.class);
return String.class.equals(sourceClass) || Number.class.isAssignableFrom(sourceClass) ||
Boolean.class.equals(sourceClass) || Character.class.equals(sourceClass) ||
CharSequence.class.isAssignableFrom(sourceClass) || StringWriter.class.isAssignableFrom(sourceClass) ||
sourceClass.isEnum() || ObjectToObjectConverter.hasValueOfMethodOrConstructor(sourceClass, String.class);
}
public Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType) {

View File

@ -75,10 +75,10 @@ public class PropertyTypeDescriptor extends TypeDescriptor {
public Annotation[] getAnnotations() {
Annotation[] anns = this.cachedAnnotations;
if (anns == null) {
Map<Class, Annotation> annMap = new LinkedHashMap<Class, Annotation>();
Map<Class<?>, Annotation> annMap = new LinkedHashMap<Class<?>, Annotation>();
String name = this.propertyDescriptor.getName();
if (StringUtils.hasLength(name)) {
Class clazz = getMethodParameter().getMethod().getDeclaringClass();
Class<?> clazz = getMethodParameter().getMethod().getDeclaringClass();
Field field = ReflectionUtils.findField(clazz, name);
if (field == null) {
// Same lenient fallback checking as in CachedIntrospectionResults...

View File

@ -20,10 +20,9 @@ import java.lang.reflect.Array;
import java.util.Collections;
import java.util.Set;
import org.springframework.core.convert.ConverterNotFoundException;
import org.springframework.core.convert.ConversionService;
import org.springframework.core.convert.TypeDescriptor;
import org.springframework.core.convert.converter.ConditionalGenericConverter;
import org.springframework.core.convert.converter.GenericConverter;
import org.springframework.util.StringUtils;
/**
@ -34,9 +33,9 @@ import org.springframework.util.StringUtils;
*/
final class StringToArrayConverter implements ConditionalGenericConverter {
private final GenericConversionService conversionService;
private final ConversionService conversionService;
public StringToArrayConverter(GenericConversionService conversionService) {
public StringToArrayConverter(ConversionService conversionService) {
this.conversionService = conversionService;
}
@ -50,25 +49,17 @@ final class StringToArrayConverter implements ConditionalGenericConverter {
public Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType) {
if (source == null) {
return this.conversionService.convertNullSource(sourceType, targetType);
return null;
}
String string = (String) source;
String[] fields = StringUtils.commaDelimitedListToStringArray(string);
TypeDescriptor targetElementType = targetType.getElementTypeDescriptor();
if (sourceType.isAssignableTo(targetElementType)) {
return fields;
}
else {
Object target = Array.newInstance(targetElementType.getType(), fields.length);
GenericConverter converter = this.conversionService.getConverter(sourceType, targetElementType);
if (converter == null) {
throw new ConverterNotFoundException(sourceType, targetElementType);
}
for (int i = 0; i < fields.length; i++) {
Array.set(target, i, ConversionUtils.invokeConverter(converter, fields[i], sourceType, targetElementType));
}
return target;
Object target = Array.newInstance(targetType.getElementType(), fields.length);
for (int i = 0; i < fields.length; i++) {
Object sourceElement = fields[i];
Object targetElement = this.conversionService.convert(sourceElement, sourceType, targetType.getElementTypeDescriptor());
Array.set(target, i, targetElement);
}
return target;
}
}

View File

@ -21,10 +21,9 @@ import java.util.Collections;
import java.util.Set;
import org.springframework.core.CollectionFactory;
import org.springframework.core.convert.ConverterNotFoundException;
import org.springframework.core.convert.ConversionService;
import org.springframework.core.convert.TypeDescriptor;
import org.springframework.core.convert.converter.ConditionalGenericConverter;
import org.springframework.core.convert.converter.GenericConverter;
import org.springframework.util.StringUtils;
/**
@ -35,9 +34,9 @@ import org.springframework.util.StringUtils;
*/
final class StringToCollectionConverter implements ConditionalGenericConverter {
private final GenericConversionService conversionService;
private final ConversionService conversionService;
public StringToCollectionConverter(GenericConversionService conversionService) {
public StringToCollectionConverter(ConversionService conversionService) {
this.conversionService = conversionService;
}
@ -52,27 +51,14 @@ final class StringToCollectionConverter implements ConditionalGenericConverter {
@SuppressWarnings("unchecked")
public Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType) {
if (source == null) {
return this.conversionService.convertNullSource(sourceType, targetType);
return null;
}
String string = (String) source;
String[] fields = StringUtils.commaDelimitedListToStringArray(string);
Collection target = CollectionFactory.createCollection(targetType.getType(), fields.length);
TypeDescriptor targetElementType = targetType.getElementTypeDescriptor();
if (targetElementType == TypeDescriptor.NULL || sourceType.isAssignableTo(targetElementType)) {
for (String field : fields) {
target.add(field);
}
}
else {
GenericConverter converter = this.conversionService.getConverter(sourceType, targetElementType);
if (converter == null) {
throw new ConverterNotFoundException(sourceType, targetElementType);
}
for (String sourceElement : fields) {
Object targetElement = ConversionUtils.invokeConverter(
converter, sourceElement, sourceType, targetElementType);
target.add(targetElement);
}
for (String sourceElement : fields) {
Object targetElement = this.conversionService.convert(sourceElement, sourceType, targetType.getElementTypeDescriptor(sourceElement));
target.add(targetElement);
}
return target;
}

View File

@ -56,7 +56,7 @@ public class TypeDescriptorTests {
assertTrue(typeDescriptor.isArray());
assertEquals(Integer.TYPE,typeDescriptor.getElementType());
}
@Test
public void complexTypeDescriptors() throws Exception {
TypeDescriptor typeDescriptor = new TypeDescriptor(TypeDescriptorTests.class.getDeclaredField("arrayOfListOfString"));

View File

@ -39,19 +39,20 @@ public class GenericConversionServiceTests {
private GenericConversionService conversionService = new GenericConversionService();
@Test
public void executeConversion() {
public void convert() {
conversionService.addConverterFactory(new StringToNumberConverterFactory());
assertEquals(new Integer(3), conversionService.convert("3", Integer.class));
}
@Test
public void executeConversionNullSource() {
public void convertNullSource() {
assertEquals(null, conversionService.convert(null, Integer.class));
}
@Test
public void executeCompatibleSource() {
public void convertAssignableSource() {
assertEquals(Boolean.FALSE, conversionService.convert(false, boolean.class));
assertEquals(Boolean.FALSE, conversionService.convert(false, Boolean.class));
}
@Test
@ -189,7 +190,7 @@ public class GenericConversionServiceTests {
input.put("key", "value");
Object converted = conversionService.convert(input, TypeDescriptor.forObject(input),
new TypeDescriptor(getClass().getField("wildcardMap")));
assertSame(input, converted);
assertEquals(input, converted);
}

View File

@ -72,7 +72,7 @@ public class TypedValue {
@Override
public String toString() {
StringBuilder str = new StringBuilder();
str.append("TypedValue: ").append(this.value).append(" of type ").append(this.getTypeDescriptor().asString());
str.append("TypedValue: ").append(this.value).append(" of ").append(this.getTypeDescriptor());
return str.toString();
}

View File

@ -29,12 +29,12 @@ import org.springframework.expression.ParseException;
import org.springframework.expression.PropertyAccessor;
import org.springframework.expression.TypedValue;
import org.springframework.expression.spel.ast.FormatHelper;
import org.springframework.expression.spel.standard.SpelExpression;
import org.springframework.expression.spel.support.ReflectionHelper;
import org.springframework.expression.spel.support.ReflectivePropertyAccessor;
import org.springframework.expression.spel.support.StandardEvaluationContext;
import org.springframework.expression.spel.support.StandardTypeConverter;
import org.springframework.expression.spel.support.ReflectionHelper.ArgsMatchKind;
import org.springframework.expression.spel.standard.SpelExpression;
/**
* Tests for any helper code.
@ -94,7 +94,7 @@ public class HelperTests extends ExpressionTestCase {
public void testTypedValue() {
TypedValue tValue = new TypedValue("hello");
Assert.assertEquals(String.class,tValue.getTypeDescriptor().getType());
Assert.assertEquals("TypedValue: hello of type java.lang.String",tValue.toString());
Assert.assertEquals("TypedValue: hello of [TypeDescriptor java.lang.String]",tValue.toString());
}
@Test