revised TypeDescriptor NULL and element/mapKey/mapValue type semantics

This commit is contained in:
Keith Donald 2011-06-05 04:43:18 +00:00
parent a2a4929c14
commit c84cccf06d
42 changed files with 732 additions and 758 deletions

View File

@ -141,8 +141,8 @@ class TypeConverterDelegate {
// Value not of required type? // Value not of required type?
if (editor != null || (requiredType != null && !ClassUtils.isAssignableValue(requiredType, convertedValue))) { if (editor != null || (requiredType != null && !ClassUtils.isAssignableValue(requiredType, convertedValue))) {
if (requiredType != null && Collection.class.isAssignableFrom(requiredType) && convertedValue instanceof String) { if (requiredType != null && Collection.class.isAssignableFrom(requiredType) && convertedValue instanceof String) {
Class elemType = typeDescriptor.getElementType(); TypeDescriptor elementType = typeDescriptor.getElementType();
if (elemType != null && Enum.class.isAssignableFrom(elemType)) { if (elementType != null && Enum.class.isAssignableFrom(elementType.getType())) {
convertedValue = StringUtils.commaDelimitedListToStringArray((String) convertedValue); convertedValue = StringUtils.commaDelimitedListToStringArray((String) convertedValue);
} }
} }
@ -459,8 +459,8 @@ class TypeConverterDelegate {
return original; return original;
} }
Class elementType = typeDescriptor.getElementType(); TypeDescriptor elementType = typeDescriptor.getElementType();
if (elementType == Object.class && originalAllowed && if (elementType == null && originalAllowed &&
!this.propertyEditorRegistry.hasCustomEditorForElement(null, propertyName)) { !this.propertyEditorRegistry.hasCustomEditorForElement(null, propertyName)) {
return original; return original;
} }
@ -506,7 +506,7 @@ class TypeConverterDelegate {
Object element = it.next(); Object element = it.next();
String indexedPropertyName = buildIndexedPropertyName(propertyName, i); String indexedPropertyName = buildIndexedPropertyName(propertyName, i);
Object convertedElement = convertIfNecessary( Object convertedElement = convertIfNecessary(
indexedPropertyName, null, element, elementType, typeDescriptor.getElementTypeDescriptor()); indexedPropertyName, null, element, elementType != null ? elementType.getType() : null , typeDescriptor.getElementType());
try { try {
convertedCopy.add(convertedElement); convertedCopy.add(convertedElement);
} }
@ -531,9 +531,9 @@ class TypeConverterDelegate {
return original; return original;
} }
Class keyType = typeDescriptor.getMapKeyType(); TypeDescriptor keyType = typeDescriptor.getMapKeyType();
Class valueType = typeDescriptor.getMapValueType(); TypeDescriptor valueType = typeDescriptor.getMapValueType();
if (keyType == Object.class && valueType == Object.class && originalAllowed && if (keyType == null && valueType == null && originalAllowed &&
!this.propertyEditorRegistry.hasCustomEditorForElement(null, propertyName)) { !this.propertyEditorRegistry.hasCustomEditorForElement(null, propertyName)) {
return original; return original;
} }
@ -579,8 +579,8 @@ class TypeConverterDelegate {
Object key = entry.getKey(); Object key = entry.getKey();
Object value = entry.getValue(); Object value = entry.getValue();
String keyedPropertyName = buildKeyedPropertyName(propertyName, key); String keyedPropertyName = buildKeyedPropertyName(propertyName, key);
Object convertedKey = convertIfNecessary(keyedPropertyName, null, key, keyType, typeDescriptor.getMapKeyTypeDescriptor()); Object convertedKey = convertIfNecessary(keyedPropertyName, null, key, keyType != null ? keyType.getType() : null, typeDescriptor.getMapKeyType());
Object convertedValue = convertIfNecessary(keyedPropertyName, null, value, valueType, typeDescriptor.getMapValueTypeDescriptor()); Object convertedValue = convertIfNecessary(keyedPropertyName, null, value, valueType!= null ? valueType.getType() : null, typeDescriptor.getMapValueType());
try { try {
convertedCopy.put(convertedKey, convertedValue); convertedCopy.put(convertedKey, convertedValue);
} }

View File

@ -24,6 +24,9 @@ abstract class AbstractDescriptor {
private final Class<?> type; private final Class<?> type;
public AbstractDescriptor(Class<?> type) { public AbstractDescriptor(Class<?> type) {
//if (type == null) {
// throw new IllegalArgumentException("type cannot be null");
//}
this.type = type; this.type = type;
} }
@ -33,31 +36,31 @@ abstract class AbstractDescriptor {
public TypeDescriptor getElementType() { public TypeDescriptor getElementType() {
if (isCollection()) { if (isCollection()) {
Class<?> elementType = wildcard(getCollectionElementClass()); Class<?> elementType = resolveCollectionElementType();
return new TypeDescriptor(nested(elementType, 0)); return elementType != null ? new TypeDescriptor(nested(elementType, 0)) : null;
} else if (isArray()) { } else if (isArray()) {
Class<?> elementType = getType().getComponentType(); Class<?> elementType = getType().getComponentType();
return new TypeDescriptor(nested(elementType, 0)); return new TypeDescriptor(nested(elementType, 0));
} else { } else {
return TypeDescriptor.NULL; return null;
} }
} }
public TypeDescriptor getMapKeyType() { public TypeDescriptor getMapKeyType() {
if (isMap()) { if (isMap()) {
Class<?> keyType = wildcard(getMapKeyClass()); Class<?> keyType = resolveMapKeyType();
return new TypeDescriptor(nested(keyType, 0)); return keyType != null ? new TypeDescriptor(nested(keyType, 0)) : null;
} else { } else {
return TypeDescriptor.NULL; return null;
} }
} }
public TypeDescriptor getMapValueType() { public TypeDescriptor getMapValueType() {
if (isMap()) { if (isMap()) {
Class<?> valueType = wildcard(getMapValueClass()); Class<?> valueType = resolveMapValueType();
return new TypeDescriptor(nested(valueType, 1)); return valueType != null ? new TypeDescriptor(nested(valueType, 1)) : null;
} else { } else {
return TypeDescriptor.NULL; return null;
} }
} }
@ -65,11 +68,11 @@ abstract class AbstractDescriptor {
public AbstractDescriptor nested() { public AbstractDescriptor nested() {
if (isCollection()) { if (isCollection()) {
return nested(wildcard(getCollectionElementClass()), 0); return nested(resolveCollectionElementType(), 0);
} else if (isArray()) { } else if (isArray()) {
return nested(getType().getComponentType(), 0); return nested(getType().getComponentType(), 0);
} else if (isMap()) { } else if (isMap()) {
return nested(wildcard(getMapValueClass()), 1); return nested(resolveMapValueType(), 1);
} else { } else {
throw new IllegalStateException("Not a collection, array, or map: cannot resolve nested value types"); throw new IllegalStateException("Not a collection, array, or map: cannot resolve nested value types");
} }
@ -77,30 +80,26 @@ abstract class AbstractDescriptor {
// subclassing hooks // subclassing hooks
protected abstract Class<?> getCollectionElementClass(); protected abstract Class<?> resolveCollectionElementType();
protected abstract Class<?> getMapKeyClass(); protected abstract Class<?> resolveMapKeyType();
protected abstract Class<?> getMapValueClass(); protected abstract Class<?> resolveMapValueType();
protected abstract AbstractDescriptor nested(Class<?> type, int typeIndex); protected abstract AbstractDescriptor nested(Class<?> type, int typeIndex);
// internal helpers // internal helpers
private boolean isCollection() { private boolean isCollection() {
return Collection.class.isAssignableFrom(getType()); return getType() != null && Collection.class.isAssignableFrom(getType());
} }
private boolean isArray() { private boolean isArray() {
return getType().isArray(); return getType() != null && getType().isArray();
} }
private boolean isMap() { private boolean isMap() {
return Map.class.isAssignableFrom(getType()); return getType() != null && Map.class.isAssignableFrom(getType());
}
private Class<?> wildcard(Class<?> type) {
return type != null ? type : Object.class;
} }
} }

View File

@ -52,17 +52,17 @@ class BeanPropertyDescriptor extends AbstractDescriptor {
} }
@Override @Override
protected Class<?> getCollectionElementClass() { protected Class<?> resolveCollectionElementType() {
return GenericCollectionTypeResolver.getCollectionParameterType(methodParameter); return GenericCollectionTypeResolver.getCollectionParameterType(methodParameter);
} }
@Override @Override
protected Class<?> getMapKeyClass() { protected Class<?> resolveMapKeyType() {
return GenericCollectionTypeResolver.getMapKeyParameterType(methodParameter); return GenericCollectionTypeResolver.getMapKeyParameterType(methodParameter);
} }
@Override @Override
protected Class<?> getMapValueClass() { protected Class<?> resolveMapValueType() {
return GenericCollectionTypeResolver.getMapValueParameterType(methodParameter); return GenericCollectionTypeResolver.getMapValueParameterType(methodParameter);
} }

View File

@ -29,18 +29,18 @@ class ClassDescriptor extends AbstractDescriptor {
} }
@Override @Override
protected Class<?> getCollectionElementClass() { protected Class<?> resolveCollectionElementType() {
return Object.class; return null;
} }
@Override @Override
protected Class<?> getMapKeyClass() { protected Class<?> resolveMapKeyType() {
return Object.class; return null;
} }
@Override @Override
protected Class<?> getMapValueClass() { protected Class<?> resolveMapValueType() {
return Object.class; return null;
} }
@Override @Override

View File

@ -1,116 +0,0 @@
/*
* Copyright 2002-2011 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;
import java.util.Collection;
import java.util.LinkedHashSet;
import java.util.LinkedList;
import java.util.Map;
import java.util.Set;
class CommonElement {
private final Class<?> type;
private final Object value;
public CommonElement(Class<?> type, Object value) {
this.type = type;
this.value = value;
}
public Class<?> getType() {
return type;
}
public Object getValue() {
return value;
}
public TypeDescriptor toTypeDescriptor() {
if (type == null) {
return TypeDescriptor.NULL;
} else if (value instanceof Collection<?>) {
Collection<?> collection = (Collection<?>) value;
return new TypeDescriptor(type, typeDescriptor(collection));
}
else if (value instanceof Map<?, ?>) {
Map<?, ?> map = (Map<?, ?>) value;
return new TypeDescriptor(type, typeDescriptor(map.keySet()), typeDescriptor(map.values()));
}
else {
return TypeDescriptor.valueOf(type);
}
}
public static TypeDescriptor typeDescriptor(Collection<?> collection) {
return findCommonElement(collection).toTypeDescriptor();
}
// internal helpers
private static CommonElement findCommonElement(Collection<?> values) {
Class<?> commonType = null;
Object candidate = null;
for (Object value : values) {
if (value != null) {
if (candidate == null) {
commonType = value.getClass();
candidate = value;
} else {
commonType = commonType(commonType, value.getClass());
if (commonType == Object.class) {
return new CommonElement(Object.class, null);
}
}
}
}
return new CommonElement(commonType, candidate);
}
private static Class<?> commonType(Class<?> commonType, Class<?> valueClass) {
Set<Class<?>> interfaces = new LinkedHashSet<Class<?>>();
LinkedList<Class<?>> classQueue = new LinkedList<Class<?>>();
classQueue.addFirst(commonType);
while (!classQueue.isEmpty()) {
Class<?> currentClass = classQueue.removeLast();
if (currentClass.isAssignableFrom(valueClass)) {
return currentClass;
}
Class<?> superClass = currentClass.getSuperclass();
if (superClass != null && superClass != Object.class) {
classQueue.addFirst(currentClass.getSuperclass());
}
for (Class<?> interfaceType : currentClass.getInterfaces()) {
addInterfaceHierarchy(interfaceType, interfaces);
}
}
for (Class<?> interfaceType : interfaces) {
if (interfaceType.isAssignableFrom(valueClass)) {
return interfaceType;
}
}
return Object.class;
}
private static void addInterfaceHierarchy(Class<?> interfaceType, Set<Class<?>> interfaces) {
interfaces.add(interfaceType);
for (Class<?> inheritedInterface : interfaceType.getInterfaces()) {
addInterfaceHierarchy(inheritedInterface, interfaces);
}
}
}

View File

@ -36,17 +36,17 @@ class FieldDescriptor extends AbstractDescriptor {
} }
@Override @Override
protected Class<?> getCollectionElementClass() { protected Class<?> resolveCollectionElementType() {
return GenericCollectionTypeResolver.getCollectionFieldType(this.field, this.nestingLevel); return GenericCollectionTypeResolver.getCollectionFieldType(this.field, this.nestingLevel);
} }
@Override @Override
protected Class<?> getMapKeyClass() { protected Class<?> resolveMapKeyType() {
return GenericCollectionTypeResolver.getMapKeyFieldType(this.field, this.nestingLevel); return GenericCollectionTypeResolver.getMapKeyFieldType(this.field, this.nestingLevel);
} }
@Override @Override
protected Class<?> getMapValueClass() { protected Class<?> resolveMapValueType() {
return GenericCollectionTypeResolver.getMapValueFieldType(this.field, this.nestingLevel); return GenericCollectionTypeResolver.getMapValueFieldType(this.field, this.nestingLevel);
} }

View File

@ -43,17 +43,17 @@ class ParameterDescriptor extends AbstractDescriptor {
} }
@Override @Override
protected Class<?> getCollectionElementClass() { protected Class<?> resolveCollectionElementType() {
return GenericCollectionTypeResolver.getCollectionParameterType(methodParameter); return GenericCollectionTypeResolver.getCollectionParameterType(methodParameter);
} }
@Override @Override
protected Class<?> getMapKeyClass() { protected Class<?> resolveMapKeyType() {
return GenericCollectionTypeResolver.getMapKeyParameterType(methodParameter); return GenericCollectionTypeResolver.getMapKeyParameterType(methodParameter);
} }
@Override @Override
protected Class<?> getMapValueClass() { protected Class<?> resolveMapValueType() {
return GenericCollectionTypeResolver.getMapValueParameterType(methodParameter); return GenericCollectionTypeResolver.getMapValueParameterType(methodParameter);
} }

View File

@ -38,9 +38,6 @@ public class TypeDescriptor {
static final Annotation[] EMPTY_ANNOTATION_ARRAY = new Annotation[0]; static final Annotation[] EMPTY_ANNOTATION_ARRAY = new Annotation[0];
/** Constant defining a TypeDescriptor for a <code>null</code> value */
public static final TypeDescriptor NULL = new TypeDescriptor();
private static final Map<Class<?>, TypeDescriptor> typeDescriptorCache = new HashMap<Class<?>, TypeDescriptor>(); private static final Map<Class<?>, TypeDescriptor> typeDescriptorCache = new HashMap<Class<?>, TypeDescriptor>();
static { static {
@ -63,7 +60,6 @@ public class TypeDescriptor {
typeDescriptorCache.put(String.class, new TypeDescriptor(String.class)); typeDescriptorCache.put(String.class, new TypeDescriptor(String.class));
} }
private final Class<?> type; private final Class<?> type;
private final TypeDescriptor elementType; private final TypeDescriptor elementType;
@ -110,9 +106,6 @@ public class TypeDescriptor {
* @return the type descriptor * @return the type descriptor
*/ */
public static TypeDescriptor valueOf(Class<?> type) { public static TypeDescriptor valueOf(Class<?> type) {
if (type == null) {
return NULL;
}
TypeDescriptor desc = typeDescriptorCache.get(type); TypeDescriptor desc = typeDescriptorCache.get(type);
return (desc != null ? desc : new TypeDescriptor(type)); return (desc != null ? desc : new TypeDescriptor(type));
} }
@ -148,37 +141,6 @@ public class TypeDescriptor {
return new TypeDescriptor(mapType, keyType, valueType); return new TypeDescriptor(mapType, keyType, valueType);
} }
/**
* Create a new type descriptor for an object.
* Use this factory method to introspect a source object's type before asking the conversion system to convert it to some another type.
* Populates nested type descriptors for collection and map objects through object introspection.
* If the provided object is null, returns {@link TypeDescriptor#NULL}.
* If the object is not a collection or map, simply calls {@link #valueOf(Class)}.
* If the object is a collection or map, this factory method will derive nested element or key/value types by introspecting the collection or map.
* The introspection algorithm derives nested element or key/value types by resolving the "common element type" across the collection or map.
* For example, if a Collection contained all java.lang.Integer elements, its element type would be java.lang.Integer.
* If a Collection contained several distinct number types all extending from java.lang.Number, its element type would be java.lang.Number.
* If a Collection contained a String and a java.util.Map element, its element type would be java.io.Serializable.
* @param object the source object
* @return the type descriptor
* @see ConversionService#convert(Object, Class)
*/
public static TypeDescriptor forObject(Object object) {
if (object == null) {
return NULL;
}
if (object instanceof Collection<?>) {
return new TypeDescriptor(object.getClass(), CommonElement.typeDescriptor((Collection<?>) object));
}
else if (object instanceof Map<?, ?>) {
Map<?, ?> map = (Map<?, ?>) object;
return new TypeDescriptor(map.getClass(), CommonElement.typeDescriptor(map.keySet()), CommonElement.typeDescriptor(map.values()));
}
else {
return valueOf(object.getClass());
}
}
/** /**
* Creates a type descriptor for a nested type declared within the method parameter. * Creates a type descriptor for a nested type declared within the method parameter.
* For example, if the methodParameter is a List&lt;String&gt; and the nestingLevel is 1, the nested type descriptor will be String.class. * For example, if the methodParameter is a List&lt;String&gt; and the nestingLevel is 1, the nested type descriptor will be String.class.
@ -224,6 +186,10 @@ public class TypeDescriptor {
return nested(new BeanPropertyDescriptor(beanClass, property), nestingLevel); return nested(new BeanPropertyDescriptor(beanClass, property), nestingLevel);
} }
public static TypeDescriptor forObject(Object source) {
return source != null ? valueOf(source.getClass()) : null;
}
/** /**
* Determine the declared (non-generic) type of the wrapped parameter/field. * Determine the declared (non-generic) type of the wrapped parameter/field.
* @return the declared type, or <code>null</code> if this is {@link TypeDescriptor#NULL} * @return the declared type, or <code>null</code> if this is {@link TypeDescriptor#NULL}
@ -237,21 +203,27 @@ public class TypeDescriptor {
* Returns the Object wrapper type if the underlying type is a primitive. * Returns the Object wrapper type if the underlying type is a primitive.
*/ */
public Class<?> getObjectType() { public Class<?> getObjectType() {
return getType() != null ? ClassUtils.resolvePrimitiveIfNecessary(getType()) : null; return ClassUtils.resolvePrimitiveIfNecessary(getType());
} }
public TypeDescriptor narrowType(Object value) {
if (value == null) {
return this;
}
return new TypeDescriptor(value.getClass(), elementType, mapKeyType, mapValueType, annotations);
}
/** /**
* Returns the name of this type: the fully qualified class name. * Returns the name of this type: the fully qualified class name.
*/ */
public String getName() { public String getName() {
return getType() != null ? ClassUtils.getQualifiedName(getType()) : null; return ClassUtils.getQualifiedName(getType());
} }
/** /**
* Is this type a primitive type? * Is this type a primitive type?
*/ */
public boolean isPrimitive() { public boolean isPrimitive() {
return getType() != null && getType().isPrimitive(); return getType().isPrimitive();
} }
/** /**
@ -281,22 +253,20 @@ public class TypeDescriptor {
* @return true if this type is assignable to the target * @return true if this type is assignable to the target
*/ */
public boolean isAssignableTo(TypeDescriptor targetType) { public boolean isAssignableTo(TypeDescriptor targetType) {
if (this == TypeDescriptor.NULL || targetType == TypeDescriptor.NULL) { boolean typesAssignable = targetType.getObjectType().isAssignableFrom(getObjectType());
if (!typesAssignable) {
return false;
}
if (isArray() && targetType.isArray()) {
return getElementType().isAssignableTo(targetType.getElementType());
}
if (isCollection() && targetType.isCollection()) {
return collectionElementsAssignable(targetType.getElementType());
} else if (isMap() && targetType.isMap()) {
return mapKeysAssignable(targetType.getMapKeyType()) && mapValuesAssignable(targetType.getMapValueType());
}
return true; return true;
} }
if (isCollection() && targetType.isCollection() || isArray() && targetType.isArray()) {
return targetType.getType().isAssignableFrom(getType()) &&
getElementTypeDescriptor().isAssignableTo(targetType.getElementTypeDescriptor());
}
else if (isMap() && targetType.isMap()) {
return targetType.getType().isAssignableFrom(getType()) &&
getMapKeyTypeDescriptor().isAssignableTo(targetType.getMapKeyTypeDescriptor()) &&
getMapValueTypeDescriptor().isAssignableTo(targetType.getMapValueTypeDescriptor());
}
else {
return targetType.getObjectType().isAssignableFrom(getObjectType());
}
}
// indexable type descriptor operations // indexable type descriptor operations
@ -304,53 +274,36 @@ public class TypeDescriptor {
* Is this type a {@link Collection} type? * Is this type a {@link Collection} type?
*/ */
public boolean isCollection() { public boolean isCollection() {
return getType() != null && Collection.class.isAssignableFrom(getType()); return Collection.class.isAssignableFrom(getType());
} }
/** /**
* Is this type an array type? * Is this type an array type?
*/ */
public boolean isArray() { public boolean isArray() {
return getType() != null && getType().isArray(); return getType().isArray();
} }
/** /**
* If this type is a {@link Collection} or array, returns the underlying element type. * If this type is an array, returns the array's component type.
* Returns Object.class if this type is a collection and the element type was not explicitly declared. * If this type is a {@link Collection} and it is parameterized, returns the Collection's element type.
* @return the map element type, or <code>null</code> if not a collection or array. * If the Collection is not parameterized, returns null indicating the element type is not declared.
* @throws IllegalStateException if this descriptor is not for a java.util.Collection or Array * @return the array component type or Collection element type, or <code>null</code> if this type is a Collection but its element type is not parameterized.
* @throws IllegalStateException if this type is not a java.util.Collection or Array type
*/ */
public Class<?> getElementType() { public TypeDescriptor getElementType() {
return getElementTypeDescriptor().getType();
}
/**
* The collection or array element type as a type descriptor.
* Returns TypeDescriptor.valueOf(Object.class) if this type is a collection and the element type is not explicitly declared.
* @throws IllegalStateException if this descriptor is not for a java.util.Collection or Array
*/
public TypeDescriptor getElementTypeDescriptor() {
if (!isCollection() && !isArray()) { if (!isCollection() && !isArray()) {
throw new IllegalStateException("Not a java.util.Collection or Array"); throw new IllegalStateException("Not a java.util.Collection or Array");
} }
return this.elementType; return this.elementType;
} }
/** public TypeDescriptor elementType(Object element) {
* Returns a copy of this type descriptor that has its elementType populated from the specified Collection. if (elementType != null) {
* This property will be set by calculating the "common element type" of the specified Collection. return elementType.narrowType(element);
* For example, if the collection contains String elements, the returned TypeDescriptor will have its elementType set to String. } else {
* This method is designed to be used when converting values read from Collection fields or method return values that are not parameterized e.g. Collection vs. Collection<String> return element != null ? new TypeDescriptor(element.getClass(), null, null, null, annotations) : null;
* In this scenario the elementType will be Object.class before invoking this method.
* @param colection the collection to derive the elementType from
* @return a new TypeDescriptor with the resolved elementType property
* @throws IllegalArgumentException if this is not a type descriptor for a java.util.Collection.
*/
public TypeDescriptor resolveCollectionElementType(Collection<?> collection) {
if (!isCollection()) {
throw new IllegalStateException("Not a java.util.Collection");
} }
return new TypeDescriptor(type, CommonElement.typeDescriptor(collection), mapKeyType, mapValueType, annotations);
} }
// map type descriptor operations // map type descriptor operations
@ -359,69 +312,49 @@ public class TypeDescriptor {
* Is this type a {@link Map} type? * Is this type a {@link Map} type?
*/ */
public boolean isMap() { public boolean isMap() {
return getType() != null && Map.class.isAssignableFrom(getType()); return Map.class.isAssignableFrom(getType());
} }
/** /**
* If this type is a {@link Map}, returns the underlying key type. * If this type is a {@link Map} and its key type is parameterized, returns the map's key type.
* Returns Object.class if this type is a map and its key type was not explicitly declared. * If the Map's key type is not parameterized, returns null indicating the key type is not declared.
* @return the map key type, or <code>null</code> if not a map. * @return the Map key type, or <code>null</code> if this type is a Map but its key type is not parameterized.
* @throws IllegalStateException if this descriptor is not for a java.util.Map * @throws IllegalStateException if this type is not a java.util.Map.
*/ */
public Class<?> getMapKeyType() { public TypeDescriptor getMapKeyType() {
return getMapKeyTypeDescriptor().getType();
}
/**
* The map key type as a type descriptor.
* Returns TypeDescriptor.valueOf(Object.class) if this type is a map and the key type is not explicitly declared.
* @throws IllegalStateException if this descriptor is not for a java.util.Map
*/
public TypeDescriptor getMapKeyTypeDescriptor() {
if (!isMap()) { if (!isMap()) {
throw new IllegalStateException("Not a map"); throw new IllegalStateException("Not a map");
} }
return this.mapKeyType; return this.mapKeyType;
} }
/** public TypeDescriptor mapKeyType(Object mapKey) {
* If this type is a {@link Map}, returns the underlying value type. if (mapKeyType != null) {
* Returns <code>null</code> if this type is not map. return mapKeyType.narrowType(mapKey);
* Returns Object.class if this type is a map and its value type was not explicitly declared. } else {
* @return the map value type, or <code>null</code> if not a map. return mapKey != null ? new TypeDescriptor(mapKey.getClass(), null, null, null, annotations) : null;
* @throws IllegalStateException if this descriptor is not for a java.util.Map }
*/
public Class<?> getMapValueType() {
return getMapValueTypeDescriptor().getType();
} }
/** /**
* The map value type as a type descriptor. * If this type is a {@link Map} and its value type is parameterized, returns the map's value type.
* Returns TypeDescriptor.valueOf(Object.class) if this type is a map and the value type is not explicitly declared. * If the Map's value type is not parameterized, returns null indicating the value type is not declared.
* @throws IllegalStateException if this descriptor is not for a java.util.Map * @return the Map value type, or <code>null</code> if this type is a Map but its value type is not parameterized.
* @throws IllegalStateException if this type is not a java.util.Map.
*/ */
public TypeDescriptor getMapValueTypeDescriptor() { public TypeDescriptor getMapValueType() {
if (!isMap()) { if (!isMap()) {
throw new IllegalStateException("Not a map"); throw new IllegalStateException("Not a map");
} }
return this.mapValueType; return this.mapValueType;
} }
/** public TypeDescriptor mapValueType(Object mapValue) {
* Returns a copy of this type descriptor that has its mapKeyType and mapValueType properties populated from the specified Map. if (mapValueType != null) {
* These properties will be set by calculating the "common element type" of the specified Map's keySet and values collection. return mapValueType.narrowType(mapValue);
* For example, if the Map contains String keys and Integer values, the returned TypeDescriptor will have its mapKeyType set to String and its mapValueType to Integer. } else {
* This method is designed to be used when converting values read from Map fields or method return values that are not parameterized e.g. Map vs. Map<String, Integer>. return mapValue != null ? new TypeDescriptor(mapValue.getClass(), null, null, null, annotations) : null;
* In this scenario the key and value types will be Object.class before invoking this method.
* @param map the map to derive key and value types from
* @return a new TypeDescriptor with the resolved mapKeyType and mapValueType properties
* @throws IllegalArgumentException if this is not a type descriptor for a java.util.Map.
*/
public TypeDescriptor resolveMapKeyValueTypes(Map<?, ?> map) {
if (!isMap()) {
throw new IllegalStateException("Not a java.util.Map");
} }
return new TypeDescriptor(type, elementType, CommonElement.typeDescriptor(map.keySet()), CommonElement.typeDescriptor(map.values()), annotations);
} }
// extending Object // extending Object
@ -450,14 +383,10 @@ public class TypeDescriptor {
} }
public int hashCode() { public int hashCode() {
return (this == TypeDescriptor.NULL ? 0 : getType().hashCode()); return getType().hashCode();
} }
public String toString() { public String toString() {
if (this == TypeDescriptor.NULL) {
return "null";
}
else {
StringBuilder builder = new StringBuilder(); StringBuilder builder = new StringBuilder();
Annotation[] anns = getAnnotations(); Annotation[] anns = getAnnotations();
for (Annotation ann : anns) { for (Annotation ann : anns) {
@ -465,15 +394,14 @@ public class TypeDescriptor {
} }
builder.append(ClassUtils.getQualifiedName(getType())); builder.append(ClassUtils.getQualifiedName(getType()));
if (isMap()) { if (isMap()) {
builder.append("<").append(getMapKeyTypeDescriptor()); builder.append("<").append(wildcard(getMapKeyType()));
builder.append(", ").append(getMapValueTypeDescriptor()).append(">"); builder.append(", ").append(wildcard(getMapValueType())).append(">");
} }
else if (isCollection()) { else if (isCollection()) {
builder.append("<").append(getElementTypeDescriptor()).append(">"); builder.append("<").append(wildcard(getElementType())).append(">");
} }
return builder.toString(); return builder.toString();
} }
}
// package private // package private
@ -485,14 +413,6 @@ public class TypeDescriptor {
this.annotations = descriptor.getAnnotations(); this.annotations = descriptor.getAnnotations();
} }
TypeDescriptor(Class<?> collectionType, TypeDescriptor elementType) {
this(collectionType, elementType, TypeDescriptor.NULL, TypeDescriptor.NULL, EMPTY_ANNOTATION_ARRAY);
}
TypeDescriptor(Class<?> mapType, TypeDescriptor keyType, TypeDescriptor valueType) {
this(mapType, TypeDescriptor.NULL, keyType, valueType, EMPTY_ANNOTATION_ARRAY);
}
static Annotation[] nullSafeAnnotations(Annotation[] annotations) { static Annotation[] nullSafeAnnotations(Annotation[] annotations) {
return annotations != null ? annotations : EMPTY_ANNOTATION_ARRAY; return annotations != null ? annotations : EMPTY_ANNOTATION_ARRAY;
} }
@ -503,8 +423,12 @@ public class TypeDescriptor {
this(new ClassDescriptor(type)); this(new ClassDescriptor(type));
} }
private TypeDescriptor() { private TypeDescriptor(Class<?> collectionType, TypeDescriptor elementType) {
this(null, TypeDescriptor.NULL, TypeDescriptor.NULL, TypeDescriptor.NULL, EMPTY_ANNOTATION_ARRAY); this(collectionType, elementType, null, null, EMPTY_ANNOTATION_ARRAY);
}
private TypeDescriptor(Class<?> mapType, TypeDescriptor keyType, TypeDescriptor valueType) {
this(mapType, null, keyType, valueType, EMPTY_ANNOTATION_ARRAY);
} }
private TypeDescriptor(Class<?> type, TypeDescriptor elementType, TypeDescriptor mapKeyType, TypeDescriptor mapValueType, Annotation[] annotations) { private TypeDescriptor(Class<?> type, TypeDescriptor elementType, TypeDescriptor mapKeyType, TypeDescriptor mapValueType, Annotation[] annotations) {
@ -515,8 +439,6 @@ public class TypeDescriptor {
this.annotations = annotations; this.annotations = annotations;
} }
// internal helpers
private static TypeDescriptor nested(AbstractDescriptor descriptor, int nestingLevel) { private static TypeDescriptor nested(AbstractDescriptor descriptor, int nestingLevel) {
for (int i = 0; i < nestingLevel; i++) { for (int i = 0; i < nestingLevel; i++) {
descriptor = descriptor.nested(); descriptor = descriptor.nested();
@ -524,4 +446,43 @@ public class TypeDescriptor {
return new TypeDescriptor(descriptor); return new TypeDescriptor(descriptor);
} }
// internal helpers
private boolean mapKeysAssignable(TypeDescriptor targetKeyType) {
TypeDescriptor keyType = getMapKeyType();
if (targetKeyType == null) {
return true;
}
if (keyType == null) {
return false;
}
return keyType.isAssignableTo(targetKeyType);
}
private boolean collectionElementsAssignable(TypeDescriptor targetElementType) {
TypeDescriptor elementType = getElementType();
if (targetElementType == null) {
return true;
}
if (elementType == null) {
return false;
}
return elementType.isAssignableTo(targetElementType);
}
private boolean mapValuesAssignable(TypeDescriptor targetValueType) {
TypeDescriptor valueType = getMapValueType();
if (targetValueType == null) {
return true;
}
if (valueType == null) {
return false;
}
return valueType.isAssignableTo(targetValueType);
}
private String wildcard(TypeDescriptor nestedType) {
return nestedType != null ? nestedType.toString() : "?";
}
} }

View File

@ -44,10 +44,6 @@ final class ArrayToArrayConverter implements GenericConverter {
return Collections.singleton(new ConvertiblePair(Object[].class, Object[].class)); return Collections.singleton(new ConvertiblePair(Object[].class, Object[].class));
} }
public boolean matches(TypeDescriptor sourceType, TypeDescriptor targetType) {
return this.helperConverter.matches(sourceType, targetType);
}
public Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType) { public Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType) {
return this.helperConverter.convert(Arrays.asList(ObjectUtils.toObjectArray(source)), sourceType, targetType); return this.helperConverter.convert(Arrays.asList(ObjectUtils.toObjectArray(source)), sourceType, targetType);
} }

View File

@ -24,7 +24,7 @@ import java.util.Set;
import org.springframework.core.CollectionFactory; import org.springframework.core.CollectionFactory;
import org.springframework.core.convert.ConversionService; import org.springframework.core.convert.ConversionService;
import org.springframework.core.convert.TypeDescriptor; 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. * Converts an Array to a Collection.
@ -36,7 +36,7 @@ import org.springframework.core.convert.converter.ConditionalGenericConverter;
* @author Keith Donald * @author Keith Donald
* @since 3.0 * @since 3.0
*/ */
final class ArrayToCollectionConverter implements ConditionalGenericConverter { final class ArrayToCollectionConverter implements GenericConverter {
private final ConversionService conversionService; private final ConversionService conversionService;
@ -48,10 +48,6 @@ final class ArrayToCollectionConverter implements ConditionalGenericConverter {
return Collections.singleton(new ConvertiblePair(Object[].class, Collection.class)); return Collections.singleton(new ConvertiblePair(Object[].class, Collection.class));
} }
public boolean matches(TypeDescriptor sourceType, TypeDescriptor targetType) {
return this.conversionService.canConvert(sourceType.getElementTypeDescriptor(), targetType.getElementTypeDescriptor());
}
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
public Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType) { public Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType) {
if (source == null) { if (source == null) {
@ -59,9 +55,7 @@ final class ArrayToCollectionConverter implements ConditionalGenericConverter {
} }
int length = Array.getLength(source); int length = Array.getLength(source);
Collection<Object> target = CollectionFactory.createCollection(targetType.getType(), length); Collection<Object> target = CollectionFactory.createCollection(targetType.getType(), length);
TypeDescriptor sourceElementType = sourceType.getElementTypeDescriptor(); if (targetType.getElementType() == null) {
TypeDescriptor targetElementType = targetType.getElementTypeDescriptor();
if (Object.class.equals(targetElementType.getType())) {
for (int i = 0; i < length; i++) { for (int i = 0; i < length; i++) {
Object sourceElement = Array.get(source, i); Object sourceElement = Array.get(source, i);
target.add(sourceElement); target.add(sourceElement);
@ -69,7 +63,7 @@ final class ArrayToCollectionConverter implements ConditionalGenericConverter {
} else { } else {
for (int i = 0; i < length; i++) { for (int i = 0; i < length; i++) {
Object sourceElement = Array.get(source, i); Object sourceElement = Array.get(source, i);
Object targetElement = this.conversionService.convert(sourceElement, sourceElementType, targetElementType); Object targetElement = this.conversionService.convert(sourceElement, sourceType.elementType(sourceElement), targetType.getElementType());
target.add(targetElement); target.add(targetElement);
} }
} }

View File

@ -22,7 +22,7 @@ import java.util.Set;
import org.springframework.core.convert.ConversionService; import org.springframework.core.convert.ConversionService;
import org.springframework.core.convert.TypeDescriptor; import org.springframework.core.convert.TypeDescriptor;
import org.springframework.core.convert.converter.ConditionalGenericConverter; import org.springframework.core.convert.converter.GenericConverter;
import org.springframework.util.ObjectUtils; import org.springframework.util.ObjectUtils;
/** /**
@ -32,7 +32,7 @@ import org.springframework.util.ObjectUtils;
* @author Keith Donald * @author Keith Donald
* @since 3.0 * @since 3.0
*/ */
final class ArrayToObjectConverter implements ConditionalGenericConverter { final class ArrayToObjectConverter implements GenericConverter {
private final CollectionToObjectConverter helperConverter; private final CollectionToObjectConverter helperConverter;
@ -44,10 +44,6 @@ final class ArrayToObjectConverter implements ConditionalGenericConverter {
return Collections.singleton(new ConvertiblePair(Object[].class, Object.class)); return Collections.singleton(new ConvertiblePair(Object[].class, Object.class));
} }
public boolean matches(TypeDescriptor sourceType, TypeDescriptor targetType) {
return this.helperConverter.matches(sourceType, targetType);
}
public Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType) { public Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType) {
return this.helperConverter.convert(Arrays.asList(ObjectUtils.toObjectArray(source)), sourceType, targetType); return this.helperConverter.convert(Arrays.asList(ObjectUtils.toObjectArray(source)), sourceType, targetType);
} }

View File

@ -22,7 +22,7 @@ import java.util.Set;
import org.springframework.core.convert.ConversionService; import org.springframework.core.convert.ConversionService;
import org.springframework.core.convert.TypeDescriptor; import org.springframework.core.convert.TypeDescriptor;
import org.springframework.core.convert.converter.ConditionalGenericConverter; import org.springframework.core.convert.converter.GenericConverter;
import org.springframework.util.ObjectUtils; import org.springframework.util.ObjectUtils;
/** /**
@ -32,7 +32,7 @@ import org.springframework.util.ObjectUtils;
* @author Keith Donald * @author Keith Donald
* @since 3.0 * @since 3.0
*/ */
final class ArrayToStringConverter implements ConditionalGenericConverter { final class ArrayToStringConverter implements GenericConverter {
private final CollectionToStringConverter helperConverter; private final CollectionToStringConverter helperConverter;
@ -44,10 +44,6 @@ final class ArrayToStringConverter implements ConditionalGenericConverter {
return Collections.singleton(new ConvertiblePair(Object[].class, String.class)); return Collections.singleton(new ConvertiblePair(Object[].class, String.class));
} }
public boolean matches(TypeDescriptor sourceType, TypeDescriptor targetType) {
return this.helperConverter.matches(sourceType, targetType);
}
public Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType) { public Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType) {
return this.helperConverter.convert(Arrays.asList(ObjectUtils.toObjectArray(source)), sourceType, targetType); return this.helperConverter.convert(Arrays.asList(ObjectUtils.toObjectArray(source)), sourceType, targetType);
} }

View File

@ -23,7 +23,7 @@ import java.util.Set;
import org.springframework.core.convert.ConversionService; import org.springframework.core.convert.ConversionService;
import org.springframework.core.convert.TypeDescriptor; 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. * Converts a Collection to an array.
@ -36,7 +36,7 @@ import org.springframework.core.convert.converter.ConditionalGenericConverter;
* @author Keith Donald * @author Keith Donald
* @since 3.0 * @since 3.0
*/ */
final class CollectionToArrayConverter implements ConditionalGenericConverter { final class CollectionToArrayConverter implements GenericConverter {
private final ConversionService conversionService; private final ConversionService conversionService;
@ -48,20 +48,15 @@ final class CollectionToArrayConverter implements ConditionalGenericConverter {
return Collections.singleton(new ConvertiblePair(Collection.class, Object[].class)); return Collections.singleton(new ConvertiblePair(Collection.class, Object[].class));
} }
public boolean matches(TypeDescriptor sourceType, TypeDescriptor targetType) {
return this.conversionService.canConvert(sourceType.getElementTypeDescriptor(), targetType.getElementTypeDescriptor());
}
public Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType) { public Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType) {
if (source == null) { if (source == null) {
return null; return null;
} }
Collection<?> sourceCollection = (Collection<?>) source; Collection<?> sourceCollection = (Collection<?>) source;
TypeDescriptor targetElementType = targetType.getElementTypeDescriptor(); Object array = Array.newInstance(targetType.getElementType().getType(), sourceCollection.size());
Object array = Array.newInstance(targetElementType.getType(), sourceCollection.size());
int i = 0; int i = 0;
for (Object sourceElement : sourceCollection) { for (Object sourceElement : sourceCollection) {
Object targetElement = this.conversionService.convert(sourceElement, sourceType.getElementTypeDescriptor(), targetElementType); Object targetElement = this.conversionService.convert(sourceElement, sourceType.elementType(sourceElement), targetType.getElementType());
Array.set(array, i++, targetElement); Array.set(array, i++, targetElement);
} }
return array; return array;

View File

@ -23,7 +23,7 @@ import java.util.Set;
import org.springframework.core.CollectionFactory; import org.springframework.core.CollectionFactory;
import org.springframework.core.convert.ConversionService; import org.springframework.core.convert.ConversionService;
import org.springframework.core.convert.TypeDescriptor; 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. * Converts from a Collection to another Collection.
@ -36,7 +36,7 @@ import org.springframework.core.convert.converter.ConditionalGenericConverter;
* @author Keith Donald * @author Keith Donald
* @since 3.0 * @since 3.0
*/ */
final class CollectionToCollectionConverter implements ConditionalGenericConverter { final class CollectionToCollectionConverter implements GenericConverter {
private final ConversionService conversionService; private final ConversionService conversionService;
@ -48,12 +48,6 @@ final class CollectionToCollectionConverter implements ConditionalGenericConvert
return Collections.singleton(new ConvertiblePair(Collection.class, Collection.class)); return Collections.singleton(new ConvertiblePair(Collection.class, Collection.class));
} }
public boolean matches(TypeDescriptor sourceType, TypeDescriptor targetType) {
TypeDescriptor sourceElementType = sourceType.getElementTypeDescriptor();
TypeDescriptor targetElementType = targetType.getElementTypeDescriptor();
return this.conversionService.canConvert(sourceElementType, targetElementType);
}
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
public Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType) { public Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType) {
if (source == null) { if (source == null) {
@ -61,15 +55,13 @@ final class CollectionToCollectionConverter implements ConditionalGenericConvert
} }
Collection<?> sourceCollection = (Collection<?>) source; Collection<?> sourceCollection = (Collection<?>) source;
Collection<Object> target = CollectionFactory.createCollection(targetType.getType(), sourceCollection.size()); Collection<Object> target = CollectionFactory.createCollection(targetType.getType(), sourceCollection.size());
TypeDescriptor sourceElementType = sourceType.getElementTypeDescriptor(); if (targetType.getElementType() == null) {
TypeDescriptor targetElementType = targetType.getElementTypeDescriptor(); for (Object element : sourceCollection) {
if (Object.class.equals(targetElementType.getType())) { target.add(element);
for (Object sourceElement : sourceCollection) {
target.add(sourceElement);
} }
} else { } else {
for (Object sourceElement : sourceCollection) { for (Object sourceElement : sourceCollection) {
Object targetElement = this.conversionService.convert(sourceElement, sourceElementType, targetElementType); Object targetElement = this.conversionService.convert(sourceElement, sourceType.elementType(sourceElement), targetType.getElementType());
target.add(targetElement); target.add(targetElement);
} }
} }

View File

@ -22,7 +22,7 @@ import java.util.Set;
import org.springframework.core.convert.ConversionService; import org.springframework.core.convert.ConversionService;
import org.springframework.core.convert.TypeDescriptor; 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. * Converts a Collection to an Object by returning the first collection element after converting it to the desired targetType.
@ -30,7 +30,7 @@ import org.springframework.core.convert.converter.ConditionalGenericConverter;
* @author Keith Donald * @author Keith Donald
* @since 3.0 * @since 3.0
*/ */
final class CollectionToObjectConverter implements ConditionalGenericConverter { final class CollectionToObjectConverter implements GenericConverter {
private final ConversionService conversionService; private final ConversionService conversionService;
@ -42,10 +42,6 @@ final class CollectionToObjectConverter implements ConditionalGenericConverter {
return Collections.singleton(new ConvertiblePair(Collection.class, Object.class)); return Collections.singleton(new ConvertiblePair(Collection.class, Object.class));
} }
public boolean matches(TypeDescriptor sourceType, TypeDescriptor targetType) {
return this.conversionService.canConvert(sourceType.getElementTypeDescriptor(), targetType);
}
public Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType) { public Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType) {
if (source == null) { if (source == null) {
return null; return null;
@ -55,7 +51,7 @@ final class CollectionToObjectConverter implements ConditionalGenericConverter {
return null; return null;
} }
Object firstElement = sourceCollection.iterator().next(); Object firstElement = sourceCollection.iterator().next();
return this.conversionService.convert(firstElement, sourceType.getElementTypeDescriptor(), targetType); return this.conversionService.convert(firstElement, sourceType.elementType(firstElement), targetType);
} }
} }

View File

@ -22,7 +22,7 @@ import java.util.Set;
import org.springframework.core.convert.ConversionService; import org.springframework.core.convert.ConversionService;
import org.springframework.core.convert.TypeDescriptor; 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. * Converts a Collection to a comma-delimited String.
@ -30,7 +30,7 @@ import org.springframework.core.convert.converter.ConditionalGenericConverter;
* @author Keith Donald * @author Keith Donald
* @since 3.0 * @since 3.0
*/ */
final class CollectionToStringConverter implements ConditionalGenericConverter { final class CollectionToStringConverter implements GenericConverter {
private static final String DELIMITER = ","; private static final String DELIMITER = ",";
@ -44,10 +44,6 @@ final class CollectionToStringConverter implements ConditionalGenericConverter {
return Collections.singleton(new ConvertiblePair(Collection.class, String.class)); return Collections.singleton(new ConvertiblePair(Collection.class, String.class));
} }
public boolean matches(TypeDescriptor sourceType, TypeDescriptor targetType) {
return this.conversionService.canConvert(sourceType.getElementTypeDescriptor(), targetType);
}
public Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType) { public Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType) {
if (source == null) { if (source == null) {
return null; return null;
@ -56,17 +52,17 @@ final class CollectionToStringConverter implements ConditionalGenericConverter {
if (sourceCollection.size() == 0) { if (sourceCollection.size() == 0) {
return ""; return "";
} }
StringBuilder string = new StringBuilder(); StringBuilder sb = new StringBuilder();
int i = 0; int i = 0;
for (Object sourceElement : sourceCollection) { for (Object sourceElement : sourceCollection) {
if (i > 0) { if (i > 0) {
string.append(DELIMITER); sb.append(DELIMITER);
} }
Object targetElement = this.conversionService.convert(sourceElement, sourceType.getElementTypeDescriptor(), targetType); Object targetElement = this.conversionService.convert(sourceElement, sourceType.elementType(sourceElement), targetType);
string.append(targetElement); sb.append(targetElement);
i++; i++;
} }
return string.toString(); return sb.toString();
} }
} }

View File

@ -138,18 +138,16 @@ public class GenericConversionService implements ConfigurableConversionService {
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
public <T> T convert(Object source, Class<T> targetType) { public <T> T convert(Object source, Class<T> targetType) {
if (targetType == null) {
throw new IllegalArgumentException("The targetType to convert to cannot be null");
}
return (T) convert(source, TypeDescriptor.forObject(source), TypeDescriptor.valueOf(targetType)); return (T) convert(source, TypeDescriptor.forObject(source), TypeDescriptor.valueOf(targetType));
} }
public boolean canConvert(TypeDescriptor sourceType, TypeDescriptor targetType) { public boolean canConvert(TypeDescriptor sourceType, TypeDescriptor targetType) {
assertNotNull(sourceType, targetType);
if (logger.isTraceEnabled()) { if (logger.isTraceEnabled()) {
logger.trace("Checking if I can convert " + sourceType + " to " + targetType); logger.trace("Checking if I can convert " + sourceType + " to " + targetType);
} }
if (sourceType == TypeDescriptor.NULL || targetType == TypeDescriptor.NULL) {
logger.trace("Yes, I can convert");
return true;
}
GenericConverter converter = getConverter(sourceType, targetType); GenericConverter converter = getConverter(sourceType, targetType);
if (converter != null) { if (converter != null) {
logger.trace("Yes, I can convert"); logger.trace("Yes, I can convert");
@ -162,21 +160,19 @@ public class GenericConversionService implements ConfigurableConversionService {
} }
public Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType) { public Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType) {
assertNotNull(sourceType, targetType); if (targetType == null) {
if (logger.isDebugEnabled()) { throw new IllegalArgumentException("The targetType to convert to cannot be null");
logger.debug("Converting value " + StylerUtils.style(source) + " of " + sourceType + " to " + targetType);
} }
if (sourceType == TypeDescriptor.NULL) { if (sourceType == null) {
Assert.isTrue(source == null, "The source must be [null] if sourceType == [null]"); Assert.isTrue(source == null, "The source must be [null] if sourceType == [null]");
return handleResult(sourceType, targetType, convertNullSource(sourceType, targetType)); return handleResult(sourceType, targetType, convertNullSource(sourceType, targetType));
} }
if (targetType == TypeDescriptor.NULL) {
logger.debug("Converted to null");
return null;
}
if (source != null && !sourceType.getObjectType().isInstance(source)) { if (source != null && !sourceType.getObjectType().isInstance(source)) {
throw new IllegalArgumentException("The source to convert from must be an instance of " + sourceType + "; instead it was a " + source.getClass().getName()); throw new IllegalArgumentException("The source to convert from must be an instance of " + sourceType + "; instead it was a " + source.getClass().getName());
} }
if (logger.isDebugEnabled()) {
logger.debug("Converting value " + StylerUtils.style(source) + " of " + sourceType + " to " + targetType);
}
GenericConverter converter = getConverter(sourceType, targetType); GenericConverter converter = getConverter(sourceType, targetType);
if (converter != null) { if (converter != null) {
return handleResult(sourceType, targetType, ConversionUtils.invokeConverter(converter, source, sourceType, targetType)); return handleResult(sourceType, targetType, ConversionUtils.invokeConverter(converter, source, sourceType, targetType));
@ -236,32 +232,28 @@ public class GenericConversionService implements ConfigurableConversionService {
if (logger.isTraceEnabled()) { if (logger.isTraceEnabled()) {
logger.trace("Matched cached converter " + converter); logger.trace("Matched cached converter " + converter);
} }
return (converter != NO_MATCH ? converter : null); return converter != NO_MATCH ? converter : null;
} }
else { else {
converter = findConverterForClassPair(sourceType, targetType); converter = findConverterForClassPair(sourceType, targetType);
if (converter != null) { if (converter == null) {
if (logger.isTraceEnabled()) {
logger.trace("Caching under " + key);
}
this.converterCache.put(key, converter);
return converter;
}
converter = getDefaultConverter(sourceType, targetType); converter = getDefaultConverter(sourceType, targetType);
}
if (converter != null) { if (converter != null) {
if (logger.isTraceEnabled()) { if (logger.isTraceEnabled()) {
logger.trace("Caching under " + key); logger.trace("Caching matched Converter under key " + key);
} }
this.converterCache.put(key, converter); this.converterCache.put(key, converter);
return converter; return converter;
} } else {
if (logger.isTraceEnabled()) { if (logger.isTraceEnabled()) {
logger.trace("Caching NO_MATCH under " + key); logger.trace("Caching Converter [NO_MATCH] result under key " + key);
} }
this.converterCache.put(key, NO_MATCH); this.converterCache.put(key, NO_MATCH);
return null; return null;
} }
} }
}
/** /**
* Return the default converter if no converter is found for the given sourceType/targetType pair. * Return the default converter if no converter is found for the given sourceType/targetType pair.
@ -312,11 +304,6 @@ public class GenericConversionService implements ConfigurableConversionService {
return sourceMap; return sourceMap;
} }
private void assertNotNull(TypeDescriptor sourceType, TypeDescriptor targetType) {
Assert.notNull(sourceType, "The sourceType to convert from is required");
Assert.notNull(targetType, "The targetType to convert to is required");
}
private GenericConverter findConverterForClassPair(TypeDescriptor sourceType, TypeDescriptor targetType) { private GenericConverter findConverterForClassPair(TypeDescriptor sourceType, TypeDescriptor targetType) {
Class<?> sourceObjectType = sourceType.getObjectType(); Class<?> sourceObjectType = sourceType.getObjectType();
if (sourceObjectType.isInterface()) { if (sourceObjectType.isInterface()) {

View File

@ -23,7 +23,7 @@ import java.util.Set;
import org.springframework.core.CollectionFactory; import org.springframework.core.CollectionFactory;
import org.springframework.core.convert.ConversionService; import org.springframework.core.convert.ConversionService;
import org.springframework.core.convert.TypeDescriptor; import org.springframework.core.convert.TypeDescriptor;
import org.springframework.core.convert.converter.ConditionalGenericConverter; import org.springframework.core.convert.converter.GenericConverter;
/** /**
* Converts a Map to another Map. * Converts a Map to another Map.
@ -36,7 +36,7 @@ import org.springframework.core.convert.converter.ConditionalGenericConverter;
* @author Keith Donald * @author Keith Donald
* @since 3.0 * @since 3.0
*/ */
final class MapToMapConverter implements ConditionalGenericConverter { final class MapToMapConverter implements GenericConverter {
private final ConversionService conversionService; private final ConversionService conversionService;
@ -48,11 +48,6 @@ final class MapToMapConverter implements ConditionalGenericConverter {
return Collections.singleton(new ConvertiblePair(Map.class, Map.class)); return Collections.singleton(new ConvertiblePair(Map.class, Map.class));
} }
public boolean matches(TypeDescriptor sourceType, TypeDescriptor targetType) {
return this.conversionService.canConvert(sourceType.getMapKeyTypeDescriptor(), targetType.getMapKeyTypeDescriptor()) &&
this.conversionService.canConvert(sourceType.getMapValueTypeDescriptor(), targetType.getMapValueTypeDescriptor());
}
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
public Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType) { public Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType) {
if (source == null) { if (source == null) {
@ -60,24 +55,30 @@ final class MapToMapConverter implements ConditionalGenericConverter {
} }
Map<Object, Object> sourceMap = (Map<Object, Object>) source; Map<Object, Object> sourceMap = (Map<Object, Object>) source;
Map<Object, Object> targetMap = CollectionFactory.createMap(targetType.getType(), sourceMap.size()); Map<Object, Object> targetMap = CollectionFactory.createMap(targetType.getType(), sourceMap.size());
TypeDescriptor sourceKeyType = sourceType.getMapKeyTypeDescriptor();
TypeDescriptor targetKeyType = targetType.getMapKeyTypeDescriptor();
TypeDescriptor sourceValueType = sourceType.getMapValueTypeDescriptor();
TypeDescriptor targetValueType = targetType.getMapValueTypeDescriptor();
if (Object.class.equals(targetKeyType.getType()) && Object.class.equals(targetValueType.getType())) {
for (Map.Entry<Object, Object> entry : sourceMap.entrySet()) {
targetMap.put(entry.getKey(), entry.getValue());
}
} else {
for (Map.Entry<Object, Object> entry : sourceMap.entrySet()) { for (Map.Entry<Object, Object> entry : sourceMap.entrySet()) {
Object sourceKey = entry.getKey(); Object sourceKey = entry.getKey();
Object sourceValue = entry.getValue(); Object sourceValue = entry.getValue();
Object targetKey = this.conversionService.convert(sourceKey, sourceKeyType, targetKeyType); Object targetKey = convertKey(sourceKey, sourceType, targetType.getMapKeyType());
Object targetValue = this.conversionService.convert(sourceValue, sourceValueType, targetValueType); Object targetValue = convertValue(sourceValue, sourceType, targetType.getMapValueType());
targetMap.put(targetKey, targetValue); targetMap.put(targetKey, targetValue);
} }
}
return targetMap; return targetMap;
} }
// internal helpers
private Object convertKey(Object sourceKey, TypeDescriptor sourceType, TypeDescriptor targetType) {
if (targetType == null) {
return sourceKey;
}
return this.conversionService.convert(sourceKey, sourceType.mapKeyType(sourceKey), targetType);
}
private Object convertValue(Object sourceValue, TypeDescriptor sourceType, TypeDescriptor targetType) {
if (targetType == null) {
return sourceValue;
}
return this.conversionService.convert(sourceValue, sourceType.mapValueType(sourceValue), targetType);
}
} }

View File

@ -22,7 +22,7 @@ import java.util.Set;
import org.springframework.core.convert.ConversionService; import org.springframework.core.convert.ConversionService;
import org.springframework.core.convert.TypeDescriptor; 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. * Converts an Object to a single-element Array containing the Object.
@ -31,7 +31,7 @@ import org.springframework.core.convert.converter.ConditionalGenericConverter;
* @author Keith Donald * @author Keith Donald
* @since 3.0 * @since 3.0
*/ */
final class ObjectToArrayConverter implements ConditionalGenericConverter { final class ObjectToArrayConverter implements GenericConverter {
private final ConversionService conversionService; private final ConversionService conversionService;
@ -43,16 +43,12 @@ final class ObjectToArrayConverter implements ConditionalGenericConverter {
return Collections.singleton(new ConvertiblePair(Object.class, Object[].class)); return Collections.singleton(new ConvertiblePair(Object.class, Object[].class));
} }
public boolean matches(TypeDescriptor sourceType, TypeDescriptor targetType) {
return this.conversionService.canConvert(sourceType, targetType.getElementTypeDescriptor());
}
public Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType) { public Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType) {
if (source == null) { if (source == null) {
return null; return null;
} }
Object target = Array.newInstance(targetType.getElementType(), 1); Object target = Array.newInstance(targetType.getElementType().getType(), 1);
Object targetElement = this.conversionService.convert(source, sourceType, targetType.getElementTypeDescriptor()); Object targetElement = this.conversionService.convert(source, sourceType, targetType.getElementType());
Array.set(target, 0, targetElement); Array.set(target, 0, targetElement);
return target; return target;
} }

View File

@ -23,7 +23,7 @@ import java.util.Set;
import org.springframework.core.CollectionFactory; import org.springframework.core.CollectionFactory;
import org.springframework.core.convert.ConversionService; import org.springframework.core.convert.ConversionService;
import org.springframework.core.convert.TypeDescriptor; 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. * Converts an Object to a single-element Collection containing the Object.
@ -33,7 +33,7 @@ import org.springframework.core.convert.converter.ConditionalGenericConverter;
* @author Juergen Hoeller * @author Juergen Hoeller
* @since 3.0 * @since 3.0
*/ */
final class ObjectToCollectionConverter implements ConditionalGenericConverter { final class ObjectToCollectionConverter implements GenericConverter {
private final ConversionService conversionService; private final ConversionService conversionService;
@ -45,22 +45,17 @@ final class ObjectToCollectionConverter implements ConditionalGenericConverter {
return Collections.singleton(new ConvertiblePair(Object.class, Collection.class)); return Collections.singleton(new ConvertiblePair(Object.class, Collection.class));
} }
public boolean matches(TypeDescriptor sourceType, TypeDescriptor targetType) {
return this.conversionService.canConvert(sourceType, targetType.getElementTypeDescriptor());
}
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
public Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType) { public Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType) {
if (source == null) { if (source == null) {
return null; return null;
} }
Collection<Object> target = CollectionFactory.createCollection(targetType.getType(), 1); Collection<Object> target = CollectionFactory.createCollection(targetType.getType(), 1);
TypeDescriptor targetElementType = targetType.getElementTypeDescriptor(); if (targetType.getElementType() == null || targetType.getElementType().isCollection()) {
// Avoid potential recursion....
if (targetElementType.isCollection()) {
target.add(source); target.add(source);
} else { } else {
target.add(this.conversionService.convert(source, sourceType, targetElementType)); Object singleElement = this.conversionService.convert(source, sourceType, targetType.getElementType());
target.add(singleElement);
} }
return target; return target;
} }

View File

@ -48,9 +48,7 @@ final class ObjectToObjectConverter implements ConditionalGenericConverter {
} }
public boolean matches(TypeDescriptor sourceType, TypeDescriptor targetType) { public boolean matches(TypeDescriptor sourceType, TypeDescriptor targetType) {
Class<?> source = sourceType.getType(); return !sourceType.equals(targetType) && hasValueOfMethodOrConstructor(targetType.getType(), sourceType.getType());
Class<?> target = targetType.getType();
return !source.equals(target) && hasValueOfMethodOrConstructor(target, source);
} }
public Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType) { public Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType) {
@ -79,16 +77,16 @@ final class ObjectToObjectConverter implements ConditionalGenericConverter {
") method or Constructor(" + sourceClass.getName() + ") exists on " + targetClass.getName()); ") method or Constructor(" + sourceClass.getName() + ") exists on " + targetClass.getName());
} }
public static boolean hasValueOfMethodOrConstructor(Class<?> targetClass, Class<?> sourceClass) { static boolean hasValueOfMethodOrConstructor(Class<?> clazz, Class<?> sourceParameterType) {
return getValueOfMethodOn(targetClass, sourceClass) != null || getConstructor(targetClass, sourceClass) != null; return getValueOfMethodOn(clazz, sourceParameterType) != null || getConstructor(clazz, sourceParameterType) != null;
} }
private static Method getValueOfMethodOn(Class<?> targetClass, Class<?> sourceClass) { private static Method getValueOfMethodOn(Class<?> clazz, Class<?> sourceParameterType) {
return ClassUtils.getStaticMethod(targetClass, "valueOf", sourceClass); return ClassUtils.getStaticMethod(clazz, "valueOf", sourceParameterType);
} }
private static Constructor<?> getConstructor(Class<?> targetClass, Class<?> sourceClass) { private static Constructor<?> getConstructor(Class<?> clazz, Class<?> sourceParameterType) {
return ClassUtils.getConstructorIfAvailable(targetClass, sourceClass); return ClassUtils.getConstructorIfAvailable(clazz, sourceParameterType);
} }
} }

View File

@ -22,7 +22,7 @@ import java.util.Set;
import org.springframework.core.convert.ConversionService; import org.springframework.core.convert.ConversionService;
import org.springframework.core.convert.TypeDescriptor; 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; import org.springframework.util.StringUtils;
/** /**
@ -31,7 +31,7 @@ import org.springframework.util.StringUtils;
* @author Keith Donald * @author Keith Donald
* @since 3.0 * @since 3.0
*/ */
final class StringToArrayConverter implements ConditionalGenericConverter { final class StringToArrayConverter implements GenericConverter {
private final ConversionService conversionService; private final ConversionService conversionService;
@ -43,20 +43,16 @@ final class StringToArrayConverter implements ConditionalGenericConverter {
return Collections.singleton(new ConvertiblePair(String.class, Object[].class)); return Collections.singleton(new ConvertiblePair(String.class, Object[].class));
} }
public boolean matches(TypeDescriptor sourceType, TypeDescriptor targetType) {
return this.conversionService.canConvert(sourceType, targetType.getElementTypeDescriptor());
}
public Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType) { public Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType) {
if (source == null) { if (source == null) {
return null; return null;
} }
String string = (String) source; String string = (String) source;
String[] fields = StringUtils.commaDelimitedListToStringArray(string); String[] fields = StringUtils.commaDelimitedListToStringArray(string);
Object target = Array.newInstance(targetType.getElementType(), fields.length); Object target = Array.newInstance(targetType.getElementType().getType(), fields.length);
for (int i = 0; i < fields.length; i++) { for (int i = 0; i < fields.length; i++) {
String sourceElement = fields[i]; String sourceElement = fields[i];
Object targetElement = this.conversionService.convert(sourceElement.trim(), sourceType, targetType.getElementTypeDescriptor()); Object targetElement = this.conversionService.convert(sourceElement.trim(), sourceType, targetType.getElementType());
Array.set(target, i, targetElement); Array.set(target, i, targetElement);
} }
return target; return target;

View File

@ -45,7 +45,10 @@ final class StringToCollectionConverter implements ConditionalGenericConverter {
} }
public boolean matches(TypeDescriptor sourceType, TypeDescriptor targetType) { public boolean matches(TypeDescriptor sourceType, TypeDescriptor targetType) {
return this.conversionService.canConvert(sourceType, targetType.getElementTypeDescriptor()); if (targetType.getElementType() == null) {
return true;
}
return this.conversionService.canConvert(sourceType, targetType.getElementType());
} }
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
@ -56,10 +59,16 @@ final class StringToCollectionConverter implements ConditionalGenericConverter {
String string = (String) source; String string = (String) source;
String[] fields = StringUtils.commaDelimitedListToStringArray(string); String[] fields = StringUtils.commaDelimitedListToStringArray(string);
Collection<Object> target = CollectionFactory.createCollection(targetType.getType(), fields.length); Collection<Object> target = CollectionFactory.createCollection(targetType.getType(), fields.length);
for (String sourceElement : fields) { if (targetType.getElementType() == null) {
Object targetElement = this.conversionService.convert(sourceElement.trim(), sourceType, targetType.getElementTypeDescriptor()); for (String field : fields) {
target.add(field.trim());
}
} else {
for (String field : fields) {
Object targetElement = this.conversionService.convert(field.trim(), sourceType, targetType.getElementType());
target.add(targetElement); target.add(targetElement);
} }
}
return target; return target;
} }

View File

@ -28,7 +28,6 @@ import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy; import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target; import java.lang.annotation.Target;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection; import java.util.Collection;
import java.util.Date; import java.util.Date;
import java.util.HashMap; import java.util.HashMap;
@ -37,7 +36,6 @@ import java.util.Map;
import org.junit.Ignore; import org.junit.Ignore;
import org.junit.Test; import org.junit.Test;
import org.springframework.core.GenericCollectionTypeResolver;
import org.springframework.core.MethodParameter; import org.springframework.core.MethodParameter;
/** /**
@ -62,18 +60,6 @@ public class TypeDescriptorTests {
public Map<String, List<Integer>> nestedMapField = new HashMap<String, List<Integer>>(); public Map<String, List<Integer>> nestedMapField = new HashMap<String, List<Integer>>();
@Test
public void nullTypeDescriptor() {
TypeDescriptor desc = TypeDescriptor.NULL;
assertEquals(false, desc.isMap());
assertEquals(false, desc.isCollection());
assertEquals(false, desc.isArray());
assertEquals(null, desc.getType());
assertEquals(null, desc.getObjectType());
assertEquals(null, desc.getName());
assertEquals(0, desc.getAnnotations().length);
}
@Test @Test
public void parameterPrimitive() throws Exception { public void parameterPrimitive() throws Exception {
TypeDescriptor desc = new TypeDescriptor(new MethodParameter(getClass().getMethod("testParameterPrimitive", int.class), 0)); TypeDescriptor desc = new TypeDescriptor(new MethodParameter(getClass().getMethod("testParameterPrimitive", int.class), 0));
@ -121,12 +107,12 @@ public class TypeDescriptorTests {
assertEquals(0, desc.getAnnotations().length); assertEquals(0, desc.getAnnotations().length);
assertTrue(desc.isCollection()); assertTrue(desc.isCollection());
assertFalse(desc.isArray()); assertFalse(desc.isArray());
assertEquals(List.class, desc.getElementType()); assertEquals(List.class, desc.getElementType().getType());
assertEquals(TypeDescriptor.nested(methodParameter, 1), desc.getElementTypeDescriptor()); assertEquals(TypeDescriptor.nested(methodParameter, 1), desc.getElementType());
assertEquals(TypeDescriptor.nested(methodParameter, 2), desc.getElementTypeDescriptor().getElementTypeDescriptor()); assertEquals(TypeDescriptor.nested(methodParameter, 2), desc.getElementType().getElementType());
assertEquals(TypeDescriptor.nested(methodParameter, 3), desc.getElementTypeDescriptor().getElementTypeDescriptor().getMapValueTypeDescriptor()); assertEquals(TypeDescriptor.nested(methodParameter, 3), desc.getElementType().getElementType().getMapValueType());
assertEquals(Integer.class, desc.getElementTypeDescriptor().getElementTypeDescriptor().getMapKeyTypeDescriptor().getType()); assertEquals(Integer.class, desc.getElementType().getElementType().getMapKeyType().getType());
assertEquals(Enum.class, desc.getElementTypeDescriptor().getElementTypeDescriptor().getMapValueTypeDescriptor().getType()); assertEquals(Enum.class, desc.getElementType().getElementType().getMapValueType().getType());
assertFalse(desc.isMap()); assertFalse(desc.isMap());
} }
@ -141,13 +127,12 @@ public class TypeDescriptorTests {
assertEquals(List.class, desc.getType()); assertEquals(List.class, desc.getType());
assertEquals(List.class, desc.getObjectType()); assertEquals(List.class, desc.getObjectType());
assertEquals("java.util.List", desc.getName()); assertEquals("java.util.List", desc.getName());
assertEquals("java.util.List<java.lang.Object>", desc.toString()); assertEquals("java.util.List<?>", desc.toString());
assertTrue(!desc.isPrimitive()); assertTrue(!desc.isPrimitive());
assertEquals(0, desc.getAnnotations().length); assertEquals(0, desc.getAnnotations().length);
assertTrue(desc.isCollection()); assertTrue(desc.isCollection());
assertFalse(desc.isArray()); assertFalse(desc.isArray());
assertEquals(Object.class, desc.getElementType()); assertNull(desc.getElementType());
assertEquals(TypeDescriptor.valueOf(Object.class), desc.getElementTypeDescriptor());
assertFalse(desc.isMap()); assertFalse(desc.isMap());
} }
@ -167,8 +152,8 @@ public class TypeDescriptorTests {
assertEquals(0, desc.getAnnotations().length); assertEquals(0, desc.getAnnotations().length);
assertFalse(desc.isCollection()); assertFalse(desc.isCollection());
assertTrue(desc.isArray()); assertTrue(desc.isArray());
assertEquals(Integer.class, desc.getElementType()); assertEquals(Integer.class, desc.getElementType().getType());
assertEquals(TypeDescriptor.valueOf(Integer.class), desc.getElementTypeDescriptor()); assertEquals(TypeDescriptor.valueOf(Integer.class), desc.getElementType());
assertFalse(desc.isMap()); assertFalse(desc.isMap());
} }
@ -189,11 +174,11 @@ public class TypeDescriptorTests {
assertFalse(desc.isCollection()); assertFalse(desc.isCollection());
assertFalse(desc.isArray()); assertFalse(desc.isArray());
assertTrue(desc.isMap()); assertTrue(desc.isMap());
assertEquals(TypeDescriptor.nested(methodParameter, 1), desc.getMapValueTypeDescriptor()); assertEquals(TypeDescriptor.nested(methodParameter, 1), desc.getMapValueType());
assertEquals(TypeDescriptor.nested(methodParameter, 2), desc.getMapValueTypeDescriptor().getElementTypeDescriptor()); assertEquals(TypeDescriptor.nested(methodParameter, 2), desc.getMapValueType().getElementType());
assertEquals(Integer.class, desc.getMapKeyTypeDescriptor().getType()); assertEquals(Integer.class, desc.getMapKeyType().getType());
assertEquals(List.class, desc.getMapValueTypeDescriptor().getType()); assertEquals(List.class, desc.getMapValueType().getType());
assertEquals(String.class, desc.getMapValueTypeDescriptor().getElementTypeDescriptor().getType()); assertEquals(String.class, desc.getMapValueType().getElementType().getType());
} }
public void testParameterMap(Map<Integer, List<String>> map) { public void testParameterMap(Map<Integer, List<String>> map) {
@ -222,8 +207,8 @@ public class TypeDescriptorTests {
public void propertyComplex() throws Exception { public void propertyComplex() throws Exception {
PropertyDescriptor property = new PropertyDescriptor("complexProperty", getClass().getMethod("getComplexProperty", null), getClass().getMethod("setComplexProperty", Map.class)); PropertyDescriptor property = new PropertyDescriptor("complexProperty", getClass().getMethod("getComplexProperty", null), getClass().getMethod("setComplexProperty", Map.class));
TypeDescriptor desc = new TypeDescriptor(getClass(), property); TypeDescriptor desc = new TypeDescriptor(getClass(), property);
//assertEquals(String.class, desc.getMapKeyType()); assertEquals(String.class, desc.getMapKeyType().getType());
assertEquals(Integer.class, desc.getMapValueTypeDescriptor().getElementTypeDescriptor().getElementType()); assertEquals(Integer.class, desc.getMapValueType().getElementType().getElementType().getType());
} }
public Map<String, List<List<Integer>>> getComplexProperty() { public Map<String, List<List<Integer>>> getComplexProperty() {
@ -248,7 +233,7 @@ public class TypeDescriptorTests {
PropertyDescriptor property = new PropertyDescriptor("listProperty", genericBean.getClass().getMethod("getListProperty", null), genericBean.getClass().getMethod("setListProperty", List.class)); PropertyDescriptor property = new PropertyDescriptor("listProperty", genericBean.getClass().getMethod("getListProperty", null), genericBean.getClass().getMethod("setListProperty", List.class));
TypeDescriptor desc = new TypeDescriptor(genericBean.getClass(), property); TypeDescriptor desc = new TypeDescriptor(genericBean.getClass(), property);
assertEquals(List.class, desc.getType()); assertEquals(List.class, desc.getType());
assertEquals(Integer.class, desc.getElementType()); assertEquals(Integer.class, desc.getElementType().getType());
} }
public interface GenericType<T> { public interface GenericType<T> {
@ -292,7 +277,7 @@ public class TypeDescriptorTests {
PropertyDescriptor property = new PropertyDescriptor("listProperty", genericBean.getClass().getMethod("getListProperty", null), genericBean.getClass().getMethod("setListProperty", List.class)); PropertyDescriptor property = new PropertyDescriptor("listProperty", genericBean.getClass().getMethod("getListProperty", null), genericBean.getClass().getMethod("setListProperty", List.class));
TypeDescriptor desc = new TypeDescriptor(genericBean.getClass(), property); TypeDescriptor desc = new TypeDescriptor(genericBean.getClass(), property);
assertEquals(List.class, desc.getType()); assertEquals(List.class, desc.getType());
assertEquals(Integer.class, desc.getElementType()); assertEquals(Integer.class, desc.getElementType().getType());
assertNotNull(desc.getAnnotation(MethodAnnotation1.class)); assertNotNull(desc.getAnnotation(MethodAnnotation1.class));
} }
@ -323,8 +308,8 @@ public class TypeDescriptorTests {
public void property() throws Exception { public void property() throws Exception {
PropertyDescriptor property = new PropertyDescriptor("property", getClass().getMethod("getProperty", null), getClass().getMethod("setProperty", Map.class)); PropertyDescriptor property = new PropertyDescriptor("property", getClass().getMethod("getProperty", null), getClass().getMethod("setProperty", Map.class));
TypeDescriptor desc = new TypeDescriptor(getClass(), property); TypeDescriptor desc = new TypeDescriptor(getClass(), property);
assertEquals(Integer.class, desc.getMapKeyTypeDescriptor().getElementType()); assertEquals(Integer.class, desc.getMapKeyType().getElementType().getType());
assertEquals(Long.class, desc.getMapValueTypeDescriptor().getElementType()); assertEquals(Long.class, desc.getMapValueType().getElementType().getType());
assertNotNull(desc.getAnnotation(MethodAnnotation1.class)); assertNotNull(desc.getAnnotation(MethodAnnotation1.class));
assertNotNull(desc.getAnnotation(MethodAnnotation2.class)); assertNotNull(desc.getAnnotation(MethodAnnotation2.class));
assertNotNull(desc.getAnnotation(MethodAnnotation3.class)); assertNotNull(desc.getAnnotation(MethodAnnotation3.class));
@ -379,8 +364,7 @@ public class TypeDescriptorTests {
TypeDescriptor typeDescriptor = new TypeDescriptor(TypeDescriptorTests.class.getDeclaredField("listOfString")); TypeDescriptor typeDescriptor = new TypeDescriptor(TypeDescriptorTests.class.getDeclaredField("listOfString"));
assertFalse(typeDescriptor.isArray()); assertFalse(typeDescriptor.isArray());
assertEquals(List.class, typeDescriptor.getType()); assertEquals(List.class, typeDescriptor.getType());
assertEquals(String.class, typeDescriptor.getElementType()); assertEquals(String.class, typeDescriptor.getElementType().getType());
// TODO caught shorten these names but it is OK that they are fully qualified for now
assertEquals("java.util.List<java.lang.String>", typeDescriptor.toString()); assertEquals("java.util.List<java.lang.String>", typeDescriptor.toString());
} }
@ -389,8 +373,8 @@ public class TypeDescriptorTests {
TypeDescriptor typeDescriptor = new TypeDescriptor(TypeDescriptorTests.class.getDeclaredField("listOfListOfString")); TypeDescriptor typeDescriptor = new TypeDescriptor(TypeDescriptorTests.class.getDeclaredField("listOfListOfString"));
assertFalse(typeDescriptor.isArray()); assertFalse(typeDescriptor.isArray());
assertEquals(List.class, typeDescriptor.getType()); assertEquals(List.class, typeDescriptor.getType());
assertEquals(List.class, typeDescriptor.getElementType()); assertEquals(List.class, typeDescriptor.getElementType().getType());
assertEquals(String.class, typeDescriptor.getElementTypeDescriptor().getElementType()); assertEquals(String.class, typeDescriptor.getElementType().getElementType().getType());
assertEquals("java.util.List<java.util.List<java.lang.String>>", typeDescriptor.toString()); assertEquals("java.util.List<java.util.List<java.lang.String>>", typeDescriptor.toString());
} }
@ -399,16 +383,16 @@ public class TypeDescriptorTests {
TypeDescriptor typeDescriptor = new TypeDescriptor(TypeDescriptorTests.class.getDeclaredField("listOfListOfUnknown")); TypeDescriptor typeDescriptor = new TypeDescriptor(TypeDescriptorTests.class.getDeclaredField("listOfListOfUnknown"));
assertFalse(typeDescriptor.isArray()); assertFalse(typeDescriptor.isArray());
assertEquals(List.class, typeDescriptor.getType()); assertEquals(List.class, typeDescriptor.getType());
assertEquals(List.class, typeDescriptor.getElementType()); assertEquals(List.class, typeDescriptor.getElementType().getType());
assertEquals(Object.class, typeDescriptor.getElementTypeDescriptor().getElementType()); assertNull(typeDescriptor.getElementType().getElementType());
assertEquals("java.util.List<java.util.List<java.lang.Object>>", typeDescriptor.toString()); assertEquals("java.util.List<java.util.List<?>>", typeDescriptor.toString());
} }
@Test @Test
public void fieldArray() throws Exception { public void fieldArray() throws Exception {
TypeDescriptor typeDescriptor = new TypeDescriptor(TypeDescriptorTests.class.getDeclaredField("intArray")); TypeDescriptor typeDescriptor = new TypeDescriptor(TypeDescriptorTests.class.getDeclaredField("intArray"));
assertTrue(typeDescriptor.isArray()); assertTrue(typeDescriptor.isArray());
assertEquals(Integer.TYPE,typeDescriptor.getElementType()); assertEquals(Integer.TYPE,typeDescriptor.getElementType().getType());
assertEquals("int[]",typeDescriptor.toString()); assertEquals("int[]",typeDescriptor.toString());
} }
@ -418,7 +402,7 @@ public class TypeDescriptorTests {
TypeDescriptor typeDescriptor = new TypeDescriptor(TypeDescriptorTests.class.getDeclaredField("arrayOfListOfString")); TypeDescriptor typeDescriptor = new TypeDescriptor(TypeDescriptorTests.class.getDeclaredField("arrayOfListOfString"));
assertTrue(typeDescriptor.isArray()); assertTrue(typeDescriptor.isArray());
assertEquals(List.class,typeDescriptor.getElementType()); assertEquals(List.class,typeDescriptor.getElementType());
assertEquals(String.class, typeDescriptor.getElementTypeDescriptor().getElementType()); assertEquals(String.class, typeDescriptor.getElementType().getElementType());
assertEquals("java.util.List[]",typeDescriptor.toString()); assertEquals("java.util.List[]",typeDescriptor.toString());
} }
@ -426,9 +410,9 @@ public class TypeDescriptorTests {
public void fieldComplexTypeDescriptor2() throws Exception { public void fieldComplexTypeDescriptor2() throws Exception {
TypeDescriptor typeDescriptor = new TypeDescriptor(TypeDescriptorTests.class.getDeclaredField("nestedMapField")); TypeDescriptor typeDescriptor = new TypeDescriptor(TypeDescriptorTests.class.getDeclaredField("nestedMapField"));
assertTrue(typeDescriptor.isMap()); assertTrue(typeDescriptor.isMap());
assertEquals(String.class,typeDescriptor.getMapKeyType()); assertEquals(String.class,typeDescriptor.getMapKeyType().getType());
assertEquals(List.class, typeDescriptor.getMapValueType()); assertEquals(List.class, typeDescriptor.getMapValueType().getType());
assertEquals(Integer.class, typeDescriptor.getMapValueTypeDescriptor().getElementType()); assertEquals(Integer.class, typeDescriptor.getMapValueType().getElementType().getType());
assertEquals("java.util.Map<java.lang.String, java.util.List<java.lang.Integer>>", typeDescriptor.toString()); assertEquals("java.util.Map<java.lang.String, java.util.List<java.lang.Integer>>", typeDescriptor.toString());
} }
@ -438,8 +422,8 @@ public class TypeDescriptorTests {
// TODO: SPR-8394: typeIndex handling not currently supported by fields // TODO: SPR-8394: typeIndex handling not currently supported by fields
TypeDescriptor desc = new TypeDescriptor(getClass().getField("fieldMap")); TypeDescriptor desc = new TypeDescriptor(getClass().getField("fieldMap"));
assertTrue(desc.isMap()); assertTrue(desc.isMap());
assertEquals(Integer.class, desc.getMapKeyTypeDescriptor().getElementType()); assertEquals(Integer.class, desc.getMapKeyType().getElementType());
assertEquals(Long.class, desc.getMapValueTypeDescriptor().getElementType()); assertEquals(Long.class, desc.getMapValueType().getElementType());
} }
public Map<List<Integer>, List<Long>> fieldMap; public Map<List<Integer>, List<Long>> fieldMap;
@ -488,7 +472,7 @@ public class TypeDescriptorTests {
assertTrue(typeDescriptor.isArray()); assertTrue(typeDescriptor.isArray());
assertFalse(typeDescriptor.isCollection()); assertFalse(typeDescriptor.isCollection());
assertFalse(typeDescriptor.isMap()); assertFalse(typeDescriptor.isMap());
assertEquals(Integer.TYPE, typeDescriptor.getElementType()); assertEquals(Integer.TYPE, typeDescriptor.getElementType().getType());
} }
@Test @Test
@ -497,117 +481,19 @@ public class TypeDescriptorTests {
assertTrue(typeDescriptor.isCollection()); assertTrue(typeDescriptor.isCollection());
assertFalse(typeDescriptor.isArray()); assertFalse(typeDescriptor.isArray());
assertFalse(typeDescriptor.isMap()); assertFalse(typeDescriptor.isMap());
assertEquals(Object.class, typeDescriptor.getElementType()); assertNull(typeDescriptor.getElementType());
} }
@Test @Test
public void forObjectCollection() { public void forObject() {
List<String> list = new ArrayList<String>(); TypeDescriptor desc = TypeDescriptor.forObject("3");
list.add("1"); assertEquals(String.class, desc.getType());
TypeDescriptor desc = TypeDescriptor.forObject(list);
assertEquals(String.class, desc.getElementType());
} }
@Test @Test
public void forObjectCollectionEmpty() { public void forObjectNullTypeDescriptor() {
List<String> list = new ArrayList<String>(); TypeDescriptor desc = TypeDescriptor.forObject(null);
TypeDescriptor desc = TypeDescriptor.forObject(list); assertNull(desc);
assertNull(desc.getElementType());
}
@Test
public void forObjectCollectionSuperClassCommonType() throws SecurityException, NoSuchFieldException {
List<Number> list = new ArrayList<Number>();
list.add(1);
list.add(2L);
TypeDescriptor desc = TypeDescriptor.forObject(list);
assertEquals(Number.class, desc.getElementType());
}
public List<Long> longs;
@Test
public void forObjectCollectionNoObviousCommonType() {
List<Object> collection = new ArrayList<Object>();
List<String> list = new ArrayList<String>();
list.add("1");
collection.add(list);
Map<String, String> map = new HashMap<String, String>();
collection.add(map);
map.put("1", "2");
TypeDescriptor desc = TypeDescriptor.forObject(collection);
assertEquals(Cloneable.class, desc.getElementType());
}
@Test
public void forObjectCollectionNoCommonType() {
List<Object> collection = new ArrayList<Object>();
collection.add(new Object());
collection.add("1");
TypeDescriptor desc = TypeDescriptor.forObject(collection);
assertEquals(Object.class, desc.getElementType());
}
@Test
public void forObjectCollectionNested() {
List<Object> collection = new ArrayList<Object>();
collection.add(Arrays.asList("1", "2"));
collection.add(Arrays.asList("3", "4"));
TypeDescriptor desc = TypeDescriptor.forObject(collection);
assertEquals(Arrays.asList("foo").getClass(), desc.getElementType());
assertEquals(String.class, desc.getElementTypeDescriptor().getElementType());
}
@Test
public void forObjectMap() {
Map<String, String> map = new HashMap<String, String>();
map.put("1", "2");
TypeDescriptor desc = TypeDescriptor.forObject(map);
assertEquals(String.class, desc.getMapKeyType());
assertEquals(String.class, desc.getMapValueType());
}
@Test
public void forObjectMapEmpty() {
Map<String, String> map = new HashMap<String, String>();
TypeDescriptor desc = TypeDescriptor.forObject(map);
assertNull(desc.getMapKeyType());
assertNull(desc.getMapValueType());
}
@Test
public void forObjectMapCommonSuperClass() {
Map<Number, Number> map = new HashMap<Number, Number>();
map.put(1, 2);
map.put(2L, 3L);
TypeDescriptor desc = TypeDescriptor.forObject(map);
assertEquals(Number.class, desc.getMapKeyType());
assertEquals(Number.class, desc.getMapValueType());
}
@Test
public void forObjectMapNoObviousCommonType() {
Map<Object, Object> map = new HashMap<Object, Object>();
map.put("1", "2");
map.put(2, 2);
TypeDescriptor desc = TypeDescriptor.forObject(map);
assertEquals(Comparable.class, desc.getMapKeyType());
assertEquals(Comparable.class, desc.getMapValueType());
}
@Test
public void forObjectMapNested() {
Map<Integer, List<String>> map = new HashMap<Integer, List<String>>();
map.put(1, Arrays.asList("1, 2"));
TypeDescriptor desc = TypeDescriptor.forObject(map);
assertEquals(Integer.class, desc.getMapKeyType());
assertEquals(String.class, desc.getMapValueTypeDescriptor().getElementType());
}
@Test
public void nestedMethodParameterType() throws Exception {
TypeDescriptor t1 = TypeDescriptor.nested(new MethodParameter(getClass().getMethod("test1", List.class), 0), 1);
assertEquals(String.class, t1.getType());
} }
@Test @Test
@ -692,8 +578,8 @@ public class TypeDescriptorTests {
assertEquals(0, desc.getAnnotations().length); assertEquals(0, desc.getAnnotations().length);
assertTrue(desc.isCollection()); assertTrue(desc.isCollection());
assertFalse(desc.isArray()); assertFalse(desc.isArray());
assertEquals(Integer.class, desc.getElementType()); assertEquals(Integer.class, desc.getElementType().getType());
assertEquals(TypeDescriptor.valueOf(Integer.class), desc.getElementTypeDescriptor()); assertEquals(TypeDescriptor.valueOf(Integer.class), desc.getElementType());
assertFalse(desc.isMap()); assertFalse(desc.isMap());
} }
@ -708,8 +594,8 @@ public class TypeDescriptorTests {
assertEquals(0, desc.getAnnotations().length); assertEquals(0, desc.getAnnotations().length);
assertTrue(desc.isCollection()); assertTrue(desc.isCollection());
assertFalse(desc.isArray()); assertFalse(desc.isArray());
assertEquals(List.class, desc.getElementType()); assertEquals(List.class, desc.getElementType().getType());
assertEquals(TypeDescriptor.valueOf(Integer.class), desc.getElementTypeDescriptor().getElementTypeDescriptor()); assertEquals(TypeDescriptor.valueOf(Integer.class), desc.getElementType().getElementType());
assertFalse(desc.isMap()); assertFalse(desc.isMap());
} }
@ -725,8 +611,8 @@ public class TypeDescriptorTests {
assertFalse(desc.isCollection()); assertFalse(desc.isCollection());
assertFalse(desc.isArray()); assertFalse(desc.isArray());
assertTrue(desc.isMap()); assertTrue(desc.isMap());
assertEquals(String.class, desc.getMapKeyTypeDescriptor().getType()); assertEquals(String.class, desc.getMapKeyType().getType());
assertEquals(Integer.class, desc.getMapValueTypeDescriptor().getType()); assertEquals(Integer.class, desc.getMapValueType().getType());
} }
@Test @Test
@ -742,9 +628,9 @@ public class TypeDescriptorTests {
assertFalse(desc.isCollection()); assertFalse(desc.isCollection());
assertFalse(desc.isArray()); assertFalse(desc.isArray());
assertTrue(desc.isMap()); assertTrue(desc.isMap());
assertEquals(String.class, desc.getMapKeyTypeDescriptor().getType()); assertEquals(String.class, desc.getMapKeyType().getType());
assertEquals(String.class, desc.getMapValueTypeDescriptor().getMapKeyTypeDescriptor().getType()); assertEquals(String.class, desc.getMapValueType().getMapKeyType().getType());
assertEquals(Integer.class, desc.getMapValueTypeDescriptor().getMapValueTypeDescriptor().getType()); assertEquals(Integer.class, desc.getMapValueType().getMapValueType().getType());
} }
@Test @Test

View File

@ -16,6 +16,7 @@ import java.util.List;
import org.junit.Before; import org.junit.Before;
import org.junit.Test; import org.junit.Test;
import org.springframework.core.convert.ConversionFailedException;
import org.springframework.core.convert.ConverterNotFoundException; import org.springframework.core.convert.ConverterNotFoundException;
import org.springframework.core.convert.TypeDescriptor; import org.springframework.core.convert.TypeDescriptor;
import org.springframework.core.io.ClassPathResource; import org.springframework.core.io.ClassPathResource;
@ -38,7 +39,12 @@ public class CollectionToCollectionConverterTests {
list.add("37"); list.add("37");
TypeDescriptor sourceType = TypeDescriptor.forObject(list); TypeDescriptor sourceType = TypeDescriptor.forObject(list);
TypeDescriptor targetType = new TypeDescriptor(getClass().getField("scalarListTarget")); TypeDescriptor targetType = new TypeDescriptor(getClass().getField("scalarListTarget"));
assertFalse(conversionService.canConvert(sourceType, targetType)); assertTrue(conversionService.canConvert(sourceType, targetType));
try {
conversionService.convert(list, sourceType, targetType);
} catch (ConversionFailedException e) {
assertTrue(e.getCause() instanceof ConverterNotFoundException);
}
conversionService.addConverterFactory(new StringToNumberConverterFactory()); conversionService.addConverterFactory(new StringToNumberConverterFactory());
assertTrue(conversionService.canConvert(sourceType, targetType)); assertTrue(conversionService.canConvert(sourceType, targetType));
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
@ -169,7 +175,7 @@ public class CollectionToCollectionConverterTests {
assertEquals(resources, conversionService.convert(resources, sourceType, new TypeDescriptor(getClass().getField("resources")))); assertEquals(resources, conversionService.convert(resources, sourceType, new TypeDescriptor(getClass().getField("resources"))));
} }
@Test(expected=ConverterNotFoundException.class) @Test
public void allNullsNotConvertible() throws Exception { public void allNullsNotConvertible() throws Exception {
List<Resource> resources = new ArrayList<Resource>(); List<Resource> resources = new ArrayList<Resource>();
resources.add(null); resources.add(null);
@ -180,7 +186,7 @@ public class CollectionToCollectionConverterTests {
public List<String> allNullsNotConvertible; public List<String> allNullsNotConvertible;
@Test(expected=ConverterNotFoundException.class) @Test(expected=ConversionFailedException.class)
public void nothingInCommon() throws Exception { public void nothingInCommon() throws Exception {
List<Object> resources = new ArrayList<Object>(); List<Object> resources = new ArrayList<Object>();
resources.add(new ClassPathResource("test")); resources.add(new ClassPathResource("test"));

View File

@ -73,7 +73,7 @@ public class GenericConversionServiceTests {
@Test(expected=IllegalArgumentException.class) @Test(expected=IllegalArgumentException.class)
public void convertNotNullSourceNullSourceTypeDescriptor() { public void convertNotNullSourceNullSourceTypeDescriptor() {
conversionService.convert("3", TypeDescriptor.NULL, TypeDescriptor.valueOf(int.class)); conversionService.convert("3", null, TypeDescriptor.valueOf(int.class));
} }
@Test @Test
@ -124,14 +124,15 @@ public class GenericConversionServiceTests {
assertNull(conversionService.convert(null, Integer.class)); assertNull(conversionService.convert(null, Integer.class));
} }
@Test(expected=IllegalArgumentException.class)
public void convertNullTargetClass() { public void convertNullTargetClass() {
assertNull(conversionService.convert("3", (Class<?>) null)); assertNull(conversionService.convert("3", (Class<?>) null));
assertNull(conversionService.convert("3", TypeDescriptor.valueOf(String.class), TypeDescriptor.NULL)); assertNull(conversionService.convert("3", TypeDescriptor.valueOf(String.class), null));
} }
@Test @Test(expected=IllegalArgumentException.class)
public void convertNullTypeDescriptor() { public void convertNullTypeDescriptor() {
assertNull(conversionService.convert("3", TypeDescriptor.valueOf(String.class), TypeDescriptor.NULL)); assertNull(conversionService.convert("3", TypeDescriptor.valueOf(String.class), null));
} }
@Test(expected=IllegalArgumentException.class) @Test(expected=IllegalArgumentException.class)
@ -186,7 +187,12 @@ public class GenericConversionServiceTests {
@Test @Test
public void genericConverterDelegatingBackToConversionServiceConverterNotFound() { public void genericConverterDelegatingBackToConversionServiceConverterNotFound() {
conversionService.addConverter(new ObjectToArrayConverter(conversionService)); conversionService.addConverter(new ObjectToArrayConverter(conversionService));
assertFalse(conversionService.canConvert(String.class, Integer[].class)); assertTrue(conversionService.canConvert(String.class, Integer[].class));
try {
conversionService.convert("3,4,5", Integer[].class);
} catch (ConversionFailedException e) {
assertTrue(e.getCause() instanceof ConverterNotFoundException);
}
} }
@Test @Test

View File

@ -12,6 +12,8 @@ import java.util.Map;
import org.junit.Before; import org.junit.Before;
import org.junit.Test; import org.junit.Test;
import org.springframework.core.convert.ConversionFailedException;
import org.springframework.core.convert.ConverterNotFoundException;
import org.springframework.core.convert.TypeDescriptor; import org.springframework.core.convert.TypeDescriptor;
public class MapToMapConverterTests { public class MapToMapConverterTests {
@ -30,7 +32,12 @@ public class MapToMapConverterTests {
map.put("2", "37"); map.put("2", "37");
TypeDescriptor sourceType = TypeDescriptor.forObject(map); TypeDescriptor sourceType = TypeDescriptor.forObject(map);
TypeDescriptor targetType = new TypeDescriptor(getClass().getField("scalarMapTarget")); TypeDescriptor targetType = new TypeDescriptor(getClass().getField("scalarMapTarget"));
assertFalse(conversionService.canConvert(sourceType, targetType)); assertTrue(conversionService.canConvert(sourceType, targetType));
try {
conversionService.convert(map, sourceType, targetType);
} catch (ConversionFailedException e) {
assertTrue(e.getCause() instanceof ConverterNotFoundException);
}
conversionService.addConverterFactory(new StringToNumberConverterFactory()); conversionService.addConverterFactory(new StringToNumberConverterFactory());
assertTrue(conversionService.canConvert(sourceType, targetType)); assertTrue(conversionService.canConvert(sourceType, targetType));
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
@ -58,7 +65,19 @@ public class MapToMapConverterTests {
map.put("2", "37"); map.put("2", "37");
TypeDescriptor sourceType = new TypeDescriptor(getClass().getField("notGenericMapSource")); TypeDescriptor sourceType = new TypeDescriptor(getClass().getField("notGenericMapSource"));
TypeDescriptor targetType = new TypeDescriptor(getClass().getField("scalarMapTarget")); TypeDescriptor targetType = new TypeDescriptor(getClass().getField("scalarMapTarget"));
assertFalse(conversionService.canConvert(sourceType, targetType)); assertTrue(conversionService.canConvert(sourceType, targetType));
try {
conversionService.convert(map, sourceType, targetType);
} catch (ConversionFailedException e) {
assertTrue(e.getCause() instanceof ConverterNotFoundException);
}
conversionService.addConverterFactory(new StringToNumberConverterFactory());
assertTrue(conversionService.canConvert(sourceType, targetType));
@SuppressWarnings("unchecked")
Map<Integer, Integer> result = (Map<Integer, Integer>) conversionService.convert(map, sourceType, targetType);
assertFalse(map.equals(result));
assertEquals((Integer) 9, result.get(1));
assertEquals((Integer) 37, result.get(2));
} }
public Map notGenericMapSource; public Map notGenericMapSource;
@ -70,7 +89,12 @@ public class MapToMapConverterTests {
map.put("2", Arrays.asList("37", "23")); map.put("2", Arrays.asList("37", "23"));
TypeDescriptor sourceType = TypeDescriptor.forObject(map); TypeDescriptor sourceType = TypeDescriptor.forObject(map);
TypeDescriptor targetType = new TypeDescriptor(getClass().getField("collectionMapTarget")); TypeDescriptor targetType = new TypeDescriptor(getClass().getField("collectionMapTarget"));
assertFalse(conversionService.canConvert(sourceType, targetType)); assertTrue(conversionService.canConvert(sourceType, targetType));
try {
conversionService.convert(map, sourceType, targetType);
} catch (ConversionFailedException e) {
assertTrue(e.getCause() instanceof ConverterNotFoundException);
}
conversionService.addConverter(new CollectionToCollectionConverter(conversionService)); conversionService.addConverter(new CollectionToCollectionConverter(conversionService));
conversionService.addConverterFactory(new StringToNumberConverterFactory()); conversionService.addConverterFactory(new StringToNumberConverterFactory());
assertTrue(conversionService.canConvert(sourceType, targetType)); assertTrue(conversionService.canConvert(sourceType, targetType));
@ -83,8 +107,6 @@ public class MapToMapConverterTests {
public Map<Integer, List<Integer>> collectionMapTarget; public Map<Integer, List<Integer>> collectionMapTarget;
public Map<String, List<String>> sourceCollectionMapTarget;
@Test @Test
public void collectionMapSourceTarget() throws Exception { public void collectionMapSourceTarget() throws Exception {
Map<String, List<String>> map = new HashMap<String, List<String>>(); Map<String, List<String>> map = new HashMap<String, List<String>>();
@ -92,7 +114,12 @@ public class MapToMapConverterTests {
map.put("2", Arrays.asList("37", "23")); map.put("2", Arrays.asList("37", "23"));
TypeDescriptor sourceType = new TypeDescriptor(getClass().getField("sourceCollectionMapTarget")); TypeDescriptor sourceType = new TypeDescriptor(getClass().getField("sourceCollectionMapTarget"));
TypeDescriptor targetType = new TypeDescriptor(getClass().getField("collectionMapTarget")); TypeDescriptor targetType = new TypeDescriptor(getClass().getField("collectionMapTarget"));
assertFalse(conversionService.canConvert(sourceType, targetType)); assertTrue(conversionService.canConvert(sourceType, targetType));
try {
conversionService.convert(map, sourceType, targetType);
} catch (ConversionFailedException e) {
assertTrue(e.getCause() instanceof ConverterNotFoundException);
}
conversionService.addConverter(new CollectionToCollectionConverter(conversionService)); conversionService.addConverter(new CollectionToCollectionConverter(conversionService));
conversionService.addConverterFactory(new StringToNumberConverterFactory()); conversionService.addConverterFactory(new StringToNumberConverterFactory());
assertTrue(conversionService.canConvert(sourceType, targetType)); assertTrue(conversionService.canConvert(sourceType, targetType));
@ -103,6 +130,8 @@ public class MapToMapConverterTests {
assertEquals(Arrays.asList(37, 23), result.get(2)); assertEquals(Arrays.asList(37, 23), result.get(2));
} }
public Map<String, List<String>> sourceCollectionMapTarget;
@Test @Test
public void collectionMapNotGenericTarget() throws Exception { public void collectionMapNotGenericTarget() throws Exception {
Map<String, List<String>> map = new HashMap<String, List<String>>(); Map<String, List<String>> map = new HashMap<String, List<String>>();

View File

@ -16,9 +16,6 @@
package org.springframework.expression; package org.springframework.expression;
import java.util.Collection;
import java.util.Map;
import org.springframework.core.convert.TypeDescriptor; import org.springframework.core.convert.TypeDescriptor;
/** /**
@ -56,7 +53,7 @@ public class TypedValue {
*/ */
public TypedValue(Object value, TypeDescriptor typeDescriptor) { public TypedValue(Object value, TypeDescriptor typeDescriptor) {
this.value = value; this.value = value;
this.typeDescriptor = initTypeDescriptor(value, typeDescriptor); this.typeDescriptor = typeDescriptor;
} }
public Object getValue() { public Object getValue() {
@ -77,20 +74,4 @@ public class TypedValue {
return str.toString(); return str.toString();
} }
// interal helpers
private static TypeDescriptor initTypeDescriptor(Object value, TypeDescriptor typeDescriptor) {
if (value == null) {
return typeDescriptor;
}
if (typeDescriptor.isCollection() && Object.class.equals(typeDescriptor.getElementType())) {
return typeDescriptor.resolveCollectionElementType((Collection<?>) value);
} else if (typeDescriptor.isMap() && Object.class.equals(typeDescriptor.getMapKeyType())
&& Object.class.equals(typeDescriptor.getMapValueType())){
return typeDescriptor.resolveMapKeyValueTypes((Map<?, ?>) value);
} else {
return typeDescriptor;
}
}
} }

View File

@ -41,7 +41,12 @@ public class FormatHelper {
if (i > 0) { if (i > 0) {
sb.append(","); sb.append(",");
} }
sb.append(formatClassNameForMessage(argumentTypes.get(i).getType())); TypeDescriptor typeDescriptor = argumentTypes.get(i);
if (typeDescriptor != null) {
sb.append(formatClassNameForMessage(typeDescriptor.getClass()));
} else {
sb.append(formatClassNameForMessage(null));
}
} }
sb.append(")"); sb.append(")");
return sb.toString(); return sb.toString();

View File

@ -106,7 +106,7 @@ public class FunctionReference extends SpelNodeImpl {
try { try {
ReflectionUtils.makeAccessible(method); ReflectionUtils.makeAccessible(method);
Object result = method.invoke(method.getClass(), functionArgs); Object result = method.invoke(method.getClass(), functionArgs);
return new TypedValue(result, new TypeDescriptor(new MethodParameter(method,-1))); return new TypedValue(result, new TypeDescriptor(new MethodParameter(method,-1)).narrowType(result));
} }
catch (Exception ex) { catch (Exception ex) {
throw new SpelEvaluationException(getStartPosition(), ex, SpelMessage.EXCEPTION_DURING_FUNCTION_CALL, throw new SpelEvaluationException(getStartPosition(), ex, SpelMessage.EXCEPTION_DURING_FUNCTION_CALL,

View File

@ -90,9 +90,12 @@ public class Indexer extends SpelNodeImpl {
// Indexing into a Map // Indexing into a Map
if (targetObject instanceof Map) { if (targetObject instanceof Map) {
Object possiblyConvertedKey = state.convertValue(index, targetObjectTypeDescriptor.getMapKeyTypeDescriptor()); Object key = index;
Object o = ((Map<?, ?>) targetObject).get(possiblyConvertedKey); if (targetObjectTypeDescriptor.getMapKeyType() != null) {
return new TypedValue(o, targetObjectTypeDescriptor.getMapValueTypeDescriptor()); key = state.convertValue(key, targetObjectTypeDescriptor.getMapKeyType());
}
Object value = ((Map<?, ?>) targetObject).get(key);
return new TypedValue(value, targetObjectTypeDescriptor.mapValueType(value));
} }
if (targetObject == null) { if (targetObject == null) {
@ -100,22 +103,22 @@ public class Indexer extends SpelNodeImpl {
} }
// if the object is something that looks indexable by an integer, attempt to treat the index value as a number // if the object is something that looks indexable by an integer, attempt to treat the index value as a number
if ((targetObject instanceof Collection ) || targetObject.getClass().isArray() || targetObject instanceof String) { if (targetObject instanceof Collection || targetObject.getClass().isArray() || targetObject instanceof String) {
int idx = (Integer) state.convertValue(index, TypeDescriptor.valueOf(Integer.class)); int idx = (Integer) state.convertValue(index, TypeDescriptor.valueOf(Integer.class));
if (targetObject.getClass().isArray()) { if (targetObject.getClass().isArray()) {
Object arrayElement = accessArrayElement(targetObject, idx); Object arrayElement = accessArrayElement(targetObject, idx);
return new TypedValue(arrayElement, targetObjectTypeDescriptor.getElementTypeDescriptor()); return new TypedValue(arrayElement, targetObjectTypeDescriptor.elementType(arrayElement));
} else if (targetObject instanceof Collection) { } else if (targetObject instanceof Collection) {
Collection c = (Collection) targetObject; Collection c = (Collection) targetObject;
if (idx >= c.size()) { if (idx >= c.size()) {
if (!growCollection(state, targetObjectTypeDescriptor.getElementType(), idx, c)) { if (!growCollection(state, targetObjectTypeDescriptor, idx, c)) {
throw new SpelEvaluationException(getStartPosition(),SpelMessage.COLLECTION_INDEX_OUT_OF_BOUNDS, c.size(), idx); throw new SpelEvaluationException(getStartPosition(),SpelMessage.COLLECTION_INDEX_OUT_OF_BOUNDS, c.size(), idx);
} }
} }
int pos = 0; int pos = 0;
for (Object o : c) { for (Object o : c) {
if (pos == idx) { if (pos == idx) {
return new TypedValue(o, targetObjectTypeDescriptor.getElementTypeDescriptor()); return new TypedValue(o, targetObjectTypeDescriptor.elementType(o));
} }
pos++; pos++;
} }
@ -181,33 +184,38 @@ public class Indexer extends SpelNodeImpl {
throw new SpelEvaluationException(SpelMessage.CANNOT_INDEX_INTO_NULL_VALUE); throw new SpelEvaluationException(SpelMessage.CANNOT_INDEX_INTO_NULL_VALUE);
} }
// Indexing into a Map // Indexing into a Map
if (targetObjectTypeDescriptor.isMap()) { if (targetObject instanceof Map) {
Map map = (Map) targetObject; Map map = (Map) targetObject;
Object possiblyConvertedKey = index; Object key = index.getValue();
Object possiblyConvertedValue = newValue; if (targetObjectTypeDescriptor.getMapKeyType() != null) {
possiblyConvertedKey = state.convertValue(index.getValue(), targetObjectTypeDescriptor.getMapKeyTypeDescriptor()); key = state.convertValue(index, targetObjectTypeDescriptor.getMapKeyType());
possiblyConvertedValue = state.convertValue(newValue, targetObjectTypeDescriptor.getMapValueTypeDescriptor()); }
map.put(possiblyConvertedKey,possiblyConvertedValue); if (targetObjectTypeDescriptor.getMapValueType() != null) {
newValue = state.convertValue(newValue, targetObjectTypeDescriptor.getMapValueType());
}
map.put(key, newValue);
return; return;
} }
if (targetObjectTypeDescriptor.isArray()) { if (targetObjectTypeDescriptor.isArray()) {
int idx = (Integer)state.convertValue(index, TypeDescriptor.valueOf(Integer.class)); int idx = (Integer)state.convertValue(index, TypeDescriptor.valueOf(Integer.class));
setArrayElement(state, contextObject.getValue(), idx, newValue, targetObjectTypeDescriptor.getElementType()); setArrayElement(state, contextObject.getValue(), idx, newValue, targetObjectTypeDescriptor.getElementType().getType());
return; return;
} }
else if (targetObjectTypeDescriptor.isCollection()) { else if (targetObject instanceof Collection) {
int idx = (Integer) state.convertValue(index, TypeDescriptor.valueOf(Integer.class)); int idx = (Integer) state.convertValue(index, TypeDescriptor.valueOf(Integer.class));
Collection c = (Collection) targetObject; Collection c = (Collection) targetObject;
if (idx >= c.size()) { if (idx >= c.size()) {
if (!growCollection(state, targetObjectTypeDescriptor.getElementType(), idx, c)) { if (!growCollection(state, targetObjectTypeDescriptor, idx, c)) {
throw new SpelEvaluationException(getStartPosition(),SpelMessage.COLLECTION_INDEX_OUT_OF_BOUNDS, c.size(), idx); throw new SpelEvaluationException(getStartPosition(),SpelMessage.COLLECTION_INDEX_OUT_OF_BOUNDS, c.size(), idx);
} }
} }
if (targetObject instanceof List) { if (targetObject instanceof List) {
List list = (List) targetObject; List list = (List) targetObject;
Object possiblyConvertedValue = state.convertValue(newValue, targetObjectTypeDescriptor.getElementTypeDescriptor()); if (targetObjectTypeDescriptor.getElementType() != null) {
list.set(idx,possiblyConvertedValue); newValue = state.convertValue(newValue, targetObjectTypeDescriptor.getElementType());
}
list.set(idx, newValue);
return; return;
} }
else { else {
@ -260,20 +268,21 @@ public class Indexer extends SpelNodeImpl {
* @return true if collection growing succeeded, otherwise false * @return true if collection growing succeeded, otherwise false
*/ */
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
private boolean growCollection(ExpressionState state, Class<?> elementType, int index, private boolean growCollection(ExpressionState state, TypeDescriptor targetType, int index,
Collection collection) { Collection collection) {
if (state.getConfiguration().isAutoGrowCollections()) { if (state.getConfiguration().isAutoGrowCollections()) {
if (targetType.getElementType() == null) {
throw new SpelEvaluationException(getStartPosition(), SpelMessage.UNABLE_TO_GROW_COLLECTION_UNKNOWN_ELEMENT_TYPE);
}
TypeDescriptor elementType = targetType.getElementType();
Object newCollectionElement = null; Object newCollectionElement = null;
try { try {
int newElements = index - collection.size(); int newElements = index - collection.size();
if (elementType == null || elementType == Object.class) {
throw new SpelEvaluationException(getStartPosition(), SpelMessage.UNABLE_TO_GROW_COLLECTION_UNKNOWN_ELEMENT_TYPE);
}
while (newElements>0) { while (newElements>0) {
collection.add(elementType.newInstance()); collection.add(elementType.getType().newInstance());
newElements--; newElements--;
} }
newCollectionElement = elementType.newInstance(); newCollectionElement = elementType.getType().newInstance();
} }
catch (Exception ex) { catch (Exception ex) {
throw new SpelEvaluationException(getStartPosition(), ex, SpelMessage.UNABLE_TO_GROW_COLLECTION); throw new SpelEvaluationException(getStartPosition(), ex, SpelMessage.UNABLE_TO_GROW_COLLECTION);

View File

@ -142,7 +142,7 @@ public class Selection extends SpelNodeImpl {
return new TypedValue(result); return new TypedValue(result);
} }
else { else {
Class<?> elementType = ClassUtils.resolvePrimitiveIfNecessary(op.getTypeDescriptor().getElementType()); Class<?> elementType = ClassUtils.resolvePrimitiveIfNecessary(op.getTypeDescriptor().getElementType().getType());
Object resultArray = Array.newInstance(elementType, result.size()); Object resultArray = Array.newInstance(elementType, result.size());
System.arraycopy(result.toArray(), 0, resultArray, 0, result.size()); System.arraycopy(result.toArray(), 0, resultArray, 0, result.size());
return new TypedValue(resultArray); return new TypedValue(resultArray);

View File

@ -103,30 +103,29 @@ public class SpelExpression implements Expression {
return ExpressionUtils.convertTypedValue(context, typedResultValue, expectedResultType); return ExpressionUtils.convertTypedValue(context, typedResultValue, expectedResultType);
} }
public Class getValueType() throws EvaluationException { public Class getValueType() throws EvaluationException {
return ast.getValueInternal(new ExpressionState(getEvaluationContext(), configuration)).getTypeDescriptor().getType(); return getValueType(getEvaluationContext());
}
public Class getValueType(Object rootObject) throws EvaluationException {
return getValueType(getEvaluationContext(), rootObject);
} }
public Class getValueType(EvaluationContext context) throws EvaluationException { public Class getValueType(EvaluationContext context) throws EvaluationException {
Assert.notNull(context, "The EvaluationContext is required"); Assert.notNull(context, "The EvaluationContext is required");
ExpressionState eState = new ExpressionState(context, configuration); ExpressionState eState = new ExpressionState(context, configuration);
TypeDescriptor typeDescriptor = ast.getValueInternal(eState).getTypeDescriptor(); TypeDescriptor typeDescriptor = ast.getValueInternal(eState).getTypeDescriptor();
return typeDescriptor.getType(); return typeDescriptor != null ? typeDescriptor.getType() : null;
} }
public Class getValueType(EvaluationContext context, Object rootObject) throws EvaluationException { public Class getValueType(EvaluationContext context, Object rootObject) throws EvaluationException {
ExpressionState eState = new ExpressionState(context, toTypedValue(rootObject), configuration); ExpressionState eState = new ExpressionState(context, toTypedValue(rootObject), configuration);
TypeDescriptor typeDescriptor = ast.getValueInternal(eState).getTypeDescriptor(); TypeDescriptor typeDescriptor = ast.getValueInternal(eState).getTypeDescriptor();
return typeDescriptor.getType(); return typeDescriptor != null ? typeDescriptor.getType() : null;
}
public Class getValueType(Object rootObject) throws EvaluationException {
return ast.getValueInternal(new ExpressionState(getEvaluationContext(), configuration)).getTypeDescriptor().getType();
} }
public TypeDescriptor getValueTypeDescriptor() throws EvaluationException { public TypeDescriptor getValueTypeDescriptor() throws EvaluationException {
return ast.getValueInternal(new ExpressionState(getEvaluationContext(), configuration)).getTypeDescriptor(); return getValueTypeDescriptor(getEvaluationContext());
} }
public TypeDescriptor getValueTypeDescriptor(Object rootObject) throws EvaluationException { public TypeDescriptor getValueTypeDescriptor(Object rootObject) throws EvaluationException {
@ -178,7 +177,6 @@ public class SpelExpression implements Expression {
ast.setValue(new ExpressionState(context, toTypedValue(rootObject), configuration), value); ast.setValue(new ExpressionState(context, toTypedValue(rootObject), configuration), value);
} }
// impl only // impl only
/** /**

View File

@ -30,6 +30,7 @@ import org.springframework.expression.spel.SpelMessage;
import org.springframework.util.Assert; import org.springframework.util.Assert;
import org.springframework.util.ClassUtils; import org.springframework.util.ClassUtils;
import org.springframework.util.MethodInvoker; import org.springframework.util.MethodInvoker;
import org.springframework.util.ObjectUtils;
/** /**
* Utility methods used by the reflection resolver code to discover the appropriate * Utility methods used by the reflection resolver code to discover the appropriate
@ -63,7 +64,7 @@ public class ReflectionHelper {
TypeDescriptor expectedArg = expectedArgTypes.get(i); TypeDescriptor expectedArg = expectedArgTypes.get(i);
if (!expectedArg.equals(suppliedArg)) { if (!expectedArg.equals(suppliedArg)) {
// The user may supply null - and that will be ok unless a primitive is expected // The user may supply null - and that will be ok unless a primitive is expected
if (suppliedArg == TypeDescriptor.NULL) { if (suppliedArg == null) {
if (expectedArg.isPrimitive()) { if (expectedArg.isPrimitive()) {
match = null; match = null;
} }
@ -112,7 +113,7 @@ public class ReflectionHelper {
for (int i = 0,max=paramTypes.size(); i < max; i++) { for (int i = 0,max=paramTypes.size(); i < max; i++) {
TypeDescriptor argType = argTypes.get(i); TypeDescriptor argType = argTypes.get(i);
TypeDescriptor paramType = paramTypes.get(i); TypeDescriptor paramType = paramTypes.get(i);
if (argType==TypeDescriptor.NULL) { if (argType == null) {
if (paramType.isPrimitive()) { if (paramType.isPrimitive()) {
return Integer.MAX_VALUE; return Integer.MAX_VALUE;
} }
@ -120,7 +121,7 @@ public class ReflectionHelper {
if (!ClassUtils.isAssignable(paramType.getClass(), argType.getClass())) { if (!ClassUtils.isAssignable(paramType.getClass(), argType.getClass())) {
return Integer.MAX_VALUE; return Integer.MAX_VALUE;
} }
if (argType != TypeDescriptor.NULL) { if (argType != null) {
Class paramTypeClazz = paramType.getType(); Class paramTypeClazz = paramType.getType();
if (paramTypeClazz.isPrimitive()) { if (paramTypeClazz.isPrimitive()) {
paramTypeClazz = Object.class; paramTypeClazz = Object.class;
@ -174,7 +175,7 @@ public class ReflectionHelper {
for (int i = 0; i < argCountUpToVarargs && match != null; i++) { for (int i = 0; i < argCountUpToVarargs && match != null; i++) {
TypeDescriptor suppliedArg = suppliedArgTypes.get(i); TypeDescriptor suppliedArg = suppliedArgTypes.get(i);
TypeDescriptor expectedArg = expectedArgTypes.get(i); TypeDescriptor expectedArg = expectedArgTypes.get(i);
if (suppliedArg == TypeDescriptor.NULL) { if (suppliedArg == null) {
if (expectedArg.isPrimitive()) { if (expectedArg.isPrimitive()) {
match = null; match = null;
} }
@ -213,13 +214,13 @@ public class ReflectionHelper {
else { else {
// Now... we have the final argument in the method we are checking as a match and we have 0 or more other // Now... we have the final argument in the method we are checking as a match and we have 0 or more other
// arguments left to pass to it. // arguments left to pass to it.
Class varargsParameterType = expectedArgTypes.get(expectedArgTypes.size() - 1).getElementType(); Class varargsParameterType = expectedArgTypes.get(expectedArgTypes.size() - 1).getElementType().getType();
// All remaining parameters must be of this type or convertable to this type // All remaining parameters must be of this type or convertable to this type
for (int i = expectedArgTypes.size() - 1; i < suppliedArgTypes.size(); i++) { for (int i = expectedArgTypes.size() - 1; i < suppliedArgTypes.size(); i++) {
TypeDescriptor suppliedArg = suppliedArgTypes.get(i); TypeDescriptor suppliedArg = suppliedArgTypes.get(i);
if (varargsParameterType != suppliedArg.getType()) { if (!ObjectUtils.nullSafeEquals(varargsParameterType, suppliedArg)) {
if (suppliedArg == TypeDescriptor.NULL) { if (suppliedArg == null) {
if (varargsParameterType.isPrimitive()) { if (varargsParameterType.isPrimitive()) {
match = null; match = null;
} }

View File

@ -66,7 +66,8 @@ class ReflectiveMethodExecutor implements MethodExecutor {
arguments = ReflectionHelper.setupArgumentsForVarargsInvocation(this.method.getParameterTypes(), arguments); arguments = ReflectionHelper.setupArgumentsForVarargsInvocation(this.method.getParameterTypes(), arguments);
} }
ReflectionUtils.makeAccessible(this.method); ReflectionUtils.makeAccessible(this.method);
return new TypedValue(this.method.invoke(target, arguments), new TypeDescriptor(new MethodParameter(this.method, -1))); Object value = this.method.invoke(target, arguments);
return new TypedValue(value, new TypeDescriptor(new MethodParameter(this.method, -1)).narrowType(value));
} }
catch (Exception ex) { catch (Exception ex) {
throw new AccessException("Problem invoking method: " + this.method, ex); throw new AccessException("Problem invoking method: " + this.method, ex);

View File

@ -109,7 +109,7 @@ public class ReflectivePropertyAccessor implements PropertyAccessor {
if (target instanceof Class) { if (target instanceof Class) {
throw new AccessException("Cannot access length on array class itself"); throw new AccessException("Cannot access length on array class itself");
} }
return new TypedValue(Array.getLength(target),TypeDescriptor.valueOf(Integer.TYPE)); return new TypedValue(Array.getLength(target));
} }
CacheKey cacheKey = new CacheKey(type, name); CacheKey cacheKey = new CacheKey(type, name);
@ -508,7 +508,8 @@ public class ReflectivePropertyAccessor implements PropertyAccessor {
if (needsToBeMadeAccessible) { if (needsToBeMadeAccessible) {
ReflectionUtils.makeAccessible((Method) member); ReflectionUtils.makeAccessible((Method) member);
} }
return new TypedValue(((Method) member).invoke(target), typeDescriptor); Object value = ((Method) member).invoke(target);
return new TypedValue(value, typeDescriptor.narrowType(value));
} }
catch (Exception ex) { catch (Exception ex) {
throw new AccessException("Unable to access property '" + name + "' through getter", ex); throw new AccessException("Unable to access property '" + name + "' through getter", ex);
@ -519,7 +520,8 @@ public class ReflectivePropertyAccessor implements PropertyAccessor {
if (needsToBeMadeAccessible) { if (needsToBeMadeAccessible) {
ReflectionUtils.makeAccessible((Field)member); ReflectionUtils.makeAccessible((Field)member);
} }
return new TypedValue(((Field)member).get(target),typeDescriptor); Object value = ((Field)member).get(target);
return new TypedValue(value, typeDescriptor.narrowType(value));
} }
catch (Exception ex) { catch (Exception ex) {
throw new AccessException("Unable to access field: " + name, ex); throw new AccessException("Unable to access field: " + name, ex);

View File

@ -16,15 +16,17 @@
package org.springframework.expression.spel; package org.springframework.expression.spel;
import static junit.framework.Assert.assertEquals;
import static junit.framework.Assert.assertNotNull;
import static junit.framework.Assert.assertTrue;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
import static junit.framework.Assert.*;
import org.junit.Before; import org.junit.Before;
import org.junit.Test; import org.junit.Test;
import org.springframework.core.MethodParameter; import org.springframework.core.MethodParameter;
import org.springframework.core.convert.ConversionService; import org.springframework.core.convert.ConversionService;
import org.springframework.core.convert.TypeDescriptor; import org.springframework.core.convert.TypeDescriptor;
@ -72,13 +74,13 @@ public class ExpressionTestsUsingCoreConversionService extends ExpressionTestCas
TypeConvertorUsingConversionService tcs = new TypeConvertorUsingConversionService(); TypeConvertorUsingConversionService tcs = new TypeConvertorUsingConversionService();
// ArrayList containing List<Integer> to List<String> // ArrayList containing List<Integer> to List<String>
Class<?> clazz = typeDescriptorForListOfString.getElementType(); Class<?> clazz = typeDescriptorForListOfString.getElementType().getType();
assertEquals(String.class,clazz); assertEquals(String.class,clazz);
List l = (List) tcs.convertValue(listOfInteger, TypeDescriptor.forObject(listOfInteger), typeDescriptorForListOfString); List l = (List) tcs.convertValue(listOfInteger, TypeDescriptor.forObject(listOfInteger), typeDescriptorForListOfString);
assertNotNull(l); assertNotNull(l);
// ArrayList containing List<String> to List<Integer> // ArrayList containing List<String> to List<Integer>
clazz = typeDescriptorForListOfInteger.getElementType(); clazz = typeDescriptorForListOfInteger.getElementType().getType();
assertEquals(Integer.class,clazz); assertEquals(Integer.class,clazz);
l = (List) tcs.convertValue(listOfString, TypeDescriptor.forObject(listOfString), typeDescriptorForListOfString); l = (List) tcs.convertValue(listOfString, TypeDescriptor.forObject(listOfString), typeDescriptorForListOfString);

View File

@ -1,42 +1,304 @@
package org.springframework.expression.spel; package org.springframework.expression.spel;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import java.lang.annotation.ElementType; import java.lang.annotation.ElementType;
import java.lang.annotation.Retention; import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy; import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target; import java.lang.annotation.Target;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import org.junit.Ignore;
import org.junit.Test; import org.junit.Test;
import org.springframework.expression.AccessException;
import org.springframework.expression.EvaluationContext;
import org.springframework.expression.EvaluationException;
import org.springframework.expression.Expression; import org.springframework.expression.Expression;
import org.springframework.expression.PropertyAccessor;
import org.springframework.expression.TypedValue;
import org.springframework.expression.spel.standard.SpelExpressionParser; import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;
public class IndexingTests { public class IndexingTests {
@Test @Test
@Ignore public void indexIntoGenericPropertyContainingMap() {
Map<String, String> property = new HashMap<String, String>();
property.put("foo", "bar");
this.property = property;
SpelExpressionParser parser = new SpelExpressionParser();
Expression expression = parser.parseExpression("property");
assertEquals("@org.springframework.expression.spel.IndexingTests$FieldAnnotation java.util.HashMap<?, ?>", expression.getValueTypeDescriptor(this).toString());
assertEquals(property, expression.getValue(this));
assertEquals(property, expression.getValue(this, Map.class));
expression = parser.parseExpression("property['foo']");
assertEquals("bar", expression.getValue(this));
}
@FieldAnnotation
public Object property;
@Test
public void indexIntoGenericPropertyContainingMapObject() {
Map<String, Map<String, String>> property = new HashMap<String, Map<String, String>>();
Map<String, String> map = new HashMap<String, String>();
map.put("foo", "bar");
property.put("property", map);
SpelExpressionParser parser = new SpelExpressionParser();
StandardEvaluationContext context = new StandardEvaluationContext();
context.addPropertyAccessor(new MapAccessor());
context.setRootObject(property);
Expression expression = parser.parseExpression("property");
assertEquals("java.util.HashMap<?, ?>", expression.getValueTypeDescriptor(context).toString());
assertEquals(map, expression.getValue(context));
assertEquals(map, expression.getValue(context, Map.class));
expression = parser.parseExpression("property['foo']");
assertEquals("bar", expression.getValue(context));
}
public static class MapAccessor implements PropertyAccessor {
public boolean canRead(EvaluationContext context, Object target, String name) throws AccessException {
return (((Map) target).containsKey(name));
}
public TypedValue read(EvaluationContext context, Object target, String name) throws AccessException {
return new TypedValue(((Map) target).get(name));
}
public boolean canWrite(EvaluationContext context, Object target, String name) throws AccessException {
return true;
}
@SuppressWarnings("unchecked")
public void write(EvaluationContext context, Object target, String name, Object newValue)
throws AccessException {
((Map) target).put(name, newValue);
}
public Class<?>[] getSpecificTargetClasses() {
return new Class[] { Map.class };
}
}
@Test
public void setGenericPropertyContainingMap() {
Map<String, String> property = new HashMap<String, String>();
property.put("foo", "bar");
this.property = property;
SpelExpressionParser parser = new SpelExpressionParser();
Expression expression = parser.parseExpression("property");
assertEquals("@org.springframework.expression.spel.IndexingTests$FieldAnnotation java.util.HashMap<?, ?>", expression.getValueTypeDescriptor(this).toString());
assertEquals(property, expression.getValue(this));
expression = parser.parseExpression("property['foo']");
assertEquals("bar", expression.getValue(this));
expression.setValue(this, "baz");
assertEquals("baz", expression.getValue(this));
}
@Test
public void setPropertyContainingMap() {
Map<Integer, Integer> property = new HashMap<Integer, Integer>();
property.put(9, 3);
this.parameterizedMap = property;
SpelExpressionParser parser = new SpelExpressionParser();
Expression expression = parser.parseExpression("parameterizedMap");
assertEquals("java.util.HashMap<java.lang.Integer, java.lang.Integer>", expression.getValueTypeDescriptor(this).toString());
assertEquals(property, expression.getValue(this));
expression = parser.parseExpression("parameterizedMap['9']");
assertEquals(3, expression.getValue(this));
expression.setValue(this, "37");
assertEquals(37, expression.getValue(this));
}
public Map<Integer, Integer> parameterizedMap;
@Test
public void setPropertyContainingMapAutoGrow() {
SpelExpressionParser parser = new SpelExpressionParser(new SpelParserConfiguration(true, false));
Expression expression = parser.parseExpression("parameterizedMap");
assertEquals("java.util.Map<java.lang.Integer, java.lang.Integer>", expression.getValueTypeDescriptor(this).toString());
assertEquals(property, expression.getValue(this));
expression = parser.parseExpression("parameterizedMap['9']");
assertEquals(null, expression.getValue(this));
expression.setValue(this, "37");
assertEquals(37, expression.getValue(this));
}
@Test
public void indexIntoGenericPropertyContainingList() {
List<String> property = new ArrayList<String>();
property.add("bar");
this.property = property;
SpelExpressionParser parser = new SpelExpressionParser();
Expression expression = parser.parseExpression("property");
assertEquals("@org.springframework.expression.spel.IndexingTests$FieldAnnotation java.util.ArrayList<?>", expression.getValueTypeDescriptor(this).toString());
assertEquals(property, expression.getValue(this));
expression = parser.parseExpression("property[0]");
assertEquals("bar", expression.getValue(this));
}
@Test
public void setGenericPropertyContainingList() {
List<Integer> property = new ArrayList<Integer>();
property.add(3);
this.property = property;
SpelExpressionParser parser = new SpelExpressionParser();
Expression expression = parser.parseExpression("property");
assertEquals("@org.springframework.expression.spel.IndexingTests$FieldAnnotation java.util.ArrayList<?>", expression.getValueTypeDescriptor(this).toString());
assertEquals(property, expression.getValue(this));
expression = parser.parseExpression("property[0]");
assertEquals(3, expression.getValue(this));
expression.setValue(this, "4");
assertEquals("4", expression.getValue(this));
}
@Test
public void setGenericPropertyContainingListAutogrow() {
List<Integer> property = new ArrayList<Integer>();
this.property = property;
SpelExpressionParser parser = new SpelExpressionParser(new SpelParserConfiguration(true, true));
Expression expression = parser.parseExpression("property");
assertEquals("@org.springframework.expression.spel.IndexingTests$FieldAnnotation java.util.ArrayList<?>", expression.getValueTypeDescriptor(this).toString());
assertEquals(property, expression.getValue(this));
expression = parser.parseExpression("property[0]");
try {
expression.setValue(this, "4");
} catch (EvaluationException e) {
assertTrue(e.getMessage().startsWith("EL1053E"));
}
}
@Test
public void indexIntoPropertyContainingList() {
List<Integer> property = new ArrayList<Integer>();
property.add(3);
this.parameterizedList = property;
SpelExpressionParser parser = new SpelExpressionParser();
Expression expression = parser.parseExpression("parameterizedList");
assertEquals("java.util.ArrayList<java.lang.Integer>", expression.getValueTypeDescriptor(this).toString());
assertEquals(property, expression.getValue(this));
expression = parser.parseExpression("parameterizedList[0]");
assertEquals(3, expression.getValue(this));
}
public List<Integer> parameterizedList;
@Test
public void indexIntoPropertyContainingListOfList() {
List<List<Integer>> property = new ArrayList<List<Integer>>();
property.add(Arrays.asList(3));
this.parameterizedListOfList = property;
SpelExpressionParser parser = new SpelExpressionParser();
Expression expression = parser.parseExpression("parameterizedListOfList[0]");
assertEquals("java.util.Arrays$ArrayList<java.lang.Integer>", expression.getValueTypeDescriptor(this).toString());
assertEquals(property.get(0), expression.getValue(this));
expression = parser.parseExpression("parameterizedListOfList[0][0]");
assertEquals(3, expression.getValue(this));
}
public List<List<Integer>> parameterizedListOfList;
@Test
public void setPropertyContainingList() {
List<Integer> property = new ArrayList<Integer>();
property.add(3);
this.parameterizedList = property;
SpelExpressionParser parser = new SpelExpressionParser();
Expression expression = parser.parseExpression("parameterizedList");
assertEquals("java.util.ArrayList<java.lang.Integer>", expression.getValueTypeDescriptor(this).toString());
assertEquals(property, expression.getValue(this));
expression = parser.parseExpression("parameterizedList[0]");
assertEquals(3, expression.getValue(this));
expression.setValue(this, "4");
assertEquals(4, expression.getValue(this));
}
@Test
public void indexIntoGenericPropertyContainingNullList() {
SpelParserConfiguration configuration = new SpelParserConfiguration(true, true);
SpelExpressionParser parser = new SpelExpressionParser(configuration);
Expression expression = parser.parseExpression("property");
assertEquals("@org.springframework.expression.spel.IndexingTests$FieldAnnotation java.lang.Object", expression.getValueTypeDescriptor(this).toString());
assertEquals(property, expression.getValue(this));
expression = parser.parseExpression("property[0]");
try {
assertEquals("bar", expression.getValue(this));
} catch (EvaluationException e) {
assertTrue(e.getMessage().startsWith("EL1027E"));
}
}
@Test
public void indexIntoGenericPropertyContainingGrowingList() {
List<String> property = new ArrayList<String>();
this.property = property;
SpelParserConfiguration configuration = new SpelParserConfiguration(true, true);
SpelExpressionParser parser = new SpelExpressionParser(configuration);
Expression expression = parser.parseExpression("property");
assertEquals("@org.springframework.expression.spel.IndexingTests$FieldAnnotation java.util.ArrayList<?>", expression.getValueTypeDescriptor(this).toString());
assertEquals(property, expression.getValue(this));
expression = parser.parseExpression("property[0]");
try {
assertEquals("bar", expression.getValue(this));
} catch (EvaluationException e) {
assertTrue(e.getMessage().startsWith("EL1053E"));
}
}
@Test
public void indexIntoGenericPropertyContainingGrowingList2() {
List<String> property2 = new ArrayList<String>();
this.property2 = property2;
SpelParserConfiguration configuration = new SpelParserConfiguration(true, true);
SpelExpressionParser parser = new SpelExpressionParser(configuration);
Expression expression = parser.parseExpression("property2");
assertEquals("java.util.ArrayList<?>", expression.getValueTypeDescriptor(this).toString());
assertEquals(property2, expression.getValue(this));
expression = parser.parseExpression("property2[0]");
try {
assertEquals("bar", expression.getValue(this));
} catch (EvaluationException e) {
assertTrue(e.getMessage().startsWith("EL1053E"));
}
}
public List property2;
@Test
public void indexIntoGenericPropertyContainingArray() {
String[] property = new String[] { "bar" };
this.property = property;
SpelExpressionParser parser = new SpelExpressionParser();
Expression expression = parser.parseExpression("property");
assertEquals("@org.springframework.expression.spel.IndexingTests$FieldAnnotation java.lang.String[]", expression.getValueTypeDescriptor(this).toString());
assertEquals(property, expression.getValue(this));
expression = parser.parseExpression("property[0]");
assertEquals("bar", expression.getValue(this));
}
@Test
public void emptyList() { public void emptyList() {
listOfScalarNotGeneric = new ArrayList(); listOfScalarNotGeneric = new ArrayList();
SpelExpressionParser parser = new SpelExpressionParser(); SpelExpressionParser parser = new SpelExpressionParser();
Expression expression = parser.parseExpression("listOfScalarNotGeneric"); Expression expression = parser.parseExpression("listOfScalarNotGeneric");
assertEquals("java.util.List<java.lang.Object>", expression.getValueTypeDescriptor(this).toString()); assertEquals("java.util.ArrayList<?>", expression.getValueTypeDescriptor(this).toString());
assertEquals("", expression.getValue(this, String.class)); assertEquals("", expression.getValue(this, String.class));
} }
@Test @Test
@Ignore
public void resolveCollectionElementType() { public void resolveCollectionElementType() {
listNotGeneric = new ArrayList(); listNotGeneric = new ArrayList();
listNotGeneric.add(5); listNotGeneric.add(5);
listNotGeneric.add(6); listNotGeneric.add(6);
SpelExpressionParser parser = new SpelExpressionParser(); SpelExpressionParser parser = new SpelExpressionParser();
Expression expression = parser.parseExpression("listNotGeneric"); Expression expression = parser.parseExpression("listNotGeneric");
assertEquals("@org.springframework.expression.spel.IndexingTests$FieldAnnotation java.util.List<@org.springframework.expression.spel.IndexingTests$FieldAnnotation java.lang.Integer>", expression.getValueTypeDescriptor(this).toString()); assertEquals("@org.springframework.expression.spel.IndexingTests$FieldAnnotation java.util.ArrayList<?>", expression.getValueTypeDescriptor(this).toString());
assertEquals("5,6", expression.getValue(this, String.class)); assertEquals("5,6", expression.getValue(this, String.class));
} }
@ -44,7 +306,7 @@ public class IndexingTests {
public void resolveCollectionElementTypeNull() { public void resolveCollectionElementTypeNull() {
SpelExpressionParser parser = new SpelExpressionParser(); SpelExpressionParser parser = new SpelExpressionParser();
Expression expression = parser.parseExpression("listNotGeneric"); Expression expression = parser.parseExpression("listNotGeneric");
assertEquals("@org.springframework.expression.spel.IndexingTests$FieldAnnotation java.util.List<@org.springframework.expression.spel.IndexingTests$FieldAnnotation java.lang.Object>", expression.getValueTypeDescriptor(this).toString()); assertEquals("@org.springframework.expression.spel.IndexingTests$FieldAnnotation java.util.List<?>", expression.getValueTypeDescriptor(this).toString());
} }
@FieldAnnotation @FieldAnnotation
@ -63,7 +325,7 @@ public class IndexingTests {
mapNotGeneric.put("bonusAmount", 7.17); mapNotGeneric.put("bonusAmount", 7.17);
SpelExpressionParser parser = new SpelExpressionParser(); SpelExpressionParser parser = new SpelExpressionParser();
Expression expression = parser.parseExpression("mapNotGeneric"); Expression expression = parser.parseExpression("mapNotGeneric");
assertEquals("@org.springframework.expression.spel.IndexingTests$FieldAnnotation java.util.Map<java.lang.String, java.lang.Double>", expression.getValueTypeDescriptor(this).toString()); assertEquals("@org.springframework.expression.spel.IndexingTests$FieldAnnotation java.util.HashMap<?, ?>", expression.getValueTypeDescriptor(this).toString());
} }
@FieldAnnotation @FieldAnnotation

View File

@ -16,6 +16,9 @@
package org.springframework.expression.spel; package org.springframework.expression.spel;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.LinkedHashSet; import java.util.LinkedHashSet;
import java.util.List; import java.util.List;
@ -23,9 +26,7 @@ import java.util.Map;
import java.util.Set; import java.util.Set;
import java.util.TreeMap; import java.util.TreeMap;
import static org.junit.Assert.*;
import org.junit.Test; import org.junit.Test;
import org.springframework.expression.EvaluationContext; import org.springframework.expression.EvaluationContext;
import org.springframework.expression.Expression; import org.springframework.expression.Expression;
import org.springframework.expression.ExpressionParser; import org.springframework.expression.ExpressionParser;
@ -112,7 +113,7 @@ public class SelectionAndProjectionTests {
Object value = expression.getValue(context); Object value = expression.getValue(context);
assertTrue(value.getClass().isArray()); assertTrue(value.getClass().isArray());
TypedValue typedValue = new TypedValue(value); TypedValue typedValue = new TypedValue(value);
assertEquals(Integer.class, typedValue.getTypeDescriptor().getElementType()); assertEquals(Integer.class, typedValue.getTypeDescriptor().getElementType().getType());
Integer[] array = (Integer[]) value; Integer[] array = (Integer[]) value;
assertEquals(5, array.length); assertEquals(5, array.length);
assertEquals(new Integer(0), array[0]); assertEquals(new Integer(0), array[0]);
@ -147,7 +148,7 @@ public class SelectionAndProjectionTests {
Object value = expression.getValue(context); Object value = expression.getValue(context);
assertTrue(value.getClass().isArray()); assertTrue(value.getClass().isArray());
TypedValue typedValue = new TypedValue(value); TypedValue typedValue = new TypedValue(value);
assertEquals(Integer.class, typedValue.getTypeDescriptor().getElementType()); assertEquals(Integer.class, typedValue.getTypeDescriptor().getElementType().getType());
Integer[] array = (Integer[]) value; Integer[] array = (Integer[]) value;
assertEquals(5, array.length); assertEquals(5, array.length);
assertEquals(new Integer(0), array[0]); assertEquals(new Integer(0), array[0]);
@ -249,7 +250,7 @@ public class SelectionAndProjectionTests {
Object value = expression.getValue(context); Object value = expression.getValue(context);
assertTrue(value.getClass().isArray()); assertTrue(value.getClass().isArray());
TypedValue typedValue = new TypedValue(value); TypedValue typedValue = new TypedValue(value);
assertEquals(Number.class, typedValue.getTypeDescriptor().getElementType()); assertEquals(Number.class, typedValue.getTypeDescriptor().getElementType().getType());
Number[] array = (Number[]) value; Number[] array = (Number[]) value;
assertEquals(3, array.length); assertEquals(3, array.length);
assertEquals(new Integer(5), array[0]); assertEquals(new Integer(5), array[0]);

View File

@ -26,7 +26,6 @@ import java.util.Map;
import junit.framework.Assert; import junit.framework.Assert;
import org.junit.Ignore;
import org.junit.Test; import org.junit.Test;
import org.springframework.expression.EvaluationContext; import org.springframework.expression.EvaluationContext;
import org.springframework.expression.Expression; import org.springframework.expression.Expression;
@ -206,7 +205,6 @@ public class SpelDocumentationTests extends ExpressionTestCase {
@Test @Test
@Ignore
public void testDictionaryAccess() throws Exception { public void testDictionaryAccess() throws Exception {
StandardEvaluationContext societyContext = new StandardEvaluationContext(); StandardEvaluationContext societyContext = new StandardEvaluationContext();
societyContext.setRootObject(new IEEE()); societyContext.setRootObject(new IEEE());

View File

@ -698,7 +698,6 @@ public class SpringEL300Tests extends ExpressionTestCase {
} }
@Test @Test
@Ignore
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
public void testMapOfMap_SPR7244() throws Exception { public void testMapOfMap_SPR7244() throws Exception {
Map<String,Object> map = new LinkedHashMap(); Map<String,Object> map = new LinkedHashMap();
@ -727,10 +726,11 @@ public class SpringEL300Tests extends ExpressionTestCase {
String el1 = "ls.![#this.equals('abc')]"; String el1 = "ls.![#this.equals('abc')]";
SpelExpression exp = parser.parseRaw(el1); SpelExpression exp = parser.parseRaw(el1);
List value = (List)exp.getValue(ctx); List value = (List)exp.getValue(ctx);
System.out.println(value);
// value is list containing [true,false] // value is list containing [true,false]
Assert.assertEquals(Boolean.class,value.get(0).getClass()); Assert.assertEquals(Boolean.class,value.get(0).getClass());
TypeDescriptor evaluated = exp.getValueTypeDescriptor(ctx); TypeDescriptor evaluated = exp.getValueTypeDescriptor(ctx);
Assert.assertEquals(Boolean.class,evaluated.getElementType()); Assert.assertEquals(null, evaluated.getElementType());
} }
@Test @Test
@ -743,7 +743,7 @@ public class SpringEL300Tests extends ExpressionTestCase {
// value is array containing [true,false] // value is array containing [true,false]
Assert.assertEquals(Boolean.class,value[0].getClass()); Assert.assertEquals(Boolean.class,value[0].getClass());
TypeDescriptor evaluated = exp.getValueTypeDescriptor(ctx); TypeDescriptor evaluated = exp.getValueTypeDescriptor(ctx);
Assert.assertEquals(Boolean.class,evaluated.getElementType()); Assert.assertEquals(Boolean.class, evaluated.getElementType().getType());
} }
@Test @Test
@ -756,7 +756,7 @@ public class SpringEL300Tests extends ExpressionTestCase {
// value is list containing [true,false] // value is list containing [true,false]
Assert.assertEquals(Boolean.class,value.get(0).getClass()); Assert.assertEquals(Boolean.class,value.get(0).getClass());
TypeDescriptor evaluated = exp.getValueTypeDescriptor(ctx); TypeDescriptor evaluated = exp.getValueTypeDescriptor(ctx);
Assert.assertEquals(Boolean.class,evaluated.getElementType()); Assert.assertEquals(null, evaluated.getElementType());
} }
static class C { static class C {