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=========================================================
26 package org.onap.appc.util;
28 import com.att.eelf.configuration.EELFLogger;
29 import com.att.eelf.configuration.EELFManager;
30 import java.util.Date;
31 import java.util.List;
33 import java.util.Properties;
35 import java.util.regex.Matcher;
36 import java.util.regex.Pattern;
39 * This class contains several static helper methods that can be used to perform string manipulation algorithms.
42 public final class StringHelper {
44 private static final EELFLogger logger = EELFManager.getInstance().getLogger(StringHelper.class);
46 public static final String DASH = "-";
47 public static final String DOT = ".";
48 public static final String ELLIPSES = "...";
49 public static final String LINE_FEED = "\n";
50 public static final String SLASH = "/";
51 public static final String COMMA = ",";
54 * Converts the specified string pattern to a regular expression string. If the supplied string is null or empty,
55 * then a regular expression that matches all strings (.*) is returned.
57 * The expression passed to this method should not already be a regular expression. If it contains problematic
58 * meta-characters for this routine (such as period, asterisk, and plus), they will be escaped and matched literally
59 * in the resulting regular expression returned.
63 * The pattern that we need to convert to a regular expression
64 * @return The regular expression that is equivalent to the pattern
66 public static String convertToRegex(String value) {
67 if (value == null || value.trim().length() == 0) {
71 StringBuilder builder = new StringBuilder(value.trim());
74 * If there are any period characters, we need to escape them so that they are exactly matched
76 Pattern pattern = Pattern.compile("\\.");
77 Matcher matcher = pattern.matcher(builder);
79 while (matcher.find(position)) {
80 builder.replace(matcher.start(), matcher.end(), "\\.");
81 position = matcher.end() + 1;
85 * If there are any asterisks or pluses, which we need to interpret as wildcard characters, we need to convert
88 pattern = Pattern.compile("\\*|\\+");
89 matcher = pattern.matcher(builder);
92 * If the string contains a .* meta-character sequence anywhere in the middle of the string (i.e., there are
93 * other characters following the .* meta-characters), OR the string ends with the .+ sequence, then we need to
94 * append the "end-of-line" boundary condition to the end of the string to get predictable results.
96 if (resolveAppendingEOL(builder, matcher)) {
99 return builder.toString();
102 private static boolean resolveAppendingEOL(StringBuilder builder, Matcher matcher) {
104 boolean appendEOL = false;
106 while (matcher.find(position)) {
107 String metachar = builder.substring(matcher.start(), matcher.end());
108 if ("*".equals(metachar)) {
109 builder.replace(matcher.start(), matcher.end(), ".*");
110 position = matcher.end() + 1;
111 if (matcher.end() < builder.length() - 1) {
114 } else if ("+".equals(metachar)) {
115 builder.replace(matcher.start(), matcher.end(), ".");
116 position = matcher.end();
117 if (matcher.end() == builder.length()) {
126 * Takes a string that may possibly be very long and return a string that is at most maxLength. If the string is
127 * longer than maxLength, the last three characters will be the ellipses (...) to indicate that the string was
130 * @param possiblyLongString
132 * must be at least 4 (one character plus ellipses)
133 * @return possibly shortened string
135 public static String getShortenedString(String possiblyLongString, int maxLength) {
136 if ((possiblyLongString != null) && (maxLength > ELLIPSES.length())
137 && (possiblyLongString.length() > maxLength)) {
138 return possiblyLongString.substring(0, maxLength - ELLIPSES.length()) + ELLIPSES;
141 return possiblyLongString;
145 * Determines that a provided string is not null and not empty (length = 0 after trimming)
148 * The string to be tested
149 * @return true if the string IS NOT null and is NOT empty
151 public static boolean isNotNullNotEmpty(String theString) {
152 return theString != null && !theString.trim().isEmpty();
156 * Determines that a provided string IS null or an empty string (length = 0 after trimming)
159 * The string to be tested
160 * @return true if the string IS null OR is empty
162 public static boolean isNullOrEmpty(String theString) {
163 return theString == null || theString.trim().isEmpty();
167 * Returns an indication if the first string is equal to the second string, allowing for either or both strings to
171 * The first string to be compared
173 * The second string to be compared
174 * @return True if both strings are null, or both strings are non-null AND they are equal. False otherwise.
176 public static boolean areEqual(String a, String b) {
177 return areEqual(a, b, false);
181 * Returns an indication if the first string is equal to the second string, allowing for either or both strings to
182 * be null, and ignoring case.
185 * The first string to be compared
187 * The second string to be compared
188 * @return True if both strings are null, or both strings are non-null AND they are equal (without regard to case).
191 public static boolean equalsIgnoreCase(String a, String b) {
192 return areEqual(a, b, true);
196 * Compares two strings (allowing either or both to be null), and allowing for optional case sensitive or
197 * insensitive comparison.
200 * The first string to be compared
202 * The second string to be compared
203 * @param caseInsensitive
204 * True if the comparison is to be case in-sensitive.
205 * @return True if both strings are null, or both strings are non-null and they are equal
207 private static boolean areEqual(String a, String b, boolean caseInsensitive) {
208 if (a == null && b == null) {
211 if (a != null && b != null) {
212 if (caseInsensitive) {
213 return a.equalsIgnoreCase(b);
223 * This method is used to mangle a name.
225 * This method will first remove all unacceptable characters from the name and translate all characters to lower
226 * case. This is done to eliminate any potentially troublesome characters. If the resulting string is empty, then a
227 * random string of characters for the minimum desired length is returned. If the string is too short to meet the
228 * minimum length requirement, it is padded with random characters.
231 * Once the string has been scrubbed and possibly padded, it may be truncated (if longer than the maximum value) and
232 * the result is returned. To make the string as unique as possible, the algorithm removes excess letters from the
233 * center of the string, concatenating the first nad last parts of the name together. The assumption is that users
234 * tend to start the names of multiple things in similar ways, and get more descriptive as the name progresses. If
235 * for example, several objects were named "A test Object", "A test Object1", and "A test Object2", shortening the
236 * name only from the left does not generate a unique name.
240 * The name to be mangled
242 * minimum number of characters for the name
244 * maximum number of characters for the name
245 * @return The mangled name, or an empty string if the value is null or an empty string.
247 public static String mangleName(String name, int minLen, int maxLen) {
248 StringBuilder builder = new StringBuilder(name == null ? "" : name);
249 Pattern pattern = Pattern.compile("[^a-z0-9]+", Pattern.CASE_INSENSITIVE);
250 Matcher matcher = pattern.matcher(builder);
252 while (matcher.find(position)) {
253 builder.delete(matcher.start(), matcher.end());
254 position = matcher.start();
257 if (builder.length() < minLen) {
258 for (int i = builder.length(); i <= minLen; i++) {
264 * Remove out of the center of the name to preserve start and end and result in a string of max len
266 if (builder.length() > maxLen) {
267 int excess = builder.length() - maxLen;
268 int left = maxLen / 2;
270 builder.delete(left, excess + left);
273 return builder.toString().toLowerCase();
277 * This method is used to normalize a string value.
279 * This method will ensure that the string value is trimmed of all leading and trailing whitespace if not null. If
280 * it is null or an empty string, then it will return null.
284 * The value to be normalized
285 * @return The normalized (no leading or trailing whitespace) value, or null if the string was null or an empty
286 * string (or all whitespace). This method will never return an empty string.
288 public static String normalizeString(String value) {
290 String temp = value.trim();
291 if (temp.length() > 0) {
299 * This method is used to strip all carriage returns and line feed characters from a string
302 * @return The original value less all carriage returns and line feeds
304 public static String stripCRLF(String value) {
309 String[] tokens = value.split("\r\n|\n\r|\r|\n");
310 StringBuilder builder = new StringBuilder();
311 for (String token : tokens) {
312 builder.append(token.trim());
314 return builder.toString();
318 * Converts UNIX-style line endings to DOS-style. Replaces LF with CR+LF as long as the LF does not already exist
322 * The content to be converted
323 * @return The converted content.
325 public static String toDOSLines(String content) {
326 if (content == null) {
330 StringBuilder builder = new StringBuilder(content);
331 Pattern pattern = Pattern.compile("^(\n)[^\r]|[^\r](\n)[^\r]|[^\r](\n)$");
332 Matcher matcher = pattern.matcher(builder);
334 while (matcher.find(position)) {
335 int index = matcher.start(1);
337 index = matcher.start(2);
340 index = matcher.start(3);
343 builder.replace(index, index + 1, "\r\n");
344 position = index + 1;
347 return builder.toString();
351 * This method will convert a string contents to use the UNIX-style line endings. That is, all occurrences of CR
352 * (Carriage Return) and LF (Line Feed) are reduced to just use LF.
355 * The buffer to be processed
356 * @return The converted contents
358 public static String toUnixLines(String content) {
359 if (content == null) {
363 StringBuilder builder = new StringBuilder(content);
364 Pattern pattern = Pattern.compile("\r\n|\n\r");
365 Matcher matcher = pattern.matcher(builder);
367 while (matcher.find(position)) {
368 builder.replace(matcher.start(), matcher.end(), "\n");
369 position = matcher.start();
372 return builder.toString();
376 * This method is used to translate characters in the input sequence that match the characters in the match list to
377 * the corresponding character in the replacement list. If the replacement list is shorter than the match list, then
378 * the character from the replacement list is taken as the modulo of the match character position and the length of
379 * the replacement list.
382 * The input sequence to be processed
384 * The list of matching characters to be searched
386 * The list of replacement characters, positional coincident with the match list. If shorter than the
387 * match list, then the position "wraps" around on the replacement list.
388 * @return The translated string contents.
390 public static Object translate(String sequence, String match, String replacement) {
392 if (sequence == null) {
396 StringBuilder builder = new StringBuilder(sequence);
398 for (int index = 0; index < builder.length(); index++) {
399 char ch = builder.charAt(index);
401 int position = match.indexOf(ch);
402 if (position == -1) {
406 if (position >= replacement.length()) {
407 position %= replacement.length();
409 builder.setCharAt(index, replacement.charAt(position));
412 return builder.toString();
416 * Ensures that the name provided is a valid identifier. This means that no spaces are allowed as well as special
417 * characters. This method translates all spaces and illegal characters to underscores (_).
420 * The name to be checked and converted to an identifier if needed
421 * @return The valid identifier from the name
423 public static String validIdentifier(String name) {
424 if (name == null || name.length() == 0) {
427 StringBuilder builder = new StringBuilder(name);
428 for (int index = 0; index < builder.length(); index++) {
429 char ch = builder.charAt(index);
431 if ((index == 0 && !Character.isJavaIdentifierStart(ch)) || (!Character.isJavaIdentifierPart(ch))) {
432 builder.setCharAt(index, '_');
435 return builder.toString();
440 * Private constructor to prevent instantiation of this class - All methods are static!
442 private StringHelper() {
447 * This method verifies that the provided string only contains characters from the legal set, and replaces any
448 * character not in the legal set with the specified replacement character.
451 * The sequence to be verified
453 * The set of all legal characters
455 * The replacement character if a character is not in the legal set
456 * @return The verified *and possibly updated) string
458 public static String verify(String sequence, String legal, char replacement) {
459 if (sequence == null) {
463 StringBuilder builder = new StringBuilder(sequence);
464 for (int index = 0; index < builder.length(); index++) {
465 char ch = builder.charAt(index);
466 if (legal.indexOf(ch) == -1) {
467 builder.setCharAt(index, replacement);
470 return builder.toString();
475 * The list of elements
476 * @return The list of elements formatted as a comma-delimited list
478 public static String asList(List<String> list) {
479 StringBuilder builder = new StringBuilder();
482 if (list.size() == 1) {
483 builder.append(list.get(0));
485 for (String element : list) {
486 builder.append(element);
487 builder.append(", ");
490 if (builder.length() > 2) {
491 builder.delete(builder.length() - 2, builder.length());
495 return builder.toString();
501 * @return A map expressed as a comma-delimited list of name=value tuples
503 public static String asList(Map<String, String> map) {
504 StringBuilder builder = new StringBuilder();
506 Set<String> keys = map.keySet();
507 for (String key : keys) {
508 builder.append(String.format("%s=%s, ", key, map.get(key)));
511 if (builder.length() > 2) {
512 builder.delete(builder.length() - 2, builder.length());
515 return builder.toString();
520 * An array or varargs of Strings to be concatenated into a comma-separated list
521 * @return The comma-seprated list of values
523 public static String asList(String... values) {
524 StringBuilder builder = new StringBuilder();
526 if (values != null && values.length > 0) {
527 int count = values.length;
528 for (int index = 0; index < count - 1; index++) {
529 builder.append(values[index]);
532 builder.append(values[count - 1]);
535 return builder.toString();
538 public static Object resolveToType(String input) {
539 String intRegex = "^(\\-)?[0-9]+$";
540 String doubleRegex = "^(\\-)?[0-9\\.]+$";
541 String boolRegex = "(^(?i)((true)|(false))$)";
549 if (input.matches(intRegex)) {
551 return Integer.parseInt(input);
552 } catch (NumberFormatException nfe) {
554 logger.error(nfe.getMessage());
558 // Check double (int + decimal point)
559 if (input.matches(doubleRegex)) {
561 return Double.parseDouble(input);
562 } catch (NumberFormatException | NullPointerException e) {
563 // NPE won't happen bc of regex check
564 logger.error("Parsing input failed", e);
569 if (input.matches(boolRegex)) {
570 return Boolean.parseBoolean(input);
573 // Try to parse a date
574 Date date = Time.utcParse(input);
579 // No special type, return string
584 * Converts a properties object to a string in the format of <pre>[ key=value, key=value, ... ]</pre>
587 * The properties object to format
588 * @return A string in the format <pre>[ key=value, ... ]</pre> or null if the input was null
590 public static String propertiesToString(Properties props) {
594 StringBuilder out = new StringBuilder();
596 for (Object key : props.keySet()) {
597 out.append(String.format(" %s = %s,", key.toString(), props.getProperty(key.toString())));
599 if (props.size() > 0) {
600 out.deleteCharAt(out.lastIndexOf(","));
603 return out.toString();