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.onap.appc.util;
29 import com.att.eelf.configuration.EELFLogger;
30 import com.att.eelf.configuration.EELFManager;
32 import java.util.Date;
33 import java.util.List;
35 import java.util.Properties;
37 import java.util.regex.Matcher;
38 import java.util.regex.Pattern;
41 * This class contains several static helper methods that can be used to perform string manipulation algorithms.
45 public final class StringHelper {
46 private static final EELFLogger logger = EELFManager.getInstance().getLogger(StringHelper.class);
48 public static final String DASH = "-";
49 public static final String DOT = ".";
50 public static final String ELLIPSES = "...";
51 public static final String LINE_FEED = "\n";
52 public static final String SLASH = "/";
53 public static final String COMMA = ",";
56 * Converts the specified string pattern to a regular expression string. If the supplied string is null or empty,
57 * then a regular expression that matches all strings (.*) is returned.
59 * The expression passed to this method should not already be a regular expression. If it contains problematic
60 * meta-characters for this routine (such as period, asterisk, and plus), they will be escaped and matched literally
61 * in the resulting regular expression returned.
65 * The pattern that we need to convert to a regular expression
66 * @return The regular expression that is equivalent to the pattern
68 public static String convertToRegex(String value) {
69 if (value == null || value.trim().length() == 0) {
72 boolean appendEOL = false;
73 StringBuffer buffer = new StringBuffer(value.trim());
76 * If there are any period characters, we need to escape them so that they are exactly matched
78 Pattern pattern = Pattern.compile("\\.");
79 Matcher matcher = pattern.matcher(buffer);
81 while (matcher.find(position)) {
82 buffer.replace(matcher.start(), matcher.end(), "\\.");
83 position = matcher.end() + 1;
87 * If there are any asterisks or pluses, which we need to interpret as wildcard characters, we need to convert
90 pattern = Pattern.compile("\\*|\\+");
91 matcher = pattern.matcher(buffer);
93 while (matcher.find(position)) {
94 String metachar = buffer.substring(matcher.start(), matcher.end());
95 if (metachar.equals("*")) {
96 buffer.replace(matcher.start(), matcher.end(), ".*");
97 position = matcher.end() + 1;
98 if (matcher.end() < buffer.length() - 1) {
101 } else if (metachar.equals("+")) {
102 buffer.replace(matcher.start(), matcher.end(), ".");
103 position = matcher.end();
104 if (matcher.end() == buffer.length()) {
111 * If the string contains a .* meta-character sequence anywhere in the middle of the string (i.e., there are
112 * other characters following the .* meta-characters), OR the string ends with the .+ sequence, then we need to
113 * append the "end-of-line" boundary condition to the end of the string to get predictable results.
118 return buffer.toString();
122 * Takes a string that may possibly be very long and return a string that is at most maxLength. If the string is
123 * longer than maxLength, the last three characters will be the ellipses (...) to indicate that the string was
126 * @param possiblyLongString
128 * must be at least 4 (one character plus ellipses)
129 * @return possibly shortened string
131 public static String getShortenedString(String possiblyLongString, int maxLength) {
132 if ((possiblyLongString != null) && (maxLength > ELLIPSES.length())
133 && (possiblyLongString.length() > maxLength)) {
134 return possiblyLongString.substring(0, maxLength - ELLIPSES.length()) + ELLIPSES;
137 return possiblyLongString;
141 * Determines that a provided string is not null and not empty (length = 0 after trimming)
144 * The string to be tested
145 * @return true if the string IS NOT null and is NOT empty
147 public static boolean isNotNullNotEmpty(String theString) {
148 return ((theString != null) && (!theString.trim().isEmpty()));
152 * Determines that a provided string IS null or an empty string (length = 0 after trimming)
155 * The string to be tested
156 * @return true if the string IS null OR is empty
158 public static boolean isNullOrEmpty(String theString) {
159 return ((theString == null) || (theString.trim().isEmpty()));
163 * Returns an indication if the first string is equal to the second string, allowing for either or both strings to
167 * The first string to be compared
169 * The second string to be compared
170 * @return True if both strings are null, or both strings are non-null AND they are equal. False otherwise.
172 public static boolean equals(String a, String b) {
173 return equals(a, b, false);
177 * Returns an indication if the first string is equal to the second string, allowing for either or both strings to
178 * be null, and ignoring case.
181 * The first string to be compared
183 * The second string to be compared
184 * @return True if both strings are null, or both strings are non-null AND they are equal (without regard to case).
187 public static boolean equalsIgnoreCase(String a, String b) {
188 return equals(a, b, true);
192 * Compares two strings (allowing either or both to be null), and allowing for optional case sensitive or
193 * insensitive comparison.
196 * The first string to be compared
198 * The second string to be compared
199 * @param caseInsensitive
200 * True if the comparison is to be case in-sensitive.
201 * @return True if both strings are null, or both strings are non-null and they are equal
203 private static boolean equals(String a, String b, boolean caseInsensitive) {
204 if (a == null && b == null) {
207 if (a != null && b != null) {
208 if (caseInsensitive) {
209 return a.equalsIgnoreCase(b);
219 * This method is used to mangle a name.
221 * This method will first remove all unacceptable characters from the name and translate all characters to lower
222 * case. This is done to eliminate any potentially troublesome characters. If the resulting string is empty, then a
223 * random string of characters for the minimum desired length is returned. If the string is too short to meet the
224 * minimum length requirement, it is padded with random characters.
227 * Once the string has been scrubbed and possibly padded, it may be truncated (if longer than the maximum value) and
228 * the result is returned. To make the string as unique as possible, the algorithm removes excess letters from the
229 * center of the string, concatenating the first nad last parts of the name together. The assumption is that users
230 * tend to start the names of multiple things in similar ways, and get more descriptive as the name progresses. If
231 * for example, several objects were named "A test Object", "A test Object1", and "A test Object2", shortening the
232 * name only from the left does not generate a unique name.
236 * The name to be mangled
238 * minimum number of characters for the name
240 * maximum number of characters for the name
241 * @return The mangled name, or an empty string if the value is null or an empty string.
243 public static String mangleName(String name, int minLen, int maxLen) {
244 StringBuffer buffer = new StringBuffer(name == null ? "" : name);
245 Pattern pattern = Pattern.compile("[^a-z0-9]+", Pattern.CASE_INSENSITIVE);
246 Matcher matcher = pattern.matcher(buffer);
248 while (matcher.find(position)) {
249 buffer.delete(matcher.start(), matcher.end());
250 position = matcher.start();
253 if (buffer.length() < minLen) {
254 for (int i = buffer.length(); i <= minLen; i++) {
260 * Remove out of the center of the name to preserve start and end and result in a string of max len
262 if (buffer.length() > maxLen) {
263 int excess = buffer.length() - maxLen;
264 int left = maxLen / 2;
266 buffer.delete(left, excess + left);
269 return buffer.toString().toLowerCase();
273 * This method is used to normalize a string value.
275 * This method will ensure that the string value is trimmed of all leading and trailing whitespace if not null. If
276 * it is null or an empty string, then it will return null.
280 * The value to be normalized
281 * @return The normalized (no leading or trailing whitespace) value, or null if the string was null or an empty
282 * string (or all whitespace). This method will never return an empty string.
284 public static String normalizeString(String value) {
286 String temp = value.trim();
287 if (temp.length() > 0) {
295 * This method is used to strip all carriage returns and line feed characters from a string
298 * @return The original value less all carriage returns and line feeds
300 public static String stripCRLF(String value) {
305 String[] tokens = value.split("\r\n|\n\r|\r|\n");
306 StringBuffer buffer = new StringBuffer();
307 for (String token : tokens) {
308 buffer.append(token.trim());
310 return buffer.toString();
314 * Converts UNIX-style line endings to DOS-style. Replaces LF with CR+LF as long as the LF does not already exist
318 * The content to be converted
319 * @return The converted content.
321 public static String toDOSLines(String content) {
322 if (content == null) {
326 StringBuffer buffer = new StringBuffer(content);
327 Pattern pattern = Pattern.compile("^(\n)[^\r]|[^\r](\n)[^\r]|[^\r](\n)$");
328 Matcher matcher = pattern.matcher(buffer);
330 while (matcher.find(position)) {
331 int index = matcher.start(1);
333 index = matcher.start(2);
336 index = matcher.start(3);
339 buffer.replace(index, index + 1, "\r\n");
340 position = index + 1;
343 return buffer.toString();
347 * This method will convert a string contents to use the UNIX-style line endings. That is, all occurrences of CR
348 * (Carriage Return) and LF (Line Feed) are reduced to just use LF.
351 * The buffer to be processed
352 * @return The converted contents
354 public static String toUnixLines(String content) {
355 if (content == null) {
359 StringBuffer buffer = new StringBuffer(content);
360 Pattern pattern = Pattern.compile("\r\n|\n\r");
361 Matcher matcher = pattern.matcher(buffer);
363 while (matcher.find(position)) {
364 buffer.replace(matcher.start(), matcher.end(), "\n");
365 position = matcher.start();
368 return buffer.toString();
372 * This method is used to translate characters in the input sequence that match the characters in the match list to
373 * the corresponding character in the replacement list. If the replacement list is shorter than the match list, then
374 * the character from the replacement list is taken as the modulo of the match character position and the length of
375 * the replacement list.
378 * The input sequence to be processed
380 * The list of matching characters to be searched
382 * The list of replacement characters, positional coincident with the match list. If shorter than the
383 * match list, then the position "wraps" around on the replacement list.
384 * @return The translated string contents.
386 public static Object translate(String sequence, String match, String replacement) {
388 if (sequence == null) {
392 StringBuffer buffer = new StringBuffer(sequence);
394 for (int index = 0; index < buffer.length(); index++) {
395 char ch = buffer.charAt(index);
397 int position = match.indexOf(ch);
398 if (position == -1) {
402 if (position >= replacement.length()) {
403 position %= replacement.length();
405 buffer.setCharAt(index, replacement.charAt(position));
408 return buffer.toString();
412 * Ensures that the name provided is a valid identifier. This means that no spaces are allowed as well as special
413 * characters. This method translates all spaces and illegal characters to underscores (_).
416 * The name to be checked and converted to an identifier if needed
417 * @return The valid identifier from the name
419 public static String validIdentifier(String name) {
420 if (name == null || name.length() == 0) {
424 StringBuffer buffer = new StringBuffer(name);
425 for (int index = 0; index < buffer.length(); index++) {
426 char ch = buffer.charAt(index);
428 if ((index == 0 && !Character.isJavaIdentifierStart(ch)) || (!Character.isJavaIdentifierPart(ch))) {
429 buffer.setCharAt(index, '_');
432 return buffer.toString();
436 * This method verifies that the provided string only contains characters from the legal set, and replaces any
437 * character not in the legal set with the specified replacement character.
440 * The sequence to be verified
442 * The set of all legal characters
444 * The replacement character if a character is not in the legal set
445 * @return The verified *and possibly updated) string
447 public static String verify(String sequence, String legal, char replacement) {
448 if (sequence == null) {
452 StringBuffer buffer = new StringBuffer(sequence);
453 for (int index = 0; index < buffer.length(); index++) {
454 char ch = buffer.charAt(index);
455 if (legal.indexOf(ch) == -1) {
456 buffer.setCharAt(index, replacement);
459 return buffer.toString();
463 * Private constructor to prevent instantiation of this class - All methods are static!
465 private StringHelper() {
471 * The list of elements
472 * @return The list of elements formatted as a comma-delimited list
474 public static String asList(List<String> list) {
475 StringBuffer buffer = new StringBuffer();
477 if (list.size() == 1) {
478 buffer.append(list.get(0));
480 for (String element : list) {
481 buffer.append(element);
485 if (buffer.length() > 2) {
486 buffer.delete(buffer.length() - 2, buffer.length());
490 return buffer.toString();
496 * @return A map expressed as a comma-delimited list of name=value tuples
498 public static String asList(Map<String, String> map) {
499 StringBuffer buffer = new StringBuffer();
501 Set<String> keys = map.keySet();
502 for (String key : keys) {
503 buffer.append(String.format("%s=%s, ", key, map.get(key)));
506 if (buffer.length() > 2) {
507 buffer.delete(buffer.length() - 2, buffer.length());
510 return buffer.toString();
515 * An array or varargs of Strings to be concatenated into a comma-separated list
516 * @return The comma-seprated list of values
518 public static String asList(String... values) {
519 StringBuilder builder = new StringBuilder();
521 if (values != null && values.length > 0) {
522 int count = values.length;
523 for (int index = 0; index < count - 1; index++) {
524 builder.append(values[index]);
527 builder.append(values[count - 1]);
530 return builder.toString();
533 public static Object resolveToType(String input) {
534 String intRegex = "^(\\-)?[0-9]+$";
535 String doubleRegex = "^(\\-)?[0-9\\.]+$";
536 String boolRegex = "(^(?i)((true)|(false))$)";
544 if (input.matches(intRegex)) {
546 return Integer.parseInt(input);
547 } catch (NumberFormatException nfe) {
549 logger.error(nfe.getMessage());
553 // Check double (int + decimal point)
554 if (input.matches(doubleRegex)) {
556 return Double.parseDouble(input);
557 } catch (NumberFormatException | NullPointerException e) {
558 // NPE won't happen bc of regex check
559 logger.error(e.getMessage());
564 if (input.matches(boolRegex)) {
565 return Boolean.parseBoolean(input);
568 // Try to parse a date
569 Date date = Time.utcParse(input);
574 // No special type, return string
579 * Converts a properties object to a string in the format of <pre>[ key=value, key=value, ... ]</pre>
582 * The properties object to format
583 * @return A string in the format <pre>[ key=value, ... ]</pre> or null if the input was null
585 public static String propertiesToString(Properties props) {
589 StringBuilder out = new StringBuilder();
591 for (Object key : props.keySet()) {
592 out.append(String.format(" %s = %s,", key.toString(), props.getProperty(key.toString())));
594 if (props.size() > 0) {
595 out.deleteCharAt(out.lastIndexOf(","));
598 return out.toString();