Change nexus values to properties
[appc.git] / app-c / appc / appc-common / src / main / java / org / openecomp / appc / util / StringHelper.java
1 /*-
2  * ============LICENSE_START=======================================================
3  * openECOMP : APP-C
4  * ================================================================================
5  * Copyright (C) 2017 AT&T Intellectual Property. All rights
6  *                                              reserved.
7  * ================================================================================
8  * Licensed under the Apache License, Version 2.0 (the "License");
9  * you may not use this file except in compliance with the License.
10  * You may obtain a copy of the License at
11  * 
12  *      http://www.apache.org/licenses/LICENSE-2.0
13  * 
14  * Unless required by applicable law or agreed to in writing, software
15  * distributed under the License is distributed on an "AS IS" BASIS,
16  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17  * See the License for the specific language governing permissions and
18  * limitations under the License.
19  * ============LICENSE_END=========================================================
20  */
21
22
23
24 package org.openecomp.appc.util;
25
26 import java.util.Date;
27 import java.util.List;
28 import java.util.Map;
29 import java.util.Properties;
30 import java.util.Set;
31 import java.util.regex.Matcher;
32 import java.util.regex.Pattern;
33
34 /**
35  * This class contains several static helper methods that can be used to perform string manipulation algorithms.
36  * 
37  */
38
39 public final class StringHelper {
40
41     public static final String DASH = "-";
42     public static final String DOT = ".";
43     public static final String ELLIPSES = "...";
44     public static final String LINE_FEED = "\n";
45     public static final String SLASH = "/";
46     public static final String COMMA = ",";
47
48     /**
49      * Converts the specified string pattern to a regular expression string. If the supplied string is null or empty,
50      * then a regular expression that matches all strings (.*) is returned.
51      * <p>
52      * The expression passed to this method should not already be a regular expression. If it contains problematic
53      * meta-characters for this routine (such as period, asterisk, and plus), they will be escaped and matched literally
54      * in the resulting regular expression returned.
55      * </p>
56      * 
57      * @param value
58      *            The pattern that we need to convert to a regular expression
59      * @return The regular expression that is equivalent to the pattern
60      */
61     public static String convertToRegex(String value) {
62         if (value == null || value.trim().length() == 0) {
63             return ".*";
64         }
65         boolean appendEOL = false;
66         StringBuffer buffer = new StringBuffer(value.trim());
67
68         /*
69          * If there are any period characters, we need to escape them so that they are exactly matched
70          */
71         Pattern pattern = Pattern.compile("\\.");
72         Matcher matcher = pattern.matcher(buffer);
73         int position = 0;
74         while (matcher.find(position)) {
75             buffer.replace(matcher.start(), matcher.end(), "\\.");
76             position = matcher.end() + 1;
77         }
78
79         /*
80          * If there are any asterisks or pluses, which we need to interpret as wildcard characters, we need to convert
81          * them into .* or .
82          */
83         pattern = Pattern.compile("\\*|\\+");
84         matcher = pattern.matcher(buffer);
85         position = 0;
86         while (matcher.find(position)) {
87             String metachar = buffer.substring(matcher.start(), matcher.end());
88             if (metachar.equals("*")) {
89                 buffer.replace(matcher.start(), matcher.end(), ".*");
90                 position = matcher.end() + 1;
91                 if (matcher.end() < buffer.length() - 1) {
92                     appendEOL = true;
93                 }
94             } else if (metachar.equals("+")) {
95                 buffer.replace(matcher.start(), matcher.end(), ".");
96                 position = matcher.end();
97                 if (matcher.end() == buffer.length()) {
98                     appendEOL = true;
99                 }
100             }
101         }
102
103         /*
104          * If the string contains a .* meta-character sequence anywhere in the middle of the string (i.e., there are
105          * other characters following the .* meta-characters), OR the string ends with the .+ sequence, then we need to
106          * append the "end-of-line" boundary condition to the end of the string to get predictable results.
107          */
108         if (appendEOL) {
109             buffer.append("$");
110         }
111         return buffer.toString();
112     }
113
114     /**
115      * Takes a string that may possibly be very long and return a string that is at most maxLength. If the string is
116      * longer than maxLength, the last three characters will be the ellipses (...) to indicate that the string was
117      * shortened.
118      * 
119      * @param possiblyLongString
120      * @param maxLength
121      *            must be at least 4 (one character plus ellipses)
122      * @return possibly shortened string
123      */
124     public static String getShortenedString(String possiblyLongString, int maxLength) {
125         if ((possiblyLongString != null) && (maxLength > ELLIPSES.length())
126             && (possiblyLongString.length() > maxLength)) {
127             return possiblyLongString.substring(0, maxLength - ELLIPSES.length()) + ELLIPSES;
128
129         }
130         return possiblyLongString;
131     }
132
133     /**
134      * Determines that a provided string is not null and not empty (length = 0 after trimming)
135      * 
136      * @param theString
137      *            The string to be tested
138      * @return true if the string IS NOT null and is NOT empty
139      */
140     public static boolean isNotNullNotEmpty(String theString) {
141         return ((theString != null) && (!theString.trim().isEmpty()));
142     }
143
144     /**
145      * Determines that a provided string IS null or an empty string (length = 0 after trimming)
146      * 
147      * @param theString
148      *            The string to be tested
149      * @return true if the string IS null OR is empty
150      */
151     public static boolean isNullOrEmpty(String theString) {
152         return ((theString == null) || (theString.trim().isEmpty()));
153     }
154
155     /**
156      * Returns an indication if the first string is equal to the second string, allowing for either or both strings to
157      * be null.
158      * 
159      * @param a
160      *            The first string to be compared
161      * @param b
162      *            The second string to be compared
163      * @return True if both strings are null, or both strings are non-null AND they are equal. False otherwise.
164      */
165     public static boolean equals(String a, String b) {
166         return equals(a, b, false);
167     }
168
169     /**
170      * Returns an indication if the first string is equal to the second string, allowing for either or both strings to
171      * be null, and ignoring case.
172      * 
173      * @param a
174      *            The first string to be compared
175      * @param b
176      *            The second string to be compared
177      * @return True if both strings are null, or both strings are non-null AND they are equal (without regard to case).
178      *         False otherwise.
179      */
180     public static boolean equalsIgnoreCase(String a, String b) {
181         return equals(a, b, true);
182     }
183
184     /**
185      * Compares two strings (allowing either or both to be null), and allowing for optional case sensitive or
186      * insensitive comparison.
187      * 
188      * @param a
189      *            The first string to be compared
190      * @param b
191      *            The second string to be compared
192      * @param caseInsensitive
193      *            True if the comparison is to be case in-sensitive.
194      * @return True if both strings are null, or both strings are non-null and they are equal
195      */
196     private static boolean equals(String a, String b, boolean caseInsensitive) {
197         if (a == null && b == null) {
198             return true;
199         }
200         if (a != null && b != null) {
201             if (caseInsensitive) {
202                 return a.equalsIgnoreCase(b);
203             } else {
204                 return a.equals(b);
205             }
206         }
207
208         return false;
209     }
210
211     /**
212      * This method is used to mangle a name.
213      * <p>
214      * This method will first remove all unacceptable characters from the name and translate all characters to lower
215      * case. This is done to eliminate any potentially troublesome characters. If the resulting string is empty, then a
216      * random string of characters for the minimum desired length is returned. If the string is too short to meet the
217      * minimum length requirement, it is padded with random characters.
218      * </p>
219      * <p>
220      * Once the string has been scrubbed and possibly padded, it may be truncated (if longer than the maximum value) and
221      * the result is returned. To make the string as unique as possible, the algorithm removes excess letters from the
222      * center of the string, concatenating the first nad last parts of the name together. The assumption is that users
223      * tend to start the names of multiple things in similar ways, and get more descriptive as the name progresses. If
224      * for example, several objects were named "A test Object", "A test Object1", and "A test Object2", shortening the
225      * name only from the left does not generate a unique name.
226      * </p>
227      * 
228      * @param name
229      *            The name to be mangled
230      * @param minLen
231      *            minimum number of characters for the name
232      * @param maxLen
233      *            maximum number of characters for the name
234      * @return The mangled name, or an empty string if the value is null or an empty string.
235      */
236     public static String mangleName(String name, int minLen, int maxLen) {
237         StringBuffer buffer = new StringBuffer(name == null ? "" : name);
238         Pattern pattern = Pattern.compile("[^a-z0-9]+", Pattern.CASE_INSENSITIVE);
239         Matcher matcher = pattern.matcher(buffer);
240         int position = 0;
241         while (matcher.find(position)) {
242             buffer.delete(matcher.start(), matcher.end());
243             position = matcher.start();
244         }
245
246         if (buffer.length() < minLen) {
247             for (int i = buffer.length(); i <= minLen; i++) {
248                 buffer.append("A");
249             }
250         }
251
252         /*
253          * Remove out of the center of the name to preserve start and end and result in a string of max len
254          */
255         if (buffer.length() > maxLen) {
256             int excess = buffer.length() - maxLen;
257             int left = maxLen / 2;
258
259             buffer.delete(left, excess + left);
260         }
261
262         return buffer.toString().toLowerCase();
263     }
264
265     /**
266      * This method is used to normalize a string value.
267      * <p>
268      * This method will ensure that the string value is trimmed of all leading and trailing whitespace if not null. If
269      * it is null or an empty string, then it will return null.
270      * </p>
271      * 
272      * @param value
273      *            The value to be normalized
274      * @return The normalized (no leading or trailing whitespace) value, or null if the string was null or an empty
275      *         string (or all whitespace). This method will never return an empty string.
276      */
277     public static String normalizeString(String value) {
278         if (value != null) {
279             String temp = value.trim();
280             if (temp.length() > 0) {
281                 return temp;
282             }
283         }
284         return null;
285     }
286
287     /**
288      * This method is used to strip all carriage returns and line feed characters from a string
289      * 
290      * @param value
291      * @return The original value less all carriage returns and line feeds
292      */
293     public static String stripCRLF(String value) {
294
295         if (value == null) {
296             return null;
297         }
298         String[] tokens = value.split("\r\n|\n\r|\r|\n");
299         StringBuffer buffer = new StringBuffer();
300         for (String token : tokens) {
301             buffer.append(token.trim());
302         }
303         return buffer.toString();
304     }
305
306     /**
307      * Converts UNIX-style line endings to DOS-style. Replaces LF with CR+LF as long as the LF does not already exist
308      * paired with a CR.
309      * 
310      * @param content
311      *            The content to be converted
312      * @return The converted content.
313      */
314     public static String toDOSLines(String content) {
315         if (content == null) {
316             return null;
317         }
318
319         StringBuffer buffer = new StringBuffer(content);
320         Pattern pattern = Pattern.compile("^(\n)[^\r]|[^\r](\n)[^\r]|[^\r](\n)$");
321         Matcher matcher = pattern.matcher(buffer);
322         int position = 0;
323         while (matcher.find(position)) {
324             int index = matcher.start(1);
325             if (index == -1) {
326                 index = matcher.start(2);
327             }
328             if (index == -1) {
329                 index = matcher.start(3);
330             }
331
332             buffer.replace(index, index + 1, "\r\n");
333             position = index + 1;
334         }
335
336         return buffer.toString();
337     }
338
339     /**
340      * This method will convert a string contents to use the UNIX-style line endings. That is, all occurrences of CR
341      * (Carriage Return) and LF (Line Feed) are reduced to just use LF.
342      * 
343      * @param content
344      *            The buffer to be processed
345      * @return The converted contents
346      */
347     public static String toUnixLines(String content) {
348         if (content == null) {
349             return null;
350         }
351
352         StringBuffer buffer = new StringBuffer(content);
353         Pattern pattern = Pattern.compile("\r\n|\n\r");
354         Matcher matcher = pattern.matcher(buffer);
355         int position = 0;
356         while (matcher.find(position)) {
357             buffer.replace(matcher.start(), matcher.end(), "\n");
358             position = matcher.start();
359         }
360
361         return buffer.toString();
362     }
363
364     /**
365      * This method is used to translate characters in the input sequence that match the characters in the match list to
366      * the corresponding character in the replacement list. If the replacement list is shorter than the match list, then
367      * the character from the replacement list is taken as the modulo of the match character position and the length of
368      * the replacement list.
369      * 
370      * @param sequence
371      *            The input sequence to be processed
372      * @param match
373      *            The list of matching characters to be searched
374      * @param replacement
375      *            The list of replacement characters, positional coincident with the match list. If shorter than the
376      *            match list, then the position "wraps" around on the replacement list.
377      * @return The translated string contents.
378      */
379     public static Object translate(String sequence, String match, String replacement) {
380
381         if (sequence == null) {
382             return sequence;
383         }
384
385         StringBuffer buffer = new StringBuffer(sequence);
386
387         for (int index = 0; index < buffer.length(); index++) {
388             char ch = buffer.charAt(index);
389
390             int position = match.indexOf(ch);
391             if (position == -1) {
392                 continue;
393             }
394
395             if (position >= replacement.length()) {
396                 position %= replacement.length();
397             }
398             buffer.setCharAt(index, replacement.charAt(position));
399         }
400
401         return buffer.toString();
402     }
403
404     /**
405      * Ensures that the name provided is a valid identifier. This means that no spaces are allowed as well as special
406      * characters. This method translates all spaces and illegal characters to underscores (_).
407      * 
408      * @param name
409      *            The name to be checked and converted to an identifier if needed
410      * @return The valid identifier from the name
411      */
412     public static String validIdentifier(String name) {
413         if (name == null || name.length() == 0) {
414             return name;
415         }
416
417         StringBuffer buffer = new StringBuffer(name);
418         for (int index = 0; index < buffer.length(); index++) {
419             char ch = buffer.charAt(index);
420
421             if ((index == 0 && !Character.isJavaIdentifierStart(ch)) || (!Character.isJavaIdentifierPart(ch))) {
422                 buffer.setCharAt(index, '_');
423             }
424         }
425         return buffer.toString();
426     }
427
428     /**
429      * This method verifies that the provided string only contains characters from the legal set, and replaces any
430      * character not in the legal set with the specified replacement character.
431      * 
432      * @param sequence
433      *            The sequence to be verified
434      * @param legal
435      *            The set of all legal characters
436      * @param replacement
437      *            The replacement character if a character is not in the legal set
438      * @return The verified *and possibly updated) string
439      */
440     public static String verify(String sequence, String legal, char replacement) {
441         if (sequence == null) {
442             return sequence;
443         }
444
445         StringBuffer buffer = new StringBuffer(sequence);
446         for (int index = 0; index < buffer.length(); index++) {
447             char ch = buffer.charAt(index);
448             if (legal.indexOf(ch) == -1) {
449                 buffer.setCharAt(index, replacement);
450             }
451         }
452         return buffer.toString();
453     }
454
455     /**
456      * Private constructor to prevent instantiation of this class - All methods are static!
457      */
458     private StringHelper() {
459
460     }
461
462     /**
463      * @param list
464      *            The list of elements
465      * @return The list of elements formatted as a comma-delimited list
466      */
467     public static String asList(List<String> list) {
468         StringBuffer buffer = new StringBuffer();
469         if (list != null) {
470             if (list.size() == 1) {
471                 buffer.append(list.get(0));
472             } else {
473                 for (String element : list) {
474                     buffer.append(element);
475                     buffer.append(", ");
476                 }
477
478                 if (buffer.length() > 2) {
479                     buffer.delete(buffer.length() - 2, buffer.length());
480                 }
481             }
482         }
483         return buffer.toString();
484     }
485
486     /**
487      * @param map
488      *            A map of strings
489      * @return A map expressed as a comma-delimited list of name=value tuples
490      */
491     public static String asList(Map<String, String> map) {
492         StringBuffer buffer = new StringBuffer();
493         if (map != null) {
494             Set<String> keys = map.keySet();
495             for (String key : keys) {
496                 buffer.append(String.format("%s=%s, ", key, map.get(key)));
497             }
498
499             if (buffer.length() > 2) {
500                 buffer.delete(buffer.length() - 2, buffer.length());
501             }
502         }
503         return buffer.toString();
504     }
505
506     /**
507      * @param values
508      *            An array or varargs of Strings to be concatenated into a comma-separated list
509      * @return The comma-seprated list of values
510      */
511     public static String asList(String... values) {
512         StringBuilder builder = new StringBuilder();
513         builder.append('[');
514         if (values != null && values.length > 0) {
515             int count = values.length;
516             for (int index = 0; index < count - 1; index++) {
517                 builder.append(values[index]);
518                 builder.append(',');
519             }
520             builder.append(values[count - 1]);
521         }
522         builder.append(']');
523         return builder.toString();
524     }
525
526     public static Object resolveToType(String input) {
527         String intRegex = "^(\\-)?[0-9]+$";
528         String doubleRegex = "^(\\-)?[0-9\\.]+$";
529         String boolRegex = "(^(?i)((true)|(false))$)";
530
531         // Check for null
532         if (input == null) {
533             return null;
534         }
535
536         // Check int first
537         if (input.matches(intRegex)) {
538             try {
539                 return Integer.parseInt(input);
540             } catch (NumberFormatException nfe) {
541                 // Should not happen
542                 nfe.printStackTrace();
543             }
544         }
545
546         // Check double (int + decimal point)
547         if (input.matches(doubleRegex)) {
548             try {
549                 return Double.parseDouble(input);
550             } catch (NumberFormatException | NullPointerException e) {
551                 // NPE won't happen bc of regex check
552             }
553         }
554
555         // Check boolean
556         if (input.matches(boolRegex)) {
557             return Boolean.parseBoolean(input);
558         }
559
560         // Try to parse a date
561         Date date = Time.utcParse(input);
562         if (date != null) {
563             return date;
564         }
565
566         // No special type, return string
567         return input;
568     }
569
570     /**
571      * Converts a properties object to a string in the format of <pre>[ key=value, key=value, ... ]</pre>
572      *
573      * @param props
574      *            The properties object to format
575      * @return A string in the format <pre>[ key=value, ... ]</pre> or null if the input was null
576      */
577     public static String propertiesToString(Properties props) {
578         if (props == null) {
579             return null;
580         }
581         StringBuilder out = new StringBuilder();
582         out.append("[");
583         for (Object key : props.keySet()) {
584             out.append(String.format(" %s = %s,", key.toString(), props.getProperty(key.toString())));
585         }
586         if (props.size() > 0) {
587             out.deleteCharAt(out.lastIndexOf(","));
588         }
589         out.append(" ]");
590         return out.toString();
591     }
592 }