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 == null) return 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 = (Map) node;
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 = (Map) node;
117 } else {
118 return false;
119 }
120 } else {
121 return false;
122 }
123 }
124 if (map == null) return 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 (T) config.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 = (Map) node;
153 } else {
154 return defaultValue;
155 }
156 } else {
157 return defaultValue;
158 }
159 }
160 if (config == null) return defaultValue;
161 Object value = config.get(keys[keys.length - 1]);
162 return value != null ? (T) value : 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 (T) value;
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 ? (T) node : 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 = (Map) node;
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 = (Map) node;
206 } else {
207 return defaultValue;
208 }
209 } else {
210 return defaultValue;
211 }
212 }
213 if (map == null) return defaultValue;
214 Object value = map.get(keys[keys.length - 1]);
215 return value != null ? (T) value : 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 key) throws 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 (T) config.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 = (Map) node;
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 (T) value;
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 key) throws 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 (T) value;
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 (T) node;
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 (T) value;
317 }
318 throw missingResource(type, key);
319 }
320
321 private static MissingResourceException missingResource(String classname, String key) throws 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(value) : null;
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 }
|