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