@ -18,10 +18,11 @@ package org.springframework.boot.context.properties.bind;
import java.lang.annotation.Annotation ;
import java.lang.annotation.Annotation ;
import java.util.Collection ;
import java.util.Collection ;
import java.util.List ;
import java.util.Collections ;
import java.util.HashSet ;
import java.util.Set ;
import java.util.TreeSet ;
import java.util.TreeSet ;
import java.util.function.Supplier ;
import java.util.function.Supplier ;
import java.util.stream.Collectors ;
import org.springframework.boot.context.properties.bind.Binder.Context ;
import org.springframework.boot.context.properties.bind.Binder.Context ;
import org.springframework.boot.context.properties.source.ConfigurationProperty ;
import org.springframework.boot.context.properties.source.ConfigurationProperty ;
@ -30,8 +31,6 @@ import org.springframework.boot.context.properties.source.ConfigurationPropertyN
import org.springframework.boot.context.properties.source.ConfigurationPropertySource ;
import org.springframework.boot.context.properties.source.ConfigurationPropertySource ;
import org.springframework.boot.context.properties.source.IterableConfigurationPropertySource ;
import org.springframework.boot.context.properties.source.IterableConfigurationPropertySource ;
import org.springframework.core.ResolvableType ;
import org.springframework.core.ResolvableType ;
import org.springframework.util.LinkedMultiValueMap ;
import org.springframework.util.MultiValueMap ;
/ * *
/ * *
* Base class for { @link AggregateBinder AggregateBinders } that read a sequential run of
* Base class for { @link AggregateBinder AggregateBinders } that read a sequential run of
@ -106,65 +105,57 @@ abstract class IndexedElementsBinder<T> extends AggregateBinder<T> {
private void bindIndexed ( ConfigurationPropertySource source , ConfigurationPropertyName root ,
private void bindIndexed ( ConfigurationPropertySource source , ConfigurationPropertyName root ,
AggregateElementBinder elementBinder , IndexedCollectionSupplier collection , ResolvableType elementType ) {
AggregateElementBinder elementBinder , IndexedCollectionSupplier collection , ResolvableType elementType ) {
int firstUnboundIndex = 0 ;
Set < String > knownIndexedChildren = Collections . emptySet ( ) ;
boolean hasBindingGap = false ;
if ( source instanceof IterableConfigurationPropertySource iterableSource ) {
source = iterableSource . filter ( root : : isAncestorOf ) ;
knownIndexedChildren = getKnownIndexedChildren ( iterableSource , root ) ;
}
for ( int i = 0 ; i < Integer . MAX_VALUE ; i + + ) {
for ( int i = 0 ; i < Integer . MAX_VALUE ; i + + ) {
ConfigurationPropertyName name = appendIndex ( root , i ) ;
ConfigurationPropertyName name = appendIndex ( root , i ) ;
Object value = elementBinder . bind ( name , Bindable . of ( elementType ) , source ) ;
Object value = elementBinder . bind ( name , Bindable . of ( elementType ) , source ) ;
if ( value ! = null ) {
if ( value = = null ) {
collection . get ( ) . add ( value ) ;
hasBindingGap = hasBindingGap | | firstUnboundIndex > 0 ;
continue ;
}
firstUnboundIndex = ( firstUnboundIndex < = 0 ) ? i : firstUnboundIndex ;
if ( i - firstUnboundIndex > 10 ) {
break ;
break ;
}
}
knownIndexedChildren . remove ( name . getLastElement ( Form . UNIFORM ) ) ;
collection . get ( ) . add ( value ) ;
}
}
if ( hasBindingGap ) {
if ( source instanceof IterableConfigurationPropertySource iterableSource ) {
assertNoUnboundChildren ( source , root , firstUnboundIndex ) ;
assertNoUnboundChildren ( knownIndexedChildren , iterableSource , root ) ;
}
}
}
}
private ConfigurationPropertyName appendIndex ( ConfigurationPropertyName root , int i ) {
private Set < String > getKnownIndexedChildren ( IterableConfigurationPropertySource filteredSource ,
return root . append ( ( i < INDEXES . length ) ? INDEXES [ i ] : "[" + i + "]" ) ;
ConfigurationPropertyName root ) {
}
Set < String > knownIndexedChildren = new HashSet < > ( ) ;
for ( ConfigurationPropertyName name : filteredSource ) {
private void assertNoUnboundChildren ( ConfigurationPropertySource source , ConfigurationPropertyName root ,
ConfigurationPropertyName choppedName = name . chop ( root . getNumberOfElements ( ) + 1 ) ;
int firstUnboundIndex ) {
if ( choppedName . isLastElementIndexed ( ) ) {
MultiValueMap < String , ConfigurationPropertyName > knownIndexedChildren = getKnownIndexedChildren ( source , root ) ;
knownIndexedChildren . add ( choppedName . getLastElement ( Form . UNIFORM ) ) ;
for ( int i = 0 ; i < firstUnboundIndex ; i + + ) {
}
ConfigurationPropertyName name = appendIndex ( root , i ) ;
knownIndexedChildren . remove ( name . getLastElement ( Form . UNIFORM ) ) ;
}
}
assertNoUnboundChildren ( source , knownIndexedChildren ) ;
return knownIndexedChildren ;
}
}
private MultiValueMap < String , ConfigurationPropertyName > getKnownIndexedChildren ( ConfigurationPropertySource source ,
private void assertNoUnboundChildren ( Set < String > unboundIndexedChildren ,
ConfigurationPropertyName root ) {
IterableConfigurationPropertySource filteredSource , ConfigurationPropertyName root ) {
MultiValueMap < String , ConfigurationPropertyName > children = new LinkedMultiValueMap < > ( ) ;
if ( unboundIndexedChildren . isEmpty ( ) ) {
if ( ! ( source instanceof IterableConfigurationPropertySource iterableSource ) ) {
return ;
return children ;
}
}
for ( ConfigurationPropertyName name : iterableSource . filter ( root : : isAncestorOf ) ) {
Set < ConfigurationProperty > unboundProperties = new TreeSet < > ( ) ;
for ( ConfigurationPropertyName name : filteredSource ) {
ConfigurationPropertyName choppedName = name . chop ( root . getNumberOfElements ( ) + 1 ) ;
ConfigurationPropertyName choppedName = name . chop ( root . getNumberOfElements ( ) + 1 ) ;
if ( choppedName . isLastElementIndexed ( ) ) {
if ( choppedName . isLastElementIndexed ( )
String key = choppedName . getLastElement ( Form . UNIFORM ) ;
& & unboundIndexedChildren . contains ( choppedName . getLastElement ( Form . UNIFORM ) ) ) {
children . add ( key , name ) ;
unboundProperties . add ( filteredSource . getConfigurationProperty ( name ) ) ;
}
}
}
}
return children ;
if ( ! unboundProperties . isEmpty ( ) ) {
throw new UnboundConfigurationPropertiesException ( unboundProperties ) ;
}
}
}
private void assertNoUnboundChildren ( ConfigurationPropertySource source ,
private ConfigurationPropertyName appendIndex ( ConfigurationPropertyName root , int i ) {
MultiValueMap < String , ConfigurationPropertyName > children ) {
return root . append ( ( i < INDEXES . length ) ? INDEXES [ i ] : "[" + i + "]" ) ;
if ( ! children . isEmpty ( ) ) {
throw new UnboundConfigurationPropertiesException ( children . values ( )
. stream ( )
. flatMap ( List : : stream )
. map ( source : : getConfigurationProperty )
. collect ( Collectors . toCollection ( TreeSet : : new ) ) ) ;
}
}
}
private < C > C convert ( Object value , ResolvableType type , Annotation . . . annotations ) {
private < C > C convert ( Object value , ResolvableType type , Annotation . . . annotations ) {