Removed JMX, other unused code from configuration
[sdc.git] / common / onap-common-configuration-management / onap-configuration-management-core / src / main / java / org / onap / config / ConfigurationUtils.java
1 /*
2  * Copyright © 2016-2018 European Support Limited
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16
17 package org.onap.config;
18
19 import static java.util.Optional.ofNullable;
20 import static org.onap.config.api.Hint.EXTERNAL_LOOKUP;
21 import static org.onap.config.api.Hint.LATEST_LOOKUP;
22 import static org.onap.config.api.Hint.NODE_SPECIFIC;
23
24 import com.virtlink.commons.configuration2.jackson.JsonConfiguration;
25 import java.io.File;
26 import java.lang.reflect.Field;
27 import java.lang.reflect.ParameterizedType;
28 import java.lang.reflect.Type;
29 import java.net.MalformedURLException;
30 import java.net.URL;
31 import java.nio.file.Files;
32 import java.nio.file.Path;
33 import java.util.ArrayDeque;
34 import java.util.ArrayList;
35 import java.util.Arrays;
36 import java.util.Collection;
37 import java.util.Collections;
38 import java.util.Deque;
39 import java.util.HashMap;
40 import java.util.HashSet;
41 import java.util.List;
42 import java.util.Map;
43 import java.util.Objects;
44 import java.util.Optional;
45 import java.util.Queue;
46 import java.util.Set;
47 import java.util.SortedSet;
48 import java.util.TreeSet;
49 import java.util.concurrent.BlockingQueue;
50 import java.util.concurrent.ConcurrentLinkedQueue;
51 import java.util.concurrent.LinkedBlockingQueue;
52 import java.util.concurrent.LinkedTransferQueue;
53 import java.util.concurrent.TransferQueue;
54 import java.util.regex.Matcher;
55 import java.util.regex.Pattern;
56 import java.util.stream.Collectors;
57 import net.sf.corn.cps.CPScanner;
58 import net.sf.corn.cps.ResourceFilter;
59 import org.apache.commons.configuration2.CompositeConfiguration;
60 import org.apache.commons.configuration2.Configuration;
61 import org.apache.commons.configuration2.FileBasedConfiguration;
62 import org.apache.commons.configuration2.PropertiesConfiguration;
63 import org.apache.commons.configuration2.XMLConfiguration;
64 import org.apache.commons.configuration2.builder.BasicConfigurationBuilder;
65 import org.apache.commons.configuration2.builder.FileBasedConfigurationBuilder;
66 import org.apache.commons.configuration2.builder.ReloadingFileBasedConfigurationBuilder;
67 import org.apache.commons.configuration2.builder.fluent.Configurations;
68 import org.apache.commons.configuration2.builder.fluent.Parameters;
69 import org.apache.commons.configuration2.convert.DefaultListDelimiterHandler;
70 import org.apache.commons.configuration2.ex.ConfigurationException;
71 import org.apache.commons.io.IOUtils;
72 import org.onap.config.api.Config;
73 import org.onap.config.api.ConfigurationManager;
74 import org.onap.config.impl.YamlConfiguration;
75 import org.onap.config.type.ConfigurationMode;
76 import org.onap.config.type.ConfigurationType;
77 import org.slf4j.Logger;
78 import org.slf4j.LoggerFactory;
79
80 public class ConfigurationUtils {
81
82     private static final Logger LOGGER = LoggerFactory.getLogger(ConfigurationUtils.class);
83
84     private static final String CONFIGURATION_TYPE_NOT_SUPPORTED = "Configuration type not supported:";
85
86     private static final Map<Class, Class> ARRAY_CLASS_MAP;
87
88     static {
89         Map<Class, Class> arrayTypes = new HashMap<>();
90         arrayTypes.put(Byte.class, Byte[].class);
91         arrayTypes.put(Short.class, Short[].class);
92         arrayTypes.put(Integer.class, Integer[].class);
93         arrayTypes.put(Long.class, Long[].class);
94         arrayTypes.put(Float.class, Float[].class);
95         arrayTypes.put(Double.class, Double[].class);
96         arrayTypes.put(Boolean.class, Boolean[].class);
97         arrayTypes.put(Character.class, Character[].class);
98         arrayTypes.put(String.class, String[].class);
99         ARRAY_CLASS_MAP = Collections.unmodifiableMap(arrayTypes);
100     }
101
102     private ConfigurationUtils() {
103         // prevent instantiation
104     }
105
106     public static Collection<File> getAllFiles(File file, boolean recursive, boolean onlyDirectory) {
107
108         ArrayList<File> collection = new ArrayList<>();
109         if (file.isDirectory() && file.exists()) {
110             File[] files = file.listFiles();
111             for (File innerFile : files) {
112                 if (innerFile.isFile() && !onlyDirectory) {
113                     collection.add(innerFile);
114                 } else if (innerFile.isDirectory()) {
115                     collection.add(innerFile);
116                     if (recursive) {
117                         collection.addAll(getAllFiles(innerFile, true, onlyDirectory));
118                     }
119                 }
120             }
121         }
122         return collection;
123     }
124
125     public static String getCommaSeparatedList(String[] list) {
126         return (list == null) || (list.length == 0) ? "" : getCommaSeparatedList(Arrays.asList(list));
127     }
128
129     public static String getCommaSeparatedList(List<?> list) {
130
131         if ((list == null) || list.isEmpty()) {
132             return "";
133         }
134
135         return list.stream().filter(o -> o != null && !o.toString().trim().isEmpty())
136                                          .map(o -> o.toString().trim()).collect(Collectors.joining(","));
137     }
138
139     public static boolean isConfig(URL url) {
140         return isConfig(url.getFile());
141     }
142
143     public static boolean isConfig(String file) {
144         file = file.toUpperCase().substring(file.lastIndexOf('!') + 1);
145         file = file.substring(file.lastIndexOf('/') + 1);
146         return file.matches(
147                 "CONFIG(-\\w*){0,1}(-" + "(" + ConfigurationMode.OVERRIDE + "|" + ConfigurationMode.MERGE + "|"
148                         + ConfigurationMode.UNION + ")){0,1}" + "\\.(" + ConfigurationType.PROPERTIES.name() + "|"
149                         + ConfigurationType.XML.name() + "|" + ConfigurationType.JSON.name() + "|"
150                         + ConfigurationType.YAML.name() + ")$") || file.matches(
151                 "CONFIG(.)*\\.(" + ConfigurationType.PROPERTIES.name() + "|" + ConfigurationType.XML.name() + "|"
152                         + ConfigurationType.JSON.name() + "|" + ConfigurationType.YAML.name() + ")$");
153     }
154
155     public static boolean isConfig(File file) {
156         return file != null && file.exists() && isConfig(file.getName());
157     }
158
159     private static Optional<String> readNamespace(Configuration config) {
160         return ofNullable(config).flatMap(configuration -> ofNullable(configuration.getString(Constants.NAMESPACE_KEY)))
161                        .map(String::toUpperCase);
162     }
163
164     private static Optional<String> readMergeStrategy(Configuration config) {
165         return ofNullable(config).flatMap(configuration -> ofNullable(configuration.getString(Constants.MODE_KEY)))
166                        .map(String::toUpperCase);
167     }
168
169     public static ConfigurationMode getMergeStrategy(File file) {
170         Optional<ConfigurationMode> configurationMode =
171                 getConfiguration(file).flatMap(ConfigurationUtils::readMergeStrategy)
172                         .flatMap(ConfigurationUtils::convertConfigurationMode);
173         return configurationMode.orElseGet(() -> getMergeStrategy(file.getName().toUpperCase()));
174     }
175
176     public static ConfigurationMode getMergeStrategy(URL url) {
177         Optional<ConfigurationMode> configurationMode =
178                 getConfiguration(url).flatMap(ConfigurationUtils::readMergeStrategy)
179                         .flatMap(ConfigurationUtils::convertConfigurationMode);
180         return configurationMode.orElseGet(() -> getMergeStrategy(url.getFile().toUpperCase()));
181     }
182
183     public static ConfigurationMode getMergeStrategy(String file) {
184
185         file = file.toUpperCase().substring(file.lastIndexOf('!') + 1);
186         file = file.substring(file.lastIndexOf('/') + 1);
187
188         Pattern pattern = Pattern.compile(
189                 "CONFIG(-\\w*){0,1}(-" + "(" + ConfigurationMode.OVERRIDE + "|" + ConfigurationMode.MERGE + "|"
190                         + ConfigurationMode.UNION + ")){0,1}" + "\\.(" + ConfigurationType.PROPERTIES.name() + "|"
191                         + ConfigurationType.XML.name() + "|" + ConfigurationType.JSON.name() + "|"
192                         + ConfigurationType.YAML.name() + ")$");
193         Matcher matcher = pattern.matcher(file);
194         boolean b1 = matcher.matches();
195         if (b1) {
196             for (int i = 1; i <= matcher.groupCount(); i++) {
197                 String modeName = matcher.group(i);
198                 if (modeName != null) {
199                     modeName = modeName.substring(1);
200                 }
201                 try {
202                     return Enum.valueOf(ConfigurationMode.class, modeName);
203                 } catch (Exception exception) {
204                     //do nothing
205                 }
206             }
207         }
208
209         return null;
210     }
211
212     public static Optional<FileBasedConfiguration> getConfiguration(URL url) {
213
214         try {
215
216             ConfigurationType configType = ConfigurationUtils.getConfigType(url);
217             switch (configType) {
218                 case PROPERTIES:
219                     return Optional.of(new Configurations().fileBased(PropertiesConfiguration.class, url));
220                 case XML:
221                     return Optional.of(new Configurations().fileBased(XMLConfiguration.class, url));
222                 case JSON:
223                     return Optional.of(new Configurations().fileBased(JsonConfiguration.class, url));
224                 case YAML:
225                     return Optional.of(new Configurations().fileBased(YamlConfiguration.class, url));
226                 default:
227                     throw new ConfigurationException(CONFIGURATION_TYPE_NOT_SUPPORTED + configType);
228             }
229         } catch (ConfigurationException exception) {
230             exception.printStackTrace();
231         }
232
233         return Optional.empty();
234     }
235
236     public static Optional<FileBasedConfiguration> getConfiguration(File file) {
237
238         try {
239             return getConfiguration(file.getAbsoluteFile().toURI().toURL());
240         } catch (MalformedURLException e) {
241             throw new IllegalStateException("Malformed URL: " + file.getAbsolutePath());
242         }
243     }
244
245     public static ConfigurationType getConfigType(File file) {
246         Objects.requireNonNull(file, "File cannot be null");
247         return Enum.valueOf(ConfigurationType.class,
248                 file.getAbsolutePath().substring(file.getAbsolutePath().lastIndexOf('.') + 1).toUpperCase());
249     }
250
251     public static ConfigurationType getConfigType(URL url) {
252         Objects.requireNonNull(url, "URL cannot be null");
253         return Enum.valueOf(ConfigurationType.class,
254                 url.getFile().substring(url.getFile().lastIndexOf('.') + 1).toUpperCase());
255     }
256
257     private static Optional<ConfigurationMode> convertConfigurationMode(String configMode) {
258         ConfigurationMode configurationMode = null;
259         try {
260             configurationMode = ConfigurationMode.valueOf(configMode);
261         } catch (Exception exception) {
262             LOGGER.error("Could not find convert {} into configuration mode", configMode);
263         }
264         return Optional.ofNullable(configurationMode);
265     }
266
267     public static Class getCollectionGenericType(Field field) {
268         Type type = field.getGenericType();
269
270         if (type instanceof ParameterizedType) {
271
272             ParameterizedType paramType = (ParameterizedType) type;
273             Type[] arr = paramType.getActualTypeArguments();
274             if (arr.length > 0) {
275                 Class<?> clazz = (Class<?>) arr[0];
276                 if (isWrapperClass(clazz)) {
277                     return clazz;
278                 } else {
279                     throw new IllegalArgumentException("Collection of type " + clazz.getName() + " not supported.");
280                 }
281             }
282         }
283
284         return String[].class;
285     }
286
287     public static boolean isWrapperClass(Class clazz) {
288         return clazz == String.class || clazz == Boolean.class || clazz == Character.class
289                        || Number.class.isAssignableFrom(clazz);
290     }
291
292     public static Class getArrayClass(Class clazz) {
293         return ARRAY_CLASS_MAP.getOrDefault(clazz, null);
294     }
295
296     public static List<URL> getAllClassPathResources() {
297         return CPScanner.scanResources(new ResourceFilter());
298     }
299
300     public static BasicConfigurationBuilder<FileBasedConfiguration> getConfigurationBuilder(File file) {
301         FileBasedConfigurationBuilder<FileBasedConfiguration> builder;
302         ConfigurationType configType = ConfigurationUtils.getConfigType(file);
303         builder = getFileBasedConfigurationBuilder(configType);
304         builder.configure(new Parameters().fileBased().setFile(file)
305                                   .setListDelimiterHandler(new DefaultListDelimiterHandler(',')));
306         return builder;
307     }
308
309     public static BasicConfigurationBuilder<FileBasedConfiguration> getConfigurationBuilder(URL url) {
310         ConfigurationType configType = ConfigurationUtils.getConfigType(url);
311         ReloadingFileBasedConfigurationBuilder<FileBasedConfiguration> builder =
312                 getFileBasedConfigurationBuilder(configType);
313         builder.configure(
314                 new Parameters().fileBased().setURL(url).setListDelimiterHandler(new DefaultListDelimiterHandler(',')));
315         return builder;
316     }
317
318     private static ReloadingFileBasedConfigurationBuilder<FileBasedConfiguration> getFileBasedConfigurationBuilder(
319             ConfigurationType configType) {
320
321         ReloadingFileBasedConfigurationBuilder<FileBasedConfiguration> builder;
322         switch (configType) {
323             case PROPERTIES:
324                 builder = new ReloadingFileBasedConfigurationBuilder<>(PropertiesConfiguration.class);
325                 break;
326             case XML:
327                 builder = new ReloadingFileBasedConfigurationBuilder<>(XMLConfiguration.class);
328                 break;
329             case JSON:
330                 builder = new ReloadingFileBasedConfigurationBuilder<>(JsonConfiguration.class);
331                 break;
332             case YAML:
333                 builder = new ReloadingFileBasedConfigurationBuilder<>(YamlConfiguration.class);
334                 break;
335             default:
336                 throw new IllegalArgumentException(CONFIGURATION_TYPE_NOT_SUPPORTED + configType);
337         }
338         return builder;
339     }
340
341     public static <T> T read(Configuration config, Class<T> clazz, String keyPrefix) throws Exception {
342         Config confAnnotation = clazz.getAnnotation(Config.class);
343         if (confAnnotation != null) {
344             keyPrefix += (confAnnotation.key() + ".");
345         }
346         T objToReturn = clazz.newInstance();
347         for (Field field : clazz.getDeclaredFields()) {
348             Config fieldAnnotation = field.getAnnotation(Config.class);
349             if (fieldAnnotation != null) {
350                 field.setAccessible(true);
351                 field.set(objToReturn, config.getProperty(keyPrefix + fieldAnnotation.key()));
352             } else if (field.getType().getAnnotation(Config.class) != null) {
353                 field.set(objToReturn, read(config, field.getType(), keyPrefix));
354             }
355         }
356         return objToReturn;
357     }
358
359     public static Object getPrimitiveArray(Collection collection, Class clazz) {
360         if (clazz == int.class) {
361             int[] array = new int[collection.size()];
362             Object[] objArray = collection.toArray();
363             for (int i = 0; i < collection.size(); i++) {
364                 array[i] = (int) objArray[i];
365             }
366             return array;
367         }
368         if (clazz == byte.class) {
369             byte[] array = new byte[collection.size()];
370             Object[] objArray = collection.toArray();
371             for (int i = 0; i < collection.size(); i++) {
372                 array[i] = (byte) objArray[i];
373             }
374             return array;
375         }
376         if (clazz == short.class) {
377             short[] array = new short[collection.size()];
378             Object[] objArray = collection.toArray();
379             for (int i = 0; i < collection.size(); i++) {
380                 array[i] = (short) objArray[i];
381             }
382             return array;
383         }
384         if (clazz == long.class) {
385             long[] array = new long[collection.size()];
386             Object[] objArray = collection.toArray();
387             for (int i = 0; i < collection.size(); i++) {
388                 array[i] = (long) objArray[i];
389             }
390             return array;
391         }
392         if (clazz == float.class) {
393             float[] array = new float[collection.size()];
394             Object[] objArray = collection.toArray();
395             for (int i = 0; i < collection.size(); i++) {
396                 array[i] = (float) objArray[i];
397             }
398             return array;
399         }
400         if (clazz == double.class) {
401             double[] array = new double[collection.size()];
402             Object[] objArray = collection.toArray();
403             for (int i = 0; i < collection.size(); i++) {
404                 array[i] = (double) objArray[i];
405             }
406             return array;
407         }
408         if (clazz == boolean.class) {
409             boolean[] array = new boolean[collection.size()];
410             Object[] objArray = collection.toArray();
411             for (int i = 0; i < collection.size(); i++) {
412                 array[i] = (boolean) objArray[i];
413             }
414             return array;
415         }
416         return null;
417     }
418
419     public static String getCollectionString(String input) {
420         Pattern pattern = Pattern.compile("^\\[(.*)\\]$");
421         Matcher matcher = pattern.matcher(input);
422         if (matcher.matches()) {
423             input = matcher.group(1);
424         }
425         return input;
426     }
427
428     public static String processVariablesIfPresent(String tenant, String namespace, String data) {
429         Pattern pattern = Pattern.compile("^.*\\$\\{(.*)\\}.*");
430         Matcher matcher = pattern.matcher(data);
431         if (matcher.matches()) {
432             String key = matcher.group(1);
433             if (key.toUpperCase().startsWith("ENV:")) {
434                 String envValue = System.getenv(key.substring(4));
435                 return processVariablesIfPresent(tenant, namespace, data.replaceAll("\\$\\{" + key + "\\}",
436                         envValue == null ? "" : envValue.replace("\\", "\\\\")));
437             } else if (key.toUpperCase().startsWith("SYS:")) {
438                 String sysValue = System.getProperty(key.substring(4));
439                 return processVariablesIfPresent(tenant, namespace, data.replaceAll("\\$\\{" + key + "\\}",
440                         sysValue == null ? "" : sysValue.replace("\\", "\\\\")));
441             } else {
442                 String propertyValue = ConfigurationUtils.getCollectionString(
443                         ConfigurationManager.lookup().getAsStringValues(tenant, namespace, key).toString());
444                 return processVariablesIfPresent(tenant, namespace, data.replaceAll("\\$\\{" + key + "\\}",
445                         propertyValue == null ? "" : propertyValue.replace("\\", "\\\\")));
446             }
447         } else {
448             return data;
449         }
450     }
451
452     public static String getFileContents(String path) {
453         try {
454             if (path != null) {
455                 return IOUtils.toString(new URL(path));
456             }
457         } catch (Exception exception) {
458             exception.printStackTrace();
459         }
460         return null;
461     }
462
463     public static String getFileContents(Path path) {
464         try {
465             if (path != null) {
466                 return new String(Files.readAllBytes(path));
467             }
468         } catch (Exception exception) {
469             exception.printStackTrace();
470         }
471         return null;
472     }
473
474     public static Object getDefaultFor(Class clazz) {
475         if (byte.class == clazz) {
476             return new Byte("0");
477         } else if (short.class == clazz) {
478             return new Short("0");
479         } else if (int.class == clazz) {
480             return new Integer("0");
481         } else if (float.class == clazz) {
482             return new Float("0");
483         } else if (long.class == clazz) {
484             return new Long("0");
485         } else if (double.class == clazz) {
486             return new Double("0");
487         } else if (boolean.class == clazz) {
488             return Boolean.FALSE;
489         }
490         return (char) 0;
491     }
492
493     public static Collection getCompatibleCollectionForAbstractDef(Class clazz) {
494         if (BlockingQueue.class.isAssignableFrom(clazz)) {
495             return getConcreteCollection(BlockingQueue.class);
496         }
497         if (TransferQueue.class.isAssignableFrom(clazz)) {
498             return getConcreteCollection(TransferQueue.class);
499         }
500         if (Deque.class.isAssignableFrom(clazz)) {
501             return getConcreteCollection(Deque.class);
502         }
503         if (Queue.class.isAssignableFrom(clazz)) {
504             return getConcreteCollection(Queue.class);
505         }
506         if (SortedSet.class.isAssignableFrom(clazz)) {
507             return getConcreteCollection(SortedSet.class);
508         }
509         if (Set.class.isAssignableFrom(clazz)) {
510             return getConcreteCollection(Set.class);
511         }
512         if (List.class.isAssignableFrom(clazz)) {
513             return getConcreteCollection(List.class);
514         }
515         return null;
516     }
517
518     public static Collection getConcreteCollection(Class clazz) {
519         switch (clazz.getName()) {
520             case "java.util.Collection":
521             case "java.util.List":
522                 return new ArrayList<>();
523             case "java.util.Set":
524                 return new HashSet<>();
525             case "java.util.SortedSet":
526                 return new TreeSet<>();
527             case "java.util.Queue":
528                 return new ConcurrentLinkedQueue<>();
529             case "java.util.Deque":
530                 return new ArrayDeque<>();
531             case "java.util.concurrent.TransferQueue":
532                 return new LinkedTransferQueue<>();
533             case "java.util.concurrent.BlockingQueue":
534                 return new LinkedBlockingQueue<>();
535             default:
536                 return null;
537         }
538     }
539
540     public static String getConfigurationRepositoryKey(File file) {
541         return getConfigurationRepositoryKey(
542                 ConfigurationUtils.getNamespace(file).split(Constants.TENANT_NAMESPACE_SEPARATOR));
543     }
544
545     public static String getConfigurationRepositoryKey(URL url) {
546         return getConfigurationRepositoryKey(
547                 ConfigurationUtils.getNamespace(url).split(Constants.TENANT_NAMESPACE_SEPARATOR));
548     }
549
550     public static String getConfigurationRepositoryKey(String[] array) {
551         Deque<String> stack = new ArrayDeque<>();
552         stack.push(Constants.DEFAULT_TENANT);
553         for (String element : array) {
554             stack.push(element);
555         }
556         String toReturn = stack.pop();
557         return stack.pop() + Constants.KEY_ELEMENTS_DELIMITER + toReturn;
558     }
559
560     public static String getNamespace(File file) {
561         Optional<String> namespace =
562                 getConfiguration(file).flatMap(ConfigurationUtils::readNamespace).map(String::toUpperCase);
563         return namespace.orElseGet(() -> getNamespace(file.getName().toUpperCase()));
564     }
565
566     public static String getNamespace(String file) {
567         file = file.toUpperCase().substring(file.lastIndexOf('!') + 1);
568         file = file.substring(file.lastIndexOf('/') + 1);
569         Pattern pattern = Pattern.compile(
570                 "CONFIG(-\\w*){0,1}(-" + "(" + ConfigurationMode.OVERRIDE + "|" + ConfigurationMode.MERGE + "|"
571                         + ConfigurationMode.UNION + ")){0,1}" + "\\.(" + ConfigurationType.PROPERTIES.name() + "|"
572                         + ConfigurationType.XML.name() + "|" + ConfigurationType.JSON.name() + "|"
573                         + ConfigurationType.YAML.name() + ")$");
574         Matcher matcher = pattern.matcher(file);
575         boolean b1 = matcher.matches();
576         if (b1) {
577             if (matcher.group(1) != null) {
578                 String moduleName = matcher.group(1).substring(1);
579                 return moduleName.equalsIgnoreCase(ConfigurationMode.OVERRIDE.name()) || moduleName.equalsIgnoreCase(
580                         ConfigurationMode.UNION.name()) || moduleName.equalsIgnoreCase(ConfigurationMode.MERGE.name())
581                                ? Constants.DEFAULT_NAMESPACE : moduleName;
582             } else {
583                 return Constants.DEFAULT_NAMESPACE;
584             }
585         } else if (isConfig(file)) {
586             return Constants.DEFAULT_NAMESPACE;
587         }
588
589         return null;
590     }
591
592     public static String getNamespace(URL url) {
593
594         Optional<String> namespace =
595                 getConfiguration(url).flatMap(ConfigurationUtils::readNamespace).map(String::toUpperCase);
596
597         return namespace.orElseGet(() -> getNamespace(url.getFile().toUpperCase()));
598     }
599
600     public static Object getProperty(Configuration config, String key, int processingHints) {
601
602         if (!isDirectLookup(processingHints) && (config instanceof CompositeConfiguration)) {
603
604             CompositeConfiguration conf = (CompositeConfiguration) config;
605             for (int i = 0; i < conf.getNumberOfConfigurations(); i++) {
606
607                 if (isNodeSpecific(processingHints)) {
608                     Object obj = conf.getConfiguration(i).getProperty(key);
609                     if (obj != null) {
610                         return obj;
611                     }
612                 }
613             }
614         }
615
616         return config.getProperty(key);
617     }
618
619     public static boolean isCollection(String input) {
620         Pattern pattern = Pattern.compile("^\\[(.*)\\]$");
621         Matcher matcher = pattern.matcher(input);
622         return matcher.matches();
623     }
624
625     public static boolean isDirectLookup(int hints) {
626         return (hints & LATEST_LOOKUP.value()) == LATEST_LOOKUP.value();
627     }
628
629     public static boolean isNodeSpecific(int hints) {
630         return (hints & NODE_SPECIFIC.value()) == NODE_SPECIFIC.value();
631     }
632
633     public static boolean isExternalLookup(int hints) {
634         return (hints & EXTERNAL_LOOKUP.value()) == EXTERNAL_LOOKUP.value();
635     }
636
637     public static boolean isZeroLengthArray(Class clazz, Object obj) {
638         if (clazz.isArray() && clazz.getComponentType().isPrimitive()) {
639             if (clazz.getComponentType() == int.class) {
640                 return ((int[]) obj).length == 0;
641             } else if (clazz.getComponentType() == byte.class) {
642                 return ((byte[]) obj).length == 0;
643             } else if (clazz.getComponentType() == short.class) {
644                 return ((short[]) obj).length == 0;
645             } else if (clazz.getComponentType() == float.class) {
646                 return ((float[]) obj).length == 0;
647             } else if (clazz.getComponentType() == boolean.class) {
648                 return ((boolean[]) obj).length == 0;
649             } else if (clazz.getComponentType() == double.class) {
650                 return ((double[]) obj).length == 0;
651             } else if (clazz.getComponentType() == long.class) {
652                 return ((long[]) obj).length == 0;
653             } else {
654                 return ((Object[]) obj).length == 0;
655             }
656         }
657
658         return false;
659     }
660
661     public static boolean isBlank(String value) {
662         return value == null || value.trim().isEmpty();
663     }
664 }