/** * Copyright (c) 2000-present Liferay, Inc. All rights reserved. * * This library is free software; you can redistribute it and/or modify it under * the terms of the GNU Lesser General Public License as published by the Free * Software Foundation; either version 2.1 of the License, or (at your option) * any later version. * * This library is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more * details. */ package com.liferay.portal.kernel.util; import com.liferay.portal.kernel.io.unsync.UnsyncBufferedReader; import com.liferay.portal.kernel.io.unsync.UnsyncStringReader; import com.liferay.portal.kernel.log.Log; import com.liferay.portal.kernel.log.LogFactoryUtil; import com.liferay.portal.kernel.search.highlight.HighlightUtil; import com.liferay.portal.kernel.security.RandomUtil; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.net.URL; import java.util.ArrayList; import java.util.Collection; import java.util.Enumeration; import java.util.List; import java.util.Locale; import java.util.Map; import java.util.Random; import java.util.regex.Pattern; /** * The String utility class. * * @author Brian Wing Shun Chan * @author Sandeep Soni * @author Ganesh Ram * @author Shuyang Zhou * @author Hugo Huijser */ public class StringUtil { /** * Adds string <code>add</code> to string <code>s</code> resulting in a * comma delimited list of strings, disallowing duplicate strings in the * list. * * <p> * The resulting string ends with a comma even if the original string does * not. * </p> * * @param s the original string, representing a comma delimited list of * strings * @param add the string to add to the original, representing the string to * add to the list * @return a string that represents the original string and the added string * separated by a comma, or <code>null</code> if the string to add * is <code>null</code> */ public static String add(String s, String add) { return add(s, add, StringPool.COMMA); } /** * Adds string <code>add</code> to string <code>s</code> that represents a * delimited list of strings, using a specified delimiter and disallowing * duplicate words. * * <p> * The returned string ends with the delimiter even if the original string * does not. * </p> * * @param s the original string, representing a delimited list of strings * @param add the string to add to the original, representing the string to * add to the list * @param delimiter the delimiter used to separate strings in the list * @return a string that represents the original string and the added string * separated by the delimiter, or <code>null</code> if the string to * add or the delimiter string is <code>null</code> */ public static String add(String s, String add, String delimiter) { return add(s, add, delimiter, false); } /** * Adds string <code>add</code> to string <code>s</code> that represents a * delimited list of strings, using a specified delimiter and optionally * allowing duplicate words. * * <p> * The returned string ends with the delimiter even if the original string * does not. * </p> * * @param s the original string, representing a delimited list of strings * @param add the string to add to the original, representing the string to * add to the list * @param delimiter the delimiter used to separate strings in the list * @param allowDuplicates whether to allow duplicate strings * @return a string that represents the original string and the added string * separated by the delimiter, or <code>null</code> if the string to * add or the delimiter string is <code>null</code> */ public static String add( String s, String add, String delimiter, boolean allowDuplicates) { if ((add == null) || (delimiter == null)) { return null; } if (s == null) { s = StringPool.BLANK; } if (allowDuplicates || !contains(s, add, delimiter)) { StringBundler sb = new StringBundler(4); sb.append(s); if (Validator.isNull(s) || s.endsWith(delimiter)) { sb.append(add); sb.append(delimiter); } else { sb.append(delimiter); sb.append(add); sb.append(delimiter); } s = sb.toString(); } return s; } /** * Returns the original string with an appended space followed by the string * value of the suffix surrounded by parentheses. * * <p> * If the original string ends with a numerical parenthetical suffix having * an integer value equal to <code>suffix - 1</code>, then the existing * parenthetical suffix is replaced by the new one. * </p> * * <p> * Examples: * </p> * * <p> * <pre> * <code> * appendParentheticalSuffix("file", 0) returns "file (0)" * appendParentheticalSuffix("file (0)", 0) returns "file (0) (0)" * appendParentheticalSuffix("file (0)", 1) returns "file (1)" * appendParentheticalSuffix("file (0)", 2) returns "file (0) (2)" * </code> * </pre> * </p> * * @param s the original string * @param suffix the suffix to be appended * @return the resultant string whose characters equal those of the original * string, followed by a space, followed by the specified suffix * enclosed in parentheses, or, if the difference between the * provided suffix and the existing suffix is 1, the existing suffix * is incremented by 1 */ public static String appendParentheticalSuffix(String s, int suffix) { if (Pattern.matches(".* \\(" + String.valueOf(suffix - 1) + "\\)", s)) { int pos = s.lastIndexOf(" ("); s = s.substring(0, pos); } return appendParentheticalSuffix(s, String.valueOf(suffix)); } /** * Returns the original string with an appended space followed by the suffix * surrounded by parentheses. * * <p> * Example: * </p> * * <p> * <pre> * <code> * appendParentheticalSuffix("Java", "EE") returns "Java (EE)" * </code> * </pre> * </p> * * @param s the original string * @param suffix the suffix to be appended * @return a string that represents the original string, followed by a * space, followed by the suffix enclosed in parentheses */ public static String appendParentheticalSuffix(String s, String suffix) { StringBundler sb = new StringBundler(5); sb.append(s); sb.append(StringPool.SPACE); sb.append(StringPool.OPEN_PARENTHESIS); sb.append(suffix); sb.append(StringPool.CLOSE_PARENTHESIS); return sb.toString(); } /** * Converts an array of bytes to a string representing the bytes in * hexadecimal form. * * @param bytes the array of bytes to be converted * @return the string representing the bytes in hexadecimal form */ public static String bytesToHexString(byte[] bytes) { char[] chars = new char[bytes.length * 2]; for (int i = 0; i < bytes.length; i++) { chars[i * 2] = HEX_DIGITS[(bytes[i] & 0xFF) >> 4]; chars[i * 2 + 1] = HEX_DIGITS[bytes[i] & 0x0F]; } return new String(chars); } /** * Returns <code>true</code> if the string contains the text as one or more * consecutive comma delimited list entries. * * <p> * Examples: * </p> * * <p> * <pre> * <code> * contains("one,two,three", "two") returns true * contains("one,two,three", "thr") returns false * contains("one,two,three", "one,two") returns true * </code> * </pre> * </p> * * @param s the string in which to search * @param text the text to search for in the string * @return <code>true</code> if the string contains the text as one or more * consecutive comma delimited list entries; <code>false</code> * otherwise */ public static boolean contains(String s, String text) { return contains(s, text, StringPool.COMMA); } /** * Returns <code>true</code> if the string contains the text as one or more * consecutive delimited list entries. * * <p> * Examples: * </p> * * <p> * <pre> * <code> * contains("three...two...one", "two", "...") returns true * contains("three...two...one", "thr", "...") returns false * contains("three...two...one", "two...one", "...") returns true * </code> * </pre> * </p> * * @param s the string in which to search * @param text the text to search for in the string * @param delimiter the delimiter * @return <code>true</code> if the string contains the text as one or more * consecutive delimited list entries; <code>false</code> otherwise */ public static boolean contains(String s, String text, String delimiter) { if ((s == null) || (text == null) || (delimiter == null)) { return false; } if (!s.endsWith(delimiter)) { s = s.concat(delimiter); } String dtd = delimiter.concat(text).concat(delimiter); int pos = s.indexOf(dtd); if (pos == -1) { String td = text.concat(delimiter); if (s.startsWith(td)) { return true; } return false; } return true; } /** * Returns <code>true</code> if the string contains the text as one or more * consecutive comma delimited list entries, ignoring case. * * <p> * Examples: * </p> * * <p> * <pre> * <code> * containsIgnoreCase("one,two,three", "Two") returns true * containsIgnoreCase("one,two,three", "thr") returns false * containsIgnoreCase("one,two,three", "one,two") returns true * </code> * </pre> * </p> * * @param s the string in which to search * @param text the text to search for in the string * @return <code>true</code> if the string contains the text as one or more * consecutive comma delimited list entries; <code>false</code> * otherwise */ public static boolean containsIgnoreCase(String s, String text) { return containsIgnoreCase(s, text, StringPool.COMMA); } /** * Returns <code>true</code> if the string contains the text as one or more * consecutive delimited list entries, ignoring case. * * <p> * Examples: * </p> * * <p> * <pre> * <code> * containsIgnoreCase("three...two...one", "Two", "...") returns true * containsIgnoreCase("three...two...one", "thr", "...") returns false * containsIgnoreCase("three...two...one", "two...one", "...") returns true * </code> * </pre> * </p> * * @param s the string in which to search * @param text the text to search for in the string * @param delimiter the delimiter * @return <code>true</code> if the string contains the text as one or more * consecutive delimited list entries; <code>false</code> otherwise */ public static boolean containsIgnoreCase( String s, String text, String delimiter) { if ((s == null) || (text == null) || (delimiter == null)) { return false; } return contains(toLowerCase(s), toLowerCase(text), delimiter); } public static int count(String s, char c) { return count(s, 0, s.length(), c); } public static int count(String s, int start, int end, char c) { if ((s == null) || s.isEmpty() || ((end - start) < 1)) { return 0; } int count = 0; int pos = start; while ((pos < end) && ((pos = s.indexOf(c, pos)) != -1)) { if (pos < end) { count++; } pos++; } return count; } public static int count(String s, int start, int end, String text) { if ((s == null) || s.isEmpty() || ((end - start) < 1) || (text == null) || text.isEmpty()) { return 0; } int count = 0; int pos = start; while ((pos < end) && (pos = s.indexOf(text, pos)) != -1) { if (pos < end) { count++; } pos += text.length(); } return count; } /** * Returns the number of times the text appears in the string. * * @param s the string in which to search * @param text the text to search for in the string * @return the number of times the text appears in the string */ public static int count(String s, String text) { return count(s, 0, s.length(), text); } /** * Returns <code>true</code> if the string ends with the specified * character, ignoring case. * * @param s the string in which to search * @param end the character to search for at the end of the string * @return <code>true</code> if the string ends with the specified * character, ignoring case; <code>false</code> otherwise */ public static boolean endsWith(String s, char end) { if ((s == null) || s.isEmpty()) { return false; } return equalsIgnoreCase(s.charAt(s.length() - 1), end); } /** * Returns <code>true</code> if the string ends with the string * <code>end</code>, ignoring case. * * @param s the string in which to search * @param end the string to check for at the end of the string * @return <code>true</code> if the string ends with the string * <code>end</code>, ignoring case; <code>false</code> otherwise */ public static boolean endsWith(String s, String end) { if ((s == null) || (end == null)) { return false; } if (end.length() > s.length()) { return false; } String temp = s.substring(s.length() - end.length()); if (equalsIgnoreCase(temp, end)) { return true; } else { return false; } } /** * Returns <code>true</code> if the strings are equal. * * @param s1 the first string to compare * @param s2 the second string to compare * @return <code>true</code> if the strings are equal; * <code>false</code> otherwise */ public static boolean equals(String s1, String s2) { if (s1 == s2) { return true; } if ((s1 == null) || (s2 == null)) { return false; } return s1.equals(s2); } /** * Returns <code>true</code> if the strings are equal, ignoring new line * characters. * * @param s1 the first string to compare * @param s2 the second string to compare * @return <code>true</code> if the strings are equal, ignoring new line * characters; <code>false</code> otherwise */ public static boolean equalsIgnoreBreakLine(String s1, String s2) { if (s1 == s2) { return true; } if ((s1 == null) || (s2 == null)) { return false; } s1 = removeChars(s1, CharPool.RETURN, CharPool.NEW_LINE); s2 = removeChars(s2, CharPool.RETURN, CharPool.NEW_LINE); if (s1.length() != s2.length()) { return false; } return s1.equals(s2); } public static boolean equalsIgnoreCase(char c1, char c2) { if (c1 == c2) { return true; } // Fast fallback for non-acsii code. if ((c1 > 127) || (c2 > 127)) { // Georgian alphabet needs to check both upper and lower case if ((Character.toLowerCase(c1) == Character.toLowerCase(c2)) || (Character.toUpperCase(c1) == Character.toUpperCase(c2))) { return true; } return false; } // Fast fallback for non-letter ascii code if ((c1 < CharPool.UPPER_CASE_A) || (c1 > CharPool.LOWER_CASE_Z) || (c2 < CharPool.UPPER_CASE_A) || (c2 > CharPool.LOWER_CASE_Z)) { return false; } int delta = c1 - c2; if ((delta != 32) && (delta != -32)) { return false; } return true; } /** * Returns <code>true</code> if the strings are equal, ignoring case. * * @param s1 the first string to compare * @param s2 the second string to compare * @return <code>true</code> if the strings are equal, ignoring case; * <code>false</code> otherwise */ public static boolean equalsIgnoreCase(String s1, String s2) { if (s1 == s2) { return true; } if ((s1 == null) || (s2 == null)) { return false; } if (s1.length() != s2.length()) { return false; } for (int i = 0; i < s1.length(); i++) { if (!equalsIgnoreCase(s1.charAt(i), s2.charAt(i))) { return false; } } return true; } /** * Returns the substring of each character instance in string <code>s</code> * that is found in the character array <code>chars</code>. The substring of * characters returned maintain their original order. * * @param s the string from which to extract characters * @param chars the characters to extract from the string * @return the substring of each character instance in string <code>s</code> * that is found in the character array <code>chars</code>, or an * empty string if the given string is <code>null</code> */ public static String extract(String s, char[] chars) { if (s == null) { return StringPool.BLANK; } StringBundler sb = new StringBundler(); for (char c1 : s.toCharArray()) { for (char c2 : chars) { if (c1 == c2) { sb.append(c1); break; } } } return sb.toString(); } /** * Returns the substring of English characters from the string. * * @param s the string from which to extract characters * @return the substring of English characters from the string, or an empty * string if the given string is <code>null</code> */ public static String extractChars(String s) { if (s == null) { return StringPool.BLANK; } StringBundler sb = new StringBundler(); char[] chars = s.toCharArray(); for (char c : chars) { if (Validator.isChar(c)) { sb.append(c); } } return sb.toString(); } /** * Returns a string consisting of all of the digits extracted from the * string. * * @param s the string from which to extract digits * @return a string consisting of all of the digits extracted from the * string */ public static String extractDigits(String s) { if (s == null) { return StringPool.BLANK; } StringBundler sb = new StringBundler(); char[] chars = s.toCharArray(); for (char c : chars) { if (Validator.isDigit(c)) { sb.append(c); } } return sb.toString(); } /** * Returns the substring of <code>s</code> up to but not including the first * occurrence of the delimiter. * * @param s the string from which to extract a substring * @param delimiter the character whose index in the string marks where to * end the substring * @return the substring of <code>s</code> up to but not including the first * occurrence of the delimiter, <code>null</code> if the string is * <code>null</code> or the delimiter does not occur in the string */ public static String extractFirst(String s, char delimiter) { if (s == null) { return null; } int index = s.indexOf(delimiter); if (index < 0) { return null; } else { return s.substring(0, index); } } /** * Returns the substring of <code>s</code> up to but not including the first * occurrence of the delimiter. * * @param s the string from which to extract a substring * @param delimiter the smaller string whose index in the larger string * marks where to end the substring * @return the substring of <code>s</code> up to but not including the first * occurrence of the delimiter, <code>null</code> if the string is * <code>null</code> or the delimiter does not occur in the string */ public static String extractFirst(String s, String delimiter) { if (s == null) { return null; } int index = s.indexOf(delimiter); if (index < 0) { return null; } else { return s.substring(0, index); } } /** * Returns the substring of <code>s</code> after but not including the last * occurrence of the delimiter. * * @param s the string from which to extract the substring * @param delimiter the character whose last index in the string marks * where to begin the substring * @return the substring of <code>s</code> after but not including the last * occurrence of the delimiter, <code>null</code> if the string is * <code>null</code> or the delimiter does not occur in the string */ public static String extractLast(String s, char delimiter) { if (s == null) { return null; } int index = s.lastIndexOf(delimiter); if (index < 0) { return null; } else { return s.substring(index + 1); } } /** * Returns the substring of <code>s</code> after but not including the last * occurrence of the delimiter. * * @param s the string from which to extract the substring * @param delimiter the string whose last index in the string marks where * to begin the substring * @return the substring of <code>s</code> after but not including the last * occurrence of the delimiter, <code>null</code> if the string is * <code>null</code> or the delimiter does not occur in the string */ public static String extractLast(String s, String delimiter) { if (s == null) { return null; } int index = s.lastIndexOf(delimiter); if (index < 0) { return null; } else { return s.substring(index + delimiter.length()); } } /** * Returns the substring of all leading digits of string <code>s</code>, or * an empty string if it has no leading digits. * * @param s the string from which to extract the substring * @return the substring of all leading digits of string <code>s</code>, or * an empty string if it has no leading digits */ public static String extractLeadingDigits(String s) { if (s == null) { return StringPool.BLANK; } StringBundler sb = new StringBundler(); char[] chars = s.toCharArray(); for (char c : chars) { if (Validator.isDigit(c)) { sb.append(c); } else { return sb.toString(); } } return sb.toString(); } /** * @deprecated As of 7.0.0, moved to {@link HighlightUtil#highlight(String, * String[])}} */ @Deprecated public static String highlight(String s, String[] queryTerms) { return HighlightUtil.highlight(s, queryTerms); } /** * @deprecated As of 7.0.0, moved to {@link HighlightUtil#highlight(String, * String[], String, String)}} */ @Deprecated public static String highlight( String s, String[] queryTerms, String highlight1, String highlight2) { return HighlightUtil.highlight(s, queryTerms, highlight1, highlight2); } /** * Returns the index within the string of the first occurrence of any * character from the array. * * <p> * A <code>null</code> string returns <code>-1</code>. A <code>null</code> * or empty array returns <code>-1</code>. * </p> * * <p> * Examples: * </p> * * <p> * <pre> * <code> * indexOfAny(null, *) returns -1 * indexOfAny(*, null) returns -1 * indexOfAny(*, []) returns -1 * indexOfAny("zzabyycdxx", ['a','c']) returns 2 * indexOfAny("zzabyycdxx", ['c','a']) returns 2 * indexOfAny("zzabyycdxx", ['m','n']) returns -1 * </code> * </pre> * </p> * * @param s the string to search (optionally <code>null</code>) * @param chars the characters to search for (optionally <code>null</code>) * @return the index within the string of the first occurrence of any * character from the array, or <code>-1</code> if none of the * characters occur */ public static int indexOfAny(String s, char[] chars) { if (s == null) { return -1; } return indexOfAny(s, chars, 0, s.length() - 1); } /** * Returns the index within the string of the first occurrence of any * character from the array, starting the search at the specified index * within the string. * * <p> * A <code>null</code> string returns <code>-1</code>. A <code>null</code> * or empty array returns <code>-1</code>. * </p> * * <p> * Examples: * </p> * * <p> * <pre> * <code> * indexOfAny(null, *, *) returns -1 * indexOfAny(*, null, *) returns -1 * indexOfAny(*, [], *) returns -1 * indexOfAny("zzabyycdxx", ['a','c'], 3) returns 6 * </code> * </pre> * </p> * * @param s the string to search (optionally <code>null</code>) * @param chars the characters to search for (optionally <code>null</code>) * @param fromIndex the start index within the string * @return the index within the string of the first occurrence of any * character from the array, starting the search at the specified * index within the string, or <code>-1</code> if none of the * characters occur */ public static int indexOfAny(String s, char[] chars, int fromIndex) { if (s == null) { return -1; } return indexOfAny(s, chars, fromIndex, s.length() - 1); } /** * Returns the index within the string of the first occurrence of any * character from the array, up to and including the specified end index * within the string, starting the search at the specified start index * within the string. * * <p> * A <code>null</code> string returns <code>-1</code>. A <code>null</code> * or empty array returns <code>-1</code>. * </p> * * <p> * Examples: * </p> * * <p> * <pre> * <code> * indexOfAny(null, *, *, *) returns -1 * indexOfAny(*, null, *, *) returns -1 * indexOfAny(*, [], *, *) returns -1 * indexOfAny("zzabyycdxx", ['a','c'], 3, 7) returns 6 * </code> * </pre> * </p> * * @param s the string to search (optionally <code>null</code>) * @param chars the characters to search for (optionally <code>null</code>) * @param fromIndex the start index within the string * @param toIndex the end index within the string * @return the index within the string of the first occurrence of any * character from the array, up to and including the specified end * index within the string, starting the search at the specified * start index within the string, or <code>-1</code> if none of the * characters occur */ public static int indexOfAny( String s, char[] chars, int fromIndex, int toIndex) { if ((s == null) || (toIndex < fromIndex)) { return -1; } if (ArrayUtil.isEmpty(chars)) { return -1; } if (fromIndex >= s.length()) { return -1; } if (fromIndex < 0) { fromIndex = 0; } if (toIndex >= s.length()) { toIndex = s.length() - 1; } for (int i = fromIndex; i <= toIndex; i++) { char c = s.charAt(i); for (int j = 0; j < chars.length; j++) { if (c == chars[j]) { return i; } } } return -1; } /** * Returns the index within the string of the first occurrence of any string * from the array. * * <p> * A <code>null</code> string returns <code>-1</code>. A <code>null</code> * or empty array returns <code>-1</code>, but an array containing * <code>""</code> returns <code>0</code> if the string is not * <code>null</code>. * </p> * * <p> * Examples: * </p> * * <p> * <pre> * <code> * indexOfAny(null, *) returns -1 * indexOfAny(*, null) returns -1 * indexOfAny(*, [null]) returns -1 * indexOfAny(*, []) returns -1 * indexOfAny("zzabyycdxx", ["ab","cd"]) returns 2 * indexOfAny("zzabyycdxx", ["cd","ab"]) returns 2 * indexOfAny("zzabyycdxx", ["mn","op"]) returns -1 * indexOfAny("zzabyycdxx", ["mn",""]) returns 0 * </code> * </pre> * </p> * * @param s the string (optionally <code>null</code>) * @param texts the strings to search for (optionally <code>null</code>) * @return the index within the string of the first occurrence of any string * from the array, <code>0</code> if the search array contains * <code>""</code>, or <code>-1</code> if none of the strings occur */ public static int indexOfAny(String s, String[] texts) { if (s == null) { return -1; } return indexOfAny(s, texts, 0, s.length() - 1); } /** * Returns the index within the string of the first occurrence of any string * from the array, starting the search at the specified index within the * string. * * <p> * A <code>null</code> string returns <code>-1</code>. A <code>null</code> * or empty array returns <code>-1</code>, but an array containing * <code>""</code> returns the specified start index if the string is not * <code>null</code>. * </p> * * <p> * Examples: * </p> * * <p> * <pre> * <code> * indexOfAny(null, *, *) returns -1 * indexOfAny(*, null, *) returns -1 * indexOfAny(*, [null], *) returns -1 * indexOfAny(*, [], *) returns -1 * indexOfAny("zzabyycdxx", ["ab","cd"], 3) returns 6 * indexOfAny("zzabyycdxx", ["cd","ab"], 3) returns 6 * indexOfAny("zzabyycdxx", ["mn","op"], *) returns -1 * indexOfAny("zzabyycdxx", ["mn",""], 3) returns 3 * </code> * </pre> * </p> * * @param s the string to search (optionally <code>null</code>) * @param texts the strings to search for (optionally <code>null</code>) * @param fromIndex the start index within the string * @return the index within the string of the first occurrence of any string * from the array, starting the search at the specified index within * the string, the start index if the search array contains * <code>""</code>, or <code>-1</code> if none of the strings occur */ public static int indexOfAny(String s, String[] texts, int fromIndex) { if (s == null) { return -1; } return indexOfAny(s, texts, fromIndex, s.length() - 1); } /** * Returns the index within the string of the first occurrence of any string * from the array, up to and including the specified end index within the * string, starting the search at the specified start index within the * string. * * <p> * A <code>null</code> string returns <code>-1</code>. A <code>null</code> * or empty array returns <code>-1</code>, but an array containing * <code>""</code> returns the specified start index if the string is not * <code>null</code>. * </p> * * <p> * Examples: * </p> * * <p> * <pre> * <code> * indexOfAny(null, *, *, *) returns -1 * indexOfAny(*, null, *, *) returns -1 * indexOfAny(*, [null], *, *) returns -1 * indexOfAny(*, [], *, *) returns -1 * indexOfAny("zzabyycdxx", ["ab","cd"], 3, 7) returns 6 * indexOfAny("zzabyycdxx", ["cd","ab"], 2, 7) returns 2 * indexOfAny("zzabyycdxx", ["mn","op"], *, *) returns -1 * indexOfAny("zzabyycdxx", ["mn",""], 3, *) returns 3 * </code> * </pre> * </p> * * @param s the string to search (optionally <code>null</code>) * @param texts the strings to search for (optionally <code>null</code>) * @param fromIndex the start index within the string * @param toIndex the end index within the string * @return the index within the string of the first occurrence of any string * from the array, up to and including the specified end index * within the string, starting the search at the specified start * index within the string, the start index if the search array * contains <code>""</code>, or <code>-1</code> if none of the * strings occur */ public static int indexOfAny( String s, String[] texts, int fromIndex, int toIndex) { if ((s == null) || (toIndex < fromIndex)) { return -1; } if (ArrayUtil.isEmpty(texts)) { return -1; } if (fromIndex >= s.length()) { return -1; } if (fromIndex < 0) { fromIndex = 0; } if (toIndex >= s.length()) { toIndex = s.length() - 1; } for (int i = fromIndex; i <= toIndex; i++) { for (int j = 0; j < texts.length; j++) { if (texts[j] == null) { continue; } if (((i + texts[j].length()) <= (toIndex + 1)) && s.startsWith(texts[j], i)) { return i; } } } return -1; } /** * Inserts one string into the other at the specified offset index. * * @param s the original string * @param insert the string to be inserted into the original string * @param offset the index of the original string where the insertion * should take place * @return a string representing the original string with the other string * inserted at the specified offset index, or <code>null</code> if * the original string is <code>null</code> */ public static String insert(String s, String insert, int offset) { if (s == null) { return null; } if (insert == null) { return s; } if (offset > s.length()) { return s.concat(insert); } String prefix = s.substring(0, offset); String postfix = s.substring(offset); return prefix.concat(insert).concat(postfix); } /** * Returns <code>true</code> if all the characters in string <code>s</code> * are lower case, ignoring any non-alphabetic characters. * * @param s the string in which to search * @return <code>true</code> if all the characters in string <code>s</code> * are lower case, ignoring any non-alphabetic characters; * <code>false</code> otherwise */ public static boolean isLowerCase(String s) { if (s == null) { return false; } for (int i = 0; i < s.length(); i++) { char c = s.charAt(i); // Fast path for ascii code, fallback to the slow unicode detection if (c <= 127) { if ((c >= CharPool.UPPER_CASE_A) && (c <= CharPool.UPPER_CASE_Z)) { return false; } continue; } if (Character.isLetter(c) && Character.isUpperCase(c)) { return false; } } return true; } /** * Returns <code>true</code> if all the characters in string <code>s</code> * are upper case, ignoring any non-alphabetic characters. * * @param s the string in which to search * @return <code>true</code> if all the characters in string <code>s</code> * are upper case, ignoring any non-alphabetic characters; * <code>false</code> otherwise */ public static boolean isUpperCase(String s) { if (s == null) { return false; } for (int i = 0; i < s.length(); i++) { char c = s.charAt(i); // Fast path for ascii code, fallback to the slow unicode detection if (c <= 127) { if ((c >= CharPool.LOWER_CASE_A) && (c <= CharPool.LOWER_CASE_Z)) { return false; } continue; } if (Character.isLetter(c) && Character.isLowerCase(c)) { return false; } } return true; } /** * Returns the index within the string of the last occurrence of any * character from the array. * * <p> * A <code>null</code> string returns <code>-1</code>. A <code>null</code> * or empty array returns <code>-1</code>. * </p> * * <p> * Examples: * </p> * * <p> * <pre> * <code> * lastIndexOfAny(null, *) returns -1 * lastIndexOfAny(*, null) returns -1 * lastIndexOfAny(*, []) returns -1 * lastIndexOfAny("zzabyycdxx", ['a','c']) returns 6 * lastIndexOfAny("zzabyycdxx", ['c','a']) returns 6 * lastIndexOfAny("zzabyycdxx", ['m','n']) returns -1 * </code> * </pre> * </p> * * @param s the string to search (optionally <code>null</code>) * @param chars the characters to search for (optionally <code>null</code>) * @return the index within the string of the last occurrence of any * character from the array, or <code>-1</code> if none of the * characters occur */ public static int lastIndexOfAny(String s, char[] chars) { if (s == null) { return -1; } return lastIndexOfAny(s, chars, 0, s.length() - 1); } /** * Returns the index within the string of the last occurrence of any * character from the array, starting the search at the specified index * within the string. * * <p> * A <code>null</code> string returns <code>-1</code>. A <code>null</code> * or empty array returns <code>-1</code>. * </p> * * <p> * Examples: * </p> * * <p> * <pre> * <code> * lastIndexOfAny(null, *, *) returns -1 * lastIndexOfAny(*, null, *) returns -1 * lastIndexOfAny(*, [], *) returns -1 * lastIndexOfAny("zzabyycdxx", ['a','c'], 5) returns 2 * lastIndexOfAny("zzabyycdxx", ['m','n'], *) returns -1 * </code> * </pre> * </p> * * @param s the string to search (optionally <code>null</code>) * @param chars the characters to search for (optionally <code>null</code>) * @param toIndex the end index within the string * @return the index within the string of the last occurrence of any * character from the array, starting the search at the specified * index within the string, or <code>-1</code> if none of the * characters occur */ public static int lastIndexOfAny(String s, char[] chars, int toIndex) { if (s == null) { return -1; } return lastIndexOfAny(s, chars, 0, toIndex); } /** * Returns the index within the string of the last occurrence of any * character from the array, up to and including the specified end index * within the string, starting the search at the specified start index * within the string. * * <p> * A <code>null</code> string returns <code>-1</code>. A <code>null</code> * or empty array returns <code>-1</code>. * </p> * * <p> * Examples: * </p> * * <p> * <pre> * <code> * lastIndexOfAny(null, *, *, *) returns -1 * lastIndexOfAny(*, null, *, *) returns -1 * lastIndexOfAny(*, [], *, *) returns -1 * lastIndexOfAny("zzabyycdxx", ['a','c'], 5, 7) returns 6 * lastIndexOfAny("zzabyycdxx", ['m','n'], *, *) returns -1 * </code> * </pre> * </p> * * @param s the string to search (optionally <code>null</code>) * @param chars the characters to search for (optionally <code>null</code>) * @param fromIndex the start index within the string * @param toIndex the end index within the string * @return the index within the string of the last occurrence of any * character from the array, up to and including the specified end * index within the string, starting the search at the specified * start index within the string, or <code>-1</code> if none of the * characters occur */ public static int lastIndexOfAny( String s, char[] chars, int fromIndex, int toIndex) { if ((s == null) || (toIndex < fromIndex)) { return -1; } if (ArrayUtil.isEmpty(chars)) { return -1; } if (fromIndex >= s.length()) { return -1; } if (fromIndex < 0) { fromIndex = 0; } if (toIndex >= s.length()) { toIndex = s.length() - 1; } for (int i = toIndex; i >= fromIndex; i--) { char c = s.charAt(i); for (int j = 0; j < chars.length; j++) { if (c == chars[j]) { return i; } } } return -1; } /** * Returns the index within the string of the last occurrence of any string * from the array. * * <p> * A <code>null</code> string returns <code>-1</code>. A <code>null</code> * or empty array returns <code>-1</code>, but an array containing * <code>""</code> returns <code>0</code> if the string is not * <code>null</code>. * </p> * * <p> * Examples: * </p> * * <p> * <pre> * <code> * lastIndexOfAny(null, *) returns -1 * lastIndexOfAny(*, null) returns -1 * lastIndexOfAny(*, []) returns -1 * lastIndexOfAny(*, [null]) returns -1 * lastIndexOfAny("zzabyycdxx", ["ab","cd"]) returns 6 * lastIndexOfAny("zzabyycdxx", ["cd","ab"]) returns 6 * lastIndexOfAny("zzabyycdxx", ["mn","op"]) returns -1 * lastIndexOfAny("zzabyycdxx", ["mn",""]) returns 10 * </code> * </pre> * </p> * * @param s the string to search (optionally <code>null</code>) * @param texts the strings to search for (optionally <code>null</code>) * @return the index within the string of the last occurrence of any string * from the array, <code>0</code> if the search array contains * <code>""</code>, or <code>-1</code> if none of the strings occur */ public static int lastIndexOfAny(String s, String[] texts) { if (s == null) { return -1; } return lastIndexOfAny(s, texts, 0, s.length() - 1); } /** * Returns the index within the string of the last occurrence of any string * from the array, starting the search at the specified index within the * string. * * <p> * A <code>null</code> string returns <code>-1</code>. A <code>null</code> * or empty array returns <code>-1</code>, but an array containing * <code>""</code> returns the specified start index if the string is not * <code>null</code>. * </p> * * <p> * Examples: * </p> * * <p> * <pre> * <code> * lastIndexOfAny(null, *, *) returns -1 * lastIndexOfAny(*, null, *) returns -1 * lastIndexOfAny(*, [], *) returns -1 * lastIndexOfAny(*, [null], *) returns -1 * lastIndexOfAny("zzabyycdxx", ["ab","cd"], 5) returns 2 * lastIndexOfAny("zzabyycdxx", ["cd","ab"], 5) returns 2 * lastIndexOfAny("zzabyycdxx", ["mn","op"], *) returns -1 * lastIndexOfAny("zzabyycdxx", ["mn",""], 5) returns 5 * </code> * </pre> * </p> * * @param s the string to search (optionally <code>null</code>) * @param texts the strings to search for (optionally <code>null</code>) * @param toIndex the end index within the string * @return the index within the string of the last occurrence of any string * from the array, starting the search at the specified index within * the string, the start index if the search array contains * <code>""</code>, or <code>-1</code> if none of the strings occur */ public static int lastIndexOfAny(String s, String[] texts, int toIndex) { if (s == null) { return -1; } return lastIndexOfAny(s, texts, 0, toIndex); } /** * Returns the index within the string of the last occurrence of any string * from the array, up to and including the specified end index within the * string, starting the search at the specified start index within the * string. * * <p> * A <code>null</code> string returns <code>-1</code>. A <code>null</code> * or empty array returns <code>-1</code>, but an array containing * <code>""</code> returns the specified end index if the string is not * <code>null</code>. * </p> * * <p> * Examples: * </p> * * <p> * <pre> * <code> * lastIndexOfAny(null, *, *, *) returns -1 * lastIndexOfAny(*, null, *, *) returns -1 * lastIndexOfAny(*, [], *, *) returns -1 * lastIndexOfAny(*, [null], *, *) returns -1 * lastIndexOfAny("zzabyycdxx", ["ab","cd"], 2, 5) returns 2 * lastIndexOfAny("zzabyycdxx", ["mn","op"], *, *) returns -1 * lastIndexOfAny("zzabyycdxx", ["mn",""], 2, 5) returns 5 * </code> * </pre> * </p> * * @param s the string to search (optionally <code>null</code>) * @param texts the strings to search for (optionally <code>null</code>) * @param fromIndex the start index within the string * @param toIndex the end index within the string * @return the index within the string of the last occurrence of any string * from the array, up to and including the specified end index * within the string, starting the search at the specified start * index within the string, the end index if the search array * contains <code>""</code>, or <code>-1</code> if none of the * strings occur */ public static int lastIndexOfAny( String s, String[] texts, int fromIndex, int toIndex) { if ((s == null) || (toIndex < fromIndex)) { return -1; } if (ArrayUtil.isEmpty(texts)) { return -1; } if (fromIndex >= s.length()) { return -1; } if (fromIndex < 0) { fromIndex = 0; } if (toIndex >= s.length()) { toIndex = s.length() - 1; } for (int i = toIndex; i >= fromIndex; i--) { for (int j = 0; j < texts.length; j++) { if (texts[j] == null) { continue; } if (((i + texts[j].length()) <= (toIndex + 1)) && s.startsWith(texts[j], i)) { return i; } } } return -1; } /** * Converts all of the characters in the string to lower case. * * @param s the string to convert * @return the string, converted to lower case, or <code>null</code> if the * string is <code>null</code> * @see String#toLowerCase() */ public static String lowerCase(String s) { return toLowerCase(s); } /** * Converts all of the characters in the arbitrary number of strings to * lower case. * * @param array the array or sequence of string arguments */ public static void lowerCase(String... array) { if (array != null) { for (int i = 0; i < array.length; i++) { array[i] = toLowerCase(array[i]); } } } /** * Converts the first character of the string to lower case. * * @param s the string whose first character is to be converted * @return the string, with its first character converted to lower-case */ public static String lowerCaseFirstLetter(String s) { char[] chars = s.toCharArray(); if ((chars[0] >= 65) && (chars[0] <= 90)) { chars[0] = (char)(chars[0] + 32); } return new String(chars); } /** * Returns <code>true</code> if the specified pattern occurs at any position * in the string. * * @param s the string * @param pattern the pattern to search for in the string * @return <code>true</code> if the specified pattern occurs at any position * in the string */ public static boolean matches(String s, String pattern) { String[] array = pattern.split("\\*"); for (String element : array) { int pos = s.indexOf(element); if (pos == -1) { return false; } s = s.substring(pos + element.length()); } return true; } /** * Returns <code>true</code> if the specified pattern occurs at any position * in the string, ignoring case. * * @param s the string * @param pattern the pattern to search for in the string * @return <code>true</code> if the specified pattern occurs at any position * in the string */ public static boolean matchesIgnoreCase(String s, String pattern) { return matches(lowerCase(s), lowerCase(pattern)); } /** * Merges the elements of the boolean array into a string representing a * comma delimited list of its values. * * @param array the boolean values to merge * @return a string representing a comma delimited list of the values of the * boolean array, an empty string if the array is empty, or * <code>null</code> if the array is <code>null</code> */ public static String merge(boolean[] array) { return merge(array, StringPool.COMMA); } /** * Merges the elements of the boolean array into a string representing a * delimited list of its values. * * @param array the boolean values to merge * @param delimiter the delimiter * @return a string representing a comma delimited list of the values of the * boolean array, an empty string if the array is empty, or * <code>null</code> if the array is <code>null</code> */ public static String merge(boolean[] array, String delimiter) { if (array == null) { return null; } if (array.length == 0) { return StringPool.BLANK; } if (array.length == 1) { return String.valueOf(array[0]); } StringBundler sb = new StringBundler(2 * array.length - 1); for (int i = 0; i < array.length; i++) { if (i != 0) { sb.append(delimiter); } sb.append(String.valueOf(array[i])); } return sb.toString(); } /** * Merges the elements of the character array into a string representing a * comma delimited list of its values. * * @param array the characters to merge * @return a string representing a comma delimited list of the values of the * character array, an empty string if the array is empty, or * <code>null</code> if the array is <code>null</code> */ public static String merge(char[] array) { return merge(array, StringPool.COMMA); } /** * Merges the elements of the character array into a string representing a * delimited list of its values. * * @param array the characters to merge * @param delimiter the delimiter * @return a string representing a delimited list of the values of the * character array, an empty string if the array is empty, or * <code>null</code> if the array is <code>null</code> */ public static String merge(char[] array, String delimiter) { if (array == null) { return null; } if (array.length == 0) { return StringPool.BLANK; } if (array.length == 1) { return String.valueOf(array[0]); } StringBundler sb = new StringBundler(2 * array.length - 1); for (int i = 0; i < array.length; i++) { if (i != 0) { sb.append(delimiter); } sb.append(String.valueOf(array[i])); } return sb.toString(); } /** * Merges the elements of the collection by returning a string representing * a comma delimited list of its values. * * @param col the collection of objects * @return the merged collection elements, or <code>null</code> if the * collection is <code>null</code> */ public static String merge(Collection<?> col) { return merge(col, StringPool.COMMA); } /** * Merges the elements of the collection by returning a string representing * a delimited list of its values. * * @param col the collection of objects * @param delimiter the string whose last index in the string marks where * to begin the substring * @return the merged collection elements, or <code>null</code> if the * collection is <code>null</code> */ public static String merge(Collection<?> col, String delimiter) { if (col == null) { return null; } if (col.isEmpty()) { return StringPool.BLANK; } StringBundler sb = new StringBundler(2 * col.size()); for (Object object : col) { String objectString = String.valueOf(object); sb.append(objectString.trim()); sb.append(delimiter); } sb.setIndex(sb.index() - 1); return sb.toString(); } /** * Merges the elements of an array of double-precision decimal numbers by * returning a string representing a comma delimited list of its values. * * @param array the doubles to merge * @return a string representing a comma delimited list of the values of the * array of double-precision decimal numbers, an empty string if the * array is empty, or <code>null</code> if the array is * <code>null</code> */ public static String merge(double[] array) { return merge(array, StringPool.COMMA); } /** * Merges the elements of an array of double-precision decimal numbers by * returning a string representing a delimited list of its values. * * @param array the doubles to merge * @param delimiter the delimiter * @return a string representing a delimited list of the values of the array * of double-precision decimal numbers, an empty string if the array * is empty, or <code>null</code> if the array is <code>null</code> */ public static String merge(double[] array, String delimiter) { if (array == null) { return null; } if (array.length == 0) { return StringPool.BLANK; } if (array.length == 1) { return String.valueOf(array[0]); } StringBundler sb = new StringBundler(2 * array.length - 1); for (int i = 0; i < array.length; i++) { if (i != 0) { sb.append(delimiter); } sb.append(String.valueOf(array[i])); } return sb.toString(); } /** * Merges the elements of an array of decimal numbers into a string * representing a comma delimited list of its values. * * @param array the floats to merge * @return a string representing a comma delimited list of the values of the * array of decimal numbers, an empty string if the array is empty, * or <code>null</code> if the array is <code>null</code> */ public static String merge(float[] array) { return merge(array, StringPool.COMMA); } /** * Merges the elements of an array of decimal numbers into a string * representing a delimited list of its values. * * @param array the floats to merge * @param delimiter the delimiter * @return a string representing a delimited list of the values of the array * of decimal numbers, an empty string if the array is empty, or * <code>null</code> if the array is <code>null</code> */ public static String merge(float[] array, String delimiter) { if (array == null) { return null; } if (array.length == 0) { return StringPool.BLANK; } if (array.length == 1) { return String.valueOf(array[0]); } StringBundler sb = new StringBundler(2 * array.length - 1); for (int i = 0; i < array.length; i++) { if (i != 0) { sb.append(delimiter); } sb.append(String.valueOf(array[i])); } return sb.toString(); } /** * Merges the elements of an array of integers into a string representing a * comma delimited list of its values. * * @param array the integers to merge * @return a string representing a comma delimited list of the values of the * array of integers, an empty string if the array is empty, or * <code>null</code> if the array is <code>null</code> */ public static String merge(int[] array) { return merge(array, StringPool.COMMA); } /** * Merges the elements of an array of integers into a string representing a * delimited list of its values. * * @param array the integers to merge * @param delimiter the delimiter * @return a string representing a delimited list of the values of the array * of integers, an empty string if the array is empty, or * <code>null</code> if the array is <code>null</code> */ public static String merge(int[] array, String delimiter) { if (array == null) { return null; } if (array.length == 0) { return StringPool.BLANK; } if (array.length == 1) { return String.valueOf(array[0]); } StringBundler sb = new StringBundler(2 * array.length - 1); for (int i = 0; i < array.length; i++) { if (i != 0) { sb.append(delimiter); } sb.append(String.valueOf(array[i])); } return sb.toString(); } /** * Merges the elements of an array of long integers by returning a string * representing a comma delimited list of its values. * * @param array the long integers to merge * @return a string representing a comma delimited list of the values of the * array of long integers, an empty string if the array is empty, or * <code>null</code> if the array is <code>null</code> */ public static String merge(long[] array) { return merge(array, StringPool.COMMA); } /** * Merges the elements of an array of long integers by returning a string * representing a delimited list of its values. * * @param array the long integers to merge * @param delimiter the delimiter * @return a string representing a delimited list of the values of the array * of long integers, an empty string if the array is empty, or * <code>null</code> if the array is <code>null</code> */ public static String merge(long[] array, String delimiter) { if (array == null) { return null; } if (array.length == 0) { return StringPool.BLANK; } if (array.length == 1) { return String.valueOf(array[0]); } StringBundler sb = new StringBundler(2 * array.length - 1); for (int i = 0; i < array.length; i++) { if (i != 0) { sb.append(delimiter); } sb.append(String.valueOf(array[i])); } return sb.toString(); } /** * Merges the elements of an array of objects into a string representing a * comma delimited list of the objects. * * @param array the objects to merge * @return a string representing a comma delimited list of the objects, an * empty string if the array is empty, or <code>null</code> if the * array is <code>null</code> */ public static String merge(Object[] array) { return merge(array, StringPool.COMMA); } /** * Merges the elements of an array of objects into a string representing a * delimited list of the objects. * * @param array the objects to merge * @param delimiter the delimiter * @return a string representing a delimited list of the objects, an empty * string if the array is empty, or <code>null</code> if the array * is <code>null</code> */ public static String merge(Object[] array, String delimiter) { if (array == null) { return null; } if (array.length == 0) { return StringPool.BLANK; } if (array.length == 1) { return String.valueOf(array[0]); } StringBundler sb = new StringBundler(2 * array.length - 1); for (int i = 0; i < array.length; i++) { if (i != 0) { sb.append(delimiter); } sb.append(String.valueOf(array[i]).trim()); } return sb.toString(); } /** * Merges the elements of an array of short integers by returning a string * representing a comma delimited list of its values. * * @param array the short integers to merge * @return a string representing a comma delimited list of the values of the * array of short integers, an empty string if the array is empty, * or <code>null</code> if the array is <code>null</code> */ public static String merge(short[] array) { return merge(array, StringPool.COMMA); } /** * Merges the elements of an array of short integers by returning a string * representing a delimited list of its values. * * @param array the short integers to merge * @param delimiter the delimiter * @return a string representing a delimited list of the values of the array * of short integers, an empty string if the array is empty, or * <code>null</code> if the array is <code>null</code> */ public static String merge(short[] array, String delimiter) { if (array == null) { return null; } if (array.length == 0) { return StringPool.BLANK; } if (array.length == 1) { return String.valueOf(array[0]); } StringBundler sb = new StringBundler(2 * array.length - 1); for (int i = 0; i < array.length; i++) { if (i != 0) { sb.append(delimiter); } sb.append(String.valueOf(array[i])); } return sb.toString(); } /** * Returns the string enclosed by apostrophes. * * <p> * Example: * </p> * * <p> * <pre> * <code> * quote("Hello, World!") returns "'Hello, World!'" * </code> * </pre> * </p> * * @param s the string to enclose in apostrophes * @return the string enclosed by apostrophes, or <code>null</code> if the * string is <code>null</code> */ public static String quote(String s) { return quote(s, CharPool.APOSTROPHE); } /** * Returns the string enclosed by the quote character. * * <p> * Example: * </p> * * <p> * <pre> * <code> * quote("PATH", '%') returns "%PATH%" * </code> * </pre> * </p> * * @param s the string to enclose in quotes * @param quote the character to insert to insert to the beginning of and * append to the end of the string * @return the string enclosed in the quote characters, or <code>null</code> * if the string is <code>null</code> */ public static String quote(String s, char quote) { if (s == null) { return null; } return quote(s, String.valueOf(quote)); } /** * Returns the string enclosed by the quote strings. * * <p> * Example: * </p> * * <p> * <pre> * <code> * quote("WARNING", "!!!") returns "!!!WARNING!!!" * </code> * </pre> * </p> * * @param s the string to enclose in quotes * @param quote the quote string to insert to insert to the beginning of * and append to the end of the string * @return the string enclosed in the quote strings, or <code>null</code> if * the string is <code>null</code> */ public static String quote(String s, String quote) { if (s == null) { return null; } return quote.concat(s).concat(quote); } /** * Returns a randomized string of four lower case, alphabetic characters. * * @return a randomized string of four lower case, alphabetic characters */ public static String randomId() { Random random = new Random(); char[] chars = new char[4]; for (int i = 0; i < 4; i++) { chars[i] = (char)(CharPool.LOWER_CASE_A + random.nextInt(26)); } return new String(chars); } /** * Pseudorandomly permutes the characters of the string. * * @param s the string whose characters are to be randomized * @return a string of the same length as the string whose characters * represent a pseudorandom permutation of the characters of the * string */ public static String randomize(String s) { return RandomUtil.shuffle(s); } /** * Returns a randomized string of eight characters consisting of lower case * letters, upper case letters, and single-digit whole numbers. * * @return a randomized string of eight characters consisting of lower case * letters, upper case letters, and single-digit whole numbers */ public static String randomString() { return randomString(8); } /** * Returns a randomized string of the specified length consisting of lower * case letters, upper case letters, and single-digit whole numbers. * * @param length the character length of the randomized string * @return a randomized string of the specified length consisting of lower * case letters, upper case letters, and single-digit whole numbers */ public static String randomString(int length) { Random random = new Random(); char[] chars = new char[length]; for (int i = 0; i < length; i++) { int index = random.nextInt(_RANDOM_STRING_CHAR_TABLE.length); chars[i] = _RANDOM_STRING_CHAR_TABLE[index]; } return new String(chars); } public static String read(Class<?> clazz, String name) { try (InputStream inputStream = clazz.getResourceAsStream(name)) { return read(inputStream); } catch (IOException ioe) { return ReflectionUtil.throwException(ioe); } } public static String read(ClassLoader classLoader, String name) throws IOException { return read(classLoader, name, false); } public static String read(ClassLoader classLoader, String name, boolean all) throws IOException { if (all) { StringBundler sb = new StringBundler(); Enumeration<URL> enu = classLoader.getResources(name); while (enu.hasMoreElements()) { URL url = enu.nextElement(); InputStream is = url.openStream(); if (is == null) { throw new IOException( "Unable to open resource at " + url.toString()); } try { String s = read(is); if (s != null) { sb.append(s); sb.append(StringPool.NEW_LINE); } } finally { StreamUtil.cleanUp(is); } } return sb.toString().trim(); } InputStream is = classLoader.getResourceAsStream(name); if (is == null) { throw new IOException( "Unable to open resource in class loader " + name); } try { String s = read(is); return s; } finally { StreamUtil.cleanUp(is); } } public static String read(InputStream is) throws IOException { StringBundler sb = new StringBundler(); try (UnsyncBufferedReader unsyncBufferedReader = new UnsyncBufferedReader(new InputStreamReader(is))) { String line = null; while ((line = unsyncBufferedReader.readLine()) != null) { sb.append(line); sb.append(CharPool.NEW_LINE); } } return sb.toString().trim(); } public static void readLines(InputStream is, Collection<String> lines) throws IOException { try (UnsyncBufferedReader unsyncBufferedReader = new UnsyncBufferedReader(new InputStreamReader(is))) { String line = null; while ((line = unsyncBufferedReader.readLine()) != null) { lines.add(line); } } } /** * @deprecated As of 7.0.0, replaced by {@link #removeFromList(String, * String)} */ @Deprecated public static String remove(String s, String element) { return removeFromList(s, element, StringPool.COMMA); } /** * @deprecated As of 7.0.0, replaced by {@link #removeFromList(String, * String, String)} */ @Deprecated public static String remove(String s, String element, String delimiter) { return removeFromList(s, element, delimiter); } public static String removeChar(String s, char oldSub) { if (s == null) { return null; } int y = s.indexOf(oldSub); if (y >= 0) { StringBundler sb = new StringBundler(); int x = 0; while (x <= y) { sb.append(s.substring(x, y)); x = y + 1; y = s.indexOf(oldSub, x); } sb.append(s.substring(x)); return sb.toString(); } else { return s; } } public static String removeChars(String s, char... oldSubs) { if (s == null) { return null; } if (oldSubs == null) { return s; } StringBuilder sb = new StringBuilder(s.length()); iterate: for (int i = 0; i < s.length(); i++) { char c = s.charAt(i); for (int j = 0; j < oldSubs.length; j++) { if (c == oldSubs[j]) { continue iterate; } } sb.append(c); } if (s.length() == sb.length()) { return s; } return sb.toString(); } /** * Removes the <code>remove</code> string from string <code>s</code> that * represents a list of comma delimited strings. * * <p> * The resulting string ends with a comma even if the original string does * not. * </p> * * <p> * Examples: * </p> * * <p> * <pre> * <code> * remove("red,blue,green,yellow", "blue") returns "red,green,yellow," * remove("blue", "blue") returns "" * remove("blue,", "blue") returns "" * </code> * </pre> * </p> * * @param s the string representing the list of comma delimited strings * @param element the string to remove * @return a string representing the list of comma delimited strings with * the <code>remove</code> string removed, or <code>null</code> if * the original string, the string to remove, or the delimiter is * <code>null</code> */ public static String removeFromList(String s, String element) { return removeFromList(s, element, StringPool.COMMA); } /** * Removes the <code>remove</code> string from string <code>s</code> that * represents a list of delimited strings. * * <p> * The resulting string ends with the delimiter even if the original string * does not. * </p> * * <p> * Examples: * </p> * * <p> * <pre> * <code> * remove("red;blue;green;yellow", "blue", ";") returns "red;green;yellow;" * remove("blue", "blue", ";") returns "" * remove("blue;", "blue", ";") returns "" * </code> * </pre> * </p> * * @param s the string representing the list of delimited strings * @param element the string to remove * @param delimiter the delimiter * @return a string representing the list of delimited strings with the * <code>remove</code> string removed, or <code>null</code> if the * original string, the string to remove, or the delimiter is * <code>null</code> */ public static String removeFromList( String s, String element, String delimiter) { if ((s == null) || (element == null) || (delimiter == null)) { return null; } if (Validator.isNotNull(s) && !s.endsWith(delimiter)) { s += delimiter; } String drd = delimiter.concat(element).concat(delimiter); String rd = element.concat(delimiter); while (contains(s, element, delimiter)) { int pos = s.indexOf(drd); if (pos == -1) { if (s.startsWith(rd)) { int x = element.length() + delimiter.length(); int y = s.length(); s = s.substring(x, y); } } else { int x = pos + element.length() + delimiter.length(); int y = s.length(); String temp = s.substring(0, pos); s = temp.concat(s.substring(x, y)); } } return s; } public static String removeSubstring(String s, String oldSub) { if (s == null) { return null; } if (oldSub == null) { return s; } int y = s.indexOf(oldSub); if (y >= 0) { StringBundler sb = new StringBundler(); int length = oldSub.length(); int x = 0; while (x <= y) { sb.append(s.substring(x, y)); x = y + length; y = s.indexOf(oldSub, x); } sb.append(s.substring(x)); return sb.toString(); } else { return s; } } public static String removeSubstrings(String s, String... oldSubs) { if (s == null) { return null; } if (ArrayUtil.isEmpty(oldSubs)) { return s; } for (String oldSub : oldSubs) { s = removeSubstring(s, oldSub); } return s; } /** * Replaces all occurrences of the character with the new character. * * @param s the original string * @param oldSub the character to be searched for and replaced in the * original string * @param newSub the character with which to replace the * <code>oldSub</code> character * @return a string representing the original string with all occurrences of * the <code>oldSub</code> character replaced with the * <code>newSub</code> character, or <code>null</code> if the * original string is <code>null</code> */ public static String replace(String s, char oldSub, char newSub) { if (s == null) { return null; } return s.replace(oldSub, newSub); } /** * Replaces all occurrences of the character with the new string. * * @param s the original string * @param oldSub the character to be searched for and replaced in the * original string * @param newSub the string with which to replace the <code>oldSub</code> * character * @return a string representing the original string with all occurrences of * the <code>oldSub</code> character replaced with the string * <code>newSub</code>, or <code>null</code> if the original string * is <code>null</code> */ public static String replace(String s, char oldSub, String newSub) { if ((s == null) || (newSub == null)) { return null; } int index = s.indexOf(oldSub); if (index == -1) { return s; } int previousIndex = index; StringBundler sb = new StringBundler(); if (previousIndex != 0) { sb.append(s.substring(0, previousIndex)); } sb.append(newSub); while ((index = s.indexOf(oldSub, previousIndex + 1)) != -1) { sb.append(s.substring(previousIndex + 1, index)); sb.append(newSub); previousIndex = index; } index = previousIndex + 1; if (index < s.length()) { sb.append(s.substring(index)); } return sb.toString(); } public static String replace(String s, char[] oldSubs, char[] newSubs) { if ((s == null) || (oldSubs == null) || (newSubs == null)) { return null; } if (oldSubs.length != newSubs.length) { return s; } StringBuilder sb = new StringBuilder(s.length()); sb.append(s); boolean modified = false; for (int i = 0; i < sb.length(); i++) { char c = sb.charAt(i); for (int j = 0; j < oldSubs.length; j++) { if (c == oldSubs[j]) { sb.setCharAt(i, newSubs[j]); modified = true; break; } } } if (modified) { return sb.toString(); } return s; } public static String replace(String s, char[] oldSubs, String[] newSubs) { if ((s == null) || (oldSubs == null) || (newSubs == null)) { return null; } if (oldSubs.length != newSubs.length) { return s; } StringBundler sb = null; int lastReplacementIndex = 0; for (int i = 0; i < s.length(); i++) { char c = s.charAt(i); for (int j = 0; j < oldSubs.length; j++) { if (c == oldSubs[j]) { if (sb == null) { sb = new StringBundler(); } if (i > lastReplacementIndex) { sb.append(s.substring(lastReplacementIndex, i)); } sb.append(newSubs[j]); lastReplacementIndex = i + 1; break; } } } if (sb == null) { return s; } if (lastReplacementIndex < s.length()) { sb.append(s.substring(lastReplacementIndex)); } return sb.toString(); } /** * Replaces all occurrences of the string with the new string. * * @param s the original string * @param oldSub the string to be searched for and replaced in the original * string * @param newSub the string with which to replace the <code>oldSub</code> * string * @return a string representing the original string with all occurrences of * the <code>oldSub</code> string replaced with the string * <code>newSub</code>, or <code>null</code> if the original string * is <code>null</code> */ public static String replace(String s, String oldSub, String newSub) { return replace(s, oldSub, newSub, 0); } /** * Replaces all occurrences of the string with the new string, starting from * the specified index. * * @param s the original string * @param oldSub the string to be searched for and replaced in the original * string * @param newSub the string with which to replace the <code>oldSub</code> * string * @param fromIndex the index of the original string from which to begin * searching * @return a string representing the original string with all occurrences of * the <code>oldSub</code> string occurring after the specified * index replaced with the string <code>newSub</code>, or * <code>null</code> if the original string is <code>null</code> */ public static String replace( String s, String oldSub, String newSub, int fromIndex) { if (s == null) { return null; } if ((oldSub == null) || oldSub.equals(StringPool.BLANK)) { return s; } if (newSub == null) { newSub = StringPool.BLANK; } int y = s.indexOf(oldSub, fromIndex); if (y >= 0) { StringBundler sb = new StringBundler(); int length = oldSub.length(); int x = 0; while (x <= y) { sb.append(s.substring(x, y)); sb.append(newSub); x = y + length; y = s.indexOf(oldSub, x); } sb.append(s.substring(x)); return sb.toString(); } else { return s; } } /** * Replaces all occurrences of the keywords found in the substring, defined * by the beginning and ending strings, with the new values. * * <p> * For example, with the following initialized variables: * </p> * * <p> * <pre> * <code> * String s = "http://www.example-url/${userId}"; * String begin = "${"; * String end = "}"; * Map<String, String> values = new HashMap<String, String>(); * values.put("userId", "jbloggs"); * </code> * </pre> * </p> * * <p> * <code>replace(s, begin, end, values)</code> returns * <code>"http://www.example-url/jbloggs"</code> * </p> * * @param s the original string * @param begin the string preceding the substring to be modified. This * string is excluded from the result. * @param end the string following the substring to be modified. This * string is excluded from the result. * @param values the key-value map values * @return a string representing the original string with all occurrences of * the of the keywords found in the substring, replaced with the new * values. <code>null</code> is returned if the original string, the * beginning string, the ending string, or the key-map values are * <code>null</code>. */ public static String replace( String s, String begin, String end, Map<String, String> values) { StringBundler sb = replaceToStringBundler(s, begin, end, values); return sb.toString(); } /** * Replaces all occurrences of the elements of the string array with the * corresponding elements of the new string array. * * @param s the original string * @param oldSubs the strings to be searched for and replaced in the * original string * @param newSubs the strings with which to replace the * <code>oldSubs</code> strings * @return a string representing the original string with all occurrences of * the <code>oldSubs</code> strings replaced with the corresponding * <code>newSubs</code> strings, or <code>null</code> if the * original string, the <code>oldSubs</code> array, or the * <code>newSubs</code> is <code>null</code> */ public static String replace(String s, String[] oldSubs, String[] newSubs) { if ((s == null) || (oldSubs == null) || (newSubs == null)) { return null; } if (oldSubs.length != newSubs.length) { return s; } for (int i = 0; i < oldSubs.length; i++) { s = replace(s, oldSubs[i], newSubs[i]); } return s; } /** * Replaces all occurrences of the elements of the string array with the * corresponding elements of the new string array, optionally replacing only * substrings that are surrounded by word boundaries. * * <p> * Examples: * </p> * * <p> * <pre> * <code> * replace("redorangeyellow", {"red", "orange", "yellow"}, {"RED","ORANGE", "YELLOW"}, false) returns "REDORANGEYELLOW" * replace("redorangeyellow", {"red", "orange", "yellow"}, {"RED","ORANGE", "YELLOW"}, true) returns "redorangeyellow" * replace("redorange yellow", {"red", "orange", "yellow"}, {"RED","ORANGE", "YELLOW"}, false) returns "REDORANGE YELLOW" * replace("redorange yellow", {"red", "orange", "yellow"}, {"RED","ORANGE", "YELLOW"}, true) returns "redorange YELLOW" * replace("red orange yellow", {"red", "orange", "yellow"}, {"RED","ORANGE", "YELLOW"}, false) returns "RED ORANGE YELLOW" * replace("redorange.yellow", {"red", "orange", "yellow"}, {"RED","ORANGE", * "YELLOW"}, true) returns "redorange.YELLOW" * </code> * </pre> * </p> * * @param s the original string * @param oldSubs the strings to be searched for and replaced in the * original string * @param newSubs the strings with which to replace the * <code>oldSubs</code> strings * @param exactMatch whether or not to replace only substrings of * <code>s</code> that are surrounded by word boundaries * @return if <code>exactMatch</code> is <code>true</code>, a string * representing the original string with all occurrences of the * <code>oldSubs</code> strings that are surrounded by word * boundaries replaced with the corresponding <code>newSubs</code> * strings, or else a string representing the original string with * all occurrences of the <code>oldSubs</code> strings replaced with * the corresponding <code>newSubs</code> strings, or * <code>null</code> if the original string, the * <code>oldSubs</code> array, or the <code>newSubs</code> is * <code>null</code> */ public static String replace( String s, String[] oldSubs, String[] newSubs, boolean exactMatch) { if ((s == null) || (oldSubs == null) || (newSubs == null)) { return null; } if (oldSubs.length != newSubs.length) { return s; } if (!exactMatch) { return replace(s, oldSubs, newSubs); } for (int i = 0; i < oldSubs.length; i++) { s = s.replaceAll("\\b" + oldSubs[i] + "\\b", newSubs[i]); } return s; } /** * Replaces the first occurrence of the character with the new character. * * @param s the original string * @param oldSub the character whose first occurrence in the original * string is to be searched for and replaced * @param newSub the character with which to replace the first occurrence * of the <code>oldSub</code> character * @return a string representing the original string except with the first * occurrence of the character <code>oldSub</code> replaced with the * character <code>newSub</code> */ public static String replaceFirst(String s, char oldSub, char newSub) { if (s == null) { return null; } return replaceFirst(s, String.valueOf(oldSub), String.valueOf(newSub)); } /** * Replaces the first occurrence of the character with the new string. * * @param s the original string * @param oldSub the character whose first occurrence in the original * string is to be searched for and replaced * @param newSub the string with which to replace the first occurrence of * the <code>oldSub</code> character * @return a string representing the original string except with the first * occurrence of the character <code>oldSub</code> replaced with the * string <code>newSub</code> */ public static String replaceFirst(String s, char oldSub, String newSub) { if ((s == null) || (newSub == null)) { return null; } return replaceFirst(s, String.valueOf(oldSub), newSub); } /** * Replaces the first occurrence of the string with the new string. * * @param s the original string * @param oldSub the string whose first occurrence in the original string * is to be searched for and replaced * @param newSub the string with which to replace the first occurrence of * the <code>oldSub</code> string * @return a string representing the original string except with the first * occurrence of the string <code>oldSub</code> replaced with the * string <code>newSub</code> */ public static String replaceFirst(String s, String oldSub, String newSub) { return replaceFirst(s, oldSub, newSub, 0); } /** * Replaces the first occurrences of the elements of the string array with * the corresponding elements of the new string array, beginning the element * search from the index position. * * @param s the original string * @param oldSub the strings whose first occurrences are to be searched for * and replaced in the original string * @param newSub the strings with which to replace the first occurrences of * the <code>oldSubs</code> strings * @param fromIndex the start index within the string * @return a string representing the original string with the first * occurrences of the <code>oldSubs</code> strings replaced with the * corresponding <code>newSubs</code> strings, or <code>null</code> * if the original string, the <code>oldSubs</code> string, or the * <code>newSubs</code> string is <code>null</code> */ public static String replaceFirst( String s, String oldSub, String newSub, int fromIndex) { if ((s == null) || (oldSub == null) || (newSub == null)) { return null; } if (oldSub.equals(newSub)) { return s; } int y = s.indexOf(oldSub, fromIndex); if (y >= 0) { return s.substring(0, y).concat(newSub).concat( s.substring(y + oldSub.length())); } else { return s; } } /** * Replaces the first occurrences of the elements of the string array with * the corresponding elements of the new string array. * * @param s the original string * @param oldSubs the strings whose first occurrences are to be searched * for and replaced in the original string * @param newSubs the strings with which to replace the first occurrences * of the <code>oldSubs</code> strings * @return a string representing the original string with the first * occurrences of the <code>oldSubs</code> strings replaced with the * corresponding <code>newSubs</code> strings, or <code>null</code> * if the original string, the <code>oldSubs</code> array, or the * <code>newSubs</code> is <code>null</code> */ public static String replaceFirst( String s, String[] oldSubs, String[] newSubs) { if ((s == null) || (oldSubs == null) || (newSubs == null)) { return null; } if (oldSubs.length != newSubs.length) { return s; } for (int i = 0; i < oldSubs.length; i++) { s = replaceFirst(s, oldSubs[i], newSubs[i]); } return s; } /** * Replaces the last occurrence of the character with the new character. * * @param s the original string * @param oldSub the character whose last occurrence in the original string * is to be searched for and replaced * @param newSub the character with which to replace the last occurrence of * the <code>oldSub</code> character * @return a string representing the original string except with the first * occurrence of the character <code>oldSub</code> replaced with the * character <code>newSub</code> */ public static String replaceLast(String s, char oldSub, char newSub) { if (s == null) { return null; } return replaceLast(s, String.valueOf(oldSub), String.valueOf(newSub)); } /** * Replaces the last occurrence of the character with the new string. * * @param s the original string * @param oldSub the character whose last occurrence in the original string * is to be searched for and replaced * @param newSub the string with which to replace the last occurrence of * the <code>oldSub</code> character * @return a string representing the original string except with the last * occurrence of the character <code>oldSub</code> replaced with the * string <code>newSub</code> */ public static String replaceLast(String s, char oldSub, String newSub) { if ((s == null) || (newSub == null)) { return null; } return replaceLast(s, String.valueOf(oldSub), newSub); } /** * Replaces the last occurrence of the string <code>oldSub</code> in the * string <code>s</code> with the string <code>newSub</code>. * * @param s the original string * @param oldSub the string whose last occurrence in the original string is * to be searched for and replaced * @param newSub the string with which to replace the last occurrence of * the <code>oldSub</code> string * @return a string representing the original string except with the last * occurrence of the string <code>oldSub</code> replaced with the * string <code>newSub</code> */ public static String replaceLast(String s, String oldSub, String newSub) { if ((s == null) || (oldSub == null) || (newSub == null)) { return null; } if (oldSub.equals(newSub)) { return s; } int y = s.lastIndexOf(oldSub); if (y >= 0) { return s.substring(0, y).concat(newSub).concat( s.substring(y + oldSub.length())); } else { return s; } } /** * Replaces the last occurrences of the elements of the string array with * the corresponding elements of the new string array. * * @param s the original string * @param oldSubs the strings whose last occurrences are to be searched for * and replaced in the original string * @param newSubs the strings with which to replace the last occurrences of * the <code>oldSubs</code> strings * @return a string representing the original string with the last * occurrences of the <code>oldSubs</code> strings replaced with the * corresponding <code>newSubs</code> strings, or <code>null</code> * if the original string, the <code>oldSubs</code> array, or the * <code>newSubs</code> is <code>null</code> */ public static String replaceLast( String s, String[] oldSubs, String[] newSubs) { if ((s == null) || (oldSubs == null) || (newSubs == null)) { return null; } if (oldSubs.length != newSubs.length) { return s; } for (int i = 0; i < oldSubs.length; i++) { s = replaceLast(s, oldSubs[i], newSubs[i]); } return s; } /** * Replaces all occurrences of the keywords found in the substring, defined * by the beginning and ending strings, with the new values. The result is * returned as a {@link StringBundler}. * * <p> * For example, with the following initialized variables: * </p> * * <p> * <pre> * <code> * String s = "http://www.example-url/${userId}"; * String begin = "${"; * String end = "}"; * Map<String, String> values = new HashMap<String, String>(); * values.put("userId", "jbloggs"); * </code> * </pre> * </p> * * <p> * <code>StringBundler sb = replaceToStringBundler(s, begin, end, * values)</code> <code>sb.toString()</code> returns * <code>"http://www.example-url/jbloggs"</code> * </p> * * @param s the original string * @param begin the string preceding the substring to be modified. This * string is excluded from the result. * @param end the string following the substring to be modified. This * string is excluded from the result. * @param values the key-value map values * @return a string bundler representing the original string with all * occurrences of the keywords found in the substring, replaced with * the new values. <code>null</code> is returned if the original * string, the beginning string, the ending string, or the key-map * values are <code>null</code>. * @see #replace(String, String, String, Map) */ public static StringBundler replaceToStringBundler( String s, String begin, String end, Map<String, String> values) { if (Validator.isBlank(s) || Validator.isBlank(begin) || Validator.isBlank(end) || MapUtil.isEmpty(values)) { return new StringBundler(s); } StringBundler sb = new StringBundler(values.size() * 2 + 1); int pos = 0; while (true) { int x = s.indexOf(begin, pos); int y = s.indexOf(end, x + begin.length()); if ((x == -1) || (y == -1)) { sb.append(s.substring(pos)); break; } else { sb.append(s.substring(pos, x)); String oldValue = s.substring(x + begin.length(), y); String newValue = values.get(oldValue); if (newValue == null) { newValue = oldValue; } sb.append(newValue); pos = y + end.length(); } } return sb; } /** * Replaces all occurrences of the keywords found in the substring, defined * by the beginning and ending strings, with the new values. The result is * returned as a {@link StringBundler}. * * @param s the original string * @param begin the string preceding the substring to be modified. This * string is removed from the result. * @param end the string following the substring to be modified. This * string is removed from the result. * @param values the key-value map values, which has string keys and {@link * StringBundler} values * @return a string bundler representing the original string with all * occurrences of the keywords found in the substring, replaced with * the new values. <code>null</code> is returned if the original * string, the beginning string, the ending string, or the key-map * values are <code>null</code>. */ public static StringBundler replaceWithStringBundler( String s, String begin, String end, Map<String, StringBundler> values) { if (Validator.isBlank(s) || Validator.isBlank(begin) || Validator.isBlank(end) || MapUtil.isEmpty(values)) { return new StringBundler(s); } int size = values.size() + 1; for (StringBundler valueSB : values.values()) { size += valueSB.index(); } StringBundler sb = new StringBundler(size); int pos = 0; while (true) { int x = s.indexOf(begin, pos); int y = s.indexOf(end, x + begin.length()); if ((x == -1) || (y == -1)) { sb.append(s.substring(pos)); break; } else { sb.append(s.substring(pos, x)); String oldValue = s.substring(x + begin.length(), y); StringBundler newValue = values.get(oldValue); if (newValue == null) { sb.append(oldValue); } else { sb.append(newValue); } pos = y + end.length(); } } return sb; } /** * Reverses the order of the characters of the string. * * @param s the original string * @return a string representing the original string with characters in * reverse order */ public static String reverse(String s) { if (s == null) { return null; } char[] chars = s.toCharArray(); char[] reverse = new char[chars.length]; for (int i = 0; i < chars.length; i++) { reverse[i] = chars[chars.length - i - 1]; } return new String(reverse); } /** * Replaces all double slashes of the string with single slashes. * * <p> * Example: * </p> * * <p> * <pre> * <code> * safePath("http://www.liferay.com") returns "http:/www.liferay.com" * </code> * </pre> * </p> * * @param path the original string * @return a string representing the original string with all double slashes * replaced with single slashes */ public static String safePath(String path) { return replace(path, StringPool.DOUBLE_SLASH, StringPool.SLASH); } /** * Returns a string representing the original string appended with suffix * "..." and then shortened to 20 characters. * * <p> * The suffix is only added if the original string exceeds 20 characters. If * the original string exceeds 20 characters and it contains whitespace, the * string is shortened at the first whitespace character. * </p> * * <p> * Examples: * </p> * * <p> * <pre> * <code> * shorten("12345678901234567890xyz") returns "12345678901234567..." * shorten("1 345678901234567890xyz") returns "1..." * shorten(" 2345678901234567890xyz") returns "..." * shorten("12345678901234567890") returns "12345678901234567890" * shorten(" 2345678901234567890") returns " 2345678901234567890" * </code> * </pre> * </p> * * @param s the original string * @return a string representing the original string shortened to 20 * characters, with suffix "..." appended to it */ public static String shorten(String s) { return shorten(s, 20); } /** * Returns a string representing the original string appended with suffix * "..." and then shortened to the specified length. * * <p> * The suffix is only added if the original string exceeds the specified * length. If the original string exceeds the specified length and it * contains whitespace, the string is shortened at the first whitespace * character. * </p> * * <p> * Examples: * </p> * * <p> * <pre> * <code> * shorten("123456789", 8) returns "12345..." * shorten("1 3456789", 8) returns "1..." * shorten(" 23456789", 8) returns "..." * shorten("12345678", 8) returns "12345678" * shorten(" 1234567", 8) returns " 1234567" * </code> * </pre> * </p> * * @param s the original string * @param length the number of characters to limit from the original string * @return a string representing the original string shortened to the * specified length, with suffix "..." appended to it */ public static String shorten(String s, int length) { return shorten(s, length, "..."); } /** * Returns a string representing the original string appended with the * specified suffix and then shortened to the specified length. * * <p> * The suffix is only added if the original string exceeds the specified * length. If the original string exceeds the specified length and it * contains whitespace, the string is shortened at the first whitespace * character. * </p> * * <p> * Examples: * </p> * * <p> * <pre> * <code> * shorten("12345678901234", 13, "... etc.") returns "12345... etc." * shorten("1 345678901234", 13, "... etc.") returns "1... etc." * shorten(" 2345678901234", 13, "... etc.") returns "... etc." * shorten("1234567890123", 13, "... etc.") returns "1234567890123" * shorten(" 123456789012", 13, "... etc.") returns " 123456789012" * </code> * </pre> * </p> * * @param s the original string * @param length the number of characters to limit from the original string * @param suffix the suffix to append * @return a string representing the original string shortened to the * specified length, with the specified suffix appended to it */ public static String shorten(String s, int length, String suffix) { if ((s == null) || (suffix == null)) { return null; } if (s.codePointCount(0, s.length()) <= length) { return s; } if (length < suffix.length()) { return s.substring(0, s.offsetByCodePoints(0, length)); } int curLength = length; for (int j = curLength - suffix.length() + 1, offset; j > 0; j--) { offset = s.offsetByCodePoints(0, j); if (Character.isWhitespace(s.codePointBefore(offset))) { curLength = j - 1; break; } } if (curLength == length) { curLength = length - suffix.length(); } String temp = s.substring(0, s.offsetByCodePoints(0, curLength)); return temp.concat(suffix); } /** * Returns a string representing the original string appended with the * specified suffix and then shortened to 20 characters. * * <p> * The suffix is only added if the original string exceeds 20 characters. If * the original string exceeds 20 characters and it contains whitespace, the * string is shortened at the first whitespace character. * </p> * * <p> * Examples: * </p> * * <p> * <pre> * <code> * shorten("12345678901234567890xyz", "... etc.") returns "123456789012... etc." * shorten("1 345678901234567890xyz", "... etc.") returns "1... etc." * shorten(" 2345678901234567890xyz", "... etc.") returns "... etc." * shorten("12345678901234567890", "... etc.") returns "12345678901234567890" * shorten(" 2345678901234567890", "... etc.") returns " 2345678901234567890" * </code> * </pre> * </p> * * @param s the original string * @param suffix the suffix to append * @return a string representing the original string shortened to 20 * characters, with the specified suffix appended to it */ public static String shorten(String s, String suffix) { return shorten(s, 20, suffix); } /** * Splits string <code>s</code> around comma characters. * * <p> * Examples: * </p> * * <p> * <pre> * <code> * split("Alice,Bob,Charlie") returns {"Alice", "Bob", "Charlie"} * split("Alice, Bob, Charlie") returns {"Alice", " Bob", " Charlie"} * </code> * </pre> * </p> * * @param s the string to split * @return the array of strings resulting from splitting string * <code>s</code> around comma characters, or an empty string array * if <code>s</code> is <code>null</code> or <code>s</code> is empty */ public static String[] split(String s) { return split(s, CharPool.COMMA); } /** * Splits the string <code>s</code> around comma characters returning the * boolean values of the substrings. * * @param s the string to split * @param x the default value to use for a substring in case an exception * occurs in getting the boolean value for that substring * @return the array of boolean values resulting from splitting string * <code>s</code> around comma characters, or an empty array if * <code>s</code> is <code>null</code> */ public static boolean[] split(String s, boolean x) { return split(s, StringPool.COMMA, x); } /** * Splits the string <code>s</code> around the specified delimiter. * * <p> * Example: * </p> * * <p> * <pre> * <code> * splitLines("First;Second;Third", ';') returns {"First","Second","Third"} * </code> * </pre> * </p> * * @param s the string to split * @param delimiter the delimiter * @return the array of strings resulting from splitting string * <code>s</code> around the specified delimiter character, or an * empty string array if <code>s</code> is <code>null</code> or if * <code>s</code> is empty */ public static String[] split(String s, char delimiter) { if (Validator.isNull(s)) { return _emptyStringArray; } s = s.trim(); if (s.length() == 0) { return _emptyStringArray; } List<String> nodeValues = new ArrayList<>(); _split(nodeValues, s, 0, delimiter); return nodeValues.toArray(new String[nodeValues.size()]); } /** * Splits the string <code>s</code> around comma characters returning the * double-precision decimal values of the substrings. * * @param s the string to split * @param x the default value to use for a substring in case an exception * occurs in getting the double-precision decimal value for that * substring * @return the array of double-precision decimal values resulting from * splitting string <code>s</code> around comma characters, or an * empty array if <code>s</code> is <code>null</code> */ public static double[] split(String s, double x) { return split(s, StringPool.COMMA, x); } /** * Splits the string <code>s</code> around comma characters returning the * decimal values of the substrings. * * @param s the string to split * @param x the default value to use for a substring in case an exception * occurs in getting the decimal value for that substring * @return the array of decimal values resulting from splitting string * <code>s</code> around comma characters, or an empty array if * <code>s</code> is <code>null</code> */ public static float[] split(String s, float x) { return split(s, StringPool.COMMA, x); } /** * Splits the string <code>s</code> around comma characters returning the * integer values of the substrings. * * @param s the string to split * @param x the default value to use for a substring in case an exception * occurs in getting the integer value for that substring * @return the array of integer values resulting from splitting string * <code>s</code> around comma characters, or an empty array if * <code>s</code> is <code>null</code> */ public static int[] split(String s, int x) { return split(s, StringPool.COMMA, x); } /** * Splits the string <code>s</code> around comma characters returning the * long integer values of the substrings. * * @param s the string to split * @param x the default value to use for a substring in case an exception * occurs in getting the long integer value for that substring * @return the array of long integer values resulting from splitting string * <code>s</code> around comma characters, or an empty array if * <code>s</code> is <code>null</code> */ public static long[] split(String s, long x) { return split(s, StringPool.COMMA, x); } /** * Splits the string <code>s</code> around comma characters returning the * short integer values of the substrings. * * @param s the string to split * @param x the default value to use for a substring in case an exception * occurs in getting the short integer value for that substring * @return the array of short integer values resulting from splitting string * <code>s</code> around comma characters, or an empty array if * <code>s</code> is <code>null</code> */ public static short[] split(String s, short x) { return split(s, StringPool.COMMA, x); } /** * Splits the string <code>s</code> around the specified delimiter string. * * <p> * Example: * </p> * * <p> * <pre> * <code> * splitLines("oneandtwoandthreeandfour", "and") returns {"one","two","three","four"} * </code> * </pre> * </p> * * @param s the string to split * @param delimiter the delimiter * @return the array of strings resulting from splitting string * <code>s</code> around the specified delimiter string, or an empty * string array if <code>s</code> is <code>null</code> or equals the * delimiter */ public static String[] split(String s, String delimiter) { if (Validator.isNull(s) || (delimiter == null) || delimiter.equals(StringPool.BLANK)) { return _emptyStringArray; } s = s.trim(); if (s.equals(delimiter)) { return _emptyStringArray; } if (delimiter.length() == 1) { return split(s, delimiter.charAt(0)); } List<String> nodeValues = new ArrayList<>(); int offset = 0; int pos = s.indexOf(delimiter, offset); while (pos != -1) { nodeValues.add(s.substring(offset, pos)); offset = pos + delimiter.length(); pos = s.indexOf(delimiter, offset); } if (offset < s.length()) { nodeValues.add(s.substring(offset)); } return nodeValues.toArray(new String[nodeValues.size()]); } /** * Splits the string <code>s</code> around the specified delimiter returning * the boolean values of the substrings. * * @param s the string to split * @param delimiter the delimiter * @param x the default value to use for a substring in case an exception * occurs in getting the boolean value for that substring * @return the array of booleans resulting from splitting string * <code>s</code> around the specified delimiter string, or an empty * array if <code>s</code> is <code>null</code> */ public static boolean[] split(String s, String delimiter, boolean x) { String[] array = split(s, delimiter); boolean[] newArray = new boolean[array.length]; for (int i = 0; i < array.length; i++) { boolean value = x; try { value = Boolean.valueOf(array[i]).booleanValue(); } catch (Exception e) { } newArray[i] = value; } return newArray; } /** * Splits the string <code>s</code> around the specified delimiter returning * the double-precision decimal values of the substrings. * * @param s the string to split * @param delimiter the delimiter * @param x the default value to use for a substring in case an exception * occurs in getting the double-precision decimal value for that * substring * @return the array of double-precision decimal values resulting from * splitting string <code>s</code> around the specified delimiter * string, or an empty array if <code>s</code> is <code>null</code> */ public static double[] split(String s, String delimiter, double x) { String[] array = split(s, delimiter); double[] newArray = new double[array.length]; for (int i = 0; i < array.length; i++) { double value = x; try { value = Double.parseDouble(array[i]); } catch (Exception e) { } newArray[i] = value; } return newArray; } /** * Splits the string <code>s</code> around the specified delimiter returning * the decimal values of the substrings. * * @param s the string to split * @param delimiter the delimiter * @param x the default value to use for a substring in case an exception * occurs in getting the decimal value for that substring * @return the array of decimal values resulting from splitting string * <code>s</code> around the specified delimiter string, or an empty * array if <code>s</code> is <code>null</code> */ public static float[] split(String s, String delimiter, float x) { String[] array = split(s, delimiter); float[] newArray = new float[array.length]; for (int i = 0; i < array.length; i++) { float value = x; try { value = Float.parseFloat(array[i]); } catch (Exception e) { } newArray[i] = value; } return newArray; } /** * Splits the string <code>s</code> around the specified delimiter returning * the integer values of the substrings. * * @param s the string to split * @param delimiter the delimiter * @param x the default value to use for a substring in case an exception * occurs in getting the integer value for that substring * @return the array of integer values resulting from splitting string * <code>s</code> around the specified delimiter string, or an empty * array if <code>s</code> is <code>null</code> */ public static int[] split(String s, String delimiter, int x) { String[] array = split(s, delimiter); int[] newArray = new int[array.length]; for (int i = 0; i < array.length; i++) { int value = x; try { value = Integer.parseInt(array[i]); } catch (Exception e) { } newArray[i] = value; } return newArray; } /** * Splits the string <code>s</code> around the specified delimiter returning * the long integer values of the substrings. * * @param s the string to split * @param delimiter the delimiter * @param x the default value to use for a substring in case an exception * occurs in getting the long integer value for that substring * @return the array of long integer values resulting from splitting string * <code>s</code> around the specified delimiter string, or an empty * array if <code>s</code> is <code>null</code> */ public static long[] split(String s, String delimiter, long x) { String[] array = split(s, delimiter); long[] newArray = new long[array.length]; for (int i = 0; i < array.length; i++) { long value = x; try { value = Long.parseLong(array[i]); } catch (Exception e) { } newArray[i] = value; } return newArray; } /** * Splits the string <code>s</code> around the specified delimiter returning * the short integer values of the substrings. * * @param s the string to split * @param delimiter the delimiter * @param x the default value to use for a substring in case an exception * occurs in getting the short integer value for that substring * @return the array of short integer values resulting from splitting string * <code>s</code> around the specified delimiter string, or an empty * array if <code>s</code> is <code>null</code> */ public static short[] split(String s, String delimiter, short x) { String[] array = split(s, delimiter); short[] newArray = new short[array.length]; for (int i = 0; i < array.length; i++) { short value = x; try { value = Short.parseShort(array[i]); } catch (Exception e) { } newArray[i] = value; } return newArray; } /** * Splits string <code>s</code> around return and newline characters. * * <p> * Example: * </p> * * <p> * <pre> * <code> * splitLines("Red\rBlue\nGreen") returns {"Red","Blue","Green"} * </code> * </pre> * </p> * * @param s the string to split * @return the array of strings resulting from splitting string * <code>s</code> around return and newline characters, or an empty * string array if string <code>s</code> is <code>null</code> */ public static String[] splitLines(String s) { if (Validator.isNull(s)) { return _emptyStringArray; } s = s.trim(); List<String> lines = new ArrayList<>(); int lastIndex = 0; while (true) { int returnIndex = s.indexOf(CharPool.RETURN, lastIndex); if (returnIndex == -1) { _split(lines, s, lastIndex, CharPool.NEW_LINE); return lines.toArray(new String[lines.size()]); } int newLineIndex = s.indexOf(CharPool.NEW_LINE, lastIndex); if (newLineIndex == -1) { _split(lines, s, lastIndex, CharPool.RETURN); return lines.toArray(new String[lines.size()]); } if (newLineIndex < returnIndex) { lines.add(s.substring(lastIndex, newLineIndex)); lastIndex = newLineIndex + 1; } else { lines.add(s.substring(lastIndex, returnIndex)); lastIndex = returnIndex + 1; if (lastIndex == newLineIndex) { lastIndex++; } } } } /** * Returns <code>true</code> if, ignoring case, the string starts with the * specified character. * * @param s the string * @param begin the character against which the initial character of the * string is to be compared * @return <code>true</code> if, ignoring case, the string starts with the * specified character; <code>false</code> otherwise */ public static boolean startsWith(String s, char begin) { if ((s == null) || s.isEmpty()) { return false; } return equalsIgnoreCase(s.charAt(0), begin); } /** * Returns <code>true</code> if, ignoring case, the string starts with the * specified start string. * * @param s the original string * @param start the string against which the beginning of string * <code>s</code> are to be compared * @return <code>true</code> if, ignoring case, the string starts with the * specified start string; <code>false</code> otherwise */ public static boolean startsWith(String s, String start) { if ((s == null) || (start == null)) { return false; } if (start.length() > s.length()) { return false; } for (int i = 0; i < start.length(); i++) { if (!equalsIgnoreCase(s.charAt(i), start.charAt(i))) { return false; } } return true; } /** * Returns the number of starting characters that <code>s1</code> and * <code>s2</code> have in common before their characters deviate. * * @param s1 string 1 * @param s2 string 2 * @return the number of starting characters that <code>s1</code> and * <code>s2</code> have in common before their characters deviate */ public static int startsWithWeight(String s1, String s2) { if ((s1 == null) || (s2 == null)) { return 0; } char[] chars1 = s1.toCharArray(); char[] chars2 = s2.toCharArray(); int i = 0; for (; (i < chars1.length) && (i < chars2.length); i++) { if (chars1[i] != chars2[i]) { break; } } return i; } /** * Returns a string representing the string <code>s</code> with all * occurrences of the specified character removed. * * <p> * Example: * </p> * * <p> * <pre> * <code> * strip("Mississipi", 'i') returns "Mssssp" * </code> * </pre> * </p> * * @param s the string from which to strip all occurrences of the character * @param remove the character to strip from the string * @return a string representing the string <code>s</code> with all * occurrences of the specified character removed, or * <code>null</code> if <code>s</code> is <code>null</code> */ public static String strip(String s, char remove) { if (s == null) { return null; } int x = s.indexOf(remove); if (x < 0) { return s; } int y = 0; StringBundler sb = new StringBundler(s.length()); while (x >= 0) { sb.append(s.subSequence(y, x)); y = x + 1; x = s.indexOf(remove, y); } sb.append(s.substring(y)); return sb.toString(); } /** * Returns a string representing the string <code>s</code> with all * occurrences of the specified characters removed. * * <p> * Example: * </p> * * <p> * <pre> * <code> * strip("Hello World", {' ', 'l', 'd'}) returns "HeoWor" * </code> * </pre> * </p> * * @param s the string from which to strip all occurrences the characters * @param remove the characters to strip from the string * @return a string representing the string <code>s</code> with all * occurrences of the specified characters removed, or * <code>null</code> if <code>s</code> is <code>null</code> */ public static String strip(String s, char[] remove) { for (char c : remove) { s = strip(s, c); } return s; } /** * Returns a string representing the combination of the substring of * <code>s</code> up to but not including the string <code>begin</code> * concatenated with the substring of <code>s</code> after but not including * the string <code>end</code>. * * <p> * Example: * </p> * * <p> * <pre> * <code> * stripBetween("One small step for man, one giant leap for mankind", "step", "giant ") returns "One small leap for mankind" * </code> * </pre> * </p> * * @param s the string from which to strip a substring * @param begin the beginning characters of the substring to be removed * @param end the ending characters of the substring to be removed * @return a string representing the combination of the substring of * <code>s</code> up to but not including the string * <code>begin</code> concatenated with the substring of * <code>s</code> after but not including the string * <code>end</code>, or the original string if the value of * <code>s</code>, <code>begin</code>, or <code>end</code> are * <code>null</code> */ public static String stripBetween(String s, String begin, String end) { if (Validator.isBlank(s) || Validator.isBlank(begin) || Validator.isBlank(end)) { return s; } StringBundler sb = new StringBundler(s.length()); int pos = 0; while (true) { int x = s.indexOf(begin, pos); int y = s.indexOf(end, x + begin.length()); if ((x == -1) || (y == -1)) { sb.append(s.substring(pos)); break; } else { sb.append(s.substring(pos, x)); pos = y + end.length(); } } return sb.toString(); } /** * Returns a string representing the string <code>s</code> with its * <code><![CDATA[]]></code> wrapper removed. * * <p> * Example: * </p> * * <p> * <pre> * <code> * stripCDATA("<![CDATA[One small step for man]]>") returns "One small step for man" * </code> * </pre> * </p> * * @param s the string from which to strip its CDATA wrapper * @return a string representing the string <code>s</code> with its * <code><![CDATA[]]></code> wrapper removed, or * <code>null</code> if <code>s</code> is <code>null</code> */ public static String stripCDATA(String s) { if (s == null) { return s; } if (s.startsWith(StringPool.CDATA_OPEN) && s.endsWith(StringPool.CDATA_CLOSE)) { s = s.substring( StringPool.CDATA_OPEN.length(), s.length() - StringPool.CDATA_CLOSE.length()); } return s; } /** * Returns a string representing the string <code>s</code> without an * appended parenthetical suffix. If there is not a space directly before * the opening parenthesis, the parenthetical suffix is not stripped. * * <p> * Examples: * </p> * * <p> * <pre> * <code> * stripParentheticalSuffix("file") returns "file" * stripParentheticalSuffix("file (0)") returns "file" * stripParentheticalSuffix("file (0 0)") returns "file" * stripParentheticalSuffix("file(0)") returns "file(0)" * </code> * </pre> * </p> * * @param s the string from which to strip its parenthetical suffix * @return a string representing the string <code>s</code> without an * appended parenthetical suffix */ public static String stripParentheticalSuffix(String s) { int x = s.lastIndexOf(StringPool.OPEN_PARENTHESIS); int y = s.lastIndexOf(StringPool.CLOSE_PARENTHESIS); if ((x == -1) || (y == -1)) { return s; } if ((x > y) || !s.endsWith(StringPool.CLOSE_PARENTHESIS)) { return s; } if (s.charAt(x - 1) != CharPool.SPACE) { return s; } return s.substring(0, x - 1).concat(s.substring(y + 1, s.length())); } /** * Returns a string representing the Unicode character codes of the * characters comprising the string <code>s</code>. * * <p> * Examples: * </p> * * <p> * <pre> * <code> * toCharCode("a") returns "97" * toCharCode("b") returns "98" * toCharCode("c") returns "99" * toCharCode("What's for lunch?") returns "87104971163911532102111114321081171109910463" * </code> * </pre> * </p> * * @param s the string whose character codes are to be represented * @return a string representing the Unicode character codes of the * characters comprising the string <code>s</code> */ public static String toCharCode(String s) { StringBundler sb = new StringBundler(s.length()); for (int i = 0; i < s.length(); i++) { sb.append(s.codePointAt(i)); } return sb.toString(); } /** * Returns a string representing the hexidecimal character code of the * integer. * * <p> * Examples: * </p> * * <p> * <pre> * <code> * toHexString(10) returns "a" * toHexString(15) returns "f" * toHexString(10995) returns "2af3" * </code> * </pre> * </p> * * @param i the integer to convert * @return a string representing the hexidecimal character code of the * integer */ public static String toHexString(int i) { char[] buffer = new char[8]; int index = 8; do { buffer[--index] = HEX_DIGITS[i & 15]; i >>>= 4; } while (i != 0); return new String(buffer, index, 8 - index); } /** * Returns a string representing the hexidecimal character code of the long * integer. * * <p> * Example: * </p> * * <p> * <pre> * <code> * toHexString(12345678910L) returns "2dfdc1c3e" * </code> * </pre> * </p> * * @param l the long integer to convert * @return a string representing the hexidecimal character code of the long * integer */ public static String toHexString(long l) { char[] buffer = new char[16]; int index = 16; do { buffer[--index] = HEX_DIGITS[(int)(l & 15)]; l >>>= 4; } while (l != 0); return new String(buffer, index, 16 - index); } /** * Returns a string representing the hexidecimal character code of the * <code>Integer</code> or <code>Long</code> object type. If the object is * not an instance of these types, the object's original value is returned. * * @param obj the object to convert * @return a string representing the hexidecimal character code of the * object */ public static String toHexString(Object obj) { if (obj instanceof Integer) { return toHexString(((Integer)obj).intValue()); } else if (obj instanceof Long) { return toHexString(((Long)obj).longValue()); } else { return String.valueOf(obj); } } /** * Converts all of the characters in the string to lower case, based on the * portal instance's default locale. * * @param s the string to convert * @return the string, converted to lower case, or <code>null</code> if the * string is <code>null</code> */ public static String toLowerCase(String s) { return toLowerCase(s, null); } /** * Converts all of the characters in the string to lower case, based on the * locale. * * @param s the string to convert * @param locale apply this locale's rules * @return the string, converted to lower case, or <code>null</code> if the * string is <code>null</code> * @see GetterUtil#_toLowerCase */ public static String toLowerCase(String s, Locale locale) { if (s == null) { return null; } StringBuilder sb = null; for (int i = 0; i < s.length(); i++) { char c = s.charAt(i); if (c > 127) { // Found non-ascii char, fallback to the slow unicode detection if (locale == null) { locale = LocaleUtil.getDefault(); } return s.toLowerCase(locale); } if ((c >= 'A') && (c <= 'Z')) { if (sb == null) { sb = new StringBuilder(s); } sb.setCharAt(i, (char)(c + 32)); } } if (sb == null) { return s; } return sb.toString(); } /** * Converts all of the characters in the string to upper case, based on the * portal instance's default locale. * * @param s the string to convert * @return the string, converted to upper case, or <code>null</code> if the * string is <code>null</code> */ public static String toUpperCase(String s) { return toUpperCase(s, null); } /** * Converts all of the characters in the string to upper case, based on the * locale. * * @param s the string to convert * @param locale apply this locale's rules * @return the string, converted to upper case, or <code>null</code> if the * string is <code>null</code> */ public static String toUpperCase(String s, Locale locale) { if (s == null) { return null; } StringBuilder sb = null; for (int i = 0; i < s.length(); i++) { char c = s.charAt(i); if (c > 127) { // Found non-ascii char, fallback to the slow unicode detection if (locale == null) { locale = LocaleUtil.getDefault(); } return s.toUpperCase(locale); } if ((c >= 'a') && (c <= 'z')) { if (sb == null) { sb = new StringBuilder(s); } sb.setCharAt(i, (char)(c - 32)); } } if (sb == null) { return s; } return sb.toString(); } /** * Trims all leading and trailing whitespace from the string. * * @param s the original string * @return a string representing the original string with all leading and * trailing whitespace removed */ public static String trim(String s) { if (s == null) { return null; } if (s.length() == 0) { return s; } int len = s.length(); int x = len; for (int i = 0; i < len; i++) { char c = s.charAt(i); if (!Character.isWhitespace(c)) { x = i; break; } } if (x == len) { return StringPool.BLANK; } int y = x + 1; for (int i = len - 1; i > x; i--) { char c = s.charAt(i); if (!Character.isWhitespace(c)) { y = i + 1; break; } } if ((x == 0) && (y == len)) { return s; } return s.substring(x, y); } /** * Trims leading and trailing whitespace from the string, up to but not * including the whitespace character specified by <code>c</code>. * * <p> * Examples: * </p> * * <p> * <pre> * <code> * trim(" \tHey\t ", '\t') returns "\tHey\t" * trim(" \t Hey \t ", '\t') returns "\t Hey \t" * </code> * </pre> * </p> * * @param s the original string * @param c the whitespace character to limit trimming * @return a string representing the original string with leading and * trailing whitespace removed, up to but not including the * whitespace character specified by <code>c</code> */ public static String trim(String s, char c) { return trim(s, new char[] {c}); } /** * Trims leading and trailing whitespace from the string, up to but not * including the whitespace characters specified by <code>exceptions</code>. * * @param s the original string * @param exceptions the whitespace characters to limit trimming * @return a string representing the original string with leading and * trailing whitespace removed, up to but not including the * whitespace characters specified by <code>exceptions</code> */ public static String trim(String s, char[] exceptions) { if (s == null) { return null; } if (s.length() == 0) { return s; } if (ArrayUtil.isEmpty(exceptions)) { return trim(s); } int len = s.length(); int x = len; for (int i = 0; i < len; i++) { char c = s.charAt(i); if (!_isTrimable(c, exceptions)) { x = i; break; } } if (x == len) { return StringPool.BLANK; } int y = x + 1; for (int i = len - 1; i > x; i--) { char c = s.charAt(i); if (!_isTrimable(c, exceptions)) { y = i + 1; break; } } if ((x == 0) && (y == len)) { return s; } else { return s.substring(x, y); } } /** * Trims all leading whitespace from the string. * * @param s the original string * @return a string representing the original string with all leading * whitespace removed */ public static String trimLeading(String s) { if (s == null) { return null; } if (s.length() == 0) { return s; } int len = s.length(); int x = len; for (int i = 0; i < len; i++) { char c = s.charAt(i); if (!Character.isWhitespace(c)) { x = i; break; } } if (x == len) { return StringPool.BLANK; } else if (x == 0) { return s; } else { return s.substring(x); } } /** * Trims leading whitespace from the string, up to but not including the * whitespace character specified by <code>c</code>. * * @param s the original string * @param c the whitespace character to limit trimming * @return a string representing the original string with leading whitespace * removed, up to but not including the whitespace character * specified by <code>c</code> */ public static String trimLeading(String s, char c) { return trimLeading(s, new char[] {c}); } /** * Trims leading whitespace from the string, up to but not including the * whitespace characters specified by <code>exceptions</code>. * * @param s the original string * @param exceptions the whitespace characters to limit trimming * @return a string representing the original string with leading whitespace * removed, up to but not including the whitespace characters * specified by <code>exceptions</code> */ public static String trimLeading(String s, char[] exceptions) { if (s == null) { return null; } if (s.length() == 0) { return s; } if (ArrayUtil.isEmpty(exceptions)) { return trimLeading(s); } int len = s.length(); int x = len; for (int i = 0; i < len; i++) { char c = s.charAt(i); if (!_isTrimable(c, exceptions)) { x = i; break; } } if (x == len) { return StringPool.BLANK; } else if (x == 0) { return s; } else { return s.substring(x); } } /** * Trims all trailing whitespace from the string. * * @param s the original string * @return a string representing the original string with all trailing * whitespace removed */ public static String trimTrailing(String s) { if (s == null) { return null; } if (s.length() == 0) { return s; } int len = s.length(); int x = 0; for (int i = len - 1; i >= 0; i--) { char c = s.charAt(i); if (!Character.isWhitespace(c)) { x = i + 1; break; } } if (x == 0) { return StringPool.BLANK; } else if (x == len) { return s; } else { return s.substring(0, x); } } /** * Trims trailing whitespace from the string, up to but not including the * whitespace character specified by <code>c</code>. * * @param s the original string * @param c the whitespace character to limit trimming * @return a string representing the original string with trailing * whitespace removed, up to but not including the whitespace * character specified by <code>c</code> */ public static String trimTrailing(String s, char c) { return trimTrailing(s, new char[] {c}); } /** * Trims trailing whitespace from the string, up to but not including the * whitespace characters specified by <code>exceptions</code>. * * @param s the original string * @param exceptions the whitespace characters to limit trimming * @return a string representing the original string with trailing * whitespace removed, up to but not including the whitespace * characters specified by <code>exceptions</code> */ public static String trimTrailing(String s, char[] exceptions) { if (s == null) { return null; } if (s.length() == 0) { return s; } if (ArrayUtil.isEmpty(exceptions)) { return trimTrailing(s); } int len = s.length(); int x = 0; for (int i = len - 1; i >= 0; i--) { char c = s.charAt(i); if (!_isTrimable(c, exceptions)) { x = i + 1; break; } } if (x == 0) { return StringPool.BLANK; } else if (x == len) { return s; } else { return s.substring(0, x); } } /** * Removes leading and trailing double and single quotation marks from the * string. * * @param s the original string * @return a string representing the original string with leading and * trailing double and single quotation marks removed, or the * original string if the original string is a <code>null</code> or * empty */ public static String unquote(String s) { if (Validator.isNull(s)) { return s; } if ((s.charAt(0) == CharPool.APOSTROPHE) && (s.charAt(s.length() - 1) == CharPool.APOSTROPHE)) { return s.substring(1, s.length() - 1); } else if ((s.charAt(0) == CharPool.QUOTE) && (s.charAt(s.length() - 1) == CharPool.QUOTE)) { return s.substring(1, s.length() - 1); } return s; } /** * Converts all of the characters in the string to upper case. * * @param s the string to convert * @return the string, converted to upper-case, or <code>null</code> if the * string is <code>null</code> * @see String#toUpperCase() */ public static String upperCase(String s) { return toUpperCase(s); } /** * Converts the first character of the string to upper case. * * @param s the string whose first character is to be converted * @return the string, with its first character converted to upper-case */ public static String upperCaseFirstLetter(String s) { char[] chars = s.toCharArray(); if ((chars[0] >= 97) && (chars[0] <= 122)) { chars[0] = (char)(chars[0] - 32); } return new String(chars); } /** * Returns the string value of the object. * * @param obj the object whose string value is to be returned * @return the string value of the object * @see String#valueOf(Object obj) */ public static String valueOf(Object obj) { return String.valueOf(obj); } /** * Returns <code>true</code> if the string matches the wildcard pattern. * * <p> * For example, with the following initialized variables: * </p> * * <p> * <pre> * <code> * String s = "*master"; * String wildcard = "/*m?st*"; * char singleWildcardCharacter = '?'; * char multipleWildcardCharacter = '*'; * char escapeWildcardCharacter = '/'; * boolean caseSensitive = false; * </code> * </pre> * </p> * * <p> * <code>wildcardMatches(s, wildcard, singleWildcardCharacter, * multipleWildcardCharacter, escapeWildcardCharacter, caseSensitive)</code> * returns <code>true</code> * </p> * * @param s the string to be checked * @param wildcard the wildcard pattern to match * @param singleWildcardCharacter the char used to match exactly one * character * @param multipleWildcardCharacter the char used to match <code>0</code> * or more characters * @param escapeWildcardCharacter the char placed in front of a wildcard * character to indicate that it should be interpreted as a regular * character * @param caseSensitive whether to use case sensitivity * @return <code>true</code> if the string matches the wildcard pattern; * <code>false</code> otherwise */ public static boolean wildcardMatches( String s, String wildcard, char singleWildcardCharacter, char multipleWildcardCharacter, char escapeWildcardCharacter, boolean caseSensitive) { if (!caseSensitive) { s = toLowerCase(s); wildcard = toLowerCase(wildcard); } // Update the wildcard, single whildcard character, and multiple // wildcard character so that they no longer have escaped wildcard // characters int index = wildcard.indexOf(escapeWildcardCharacter); if (index != -1) { // Search for safe wildcard replacement char newSingleWildcardCharacter = 0; while (wildcard.indexOf(newSingleWildcardCharacter) != -1) { newSingleWildcardCharacter++; } char newMultipleWildcardCharacter = (char)(newSingleWildcardCharacter + 1); while (wildcard.indexOf(newMultipleWildcardCharacter) != -1) { newMultipleWildcardCharacter++; } // Purify StringBuilder sb = new StringBuilder(wildcard); for (int i = 0; i < sb.length(); i++) { char c = sb.charAt(i); if (c == escapeWildcardCharacter) { sb.deleteCharAt(i); } else if (c == singleWildcardCharacter) { sb.setCharAt(i, newSingleWildcardCharacter); } else if (c == multipleWildcardCharacter) { sb.setCharAt(i, newMultipleWildcardCharacter); } } wildcard = sb.toString(); singleWildcardCharacter = newSingleWildcardCharacter; multipleWildcardCharacter = newMultipleWildcardCharacter; } // Align head for (index = 0; index < s.length(); index++) { if (index >= wildcard.length()) { return false; } char c = wildcard.charAt(index); if (c == multipleWildcardCharacter) { break; } if ((s.charAt(index) != c) && (c != singleWildcardCharacter)) { return false; } } // Match body int sIndex = index; int wildcardIndex = index; int matchPoint = 0; int comparePoint = 0; while (sIndex < s.length()) { if (wildcardIndex == wildcard.length()) { // Wildcard exhausted before s return false; } char c = wildcard.charAt(wildcardIndex); if (c == multipleWildcardCharacter) { if (++wildcardIndex == wildcard.length()) { return true; } matchPoint = wildcardIndex; comparePoint = sIndex + 1; } else if ((c == s.charAt(sIndex)) || (c == singleWildcardCharacter)) { sIndex++; wildcardIndex++; } else { wildcardIndex = matchPoint; sIndex = comparePoint++; } } // Match tail while (wildcardIndex < wildcard.length()) { if (wildcard.charAt(wildcardIndex) != multipleWildcardCharacter) { break; } wildcardIndex++; } if (wildcardIndex == wildcard.length()) { return true; } else { return false; } } /** * Wraps the text when it exceeds the <code>80</code> column width limit, * using a {@link StringPool#NEW_LINE} to break each wrapped line. * * @param text the text to wrap * @return the wrapped text following the column width limit, or * <code>null</code> if the text is <code>null</code> */ public static String wrap(String text) { return wrap(text, 80, StringPool.NEW_LINE); } /** * Wraps the text when it exceeds the column width limit, using the line * separator to break each wrapped line. * * @param text the text to wrap * @param width the column width limit for the text * @param lineSeparator the string to use in breaking each wrapped line * @return the wrapped text and line separators, following the column width * limit, or <code>null</code> if the text is <code>null</code> */ public static String wrap(String text, int width, String lineSeparator) { try { return _wrap(text, width, lineSeparator); } catch (IOException ioe) { _log.error(ioe.getMessage()); return text; } } protected static final char[] HEX_DIGITS = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' }; /** * Returns <code>false</code> if the character is not whitespace or is equal * to any of the exception characters. * * @param c the character whose trim-ability is to be determined * @param exceptions the whitespace characters to exclude from trimming * @return <code>false</code> if the character is not whitespace or is equal * to any of the exception characters; <code>true</code> otherwise */ private static boolean _isTrimable(char c, char[] exceptions) { for (char exception : exceptions) { if (c == exception) { return false; } } return Character.isWhitespace(c); } private static void _split( List<String> values, String s, int offset, char delimiter) { int pos = s.indexOf(delimiter, offset); while (pos != -1) { values.add(s.substring(offset, pos)); offset = pos + 1; pos = s.indexOf(delimiter, offset); } if (offset < s.length()) { values.add(s.substring(offset)); } } private static String _wrap(String text, int width, String lineSeparator) throws IOException { if (text == null) { return null; } StringBundler sb = new StringBundler(); UnsyncBufferedReader unsyncBufferedReader = new UnsyncBufferedReader( new UnsyncStringReader(text)); String s = StringPool.BLANK; while ((s = unsyncBufferedReader.readLine()) != null) { if (s.length() == 0) { sb.append(lineSeparator); continue; } int lineLength = 0; String[] tokens = s.split(StringPool.SPACE); for (String token : tokens) { if ((lineLength + token.length() + 1) > width) { if (lineLength > 0) { sb.append(lineSeparator); } if (token.length() > width) { int pos = token.indexOf(CharPool.OPEN_PARENTHESIS); if (pos != -1) { sb.append(token.substring(0, pos + 1)); sb.append(lineSeparator); token = token.substring(pos + 1); sb.append(token); lineLength = token.length(); } else { sb.append(token); lineLength = token.length(); } } else { sb.append(token); lineLength = token.length(); } } else { if (lineLength > 0) { sb.append(StringPool.SPACE); lineLength++; } sb.append(token); lineLength += token.length(); } } sb.append(lineSeparator); } return sb.toString(); } private static final char[] _RANDOM_STRING_CHAR_TABLE = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z' }; private static final Log _log = LogFactoryUtil.getLog(StringUtil.class); private static final String[] _emptyStringArray = new String[0]; }