From 54dc8b0543db5f8504eb684553ec58f93aabe156 Mon Sep 17 00:00:00 2001 From: gered Date: Tue, 1 Apr 2014 22:57:21 -0400 Subject: [PATCH] alternate StringBuilder implementation. add function to convert RGB components to 2-digit hex strings --- src/java/toascii/images/ImageToAscii.java | 39 +- src/java/toascii/images/StringBuilder.java | 1204 ++++++++++++++++++++ 2 files changed, 1235 insertions(+), 8 deletions(-) create mode 100644 src/java/toascii/images/StringBuilder.java 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