|
|
|
|
@ -964,7 +964,7 @@ public class DataBinder implements PropertyEditorRegistry, TypeConverter {
@@ -964,7 +964,7 @@ public class DataBinder implements PropertyEditorRegistry, TypeConverter {
|
|
|
|
|
value = createMap(paramPath, paramType, resolvableType, valueResolver); |
|
|
|
|
} |
|
|
|
|
else if (paramType.isArray()) { |
|
|
|
|
value = createArray(paramPath, resolvableType, valueResolver); |
|
|
|
|
value = createArray(paramPath, paramType, resolvableType, valueResolver); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
@ -981,11 +981,9 @@ public class DataBinder implements PropertyEditorRegistry, TypeConverter {
@@ -981,11 +981,9 @@ public class DataBinder implements PropertyEditorRegistry, TypeConverter {
|
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
catch (TypeMismatchException ex) { |
|
|
|
|
ex.initPropertyName(paramPath); |
|
|
|
|
args[i] = null; |
|
|
|
|
failedParamNames.add(paramPath); |
|
|
|
|
getBindingResult().recordFieldValue(paramPath, paramType, value); |
|
|
|
|
getBindingErrorProcessor().processPropertyAccessException(ex, getBindingResult()); |
|
|
|
|
handleTypeMismatchException(ex, paramPath, paramType, value); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
@ -1048,9 +1046,8 @@ public class DataBinder implements PropertyEditorRegistry, TypeConverter {
@@ -1048,9 +1046,8 @@ public class DataBinder implements PropertyEditorRegistry, TypeConverter {
|
|
|
|
|
return false; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
@SuppressWarnings("unchecked") |
|
|
|
|
@Nullable |
|
|
|
|
private <V> List<V> createList( |
|
|
|
|
private List<?> createList( |
|
|
|
|
String paramPath, Class<?> paramType, ResolvableType type, ValueResolver valueResolver) { |
|
|
|
|
|
|
|
|
|
ResolvableType elementType = type.getNested(2); |
|
|
|
|
@ -1058,18 +1055,21 @@ public class DataBinder implements PropertyEditorRegistry, TypeConverter {
@@ -1058,18 +1055,21 @@ public class DataBinder implements PropertyEditorRegistry, TypeConverter {
|
|
|
|
|
if (indexes == null) { |
|
|
|
|
return null; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
int size = (indexes.last() < this.autoGrowCollectionLimit ? indexes.last() + 1 : 0); |
|
|
|
|
List<V> list = (List<V>) CollectionFactory.createCollection(paramType, size); |
|
|
|
|
List<?> list = (List<?>) CollectionFactory.createCollection(paramType, size); |
|
|
|
|
for (int i = 0; i < size; i++) { |
|
|
|
|
list.add(null); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
for (int index : indexes) { |
|
|
|
|
list.set(index, (V) createObject(elementType, paramPath + "[" + index + "].", valueResolver)); |
|
|
|
|
String indexedPath = paramPath + "[" + index + "]"; |
|
|
|
|
list.set(index, createIndexedValue(paramPath, paramType, elementType, indexedPath, valueResolver)); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
return list; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
@SuppressWarnings("unchecked") |
|
|
|
|
@Nullable |
|
|
|
|
private <V> Map<String, V> createMap( |
|
|
|
|
String paramPath, Class<?> paramType, ResolvableType type, ValueResolver valueResolver) { |
|
|
|
|
@ -1080,34 +1080,42 @@ public class DataBinder implements PropertyEditorRegistry, TypeConverter {
@@ -1080,34 +1080,42 @@ public class DataBinder implements PropertyEditorRegistry, TypeConverter {
|
|
|
|
|
if (!name.startsWith(paramPath + "[")) { |
|
|
|
|
continue; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
int startIdx = paramPath.length() + 1; |
|
|
|
|
int endIdx = name.indexOf(']', startIdx); |
|
|
|
|
String nestedPath = name.substring(0, endIdx + 2); |
|
|
|
|
boolean quoted = (endIdx - startIdx > 2 && name.charAt(startIdx) == '\'' && name.charAt(endIdx - 1) == '\''); |
|
|
|
|
String key = (quoted ? name.substring(startIdx + 1, endIdx - 1) : name.substring(startIdx, endIdx)); |
|
|
|
|
|
|
|
|
|
if (map == null) { |
|
|
|
|
map = CollectionFactory.createMap(paramType, 16); |
|
|
|
|
} |
|
|
|
|
if (!map.containsKey(key)) { |
|
|
|
|
map.put(key, (V) createObject(elementType, nestedPath, valueResolver)); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
String indexedPath = name.substring(0, endIdx + 1); |
|
|
|
|
map.put(key, createIndexedValue(paramPath, paramType, elementType, indexedPath, valueResolver)); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
return map; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
@SuppressWarnings("unchecked") |
|
|
|
|
@Nullable |
|
|
|
|
private <V> V[] createArray(String paramPath, ResolvableType type, ValueResolver valueResolver) { |
|
|
|
|
private <V> V[] createArray( |
|
|
|
|
String paramPath, Class<?> paramType, ResolvableType type, ValueResolver valueResolver) { |
|
|
|
|
|
|
|
|
|
ResolvableType elementType = type.getNested(2); |
|
|
|
|
SortedSet<Integer> indexes = getIndexes(paramPath, valueResolver); |
|
|
|
|
if (indexes == null) { |
|
|
|
|
return null; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
int size = (indexes.last() < this.autoGrowCollectionLimit ? indexes.last() + 1: 0); |
|
|
|
|
V[] array = (V[]) Array.newInstance(elementType.resolve(), size); |
|
|
|
|
|
|
|
|
|
for (int index : indexes) { |
|
|
|
|
array[index] = (V) createObject(elementType, paramPath + "[" + index + "].", valueResolver); |
|
|
|
|
String indexedPath = paramPath + "[" + index + "]"; |
|
|
|
|
array[index] = createIndexedValue(paramPath, paramType, elementType, indexedPath, valueResolver); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
return array; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
@ -1118,14 +1126,46 @@ public class DataBinder implements PropertyEditorRegistry, TypeConverter {
@@ -1118,14 +1126,46 @@ public class DataBinder implements PropertyEditorRegistry, TypeConverter {
|
|
|
|
|
if (name.startsWith(paramPath + "[")) { |
|
|
|
|
int endIndex = name.indexOf(']', paramPath.length() + 2); |
|
|
|
|
String rawIndex = name.substring(paramPath.length() + 1, endIndex); |
|
|
|
|
int index = Integer.parseInt(rawIndex); |
|
|
|
|
indexes = (indexes != null ? indexes : new TreeSet<>()); |
|
|
|
|
indexes.add(index); |
|
|
|
|
if (StringUtils.hasLength(rawIndex)) { |
|
|
|
|
int index = Integer.parseInt(rawIndex); |
|
|
|
|
indexes = (indexes != null ? indexes : new TreeSet<>()); |
|
|
|
|
indexes.add(index); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
return indexes; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
@SuppressWarnings("unchecked") |
|
|
|
|
private <V> @Nullable V createIndexedValue( |
|
|
|
|
String paramPath, Class<?> paramType, ResolvableType elementType, |
|
|
|
|
String indexedPath, ValueResolver valueResolver) { |
|
|
|
|
|
|
|
|
|
Object value = null; |
|
|
|
|
Class<?> elementClass = elementType.resolve(Object.class); |
|
|
|
|
Object rawValue = valueResolver.resolveValue(indexedPath, elementClass); |
|
|
|
|
if (rawValue != null) { |
|
|
|
|
try { |
|
|
|
|
value = convertIfNecessary(rawValue, elementClass); |
|
|
|
|
} |
|
|
|
|
catch (TypeMismatchException ex) { |
|
|
|
|
handleTypeMismatchException(ex, paramPath, paramType, rawValue); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
else { |
|
|
|
|
value = createObject(elementType, indexedPath + ".", valueResolver); |
|
|
|
|
} |
|
|
|
|
return (V) value; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
private void handleTypeMismatchException( |
|
|
|
|
TypeMismatchException ex, String paramPath, Class<?> paramType, @Nullable Object value) { |
|
|
|
|
|
|
|
|
|
ex.initPropertyName(paramPath); |
|
|
|
|
getBindingResult().recordFieldValue(paramPath, paramType, value); |
|
|
|
|
getBindingErrorProcessor().processPropertyAccessException(ex, getBindingResult()); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
private void validateConstructorArgument( |
|
|
|
|
Class<?> constructorClass, String nestedPath, String name, @Nullable Object value) { |
|
|
|
|
|
|
|
|
|
|