17e9ef048f73da6210ef75866960ec9d7d4c7539
[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 com.virtlink.commons.configuration2.jackson.JsonConfiguration;
23 import net.sf.corn.cps.CPScanner;
24 import net.sf.corn.cps.ResourceFilter;
25 import org.apache.commons.configuration2.Configuration;
26 import org.apache.commons.configuration2.CompositeConfiguration;
27 import org.apache.commons.configuration2.FileBasedConfiguration;
28 import org.apache.commons.configuration2.PropertiesConfiguration;
29 import org.apache.commons.configuration2.XMLConfiguration;
30 import org.apache.commons.configuration2.builder.BasicConfigurationBuilder;
31 import org.apache.commons.configuration2.builder.FileBasedConfigurationBuilder;
32 import org.apache.commons.configuration2.builder.ReloadingFileBasedConfigurationBuilder;
33 import org.apache.commons.configuration2.builder.fluent.Configurations;
34 import org.apache.commons.configuration2.builder.fluent.Parameters;
35 import org.apache.commons.configuration2.convert.DefaultListDelimiterHandler;
36 import org.apache.commons.configuration2.ex.ConfigurationException;
37 import org.apache.commons.io.IOUtils;
38 import org.onap.config.api.Config;
39 import org.onap.config.api.ConfigurationManager;
40 import org.onap.config.impl.YamlConfiguration;
41 import org.onap.config.type.ConfigurationMode;
42 import org.onap.config.type.ConfigurationType;
43 import org.slf4j.Logger;
44 import org.slf4j.LoggerFactory;
45
46 import java.io.File;
47 import java.io.IOException;
48 import java.lang.reflect.Field;
49 import java.lang.reflect.ParameterizedType;
50 import java.lang.reflect.Type;
51 import java.net.MalformedURLException;
52 import java.net.URL;
53 import java.nio.charset.Charset;
54 import java.nio.file.Files;
55 import java.nio.file.FileVisitResult;
56 import java.nio.file.Path;
57 import java.nio.file.SimpleFileVisitor;
58 import java.nio.file.attribute.BasicFileAttributes;
59 import java.util.ArrayDeque;
60 import java.util.ArrayList;
61 import java.util.Arrays;
62 import java.util.Collection;
63 import java.util.Collections;
64 import java.util.Deque;
65 import java.util.HashMap;
66 import java.util.HashSet;
67 import java.util.List;
68 import java.util.Map;
69 import java.util.Objects;
70 import java.util.Optional;
71 import java.util.Queue;
72 import java.util.Set;
73 import java.util.SortedSet;
74 import java.util.TreeSet;
75 import java.util.concurrent.BlockingQueue;
76 import java.util.concurrent.ConcurrentLinkedQueue;
77 import java.util.concurrent.LinkedBlockingQueue;
78 import java.util.concurrent.LinkedTransferQueue;
79 import java.util.concurrent.TransferQueue;
80 import java.util.regex.Matcher;
81 import java.util.regex.Pattern;
82 import java.util.stream.Collectors;
83
84 import static java.util.Optional.ofNullable;
85 import static org.onap.config.api.Hint.EXTERNAL_LOOKUP;
86 import static org.onap.config.api.Hint.LATEST_LOOKUP;
87 import static org.onap.config.api.Hint.NODE_SPECIFIC;
88
89 public class ConfigurationUtils {
90
91     private static final Logger LOGGER = LoggerFactory.getLogger(ConfigurationUtils.class);
92
93     private static final String CONFIGURATION_TYPE_NOT_SUPPORTED = "Configuration type not supported:";
94
95     private static final Map<Class, Class> ARRAY_CLASS_MAP;
96
97     private static final String CONFIG_REGEX_TPL_OPT_1 = "CONFIG(-\\w*){0,1}(-(%s|%s|%s)){0,1}\\.(%s|%s|%s|%s)$";
98
99     private static final String CONFIG_REGEX_TPL_OPT_2 = "CONFIG(.)*\\.(%s|%s|%s|%s)$";
100
101     static {
102         Map<Class, Class> arrayTypes = new HashMap<>();
103         arrayTypes.put(Byte.class, Byte[].class);
104         arrayTypes.put(Short.class, Short[].class);
105         arrayTypes.put(Integer.class, Integer[].class);
106         arrayTypes.put(Long.class, Long[].class);
107         arrayTypes.put(Float.class, Float[].class);
108         arrayTypes.put(Double.class, Double[].class);
109         arrayTypes.put(Boolean.class, Boolean[].class);
110         arrayTypes.put(Character.class, Character[].class);
111         arrayTypes.put(String.class, String[].class);
112         ARRAY_CLASS_MAP = Collections.unmodifiableMap(arrayTypes);
113     }
114
115     private ConfigurationUtils() {
116         // prevent instantiation
117     }
118
119     public static Collection<File> getAllFiles(File file, boolean recursive, boolean onlyDirectory) {
120         ArrayList<File> collection = new ArrayList<>();
121         Path rootPath = file.toPath();
122         try {
123             Files.walkFileTree(rootPath, new SimpleFileVisitor<Path>() {
124                 @Override
125                 public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException {
126                     super.preVisitDirectory(dir,attrs);
127                     if (rootPath.equals(dir)) {
128                         return FileVisitResult.CONTINUE;
129                     }
130                     collection.add(dir.toFile());
131                     return recursive? FileVisitResult.CONTINUE : FileVisitResult.SKIP_SUBTREE;
132                 }
133
134                 @Override
135                 public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
136                     super.visitFile(file, attrs);
137                     if (!onlyDirectory) {
138                         collection.add(file.toFile());
139                     }
140                     return FileVisitResult.CONTINUE;
141                 }
142             });
143         } catch (IOException e) {
144             LOGGER.error("Failed to walk through directories starting from: {}.", file.toString(), e);
145         }
146         return collection;
147     }
148
149     public static String getCommaSeparatedList(String[] list) {
150         return (list == null) || (list.length == 0) ? "" : getCommaSeparatedList(Arrays.asList(list));
151     }
152
153     public static String getCommaSeparatedList(List<?> list) {
154
155         if ((list == null) || list.isEmpty()) {
156             return "";
157         }
158
159         return list.stream().filter(o -> o != null && !o.toString().trim().isEmpty())
160                 .map(o -> o.toString().trim()).collect(Collectors.joining(","));
161     }
162
163     public static boolean isConfig(URL url) {
164         return isConfig(url.getFile());
165     }
166
167     public static boolean isConfig(String file) {
168         file = file.toUpperCase().substring(file.lastIndexOf('!') + 1);
169         file = file.substring(file.lastIndexOf('/') + 1);
170         return file.matches(
171                 String.format(
172                         CONFIG_REGEX_TPL_OPT_1,
173                         ConfigurationMode.OVERRIDE,
174                         ConfigurationMode.MERGE,
175                         ConfigurationMode.UNION,
176                         ConfigurationType.PROPERTIES.name(),
177                         ConfigurationType.XML.name(),
178                         ConfigurationType.JSON.name(),
179                         ConfigurationType.YAML.name()
180                 )
181         ) || file.matches(
182                 String.format(
183                         CONFIG_REGEX_TPL_OPT_2,
184                         ConfigurationType.PROPERTIES.name(),
185                         ConfigurationType.XML.name(),
186                         ConfigurationType.JSON.name(),
187                         ConfigurationType.YAML.name()
188                 )
189         );
190     }
191
192     public static boolean isConfig(File file) {
193         return file != null && file.exists() && isConfig(file.getName());
194     }
195
196     private static Optional<String> readNamespace(Configuration config) {
197         return ofNullable(config).flatMap(configuration -> ofNullable(configuration.getString(Constants.NAMESPACE_KEY)))
198                 .map(String::toUpperCase);
199     }
200
201     private static Optional<String> readMergeStrategy(Configuration config) {
202         return ofNullable(config).flatMap(configuration -> ofNullable(configuration.getString(Constants.MODE_KEY)))
203                 .map(String::toUpperCase);
204     }
205
206     public static ConfigurationMode getMergeStrategy(File file) {
207         Optional<ConfigurationMode> configurationMode =
208                 getConfiguration(file).flatMap(ConfigurationUtils::readMergeStrategy)
209                         .flatMap(ConfigurationUtils::convertConfigurationMode);
210         return configurationMode.orElseGet(() -> getMergeStrategy(file.getName().toUpperCase()));
211     }
212
213     public static ConfigurationMode getMergeStrategy(URL url) {
214         Optional<ConfigurationMode> configurationMode =
215                 getConfiguration(url).flatMap(ConfigurationUtils::readMergeStrategy)
216                         .flatMap(ConfigurationUtils::convertConfigurationMode);
217         return configurationMode.orElseGet(() -> getMergeStrategy(url.getFile().toUpperCase()));
218     }
219
220     public static ConfigurationMode getMergeStrategy(String file) {
221
222         file = file.toUpperCase().substring(file.lastIndexOf('!') + 1);
223         file = file.substring(file.lastIndexOf('/') + 1);
224         Pattern pattern = Pattern.compile(
225                 String.format(
226                         CONFIG_REGEX_TPL_OPT_1,
227                         ConfigurationMode.OVERRIDE,
228                         ConfigurationMode.MERGE,
229                         ConfigurationMode.UNION,
230                         ConfigurationType.PROPERTIES.name(),
231                         ConfigurationType.XML.name(),
232                         ConfigurationType.JSON.name(),
233                         ConfigurationType.YAML.name()
234                 )
235         );
236         Matcher matcher = pattern.matcher(file);
237         boolean b1 = matcher.matches();
238         if (b1) {
239             for (int i = 1; i <= matcher.groupCount(); i++) {
240                 String modeName = matcher.group(i);
241                 if (modeName != null) {
242                     modeName = modeName.substring(1);
243                 }
244                 try {
245                     return Enum.valueOf(ConfigurationMode.class, modeName);
246                 } catch (Exception exception) {
247                     LOGGER.debug("Configuration mode for merge strategy '{}' not found", modeName, exception);
248                 }
249             }
250         }
251
252         return null;
253     }
254
255     public static Optional<FileBasedConfiguration> getConfiguration(URL url) {
256
257         try {
258
259             ConfigurationType configType = ConfigurationUtils.getConfigType(url);
260             switch (configType) {
261                 case PROPERTIES:
262                     return Optional.of(new Configurations().fileBased(PropertiesConfiguration.class, url));
263                 case XML:
264                     return Optional.of(new Configurations().fileBased(XMLConfiguration.class, url));
265                 case JSON:
266                     return Optional.of(new Configurations().fileBased(JsonConfiguration.class, url));
267                 case YAML:
268                     return Optional.of(new Configurations().fileBased(YamlConfiguration.class, url));
269                 default:
270                     throw new ConfigurationException(CONFIGURATION_TYPE_NOT_SUPPORTED + configType);
271             }
272         } catch (ConfigurationException exception) {
273             LOGGER.error("Error reading configuration at {}.", url.toString(), exception);
274         }
275
276         return Optional.empty();
277     }
278
279     public static Optional<FileBasedConfiguration> getConfiguration(File file) {
280
281         try {
282             return getConfiguration(file.getAbsoluteFile().toURI().toURL());
283         } catch (MalformedURLException e) {
284             throw new IllegalStateException("Malformed URL: " + file.getAbsolutePath());
285         }
286     }
287
288     public static ConfigurationType getConfigType(File file) {
289         Objects.requireNonNull(file, "File cannot be null");
290         return Enum.valueOf(ConfigurationType.class,
291                 file.getAbsolutePath().substring(file.getAbsolutePath().lastIndexOf('.') + 1).toUpperCase());
292     }
293
294     public static ConfigurationType getConfigType(URL url) {
295         Objects.requireNonNull(url, "URL cannot be null");
296         return Enum.valueOf(ConfigurationType.class,
297                 url.getFile().substring(url.getFile().lastIndexOf('.') + 1).toUpperCase());
298     }
299
300     private static Optional<ConfigurationMode> convertConfigurationMode(String configMode) {
301         ConfigurationMode configurationMode = null;
302         try {
303             configurationMode = ConfigurationMode.valueOf(configMode);
304         } catch (Exception exception) {
305             LOGGER.error("Could not find convert {} into configuration mode.", configMode, exception);
306         }
307         return Optional.ofNullable(configurationMode);
308     }
309
310     public static Class getCollectionGenericType(Field field) {
311         Type type = field.getGenericType();
312
313         if (type instanceof ParameterizedType) {
314
315             ParameterizedType paramType = (ParameterizedType) type;
316             Type[] arr = paramType.getActualTypeArguments();
317             if (arr.length > 0) {
318                 Class<?> clazz = (Class<?>) arr[0];
319                 if (isWrapperClass(clazz)) {
320                     return clazz;
321                 } else {
322                     throw new IllegalArgumentException("Collection of type " + clazz.getName() + " not supported.");
323                 }
324             }
325         }
326
327         return String[].class;
328     }
329
330     public static boolean isWrapperClass(Class clazz) {
331         return clazz == String.class || clazz == Boolean.class || clazz == Character.class
332                 || Number.class.isAssignableFrom(clazz);
333     }
334
335     public static Class getArrayClass(Class clazz) {
336         return ARRAY_CLASS_MAP.getOrDefault(clazz, null);
337     }
338
339     public static List<URL> getAllClassPathResources() {
340         return CPScanner.scanResources(new ResourceFilter());
341     }
342
343     public static BasicConfigurationBuilder<FileBasedConfiguration> getConfigurationBuilder(File file) {
344         FileBasedConfigurationBuilder<FileBasedConfiguration> builder;
345         ConfigurationType configType = ConfigurationUtils.getConfigType(file);
346         builder = getFileBasedConfigurationBuilder(configType);
347         builder.configure(new Parameters().fileBased().setFile(file)
348                 .setListDelimiterHandler(new DefaultListDelimiterHandler(',')));
349         return builder;
350     }
351
352     public static BasicConfigurationBuilder<FileBasedConfiguration> getConfigurationBuilder(URL url) {
353         ConfigurationType configType = ConfigurationUtils.getConfigType(url);
354         ReloadingFileBasedConfigurationBuilder<FileBasedConfiguration> builder =
355                 getFileBasedConfigurationBuilder(configType);
356         builder.configure(
357                 new Parameters().fileBased().setURL(url).setListDelimiterHandler(new DefaultListDelimiterHandler(',')));
358         return builder;
359     }
360
361     private static ReloadingFileBasedConfigurationBuilder<FileBasedConfiguration> getFileBasedConfigurationBuilder(
362             ConfigurationType configType) {
363
364         ReloadingFileBasedConfigurationBuilder<FileBasedConfiguration> builder;
365         switch (configType) {
366             case PROPERTIES:
367                 builder = new ReloadingFileBasedConfigurationBuilder<>(PropertiesConfiguration.class);
368                 break;
369             case XML:
370                 builder = new ReloadingFileBasedConfigurationBuilder<>(XMLConfiguration.class);
371                 break;
372             case JSON:
373                 builder = new ReloadingFileBasedConfigurationBuilder<>(JsonConfiguration.class);
374                 break;
375             case YAML:
376                 builder = new ReloadingFileBasedConfigurationBuilder<>(YamlConfiguration.class);
377                 break;
378             default:
379                 throw new IllegalArgumentException(CONFIGURATION_TYPE_NOT_SUPPORTED + configType);
380         }
381         return builder;
382     }
383
384     public static <T> T read(Configuration config, Class<T> clazz, String keyPrefix) throws Exception {
385         Config confAnnotation = clazz.getAnnotation(Config.class);
386         if (confAnnotation != null) {
387             keyPrefix += (confAnnotation.key() + ".");
388         }
389         T objToReturn = clazz.newInstance();
390         for (Field field : clazz.getDeclaredFields()) {
391             Config fieldAnnotation = field.getAnnotation(Config.class);
392             if (fieldAnnotation != null) {
393                 field.setAccessible(true);
394                 field.set(objToReturn, config.getProperty(keyPrefix + fieldAnnotation.key()));
395             } else if (field.getType().getAnnotation(Config.class) != null) {
396                 field.set(objToReturn, read(config, field.getType(), keyPrefix));
397             }
398         }
399         return objToReturn;
400     }
401
402     public static Object getPrimitiveArray(Collection collection, Class clazz) {
403         switch (clazz.getName()) {
404             case "int":
405                 return getIntsPrimitiveArray(collection);
406             case "byte":
407                 return getBytesPrimitiveArray(collection);
408             case "short":
409                 return getShortsPrimitiveArray(collection);
410             case "long":
411                 return getLongsPrimitiveArray(collection);
412             case "float":
413                 return getFloatsPrimitiveArray(collection);
414             case "double":
415                 return getDoublesPrimitiveArray(collection);
416             case "boolean":
417                 return getBooleansPrimitiveArray(collection);
418             default:
419                 return null;
420         }
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             LOGGER.error("Error while getting '{}' content", path, exception);
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             LOGGER.error("Error while getting '{}' content", path.toString(), exception);
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                 String.format(
575                         CONFIG_REGEX_TPL_OPT_1,
576                         ConfigurationMode.OVERRIDE,
577                         ConfigurationMode.MERGE,
578                         ConfigurationMode.UNION,
579                         ConfigurationType.PROPERTIES.name(),
580                         ConfigurationType.XML.name(),
581                         ConfigurationType.JSON.name(),
582                         ConfigurationType.YAML.name()
583                 )
584         );
585         Matcher matcher = pattern.matcher(file);
586         boolean b1 = matcher.matches();
587         if (b1) {
588             if (matcher.group(1) != null) {
589                 String moduleName = matcher.group(1).substring(1);
590                 return moduleName.equalsIgnoreCase(ConfigurationMode.OVERRIDE.name()) || moduleName.equalsIgnoreCase(
591                         ConfigurationMode.UNION.name()) || moduleName.equalsIgnoreCase(ConfigurationMode.MERGE.name())
592                         ? Constants.DEFAULT_NAMESPACE : moduleName;
593             } else {
594                 return Constants.DEFAULT_NAMESPACE;
595             }
596         } else if (isConfig(file)) {
597             return Constants.DEFAULT_NAMESPACE;
598         }
599
600         return null;
601     }
602
603     public static String getNamespace(URL url) {
604
605         Optional<String> namespace =
606                 getConfiguration(url).flatMap(ConfigurationUtils::readNamespace).map(String::toUpperCase);
607
608         return namespace.orElseGet(() -> getNamespace(url.getFile().toUpperCase()));
609     }
610
611     public static Object getProperty(Configuration config, String key, int processingHints) {
612         if (!isDirectLookup(processingHints) && isNodeSpecific(processingHints) && (config instanceof CompositeConfiguration)) {
613             CompositeConfiguration conf = (CompositeConfiguration) config;
614             for (int i = 0; i < conf.getNumberOfConfigurations(); i++) {
615                 Object obj = conf.getConfiguration(i).getProperty(key);
616                 if (obj != null) {
617                     return obj;
618                 }
619             }
620         }
621         return config.getProperty(key);
622     }
623
624     public static boolean isCollection(String input) {
625         Pattern pattern = Pattern.compile("^\\[(.*)\\]$");
626         Matcher matcher = pattern.matcher(input);
627         return matcher.matches();
628     }
629
630     public static boolean isDirectLookup(int hints) {
631         return (hints & LATEST_LOOKUP.value()) == LATEST_LOOKUP.value();
632     }
633
634     public static boolean isNodeSpecific(int hints) {
635         return (hints & NODE_SPECIFIC.value()) == NODE_SPECIFIC.value();
636     }
637
638     public static boolean isExternalLookup(int hints) {
639         return (hints & EXTERNAL_LOOKUP.value()) == EXTERNAL_LOOKUP.value();
640     }
641
642     public static boolean isZeroLengthArray(Class clazz, Object obj) {
643         if (clazz.isArray() && clazz.getComponentType().isPrimitive()) {
644             if (clazz.getComponentType() == int.class) {
645                 return ((int[]) obj).length == 0;
646             } else if (clazz.getComponentType() == byte.class) {
647                 return ((byte[]) obj).length == 0;
648             } else if (clazz.getComponentType() == short.class) {
649                 return ((short[]) obj).length == 0;
650             } else if (clazz.getComponentType() == float.class) {
651                 return ((float[]) obj).length == 0;
652             } else if (clazz.getComponentType() == boolean.class) {
653                 return ((boolean[]) obj).length == 0;
654             } else if (clazz.getComponentType() == double.class) {
655                 return ((double[]) obj).length == 0;
656             } else if (clazz.getComponentType() == long.class) {
657                 return ((long[]) obj).length == 0;
658             } else {
659                 return ((Object[]) obj).length == 0;
660             }
661         }
662
663         return false;
664     }
665
666     public static boolean isBlank(String value) {
667         return value == null || value.trim().isEmpty();
668     }
669
670     // private methods section starts here
671
672     private static int[] getIntsPrimitiveArray(Collection collection) {
673         int collectionSize = collection.size();
674         int[] array = new int[collectionSize];
675         Object[] objArray = collection.toArray();
676         for (int i = 0; i < collectionSize; i++) {
677             array[i] = (int) objArray[i];
678         }
679         return array;
680     }
681
682     private static byte[] getBytesPrimitiveArray(Collection collection) {
683         int collectionSize = collection.size();
684         byte[] array = new byte[collectionSize];
685         Object[] objArray = collection.toArray();
686         for (int i = 0; i < collectionSize; i++) {
687             array[i] = (byte) objArray[i];
688         }
689         return array;
690     }
691
692     private static short[] getShortsPrimitiveArray(Collection collection) {
693         int collectionSize = collection.size();
694         short[] array = new short[collectionSize];
695         Object[] objArray = collection.toArray();
696         for (int i = 0; i < collectionSize; i++) {
697             array[i] = (short) objArray[i];
698         }
699         return array;
700     }
701
702     private static long[] getLongsPrimitiveArray(Collection collection) {
703         int collectionSize = collection.size();
704         long[] array = new long[collectionSize];
705         Object[] objArray = collection.toArray();
706         for (int i = 0; i < collectionSize; i++) {
707             array[i] = (long) objArray[i];
708         }
709         return array;
710     }
711
712     private static float[] getFloatsPrimitiveArray(Collection collection) {
713         int collectionSize = collection.size();
714         float[] array = new float[collectionSize];
715         Object[] objArray = collection.toArray();
716         for (int i = 0; i < collectionSize; i++) {
717             array[i] = (float) objArray[i];
718         }
719         return array;
720     }
721
722     private static double[] getDoublesPrimitiveArray(Collection collection) {
723         int collectionSize = collection.size();
724         double[] array = new double[collectionSize];
725         Object[] objArray = collection.toArray();
726         for (int i = 0; i < collectionSize; i++) {
727             array[i] = (double) objArray[i];
728         }
729         return array;
730     }
731
732     private static boolean[] getBooleansPrimitiveArray(Collection collection) {
733         int collectionSize = collection.size();
734         boolean[] array = new boolean[collectionSize];
735         Object[] objArray = collection.toArray();
736         for (int i = 0; i < collectionSize; i++) {
737             array[i] = (boolean) objArray[i];
738         }
739         return array;
740     }
741 }