Data class construction supports field default/marker parameters
Issue: SPR-15871
This commit is contained in:
@@ -206,8 +206,8 @@ public class WebDataBinder extends DataBinder {
|
||||
* @see #getFieldDefaultPrefix
|
||||
*/
|
||||
protected void checkFieldDefaults(MutablePropertyValues mpvs) {
|
||||
if (getFieldDefaultPrefix() != null) {
|
||||
String fieldDefaultPrefix = getFieldDefaultPrefix();
|
||||
String fieldDefaultPrefix = getFieldDefaultPrefix();
|
||||
if (fieldDefaultPrefix != null) {
|
||||
PropertyValue[] pvArray = mpvs.getPropertyValues();
|
||||
for (PropertyValue pv : pvArray) {
|
||||
if (pv.getName().startsWith(fieldDefaultPrefix)) {
|
||||
@@ -233,8 +233,8 @@ public class WebDataBinder extends DataBinder {
|
||||
* @see #getEmptyValue(String, Class)
|
||||
*/
|
||||
protected void checkFieldMarkers(MutablePropertyValues mpvs) {
|
||||
if (getFieldMarkerPrefix() != null) {
|
||||
String fieldMarkerPrefix = getFieldMarkerPrefix();
|
||||
String fieldMarkerPrefix = getFieldMarkerPrefix();
|
||||
if (fieldMarkerPrefix != null) {
|
||||
PropertyValue[] pvArray = mpvs.getPropertyValues();
|
||||
for (PropertyValue pv : pvArray) {
|
||||
if (pv.getName().startsWith(fieldMarkerPrefix)) {
|
||||
@@ -251,7 +251,20 @@ public class WebDataBinder extends DataBinder {
|
||||
|
||||
/**
|
||||
* Determine an empty value for the specified field.
|
||||
* <p>Default implementation returns:
|
||||
* <p>The default implementation delegates to {@link #getEmptyValue(Class)}
|
||||
* if the field type is known, otherwise falls back to {@code null}.
|
||||
* @param field the name of the field
|
||||
* @param fieldType the type of the field
|
||||
* @return the empty value (for most fields: {@code null})
|
||||
*/
|
||||
@Nullable
|
||||
protected Object getEmptyValue(String field, @Nullable Class<?> fieldType) {
|
||||
return (fieldType != null ? getEmptyValue(fieldType) : null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine an empty value for the specified field.
|
||||
* <p>The default implementation returns:
|
||||
* <ul>
|
||||
* <li>{@code Boolean.FALSE} for boolean fields
|
||||
* <li>an empty array for array types
|
||||
@@ -259,39 +272,39 @@ public class WebDataBinder extends DataBinder {
|
||||
* <li>Map implementations for Map types
|
||||
* <li>else, {@code null} is used as default
|
||||
* </ul>
|
||||
* @param field the name of the field
|
||||
* @param fieldType the type of the field
|
||||
* @return the empty value (for most fields: null)
|
||||
* @return the empty value (for most fields: {@code null})
|
||||
* @since 5.0
|
||||
*/
|
||||
@Nullable
|
||||
protected Object getEmptyValue(String field, @Nullable Class<?> fieldType) {
|
||||
if (fieldType != null) {
|
||||
try {
|
||||
if (boolean.class == fieldType || Boolean.class == fieldType) {
|
||||
// Special handling of boolean property.
|
||||
return Boolean.FALSE;
|
||||
}
|
||||
else if (fieldType.isArray()) {
|
||||
// Special handling of array property.
|
||||
return Array.newInstance(fieldType.getComponentType(), 0);
|
||||
}
|
||||
else if (Collection.class.isAssignableFrom(fieldType)) {
|
||||
return CollectionFactory.createCollection(fieldType, 0);
|
||||
}
|
||||
else if (Map.class.isAssignableFrom(fieldType)) {
|
||||
return CollectionFactory.createMap(fieldType, 0);
|
||||
}
|
||||
} catch (IllegalArgumentException exc) {
|
||||
return null;
|
||||
public Object getEmptyValue(Class<?> fieldType) {
|
||||
try {
|
||||
if (boolean.class == fieldType || Boolean.class == fieldType) {
|
||||
// Special handling of boolean property.
|
||||
return Boolean.FALSE;
|
||||
}
|
||||
else if (fieldType.isArray()) {
|
||||
// Special handling of array property.
|
||||
return Array.newInstance(fieldType.getComponentType(), 0);
|
||||
}
|
||||
else if (Collection.class.isAssignableFrom(fieldType)) {
|
||||
return CollectionFactory.createCollection(fieldType, 0);
|
||||
}
|
||||
else if (Map.class.isAssignableFrom(fieldType)) {
|
||||
return CollectionFactory.createMap(fieldType, 0);
|
||||
}
|
||||
}
|
||||
// Default value: try null.
|
||||
catch (IllegalArgumentException ex) {
|
||||
logger.debug("Failed to create default value - falling back to null: " + ex.getMessage());
|
||||
}
|
||||
// Default value: null.
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Bind all multipart files contained in the given request, if any
|
||||
* (in case of a multipart request).
|
||||
* (in case of a multipart request). To be called by subclasses.
|
||||
* <p>Multipart files will only be added to the property values if they
|
||||
* are not empty or if we're configured to bind empty multipart files too.
|
||||
* @param multipartFiles Map of field name String to MultipartFile object
|
||||
|
||||
@@ -241,14 +241,30 @@ public class ModelAttributeMethodProcessor implements HandlerMethodArgumentResol
|
||||
Class<?>[] paramTypes = ctor.getParameterTypes();
|
||||
Assert.state(paramNames.length == paramTypes.length,
|
||||
() -> "Invalid number of parameter names: " + paramNames.length + " for constructor " + ctor);
|
||||
|
||||
Object[] args = new Object[paramTypes.length];
|
||||
WebDataBinder binder = binderFactory.createBinder(webRequest, null, attributeName);
|
||||
String fieldDefaultPrefix = binder.getFieldDefaultPrefix();
|
||||
String fieldMarkerPrefix = binder.getFieldMarkerPrefix();
|
||||
boolean bindingFailure = false;
|
||||
|
||||
for (int i = 0; i < paramNames.length; i++) {
|
||||
String[] paramValues = webRequest.getParameterValues(paramNames[i]);
|
||||
String paramName = paramNames[i];
|
||||
Class<?> paramType = paramTypes[i];
|
||||
Object value = webRequest.getParameterValues(paramName);
|
||||
if (value == null) {
|
||||
if (fieldDefaultPrefix != null) {
|
||||
value = webRequest.getParameter(fieldDefaultPrefix + paramName);
|
||||
}
|
||||
if (value == null && fieldMarkerPrefix != null) {
|
||||
if (webRequest.getParameter(fieldMarkerPrefix + paramName) != null) {
|
||||
value = binder.getEmptyValue(paramType);
|
||||
}
|
||||
}
|
||||
}
|
||||
try {
|
||||
args[i] = (paramValues != null ?
|
||||
binder.convertIfNecessary(paramValues, paramTypes[i], new MethodParameter(ctor, i)) : null);
|
||||
args[i] = (value != null ?
|
||||
binder.convertIfNecessary(value, paramType, new MethodParameter(ctor, i)) : null);
|
||||
}
|
||||
catch (TypeMismatchException ex) {
|
||||
bindingFailure = true;
|
||||
@@ -257,6 +273,7 @@ public class ModelAttributeMethodProcessor implements HandlerMethodArgumentResol
|
||||
new String[] {ex.getErrorCode()}, null, ex.getLocalizedMessage()));
|
||||
}
|
||||
}
|
||||
|
||||
if (bindingFailure) {
|
||||
throw new BindException(binder.getBindingResult());
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user