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