@@ -18,6 +18,7 @@ package org.grails.config
1818import groovy.transform.CompileDynamic
1919import groovy.transform.CompileStatic
2020import groovy.transform.EqualsAndHashCode
21+ import org.codehaus.groovy.runtime.DefaultGroovyMethods
2122import org.slf4j.Logger
2223import org.slf4j.LoggerFactory
2324
@@ -47,14 +48,14 @@ class NavigableMap implements Map<String, Object>, Cloneable {
4748 final Map<String , Object > delegateMap
4849 final String dottedPath
4950
50- public NavigableMap () {
51+ NavigableMap () {
5152 rootConfig = this
5253 path = []
5354 dottedPath = " "
5455 delegateMap = new LinkedHashMap<> ()
5556 }
5657
57- public NavigableMap (NavigableMap rootConfig , List<String > path ) {
58+ NavigableMap (NavigableMap rootConfig , List<String > path ) {
5859 super ()
5960 this . rootConfig = rootConfig
6061 this . path = path
@@ -144,7 +145,7 @@ class NavigableMap implements Map<String, Object>, Cloneable {
144145 delegateMap. entrySet()
145146 }
146147
147- public void merge (Map sourceMap , boolean parseFlatKeys = false ) {
148+ void merge (Map sourceMap , boolean parseFlatKeys = false ) {
148149 mergeMaps(this , " " , this , sourceMap, parseFlatKeys)
149150 }
150151
@@ -341,17 +342,17 @@ class NavigableMap implements Map<String, Object>, Cloneable {
341342 targetMap. put(sourceKey, newValue)
342343 }
343344
344- public Object getAt (Object key ) {
345+ Object getAt (Object key ) {
345346 getProperty(String . valueOf(key))
346347 }
347-
348- public void setAt (Object key , Object value ) {
348+
349+ void setAt (Object key , Object value ) {
349350 setProperty(String . valueOf(key), value)
350351 }
351352
352- public Object getProperty (String name ) {
353+ Object getProperty (String name ) {
353354 if (! containsKey(name)) {
354- return null
355+ return new NullSafeNavigator ( this , [name] . asImmutable())
355356 }
356357 Object result = get(name)
357358 if (! (result instanceof NavigableMap )) {
@@ -361,12 +362,12 @@ class NavigableMap implements Map<String, Object>, Cloneable {
361362 }
362363 return result
363364 }
364-
365- public void setProperty (String name , Object value ) {
365+
366+ void setProperty (String name , Object value ) {
366367 mergeMapEntry(rootConfig, dottedPath, this , name, value, false , true )
367368 }
368-
369- public Object navigate (String ... path ) {
369+
370+ Object navigate (String ... path ) {
370371 return navigateMap(this , path)
371372 }
372373
@@ -392,8 +393,8 @@ class NavigableMap implements Map<String, Object>, Cloneable {
392393 }
393394 }
394395 }
395-
396- public NavigableMap navigateSubMap (List<String > path , boolean createMissing ) {
396+
397+ NavigableMap navigateSubMap (List<String > path , boolean createMissing ) {
397398 NavigableMap rootMap = this
398399 NavigableMap currentMap = this
399400 StringBuilder accumulatedPath = new StringBuilder ()
@@ -488,4 +489,172 @@ class NavigableMap implements Map<String, Object>, Cloneable {
488489 boolean equals (Object obj ) {
489490 return delegateMap. equals(obj)
490491 }
492+
493+ /**
494+ * @deprecated This class should be avoided due to known performance reasons. Use {@code config.getProperty(String key, Class<T> targetType) } instead of dot based navigation.
495+ */
496+ @Deprecated
497+ @CompileStatic
498+ static class NullSafeNavigator implements Map<String , Object > {
499+ final NavigableMap parent
500+ final List<String > path
501+
502+ NullSafeNavigator (NavigableMap parent , List<String > path ) {
503+ this . parent = parent
504+ this . path = path
505+ if (LOG . isWarnEnabled()) {
506+ LOG . warn(" Accessing config key '{}' through dot notation has known performance issues, consider using 'config.getProperty(key, targetClass)' instead." , path)
507+ }
508+ }
509+
510+ Object getAt (Object key ) {
511+ getProperty(String . valueOf(key))
512+ }
513+
514+ void setAt (Object key , Object value ) {
515+ setProperty(String . valueOf(key), value)
516+ }
517+
518+ @Override
519+ int size () {
520+ NavigableMap parentMap = parent. navigateSubMap(path, false )
521+ if (parentMap != null ) {
522+ return parentMap. size()
523+ }
524+ return 0
525+ }
526+
527+ @Override
528+ boolean isEmpty () {
529+ NavigableMap parentMap = parent. navigateSubMap(path, false )
530+ if (parentMap != null ) {
531+ return parentMap. isEmpty()
532+ }
533+ return true
534+ }
535+
536+ boolean containsKey (Object key ) {
537+ NavigableMap parentMap = parent. navigateSubMap(path, false )
538+ if (parentMap == null ) return false
539+ else {
540+ return parentMap. containsKey(key)
541+ }
542+ }
543+
544+ @Override
545+ boolean containsValue (Object value ) {
546+ NavigableMap parentMap = parent. navigateSubMap(path, false )
547+ if (parentMap != null ) {
548+ return parentMap. containsValue(value)
549+ }
550+ return false
551+ }
552+
553+ @Override
554+ Object get (Object key ) {
555+ return getAt(key)
556+ }
557+
558+ @Override
559+ Object put (String key , Object value ) {
560+ throw new UnsupportedOperationException (" Configuration cannot be modified" );
561+ }
562+
563+ @Override
564+ Object remove (Object key ) {
565+ throw new UnsupportedOperationException (" Configuration cannot be modified" );
566+ }
567+
568+ @Override
569+ void putAll (Map<? extends String , ?> m ) {
570+ throw new UnsupportedOperationException (" Configuration cannot be modified" );
571+ }
572+
573+ @Override
574+ void clear () {
575+ throw new UnsupportedOperationException (" Configuration cannot be modified" );
576+ }
577+
578+ @Override
579+ Set<String > keySet () {
580+ NavigableMap parentMap = parent. navigateSubMap(path, false )
581+ if (parentMap != null ) {
582+ return parentMap. keySet()
583+ }
584+ return Collections . emptySet()
585+ }
586+
587+ @Override
588+ Collection<Object > values () {
589+ NavigableMap parentMap = parent. navigateSubMap(path, false )
590+ if (parentMap != null ) {
591+ return parentMap. values()
592+ }
593+ return Collections . emptySet()
594+ }
595+
596+ @Override
597+ Set<Entry<String , Object > > entrySet () {
598+ NavigableMap parentMap = parent. navigateSubMap(path, false )
599+ if (parentMap != null ) {
600+ return parentMap. entrySet()
601+ }
602+ return Collections . emptySet()
603+ }
604+
605+ Object getProperty (String name ) {
606+ NavigableMap parentMap = parent. navigateSubMap(path, false )
607+ if (parentMap == null ) {
608+ return new NullSafeNavigator (parent, ((path + [name]) as List<String > ). asImmutable())
609+ } else {
610+ return parentMap. get(name)
611+ }
612+ }
613+
614+ void setProperty (String name , Object value ) {
615+ NavigableMap parentMap = parent. navigateSubMap(path, true )
616+ parentMap. setProperty(name, value)
617+ }
618+
619+ boolean asBoolean () {
620+ false
621+ }
622+
623+ Object invokeMethod (String name , Object args ) {
624+ throw new NullPointerException (" Cannot invoke method " + name + " () on NullSafeNavigator" );
625+ }
626+
627+ boolean equals (Object to ) {
628+ return to == null || DefaultGroovyMethods . is(this , to)
629+ }
630+
631+ Iterator iterator () {
632+ return Collections . EMPTY_LIST . iterator()
633+ }
634+
635+ Object plus (String s ) {
636+ return toString() + s
637+ }
638+
639+ Object plus (Object o ) {
640+ throw new NullPointerException (" Cannot invoke method plus on NullSafeNavigator" )
641+ }
642+
643+ boolean is (Object other ) {
644+ return other == null || DefaultGroovyMethods . is(this , other)
645+ }
646+
647+ Object asType (Class c ) {
648+ if (c== Boolean || c== boolean ) return false
649+ return null
650+ }
651+
652+ String toString () {
653+ return null
654+ }
655+
656+ // public int hashCode() {
657+ // throw new NullPointerException("Cannot invoke method hashCode() on NullSafeNavigator");
658+ // }
659+ }
491660}
0 commit comments