DataBinder activates autoGrowNestedPaths by default; fixed enum binding with WebRequestDataBinder

This commit is contained in:
Juergen Hoeller
2009-10-13 20:55:57 +00:00
parent a293f50851
commit 84447cdf94
6 changed files with 342 additions and 40 deletions

View File

@@ -78,4 +78,18 @@ public interface BeanWrapper extends ConfigurablePropertyAccessor {
*/
PropertyDescriptor getPropertyDescriptor(String propertyName) throws InvalidPropertyException;
/**
* Set if this BeanWrapper should attempt to "auto-grow" a nested path that contains a null value.
* <p>If true, a null path location will be populated with a default object value and traversed
* instead of resulting in a {@link NullValueInNestedPathException}. Turning this flag on also
* enables auto-growth of collection elements when an index that is out of bounds is accessed.
* <p>Default is false.
*/
void setAutoGrowNestedPaths(boolean autoGrowNestedPaths);
/**
* Return whether "auto-growing" of nested paths has been activated.
*/
boolean isAutoGrowNestedPaths();
}

View File

@@ -114,7 +114,8 @@ public class BeanWrapperImpl extends AbstractPropertyAccessor implements BeanWra
/** The security context used for invoking the property methods */
private AccessControlContext acc;
private boolean autoGrowNestedPaths;
private boolean autoGrowNestedPaths = false;
/**
* Create new empty BeanWrapperImpl. Wrapped instance needs to be set afterwards.
@@ -252,25 +253,14 @@ public class BeanWrapperImpl extends AbstractPropertyAccessor implements BeanWra
return (this.rootObject != null ? this.rootObject.getClass() : null);
}
/**
* If this BeanWrapper should attempt to "autogrow" a nested path that contains a null value.
* If true, a null path location will be populated with a default object value and traversed instead of resulting in a {@link NullValueInNestedPathException}.
* Turning this flag on also enables auto-growth of collection elements when an index that is out of bounds is accessed.
*/
public boolean getAutoGrowNestedPaths() {
return this.autoGrowNestedPaths;
}
/**
* Sets if this BeanWrapper should attempt to "autogrow" a nested path that contains a null value.
* If true, a null path location will be populated with a default object value and traversed instead of resulting in a {@link NullValueInNestedPathException}.
* Turning this flag on also enables auto-growth of collection elements when an index that is out of bounds is accessed.
* Default is false.
*/
public void setAutoGrowNestedPaths(boolean autoGrowNestedPaths) {
this.autoGrowNestedPaths = autoGrowNestedPaths;
}
public boolean isAutoGrowNestedPaths() {
return this.autoGrowNestedPaths;
}
/**
* Set the class to introspect.
* Needs to be called when the target object changes.
@@ -506,9 +496,10 @@ public class BeanWrapperImpl extends AbstractPropertyAccessor implements BeanWra
String canonicalName = tokens.canonicalName;
Object propertyValue = getPropertyValue(tokens);
if (propertyValue == null) {
if (autoGrowNestedPaths) {
if (this.autoGrowNestedPaths) {
propertyValue = setDefaultValue(tokens);
} else {
}
else {
throw new NullValueInNestedPathException(getRootClass(), this.nestedPath + canonicalName);
}
}
@@ -561,20 +552,22 @@ public class BeanWrapperImpl extends AbstractPropertyAccessor implements BeanWra
Object array = Array.newInstance(componentType, 1);
Array.set(array, 0, Array.newInstance(componentType.getComponentType(), 0));
return array;
} else {
}
else {
return Array.newInstance(componentType, 0);
}
} else {
}
else {
if (Collection.class.isAssignableFrom(type)) {
return CollectionFactory.createCollection(type, 16);
} else {
}
else {
return type.newInstance();
}
}
} catch (InstantiationException e) {
throw new NullValueInNestedPathException(getRootClass(), this.nestedPath + name, "Could not instantiate propertyType [" + type.getName() + "] to auto-grow nested property path");
} catch (IllegalAccessException e) {
throw new NullValueInNestedPathException(getRootClass(), this.nestedPath + name, "Could not instantiate propertyType [" + type.getName() + "] to auto-grow nested property path");
}
catch (Exception ex) {
throw new NullValueInNestedPathException(getRootClass(), this.nestedPath + name, "Could not instantiate property type [" + type.getName() + "] to auto-grow nested property path: " + ex);
}
}
@@ -685,9 +678,10 @@ public class BeanWrapperImpl extends AbstractPropertyAccessor implements BeanWra
if (tokens.keys != null) {
if (value == null) {
if (autoGrowNestedPaths) {
if (this.autoGrowNestedPaths) {
value = setDefaultValue(tokens.actualName);
} else {
}
else {
throw new NullValueInNestedPathException(getRootClass(), this.nestedPath + propertyName,
"Cannot access indexed value of property referenced in indexed " +
"property path '" + propertyName + "': returned null");
@@ -775,7 +769,7 @@ public class BeanWrapperImpl extends AbstractPropertyAccessor implements BeanWra
}
private Object growArrayIfNecessary(Object array, int index, String name) {
if (!autoGrowNestedPaths) {
if (!this.autoGrowNestedPaths) {
return array;
}
int length = Array.getLength(array);
@@ -794,7 +788,7 @@ public class BeanWrapperImpl extends AbstractPropertyAccessor implements BeanWra
}
private void growCollectionIfNecessary(Collection collection, int index, String name, PropertyDescriptor pd, int nestingLevel) {
if (!autoGrowNestedPaths) {
if (!this.autoGrowNestedPaths) {
return;
}
if (index >= collection.size()) {

View File

@@ -24,6 +24,7 @@ import java.lang.reflect.Field;
import java.util.Collection;
import java.util.Iterator;
import java.util.Map;
import java.util.Arrays;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
@@ -192,11 +193,7 @@ class TypeConverterDelegate {
// Try to apply some standard type conversion rules if appropriate.
if (convertedValue != null) {
if (String.class.equals(requiredType) && ClassUtils.isPrimitiveOrWrapper(convertedValue.getClass())) {
// We can stringify any primitive value...
return (T) convertedValue.toString();
}
else if (requiredType.isArray()) {
if (requiredType.isArray()) {
// Array required -> apply appropriate conversion of elements.
return (T) convertToTypedArray(convertedValue, propertyName, requiredType.getComponentType());
}
@@ -210,6 +207,13 @@ class TypeConverterDelegate {
convertedValue = convertToTypedMap(
(Map) convertedValue, propertyName, requiredType, methodParam);
}
if (convertedValue.getClass().isArray() && Array.getLength(convertedValue) == 1) {
convertedValue = Array.get(convertedValue, 0);
}
if (String.class.equals(requiredType) && ClassUtils.isPrimitiveOrWrapper(convertedValue.getClass())) {
// We can stringify any primitive value...
return (T) convertedValue.toString();
}
else if (convertedValue instanceof String && !requiredType.isInstance(convertedValue)) {
if (!requiredType.isInterface() && !requiredType.isEnum()) {
try {
@@ -264,20 +268,19 @@ class TypeConverterDelegate {
private Object attemptToConvertStringToEnum(Class<?> requiredType, String trimmedValue, Object currentConvertedValue) {
Object convertedValue = currentConvertedValue;
if(Enum.class.equals(requiredType)) {
if (Enum.class.equals(requiredType)) {
// target type is declared as raw enum, treat the trimmed value as <enum.fqn>.FIELD_NAME
int index = trimmedValue.lastIndexOf(".");
if(index > - 1) {
if (index > - 1) {
String enumType = trimmedValue.substring(0, index);
String fieldName = trimmedValue.substring(index + 1);
ClassLoader loader = this.targetObject.getClass().getClassLoader();
try {
Class<?> enumValueType = loader.loadClass(enumType);
Field enumField = enumValueType.getField(fieldName);
convertedValue = enumField.get(null);
} catch(ClassNotFoundException ex) {
}
catch (ClassNotFoundException ex) {
if(logger.isTraceEnabled()) {
logger.trace("Enum class [" + enumType + "] cannot be loaded from [" + loader + "]", ex);
}
@@ -289,6 +292,7 @@ class TypeConverterDelegate {
}
}
}
if (convertedValue == currentConvertedValue) {
// Try field lookup as fallback: for JDK 1.5 enum or custom enum
// with values defined as static fields. Resulting value still needs