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