/* | |
* Licensed to the Apache Software Foundation (ASF) under one or more | |
* contributor license agreements. See the NOTICE file distributed with | |
* this work for additional information regarding copyright ownership. | |
* The ASF licenses this file to You under the Apache License, Version 2.0 | |
* (the "License"); you may not use this file except in compliance with | |
* the License. You may obtain a copy of the License at | |
* | |
* http://www.apache.org/licenses/LICENSE-2.0 | |
* | |
* Unless required by applicable law or agreed to in writing, software | |
* distributed under the License is distributed on an "AS IS" BASIS, | |
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
* See the License for the specific language governing permissions and | |
* limitations under the License. | |
*/ | |
package com.badlogic.gdx.utils; | |
import java.util.Arrays; | |
/** A {@link java.lang.StringBuilder} that implements equals and hashcode. | |
* @see CharSequence | |
* @see Appendable | |
* @see java.lang.StringBuilder | |
* @see String */ | |
public class StringBuilder implements Appendable, CharSequence { | |
static final int INITIAL_CAPACITY = 16; | |
public char[] chars; | |
public int length; | |
private static final char[] digits = new char[] {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9'}; | |
/** @return the number of characters required to represent the specified value with the specified radix */ | |
public static int numChars (int value, int radix) { | |
int result = (value < 0) ? 2 : 1; | |
while ((value /= radix) != 0) | |
++result; | |
return result; | |
} | |
/** @return the number of characters required to represent the specified value with the specified radix */ | |
public static int numChars (long value, int radix) { | |
int result = (value < 0) ? 2 : 1; | |
while ((value /= radix) != 0) | |
++result; | |
return result; | |
} | |
/* | |
* Returns the character array. | |
*/ | |
final char[] getValue () { | |
return chars; | |
} | |
/** Constructs an instance with an initial capacity of {@code 16}. | |
* | |
* @see #capacity() */ | |
public StringBuilder () { | |
chars = new char[INITIAL_CAPACITY]; | |
} | |
/** Constructs an instance with the specified capacity. | |
* | |
* @param capacity the initial capacity to use. | |
* @throws NegativeArraySizeException if the specified {@code capacity} is negative. | |
* @see #capacity() */ | |
public StringBuilder (int capacity) { | |
if (capacity < 0) { | |
throw new NegativeArraySizeException(); | |
} | |
chars = new char[capacity]; | |
} | |
/** Constructs an instance that's initialized with the contents of the specified {@code CharSequence}. The capacity of the new | |
* builder will be the length of the {@code CharSequence} plus 16. | |
* | |
* @param seq the {@code CharSequence} to copy into the builder. | |
* @throws NullPointerException if {@code seq} is {@code null}. */ | |
public StringBuilder (CharSequence seq) { | |
this(seq.toString()); | |
} | |
public StringBuilder (StringBuilder builder) { | |
length = builder.length; | |
chars = new char[length + INITIAL_CAPACITY]; | |
System.arraycopy(builder.chars, 0, chars, 0, length); | |
} | |
/** Constructs an instance that's initialized with the contents of the specified {@code String}. The capacity of the new builder | |
* will be the length of the {@code String} plus 16. | |
* | |
* @param string the {@code String} to copy into the builder. | |
* @throws NullPointerException if {@code str} is {@code null}. */ | |
public StringBuilder (String string) { | |
length = string.length(); | |
chars = new char[length + INITIAL_CAPACITY]; | |
string.getChars(0, length, chars, 0); | |
} | |
private void enlargeBuffer (int min) { | |
int newSize = (chars.length >> 1) + chars.length + 2; | |
char[] newData = new char[min > newSize ? min : newSize]; | |
System.arraycopy(chars, 0, newData, 0, length); | |
chars = newData; | |
} | |
final void appendNull () { | |
int newSize = length + 4; | |
if (newSize > chars.length) { | |
enlargeBuffer(newSize); | |
} | |
chars[length++] = 'n'; | |
chars[length++] = 'u'; | |
chars[length++] = 'l'; | |
chars[length++] = 'l'; | |
} | |
final void append0 (char[] value) { | |
int newSize = length + value.length; | |
if (newSize > chars.length) { | |
enlargeBuffer(newSize); | |
} | |
System.arraycopy(value, 0, chars, length, value.length); | |
length = newSize; | |
} | |
final void append0 (char[] value, int offset, int length) { | |
// Force null check of chars first! | |
if (offset > value.length || offset < 0) { | |
throw new ArrayIndexOutOfBoundsException("Offset out of bounds: " + offset); | |
} | |
if (length < 0 || value.length - offset < length) { | |
throw new ArrayIndexOutOfBoundsException("Length out of bounds: " + length); | |
} | |
int newSize = this.length + length; | |
if (newSize > chars.length) { | |
enlargeBuffer(newSize); | |
} | |
System.arraycopy(value, offset, chars, this.length, length); | |
this.length = newSize; | |
} | |
final void append0 (char ch) { | |
if (length == chars.length) { | |
enlargeBuffer(length + 1); | |
} | |
chars[length++] = ch; | |
} | |
final void append0 (String string) { | |
if (string == null) { | |
appendNull(); | |
return; | |
} | |
int adding = string.length(); | |
int newSize = length + adding; | |
if (newSize > chars.length) { | |
enlargeBuffer(newSize); | |
} | |
string.getChars(0, adding, chars, length); | |
length = newSize; | |
} | |
final void append0 (CharSequence s, int start, int end) { | |
if (s == null) { | |
s = "null"; | |
} | |
if (start < 0 || end < 0 || start > end || end > s.length()) { | |
throw new IndexOutOfBoundsException(); | |
} | |
append0(s.subSequence(start, end).toString()); | |
} | |
/** Returns the number of characters that can be held without growing. | |
* | |
* @return the capacity | |
* @see #ensureCapacity | |
* @see #length */ | |
public int capacity () { | |
return chars.length; | |
} | |
/** Retrieves the character at the {@code index}. | |
* | |
* @param index the index of the character to retrieve. | |
* @return the char value. | |
* @throws IndexOutOfBoundsException if {@code index} is negative or greater than or equal to the current {@link #length()}. */ | |
public char charAt (int index) { | |
if (index < 0 || index >= length) { | |
throw new StringIndexOutOfBoundsException(index); | |
} | |
return chars[index]; | |
} | |
final void delete0 (int start, int end) { | |
if (start >= 0) { | |
if (end > length) { | |
end = length; | |
} | |
if (end == start) { | |
return; | |
} | |
if (end > start) { | |
int count = length - end; | |
if (count >= 0) System.arraycopy(chars, end, chars, start, count); | |
length -= end - start; | |
return; | |
} | |
} | |
throw new StringIndexOutOfBoundsException(); | |
} | |
final void deleteCharAt0 (int location) { | |
if (0 > location || location >= length) { | |
throw new StringIndexOutOfBoundsException(location); | |
} | |
int count = length - location - 1; | |
if (count > 0) { | |
System.arraycopy(chars, location + 1, chars, location, count); | |
} | |
length--; | |
} | |
/** Ensures that this object has a minimum capacity available before requiring the internal buffer to be enlarged. The general | |
* policy of this method is that if the {@code minimumCapacity} is larger than the current {@link #capacity()}, then the | |
* capacity will be increased to the largest value of either the {@code minimumCapacity} or the current capacity multiplied by | |
* two plus two. Although this is the general policy, there is no guarantee that the capacity will change. | |
* | |
* @param min the new minimum capacity to set. */ | |
public void ensureCapacity (int min) { | |
if (min > chars.length) { | |
int twice = (chars.length << 1) + 2; | |
enlargeBuffer(twice > min ? twice : min); | |
} | |
} | |
/** Copies the requested sequence of characters to the {@code char[]} passed starting at {@code destStart}. | |
* | |
* @param start the inclusive start index of the characters to copy. | |
* @param end the exclusive end index of the characters to copy. | |
* @param dest the {@code char[]} to copy the characters to. | |
* @param destStart the inclusive start index of {@code dest} to begin copying to. | |
* @throws IndexOutOfBoundsException if the {@code start} is negative, the {@code destStart} is negative, the {@code start} is | |
* greater than {@code end}, the {@code end} is greater than the current {@link #length()} or | |
* {@code destStart + end - begin} is greater than {@code dest.length}. */ | |
public void getChars (int start, int end, char[] dest, int destStart) { | |
if (start > length || end > length || start > end) { | |
throw new StringIndexOutOfBoundsException(); | |
} | |
System.arraycopy(chars, start, dest, destStart, end - start); | |
} | |
final void insert0 (int index, char[] value) { | |
if (0 > index || index > length) { | |
throw new StringIndexOutOfBoundsException(index); | |
} | |
if (value.length != 0) { | |
move(value.length, index); | |
System.arraycopy(value, 0, value, index, value.length); | |
length += value.length; | |
} | |
} | |
final void insert0 (int index, char[] value, int start, int length) { | |
if (0 <= index && index <= length) { | |
// start + length could overflow, start/length maybe MaxInt | |
if (start >= 0 && 0 <= length && length <= value.length - start) { | |
if (length != 0) { | |
move(length, index); | |
System.arraycopy(value, start, chars, index, length); | |
this.length += length; | |
} | |
return; | |
} | |
throw new StringIndexOutOfBoundsException("offset " + start + ", length " + length + ", char[].length " + value.length); | |
} | |
throw new StringIndexOutOfBoundsException(index); | |
} | |
final void insert0 (int index, char ch) { | |
if (0 > index || index > length) { | |
// RI compatible exception type | |
throw new ArrayIndexOutOfBoundsException(index); | |
} | |
move(1, index); | |
chars[index] = ch; | |
length++; | |
} | |
final void insert0 (int index, String string) { | |
if (0 <= index && index <= length) { | |
if (string == null) { | |
string = "null"; | |
} | |
int min = string.length(); | |
if (min != 0) { | |
move(min, index); | |
string.getChars(0, min, chars, index); | |
length += min; | |
} | |
} else { | |
throw new StringIndexOutOfBoundsException(index); | |
} | |
} | |
final void insert0 (int index, CharSequence s, int start, int end) { | |
if (s == null) { | |
s = "null"; | |
} | |
if (index < 0 || index > length || start < 0 || end < 0 || start > end || end > s.length()) { | |
throw new IndexOutOfBoundsException(); | |
} | |
insert0(index, s.subSequence(start, end).toString()); | |
} | |
/** The current length. | |
* | |
* @return the number of characters contained in this instance. */ | |
public int length () { | |
return length; | |
} | |
private void move (int size, int index) { | |
if (chars.length - length >= size) { | |
System.arraycopy(chars, index, chars, index + size, length - index); // index == count case is no-op | |
return; | |
} | |
int a = length + size, b = (chars.length << 1) + 2; | |
int newSize = a > b ? a : b; | |
char[] newData = new char[newSize]; | |
System.arraycopy(chars, 0, newData, 0, index); | |
// index == count case is no-op | |
System.arraycopy(chars, index, newData, index + size, length - index); | |
chars = newData; | |
} | |
final void replace0 (int start, int end, String string) { | |
if (start >= 0) { | |
if (end > length) { | |
end = length; | |
} | |
if (end > start) { | |
int stringLength = string.length(); | |
int diff = end - start - stringLength; | |
if (diff > 0) { // replacing with fewer characters | |
// index == count case is no-op | |
System.arraycopy(chars, end, chars, start + stringLength, length - end); | |
} else if (diff < 0) { | |
// replacing with more characters...need some room | |
move(-diff, end); | |
} | |
string.getChars(0, stringLength, chars, start); | |
length -= diff; | |
return; | |
} | |
if (start == end) { | |
if (string == null) { | |
throw new NullPointerException(); | |
} | |
insert0(start, string); | |
return; | |
} | |
} | |
throw new StringIndexOutOfBoundsException(); | |
} | |
final void reverse0 () { | |
if (length < 2) { | |
return; | |
} | |
int end = length - 1; | |
char frontHigh = chars[0]; | |
char endLow = chars[end]; | |
boolean allowFrontSur = true, allowEndSur = true; | |
for (int i = 0, mid = length / 2; i < mid; i++, --end) { | |
char frontLow = chars[i + 1]; | |
char endHigh = chars[end - 1]; | |
boolean surAtFront = allowFrontSur && frontLow >= 0xdc00 && frontLow <= 0xdfff && frontHigh >= 0xd800 | |
&& frontHigh <= 0xdbff; | |
if (surAtFront && length < 3) { | |
return; | |
} | |
boolean surAtEnd = allowEndSur && endHigh >= 0xd800 && endHigh <= 0xdbff && endLow >= 0xdc00 && endLow <= 0xdfff; | |
allowFrontSur = allowEndSur = true; | |
if (surAtFront == surAtEnd) { | |
if (surAtFront) { | |
// both surrogates | |
chars[end] = frontLow; | |
chars[end - 1] = frontHigh; | |
chars[i] = endHigh; | |
chars[i + 1] = endLow; | |
frontHigh = chars[i + 2]; | |
endLow = chars[end - 2]; | |
i++; | |
end--; | |
} else { | |
// neither surrogates | |
chars[end] = frontHigh; | |
chars[i] = endLow; | |
frontHigh = frontLow; | |
endLow = endHigh; | |
} | |
} else { | |
if (surAtFront) { | |
// surrogate only at the front | |
chars[end] = frontLow; | |
chars[i] = endLow; | |
endLow = endHigh; | |
allowFrontSur = false; | |
} else { | |
// surrogate only at the end | |
chars[end] = frontHigh; | |
chars[i] = endHigh; | |
frontHigh = frontLow; | |
allowEndSur = false; | |
} | |
} | |
} | |
if ((length & 1) == 1 && (!allowFrontSur || !allowEndSur)) { | |
chars[end] = allowFrontSur ? endLow : frontHigh; | |
} | |
} | |
/** Sets the character at the {@code index}. | |
* | |
* @param index the zero-based index of the character to replace. | |
* @param ch the character to set. | |
* @throws IndexOutOfBoundsException if {@code index} is negative or greater than or equal to the current {@link #length()}. */ | |
public void setCharAt (int index, char ch) { | |
if (0 > index || index >= length) { | |
throw new StringIndexOutOfBoundsException(index); | |
} | |
chars[index] = ch; | |
} | |
/** Sets the current length to a new value. If the new length is larger than the current length, then the new characters at the | |
* end of this object will contain the {@code char} value of {@code \u0000}. | |
* | |
* @param newLength the new length of this StringBuilder. | |
* @exception IndexOutOfBoundsException if {@code length < 0}. | |
* @see #length */ | |
public void setLength (int newLength) { | |
if (newLength < 0) { | |
throw new StringIndexOutOfBoundsException(newLength); | |
} | |
if (newLength > chars.length) { | |
enlargeBuffer(newLength); | |
} else { | |
if (length < newLength) { | |
Arrays.fill(chars, length, newLength, (char)0); | |
} | |
} | |
length = newLength; | |
} | |
/** Returns the String value of the subsequence from the {@code start} index to the current end. | |
* | |
* @param start the inclusive start index to begin the subsequence. | |
* @return a String containing the subsequence. | |
* @throws StringIndexOutOfBoundsException if {@code start} is negative or greater than the current {@link #length()}. */ | |
public String substring (int start) { | |
if (0 <= start && start <= length) { | |
if (start == length) { | |
return ""; | |
} | |
// Remove String sharing for more performance | |
return new String(chars, start, length - start); | |
} | |
throw new StringIndexOutOfBoundsException(start); | |
} | |
/** Returns the String value of the subsequence from the {@code start} index to the {@code end} index. | |
* | |
* @param start the inclusive start index to begin the subsequence. | |
* @param end the exclusive end index to end the subsequence. | |
* @return a String containing the subsequence. | |
* @throws StringIndexOutOfBoundsException if {@code start} is negative, greater than {@code end} or if {@code end} is greater | |
* than the current {@link #length()}. */ | |
public String substring (int start, int end) { | |
if (0 <= start && start <= end && end <= length) { | |
if (start == end) { | |
return ""; | |
} | |
// Remove String sharing for more performance | |
return new String(chars, start, end - start); | |
} | |
throw new StringIndexOutOfBoundsException(); | |
} | |
/** Returns the current String representation. | |
* | |
* @return a String containing the characters in this instance. */ | |
@Override | |
public String toString () { | |
if (length == 0) return ""; | |
return new String(chars, 0, length); | |
} | |
/** Returns a {@code CharSequence} of the subsequence from the {@code start} index to the {@code end} index. | |
* | |
* @param start the inclusive start index to begin the subsequence. | |
* @param end the exclusive end index to end the subsequence. | |
* @return a CharSequence containing the subsequence. | |
* @throws IndexOutOfBoundsException if {@code start} is negative, greater than {@code end} or if {@code end} is greater than | |
* the current {@link #length()}. | |
* @since 1.4 */ | |
public CharSequence subSequence (int start, int end) { | |
return substring(start, end); | |
} | |
/** Searches for the first index of the specified character. The search for the character starts at the beginning and moves | |
* towards the end. | |
* | |
* @param string the string to find. | |
* @return the index of the specified character, -1 if the character isn't found. | |
* @see #lastIndexOf(String) | |
* @since 1.4 */ | |
public int indexOf (String string) { | |
return indexOf(string, 0); | |
} | |
/** Searches for the index of the specified character. The search for the character starts at the specified offset and moves | |
* towards the end. | |
* | |
* @param subString the string to find. | |
* @param start the starting offset. | |
* @return the index of the specified character, -1 if the character isn't found | |
* @see #lastIndexOf(String,int) | |
* @since 1.4 */ | |
public int indexOf (String subString, int start) { | |
if (start < 0) { | |
start = 0; | |
} | |
int subCount = subString.length(); | |
if (subCount > 0) { | |
if (subCount + start > length) { | |
return -1; | |
} | |
char firstChar = subString.charAt(0); | |
while (true) { | |
int i = start; | |
boolean found = false; | |
for (; i < length; i++) { | |
if (chars[i] == firstChar) { | |
found = true; | |
break; | |
} | |
} | |
if (!found || subCount + i > length) { | |
return -1; // handles subCount > count || start >= count | |
} | |
int o1 = i, o2 = 0; | |
while (++o2 < subCount && chars[++o1] == subString.charAt(o2)) { | |
// Intentionally empty | |
} | |
if (o2 == subCount) { | |
return i; | |
} | |
start = i + 1; | |
} | |
} | |
return start < length || start == 0 ? start : length; | |
} | |
/** Searches for the last index of the specified character. The search for the character starts at the end and moves towards the | |
* beginning. | |
* | |
* @param string the string to find. | |
* @return the index of the specified character, -1 if the character isn't found. | |
* @throws NullPointerException if {@code string} is {@code null}. | |
* @see String#lastIndexOf(java.lang.String) | |
* @since 1.4 */ | |
public int lastIndexOf (String string) { | |
return lastIndexOf(string, length); | |
} | |
/** Searches for the index of the specified character. The search for the character starts at the specified offset and moves | |
* towards the beginning. | |
* | |
* @param subString the string to find. | |
* @param start the starting offset. | |
* @return the index of the specified character, -1 if the character isn't found. | |
* @throws NullPointerException if {@code subString} is {@code null}. | |
* @see String#lastIndexOf(String,int) | |
* @since 1.4 */ | |
public int lastIndexOf (String subString, int start) { | |
int subCount = subString.length(); | |
if (subCount <= length && start >= 0) { | |
if (subCount > 0) { | |
if (start > length - subCount) { | |
start = length - subCount; // count and subCount are both | |
} | |
// >= 1 | |
char firstChar = subString.charAt(0); | |
while (true) { | |
int i = start; | |
boolean found = false; | |
for (; i >= 0; --i) { | |
if (chars[i] == firstChar) { | |
found = true; | |
break; | |
} | |
} | |
if (!found) { | |
return -1; | |
} | |
int o1 = i, o2 = 0; | |
while (++o2 < subCount && chars[++o1] == subString.charAt(o2)) { | |
// Intentionally empty | |
} | |
if (o2 == subCount) { | |
return i; | |
} | |
start = i - 1; | |
} | |
} | |
return start < length ? start : length; | |
} | |
return -1; | |
} | |
/** Trims off any extra capacity beyond the current length. Note, this method is NOT guaranteed to change the capacity of this | |
* object. | |
* | |
* @since 1.5 */ | |
public void trimToSize () { | |
if (length < chars.length) { | |
char[] newValue = new char[length]; | |
System.arraycopy(chars, 0, newValue, 0, length); | |
chars = newValue; | |
} | |
} | |
/** Retrieves the Unicode code point value at the {@code index}. | |
* | |
* @param index the index to the {@code char} code unit. | |
* @return the Unicode code point value. | |
* @throws IndexOutOfBoundsException if {@code index} is negative or greater than or equal to {@link #length()}. | |
* @see Character | |
* @see Character#codePointAt(char[], int, int) | |
* @since 1.5 */ | |
public int codePointAt (int index) { | |
if (index < 0 || index >= length) { | |
throw new StringIndexOutOfBoundsException(index); | |
} | |
return Character.codePointAt(chars, index, length); | |
} | |
/** Retrieves the Unicode code point value that precedes the {@code index}. | |
* | |
* @param index the index to the {@code char} code unit within this object. | |
* @return the Unicode code point value. | |
* @throws IndexOutOfBoundsException if {@code index} is less than 1 or greater than {@link #length()}. | |
* @see Character | |
* @see Character#codePointBefore(char[], int, int) | |
* @since 1.5 */ | |
public int codePointBefore (int index) { | |
if (index < 1 || index > length) { | |
throw new StringIndexOutOfBoundsException(index); | |
} | |
return Character.codePointBefore(chars, index); | |
} | |
/** Calculates the number of Unicode code points between {@code beginIndex} and {@code endIndex}. | |
* | |
* @param beginIndex the inclusive beginning index of the subsequence. | |
* @param endIndex the exclusive end index of the subsequence. | |
* @return the number of Unicode code points in the subsequence. | |
* @throws IndexOutOfBoundsException if {@code beginIndex} is negative or greater than {@code endIndex} or {@code endIndex} is | |
* greater than {@link #length()}. | |
* @see Character | |
* @see Character#codePointCount(char[], int, int) | |
* @since 1.5 */ | |
public int codePointCount (int beginIndex, int endIndex) { | |
if (beginIndex < 0 || endIndex > length || beginIndex > endIndex) { | |
throw new StringIndexOutOfBoundsException(); | |
} | |
return Character.codePointCount(chars, beginIndex, endIndex - beginIndex); | |
} | |
/** Returns the index that is offset {@code codePointOffset} code points from {@code index}. | |
* | |
* @param index the index to calculate the offset from. | |
* @param codePointOffset the number of code points to count. | |
* @return the index that is {@code codePointOffset} code points away from index. | |
* @throws IndexOutOfBoundsException if {@code index} is negative or greater than {@link #length()} or if there aren't enough | |
* code points before or after {@code index} to match {@code codePointOffset}. | |
* @see Character | |
* @see Character#offsetByCodePoints(char[], int, int, int, int) | |
* @since 1.5 */ | |
public int offsetByCodePoints (int index, int codePointOffset) { | |
return Character.offsetByCodePoints(chars, 0, length, index, codePointOffset); | |
} | |
/** Appends the string representation of the specified {@code boolean} value. The {@code boolean} value is converted to a String | |
* according to the rule defined by {@link String#valueOf(boolean)}. | |
* | |
* @param b the {@code boolean} value to append. | |
* @return this builder. | |
* @see String#valueOf(boolean) */ | |
public StringBuilder append (boolean b) { | |
append0(b ? "true" : "false"); //$NON-NLS-1$ //$NON-NLS-2$ | |
return this; | |
} | |
/** Appends the string representation of the specified {@code char} value. The {@code char} value is converted to a string | |
* according to the rule defined by {@link String#valueOf(char)}. | |
* | |
* @param c the {@code char} value to append. | |
* @return this builder. | |
* @see String#valueOf(char) */ | |
public StringBuilder append (char c) { | |
append0(c); | |
return this; | |
} | |
/** Appends the string representation of the specified {@code int} value. The {@code int} value is converted to a string without | |
* memory allocation. | |
* | |
* @param value the {@code int} value to append. | |
* @return this builder. | |
* @see String#valueOf(int) */ | |
public StringBuilder append (int value) { | |
return append(value, 0); | |
} | |
/** Appends the string representation of the specified {@code int} value. The {@code int} value is converted to a string without | |
* memory allocation. | |
* | |
* @param value the {@code int} value to append. | |
* @param minLength the minimum number of characters to add | |
* @return this builder. | |
* @see String#valueOf(int) */ | |
public StringBuilder append (int value, int minLength) { | |
return append(value, minLength, '0'); | |
} | |
/** Appends the string representation of the specified {@code int} value. The {@code int} value is converted to a string without | |
* memory allocation. | |
* | |
* @param value the {@code int} value to append. | |
* @param minLength the minimum number of characters to add | |
* @param prefix the character to use as prefix | |
* @return this builder. | |
* @see String#valueOf(int) */ | |
public StringBuilder append (int value, final int minLength, final char prefix) { | |
if (value == Integer.MIN_VALUE) { | |
append0("-2147483648"); | |
return this; | |
} | |
if (value < 0) { | |
append0('-'); | |
value = -value; | |
} | |
if (minLength > 1) { | |
for (int j = minLength - numChars(value, 10); j > 0; --j) | |
append(prefix); | |
} | |
if (value >= 10000) { | |
if (value >= 1000000000) append0(digits[(int)((long)value % 10000000000L / 1000000000L)]); | |
if (value >= 100000000) append0(digits[value % 1000000000 / 100000000]); | |
if (value >= 10000000) append0(digits[value % 100000000 / 10000000]); | |
if (value >= 1000000) append0(digits[value % 10000000 / 1000000]); | |
if (value >= 100000) append0(digits[value % 1000000 / 100000]); | |
append0(digits[value % 100000 / 10000]); | |
} | |
if (value >= 1000) append0(digits[value % 10000 / 1000]); | |
if (value >= 100) append0(digits[value % 1000 / 100]); | |
if (value >= 10) append0(digits[value % 100 / 10]); | |
append0(digits[value % 10]); | |
return this; | |
} | |
/** Appends the string representation of the specified {@code long} value. The {@code long} value is converted to a string | |
* without memory allocation. | |
* | |
* @param value the {@code long} value. | |
* @return this builder. */ | |
public StringBuilder append (long value) { | |
return append(value, 0); | |
} | |
/** Appends the string representation of the specified {@code long} value. The {@code long} value is converted to a string | |
* without memory allocation. | |
* | |
* @param value the {@code long} value. | |
* @param minLength the minimum number of characters to add | |
* @return this builder. */ | |
public StringBuilder append (long value, int minLength) { | |
return append(value, minLength, '0'); | |
} | |
/** Appends the string representation of the specified {@code long} value. The {@code long} value is converted to a string | |
* without memory allocation. | |
* | |
* @param value the {@code long} value. | |
* @param minLength the minimum number of characters to add | |
* @param prefix the character to use as prefix | |
* @return this builder. */ | |
public StringBuilder append (long value, int minLength, char prefix) { | |
if (value == Long.MIN_VALUE) { | |
append0("-9223372036854775808"); | |
return this; | |
} | |
if (value < 0L) { | |
append0('-'); | |
value = -value; | |
} | |
if (minLength > 1) { | |
for (int j = minLength - numChars(value, 10); j > 0; --j) | |
append(prefix); | |
} | |
if (value >= 10000) { | |
if (value >= 1000000000000000000L) append0(digits[(int)(value % 10000000000000000000D / 1000000000000000000L)]); | |
if (value >= 100000000000000000L) append0(digits[(int)(value % 1000000000000000000L / 100000000000000000L)]); | |
if (value >= 10000000000000000L) append0(digits[(int)(value % 100000000000000000L / 10000000000000000L)]); | |
if (value >= 1000000000000000L) append0(digits[(int)(value % 10000000000000000L / 1000000000000000L)]); | |
if (value >= 100000000000000L) append0(digits[(int)(value % 1000000000000000L / 100000000000000L)]); | |
if (value >= 10000000000000L) append0(digits[(int)(value % 100000000000000L / 10000000000000L)]); | |
if (value >= 1000000000000L) append0(digits[(int)(value % 10000000000000L / 1000000000000L)]); | |
if (value >= 100000000000L) append0(digits[(int)(value % 1000000000000L / 100000000000L)]); | |
if (value >= 10000000000L) append0(digits[(int)(value % 100000000000L / 10000000000L)]); | |
if (value >= 1000000000L) append0(digits[(int)(value % 10000000000L / 1000000000L)]); | |
if (value >= 100000000L) append0(digits[(int)(value % 1000000000L / 100000000L)]); | |
if (value >= 10000000L) append0(digits[(int)(value % 100000000L / 10000000L)]); | |
if (value >= 1000000L) append0(digits[(int)(value % 10000000L / 1000000L)]); | |
if (value >= 100000L) append0(digits[(int)(value % 1000000L / 100000L)]); | |
append0(digits[(int)(value % 100000L / 10000L)]); | |
} | |
if (value >= 1000L) append0(digits[(int)(value % 10000L / 1000L)]); | |
if (value >= 100L) append0(digits[(int)(value % 1000L / 100L)]); | |
if (value >= 10L) append0(digits[(int)(value % 100L / 10L)]); | |
append0(digits[(int)(value % 10L)]); | |
return this; | |
} | |
/** Appends the string representation of the specified {@code float} value. The {@code float} value is converted to a string | |
* according to the rule defined by {@link String#valueOf(float)}. | |
* | |
* @param f the {@code float} value to append. | |
* @return this builder. */ | |
public StringBuilder append (float f) { | |
append0(Float.toString(f)); | |
return this; | |
} | |
/** Appends the string representation of the specified {@code double} value. The {@code double} value is converted to a string | |
* according to the rule defined by {@link String#valueOf(double)}. | |
* | |
* @param d the {@code double} value to append. | |
* @return this builder. | |
* @see String#valueOf(double) */ | |
public StringBuilder append (double d) { | |
append0(Double.toString(d)); | |
return this; | |
} | |
/** Appends the string representation of the specified {@code Object}. The {@code Object} value is converted to a string | |
* according to the rule defined by {@link String#valueOf(Object)}. | |
* | |
* @param obj the {@code Object} to append. | |
* @return this builder. | |
* @see String#valueOf(Object) */ | |
public StringBuilder append (Object obj) { | |
if (obj == null) { | |
appendNull(); | |
} else { | |
append0(obj.toString()); | |
} | |
return this; | |
} | |
/** Appends the contents of the specified string. If the string is {@code null}, then the string {@code "null"} is appended. | |
* | |
* @param str the string to append. | |
* @return this builder. */ | |
public StringBuilder append (String str) { | |
append0(str); | |
return this; | |
} | |
/** Appends the string representation of the specified {@code char[]}. The {@code char[]} is converted to a string according to | |
* the rule defined by {@link String#valueOf(char[])}. | |
* | |
* @param ch the {@code char[]} to append.. | |
* @return this builder. | |
* @see String#valueOf(char[]) */ | |
public StringBuilder append (char[] ch) { | |
append0(ch); | |
return this; | |
} | |
/** Appends the string representation of the specified subset of the {@code char[]}. The {@code char[]} value is converted to a | |
* String according to the rule defined by {@link String#valueOf(char[],int,int)}. | |
* | |
* @param str the {@code char[]} to append. | |
* @param offset the inclusive offset index. | |
* @param len the number of characters. | |
* @return this builder. | |
* @throws ArrayIndexOutOfBoundsException if {@code offset} and {@code len} do not specify a valid subsequence. | |
* @see String#valueOf(char[],int,int) */ | |
public StringBuilder append (char[] str, int offset, int len) { | |
append0(str, offset, len); | |
return this; | |
} | |
/** Appends the string representation of the specified {@code CharSequence}. If the {@code CharSequence} is {@code null}, then | |
* the string {@code "null"} is appended. | |
* | |
* @param csq the {@code CharSequence} to append. | |
* @return this builder. */ | |
public StringBuilder append (CharSequence csq) { | |
if (csq == null) { | |
appendNull(); | |
} else if (csq instanceof StringBuilder) { | |
StringBuilder builder = (StringBuilder)csq; | |
append0(builder.chars, 0, builder.length); | |
} else { | |
append0(csq.toString()); | |
} | |
return this; | |
} | |
public StringBuilder append (StringBuilder builder) { | |
if (builder == null) | |
appendNull(); | |
else | |
append0(builder.chars, 0, builder.length); | |
return this; | |
} | |
/** Appends the string representation of the specified subsequence of the {@code CharSequence}. If the {@code CharSequence} is | |
* {@code null}, then the string {@code "null"} is used to extract the subsequence from. | |
* | |
* @param csq the {@code CharSequence} to append. | |
* @param start the beginning index. | |
* @param end the ending index. | |
* @return this builder. | |
* @throws IndexOutOfBoundsException if {@code start} or {@code end} are negative, {@code start} is greater than {@code end} or | |
* {@code end} is greater than the length of {@code csq}. */ | |
public StringBuilder append (CharSequence csq, int start, int end) { | |
append0(csq, start, end); | |
return this; | |
} | |
public StringBuilder append (StringBuilder builder, int start, int end) { | |
if (builder == null) | |
appendNull(); | |
else | |
append0(builder.chars, start, end); | |
return this; | |
} | |
/** Appends the encoded Unicode code point. The code point is converted to a {@code char[]} as defined by | |
* {@link Character#toChars(int)}. | |
* | |
* @param codePoint the Unicode code point to encode and append. | |
* @return this builder. | |
* @see Character#toChars(int) */ | |
public StringBuilder appendCodePoint (int codePoint) { | |
append0(Character.toChars(codePoint)); | |
return this; | |
} | |
/** Deletes a sequence of characters specified by {@code start} and {@code end}. Shifts any remaining characters to the left. | |
* | |
* @param start the inclusive start index. | |
* @param end the exclusive end index. | |
* @return this builder. | |
* @throws StringIndexOutOfBoundsException if {@code start} is less than zero, greater than the current length or greater than | |
* {@code end}. */ | |
public StringBuilder delete (int start, int end) { | |
delete0(start, end); | |
return this; | |
} | |
/** Deletes the character at the specified index. shifts any remaining characters to the left. | |
* | |
* @param index the index of the character to delete. | |
* @return this builder. | |
* @throws StringIndexOutOfBoundsException if {@code index} is less than zero or is greater than or equal to the current | |
* length. */ | |
public StringBuilder deleteCharAt (int index) { | |
deleteCharAt0(index); | |
return this; | |
} | |
/** Inserts the string representation of the specified {@code boolean} value at the specified {@code offset}. The | |
* {@code boolean} value is converted to a string according to the rule defined by {@link String#valueOf(boolean)}. | |
* | |
* @param offset the index to insert at. | |
* @param b the {@code boolean} value to insert. | |
* @return this builder. | |
* @throws StringIndexOutOfBoundsException if {@code offset} is negative or greater than the current {@code length}. | |
* @see String#valueOf(boolean) */ | |
public StringBuilder insert (int offset, boolean b) { | |
insert0(offset, b ? "true" : "false"); //$NON-NLS-1$ //$NON-NLS-2$ | |
return this; | |
} | |
/** Inserts the string representation of the specified {@code char} value at the specified {@code offset}. The {@code char} | |
* value is converted to a string according to the rule defined by {@link String#valueOf(char)}. | |
* | |
* @param offset the index to insert at. | |
* @param c the {@code char} value to insert. | |
* @return this builder. | |
* @throws IndexOutOfBoundsException if {@code offset} is negative or greater than the current {@code length()}. | |
* @see String#valueOf(char) */ | |
public StringBuilder insert (int offset, char c) { | |
insert0(offset, c); | |
return this; | |
} | |
/** Inserts the string representation of the specified {@code int} value at the specified {@code offset}. The {@code int} value | |
* is converted to a String according to the rule defined by {@link String#valueOf(int)}. | |
* | |
* @param offset the index to insert at. | |
* @param i the {@code int} value to insert. | |
* @return this builder. | |
* @throws StringIndexOutOfBoundsException if {@code offset} is negative or greater than the current {@code length()}. | |
* @see String#valueOf(int) */ | |
public StringBuilder insert (int offset, int i) { | |
insert0(offset, Integer.toString(i)); | |
return this; | |
} | |
/** Inserts the string representation of the specified {@code long} value at the specified {@code offset}. The {@code long} | |
* value is converted to a String according to the rule defined by {@link String#valueOf(long)}. | |
* | |
* @param offset the index to insert at. | |
* @param l the {@code long} value to insert. | |
* @return this builder. | |
* @throws StringIndexOutOfBoundsException if {@code offset} is negative or greater than the current {code length()}. | |
* @see String#valueOf(long) */ | |
public StringBuilder insert (int offset, long l) { | |
insert0(offset, Long.toString(l)); | |
return this; | |
} | |
/** Inserts the string representation of the specified {@code float} value at the specified {@code offset}. The {@code float} | |
* value is converted to a string according to the rule defined by {@link String#valueOf(float)}. | |
* | |
* @param offset the index to insert at. | |
* @param f the {@code float} value to insert. | |
* @return this builder. | |
* @throws StringIndexOutOfBoundsException if {@code offset} is negative or greater than the current {@code length()}. | |
* @see String#valueOf(float) */ | |
public StringBuilder insert (int offset, float f) { | |
insert0(offset, Float.toString(f)); | |
return this; | |
} | |
/** Inserts the string representation of the specified {@code double} value at the specified {@code offset}. The {@code double} | |
* value is converted to a String according to the rule defined by {@link String#valueOf(double)}. | |
* | |
* @param offset the index to insert at. | |
* @param d the {@code double} value to insert. | |
* @return this builder. | |
* @throws StringIndexOutOfBoundsException if {@code offset} is negative or greater than the current {@code length()}. | |
* @see String#valueOf(double) */ | |
public StringBuilder insert (int offset, double d) { | |
insert0(offset, Double.toString(d)); | |
return this; | |
} | |
/** Inserts the string representation of the specified {@code Object} at the specified {@code offset}. The {@code Object} value | |
* is converted to a String according to the rule defined by {@link String#valueOf(Object)}. | |
* | |
* @param offset the index to insert at. | |
* @param obj the {@code Object} to insert. | |
* @return this builder. | |
* @throws StringIndexOutOfBoundsException if {@code offset} is negative or greater than the current {@code length()}. | |
* @see String#valueOf(Object) */ | |
public StringBuilder insert (int offset, Object obj) { | |
insert0(offset, obj == null ? "null" : obj.toString()); //$NON-NLS-1$ | |
return this; | |
} | |
/** Inserts the specified string at the specified {@code offset}. If the specified string is null, then the String | |
* {@code "null"} is inserted. | |
* | |
* @param offset the index to insert at. | |
* @param str the {@code String} to insert. | |
* @return this builder. | |
* @throws StringIndexOutOfBoundsException if {@code offset} is negative or greater than the current {@code length()}. */ | |
public StringBuilder insert (int offset, String str) { | |
insert0(offset, str); | |
return this; | |
} | |
/** Inserts the string representation of the specified {@code char[]} at the specified {@code offset}. The {@code char[]} value | |
* is converted to a String according to the rule defined by {@link String#valueOf(char[])}. | |
* | |
* @param offset the index to insert at. | |
* @param ch the {@code char[]} to insert. | |
* @return this builder. | |
* @throws StringIndexOutOfBoundsException if {@code offset} is negative or greater than the current {@code length()}. | |
* @see String#valueOf(char[]) */ | |
public StringBuilder insert (int offset, char[] ch) { | |
insert0(offset, ch); | |
return this; | |
} | |
/** Inserts the string representation of the specified subsequence of the {@code char[]} at the specified {@code offset}. The | |
* {@code char[]} value is converted to a String according to the rule defined by {@link String#valueOf(char[],int,int)}. | |
* | |
* @param offset the index to insert at. | |
* @param str the {@code char[]} to insert. | |
* @param strOffset the inclusive index. | |
* @param strLen the number of characters. | |
* @return this builder. | |
* @throws StringIndexOutOfBoundsException if {@code offset} is negative or greater than the current {@code length()}, or | |
* {@code strOffset} and {@code strLen} do not specify a valid subsequence. | |
* @see String#valueOf(char[],int,int) */ | |
public StringBuilder insert (int offset, char[] str, int strOffset, int strLen) { | |
insert0(offset, str, strOffset, strLen); | |
return this; | |
} | |
/** Inserts the string representation of the specified {@code CharSequence} at the specified {@code offset}. The | |
* {@code CharSequence} is converted to a String as defined by {@link CharSequence#toString()}. If {@code s} is {@code null}, | |
* then the String {@code "null"} is inserted. | |
* | |
* @param offset the index to insert at. | |
* @param s the {@code CharSequence} to insert. | |
* @return this builder. | |
* @throws IndexOutOfBoundsException if {@code offset} is negative or greater than the current {@code length()}. | |
* @see CharSequence#toString() */ | |
public StringBuilder insert (int offset, CharSequence s) { | |
insert0(offset, s == null ? "null" : s.toString()); //$NON-NLS-1$ | |
return this; | |
} | |
/** Inserts the string representation of the specified subsequence of the {@code CharSequence} at the specified {@code offset}. | |
* The {@code CharSequence} is converted to a String as defined by {@link CharSequence#subSequence(int, int)}. If the | |
* {@code CharSequence} is {@code null}, then the string {@code "null"} is used to determine the subsequence. | |
* | |
* @param offset the index to insert at. | |
* @param s the {@code CharSequence} to insert. | |
* @param start the start of the subsequence of the character sequence. | |
* @param end the end of the subsequence of the character sequence. | |
* @return this builder. | |
* @throws IndexOutOfBoundsException if {@code offset} is negative or greater than the current {@code length()}, or | |
* {@code start} and {@code end} do not specify a valid subsequence. | |
* @see CharSequence#subSequence(int, int) */ | |
public StringBuilder insert (int offset, CharSequence s, int start, int end) { | |
insert0(offset, s, start, end); | |
return this; | |
} | |
/** Replaces the specified subsequence in this builder with the specified string. | |
* | |
* @param start the inclusive begin index. | |
* @param end the exclusive end index. | |
* @param str the replacement string. | |
* @return this builder. | |
* @throws StringIndexOutOfBoundsException if {@code start} is negative, greater than the current {@code length()} or greater | |
* than {@code end}. | |
* @throws NullPointerException if {@code str} is {@code null}. */ | |
public StringBuilder replace (int start, int end, String str) { | |
replace0(start, end, str); | |
return this; | |
} | |
/** Replaces all instances of {@code find} with {@code replace}. */ | |
public StringBuilder replace (String find, String replace) { | |
int findLength = find.length(), replaceLength = replace.length(); | |
int index = 0; | |
while (true) { | |
index = indexOf(find, index); | |
if (index == -1) break; | |
replace0(index, index + findLength, replace); | |
index += replaceLength; | |
} | |
return this; | |
} | |
/** Replaces all instances of {@code find} with {@code replace}. */ | |
public StringBuilder replace (char find, String replace) { | |
int replaceLength = replace.length(); | |
int index = 0; | |
while (true) { | |
while (true) { | |
if (index == length) return this; | |
if (chars[index] == find) break; | |
index++; | |
} | |
replace0(index, index + 1, replace); | |
index += replaceLength; | |
} | |
} | |
/** Reverses the order of characters in this builder. | |
* | |
* @return this buffer. */ | |
public StringBuilder reverse () { | |
reverse0(); | |
return this; | |
} | |
public int hashCode () { | |
final int prime = 31; | |
int result = 1; | |
result = prime + length; | |
result = prime * result + Arrays.hashCode(chars); | |
return result; | |
} | |
public boolean equals (Object obj) { | |
if (this == obj) return true; | |
if (obj == null) return false; | |
if (getClass() != obj.getClass()) return false; | |
StringBuilder other = (StringBuilder)obj; | |
int length = this.length; | |
if (length != other.length) return false; | |
char[] chars = this.chars; | |
char[] chars2 = other.chars; | |
if (chars == chars2) return true; | |
if (chars == null || chars2 == null) return false; | |
for (int i = 0; i < length; i++) | |
if (chars[i] != chars2[i]) return false; | |
return true; | |
} | |
} |