Skip to content

Commit 03bef04

Browse files
committed
Optimize merging of subscript lists/maps
1 parent a892fc7 commit 03bef04

File tree

2 files changed

+127
-96
lines changed

2 files changed

+127
-96
lines changed

grails-bootstrap/src/main/groovy/org/grails/config/NavigableMap.groovy

Lines changed: 99 additions & 94 deletions
Original file line numberDiff line numberDiff line change
@@ -139,27 +139,23 @@ class NavigableMap implements Map<String, Object>, Cloneable {
139139
NavigableMap targetMap,
140140
Map sourceMap,
141141
boolean parseFlatKeys) {
142-
143-
Map collapseSourcedMap = collapseKeysWithSubscript(sourceMap)
144-
if (!shouldSkipBlock(collapseSourcedMap, path)) {
145-
for (Entry entry in collapseSourcedMap) {
146-
Object sourceKeyObject = entry.key
147-
Object sourceValue = entry.value
148-
String sourceKey = String.valueOf(sourceKeyObject)
149-
if (parseFlatKeys) {
150-
String[] keyParts = sourceKey.split(/\./)
151-
if (keyParts.length > 1) {
152-
mergeMapEntry(rootMap, path, targetMap, sourceKey, sourceValue, parseFlatKeys)
153-
def pathParts = keyParts[0..-2]
154-
Map actualTarget = targetMap.navigateSubMap(pathParts as List, true)
155-
sourceKey = keyParts[-1]
156-
mergeMapEntry(rootMap, pathParts.join('.'), actualTarget, sourceKey, sourceValue, parseFlatKeys)
157-
} else {
158-
mergeMapEntry(rootMap, path, targetMap, sourceKey, sourceValue, parseFlatKeys)
159-
}
142+
for (Entry entry in sourceMap) {
143+
Object sourceKeyObject = entry.key
144+
Object sourceValue = entry.value
145+
String sourceKey = String.valueOf(sourceKeyObject)
146+
if (parseFlatKeys) {
147+
String[] keyParts = sourceKey.split(/\./)
148+
if (keyParts.length > 1) {
149+
mergeMapEntry(rootMap, path, targetMap, sourceKey, sourceValue, parseFlatKeys)
150+
def pathParts = keyParts[0..-2]
151+
Map actualTarget = targetMap.navigateSubMap(pathParts as List, true)
152+
sourceKey = keyParts[-1]
153+
mergeMapEntry(rootMap, pathParts.join('.'), actualTarget, sourceKey, sourceValue, parseFlatKeys)
160154
} else {
161155
mergeMapEntry(rootMap, path, targetMap, sourceKey, sourceValue, parseFlatKeys)
162156
}
157+
} else {
158+
mergeMapEntry(rootMap, path, targetMap, sourceKey, sourceValue, parseFlatKeys)
163159
}
164160
}
165161
}
@@ -174,48 +170,97 @@ class NavigableMap implements Map<String, Object>, Cloneable {
174170
}
175171

176172
protected void mergeMapEntry(NavigableMap rootMap, String path, NavigableMap targetMap, String sourceKey, Object sourceValue, boolean parseFlatKeys, boolean isNestedSet = false) {
177-
Object currentValue = targetMap.containsKey(sourceKey) ? targetMap.get(sourceKey) : null
178-
Object newValue
179-
if(sourceValue instanceof Map) {
180-
List<String> newPathList = []
181-
newPathList.addAll( targetMap.getPath() )
182-
newPathList.add(sourceKey)
183-
NavigableMap subMap
184-
if(currentValue instanceof NavigableMap) {
185-
subMap = (NavigableMap)currentValue
186-
}
187-
else {
188-
subMap = new NavigableMap( (NavigableMap)targetMap.rootConfig, newPathList.asImmutable())
189-
if(currentValue instanceof Map) {
190-
subMap.putAll((Map)currentValue)
173+
int subscriptStart = sourceKey.indexOf('[')
174+
int subscriptEnd = sourceKey.indexOf(']')
175+
if (subscriptEnd > subscriptStart) {
176+
if(subscriptStart > -1 && subscriptEnd != sourceKey.length() - 1) {
177+
String k = sourceKey[0..<subscriptStart]
178+
String index = sourceKey[subscriptStart+1..<subscriptEnd]
179+
String remainder = sourceKey[subscriptEnd+2..-1]
180+
if (remainder) {
181+
182+
boolean isNumber = index.isNumber()
183+
if (isNumber) {
184+
int i = index.toInteger()
185+
def currentValue = targetMap.get(k)
186+
List list = currentValue instanceof List ? currentValue : []
187+
if (list.size() > i) {
188+
def v = list.get(i)
189+
if (v instanceof Map) {
190+
((Map)v).put(remainder, sourceValue)
191+
} else {
192+
Map newMap = [:]
193+
newMap.put(remainder, sourceValue)
194+
list.set(i, newMap)
195+
}
196+
} else {
197+
Map newMap = [:]
198+
newMap.put(remainder, sourceValue)
199+
200+
list.add(i, newMap)
201+
}
202+
targetMap.put(k, list)
203+
} else {
204+
def currentValue = targetMap.get(k)
205+
Map nestedMap = currentValue instanceof Map ? currentValue : [:]
206+
targetMap.put(k, nestedMap)
207+
208+
def v = nestedMap.get(index)
209+
if (v instanceof Map) {
210+
((Map)v).put(remainder, sourceValue)
211+
} else {
212+
Map newMap = [:]
213+
newMap.put(remainder, sourceValue)
214+
nestedMap.put(index, newMap)
215+
}
216+
}
217+
}
218+
219+
}
220+
} else {
221+
Object currentValue = targetMap.containsKey(sourceKey) ? targetMap.get(sourceKey) : null
222+
Object newValue
223+
if(sourceValue instanceof Map) {
224+
List<String> newPathList = []
225+
newPathList.addAll( targetMap.getPath() )
226+
newPathList.add(sourceKey)
227+
NavigableMap subMap
228+
if(currentValue instanceof NavigableMap) {
229+
subMap = (NavigableMap)currentValue
230+
}
231+
else {
232+
subMap = new NavigableMap( (NavigableMap)targetMap.rootConfig, newPathList.asImmutable())
233+
if(currentValue instanceof Map) {
234+
subMap.putAll((Map)currentValue)
235+
}
191236
}
237+
String newPath = path ? "${path}.${sourceKey}" : sourceKey
238+
mergeMaps(rootMap, newPath , subMap, (Map)sourceValue, parseFlatKeys)
239+
newValue = subMap
240+
} else {
241+
newValue = sourceValue
192242
}
193-
String newPath = path ? "${path}.${sourceKey}" : sourceKey
194-
mergeMaps(rootMap, newPath , subMap, (Map)sourceValue, parseFlatKeys)
195-
newValue = subMap
196-
} else {
197-
newValue = sourceValue
198-
}
199-
if (isNestedSet && newValue == null) {
200-
if(path) {
243+
if (isNestedSet && newValue == null) {
244+
if(path) {
201245

202-
def subMap = rootMap.get(path)
203-
if(subMap instanceof Map) {
204-
subMap.remove(sourceKey)
205-
}
206-
def keysToRemove = rootMap.keySet().findAll() { String key ->
207-
key.startsWith("${path}.")
246+
def subMap = rootMap.get(path)
247+
if(subMap instanceof Map) {
248+
subMap.remove(sourceKey)
249+
}
250+
def keysToRemove = rootMap.keySet().findAll() { String key ->
251+
key.startsWith("${path}.")
252+
}
253+
for(key in keysToRemove) {
254+
rootMap.remove(key)
255+
}
208256
}
209-
for(key in keysToRemove) {
210-
rootMap.remove(key)
257+
targetMap.remove(sourceKey)
258+
} else {
259+
if(path) {
260+
rootMap.put( "${path}.${sourceKey}".toString(), newValue )
211261
}
262+
mergeMapEntry(targetMap, sourceKey, newValue)
212263
}
213-
targetMap.remove(sourceKey)
214-
} else {
215-
if(path) {
216-
rootMap.put( "${path}.${sourceKey}".toString(), newValue )
217-
}
218-
mergeMapEntry(targetMap, sourceKey, newValue)
219264
}
220265
}
221266

@@ -518,44 +563,4 @@ class NavigableMap implements Map<String, Object>, Cloneable {
518563
// }
519564
}
520565

521-
@CompileDynamic
522-
static Map collapseKeysWithSubscript(Map m) {
523-
Set<Map> keys = m.collect { k, v ->
524-
if (k ==~ SUBSCRIPT_REGEX) {
525-
def matcher = k =~ SUBSCRIPT_REGEX
526-
return [
527-
subscript: matcher[0][1],
528-
name: matcher[0][2]
529-
]
530-
}
531-
[subscript: null,
532-
name: k]
533-
}
534-
Map result = [:]
535-
Set keyNames = keys.collect { it.name } as Set
536-
for (key in keyNames) {
537-
Map submap = keys.find { it.name == key }
538-
if (submap.subscript == null) {
539-
result[key] = m[key]
540-
} else {
541-
Set<Map> submaps = keys.findAll { it.name == key }
542-
List l = []
543-
for (submapkey in submaps.collect { it.subscript }) {
544-
Map mapofsubkeys = m.findAll { k, v -> k == submapkey }
545-
l.addAll(mapofsubkeys.collect { k, v -> v })
546-
mapofsubkeys = m.findAll { k, v -> k != submapkey && k.startsWith(submapkey) }
547-
if (mapofsubkeys) {
548-
Map subscriptMap = [:]
549-
mapofsubkeys.each { k, v ->
550-
String subkey = k.substring(submapkey.length() + ".".length())
551-
subscriptMap[subkey] = v
552-
}
553-
l << subscriptMap
554-
}
555-
}
556-
result[key] = l
557-
}
558-
}
559-
result
560-
}
561566
}

grails-bootstrap/src/test/groovy/grails/config/NavigableMapSpec.groovy

Lines changed: 28 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,8 @@ class NavigableMapSpec extends Specification {
1010
def "#input "(Map input) {
1111

1212
when:
13-
Map output = NavigableMap.collapseKeysWithSubscript(input)
13+
Map output = new NavigableMap()
14+
output.merge(input)
1415

1516
then:
1617
output.keySet().sort() as List<String> == ['xml', 'grails.cors.mappings[/api/**]', 'js', 'json'].sort()
@@ -37,7 +38,8 @@ class NavigableMapSpec extends Specification {
3738
]
3839

3940
when:
40-
Map output = NavigableMap.collapseKeysWithSubscript(input)
41+
Map output = new NavigableMap()
42+
output.merge(input)
4143

4244
then:
4345
output.keySet() as List<String> == ['rabbitmq.connections']
@@ -50,4 +52,28 @@ class NavigableMapSpec extends Specification {
5052
output['rabbitmq.connections'][0].password == 'guest'
5153
}
5254

55+
def "multiple subscript entries are collapse to a map of maps"() {
56+
given:
57+
Map input = [
58+
'rabbitmq.connections[foo].name': 'main',
59+
'rabbitmq.connections[foo].host': '1109201498',
60+
'rabbitmq.connections[foo].host2': '635494740',
61+
'rabbitmq.connections[foo].username': 'guest',
62+
'rabbitmq.connections[foo].password': 'guest',
63+
]
64+
65+
when:
66+
Map output = new NavigableMap()
67+
output.merge(input)
68+
69+
then:
70+
output.keySet() as List<String> == ['rabbitmq.connections']
71+
output['rabbitmq.connections'] instanceof Map
72+
output['rabbitmq.connections'].size() == 1
73+
output['rabbitmq.connections']['foo'].name == 'main'
74+
output['rabbitmq.connections']['foo'].host == '1109201498'
75+
output['rabbitmq.connections']['foo'].host2 == '635494740'
76+
output['rabbitmq.connections']['foo'].username == 'guest'
77+
output['rabbitmq.connections']['foo'].password == 'guest'
78+
}
5379
}

0 commit comments

Comments
 (0)