|
6 | 6 | import java.util.ArrayList; |
7 | 7 | import java.util.Collection; |
8 | 8 | import java.util.Collections; |
9 | | -import java.util.LinkedHashMap; |
10 | 9 | import java.util.List; |
11 | | -import java.util.Map; |
12 | 10 |
|
13 | 11 | import com.typesafe.config.ConfigException; |
14 | 12 | import com.typesafe.config.ConfigOrigin; |
@@ -134,20 +132,19 @@ else if (end instanceof Unmergeable) { |
134 | 132 | sourceForEnd = source.pushParent(replaceable); |
135 | 133 |
|
136 | 134 | // Same spec rule as the short-circuit above, applied per-key: |
137 | | - // keys in the lower-priority 'end' object that are already |
138 | | - // shadowed in 'merged' by a value ignoring fallbacks would be |
139 | | - // discarded by the subsequent merge, so don't resolve them. |
140 | | - if (merged instanceof AbstractConfigObject && end instanceof AbstractConfigObject) { |
141 | | - AbstractConfigObject prunedEnd = pruneShadowedKeys( |
142 | | - (AbstractConfigObject) end, (AbstractConfigObject) merged); |
143 | | - if (prunedEnd == null) { |
144 | | - if (ConfigImpl.traceSubstitutionsEnabled()) |
145 | | - ConfigImpl.trace(newContext.depth(), |
146 | | - "all keys in end are shadowed by merged, skipping"); |
147 | | - count += 1; |
148 | | - continue; |
149 | | - } |
150 | | - end = prunedEnd; |
| 135 | + // if every key in the lower-priority 'end' object is already |
| 136 | + // shadowed in 'merged' by a value ignoring fallbacks, the whole |
| 137 | + // 'end' would be discarded by the subsequent merge. Skip |
| 138 | + // resolving it. (We only skip the whole entry — substituting a |
| 139 | + // partial copy of 'end' would change identity and break parent |
| 140 | + // chain walks during inner substitution resolution.) |
| 141 | + if (merged instanceof AbstractConfigObject && end instanceof SimpleConfigObject |
| 142 | + && allKeysShadowed((SimpleConfigObject) end, (AbstractConfigObject) merged)) { |
| 143 | + if (ConfigImpl.traceSubstitutionsEnabled()) |
| 144 | + ConfigImpl.trace(newContext.depth(), |
| 145 | + "all keys in end are shadowed by merged, skipping"); |
| 146 | + count += 1; |
| 147 | + continue; |
151 | 148 | } |
152 | 149 | } |
153 | 150 |
|
@@ -181,36 +178,22 @@ else if (end instanceof Unmergeable) { |
181 | 178 | return ResolveResult.make(newContext, merged); |
182 | 179 | } |
183 | 180 |
|
184 | | - // Returns a copy of 'end' with keys removed when 'merged' already has a |
185 | | - // value at that key which ignores fallbacks. Returns null if every key in |
186 | | - // 'end' is shadowed. Returns 'end' unchanged if no pruning applies or if |
187 | | - // we cannot safely inspect merged (e.g. unresolved CDMO). |
188 | | - private static AbstractConfigObject pruneShadowedKeys(AbstractConfigObject end, |
189 | | - AbstractConfigObject merged) { |
190 | | - if (!(end instanceof SimpleConfigObject)) |
191 | | - return end; |
192 | | - SimpleConfigObject simple = (SimpleConfigObject) end; |
193 | | - Map<String, AbstractConfigValue> kept = new LinkedHashMap<String, AbstractConfigValue>(); |
194 | | - boolean pruned = false; |
195 | | - for (String key : simple.keySet()) { |
| 181 | + // True when every key in 'end' is shadowed by a value in 'merged' that |
| 182 | + // ignores fallbacks (so the subsequent merge would drop the whole 'end'). |
| 183 | + private static boolean allKeysShadowed(SimpleConfigObject end, AbstractConfigObject merged) { |
| 184 | + if (end.isEmpty()) |
| 185 | + return false; |
| 186 | + for (String key : end.keySet()) { |
196 | 187 | AbstractConfigValue mergedValue; |
197 | 188 | try { |
198 | 189 | mergedValue = merged.attemptPeekWithPartialResolve(key); |
199 | 190 | } catch (ConfigException.NotResolved e) { |
200 | | - return end; |
201 | | - } |
202 | | - if (mergedValue != null && mergedValue.ignoresFallbacks()) { |
203 | | - pruned = true; |
204 | | - } else { |
205 | | - kept.put(key, simple.attemptPeekWithPartialResolve(key)); |
| 191 | + return false; |
206 | 192 | } |
| 193 | + if (mergedValue == null || !mergedValue.ignoresFallbacks()) |
| 194 | + return false; |
207 | 195 | } |
208 | | - if (!pruned) |
209 | | - return end; |
210 | | - if (kept.isEmpty()) |
211 | | - return null; |
212 | | - return new SimpleConfigObject(end.origin(), kept, |
213 | | - ResolveStatus.fromValues(kept.values()), false); |
| 196 | + return true; |
214 | 197 | } |
215 | 198 |
|
216 | 199 | @Override |
|
0 commit comments