@ -17,7 +17,9 @@
@@ -17,7 +17,9 @@
package org.springframework.boot.context.properties.bind.validation ;
import java.util.ArrayList ;
import java.util.LinkedHashMap ;
import java.util.List ;
import java.util.Map ;
import java.util.Set ;
import javax.validation.Valid ;
@ -35,11 +37,16 @@ import org.springframework.boot.context.properties.bind.Binder;
@@ -35,11 +37,16 @@ import org.springframework.boot.context.properties.bind.Binder;
import org.springframework.boot.context.properties.source.ConfigurationProperty ;
import org.springframework.boot.context.properties.source.ConfigurationPropertyName ;
import org.springframework.boot.context.properties.source.ConfigurationPropertySource ;
import org.springframework.boot.context.properties.source.ConfigurationPropertySources ;
import org.springframework.boot.context.properties.source.MockConfigurationPropertySource ;
import org.springframework.boot.origin.Origin ;
import org.springframework.core.convert.ConverterNotFoundException ;
import org.springframework.core.env.MapPropertySource ;
import org.springframework.validation.Errors ;
import org.springframework.validation.FieldError ;
import org.springframework.validation.ObjectError ;
import org.springframework.validation.ValidationUtils ;
import org.springframework.validation.Validator ;
import org.springframework.validation.annotation.Validated ;
import org.springframework.validation.beanvalidation.LocalValidatorFactoryBean ;
@ -210,6 +217,54 @@ class ValidationBindHandlerTests {
@@ -210,6 +217,54 @@ class ValidationBindHandlerTests {
assertThat ( fieldError . getField ( ) ) . isEqualTo ( "personAge" ) ;
}
@Test
void validateMapValues ( ) throws Exception {
MockConfigurationPropertySource source = new MockConfigurationPropertySource ( ) ;
source . put ( "test.items.[itemOne].number" , "one" ) ;
source . put ( "test.items.[ITEM2].number" , "two" ) ;
this . sources . add ( source ) ;
Validator validator = getMapValidator ( ) ;
this . handler = new ValidationBindHandler ( validator ) ;
this . binder . bind ( ConfigurationPropertyName . of ( "test" ) , Bindable . of ( ExampleWithMap . class ) , this . handler ) ;
}
@Test
void validateMapValuesWithNonUniformSource ( ) throws Exception {
Map < String , Object > map = new LinkedHashMap < > ( ) ;
map . put ( "test.items.itemOne.number" , "one" ) ;
map . put ( "test.items.ITEM2.number" , "two" ) ;
this . sources . add ( ConfigurationPropertySources . from ( new MapPropertySource ( "test" , map ) ) . iterator ( ) . next ( ) ) ;
Validator validator = getMapValidator ( ) ;
this . handler = new ValidationBindHandler ( validator ) ;
this . binder . bind ( ConfigurationPropertyName . of ( "test" ) , Bindable . of ( ExampleWithMap . class ) , this . handler ) ;
}
private Validator getMapValidator ( ) {
return new Validator ( ) {
@Override
public boolean supports ( Class < ? > clazz ) {
return ExampleWithMap . class = = clazz ;
}
@Override
public void validate ( Object target , Errors errors ) {
ExampleWithMap value = ( ExampleWithMap ) target ;
value . getItems ( ) . forEach ( ( k , v ) - > {
try {
errors . pushNestedPath ( "items[" + k + "]" ) ;
ValidationUtils . rejectIfEmptyOrWhitespace ( errors , "number" , "NUMBER_ERR" ) ;
}
finally {
errors . popNestedPath ( ) ;
}
} ) ;
}
} ;
}
private BindValidationException bindAndExpectValidationError ( Runnable action ) {
try {
action . run ( ) ;
@ -358,6 +413,30 @@ class ValidationBindHandlerTests {
@@ -358,6 +413,30 @@ class ValidationBindHandlerTests {
}
static class ExampleWithMap {
private Map < String , ExampleMapValue > items = new LinkedHashMap < > ( ) ;
Map < String , ExampleMapValue > getItems ( ) {
return this . items ;
}
}
static class ExampleMapValue {
private String number ;
String getNumber ( ) {
return this . number ;
}
void setNumber ( String number ) {
this . number = number ;
}
}
static class TestHandler extends AbstractBindHandler {
private Object result ;