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