Duplicate values should bind properly to List

Fixes gh-10106
pull/10355/head
Madhura Bhave 7 years ago
parent 0f25dd0ea6
commit 4a740a16f3

@ -19,7 +19,6 @@ package org.springframework.boot.context.properties.bind;
import java.util.function.Supplier;
import org.springframework.boot.context.properties.source.ConfigurationPropertyName;
import org.springframework.core.ResolvableType;
/**
* Internal strategy used by {@link Binder} to bind aggregates (Maps, Lists, Arrays).
@ -47,9 +46,7 @@ abstract class AggregateBinder<T> {
public final Object bind(ConfigurationPropertyName name, Bindable<?> target,
AggregateElementBinder itemBinder) {
Supplier<?> value = target.getValue();
Class<?> type = (value == null ? target.getType().resolve()
: ResolvableType.forClass(AggregateBinder.class, getClass())
.resolveGeneric());
Class<?> type = (value == null ? target.getType().resolve() : null);
Object result = bind(name, target, itemBinder, type);
if (result == null || value == null || value.get() == null) {
return result;

@ -17,6 +17,7 @@
package org.springframework.boot.context.properties.bind;
import java.util.Collection;
import java.util.List;
import org.springframework.boot.context.properties.source.ConfigurationPropertyName;
import org.springframework.core.CollectionFactory;
@ -37,10 +38,12 @@ class CollectionBinder extends IndexedElementsBinder<Collection<Object>> {
@Override
protected Object bind(ConfigurationPropertyName name, Bindable<?> target,
AggregateElementBinder elementBinder, Class<?> type) {
Class<?> collectionType = (type != null ? type
: ResolvableType.forClassWithGenerics(List.class, Object.class).resolve());
IndexedCollectionSupplier collection = new IndexedCollectionSupplier(
() -> CollectionFactory.createCollection(type, 0));
() -> CollectionFactory.createCollection(collectionType, 0));
ResolvableType elementType = target.getType().asCollection().getGeneric();
bindIndexed(name, target, elementBinder, collection, target.getType(),
bindIndexed(name, target, elementBinder, collection, ResolvableType.forClass(collectionType),
elementType);
if (collection.wasSupplied()) {
return collection.get();

@ -48,7 +48,9 @@ class MapBinder extends AggregateBinder<Map<Object, Object>> {
@Override
protected Object bind(ConfigurationPropertyName name, Bindable<?> target,
AggregateElementBinder elementBinder, Class<?> type) {
Map<Object, Object> map = CollectionFactory.createMap(type, 0);
Class<?> mapType = (type != null ? type
: ResolvableType.forClassWithGenerics(Map.class, Object.class, Object.class).resolve());
Map<Object, Object> map = CollectionFactory.createMap(mapType, 0);
Bindable<?> resolvedTarget = resolveTarget(target);
for (ConfigurationPropertySource source : getContext().getSources()) {
if (!ConfigurationPropertyName.EMPTY.equals(name)) {

@ -18,6 +18,7 @@ package org.springframework.boot.context.properties.bind;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
@ -316,10 +317,45 @@ public class CollectionBinderTests {
assertThat(result.getItems()).containsExactly("a", "b", "c", "d");
}
@Test
public void bindToCollectionWithNoDefaultConstructor() throws Exception {
MockConfigurationPropertySource source = new MockConfigurationPropertySource();
source.put("foo.items", "a,b,c,c");
this.sources.add(source);
ExampleCustomBean result = this.binder
.bind("foo", ExampleCustomBean.class).get();
assertThat(result.getItems()).hasSize(4);
assertThat(result.getItems()).containsExactly("a", "b", "c", "c");
}
@Test
public void bindToListShouldAllowDuplicateValues() throws Exception {
MockConfigurationPropertySource source = new MockConfigurationPropertySource();
source.put("foo.items", "a,b,c,c");
this.sources.add(source);
ExampleCollectionBean result = this.binder
.bind("foo", ExampleCollectionBean.class).get();
assertThat(result.getItems()).hasSize(5);
assertThat(result.getItems()).containsExactly("a", "b", "c", "c", "d");
}
@Test
public void bindToSetShouldNotAllowDuplicateValues() throws Exception {
MockConfigurationPropertySource source = new MockConfigurationPropertySource();
source.put("foo.items-set", "a,b,c,c");
this.sources.add(source);
ExampleCollectionBean result = this.binder
.bind("foo", ExampleCollectionBean.class).get();
assertThat(result.getItemsSet()).hasSize(3);
assertThat(result.getItemsSet()).containsExactly("a", "b", "c");
}
public static class ExampleCollectionBean {
private List<String> items = new ArrayList<>();
private Set<String> itemsSet = new HashSet<>();
public List<String> getItems() {
return this.items;
}
@ -328,6 +364,36 @@ public class CollectionBinderTests {
this.items.add("d");
}
public Set<String> getItemsSet() {
return this.itemsSet;
}
public void setItemsSet(Set<String> itemsSet) {
this.itemsSet = itemsSet;
}
}
public static class ExampleCustomBean {
private MyCustomList items = new MyCustomList(Collections.singletonList("foo"));
public MyCustomList getItems() {
return this.items;
}
public void setItems(MyCustomList items) {
this.items = items;
}
}
public static class MyCustomList extends ArrayList {
private List<String> items = new ArrayList<>(Collections.singletonList("foo"));
public MyCustomList(List<String> items) {
this.items = items;
}
}
}

Loading…
Cancel
Save