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