polishing

git-svn-id: https://src.springframework.org/svn/spring-framework/trunk@4451 50f2f4bb-b051-0410-bef5-90022cba6387
This commit is contained in:
Keith Donald 2011-06-05 05:46:27 +00:00
parent a34147a53d
commit b58386da08
8 changed files with 86 additions and 29 deletions

View File

@ -30,6 +30,7 @@ import org.springframework.core.CollectionFactory;
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;
import org.springframework.core.type.filter.TypeFilter;
import org.springframework.util.ClassUtils; import org.springframework.util.ClassUtils;
import org.springframework.util.StringUtils; import org.springframework.util.StringUtils;
@ -131,7 +132,7 @@ class TypeConverterDelegate {
// No custom editor but custom ConversionService specified? // No custom editor but custom ConversionService specified?
ConversionService conversionService = this.propertyEditorRegistry.getConversionService(); ConversionService conversionService = this.propertyEditorRegistry.getConversionService();
if (editor == null && conversionService != null && convertedValue != null) { if (editor == null && conversionService != null && convertedValue != null) {
TypeDescriptor sourceTypeDesc = TypeDescriptor.forObject(convertedValue); TypeDescriptor sourceTypeDesc = TypeDescriptor.forObject(newValue);
TypeDescriptor targetTypeDesc = typeDescriptor; TypeDescriptor targetTypeDesc = typeDescriptor;
if (conversionService.canConvert(sourceTypeDesc, targetTypeDesc)) { if (conversionService.canConvert(sourceTypeDesc, targetTypeDesc)) {
return (T) conversionService.convert(convertedValue, sourceTypeDesc, targetTypeDesc); return (T) conversionService.convert(convertedValue, sourceTypeDesc, targetTypeDesc);
@ -458,7 +459,7 @@ class TypeConverterDelegate {
if (!originalAllowed && !Collection.class.isAssignableFrom(requiredType)) { if (!originalAllowed && !Collection.class.isAssignableFrom(requiredType)) {
return original; return original;
} }
typeDescriptor = typeDescriptor.narrow(original);
TypeDescriptor elementType = typeDescriptor.getElementType(); TypeDescriptor elementType = typeDescriptor.getElementType();
if (elementType == null && originalAllowed && if (elementType == null && originalAllowed &&
!this.propertyEditorRegistry.hasCustomEditorForElement(null, propertyName)) { !this.propertyEditorRegistry.hasCustomEditorForElement(null, propertyName)) {
@ -530,7 +531,7 @@ class TypeConverterDelegate {
if (!originalAllowed && !Map.class.isAssignableFrom(requiredType)) { if (!originalAllowed && !Map.class.isAssignableFrom(requiredType)) {
return original; return original;
} }
typeDescriptor = typeDescriptor.narrow(original);
TypeDescriptor keyType = typeDescriptor.getMapKeyType(); TypeDescriptor keyType = typeDescriptor.getMapKeyType();
TypeDescriptor valueType = typeDescriptor.getMapValueType(); TypeDescriptor valueType = typeDescriptor.getMapValueType();
if (keyType == null && valueType == null && originalAllowed && if (keyType == null && valueType == null && originalAllowed &&

View File

@ -102,6 +102,15 @@ public class CallbacksSecurityTests {
return null; return null;
} }
public void setListProperty(Object value) {
checkCurrentContext();
}
public Object getListProperty() {
checkCurrentContext();
return null;
}
private void checkCurrentContext() { private void checkCurrentContext() {
assertEquals(expectedName, getCurrentSubjectName()); assertEquals(expectedName, getCurrentSubjectName());
} }

View File

@ -82,6 +82,12 @@
<entry key-ref="trusted-property-injection" value-ref="trusted-factory-method"/> <entry key-ref="trusted-property-injection" value-ref="trusted-factory-method"/>
</map> </map>
</property> </property>
<property name="listProperty">
<list>
<value>foo</value>
<value>bar</value>
</list>
</property>
</bean> </bean>
</beans> </beans>

View File

@ -186,6 +186,13 @@ public class TypeDescriptor {
return nested(new BeanPropertyDescriptor(beanClass, property), nestingLevel); return nested(new BeanPropertyDescriptor(beanClass, property), nestingLevel);
} }
/**
* Create a new type descriptor for an object.
* Use this factory method to introspect a source object before asking the conversion system to convert it to some another type.
* If the provided object is null, returns null, else calls {@link #valueOf(Class)} to build a TypeDescriptor from the object's class.
* @param object the source object
* @return the type descriptor
*/
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;
} }
@ -206,12 +213,24 @@ public class TypeDescriptor {
return ClassUtils.resolvePrimitiveIfNecessary(getType()); return ClassUtils.resolvePrimitiveIfNecessary(getType());
} }
public TypeDescriptor narrowType(Object value) { /**
* Narrows this {@link TypeDescriptor} by setting its type to the class of the provided value.
* If the value is null, no narrowing is performed and this TypeDescriptor is returned unchanged.
* Designed to be called by binding frameworks when they read property, field, or method return values.
* Allows such frameworks to narrow a TypeDescriptor built from a declared property, field, or method return value type.
* For example, a field declared as java.lang.Object would be narrowed to java.util.HashMap if it was set to a java.util.HashMap value.
* The narrowed TypeDescriptor can then be used to convert the HashMap to some other type.
* Annotation and nested type context is preserved by the narrowed copy.
* @param value the value to use for narrowing this type descriptor
* @return this TypeDescriptor narrowed (returns a copy with its type updated to the class of the provided value)
*/
public TypeDescriptor narrow(Object value) {
if (value == null) { if (value == null) {
return this; return this;
} }
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.
*/ */
@ -292,15 +311,19 @@ public class TypeDescriptor {
* @throws IllegalStateException if this type is not a java.util.Collection or Array type * @throws IllegalStateException if this type is not a java.util.Collection or Array type
*/ */
public TypeDescriptor getElementType() { public TypeDescriptor getElementType() {
if (!isCollection() && !isArray()) { assertCollectionOrArray();
throw new IllegalStateException("Not a java.util.Collection or Array");
}
return this.elementType; return this.elementType;
} }
/**
* Creates a elementType descriptor from the provided collection or array element.
* @param element the collection or array element
* @return the element type descriptor
*/
public TypeDescriptor elementType(Object element) { public TypeDescriptor elementType(Object element) {
assertCollectionOrArray();
if (elementType != null) { if (elementType != null) {
return elementType.narrowType(element); return elementType.narrow(element);
} else { } else {
return element != null ? new TypeDescriptor(element.getClass(), null, null, null, annotations) : null; return element != null ? new TypeDescriptor(element.getClass(), null, null, null, annotations) : null;
} }
@ -322,15 +345,19 @@ public class TypeDescriptor {
* @throws IllegalStateException if this type is not a java.util.Map. * @throws IllegalStateException if this type is not a java.util.Map.
*/ */
public TypeDescriptor getMapKeyType() { public TypeDescriptor getMapKeyType() {
if (!isMap()) { assertMap();
throw new IllegalStateException("Not a map");
}
return this.mapKeyType; return this.mapKeyType;
} }
/**
* Creates a mapKeyType descriptor from the provided map key.
* @param mapKey the map key
* @return the map key type descriptor
*/
public TypeDescriptor mapKeyType(Object mapKey) { public TypeDescriptor mapKeyType(Object mapKey) {
assertMap();
if (mapKeyType != null) { if (mapKeyType != null) {
return mapKeyType.narrowType(mapKey); return mapKeyType.narrow(mapKey);
} else { } else {
return mapKey != null ? new TypeDescriptor(mapKey.getClass(), null, null, null, annotations) : null; return mapKey != null ? new TypeDescriptor(mapKey.getClass(), null, null, null, annotations) : null;
} }
@ -343,15 +370,19 @@ public class TypeDescriptor {
* @throws IllegalStateException if this type is not a java.util.Map. * @throws IllegalStateException if this type is not a java.util.Map.
*/ */
public TypeDescriptor getMapValueType() { public TypeDescriptor getMapValueType() {
if (!isMap()) { assertMap();
throw new IllegalStateException("Not a map");
}
return this.mapValueType; return this.mapValueType;
} }
/**
* Creates a mapValueType descriptor from the provided map value.
* @param mapValue the map value
* @return the map value type descriptor
*/
public TypeDescriptor mapValueType(Object mapValue) { public TypeDescriptor mapValueType(Object mapValue) {
assertMap();
if (mapValueType != null) { if (mapValueType != null) {
return mapValueType.narrowType(mapValue); return mapValueType.narrow(mapValue);
} else { } else {
return mapValue != null ? new TypeDescriptor(mapValue.getClass(), null, null, null, annotations) : null; return mapValue != null ? new TypeDescriptor(mapValue.getClass(), null, null, null, annotations) : null;
} }
@ -481,6 +512,18 @@ public class TypeDescriptor {
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) { private String wildcard(TypeDescriptor nestedType) {
return nestedType != null ? nestedType.toString() : "?"; return nestedType != null ? nestedType.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)).narrowType(result)); return new TypedValue(result, new TypeDescriptor(new MethodParameter(method,-1)).narrow(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

@ -30,7 +30,6 @@ 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
@ -219,13 +218,12 @@ public class ReflectionHelper {
// 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 (!ObjectUtils.nullSafeEquals(varargsParameterType, suppliedArg)) {
if (suppliedArg == null) { if (suppliedArg == null) {
if (varargsParameterType.isPrimitive()) { if (varargsParameterType.isPrimitive()) {
match = null; match = null;
} }
} } else {
else { if (varargsParameterType != suppliedArg.getType()) {
if (ClassUtils.isAssignable(varargsParameterType, suppliedArg.getType())) { if (ClassUtils.isAssignable(varargsParameterType, suppliedArg.getType())) {
if (match != ArgsMatchKind.REQUIRES_CONVERSION) { if (match != ArgsMatchKind.REQUIRES_CONVERSION) {
match = ArgsMatchKind.CLOSE; match = ArgsMatchKind.CLOSE;

View File

@ -67,7 +67,7 @@ class ReflectiveMethodExecutor implements MethodExecutor {
} }
ReflectionUtils.makeAccessible(this.method); ReflectionUtils.makeAccessible(this.method);
Object value = this.method.invoke(target, arguments); Object value = this.method.invoke(target, arguments);
return new TypedValue(value, new TypeDescriptor(new MethodParameter(this.method, -1)).narrowType(value)); return new TypedValue(value, new TypeDescriptor(new MethodParameter(this.method, -1)).narrow(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

@ -509,7 +509,7 @@ public class ReflectivePropertyAccessor implements PropertyAccessor {
ReflectionUtils.makeAccessible((Method) member); ReflectionUtils.makeAccessible((Method) member);
} }
Object value = ((Method) member).invoke(target); Object value = ((Method) member).invoke(target);
return new TypedValue(value, typeDescriptor.narrowType(value)); return new TypedValue(value, typeDescriptor.narrow(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);
@ -521,7 +521,7 @@ public class ReflectivePropertyAccessor implements PropertyAccessor {
ReflectionUtils.makeAccessible((Field)member); ReflectionUtils.makeAccessible((Field)member);
} }
Object value = ((Field)member).get(target); Object value = ((Field)member).get(target);
return new TypedValue(value, typeDescriptor.narrowType(value)); return new TypedValue(value, typeDescriptor.narrow(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);