2 * ============LICENSE_START=======================================================
4 * ================================================================================
5 * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
6 * ================================================================================
7 * Copyright (C) 2017 Amdocs
8 * =============================================================================
9 * Licensed under the Apache License, Version 2.0 (the "License");
10 * you may not use this file except in compliance with the License.
11 * You may obtain a copy of the License at
13 * http://www.apache.org/licenses/LICENSE-2.0
15 * Unless required by applicable law or agreed to in writing, software
16 * distributed under the License is distributed on an "AS IS" BASIS,
17 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
18 * See the License for the specific language governing permissions and
19 * limitations under the License.
21 * ECOMP is a trademark and service mark of AT&T Intellectual Property.
22 * ============LICENSE_END=========================================================
27 package org.openecomp.appc.util;
29 import java.util.Date;
30 import java.util.List;
32 import java.util.Properties;
34 import java.util.regex.Matcher;
35 import java.util.regex.Pattern;
38 * This class contains several static helper methods that can be used to perform string manipulation algorithms.
42 public final class StringHelper {
44 public static final String DASH = "-";
45 public static final String DOT = ".";
46 public static final String ELLIPSES = "...";
47 public static final String LINE_FEED = "\n";
48 public static final String SLASH = "/";
49 public static final String COMMA = ",";
52 * Converts the specified string pattern to a regular expression string. If the supplied string is null or empty,
53 * then a regular expression that matches all strings (.*) is returned.
55 * The expression passed to this method should not already be a regular expression. If it contains problematic
56 * meta-characters for this routine (such as period, asterisk, and plus), they will be escaped and matched literally
57 * in the resulting regular expression returned.
61 * The pattern that we need to convert to a regular expression
62 * @return The regular expression that is equivalent to the pattern
64 public static String convertToRegex(String value) {
65 if (value == null || value.trim().length() == 0) {
68 boolean appendEOL = false;
69 StringBuffer buffer = new StringBuffer(value.trim());
72 * If there are any period characters, we need to escape them so that they are exactly matched
74 Pattern pattern = Pattern.compile("\\.");
75 Matcher matcher = pattern.matcher(buffer);
77 while (matcher.find(position)) {
78 buffer.replace(matcher.start(), matcher.end(), "\\.");
79 position = matcher.end() + 1;
83 * If there are any asterisks or pluses, which we need to interpret as wildcard characters, we need to convert
86 pattern = Pattern.compile("\\*|\\+");
87 matcher = pattern.matcher(buffer);
89 while (matcher.find(position)) {
90 String metachar = buffer.substring(matcher.start(), matcher.end());
91 if (metachar.equals("*")) {
92 buffer.replace(matcher.start(), matcher.end(), ".*");
93 position = matcher.end() + 1;
94 if (matcher.end() < buffer.length() - 1) {
97 } else if (metachar.equals("+")) {
98 buffer.replace(matcher.start(), matcher.end(), ".");
99 position = matcher.end();
100 if (matcher.end() == buffer.length()) {
107 * If the string contains a .* meta-character sequence anywhere in the middle of the string (i.e., there are
108 * other characters following the .* meta-characters), OR the string ends with the .+ sequence, then we need to
109 * append the "end-of-line" boundary condition to the end of the string to get predictable results.
114 return buffer.toString();
118 * Takes a string that may possibly be very long and return a string that is at most maxLength. If the string is
119 * longer than maxLength, the last three characters will be the ellipses (...) to indicate that the string was
122 * @param possiblyLongString
124 * must be at least 4 (one character plus ellipses)
125 * @return possibly shortened string
127 public static String getShortenedString(String possiblyLongString, int maxLength) {
128 if ((possiblyLongString != null) && (maxLength > ELLIPSES.length())
129 && (possiblyLongString.length() > maxLength)) {
130 return possiblyLongString.substring(0, maxLength - ELLIPSES.length()) + ELLIPSES;
133 return possiblyLongString;
137 * Determines that a provided string is not null and not empty (length = 0 after trimming)
140 * The string to be tested
141 * @return true if the string IS NOT null and is NOT empty
143 public static boolean isNotNullNotEmpty(String theString) {
144 return ((theString != null) && (!theString.trim().isEmpty()));
148 * Determines that a provided string IS null or an empty string (length = 0 after trimming)
151 * The string to be tested
152 * @return true if the string IS null OR is empty
154 public static boolean isNullOrEmpty(String theString) {
155 return ((theString == null) || (theString.trim().isEmpty()));
159 * Returns an indication if the first string is equal to the second string, allowing for either or both strings to
163 * The first string to be compared
165 * The second string to be compared
166 * @return True if both strings are null, or both strings are non-null AND they are equal. False otherwise.
168 public static boolean equals(String a, String b) {
169 return equals(a, b, false);
173 * Returns an indication if the first string is equal to the second string, allowing for either or both strings to
174 * be null, and ignoring case.
177 * The first string to be compared
179 * The second string to be compared
180 * @return True if both strings are null, or both strings are non-null AND they are equal (without regard to case).
183 public static boolean equalsIgnoreCase(String a, String b) {
184 return equals(a, b, true);
188 * Compares two strings (allowing either or both to be null), and allowing for optional case sensitive or
189 * insensitive comparison.
192 * The first string to be compared
194 * The second string to be compared
195 * @param caseInsensitive
196 * True if the comparison is to be case in-sensitive.
197 * @return True if both strings are null, or both strings are non-null and they are equal
199 private static boolean equals(String a, String b, boolean caseInsensitive) {
200 if (a == null && b == null) {
203 if (a != null && b != null) {
204 if (caseInsensitive) {
205 return a.equalsIgnoreCase(b);
215 * This method is used to mangle a name.
217 * This method will first remove all unacceptable characters from the name and translate all characters to lower
218 * case. This is done to eliminate any potentially troublesome characters. If the resulting string is empty, then a
219 * random string of characters for the minimum desired length is returned. If the string is too short to meet the
220 * minimum length requirement, it is padded with random characters.
223 * Once the string has been scrubbed and possibly padded, it may be truncated (if longer than the maximum value) and
224 * the result is returned. To make the string as unique as possible, the algorithm removes excess letters from the
225 * center of the string, concatenating the first nad last parts of the name together. The assumption is that users
226 * tend to start the names of multiple things in similar ways, and get more descriptive as the name progresses. If
227 * for example, several objects were named "A test Object", "A test Object1", and "A test Object2", shortening the
228 * name only from the left does not generate a unique name.
232 * The name to be mangled
234 * minimum number of characters for the name
236 * maximum number of characters for the name
237 * @return The mangled name, or an empty string if the value is null or an empty string.
239 public static String mangleName(String name, int minLen, int maxLen) {
240 StringBuffer buffer = new StringBuffer(name == null ? "" : name);
241 Pattern pattern = Pattern.compile("[^a-z0-9]+", Pattern.CASE_INSENSITIVE);
242 Matcher matcher = pattern.matcher(buffer);
244 while (matcher.find(position)) {
245 buffer.delete(matcher.start(), matcher.end());
246 position = matcher.start();
249 if (buffer.length() < minLen) {
250 for (int i = buffer.length(); i <= minLen; i++) {
256 * Remove out of the center of the name to preserve start and end and result in a string of max len
258 if (buffer.length() > maxLen) {
259 int excess = buffer.length() - maxLen;
260 int left = maxLen / 2;
262 buffer.delete(left, excess + left);
265 return buffer.toString().toLowerCase();
269 * This method is used to normalize a string value.
271 * This method will ensure that the string value is trimmed of all leading and trailing whitespace if not null. If
272 * it is null or an empty string, then it will return null.
276 * The value to be normalized
277 * @return The normalized (no leading or trailing whitespace) value, or null if the string was null or an empty
278 * string (or all whitespace). This method will never return an empty string.
280 public static String normalizeString(String value) {
282 String temp = value.trim();
283 if (temp.length() > 0) {
291 * This method is used to strip all carriage returns and line feed characters from a string
294 * @return The original value less all carriage returns and line feeds
296 public static String stripCRLF(String value) {
301 String[] tokens = value.split("\r\n|\n\r|\r|\n");
302 StringBuffer buffer = new StringBuffer();
303 for (String token : tokens) {
304 buffer.append(token.trim());
306 return buffer.toString();
310 * Converts UNIX-style line endings to DOS-style. Replaces LF with CR+LF as long as the LF does not already exist
314 * The content to be converted
315 * @return The converted content.
317 public static String toDOSLines(String content) {
318 if (content == null) {
322 StringBuffer buffer = new StringBuffer(content);
323 Pattern pattern = Pattern.compile("^(\n)[^\r]|[^\r](\n)[^\r]|[^\r](\n)$");
324 Matcher matcher = pattern.matcher(buffer);
326 while (matcher.find(position)) {
327 int index = matcher.start(1);
329 index = matcher.start(2);
332 index = matcher.start(3);
335 buffer.replace(index, index + 1, "\r\n");
336 position = index + 1;
339 return buffer.toString();
343 * This method will convert a string contents to use the UNIX-style line endings. That is, all occurrences of CR
344 * (Carriage Return) and LF (Line Feed) are reduced to just use LF.
347 * The buffer to be processed
348 * @return The converted contents
350 public static String toUnixLines(String content) {
351 if (content == null) {
355 StringBuffer buffer = new StringBuffer(content);
356 Pattern pattern = Pattern.compile("\r\n|\n\r");
357 Matcher matcher = pattern.matcher(buffer);
359 while (matcher.find(position)) {
360 buffer.replace(matcher.start(), matcher.end(), "\n");
361 position = matcher.start();
364 return buffer.toString();
368 * This method is used to translate characters in the input sequence that match the characters in the match list to
369 * the corresponding character in the replacement list. If the replacement list is shorter than the match list, then
370 * the character from the replacement list is taken as the modulo of the match character position and the length of
371 * the replacement list.
374 * The input sequence to be processed
376 * The list of matching characters to be searched
378 * The list of replacement characters, positional coincident with the match list. If shorter than the
379 * match list, then the position "wraps" around on the replacement list.
380 * @return The translated string contents.
382 public static Object translate(String sequence, String match, String replacement) {
384 if (sequence == null) {
388 StringBuffer buffer = new StringBuffer(sequence);
390 for (int index = 0; index < buffer.length(); index++) {
391 char ch = buffer.charAt(index);
393 int position = match.indexOf(ch);
394 if (position == -1) {
398 if (position >= replacement.length()) {
399 position %= replacement.length();
401 buffer.setCharAt(index, replacement.charAt(position));
404 return buffer.toString();
408 * Ensures that the name provided is a valid identifier. This means that no spaces are allowed as well as special
409 * characters. This method translates all spaces and illegal characters to underscores (_).
412 * The name to be checked and converted to an identifier if needed
413 * @return The valid identifier from the name
415 public static String validIdentifier(String name) {
416 if (name == null || name.length() == 0) {
420 StringBuffer buffer = new StringBuffer(name);
421 for (int index = 0; index < buffer.length(); index++) {
422 char ch = buffer.charAt(index);
424 if ((index == 0 && !Character.isJavaIdentifierStart(ch)) || (!Character.isJavaIdentifierPart(ch))) {
425 buffer.setCharAt(index, '_');
428 return buffer.toString();
432 * This method verifies that the provided string only contains characters from the legal set, and replaces any
433 * character not in the legal set with the specified replacement character.
436 * The sequence to be verified
438 * The set of all legal characters
440 * The replacement character if a character is not in the legal set
441 * @return The verified *and possibly updated) string
443 public static String verify(String sequence, String legal, char replacement) {
444 if (sequence == null) {
448 StringBuffer buffer = new StringBuffer(sequence);
449 for (int index = 0; index < buffer.length(); index++) {
450 char ch = buffer.charAt(index);
451 if (legal.indexOf(ch) == -1) {
452 buffer.setCharAt(index, replacement);
455 return buffer.toString();
459 * Private constructor to prevent instantiation of this class - All methods are static!
461 private StringHelper() {
467 * The list of elements
468 * @return The list of elements formatted as a comma-delimited list
470 public static String asList(List<String> list) {
471 StringBuffer buffer = new StringBuffer();
473 if (list.size() == 1) {
474 buffer.append(list.get(0));
476 for (String element : list) {
477 buffer.append(element);
481 if (buffer.length() > 2) {
482 buffer.delete(buffer.length() - 2, buffer.length());
486 return buffer.toString();
492 * @return A map expressed as a comma-delimited list of name=value tuples
494 public static String asList(Map<String, String> map) {
495 StringBuffer buffer = new StringBuffer();
497 Set<String> keys = map.keySet();
498 for (String key : keys) {
499 buffer.append(String.format("%s=%s, ", key, map.get(key)));
502 if (buffer.length() > 2) {
503 buffer.delete(buffer.length() - 2, buffer.length());
506 return buffer.toString();
511 * An array or varargs of Strings to be concatenated into a comma-separated list
512 * @return The comma-seprated list of values
514 public static String asList(String... values) {
515 StringBuilder builder = new StringBuilder();
517 if (values != null && values.length > 0) {
518 int count = values.length;
519 for (int index = 0; index < count - 1; index++) {
520 builder.append(values[index]);
523 builder.append(values[count - 1]);
526 return builder.toString();
529 public static Object resolveToType(String input) {
530 String intRegex = "^(\\-)?[0-9]+$";
531 String doubleRegex = "^(\\-)?[0-9\\.]+$";
532 String boolRegex = "(^(?i)((true)|(false))$)";
540 if (input.matches(intRegex)) {
542 return Integer.parseInt(input);
543 } catch (NumberFormatException nfe) {
545 nfe.printStackTrace();
549 // Check double (int + decimal point)
550 if (input.matches(doubleRegex)) {
552 return Double.parseDouble(input);
553 } catch (NumberFormatException | NullPointerException e) {
554 // NPE won't happen bc of regex check
559 if (input.matches(boolRegex)) {
560 return Boolean.parseBoolean(input);
563 // Try to parse a date
564 Date date = Time.utcParse(input);
569 // No special type, return string
574 * Converts a properties object to a string in the format of <pre>[ key=value, key=value, ... ]</pre>
577 * The properties object to format
578 * @return A string in the format <pre>[ key=value, ... ]</pre> or null if the input was null
580 public static String propertiesToString(Properties props) {
584 StringBuilder out = new StringBuilder();
586 for (Object key : props.keySet()) {
587 out.append(String.format(" %s = %s,", key.toString(), props.getProperty(key.toString())));
589 if (props.size() > 0) {
590 out.deleteCharAt(out.lastIndexOf(","));
593 return out.toString();