ConfigUtils.java
001 /*
002  * Copyright 2008-2014 the original author or authors.
003  *
004  * Licensed under the Apache License, Version 2.0 (the "License");
005  * you may not use this file except in compliance with the License.
006  * You may obtain a copy of the License at
007  *
008  *     http://www.apache.org/licenses/LICENSE-2.0
009  *
010  * Unless required by applicable law or agreed to in writing, software
011  * distributed under the License is distributed on an "AS IS" BASIS,
012  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013  * See the License for the specific language governing permissions and
014  * limitations under the License.
015  */
016 package griffon.util;
017 
018 import javax.annotation.Nonnull;
019 import javax.annotation.Nullable;
020 import java.util.*;
021 
022 import static griffon.util.GriffonNameUtils.requireNonBlank;
023 import static griffon.util.TypeUtils.*;
024 import static java.util.Collections.unmodifiableSortedSet;
025 import static java.util.Objects.requireNonNull;
026 
027 /**
028  * Utility class for reading configuration properties.
029  *
030  @author Andres Almiray
031  */
032 public final class ConfigUtils {
033     private static final String ERROR_CONFIG_NULL = "Argument 'config' must not be null";
034     private static final String ERROR_KEY_BLANK = "Argument 'key' must not be blank";
035 
036     private ConfigUtils() {
037         // prevent instantiation
038     }
039 
040     /**
041      * Returns true if there's a on-null value for the specified key.
042      *
043      @param config the configuration object to be searched upon
044      @param key    the key to be searched
045      @return true if there's a value for the specified key, false otherwise
046      */
047     @SuppressWarnings("unchecked")
048     public static boolean isValueDefined(@Nonnull Map<String, Object> config, @Nonnull String key) {
049         requireNonNull(config, ERROR_CONFIG_NULL);
050         requireNonBlank(key, ERROR_KEY_BLANK);
051 
052         if (config.containsKey(key)) {
053             return true;
054         }
055 
056         String[] keys = key.split("\\.");
057         for (int i = 0; i < keys.length - 1; i++) {
058             if (config != null) {
059                 Object node = config.get(keys[i]);
060                 if (node instanceof Map) {
061                     config = (Map<String, Object>node;
062                 else {
063                     return false;
064                 }
065             else {
066                 return false;
067             }
068         }
069         if (config == nullreturn false;
070         Object value = config.get(keys[keys.length - 1]);
071         return value != null;
072     }
073 
074     /**
075      * Returns true if there's a on-null value for the specified key.
076      *
077      @param config the configuration object to be searched upon
078      @param key    the key to be searched
079      @return true if there's a value for the specified key, false otherwise
080      */
081     @SuppressWarnings("unchecked")
082     public static boolean isValueDefined(@Nonnull ResourceBundle config, @Nonnull String key) {
083         requireNonNull(config, ERROR_CONFIG_NULL);
084         requireNonBlank(key, ERROR_KEY_BLANK);
085 
086         String[] keys = key.split("\\.");
087 
088         try {
089             Object value = config.getObject(key);
090             if (value != null) {
091                 return true;
092             }
093         catch (MissingResourceException mre) {
094             // OK
095         }
096 
097         if (keys.length == 1) {
098             try {
099                 Object node = config.getObject(keys[0]);
100                 return node != null;
101             catch (MissingResourceException mre) {
102                 return false;
103             }
104         }
105 
106         Object node = config.getObject(keys[0]);
107         if (!(node instanceof Map)) {
108             return false;
109         }
110 
111         Map<String, Object> map = (Mapnode;
112         for (int i = 1; i < keys.length - 1; i++) {
113             if (map != null) {
114                 node = map.get(keys[i]);
115                 if (node instanceof Map) {
116                     map = (Mapnode;
117                 else {
118                     return false;
119                 }
120             else {
121                 return false;
122             }
123         }
124         if (map == nullreturn false;
125         Object value = map.get(keys[keys.length - 1]);
126         return value != null;
127     }
128 
129     /**
130      * Returns the value for the specified key with an optional default value if no match is found.
131      *
132      @param config       the configuration object to be searched upon
133      @param key          the key to be searched
134      @param defaultValue the value to send back if no match is found
135      @return the value of the key or the default value if no match is found
136      */
137     @Nullable
138     @SuppressWarnings({"unchecked""ConstantConditions"})
139     public static <T> T getConfigValue(@Nonnull Map<String, Object> config, @Nonnull String key, @Nullable T defaultValue) {
140         requireNonNull(config, ERROR_CONFIG_NULL);
141         requireNonBlank(key, ERROR_KEY_BLANK);
142 
143         if (config.containsKey(key)) {
144             return (Tconfig.get(key);
145         }
146 
147         String[] keys = key.split("\\.");
148         for (int i = 0; i < keys.length - 1; i++) {
149             if (config != null) {
150                 Object node = config.get(keys[i]);
151                 if (node instanceof Map) {
152                     config = (Mapnode;
153                 else {
154                     return defaultValue;
155                 }
156             else {
157                 return defaultValue;
158             }
159         }
160         if (config == nullreturn defaultValue;
161         Object value = config.get(keys[keys.length - 1]);
162         return value != null (Tvalue : defaultValue;
163     }
164 
165     /**
166      * Returns the value for the specified key with an optional default value if no match is found.
167      *
168      @param config       the configuration object to be searched upon
169      @param key          the key to be searched
170      @param defaultValue the value to send back if no match is found
171      @return the value of the key or the default value if no match is found
172      */
173     @Nullable
174     @SuppressWarnings({"unchecked""ConstantConditions"})
175     public static <T> T getConfigValue(@Nonnull ResourceBundle config, @Nonnull String key, @Nullable T defaultValue) {
176         requireNonNull(config, ERROR_CONFIG_NULL);
177         requireNonBlank(key, ERROR_KEY_BLANK);
178 
179         String[] keys = key.split("\\.");
180 
181         try {
182             Object value = config.getObject(key);
183             if (value != null) {
184                 return (Tvalue;
185             }
186         catch (MissingResourceException mre) {
187             // OK
188         }
189 
190         if (keys.length == 1) {
191             Object node = config.getObject(keys[0]);
192             return node != null (Tnode : defaultValue;
193         }
194 
195         Object node = config.getObject(keys[0]);
196         if (!(node instanceof Map)) {
197             return defaultValue;
198         }
199 
200         Map<String, Object> map = (Mapnode;
201         for (int i = 1; i < keys.length - 1; i++) {
202             if (map != null) {
203                 node = map.get(keys[i]);
204                 if (node instanceof Map) {
205                     map = (Mapnode;
206                 else {
207                     return defaultValue;
208                 }
209             else {
210                 return defaultValue;
211             }
212         }
213         if (map == nullreturn defaultValue;
214         Object value = map.get(keys[keys.length - 1]);
215         return value != null (Tvalue : defaultValue;
216     }
217 
218     /**
219      * Returns the value for the specified key with an optional default value if no match is found.
220      *
221      @param config the configuration object to be searched upon
222      @param key    the key to be searched
223      @return the value of the key or the default value if no match is found
224      */
225     @Nullable
226     @SuppressWarnings({"unchecked""ConstantConditions"})
227     public static <T> T getConfigValue(@Nonnull Map<String, Object> config, @Nonnull String keythrows MissingResourceException {
228         requireNonNull(config, ERROR_CONFIG_NULL);
229         requireNonBlank(key, ERROR_KEY_BLANK);
230         String type = config.getClass().getName();
231 
232         if (config.containsKey(key)) {
233             return (Tconfig.get(key);
234         }
235 
236         String[] keys = key.split("\\.");
237         for (int i = 0; i < keys.length - 1; i++) {
238             if (config != null) {
239                 Object node = config.get(keys[i]);
240                 if (node instanceof Map) {
241                     config = (Mapnode;
242                 else {
243                     throw missingResource(type, key);
244                 }
245             else {
246                 throw missingResource(type, key);
247             }
248         }
249         if (config == null) {
250             throw missingResource(type, key);
251         }
252         Object value = config.get(keys[keys.length - 1]);
253         if (value != null) {
254             return (Tvalue;
255         }
256         throw missingResource(type, key);
257     }
258 
259     /**
260      * Returns the value for the specified key with an optional default value if no match is found.
261      *
262      @param config the configuration object to be searched upon
263      @param key    the key to be searched
264      @return the value of the key or the default value if no match is found
265      */
266     @Nullable
267     @SuppressWarnings({"unchecked""ConstantConditions"})
268     public static <T> T getConfigValue(@Nonnull ResourceBundle config, @Nonnull String keythrows MissingResourceException {
269         requireNonNull(config, ERROR_CONFIG_NULL);
270         requireNonBlank(key, ERROR_KEY_BLANK);
271         String type = config.getClass().getName();
272 
273         String[] keys = key.split("\\.");
274 
275         try {
276             Object value = config.getObject(key);
277             if (value != null) {
278                 return (Tvalue;
279             }
280         catch (MissingResourceException mre) {
281             // OK
282         }
283 
284         if (keys.length == 1) {
285             Object node = config.getObject(keys[0]);
286             if (node != null) {
287                 return (Tnode;
288             }
289             throw missingResource(type, key);
290         }
291 
292         Object node = config.getObject(keys[0]);
293         if (!(node instanceof Map)) {
294             throw missingResource(type, key);
295         }
296 
297         Map<String, Object> map = (Map<String, Object>node;
298         for (int i = 1; i < keys.length - 1; i++) {
299             if (map != null) {
300                 node = map.get(keys[i]);
301                 if (node instanceof Map) {
302                     map = (Map<String, Object>node;
303                 else {
304                     throw missingResource(type, key);
305                 }
306             else {
307                 throw missingResource(type, key);
308             }
309         }
310         if (map == null) {
311             throw missingResource(type, key);
312         }
313 
314         Object value = map.get(keys[keys.length - 1]);
315         if (value != null) {
316             return (Tvalue;
317         }
318         throw missingResource(type, key);
319     }
320 
321     private static MissingResourceException missingResource(String classname, String keythrows MissingResourceException {
322         return new MissingResourceException("Can't find resource for bundle " + classname + ", key " + key, classname, key);
323     }
324 
325     /**
326      * Returns the value for the specified key coerced to a boolean.
327      *
328      @param config the configuration object to be searched upon
329      @param key    the key to be searched
330      @return the value of the key. Returns {@code false} if no match.
331      */
332     public static boolean getConfigValueAsBoolean(@Nonnull Map<String, Object> config, @Nonnull String key) {
333         return getConfigValueAsBoolean(config, key, false);
334     }
335 
336     /**
337      * Returns the value for the specified key with an optional default value if no match is found.
338      *
339      @param config       the configuration object to be searched upon
340      @param key          the key to be searched
341      @param defaultValue the value to send back if no match is found
342      @return the value of the key or the default value if no match is found
343      */
344     public static boolean getConfigValueAsBoolean(@Nonnull Map<String, Object> config, @Nonnull String key, boolean defaultValue) {
345         Object value = getConfigValue(config, key, defaultValue);
346         return castToBoolean(value);
347     }
348 
349     /**
350      * Returns the value for the specified key coerced to an int.
351      *
352      @param config the configuration object to be searched upon
353      @param key    the key to be searched
354      @return the value of the key. Returns {@code 0} if no match.
355      */
356     public static int getConfigValueAsInt(@Nonnull Map<String, Object> config, @Nonnull String key) {
357         return getConfigValueAsInt(config, key, 0);
358     }
359 
360     /**
361      * Returns the value for the specified key with an optional default value if no match is found.
362      *
363      @param config       the configuration object to be searched upon
364      @param key          the key to be searched
365      @param defaultValue the value to send back if no match is found
366      @return the value of the key or the default value if no match is found
367      */
368     public static int getConfigValueAsInt(@Nonnull Map<String, Object> config, @Nonnull String key, int defaultValue) {
369         Object value = getConfigValue(config, key, defaultValue);
370         return castToInt(value);
371     }
372 
373     /**
374      * Returns the value for the specified key coerced to a long.
375      *
376      @param config the configuration object to be searched upon
377      @param key    the key to be searched
378      @return the value of the key. Returns {@code 0L} if no match.
379      */
380     public static long getConfigValueAsLong(@Nonnull Map<String, Object> config, @Nonnull String key) {
381         return getConfigValueAsLong(config, key, 0L);
382     }
383 
384     /**
385      * Returns the value for the specified key with an optional default value if no match is found.
386      *
387      @param config       the configuration object to be searched upon
388      @param key          the key to be searched
389      @param defaultValue the value to send back if no match is found
390      @return the value of the key or the default value if no match is found
391      */
392     public static long getConfigValueAsLong(@Nonnull Map<String, Object> config, @Nonnull String key, long defaultValue) {
393         Object value = getConfigValue(config, key, defaultValue);
394         return castToLong(value);
395     }
396 
397     /**
398      * Returns the value for the specified key coerced to a double.
399      *
400      @param config the configuration object to be searched upon
401      @param key    the key to be searched
402      @return the value of the key. Returns {@code 0d} if no match.
403      */
404     public static double getConfigValueAsDouble(@Nonnull Map<String, Object> config, @Nonnull String key) {
405         return getConfigValueAsDouble(config, key, 0d);
406     }
407 
408     /**
409      * Returns the value for the specified key with an optional default value if no match is found.
410      *
411      @param config       the configuration object to be searched upon
412      @param key          the key to be searched
413      @param defaultValue the value to send back if no match is found
414      @return the value of the key or the default value if no match is found
415      */
416     public static double getConfigValueAsDouble(@Nonnull Map<String, Object> config, @Nonnull String key, double defaultValue) {
417         Object value = getConfigValue(config, key, defaultValue);
418         return castToDouble(value);
419     }
420 
421     /**
422      * Returns the value for the specified key coerced to a float.
423      *
424      @param config the configuration object to be searched upon
425      @param key    the key to be searched
426      @return the value of the key. Returns {@code 0f} if no match.
427      */
428     public static float getConfigValueAsFloat(@Nonnull Map<String, Object> config, @Nonnull String key) {
429         return getConfigValueAsFloat(config, key, 0f);
430     }
431 
432     /**
433      * Returns the value for the specified key with an optional default value if no match is found.
434      *
435      @param config       the configuration object to be searched upon
436      @param key          the key to be searched
437      @param defaultValue the value to send back if no match is found
438      @return the value of the key or the default value if no match is found
439      */
440     public static float getConfigValueAsFloat(@Nonnull Map<String, Object> config, @Nonnull String key, float defaultValue) {
441         Object value = getConfigValue(config, key, defaultValue);
442         return castToFloat(value);
443     }
444 
445     /**
446      * Returns the value for the specified key coerced to a Number.
447      *
448      @param config the configuration object to be searched upon
449      @param key    the key to be searched
450      @return the value of the key. Returns {@code null} if no match.
451      */
452     @Nullable
453     public static Number getConfigValueAsNumber(@Nonnull Map<String, Object> config, @Nonnull String key) {
454         return getConfigValueAsNumber(config, key, null);
455     }
456 
457     /**
458      * Returns the value for the specified key with an optional default value if no match is found.
459      *
460      @param config       the configuration object to be searched upon
461      @param key          the key to be searched
462      @param defaultValue the value to send back if no match is found
463      @return the value of the key or the default value if no match is found
464      */
465     @Nullable
466     public static Number getConfigValueAsNumber(@Nonnull Map<String, Object> config, @Nonnull String key, @Nullable Number defaultValue) {
467         Object value = getConfigValue(config, key, defaultValue);
468         return castToNumber(value);
469     }
470 
471     /**
472      * Returns the value for the specified key converted to a String.
473      *
474      @param config the configuration object to be searched upon
475      @param key    the key to be searched
476      @return the value of the key. Returns {@code ""} if no match.
477      */
478     @Nullable
479     public static String getConfigValueAsString(@Nonnull Map<String, Object> config, @Nonnull String key) {
480         return getConfigValueAsString(config, key, "");
481     }
482 
483     /**
484      * Returns the value for the specified key with an optional default value if no match is found.
485      *
486      @param config       the configuration object to be searched upon
487      @param key          the key to be searched
488      @param defaultValue the value to send back if no match is found
489      @return the value of the key or the default value if no match is found
490      */
491     @Nullable
492     public static String getConfigValueAsString(@Nonnull Map<String, Object> config, @Nonnull String key, @Nullable String defaultValue) {
493         Object value = getConfigValue(config, key, defaultValue);
494         return value != null ? String.valueOf(valuenull;
495     }
496 
497     // the following taken from SpringFramework::org.springframework.util.StringUtils
498 
499     /**
500      * Extract the filename extension from the given path,
501      * e.g. "mypath/myfile.txt" -> "txt".
502      *
503      @param path the file path (may be <code>null</code>)
504      @return the extracted filename extension, or <code>null</code> if none
505      */
506     public static String getFilenameExtension(String path) {
507         if (path == null) {
508             return null;
509         }
510         int extIndex = path.lastIndexOf(".");
511         if (extIndex == -1) {
512             return null;
513         }
514         int folderIndex = path.lastIndexOf("/");
515         if (folderIndex > extIndex) {
516             return null;
517         }
518         return path.substring(extIndex + 1);
519     }
520 
521     /**
522      * Strip the filename extension from the given path,
523      * e.g. "mypath/myfile.txt" -> "mypath/myfile".
524      *
525      @param path the file path (may be <code>null</code>)
526      @return the path with stripped filename extension,
527      *         or <code>null</code> if none
528      */
529     public static String stripFilenameExtension(String path) {
530         if (path == null) {
531             return null;
532         }
533         int extIndex = path.lastIndexOf(".");
534         if (extIndex == -1) {
535             return path;
536         }
537         int folderIndex = path.lastIndexOf("/");
538         if (folderIndex > extIndex) {
539             return path;
540         }
541         return path.substring(0, extIndex);
542     }
543 
544     @Nonnull
545     public static Set<String> collectKeys(@Nonnull Map<String, Object> map) {
546         requireNonNull(map, "Argument 'map' must not be null");
547 
548         SortedSet<String> keys = new TreeSet<>();
549         for (Map.Entry<String, Object> entry : map.entrySet()) {
550             String key = entry.getKey();
551             Object value = entry.getValue();
552             doCollectKeys(key, value, keys);
553         }
554 
555         return unmodifiableSortedSet(keys);
556     }
557 
558     @SuppressWarnings("unchecked")
559     private static void doCollectKeys(String key, Object value, SortedSet<String> keys) {
560         if (value instanceof Map) {
561             Map<String, Object> map = (Map<String, Object>value;
562             for (Map.Entry<String, Object> entry : map.entrySet()) {
563                 doCollectKeys(key + "." + entry.getKey(), entry.getValue(), keys);
564             }
565         else {
566             keys.add(key);
567         }
568     }
569 }