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