diff --git a/src/java/toascii/images/ImageToAscii.java b/src/java/toascii/images/ImageToAscii.java
index bfc389f..73cee62 100644
--- a/src/java/toascii/images/ImageToAscii.java
+++ b/src/java/toascii/images/ImageToAscii.java
@@ -5,9 +5,33 @@ import java.awt.image.BufferedImage;
public class ImageToAscii {
static final char[] asciiChars = {'#', 'A', '@', '%', '$', '+', '=', '*', ':', ',', '.', ' '};
static final int numAsciiChars = asciiChars.length - 1;
- static final int spanLength = "X".length();
+ static final int spanLength = "X".length();
static final int lineTerminatorLength = "
".length();
+ // copied from java.lang.Integer.digits (which is private so we can't just reference it, boourns)
+ static final char[] digits = {
+ '0' , '1' , '2' , '3' , '4' , '5' ,
+ '6' , '7' , '8' , '9' , 'a' , 'b' ,
+ 'c' , 'd' , 'e' , 'f' , 'g' , 'h' ,
+ 'i' , 'j' , 'k' , 'l' , 'm' , 'n' ,
+ 'o' , 'p' , 'q' , 'r' , 's' , 't' ,
+ 'u' , 'v' , 'w' , 'x' , 'y' , 'z'
+ };
+
+ // modification of java.lang.Integer.toUnsignedString -- no garbage generated, but limited to max value
+ // of 255 ...hence the 'unsigned byte' thing :)
+ private static void unsignedByteToHex(int unsignedByte, StringBuilder sb) {
+ for (int i = 0; i < 2; ++i) {
+ int index = sb.length + 1 - i;
+ if (unsignedByte != 0) {
+ sb.chars[index] = digits[unsignedByte & 15];
+ unsignedByte >>>= 4;
+ } else
+ sb.chars[index] = '0';
+ }
+ sb.length += 2;
+ }
+
public static String convert(BufferedImage image, boolean useColor) {
final int width = image.getWidth();
final int height = image.getHeight();
@@ -19,6 +43,7 @@ public class ImageToAscii {
final StringBuilder sb = new StringBuilder(maxLength);
final int[] pixels = image.getRGB(0, 0, width, height, null, 0, width);
+
for (int y = 0; y < height; ++y) {
for (int x = 0; x < width; ++x) {
final int argb = pixels[(y * width) + x];
@@ -37,13 +62,11 @@ public class ImageToAscii {
final char pixelChar = asciiChars[charIndex > 0 ? charIndex : 0];
if (useColor) {
- sb.append("");
+ sb.append("");
sb.append(pixelChar);
sb.append("");
} else
diff --git a/src/java/toascii/images/StringBuilder.java b/src/java/toascii/images/StringBuilder.java
new file mode 100644
index 0000000..696ddf8
--- /dev/null
+++ b/src/java/toascii/images/StringBuilder.java
@@ -0,0 +1,1204 @@
+/*
+ * 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.
+ */
+
+// This StringBuilder implementation is borrowed from libgdx, mainly so that we can fiddle with the
+// underlying char array directly.
+// Source: https://github.com/libgdx/libgdx/blob/master/gdx/src/com/badlogic/gdx/utils/StringBuilder.java
+
+package toascii.images;
+
+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 {
+ 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;
+ }
+
+ /** 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;
+ }
+}
\ No newline at end of file