jamesfredley commented on code in PR #16772:
URL: https://github.com/apache/grails-core/pull/16772#discussion_r4420900158


##########
grails-bootstrap/src/main/groovy/org/grails/config/NavigableMap.groovy:
##########
@@ -374,15 +375,119 @@ class NavigableMap implements Map<String, Object>, 
Cloneable {
     Map<String, Object> toFlatConfig() {
         Map<String, Object> flatConfig = [:]
         flattenKeys(flatConfig, this, [], false)
+        removeDottedDuplicates(flatConfig)
         flatConfig
     }
 
     Properties toProperties() {
         Properties properties = new Properties()
         flattenKeys((Map<Object, Object>) properties, this, [], true)
+        removeDottedDuplicates((Map<Object, Object>) properties)
         properties
     }
 
+    /**
+     * Removes each name that only writes a bracketed name with dots, such as 
{@code app.routes./api/**} beside
+     * {@code app.routes[/api/**]}, which Spring Boot would bind as the key 
{@code api}. A map written by map subscripts
+     * holds its entries under their bracketed names and is walked under their 
dotted ones too, so each entry is
+     * flattened under both. A dotted name is removed only when every 
bracketed name that it writes with dots holds the
+     * same value; where they differ, as when a later write changed one of 
them, every name stays, as it was before. A
+     * bracketed key with a dot in it, {@code a[b.c]}, names a key of {@code 
a}, so the dotted name {@code a.b.c} is
+     * a property of its own, and it stays when the map holds it as a key, as 
a put of {@code a.b.c} does, or the nested
+     * maps hold a value under that path.
+     */
+    private void removeDottedDuplicates(Map<? extends Object, Object> 
flatConfig) {
+        Map<String, List<Object>> bracketedValues = [:]
+        Set<String> pathsToCheck = [] as Set<String>
+        for (Map.Entry<? extends Object, Object> entry in 
flatConfig.entrySet()) {
+            String name = String.valueOf(entry.key)
+            if (name.indexOf('[') > -1) {
+                for (Map.Entry<String, Boolean> dottedName in 
dottedNamesOf(name).entrySet()) {
+                    List<Object> values = bracketedValues.get(dottedName.key)
+                    if (values == null) {
+                        values = []
+                        bracketedValues.put(dottedName.key, values)
+                    }
+                    values.add(entry.value)
+                    if (dottedName.value) {
+                        pathsToCheck.add(dottedName.key)
+                    }
+                }
+            }
+        }
+        for (Map.Entry<String, List<Object>> candidate in 
bracketedValues.entrySet()) {
+            if (!flatConfig.containsKey(candidate.key)) {
+                continue
+            }
+            Object value = flatConfig.get(candidate.key)
+            if (!candidate.value.every { Object bracketedValue -> 
bracketedValue == value }) {
+                continue
+            }
+            if (candidate.key in pathsToCheck && (containsKey(candidate.key) 
|| holdsPath(candidate.key.tokenize('.')))) {

Review Comment:
   `put('a.b', [c: 'x'])` then `merge(['a[b.c]': 'x'], true)` leaves only 
`a[b.c]`. `toFlatConfig()` and `toProperties()` drop `a.b.c`, which comes from 
the map at `a.b` and not from walking the bracketed key. Export both. Recognize 
a dotted prefix that is held as a map, and still require the rest of the path.



-- 
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