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