jdaugherty commented on code in PR #16798:
URL: https://github.com/apache/grails-core/pull/16798#discussion_r4424614541


##########
grails-core/src/main/groovy/org/grails/config/NavigableMapConfig.java:
##########
@@ -56,13 +57,93 @@ public abstract class NavigableMapConfig implements Config {
     protected ClassLoader classLoader = 
Thread.currentThread().getContextClassLoader();
     protected ConfigurableConversionService conversionService = new 
DefaultConversionService();
     protected NavigableMap configMap = new NavigableMap() {
+
+        /**
+         * A value resolved for a dotted key, which the merge writes twice in 
a row: under the whole key and then,
+         * in the nested map, under its last part. The second write takes the 
value the first one resolved.
+         */
+        private Object pendingSource;
+        private Object pendingResolved;
+
+        /**
+         * Resolves the placeholders in a list, which is stored as it is, and 
in a value written under an indexed key,
+         * {@code rows[0]} or {@code rows[0].url}, which is written into its 
list or map without the hook below.
+         */
+        @Override
+        protected void mergeMapEntry(NavigableMap rootMap, String path, 
NavigableMap targetMap, String sourceKey,
+                Object sourceValue, boolean parseFlatKeys, boolean 
isNestedSet) {
+            Object value = pendingSource != null && pendingSource == 
sourceValue ? pendingResolved : resolvedBeforeMerge(sourceKey, sourceValue);
+            pendingSource = null;
+            pendingResolved = null;
+            if (value != sourceValue && parseFlatKeys && 
sourceKey.split("\\.").length > 1) {
+                pendingSource = sourceValue;
+                pendingResolved = value;
+            }
+            super.mergeMapEntry(rootMap, path, targetMap, sourceKey, value, 
parseFlatKeys, isNestedSet);
+        }
+
         @Override
         protected Object mergeMapEntry(NavigableMap targetMap, String 
sourceKey, Object newValue) {
             if (newValue instanceof CharSequence) {
                 newValue = resolvePlaceholders(newValue.toString());
             }
             return super.mergeMapEntry(targetMap, sourceKey, newValue);
         }
+
+        private Object resolvedBeforeMerge(String sourceKey, Object value) {
+            if (value instanceof List) {

Review Comment:
   Fixed in f2fa67765c. The values of an ordinary property source are resolved 
once, by `processAndEvaluate()`, and the merge now writes them as they are. 
With `host=secret`, `\${host}` reads as `${host}` as a value, in a list and 
under an indexed key, as the `Environment` reads it.
   
   A plain string from such a source was already resolved twice on 7.0.x and 
read `secret`; it now reads `${host}` too. A `ConfigObject` value, which 
skipped `processAndEvaluate()` and was resolved by the merge, now goes through 
`processAndEvaluate()` like the others, so its placeholders are still resolved.
   
   Covered by `a value of an ordinary property source is resolved once, so an 
escaped placeholder is kept as text, as the environment keeps it`. The 
configuration guide's YAML page now says that an escaped placeholder is kept as 
text.
   



##########
grails-core/src/main/groovy/org/grails/config/NavigableMapConfig.java:
##########
@@ -56,13 +57,93 @@ public abstract class NavigableMapConfig implements Config {
     protected ClassLoader classLoader = 
Thread.currentThread().getContextClassLoader();
     protected ConfigurableConversionService conversionService = new 
DefaultConversionService();
     protected NavigableMap configMap = new NavigableMap() {
+
+        /**
+         * A value resolved for a dotted key, which the merge writes twice in 
a row: under the whole key and then,
+         * in the nested map, under its last part. The second write takes the 
value the first one resolved.
+         */
+        private Object pendingSource;
+        private Object pendingResolved;
+
+        /**
+         * Resolves the placeholders in a list, which is stored as it is, and 
in a value written under an indexed key,
+         * {@code rows[0]} or {@code rows[0].url}, which is written into its 
list or map without the hook below.
+         */
+        @Override
+        protected void mergeMapEntry(NavigableMap rootMap, String path, 
NavigableMap targetMap, String sourceKey,
+                Object sourceValue, boolean parseFlatKeys, boolean 
isNestedSet) {
+            Object value = pendingSource != null && pendingSource == 
sourceValue ? pendingResolved : resolvedBeforeMerge(sourceKey, sourceValue);

Review Comment:
   Fixed in f2fa67765c. A merge now resolves a text once under the full name of 
its entry, such as `app.hosts[0]`, `app.rows[0].url` or `app.rows[0].tags[0]`, 
whichever key writes it: the list in the nested map, the list under the flat 
key, or the indexed key. This replaces the pending value, so it also holds when 
flat keys are off.
   
   `a placeholder in a list element of application.yml is resolved once, alike 
for every read` loads `application.yml` through Grails' YAML loader, beside a 
property source that answers a new value on every read. The source is read 
three times, once per element, and `config['app.hosts[0]']`, 
`getProperty('app.hosts[0]')`, the whole list, `navigate` and `toProperties()` 
agree for each element. The previous head read it 13 times.
   



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to