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