2 * ============LICENSE_START=======================================================
4 * ================================================================================
5 * Copyright (C) 2017 AT&T Intellectual Property. All rights
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
12 * http://www.apache.org/licenses/LICENSE-2.0
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=========================================================
24 package org.openecomp.appc.util;
26 import java.util.Date;
27 import java.util.List;
29 import java.util.Properties;
31 import java.util.regex.Matcher;
32 import java.util.regex.Pattern;
35 * This class contains several static helper methods that can be used to perform string manipulation algorithms.
39 public final class StringHelper {
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 = ",";
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.
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.
58 * The pattern that we need to convert to a regular expression
59 * @return The regular expression that is equivalent to the pattern
61 public static String convertToRegex(String value) {
62 if (value == null || value.trim().length() == 0) {
65 boolean appendEOL = false;
66 StringBuffer buffer = new StringBuffer(value.trim());
69 * If there are any period characters, we need to escape them so that they are exactly matched
71 Pattern pattern = Pattern.compile("\\.");
72 Matcher matcher = pattern.matcher(buffer);
74 while (matcher.find(position)) {
75 buffer.replace(matcher.start(), matcher.end(), "\\.");
76 position = matcher.end() + 1;
80 * If there are any asterisks or pluses, which we need to interpret as wildcard characters, we need to convert
83 pattern = Pattern.compile("\\*|\\+");
84 matcher = pattern.matcher(buffer);
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) {
94 } else if (metachar.equals("+")) {
95 buffer.replace(matcher.start(), matcher.end(), ".");
96 position = matcher.end();
97 if (matcher.end() == buffer.length()) {
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.
111 return buffer.toString();
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
119 * @param possiblyLongString
121 * must be at least 4 (one character plus ellipses)
122 * @return possibly shortened string
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;
130 return possiblyLongString;
134 * Determines that a provided string is not null and not empty (length = 0 after trimming)
137 * The string to be tested
138 * @return true if the string IS NOT null and is NOT empty
140 public static boolean isNotNullNotEmpty(String theString) {
141 return ((theString != null) && (!theString.trim().isEmpty()));
145 * Determines that a provided string IS null or an empty string (length = 0 after trimming)
148 * The string to be tested
149 * @return true if the string IS null OR is empty
151 public static boolean isNullOrEmpty(String theString) {
152 return ((theString == null) || (theString.trim().isEmpty()));
156 * Returns an indication if the first string is equal to the second string, allowing for either or both strings to
160 * The first string to be compared
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.
165 public static boolean equals(String a, String b) {
166 return equals(a, b, false);
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.
174 * The first string to be compared
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).
180 public static boolean equalsIgnoreCase(String a, String b) {
181 return equals(a, b, true);
185 * Compares two strings (allowing either or both to be null), and allowing for optional case sensitive or
186 * insensitive comparison.
189 * The first string to be compared
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
196 private static boolean equals(String a, String b, boolean caseInsensitive) {
197 if (a == null && b == null) {
200 if (a != null && b != null) {
201 if (caseInsensitive) {
202 return a.equalsIgnoreCase(b);
212 * This method is used to mangle a name.
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.
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.
229 * The name to be mangled
231 * minimum number of characters for the name
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.
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);
241 while (matcher.find(position)) {
242 buffer.delete(matcher.start(), matcher.end());
243 position = matcher.start();
246 if (buffer.length() < minLen) {
247 for (int i = buffer.length(); i <= minLen; i++) {
253 * Remove out of the center of the name to preserve start and end and result in a string of max len
255 if (buffer.length() > maxLen) {
256 int excess = buffer.length() - maxLen;
257 int left = maxLen / 2;
259 buffer.delete(left, excess + left);
262 return buffer.toString().toLowerCase();
266 * This method is used to normalize a string value.
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.
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.
277 public static String normalizeString(String value) {
279 String temp = value.trim();
280 if (temp.length() > 0) {
288 * This method is used to strip all carriage returns and line feed characters from a string
291 * @return The original value less all carriage returns and line feeds
293 public static String stripCRLF(String value) {
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());
303 return buffer.toString();
307 * Converts UNIX-style line endings to DOS-style. Replaces LF with CR+LF as long as the LF does not already exist
311 * The content to be converted
312 * @return The converted content.
314 public static String toDOSLines(String content) {
315 if (content == null) {
319 StringBuffer buffer = new StringBuffer(content);
320 Pattern pattern = Pattern.compile("^(\n)[^\r]|[^\r](\n)[^\r]|[^\r](\n)$");
321 Matcher matcher = pattern.matcher(buffer);
323 while (matcher.find(position)) {
324 int index = matcher.start(1);
326 index = matcher.start(2);
329 index = matcher.start(3);
332 buffer.replace(index, index + 1, "\r\n");
333 position = index + 1;
336 return buffer.toString();
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.
344 * The buffer to be processed
345 * @return The converted contents
347 public static String toUnixLines(String content) {
348 if (content == null) {
352 StringBuffer buffer = new StringBuffer(content);
353 Pattern pattern = Pattern.compile("\r\n|\n\r");
354 Matcher matcher = pattern.matcher(buffer);
356 while (matcher.find(position)) {
357 buffer.replace(matcher.start(), matcher.end(), "\n");
358 position = matcher.start();
361 return buffer.toString();
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.
371 * The input sequence to be processed
373 * The list of matching characters to be searched
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.
379 public static Object translate(String sequence, String match, String replacement) {
381 if (sequence == null) {
385 StringBuffer buffer = new StringBuffer(sequence);
387 for (int index = 0; index < buffer.length(); index++) {
388 char ch = buffer.charAt(index);
390 int position = match.indexOf(ch);
391 if (position == -1) {
395 if (position >= replacement.length()) {
396 position %= replacement.length();
398 buffer.setCharAt(index, replacement.charAt(position));
401 return buffer.toString();
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 (_).
409 * The name to be checked and converted to an identifier if needed
410 * @return The valid identifier from the name
412 public static String validIdentifier(String name) {
413 if (name == null || name.length() == 0) {
417 StringBuffer buffer = new StringBuffer(name);
418 for (int index = 0; index < buffer.length(); index++) {
419 char ch = buffer.charAt(index);
421 if ((index == 0 && !Character.isJavaIdentifierStart(ch)) || (!Character.isJavaIdentifierPart(ch))) {
422 buffer.setCharAt(index, '_');
425 return buffer.toString();
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.
433 * The sequence to be verified
435 * The set of all legal characters
437 * The replacement character if a character is not in the legal set
438 * @return The verified *and possibly updated) string
440 public static String verify(String sequence, String legal, char replacement) {
441 if (sequence == null) {
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);
452 return buffer.toString();
456 * Private constructor to prevent instantiation of this class - All methods are static!
458 private StringHelper() {
464 * The list of elements
465 * @return The list of elements formatted as a comma-delimited list
467 public static String asList(List<String> list) {
468 StringBuffer buffer = new StringBuffer();
470 if (list.size() == 1) {
471 buffer.append(list.get(0));
473 for (String element : list) {
474 buffer.append(element);
478 if (buffer.length() > 2) {
479 buffer.delete(buffer.length() - 2, buffer.length());
483 return buffer.toString();
489 * @return A map expressed as a comma-delimited list of name=value tuples
491 public static String asList(Map<String, String> map) {
492 StringBuffer buffer = new StringBuffer();
494 Set<String> keys = map.keySet();
495 for (String key : keys) {
496 buffer.append(String.format("%s=%s, ", key, map.get(key)));
499 if (buffer.length() > 2) {
500 buffer.delete(buffer.length() - 2, buffer.length());
503 return buffer.toString();
508 * An array or varargs of Strings to be concatenated into a comma-separated list
509 * @return The comma-seprated list of values
511 public static String asList(String... values) {
512 StringBuilder builder = new StringBuilder();
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]);
520 builder.append(values[count - 1]);
523 return builder.toString();
526 public static Object resolveToType(String input) {
527 String intRegex = "^(\\-)?[0-9]+$";
528 String doubleRegex = "^(\\-)?[0-9\\.]+$";
529 String boolRegex = "(^(?i)((true)|(false))$)";
537 if (input.matches(intRegex)) {
539 return Integer.parseInt(input);
540 } catch (NumberFormatException nfe) {
542 nfe.printStackTrace();
546 // Check double (int + decimal point)
547 if (input.matches(doubleRegex)) {
549 return Double.parseDouble(input);
550 } catch (NumberFormatException | NullPointerException e) {
551 // NPE won't happen bc of regex check
556 if (input.matches(boolRegex)) {
557 return Boolean.parseBoolean(input);
560 // Try to parse a date
561 Date date = Time.utcParse(input);
566 // No special type, return string
571 * Converts a properties object to a string in the format of <pre>[ key=value, key=value, ... ]</pre>
574 * The properties object to format
575 * @return A string in the format <pre>[ key=value, ... ]</pre> or null if the input was null
577 public static String propertiesToString(Properties props) {
581 StringBuilder out = new StringBuilder();
583 for (Object key : props.keySet()) {
584 out.append(String.format(" %s = %s,", key.toString(), props.getProperty(key.toString())));
586 if (props.size() > 0) {
587 out.deleteCharAt(out.lastIndexOf(","));
590 return out.toString();