Skip to content

Commit

Permalink
Support binding indexed values for record class
Browse files Browse the repository at this point in the history
  • Loading branch information
quaff committed Mar 7, 2024
1 parent 1a8d64f commit acf867f
Show file tree
Hide file tree
Showing 2 changed files with 51 additions and 1 deletion.
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2023 the original author or authors.
* Copyright 2002-2024 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand Down Expand Up @@ -49,6 +49,7 @@
import org.springframework.beans.SimpleTypeConverter;
import org.springframework.beans.TypeConverter;
import org.springframework.beans.TypeMismatchException;
import org.springframework.core.CollectionFactory;
import org.springframework.core.KotlinDetector;
import org.springframework.core.MethodParameter;
import org.springframework.core.ResolvableType;
Expand Down Expand Up @@ -112,6 +113,7 @@
* @author Stephane Nicoll
* @author Kazuki Shimizu
* @author Sam Brannen
* @author Yanming Zhou
* @see #setAllowedFields
* @see #setRequiredFields
* @see #registerCustomEditor
Expand Down Expand Up @@ -952,6 +954,19 @@ private Object createObject(ResolvableType objectType, String nestedPath, ValueR
ResolvableType type = ResolvableType.forMethodParameter(param);
args[i] = createObject(type, paramPath + ".", valueResolver);
}
else if (value == null && (Collection.class == paramType || List.class.isAssignableFrom(paramType)
|| Map.class.isAssignableFrom(paramType)) && hasIndexedValuesFor(paramPath, valueResolver)) {
if (Collection.class == paramType) {
// CollectionFactory.createCollection() will create LinkedHashSet which doesn't support indexed property path
args[i] = new ArrayList<>(16);
}
else if (List.class.isAssignableFrom(paramType)) {
args[i] = CollectionFactory.createCollection(paramType, 16);
}
else if (Map.class.isAssignableFrom(paramType)) {
args[i] = CollectionFactory.createMap(paramType, 16);
}
}
else {
try {
if (value == null && (param.isOptional() || getBindingResult().hasErrors())) {
Expand Down Expand Up @@ -1031,6 +1046,15 @@ private boolean hasValuesFor(String paramPath, ValueResolver resolver) {
return false;
}

private boolean hasIndexedValuesFor(String paramPath, ValueResolver resolver) {
for (String name : resolver.getNames()) {
if (name.startsWith(paramPath + "[")) {
return true;
}
}
return false;
}

private void validateConstructorArgument(
Class<?> constructorClass, String nestedPath, String name, @Nullable Object value) {

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,16 @@
package org.springframework.validation;

import java.beans.ConstructorProperties;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;

import jakarta.validation.constraints.NotNull;
import org.junit.jupiter.api.Test;

import org.springframework.beans.MutablePropertyValues;
import org.springframework.core.ResolvableType;
import org.springframework.format.support.DefaultFormattingConversionService;
import org.springframework.lang.Nullable;
Expand All @@ -35,6 +38,7 @@
* Tests for {@link DataBinder} with constructor binding.
*
* @author Rossen Stoyanchev
* @author Yanming Zhou
*/
class DataBinderConstructTests {

Expand Down Expand Up @@ -101,6 +105,24 @@ void dataClassBindingWithConversionError() {
assertThat(bindingResult.getFieldValue("param3")).isNull();
}

@Test
void recordClassBindingWithIndexedValue() {
Map<String, Object> data = Map.of("collection[0]", "test","list[0]", "test", "map[foo]", "bar");
MapValueResolver valueResolver = new MapValueResolver(data);
DataBinder binder = initDataBinder(RecordClass.class);
binder.construct(valueResolver);
binder.bind(new MutablePropertyValues(data));

BindingResult bindingResult = binder.getBindingResult();
assertThat(bindingResult.getAllErrors()).hasSize(0);

RecordClass target = (RecordClass) binder.getTarget();
assertThat(target).isNotNull();
assertThat(target.collection).hasSize(1).first().isEqualTo("test");
assertThat(target.list).hasSize(1).first().isEqualTo("test");
assertThat(target.map).hasSize(1).containsEntry("foo", "bar");
}

@SuppressWarnings("SameParameterValue")
private static DataBinder initDataBinder(Class<?> targetType) {
DataBinder binder = new DataBinder(null);
Expand Down Expand Up @@ -191,4 +213,8 @@ public Set<String> getNames() {
}
}

record RecordClass(Collection<String> collection, List<String> list, Map<String, String> map) {

}

}

0 comments on commit acf867f

Please sign in to comment.