removed dependency on java.beans

git-svn-id: https://src.springframework.org/svn/spring-framework/trunk@4455 50f2f4bb-b051-0410-bef5-90022cba6387
This commit is contained in:
Keith Donald 2011-06-05 08:29:14 +00:00
parent 911eb5fca8
commit 89096b46b8
6 changed files with 246 additions and 172 deletions

View File

@ -43,6 +43,7 @@ import org.springframework.core.MethodParameter;
import org.springframework.core.convert.ConversionException; import org.springframework.core.convert.ConversionException;
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.convert.TypeDescriptor.Property;
import org.springframework.util.Assert; import org.springframework.util.Assert;
import org.springframework.util.ObjectUtils; import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils; import org.springframework.util.StringUtils;
@ -369,11 +370,11 @@ public class BeanWrapperImpl extends AbstractPropertyAccessor implements BeanWra
if (pd != null) { if (pd != null) {
if (tokens.keys != null) { if (tokens.keys != null) {
if (pd.getReadMethod() != null || pd.getWriteMethod() != null) { if (pd.getReadMethod() != null || pd.getWriteMethod() != null) {
return TypeDescriptor.nested(nestedBw.getWrappedClass(), pd, tokens.keys.length); return TypeDescriptor.nested(property(pd), tokens.keys.length);
} }
} else { } else {
if (pd.getReadMethod() != null || pd.getWriteMethod() != null) { if (pd.getReadMethod() != null || pd.getWriteMethod() != null) {
return new TypeDescriptor(nestedBw.getWrappedClass(), pd); return new TypeDescriptor(property(pd));
} }
} }
} }
@ -493,7 +494,7 @@ public class BeanWrapperImpl extends AbstractPropertyAccessor implements BeanWra
throws TypeMismatchException { throws TypeMismatchException {
GenericTypeAwarePropertyDescriptor gpd = (GenericTypeAwarePropertyDescriptor) pd; GenericTypeAwarePropertyDescriptor gpd = (GenericTypeAwarePropertyDescriptor) pd;
Class<?> beanClass = gpd.getBeanClass(); Class<?> beanClass = gpd.getBeanClass();
return convertIfNecessary(propertyName, oldValue, newValue, pd.getPropertyType(), new TypeDescriptor(beanClass, pd)); return convertIfNecessary(propertyName, oldValue, newValue, pd.getPropertyType(), new TypeDescriptor(property(pd)));
} }
@ -948,7 +949,7 @@ public class BeanWrapperImpl extends AbstractPropertyAccessor implements BeanWra
if (isExtractOldValueForEditor() && arrayIndex < Array.getLength(propValue)) { if (isExtractOldValueForEditor() && arrayIndex < Array.getLength(propValue)) {
oldValue = Array.get(propValue, arrayIndex); oldValue = Array.get(propValue, arrayIndex);
} }
Object convertedValue = convertIfNecessary(propertyName, oldValue, pv.getValue(), requiredType, TypeDescriptor.nested(getWrappedClass(), pd, tokens.keys.length)); Object convertedValue = convertIfNecessary(propertyName, oldValue, pv.getValue(), requiredType, TypeDescriptor.nested(property(pd), tokens.keys.length));
// TODO review this grow algorithm along side the null gap algorithm for setting lists below ... the two are inconsistent // TODO review this grow algorithm along side the null gap algorithm for setting lists below ... the two are inconsistent
propValue = growArrayIfNecessary(propValue, arrayIndex, actualName); propValue = growArrayIfNecessary(propValue, arrayIndex, actualName);
Array.set(propValue, arrayIndex, convertedValue); Array.set(propValue, arrayIndex, convertedValue);
@ -968,7 +969,7 @@ public class BeanWrapperImpl extends AbstractPropertyAccessor implements BeanWra
if (isExtractOldValueForEditor() && index < list.size()) { if (isExtractOldValueForEditor() && index < list.size()) {
oldValue = list.get(index); oldValue = list.get(index);
} }
Object convertedValue = convertIfNecessary(propertyName, oldValue, pv.getValue(), requiredType, TypeDescriptor.nested(getWrappedClass(), pd, tokens.keys.length)); Object convertedValue = convertIfNecessary(propertyName, oldValue, pv.getValue(), requiredType, TypeDescriptor.nested(property(pd), tokens.keys.length));
if (index < list.size()) { if (index < list.size()) {
list.set(index, convertedValue); list.set(index, convertedValue);
} }
@ -1005,7 +1006,7 @@ public class BeanWrapperImpl extends AbstractPropertyAccessor implements BeanWra
// Pass full property name and old value in here, since we want full // Pass full property name and old value in here, since we want full
// conversion ability for map values. // conversion ability for map values.
Object convertedMapValue = convertIfNecessary( Object convertedMapValue = convertIfNecessary(
propertyName, oldValue, pv.getValue(), mapValueType, TypeDescriptor.nested(getWrappedClass(), pd, tokens.keys.length)); propertyName, oldValue, pv.getValue(), mapValueType, TypeDescriptor.nested(property(pd), tokens.keys.length));
map.put(convertedMapKey, convertedMapValue); map.put(convertedMapKey, convertedMapValue);
} }
else { else {
@ -1167,5 +1168,10 @@ public class BeanWrapperImpl extends AbstractPropertyAccessor implements BeanWra
public String[] keys; public String[] keys;
} }
private Property property(PropertyDescriptor pd) {
GenericTypeAwarePropertyDescriptor typeAware = (GenericTypeAwarePropertyDescriptor) pd;
return new Property(typeAware.getBeanClass(), typeAware.getReadMethod(), typeAware.getWriteMethod());
}
} }

View File

@ -23,7 +23,7 @@ abstract class AbstractDescriptor {
private final Class<?> type; private final Class<?> type;
public AbstractDescriptor(Class<?> type) { protected AbstractDescriptor(Class<?> type) {
if (type == null) { if (type == null) {
throw new IllegalArgumentException("type cannot be null"); throw new IllegalArgumentException("type cannot be null");
} }
@ -93,15 +93,15 @@ abstract class AbstractDescriptor {
// internal helpers // internal helpers
private boolean isCollection() { private boolean isCollection() {
return getType() != null && Collection.class.isAssignableFrom(getType()); return Collection.class.isAssignableFrom(getType());
} }
private boolean isArray() { private boolean isArray() {
return getType() != null && getType().isArray(); return getType().isArray();
} }
private boolean isMap() { private boolean isMap() {
return getType() != null && Map.class.isAssignableFrom(getType()); return Map.class.isAssignableFrom(getType());
} }
} }

View File

@ -15,35 +15,25 @@
*/ */
package org.springframework.core.convert; package org.springframework.core.convert;
import java.beans.PropertyDescriptor;
import java.lang.annotation.Annotation; import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.LinkedHashMap;
import java.util.Map;
import org.springframework.core.GenericCollectionTypeResolver; import org.springframework.core.GenericCollectionTypeResolver;
import org.springframework.core.GenericTypeResolver;
import org.springframework.core.MethodParameter; import org.springframework.core.MethodParameter;
import org.springframework.util.ReflectionUtils; import org.springframework.core.convert.TypeDescriptor.Property;
import org.springframework.util.StringUtils;
class BeanPropertyDescriptor extends AbstractDescriptor { class BeanPropertyDescriptor extends AbstractDescriptor {
private final Class<?> beanClass; private final Property property;
private final PropertyDescriptor property;
private final MethodParameter methodParameter; private final MethodParameter methodParameter;
private final Annotation[] annotations; private final Annotation[] annotations;
public BeanPropertyDescriptor(Class<?> beanClass, PropertyDescriptor property) { public BeanPropertyDescriptor(Property property) {
super(property.getPropertyType()); super(property.getType());
this.beanClass = beanClass;
this.property = property; this.property = property;
this.methodParameter = resolveMethodParameter(); this.methodParameter = property.getMethodParameter();
this.annotations = resolveAnnotations(); this.annotations = property.getAnnotations();
} }
@Override @Override
@ -71,79 +61,13 @@ class BeanPropertyDescriptor extends AbstractDescriptor {
MethodParameter methodParameter = new MethodParameter(this.methodParameter); MethodParameter methodParameter = new MethodParameter(this.methodParameter);
methodParameter.increaseNestingLevel(); methodParameter.increaseNestingLevel();
methodParameter.setTypeIndexForCurrentLevel(typeIndex); methodParameter.setTypeIndexForCurrentLevel(typeIndex);
return new BeanPropertyDescriptor(type, beanClass, property, methodParameter, annotations); return new BeanPropertyDescriptor(type, property, methodParameter, annotations);
} }
// internal // internal
private MethodParameter resolveMethodParameter() { private BeanPropertyDescriptor(Class<?> type, Property propertyDescriptor, MethodParameter methodParameter, Annotation[] annotations) {
MethodParameter parameter = parameterForPropertyMethod();
// needed to resolve generic property types that parameterized by sub-classes e.g. T getFoo();
GenericTypeResolver.resolveParameterType(parameter, beanClass);
return parameter;
}
private MethodParameter parameterForPropertyMethod() {
if (property.getReadMethod() != null) {
return new MethodParameter(property.getReadMethod(), -1);
} else if (property.getWriteMethod() != null) {
return new MethodParameter(property.getWriteMethod(), 0);
} else {
throw new IllegalArgumentException("Property is neither readable or writeable");
}
}
private Annotation[] resolveAnnotations() {
Map<Class<?>, Annotation> annMap = new LinkedHashMap<Class<?>, Annotation>();
Method readMethod = this.property.getReadMethod();
if (readMethod != null) {
for (Annotation ann : readMethod.getAnnotations()) {
annMap.put(ann.annotationType(), ann);
}
}
Method writeMethod = this.property.getWriteMethod();
if (writeMethod != null) {
for (Annotation ann : writeMethod.getAnnotations()) {
annMap.put(ann.annotationType(), ann);
}
}
Field field = getField();
if (field != null) {
for (Annotation ann : field.getAnnotations()) {
annMap.put(ann.annotationType(), ann);
}
}
return annMap.values().toArray(new Annotation[annMap.size()]);
}
private Field getField() {
String name = this.property.getName();
if (!StringUtils.hasLength(name)) {
return null;
}
Class<?> declaringClass = declaringClass();
Field field = ReflectionUtils.findField(declaringClass, name);
if (field == null) {
// Same lenient fallback checking as in CachedIntrospectionResults...
field = ReflectionUtils.findField(declaringClass, name.substring(0, 1).toLowerCase() + name.substring(1));
if (field == null) {
field = ReflectionUtils.findField(declaringClass, name.substring(0, 1).toUpperCase() + name.substring(1));
}
}
return field;
}
private Class<?> declaringClass() {
if (this.property.getReadMethod() != null) {
return this.property.getReadMethod().getDeclaringClass();
} else {
return this.property.getWriteMethod().getDeclaringClass();
}
}
private BeanPropertyDescriptor(Class<?> type, Class<?> beanClass, java.beans.PropertyDescriptor propertyDescriptor, MethodParameter methodParameter, Annotation[] annotations) {
super(type); super(type);
this.beanClass = beanClass;
this.property = propertyDescriptor; this.property = propertyDescriptor;
this.methodParameter = methodParameter; this.methodParameter = methodParameter;
this.annotations = annotations; this.annotations = annotations;

View File

@ -13,19 +13,22 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
package org.springframework.core.convert; package org.springframework.core.convert;
import java.beans.PropertyDescriptor;
import java.lang.annotation.Annotation; import java.lang.annotation.Annotation;
import java.lang.reflect.Field; import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.Collection; import java.util.Collection;
import java.util.HashMap; import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map; import java.util.Map;
import org.springframework.core.GenericTypeResolver;
import org.springframework.core.MethodParameter; import org.springframework.core.MethodParameter;
import org.springframework.util.ClassUtils; import org.springframework.util.ClassUtils;
import org.springframework.util.ObjectUtils; import org.springframework.util.ObjectUtils;
import org.springframework.util.ReflectionUtils;
import org.springframework.util.StringUtils;
/** /**
* Context about a type to convert from or to. * Context about a type to convert from or to.
@ -91,11 +94,10 @@ public class TypeDescriptor {
/** /**
* Create a new type descriptor for a bean property. * Create a new type descriptor for a bean property.
* Use this constructor when a target conversion point is a property on a Java class. * Use this constructor when a target conversion point is a property on a Java class.
* @param beanClass the class that declares the property * @param property the property
* @param property the property descriptor
*/ */
public TypeDescriptor(Class<?> beanClass, PropertyDescriptor property) { public TypeDescriptor(Property property) {
this(new BeanPropertyDescriptor(beanClass, property)); this(new BeanPropertyDescriptor(property));
} }
/** /**
@ -140,7 +142,7 @@ public class TypeDescriptor {
} }
return new TypeDescriptor(mapType, keyType, valueType); return new TypeDescriptor(mapType, keyType, valueType);
} }
/** /**
* 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.
@ -182,8 +184,8 @@ public class TypeDescriptor {
* @return the nested type descriptor * @return the nested type descriptor
* @throws IllegalArgumentException if the property is not of a collection, array, or map type. * @throws IllegalArgumentException if the property is not of a collection, array, or map type.
*/ */
public static TypeDescriptor nested(Class<?> beanClass, PropertyDescriptor property, int nestingLevel) { public static TypeDescriptor nested(Property property, int nestingLevel) {
return nested(new BeanPropertyDescriptor(beanClass, property), nestingLevel); return nested(new BeanPropertyDescriptor(property), nestingLevel);
} }
/** /**
@ -196,7 +198,7 @@ public class TypeDescriptor {
public static TypeDescriptor forObject(Object source) { public static TypeDescriptor forObject(Object source) {
return source != null ? valueOf(source.getClass()) : null; 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}
@ -230,7 +232,7 @@ public class TypeDescriptor {
} }
return new TypeDescriptor(value.getClass(), elementType, mapKeyType, mapValueType, annotations); 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.
*/ */
@ -288,7 +290,7 @@ public class TypeDescriptor {
} }
// indexable type descriptor operations // indexable type descriptor operations
/** /**
* Is this type a {@link Collection} type? * Is this type a {@link Collection} type?
*/ */
@ -336,7 +338,7 @@ public class TypeDescriptor {
} }
// map type descriptor operations // map type descriptor operations
/** /**
* Is this type a {@link Map} type? * Is this type a {@link Map} type?
*/ */
@ -406,7 +408,7 @@ public class TypeDescriptor {
} }
// extending Object // extending Object
public boolean equals(Object obj) { public boolean equals(Object obj) {
if (this == obj) { if (this == obj) {
return true; return true;
@ -415,17 +417,17 @@ public class TypeDescriptor {
return false; return false;
} }
TypeDescriptor other = (TypeDescriptor) obj; TypeDescriptor other = (TypeDescriptor) obj;
boolean annotatedTypeEquals = ObjectUtils.nullSafeEquals(getType(), other.getType()) && ObjectUtils.nullSafeEquals(getAnnotations(), other.getAnnotations()); boolean annotatedTypeEquals = ObjectUtils.nullSafeEquals(getType(), other.getType())
&& ObjectUtils.nullSafeEquals(getAnnotations(), other.getAnnotations());
if (!annotatedTypeEquals) { if (!annotatedTypeEquals) {
return false; return false;
} }
if (isCollection() || isArray()) { if (isCollection() || isArray()) {
return ObjectUtils.nullSafeEquals(getElementType(), other.getElementType()); return ObjectUtils.nullSafeEquals(getElementType(), other.getElementType());
} } else if (isMap()) {
else if (isMap()) { return ObjectUtils.nullSafeEquals(getMapKeyType(), other.getMapKeyType())
return ObjectUtils.nullSafeEquals(getMapKeyType(), other.getMapKeyType()) && ObjectUtils.nullSafeEquals(getMapValueType(), other.getMapValueType()); && ObjectUtils.nullSafeEquals(getMapValueType(), other.getMapValueType());
} } else {
else {
return true; return true;
} }
} }
@ -444,13 +446,171 @@ public class TypeDescriptor {
if (isMap()) { if (isMap()) {
builder.append("<").append(wildcard(getMapKeyType())); builder.append("<").append(wildcard(getMapKeyType()));
builder.append(", ").append(wildcard(getMapValueType())).append(">"); builder.append(", ").append(wildcard(getMapValueType())).append(">");
} } else if (isCollection()) {
else if (isCollection()) {
builder.append("<").append(wildcard(getElementType())).append(">"); builder.append("<").append(wildcard(getElementType())).append(">");
} }
return builder.toString(); return builder.toString();
} }
// helper public class
public static final class Property {
private final Class<?> objectType;
private final Method readMethod;
private final Method writeMethod;
private final String name;
private final MethodParameter methodParameter;
private final Annotation[] annotations;
public Property(Class<?> objectType, Method readMethod, Method writeMethod) {
this.objectType = objectType;
this.readMethod = readMethod;
this.writeMethod = writeMethod;
this.methodParameter = resolveMethodParameter();
this.name = resolveName();
this.annotations = resolveAnnotations();
}
public Class<?> getObjectType() {
return objectType;
}
public String getName() {
return name;
}
public Class<?> getType() {
return methodParameter.getParameterType();
}
public Method getReadMethod() {
return readMethod;
}
public Method getWriteMethod() {
return writeMethod;
}
MethodParameter getMethodParameter() {
return methodParameter;
}
Annotation[] getAnnotations() {
return annotations;
}
private String resolveName() {
if (readMethod != null) {
int index = readMethod.getName().indexOf("get");
if (index != -1) {
index += 3;
} else {
index = readMethod.getName().indexOf("is");
if (index == -1) {
throw new IllegalArgumentException("Not a getter method");
}
index += 2;
}
return StringUtils.uncapitalize(readMethod.getName().substring(index));
} else {
int index = writeMethod.getName().indexOf("set") + 3;
if (index == -1) {
throw new IllegalArgumentException("Not a setter method");
}
return StringUtils.uncapitalize(writeMethod.getName().substring(index));
}
}
private MethodParameter resolveMethodParameter() {
MethodParameter read = resolveReadMethodParameter();
MethodParameter write = resolveWriteMethodParameter();
if (read == null && write == null) {
throw new IllegalStateException("Property is neither readable or writeable");
}
if (read != null && write != null && !read.getParameterType().equals(write.getParameterType())) {
throw new IllegalStateException("Read and write parameter types are not the same");
}
return read != null ? read : write;
}
private MethodParameter resolveReadMethodParameter() {
if (getReadMethod() == null) {
return null;
}
return resolveParameterType(new MethodParameter(getReadMethod(), -1));
}
private MethodParameter resolveWriteMethodParameter() {
if (getWriteMethod() == null) {
return null;
}
return resolveParameterType(new MethodParameter(getWriteMethod(), 0));
}
private MethodParameter resolveParameterType(MethodParameter parameter) {
// needed to resolve generic property types that parameterized by sub-classes e.g. T getFoo();
GenericTypeResolver.resolveParameterType(parameter, getObjectType());
return parameter;
}
private Annotation[] resolveAnnotations() {
Map<Class<?>, Annotation> annMap = new LinkedHashMap<Class<?>, Annotation>();
Method readMethod = getReadMethod();
if (readMethod != null) {
for (Annotation ann : readMethod.getAnnotations()) {
annMap.put(ann.annotationType(), ann);
}
}
Method writeMethod = getWriteMethod();
if (writeMethod != null) {
for (Annotation ann : writeMethod.getAnnotations()) {
annMap.put(ann.annotationType(), ann);
}
}
Field field = getField();
if (field != null) {
for (Annotation ann : field.getAnnotations()) {
annMap.put(ann.annotationType(), ann);
}
}
return annMap.values().toArray(new Annotation[annMap.size()]);
}
private Field getField() {
String name = getName();
if (!StringUtils.hasLength(name)) {
return null;
}
Class<?> declaringClass = declaringClass();
Field field = ReflectionUtils.findField(declaringClass, name);
if (field == null) {
// Same lenient fallback checking as in CachedIntrospectionResults...
field = ReflectionUtils.findField(declaringClass,
name.substring(0, 1).toLowerCase() + name.substring(1));
if (field == null) {
field = ReflectionUtils.findField(declaringClass,
name.substring(0, 1).toUpperCase() + name.substring(1));
}
}
return field;
}
private Class<?> declaringClass() {
if (getReadMethod() != null) {
return getReadMethod().getDeclaringClass();
} else {
return getWriteMethod().getDeclaringClass();
}
}
}
// package private // package private
TypeDescriptor(AbstractDescriptor descriptor) { TypeDescriptor(AbstractDescriptor descriptor) {
@ -464,7 +624,7 @@ public class TypeDescriptor {
static Annotation[] nullSafeAnnotations(Annotation[] annotations) { static Annotation[] nullSafeAnnotations(Annotation[] annotations) {
return annotations != null ? annotations : EMPTY_ANNOTATION_ARRAY; return annotations != null ? annotations : EMPTY_ANNOTATION_ARRAY;
} }
// internal constructors // internal constructors
private TypeDescriptor(Class<?> type) { private TypeDescriptor(Class<?> type) {
@ -479,7 +639,8 @@ public class TypeDescriptor {
this(mapType, null, keyType, valueType, EMPTY_ANNOTATION_ARRAY); 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) {
this.type = type; this.type = type;
this.elementType = elementType; this.elementType = elementType;
this.mapKeyType = mapKeyType; this.mapKeyType = mapKeyType;
@ -494,7 +655,7 @@ public class TypeDescriptor {
return null; return null;
} }
} }
return new TypeDescriptor(descriptor); return new TypeDescriptor(descriptor);
} }
// internal helpers // internal helpers
@ -507,7 +668,7 @@ public class TypeDescriptor {
if (keyType == null) { if (keyType == null) {
return false; return false;
} }
return keyType.isAssignableTo(targetKeyType); return keyType.isAssignableTo(targetKeyType);
} }
private boolean collectionElementsAssignable(TypeDescriptor targetElementType) { private boolean collectionElementsAssignable(TypeDescriptor targetElementType) {
@ -518,9 +679,9 @@ public class TypeDescriptor {
if (elementType == null) { if (elementType == null) {
return false; return false;
} }
return elementType.isAssignableTo(targetElementType); return elementType.isAssignableTo(targetElementType);
} }
private boolean mapValuesAssignable(TypeDescriptor targetValueType) { private boolean mapValuesAssignable(TypeDescriptor targetValueType) {
TypeDescriptor valueType = getMapValueType(); TypeDescriptor valueType = getMapValueType();
if (targetValueType == null) { if (targetValueType == null) {
@ -529,21 +690,21 @@ public class TypeDescriptor {
if (valueType == null) { if (valueType == null) {
return false; return false;
} }
return valueType.isAssignableTo(targetValueType); return valueType.isAssignableTo(targetValueType);
} }
private void assertCollectionOrArray() { private void assertCollectionOrArray() {
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");
} }
} }
private void assertMap() { private void assertMap() {
if (!isMap()) { if (!isMap()) {
throw new IllegalStateException("Not a java.util.Map"); throw new IllegalStateException("Not a java.util.Map");
} }
} }
private String wildcard(TypeDescriptor nestedType) { private String wildcard(TypeDescriptor nestedType) {
return nestedType != null ? nestedType.toString() : "?"; return nestedType != null ? nestedType.toString() : "?";
} }

View File

@ -22,7 +22,6 @@ import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull; import static org.junit.Assert.assertNull;
import java.beans.PropertyDescriptor;
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;
@ -37,6 +36,7 @@ import java.util.Map;
import org.junit.Ignore; import org.junit.Ignore;
import org.junit.Test; import org.junit.Test;
import org.springframework.core.MethodParameter; import org.springframework.core.MethodParameter;
import org.springframework.core.convert.TypeDescriptor.Property;
/** /**
* @author Keith Donald * @author Keith Donald
@ -205,8 +205,8 @@ public class TypeDescriptorTests {
@Test @Test
public void propertyComplex() throws Exception { public void propertyComplex() throws Exception {
PropertyDescriptor property = new PropertyDescriptor("complexProperty", getClass().getMethod("getComplexProperty", null), getClass().getMethod("setComplexProperty", Map.class)); Property property = new Property(getClass(), getClass().getMethod("getComplexProperty", null), getClass().getMethod("setComplexProperty", Map.class));
TypeDescriptor desc = new TypeDescriptor(getClass(), property); TypeDescriptor desc = new TypeDescriptor(property);
assertEquals(String.class, desc.getMapKeyType().getType()); assertEquals(String.class, desc.getMapKeyType().getType());
assertEquals(Integer.class, desc.getMapValueType().getElementType().getElementType().getType()); assertEquals(Integer.class, desc.getMapValueType().getElementType().getElementType().getType());
} }
@ -222,16 +222,16 @@ public class TypeDescriptorTests {
@Test @Test
public void propertyGenericType() throws Exception { public void propertyGenericType() throws Exception {
GenericType<Integer> genericBean = new IntegerType(); GenericType<Integer> genericBean = new IntegerType();
PropertyDescriptor property = new PropertyDescriptor("property", genericBean.getClass().getMethod("getProperty", null), genericBean.getClass().getMethod("setProperty", Integer.class)); Property property = new Property(getClass(), genericBean.getClass().getMethod("getProperty", null), genericBean.getClass().getMethod("setProperty", Integer.class));
TypeDescriptor desc = new TypeDescriptor(genericBean.getClass(), property); TypeDescriptor desc = new TypeDescriptor(property);
assertEquals(Integer.class, desc.getType()); assertEquals(Integer.class, desc.getType());
} }
@Test @Test
public void propertyGenericTypeList() throws Exception { public void propertyGenericTypeList() throws Exception {
GenericType<Integer> genericBean = new IntegerType(); GenericType<Integer> genericBean = new IntegerType();
PropertyDescriptor property = new PropertyDescriptor("listProperty", genericBean.getClass().getMethod("getListProperty", null), genericBean.getClass().getMethod("setListProperty", List.class)); Property property = new Property(getClass(), genericBean.getClass().getMethod("getListProperty", null), genericBean.getClass().getMethod("setListProperty", List.class));
TypeDescriptor desc = new TypeDescriptor(genericBean.getClass(), property); TypeDescriptor desc = new TypeDescriptor(property);
assertEquals(List.class, desc.getType()); assertEquals(List.class, desc.getType());
assertEquals(Integer.class, desc.getElementType().getType()); assertEquals(Integer.class, desc.getElementType().getType());
} }
@ -274,8 +274,8 @@ public class TypeDescriptorTests {
@Test @Test
public void propertyGenericClassList() throws Exception { public void propertyGenericClassList() throws Exception {
IntegerClass genericBean = new IntegerClass(); IntegerClass genericBean = new IntegerClass();
PropertyDescriptor property = new PropertyDescriptor("listProperty", genericBean.getClass().getMethod("getListProperty", null), genericBean.getClass().getMethod("setListProperty", List.class)); Property property = new Property(genericBean.getClass(), genericBean.getClass().getMethod("getListProperty", null), genericBean.getClass().getMethod("setListProperty", List.class));
TypeDescriptor desc = new TypeDescriptor(genericBean.getClass(), property); TypeDescriptor desc = new TypeDescriptor(property);
assertEquals(List.class, desc.getType()); assertEquals(List.class, desc.getType());
assertEquals(Integer.class, desc.getElementType().getType()); assertEquals(Integer.class, desc.getElementType().getType());
assertNotNull(desc.getAnnotation(MethodAnnotation1.class)); assertNotNull(desc.getAnnotation(MethodAnnotation1.class));
@ -306,8 +306,9 @@ public class TypeDescriptorTests {
@Test @Test
public void property() throws Exception { public void property() throws Exception {
PropertyDescriptor property = new PropertyDescriptor("property", getClass().getMethod("getProperty", null), getClass().getMethod("setProperty", Map.class)); Property property = new Property(getClass(), getClass().getMethod("getProperty", null), getClass().getMethod("setProperty", Map.class));
TypeDescriptor desc = new TypeDescriptor(getClass(), property); TypeDescriptor desc = new TypeDescriptor(property);
assertEquals(Map.class, desc.getType());
assertEquals(Integer.class, desc.getMapKeyType().getElementType().getType()); assertEquals(Integer.class, desc.getMapKeyType().getElementType().getType());
assertEquals(Long.class, desc.getMapValueType().getElementType().getType()); assertEquals(Long.class, desc.getMapValueType().getElementType().getType());
assertNotNull(desc.getAnnotation(MethodAnnotation1.class)); assertNotNull(desc.getAnnotation(MethodAnnotation1.class));
@ -573,8 +574,8 @@ public class TypeDescriptorTests {
@Test @Test
public void nestedPropertyTypeMapTwoLevels() throws Exception { public void nestedPropertyTypeMapTwoLevels() throws Exception {
PropertyDescriptor property = new PropertyDescriptor("test4", getClass().getMethod("getTest4", null), getClass().getMethod("setTest4", List.class)); Property property = new Property(getClass(), getClass().getMethod("getTest4", null), getClass().getMethod("setTest4", List.class));
TypeDescriptor t1 = TypeDescriptor.nested(getClass(), property, 2); TypeDescriptor t1 = TypeDescriptor.nested(property, 2);
assertEquals(String.class, t1.getType()); assertEquals(String.class, t1.getType());
} }

View File

@ -16,8 +16,6 @@
package org.springframework.expression.spel.support; package org.springframework.expression.spel.support;
import java.beans.IntrospectionException;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Array; import java.lang.reflect.Array;
import java.lang.reflect.Field; import java.lang.reflect.Field;
import java.lang.reflect.Member; import java.lang.reflect.Member;
@ -28,6 +26,7 @@ import java.util.concurrent.ConcurrentHashMap;
import org.springframework.core.MethodParameter; import org.springframework.core.MethodParameter;
import org.springframework.core.convert.TypeDescriptor; import org.springframework.core.convert.TypeDescriptor;
import org.springframework.core.convert.TypeDescriptor.Property;
import org.springframework.expression.AccessException; import org.springframework.expression.AccessException;
import org.springframework.expression.EvaluationContext; import org.springframework.expression.EvaluationContext;
import org.springframework.expression.EvaluationException; import org.springframework.expression.EvaluationException;
@ -75,17 +74,12 @@ public class ReflectivePropertyAccessor implements PropertyAccessor {
Method method = findGetterForProperty(name, type, target instanceof Class); Method method = findGetterForProperty(name, type, target instanceof Class);
if (method != null) { if (method != null) {
// Treat it like a property // Treat it like a property
try { // The readerCache will only contain gettable properties (let's not worry about setters for now)
// The readerCache will only contain gettable properties (let's not worry about setters for now) Property property = new Property(type, method, null);
PropertyDescriptor propertyDescriptor = new PropertyDescriptor(name, method, null); TypeDescriptor typeDescriptor = new TypeDescriptor(property);
TypeDescriptor typeDescriptor = new TypeDescriptor(type, propertyDescriptor); this.readerCache.put(cacheKey, new InvokerPair(method, typeDescriptor));
this.readerCache.put(cacheKey, new InvokerPair(method, typeDescriptor)); this.typeDescriptorCache.put(cacheKey, typeDescriptor);
this.typeDescriptorCache.put(cacheKey, typeDescriptor); return true;
return true;
}
catch (IntrospectionException ex) {
throw new AccessException("Unable to access property '" + name + "' through getter " + method, ex);
}
} }
else { else {
Field field = findField(name, type, target instanceof Class); Field field = findField(name, type, target instanceof Class);
@ -122,17 +116,11 @@ public class ReflectivePropertyAccessor implements PropertyAccessor {
if (method != null) { if (method != null) {
// TODO remove the duplication here between canRead and read // TODO remove the duplication here between canRead and read
// Treat it like a property // Treat it like a property
try { // The readerCache will only contain gettable properties (let's not worry about setters for now)
// The readerCache will only contain gettable properties (let's not worry about setters for now) Property property = new Property(type, method, null);
PropertyDescriptor propertyDescriptor = new PropertyDescriptor(name, method, null); TypeDescriptor typeDescriptor = new TypeDescriptor(property);
TypeDescriptor typeDescriptor = new TypeDescriptor(type, propertyDescriptor); invoker = new InvokerPair(method, typeDescriptor);
invoker = new InvokerPair(method, typeDescriptor); this.readerCache.put(cacheKey, invoker);
this.readerCache.put(cacheKey, invoker);
}
catch (IntrospectionException ex) {
throw new AccessException(
"Unable to access property '" + name + "' through getter " + method, ex);
}
} }
} }
if (method != null) { if (method != null) {
@ -183,14 +171,8 @@ public class ReflectivePropertyAccessor implements PropertyAccessor {
Method method = findSetterForProperty(name, type, target instanceof Class); Method method = findSetterForProperty(name, type, target instanceof Class);
if (method != null) { if (method != null) {
// Treat it like a property // Treat it like a property
PropertyDescriptor propertyDescriptor = null; Property property = new Property(type, null, method);
try { TypeDescriptor typeDescriptor = new TypeDescriptor(property);
propertyDescriptor = new PropertyDescriptor(name, null, method);
}
catch (IntrospectionException ex) {
throw new AccessException("Unable to access property '" + name + "' through setter "+method, ex);
}
TypeDescriptor typeDescriptor = new TypeDescriptor(type, propertyDescriptor);
this.writerCache.put(cacheKey, method); this.writerCache.put(cacheKey, method);
this.typeDescriptorCache.put(cacheKey, typeDescriptor); this.typeDescriptorCache.put(cacheKey, typeDescriptor);
return true; return true;