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