org.jooq.tools.StringUtils Maven / Gradle / Ivy
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jooq.tools;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
*
* Operations on {@link java.lang.String} that are null
safe.
*
*
* - IsEmpty/IsBlank - checks if a String contains text
* - Trim/Strip - removes leading and trailing whitespace
* - Equals - compares two strings null-safe
* - startsWith - check if a String starts with a prefix null-safe
* - endsWith - check if a String ends with a suffix null-safe
* - IndexOf/LastIndexOf/Contains - null-safe index-of checks
*
- IndexOfAny/LastIndexOfAny/IndexOfAnyBut/LastIndexOfAnyBut -
* index-of any of a set of Strings
* - ContainsOnly/ContainsNone/ContainsAny - does String contains
* only/none/any of these characters
* - Substring/Left/Right/Mid - null-safe substring extractions
* - SubstringBefore/SubstringAfter/SubstringBetween - substring
* extraction relative to other strings
* - Split/Join - splits a String into an array of substrings and vice
* versa
* - Remove/Delete - removes part of a String
* - Replace/Overlay - Searches a String and replaces one String with
* another
* - Chomp/Chop - removes the last part of a String
* - LeftPad/RightPad/Center/Repeat - pads a String
* - UpperCase/LowerCase/SwapCase/Capitalize/Uncapitalize - changes the
* case of a String
* - CountMatches - counts the number of occurrences of one String in
* another
* - IsAlpha/IsNumeric/IsWhitespace/IsAsciiPrintable - checks the
* characters in a String
* - DefaultString - protects against a null input String
* - Reverse/ReverseDelimited - reverses a String
* - Abbreviate - abbreviates a string using ellipsis
* - Difference - compares Strings and reports on their differences
* - LevensteinDistance - the number of changes needed to change one
* String into another
*
*
* The StringUtils
class defines certain words related to String
* handling.
*
*
* - null -
null
* - empty - a zero-length string (
""
)
* - space - the space character (
' '
, char 32)
* - whitespace - the characters defined by
* {@link Character#isWhitespace(char)}
* - trim - the characters <= 32 as in {@link String#trim()}
*
*
* StringUtils
handles null
input Strings quietly.
* That is to say that a null
input will return null
.
* Where a boolean
or int
is being returned details
* vary by method.
*
*
* A side effect of the null
handling is that a
* NullPointerException
should be considered a bug in
* StringUtils
(except for deprecated methods).
*
*
* Methods in this class give sample code to explain their operation. The symbol
* *
is used to indicate any input including null
.
*
*
* @see java.lang.String
* @author Apache Software Foundation
* @author Apache Jakarta
* Turbine
* @author Jon S. Stevens
* @author Daniel L. Rall
* @author Greg Coladonato
* @author Ed Korthof
* @author Rand McNeely
* @author Fredrik Westermarck
* @author Holger Krauth
* @author Alexander Day Chaffee
* @author Henning P. Schmiedehausen
* @author Arun Mammen Thomas
* @author Gary Gregory
* @author Phil Steitz
* @author Al Chou
* @author Michael Davey
* @author Reuben Sivan
* @author Chris Hyzer
* @author Scott Johnson
* @since 1.0
* @version $Id: StringUtils.java 911986 2010-02-19 21:19:05Z niallp $
*/
public final class StringUtils {
/**
* The empty String {@code ""}.
* @since 2.0
*/
public static final String EMPTY = "";
/**
* Represents a failed index search.
* @since 2.1
*/
public static final int INDEX_NOT_FOUND = -1;
/**
* The maximum size to which the padding constant(s) can expand.
*/
private static final int PAD_LIMIT = 8192;
// Defaults
// -----------------------------------------------------------------------
/**
*
* Returns either the passed in String, or if the String is
* null
, an empty String ("").
*
*
*
* StringUtils.defaultString(null) = ""
* StringUtils.defaultString("") = ""
* StringUtils.defaultString("bat") = "bat"
*
*
* @see String#valueOf(Object)
* @param str the String to check, may be null
* @return the passed in String, or the empty String if it was
* null
*/
public static String defaultString(String str) {
return str == null ? "" : str;
}
/**
* Returns either the passed in String, or if the String is
* null
, the value of defaultStr
.
*
*
* StringUtils.defaultString(null, "NULL") = "NULL"
* StringUtils.defaultString("", "NULL") = ""
* StringUtils.defaultString("bat", "NULL") = "bat"
*
*
* @see String#valueOf(Object)
* @param str the String to check, may be null
* @param defaultStr the default String to return
* if the input is null
, may be null
* @return the passed in String, or the default if it was null
*/
public static String defaultString(String str, String defaultStr) {
return str == null ? defaultStr : str;
}
/**
* Returns either the passed in String, or if the String is
* empty or null
, the value of defaultStr
.
*
*
* StringUtils.defaultIfEmpty(null, "NULL") = "NULL"
* StringUtils.defaultIfEmpty("", "NULL") = "NULL"
* StringUtils.defaultIfEmpty("bat", "NULL") = "bat"
*
*
* @see StringUtils#defaultString(String, String)
* @param str the String to check, may be null
* @param defaultStr the default String to return
* if the input is empty ("") or null
, may be null
* @return the passed in String, or the default
*/
public static String defaultIfEmpty(String str, String defaultStr) {
return StringUtils.isEmpty(str) ? defaultStr : str;
}
/**
* Returns either the passed in CharSequence, or if the CharSequence is
* whitespace, empty ("") or {@code null}, the value of {@code defaultStr}.
*
*
* StringUtils.defaultIfBlank(null, "NULL") = "NULL"
* StringUtils.defaultIfBlank("", "NULL") = "NULL"
* StringUtils.defaultIfBlank(" ", "NULL") = "NULL"
* StringUtils.defaultIfBlank("bat", "NULL") = "bat"
* StringUtils.defaultIfBlank("", null) = null
*
* @param str the CharSequence to check, may be null
* @param defaultStr the default CharSequence to return
* if the input is whitespace, empty ("") or {@code null}, may be null
* @return the passed in CharSequence, or the default
* @see StringUtils#defaultString(String, String)
*/
public static String defaultIfBlank(String str, String defaultStr) {
return StringUtils.isBlank(str) ? defaultStr : str;
}
// Empty checks
// -----------------------------------------------------------------------
/**
*
* Checks if a String is empty ("") or null.
*
*
*
* StringUtils.isEmpty(null) = true
* StringUtils.isEmpty("") = true
* StringUtils.isEmpty(" ") = false
* StringUtils.isEmpty("bob") = false
* StringUtils.isEmpty(" bob ") = false
*
*
* NOTE: This method changed in Lang version 2.0. It no longer trims the
* String. That functionality is available in isBlank().
*
*
* @param str the String to check, may be null
* @return true
if the String is empty or null
*/
public static boolean isEmpty(String str) {
return str == null || str.length() == 0;
}
/**
*
* Checks if a String is whitespace, empty ("") or null.
*
*
*
* StringUtils.isBlank(null) = true
* StringUtils.isBlank("") = true
* StringUtils.isBlank(" ") = true
* StringUtils.isBlank("bob") = false
* StringUtils.isBlank(" bob ") = false
*
*
* @param str the String to check, may be null
* @return true
if the String is null, empty or whitespace
* @since 2.0
*/
public static boolean isBlank(String str) {
int strLen;
if (str == null || (strLen = str.length()) == 0) {
return true;
}
for (int i = 0; i < strLen; i++) {
if ((Character.isWhitespace(str.charAt(i)) == false)) {
return false;
}
}
return true;
}
// Count matches
// -----------------------------------------------------------------------
/**
*
* Counts how many times the substring appears in the larger String.
*
*
* A null
or empty ("") String input returns 0
.
*
*
*
* StringUtils.countMatches(null, *) = 0
* StringUtils.countMatches("", *) = 0
* StringUtils.countMatches("abba", null) = 0
* StringUtils.countMatches("abba", "") = 0
* StringUtils.countMatches("abba", "a") = 2
* StringUtils.countMatches("abba", "ab") = 1
* StringUtils.countMatches("abba", "xxx") = 0
*
*
* @param str the String to check, may be null
* @param sub the substring to count, may be null
* @return the number of occurrences, 0 if either String is
* null
*/
public static int countMatches(String str, String sub) {
if (isEmpty(str) || isEmpty(sub)) {
return 0;
}
int count = 0;
int idx = 0;
while ((idx = str.indexOf(sub, idx)) != -1) {
count++;
idx += sub.length();
}
return count;
}
// Padding
// -----------------------------------------------------------------------
/**
* Right pad a String with spaces (' ').
*
* The String is padded to the size of size
.
*
*
* StringUtils.rightPad(null, *) = null
* StringUtils.rightPad("", 3) = " "
* StringUtils.rightPad("bat", 3) = "bat"
* StringUtils.rightPad("bat", 5) = "bat "
* StringUtils.rightPad("bat", 1) = "bat"
* StringUtils.rightPad("bat", -1) = "bat"
*
*
* @param str the String to pad out, may be null
* @param size the size to pad to
* @return right padded String or original String if no padding is necessary,
* null
if null String input
*/
public static String rightPad(String str, int size) {
return rightPad(str, size, ' ');
}
/**
* Right pad a String with a specified character.
*
* The String is padded to the size of size
.
*
*
* StringUtils.rightPad(null, *, *) = null
* StringUtils.rightPad("", 3, 'z') = "zzz"
* StringUtils.rightPad("bat", 3, 'z') = "bat"
* StringUtils.rightPad("bat", 5, 'z') = "batzz"
* StringUtils.rightPad("bat", 1, 'z') = "bat"
* StringUtils.rightPad("bat", -1, 'z') = "bat"
*
*
* @param str the String to pad out, may be null
* @param size the size to pad to
* @param padChar the character to pad with
* @return right padded String or original String if no padding is necessary,
* null
if null String input
* @since 2.0
*/
public static String rightPad(String str, int size, char padChar) {
if (str == null) {
return null;
}
int pads = size - str.length();
if (pads <= 0) {
return str; // returns original String when possible
}
if (pads > PAD_LIMIT) {
return rightPad(str, size, String.valueOf(padChar));
}
return str.concat(padding(pads, padChar));
}
/**
* Right pad a String with a specified String.
*
* The String is padded to the size of size
.
*
*
* StringUtils.rightPad(null, *, *) = null
* StringUtils.rightPad("", 3, "z") = "zzz"
* StringUtils.rightPad("bat", 3, "yz") = "bat"
* StringUtils.rightPad("bat", 5, "yz") = "batyz"
* StringUtils.rightPad("bat", 8, "yz") = "batyzyzy"
* StringUtils.rightPad("bat", 1, "yz") = "bat"
* StringUtils.rightPad("bat", -1, "yz") = "bat"
* StringUtils.rightPad("bat", 5, null) = "bat "
* StringUtils.rightPad("bat", 5, "") = "bat "
*
*
* @param str the String to pad out, may be null
* @param size the size to pad to
* @param padStr the String to pad with, null or empty treated as single space
* @return right padded String or original String if no padding is necessary,
* null
if null String input
*/
public static String rightPad(String str, int size, String padStr) {
if (str == null) {
return null;
}
if (isEmpty(padStr)) {
padStr = " ";
}
int padLen = padStr.length();
int strLen = str.length();
int pads = size - strLen;
if (pads <= 0) {
return str; // returns original String when possible
}
if (padLen == 1 && pads <= PAD_LIMIT) {
return rightPad(str, size, padStr.charAt(0));
}
if (pads == padLen) {
return str.concat(padStr);
} else if (pads < padLen) {
return str.concat(padStr.substring(0, pads));
} else {
char[] padding = new char[pads];
char[] padChars = padStr.toCharArray();
for (int i = 0; i < pads; i++) {
padding[i] = padChars[i % padLen];
}
return str.concat(new String(padding));
}
}
/**
* Left pad a String with spaces (' ').
*
* The String is padded to the size of size.
*
*
* StringUtils.leftPad(null, *) = null
* StringUtils.leftPad("", 3) = " "
* StringUtils.leftPad("bat", 3) = "bat"
* StringUtils.leftPad("bat", 5) = " bat"
* StringUtils.leftPad("bat", 1) = "bat"
* StringUtils.leftPad("bat", -1) = "bat"
*
*
* @param str the String to pad out, may be null
* @param size the size to pad to
* @return left padded String or original String if no padding is necessary,
* null
if null String input
*/
public static String leftPad(String str, int size) {
return leftPad(str, size, ' ');
}
/**
* Left pad a String with a specified character.
*
* Pad to a size of size
.
*
*
* StringUtils.leftPad(null, *, *) = null
* StringUtils.leftPad("", 3, 'z') = "zzz"
* StringUtils.leftPad("bat", 3, 'z') = "bat"
* StringUtils.leftPad("bat", 5, 'z') = "zzbat"
* StringUtils.leftPad("bat", 1, 'z') = "bat"
* StringUtils.leftPad("bat", -1, 'z') = "bat"
*
*
* @param str the String to pad out, may be null
* @param size the size to pad to
* @param padChar the character to pad with
* @return left padded String or original String if no padding is necessary,
* null
if null String input
* @since 2.0
*/
public static String leftPad(String str, int size, char padChar) {
if (str == null) {
return null;
}
int pads = size - str.length();
if (pads <= 0) {
return str; // returns original String when possible
}
if (pads > PAD_LIMIT) {
return leftPad(str, size, String.valueOf(padChar));
}
return padding(pads, padChar).concat(str);
}
/**
* Left pad a String with a specified String.
*
* Pad to a size of size
.
*
*
* StringUtils.leftPad(null, *, *) = null
* StringUtils.leftPad("", 3, "z") = "zzz"
* StringUtils.leftPad("bat", 3, "yz") = "bat"
* StringUtils.leftPad("bat", 5, "yz") = "yzbat"
* StringUtils.leftPad("bat", 8, "yz") = "yzyzybat"
* StringUtils.leftPad("bat", 1, "yz") = "bat"
* StringUtils.leftPad("bat", -1, "yz") = "bat"
* StringUtils.leftPad("bat", 5, null) = " bat"
* StringUtils.leftPad("bat", 5, "") = " bat"
*
*
* @param str the String to pad out, may be null
* @param size the size to pad to
* @param padStr the String to pad with, null or empty treated as single space
* @return left padded String or original String if no padding is necessary,
* null
if null String input
*/
public static String leftPad(String str, int size, String padStr) {
if (str == null) {
return null;
}
if (isEmpty(padStr)) {
padStr = " ";
}
int padLen = padStr.length();
int strLen = str.length();
int pads = size - strLen;
if (pads <= 0) {
return str; // returns original String when possible
}
if (padLen == 1 && pads <= PAD_LIMIT) {
return leftPad(str, size, padStr.charAt(0));
}
if (pads == padLen) {
return padStr.concat(str);
} else if (pads < padLen) {
return padStr.substring(0, pads).concat(str);
} else {
char[] padding = new char[pads];
char[] padChars = padStr.toCharArray();
for (int i = 0; i < pads; i++) {
padding[i] = padChars[i % padLen];
}
return new String(padding).concat(str);
}
}
/**
* Returns padding using the specified delimiter repeated
* to a given length.
*
*
* StringUtils.padding(0, 'e') = ""
* StringUtils.padding(3, 'e') = "eee"
* StringUtils.padding(-2, 'e') = IndexOutOfBoundsException
*
*
* Note: this method doesn't not support padding with
* Unicode Supplementary Characters
* as they require a pair of char
s to be represented.
* If you are needing to support full I18N of your applications
* consider using {@link #repeat(String, int)} instead.
*
*
* @param repeat number of times to repeat delim
* @param padChar character to repeat
* @return String with repeated character
* @throws IndexOutOfBoundsException if repeat < 0
*/
private static String padding(int repeat, char padChar) throws IndexOutOfBoundsException {
if (repeat < 0) {
throw new IndexOutOfBoundsException("Cannot pad a negative amount: " + repeat);
}
final char[] buf = new char[repeat];
for (int i = 0; i < buf.length; i++) {
buf[i] = padChar;
}
return new String(buf);
}
// Abbreviating
//-----------------------------------------------------------------------
/**
* Abbreviates a String using ellipses. This will turn
* "Now is the time for all good men" into "Now is the time for..."
*
* Specifically:
*
* - If
str
is less than maxWidth
characters
* long, return it.
* - Else abbreviate it to
(substring(str, 0, max-3) + "...")
.
* - If
maxWidth
is less than 4
, throw an
* IllegalArgumentException
.
* - In no case will it return a String of length greater than
*
maxWidth
.
*
*
*
*
* StringUtils.abbreviate(null, *) = null
* StringUtils.abbreviate("", 4) = ""
* StringUtils.abbreviate("abcdefg", 6) = "abc..."
* StringUtils.abbreviate("abcdefg", 7) = "abcdefg"
* StringUtils.abbreviate("abcdefg", 8) = "abcdefg"
* StringUtils.abbreviate("abcdefg", 4) = "a..."
* StringUtils.abbreviate("abcdefg", 3) = IllegalArgumentException
*
*
* @param str the String to check, may be null
* @param maxWidth maximum length of result String, must be at least 4
* @return abbreviated String, null
if null String input
* @throws IllegalArgumentException if the width is too small
* @since 2.0
*/
public static String abbreviate(String str, int maxWidth) {
return abbreviate(str, 0, maxWidth);
}
/**
* Abbreviates a String using ellipses. This will turn
* "Now is the time for all good men" into "...is the time for..."
*
* Works like abbreviate(String, int)
, but allows you to specify
* a "left edge" offset. Note that this left edge is not necessarily going to
* be the leftmost character in the result, or the first character following the
* ellipses, but it will appear somewhere in the result.
*
*
In no case will it return a String of length greater than
* maxWidth
.
*
*
* StringUtils.abbreviate(null, *, *) = null
* StringUtils.abbreviate("", 0, 4) = ""
* StringUtils.abbreviate("abcdefghijklmno", -1, 10) = "abcdefg..."
* StringUtils.abbreviate("abcdefghijklmno", 0, 10) = "abcdefg..."
* StringUtils.abbreviate("abcdefghijklmno", 1, 10) = "abcdefg..."
* StringUtils.abbreviate("abcdefghijklmno", 4, 10) = "abcdefg..."
* StringUtils.abbreviate("abcdefghijklmno", 5, 10) = "...fghi..."
* StringUtils.abbreviate("abcdefghijklmno", 6, 10) = "...ghij..."
* StringUtils.abbreviate("abcdefghijklmno", 8, 10) = "...ijklmno"
* StringUtils.abbreviate("abcdefghijklmno", 10, 10) = "...ijklmno"
* StringUtils.abbreviate("abcdefghijklmno", 12, 10) = "...ijklmno"
* StringUtils.abbreviate("abcdefghij", 0, 3) = IllegalArgumentException
* StringUtils.abbreviate("abcdefghij", 5, 6) = IllegalArgumentException
*
*
* @param str the String to check, may be null
* @param offset left edge of source String
* @param maxWidth maximum length of result String, must be at least 4
* @return abbreviated String, null
if null String input
* @throws IllegalArgumentException if the width is too small
* @since 2.0
*/
public static String abbreviate(String str, int offset, int maxWidth) {
if (str == null) {
return null;
}
if (maxWidth < 4) {
throw new IllegalArgumentException("Minimum abbreviation width is 4");
}
if (str.length() <= maxWidth) {
return str;
}
if (offset > str.length()) {
offset = str.length();
}
if ((str.length() - offset) < (maxWidth - 3)) {
offset = str.length() - (maxWidth - 3);
}
if (offset <= 4) {
return str.substring(0, maxWidth - 3) + "...";
}
if (maxWidth < 7) {
throw new IllegalArgumentException("Minimum abbreviation width with offset is 7");
}
if ((offset + (maxWidth - 3)) < str.length()) {
return "..." + abbreviate(str.substring(offset), maxWidth - 3);
}
return "..." + str.substring(str.length() - (maxWidth - 3));
}
// ContainsAny
//-----------------------------------------------------------------------
/**
* Checks if the String contains any character in the given
* set of characters.
*
* A null
String will return false
.
* A null
or zero length search array will return false
.
*
*
* StringUtils.containsAny(null, *) = false
* StringUtils.containsAny("", *) = false
* StringUtils.containsAny(*, null) = false
* StringUtils.containsAny(*, []) = false
* StringUtils.containsAny("zzabyycdxx",['z','a']) = true
* StringUtils.containsAny("zzabyycdxx",['b','y']) = true
* StringUtils.containsAny("aba", ['z']) = false
*
*
* @param str the String to check, may be null
* @param searchChars the chars to search for, may be null
* @return the true
if any of the chars are found,
* false
if no match or null input
* @since 2.4
*/
public static boolean containsAny(String str, char... searchChars) {
if (str == null || str.length() == 0 || searchChars == null || searchChars.length == 0) {
return false;
}
for (int i = 0; i < str.length(); i++) {
char ch = str.charAt(i);
for (int j = 0; j < searchChars.length; j++) {
if (searchChars[j] == ch) {
return true;
}
}
}
return false;
}
/**
* Replaces all occurrences of a String within another String.
*
* A {@code null} reference passed to this method is a no-op.
*
*
* StringUtils.replace(null, *, *) = null
* StringUtils.replace("", *, *) = ""
* StringUtils.replace("any", null, *) = "any"
* StringUtils.replace("any", *, null) = "any"
* StringUtils.replace("any", "", *) = "any"
* StringUtils.replace("aba", "a", null) = "aba"
* StringUtils.replace("aba", "a", "") = "b"
* StringUtils.replace("aba", "a", "z") = "zbz"
*
*
* @see #replace(String text, String searchString, String replacement, int max)
* @param text text to search and replace in, may be null
* @param searchString the String to search for, may be null
* @param replacement the String to replace it with, may be null
* @return the text with any replacements processed,
* {@code null} if null String input
*/
public static String replace(String text, String searchString, String replacement) {
return replace(text, searchString, replacement, -1);
}
/**
* Replaces a String with another String inside a larger String,
* for the first {@code max} values of the search String.
*
* A {@code null} reference passed to this method is a no-op.
*
*
* StringUtils.replace(null, *, *, *) = null
* StringUtils.replace("", *, *, *) = ""
* StringUtils.replace("any", null, *, *) = "any"
* StringUtils.replace("any", *, null, *) = "any"
* StringUtils.replace("any", "", *, *) = "any"
* StringUtils.replace("any", *, *, 0) = "any"
* StringUtils.replace("abaa", "a", null, -1) = "abaa"
* StringUtils.replace("abaa", "a", "", -1) = "b"
* StringUtils.replace("abaa", "a", "z", 0) = "abaa"
* StringUtils.replace("abaa", "a", "z", 1) = "zbaa"
* StringUtils.replace("abaa", "a", "z", 2) = "zbza"
* StringUtils.replace("abaa", "a", "z", -1) = "zbzz"
*
*
* @param text text to search and replace in, may be null
* @param searchString the String to search for, may be null
* @param replacement the String to replace it with, may be null
* @param max maximum number of values to replace, or {@code -1} if no maximum
* @return the text with any replacements processed,
* {@code null} if null String input
*/
public static String replace(String text, String searchString, String replacement, int max) {
if (isEmpty(text) || isEmpty(searchString) || replacement == null || max == 0) {
return text;
}
int start = 0;
int end = text.indexOf(searchString, start);
if (end == INDEX_NOT_FOUND) {
return text;
}
int replLength = searchString.length();
int increase = replacement.length() - replLength;
increase = (increase < 0 ? 0 : increase);
increase *= (max < 0 ? 16 : (max > 64 ? 64 : max));
StringBuilder buf = new StringBuilder(text.length() + increase);
while (end != INDEX_NOT_FOUND) {
buf.append(text.substring(start, end)).append(replacement);
start = end + replLength;
if (--max == 0) {
break;
}
end = text.indexOf(searchString, start);
}
buf.append(text.substring(start));
return buf.toString();
}
/**
*
* Replaces all occurrences of Strings within another String.
*
*
*
* A null
reference passed to this method is a no-op, or if
* any "search string" or "string to replace" is null, that replace will be
* ignored. This will not repeat. For repeating replaces, call the
* overloaded method.
*
*
*
* StringUtils.replaceEach(null, *, *) = null
* StringUtils.replaceEach("", *, *) = ""
* StringUtils.replaceEach("aba", null, null) = "aba"
* StringUtils.replaceEach("aba", new String[0], null) = "aba"
* StringUtils.replaceEach("aba", null, new String[0]) = "aba"
* StringUtils.replaceEach("aba", new String[]{"a"}, null) = "aba"
* StringUtils.replaceEach("aba", new String[]{"a"}, new String[]{""}) = "b"
* StringUtils.replaceEach("aba", new String[]{null}, new String[]{"a"}) = "aba"
* StringUtils.replaceEach("abcde", new String[]{"ab", "d"}, new String[]{"w", "t"}) = "wcte"
* (example of how it does not repeat)
* StringUtils.replaceEach("abcde", new String[]{"ab", "d"}, new String[]{"d", "t"}) = "dcte"
*
*
* @param text
* text to search and replace in, no-op if null
* @param searchList
* the Strings to search for, no-op if null
* @param replacementList
* the Strings to replace them with, no-op if null
* @return the text with any replacements processed, null
if
* null String input
* @throws IndexOutOfBoundsException
* if the lengths of the arrays are not the same (null is ok,
* and/or size 0)
* @since 2.4
*/
public static String replaceEach(String text, String[] searchList, String[] replacementList) {
return replaceEach(text, searchList, replacementList, false, 0);
}
/**
*
* Replaces all occurrences of Strings within another String.
*
*
*
* A null
reference passed to this method is a no-op, or if
* any "search string" or "string to replace" is null, that replace will be
* ignored.
*
*
*
* StringUtils.replaceEach(null, *, *, *) = null
* StringUtils.replaceEach("", *, *, *) = ""
* StringUtils.replaceEach("aba", null, null, *) = "aba"
* StringUtils.replaceEach("aba", new String[0], null, *) = "aba"
* StringUtils.replaceEach("aba", null, new String[0], *) = "aba"
* StringUtils.replaceEach("aba", new String[]{"a"}, null, *) = "aba"
* StringUtils.replaceEach("aba", new String[]{"a"}, new String[]{""}, *) = "b"
* StringUtils.replaceEach("aba", new String[]{null}, new String[]{"a"}, *) = "aba"
* StringUtils.replaceEach("abcde", new String[]{"ab", "d"}, new String[]{"w", "t"}, *) = "wcte"
* (example of how it repeats)
* StringUtils.replaceEach("abcde", new String[]{"ab", "d"}, new String[]{"d", "t"}, false) = "dcte"
* StringUtils.replaceEach("abcde", new String[]{"ab", "d"}, new String[]{"d", "t"}, true) = "tcte"
* StringUtils.replaceEach("abcde", new String[]{"ab", "d"}, new String[]{"d", "ab"}, *) = IllegalArgumentException
*
*
* @param text
* text to search and replace in, no-op if null
* @param searchList
* the Strings to search for, no-op if null
* @param replacementList
* the Strings to replace them with, no-op if null
* @param repeat if true, then replace repeatedly
* until there are no more possible replacements or timeToLive < 0
* @param timeToLive
* if less than 0 then there is a circular reference and endless
* loop
* @return the text with any replacements processed, null
if
* null String input
* @throws IllegalArgumentException
* if the search is repeating and there is an endless loop due
* to outputs of one being inputs to another
* @throws IndexOutOfBoundsException
* if the lengths of the arrays are not the same (null is ok,
* and/or size 0)
* @since 2.4
*/
private static String replaceEach(String text, String[] searchList, String[] replacementList,
boolean repeat, int timeToLive)
{
// mchyzer Performance note: This creates very few new objects (one major goal)
// let me know if there are performance requests, we can create a harness to measure
if (text == null || text.length() == 0 || searchList == null ||
searchList.length == 0 || replacementList == null || replacementList.length == 0)
{
return text;
}
// if recursing, this shouldnt be less than 0
if (timeToLive < 0) {
throw new IllegalStateException("TimeToLive of " + timeToLive + " is less than 0: " + text);
}
int searchLength = searchList.length;
int replacementLength = replacementList.length;
// make sure lengths are ok, these need to be equal
if (searchLength != replacementLength) {
throw new IllegalArgumentException("Search and Replace array lengths don't match: "
+ searchLength
+ " vs "
+ replacementLength);
}
// keep track of which still have matches
boolean[] noMoreMatchesForReplIndex = new boolean[searchLength];
// index on index that the match was found
int textIndex = -1;
int replaceIndex = -1;
int tempIndex = -1;
// index of replace array that will replace the search string found
// NOTE: logic duplicated below START
for (int i = 0; i < searchLength; i++) {
if (noMoreMatchesForReplIndex[i] || searchList[i] == null ||
searchList[i].length() == 0 || replacementList[i] == null)
{
continue;
}
tempIndex = text.indexOf(searchList[i]);
// see if we need to keep searching for this
if (tempIndex == -1) {
noMoreMatchesForReplIndex[i] = true;
} else {
if (textIndex == -1 || tempIndex < textIndex) {
textIndex = tempIndex;
replaceIndex = i;
}
}
}
// NOTE: logic mostly below END
// no search strings found, we are done
if (textIndex == -1) {
return text;
}
int start = 0;
// get a good guess on the size of the result buffer so it doesnt have to double if it goes over a bit
int increase = 0;
// count the replacement text elements that are larger than their corresponding text being replaced
for (int i = 0; i < searchList.length; i++) {
int greater = replacementList[i].length() - searchList[i].length();
if (greater > 0) {
increase += 3 * greater; // assume 3 matches
}
}
// have upper-bound at 20% increase, then let Java take over
increase = Math.min(increase, text.length() / 5);
StringBuffer buf = new StringBuffer(text.length() + increase);
while (textIndex != -1) {
for (int i = start; i < textIndex; i++) {
buf.append(text.charAt(i));
}
buf.append(replacementList[replaceIndex]);
start = textIndex + searchList[replaceIndex].length();
textIndex = -1;
replaceIndex = -1;
tempIndex = -1;
// find the next earliest match
// NOTE: logic mostly duplicated above START
for (int i = 0; i < searchLength; i++) {
if (noMoreMatchesForReplIndex[i] || searchList[i] == null ||
searchList[i].length() == 0 || replacementList[i] == null)
{
continue;
}
tempIndex = text.indexOf(searchList[i], start);
// see if we need to keep searching for this
if (tempIndex == -1) {
noMoreMatchesForReplIndex[i] = true;
} else {
if (textIndex == -1 || tempIndex < textIndex) {
textIndex = tempIndex;
replaceIndex = i;
}
}
}
// NOTE: logic duplicated above END
}
int textLength = text.length();
for (int i = start; i < textLength; i++) {
buf.append(text.charAt(i));
}
String result = buf.toString();
if (!repeat) {
return result;
}
return replaceEach(result, searchList, replacementList, repeat, timeToLive - 1);
}
// Joining
//-----------------------------------------------------------------------
/**
* Joins the elements of the provided array into a single String
* containing the provided list of elements.
*
* No separator is added to the joined String.
* Null objects or empty strings within the array are represented by
* empty strings.
*
*
* StringUtils.join(null) = null
* StringUtils.join([]) = ""
* StringUtils.join([null]) = ""
* StringUtils.join(["a", "b", "c"]) = "abc"
* StringUtils.join([null, "", "a"]) = "a"
*
*
* @param the specific type of values to join together
* @param elements the values to join together, may be null
* @return the joined String, {@code null} if null array input
* @since 2.0
* @since 3.0 Changed signature to use varargs
*/
public static String join(T... elements) {
return join(elements, null);
}
/**
* Joins the elements of the provided array into a single String
* containing the provided list of elements.
*
* No delimiter is added before or after the list.
* Null objects or empty strings within the array are represented by
* empty strings.
*
*
* StringUtils.join(null, *) = null
* StringUtils.join([], *) = ""
* StringUtils.join([null], *) = ""
* StringUtils.join(["a", "b", "c"], ';') = "a;b;c"
* StringUtils.join(["a", "b", "c"], null) = "abc"
* StringUtils.join([null, "", "a"], ';') = ";;a"
*
*
* @param array the array of values to join together, may be null
* @param separator the separator character to use
* @return the joined String, {@code null} if null array input
* @since 2.0
*/
public static String join(Object[] array, char separator) {
if (array == null) {
return null;
}
return join(array, separator, 0, array.length);
}
/**
* Joins the elements of the provided array into a single String
* containing the provided list of elements.
*
* No delimiter is added before or after the list.
* Null objects or empty strings within the array are represented by
* empty strings.
*
*
* StringUtils.join(null, *) = null
* StringUtils.join([], *) = ""
* StringUtils.join([null], *) = ""
* StringUtils.join(["a", "b", "c"], ';') = "a;b;c"
* StringUtils.join(["a", "b", "c"], null) = "abc"
* StringUtils.join([null, "", "a"], ';') = ";;a"
*
*
* @param array the array of values to join together, may be null
* @param separator the separator character to use
* @param startIndex the first index to start joining from. It is
* an error to pass in an end index past the end of the array
* @param endIndex the index to stop joining from (exclusive). It is
* an error to pass in an end index past the end of the array
* @return the joined String, {@code null} if null array input
* @since 2.0
*/
public static String join(Object[] array, char separator, int startIndex, int endIndex) {
if (array == null) {
return null;
}
int noOfItems = (endIndex - startIndex);
if (noOfItems <= 0) {
return EMPTY;
}
StringBuilder buf = new StringBuilder(noOfItems * 16);
for (int i = startIndex; i < endIndex; i++) {
if (i > startIndex) {
buf.append(separator);
}
if (array[i] != null) {
buf.append(array[i]);
}
}
return buf.toString();
}
/**
* Joins the elements of the provided array into a single String
* containing the provided list of elements.
*
* No delimiter is added before or after the list.
* A {@code null} separator is the same as an empty String ("").
* Null objects or empty strings within the array are represented by
* empty strings.
*
*
* StringUtils.join(null, *) = null
* StringUtils.join([], *) = ""
* StringUtils.join([null], *) = ""
* StringUtils.join(["a", "b", "c"], "--") = "a--b--c"
* StringUtils.join(["a", "b", "c"], null) = "abc"
* StringUtils.join(["a", "b", "c"], "") = "abc"
* StringUtils.join([null, "", "a"], ',') = ",,a"
*
*
* @param array the array of values to join together, may be null
* @param separator the separator character to use, null treated as ""
* @return the joined String, {@code null} if null array input
*/
public static String join(Object[] array, String separator) {
if (array == null) {
return null;
}
return join(array, separator, 0, array.length);
}
/**
* Joins the elements of the provided array into a single String
* containing the provided list of elements.
*
* No delimiter is added before or after the list.
* A {@code null} separator is the same as an empty String ("").
* Null objects or empty strings within the array are represented by
* empty strings.
*
*
* StringUtils.join(null, *) = null
* StringUtils.join([], *) = ""
* StringUtils.join([null], *) = ""
* StringUtils.join(["a", "b", "c"], "--") = "a--b--c"
* StringUtils.join(["a", "b", "c"], null) = "abc"
* StringUtils.join(["a", "b", "c"], "") = "abc"
* StringUtils.join([null, "", "a"], ',') = ",,a"
*
*
* @param array the array of values to join together, may be null
* @param separator the separator character to use, null treated as ""
* @param startIndex the first index to start joining from. It is
* an error to pass in an end index past the end of the array
* @param endIndex the index to stop joining from (exclusive). It is
* an error to pass in an end index past the end of the array
* @return the joined String, {@code null} if null array input
*/
public static String join(Object[] array, String separator, int startIndex, int endIndex) {
if (array == null) {
return null;
}
if (separator == null) {
separator = EMPTY;
}
// endIndex - startIndex > 0: Len = NofStrings *(len(firstString) + len(separator))
// (Assuming that all Strings are roughly equally long)
int noOfItems = (endIndex - startIndex);
if (noOfItems <= 0) {
return EMPTY;
}
StringBuilder buf = new StringBuilder(noOfItems * 16);
for (int i = startIndex; i < endIndex; i++) {
if (i > startIndex) {
buf.append(separator);
}
if (array[i] != null) {
buf.append(array[i]);
}
}
return buf.toString();
}
private StringUtils() {}
// -------------------------------------------------------------------------
// XXX: The following methods are taken from ObjectUtils
// -------------------------------------------------------------------------
/**
* Compares two objects for equality, where either one or both
* objects may be {@code null}.
*
*
* ObjectUtils.equals(null, null) = true
* ObjectUtils.equals(null, "") = false
* ObjectUtils.equals("", null) = false
* ObjectUtils.equals("", "") = true
* ObjectUtils.equals(Boolean.TRUE, null) = false
* ObjectUtils.equals(Boolean.TRUE, "true") = false
* ObjectUtils.equals(Boolean.TRUE, Boolean.TRUE) = true
* ObjectUtils.equals(Boolean.TRUE, Boolean.FALSE) = false
*
*
* @param object1 the first object, may be {@code null}
* @param object2 the second object, may be {@code null}
* @return {@code true} if the values of both objects are the same
*/
public static boolean equals(Object object1, Object object2) {
if (object1 == object2) {
return true;
}
if ((object1 == null) || (object2 == null)) {
return false;
}
return object1.equals(object2);
}
/**
* Returns a default value if the object passed is {@code null}.
*
*
* ObjectUtils.defaultIfNull(null, null) = null
* ObjectUtils.defaultIfNull(null, "") = ""
* ObjectUtils.defaultIfNull(null, "zz") = "zz"
* ObjectUtils.defaultIfNull("abc", *) = "abc"
* ObjectUtils.defaultIfNull(Boolean.TRUE, *) = Boolean.TRUE
*
*
* @param the type of the object
* @param object the {@code Object} to test, may be {@code null}
* @param defaultValue the default value to return, may be {@code null}
* @return {@code object} if it is not {@code null}, defaultValue otherwise
*/
public static T defaultIfNull(T object, T defaultValue) {
return object != null ? object : defaultValue;
}
// -------------------------------------------------------------------------
// XXX: The following methods are not part of Apache's commons-lang library
// -------------------------------------------------------------------------
/**
* Convert a string to camel case
*/
public static String toCamelCase(String string) {
StringBuilder result = new StringBuilder();
// [#2515] - Keep trailing underscores
for (String word : string.split("_", -1)) {
// Uppercase first letter of a word
if (word.length() > 0) {
// [#82] - If a word starts with a digit, prevail the
// underscore to prevent naming clashes
if (Character.isDigit(word.charAt(0))) {
result.append("_");
}
result.append(word.substring(0, 1).toUpperCase());
result.append(word.substring(1).toLowerCase());
}
// If no letter exists, prevail the underscore (e.g. leading
// underscores)
else {
result.append("_");
}
}
return result.toString();
}
/**
* Convert a string to camel case starting with a lower case letter
*/
public static String toCamelCaseLC(String string) {
return toLC(toCamelCase(string));
}
/**
* Change a string's first letter to lower case
*/
public static String toLC(String string) {
if (string == null || string.isEmpty()) {
return string;
}
return Character.toLowerCase(string.charAt(0)) + string.substring(1);
}
/**
* A custom adaptation of {@link Pattern#split(CharSequence, int)}.
*
* This is useful if the matched split-tokens should be returned as well.
* For example:
* split("e", "hello world") // ["h", "e", "llo world"]
* split("o", "hello world") // ["hell", "o", " w", "o", "rld"]
* split("[eo]", "hello world") // ["h", "e", "ll", "o", " w", "o", "rld"]
*
*
* The result will always be an odd-length array.
*/
public static String[] split(String regex, CharSequence input) {
int index = 0;
ArrayList matchList = new ArrayList();
Matcher m = Pattern.compile(regex).matcher(input);
// Add segments before each match found
while (m.find()) {
matchList.add(input.subSequence(index, m.start()).toString());
matchList.add(input.subSequence(m.start(), m.end()).toString());
index = m.end();
}
// If no match was found, return this
if (index == 0)
return new String[] { input.toString() };
// Add remaining segment
matchList.add(input.subSequence(index, input.length()).toString());
// Construct result
Iterator it = matchList.iterator();
while (it.hasNext()) {
if ("".equals(it.next())) {
it.remove();
}
}
String[] result = new String[matchList.size()];
return matchList.toArray(result);
}
}