commit
368a77f355
|
|
@ -43,6 +43,8 @@ public class NoUnboundElementsBindHandler extends AbstractBindHandler {
|
|||
|
||||
private final Set<ConfigurationPropertyName> boundNames = new HashSet<>();
|
||||
|
||||
private final Set<ConfigurationPropertyName> attemptedNames = new HashSet<>();
|
||||
|
||||
private final Function<ConfigurationPropertySource, Boolean> filter;
|
||||
|
||||
NoUnboundElementsBindHandler() {
|
||||
|
|
@ -58,6 +60,12 @@ public class NoUnboundElementsBindHandler extends AbstractBindHandler {
|
|||
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
|
||||
public Object onSuccess(ConfigurationPropertyName name, Bindable<?> target, BindContext context, Object result) {
|
||||
this.boundNames.add(name);
|
||||
|
|
@ -106,11 +114,54 @@ public class NoUnboundElementsBindHandler extends AbstractBindHandler {
|
|||
|
||||
private boolean isOverriddenCollectionElement(ConfigurationPropertyName candidate) {
|
||||
int lastIndex = candidate.getNumberOfElements() - 1;
|
||||
if (candidate.isNumericIndex(lastIndex)) {
|
||||
if (candidate.isLastElementIndexed()) {
|
||||
ConfigurationPropertyName propertyName = candidate.chop(lastIndex);
|
||||
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;
|
||||
}
|
||||
|
||||
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"));
|
||||
}
|
||||
|
||||
@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 {
|
||||
|
||||
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…
Reference in New Issue