Merge branch '2.1.x' into 2.2.x

Closes gh-20306
pull/20310/head
Madhura Bhave 5 years ago
commit 368a77f355

@ -43,6 +43,8 @@ public class NoUnboundElementsBindHandler extends AbstractBindHandler {
private final Set<ConfigurationPropertyName> boundNames = new HashSet<>(); private final Set<ConfigurationPropertyName> boundNames = new HashSet<>();
private final Set<ConfigurationPropertyName> attemptedNames = new HashSet<>();
private final Function<ConfigurationPropertySource, Boolean> filter; private final Function<ConfigurationPropertySource, Boolean> filter;
NoUnboundElementsBindHandler() { NoUnboundElementsBindHandler() {
@ -58,6 +60,12 @@ public class NoUnboundElementsBindHandler extends AbstractBindHandler {
this.filter = filter; this.filter = filter;
} }
@Override
public <T> Bindable<T> onStart(ConfigurationPropertyName name, Bindable<T> target, BindContext context) {
this.attemptedNames.add(name);
return super.onStart(name, target, context);
}
@Override @Override
public Object onSuccess(ConfigurationPropertyName name, Bindable<?> target, BindContext context, Object result) { public Object onSuccess(ConfigurationPropertyName name, Bindable<?> target, BindContext context, Object result) {
this.boundNames.add(name); this.boundNames.add(name);
@ -106,11 +114,54 @@ public class NoUnboundElementsBindHandler extends AbstractBindHandler {
private boolean isOverriddenCollectionElement(ConfigurationPropertyName candidate) { private boolean isOverriddenCollectionElement(ConfigurationPropertyName candidate) {
int lastIndex = candidate.getNumberOfElements() - 1; int lastIndex = candidate.getNumberOfElements() - 1;
if (candidate.isNumericIndex(lastIndex)) { if (candidate.isLastElementIndexed()) {
ConfigurationPropertyName propertyName = candidate.chop(lastIndex); ConfigurationPropertyName propertyName = candidate.chop(lastIndex);
return this.boundNames.contains(propertyName); return this.boundNames.contains(propertyName);
} }
Indexed indexed = getIndexed(candidate);
if (indexed != null) {
String zeroethProperty = indexed.getName() + "[0]";
if (this.boundNames.contains(ConfigurationPropertyName.of(zeroethProperty))) {
String nestedZeroethProperty = zeroethProperty + "." + indexed.getNestedPropertyName();
return isCandidateValidPropertyName(nestedZeroethProperty);
}
}
return false; return false;
} }
private boolean isCandidateValidPropertyName(String nestedZeroethProperty) {
return this.attemptedNames.contains(ConfigurationPropertyName.of(nestedZeroethProperty));
}
private Indexed getIndexed(ConfigurationPropertyName candidate) {
for (int i = 0; i < candidate.getNumberOfElements(); i++) {
if (candidate.isNumericIndex(i)) {
return new Indexed(candidate.chop(i).toString(),
candidate.getElement(i + 1, ConfigurationPropertyName.Form.UNIFORM));
}
}
return null;
}
private static final class Indexed {
private final String name;
private final String nestedPropertyName;
private Indexed(String name, String nestedPropertyName) {
this.name = name;
this.nestedPropertyName = nestedPropertyName;
}
String getName() {
return this.name;
}
String getNestedPropertyName() {
return this.nestedPropertyName;
}
}
} }

@ -131,6 +131,42 @@ class NoUnboundElementsBindHandlerTests {
.contains("The elements [example.foo[0]] were left unbound")); .contains("The elements [example.foo[0]] were left unbound"));
} }
@Test
void bindWhenUsingNoUnboundElementsHandlerShouldBindIfUnboundNestedCollectionProperties() {
MockConfigurationPropertySource source1 = new MockConfigurationPropertySource();
source1.put("example.nested[0].string-value", "bar");
MockConfigurationPropertySource source2 = new MockConfigurationPropertySource();
source2.put("example.nested[0].string-value", "bar");
source2.put("example.nested[0].int-value", "2");
source2.put("example.nested[1].string-value", "baz");
source2.put("example.nested[1].other-nested.baz", "baz");
this.sources.add(source1);
this.sources.add(source2);
this.binder = new Binder(this.sources);
NoUnboundElementsBindHandler handler = new NoUnboundElementsBindHandler();
ExampleWithNestedList bound = this.binder.bind("example", Bindable.of(ExampleWithNestedList.class), handler)
.get();
assertThat(bound.getNested().get(0).getStringValue()).isEqualTo("bar");
}
@Test
void bindWhenUsingNoUnboundElementsHandlerAndUnboundCollectionElementsWithInvalidPropertyShouldThrowException() {
MockConfigurationPropertySource source1 = new MockConfigurationPropertySource();
source1.put("example.nested[0].string-value", "bar");
MockConfigurationPropertySource source2 = new MockConfigurationPropertySource();
source2.put("example.nested[0].string-value", "bar");
source2.put("example.nested[1].int-value", "1");
source2.put("example.nested[1].invalid", "baz");
this.sources.add(source1);
this.sources.add(source2);
this.binder = new Binder(this.sources);
assertThatExceptionOfType(BindException.class)
.isThrownBy(() -> this.binder.bind("example", Bindable.of(ExampleWithNestedList.class),
new NoUnboundElementsBindHandler()))
.satisfies((ex) -> assertThat(ex.getCause().getMessage())
.contains("The elements [example.nested[1].invalid] were left unbound"));
}
static class Example { static class Example {
private String foo; private String foo;
@ -159,4 +195,66 @@ class NoUnboundElementsBindHandlerTests {
} }
static class ExampleWithNestedList {
private List<Nested> nested;
List<Nested> getNested() {
return this.nested;
}
void setNested(List<Nested> nested) {
this.nested = nested;
}
}
static class Nested {
private String stringValue;
private Integer intValue;
private OtherNested otherNested;
String getStringValue() {
return this.stringValue;
}
void setStringValue(String value) {
this.stringValue = value;
}
Integer getIntValue() {
return this.intValue;
}
void setIntValue(Integer intValue) {
this.intValue = intValue;
}
OtherNested getOtherNested() {
return this.otherNested;
}
void setOtherNested(OtherNested otherNested) {
this.otherNested = otherNested;
}
}
static class OtherNested {
private String baz;
String getBaz() {
return this.baz;
}
void setBaz(String baz) {
this.baz = baz;
}
}
} }

Loading…
Cancel
Save