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