moved applyIndexedObject internal, now invoked inside forObject static factory method
This commit is contained in:
parent
4d6a5849f7
commit
79f9d1cfc6
|
|
@ -24,6 +24,7 @@ import org.springframework.core.NestedRuntimeException;
|
|||
* @author Keith Donald
|
||||
* @since 3.0
|
||||
*/
|
||||
@SuppressWarnings("serial")
|
||||
public abstract class ConversionException extends NestedRuntimeException {
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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());
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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())) {
|
||||
|
|
|
|||
|
|
@ -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));
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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++;
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue