DataBinder activates autoGrowNestedPaths by default; fixed enum binding with WebRequestDataBinder
This commit is contained in:
@@ -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();
|
||||
|
||||
}
|
||||
|
||||
@@ -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()) {
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user