diff --git a/org.springframework.beans/src/main/java/org/springframework/beans/BeanWrapperImpl.java b/org.springframework.beans/src/main/java/org/springframework/beans/BeanWrapperImpl.java index 7e79119747d..19e58fc4b78 100644 --- a/org.springframework.beans/src/main/java/org/springframework/beans/BeanWrapperImpl.java +++ b/org.springframework.beans/src/main/java/org/springframework/beans/BeanWrapperImpl.java @@ -43,6 +43,7 @@ import org.springframework.core.MethodParameter; import org.springframework.core.convert.ConversionException; import org.springframework.core.convert.ConverterNotFoundException; import org.springframework.core.convert.TypeDescriptor; +import org.springframework.core.convert.TypeDescriptor.Property; import org.springframework.util.Assert; import org.springframework.util.ObjectUtils; import org.springframework.util.StringUtils; @@ -369,11 +370,11 @@ public class BeanWrapperImpl extends AbstractPropertyAccessor implements BeanWra if (pd != null) { if (tokens.keys != 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 { 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 { GenericTypeAwarePropertyDescriptor gpd = (GenericTypeAwarePropertyDescriptor) pd; 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)) { 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 propValue = growArrayIfNecessary(propValue, arrayIndex, actualName); Array.set(propValue, arrayIndex, convertedValue); @@ -968,7 +969,7 @@ public class BeanWrapperImpl extends AbstractPropertyAccessor implements BeanWra if (isExtractOldValueForEditor() && index < list.size()) { 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()) { 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 // conversion ability for map values. 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); } else { @@ -1167,5 +1168,10 @@ public class BeanWrapperImpl extends AbstractPropertyAccessor implements BeanWra public String[] keys; } + + private Property property(PropertyDescriptor pd) { + GenericTypeAwarePropertyDescriptor typeAware = (GenericTypeAwarePropertyDescriptor) pd; + return new Property(typeAware.getBeanClass(), typeAware.getReadMethod(), typeAware.getWriteMethod()); + } } diff --git a/org.springframework.core/src/main/java/org/springframework/core/convert/AbstractDescriptor.java b/org.springframework.core/src/main/java/org/springframework/core/convert/AbstractDescriptor.java index 5bd3e7fcf5d..9632decced8 100644 --- a/org.springframework.core/src/main/java/org/springframework/core/convert/AbstractDescriptor.java +++ b/org.springframework.core/src/main/java/org/springframework/core/convert/AbstractDescriptor.java @@ -23,7 +23,7 @@ abstract class AbstractDescriptor { private final Class type; - public AbstractDescriptor(Class type) { + protected AbstractDescriptor(Class type) { if (type == null) { throw new IllegalArgumentException("type cannot be null"); } @@ -93,15 +93,15 @@ abstract class AbstractDescriptor { // internal helpers private boolean isCollection() { - return getType() != null && Collection.class.isAssignableFrom(getType()); + return Collection.class.isAssignableFrom(getType()); } private boolean isArray() { - return getType() != null && getType().isArray(); + return getType().isArray(); } private boolean isMap() { - return getType() != null && Map.class.isAssignableFrom(getType()); + return Map.class.isAssignableFrom(getType()); } } \ No newline at end of file diff --git a/org.springframework.core/src/main/java/org/springframework/core/convert/BeanPropertyDescriptor.java b/org.springframework.core/src/main/java/org/springframework/core/convert/BeanPropertyDescriptor.java index a2e6ebb4cf6..9bf16f667ba 100644 --- a/org.springframework.core/src/main/java/org/springframework/core/convert/BeanPropertyDescriptor.java +++ b/org.springframework.core/src/main/java/org/springframework/core/convert/BeanPropertyDescriptor.java @@ -15,35 +15,25 @@ */ package org.springframework.core.convert; -import java.beans.PropertyDescriptor; 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.GenericTypeResolver; import org.springframework.core.MethodParameter; -import org.springframework.util.ReflectionUtils; -import org.springframework.util.StringUtils; +import org.springframework.core.convert.TypeDescriptor.Property; class BeanPropertyDescriptor extends AbstractDescriptor { - private final Class beanClass; - - private final PropertyDescriptor property; + private final Property property; private final MethodParameter methodParameter; private final Annotation[] annotations; - public BeanPropertyDescriptor(Class beanClass, PropertyDescriptor property) { - super(property.getPropertyType()); - this.beanClass = beanClass; + public BeanPropertyDescriptor(Property property) { + super(property.getType()); this.property = property; - this.methodParameter = resolveMethodParameter(); - this.annotations = resolveAnnotations(); + this.methodParameter = property.getMethodParameter(); + this.annotations = property.getAnnotations(); } @Override @@ -71,79 +61,13 @@ class BeanPropertyDescriptor extends AbstractDescriptor { MethodParameter methodParameter = new MethodParameter(this.methodParameter); methodParameter.increaseNestingLevel(); methodParameter.setTypeIndexForCurrentLevel(typeIndex); - return new BeanPropertyDescriptor(type, beanClass, property, methodParameter, annotations); + return new BeanPropertyDescriptor(type, property, methodParameter, annotations); } // internal - private MethodParameter resolveMethodParameter() { - 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, Annotation> annMap = new LinkedHashMap, 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) { + private BeanPropertyDescriptor(Class type, Property propertyDescriptor, MethodParameter methodParameter, Annotation[] annotations) { super(type); - this.beanClass = beanClass; this.property = propertyDescriptor; this.methodParameter = methodParameter; this.annotations = annotations; diff --git a/org.springframework.core/src/main/java/org/springframework/core/convert/TypeDescriptor.java b/org.springframework.core/src/main/java/org/springframework/core/convert/TypeDescriptor.java index 21ab48d7ab8..a614595af01 100644 --- a/org.springframework.core/src/main/java/org/springframework/core/convert/TypeDescriptor.java +++ b/org.springframework.core/src/main/java/org/springframework/core/convert/TypeDescriptor.java @@ -13,19 +13,22 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package org.springframework.core.convert; -import java.beans.PropertyDescriptor; import java.lang.annotation.Annotation; import java.lang.reflect.Field; +import java.lang.reflect.Method; import java.util.Collection; import java.util.HashMap; +import java.util.LinkedHashMap; import java.util.Map; +import org.springframework.core.GenericTypeResolver; import org.springframework.core.MethodParameter; import org.springframework.util.ClassUtils; import org.springframework.util.ObjectUtils; +import org.springframework.util.ReflectionUtils; +import org.springframework.util.StringUtils; /** * 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. * 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 descriptor + * @param property the property */ - public TypeDescriptor(Class beanClass, PropertyDescriptor property) { - this(new BeanPropertyDescriptor(beanClass, property)); + public TypeDescriptor(Property property) { + this(new BeanPropertyDescriptor(property)); } /** @@ -140,7 +142,7 @@ public class TypeDescriptor { } return new TypeDescriptor(mapType, keyType, valueType); } - + /** * Creates a type descriptor for a nested type declared within the method parameter. * For example, if the methodParameter is a List<String> 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 * @throws IllegalArgumentException if the property is not of a collection, array, or map type. */ - public static TypeDescriptor nested(Class beanClass, PropertyDescriptor property, int nestingLevel) { - return nested(new BeanPropertyDescriptor(beanClass, property), nestingLevel); + public static TypeDescriptor nested(Property property, int nestingLevel) { + return nested(new BeanPropertyDescriptor(property), nestingLevel); } /** @@ -196,7 +198,7 @@ public class TypeDescriptor { public static TypeDescriptor forObject(Object source) { return source != null ? valueOf(source.getClass()) : null; } - + /** * Determine the declared (non-generic) type of the wrapped parameter/field. * @return the declared type, or null if this is {@link TypeDescriptor#NULL} @@ -230,7 +232,7 @@ public class TypeDescriptor { } return new TypeDescriptor(value.getClass(), elementType, mapKeyType, mapValueType, annotations); } - + /** * Returns the name of this type: the fully qualified class name. */ @@ -288,7 +290,7 @@ public class TypeDescriptor { } // indexable type descriptor operations - + /** * Is this type a {@link Collection} type? */ @@ -336,7 +338,7 @@ public class TypeDescriptor { } // map type descriptor operations - + /** * Is this type a {@link Map} type? */ @@ -406,7 +408,7 @@ public class TypeDescriptor { } // extending Object - + public boolean equals(Object obj) { if (this == obj) { return true; @@ -415,17 +417,17 @@ public class TypeDescriptor { return false; } 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) { return false; } if (isCollection() || isArray()) { return ObjectUtils.nullSafeEquals(getElementType(), other.getElementType()); - } - else if (isMap()) { - return ObjectUtils.nullSafeEquals(getMapKeyType(), other.getMapKeyType()) && ObjectUtils.nullSafeEquals(getMapValueType(), other.getMapValueType()); - } - else { + } else if (isMap()) { + return ObjectUtils.nullSafeEquals(getMapKeyType(), other.getMapKeyType()) + && ObjectUtils.nullSafeEquals(getMapValueType(), other.getMapValueType()); + } else { return true; } } @@ -444,13 +446,171 @@ public class TypeDescriptor { if (isMap()) { builder.append("<").append(wildcard(getMapKeyType())); builder.append(", ").append(wildcard(getMapValueType())).append(">"); - } - else if (isCollection()) { + } else if (isCollection()) { builder.append("<").append(wildcard(getElementType())).append(">"); } 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, Annotation> annMap = new LinkedHashMap, 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 TypeDescriptor(AbstractDescriptor descriptor) { @@ -464,7 +624,7 @@ public class TypeDescriptor { static Annotation[] nullSafeAnnotations(Annotation[] annotations) { return annotations != null ? annotations : EMPTY_ANNOTATION_ARRAY; } - + // internal constructors private TypeDescriptor(Class type) { @@ -479,7 +639,8 @@ public class TypeDescriptor { 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.elementType = elementType; this.mapKeyType = mapKeyType; @@ -494,7 +655,7 @@ public class TypeDescriptor { return null; } } - return new TypeDescriptor(descriptor); + return new TypeDescriptor(descriptor); } // internal helpers @@ -507,7 +668,7 @@ public class TypeDescriptor { if (keyType == null) { return false; } - return keyType.isAssignableTo(targetKeyType); + return keyType.isAssignableTo(targetKeyType); } private boolean collectionElementsAssignable(TypeDescriptor targetElementType) { @@ -518,9 +679,9 @@ public class TypeDescriptor { if (elementType == null) { return false; } - return elementType.isAssignableTo(targetElementType); + return elementType.isAssignableTo(targetElementType); } - + private boolean mapValuesAssignable(TypeDescriptor targetValueType) { TypeDescriptor valueType = getMapValueType(); if (targetValueType == null) { @@ -529,21 +690,21 @@ public class TypeDescriptor { if (valueType == null) { return false; } - return valueType.isAssignableTo(targetValueType); + return valueType.isAssignableTo(targetValueType); } private void assertCollectionOrArray() { if (!isCollection() && !isArray()) { throw new IllegalStateException("Not a java.util.Collection or Array"); - } + } } - + private void assertMap() { if (!isMap()) { throw new IllegalStateException("Not a java.util.Map"); - } + } } - + private String wildcard(TypeDescriptor nestedType) { return nestedType != null ? nestedType.toString() : "?"; } diff --git a/org.springframework.core/src/test/java/org/springframework/core/convert/TypeDescriptorTests.java b/org.springframework.core/src/test/java/org/springframework/core/convert/TypeDescriptorTests.java index a6284d05507..fbf978610de 100644 --- a/org.springframework.core/src/test/java/org/springframework/core/convert/TypeDescriptorTests.java +++ b/org.springframework.core/src/test/java/org/springframework/core/convert/TypeDescriptorTests.java @@ -22,7 +22,6 @@ import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; -import java.beans.PropertyDescriptor; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; @@ -37,6 +36,7 @@ import java.util.Map; import org.junit.Ignore; import org.junit.Test; import org.springframework.core.MethodParameter; +import org.springframework.core.convert.TypeDescriptor.Property; /** * @author Keith Donald @@ -205,8 +205,8 @@ public class TypeDescriptorTests { @Test public void propertyComplex() throws Exception { - PropertyDescriptor property = new PropertyDescriptor("complexProperty", getClass().getMethod("getComplexProperty", null), getClass().getMethod("setComplexProperty", Map.class)); - TypeDescriptor desc = new TypeDescriptor(getClass(), property); + Property property = new Property(getClass(), getClass().getMethod("getComplexProperty", null), getClass().getMethod("setComplexProperty", Map.class)); + TypeDescriptor desc = new TypeDescriptor(property); assertEquals(String.class, desc.getMapKeyType().getType()); assertEquals(Integer.class, desc.getMapValueType().getElementType().getElementType().getType()); } @@ -222,16 +222,16 @@ public class TypeDescriptorTests { @Test public void propertyGenericType() throws Exception { GenericType genericBean = new IntegerType(); - PropertyDescriptor property = new PropertyDescriptor("property", genericBean.getClass().getMethod("getProperty", null), genericBean.getClass().getMethod("setProperty", Integer.class)); - TypeDescriptor desc = new TypeDescriptor(genericBean.getClass(), property); + Property property = new Property(getClass(), genericBean.getClass().getMethod("getProperty", null), genericBean.getClass().getMethod("setProperty", Integer.class)); + TypeDescriptor desc = new TypeDescriptor(property); assertEquals(Integer.class, desc.getType()); } @Test public void propertyGenericTypeList() throws Exception { GenericType genericBean = new IntegerType(); - PropertyDescriptor property = new PropertyDescriptor("listProperty", genericBean.getClass().getMethod("getListProperty", null), genericBean.getClass().getMethod("setListProperty", List.class)); - TypeDescriptor desc = new TypeDescriptor(genericBean.getClass(), property); + Property property = new Property(getClass(), genericBean.getClass().getMethod("getListProperty", null), genericBean.getClass().getMethod("setListProperty", List.class)); + TypeDescriptor desc = new TypeDescriptor(property); assertEquals(List.class, desc.getType()); assertEquals(Integer.class, desc.getElementType().getType()); } @@ -274,8 +274,8 @@ public class TypeDescriptorTests { @Test public void propertyGenericClassList() throws Exception { IntegerClass genericBean = new IntegerClass(); - PropertyDescriptor property = new PropertyDescriptor("listProperty", genericBean.getClass().getMethod("getListProperty", null), genericBean.getClass().getMethod("setListProperty", List.class)); - TypeDescriptor desc = new TypeDescriptor(genericBean.getClass(), property); + Property property = new Property(genericBean.getClass(), genericBean.getClass().getMethod("getListProperty", null), genericBean.getClass().getMethod("setListProperty", List.class)); + TypeDescriptor desc = new TypeDescriptor(property); assertEquals(List.class, desc.getType()); assertEquals(Integer.class, desc.getElementType().getType()); assertNotNull(desc.getAnnotation(MethodAnnotation1.class)); @@ -306,8 +306,9 @@ public class TypeDescriptorTests { @Test public void property() throws Exception { - PropertyDescriptor property = new PropertyDescriptor("property", getClass().getMethod("getProperty", null), getClass().getMethod("setProperty", Map.class)); - TypeDescriptor desc = new TypeDescriptor(getClass(), property); + Property property = new Property(getClass(), getClass().getMethod("getProperty", null), getClass().getMethod("setProperty", Map.class)); + TypeDescriptor desc = new TypeDescriptor(property); + assertEquals(Map.class, desc.getType()); assertEquals(Integer.class, desc.getMapKeyType().getElementType().getType()); assertEquals(Long.class, desc.getMapValueType().getElementType().getType()); assertNotNull(desc.getAnnotation(MethodAnnotation1.class)); @@ -573,8 +574,8 @@ public class TypeDescriptorTests { @Test public void nestedPropertyTypeMapTwoLevels() throws Exception { - PropertyDescriptor property = new PropertyDescriptor("test4", getClass().getMethod("getTest4", null), getClass().getMethod("setTest4", List.class)); - TypeDescriptor t1 = TypeDescriptor.nested(getClass(), property, 2); + Property property = new Property(getClass(), getClass().getMethod("getTest4", null), getClass().getMethod("setTest4", List.class)); + TypeDescriptor t1 = TypeDescriptor.nested(property, 2); assertEquals(String.class, t1.getType()); } diff --git a/org.springframework.expression/src/main/java/org/springframework/expression/spel/support/ReflectivePropertyAccessor.java b/org.springframework.expression/src/main/java/org/springframework/expression/spel/support/ReflectivePropertyAccessor.java index 2c725ae12ba..6049c31d331 100644 --- a/org.springframework.expression/src/main/java/org/springframework/expression/spel/support/ReflectivePropertyAccessor.java +++ b/org.springframework.expression/src/main/java/org/springframework/expression/spel/support/ReflectivePropertyAccessor.java @@ -16,8 +16,6 @@ package org.springframework.expression.spel.support; -import java.beans.IntrospectionException; -import java.beans.PropertyDescriptor; import java.lang.reflect.Array; import java.lang.reflect.Field; import java.lang.reflect.Member; @@ -28,6 +26,7 @@ import java.util.concurrent.ConcurrentHashMap; import org.springframework.core.MethodParameter; import org.springframework.core.convert.TypeDescriptor; +import org.springframework.core.convert.TypeDescriptor.Property; import org.springframework.expression.AccessException; import org.springframework.expression.EvaluationContext; import org.springframework.expression.EvaluationException; @@ -75,17 +74,12 @@ public class ReflectivePropertyAccessor implements PropertyAccessor { Method method = findGetterForProperty(name, type, target instanceof Class); if (method != null) { // Treat it like a property - try { - // The readerCache will only contain gettable properties (let's not worry about setters for now) - PropertyDescriptor propertyDescriptor = new PropertyDescriptor(name, method, null); - TypeDescriptor typeDescriptor = new TypeDescriptor(type, propertyDescriptor); - this.readerCache.put(cacheKey, new InvokerPair(method, typeDescriptor)); - this.typeDescriptorCache.put(cacheKey, typeDescriptor); - return true; - } - catch (IntrospectionException ex) { - throw new AccessException("Unable to access property '" + name + "' through getter " + method, ex); - } + // The readerCache will only contain gettable properties (let's not worry about setters for now) + Property property = new Property(type, method, null); + TypeDescriptor typeDescriptor = new TypeDescriptor(property); + this.readerCache.put(cacheKey, new InvokerPair(method, typeDescriptor)); + this.typeDescriptorCache.put(cacheKey, typeDescriptor); + return true; } else { Field field = findField(name, type, target instanceof Class); @@ -122,17 +116,11 @@ public class ReflectivePropertyAccessor implements PropertyAccessor { if (method != null) { // TODO remove the duplication here between canRead and read // Treat it like a property - try { - // The readerCache will only contain gettable properties (let's not worry about setters for now) - PropertyDescriptor propertyDescriptor = new PropertyDescriptor(name, method, null); - TypeDescriptor typeDescriptor = new TypeDescriptor(type, propertyDescriptor); - invoker = new InvokerPair(method, typeDescriptor); - this.readerCache.put(cacheKey, invoker); - } - catch (IntrospectionException ex) { - throw new AccessException( - "Unable to access property '" + name + "' through getter " + method, ex); - } + // The readerCache will only contain gettable properties (let's not worry about setters for now) + Property property = new Property(type, method, null); + TypeDescriptor typeDescriptor = new TypeDescriptor(property); + invoker = new InvokerPair(method, typeDescriptor); + this.readerCache.put(cacheKey, invoker); } } if (method != null) { @@ -183,14 +171,8 @@ public class ReflectivePropertyAccessor implements PropertyAccessor { Method method = findSetterForProperty(name, type, target instanceof Class); if (method != null) { // Treat it like a property - PropertyDescriptor propertyDescriptor = null; - try { - 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); + Property property = new Property(type, null, method); + TypeDescriptor typeDescriptor = new TypeDescriptor(property); this.writerCache.put(cacheKey, method); this.typeDescriptorCache.put(cacheKey, typeDescriptor); return true;