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