20
20
import java .util .Collections ;
21
21
import java .util .ConcurrentModificationException ;
22
22
import java .util .HashMap ;
23
+ import java .util .HashSet ;
23
24
import java .util .Iterator ;
24
- import java .util .List ;
25
25
import java .util .Map ;
26
26
import java .util .NoSuchElementException ;
27
27
import java .util .Objects ;
28
+ import java .util .Set ;
28
29
import java .util .function .BiPredicate ;
29
30
import java .util .function .Supplier ;
30
31
import java .util .stream .Stream ;
37
38
import org .springframework .core .env .PropertySource ;
38
39
import org .springframework .core .env .StandardEnvironment ;
39
40
import org .springframework .core .env .SystemEnvironmentPropertySource ;
40
- import org .springframework .util .LinkedMultiValueMap ;
41
- import org .springframework .util .MultiValueMap ;
42
41
43
42
/**
44
43
* {@link ConfigurationPropertySource} backed by an {@link EnumerablePropertySource}.
54
53
class SpringIterableConfigurationPropertySource extends SpringConfigurationPropertySource
55
54
implements IterableConfigurationPropertySource , CachingConfigurationPropertySource {
56
55
57
- private volatile ConfigurationPropertyName [] configurationPropertyNames ;
56
+ private final BiPredicate < ConfigurationPropertyName , ConfigurationPropertyName > ancestorOfCheck ;
58
57
59
58
private final SoftReferenceConfigurationPropertyCache <Mappings > cache ;
60
59
61
- private final BiPredicate < ConfigurationPropertyName , ConfigurationPropertyName > ancestorOfCheck ;
60
+ private volatile ConfigurationPropertyName [] configurationPropertyNames ;
62
61
63
62
SpringIterableConfigurationPropertySource (EnumerablePropertySource <?> propertySource , PropertyMapper ... mappers ) {
64
63
super (propertySource , mappers );
65
64
assertEnumerablePropertySource ();
66
- this .cache = new SoftReferenceConfigurationPropertyCache <>(isImmutablePropertySource ());
67
65
this .ancestorOfCheck = getAncestorOfCheck (mappers );
66
+ this .cache = new SoftReferenceConfigurationPropertyCache <>(isImmutablePropertySource ());
68
67
}
69
68
70
69
private BiPredicate <ConfigurationPropertyName , ConfigurationPropertyName > getAncestorOfCheck (
@@ -129,6 +128,9 @@ public ConfigurationPropertyState containsDescendantOf(ConfigurationPropertyName
129
128
if (result != ConfigurationPropertyState .UNKNOWN ) {
130
129
return result ;
131
130
}
131
+ if (this .ancestorOfCheck == PropertyMapper .DEFAULT_ANCESTOR_OF_CHECK ) {
132
+ return getMappings ().containsDescendantOf (name , this .ancestorOfCheck );
133
+ }
132
134
ConfigurationPropertyName [] candidates = getConfigurationPropertyNames ();
133
135
for (ConfigurationPropertyName candidate : candidates ) {
134
136
if (candidate != null && this .ancestorOfCheck .test (name , candidate )) {
@@ -156,7 +158,8 @@ private Mappings getMappings() {
156
158
}
157
159
158
160
private Mappings createMappings () {
159
- return new Mappings (getMappers (), isImmutablePropertySource ());
161
+ return new Mappings (getMappers (), isImmutablePropertySource (),
162
+ this .ancestorOfCheck == PropertyMapper .DEFAULT_ANCESTOR_OF_CHECK );
160
163
}
161
164
162
165
private Mappings updateMappings (Mappings mappings ) {
@@ -188,17 +191,22 @@ private static class Mappings {
188
191
189
192
private final boolean immutable ;
190
193
191
- private volatile MultiValueMap <ConfigurationPropertyName , String > mappings ;
194
+ private final boolean trackDescendants ;
195
+
196
+ private volatile Map <ConfigurationPropertyName , Set <String >> mappings ;
192
197
193
198
private volatile Map <String , ConfigurationPropertyName > reverseMappings ;
194
199
200
+ private volatile Map <ConfigurationPropertyName , Set <ConfigurationPropertyName >> descendants ;
201
+
195
202
private volatile ConfigurationPropertyName [] configurationPropertyNames ;
196
203
197
204
private volatile String [] lastUpdated ;
198
205
199
- Mappings (PropertyMapper [] mappers , boolean immutable ) {
206
+ Mappings (PropertyMapper [] mappers , boolean immutable , boolean trackDescendants ) {
200
207
this .mappers = mappers ;
201
208
this .immutable = immutable ;
209
+ this .trackDescendants = trackDescendants ;
202
210
}
203
211
204
212
void updateMappings (Supplier <String []> propertyNames ) {
@@ -223,32 +231,52 @@ private void updateMappings(String[] propertyNames) {
223
231
if (lastUpdated != null && Arrays .equals (lastUpdated , propertyNames )) {
224
232
return ;
225
233
}
226
- MultiValueMap <ConfigurationPropertyName , String > previousMappings = this .mappings ;
227
- MultiValueMap <ConfigurationPropertyName , String > mappings = (previousMappings != null )
228
- ? new LinkedMultiValueMap <>(previousMappings ) : new LinkedMultiValueMap <>(propertyNames .length );
229
- Map <String , ConfigurationPropertyName > previousReverseMappings = this .reverseMappings ;
230
- Map <String , ConfigurationPropertyName > reverseMappings = (previousReverseMappings != null )
231
- ? new HashMap <>(previousReverseMappings ) : new HashMap <>(propertyNames .length );
234
+ int size = propertyNames .length ;
235
+ Map <ConfigurationPropertyName , Set <String >> mappings = cloneOrCreate (this .mappings , size );
236
+ Map <String , ConfigurationPropertyName > reverseMappings = cloneOrCreate (this .reverseMappings , size );
237
+ Map <ConfigurationPropertyName , Set <ConfigurationPropertyName >> descendants = cloneOrCreate (this .descendants ,
238
+ size );
232
239
for (PropertyMapper propertyMapper : this .mappers ) {
233
240
for (String propertyName : propertyNames ) {
234
241
if (!reverseMappings .containsKey (propertyName )) {
235
242
ConfigurationPropertyName configurationPropertyName = propertyMapper .map (propertyName );
236
243
if (configurationPropertyName != null && !configurationPropertyName .isEmpty ()) {
237
- mappings . add (configurationPropertyName , propertyName );
244
+ add (mappings , configurationPropertyName , propertyName );
238
245
reverseMappings .put (propertyName , configurationPropertyName );
246
+ if (this .trackDescendants ) {
247
+ addParents (descendants , configurationPropertyName );
248
+ }
239
249
}
240
250
}
241
251
}
242
252
}
243
253
this .mappings = mappings ;
244
254
this .reverseMappings = reverseMappings ;
255
+ this .descendants = descendants ;
245
256
this .lastUpdated = this .immutable ? null : propertyNames ;
246
257
this .configurationPropertyNames = this .immutable
247
258
? reverseMappings .values ().toArray (new ConfigurationPropertyName [0 ]) : null ;
248
259
}
249
260
250
- List <String > getMapped (ConfigurationPropertyName configurationPropertyName ) {
251
- return this .mappings .getOrDefault (configurationPropertyName , Collections .emptyList ());
261
+ private <K , V > Map <K , V > cloneOrCreate (Map <K , V > source , int size ) {
262
+ return (source != null ) ? new HashMap <>(source ) : new HashMap <>(size );
263
+ }
264
+
265
+ private void addParents (Map <ConfigurationPropertyName , Set <ConfigurationPropertyName >> descendants ,
266
+ ConfigurationPropertyName name ) {
267
+ ConfigurationPropertyName parent = name ;
268
+ while (!parent .isEmpty ()) {
269
+ add (descendants , parent , name );
270
+ parent = parent .getParent ();
271
+ }
272
+ }
273
+
274
+ private <K , T > void add (Map <K , Set <T >> map , K key , T value ) {
275
+ map .computeIfAbsent (key , (k ) -> new HashSet <>()).add (value );
276
+ }
277
+
278
+ Set <String > getMapped (ConfigurationPropertyName configurationPropertyName ) {
279
+ return this .mappings .getOrDefault (configurationPropertyName , Collections .emptySet ());
252
280
}
253
281
254
282
ConfigurationPropertyName [] getConfigurationPropertyNames (String [] propertyNames ) {
@@ -267,6 +295,20 @@ ConfigurationPropertyName[] getConfigurationPropertyNames(String[] propertyNames
267
295
return names ;
268
296
}
269
297
298
+ ConfigurationPropertyState containsDescendantOf (ConfigurationPropertyName name ,
299
+ BiPredicate <ConfigurationPropertyName , ConfigurationPropertyName > ancestorOfCheck ) {
300
+ if (name .isEmpty () && !this .descendants .isEmpty ()) {
301
+ return ConfigurationPropertyState .PRESENT ;
302
+ }
303
+ Set <ConfigurationPropertyName > candidates = this .descendants .getOrDefault (name , Collections .emptySet ());
304
+ for (ConfigurationPropertyName candidate : candidates ) {
305
+ if (ancestorOfCheck .test (name , candidate )) {
306
+ return ConfigurationPropertyState .PRESENT ;
307
+ }
308
+ }
309
+ return ConfigurationPropertyState .ABSENT ;
310
+ }
311
+
270
312
}
271
313
272
314
/**
0 commit comments