revised BeanWrapper's exception wrapping to consistently handle ConversionExceptions (SPR-7177)

This commit is contained in:
Juergen Hoeller 2010-05-17 21:59:02 +00:00
parent 57a503b274
commit 2ad2022058
4 changed files with 156 additions and 185 deletions

View File

@ -422,8 +422,8 @@ public class BeanWrapperImpl extends AbstractPropertyAccessor implements BeanWra
return false;
}
public <T> T convertIfNecessary(
Object value, Class<T> requiredType, MethodParameter methodParam) throws TypeMismatchException {
public <T> T convertIfNecessary(Object value, Class<T> requiredType, MethodParameter methodParam)
throws TypeMismatchException {
try {
return this.typeConverterDelegate.convertIfNecessary(value, requiredType, methodParam);
}
@ -441,6 +441,39 @@ public class BeanWrapperImpl extends AbstractPropertyAccessor implements BeanWra
}
}
private Object convertIfNecessary(String propertyName, Object oldValue, Object newValue, Class<?> requiredType,
TypeDescriptor td) throws TypeMismatchException {
try {
return this.typeConverterDelegate.convertIfNecessary(propertyName, oldValue, newValue, requiredType, td);
}
catch (ConverterNotFoundException ex) {
PropertyChangeEvent pce =
new PropertyChangeEvent(this.rootObject, this.nestedPath + propertyName, oldValue, newValue);
throw new ConversionNotSupportedException(pce, td.getType(), ex);
}
catch (ConversionException ex) {
PropertyChangeEvent pce =
new PropertyChangeEvent(this.rootObject, this.nestedPath + propertyName, oldValue, newValue);
throw new TypeMismatchException(pce, requiredType, ex);
}
catch (IllegalStateException ex) {
PropertyChangeEvent pce =
new PropertyChangeEvent(this.rootObject, this.nestedPath + propertyName, oldValue, newValue);
throw new ConversionNotSupportedException(pce, requiredType, ex);
}
catch (IllegalArgumentException ex) {
PropertyChangeEvent pce =
new PropertyChangeEvent(this.rootObject, this.nestedPath + propertyName, oldValue, newValue);
throw new TypeMismatchException(pce, requiredType, ex);
}
}
private Object convertIfNecessary(String propertyName, Object oldValue, Object newValue, Class<?> requiredType)
throws TypeMismatchException {
return convertIfNecessary(propertyName, oldValue, newValue, requiredType, TypeDescriptor.valueOf(requiredType));
}
/**
* Convert the given value for the specified property to the latter's type.
* <p>This method is only intended for optimizations in a BeanFactory.
@ -457,19 +490,14 @@ public class BeanWrapperImpl extends AbstractPropertyAccessor implements BeanWra
throw new InvalidPropertyException(getRootClass(), this.nestedPath + propertyName,
"No property '" + propertyName + "' found");
}
try {
return this.typeConverterDelegate.convertIfNecessary(null, value, pd);
}
catch (IllegalArgumentException ex) {
PropertyChangeEvent pce =
new PropertyChangeEvent(this.rootObject, this.nestedPath + propertyName, null, value);
throw new TypeMismatchException(pce, pd.getPropertyType(), ex);
}
catch (IllegalStateException ex) {
PropertyChangeEvent pce =
new PropertyChangeEvent(this.rootObject, this.nestedPath + propertyName, null, value);
throw new ConversionNotSupportedException(pce, pd.getPropertyType(), ex);
return convertForProperty(propertyName, null, value, pd);
}
private Object convertForProperty(String propertyName, Object oldValue, Object newValue, PropertyDescriptor pd)
throws TypeMismatchException {
return convertIfNecessary(propertyName, oldValue, newValue, pd.getPropertyType(),
new PropertyTypeDescriptor(pd, BeanUtils.getWriteMethodParameter(pd)));
}
@ -690,7 +718,6 @@ public class BeanWrapperImpl extends AbstractPropertyAccessor implements BeanWra
}
Object value;
if (System.getSecurityManager() != null) {
try {
value = AccessController.doPrivileged(new PrivilegedExceptionAction<Object>() {
@ -761,7 +788,7 @@ public class BeanWrapperImpl extends AbstractPropertyAccessor implements BeanWra
Class<?> mapKeyType = GenericCollectionTypeResolver.getMapKeyReturnType(pd.getReadMethod(), i + 1);
// IMPORTANT: Do not pass full property name in here - property editors
// must not kick in for map keys but rather only for map values.
Object convertedMapKey = this.typeConverterDelegate.convertIfNecessary(key, mapKeyType);
Object convertedMapKey = convertIfNecessary(null, null, key, mapKeyType);
// Pass full property name and old value in here, since we want full
// conversion ability for map values.
value = map.get(convertedMapKey);
@ -776,15 +803,6 @@ public class BeanWrapperImpl extends AbstractPropertyAccessor implements BeanWra
}
return value;
}
catch (InvocationTargetException ex) {
throw new InvalidPropertyException(getRootClass(), this.nestedPath + propertyName,
"Getter for property '" + actualName + "' threw exception", ex);
}
catch (IllegalAccessException ex) {
throw new InvalidPropertyException(getRootClass(), this.nestedPath + propertyName,
"Illegal attempt to get property '" + actualName + "' threw exception", ex);
}
catch (IndexOutOfBoundsException ex) {
throw new InvalidPropertyException(getRootClass(), this.nestedPath + propertyName,
"Index of out of bounds in property path '" + propertyName + "'", ex);
@ -793,10 +811,18 @@ public class BeanWrapperImpl extends AbstractPropertyAccessor implements BeanWra
throw new InvalidPropertyException(getRootClass(), this.nestedPath + propertyName,
"Invalid index in property path '" + propertyName + "'", ex);
}
catch (Exception ex) {
catch (TypeMismatchException ex) {
throw new InvalidPropertyException(getRootClass(), this.nestedPath + propertyName,
"Invalid index in property path '" + propertyName + "'", ex);
}
catch (InvocationTargetException ex) {
throw new InvalidPropertyException(getRootClass(), this.nestedPath + propertyName,
"Getter for property '" + actualName + "' threw exception", ex);
}
catch (Exception ex) {
throw new InvalidPropertyException(getRootClass(), this.nestedPath + propertyName,
"Illegal attempt to get property '" + actualName + "' threw exception", ex);
}
}
private Object growArrayIfNecessary(Object array, int index, String name) {
@ -819,7 +845,10 @@ public class BeanWrapperImpl extends AbstractPropertyAccessor implements BeanWra
}
}
private void growCollectionIfNecessary(Collection collection, int index, String name, PropertyDescriptor pd, int nestingLevel) {
@SuppressWarnings("unchecked")
private void growCollectionIfNecessary(
Collection collection, int index, String name, PropertyDescriptor pd, int nestingLevel) {
if (!this.autoGrowNestedPaths) {
return;
}
@ -907,19 +936,8 @@ public class BeanWrapperImpl extends AbstractPropertyAccessor implements BeanWra
if (isExtractOldValueForEditor()) {
oldValue = Array.get(propValue, arrayIndex);
}
Object convertedValue = this.typeConverterDelegate.convertIfNecessary(
propertyName, oldValue, pv.getValue(), requiredType);
Array.set(propValue, Integer.parseInt(key), convertedValue);
}
catch (IllegalArgumentException ex) {
PropertyChangeEvent pce =
new PropertyChangeEvent(this.rootObject, this.nestedPath + propertyName, oldValue, pv.getValue());
throw new TypeMismatchException(pce, requiredType, ex);
}
catch (IllegalStateException ex) {
PropertyChangeEvent pce =
new PropertyChangeEvent(this.rootObject, this.nestedPath + propertyName, oldValue, pv.getValue());
throw new ConversionNotSupportedException(pce, requiredType, ex);
Object convertedValue = convertIfNecessary(propertyName, oldValue, pv.getValue(), requiredType);
Array.set(propValue, arrayIndex, convertedValue);
}
catch (IndexOutOfBoundsException ex) {
throw new InvalidPropertyException(getRootClass(), this.nestedPath + propertyName,
@ -936,9 +954,7 @@ public class BeanWrapperImpl extends AbstractPropertyAccessor implements BeanWra
if (isExtractOldValueForEditor() && index < list.size()) {
oldValue = list.get(index);
}
try {
Object convertedValue = this.typeConverterDelegate.convertIfNecessary(
propertyName, oldValue, pv.getValue(), requiredType);
Object convertedValue = convertIfNecessary(propertyName, oldValue, pv.getValue(), requiredType);
if (index < list.size()) {
list.set(index, convertedValue);
}
@ -957,12 +973,6 @@ public class BeanWrapperImpl extends AbstractPropertyAccessor implements BeanWra
list.add(convertedValue);
}
}
catch (IllegalArgumentException ex) {
PropertyChangeEvent pce =
new PropertyChangeEvent(this.rootObject, this.nestedPath + propertyName, oldValue, pv.getValue());
throw new TypeMismatchException(pce, requiredType, ex);
}
}
else if (propValue instanceof Map) {
PropertyDescriptor pd = getCachedIntrospectionResults().getPropertyDescriptor(actualName);
Class mapKeyType = GenericCollectionTypeResolver.getMapKeyReturnType(
@ -970,34 +980,18 @@ public class BeanWrapperImpl extends AbstractPropertyAccessor implements BeanWra
Class mapValueType = GenericCollectionTypeResolver.getMapValueReturnType(
pd.getReadMethod(), tokens.keys.length);
Map map = (Map) propValue;
Object convertedMapKey;
Object convertedMapValue;
try {
// IMPORTANT: Do not pass full property name in here - property editors
// must not kick in for map keys but rather only for map values.
convertedMapKey = this.typeConverterDelegate.convertIfNecessary(key, mapKeyType);
}
catch (IllegalArgumentException ex) {
PropertyChangeEvent pce =
new PropertyChangeEvent(this.rootObject, this.nestedPath + propertyName, null, pv.getValue());
throw new TypeMismatchException(pce, mapKeyType, ex);
}
Object convertedMapKey = convertIfNecessary(null, null, key, mapKeyType);
Object oldValue = null;
if (isExtractOldValueForEditor()) {
oldValue = map.get(convertedMapKey);
}
try {
// Pass full property name and old value in here, since we want full
// conversion ability for map values.
convertedMapValue = this.typeConverterDelegate.convertIfNecessary(
Object convertedMapValue = convertIfNecessary(
propertyName, oldValue, pv.getValue(), mapValueType,
new MethodParameter(pd.getReadMethod(), -1, tokens.keys.length + 1));
}
catch (IllegalArgumentException ex) {
PropertyChangeEvent pce =
new PropertyChangeEvent(this.rootObject, this.nestedPath + propertyName, oldValue, pv.getValue());
throw new TypeMismatchException(pce, mapValueType, ex);
}
new TypeDescriptor(new MethodParameter(pd.getReadMethod(), -1, tokens.keys.length + 1)));
map.put(convertedMapKey, convertedMapValue);
}
else {
@ -1038,7 +1032,8 @@ public class BeanWrapperImpl extends AbstractPropertyAccessor implements BeanWra
else {
if (isExtractOldValueForEditor() && pd.getReadMethod() != null) {
final Method readMethod = pd.getReadMethod();
if (!Modifier.isPublic(readMethod.getDeclaringClass().getModifiers()) && !readMethod.isAccessible()) {
if (!Modifier.isPublic(readMethod.getDeclaringClass().getModifiers()) &&
!readMethod.isAccessible()) {
if (System.getSecurityManager()!= null) {
AccessController.doPrivileged(new PrivilegedAction<Object>() {
public Object run() {
@ -1073,7 +1068,7 @@ public class BeanWrapperImpl extends AbstractPropertyAccessor implements BeanWra
}
}
}
valueToApply = this.typeConverterDelegate.convertIfNecessary(oldValue, originalValue, pd);
valueToApply = convertForProperty(propertyName, oldValue, originalValue, pd);
}
pv.getOriginalPropertyValue().conversionNecessary = (valueToApply != originalValue);
}
@ -1094,7 +1089,6 @@ public class BeanWrapperImpl extends AbstractPropertyAccessor implements BeanWra
}
}
final Object value = valueToApply;
if (System.getSecurityManager() != null) {
try {
AccessController.doPrivileged(new PrivilegedExceptionAction<Object>() {
@ -1103,14 +1097,17 @@ public class BeanWrapperImpl extends AbstractPropertyAccessor implements BeanWra
return null;
}
}, acc);
} catch (PrivilegedActionException ex) {
}
catch (PrivilegedActionException ex) {
throw ex.getException();
}
}
else {
writeMethod.invoke(object, value);
writeMethod.invoke(this.object, value);
}
}
catch (TypeMismatchException ex) {
throw ex;
}
catch (InvocationTargetException ex) {
PropertyChangeEvent propertyChangeEvent =
@ -1122,31 +1119,6 @@ public class BeanWrapperImpl extends AbstractPropertyAccessor implements BeanWra
throw new MethodInvocationException(propertyChangeEvent, ex.getTargetException());
}
}
catch (ConverterNotFoundException ex) {
PropertyChangeEvent pce =
new PropertyChangeEvent(this.rootObject, this.nestedPath + propertyName, oldValue, pv.getValue());
throw new ConversionNotSupportedException(pce, pd.getPropertyType(), ex);
}
catch (ConversionException ex) {
PropertyChangeEvent pce =
new PropertyChangeEvent(this.rootObject, this.nestedPath + propertyName, oldValue, pv.getValue());
throw new TypeMismatchException(pce, pd.getPropertyType(), ex);
}
catch (IllegalStateException ex) {
PropertyChangeEvent pce =
new PropertyChangeEvent(this.rootObject, this.nestedPath + propertyName, oldValue, pv.getValue());
throw new ConversionNotSupportedException(pce, pd.getPropertyType(), ex);
}
catch (IllegalArgumentException ex) {
PropertyChangeEvent pce =
new PropertyChangeEvent(this.rootObject, this.nestedPath + propertyName, oldValue, pv.getValue());
throw new TypeMismatchException(pce, pd.getPropertyType(), ex);
}
catch (IllegalAccessException ex) {
PropertyChangeEvent pce =
new PropertyChangeEvent(this.rootObject, this.nestedPath + propertyName, oldValue, pv.getValue());
throw new MethodInvocationException(pce, ex);
}
catch (Exception ex) {
PropertyChangeEvent pce =
new PropertyChangeEvent(this.rootObject, this.nestedPath + propertyName, oldValue, pv.getValue());

View File

@ -124,7 +124,8 @@ public class DirectFieldAccessor extends AbstractPropertyAccessor {
try {
ReflectionUtils.makeAccessible(field);
oldValue = field.get(this.target);
Object convertedValue = this.typeConverterDelegate.convertIfNecessary(oldValue, newValue, field);
Object convertedValue = this.typeConverterDelegate.convertIfNecessary(
field.getName(), oldValue, newValue, field.getType(), new TypeDescriptor(field));
field.set(this.target, convertedValue);
}
catch (ConverterNotFoundException ex) {

View File

@ -77,18 +77,6 @@ class TypeConverterDelegate {
}
/**
* Convert the value to the specified required type.
* @param newValue the proposed new value
* @param requiredType the type we must convert to
* (or <code>null</code> if not known, for example in case of a collection element)
* @return the new value, possibly the result of type conversion
* @throws IllegalArgumentException if type conversion failed
*/
public <T> T convertIfNecessary(Object newValue, Class<T> requiredType) throws IllegalArgumentException {
return convertIfNecessary(null, null, newValue, requiredType, TypeDescriptor.valueOf(requiredType));
}
/**
* Convert the value to the specified required type.
* @param newValue the proposed new value
@ -123,54 +111,6 @@ class TypeConverterDelegate {
return convertIfNecessary(propertyName, oldValue, newValue, requiredType, TypeDescriptor.valueOf(requiredType));
}
/**
* Convert the value to the required type (if necessary from a String),
* for the specified property.
* @param propertyName name of the property
* @param oldValue the previous value, if available (may be <code>null</code>)
* @param newValue the proposed new value
* @param requiredType the type we must convert to
* (or <code>null</code> if not known, for example in case of a collection element)
* @param methodParam the method parameter that is the target of the conversion
* @return the new value, possibly the result of type conversion
* @throws IllegalArgumentException if type conversion failed
*/
public <T> T convertIfNecessary(String propertyName, Object oldValue, Object newValue,
Class<T> requiredType, MethodParameter methodParam) throws IllegalArgumentException {
return convertIfNecessary(propertyName, oldValue, newValue, requiredType, new TypeDescriptor(methodParam));
}
/**
* Convert the value to the required type for the specified property.
* @param oldValue the previous value, if available (may be <code>null</code>)
* @param newValue the proposed new value
* @param descriptor the JavaBeans descriptor for the property
* @return the new value, possibly the result of type conversion
* @throws IllegalArgumentException if type conversion failed
*/
public Object convertIfNecessary(Object oldValue, Object newValue, PropertyDescriptor descriptor)
throws IllegalArgumentException {
return convertIfNecessary(descriptor.getName(), oldValue, newValue, descriptor.getPropertyType(),
new PropertyTypeDescriptor(descriptor, BeanUtils.getWriteMethodParameter(descriptor)));
}
/**
* Convert the value to the required type for the specified property.
* @param oldValue the previous value, if available (may be <code>null</code>)
* @param newValue the proposed new value
* @param field the field that is the target of the conversion
* @return the new value, possibly the result of type conversion
* @throws IllegalArgumentException if type conversion failed
*/
public Object convertIfNecessary(Object oldValue, Object newValue, Field field)
throws IllegalArgumentException {
return convertIfNecessary(field.getName(), oldValue, newValue, field.getType(), new TypeDescriptor(field));
}
/**
* Convert the value to the required type (if necessary from a String),
* for the specified property.
@ -184,7 +124,7 @@ class TypeConverterDelegate {
* @throws IllegalArgumentException if type conversion failed
*/
@SuppressWarnings("unchecked")
private <T> T convertIfNecessary(String propertyName, Object oldValue, Object newValue,
public <T> T convertIfNecessary(String propertyName, Object oldValue, Object newValue,
Class<T> requiredType, TypeDescriptor typeDescriptor) throws IllegalArgumentException {
Object convertedValue = newValue;

View File

@ -22,6 +22,7 @@ import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
@ -349,6 +350,49 @@ public class DataBinderTests extends TestCase {
}
}
public void testBindingWithFormatterAgainstList() {
BeanWithIntegerList tb = new BeanWithIntegerList();
DataBinder binder = new DataBinder(tb);
FormattingConversionService conversionService = new FormattingConversionService();
ConversionServiceFactory.addDefaultConverters(conversionService);
conversionService.addFormatterForFieldType(Float.class, new NumberFormatter());
binder.setConversionService(conversionService);
MutablePropertyValues pvs = new MutablePropertyValues();
pvs.add("integerList[0]", "1");
LocaleContextHolder.setLocale(Locale.GERMAN);
try {
binder.bind(pvs);
assertEquals(new Integer(1), tb.getIntegerList().get(0));
assertEquals("1", binder.getBindingResult().getFieldValue("integerList[0]"));
}
finally {
LocaleContextHolder.resetLocaleContext();
}
}
public void testBindingErrorWithFormatterAgainstList() {
BeanWithIntegerList tb = new BeanWithIntegerList();
DataBinder binder = new DataBinder(tb);
FormattingConversionService conversionService = new FormattingConversionService();
ConversionServiceFactory.addDefaultConverters(conversionService);
conversionService.addFormatterForFieldType(Float.class, new NumberFormatter());
binder.setConversionService(conversionService);
MutablePropertyValues pvs = new MutablePropertyValues();
pvs.add("integerList[0]", "1x2");
LocaleContextHolder.setLocale(Locale.GERMAN);
try {
binder.bind(pvs);
assertTrue(tb.getIntegerList().isEmpty());
assertEquals("1x2", binder.getBindingResult().getFieldValue("integerList[0]"));
assertTrue(binder.getBindingResult().hasFieldErrors("integerList[0]"));
}
finally {
LocaleContextHolder.resetLocaleContext();
}
}
public void testBindingWithFormatterAgainstFields() {
TestBean tb = new TestBean();
DataBinder binder = new DataBinder(tb);
@ -1436,6 +1480,20 @@ public class DataBinderTests extends TestCase {
}
private static class BeanWithIntegerList {
private List<Integer> integerList;
public List<Integer> getIntegerList() {
return integerList;
}
public void setIntegerList(List<Integer> integerList) {
this.integerList = integerList;
}
}
private static class Book {
private String Title;