e7a4c458dcff6139fdbcbb6d9c202dff8b3a1f74
[sdc.git] /
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     private static final String CONFIG_REGEX_TPL_OPT_1 = "CONFIG(-\\w*){0,1}(-(%s|%s|%s)){0,1}\\.(%s|%s|%s|%s)$";
93
94     private static final String CONFIG_REGEX_TPL_OPT_2 = "CONFIG(.)*\\.(%s|%s|%s|%s)$";
95
96     static {
97         Map<Class, Class> arrayTypes = new HashMap<>();
98         arrayTypes.put(Byte.class, Byte[].class);
99         arrayTypes.put(Short.class, Short[].class);
100         arrayTypes.put(Integer.class, Integer[].class);
101         arrayTypes.put(Long.class, Long[].class);
102         arrayTypes.put(Float.class, Float[].class);
103         arrayTypes.put(Double.class, Double[].class);
104         arrayTypes.put(Boolean.class, Boolean[].class);
105         arrayTypes.put(Character.class, Character[].class);
106         arrayTypes.put(String.class, String[].class);
107         ARRAY_CLASS_MAP = Collections.unmodifiableMap(arrayTypes);
108     }
109
110     private ConfigurationUtils() {
111         // prevent instantiation
112     }
113
114     public static Collection<File> getAllFiles(File file, boolean recursive, boolean onlyDirectory) {
115
116         ArrayList<File> collection = new ArrayList<>();
117         if (file.isDirectory() && file.exists()) {
118             File[] files = file.listFiles();
119             for (File innerFile : Objects.requireNonNull(files)) {
120                 if (innerFile.isFile() && !onlyDirectory) {
121                     collection.add(innerFile);
122                 } else if (innerFile.isDirectory()) {
123                     collection.add(innerFile);
124                     if (recursive) {
125                         collection.addAll(getAllFiles(innerFile, true, onlyDirectory));
126                     }
127                 }
128             }
129         }
130         return collection;
131     }
132
133     public static String getCommaSeparatedList(String[] list) {
134         return (list == null) || (list.length == 0) ? "" : getCommaSeparatedList(Arrays.asList(list));
135     }
136
137     public static String getCommaSeparatedList(List<?> list) {
138
139         if ((list == null) || list.isEmpty()) {
140             return "";
141         }
142
143         return list.stream().filter(o -> o != null && !o.toString().trim().isEmpty())
144                                          .map(o -> o.toString().trim()).collect(Collectors.joining(","));
145     }
146
147     public static boolean isConfig(URL url) {
148         return isConfig(url.getFile());
149     }
150
151     public static boolean isConfig(String file) {
152         file = file.toUpperCase().substring(file.lastIndexOf('!') + 1);
153         file = file.substring(file.lastIndexOf('/') + 1);
154         return file.matches(
155                 String.format(
156                         CONFIG_REGEX_TPL_OPT_1,
157                         ConfigurationMode.OVERRIDE,
158                         ConfigurationMode.MERGE,
159                         ConfigurationMode.UNION,
160                         ConfigurationType.PROPERTIES.name(),
161                         ConfigurationType.XML.name(),
162                         ConfigurationType.JSON.name(),
163                         ConfigurationType.YAML.name()
164                 )
165         ) || file.matches(
166                 String.format(
167                         CONFIG_REGEX_TPL_OPT_2,
168                         ConfigurationType.PROPERTIES.name(),
169                         ConfigurationType.XML.name(),
170                         ConfigurationType.JSON.name(),
171                         ConfigurationType.YAML.name()
172                 )
173         );
174     }
175
176     public static boolean isConfig(File file) {
177         return file != null && file.exists() && isConfig(file.getName());
178     }
179
180     private static Optional<String> readNamespace(Configuration config) {
181         return ofNullable(config).flatMap(configuration -> ofNullable(configuration.getString(Constants.NAMESPACE_KEY)))
182                        .map(String::toUpperCase);
183     }
184
185     private static Optional<String> readMergeStrategy(Configuration config) {
186         return ofNullable(config).flatMap(configuration -> ofNullable(configuration.getString(Constants.MODE_KEY)))
187                        .map(String::toUpperCase);
188     }
189
190     public static ConfigurationMode getMergeStrategy(File file) {
191         Optional<ConfigurationMode> configurationMode =
192                 getConfiguration(file).flatMap(ConfigurationUtils::readMergeStrategy)
193                         .flatMap(ConfigurationUtils::convertConfigurationMode);
194         return configurationMode.orElseGet(() -> getMergeStrategy(file.getName().toUpperCase()));
195     }
196
197     public static ConfigurationMode getMergeStrategy(URL url) {
198         Optional<ConfigurationMode> configurationMode =
199                 getConfiguration(url).flatMap(ConfigurationUtils::readMergeStrategy)
200                         .flatMap(ConfigurationUtils::convertConfigurationMode);
201         return configurationMode.orElseGet(() -> getMergeStrategy(url.getFile().toUpperCase()));
202     }
203
204     public static ConfigurationMode getMergeStrategy(String file) {
205
206         file = file.toUpperCase().substring(file.lastIndexOf('!') + 1);
207         file = file.substring(file.lastIndexOf('/') + 1);
208         Pattern pattern = Pattern.compile(
209                 String.format(
210                         CONFIG_REGEX_TPL_OPT_1,
211                         ConfigurationMode.OVERRIDE,
212                         ConfigurationMode.MERGE,
213                         ConfigurationMode.UNION,
214                         ConfigurationType.PROPERTIES.name(),
215                         ConfigurationType.XML.name(),
216                         ConfigurationType.JSON.name(),
217                         ConfigurationType.YAML.name()
218                 )
219         );
220         Matcher matcher = pattern.matcher(file);
221         boolean b1 = matcher.matches();
222         if (b1) {
223             for (int i = 1; i <= matcher.groupCount(); i++) {
224                 String modeName = matcher.group(i);
225                 if (modeName != null) {
226                     modeName = modeName.substring(1);
227                 }
228                 try {
229                     return Enum.valueOf(ConfigurationMode.class, modeName);
230                 } catch (Exception exception) {
231                     LOGGER.debug("Configuration mode for merge strategy '{}' not found", modeName, exception);
232                 }
233             }
234         }
235
236         return null;
237     }
238
239     public static Optional<FileBasedConfiguration> getConfiguration(URL url) {
240
241         try {
242
243             ConfigurationType configType = ConfigurationUtils.getConfigType(url);
244             switch (configType) {
245                 case PROPERTIES:
246                     return Optional.of(new Configurations().fileBased(PropertiesConfiguration.class, url));
247                 case XML:
248                     return Optional.of(new Configurations().fileBased(XMLConfiguration.class, url));
249                 case JSON:
250                     return Optional.of(new Configurations().fileBased(JsonConfiguration.class, url));
251                 case YAML:
252                     return Optional.of(new Configurations().fileBased(YamlConfiguration.class, url));
253                 default:
254                     throw new ConfigurationException(CONFIGURATION_TYPE_NOT_SUPPORTED + configType);
255             }
256         } catch (ConfigurationException exception) {
257             LOGGER.error("Error reading configuration at {}.", url.toString(), exception);
258         }
259
260         return Optional.empty();
261     }
262
263     public static Optional<FileBasedConfiguration> getConfiguration(File file) {
264
265         try {
266             return getConfiguration(file.getAbsoluteFile().toURI().toURL());
267         } catch (MalformedURLException e) {
268             throw new IllegalStateException("Malformed URL: " + file.getAbsolutePath());
269         }
270     }
271
272     public static ConfigurationType getConfigType(File file) {
273         Objects.requireNonNull(file, "File cannot be null");
274         return Enum.valueOf(ConfigurationType.class,
275                 file.getAbsolutePath().substring(file.getAbsolutePath().lastIndexOf('.') + 1).toUpperCase());
276     }
277
278     public static ConfigurationType getConfigType(URL url) {
279         Objects.requireNonNull(url, "URL cannot be null");
280         return Enum.valueOf(ConfigurationType.class,
281                 url.getFile().substring(url.getFile().lastIndexOf('.') + 1).toUpperCase());
282     }
283
284     private static Optional<ConfigurationMode> convertConfigurationMode(String configMode) {
285         ConfigurationMode configurationMode = null;
286         try {
287             configurationMode = ConfigurationMode.valueOf(configMode);
288         } catch (Exception exception) {
289             LOGGER.error("Could not find convert {} into configuration mode.", configMode, exception);
290         }
291         return Optional.ofNullable(configurationMode);
292     }
293
294     public static Class getCollectionGenericType(Field field) {
295         Type type = field.getGenericType();
296
297         if (type instanceof ParameterizedType) {
298
299             ParameterizedType paramType = (ParameterizedType) type;
300             Type[] arr = paramType.getActualTypeArguments();
301             if (arr.length > 0) {
302                 Class<?> clazz = (Class<?>) arr[0];
303                 if (isWrapperClass(clazz)) {
304                     return clazz;
305                 } else {
306                     throw new IllegalArgumentException("Collection of type " + clazz.getName() + " not supported.");
307                 }
308             }
309         }
310
311         return String[].class;
312     }
313
314     public static boolean isWrapperClass(Class clazz) {
315         return clazz == String.class || clazz == Boolean.class || clazz == Character.class
316                        || Number.class.isAssignableFrom(clazz);
317     }
318
319     public static Class getArrayClass(Class clazz) {
320         return ARRAY_CLASS_MAP.getOrDefault(clazz, null);
321     }
322
323     public static List<URL> getAllClassPathResources() {
324         return CPScanner.scanResources(new ResourceFilter());
325     }
326
327     public static BasicConfigurationBuilder<FileBasedConfiguration> getConfigurationBuilder(File file) {
328         FileBasedConfigurationBuilder<FileBasedConfiguration> builder;
329         ConfigurationType configType = ConfigurationUtils.getConfigType(file);
330         builder = getFileBasedConfigurationBuilder(configType);
331         builder.configure(new Parameters().fileBased().setFile(file)
332                                   .setListDelimiterHandler(new DefaultListDelimiterHandler(',')));
333         return builder;
334     }
335
336     public static BasicConfigurationBuilder<FileBasedConfiguration> getConfigurationBuilder(URL url) {
337         ConfigurationType configType = ConfigurationUtils.getConfigType(url);
338         ReloadingFileBasedConfigurationBuilder<FileBasedConfiguration> builder =
339                 getFileBasedConfigurationBuilder(configType);
340         builder.configure(
341                 new Parameters().fileBased().setURL(url).setListDelimiterHandler(new DefaultListDelimiterHandler(',')));
342         return builder;
343     }
344
345     private static ReloadingFileBasedConfigurationBuilder<FileBasedConfiguration> getFileBasedConfigurationBuilder(
346             ConfigurationType configType) {
347
348         ReloadingFileBasedConfigurationBuilder<FileBasedConfiguration> builder;
349         switch (configType) {
350             case PROPERTIES:
351                 builder = new ReloadingFileBasedConfigurationBuilder<>(PropertiesConfiguration.class);
352                 break;
353             case XML:
354                 builder = new ReloadingFileBasedConfigurationBuilder<>(XMLConfiguration.class);
355                 break;
356             case JSON:
357                 builder = new ReloadingFileBasedConfigurationBuilder<>(JsonConfiguration.class);
358                 break;
359             case YAML:
360                 builder = new ReloadingFileBasedConfigurationBuilder<>(YamlConfiguration.class);
361                 break;
362             default:
363                 throw new IllegalArgumentException(CONFIGURATION_TYPE_NOT_SUPPORTED + configType);
364         }
365         return builder;
366     }
367
368     public static <T> T read(Configuration config, Class<T> clazz, String keyPrefix) throws Exception {
369         Config confAnnotation = clazz.getAnnotation(Config.class);
370         if (confAnnotation != null) {
371             keyPrefix += (confAnnotation.key() + ".");
372         }
373         T objToReturn = clazz.newInstance();
374         for (Field field : clazz.getDeclaredFields()) {
375             Config fieldAnnotation = field.getAnnotation(Config.class);
376             if (fieldAnnotation != null) {
377                 field.setAccessible(true);
378                 field.set(objToReturn, config.getProperty(keyPrefix + fieldAnnotation.key()));
379             } else if (field.getType().getAnnotation(Config.class) != null) {
380                 field.set(objToReturn, read(config, field.getType(), keyPrefix));
381             }
382         }
383         return objToReturn;
384     }
385
386     public static Object getPrimitiveArray(Collection collection, Class clazz) {
387         switch (clazz.getName()) {
388             case "int":
389                 return getIntsPrimitiveArray(collection);
390             case "byte":
391                 return getBytesPrimitiveArray(collection);
392             case "short":
393                 return getShortsPrimitiveArray(collection);
394             case "long":
395                 return getLongsPrimitiveArray(collection);
396             case "float":
397                 return getFloatsPrimitiveArray(collection);
398             case "double":
399                 return getDoublesPrimitiveArray(collection);
400             case "boolean":
401                 return getBooleansPrimitiveArray(collection);
402             default:
403                 return null;
404         }
405     }
406
407     public static String getCollectionString(String input) {
408         Pattern pattern = Pattern.compile("^\\[(.*)\\]$");
409         Matcher matcher = pattern.matcher(input);
410         if (matcher.matches()) {
411             input = matcher.group(1);
412         }
413         return input;
414     }
415
416     public static String processVariablesIfPresent(String tenant, String namespace, String data) {
417         Pattern pattern = Pattern.compile("^.*\\$\\{(.*)\\}.*");
418         Matcher matcher = pattern.matcher(data);
419         if (matcher.matches()) {
420             final int substringStartIndex = 4;
421             String key = matcher.group(1);
422             String value;
423             if (key.toUpperCase().startsWith("ENV:")) {
424                 value = System.getenv(key.substring(substringStartIndex));
425             } else if (key.toUpperCase().startsWith("SYS:")) {
426                 value = System.getProperty(key.substring(substringStartIndex));
427             } else {
428                 value = ConfigurationUtils.getCollectionString(
429                         ConfigurationManager.lookup().getAsStringValues(tenant, namespace, key).toString());
430             }
431             return processVariablesIfPresent(tenant, namespace, data.replaceAll("\\$\\{" + key + "}",
432                     value == null ? "" : value.replace("\\", "\\\\")));
433         } else {
434             return data;
435         }
436     }
437
438     public static String getFileContents(String path) {
439         try {
440             if (path != null) {
441                 return IOUtils.toString(new URL(path), Charset.defaultCharset());
442             }
443         } catch (Exception exception) {
444             LOGGER.error("Error while getting '{}' content", path, exception);
445         }
446         return null;
447     }
448
449     public static String getFileContents(Path path) {
450         try {
451             if (path != null) {
452                 return new String(Files.readAllBytes(path));
453             }
454         } catch (Exception exception) {
455             LOGGER.error("Error while getting '{}' content", path.toString(), exception);
456         }
457         return null;
458     }
459
460     public static Object getDefaultFor(Class clazz) {
461         if (byte.class == clazz) {
462             return new Byte("0");
463         } else if (short.class == clazz) {
464             return new Short("0");
465         } else if (int.class == clazz) {
466             return new Integer("0");
467         } else if (float.class == clazz) {
468             return new Float("0");
469         } else if (long.class == clazz) {
470             return new Long("0");
471         } else if (double.class == clazz) {
472             return new Double("0");
473         } else if (boolean.class == clazz) {
474             return Boolean.FALSE;
475         }
476         return (char) 0;
477     }
478
479     public static Collection getCompatibleCollectionForAbstractDef(Class clazz) {
480         if (TransferQueue.class.isAssignableFrom(clazz)) {
481             return getConcreteCollection(TransferQueue.class);
482         }
483         if (BlockingQueue.class.isAssignableFrom(clazz)) {
484             return getConcreteCollection(BlockingQueue.class);
485         }
486         if (Deque.class.isAssignableFrom(clazz)) {
487             return getConcreteCollection(Deque.class);
488         }
489         if (Queue.class.isAssignableFrom(clazz)) {
490             return getConcreteCollection(Queue.class);
491         }
492         if (SortedSet.class.isAssignableFrom(clazz)) {
493             return getConcreteCollection(SortedSet.class);
494         }
495         if (Set.class.isAssignableFrom(clazz)) {
496             return getConcreteCollection(Set.class);
497         }
498         if (List.class.isAssignableFrom(clazz)) {
499             return getConcreteCollection(List.class);
500         }
501         throw new IllegalArgumentException("Only corresponding array classes and any are allowed as argument."
502                 + "assignable from TransferQueue, BlockingQueue, Deque, Queue, SortedSet, Set, List class");
503     }
504
505     public static Collection getConcreteCollection(Class clazz) {
506         switch (clazz.getName()) {
507             case "java.util.Collection":
508             case "java.util.List":
509                 return new ArrayList<>();
510             case "java.util.Set":
511                 return new HashSet<>();
512             case "java.util.SortedSet":
513                 return new TreeSet<>();
514             case "java.util.Queue":
515                 return new ConcurrentLinkedQueue<>();
516             case "java.util.Deque":
517                 return new ArrayDeque<>();
518             case "java.util.concurrent.TransferQueue":
519                 return new LinkedTransferQueue<>();
520             case "java.util.concurrent.BlockingQueue":
521                 return new LinkedBlockingQueue<>();
522             default:
523                 throw new IllegalArgumentException("Only corresponding array classes and any are allowed as argument."
524                         + "assignable from TransferQueue, BlockingQueue, Deque, Queue, SortedSet, Set, List class");
525         }
526     }
527
528     public static String getConfigurationRepositoryKey(File file) {
529         return getConfigurationRepositoryKey(
530                 ConfigurationUtils.getNamespace(file).split(Constants.TENANT_NAMESPACE_SEPARATOR));
531     }
532
533     public static String getConfigurationRepositoryKey(URL url) {
534         return getConfigurationRepositoryKey(
535                 ConfigurationUtils.getNamespace(url).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::readNamespace).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                 String.format(
559                         CONFIG_REGEX_TPL_OPT_1,
560                         ConfigurationMode.OVERRIDE,
561                         ConfigurationMode.MERGE,
562                         ConfigurationMode.UNION,
563                         ConfigurationType.PROPERTIES.name(),
564                         ConfigurationType.XML.name(),
565                         ConfigurationType.JSON.name(),
566                         ConfigurationType.YAML.name()
567                 )
568         );
569         Matcher matcher = pattern.matcher(file);
570         boolean b1 = matcher.matches();
571         if (b1) {
572             if (matcher.group(1) != null) {
573                 String moduleName = matcher.group(1).substring(1);
574                 return moduleName.equalsIgnoreCase(ConfigurationMode.OVERRIDE.name()) || moduleName.equalsIgnoreCase(
575                         ConfigurationMode.UNION.name()) || moduleName.equalsIgnoreCase(ConfigurationMode.MERGE.name())
576                                ? Constants.DEFAULT_NAMESPACE : moduleName;
577             } else {
578                 return Constants.DEFAULT_NAMESPACE;
579             }
580         } else if (isConfig(file)) {
581             return Constants.DEFAULT_NAMESPACE;
582         }
583
584         return null;
585     }
586
587     public static String getNamespace(URL url) {
588
589         Optional<String> namespace =
590                 getConfiguration(url).flatMap(ConfigurationUtils::readNamespace).map(String::toUpperCase);
591
592         return namespace.orElseGet(() -> getNamespace(url.getFile().toUpperCase()));
593     }
594
595     public static Object getProperty(Configuration config, String key, int processingHints) {
596
597         if (!isDirectLookup(processingHints) && (config instanceof CompositeConfiguration)) {
598
599             CompositeConfiguration conf = (CompositeConfiguration) config;
600             for (int i = 0; i < conf.getNumberOfConfigurations(); i++) {
601
602                 if (isNodeSpecific(processingHints)) {
603                     Object obj = conf.getConfiguration(i).getProperty(key);
604                     if (obj != null) {
605                         return obj;
606                     }
607                 }
608             }
609         }
610
611         return config.getProperty(key);
612     }
613
614     public static boolean isCollection(String input) {
615         Pattern pattern = Pattern.compile("^\\[(.*)\\]$");
616         Matcher matcher = pattern.matcher(input);
617         return matcher.matches();
618     }
619
620     public static boolean isDirectLookup(int hints) {
621         return (hints & LATEST_LOOKUP.value()) == LATEST_LOOKUP.value();
622     }
623
624     public static boolean isNodeSpecific(int hints) {
625         return (hints & NODE_SPECIFIC.value()) == NODE_SPECIFIC.value();
626     }
627
628     public static boolean isExternalLookup(int hints) {
629         return (hints & EXTERNAL_LOOKUP.value()) == EXTERNAL_LOOKUP.value();
630     }
631
632     public static boolean isZeroLengthArray(Class clazz, Object obj) {
633         if (clazz.isArray() && clazz.getComponentType().isPrimitive()) {
634             if (clazz.getComponentType() == int.class) {
635                 return ((int[]) obj).length == 0;
636             } else if (clazz.getComponentType() == byte.class) {
637                 return ((byte[]) obj).length == 0;
638             } else if (clazz.getComponentType() == short.class) {
639                 return ((short[]) obj).length == 0;
640             } else if (clazz.getComponentType() == float.class) {
641                 return ((float[]) obj).length == 0;
642             } else if (clazz.getComponentType() == boolean.class) {
643                 return ((boolean[]) obj).length == 0;
644             } else if (clazz.getComponentType() == double.class) {
645                 return ((double[]) obj).length == 0;
646             } else if (clazz.getComponentType() == long.class) {
647                 return ((long[]) obj).length == 0;
648             } else {
649                 return ((Object[]) obj).length == 0;
650             }
651         }
652
653         return false;
654     }
655
656     public static boolean isBlank(String value) {
657         return value == null || value.trim().isEmpty();
658     }
659
660     // private methods section starts here
661
662     private static int[] getIntsPrimitiveArray(Collection collection) {
663         int collectionSize = collection.size();
664         int[] array = new int[collectionSize];
665         Object[] objArray = collection.toArray();
666         for (int i = 0; i < collectionSize; i++) {
667             array[i] = (int) objArray[i];
668         }
669         return array;
670     }
671
672     private static byte[] getBytesPrimitiveArray(Collection collection) {
673         int collectionSize = collection.size();
674         byte[] array = new byte[collectionSize];
675         Object[] objArray = collection.toArray();
676         for (int i = 0; i < collectionSize; i++) {
677             array[i] = (byte) objArray[i];
678         }
679         return array;
680     }
681
682     private static short[] getShortsPrimitiveArray(Collection collection) {
683         int collectionSize = collection.size();
684         short[] array = new short[collectionSize];
685         Object[] objArray = collection.toArray();
686         for (int i = 0; i < collectionSize; i++) {
687             array[i] = (short) objArray[i];
688         }
689         return array;
690     }
691
692     private static long[] getLongsPrimitiveArray(Collection collection) {
693         int collectionSize = collection.size();
694         long[] array = new long[collectionSize];
695         Object[] objArray = collection.toArray();
696         for (int i = 0; i < collectionSize; i++) {
697             array[i] = (long) objArray[i];
698         }
699         return array;
700     }
701
702     private static float[] getFloatsPrimitiveArray(Collection collection) {
703         int collectionSize = collection.size();
704         float[] array = new float[collectionSize];
705         Object[] objArray = collection.toArray();
706         for (int i = 0; i < collectionSize; i++) {
707             array[i] = (float) objArray[i];
708         }
709         return array;
710     }
711
712     private static double[] getDoublesPrimitiveArray(Collection collection) {
713         int collectionSize = collection.size();
714         double[] array = new double[collectionSize];
715         Object[] objArray = collection.toArray();
716         for (int i = 0; i < collectionSize; i++) {
717             array[i] = (double) objArray[i];
718         }
719         return array;
720     }
721
722     private static boolean[] getBooleansPrimitiveArray(Collection collection) {
723         int collectionSize = collection.size();
724         boolean[] array = new boolean[collectionSize];
725         Object[] objArray = collection.toArray();
726         for (int i = 0; i < collectionSize; i++) {
727             array[i] = (boolean) objArray[i];
728         }
729         return array;
730     }
731 }