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 == null) return 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 = (Map) node;
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 = (Map) node;
127 } else {
128 return false;
129 }
130 } else {
131 return false;
132 }
133 }
134 if (map == null) return 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 (T) config.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 = (Map) node;
163 } else {
164 return defaultValue;
165 }
166 } else {
167 return defaultValue;
168 }
169 }
170 if (config == null) return defaultValue;
171 Object value = config.get(keys[keys.length - 1]);
172 return value != null ? (T) value : 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 (T) value;
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 ? (T) node : 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 = (Map) node;
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 = (Map) node;
216 } else {
217 return defaultValue;
218 }
219 } else {
220 return defaultValue;
221 }
222 }
223 if (map == null) return defaultValue;
224 Object value = map.get(keys[keys.length - 1]);
225 return value != null ? (T) value : 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 key) throws 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 (T) config.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 = (Map) node;
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 (T) value;
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 key) throws 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 (T) value;
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 (T) node;
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 (T) value;
327 }
328 throw missingResource(type, key);
329 }
330
331 private static MissingResourceException missingResource(String classname, String key) throws 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(value) : null;
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 }
|