/*
* Copyright (c) 2008, Harald Kuhr
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* * Neither the name of the copyright holder nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package com.twelvemonkeys.lang;
import com.twelvemonkeys.util.
StringTokenIterator;
import java.awt.*;
import java.io.
UnsupportedEncodingException;
import java.lang.reflect.
Array;
import java.lang.reflect.
Field;
import java.lang.reflect.
Method;
import java.lang.reflect.
Modifier;
import java.nio.charset.
UnsupportedCharsetException;
import java.sql.
Timestamp;
import java.text.
DateFormat;
import java.text.
ParseException;
import java.text.
SimpleDateFormat;
import java.util.
ArrayList;
import java.util.
Date;
import java.util.
List;
import java.util.regex.
Pattern;
import java.util.regex.
PatternSyntaxException;
/**
* A utility class with some useful string manipulation methods.
*
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @author <A href="mailto:eirik.torske@twelvemonkeys.com">Eirik Torske</A>
* @author last modified by $Author: haku $
* @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/main/java/com/twelvemonkeys/lang/StringUtil.java#2 $
* @todo Consistency check: Method names, parameter sequence, Exceptions,
* return values, null-value handling and parameter names (cosmetics).
*/
public final class
StringUtil {
/**
* The default delimiter string, used by the {@code toXXXArray()}
* methods.
* Its value is {@code ", \t\n\r\f"}.
* <!-- No, it IS actually ", \t\b\r\f", but looks better in a browser -->
*
* @see #toStringArray(String)
* @see #toIntArray(String)
* @see #toLongArray(String)
* @see #toDoubleArray(String)
*/
public final static
String DELIMITER_STRING = ", \t\n\r\f";
// Avoid constructor showing up in API doc
private
StringUtil() {
}
/**
* Constructs a new {@link String} by decoding the specified sub array of bytes using the specified charset.
* Replacement for {@link String#String(byte[], int, int, String) new String(byte[], int, int, String)}, that does
* not throw the checked {@link UnsupportedEncodingException},
* but instead the unchecked {@link UnsupportedCharsetException} if the character set is not supported.
*
* @param pData the bytes to be decoded to characters
* @param pOffset the index of the first byte to decode
* @param pLength the number of bytes to decode
* @param pCharset the name of a supported character set
* @return a newly created string.
* @throws UnsupportedCharsetException
*
* @see String#String(byte[], int, int, String)
*/
public static
String decode(final byte[]
pData, final int
pOffset, final int
pLength, final
String pCharset) {
try {
return new
String(
pData,
pOffset,
pLength,
pCharset);
}
catch (
UnsupportedEncodingException e) {
throw new
UnsupportedCharsetException(
pCharset);
}
}
/**
* Returns the value of the given {@code Object}, as a {@code String}.
* Unlike String.valueOf, this method returns {@code null}
* instead of the {@code String} "null", if {@code null} is given as
* the argument.
*
* @param pObj the Object to find the {@code String} value of.
* @return the String value of the given object, or {@code null} if the
* {@code pObj} == {@code null}.
* @see String#valueOf(Object)
* @see String#toString()
*/
public static
String valueOf(
Object pObj) {
return ((
pObj != null) ?
pObj.
toString() : null);
}
/**
* Converts a string to uppercase.
*
* @param pString the string to convert
* @return the string converted to uppercase, or null if the argument was
* null.
*/
public static
String toUpperCase(
String pString) {
if (
pString != null) {
return
pString.
toUpperCase();
}
return null;
}
/**
* Converts a string to lowercase.
*
* @param pString the string to convert
* @return the string converted to lowercase, or null if the argument was
* null.
*/
public static
String toLowerCase(
String pString) {
if (
pString != null) {
return
pString.
toLowerCase();
}
return null;
}
/**
* Tests if a String is null, or contains nothing but white-space.
*
* @param pString The string to test
* @return true if the string is null or contains only whitespace,
* otherwise false.
*/
public static boolean
isEmpty(
String pString) {
return ((
pString == null) || (
pString.
trim().
length() == 0));
}
/**
* Tests a string array, to see if all items are null or an empty string.
*
* @param pStringArray The string array to check.
* @return true if the string array is null or only contains string items
* that are null or contain only whitespace, otherwise false.
*/
public static boolean
isEmpty(
String[]
pStringArray) {
// No elements to test
if (
pStringArray == null) {
return true;
}
// Test all the elements
for (
String string :
pStringArray) {
if (!
isEmpty(
string)) {
return false;
}
}
// All elements are empty
return true;
}
/**
* Tests if a string contains another string.
*
* @param pContainer The string to test
* @param pLookFor The string to look for
* @return {@code true} if the container string is contains the string, and
* both parameters are non-{@code null}, otherwise {@code false}.
*/
public static boolean
contains(
String pContainer,
String pLookFor) {
return ((
pContainer != null) && (
pLookFor != null) && (
pContainer.
indexOf(
pLookFor) >= 0));
}
/**
* Tests if a string contains another string, ignoring case.
*
* @param pContainer The string to test
* @param pLookFor The string to look for
* @return {@code true} if the container string is contains the string, and
* both parameters are non-{@code null}, otherwise {@code false}.
* @see #contains(String,String)
*/
public static boolean
containsIgnoreCase(
String pContainer,
String pLookFor) {
return
indexOfIgnoreCase(
pContainer,
pLookFor, 0) >= 0;
}
/**
* Tests if a string contains a specific character.
*
* @param pString The string to check.
* @param pChar The character to search for.
* @return true if the string contains the specific character.
*/
public static boolean
contains(final
String pString, final int
pChar) {
return ((
pString != null) && (
pString.
indexOf(
pChar) >= 0));
}
/**
* Tests if a string contains a specific character, ignoring case.
*
* @param pString The string to check.
* @param pChar The character to search for.
* @return true if the string contains the specific character.
*/
public static boolean
containsIgnoreCase(
String pString, int
pChar) {
return ((
pString != null)
&& ((
pString.
indexOf(
Character.
toLowerCase((char)
pChar)) >= 0)
|| (
pString.
indexOf(
Character.
toUpperCase((char)
pChar)) >= 0)));
// NOTE: I don't convert the string to uppercase, but instead test
// the string (potentially) two times, as this is more efficient for
// long strings (in most cases).
}
/**
* Returns the index within this string of the first occurrence of the
* specified substring.
*
* @param pString The string to test
* @param pLookFor The string to look for
* @return if the string argument occurs as a substring within this object,
* then the index of the first character of the first such substring is
* returned; if it does not occur as a substring, -1 is returned.
* @see String#indexOf(String)
*/
public static int
indexOfIgnoreCase(
String pString,
String pLookFor) {
return
indexOfIgnoreCase(
pString,
pLookFor, 0);
}
/**
* Returns the index within this string of the first occurrence of the
* specified substring, starting at the specified index.
*
* @param pString The string to test
* @param pLookFor The string to look for
* @param pPos The first index to test
* @return if the string argument occurs as a substring within this object,
* then the index of the first character of the first such substring is
* returned; if it does not occur as a substring, -1 is returned.
* @see String#indexOf(String,int)
*/
public static int
indexOfIgnoreCase(
String pString,
String pLookFor, int
pPos) {
if ((
pString == null) || (
pLookFor == null)) {
return -1;
}
if (
pLookFor.
length() == 0) {
return
pPos;// All strings "contains" the empty string
}
if (
pLookFor.
length() >
pString.
length()) {
return -1;// Cannot contain string longer than itself
}
// Get first char
char
firstL =
Character.
toLowerCase(
pLookFor.
charAt(0));
char
firstU =
Character.
toUpperCase(
pLookFor.
charAt(0));
int
indexLower = 0;
int
indexUpper = 0;
for (int
i =
pPos;
i <= (
pString.
length() -
pLookFor.
length());
i++) {
// Peek for first char
indexLower = ((
indexLower >= 0) && (
indexLower <=
i))
?
pString.
indexOf(
firstL,
i)
:
indexLower;
indexUpper = ((
indexUpper >= 0) && (
indexUpper <=
i))
?
pString.
indexOf(
firstU,
i)
:
indexUpper;
if (
indexLower < 0) {
if (
indexUpper < 0) {
return -1;// First char not found
}
else {
i =
indexUpper;// Only upper
}
}
else if (
indexUpper < 0) {
i =
indexLower;// Only lower
}
else {
// Both found, select first occurence
i = (
indexLower <
indexUpper)
?
indexLower
:
indexUpper;
}
// Only one?
if (
pLookFor.
length() == 1) {
return
i;// The only char found!
}
// Test if we still have enough chars
else if (
i > (
pString.
length() -
pLookFor.
length())) {
return -1;
}
// Test if last char equals! (regionMatches is expensive)
else if ((
pString.
charAt(
i +
pLookFor.
length() - 1) !=
Character.
toLowerCase(
pLookFor.
charAt(
pLookFor.
length() - 1)))
&& (
pString.
charAt(
i +
pLookFor.
length() - 1) !=
Character.
toUpperCase(
pLookFor.
charAt(
pLookFor.
length() - 1)))) {
continue;// Nope, try next
}
// Test from second char, until second-last char
else if ((
pLookFor.
length() <= 2) ||
pString.
regionMatches(true,
i + 1,
pLookFor, 1,
pLookFor.
length() - 2)) {
return
i;
}
}
return -1;
}
/**
* Returns the index within this string of the rightmost occurrence of the
* specified substring. The rightmost empty string "" is considered to
* occur at the index value {@code pString.length() - 1}.
*
* @param pString The string to test
* @param pLookFor The string to look for
* @return If the string argument occurs one or more times as a substring
* within this object at a starting index no greater than fromIndex, then
* the index of the first character of the last such substring is returned.
* If it does not occur as a substring starting at fromIndex or earlier, -1
* is returned.
* @see String#lastIndexOf(String)
*/
public static int
lastIndexOfIgnoreCase(
String pString,
String pLookFor) {
return
lastIndexOfIgnoreCase(
pString,
pLookFor,
pString != null ?
pString.
length() - 1 : -1);
}
/**
* Returns the index within this string of the rightmost occurrence of the
* specified substring. The rightmost empty string "" is considered to
* occur at the index value {@code pPos}
*
* @param pString The string to test
* @param pLookFor The string to look for
* @param pPos The last index to test
* @return If the string argument occurs one or more times as a substring
* within this object at a starting index no greater than fromIndex, then
* the index of the first character of the last such substring is returned.
* If it does not occur as a substring starting at fromIndex or earlier, -1
* is returned.
* @see String#lastIndexOf(String,int)
*/
public static int
lastIndexOfIgnoreCase(
String pString,
String pLookFor, int
pPos) {
if ((
pString == null) || (
pLookFor == null)) {
return -1;
}
if (
pLookFor.
length() == 0) {
return
pPos;// All strings "contains" the empty string
}
if (
pLookFor.
length() >
pString.
length()) {
return -1;// Cannot contain string longer than itself
}
// Get first char
char
firstL =
Character.
toLowerCase(
pLookFor.
charAt(0));
char
firstU =
Character.
toUpperCase(
pLookFor.
charAt(0));
int
indexLower =
pPos;
int
indexUpper =
pPos;
for (int
i =
pPos;
i >= 0;
i--) {
// Peek for first char
indexLower = ((
indexLower >= 0) && (
indexLower >=
i))
?
pString.
lastIndexOf(
firstL,
i)
:
indexLower;
indexUpper = ((
indexUpper >= 0) && (
indexUpper >=
i))
?
pString.
lastIndexOf(
firstU,
i)
:
indexUpper;
if (
indexLower < 0) {
if (
indexUpper < 0) {
return -1;// First char not found
}
else {
i =
indexUpper;// Only upper
}
}
else if (
indexUpper < 0) {
i =
indexLower;// Only lower
}
else {
// Both found, select last occurence
i = (
indexLower >
indexUpper)
?
indexLower
:
indexUpper;
}
// Only one?
if (
pLookFor.
length() == 1) {
return
i;// The only char found!
}
// Test if we still have enough chars
else if (
i > (
pString.
length() -
pLookFor.
length())) {
//return -1;
continue;
}
// Test if last char equals! (regionMatches is expensive)
else
if ((
pString.
charAt(
i +
pLookFor.
length() - 1) !=
Character.
toLowerCase(
pLookFor.
charAt(
pLookFor.
length() - 1)))
&& (
pString.
charAt(
i +
pLookFor.
length() - 1) !=
Character.
toUpperCase(
pLookFor.
charAt(
pLookFor.
length() - 1)))) {
continue;// Nope, try next
}
// Test from second char, until second-last char
else
if ((
pLookFor.
length() <= 2) ||
pString.
regionMatches(true,
i + 1,
pLookFor, 1,
pLookFor.
length() - 2)) {
return
i;
}
}
return -1;
}
/**
* Returns the index within this string of the first occurrence of the
* specified character.
*
* @param pString The string to test
* @param pChar The character to look for
* @return if the string argument occurs as a substring within this object,
* then the index of the first character of the first such substring is
* returned; if it does not occur as a substring, -1 is returned.
* @see String#indexOf(int)
*/
public static int
indexOfIgnoreCase(
String pString, int
pChar) {
return
indexOfIgnoreCase(
pString,
pChar, 0);
}
/**
* Returns the index within this string of the first occurrence of the
* specified character, starting at the specified index.
*
* @param pString The string to test
* @param pChar The character to look for
* @param pPos The first index to test
* @return if the string argument occurs as a substring within this object,
* then the index of the first character of the first such substring is
* returned; if it does not occur as a substring, -1 is returned.
* @see String#indexOf(int,int)
*/
public static int
indexOfIgnoreCase(
String pString, int
pChar, int
pPos) {
if ((
pString == null)) {
return -1;
}
// Get first char
char
lower =
Character.
toLowerCase((char)
pChar);
char
upper =
Character.
toUpperCase((char)
pChar);
int
indexLower;
int
indexUpper;
// Test for char
indexLower =
pString.
indexOf(
lower,
pPos);
indexUpper =
pString.
indexOf(
upper,
pPos);
if (
indexLower < 0) {
/* if (indexUpper < 0)
return -1; // First char not found
else */
return
indexUpper;// Only upper
}
else if (
indexUpper < 0) {
return
indexLower;// Only lower
}
else {
// Both found, select first occurence
return (
indexLower <
indexUpper)
?
indexLower
:
indexUpper;
}
}
/**
* Returns the index within this string of the last occurrence of the
* specified character.
*
* @param pString The string to test
* @param pChar The character to look for
* @return if the string argument occurs as a substring within this object,
* then the index of the first character of the first such substring is
* returned; if it does not occur as a substring, -1 is returned.
* @see String#lastIndexOf(int)
*/
public static int
lastIndexOfIgnoreCase(
String pString, int
pChar) {
return
lastIndexOfIgnoreCase(
pString,
pChar,
pString != null ?
pString.
length() : -1);
}
/**
* Returns the index within this string of the last occurrence of the
* specified character, searching backward starting at the specified index.
*
* @param pString The string to test
* @param pChar The character to look for
* @param pPos The last index to test
* @return if the string argument occurs as a substring within this object,
* then the index of the first character of the first such substring is
* returned; if it does not occur as a substring, -1 is returned.
* @see String#lastIndexOf(int,int)
*/
public static int
lastIndexOfIgnoreCase(
String pString, int
pChar, int
pPos) {
if ((
pString == null)) {
return -1;
}
// Get first char
char
lower =
Character.
toLowerCase((char)
pChar);
char
upper =
Character.
toUpperCase((char)
pChar);
int
indexLower;
int
indexUpper;
// Test for char
indexLower =
pString.
lastIndexOf(
lower,
pPos);
indexUpper =
pString.
lastIndexOf(
upper,
pPos);
if (
indexLower < 0) {
/* if (indexUpper < 0)
return -1; // First char not found
else */
return
indexUpper;// Only upper
}
else if (
indexUpper < 0) {
return
indexLower;// Only lower
}
else {
// Both found, select last occurence
return (
indexLower >
indexUpper)
?
indexLower
:
indexUpper;
}
}
/**
* Trims the argument string for whitespace on the left side only.
*
* @param pString the string to trim
* @return the string with no whitespace on the left, or {@code null} if
* the string argument is {@code null}.
* @see #rtrim
* @see String#trim()
*/
public static
String ltrim(
String pString) {
if ((
pString == null) || (
pString.
length() == 0)) {
return
pString;// Null or empty string
}
for (int
i = 0;
i <
pString.
length();
i++) {
if (!
Character.
isWhitespace(
pString.
charAt(
i))) {
if (
i == 0) {
return
pString;// First char is not whitespace
}
else {
return
pString.
substring(
i);// Return rest after whitespace
}
}
}
// If all whitespace, return empty string
return "";
}
/**
* Trims the argument string for whitespace on the right side only.
*
* @param pString the string to trim
* @return the string with no whitespace on the right, or {@code null} if
* the string argument is {@code null}.
* @see #ltrim
* @see String#trim()
*/
public static
String rtrim(
String pString) {
if ((
pString == null) || (
pString.
length() == 0)) {
return
pString;// Null or empty string
}
for (int
i =
pString.
length();
i > 0;
i--) {
if (!
Character.
isWhitespace(
pString.
charAt(
i - 1))) {
if (
i ==
pString.
length()) {
return
pString;// First char is not whitespace
}
else {
return
pString.
substring(0,
i);// Return before whitespace
}
}
}
// If all whitespace, return empty string
return "";
}
/**
* Replaces a substring of a string with another string. All matches are
* replaced.
*
* @param pSource The source String
* @param pPattern The pattern to replace
* @param pReplace The new String to be inserted instead of the
* replace String
* @return The new String with the pattern replaced
*/
public static
String replace(
String pSource,
String pPattern,
String pReplace) {
if (
pPattern.
length() == 0) {
return
pSource;// Special case: No pattern to replace
}
int
match;
int
offset = 0;
StringBuilder result = new
StringBuilder();
// Loop string, until last occurence of pattern, and replace
while ((
match =
pSource.
indexOf(
pPattern,
offset)) != -1) {
// Append everything until pattern
result.
append(
pSource.
substring(
offset,
match));
// Append the replace string
result.
append(
pReplace);
offset =
match +
pPattern.
length();
}
// Append rest of string and return
result.
append(
pSource.
substring(
offset));
return
result.
toString();
}
/**
* Replaces a substring of a string with another string, ignoring case.
* All matches are replaced.
*
* @param pSource The source String
* @param pPattern The pattern to replace
* @param pReplace The new String to be inserted instead of the
* replace String
* @return The new String with the pattern replaced
* @see #replace(String,String,String)
*/
public static
String replaceIgnoreCase(
String pSource,
String pPattern,
String pReplace) {
if (
pPattern.
length() == 0) {
return
pSource;// Special case: No pattern to replace
}
int
match;
int
offset = 0;
StringBuilder result = new
StringBuilder();
while ((
match =
indexOfIgnoreCase(
pSource,
pPattern,
offset)) != -1) {
result.
append(
pSource.
substring(
offset,
match));
result.
append(
pReplace);
offset =
match +
pPattern.
length();
}
result.
append(
pSource.
substring(
offset));
return
result.
toString();
}
/**
* Cuts a string between two words, before a sepcified length, if the
* string is longer than the maxium lenght. The string is optionally padded
* with the pad argument. The method assumes words to be separated by the
* space character (" ").
* Note that the maximum length argument is absolute, and will also include
* the length of the padding.
*
* @param pString The string to cut
* @param pMaxLen The maximum length before cutting
* @param pPad The string to append at the end, aftrer cutting
* @return The cutted string with padding, or the original string, if it
* was shorter than the max length.
* @see #pad(String,int,String,boolean)
*/
public static
String cut(
String pString, int
pMaxLen,
String pPad) {
if (
pString == null) {
return null;
}
if (
pPad == null) {
pPad = "";
}
int
len =
pString.
length();
if (
len >
pMaxLen) {
len =
pString.
lastIndexOf(' ',
pMaxLen -
pPad.
length());
}
else {
return
pString;
}
return
pString.
substring(0,
len) +
pPad;
}
/**
* Makes the Nth letter of a String uppercase. If the index is outside the
* the length of the argument string, the argument is simply returned.
*
* @param pString The string to capitalize
* @param pIndex The base-0 index of the char to capitalize.
* @return The capitalized string, or null, if a null argument was given.
*/
public static
String capitalize(
String pString, int
pIndex) {
if (
pIndex < 0) {
throw new
IndexOutOfBoundsException("Negative index not allowed: " +
pIndex);
}
if (
pString == null ||
pString.
length() <=
pIndex) {
return
pString;
}
// This is the fastest method, according to my tests
// Skip array duplication if allready capitalized
if (
Character.
isUpperCase(
pString.
charAt(
pIndex))) {
return
pString;
}
// Convert to char array, capitalize and create new String
char[]
charArray =
pString.
toCharArray();
charArray[
pIndex] =
Character.
toUpperCase(
charArray[
pIndex]);
return new
String(
charArray);
/**
StringBuilder buf = new StringBuilder(pString);
buf.setCharAt(pIndex, Character.toUpperCase(buf.charAt(pIndex)));
return buf.toString();
//*/
/**
return pString.substring(0, pIndex)
+ Character.toUpperCase(pString.charAt(pIndex))
+ pString.substring(pIndex + 1);
//*/
}
/**
* Makes the first letter of a String uppercase.
*
* @param pString The string to capitalize
* @return The capitalized string, or null, if a null argument was given.
*/
public static
String capitalize(
String pString) {
return
capitalize(
pString, 0);
}
/**
* Formats a number with leading zeroes, to a specified length.
*
* @param pNum The number to format
* @param pLen The number of digits
* @return A string containing the formatted number
* @throws IllegalArgumentException Thrown, if the number contains
* more digits than allowed by the length argument.
* @see #pad(String,int,String,boolean)
* @deprecated Use StringUtil.pad instead!
*/
/*public*/
static
String formatNumber(long
pNum, int
pLen) throws
IllegalArgumentException {
StringBuilder result = new
StringBuilder();
if (
pNum >=
Math.
pow(10,
pLen)) {
throw new
IllegalArgumentException("The number to format cannot contain more digits than the length argument specifies!");
}
for (int
i =
pLen;
i > 1;
i--) {
if (
pNum <
Math.
pow(10,
i - 1)) {
result.
append('0');
}
else {
break;
}
}
result.
append(
pNum);
return
result.
toString();
}
/**
* String length check with simple concatenation of selected pad-string.
* E.g. a zip number from 123 to the correct 0123.
*
* @param pSource The source string.
* @param pRequiredLength The accurate length of the resulting string.
* @param pPadString The string for concatenation.
* @param pPrepend The location of fill-ins, prepend (true),
* or append (false)
* @return a concatenated string.
* @todo What if source is allready longer than required length?
* @todo Consistency with cut
* @see #cut(String,int,String)
*/
public static
String pad(
String pSource, int
pRequiredLength,
String pPadString, boolean
pPrepend) {
if (
pPadString == null ||
pPadString.
length() == 0) {
throw new
IllegalArgumentException("Pad string: \"" +
pPadString + "\"");
}
if (
pSource.
length() >=
pRequiredLength) {
return
pSource;
}
// TODO: Benchmark the new version against the old one, to see if it's really faster
// Rewrite to first create pad
// - pad += pad; - until length is >= gap
// then append the pad and cut if too long
int
gap =
pRequiredLength -
pSource.
length();
StringBuilder result = new
StringBuilder(
pPadString);
while (
result.
length() <
gap) {
result.
append(
result);
}
if (
result.
length() >
gap) {
result.
delete(
gap,
result.
length());
}
return
pPrepend ?
result.
append(
pSource).
toString() :
result.
insert(0,
pSource).
toString();
/*
StringBuilder result = new StringBuilder(pSource);
// Concatenation until proper string length
while (result.length() < pRequiredLength) {
// Prepend or append
if (pPrepend) { // Front
result.insert(0, pPadString);
}
else { // Back
result.append(pPadString);
}
}
// Truncate
if (result.length() > pRequiredLength) {
if (pPrepend) {
result.delete(0, result.length() - pRequiredLength);
}
else {
result.delete(pRequiredLength, result.length());
}
}
return result.toString();
*/
}
/**
* Converts the string to a date, using the default date format.
*
* @param pString the string to convert
* @return the date
* @see DateFormat
* @see DateFormat#getInstance()
*/
public static
Date toDate(
String pString) {
// Default
return
toDate(
pString,
DateFormat.
getInstance());
}
/**
* Converts the string to a date, using the given format.
*
* @param pString the string to convert
* @param pFormat the date format
* @return the date
* @todo cache formats?
* @see java.text.SimpleDateFormat
* @see java.text.SimpleDateFormat#SimpleDateFormat(String)
*/
public static
Date toDate(
String pString,
String pFormat) {
// Get the format from cache, or create new and insert
// Return new date
return
toDate(
pString, new
SimpleDateFormat(
pFormat));
}
/**
* Converts the string to a date, using the given format.
*
* @param pString the string to convert
* @param pFormat the date format
* @return the date
* @see SimpleDateFormat
* @see SimpleDateFormat#SimpleDateFormat(String)
* @see DateFormat
*/
public static
Date toDate(final
String pString, final
DateFormat pFormat) {
try {
synchronized (
pFormat) {
// Parse date using given format
return
pFormat.
parse(
pString);
}
}
catch (
ParseException pe) {
// Wrap in RuntimeException
throw new
IllegalArgumentException(
pe.
getMessage());
}
}
/**
* Converts the string to a jdbc Timestamp, using the standard Timestamp
* escape format.
*
* @param pValue the value
* @return a new {@code Timestamp}
* @see java.sql.Timestamp
* @see java.sql.Timestamp#valueOf(String)
*/
public static
Timestamp toTimestamp(final
String pValue) {
// Parse date using default format
return
Timestamp.
valueOf(
pValue);
}
/**
* Converts a delimiter separated String to an array of Strings.
*
* @param pString The comma-separated string
* @param pDelimiters The delimiter string
* @return a {@code String} array containing the delimiter separated elements
*/
public static
String[]
toStringArray(
String pString,
String pDelimiters) {
if (
isEmpty(
pString)) {
return new
String[0];
}
StringTokenIterator st = new
StringTokenIterator(
pString,
pDelimiters);
List<
String>
v = new
ArrayList<
String>();
while (
st.
hasMoreElements()) {
v.
add(
st.
nextToken());
}
return
v.
toArray(new
String[
v.
size()]);
}
/**
* Converts a comma-separated String to an array of Strings.
*
* @param pString The comma-separated string
* @return a {@code String} array containing the comma-separated elements
* @see #toStringArray(String,String)
*/
public static
String[]
toStringArray(
String pString) {
return
toStringArray(
pString,
DELIMITER_STRING);
}
/**
* Converts a comma-separated String to an array of ints.
*
* @param pString The comma-separated string
* @param pDelimiters The delimiter string
* @param pBase The radix
* @return an {@code int} array
* @throws NumberFormatException if any of the elements are not parseable
* as an int
*/
public static int[]
toIntArray(
String pString,
String pDelimiters, int
pBase) {
if (
isEmpty(
pString)) {
return new int[0];
}
// Some room for improvement here...
String[]
temp =
toStringArray(
pString,
pDelimiters);
int[]
array = new int[
temp.length];
for (int
i = 0;
i <
array.length;
i++) {
array[
i] =
Integer.
parseInt(
temp[
i],
pBase);
}
return
array;
}
/**
* Converts a comma-separated String to an array of ints.
*
* @param pString The comma-separated string
* @return an {@code int} array
* @throws NumberFormatException if any of the elements are not parseable
* as an int
* @see #toStringArray(String,String)
* @see #DELIMITER_STRING
*/
public static int[]
toIntArray(
String pString) {
return
toIntArray(
pString,
DELIMITER_STRING, 10);
}
/**
* Converts a comma-separated String to an array of ints.
*
* @param pString The comma-separated string
* @param pDelimiters The delimiter string
* @return an {@code int} array
* @throws NumberFormatException if any of the elements are not parseable
* as an int
* @see #toIntArray(String,String)
*/
public static int[]
toIntArray(
String pString,
String pDelimiters) {
return
toIntArray(
pString,
pDelimiters, 10);
}
/**
* Converts a comma-separated String to an array of longs.
*
* @param pString The comma-separated string
* @param pDelimiters The delimiter string
* @return a {@code long} array
* @throws NumberFormatException if any of the elements are not parseable
* as a long
*/
public static long[]
toLongArray(
String pString,
String pDelimiters) {
if (
isEmpty(
pString)) {
return new long[0];
}
// Some room for improvement here...
String[]
temp =
toStringArray(
pString,
pDelimiters);
long[]
array = new long[
temp.length];
for (int
i = 0;
i <
array.length;
i++) {
array[
i] =
Long.
parseLong(
temp[
i]);
}
return
array;
}
/**
* Converts a comma-separated String to an array of longs.
*
* @param pString The comma-separated string
* @return a {@code long} array
* @throws NumberFormatException if any of the elements are not parseable
* as a long
* @see #toStringArray(String,String)
* @see #DELIMITER_STRING
*/
public static long[]
toLongArray(
String pString) {
return
toLongArray(
pString,
DELIMITER_STRING);
}
/**
* Converts a comma-separated String to an array of doubles.
*
* @param pString The comma-separated string
* @param pDelimiters The delimiter string
* @return a {@code double} array
* @throws NumberFormatException if any of the elements are not parseable
* as a double
*/
public static double[]
toDoubleArray(
String pString,
String pDelimiters) {
if (
isEmpty(
pString)) {
return new double[0];
}
// Some room for improvement here...
String[]
temp =
toStringArray(
pString,
pDelimiters);
double[]
array = new double[
temp.length];
for (int
i = 0;
i <
array.length;
i++) {
array[
i] =
Double.
valueOf(
temp[
i]);
// Double.parseDouble() is 1.2...
}
return
array;
}
/**
* Converts a comma-separated String to an array of doubles.
*
* @param pString The comma-separated string
* @return a {@code double} array
* @throws NumberFormatException if any of the elements are not parseable
* as a double
* @see #toDoubleArray(String,String)
* @see #DELIMITER_STRING
*/
public static double[]
toDoubleArray(
String pString) {
return
toDoubleArray(
pString,
DELIMITER_STRING);
}
/**
* Parses a string to a Color.
* The argument can be a color constant (static constant from
* {@link java.awt.Color java.awt.Color}), like {@code black} or
* {@code red}, or it can be HTML/CSS-style, on the format:
* <UL>
* <LI>{@code #RRGGBB}, where RR, GG and BB means two digit
* hexadecimal for red, green and blue values respectively.</LI>
* <LI>{@code #AARRGGBB}, as above, with AA as alpha component.</LI>
* <LI>{@code #RGB}, where R, G and B means one digit
* hexadecimal for red, green and blue values respectively.</LI>
* <LI>{@code #ARGB}, as above, with A as alpha component.</LI>
* </UL>
*
* @param pString the string representation of the color
* @return the {@code Color} object, or {@code null} if the argument
* is {@code null}
* @throws IllegalArgumentException if the string does not map to a color.
* @see java.awt.Color
*/
public static
Color toColor(
String pString) {
// No string, no color
if (
pString == null) {
return null;
}
// #RRGGBB format
if (
pString.
charAt(0) == '#') {
int
r = 0;
int
g = 0;
int
b = 0;
int
a = -1;// alpha
if (
pString.
length() >= 7) {
int
idx = 1;
// AA
if (
pString.
length() >= 9) {
a =
Integer.
parseInt(
pString.
substring(
idx,
idx + 2), 0x10);
idx += 2;
}
// RR GG BB
r =
Integer.
parseInt(
pString.
substring(
idx,
idx + 2), 0x10);
g =
Integer.
parseInt(
pString.
substring(
idx + 2,
idx + 4), 0x10);
b =
Integer.
parseInt(
pString.
substring(
idx + 4,
idx + 6), 0x10);
}
else if (
pString.
length() >= 4) {
int
idx = 1;
// A
if (
pString.
length() >= 5) {
a =
Integer.
parseInt(
pString.
substring(
idx, ++
idx), 0x10) * 0x10;
}
// R G B
r =
Integer.
parseInt(
pString.
substring(
idx, ++
idx), 0x10) * 0x10;
g =
Integer.
parseInt(
pString.
substring(
idx, ++
idx), 0x10) * 0x10;
b =
Integer.
parseInt(
pString.
substring(
idx, ++
idx), 0x10) * 0x10;
}
if (
a != -1) {
// With alpha
return new
Color(
r,
g,
b,
a);
}
// No alpha
return new
Color(
r,
g,
b);
}
// Get color by name
try {
Class colorClass =
Color.class;
Field field = null;
// Workaround for stupidity in Color class constant field names
try {
field =
colorClass.
getField(
pString);
}
catch (
Exception e) {
// Don't care, this is just a workaround...
}
if (
field == null) {
// NOTE: The toLowerCase() on the next line will lose darkGray
// and lightGray...
field =
colorClass.
getField(
pString.
toLowerCase());
}
// Only try to get public final fields
int
mod =
field.
getModifiers();
if (
Modifier.
isPublic(
mod) &&
Modifier.
isStatic(
mod)) {
return (
Color)
field.
get(null);
}
}
catch (
NoSuchFieldException nsfe) {
// No such color, throw illegal argument?
throw new
IllegalArgumentException("No such color: " +
pString);
}
catch (
SecurityException se) {
// Can't access field, return null
}
catch (
IllegalAccessException iae) {
// Can't happen, as the field must be public static
}
catch (
IllegalArgumentException iar) {
// Can't happen, as the field must be static
}
// This should never be reached, but you never know... ;-)
return null;
}
/**
* Creates a HTML/CSS String representation of the given color.
* The HTML/CSS color format is defined as:
* <UL>
* <LI>{@code #RRGGBB}, where RR, GG and BB means two digit
* hexadecimal for red, green and blue values respectively.</LI>
* <LI>{@code #AARRGGBB}, as above, with AA as alpha component.</LI>
* </UL>
* <p/>
* Examlples: {@code toColorString(Color.red) == "#ff0000"},
* {@code toColorString(new Color(0xcc, 0xcc, 0xcc)) == "#cccccc"}.
*
* @param pColor the color
* @return A String representation of the color on HTML/CSS form
* @todo Consider moving to ImageUtil?
*/
public static
String toColorString(
Color pColor) {
// Not a color...
if (
pColor == null) {
return null;
}
StringBuilder str = new
StringBuilder(
Integer.
toHexString(
pColor.
getRGB()));
// Make sure string is 8 chars
for (int
i =
str.
length();
i < 8;
i++) {
str.
insert(0, '0');
}
// All opaque is default
if (
str.
charAt(0) == 'f' &&
str.
charAt(1) == 'f') {
str.
delete(0, 2);
}
// Prepend hash
return
str.
insert(0, '#').
toString();
}
/**
* Tests a string, to see if it is an number (element of <b>Z</b>).
* Valid integers are positive natural numbers (1, 2, 3, ...),
* their negatives (?1, ?2, ?3, ...) and the number zero.
* <p/>
* Note that there is no guarantees made, that this number can be
* represented as either an int or a long.
*
* @param pString The string to check.
* @return true if the String is a natural number.
*/
public static boolean
isNumber(
String pString) {
if (
isEmpty(
pString)) {
return false;
}
// Special case for first char, may be minus sign ('-')
char
ch =
pString.
charAt(0);
if (!(
ch == '-' ||
Character.
isDigit(
ch))) {
return false;
}
// Test every char
for (int
i = 1;
i <
pString.
length();
i++) {
if (!
Character.
isDigit(
pString.
charAt(
i))) {
return false;
}
}
// All digits must be a natural number
return true;
}
/*
* This version is benchmarked against toStringArray and found to be
* increasingly slower, the more elements the string contains.
* Kept here
*/
/**
* Removes all occurences of a specific character in a string.
* <i>This method is not design for efficiency!</i>
* <p>
*
* @param pSource
* @param pSubstring
* @param pPosition
* @return the modified string.
*/
/*
public static String removeChar(String pSourceString, final char pBadChar) {
char[] sourceCharArray = pSourceString.toCharArray();
List modifiedCharList = new Vector(sourceCharArray.length, 1);
// Filter the string
for (int i = 0; i < sourceCharArray.length; i++) {
if (sourceCharArray[i] != pBadChar) {
modifiedCharList.add(new Character(sourceCharArray[i]));
}
}
// Clean the character list
modifiedCharList = (List) CollectionUtil.purifyCollection((Collection) modifiedCharList);
// Create new modified String
char[] modifiedCharArray = new char[modifiedCharList.size()];
for (int i = 0; i < modifiedCharArray.length; i++) {
modifiedCharArray[i] = ((Character) modifiedCharList.get(i)).charValue();
}
return new String(modifiedCharArray);
}
*/
/**
*
* <i>This method is not design for efficiency!</i>
* <p>
* @param pSourceString The String for modification.
* @param pBadChars The char array containing the characters to remove from the source string.
* @return the modified string.
* @-deprecated Not tested yet!
*
*/
/*
public static String removeChars(String pSourceString, final char[] pBadChars) {
char[] sourceCharArray = pSourceString.toCharArray();
List modifiedCharList = new Vector(sourceCharArray.length, 1);
Map badCharMap = new Hashtable();
Character dummyChar = new Character('*');
for (int i = 0; i < pBadChars.length; i++) {
badCharMap.put(new Character(pBadChars[i]), dummyChar);
}
// Filter the string
for (int i = 0; i < sourceCharArray.length; i++) {
Character arrayChar = new Character(sourceCharArray[i]);
if (!badCharMap.containsKey(arrayChar)) {
modifiedCharList.add(new Character(sourceCharArray[i]));
}
}
// Clean the character list
modifiedCharList = (List) CollectionUtil.purifyCollection((Collection) modifiedCharList);
// Create new modified String
char[] modifiedCharArray = new char[modifiedCharList.size()];
for (int i = 0; i < modifiedCharArray.length; i++) {
modifiedCharArray[i] = ((Character) modifiedCharList.get(i)).charValue();
}
return new String(modifiedCharArray);
}
*/
/**
* Ensures that a string includes a given substring at a given position.
* <p/>
* Extends the string with a given string if it is not already there.
* E.g an URL "www.vg.no", to "http://www.vg.no".
*
* @param pSource The source string.
* @param pSubstring The substring to include.
* @param pPosition The location of the fill-in, the index starts with 0.
* @return the string, with the substring at the given location.
*/
static
String ensureIncludesAt(
String pSource,
String pSubstring, int
pPosition) {
StringBuilder newString = new
StringBuilder(
pSource);
try {
String existingSubstring =
pSource.
substring(
pPosition,
pPosition +
pSubstring.
length());
if (!
existingSubstring.
equalsIgnoreCase(
pSubstring)) {
newString.
insert(
pPosition,
pSubstring);
}
}
catch (
Exception e) {
// Do something!?
}
return
newString.
toString();
}
/**
* Ensures that a string does not include a given substring at a given
* position.
* <p/>
* Removes a given substring from a string if it is there.
* E.g an URL "http://www.vg.no", to "www.vg.no".
*
* @param pSource The source string.
* @param pSubstring The substring to check and possibly remove.
* @param pPosition The location of possible substring removal, the index starts with 0.
* @return the string, without the substring at the given location.
*/
static
String ensureExcludesAt(
String pSource,
String pSubstring, int
pPosition) {
StringBuilder newString = new
StringBuilder(
pSource);
try {
String existingString =
pSource.
substring(
pPosition + 1,
pPosition +
pSubstring.
length() + 1);
if (!
existingString.
equalsIgnoreCase(
pSubstring)) {
newString.
delete(
pPosition,
pPosition +
pSubstring.
length());
}
}
catch (
Exception e) {
// Do something!?
}
return
newString.
toString();
}
/**
* Gets the first substring between the given string boundaries.
* <p/>
*
* @param pSource The source string.
* @param pBeginBoundaryString The string that marks the beginning.
* @param pEndBoundaryString The string that marks the end.
* @param pOffset The index to start searching in the source
* string. If it is less than 0, the index will be set to 0.
* @return the substring demarcated by the given string boundaries or null
* if not both string boundaries are found.
*/
public static
String substring(final
String pSource, final
String pBeginBoundaryString, final
String pEndBoundaryString,
final int
pOffset) {
// Check offset
int
offset = (
pOffset < 0)
? 0
:
pOffset;
// Find the start index
int
startIndex =
pSource.
indexOf(
pBeginBoundaryString,
offset) +
pBeginBoundaryString.
length();
if (
startIndex < 0) {
return null;
}
// Find the end index
int
endIndex =
pSource.
indexOf(
pEndBoundaryString,
startIndex);
if (
endIndex < 0) {
return null;
}
return
pSource.
substring(
startIndex,
endIndex);
}
/**
* Removes the first substring demarcated by the given string boundaries.
* <p/>
*
* @param pSource The source string.
* @param pBeginBoundaryChar The character that marks the beginning of the
* unwanted substring.
* @param pEndBoundaryChar The character that marks the end of the
* unwanted substring.
* @param pOffset The index to start searching in the source
* string. If it is less than 0, the index will be set to 0.
* @return the source string with all the demarcated substrings removed,
* included the demarcation characters.
* @deprecated this method actually removes all demarcated substring.. doesn't it?
*/
/*public*/
static
String removeSubstring(final
String pSource, final char
pBeginBoundaryChar, final char
pEndBoundaryChar, final int
pOffset) {
StringBuilder filteredString = new
StringBuilder();
boolean
insideDemarcatedArea = false;
char[]
charArray =
pSource.
toCharArray();
for (char
c :
charArray) {
if (!
insideDemarcatedArea) {
if (
c ==
pBeginBoundaryChar) {
insideDemarcatedArea = true;
}
else {
filteredString.
append(
c);
}
}
else {
if (
c ==
pEndBoundaryChar) {
insideDemarcatedArea = false;
}
}
}
return
filteredString.
toString();
}
/**
* Removes all substrings demarcated by the given string boundaries.
* <p/>
*
* @param pSource The source string.
* @param pBeginBoundaryChar The character that marks the beginning of the unwanted substring.
* @param pEndBoundaryChar The character that marks the end of the unwanted substring.
* @return the source string with all the demarcated substrings removed, included the demarcation characters.
*/
/*public*/
static
String removeSubstrings(final
String pSource, final char
pBeginBoundaryChar, final char
pEndBoundaryChar) {
StringBuilder filteredString = new
StringBuilder();
boolean
insideDemarcatedArea = false;
char[]
charArray =
pSource.
toCharArray();
for (char
c :
charArray) {
if (!
insideDemarcatedArea) {
if (
c ==
pBeginBoundaryChar) {
insideDemarcatedArea = true;
}
else {
filteredString.
append(
c);
}
}
else {
if (
c ==
pEndBoundaryChar) {
insideDemarcatedArea = false;
}
}
}
return
filteredString.
toString();
}
/**
* Gets the first element of a {@code String} containing string elements delimited by a given delimiter.
* <i>NB - Straightforward implementation!</i>
* <p/>
*
* @param pSource The source string.
* @param pDelimiter The delimiter used in the source string.
* @return The last string element.
* @todo This method should be re-implemented for more efficient execution.
*/
public static
String getFirstElement(final
String pSource, final
String pDelimiter) {
if (
pDelimiter == null) {
throw new
IllegalArgumentException("delimiter == null");
}
if (
StringUtil.
isEmpty(
pSource)) {
return
pSource;
}
int
idx =
pSource.
indexOf(
pDelimiter);
if (
idx >= 0) {
return
pSource.
substring(0,
idx);
}
return
pSource;
}
/**
* Gets the last element of a {@code String} containing string elements
* delimited by a given delimiter.
* <i>NB - Straightforward implementation!</i>
* <p/>
*
* @param pSource The source string.
* @param pDelimiter The delimiter used in the source string.
* @return The last string element.
*/
public static
String getLastElement(final
String pSource, final
String pDelimiter) {
if (
pDelimiter == null) {
throw new
IllegalArgumentException("delimiter == null");
}
if (
StringUtil.
isEmpty(
pSource)) {
return
pSource;
}
int
idx =
pSource.
lastIndexOf(
pDelimiter);
if (
idx >= 0) {
return
pSource.
substring(
idx + 1);
}
return
pSource;
}
/**
* Converts a string array to a string of comma-separated values.
*
* @param pStringArray the string array
* @return A string of comma-separated values
*/
public static
String toCSVString(
Object[]
pStringArray) {
return
toCSVString(
pStringArray, ", ");
}
/**
* Converts a string array to a string separated by the given delimiter.
*
* @param pStringArray the string array
* @param pDelimiterString the delimiter string
* @return string of delimiter separated values
* @throws IllegalArgumentException if {@code pDelimiterString == null}
*/
public static
String toCSVString(
Object[]
pStringArray,
String pDelimiterString) {
if (
pStringArray == null) {
return "";
}
if (
pDelimiterString == null) {
throw new
IllegalArgumentException("delimiter == null");
}
StringBuilder buffer = new
StringBuilder();
for (int
i = 0;
i <
pStringArray.length;
i++) {
if (
i > 0) {
buffer.
append(
pDelimiterString);
}
buffer.
append(
pStringArray[
i]);
}
return
buffer.
toString();
}
/**
* @param pObject the object
* @return a deep string representation of the given object
*/
public static
String deepToString(
Object pObject) {
return
deepToString(
pObject, false, 1);
}
/**
* @param pObject the object
* @param pDepth the maximum depth
* @param pForceDeep {@code true} to force deep {@code toString}, even
* if object overrides toString
* @return a deep string representation of the given object
* @todo Array handling (print full type and length)
* @todo Register handlers for specific toDebugString handling? :-)
*/
public static
String deepToString(
Object pObject, boolean
pForceDeep, int
pDepth) {
// Null is null
if (
pObject == null) {
return null;
}
// Implements toString, use it as-is unless pForceDeep
if (!
pForceDeep && !
isIdentityToString(
pObject)) {
return
pObject.
toString();
}
StringBuilder buffer = new
StringBuilder();
if (
pObject.
getClass().
isArray()) {
// Special array handling
Class componentClass =
pObject.
getClass();
while (
componentClass.
isArray()) {
buffer.
append('[');
buffer.
append(
Array.
getLength(
pObject));
buffer.
append(']');
componentClass =
componentClass.
getComponentType();
}
buffer.
insert(0,
componentClass);
buffer.
append(" {hashCode=");
buffer.
append(
Integer.
toHexString(
pObject.
hashCode()));
buffer.
append("}");
}
else {
// Append toString value only if overridden
if (
isIdentityToString(
pObject)) {
buffer.
append(" {");
}
else {
buffer.
append(" {toString=");
buffer.
append(
pObject.
toString());
buffer.
append(", ");
}
buffer.
append("hashCode=");
buffer.
append(
Integer.
toHexString(
pObject.
hashCode()));
// Loop through, and filter out any getters
Method[]
methods =
pObject.
getClass().
getMethods();
for (
Method method :
methods) {
// Filter only public methods
if (
Modifier.
isPublic(
method.
getModifiers())) {
String methodName =
method.
getName();
// Find name of property
String name = null;
if (!
methodName.
equals("getClass")
&&
methodName.
length() > 3 &&
methodName.
startsWith("get")
&&
Character.
isUpperCase(
methodName.
charAt(3))) {
name =
methodName.
substring(3);
}
else if (
methodName.
length() > 2 &&
methodName.
startsWith("is")
&&
Character.
isUpperCase(
methodName.
charAt(2))) {
name =
methodName.
substring(2);
}
if (
name != null) {
// If lowercase name, convert, else keep case
if (
name.
length() > 1 &&
Character.
isLowerCase(
name.
charAt(1))) {
name =
Character.
toLowerCase(
name.
charAt(0)) +
name.
substring(1);
}
Class[]
paramTypes =
method.
getParameterTypes();// involves array copying...
boolean
hasParams = (
paramTypes != null &&
paramTypes.length > 0);
boolean
isVoid =
Void.
TYPE.
equals(
method.
getReturnType());
// Filter return type & parameters
if (!
isVoid && !
hasParams) {
try {
Object value =
method.
invoke(
pObject);
buffer.
append(", ");
buffer.
append(
name);
buffer.
append('=');
if (
pDepth != 0 &&
value != null &&
isIdentityToString(
value)) {
buffer.
append(
deepToString(
value,
pForceDeep,
pDepth > 0 ?
pDepth - 1 : -1));
}
else {
buffer.
append(
value);
}
}
catch (
Exception e) {
// Next..!
}
}
}
}
}
buffer.
append('}');
// Get toString from original object
buffer.
insert(0,
pObject.
getClass().
getName());
}
return
buffer.
toString();
}
/**
* Tests if the {@code toString} method of the given object is inherited
* from {@code Object}.
*
* @param pObject the object
* @return {@code true} if toString of class Object
*/
private static boolean
isIdentityToString(
Object pObject) {
try {
Method toString =
pObject.
getClass().
getMethod("toString");
if (
toString.
getDeclaringClass() ==
Object.class) {
return true;
}
}
catch (
Exception ignore) {
// Ignore
}
return false;
}
/**
* Returns a string on the same format as {@code Object.toString()}.
*
* @param pObject the object
* @return the object as a {@code String} on the format of
* {@code Object.toString()}
*/
public static
String identityToString(
Object pObject) {
if (
pObject == null) {
return null;
}
else {
return
pObject.
getClass().
getName() + '@' +
Integer.
toHexString(
System.
identityHashCode(
pObject));
}
}
/**
* Tells whether or not the given string string matches the given regular
* expression.
* <p/>
* An invocation of this method of the form
* <tt>matches(<i>str</i>, <i>regex</i>)</tt> yields exactly the
* same result as the expression
* <p/>
* <blockquote><tt> {@link Pattern}.
* {@link Pattern#matches(String, CharSequence) matches}
* (<i>regex</i>, <i>str</i>)</tt></blockquote>
*
* @param pString the string
* @param pRegex the regular expression to which this string is to be matched
* @return {@code true} if, and only if, this string matches the
* given regular expression
* @throws PatternSyntaxException if the regular expression's syntax is invalid
* @see Pattern
* @see String#matches(String)
*/
public boolean
matches(
String pString,
String pRegex) throws
PatternSyntaxException {
return
Pattern.
matches(
pRegex,
pString);
}
/**
* Replaces the first substring of the given string that matches the given
* regular expression with the given pReplacement.
* <p/>
* An invocation of this method of the form
* <tt>replaceFirst(<i>str</i>, </tt><i>regex</i>, <i>repl</i>)</tt>
* yields exactly the same result as the expression
* <p/>
* <blockquote><tt>
* {@link Pattern}.{@link Pattern#compile compile}(<i>regex</i>).
* {@link Pattern#matcher matcher}(<i>str</i>).
* {@link java.util.regex.Matcher#replaceFirst replaceFirst}(<i>repl</i>)</tt></blockquote>
*
* @param pString the string
* @param pRegex the regular expression to which this string is to be matched
* @param pReplacement the replacement text
* @return The resulting {@code String}
* @throws PatternSyntaxException if the regular expression's syntax is invalid
* @see Pattern
* @see java.util.regex.Matcher#replaceFirst(String)
*/
public
String replaceFirst(
String pString,
String pRegex,
String pReplacement) {
return
Pattern.
compile(
pRegex).
matcher(
pString).
replaceFirst(
pReplacement);
}
/**
* Replaces each substring of this string that matches the given
* regular expression with the given pReplacement.
* <p/>
* An invocation of this method of the form
* <tt>replaceAll(<i>str</i>, <i>pRegex</i>, <i>repl</i><)</tt>
* yields exactly the same result as the expression
* <p/>
* <blockquote><tt>
* {@link Pattern}.{@link Pattern#compile compile}(<i>pRegex</i>).
* {@link Pattern#matcher matcher}(</tt><i>str</i>{@code ).
* {@link java.util.regex.Matcher#replaceAll replaceAll}(}<i>repl</i>{@code )}</blockquote>
*
* @param pString the string
* @param pRegex the regular expression to which this string is to be matched
* @param pReplacement the replacement string
* @return The resulting {@code String}
* @throws PatternSyntaxException if the regular expression's syntax is invalid
* @see Pattern
* @see String#replaceAll(String,String)
*/
public
String replaceAll(
String pString,
String pRegex,
String pReplacement) {
return
Pattern.
compile(
pRegex).
matcher(
pString).
replaceAll(
pReplacement);
}
/**
* Splits this string around matches of the given regular expression.
* <p/>
* The array returned by this method contains each substring of this
* string that is terminated by another substring that matches the given
* expression or is terminated by the end of the string. The substrings in
* the array are in the order in which they occur in this string. If the
* expression does not match any part of the input then the resulting array
* has just one element, namely this string.
* <p/>
* The {@code pLimit} parameter controls the number of times the
* pattern is applied and therefore affects the length of the resulting
* array. If the pLimit <i>n</i> is greater than zero then the pattern
* will be applied at most <i>n</i> - 1 times, the array's
* length will be no greater than <i>n</i>, and the array's last entry
* will contain all input beyond the last matched delimiter. If <i>n</i>
* is non-positive then the pattern will be applied as many times as
* possible and the array can have any length. If <i>n</i> is zero then
* the pattern will be applied as many times as possible, the array can
* have any length, and trailing empty strings will be discarded.
* <p/>
* An invocation of this method of the form
* <tt>split(<i>str</i>, <i>regex</i>, <i>n</i>)</tt>
* yields the same result as the expression
* <p/>
* <blockquote>{@link Pattern}.
* {@link Pattern#compile compile}<tt>(<i>regex</i>).
* {@link Pattern#split(CharSequence,int) split}(<i>str</i>, <i>n</i>)</tt>
* </blockquote>
*
* @param pString the string
* @param pRegex the delimiting regular expression
* @param pLimit the result threshold, as described above
* @return the array of strings computed by splitting this string
* around matches of the given regular expression
* @throws PatternSyntaxException
* if the regular expression's syntax is invalid
* @see Pattern
* @see String#split(String,int)
*/
public
String[]
split(
String pString,
String pRegex, int
pLimit) {
return
Pattern.
compile(
pRegex).
split(
pString,
pLimit);
}
/**
* Splits this string around matches of the given regular expression.
* <p/>
* This method works as if by invoking the two-argument
* {@link #split(String,String,int) split} method with the given
* expression and a limit argument of zero.
* Trailing empty strings are therefore not included in the resulting array.
*
* @param pString the string
* @param pRegex the delimiting regular expression
* @return the array of strings computed by splitting this string
* around matches of the given regular expression
* @throws PatternSyntaxException if the regular expression's syntax is invalid
* @see Pattern
* @see String#split(String)
*/
public
String[]
split(
String pString,
String pRegex) {
return
split(
pString,
pRegex, 0);
}
/**
* Converts the input string
* from camel-style (Java in-fix) naming convention
* to Lisp-style naming convention (hyphen delimitted, all lower case).
* Other characters in the string are left untouched.
* <p/>
* Eg.
* {@code "foo" => "foo"},
* {@code "fooBar" => "foo-bar"},
* {@code "myURL" => "my-url"},
* {@code "HttpRequestWrapper" => "http-request-wrapper"}
* {@code "HttpURLConnection" => "http-url-connection"}
* {@code "my45Caliber" => "my-45-caliber"}
* {@code "allready-lisp" => "allready-lisp"}
*
* @param pString the camel-style input string
* @return the string converted to lisp-style naming convention
* @throws IllegalArgumentException if {@code pString == null}
* @see #lispToCamel(String)
*/
// TODO: RefactorMe!
public static
String camelToLisp(final
String pString) {
if (
pString == null) {
throw new
IllegalArgumentException("string == null");
}
if (
pString.
length() == 0) {
return
pString;
}
StringBuilder buf = null;
int
lastPos = 0;
boolean
inCharSequence = false;
boolean
inNumberSequence = false;
// NOTE: Start at index 1, as first letter should never be hyphen
for (int
i = 1;
i <
pString.
length();
i++) {
char
current =
pString.
charAt(
i);
if (
Character.
isUpperCase(
current)) {
// Init buffer if necessary
if (
buf == null) {
buf = new
StringBuilder(
pString.
length() + 3);// Allow for some growth
}
if (
inNumberSequence) {
// Sequence end
inNumberSequence = false;
buf.
append(
pString.
substring(
lastPos,
i));
if (
current != '-') {
buf.
append('-');
}
lastPos =
i;
continue;
}
// Treat multiple uppercase chars as single word
char
previous =
pString.
charAt(
i - 1);
if (
i ==
lastPos ||
Character.
isUpperCase(
previous)) {
inCharSequence = true;
continue;
}
// Append word
buf.
append(
pString.
substring(
lastPos,
i).
toLowerCase());
if (
previous != '-') {
buf.
append('-');
}
buf.
append(
Character.
toLowerCase(
current));
lastPos =
i + 1;
}
else if (
Character.
isDigit(
current)) {
// Init buffer if necessary
if (
buf == null) {
buf = new
StringBuilder(
pString.
length() + 3);// Allow for some growth
}
if (
inCharSequence) {
// Sequence end
inCharSequence = false;
buf.
append(
pString.
substring(
lastPos,
i).
toLowerCase());
if (
current != '-') {
buf.
append('-');
}
lastPos =
i;
continue;
}
// Treat multiple digits as single word
char
previous =
pString.
charAt(
i - 1);
if (
i ==
lastPos ||
Character.
isDigit(
previous)) {
inNumberSequence = true;
continue;
}
// Append word
buf.
append(
pString.
substring(
lastPos,
i).
toLowerCase());
if (
previous != '-') {
buf.
append('-');
}
buf.
append(
Character.
toLowerCase(
current));
lastPos =
i + 1;
}
else if (
inNumberSequence) {
// Sequence end
inNumberSequence = false;
buf.
append(
pString.
substring(
lastPos,
i));
if (
current != '-') {
buf.
append('-');
}
lastPos =
i;
}
else if (
inCharSequence) {
// Sequence end
inCharSequence = false;
// NOTE: Special treatment! Last upper case, is first char in
// next word, not last char in this word
buf.
append(
pString.
substring(
lastPos,
i - 1).
toLowerCase());
if (
current != '-') {
buf.
append('-');
}
lastPos =
i - 1;
}
}
if (
buf != null) {
// Append the rest
buf.
append(
pString.
substring(
lastPos).
toLowerCase());
return
buf.
toString();
}
else {
return
Character.
isUpperCase(
pString.
charAt(0)) ?
pString.
toLowerCase() :
pString;
}
}
/**
* Converts the input string
* from Lisp-style naming convention (hyphen delimitted, all lower case)
* to camel-style (Java in-fix) naming convention.
* Other characters in the string are left untouched.
* <p/>
* Eg.
* {@code "foo" => "foo"},
* {@code "foo-bar" => "fooBar"},
* {@code "http-request-wrapper" => "httpRequestWrapper"}
* {@code "my-45-caliber" => "my45Caliber"}
* {@code "allreadyCamel" => "allreadyCamel"}
* <p/>
*
* @param pString the lisp-style input string
* @return the string converted to camel-style
* @throws IllegalArgumentException if {@code pString == null}
* @see #lispToCamel(String,boolean)
* @see #camelToLisp(String)
*/
public static
String lispToCamel(final
String pString) {
return
lispToCamel(
pString, false);
}
/**
* Converts the input string
* from Lisp-style naming convention (hyphen delimitted, all lower case)
* to camel-style (Java in-fix) naming convention.
* Other characters in the string are left untouched.
* <p/>
* To create a string starting with a lower case letter
* (like Java variable names, etc),
* specify the {@code pFirstUpperCase} paramter to be {@code false}.
* Eg.
* {@code "foo" => "foo"},
* {@code "foo-bar" => "fooBar"},
* {@code "allreadyCamel" => "allreadyCamel"}
* <p/>
* To create a string starting with an upper case letter
* (like Java class name, etc),
* specify the {@code pFirstUpperCase} paramter to be {@code true}.
* Eg.
* {@code "http-request-wrapper" => "HttpRequestWrapper"}
* {@code "my-45-caliber" => "My45Caliber"}
* <p/>
*
* @param pString the lisp-style input string
* @param pFirstUpperCase {@code true} if the first char should be
* upper case
* @return the string converted to camel-style
* @throws IllegalArgumentException if {@code pString == null}
* @see #camelToLisp(String)
*/
public static
String lispToCamel(final
String pString, final boolean
pFirstUpperCase) {
if (
pString == null) {
throw new
IllegalArgumentException("string == null");
}
if (
pString.
length() == 0) {
return
pString;
}
StringBuilder buf = null;
int
lastPos = 0;
for (int
i = 0;
i <
pString.
length();
i++) {
char
current =
pString.
charAt(
i);
if (
current == '-') {
// Init buffer if necessary
if (
buf == null) {
buf = new
StringBuilder(
pString.
length() - 1);// Can't be larger
}
// Append with upper case
if (
lastPos != 0 ||
pFirstUpperCase) {
buf.
append(
Character.
toUpperCase(
pString.
charAt(
lastPos)));
lastPos++;
}
buf.
append(
pString.
substring(
lastPos,
i).
toLowerCase());
lastPos =
i + 1;
}
}
if (
buf != null) {
buf.
append(
Character.
toUpperCase(
pString.
charAt(
lastPos)));
buf.
append(
pString.
substring(
lastPos + 1).
toLowerCase());
return
buf.
toString();
}
else {
if (
pFirstUpperCase && !
Character.
isUpperCase(
pString.
charAt(0))) {
return
capitalize(
pString, 0);
}
else
if (!
pFirstUpperCase &&
Character.
isUpperCase(
pString.
charAt(0))) {
return
Character.
toLowerCase(
pString.
charAt(0)) +
pString.
substring(1);
}
return
pString;
}
}
public static
String reverse(final
String pString) {
final char[]
chars = new char[
pString.
length()];
pString.
getChars(0,
chars.length,
chars, 0);
for (int
i = 0;
i <
chars.length / 2;
i++) {
char
temp =
chars[
i];
chars[
i] =
chars[
chars.length - 1 -
i];
chars[
chars.length - 1 -
i] =
temp;
}
return new
String(
chars);
}
}