mirror of
https://github.com/haraldk/TwelveMonkeys.git
synced 2026-05-20 00:00:03 -04:00
f35f5c6b24
* Added the `@Deprecated` tag to instances where is was mentioned in documentation, but not for the actual code itself.
Changed one documentation link pointing at a non-existing item.
* As per PR suggestion.
(cherry picked from commit 196081a317)
2163 lines
77 KiB
Java
Executable File
2163 lines
77 KiB
Java
Executable File
/*
|
|
* 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 $
|
|
* return values, null-value handling and parameter names (cosmetics).
|
|
*/
|
|
// TODO: Consistency check: Method names, parameter sequence, Exceptions,
|
|
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*/
|
|
@Deprecated
|
|
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.
|
|
* @see #cut(String,int,String)
|
|
*/
|
|
// TODO: What if source is allready longer than required length?
|
|
// TODO: Consistency with cut
|
|
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
|
|
*
|
|
* @see java.text.SimpleDateFormat
|
|
* @see java.text.SimpleDateFormat#SimpleDateFormat(String)
|
|
*/
|
|
// TODO: cache formats?
|
|
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"}.
|
|
* </p>
|
|
*
|
|
* @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.
|
|
* </p>
|
|
*
|
|
* @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".
|
|
* </p>
|
|
*
|
|
* @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".
|
|
* </p>
|
|
*
|
|
* @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.
|
|
*
|
|
* @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.
|
|
*
|
|
* @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*/
|
|
@Deprecated
|
|
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.
|
|
*
|
|
* @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>
|
|
*
|
|
* @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>
|
|
*
|
|
* @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>, <i>regex</i>, <i>repl</i>)
|
|
* </tt>
|
|
* yields exactly the same result as the expression:
|
|
* </p>
|
|
* <blockquote><tt>
|
|
* {@link Pattern}.{@link Pattern#compile(String) 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(String) compile}(<i>pRegex</i>).
|
|
* {@link Pattern#matcher matcher}(<i>str</i>{@code ).
|
|
* {@link java.util.regex.Matcher#replaceAll replaceAll}(}<i>repl</i>{@code )}
|
|
* </tt></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>
|
|
* <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>
|
|
* <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(String) 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.
|
|
* </p>
|
|
*
|
|
* @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"}
|
|
* </p>
|
|
*
|
|
* @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>
|
|
* <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-12-monkeys" => "My12Monkeys"}
|
|
* </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);
|
|
}
|
|
} |