    001/*
    002 * Licensed to the Apache Software Foundation (ASF) under one or more
    003 * contributor license agreements.  See the NOTICE file distributed with
    004 * this work for additional information regarding copyright ownership.
    005 * The ASF licenses this file to You under the Apache License, Version 2.0
    006 * (the "License"); you may not use this file except in compliance with
    007 * the License.  You may obtain a copy of the License at
    008 *
    009 *      http://www.apache.org/licenses/LICENSE-2.0
    010 *
    011 * Unless required by applicable law or agreed to in writing, software
    012 * distributed under the License is distributed on an "AS IS" BASIS,
    013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    014 * See the License for the specific language governing permissions and
    015 * limitations under the License.
    016 */
    017package org.apache.commons.text;
    018
    019import java.io.IOException;
    020import java.io.Reader;
    021import java.io.Serializable;
    022import java.io.Writer;
    023import java.nio.CharBuffer;
    024import java.util.Iterator;
    025import java.util.List;
    026import java.util.Objects;
    027
    028import org.apache.commons.text.matcher.StringMatcher;
    029
    030/**
    031 * Builds a string from constituent parts providing a more flexible and powerful API than StringBuffer.
    032 * <p>
    033 * The main differences from StringBuffer/StringBuilder are:
    034 * </p>
    035 * <ul>
    036 * <li>Not synchronized</li>
    037 * <li>Not final</li>
    038 * <li>Subclasses have direct access to character array</li>
    039 * <li>Additional methods
    040 * <ul>
    041 * <li>appendWithSeparators - adds an array of values, with a separator</li>
    042 * <li>appendPadding - adds a length padding characters</li>
    043 * <li>appendFixedLength - adds a fixed width field to the builder</li>
    044 * <li>toCharArray/getChars - simpler ways to get a range of the character array</li>
    045 * <li>delete - delete char or string</li>
    046 * <li>replace - search and replace for a char or string</li>
    047 * <li>leftString/rightString/midString - substring without exceptions</li>
    048 * <li>contains - whether the builder contains a char or string</li>
    049 * <li>size/clear/isEmpty - collections style API methods</li>
    050 * </ul>
    051 * </li>
    052 * <li>Views
    053 * <ul>
    054 * <li>asTokenizer - uses the internal buffer as the source of a StrTokenizer</li>
    055 * <li>asReader - uses the internal buffer as the source of a Reader</li>
    056 * <li>asWriter - allows a Writer to write directly to the internal buffer</li>
    057 * </ul>
    058 * </li>
    059 * </ul>
    060 * <p>
    061 * The aim has been to provide an API that mimics very closely what StringBuffer provides, but with additional methods.
    062 * It should be noted that some edge cases, with invalid indices or null input, have been altered - see individual
    063 * methods. The biggest of these changes is that by default, null will not output the text 'null'. This can be
    064 * controlled by a property, {@link #setNullText(String)}.
    065 * </p>
    066 * <p>
    067 * This class is called {@code TextStringBuilder} instead of {@code StringBuilder} to avoid clashing with
    068 * {@link java.lang.StringBuilder}.
    069 * </p>
    070 *
    071 * @since 1.3
    072 */
    073public class TextStringBuilder implements CharSequence, Appendable, Serializable, Builder<String> {
    074
    075    /**
    076     * The size of the string {@code "false"}.
    077     */
    078    private static final int FALSE_STRING_SIZE = "false".length();
    079
    080    /**
    081     * The size of the string {@code "true"}.
    082     */
    083    private static final int TRUE_STRING_SIZE = "true".length();
    084
    085    /**
    086     * The extra capacity for new builders.
    087     */
    088    static final int CAPACITY = 32;
    089
    090    /**
    091     * Required for serialization support.
    092     *
    093     * @see java.io.Serializable
    094     */
    095    private static final long serialVersionUID = 1L;
    096
    097    /** Internal data storage. */
    098    char[] buffer; // package-protected for test code use only
    099    /** Current size of the buffer. */
    100    private int size;
    101    /** The new line. */
    102    private String newLine;
    103    /** The null text. */
    104    private String nullText;
    105
    106    // -----------------------------------------------------------------------
    107    /**
    108     * Constructor that creates an empty builder initial capacity 32 characters.
    109     */
    110    public TextStringBuilder() {
    111        this(CAPACITY);
    112    }
    113
    114    /**
    115     * Constructor that creates an empty builder the specified initial capacity.
    116     *
    117     * @param initialCapacity
    118     *            the initial capacity, zero or less will be converted to 32
    119     */
    120    public TextStringBuilder(int initialCapacity) {
    121        super();
    122        if (initialCapacity <= 0) {
    123            initialCapacity = CAPACITY;
    124        }
    125        buffer = new char[initialCapacity];
    126    }
    127
    128    /**
    129     * Constructor that creates a builder from the string, allocating 32 extra characters for growth.
    130     *
    131     * @param str
    132     *            the string to copy, null treated as blank string
    133     */
    134    public TextStringBuilder(final String str) {
    135        super();
    136        if (str == null) {
    137            buffer = new char[CAPACITY];
    138        } else {
    139            buffer = new char[str.length() + CAPACITY];
    140            append(str);
    141        }
    142    }
    143
    144    // -----------------------------------------------------------------------
    145    /**
    146     * Gets the text to be appended when a new line is added.
    147     *
    148     * @return The new line text, null means use system default
    149     */
    150    public String getNewLineText() {
    151        return newLine;
    152    }
    153
    154    /**
    155     * Sets the text to be appended when a new line is added.
    156     *
    157     * @param newLine
    158     *            the new line text, null means use system default
    159     * @return this, to enable chaining
    160     */
    161    public TextStringBuilder setNewLineText(final String newLine) {
    162        this.newLine = newLine;
    163        return this;
    164    }
    165
    166    // -----------------------------------------------------------------------
    167    /**
    168     * Gets the text to be appended when null is added.
    169     *
    170     * @return The null text, null means no append
    171     */
    172    public String getNullText() {
    173        return nullText;
    174    }
    175
    176    /**
    177     * Sets the text to be appended when null is added.
    178     *
    179     * @param nullText
    180     *            the null text, null means no append
    181     * @return this, to enable chaining
    182     */
    183    public TextStringBuilder setNullText(String nullText) {
    184        if (nullText != null && nullText.isEmpty()) {
    185            nullText = null;
    186        }
    187        this.nullText = nullText;
    188        return this;
    189    }
    190
    191    // -----------------------------------------------------------------------
    192    /**
    193     * Gets the length of the string builder.
    194     *
    195     * @return The length
    196     */
    197    @Override
    198    public int length() {
    199        return size;
    200    }
    201
    202    /**
    203     * Updates the length of the builder by either dropping the last characters or adding filler of Unicode zero.
    204     *
    205     * @param length
    206     *            the length to set to, must be zero or positive
    207     * @return this, to enable chaining
    208     * @throws IndexOutOfBoundsException
    209     *             if the length is negative
    210     */
    211    public TextStringBuilder setLength(final int length) {
    212        if (length < 0) {
    213            throw new StringIndexOutOfBoundsException(length);
    214        }
    215        if (length < size) {
    216            size = length;
    217        } else if (length > size) {
    218            ensureCapacity(length);
    219            final int oldEnd = size;
    220            final int newEnd = length;
    221            size = length;
    222            for (int i = oldEnd; i < newEnd; i++) {
    223                buffer[i] = '\0';
    224            }
    225        }
    226        return this;
    227    }
    228
    229    // -----------------------------------------------------------------------
    230    /**
    231     * Gets the current size of the internal character array buffer.
    232     *
    233     * @return The capacity
    234     */
    235    public int capacity() {
    236        return buffer.length;
    237    }
    238
    239    /**
    240     * Checks the capacity and ensures that it is at least the size specified.
    241     *
    242     * @param capacity
    243     *            the capacity to ensure
    244     * @return this, to enable chaining
    245     */
    246    public TextStringBuilder ensureCapacity(final int capacity) {
    247        if (capacity > buffer.length) {
    248            final char[] old = buffer;
    249            buffer = new char[capacity * 2];
    250            System.arraycopy(old, 0, buffer, 0, size);
    251        }
    252        return this;
    253    }
    254
    255    /**
    256     * Minimizes the capacity to the actual length of the string.
    257     *
    258     * @return this, to enable chaining
    259     */
    260    public TextStringBuilder minimizeCapacity() {
    261        if (buffer.length > length()) {
    262            final char[] old = buffer;
    263            buffer = new char[length()];
    264            System.arraycopy(old, 0, buffer, 0, size);
    265        }
    266        return this;
    267    }
    268
    269    // -----------------------------------------------------------------------
    270    /**
    271     * Gets the length of the string builder.
    272     * <p>
    273     * This method is the same as {@link #length()} and is provided to match the API of Collections.
    274     *
    275     * @return The length
    276     */
    277    public int size() {
    278        return size;
    279    }
    280
    281    /**
    282     * Checks is the string builder is empty (convenience Collections API style method).
    283     * <p>
    284     * This method is the same as checking {@link #length()} and is provided to match the API of Collections.
    285     *
    286     * @return <code>true</code> if the size is <code>0</code>.
    287     */
    288    public boolean isEmpty() {
    289        return size == 0;
    290    }
    291
    292    /**
    293     * Clears the string builder (convenience Collections API style method).
    294     * <p>
    295     * This method does not reduce the size of the internal character buffer. To do that, call <code>clear()</code>
    296     * followed by {@link #minimizeCapacity()}.
    297     * <p>
    298     * This method is the same as {@link #setLength(int)} called with zero and is provided to match the API of
    299     * Collections.
    300     *
    301     * @return this, to enable chaining
    302     */
    303    public TextStringBuilder clear() {
    304        size = 0;
    305        return this;
    306    }
    307
    308    // -----------------------------------------------------------------------
    309    /**
    310     * Gets the character at the specified index.
    311     *
    312     * @see #setCharAt(int, char)
    313     * @see #deleteCharAt(int)
    314     * @param index
    315     *            the index to retrieve, must be valid
    316     * @return The character at the index
    317     * @throws IndexOutOfBoundsException
    318     *             if the index is invalid
    319     */
    320    @Override
    321    public char charAt(final int index) {
    322        if (index < 0 || index >= length()) {
    323            throw new StringIndexOutOfBoundsException(index);
    324        }
    325        return buffer[index];
    326    }
    327
    328    /**
    329     * Sets the character at the specified index.
    330     *
    331     * @see #charAt(int)
    332     * @see #deleteCharAt(int)
    333     * @param index
    334     *            the index to set
    335     * @param ch
    336     *            the new character
    337     * @return this, to enable chaining
    338     * @throws IndexOutOfBoundsException
    339     *             if the index is invalid
    340     */
    341    public TextStringBuilder setCharAt(final int index, final char ch) {
    342        if (index < 0 || index >= length()) {
    343            throw new StringIndexOutOfBoundsException(index);
    344        }
    345        buffer[index] = ch;
    346        return this;
    347    }
    348
    349    /**
    350     * Deletes the character at the specified index.
    351     *
    352     * @see #charAt(int)
    353     * @see #setCharAt(int, char)
    354     * @param index
    355     *            the index to delete
    356     * @return this, to enable chaining
    357     * @throws IndexOutOfBoundsException
    358     *             if the index is invalid
    359     */
    360    public TextStringBuilder deleteCharAt(final int index) {
    361        if (index < 0 || index >= size) {
    362            throw new StringIndexOutOfBoundsException(index);
    363        }
    364        deleteImpl(index, index + 1, 1);
    365        return this;
    366    }
    367
    368    // -----------------------------------------------------------------------
    369    /**
    370     * Copies the builder's character array into a new character array.
    371     *
    372     * @return a new array that represents the contents of the builder
    373     */
    374    public char[] toCharArray() {
    375        if (size == 0) {
    376            return new char[0];
    377        }
    378        final char[] chars = new char[size];
    379        System.arraycopy(buffer, 0, chars, 0, size);
    380        return chars;
    381    }
    382
    383    /**
    384     * Copies part of the builder's character array into a new character array.
    385     *
    386     * @param startIndex
    387     *            the start index, inclusive, must be valid
    388     * @param endIndex
    389     *            the end index, exclusive, must be valid except that if too large it is treated as end of string
    390     * @return a new array that holds part of the contents of the builder
    391     * @throws IndexOutOfBoundsException
    392     *             if startIndex is invalid, or if endIndex is invalid (but endIndex greater than size is valid)
    393     */
    394    public char[] toCharArray(final int startIndex, int endIndex) {
    395        endIndex = validateRange(startIndex, endIndex);
    396        final int len = endIndex - startIndex;
    397        if (len == 0) {
    398            return new char[0];
    399        }
    400        final char[] chars = new char[len];
    401        System.arraycopy(buffer, startIndex, chars, 0, len);
    402        return chars;
    403    }
    404
    405    /**
    406     * Copies the character array into the specified array.
    407     *
    408     * @param destination
    409     *            the destination array, null will cause an array to be created
    410     * @return The input array, unless that was null or too small
    411     */
    412    public char[] getChars(char[] destination) {
    413        final int len = length();
    414        if (destination == null || destination.length < len) {
    415            destination = new char[len];
    416        }
    417        System.arraycopy(buffer, 0, destination, 0, len);
    418        return destination;
    419    }
    420
    421    /**
    422     * Copies the character array into the specified array.
    423     *
    424     * @param startIndex
    425     *            first index to copy, inclusive, must be valid
    426     * @param endIndex
    427     *            last index, exclusive, must be valid
    428     * @param destination
    429     *            the destination array, must not be null or too small
    430     * @param destinationIndex
    431     *            the index to start copying in destination
    432     * @throws NullPointerException
    433     *             if the array is null
    434     * @throws IndexOutOfBoundsException
    435     *             if any index is invalid
    436     */
    437    public void getChars(final int startIndex, final int endIndex, final char[] destination,
    438            final int destinationIndex) {
    439        if (startIndex < 0) {
    440            throw new StringIndexOutOfBoundsException(startIndex);
    441        }
    442        if (endIndex < 0 || endIndex > length()) {
    443            throw new StringIndexOutOfBoundsException(endIndex);
    444        }
    445        if (startIndex > endIndex) {
    446            throw new StringIndexOutOfBoundsException("end < start");
    447        }
    448        System.arraycopy(buffer, startIndex, destination, destinationIndex, endIndex - startIndex);
    449    }
    450
    451    // -----------------------------------------------------------------------
    452    /**
    453     * If possible, reads chars from the provided {@link Readable} directly into underlying character buffer without
    454     * making extra copies.
    455     *
    456     * @param readable
    457     *            object to read from
    458     * @return The number of characters read
    459     * @throws IOException
    460     *             if an I/O error occurs
    461     *
    462     * @see #appendTo(Appendable)
    463     */
    464    public int readFrom(final Readable readable) throws IOException {
    465        final int oldSize = size;
    466        if (readable instanceof Reader) {
    467            final Reader r = (Reader) readable;
    468            ensureCapacity(size + 1);
    469            int read;
    470            while ((read = r.read(buffer, size, buffer.length - size)) != -1) {
    471                size += read;
    472                ensureCapacity(size + 1);
    473            }
    474        } else if (readable instanceof CharBuffer) {
    475            final CharBuffer cb = (CharBuffer) readable;
    476            final int remaining = cb.remaining();
    477            ensureCapacity(size + remaining);
    478            cb.get(buffer, size, remaining);
    479            size += remaining;
    480        } else {
    481            while (true) {
    482                ensureCapacity(size + 1);
    483                final CharBuffer buf = CharBuffer.wrap(buffer, size, buffer.length - size);
    484                final int read = readable.read(buf);
    485                if (read == -1) {
    486                    break;
    487                }
    488                size += read;
    489            }
    490        }
    491        return size - oldSize;
    492    }
    493
    494    // -----------------------------------------------------------------------
    495    /**
    496     * Appends the new line string to this string builder.
    497     * <p>
    498     * The new line string can be altered using {@link #setNewLineText(String)}. This might be used to force the output
    499     * to always use Unix line endings even when on Windows.
    500     *
    501     * @return this, to enable chaining
    502     */
    503    public TextStringBuilder appendNewLine() {
    504        if (newLine == null) {
    505            append(System.lineSeparator());
    506            return this;
    507        }
    508        return append(newLine);
    509    }
    510
    511    /**
    512     * Appends the text representing <code>null</code> to this string builder.
    513     *
    514     * @return this, to enable chaining
    515     */
    516    public TextStringBuilder appendNull() {
    517        if (nullText == null) {
    518            return this;
    519        }
    520        return append(nullText);
    521    }
    522
    523    /**
    524     * Appends an object to this string builder. Appending null will call {@link #appendNull()}.
    525     *
    526     * @param obj
    527     *            the object to append
    528     * @return this, to enable chaining
    529     */
    530    public TextStringBuilder append(final Object obj) {
    531        if (obj == null) {
    532            return appendNull();
    533        }
    534        if (obj instanceof CharSequence) {
    535            return append((CharSequence) obj);
    536        }
    537        return append(obj.toString());
    538    }
    539
    540    /**
    541     * Appends a CharSequence to this string builder. Appending null will call {@link #appendNull()}.
    542     *
    543     * @param seq
    544     *            the CharSequence to append
    545     * @return this, to enable chaining
    546     */
    547    @Override
    548    public TextStringBuilder append(final CharSequence seq) {
    549        if (seq == null) {
    550            return appendNull();
    551        }
    552        if (seq instanceof TextStringBuilder) {
    553            return append((TextStringBuilder) seq);
    554        }
    555        if (seq instanceof StringBuilder) {
    556            return append((StringBuilder) seq);
    557        }
    558        if (seq instanceof StringBuffer) {
    559            return append((StringBuffer) seq);
    560        }
    561        if (seq instanceof CharBuffer) {
    562            return append((CharBuffer) seq);
    563        }
    564        return append(seq.toString());
    565    }
    566
    567    /**
    568     * Appends part of a CharSequence to this string builder. Appending null will call {@link #appendNull()}.
    569     *
    570     * @param seq
    571     *            the CharSequence to append
    572     * @param startIndex
    573     *            the start index, inclusive, must be valid
    574     * @param endIndex
    575     *            the end index, exclusive, must be valid
    576     * @return this, to enable chaining
    577     */
    578    @Override
    579    public TextStringBuilder append(final CharSequence seq, final int startIndex, final int endIndex) {
    580        if (seq == null) {
    581            return appendNull();
    582        }
    583        if (endIndex <= 0) {
    584            throw new StringIndexOutOfBoundsException("endIndex must be valid");
    585        }
    586        if (startIndex >= endIndex) {
    587            throw new StringIndexOutOfBoundsException("endIndex must be greater than startIndex");
    588        }
    589        return append(seq.toString(), startIndex, endIndex - startIndex);
    590    }
    591
    592    /**
    593     * Appends a string to this string builder. Appending null will call {@link #appendNull()}.
    594     *
    595     * @param str
    596     *            the string to append
    597     * @return this, to enable chaining
    598     */
    599    public TextStringBuilder append(final String str) {
    600        if (str == null) {
    601            return appendNull();
    602        }
    603        final int strLen = str.length();
    604        if (strLen > 0) {
    605            final int len = length();
    606            ensureCapacity(len + strLen);
    607            str.getChars(0, strLen, buffer, len);
    608            size += strLen;
    609        }
    610        return this;
    611    }
    612
    613    /**
    614     * Appends part of a string to this string builder. Appending null will call {@link #appendNull()}.
    615     *
    616     * @param str
    617     *            the string to append
    618     * @param startIndex
    619     *            the start index, inclusive, must be valid
    620     * @param length
    621     *            the length to append, must be valid
    622     * @return this, to enable chaining
    623     */
    624    public TextStringBuilder append(final String str, final int startIndex, final int length) {
    625        if (str == null) {
    626            return appendNull();
    627        }
    628        if (startIndex < 0 || startIndex > str.length()) {
    629            throw new StringIndexOutOfBoundsException("startIndex must be valid");
    630        }
    631        if (length < 0 || startIndex + length > str.length()) {
    632            throw new StringIndexOutOfBoundsException("length must be valid");
    633        }
    634        if (length > 0) {
    635            final int len = length();
    636            ensureCapacity(len + length);
    637            str.getChars(startIndex, startIndex + length, buffer, len);
    638            size += length;
    639        }
    640        return this;
    641    }
    642
    643    /**
    644     * Calls {@link String#format(String, Object...)} and appends the result.
    645     *
    646     * @param format
    647     *            the format string
    648     * @param objs
    649     *            the objects to use in the format string
    650     * @return {@code this} to enable chaining
    651     * @see String#format(String, Object...)
    652     */
    653    public TextStringBuilder append(final String format, final Object... objs) {
    654        return append(String.format(format, objs));
    655    }
    656
    657    /**
    658     * Appends the contents of a char buffer to this string builder. Appending null will call {@link #appendNull()}.
    659     *
    660     * @param buf
    661     *            the char buffer to append
    662     * @return this, to enable chaining
    663     */
    664    public TextStringBuilder append(final CharBuffer buf) {
    665        if (buf == null) {
    666            return appendNull();
    667        }
    668        if (buf.hasArray()) {
    669            final int length = buf.remaining();
    670            final int len = length();
    671            ensureCapacity(len + length);
    672            System.arraycopy(buf.array(), buf.arrayOffset() + buf.position(), buffer, len, length);
    673            size += length;
    674        } else {
    675            append(buf.toString());
    676        }
    677        return this;
    678    }
    679
    680    /**
    681     * Appends the contents of a char buffer to this string builder. Appending null will call {@link #appendNull()}.
    682     *
    683     * @param buf
    684     *            the char buffer to append
    685     * @param startIndex
    686     *            the start index, inclusive, must be valid
    687     * @param length
    688     *            the length to append, must be valid
    689     * @return this, to enable chaining
    690     */
    691    public TextStringBuilder append(final CharBuffer buf, final int startIndex, final int length) {
    692        if (buf == null) {
    693            return appendNull();
    694        }
    695        if (buf.hasArray()) {
    696            final int totalLength = buf.remaining();
    697            if (startIndex < 0 || startIndex > totalLength) {
    698                throw new StringIndexOutOfBoundsException("startIndex must be valid");
    699            }
    700            if (length < 0 || startIndex + length > totalLength) {
    701                throw new StringIndexOutOfBoundsException("length must be valid");
    702            }
    703            final int len = length();
    704            ensureCapacity(len + length);
    705            System.arraycopy(buf.array(), buf.arrayOffset() + buf.position() + startIndex, buffer, len, length);
    706            size += length;
    707        } else {
    708            append(buf.toString(), startIndex, length);
    709        }
    710        return this;
    711    }
    712
    713    /**
    714     * Appends a string buffer to this string builder. Appending null will call {@link #appendNull()}.
    715     *
    716     * @param str
    717     *            the string buffer to append
    718     * @return this, to enable chaining
    719     */
    720    public TextStringBuilder append(final StringBuffer str) {
    721        if (str == null) {
    722            return appendNull();
    723        }
    724        final int strLen = str.length();
    725        if (strLen > 0) {
    726            final int len = length();
    727            ensureCapacity(len + strLen);
    728            str.getChars(0, strLen, buffer, len);
    729            size += strLen;
    730        }
    731        return this;
    732    }
    733
    734    /**
    735     * Appends part of a string buffer to this string builder. Appending null will call {@link #appendNull()}.
    736     *
    737     * @param str
    738     *            the string to append
    739     * @param startIndex
    740     *            the start index, inclusive, must be valid
    741     * @param length
    742     *            the length to append, must be valid
    743     * @return this, to enable chaining
    744     */
    745    public TextStringBuilder append(final StringBuffer str, final int startIndex, final int length) {
    746        if (str == null) {
    747            return appendNull();
    748        }
    749        if (startIndex < 0 || startIndex > str.length()) {
    750            throw new StringIndexOutOfBoundsException("startIndex must be valid");
    751        }
    752        if (length < 0 || startIndex + length > str.length()) {
    753            throw new StringIndexOutOfBoundsException("length must be valid");
    754        }
    755        if (length > 0) {
    756            final int len = length();
    757            ensureCapacity(len + length);
    758            str.getChars(startIndex, startIndex + length, buffer, len);
    759            size += length;
    760        }
    761        return this;
    762    }
    763
    764    /**
    765     * Appends a StringBuilder to this string builder. Appending null will call {@link #appendNull()}.
    766     *
    767     * @param str
    768     *            the StringBuilder to append
    769     * @return this, to enable chaining
    770     */
    771    public TextStringBuilder append(final StringBuilder str) {
    772        if (str == null) {
    773            return appendNull();
    774        }
    775        final int strLen = str.length();
    776        if (strLen > 0) {
    777            final int len = length();
    778            ensureCapacity(len + strLen);
    779            str.getChars(0, strLen, buffer, len);
    780            size += strLen;
    781        }
    782        return this;
    783    }
    784
    785    /**
    786     * Appends part of a StringBuilder to this string builder. Appending null will call {@link #appendNull()}.
    787     *
    788     * @param str
    789     *            the StringBuilder to append
    790     * @param startIndex
    791     *            the start index, inclusive, must be valid
    792     * @param length
    793     *            the length to append, must be valid
    794     * @return this, to enable chaining
    795     */
    796    public TextStringBuilder append(final StringBuilder str, final int startIndex, final int length) {
    797        if (str == null) {
    798            return appendNull();
    799        }
    800        if (startIndex < 0 || startIndex > str.length()) {
    801            throw new StringIndexOutOfBoundsException("startIndex must be valid");
    802        }
    803        if (length < 0 || startIndex + length > str.length()) {
    804            throw new StringIndexOutOfBoundsException("length must be valid");
    805        }
    806        if (length > 0) {
    807            final int len = length();
    808            ensureCapacity(len + length);
    809            str.getChars(startIndex, startIndex + length, buffer, len);
    810            size += length;
    811        }
    812        return this;
    813    }
    814
    815    /**
    816     * Appends another string builder to this string builder. Appending null will call {@link #appendNull()}.
    817     *
    818     * @param str
    819     *            the string builder to append
    820     * @return this, to enable chaining
    821     */
    822    public TextStringBuilder append(final TextStringBuilder str) {
    823        if (str == null) {
    824            return appendNull();
    825        }
    826        final int strLen = str.length();
    827        if (strLen > 0) {
    828            final int len = length();
    829            ensureCapacity(len + strLen);
    830            System.arraycopy(str.buffer, 0, buffer, len, strLen);
    831            size += strLen;
    832        }
    833        return this;
    834    }
    835
    836    /**
    837     * Appends part of a string builder to this string builder. Appending null will call {@link #appendNull()}.
    838     *
    839     * @param str
    840     *            the string to append
    841     * @param startIndex
    842     *            the start index, inclusive, must be valid
    843     * @param length
    844     *            the length to append, must be valid
    845     * @return this, to enable chaining
    846     */
    847    public TextStringBuilder append(final TextStringBuilder str, final int startIndex, final int length) {
    848        if (str == null) {
    849            return appendNull();
    850        }
    851        if (startIndex < 0 || startIndex > str.length()) {
    852            throw new StringIndexOutOfBoundsException("startIndex must be valid");
    853        }
    854        if (length < 0 || startIndex + length > str.length()) {
    855            throw new StringIndexOutOfBoundsException("length must be valid");
    856        }
    857        if (length > 0) {
    858            final int len = length();
    859            ensureCapacity(len + length);
    860            str.getChars(startIndex, startIndex + length, buffer, len);
    861            size += length;
    862        }
    863        return this;
    864    }
    865
    866    /**
    867     * Appends a char array to the string builder. Appending null will call {@link #appendNull()}.
    868     *
    869     * @param chars
    870     *            the char array to append
    871     * @return this, to enable chaining
    872     */
    873    public TextStringBuilder append(final char[] chars) {
    874        if (chars == null) {
    875            return appendNull();
    876        }
    877        final int strLen = chars.length;
    878        if (strLen > 0) {
    879            final int len = length();
    880            ensureCapacity(len + strLen);
    881            System.arraycopy(chars, 0, buffer, len, strLen);
    882            size += strLen;
    883        }
    884        return this;
    885    }
    886
    887    /**
    888     * Appends a char array to the string builder. Appending null will call {@link #appendNull()}.
    889     *
    890     * @param chars
    891     *            the char array to append
    892     * @param startIndex
    893     *            the start index, inclusive, must be valid
    894     * @param length
    895     *            the length to append, must be valid
    896     * @return this, to enable chaining
    897     */
    898    public TextStringBuilder append(final char[] chars, final int startIndex, final int length) {
    899        if (chars == null) {
    900            return appendNull();
    901        }
    902        if (startIndex < 0 || startIndex > chars.length) {
    903            throw new StringIndexOutOfBoundsException("Invalid startIndex: " + length);
    904        }
    905        if (length < 0 || startIndex + length > chars.length) {
    906            throw new StringIndexOutOfBoundsException("Invalid length: " + length);
    907        }
    908        if (length > 0) {
    909            final int len = length();
    910            ensureCapacity(len + length);
    911            System.arraycopy(chars, startIndex, buffer, len, length);
    912            size += length;
    913        }
    914        return this;
    915    }
    916
    917    /**
    918     * Appends a boolean value to the string builder.
    919     *
    920     * @param value
    921     *            the value to append
    922     * @return this, to enable chaining
    923     */
    924    public TextStringBuilder append(final boolean value) {
    925        if (value) {
    926            ensureCapacity(size + TRUE_STRING_SIZE);
    927            buffer[size++] = 't';
    928            buffer[size++] = 'r';
    929            buffer[size++] = 'u';
    930            buffer[size++] = 'e';
    931        } else {
    932            ensureCapacity(size + FALSE_STRING_SIZE);
    933            buffer[size++] = 'f';
    934            buffer[size++] = 'a';
    935            buffer[size++] = 'l';
    936            buffer[size++] = 's';
    937            buffer[size++] = 'e';
    938        }
    939        return this;
    940    }
    941
    942    /**
    943     * Appends a char value to the string builder.
    944     *
    945     * @param ch
    946     *            the value to append
    947     * @return this, to enable chaining
    948     */
    949    @Override
    950    public TextStringBuilder append(final char ch) {
    951        final int len = length();
    952        ensureCapacity(len + 1);
    953        buffer[size++] = ch;
    954        return this;
    955    }
    956
    957    /**
    958     * Appends an int value to the string builder using <code>String.valueOf</code>.
    959     *
    960     * @param value
    961     *            the value to append
    962     * @return this, to enable chaining
    963     */
    964    public TextStringBuilder append(final int value) {
    965        return append(String.valueOf(value));
    966    }
    967
    968    /**
    969     * Appends a long value to the string builder using <code>String.valueOf</code>.
    970     *
    971     * @param value
    972     *            the value to append
    973     * @return this, to enable chaining
    974     */
    975    public TextStringBuilder append(final long value) {
    976        return append(String.valueOf(value));
    977    }
    978
    979    /**
    980     * Appends a float value to the string builder using <code>String.valueOf</code>.
    981     *
    982     * @param value
    983     *            the value to append
    984     * @return this, to enable chaining
    985     */
    986    public TextStringBuilder append(final float value) {
    987        return append(String.valueOf(value));
    988    }
    989
    990    /**
    991     * Appends a double value to the string builder using <code>String.valueOf</code>.
    992     *
    993     * @param value
    994     *            the value to append
    995     * @return this, to enable chaining
    996     */
    997    public TextStringBuilder append(final double value) {
    998        return append(String.valueOf(value));
    999    }
    1000
    1001    // -----------------------------------------------------------------------
    1002    /**
    1003     * Appends an object followed by a new line to this string builder. Appending null will call {@link #appendNull()}.
    1004     *
    1005     * @param obj
    1006     *            the object to append
    1007     * @return this, to enable chaining
    1008     */
    1009    public TextStringBuilder appendln(final Object obj) {
    1010        return append(obj).appendNewLine();
    1011    }
    1012
    1013    /**
    1014     * Appends a string followed by a new line to this string builder. Appending null will call {@link #appendNull()}.
    1015     *
    1016     * @param str
    1017     *            the string to append
    1018     * @return this, to enable chaining
    1019     */
    1020    public TextStringBuilder appendln(final String str) {
    1021        return append(str).appendNewLine();
    1022    }
    1023
    1024    /**
    1025     * Appends part of a string followed by a new line to this string builder. Appending null will call
    1026     * {@link #appendNull()}.
    1027     *
    1028     * @param str
    1029     *            the string to append
    1030     * @param startIndex
    1031     *            the start index, inclusive, must be valid
    1032     * @param length
    1033     *            the length to append, must be valid
    1034     * @return this, to enable chaining
    1035     */
    1036    public TextStringBuilder appendln(final String str, final int startIndex, final int length) {
    1037        return append(str, startIndex, length).appendNewLine();
    1038    }
    1039
    1040    /**
    1041     * Calls {@link String#format(String, Object...)} and appends the result.
    1042     *
    1043     * @param format
    1044     *            the format string
    1045     * @param objs
    1046     *            the objects to use in the format string
    1047     * @return {@code this} to enable chaining
    1048     * @see String#format(String, Object...)
    1049     */
    1050    public TextStringBuilder appendln(final String format, final Object... objs) {
    1051        return append(format, objs).appendNewLine();
    1052    }
    1053
    1054    /**
    1055     * Appends a string buffer followed by a new line to this string builder. Appending null will call
    1056     * {@link #appendNull()}.
    1057     *
    1058     * @param str
    1059     *            the string buffer to append
    1060     * @return this, to enable chaining
    1061     */
    1062    public TextStringBuilder appendln(final StringBuffer str) {
    1063        return append(str).appendNewLine();
    1064    }
    1065
    1066    /**
    1067     * Appends a string builder followed by a new line to this string builder. Appending null will call
    1068     * {@link #appendNull()}.
    1069     *
    1070     * @param str
    1071     *            the string builder to append
    1072     * @return this, to enable chaining
    1073     */
    1074    public TextStringBuilder appendln(final StringBuilder str) {
    1075        return append(str).appendNewLine();
    1076    }
    1077
    1078    /**
    1079     * Appends part of a string builder followed by a new line to this string builder. Appending null will call
    1080     * {@link #appendNull()}.
    1081     *
    1082     * @param str
    1083     *            the string builder to append
    1084     * @param startIndex
    1085     *            the start index, inclusive, must be valid
    1086     * @param length
    1087     *            the length to append, must be valid
    1088     * @return this, to enable chaining
    1089     */
    1090    public TextStringBuilder appendln(final StringBuilder str, final int startIndex, final int length) {
    1091        return append(str, startIndex, length).appendNewLine();
    1092    }
    1093
    1094    /**
    1095     * Appends part of a string buffer followed by a new line to this string builder. Appending null will call
    1096     * {@link #appendNull()}.
    1097     *
    1098     * @param str
    1099     *            the string to append
    1100     * @param startIndex
    1101     *            the start index, inclusive, must be valid
    1102     * @param length
    1103     *            the length to append, must be valid
    1104     * @return this, to enable chaining
    1105     */
    1106    public TextStringBuilder appendln(final StringBuffer str, final int startIndex, final int length) {
    1107        return append(str, startIndex, length).appendNewLine();
    1108    }
    1109
    1110    /**
    1111     * Appends another string builder followed by a new line to this string builder. Appending null will call
    1112     * {@link #appendNull()}.
    1113     *
    1114     * @param str
    1115     *            the string builder to append
    1116     * @return this, to enable chaining
    1117     */
    1118    public TextStringBuilder appendln(final TextStringBuilder str) {
    1119        return append(str).appendNewLine();
    1120    }
    1121
    1122    /**
    1123     * Appends part of a string builder followed by a new line to this string builder. Appending null will call
    1124     * {@link #appendNull()}.
    1125     *
    1126     * @param str
    1127     *            the string to append
    1128     * @param startIndex
    1129     *            the start index, inclusive, must be valid
    1130     * @param length
    1131     *            the length to append, must be valid
    1132     * @return this, to enable chaining
    1133     */
    1134    public TextStringBuilder appendln(final TextStringBuilder str, final int startIndex, final int length) {
    1135        return append(str, startIndex, length).appendNewLine();
    1136    }
    1137
    1138    /**
    1139     * Appends a char array followed by a new line to the string builder. Appending null will call
    1140     * {@link #appendNull()}.
    1141     *
    1142     * @param chars
    1143     *            the char array to append
    1144     * @return this, to enable chaining
    1145     */
    1146    public TextStringBuilder appendln(final char[] chars) {
    1147        return append(chars).appendNewLine();
    1148    }
    1149
    1150    /**
    1151     * Appends a char array followed by a new line to the string builder. Appending null will call
    1152     * {@link #appendNull()}.
    1153     *
    1154     * @param chars
    1155     *            the char array to append
    1156     * @param startIndex
    1157     *            the start index, inclusive, must be valid
    1158     * @param length
    1159     *            the length to append, must be valid
    1160     * @return this, to enable chaining
    1161     */
    1162    public TextStringBuilder appendln(final char[] chars, final int startIndex, final int length) {
    1163        return append(chars, startIndex, length).appendNewLine();
    1164    }
    1165
    1166    /**
    1167     * Appends a boolean value followed by a new line to the string builder.
    1168     *
    1169     * @param value
    1170     *            the value to append
    1171     * @return this, to enable chaining
    1172     */
    1173    public TextStringBuilder appendln(final boolean value) {
    1174        return append(value).appendNewLine();
    1175    }
    1176
    1177    /**
    1178     * Appends a char value followed by a new line to the string builder.
    1179     *
    1180     * @param ch
    1181     *            the value to append
    1182     * @return this, to enable chaining
    1183     */
    1184    public TextStringBuilder appendln(final char ch) {
    1185        return append(ch).appendNewLine();
    1186    }
    1187
    1188    /**
    1189     * Appends an int value followed by a new line to the string builder using <code>String.valueOf</code>.
    1190     *
    1191     * @param value
    1192     *            the value to append
    1193     * @return this, to enable chaining
    1194     */
    1195    public TextStringBuilder appendln(final int value) {
    1196        return append(value).appendNewLine();
    1197    }
    1198
    1199    /**
    1200     * Appends a long value followed by a new line to the string builder using <code>String.valueOf</code>.
    1201     *
    1202     * @param value
    1203     *            the value to append
    1204     * @return this, to enable chaining
    1205     */
    1206    public TextStringBuilder appendln(final long value) {
    1207        return append(value).appendNewLine();
    1208    }
    1209
    1210    /**
    1211     * Appends a float value followed by a new line to the string builder using <code>String.valueOf</code>.
    1212     *
    1213     * @param value
    1214     *            the value to append
    1215     * @return this, to enable chaining
    1216     */
    1217    public TextStringBuilder appendln(final float value) {
    1218        return append(value).appendNewLine();
    1219    }
    1220
    1221    /**
    1222     * Appends a double value followed by a new line to the string builder using <code>String.valueOf</code>.
    1223     *
    1224     * @param value
    1225     *            the value to append
    1226     * @return this, to enable chaining
    1227     */
    1228    public TextStringBuilder appendln(final double value) {
    1229        return append(value).appendNewLine();
    1230    }
    1231
    1232    // -----------------------------------------------------------------------
    1233    /**
    1234     * Appends each item in an array to the builder without any separators. Appending a null array will have no effect.
    1235     * Each object is appended using {@link #append(Object)}.
    1236     *
    1237     * @param <T>
    1238     *            the element type
    1239     * @param array
    1240     *            the array to append
    1241     * @return this, to enable chaining
    1242     */
    1243    public <T> TextStringBuilder appendAll(@SuppressWarnings("unchecked") final T... array) {
    1244        /*
    1245         * @SuppressWarnings used to hide warning about vararg usage. We cannot use @SafeVarargs, since this method is
    1246         * not final. Using @SuppressWarnings is fine, because it isn't inherited by subclasses, so each subclass must
    1247         * vouch for itself whether its use of 'array' is safe.
    1248         */
    1249        if (array != null && array.length > 0) {
    1250            for (final Object element : array) {
    1251                append(element);
    1252            }
    1253        }
    1254        return this;
    1255    }
    1256
    1257    /**
    1258     * Appends each item in an iterable to the builder without any separators. Appending a null iterable will have no
    1259     * effect. Each object is appended using {@link #append(Object)}.
    1260     *
    1261     * @param iterable
    1262     *            the iterable to append
    1263     * @return this, to enable chaining
    1264     */
    1265    public TextStringBuilder appendAll(final Iterable<?> iterable) {
    1266        if (iterable != null) {
    1267            for (final Object o : iterable) {
    1268                append(o);
    1269            }
    1270        }
    1271        return this;
    1272    }
    1273
    1274    /**
    1275     * Appends each item in an iterator to the builder without any separators. Appending a null iterator will have no
    1276     * effect. Each object is appended using {@link #append(Object)}.
    1277     *
    1278     * @param it
    1279     *            the iterator to append
    1280     * @return this, to enable chaining
    1281     */
    1282    public TextStringBuilder appendAll(final Iterator<?> it) {
    1283        if (it != null) {
    1284            while (it.hasNext()) {
    1285                append(it.next());
    1286            }
    1287        }
    1288        return this;
    1289    }
    1290
    1291    // -----------------------------------------------------------------------
    1292    /**
    1293     * Appends an array placing separators between each value, but not before the first or after the last. Appending a
    1294     * null array will have no effect. Each object is appended using {@link #append(Object)}.
    1295     *
    1296     * @param array
    1297     *            the array to append
    1298     * @param separator
    1299     *            the separator to use, null means no separator
    1300     * @return this, to enable chaining
    1301     */
    1302    public TextStringBuilder appendWithSeparators(final Object[] array, final String separator) {
    1303        if (array != null && array.length > 0) {
    1304            final String sep = Objects.toString(separator, "");
    1305            append(array[0]);
    1306            for (int i = 1; i < array.length; i++) {
    1307                append(sep);
    1308                append(array[i]);
    1309            }
    1310        }
    1311        return this;
    1312    }
    1313
    1314    /**
    1315     * Appends an iterable placing separators between each value, but not before the first or after the last. Appending
    1316     * a null iterable will have no effect. Each object is appended using {@link #append(Object)}.
    1317     *
    1318     * @param iterable
    1319     *            the iterable to append
    1320     * @param separator
    1321     *            the separator to use, null means no separator
    1322     * @return this, to enable chaining
    1323     */
    1324    public TextStringBuilder appendWithSeparators(final Iterable<?> iterable, final String separator) {
    1325        if (iterable != null) {
    1326            final String sep = Objects.toString(separator, "");
    1327            final Iterator<?> it = iterable.iterator();
    1328            while (it.hasNext()) {
    1329                append(it.next());
    1330                if (it.hasNext()) {
    1331                    append(sep);
    1332                }
    1333            }
    1334        }
    1335        return this;
    1336    }
    1337
    1338    /**
    1339     * Appends an iterator placing separators between each value, but not before the first or after the last. Appending
    1340     * a null iterator will have no effect. Each object is appended using {@link #append(Object)}.
    1341     *
    1342     * @param it
    1343     *            the iterator to append
    1344     * @param separator
    1345     *            the separator to use, null means no separator
    1346     * @return this, to enable chaining
    1347     */
    1348    public TextStringBuilder appendWithSeparators(final Iterator<?> it, final String separator) {
    1349        if (it != null) {
    1350            final String sep = Objects.toString(separator, "");
    1351            while (it.hasNext()) {
    1352                append(it.next());
    1353                if (it.hasNext()) {
    1354                    append(sep);
    1355                }
    1356            }
    1357        }
    1358        return this;
    1359    }
    1360
    1361    // -----------------------------------------------------------------------
    1362    /**
    1363     * Appends a separator if the builder is currently non-empty. Appending a null separator will have no effect. The
    1364     * separator is appended using {@link #append(String)}.
    1365     * <p>
    1366     * This method is useful for adding a separator each time around the loop except the first.
    1367     *
    1368     * <pre>
    1369     * for (Iterator it = list.iterator(); it.hasNext();) {
    1370     *     appendSeparator(",");
    1371     *     append(it.next());
    1372     * }
    1373     * </pre>
    1374     *
    1375     * Note that for this simple example, you should use {@link #appendWithSeparators(Iterable, String)}.
    1376     *
    1377     * @param separator
    1378     *            the separator to use, null means no separator
    1379     * @return this, to enable chaining
    1380     */
    1381    public TextStringBuilder appendSeparator(final String separator) {
    1382        return appendSeparator(separator, null);
    1383    }
    1384
    1385    /**
    1386     * Appends one of both separators to the StrBuilder. If the builder is currently empty it will append the
    1387     * defaultIfEmpty-separator Otherwise it will append the standard-separator
    1388     *
    1389     * Appending a null separator will have no effect. The separator is appended using {@link #append(String)}.
    1390     * <p>
    1391     * This method is for example useful for constructing queries
    1392     *
    1393     * <pre>
    1394     * StrBuilder whereClause = new StrBuilder();
    1395     * if(searchCommand.getPriority() != null) {
    1396     *  whereClause.appendSeparator(" and", " where");
    1397     *  whereClause.append(" priority = ?")
    1398     * }
    1399     * if(searchCommand.getComponent() != null) {
    1400     *  whereClause.appendSeparator(" and", " where");
    1401     *  whereClause.append(" component = ?")
    1402     * }
    1403     * selectClause.append(whereClause)
    1404     * </pre>
    1405     *
    1406     * @param standard
    1407     *            the separator if builder is not empty, null means no separator
    1408     * @param defaultIfEmpty
    1409     *            the separator if builder is empty, null means no separator
    1410     * @return this, to enable chaining
    1411     */
    1412    public TextStringBuilder appendSeparator(final String standard, final String defaultIfEmpty) {
    1413        final String str = isEmpty() ? defaultIfEmpty : standard;
    1414        if (str != null) {
    1415            append(str);
    1416        }
    1417        return this;
    1418    }
    1419
    1420    /**
    1421     * Appends a separator if the builder is currently non-empty. The separator is appended using {@link #append(char)}.
    1422     * <p>
    1423     * This method is useful for adding a separator each time around the loop except the first.
    1424     *
    1425     * <pre>
    1426     * for (Iterator it = list.iterator(); it.hasNext();) {
    1427     *     appendSeparator(',');
    1428     *     append(it.next());
    1429     * }
    1430     * </pre>
    1431     *
    1432     * Note that for this simple example, you should use {@link #appendWithSeparators(Iterable, String)}.
    1433     *
    1434     * @param separator
    1435     *            the separator to use
    1436     * @return this, to enable chaining
    1437     */
    1438    public TextStringBuilder appendSeparator(final char separator) {
    1439        if (size() > 0) {
    1440            append(separator);
    1441        }
    1442        return this;
    1443    }
    1444
    1445    /**
    1446     * Append one of both separators to the builder If the builder is currently empty it will append the
    1447     * defaultIfEmpty-separator Otherwise it will append the standard-separator
    1448     *
    1449     * The separator is appended using {@link #append(char)}.
    1450     *
    1451     * @param standard
    1452     *            the separator if builder is not empty
    1453     * @param defaultIfEmpty
    1454     *            the separator if builder is empty
    1455     * @return this, to enable chaining
    1456     */
    1457    public TextStringBuilder appendSeparator(final char standard, final char defaultIfEmpty) {
    1458        if (size() > 0) {
    1459            append(standard);
    1460        } else {
    1461            append(defaultIfEmpty);
    1462        }
    1463        return this;
    1464    }
    1465
    1466    /**
    1467     * Appends a separator to the builder if the loop index is greater than zero. Appending a null separator will have
    1468     * no effect. The separator is appended using {@link #append(String)}.
    1469     * <p>
    1470     * This method is useful for adding a separator each time around the loop except the first.
    1471     * </p>
    1472     *
    1473     * <pre>
    1474     * for (int i = 0; i &lt; list.size(); i++) {
    1475     *     appendSeparator(",", i);
    1476     *     append(list.get(i));
    1477     * }
    1478     * </pre>
    1479     *
    1480     * Note that for this simple example, you should use {@link #appendWithSeparators(Iterable, String)}.
    1481     *
    1482     * @param separator
    1483     *            the separator to use, null means no separator
    1484     * @param loopIndex
    1485     *            the loop index
    1486     * @return this, to enable chaining
    1487     */
    1488    public TextStringBuilder appendSeparator(final String separator, final int loopIndex) {
    1489        if (separator != null && loopIndex > 0) {
    1490            append(separator);
    1491        }
    1492        return this;
    1493    }
    1494
    1495    /**
    1496     * Appends a separator to the builder if the loop index is greater than zero. The separator is appended using
    1497     * {@link #append(char)}.
    1498     * <p>
    1499     * This method is useful for adding a separator each time around the loop except the first.
    1500     * </p>
    1501     *
    1502     * <pre>
    1503     * for (int i = 0; i &lt; list.size(); i++) {
    1504     *     appendSeparator(",", i);
    1505     *     append(list.get(i));
    1506     * }
    1507     * </pre>
    1508     *
    1509     * Note that for this simple example, you should use {@link #appendWithSeparators(Iterable, String)}.
    1510     *
    1511     * @param separator
    1512     *            the separator to use
    1513     * @param loopIndex
    1514     *            the loop index
    1515     * @return this, to enable chaining
    1516     */
    1517    public TextStringBuilder appendSeparator(final char separator, final int loopIndex) {
    1518        if (loopIndex > 0) {
    1519            append(separator);
    1520        }
    1521        return this;
    1522    }
    1523
    1524    // -----------------------------------------------------------------------
    1525    /**
    1526     * Appends the pad character to the builder the specified number of times.
    1527     *
    1528     * @param length
    1529     *            the length to append, negative means no append
    1530     * @param padChar
    1531     *            the character to append
    1532     * @return this, to enable chaining
    1533     */
    1534    public TextStringBuilder appendPadding(final int length, final char padChar) {
    1535        if (length >= 0) {
    1536            ensureCapacity(size + length);
    1537            for (int i = 0; i < length; i++) {
    1538                buffer[size++] = padChar;
    1539            }
    1540        }
    1541        return this;
    1542    }
    1543
    1544    // -----------------------------------------------------------------------
    1545    /**
    1546     * Appends an object to the builder padding on the left to a fixed width. The <code>toString</code> of the object is
    1547     * used. If the object is larger than the length, the left hand side is lost. If the object is null, the null text
    1548     * value is used.
    1549     *
    1550     * @param obj
    1551     *            the object to append, null uses null text
    1552     * @param width
    1553     *            the fixed field width, zero or negative has no effect
    1554     * @param padChar
    1555     *            the pad character to use
    1556     * @return this, to enable chaining
    1557     */
    1558    public TextStringBuilder appendFixedWidthPadLeft(final Object obj, final int width, final char padChar) {
    1559        if (width > 0) {
    1560            ensureCapacity(size + width);
    1561            String str = obj == null ? getNullText() : obj.toString();
    1562            if (str == null) {
    1563                str = "";
    1564            }
    1565            final int strLen = str.length();
    1566            if (strLen >= width) {
    1567                str.getChars(strLen - width, strLen, buffer, size);
    1568            } else {
    1569                final int padLen = width - strLen;
    1570                for (int i = 0; i < padLen; i++) {
    1571                    buffer[size + i] = padChar;
    1572                }
    1573                str.getChars(0, strLen, buffer, size + padLen);
    1574            }
    1575            size += width;
    1576        }
    1577        return this;
    1578    }
    1579
    1580    /**
    1581     * Appends an object to the builder padding on the left to a fixed width. The <code>String.valueOf</code> of the
    1582     * <code>int</code> value is used. If the formatted value is larger than the length, the left hand side is lost.
    1583     *
    1584     * @param value
    1585     *            the value to append
    1586     * @param width
    1587     *            the fixed field width, zero or negative has no effect
    1588     * @param padChar
    1589     *            the pad character to use
    1590     * @return this, to enable chaining
    1591     */
    1592    public TextStringBuilder appendFixedWidthPadLeft(final int value, final int width, final char padChar) {
    1593        return appendFixedWidthPadLeft(String.valueOf(value), width, padChar);
    1594    }
    1595
    1596    /**
    1597     * Appends an object to the builder padding on the right to a fixed length. The <code>toString</code> of the object
    1598     * is used. If the object is larger than the length, the right hand side is lost. If the object is null, null text
    1599     * value is used.
    1600     *
    1601     * @param obj
    1602     *            the object to append, null uses null text
    1603     * @param width
    1604     *            the fixed field width, zero or negative has no effect
    1605     * @param padChar
    1606     *            the pad character to use
    1607     * @return this, to enable chaining
    1608     */
    1609    public TextStringBuilder appendFixedWidthPadRight(final Object obj, final int width, final char padChar) {
    1610        if (width > 0) {
    1611            ensureCapacity(size + width);
    1612            String str = obj == null ? getNullText() : obj.toString();
    1613            if (str == null) {
    1614                str = "";
    1615            }
    1616            final int strLen = str.length();
    1617            if (strLen >= width) {
    1618                str.getChars(0, width, buffer, size);
    1619            } else {
    1620                final int padLen = width - strLen;
    1621                str.getChars(0, strLen, buffer, size);
    1622                for (int i = 0; i < padLen; i++) {
    1623                    buffer[size + strLen + i] = padChar;
    1624                }
    1625            }
    1626            size += width;
    1627        }
    1628        return this;
    1629    }
    1630
    1631    /**
    1632     * Appends an object to the builder padding on the right to a fixed length. The <code>String.valueOf</code> of the
    1633     * <code>int</code> value is used. If the object is larger than the length, the right hand side is lost.
    1634     *
    1635     * @param value
    1636     *            the value to append
    1637     * @param width
    1638     *            the fixed field width, zero or negative has no effect
    1639     * @param padChar
    1640     *            the pad character to use
    1641     * @return this, to enable chaining
    1642     */
    1643    public TextStringBuilder appendFixedWidthPadRight(final int value, final int width, final char padChar) {
    1644        return appendFixedWidthPadRight(String.valueOf(value), width, padChar);
    1645    }
    1646
    1647    // -----------------------------------------------------------------------
    1648    /**
    1649     * Inserts the string representation of an object into this builder. Inserting null will use the stored null text
    1650     * value.
    1651     *
    1652     * @param index
    1653     *            the index to add at, must be valid
    1654     * @param obj
    1655     *            the object to insert
    1656     * @return this, to enable chaining
    1657     * @throws IndexOutOfBoundsException
    1658     *             if the index is invalid
    1659     */
    1660    public TextStringBuilder insert(final int index, final Object obj) {
    1661        if (obj == null) {
    1662            return insert(index, nullText);
    1663        }
    1664        return insert(index, obj.toString());
    1665    }
    1666
    1667    /**
    1668     * Inserts the string into this builder. Inserting null will use the stored null text value.
    1669     *
    1670     * @param index
    1671     *            the index to add at, must be valid
    1672     * @param str
    1673     *            the string to insert
    1674     * @return this, to enable chaining
    1675     * @throws IndexOutOfBoundsException
    1676     *             if the index is invalid
    1677     */
    1678    public TextStringBuilder insert(final int index, String str) {
    1679        validateIndex(index);
    1680        if (str == null) {
    1681            str = nullText;
    1682        }
    1683        if (str != null) {
    1684            final int strLen = str.length();
    1685            if (strLen > 0) {
    1686                final int newSize = size + strLen;
    1687                ensureCapacity(newSize);
    1688                System.arraycopy(buffer, index, buffer, index + strLen, size - index);
    1689                size = newSize;
    1690                str.getChars(0, strLen, buffer, index);
    1691            }
    1692        }
    1693        return this;
    1694    }
    1695
    1696    /**
    1697     * Inserts the character array into this builder. Inserting null will use the stored null text value.
    1698     *
    1699     * @param index
    1700     *            the index to add at, must be valid
    1701     * @param chars
    1702     *            the char array to insert
    1703     * @return this, to enable chaining
    1704     * @throws IndexOutOfBoundsException
    1705     *             if the index is invalid
    1706     */
    1707    public TextStringBuilder insert(final int index, final char[] chars) {
    1708        validateIndex(index);
    1709        if (chars == null) {
    1710            return insert(index, nullText);
    1711        }
    1712        final int len = chars.length;
    1713        if (len > 0) {
    1714            ensureCapacity(size + len);
    1715            System.arraycopy(buffer, index, buffer, index + len, size - index);
    1716            System.arraycopy(chars, 0, buffer, index, len);
    1717            size += len;
    1718        }
    1719        return this;
    1720    }
    1721
    1722    /**
    1723     * Inserts part of the character array into this builder. Inserting null will use the stored null text value.
    1724     *
    1725     * @param index
    1726     *            the index to add at, must be valid
    1727     * @param chars
    1728     *            the char array to insert
    1729     * @param offset
    1730     *            the offset into the character array to start at, must be valid
    1731     * @param length
    1732     *            the length of the character array part to copy, must be positive
    1733     * @return this, to enable chaining
    1734     * @throws IndexOutOfBoundsException
    1735     *             if any index is invalid
    1736     */
    1737    public TextStringBuilder insert(final int index, final char[] chars, final int offset, final int length) {
    1738        validateIndex(index);
    1739        if (chars == null) {
    1740            return insert(index, nullText);
    1741        }
    1742        if (offset < 0 || offset > chars.length) {
    1743            throw new StringIndexOutOfBoundsException("Invalid offset: " + offset);
    1744        }
    1745        if (length < 0 || offset + length > chars.length) {
    1746            throw new StringIndexOutOfBoundsException("Invalid length: " + length);
    1747        }
    1748        if (length > 0) {
    1749            ensureCapacity(size + length);
    1750            System.arraycopy(buffer, index, buffer, index + length, size - index);
    1751            System.arraycopy(chars, offset, buffer, index, length);
    1752            size += length;
    1753        }
    1754        return this;
    1755    }
    1756
    1757    /**
    1758     * Inserts the value into this builder.
    1759     *
    1760     * @param index
    1761     *            the index to add at, must be valid
    1762     * @param value
    1763     *            the value to insert
    1764     * @return this, to enable chaining
    1765     * @throws IndexOutOfBoundsException
    1766     *             if the index is invalid
    1767     */
    1768    public TextStringBuilder insert(int index, final boolean value) {
    1769        validateIndex(index);
    1770        if (value) {
    1771            ensureCapacity(size + TRUE_STRING_SIZE);
    1772            System.arraycopy(buffer, index, buffer, index + TRUE_STRING_SIZE, size - index);
    1773            buffer[index++] = 't';
    1774            buffer[index++] = 'r';
    1775            buffer[index++] = 'u';
    1776            buffer[index] = 'e';
    1777            size += TRUE_STRING_SIZE;
    1778        } else {
    1779            ensureCapacity(size + FALSE_STRING_SIZE);
    1780            System.arraycopy(buffer, index, buffer, index + FALSE_STRING_SIZE, size - index);
    1781            buffer[index++] = 'f';
    1782            buffer[index++] = 'a';
    1783            buffer[index++] = 'l';
    1784            buffer[index++] = 's';
    1785            buffer[index] = 'e';
    1786            size += FALSE_STRING_SIZE;
    1787        }
    1788        return this;
    1789    }
    1790
    1791    /**
    1792     * Inserts the value into this builder.
    1793     *
    1794     * @param index
    1795     *            the index to add at, must be valid
    1796     * @param value
    1797     *            the value to insert
    1798     * @return this, to enable chaining
    1799     * @throws IndexOutOfBoundsException
    1800     *             if the index is invalid
    1801     */
    1802    public TextStringBuilder insert(final int index, final char value) {
    1803        validateIndex(index);
    1804        ensureCapacity(size + 1);
    1805        System.arraycopy(buffer, index, buffer, index + 1, size - index);
    1806        buffer[index] = value;
    1807        size++;
    1808        return this;
    1809    }
    1810
    1811    /**
    1812     * Inserts the value into this builder.
    1813     *
    1814     * @param index
    1815     *            the index to add at, must be valid
    1816     * @param value
    1817     *            the value to insert
    1818     * @return this, to enable chaining
    1819     * @throws IndexOutOfBoundsException
    1820     *             if the index is invalid
    1821     */
    1822    public TextStringBuilder insert(final int index, final int value) {
    1823        return insert(index, String.valueOf(value));
    1824    }
    1825
    1826    /**
    1827     * Inserts the value into this builder.
    1828     *
    1829     * @param index
    1830     *            the index to add at, must be valid
    1831     * @param value
    1832     *            the value to insert
    1833     * @return this, to enable chaining
    1834     * @throws IndexOutOfBoundsException
    1835     *             if the index is invalid
    1836     */
    1837    public TextStringBuilder insert(final int index, final long value) {
    1838        return insert(index, String.valueOf(value));
    1839    }
    1840
    1841    /**
    1842     * Inserts the value into this builder.
    1843     *
    1844     * @param index
    1845     *            the index to add at, must be valid
    1846     * @param value
    1847     *            the value to insert
    1848     * @return this, to enable chaining
    1849     * @throws IndexOutOfBoundsException
    1850     *             if the index is invalid
    1851     */
    1852    public TextStringBuilder insert(final int index, final float value) {
    1853        return insert(index, String.valueOf(value));
    1854    }
    1855
    1856    /**
    1857     * Inserts the value into this builder.
    1858     *
    1859     * @param index
    1860     *            the index to add at, must be valid
    1861     * @param value
    1862     *            the value to insert
    1863     * @return this, to enable chaining
    1864     * @throws IndexOutOfBoundsException
    1865     *             if the index is invalid
    1866     */
    1867    public TextStringBuilder insert(final int index, final double value) {
    1868        return insert(index, String.valueOf(value));
    1869    }
    1870
    1871    // -----------------------------------------------------------------------
    1872    /**
    1873     * Internal method to delete a range without validation.
    1874     *
    1875     * @param startIndex
    1876     *            the start index, must be valid
    1877     * @param endIndex
    1878     *            the end index (exclusive), must be valid
    1879     * @param len
    1880     *            the length, must be valid
    1881     * @throws IndexOutOfBoundsException
    1882     *             if any index is invalid
    1883     */
    1884    private void deleteImpl(final int startIndex, final int endIndex, final int len) {
    1885        System.arraycopy(buffer, endIndex, buffer, startIndex, size - endIndex);
    1886        size -= len;
    1887    }
    1888
    1889    /**
    1890     * Deletes the characters between the two specified indices.
    1891     *
    1892     * @param startIndex
    1893     *            the start index, inclusive, must be valid
    1894     * @param endIndex
    1895     *            the end index, exclusive, must be valid except that if too large it is treated as end of string
    1896     * @return this, to enable chaining
    1897     * @throws IndexOutOfBoundsException
    1898     *             if the index is invalid
    1899     */
    1900    public TextStringBuilder delete(final int startIndex, int endIndex) {
    1901        endIndex = validateRange(startIndex, endIndex);
    1902        final int len = endIndex - startIndex;
    1903        if (len > 0) {
    1904            deleteImpl(startIndex, endIndex, len);
    1905        }
    1906        return this;
    1907    }
    1908
    1909    // -----------------------------------------------------------------------
    1910    /**
    1911     * Deletes the character wherever it occurs in the builder.
    1912     *
    1913     * @param ch
    1914     *            the character to delete
    1915     * @return this, to enable chaining
    1916     */
    1917    public TextStringBuilder deleteAll(final char ch) {
    1918        for (int i = 0; i < size; i++) {
    1919            if (buffer[i] == ch) {
    1920                final int start = i;
    1921                while (++i < size) {
    1922                    if (buffer[i] != ch) {
    1923                        break;
    1924                    }
    1925                }
    1926                final int len = i - start;
    1927                deleteImpl(start, i, len);
    1928                i -= len;
    1929            }
    1930        }
    1931        return this;
    1932    }
    1933
    1934    /**
    1935     * Deletes the character wherever it occurs in the builder.
    1936     *
    1937     * @param ch
    1938     *            the character to delete
    1939     * @return this, to enable chaining
    1940     */
    1941    public TextStringBuilder deleteFirst(final char ch) {
    1942        for (int i = 0; i < size; i++) {
    1943            if (buffer[i] == ch) {
    1944                deleteImpl(i, i + 1, 1);
    1945                break;
    1946            }
    1947        }
    1948        return this;
    1949    }
    1950
    1951    // -----------------------------------------------------------------------
    1952    /**
    1953     * Deletes the string wherever it occurs in the builder.
    1954     *
    1955     * @param str
    1956     *            the string to delete, null causes no action
    1957     * @return this, to enable chaining
    1958     */
    1959    public TextStringBuilder deleteAll(final String str) {
    1960        final int len = str == null ? 0 : str.length();
    1961        if (len > 0) {
    1962            int index = indexOf(str, 0);
    1963            while (index >= 0) {
    1964                deleteImpl(index, index + len, len);
    1965                index = indexOf(str, index);
    1966            }
    1967        }
    1968        return this;
    1969    }
    1970
    1971    /**
    1972     * Deletes the string wherever it occurs in the builder.
    1973     *
    1974     * @param str
    1975     *            the string to delete, null causes no action
    1976     * @return this, to enable chaining
    1977     */
    1978    public TextStringBuilder deleteFirst(final String str) {
    1979        final int len = str == null ? 0 : str.length();
    1980        if (len > 0) {
    1981            final int index = indexOf(str, 0);
    1982            if (index >= 0) {
    1983                deleteImpl(index, index + len, len);
    1984            }
    1985        }
    1986        return this;
    1987    }
    1988
    1989    // -----------------------------------------------------------------------
    1990    /**
    1991     * Deletes all parts of the builder that the matcher matches.
    1992     * <p>
    1993     * Matchers can be used to perform advanced deletion behaviour. For example you could write a matcher to delete all
    1994     * occurrences where the character 'a' is followed by a number.
    1995     *
    1996     * @param matcher
    1997     *            the matcher to use to find the deletion, null causes no action
    1998     * @return this, to enable chaining
    1999     */
    2000    public TextStringBuilder deleteAll(final StringMatcher matcher) {
    2001        return replace(matcher, null, 0, size, -1);
    2002    }
    2003
    2004    /**
    2005     * Deletes the first match within the builder using the specified matcher.
    2006     * <p>
    2007     * Matchers can be used to perform advanced deletion behaviour. For example you could write a matcher to delete
    2008     * where the character 'a' is followed by a number.
    2009     *
    2010     * @param matcher
    2011     *            the matcher to use to find the deletion, null causes no action
    2012     * @return this, to enable chaining
    2013     */
    2014    public TextStringBuilder deleteFirst(final StringMatcher matcher) {
    2015        return replace(matcher, null, 0, size, 1);
    2016    }
    2017
    2018    // -----------------------------------------------------------------------
    2019    /**
    2020     * Internal method to delete a range without validation.
    2021     *
    2022     * @param startIndex
    2023     *            the start index, must be valid
    2024     * @param endIndex
    2025     *            the end index (exclusive), must be valid
    2026     * @param removeLen
    2027     *            the length to remove (endIndex - startIndex), must be valid
    2028     * @param insertStr
    2029     *            the string to replace with, null means delete range
    2030     * @param insertLen
    2031     *            the length of the insert string, must be valid
    2032     * @throws IndexOutOfBoundsException
    2033     *             if any index is invalid
    2034     */
    2035    private void replaceImpl(final int startIndex, final int endIndex, final int removeLen, final String insertStr,
    2036            final int insertLen) {
    2037        final int newSize = size - removeLen + insertLen;
    2038        if (insertLen != removeLen) {
    2039            ensureCapacity(newSize);
    2040            System.arraycopy(buffer, endIndex, buffer, startIndex + insertLen, size - endIndex);
    2041            size = newSize;
    2042        }
    2043        if (insertLen > 0) {
    2044            insertStr.getChars(0, insertLen, buffer, startIndex);
    2045        }
    2046    }
    2047
    2048    /**
    2049     * Replaces a portion of the string builder with another string. The length of the inserted string does not have to
    2050     * match the removed length.
    2051     *
    2052     * @param startIndex
    2053     *            the start index, inclusive, must be valid
    2054     * @param endIndex
    2055     *            the end index, exclusive, must be valid except that if too large it is treated as end of string
    2056     * @param replaceStr
    2057     *            the string to replace with, null means delete range
    2058     * @return this, to enable chaining
    2059     * @throws IndexOutOfBoundsException
    2060     *             if the index is invalid
    2061     */
    2062    public TextStringBuilder replace(final int startIndex, int endIndex, final String replaceStr) {
    2063        endIndex = validateRange(startIndex, endIndex);
    2064        final int insertLen = replaceStr == null ? 0 : replaceStr.length();
    2065        replaceImpl(startIndex, endIndex, endIndex - startIndex, replaceStr, insertLen);
    2066        return this;
    2067    }
    2068
    2069    // -----------------------------------------------------------------------
    2070    /**
    2071     * Replaces the search character with the replace character throughout the builder.
    2072     *
    2073     * @param search
    2074     *            the search character
    2075     * @param replace
    2076     *            the replace character
    2077     * @return this, to enable chaining
    2078     */
    2079    public TextStringBuilder replaceAll(final char search, final char replace) {
    2080        if (search != replace) {
    2081            for (int i = 0; i < size; i++) {
    2082                if (buffer[i] == search) {
    2083                    buffer[i] = replace;
    2084                }
    2085            }
    2086        }
    2087        return this;
    2088    }
    2089
    2090    /**
    2091     * Replaces the first instance of the search character with the replace character in the builder.
    2092     *
    2093     * @param search
    2094     *            the search character
    2095     * @param replace
    2096     *            the replace character
    2097     * @return this, to enable chaining
    2098     */
    2099    public TextStringBuilder replaceFirst(final char search, final char replace) {
    2100        if (search != replace) {
    2101            for (int i = 0; i < size; i++) {
    2102                if (buffer[i] == search) {
    2103                    buffer[i] = replace;
    2104                    break;
    2105                }
    2106            }
    2107        }
    2108        return this;
    2109    }
    2110
    2111    // -----------------------------------------------------------------------
    2112    /**
    2113     * Replaces the search string with the replace string throughout the builder.
    2114     *
    2115     * @param searchStr
    2116     *            the search string, null causes no action to occur
    2117     * @param replaceStr
    2118     *            the replace string, null is equivalent to an empty string
    2119     * @return this, to enable chaining
    2120     */
    2121    public TextStringBuilder replaceAll(final String searchStr, final String replaceStr) {
    2122        final int searchLen = searchStr == null ? 0 : searchStr.length();
    2123        if (searchLen > 0) {
    2124            final int replaceLen = replaceStr == null ? 0 : replaceStr.length();
    2125            int index = indexOf(searchStr, 0);
    2126            while (index >= 0) {
    2127                replaceImpl(index, index + searchLen, searchLen, replaceStr, replaceLen);
    2128                index = indexOf(searchStr, index + replaceLen);
    2129            }
    2130        }
    2131        return this;
    2132    }
    2133
    2134    /**
    2135     * Replaces the first instance of the search string with the replace string.
    2136     *
    2137     * @param searchStr
    2138     *            the search string, null causes no action to occur
    2139     * @param replaceStr
    2140     *            the replace string, null is equivalent to an empty string
    2141     * @return this, to enable chaining
    2142     */
    2143    public TextStringBuilder replaceFirst(final String searchStr, final String replaceStr) {
    2144        final int searchLen = searchStr == null ? 0 : searchStr.length();
    2145        if (searchLen > 0) {
    2146            final int index = indexOf(searchStr, 0);
    2147            if (index >= 0) {
    2148                final int replaceLen = replaceStr == null ? 0 : replaceStr.length();
    2149                replaceImpl(index, index + searchLen, searchLen, replaceStr, replaceLen);
    2150            }
    2151        }
    2152        return this;
    2153    }
    2154
    2155    // -----------------------------------------------------------------------
    2156    /**
    2157     * Replaces all matches within the builder with the replace string.
    2158     * <p>
    2159     * Matchers can be used to perform advanced replace behaviour. For example you could write a matcher to replace all
    2160     * occurrences where the character 'a' is followed by a number.
    2161     *
    2162     * @param matcher
    2163     *            the matcher to use to find the deletion, null causes no action
    2164     * @param replaceStr
    2165     *            the replace string, null is equivalent to an empty string
    2166     * @return this, to enable chaining
    2167     */
    2168    public TextStringBuilder replaceAll(final StringMatcher matcher, final String replaceStr) {
    2169        return replace(matcher, replaceStr, 0, size, -1);
    2170    }
    2171
    2172    /**
    2173     * Replaces the first match within the builder with the replace string.
    2174     * <p>
    2175     * Matchers can be used to perform advanced replace behaviour. For example you could write a matcher to replace
    2176     * where the character 'a' is followed by a number.
    2177     *
    2178     * @param matcher
    2179     *            the matcher to use to find the deletion, null causes no action
    2180     * @param replaceStr
    2181     *            the replace string, null is equivalent to an empty string
    2182     * @return this, to enable chaining
    2183     */
    2184    public TextStringBuilder replaceFirst(final StringMatcher matcher, final String replaceStr) {
    2185        return replace(matcher, replaceStr, 0, size, 1);
    2186    }
    2187
    2188    // -----------------------------------------------------------------------
    2189    /**
    2190     * Advanced search and replaces within the builder using a matcher.
    2191     * <p>
    2192     * Matchers can be used to perform advanced behaviour. For example you could write a matcher to delete all
    2193     * occurrences where the character 'a' is followed by a number.
    2194     *
    2195     * @param matcher
    2196     *            the matcher to use to find the deletion, null causes no action
    2197     * @param replaceStr
    2198     *            the string to replace the match with, null is a delete
    2199     * @param startIndex
    2200     *            the start index, inclusive, must be valid
    2201     * @param endIndex
    2202     *            the end index, exclusive, must be valid except that if too large it is treated as end of string
    2203     * @param replaceCount
    2204     *            the number of times to replace, -1 for replace all
    2205     * @return this, to enable chaining
    2206     * @throws IndexOutOfBoundsException
    2207     *             if start index is invalid
    2208     */
    2209    public TextStringBuilder replace(final StringMatcher matcher, final String replaceStr, final int startIndex,
    2210            int endIndex, final int replaceCount) {
    2211        endIndex = validateRange(startIndex, endIndex);
    2212        return replaceImpl(matcher, replaceStr, startIndex, endIndex, replaceCount);
    2213    }
    2214
    2215    /**
    2216     * Replaces within the builder using a matcher.
    2217     * <p>
    2218     * Matchers can be used to perform advanced behaviour. For example you could write a matcher to delete all
    2219     * occurrences where the character 'a' is followed by a number.
    2220     *
    2221     * @param matcher
    2222     *            the matcher to use to find the deletion, null causes no action
    2223     * @param replaceStr
    2224     *            the string to replace the match with, null is a delete
    2225     * @param from
    2226     *            the start index, must be valid
    2227     * @param to
    2228     *            the end index (exclusive), must be valid
    2229     * @param replaceCount
    2230     *            the number of times to replace, -1 for replace all
    2231     * @return this, to enable chaining
    2232     * @throws IndexOutOfBoundsException
    2233     *             if any index is invalid
    2234     */
    2235    private TextStringBuilder replaceImpl(final StringMatcher matcher, final String replaceStr, final int from, int to,
    2236            int replaceCount) {
    2237        if (matcher == null || size == 0) {
    2238            return this;
    2239        }
    2240        final int replaceLen = replaceStr == null ? 0 : replaceStr.length();
    2241        for (int i = from; i < to && replaceCount != 0; i++) {
    2242            final char[] buf = buffer;
    2243            final int removeLen = matcher.isMatch(buf, i, from, to);
    2244            if (removeLen > 0) {
    2245                replaceImpl(i, i + removeLen, removeLen, replaceStr, replaceLen);
    2246                to = to - removeLen + replaceLen;
    2247                i = i + replaceLen - 1;
    2248                if (replaceCount > 0) {
    2249                    replaceCount--;
    2250                }
    2251            }
    2252        }
    2253        return this;
    2254    }
    2255
    2256    // -----------------------------------------------------------------------
    2257    /**
    2258     * Reverses the string builder placing each character in the opposite index.
    2259     *
    2260     * @return this, to enable chaining
    2261     */
    2262    public TextStringBuilder reverse() {
    2263        if (size == 0) {
    2264            return this;
    2265        }
    2266
    2267        final int half = size / 2;
    2268        final char[] buf = buffer;
    2269        for (int leftIdx = 0, rightIdx = size - 1; leftIdx < half; leftIdx++, rightIdx--) {
    2270            final char swap = buf[leftIdx];
    2271            buf[leftIdx] = buf[rightIdx];
    2272            buf[rightIdx] = swap;
    2273        }
    2274        return this;
    2275    }
    2276
    2277    // -----------------------------------------------------------------------
    2278    /**
    2279     * Trims the builder by removing characters less than or equal to a space from the beginning and end.
    2280     *
    2281     * @return this, to enable chaining
    2282     */
    2283    public TextStringBuilder trim() {
    2284        if (size == 0) {
    2285            return this;
    2286        }
    2287        int len = size;
    2288        final char[] buf = buffer;
    2289        int pos = 0;
    2290        while (pos < len && buf[pos] <= ' ') {
    2291            pos++;
    2292        }
    2293        while (pos < len && buf[len - 1] <= ' ') {
    2294            len--;
    2295        }
    2296        if (len < size) {
    2297            delete(len, size);
    2298        }
    2299        if (pos > 0) {
    2300            delete(0, pos);
    2301        }
    2302        return this;
    2303    }
    2304
    2305    // -----------------------------------------------------------------------
    2306    /**
    2307     * Checks whether this builder starts with the specified string.
    2308     * <p>
    2309     * Note that this method handles null input quietly, unlike String.
    2310     *
    2311     * @param str
    2312     *            the string to search for, null returns false
    2313     * @return true if the builder starts with the string
    2314     */
    2315    public boolean startsWith(final String str) {
    2316        if (str == null) {
    2317            return false;
    2318        }
    2319        final int len = str.length();
    2320        if (len == 0) {
    2321            return true;
    2322        }
    2323        if (len > size) {
    2324            return false;
    2325        }
    2326        for (int i = 0; i < len; i++) {
    2327            if (buffer[i] != str.charAt(i)) {
    2328                return false;
    2329            }
    2330        }
    2331        return true;
    2332    }
    2333
    2334    /**
    2335     * Checks whether this builder ends with the specified string.
    2336     * <p>
    2337     * Note that this method handles null input quietly, unlike String.
    2338     *
    2339     * @param str
    2340     *            the string to search for, null returns false
    2341     * @return true if the builder ends with the string
    2342     */
    2343    public boolean endsWith(final String str) {
    2344        if (str == null) {
    2345            return false;
    2346        }
    2347        final int len = str.length();
    2348        if (len == 0) {
    2349            return true;
    2350        }
    2351        if (len > size) {
    2352            return false;
    2353        }
    2354        int pos = size - len;
    2355        for (int i = 0; i < len; i++, pos++) {
    2356            if (buffer[pos] != str.charAt(i)) {
    2357                return false;
    2358            }
    2359        }
    2360        return true;
    2361    }
    2362
    2363    // -----------------------------------------------------------------------
    2364    /**
    2365     * {@inheritDoc}
    2366     */
    2367    @Override
    2368    public CharSequence subSequence(final int startIndex, final int endIndex) {
    2369        if (startIndex < 0) {
    2370            throw new StringIndexOutOfBoundsException(startIndex);
    2371        }
    2372        if (endIndex > size) {
    2373            throw new StringIndexOutOfBoundsException(endIndex);
    2374        }
    2375        if (startIndex > endIndex) {
    2376            throw new StringIndexOutOfBoundsException(endIndex - startIndex);
    2377        }
    2378        return substring(startIndex, endIndex);
    2379    }
    2380
    2381    /**
    2382     * Extracts a portion of this string builder as a string.
    2383     *
    2384     * @param start
    2385     *            the start index, inclusive, must be valid
    2386     * @return The new string
    2387     * @throws IndexOutOfBoundsException
    2388     *             if the index is invalid
    2389     */
    2390    public String substring(final int start) {
    2391        return substring(start, size);
    2392    }
    2393
    2394    /**
    2395     * Extracts a portion of this string builder as a string.
    2396     * <p>
    2397     * Note: This method treats an endIndex greater than the length of the builder as equal to the length of the
    2398     * builder, and continues without error, unlike StringBuffer or String.
    2399     *
    2400     * @param startIndex
    2401     *            the start index, inclusive, must be valid
    2402     * @param endIndex
    2403     *            the end index, exclusive, must be valid except that if too large it is treated as end of string
    2404     * @return The new string
    2405     * @throws IndexOutOfBoundsException
    2406     *             if the index is invalid
    2407     */
    2408    public String substring(final int startIndex, int endIndex) {
    2409        endIndex = validateRange(startIndex, endIndex);
    2410        return new String(buffer, startIndex, endIndex - startIndex);
    2411    }
    2412
    2413    /**
    2414     * Extracts the leftmost characters from the string builder without throwing an exception.
    2415     * <p>
    2416     * This method extracts the left <code>length</code> characters from the builder. If this many characters are not
    2417     * available, the whole builder is returned. Thus the returned string may be shorter than the length requested.
    2418     *
    2419     * @param length
    2420     *            the number of characters to extract, negative returns empty string
    2421     * @return The new string
    2422     */
    2423    public String leftString(final int length) {
    2424        if (length <= 0) {
    2425            return "";
    2426        } else if (length >= size) {
    2427            return new String(buffer, 0, size);
    2428        } else {
    2429            return new String(buffer, 0, length);
    2430        }
    2431    }
    2432
    2433    /**
    2434     * Extracts the rightmost characters from the string builder without throwing an exception.
    2435     * <p>
    2436     * This method extracts the right <code>length</code> characters from the builder. If this many characters are not
    2437     * available, the whole builder is returned. Thus the returned string may be shorter than the length requested.
    2438     *
    2439     * @param length
    2440     *            the number of characters to extract, negative returns empty string
    2441     * @return The new string
    2442     */
    2443    public String rightString(final int length) {
    2444        if (length <= 0) {
    2445            return "";
    2446        } else if (length >= size) {
    2447            return new String(buffer, 0, size);
    2448        } else {
    2449            return new String(buffer, size - length, length);
    2450        }
    2451    }
    2452
    2453    /**
    2454     * Extracts some characters from the middle of the string builder without throwing an exception.
    2455     * <p>
    2456     * This method extracts <code>length</code> characters from the builder at the specified index. If the index is
    2457     * negative it is treated as zero. If the index is greater than the builder size, it is treated as the builder size.
    2458     * If the length is negative, the empty string is returned. If insufficient characters are available in the builder,
    2459     * as much as possible is returned. Thus the returned string may be shorter than the length requested.
    2460     *
    2461     * @param index
    2462     *            the index to start at, negative means zero
    2463     * @param length
    2464     *            the number of characters to extract, negative returns empty string
    2465     * @return The new string
    2466     */
    2467    public String midString(int index, final int length) {
    2468        if (index < 0) {
    2469            index = 0;
    2470        }
    2471        if (length <= 0 || index >= size) {
    2472            return "";
    2473        }
    2474        if (size <= index + length) {
    2475            return new String(buffer, index, size - index);
    2476        }
    2477        return new String(buffer, index, length);
    2478    }
    2479
    2480    // -----------------------------------------------------------------------
    2481    /**
    2482     * Checks if the string builder contains the specified char.
    2483     *
    2484     * @param ch
    2485     *            the character to find
    2486     * @return true if the builder contains the character
    2487     */
    2488    public boolean contains(final char ch) {
    2489        final char[] thisBuf = buffer;
    2490        for (int i = 0; i < this.size; i++) {
    2491            if (thisBuf[i] == ch) {
    2492                return true;
    2493            }
    2494        }
    2495        return false;
    2496    }
    2497
    2498    /**
    2499     * Checks if the string builder contains the specified string.
    2500     *
    2501     * @param str
    2502     *            the string to find
    2503     * @return true if the builder contains the string
    2504     */
    2505    public boolean contains(final String str) {
    2506        return indexOf(str, 0) >= 0;
    2507    }
    2508
    2509    /**
    2510     * Checks if the string builder contains a string matched using the specified matcher.
    2511     * <p>
    2512     * Matchers can be used to perform advanced searching behaviour. For example you could write a matcher to search for
    2513     * the character 'a' followed by a number.
    2514     *
    2515     * @param matcher
    2516     *            the matcher to use, null returns -1
    2517     * @return true if the matcher finds a match in the builder
    2518     */
    2519    public boolean contains(final StringMatcher matcher) {
    2520        return indexOf(matcher, 0) >= 0;
    2521    }
    2522
    2523    // -----------------------------------------------------------------------
    2524    /**
    2525     * Searches the string builder to find the first reference to the specified char.
    2526     *
    2527     * @param ch
    2528     *            the character to find
    2529     * @return The first index of the character, or -1 if not found
    2530     */
    2531    public int indexOf(final char ch) {
    2532        return indexOf(ch, 0);
    2533    }
    2534
    2535    /**
    2536     * Searches the string builder to find the first reference to the specified char.
    2537     *
    2538     * @param ch
    2539     *            the character to find
    2540     * @param startIndex
    2541     *            the index to start at, invalid index rounded to edge
    2542     * @return The first index of the character, or -1 if not found
    2543     */
    2544    public int indexOf(final char ch, int startIndex) {
    2545        startIndex = startIndex < 0 ? 0 : startIndex;
    2546        if (startIndex >= size) {
    2547            return -1;
    2548        }
    2549        final char[] thisBuf = buffer;
    2550        for (int i = startIndex; i < size; i++) {
    2551            if (thisBuf[i] == ch) {
    2552                return i;
    2553            }
    2554        }
    2555        return -1;
    2556    }
    2557
    2558    /**
    2559     * Searches the string builder to find the first reference to the specified string.
    2560     * <p>
    2561     * Note that a null input string will return -1, whereas the JDK throws an exception.
    2562     *
    2563     * @param str
    2564     *            the string to find, null returns -1
    2565     * @return The first index of the string, or -1 if not found
    2566     */
    2567    public int indexOf(final String str) {
    2568        return indexOf(str, 0);
    2569    }
    2570
    2571    /**
    2572     * Searches the string builder to find the first reference to the specified string starting searching from the given
    2573     * index.
    2574     * <p>
    2575     * Note that a null input string will return -1, whereas the JDK throws an exception.
    2576     *
    2577     * @param str
    2578     *            the string to find, null returns -1
    2579     * @param startIndex
    2580     *            the index to start at, invalid index rounded to edge
    2581     * @return The first index of the string, or -1 if not found
    2582     */
    2583    public int indexOf(final String str, int startIndex) {
    2584        startIndex = startIndex < 0 ? 0 : startIndex;
    2585        if (str == null || startIndex >= size) {
    2586            return -1;
    2587        }
    2588        final int strLen = str.length();
    2589        if (strLen == 1) {
    2590            return indexOf(str.charAt(0), startIndex);
    2591        }
    2592        if (strLen == 0) {
    2593            return startIndex;
    2594        }
    2595        if (strLen > size) {
    2596            return -1;
    2597        }
    2598        final char[] thisBuf = buffer;
    2599        final int len = size - strLen + 1;
    2600        outer: for (int i = startIndex; i < len; i++) {
    2601            for (int j = 0; j < strLen; j++) {
    2602                if (str.charAt(j) != thisBuf[i + j]) {
    2603                    continue outer;
    2604                }
    2605            }
    2606            return i;
    2607        }
    2608        return -1;
    2609    }
    2610
    2611    /**
    2612     * Searches the string builder using the matcher to find the first match.
    2613     * <p>
    2614     * Matchers can be used to perform advanced searching behaviour. For example you could write a matcher to find the
    2615     * character 'a' followed by a number.
    2616     *
    2617     * @param matcher
    2618     *            the matcher to use, null returns -1
    2619     * @return The first index matched, or -1 if not found
    2620     */
    2621    public int indexOf(final StringMatcher matcher) {
    2622        return indexOf(matcher, 0);
    2623    }
    2624
    2625    /**
    2626     * Searches the string builder using the matcher to find the first match searching from the given index.
    2627     * <p>
    2628     * Matchers can be used to perform advanced searching behaviour. For example you could write a matcher to find the
    2629     * character 'a' followed by a number.
    2630     *
    2631     * @param matcher
    2632     *            the matcher to use, null returns -1
    2633     * @param startIndex
    2634     *            the index to start at, invalid index rounded to edge
    2635     * @return The first index matched, or -1 if not found
    2636     */
    2637    public int indexOf(final StringMatcher matcher, int startIndex) {
    2638        startIndex = startIndex < 0 ? 0 : startIndex;
    2639        if (matcher == null || startIndex >= size) {
    2640            return -1;
    2641        }
    2642        final int len = size;
    2643        final char[] buf = buffer;
    2644        for (int i = startIndex; i < len; i++) {
    2645            if (matcher.isMatch(buf, i, startIndex, len) > 0) {
    2646                return i;
    2647            }
    2648        }
    2649        return -1;
    2650    }
    2651
    2652    // -----------------------------------------------------------------------
    2653    /**
    2654     * Searches the string builder to find the last reference to the specified char.
    2655     *
    2656     * @param ch
    2657     *            the character to find
    2658     * @return The last index of the character, or -1 if not found
    2659     */
    2660    public int lastIndexOf(final char ch) {
    2661        return lastIndexOf(ch, size - 1);
    2662    }
    2663
    2664    /**
    2665     * Searches the string builder to find the last reference to the specified char.
    2666     *
    2667     * @param ch
    2668     *            the character to find
    2669     * @param startIndex
    2670     *            the index to start at, invalid index rounded to edge
    2671     * @return The last index of the character, or -1 if not found
    2672     */
    2673    public int lastIndexOf(final char ch, int startIndex) {
    2674        startIndex = startIndex >= size ? size - 1 : startIndex;
    2675        if (startIndex < 0) {
    2676            return -1;
    2677        }
    2678        for (int i = startIndex; i >= 0; i--) {
    2679            if (buffer[i] == ch) {
    2680                return i;
    2681            }
    2682        }
    2683        return -1;
    2684    }
    2685
    2686    /**
    2687     * Searches the string builder to find the last reference to the specified string.
    2688     * <p>
    2689     * Note that a null input string will return -1, whereas the JDK throws an exception.
    2690     *
    2691     * @param str
    2692     *            the string to find, null returns -1
    2693     * @return The last index of the string, or -1 if not found
    2694     */
    2695    public int lastIndexOf(final String str) {
    2696        return lastIndexOf(str, size - 1);
    2697    }
    2698
    2699    /**
    2700     * Searches the string builder to find the last reference to the specified string starting searching from the given
    2701     * index.
    2702     * <p>
    2703     * Note that a null input string will return -1, whereas the JDK throws an exception.
    2704     *
    2705     * @param str
    2706     *            the string to find, null returns -1
    2707     * @param startIndex
    2708     *            the index to start at, invalid index rounded to edge
    2709     * @return The last index of the string, or -1 if not found
    2710     */
    2711    public int lastIndexOf(final String str, int startIndex) {
    2712        startIndex = startIndex >= size ? size - 1 : startIndex;
    2713        if (str == null || startIndex < 0) {
    2714            return -1;
    2715        }
    2716        final int strLen = str.length();
    2717        if (strLen > 0 && strLen <= size) {
    2718            if (strLen == 1) {
    2719                return lastIndexOf(str.charAt(0), startIndex);
    2720            }
    2721
    2722            outer: for (int i = startIndex - strLen + 1; i >= 0; i--) {
    2723                for (int j = 0; j < strLen; j++) {
    2724                    if (str.charAt(j) != buffer[i + j]) {
    2725                        continue outer;
    2726                    }
    2727                }
    2728                return i;
    2729            }
    2730
    2731        } else if (strLen == 0) {
    2732            return startIndex;
    2733        }
    2734        return -1;
    2735    }
    2736
    2737    /**
    2738     * Searches the string builder using the matcher to find the last match.
    2739     * <p>
    2740     * Matchers can be used to perform advanced searching behaviour. For example you could write a matcher to find the
    2741     * character 'a' followed by a number.
    2742     *
    2743     * @param matcher
    2744     *            the matcher to use, null returns -1
    2745     * @return The last index matched, or -1 if not found
    2746     */
    2747    public int lastIndexOf(final StringMatcher matcher) {
    2748        return lastIndexOf(matcher, size);
    2749    }
    2750
    2751    /**
    2752     * Searches the string builder using the matcher to find the last match searching from the given index.
    2753     * <p>
    2754     * Matchers can be used to perform advanced searching behaviour. For example you could write a matcher to find the
    2755     * character 'a' followed by a number.
    2756     *
    2757     * @param matcher
    2758     *            the matcher to use, null returns -1
    2759     * @param startIndex
    2760     *            the index to start at, invalid index rounded to edge
    2761     * @return The last index matched, or -1 if not found
    2762     */
    2763    public int lastIndexOf(final StringMatcher matcher, int startIndex) {
    2764        startIndex = startIndex >= size ? size - 1 : startIndex;
    2765        if (matcher == null || startIndex < 0) {
    2766            return -1;
    2767        }
    2768        final char[] buf = buffer;
    2769        final int endIndex = startIndex + 1;
    2770        for (int i = startIndex; i >= 0; i--) {
    2771            if (matcher.isMatch(buf, i, 0, endIndex) > 0) {
    2772                return i;
    2773            }
    2774        }
    2775        return -1;
    2776    }
    2777
    2778    // -----------------------------------------------------------------------
    2779    /**
    2780     * Creates a tokenizer that can tokenize the contents of this builder.
    2781     * <p>
    2782     * This method allows the contents of this builder to be tokenized. The tokenizer will be setup by default to
    2783     * tokenize on space, tab, newline and form feed (as per StringTokenizer). These values can be changed on the
    2784     * tokenizer class, before retrieving the tokens.
    2785     * <p>
    2786     * The returned tokenizer is linked to this builder. You may intermix calls to the builder and tokenizer within
    2787     * certain limits, however there is no synchronization. Once the tokenizer has been used once, it must be
    2788     * {@link StringTokenizer#reset() reset} to pickup the latest changes in the builder. For example:
    2789     *
    2790     * <pre>
    2791     * StrBuilder b = new StrBuilder();
    2792     * b.append("a b ");
    2793     * StrTokenizer t = b.asTokenizer();
    2794     * String[] tokens1 = t.getTokenArray(); // returns a,b
    2795     * b.append("c d ");
    2796     * String[] tokens2 = t.getTokenArray(); // returns a,b (c and d ignored)
    2797     * t.reset(); // reset causes builder changes to be picked up
    2798     * String[] tokens3 = t.getTokenArray(); // returns a,b,c,d
    2799     * </pre>
    2800     *
    2801     * In addition to simply intermixing appends and tokenization, you can also call the set methods on the tokenizer to
    2802     * alter how it tokenizes. Just remember to call reset when you want to pickup builder changes.
    2803     * <p>
    2804     * Calling {@link StringTokenizer#reset(String)} or {@link StringTokenizer#reset(char[])} with a non-null value will
    2805     * break the link with the builder.
    2806     *
    2807     * @return a tokenizer that is linked to this builder
    2808     */
    2809    public StringTokenizer asTokenizer() {
    2810        return new TextStringBuilderTokenizer();
    2811    }
    2812
    2813    // -----------------------------------------------------------------------
    2814    /**
    2815     * Gets the contents of this builder as a Reader.
    2816     * <p>
    2817     * This method allows the contents of the builder to be read using any standard method that expects a Reader.
    2818     * <p>
    2819     * To use, simply create a <code>StrBuilder</code>, populate it with data, call <code>asReader</code>, and then read
    2820     * away.
    2821     * <p>
    2822     * The internal character array is shared between the builder and the reader. This allows you to append to the
    2823     * builder after creating the reader, and the changes will be picked up. Note however, that no synchronization
    2824     * occurs, so you must perform all operations with the builder and the reader in one thread.
    2825     * <p>
    2826     * The returned reader supports marking, and ignores the flush method.
    2827     *
    2828     * @return a reader that reads from this builder
    2829     */
    2830    public Reader asReader() {
    2831        return new StrBuilderReader();
    2832    }
    2833
    2834    // -----------------------------------------------------------------------
    2835    /**
    2836     * Gets this builder as a Writer that can be written to.
    2837     * <p>
    2838     * This method allows you to populate the contents of the builder using any standard method that takes a Writer.
    2839     * <p>
    2840     * To use, simply create a <code>StrBuilder</code>, call <code>asWriter</code>, and populate away. The data is
    2841     * available at any time using the methods of the <code>StrBuilder</code>.
    2842     * <p>
    2843     * The internal character array is shared between the builder and the writer. This allows you to intermix calls that
    2844     * append to the builder and write using the writer and the changes will be occur correctly. Note however, that no
    2845     * synchronization occurs, so you must perform all operations with the builder and the writer in one thread.
    2846     * <p>
    2847     * The returned writer ignores the close and flush methods.
    2848     *
    2849     * @return a writer that populates this builder
    2850     */
    2851    public Writer asWriter() {
    2852        return new StrBuilderWriter();
    2853    }
    2854
    2855    /**
    2856     * Appends current contents of this <code>StrBuilder</code> to the provided {@link Appendable}.
    2857     * <p>
    2858     * This method tries to avoid doing any extra copies of contents.
    2859     *
    2860     * @param appendable
    2861     *            the appendable to append data to
    2862     * @throws IOException
    2863     *             if an I/O error occurs
    2864     *
    2865     * @see #readFrom(Readable)
    2866     */
    2867    public void appendTo(final Appendable appendable) throws IOException {
    2868        if (appendable instanceof Writer) {
    2869            ((Writer) appendable).write(buffer, 0, size);
    2870        } else if (appendable instanceof StringBuilder) {
    2871            ((StringBuilder) appendable).append(buffer, 0, size);
    2872        } else if (appendable instanceof StringBuffer) {
    2873            ((StringBuffer) appendable).append(buffer, 0, size);
    2874        } else if (appendable instanceof CharBuffer) {
    2875            ((CharBuffer) appendable).put(buffer, 0, size);
    2876        } else {
    2877            appendable.append(this);
    2878        }
    2879    }
    2880
    2881    /**
    2882     * Checks the contents of this builder against another to see if they contain the same character content ignoring
    2883     * case.
    2884     *
    2885     * @param other
    2886     *            the object to check, null returns false
    2887     * @return true if the builders contain the same characters in the same order
    2888     */
    2889    public boolean equalsIgnoreCase(final TextStringBuilder other) {
    2890        if (this == other) {
    2891            return true;
    2892        }
    2893        if (this.size != other.size) {
    2894            return false;
    2895        }
    2896        final char[] thisBuf = this.buffer;
    2897        final char[] otherBuf = other.buffer;
    2898        for (int i = size - 1; i >= 0; i--) {
    2899            final char c1 = thisBuf[i];
    2900            final char c2 = otherBuf[i];
    2901            if (c1 != c2 && Character.toUpperCase(c1) != Character.toUpperCase(c2)) {
    2902                return false;
    2903            }
    2904        }
    2905        return true;
    2906    }
    2907
    2908    /**
    2909     * Checks the contents of this builder against another to see if they contain the same character content.
    2910     *
    2911     * @param other
    2912     *            the object to check, null returns false
    2913     * @return true if the builders contain the same characters in the same order
    2914     */
    2915    public boolean equals(final TextStringBuilder other) {
    2916        if (this == other) {
    2917            return true;
    2918        }
    2919        if (other == null) {
    2920            return false;
    2921        }
    2922        if (this.size != other.size) {
    2923            return false;
    2924        }
    2925        final char[] thisBuf = this.buffer;
    2926        final char[] otherBuf = other.buffer;
    2927        for (int i = size - 1; i >= 0; i--) {
    2928            if (thisBuf[i] != otherBuf[i]) {
    2929                return false;
    2930            }
    2931        }
    2932        return true;
    2933    }
    2934
    2935    /**
    2936     * Checks the contents of this builder against another to see if they contain the same character content.
    2937     *
    2938     * @param obj
    2939     *            the object to check, null returns false
    2940     * @return true if the builders contain the same characters in the same order
    2941     */
    2942    @Override
    2943    public boolean equals(final Object obj) {
    2944        return obj instanceof TextStringBuilder && equals((TextStringBuilder) obj);
    2945    }
    2946
    2947    /**
    2948     * Gets a suitable hash code for this builder.
    2949     *
    2950     * @return a hash code
    2951     */
    2952    @Override
    2953    public int hashCode() {
    2954        final char[] buf = buffer;
    2955        int hash = 0;
    2956        for (int i = size - 1; i >= 0; i--) {
    2957            hash = 31 * hash + buf[i];
    2958        }
    2959        return hash;
    2960    }
    2961
    2962    // -----------------------------------------------------------------------
    2963    /**
    2964     * Gets a String version of the string builder, creating a new instance each time the method is called.
    2965     * <p>
    2966     * Note that unlike StringBuffer, the string version returned is independent of the string builder.
    2967     *
    2968     * @return The builder as a String
    2969     */
    2970    @Override
    2971    public String toString() {
    2972        return new String(buffer, 0, size);
    2973    }
    2974
    2975    /**
    2976     * Gets a StringBuffer version of the string builder, creating a new instance each time the method is called.
    2977     *
    2978     * @return The builder as a StringBuffer
    2979     */
    2980    public StringBuffer toStringBuffer() {
    2981        return new StringBuffer(size).append(buffer, 0, size);
    2982    }
    2983
    2984    /**
    2985     * Gets a StringBuilder version of the string builder, creating a new instance each time the method is called.
    2986     *
    2987     * @return The builder as a StringBuilder
    2988     */
    2989    public StringBuilder toStringBuilder() {
    2990        return new StringBuilder(size).append(buffer, 0, size);
    2991    }
    2992
    2993    /**
    2994     * Implement the {@link Builder} interface.
    2995     *
    2996     * @return The builder as a String
    2997     * @see #toString()
    2998     */
    2999    @Override
    3000    public String build() {
    3001        return toString();
    3002    }
    3003
    3004    // -----------------------------------------------------------------------
    3005    /**
    3006     * Validates parameters defining a range of the builder.
    3007     *
    3008     * @param startIndex
    3009     *            the start index, inclusive, must be valid
    3010     * @param endIndex
    3011     *            the end index, exclusive, must be valid except that if too large it is treated as end of string
    3012     * @return The new string
    3013     * @throws IndexOutOfBoundsException
    3014     *             if the index is invalid
    3015     */
    3016    protected int validateRange(final int startIndex, int endIndex) {
    3017        if (startIndex < 0) {
    3018            throw new StringIndexOutOfBoundsException(startIndex);
    3019        }
    3020        if (endIndex > size) {
    3021            endIndex = size;
    3022        }
    3023        if (startIndex > endIndex) {
    3024            throw new StringIndexOutOfBoundsException("end < start");
    3025        }
    3026        return endIndex;
    3027    }
    3028
    3029    /**
    3030     * Validates parameters defining a single index in the builder.
    3031     *
    3032     * @param index
    3033     *            the index, must be valid
    3034     * @throws IndexOutOfBoundsException
    3035     *             if the index is invalid
    3036     */
    3037    protected void validateIndex(final int index) {
    3038        if (index < 0 || index > size) {
    3039            throw new StringIndexOutOfBoundsException(index);
    3040        }
    3041    }
    3042
    3043    // -----------------------------------------------------------------------
    3044    /**
    3045     * Inner class to allow StrBuilder to operate as a tokenizer.
    3046     */
    3047    class TextStringBuilderTokenizer extends StringTokenizer {
    3048
    3049        /**
    3050         * Default constructor.
    3051         */
    3052        TextStringBuilderTokenizer() {
    3053            super();
    3054        }
    3055
    3056        /** {@inheritDoc} */
    3057        @Override
    3058        protected List<String> tokenize(final char[] chars, final int offset, final int count) {
    3059            if (chars == null) {
    3060                return super.tokenize(TextStringBuilder.this.buffer, 0, TextStringBuilder.this.size());
    3061            }
    3062            return super.tokenize(chars, offset, count);
    3063        }
    3064
    3065        /** {@inheritDoc} */
    3066        @Override
    3067        public String getContent() {
    3068            final String str = super.getContent();
    3069            if (str == null) {
    3070                return TextStringBuilder.this.toString();
    3071            }
    3072            return str;
    3073        }
    3074    }
    3075
    3076    // -----------------------------------------------------------------------
    3077    /**
    3078     * Inner class to allow StrBuilder to operate as a reader.
    3079     */
    3080    class StrBuilderReader extends Reader {
    3081        /** The current stream position. */
    3082        private int pos;
    3083        /** The last mark position. */
    3084        private int mark;
    3085
    3086        /**
    3087         * Default constructor.
    3088         */
    3089        StrBuilderReader() {
    3090            super();
    3091        }
    3092
    3093        /** {@inheritDoc} */
    3094        @Override
    3095        public void close() {
    3096            // do nothing
    3097        }
    3098
    3099        /** {@inheritDoc} */
    3100        @Override
    3101        public int read() {
    3102            if (!ready()) {
    3103                return -1;
    3104            }
    3105            return TextStringBuilder.this.charAt(pos++);
    3106        }
    3107
    3108        /** {@inheritDoc} */
    3109        @Override
    3110        public int read(final char[] b, final int off, int len) {
    3111            if (off < 0 || len < 0 || off > b.length || off + len > b.length || off + len < 0) {
    3112                throw new IndexOutOfBoundsException();
    3113            }
    3114            if (len == 0) {
    3115                return 0;
    3116            }
    3117            if (pos >= TextStringBuilder.this.size()) {
    3118                return -1;
    3119            }
    3120            if (pos + len > size()) {
    3121                len = TextStringBuilder.this.size() - pos;
    3122            }
    3123            TextStringBuilder.this.getChars(pos, pos + len, b, off);
    3124            pos += len;
    3125            return len;
    3126        }
    3127
    3128        /** {@inheritDoc} */
    3129        @Override
    3130        public long skip(long n) {
    3131            if (pos + n > TextStringBuilder.this.size()) {
    3132                n = TextStringBuilder.this.size() - pos;
    3133            }
    3134            if (n < 0) {
    3135                return 0;
    3136            }
    3137            pos += n;
    3138            return n;
    3139        }
    3140
    3141        /** {@inheritDoc} */
    3142        @Override
    3143        public boolean ready() {
    3144            return pos < TextStringBuilder.this.size();
    3145        }
    3146
    3147        /** {@inheritDoc} */
    3148        @Override
    3149        public boolean markSupported() {
    3150            return true;
    3151        }
    3152
    3153        /** {@inheritDoc} */
    3154        @Override
    3155        public void mark(final int readAheadLimit) {
    3156            mark = pos;
    3157        }
    3158
    3159        /** {@inheritDoc} */
    3160        @Override
    3161        public void reset() {
    3162            pos = mark;
    3163        }
    3164    }
    3165
    3166    // -----------------------------------------------------------------------
    3167    /**
    3168     * Inner class to allow StrBuilder to operate as a writer.
    3169     */
    3170    class StrBuilderWriter extends Writer {
    3171
    3172        /**
    3173         * Default constructor.
    3174         */
    3175        StrBuilderWriter() {
    3176            super();
    3177        }
    3178
    3179        /** {@inheritDoc} */
    3180        @Override
    3181        public void close() {
    3182            // do nothing
    3183        }
    3184
    3185        /** {@inheritDoc} */
    3186        @Override
    3187        public void flush() {
    3188            // do nothing
    3189        }
    3190
    3191        /** {@inheritDoc} */
    3192        @Override
    3193        public void write(final int c) {
    3194            TextStringBuilder.this.append((char) c);
    3195        }
    3196
    3197        /** {@inheritDoc} */
    3198        @Override
    3199        public void write(final char[] cbuf) {
    3200            TextStringBuilder.this.append(cbuf);
    3201        }
    3202
    3203        /** {@inheritDoc} */
    3204        @Override
    3205        public void write(final char[] cbuf, final int off, final int len) {
    3206            TextStringBuilder.this.append(cbuf, off, len);
    3207        }
    3208
    3209        /** {@inheritDoc} */
    3210        @Override
    3211        public void write(final String str) {
    3212            TextStringBuilder.this.append(str);
    3213        }
    3214
    3215        /** {@inheritDoc} */
    3216        @Override
    3217        public void write(final String str, final int off, final int len) {
    3218            TextStringBuilder.this.append(str, off, len);
    3219        }
    3220    }
    3221
    3222}