2 * ============LICENSE_START=======================================================
4 * ================================================================================
5 * Copyright (C) 2017-2018 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 * ============LICENSE_END=========================================================
25 package org.onap.appc.util;
27 import com.att.eelf.configuration.EELFLogger;
28 import com.att.eelf.configuration.EELFManager;
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.
41 public final class StringHelper {
43 private static final EELFLogger logger = EELFManager.getInstance().getLogger(StringHelper.class);
45 public static final String DASH = "-";
46 public static final String DOT = ".";
47 public static final String ELLIPSES = "...";
48 public static final String LINE_FEED = "\n";
49 public static final String SLASH = "/";
50 public static final String COMMA = ",";
53 * Converts the specified string pattern to a regular expression string. If the supplied string is null or empty,
54 * then a regular expression that matches all strings (.*) is returned.
56 * The expression passed to this method should not already be a regular expression. If it contains problematic
57 * meta-characters for this routine (such as period, asterisk, and plus), they will be escaped and matched literally
58 * in the resulting regular expression returned.
62 * The pattern that we need to convert to a regular expression
63 * @return The regular expression that is equivalent to the pattern
65 public static String convertToRegex(String value) {
66 if (value == null || value.trim().length() == 0) {
70 StringBuilder builder = new StringBuilder(value.trim());
73 * If there are any period characters, we need to escape them so that they are exactly matched
75 Pattern pattern = Pattern.compile("\\.");
76 Matcher matcher = pattern.matcher(builder);
78 while (matcher.find(position)) {
79 builder.replace(matcher.start(), matcher.end(), "\\.");
80 position = matcher.end() + 1;
84 * If there are any asterisks or pluses, which we need to interpret as wildcard characters, we need to convert
87 pattern = Pattern.compile("\\*|\\+");
88 matcher = pattern.matcher(builder);
91 * If the string contains a .* meta-character sequence anywhere in the middle of the string (i.e., there are
92 * other characters following the .* meta-characters), OR the string ends with the .+ sequence, then we need to
93 * append the "end-of-line" boundary condition to the end of the string to get predictable results.
95 if (resolveAppendingEOL(builder, matcher)) {
98 return builder.toString();
101 private static boolean resolveAppendingEOL(StringBuilder builder, Matcher matcher) {
103 boolean appendEOL = false;
105 while (matcher.find(position)) {
106 String metachar = builder.substring(matcher.start(), matcher.end());
107 if ("*".equals(metachar)) {
108 builder.replace(matcher.start(), matcher.end(), ".*");
109 position = matcher.end() + 1;
110 if (matcher.end() < builder.length() - 1) {
113 } else if ("+".equals(metachar)) {
114 builder.replace(matcher.start(), matcher.end(), ".");
115 position = matcher.end();
116 if (matcher.end() == builder.length()) {
125 * Takes a string that may possibly be very long and return a string that is at most maxLength. If the string is
126 * longer than maxLength, the last three characters will be the ellipses (...) to indicate that the string was
129 * @param possiblyLongString
131 * must be at least 4 (one character plus ellipses)
132 * @return possibly shortened string
134 public static String getShortenedString(String possiblyLongString, int maxLength) {
135 if ((possiblyLongString != null) && (maxLength > ELLIPSES.length())
136 && (possiblyLongString.length() > maxLength)) {
137 return possiblyLongString.substring(0, maxLength - ELLIPSES.length()) + ELLIPSES;
140 return possiblyLongString;
144 * Determines that a provided string is not null and not empty (length = 0 after trimming)
147 * The string to be tested
148 * @return true if the string IS NOT null and is NOT empty
150 public static boolean isNotNullNotEmpty(String theString) {
151 return theString != null && !theString.trim().isEmpty();
155 * Determines that a provided string IS null or an empty string (length = 0 after trimming)
158 * The string to be tested
159 * @return true if the string IS null OR is empty
161 public static boolean isNullOrEmpty(String theString) {
162 return theString == null || theString.trim().isEmpty();
166 * Returns an indication if the first string is equal to the second string, allowing for either or both strings to
170 * The first string to be compared
172 * The second string to be compared
173 * @return True if both strings are null, or both strings are non-null AND they are equal. False otherwise.
175 public static boolean areEqual(String a, String b) {
176 return areEqual(a, b, false);
180 * Returns an indication if the first string is equal to the second string, allowing for either or both strings to
181 * be null, and ignoring case.
184 * The first string to be compared
186 * The second string to be compared
187 * @return True if both strings are null, or both strings are non-null AND they are equal (without regard to case).
190 public static boolean equalsIgnoreCase(String a, String b) {
191 return areEqual(a, b, true);
195 * Compares two strings (allowing either or both to be null), and allowing for optional case sensitive or
196 * insensitive comparison.
199 * The first string to be compared
201 * The second string to be compared
202 * @param caseInsensitive
203 * True if the comparison is to be case in-sensitive.
204 * @return True if both strings are null, or both strings are non-null and they are equal
206 private static boolean areEqual(String a, String b, boolean caseInsensitive) {
207 if (a == null && b == null) {
210 if (a != null && b != null) {
211 if (caseInsensitive) {
212 return a.equalsIgnoreCase(b);
222 * This method is used to mangle a name.
224 * This method will first remove all unacceptable characters from the name and translate all characters to lower
225 * case. This is done to eliminate any potentially troublesome characters. If the resulting string is empty, then a
226 * random string of characters for the minimum desired length is returned. If the string is too short to meet the
227 * minimum length requirement, it is padded with random characters.
230 * Once the string has been scrubbed and possibly padded, it may be truncated (if longer than the maximum value) and
231 * the result is returned. To make the string as unique as possible, the algorithm removes excess letters from the
232 * center of the string, concatenating the first nad last parts of the name together. The assumption is that users
233 * tend to start the names of multiple things in similar ways, and get more descriptive as the name progresses. If
234 * for example, several objects were named "A test Object", "A test Object1", and "A test Object2", shortening the
235 * name only from the left does not generate a unique name.
239 * The name to be mangled
241 * minimum number of characters for the name
243 * maximum number of characters for the name
244 * @return The mangled name, or an empty string if the value is null or an empty string.
246 public static String mangleName(String name, int minLen, int maxLen) {
247 StringBuilder builder = new StringBuilder(name == null ? "" : name);
248 Pattern pattern = Pattern.compile("[^a-z0-9]+", Pattern.CASE_INSENSITIVE);
249 Matcher matcher = pattern.matcher(builder);
251 while (matcher.find(position)) {
252 builder.delete(matcher.start(), matcher.end());
253 position = matcher.start();
256 if (builder.length() < minLen) {
257 for (int i = builder.length(); i <= minLen; i++) {
263 * Remove out of the center of the name to preserve start and end and result in a string of max len
265 if (builder.length() > maxLen) {
266 int excess = builder.length() - maxLen;
267 int left = maxLen / 2;
269 builder.delete(left, excess + left);
272 return builder.toString().toLowerCase();
276 * This method is used to normalize a string value.
278 * This method will ensure that the string value is trimmed of all leading and trailing whitespace if not null. If
279 * it is null or an empty string, then it will return null.
283 * The value to be normalized
284 * @return The normalized (no leading or trailing whitespace) value, or null if the string was null or an empty
285 * string (or all whitespace). This method will never return an empty string.
287 public static String normalizeString(String value) {
289 String temp = value.trim();
290 if (temp.length() > 0) {
298 * This method is used to strip all carriage returns and line feed characters from a string
301 * @return The original value less all carriage returns and line feeds
303 public static String stripCRLF(String value) {
308 String[] tokens = value.split("\r\n|\n\r|\r|\n");
309 StringBuilder builder = new StringBuilder();
310 for (String token : tokens) {
311 builder.append(token.trim());
313 return builder.toString();
317 * Converts UNIX-style line endings to DOS-style. Replaces LF with CR+LF as long as the LF does not already exist
321 * The content to be converted
322 * @return The converted content.
324 public static String toDOSLines(String content) {
325 if (content == null) {
329 StringBuilder builder = new StringBuilder(content);
330 Pattern pattern = Pattern.compile("^(\n)[^\r]|[^\r](\n)[^\r]|[^\r](\n)$");
331 Matcher matcher = pattern.matcher(builder);
333 while (matcher.find(position)) {
334 int index = matcher.start(1);
336 index = matcher.start(2);
339 index = matcher.start(3);
342 builder.replace(index, index + 1, "\r\n");
343 position = index + 1;
346 return builder.toString();
350 * This method will convert a string contents to use the UNIX-style line endings. That is, all occurrences of CR
351 * (Carriage Return) and LF (Line Feed) are reduced to just use LF.
354 * The buffer to be processed
355 * @return The converted contents
357 public static String toUnixLines(String content) {
358 if (content == null) {
362 StringBuilder builder = new StringBuilder(content);
363 Pattern pattern = Pattern.compile("\r\n|\n\r");
364 Matcher matcher = pattern.matcher(builder);
366 while (matcher.find(position)) {
367 builder.replace(matcher.start(), matcher.end(), "\n");
368 position = matcher.start();
371 return builder.toString();
375 * This method is used to translate characters in the input sequence that match the characters in the match list to
376 * the corresponding character in the replacement list. If the replacement list is shorter than the match list, then
377 * the character from the replacement list is taken as the modulo of the match character position and the length of
378 * the replacement list.
381 * The input sequence to be processed
383 * The list of matching characters to be searched
385 * The list of replacement characters, positional coincident with the match list. If shorter than the
386 * match list, then the position "wraps" around on the replacement list.
387 * @return The translated string contents.
389 public static Object translate(String sequence, String match, String replacement) {
391 if (sequence == null) {
395 StringBuilder builder = new StringBuilder(sequence);
397 for (int index = 0; index < builder.length(); index++) {
398 char ch = builder.charAt(index);
400 int position = match.indexOf(ch);
401 if (position == -1) {
405 if (position >= replacement.length()) {
406 position %= replacement.length();
408 builder.setCharAt(index, replacement.charAt(position));
411 return builder.toString();
415 * Ensures that the name provided is a valid identifier. This means that no spaces are allowed as well as special
416 * characters. This method translates all spaces and illegal characters to underscores (_).
419 * The name to be checked and converted to an identifier if needed
420 * @return The valid identifier from the name
422 public static String validIdentifier(String name) {
423 if (name == null || name.length() == 0) {
426 StringBuilder builder = new StringBuilder(name);
427 for (int index = 0; index < builder.length(); index++) {
428 char ch = builder.charAt(index);
430 if ((index == 0 && !Character.isJavaIdentifierStart(ch)) || (!Character.isJavaIdentifierPart(ch))) {
431 builder.setCharAt(index, '_');
434 return builder.toString();
439 * Private constructor to prevent instantiation of this class - All methods are static!
441 private StringHelper() {
446 * This method verifies that the provided string only contains characters from the legal set, and replaces any
447 * character not in the legal set with the specified replacement character.
450 * The sequence to be verified
452 * The set of all legal characters
454 * The replacement character if a character is not in the legal set
455 * @return The verified *and possibly updated) string
457 public static String verify(String sequence, String legal, char replacement) {
458 if (sequence == null) {
462 StringBuilder builder = new StringBuilder(sequence);
463 for (int index = 0; index < builder.length(); index++) {
464 char ch = builder.charAt(index);
465 if (legal.indexOf(ch) == -1) {
466 builder.setCharAt(index, replacement);
469 return builder.toString();
474 * The list of elements
475 * @return The list of elements formatted as a comma-delimited list
477 public static String asList(List<String> list) {
478 StringBuilder builder = new StringBuilder();
481 if (list.size() == 1) {
482 builder.append(list.get(0));
484 for (String element : list) {
485 builder.append(element);
486 builder.append(", ");
489 if (builder.length() > 2) {
490 builder.delete(builder.length() - 2, builder.length());
494 return builder.toString();
500 * @return A map expressed as a comma-delimited list of name=value tuples
502 public static String asList(Map<String, String> map) {
503 StringBuilder builder = new StringBuilder();
505 Set<String> keys = map.keySet();
506 for (String key : keys) {
507 builder.append(String.format("%s=%s, ", key, map.get(key)));
510 if (builder.length() > 2) {
511 builder.delete(builder.length() - 2, builder.length());
514 return builder.toString();
519 * An array or varargs of Strings to be concatenated into a comma-separated list
520 * @return The comma-seprated list of values
522 public static String asList(String... values) {
523 StringBuilder builder = new StringBuilder();
525 if (values != null && values.length > 0) {
526 int count = values.length;
527 for (int index = 0; index < count - 1; index++) {
528 builder.append(values[index]);
531 builder.append(values[count - 1]);
534 return builder.toString();
537 public static Object resolveToType(String input) {
538 String intRegex = "^(\\-)?[0-9]+$";
539 String doubleRegex = "^(\\-)?[0-9\\.]+$";
540 String boolRegex = "(^(?i)((true)|(false))$)";
548 if (input.matches(intRegex)) {
550 return Integer.parseInt(input);
551 } catch (NumberFormatException nfe) {
553 logger.error(nfe.getMessage());
557 // Check double (int + decimal point)
558 if (input.matches(doubleRegex)) {
560 return Double.parseDouble(input);
561 } catch (NumberFormatException | NullPointerException e) {
562 // NPE won't happen bc of regex check
563 logger.error("Parsing input failed", e);
568 if (input.matches(boolRegex)) {
569 return Boolean.parseBoolean(input);
572 // Try to parse a date
573 Date date = Time.utcParse(input);
578 // No special type, return string
583 * Converts a properties object to a string in the format of <pre>[ key=value, key=value, ... ]</pre>
586 * The properties object to format
587 * @return A string in the format <pre>[ key=value, ... ]</pre> or null if the input was null
589 public static String propertiesToString(Properties props) {
593 StringBuilder out = new StringBuilder();
595 for (Object key : props.keySet()) {
596 out.append(String.format(" %s = %s,", key.toString(), props.getProperty(key.toString())));
598 if (props.size() > 0) {
599 out.deleteCharAt(out.lastIndexOf(","));
602 return out.toString();