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