/*
* 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 net.hasor.core.utils;
import java.io.IOException;
import java.util.Arrays;
/**
* <p>Operations on {@link java.lang.String} that are <code>null</code> safe.</p>
*/
public class StringUtils {
public static final String[] EMPTY_STRING_ARRAY = new String[0];
/**
* The empty String <code>""</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;
/**
* <p>The maximum size to which the padding constant(s) can expand.</p>
*/
private static final int PAD_LIMIT = 8192;
// Empty checks
//-----------------------------------------------------------------------
/**
* <p>Checks if a String is empty ("") or null.</p>
*
* <pre>
* StringUtils.isEmpty(null) = true
* StringUtils.isEmpty("") = true
* StringUtils.isEmpty(" ") = false
* StringUtils.isEmpty("bob") = false
* StringUtils.isEmpty(" bob ") = false
* </pre>
*
* <p>NOTE: This method changed in Lang version 2.0.
* It no longer trims the String.
* That functionality is available in isBlank().</p>
*
* @param str the String to check, may be null
* @return <code>true</code> if the String is empty or null
*/
public static boolean isEmpty(final String str) {
return str == null || str.length() == 0;
}
/**
* <p>Checks if a String is whitespace, empty ("") or null.</p>
*
* <pre>
* StringUtils.isBlank(null) = true
* StringUtils.isBlank("") = true
* StringUtils.isBlank(" ") = true
* StringUtils.isBlank("bob") = false
* StringUtils.isBlank(" bob ") = false
* </pre>
*
* @param str the String to check, may be null
* @return <code>true</code> if the String is null, empty or whitespace
* @since 2.0
*/
public static boolean isBlank(final 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))) {
return false;
}
}
return true;
}
/**
* <p>Checks if a String is not empty (""), not null and not whitespace only.</p>
*
* <pre>
* StringUtils.isNotBlank(null) = false
* StringUtils.isNotBlank("") = false
* StringUtils.isNotBlank(" ") = false
* StringUtils.isNotBlank("bob") = true
* StringUtils.isNotBlank(" bob ") = true
* </pre>
*
* @param str the String to check, may be null
* @return <code>true</code> if the String is
* not empty and not null and not whitespace
* @since 2.0
*/
public static boolean isNotBlank(final String str) {
return !StringUtils.isBlank(str);
}
// Joining
//-----------------------------------------------------------------------
/**
* <p>Joins the elements of the provided array into a single String
* containing the provided list of elements.</p>
*
* <p>No delimiter is added before or after the list.
* A <code>null</code> separator is the same as an empty String ("").
* Null objects or empty strings within the array are represented by
* empty strings.</p>
*
* <pre>
* 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"
* </pre>
*
* @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</code> if null array input
*/
public static String join(final Object[] array, String separator) {
if (array == null) {
return null;
}
if (separator == null) {
separator = StringUtils.EMPTY;
}
//
int startIndex = 0;
int endIndex = array.length;
// endIndex - startIndex > 0: Len = NofStrings *(len(firstString) + len(separator))
// (Assuming that all Strings are roughly equally long)
int bufSize = endIndex - startIndex;
if (bufSize <= 0) {
return StringUtils.EMPTY;
}
bufSize *= (array[startIndex] == null ? 16 : array[startIndex].toString().length()) + separator.length();
StringBuilder buf = new StringBuilder(bufSize);
for (int i = startIndex; i < endIndex; i++) {
if (i > startIndex) {
buf.append(separator);
}
if (array[i] != null) {
buf.append(array[i]);
}
}
return buf.toString();
}
// Padding
//-----------------------------------------------------------------------
/**
* <p>Returns padding using the specified delimiter repeated
* to a given length.</p>
*
* <pre>
* StringUtils.padding(0, 'e') = ""
* StringUtils.padding(3, 'e') = "eee"
* StringUtils.padding(-2, 'e') = IndexOutOfBoundsException
* </pre>
*
* <p>Note: this method doesn't not support padding with
* <a href="http://www.unicode.org/glossary/#supplementary_character">Unicode Supplementary Characters</a>
* as they require a pair of <code>char</code>s to be represented.
* If you are needing to support full I18N of your applications
* consider using replace instead.
* </p>
*
* @param repeat number of times to repeat delim
* @param padChar character to repeat
* @return String with repeated character
* @throws IndexOutOfBoundsException if <code>repeat < 0</code>
*/
private static String padding(final int repeat, final 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);
}
/**
* <p>Right pad a String with a specified character.</p>
*
* <p>The String is padded to the size of <code>size</code>.</p>
*
* <pre>
* 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"
* </pre>
*
* @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,
* <code>null</code> if null String input
* @since 2.0
*/
public static String rightPad(final String str, final int size, final char padChar) {
if (str == null) {
return null;
}
int pads = size - str.length();
if (pads <= 0) {
return str; // returns original String when possible
}
if (pads > StringUtils.PAD_LIMIT) {
return StringUtils.rightPad(str, size, String.valueOf(padChar));
}
return str.concat(StringUtils.padding(pads, padChar));
}
/**
* <p>Right pad a String with a specified String.</p>
*
* <p>The String is padded to the size of <code>size</code>.</p>
*
* <pre>
* 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 "
* </pre>
*
* @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,
* <code>null</code> if null String input
*/
public static String rightPad(final String str, final int size, String padStr) {
if (str == null) {
return null;
}
if (StringUtils.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 <= StringUtils.PAD_LIMIT) {
return StringUtils.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));
}
}
/**
* <p>Left pad a String with a specified character.</p>
*
* <p>Pad to a size of <code>size</code>.</p>
*
* <pre>
* 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"
* </pre>
*
* @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,
* <code>null</code> if null String input
* @since 2.0
*/
public static String leftPad(final String str, final int size, final char padChar) {
if (str == null) {
return null;
}
int pads = size - str.length();
if (pads <= 0) {
return str; // returns original String when possible
}
if (pads > StringUtils.PAD_LIMIT) {
return StringUtils.leftPad(str, size, String.valueOf(padChar));
}
return StringUtils.padding(pads, padChar).concat(str);
}
/**
* <p>Left pad a String with a specified String.</p>
*
* <p>Pad to a size of <code>size</code>.</p>
*
* <pre>
* 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"
* </pre>
*
* @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,
* <code>null</code> if null String input
*/
public static String leftPad(final String str, final int size, String padStr) {
if (str == null) {
return null;
}
if (StringUtils.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 <= StringUtils.PAD_LIMIT) {
return StringUtils.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);
}
}
// Centering
//-----------------------------------------------------------------------
/**
* <p>Centers a String in a larger String of size <code>size</code>.
* Uses a supplied String as the value to pad the String with.</p>
*
* <p>If the size is less than the String length, the String is returned.
* A <code>null</code> String returns <code>null</code>.
* A negative size is treated as zero.</p>
*
* <pre>
* StringUtils.center(null, *, *) = null
* StringUtils.center("", 4, " ") = " "
* StringUtils.center("ab", -1, " ") = "ab"
* StringUtils.center("ab", 4, " ") = " ab"
* StringUtils.center("abcd", 2, " ") = "abcd"
* StringUtils.center("a", 4, " ") = " a "
* StringUtils.center("a", 4, "yz") = "yayz"
* StringUtils.center("abc", 7, null) = " abc "
* StringUtils.center("abc", 7, "") = " abc "
* </pre>
*
* @param str the String to center, may be null
* @param size the int size of new String, negative treated as zero
* @param padStr the String to pad the new String with, must not be null or empty
* @return centered String, <code>null</code> if null String input
* @throws IllegalArgumentException if padStr is <code>null</code> or empty
*/
public static String center(String str, final int size, String padStr) {
if (str == null || size <= 0) {
return str;
}
if (StringUtils.isEmpty(padStr)) {
padStr = " ";
}
int strLen = str.length();
int pads = size - strLen;
if (pads <= 0) {
return str;
}
str = StringUtils.leftPad(str, strLen + pads / 2, padStr);
str = StringUtils.rightPad(str, size, padStr);
return str;
}
/**
* 转换首字母大写。
* <pre>
* StringUtils.firstCharToUpperCase(null) = null
* StringUtils.firstCharToUpperCase("") = ""
* StringUtils.firstCharToUpperCase("abc") = "Abc"
* StringUtils.firstCharToUpperCase("Abc") = "Abc"
* StringUtils.firstCharToUpperCase("ABC") = "ABC"
* </pre>
*/
public static String firstCharToUpperCase(final String value) {
if (StringUtils.isBlank(value)) {
return value;
}
StringBuffer sb = new StringBuffer(value);
char firstChar = sb.charAt(0);
sb.delete(0, 1);
sb.insert(0, (char) (firstChar >= 97 ? firstChar - 32 : firstChar));
return sb.toString();
}
/**
* 转换首字母小写。
* <pre>
* StringUtils.firstCharToLowerCase(null) = null
* StringUtils.firstCharToLowerCase("") = ""
* StringUtils.firstCharToLowerCase("abc") = "abc"
* StringUtils.firstCharToLowerCase("Abc") = "abc"
* StringUtils.firstCharToLowerCase("ABC") = "aBC"
* </pre>
*/
public static String firstCharToLowerCase(final String value) {
if (StringUtils.isBlank(value)) {
return value;
}
StringBuffer sb = new StringBuffer(value);
char firstChar = sb.charAt(0);
sb.delete(0, 1);
sb.insert(0, (char) (firstChar <= 90 ? firstChar + 32 : firstChar));
return sb.toString();
}
/**
* 按照某个字符组装固定长度的的字符串。
* <pre>
* StringUtils.fixedString(' ',4) = " "
* StringUtils.fixedString(' ',2) = " "
* StringUtils.fixedString('A',2) = "AA"
* StringUtils.fixedString('A',0) = ""
* StringUtils.fixedString('A',-1) = null
* </pre>
*/
public static String fixedString(final char c, final int length) {
if (length < 0) {
return null;
}
if (length == 0) {
return "";
}
//
char[] chars = new char[length];
for (int i = 0; i < chars.length; i++) {
chars[i] = c;
}
return new String(chars);
}
/**
* <p>Checks whether the character is ASCII 7 bit.</p>
*
* <pre>
* CharUtils.isAscii('a') = true
* CharUtils.isAscii('A') = true
* CharUtils.isAscii('3') = true
* CharUtils.isAscii('-') = true
* CharUtils.isAscii('\n') = true
* CharUtils.isAscii('©') = false
* </pre>
*
* @param ch the character to check
* @return true if less than 128
*/
public static boolean isAscii(final char ch) {
return ch < 128;
}
/**
* <p>Checks if an array of primitive longs is empty or <code>null</code>.</p>
*
* @param array the array to test
* @return <code>true</code> if the array is empty or <code>null</code>
* @since 2.1
*/
public static boolean isEmptyArray(final Object[] array) {
return array == null || array.length == 0;
}
/* ------------------------------------------------------------ */
private static final char[] escapes = new char[32];
static {
Arrays.fill(escapes, (char) 0xFFFF);
escapes['\b'] = 'b';
escapes['\t'] = 't';
escapes['\n'] = 'n';
escapes['\f'] = 'f';
escapes['\r'] = 'r';
}
/** Quote a string into an Appendable.
* The characters ", \, \n, \r, \t, \f and \b are escaped
* @param input The String to quote.
*/
public static String quote(String input) {
StringBuilder buffer = new StringBuilder();
quote(buffer, input);
return buffer.toString();
}
public static void quote(Appendable buffer, String input) {
try {
buffer.append('"');
for (int i = 0; i < input.length(); ++i) {
char c = input.charAt(i);
if (c >= 32) {
if (c == '"' || c == '\\')
buffer.append('\\');
buffer.append(c);
} else {
char escape = escapes[c];
if (escape == 0xFFFF) {
// Unicode escape
buffer.append('\\').append('u').append('0').append('0');
if (c < 0x10)
buffer.append('0');
buffer.append(Integer.toString(c, 16));
} else {
buffer.append('\\').append(escape);
}
}
}
buffer.append('"');
} catch (IOException x) {
throw new RuntimeException(x);
}
}
//
}