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