Handle [] leniently in constructor binding

See gh-34305
This commit is contained in:
rstoyanchev 2025-02-10 17:41:05 +00:00
parent 9797bc0acd
commit 4591a67641
2 changed files with 38 additions and 12 deletions

View File

@ -142,6 +142,10 @@ public class DataBinder implements PropertyEditorRegistry, TypeConverter {
*/ */
protected static final Log logger = LogFactory.getLog(DataBinder.class); protected static final Log logger = LogFactory.getLog(DataBinder.class);
/** Internal constant for constructor binding via "[]". */
private static final int NO_INDEX = -1;
@Nullable @Nullable
private Object target; private Object target;
@ -1056,15 +1060,17 @@ public class DataBinder implements PropertyEditorRegistry, TypeConverter {
return null; return null;
} }
int size = (indexes.last() < this.autoGrowCollectionLimit ? indexes.last() + 1 : 0); int lastIndex = Math.max(indexes.last(), 0);
int size = (lastIndex < this.autoGrowCollectionLimit ? lastIndex + 1 : 0);
List<?> list = (List<?>) CollectionFactory.createCollection(paramType, size); List<?> list = (List<?>) CollectionFactory.createCollection(paramType, size);
for (int i = 0; i < size; i++) { for (int i = 0; i < size; i++) {
list.add(null); list.add(null);
} }
for (int index : indexes) { for (int index : indexes) {
String indexedPath = paramPath + "[" + index + "]"; String indexedPath = paramPath + "[" + (index != NO_INDEX ? index : "") + "]";
list.set(index, createIndexedValue(paramPath, paramType, elementType, indexedPath, valueResolver)); list.set(Math.max(index, 0),
createIndexedValue(paramPath, paramType, elementType, indexedPath, valueResolver));
} }
return list; return list;
@ -1108,12 +1114,14 @@ public class DataBinder implements PropertyEditorRegistry, TypeConverter {
return null; return null;
} }
int size = (indexes.last() < this.autoGrowCollectionLimit ? indexes.last() + 1: 0); int lastIndex = Math.max(indexes.last(), 0);
int size = (lastIndex < this.autoGrowCollectionLimit ? lastIndex + 1: 0);
V[] array = (V[]) Array.newInstance(elementType.resolve(), size); V[] array = (V[]) Array.newInstance(elementType.resolve(), size);
for (int index : indexes) { for (int index : indexes) {
String indexedPath = paramPath + "[" + index + "]"; String indexedPath = paramPath + "[" + (index != NO_INDEX ? index : "") + "]";
array[index] = createIndexedValue(paramPath, paramType, elementType, indexedPath, valueResolver); array[Math.max(index, 0)] =
createIndexedValue(paramPath, paramType, elementType, indexedPath, valueResolver);
} }
return array; return array;
@ -1124,13 +1132,20 @@ public class DataBinder implements PropertyEditorRegistry, TypeConverter {
SortedSet<Integer> indexes = null; SortedSet<Integer> indexes = null;
for (String name : valueResolver.getNames()) { for (String name : valueResolver.getNames()) {
if (name.startsWith(paramPath + "[")) { if (name.startsWith(paramPath + "[")) {
int endIndex = name.indexOf(']', paramPath.length() + 2); int index;
String rawIndex = name.substring(paramPath.length() + 1, endIndex); if (paramPath.length() + 2 == name.length()) {
if (StringUtils.hasLength(rawIndex)) { if (!name.endsWith("[]")) {
int index = Integer.parseInt(rawIndex); continue;
indexes = (indexes != null ? indexes : new TreeSet<>()); }
indexes.add(index); index = NO_INDEX;
} }
else {
int endIndex = name.indexOf(']', paramPath.length() + 2);
String indexValue = name.substring(paramPath.length() + 1, endIndex);
index = Integer.parseInt(indexValue);
}
indexes = (indexes != null ? indexes : new TreeSet<>());
indexes.add(index);
} }
} }
return indexes; return indexes;

View File

@ -188,6 +188,17 @@ class DataBinderConstructTests {
assertThat(target.integerList()).containsExactly(1, 2); assertThat(target.integerList()).containsExactly(1, 2);
} }
@Test
void simpleListBindingEmptyBrackets() {
MapValueResolver valueResolver = new MapValueResolver(Map.of("integerList[]", "1"));
DataBinder binder = initDataBinder(IntegerListRecord.class);
binder.construct(valueResolver);
IntegerListRecord target = getTarget(binder);
assertThat(target.integerList()).containsExactly(1);
}
@Test @Test
void simpleMapBinding() { void simpleMapBinding() {
MapValueResolver valueResolver = new MapValueResolver(Map.of("integerMap[a]", "1", "integerMap[b]", "2")); MapValueResolver valueResolver = new MapValueResolver(Map.of("integerMap[a]", "1", "integerMap[b]", "2"));