moved applyIndexedObject internal, now invoked inside forObject static factory method

This commit is contained in:
Keith Donald 2011-05-23 01:08:18 +00:00
parent 4d6a5849f7
commit 79f9d1cfc6
16 changed files with 77 additions and 93 deletions

View File

@ -24,6 +24,7 @@ import org.springframework.core.NestedRuntimeException;
* @author Keith Donald
* @since 3.0
*/
@SuppressWarnings("serial")
public abstract class ConversionException extends NestedRuntimeException {
/**

View File

@ -25,6 +25,7 @@ import org.springframework.util.ObjectUtils;
* @author Juergen Hoeller
* @since 3.0
*/
@SuppressWarnings("serial")
public final class ConversionFailedException extends ConversionException {
private final TypeDescriptor sourceType;

View File

@ -138,10 +138,11 @@ public class TypeDescriptor {
return NULL;
}
if (object instanceof Collection<?>) {
return new TypeDescriptor(object.getClass(), CollectionUtils.findCommonElementType((Collection<?>) object));
return new TypeDescriptor(object.getClass(), findCommonElement((Collection<?>) object));
}
else if (object instanceof Map<?, ?>) {
return new TypeDescriptor(object.getClass(), CollectionUtils.findCommonElementType(((Map<?, ?>) object).keySet()), CollectionUtils.findCommonElementType(((Map<?, ?>) object).values()));
Map<?, ?> map = (Map<?, ?>) object;
return new TypeDescriptor(map.getClass(), findCommonElement(map.keySet()), findCommonElement(map.values()));
}
else {
return valueOf(object.getClass());
@ -342,37 +343,6 @@ public class TypeDescriptor {
return methodParameter;
}
/**
* Create a copy of this nested type descriptor and apply the specific type information from the indexed value, if necessary.
* Used to support collection and map indexing scenarios, where the indexer has a reference to the indexed type descriptor but needs to ensure its type actually represents the indexed object type.
* This is necessary to support type conversion during collection and map binding operations where generic information may not be available.
*/
public TypeDescriptor applyIndexedObject(Object object) {
if (object == null) {
return this;
}
if (isCollection() && Object.class.equals(getElementType())) {
Collection<?> collection = (Collection<?>) object;
if (collection.size() > 0) {
return new TypeDescriptor(object.getClass(), CollectionUtils.findCommonElementType((Collection<?>) object), methodParameter, field, fieldNestingLevel, annotations);
} else {
return this;
}
}
else if (isMap() && Object.class.equals(getMapKeyType()) && Object.class.equals(getMapValueType())) {
Map<?, ?> map = (Map<?, ?>) object;
if (map.size() > 0) {
return new TypeDescriptor(object.getClass(), CollectionUtils.findCommonElementType(((Map<?, ?>) object).keySet()),
CollectionUtils.findCommonElementType(((Map<?, ?>) object).values()), methodParameter, field, fieldNestingLevel, annotations);
} else {
return this;
}
}
else {
return this;
}
}
// extending Object
public boolean equals(Object obj) {
@ -545,6 +515,20 @@ public class TypeDescriptor {
return methodParameter;
}
private static Object findCommonElement(Collection<?> values) {
Object candidate = null;
for (Object value : values) {
if (value != null) {
if (candidate == null) {
candidate = value;
} else if (candidate.getClass() != value.getClass()) {
return null;
}
}
}
return candidate;
}
// internal constructors
private TypeDescriptor() {
@ -555,46 +539,44 @@ public class TypeDescriptor {
this.type = type;
}
private TypeDescriptor(Class<?> collectionType, Class<?> elementType) {
this.type = collectionType;
if (elementType == null) {
elementType = Object.class;
}
this.elementType = TypeDescriptor.valueOf(elementType);
}
private TypeDescriptor(Class<?> mapType, Class<?> keyType, Class<?> valueType) {
this.type = mapType;
if (keyType == null) {
keyType = Object.class;
}
if (valueType == null) {
valueType = Object.class;
}
this.mapKeyType = TypeDescriptor.valueOf(keyType);
this.mapValueType = TypeDescriptor.valueOf(valueType);
}
private TypeDescriptor(Class<?> collectionType, Class<?> elementType, MethodParameter methodParameter, Field field, int fieldNestingLevel, Annotation[] annotations) {
this(collectionType, elementType);
this.methodParameter = methodParameter;
this.field = field;
this.fieldNestingLevel = fieldNestingLevel;
this.annotations = annotations;
}
private TypeDescriptor(Class<?> mapType, Class<?> keyType, Class<?> valueType, MethodParameter methodParameter, Field field, int fieldNestingLevel, Annotation[] annotations) {
this(mapType, keyType, valueType);
this.methodParameter = methodParameter;
this.field = field;
this.fieldNestingLevel = fieldNestingLevel;
this.annotations = annotations;
}
private TypeDescriptor(Class<?> nestedType, Field field, int nestingLevel) {
this.type = nestedType;
this.field = field;
this.fieldNestingLevel = nestingLevel;
}
public TypeDescriptor(Class<?> mapType, Object commonKey, Object commonValue) {
this.type = mapType;
this.mapKeyType = applyIndexedObject(commonKey);
this.mapValueType = applyIndexedObject(commonValue);
}
public TypeDescriptor(Class<?> collectionType, Object commonElement) {
this.type = collectionType;
this.elementType = applyIndexedObject(commonElement);
}
private TypeDescriptor applyIndexedObject(Object object) {
if (object == null) {
return TypeDescriptor.valueOf(Object.class);
}
if (object instanceof Collection<?>) {
Collection<?> collection = (Collection<?>) object;
if (collection.size() == 0) {
return TypeDescriptor.valueOf(Object.class);
}
return new TypeDescriptor(object.getClass(), findCommonElement((Collection<?>) object));
}
else if (object instanceof Map<?, ?>) {
Map<?, ?> map = (Map<?, ?>) object;
if (map.size() == 0) {
return TypeDescriptor.valueOf(Object.class);
}
return new TypeDescriptor(object.getClass(), findCommonElement(map.keySet()), findCommonElement(map.values()));
}
else {
return TypeDescriptor.valueOf(object.getClass());
}
}
}

View File

@ -52,12 +52,13 @@ final class ArrayToCollectionConverter implements ConditionalGenericConverter {
return this.conversionService.canConvert(sourceType.getElementTypeDescriptor(), targetType.getElementTypeDescriptor());
}
@SuppressWarnings("unchecked")
public Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType) {
if (source == null) {
return null;
}
int length = Array.getLength(source);
Collection target = CollectionFactory.createCollection(targetType.getType(), length);
Collection<Object> 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());

View File

@ -64,7 +64,7 @@ final class CollectionToArrayConverter implements ConditionalGenericConverter {
Object array = Array.newInstance(targetType.getElementType(), sourceCollection.size());
int i = 0;
for (Object sourceElement : sourceCollection) {
Object targetElement = this.conversionService.convert(sourceElement, sourceType.getElementTypeDescriptor().applyIndexedObject(sourceElement), targetType.getElementTypeDescriptor());
Object targetElement = this.conversionService.convert(sourceElement, sourceType.getElementTypeDescriptor(), targetType.getElementTypeDescriptor());
Array.set(array, i++, targetElement);
}
return array;

View File

@ -57,14 +57,15 @@ final class CollectionToCollectionConverter implements ConditionalGenericConvert
return this.conversionService.canConvert(sourceElementType, targetElementType);
}
@SuppressWarnings("unchecked")
public Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType) {
if (source == null) {
return null;
}
Collection<?> sourceCollection = (Collection<?>) source;
Collection target = CollectionFactory.createCollection(targetType.getType(), sourceCollection.size());
Collection<Object> target = CollectionFactory.createCollection(targetType.getType(), sourceCollection.size());
for (Object sourceElement : sourceCollection) {
Object targetElement = this.conversionService.convert(sourceElement, sourceType.getElementTypeDescriptor().applyIndexedObject(sourceElement), targetType.getElementTypeDescriptor());
Object targetElement = this.conversionService.convert(sourceElement, sourceType.getElementTypeDescriptor(), targetType.getElementTypeDescriptor());
target.add(targetElement);
}
return target;

View File

@ -59,7 +59,7 @@ final class CollectionToObjectConverter implements ConditionalGenericConverter {
return null;
}
Object firstElement = sourceCollection.iterator().next();
return this.conversionService.convert(firstElement, sourceType.getElementTypeDescriptor().applyIndexedObject(firstElement), targetType);
return this.conversionService.convert(firstElement, sourceType.getElementTypeDescriptor(), targetType);
}
}

View File

@ -16,9 +16,6 @@
package org.springframework.core.convert.support;
import org.springframework.core.convert.ConversionService;
import org.springframework.core.convert.converter.ConverterRegistry;
/**
* A specialization of {@link GenericConversionService} configured by default with
* converters appropriate for most applications.

View File

@ -481,10 +481,10 @@ public class GenericConversionService implements ConversionService, ConverterReg
private final ConvertiblePair typeInfo;
private final Converter converter;
private final Converter<Object, Object> converter;
public ConverterAdapter(ConvertiblePair typeInfo, Converter<?, ?> converter) {
this.converter = converter;
this.converter = (Converter<Object, Object>) converter;
this.typeInfo = typeInfo;
}
@ -511,10 +511,10 @@ public class GenericConversionService implements ConversionService, ConverterReg
private final ConvertiblePair typeInfo;
private final ConverterFactory converterFactory;
private final ConverterFactory<Object, Object> converterFactory;
public ConverterFactoryAdapter(ConvertiblePair typeInfo, ConverterFactory<?, ?> converterFactory) {
this.converterFactory = converterFactory;
this.converterFactory = (ConverterFactory<Object, Object>) converterFactory;
this.typeInfo = typeInfo;
}

View File

@ -55,6 +55,7 @@ final class MapToMapConverter implements ConditionalGenericConverter {
TypeDescriptor targetValueType = targetType.getMapValueTypeDescriptor();
if (Object.class.equals(sourceKeyType.getType()) && Object.class.equals(sourceValueType.getType())
|| Object.class.equals(targetKeyType.getType()) && Object.class.equals(targetValueType.getType())) {
// catches the empty map case
return true;
}
return this.conversionService.canConvert(sourceType.getMapKeyTypeDescriptor(), targetType.getMapKeyTypeDescriptor()) &&
@ -78,8 +79,8 @@ final class MapToMapConverter implements ConditionalGenericConverter {
for (Map.Entry<Object, Object> entry : sourceMap.entrySet()) {
Object sourceKey = entry.getKey();
Object sourceValue = entry.getValue();
Object targetKey = this.conversionService.convert(sourceKey, sourceType.getMapKeyTypeDescriptor().applyIndexedObject(sourceKey), targetType.getMapKeyTypeDescriptor());
Object targetValue = this.conversionService.convert(sourceValue, sourceType.getMapValueTypeDescriptor().applyIndexedObject(sourceValue), targetType.getMapValueTypeDescriptor());
Object targetKey = this.conversionService.convert(sourceKey, sourceType.getMapKeyTypeDescriptor(), targetType.getMapKeyTypeDescriptor());
Object targetValue = this.conversionService.convert(sourceValue, sourceType.getMapValueTypeDescriptor(), targetType.getMapValueTypeDescriptor());
targetMap.put(targetKey, targetValue);
}
}

View File

@ -49,11 +49,12 @@ final class ObjectToCollectionConverter implements ConditionalGenericConverter {
return this.conversionService.canConvert(sourceType, targetType.getElementTypeDescriptor());
}
@SuppressWarnings("unchecked")
public Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType) {
if (source == null) {
return null;
}
Collection target = CollectionFactory.createCollection(targetType.getType(), 1);
Collection<Object> target = CollectionFactory.createCollection(targetType.getType(), 1);
TypeDescriptor elementType = targetType.getElementTypeDescriptor();
// Avoid potential recursion...
if (!Collection.class.isAssignableFrom(elementType.getType())) {

View File

@ -44,8 +44,8 @@ import org.springframework.util.ReflectionUtils;
final class ObjectToObjectConverter implements ConditionalGenericConverter {
public boolean matches(TypeDescriptor sourceType, TypeDescriptor targetType) {
Class source = sourceType.getObjectType();
Class target = targetType.getObjectType();
Class<?> source = sourceType.getObjectType();
Class<?> target = targetType.getObjectType();
return (!source.equals(target) && hasValueOfMethodOrConstructor(target, source));
}

View File

@ -20,11 +20,9 @@ import java.beans.PropertyDescriptor;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.Collection;
import java.util.LinkedHashMap;
import java.util.Map;
import org.springframework.core.GenericCollectionTypeResolver;
import org.springframework.core.MethodParameter;
import org.springframework.core.convert.TypeDescriptor;
import org.springframework.util.ReflectionUtils;

View File

@ -48,13 +48,14 @@ final class StringToCollectionConverter implements ConditionalGenericConverter {
return this.conversionService.canConvert(sourceType, targetType.getElementTypeDescriptor());
}
@SuppressWarnings("unchecked")
public Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType) {
if (source == null) {
return null;
}
String string = (String) source;
String[] fields = StringUtils.commaDelimitedListToStringArray(string);
Collection target = CollectionFactory.createCollection(targetType.getType(), fields.length);
Collection<Object> target = CollectionFactory.createCollection(targetType.getType(), fields.length);
for (String sourceElement : fields) {
Object targetElement = this.conversionService.convert(sourceElement.trim(), sourceType, targetType.getElementTypeDescriptor());
target.add(targetElement);

View File

@ -25,7 +25,7 @@ import org.springframework.core.convert.converter.ConverterFactory;
* @author Keith Donald
* @since 3.0
*/
@SuppressWarnings("unchecked")
@SuppressWarnings({ "unchecked", "rawtypes" })
final class StringToEnumConverterFactory implements ConverterFactory<String, Enum> {
public <T extends Enum> Converter<String, T> getConverter(Class<T> targetType) {

View File

@ -93,7 +93,7 @@ public class Indexer extends SpelNodeImpl {
Object possiblyConvertedKey = index;
possiblyConvertedKey = state.convertValue(index, targetObjectTypeDescriptor.getMapKeyTypeDescriptor());
Object o = ((Map<?, ?>) targetObject).get(possiblyConvertedKey);
return new TypedValue(o, targetObjectTypeDescriptor.getMapValueTypeDescriptor().applyIndexedObject(o));
return new TypedValue(o, targetObjectTypeDescriptor.getMapValueTypeDescriptor());
}
if (targetObject == null) {
@ -105,7 +105,7 @@ public class Indexer extends SpelNodeImpl {
int idx = (Integer)state.convertValue(index, TypeDescriptor.valueOf(Integer.class));
if (targetObject.getClass().isArray()) {
Object arrayElement = accessArrayElement(targetObject, idx);
return new TypedValue(arrayElement, targetObjectTypeDescriptor.getElementTypeDescriptor().applyIndexedObject(arrayElement));
return new TypedValue(arrayElement, targetObjectTypeDescriptor.getElementTypeDescriptor());
} else if (targetObject instanceof Collection) {
Collection c = (Collection) targetObject;
if (idx >= c.size()) {
@ -116,7 +116,7 @@ public class Indexer extends SpelNodeImpl {
int pos = 0;
for (Object o : c) {
if (pos == idx) {
return new TypedValue(o, targetObjectTypeDescriptor.getElementTypeDescriptor().applyIndexedObject(o));
return new TypedValue(o, targetObjectTypeDescriptor.getElementTypeDescriptor());
}
pos++;
}