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     * @author Apache Software Foundation
070     * @author Robert Scholte
071     * @since 2.2
072     * @version $Id: StrBuilder.java 916081 2010-02-25 01:28:13Z niallp $
073     */
074    public class StrBuilder implements CharSequence, Appendable {
075    
076        /**
077         * The extra capacity for new builders.
078         */
079        static final int CAPACITY = 32;
080    
081        /**
082         * Required for serialization support.
083         * 
084         * @see java.io.Serializable
085         */
086        private static final long serialVersionUID = 7628716375283629643L;
087    
088        /** Internal data storage. */
089        protected char[] buffer; // TODO make private?
090        /** Current size of the buffer. */
091        protected int size; // TODO make private?
092        /** The new line. */
093        private String newLine;
094        /** The null text. */
095        private String nullText;
096    
097        //-----------------------------------------------------------------------
098        /**
099         * Constructor that creates an empty builder initial capacity 32 characters.
100         */
101        public StrBuilder() {
102            this(CAPACITY);
103        }
104    
105        /**
106         * Constructor that creates an empty builder the specified initial capacity.
107         *
108         * @param initialCapacity  the initial capacity, zero or less will be converted to 32
109         */
110        public StrBuilder(int initialCapacity) {
111            super();
112            if (initialCapacity <= 0) {
113                initialCapacity = CAPACITY;
114            }
115            buffer = new char[initialCapacity];
116        }
117    
118        /**
119         * Constructor that creates a builder from the string, allocating
120         * 32 extra characters for growth.
121         *
122         * @param str  the string to copy, null treated as blank string
123         */
124        public StrBuilder(String str) {
125            super();
126            if (str == null) {
127                buffer = new char[CAPACITY];
128            } else {
129                buffer = new char[str.length() + CAPACITY];
130                append(str);
131            }
132        }
133    
134        //-----------------------------------------------------------------------
135        /**
136         * Gets the text to be appended when a new line is added.
137         *
138         * @return the new line text, null means use system default
139         */
140        public String getNewLineText() {
141            return newLine;
142        }
143    
144        /**
145         * Sets the text to be appended when a new line is added.
146         *
147         * @param newLine  the new line text, null means use system default
148         * @return this, to enable chaining
149         */
150        public StrBuilder setNewLineText(String newLine) {
151            this.newLine = newLine;
152            return this;
153        }
154    
155        //-----------------------------------------------------------------------
156        /**
157         * Gets the text to be appended when null is added.
158         *
159         * @return the null text, null means no append
160         */
161        public String getNullText() {
162            return nullText;
163        }
164    
165        /**
166         * Sets the text to be appended when null is added.
167         *
168         * @param nullText  the null text, null means no append
169         * @return this, to enable chaining
170         */
171        public StrBuilder setNullText(String nullText) {
172            if (nullText != null && nullText.length() == 0) {
173                nullText = null;
174            }
175            this.nullText = nullText;
176            return this;
177        }
178    
179        //-----------------------------------------------------------------------
180        /**
181         * Gets the length of the string builder.
182         *
183         * @return the length
184         */
185        public int length() {
186            return size;
187        }
188    
189        /**
190         * Updates the length of the builder by either dropping the last characters
191         * or adding filler of unicode zero.
192         *
193         * @param length  the length to set to, must be zero or positive
194         * @return this, to enable chaining
195         * @throws IndexOutOfBoundsException if the length is negative
196         */
197        public StrBuilder setLength(int length) {
198            if (length < 0) {
199                throw new StringIndexOutOfBoundsException(length);
200            }
201            if (length < size) {
202                size = length;
203            } else if (length > size) {
204                ensureCapacity(length);
205                int oldEnd = size;
206                int newEnd = length;
207                size = length;
208                for (int i = oldEnd; i < newEnd; i++) {
209                    buffer[i] = '\0';
210                }
211            }
212            return this;
213        }
214    
215        //-----------------------------------------------------------------------
216        /**
217         * Gets the current size of the internal character array buffer.
218         *
219         * @return the capacity
220         */
221        public int capacity() {
222            return buffer.length;
223        }
224    
225        /**
226         * Checks the capacity and ensures that it is at least the size specified.
227         *
228         * @param capacity  the capacity to ensure
229         * @return this, to enable chaining
230         */
231        public StrBuilder ensureCapacity(int capacity) {
232            if (capacity > buffer.length) {
233                char[] old = buffer;
234                buffer = new char[capacity * 2];
235                System.arraycopy(old, 0, buffer, 0, size);
236            }
237            return this;
238        }
239    
240        /**
241         * Minimizes the capacity to the actual length of the string.
242         *
243         * @return this, to enable chaining
244         */
245        public StrBuilder minimizeCapacity() {
246            if (buffer.length > length()) {
247                char[] old = buffer;
248                buffer = new char[length()];
249                System.arraycopy(old, 0, buffer, 0, size);
250            }
251            return this;
252        }
253    
254        //-----------------------------------------------------------------------
255        /**
256         * Gets the length of the string builder.
257         * <p>
258         * This method is the same as {@link #length()} and is provided to match the
259         * API of Collections.
260         *
261         * @return the length
262         */
263        public int size() {
264            return size;
265        }
266    
267        /**
268         * Checks is the string builder is empty (convenience Collections API style method).
269         * <p>
270         * This method is the same as checking {@link #length()} and is provided to match the
271         * API of Collections.
272         *
273         * @return <code>true</code> if the size is <code>0</code>.
274         */
275        public boolean isEmpty() {
276            return size == 0;
277        }
278    
279        /**
280         * Clears the string builder (convenience Collections API style method).
281         * <p>
282         * This method does not reduce the size of the internal character buffer.
283         * To do that, call <code>clear()</code> followed by {@link #minimizeCapacity()}.
284         * <p>
285         * This method is the same as {@link #setLength(int)} called with zero
286         * and is provided to match the API of Collections.
287         *
288         * @return this, to enable chaining
289         */
290        public StrBuilder clear() {
291            size = 0;
292            return this;
293        }
294    
295        //-----------------------------------------------------------------------
296        /**
297         * Gets the character at the specified index.
298         *
299         * @see #setCharAt(int, char)
300         * @see #deleteCharAt(int)
301         * @param index  the index to retrieve, must be valid
302         * @return the character at the index
303         * @throws IndexOutOfBoundsException if the index is invalid
304         */
305        public char charAt(int index) {
306            if (index < 0 || index >= length()) {
307                throw new StringIndexOutOfBoundsException(index);
308            }
309            return buffer[index];
310        }
311    
312        /**
313         * Sets the character at the specified index.
314         *
315         * @see #charAt(int)
316         * @see #deleteCharAt(int)
317         * @param index  the index to set
318         * @param ch  the new character
319         * @return this, to enable chaining
320         * @throws IndexOutOfBoundsException if the index is invalid
321         */
322        public StrBuilder setCharAt(int index, char ch) {
323            if (index < 0 || index >= length()) {
324                throw new StringIndexOutOfBoundsException(index);
325            }
326            buffer[index] = ch;
327            return this;
328        }
329    
330        /**
331         * Deletes the character at the specified index.
332         *
333         * @see #charAt(int)
334         * @see #setCharAt(int, char)
335         * @param index  the index to delete
336         * @return this, to enable chaining
337         * @throws IndexOutOfBoundsException if the index is invalid
338         */
339        public StrBuilder deleteCharAt(int index) {
340            if (index < 0 || index >= size) {
341                throw new StringIndexOutOfBoundsException(index);
342            }
343            deleteImpl(index, index + 1, 1);
344            return this;
345        }
346    
347        //-----------------------------------------------------------------------
348        /**
349         * Copies the builder's character array into a new character array.
350         * 
351         * @return a new array that represents the contents of the builder
352         */
353        public char[] toCharArray() {
354            if (size == 0) {
355                return ArrayUtils.EMPTY_CHAR_ARRAY;
356            }
357            char chars[] = new char[size];
358            System.arraycopy(buffer, 0, chars, 0, size);
359            return chars;
360        }
361    
362        /**
363         * Copies part of the builder's character array into a new character array.
364         * 
365         * @param startIndex  the start index, inclusive, must be valid
366         * @param endIndex  the end index, exclusive, must be valid except that
367         *  if too large it is treated as end of string
368         * @return a new array that holds part of the contents of the builder
369         * @throws IndexOutOfBoundsException if startIndex is invalid,
370         *  or if endIndex is invalid (but endIndex greater than size is valid)
371         */
372        public char[] toCharArray(int startIndex, int endIndex) {
373            endIndex = validateRange(startIndex, endIndex);
374            int len = endIndex - startIndex;
375            if (len == 0) {
376                return ArrayUtils.EMPTY_CHAR_ARRAY;
377            }
378            char chars[] = new char[len];
379            System.arraycopy(buffer, startIndex, chars, 0, len);
380            return chars;
381        }
382    
383        /**
384         * Copies the character array into the specified array.
385         * 
386         * @param destination  the destination array, null will cause an array to be created
387         * @return the input array, unless that was null or too small
388         */
389        public char[] getChars(char[] destination) {
390            int len = length();
391            if (destination == null || destination.length < len) {
392                destination = new char[len];
393            }
394            System.arraycopy(buffer, 0, destination, 0, len);
395            return destination;
396        }
397    
398        /**
399         * Copies the character array into the specified array.
400         *
401         * @param startIndex  first index to copy, inclusive, must be valid
402         * @param endIndex  last index, exclusive, must be valid
403         * @param destination  the destination array, must not be null or too small
404         * @param destinationIndex  the index to start copying in destination
405         * @throws NullPointerException if the array is null
406         * @throws IndexOutOfBoundsException if any index is invalid
407         */
408        public void getChars(int startIndex, int endIndex, char destination[], int destinationIndex) {
409            if (startIndex < 0) {
410                throw new StringIndexOutOfBoundsException(startIndex);
411            }
412            if (endIndex < 0 || endIndex > length()) {
413                throw new StringIndexOutOfBoundsException(endIndex);
414            }
415            if (startIndex > endIndex) {
416                throw new StringIndexOutOfBoundsException("end < start");
417            }
418            System.arraycopy(buffer, startIndex, destination, destinationIndex, endIndex - startIndex);
419        }
420    
421        //-----------------------------------------------------------------------
422        /**
423         * Appends the new line string to this string builder.
424         * <p>
425         * The new line string can be altered using {@link #setNewLineText(String)}.
426         * This might be used to force the output to always use Unix line endings
427         * even when on Windows.
428         *
429         * @return this, to enable chaining
430         */
431        public StrBuilder appendNewLine() {
432            if (newLine == null)  {
433                append(SystemUtils.LINE_SEPARATOR);
434                return this;
435            }
436            return append(newLine);
437        }
438    
439        /**
440         * Appends the text representing <code>null</code> to this string builder.
441         *
442         * @return this, to enable chaining
443         */
444        public StrBuilder appendNull() {
445            if (nullText == null)  {
446                return this;
447            }
448            return append(nullText);
449        }
450    
451        /**
452         * Appends an object to this string builder.
453         * Appending null will call {@link #appendNull()}.
454         *
455         * @param obj  the object to append
456         * @return this, to enable chaining
457         */
458        public StrBuilder append(Object obj) {
459            if (obj == null) {
460                return appendNull();
461            } 
462            return append(obj.toString());        
463        }
464    
465        /**
466         * Appends a CharSequence to this string builder.
467         * Appending null will call {@link #appendNull()}.
468         *
469         * @param seq  the CharSequence to append
470         * @return this, to enable chaining
471         */
472        public StrBuilder append(CharSequence seq) {
473            if (seq == null) {
474                return appendNull();
475            } 
476            return append(seq.toString());        
477        }
478    
479        /**
480         * Appends part of a CharSequence to this string builder.
481         * Appending null will call {@link #appendNull()}.
482         *
483         * @param seq  the CharSequence to append
484         * @param startIndex  the start index, inclusive, must be valid
485         * @param length  the length to append, must be valid
486         * @return this, to enable chaining
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         */
721        public StrBuilder append(char ch) {
722            int len = length();
723            ensureCapacity(len + 1);
724            buffer[size++] = ch;
725            return this;
726        }
727    
728        /**
729         * Appends an int value to the string builder using <code>String.valueOf</code>.
730         *
731         * @param value  the value to append
732         * @return this, to enable chaining
733         */
734        public StrBuilder append(int value) {
735            return append(String.valueOf(value));
736        }
737    
738        /**
739         * Appends a long value to the string builder using <code>String.valueOf</code>.
740         *
741         * @param value  the value to append
742         * @return this, to enable chaining
743         */
744        public StrBuilder append(long value) {
745            return append(String.valueOf(value));
746        }
747    
748        /**
749         * Appends a float value to the string builder using <code>String.valueOf</code>.
750         *
751         * @param value  the value to append
752         * @return this, to enable chaining
753         */
754        public StrBuilder append(float value) {
755            return append(String.valueOf(value));
756        }
757    
758        /**
759         * Appends a double value to the string builder using <code>String.valueOf</code>.
760         *
761         * @param value  the value to append
762         * @return this, to enable chaining
763         */
764        public StrBuilder append(double value) {
765            return append(String.valueOf(value));
766        }
767    
768        //-----------------------------------------------------------------------
769        /**
770         * Appends an object followed by a new line to this string builder.
771         * Appending null will call {@link #appendNull()}.
772         *
773         * @param obj  the object to append
774         * @return this, to enable chaining
775         * @since 2.3
776         */
777        public StrBuilder appendln(Object obj) {
778            return append(obj).appendNewLine();
779        }
780    
781        /**
782         * Appends a string followed by a new line to this string builder.
783         * Appending null will call {@link #appendNull()}.
784         *
785         * @param str  the string to append
786         * @return this, to enable chaining
787         * @since 2.3
788         */
789        public StrBuilder appendln(String str) {
790            return append(str).appendNewLine();
791        }
792    
793        /**
794         * Appends part of a string followed by a new line to this string builder.
795         * Appending null will call {@link #appendNull()}.
796         *
797         * @param str  the string to append
798         * @param startIndex  the start index, inclusive, must be valid
799         * @param length  the length to append, must be valid
800         * @return this, to enable chaining
801         * @since 2.3
802         */
803        public StrBuilder appendln(String str, int startIndex, int length) {
804            return append(str, startIndex, length).appendNewLine();
805        }
806    
807        /**
808         * Appends a string buffer followed by a new line to this string builder.
809         * Appending null will call {@link #appendNull()}.
810         *
811         * @param str  the string buffer to append
812         * @return this, to enable chaining
813         * @since 2.3
814         */
815        public StrBuilder appendln(StringBuffer str) {
816            return append(str).appendNewLine();
817        }
818    
819        /**
820         * Appends part of a string buffer followed by a new line to this string builder.
821         * Appending null will call {@link #appendNull()}.
822         *
823         * @param str  the string to append
824         * @param startIndex  the start index, inclusive, must be valid
825         * @param length  the length to append, must be valid
826         * @return this, to enable chaining
827         * @since 2.3
828         */
829        public StrBuilder appendln(StringBuffer str, int startIndex, int length) {
830            return append(str, startIndex, length).appendNewLine();
831        }
832    
833        /**
834         * Appends another string builder followed by a new line to this string builder.
835         * Appending null will call {@link #appendNull()}.
836         *
837         * @param str  the string builder to append
838         * @return this, to enable chaining
839         * @since 2.3
840         */
841        public StrBuilder appendln(StrBuilder str) {
842            return append(str).appendNewLine();
843        }
844    
845        /**
846         * Appends part of a string builder followed by a new line to this string builder.
847         * Appending null will call {@link #appendNull()}.
848         *
849         * @param str  the string to append
850         * @param startIndex  the start index, inclusive, must be valid
851         * @param length  the length to append, must be valid
852         * @return this, to enable chaining
853         * @since 2.3
854         */
855        public StrBuilder appendln(StrBuilder str, int startIndex, int length) {
856            return append(str, startIndex, length).appendNewLine();
857        }
858    
859        /**
860         * Appends a char array followed by a new line to the string builder.
861         * Appending null will call {@link #appendNull()}.
862         *
863         * @param chars  the char array to append
864         * @return this, to enable chaining
865         * @since 2.3
866         */
867        public StrBuilder appendln(char[] chars) {
868            return append(chars).appendNewLine();
869        }
870    
871        /**
872         * Appends a char array followed by a new line to the string builder.
873         * Appending null will call {@link #appendNull()}.
874         *
875         * @param chars  the char array to append
876         * @param startIndex  the start index, inclusive, must be valid
877         * @param length  the length to append, must be valid
878         * @return this, to enable chaining
879         * @since 2.3
880         */
881        public StrBuilder appendln(char[] chars, int startIndex, int length) {
882            return append(chars, startIndex, length).appendNewLine();
883        }
884    
885        /**
886         * Appends a boolean value followed by a new line to the string builder.
887         *
888         * @param value  the value to append
889         * @return this, to enable chaining
890         * @since 2.3
891         */
892        public StrBuilder appendln(boolean value) {
893            return append(value).appendNewLine();
894        }
895    
896        /**
897         * Appends a char value followed by a new line to the string builder.
898         *
899         * @param ch  the value to append
900         * @return this, to enable chaining
901         * @since 2.3
902         */
903        public StrBuilder appendln(char ch) {
904            return append(ch).appendNewLine();
905        }
906    
907        /**
908         * Appends an int value followed by a new line to the string builder using <code>String.valueOf</code>.
909         *
910         * @param value  the value to append
911         * @return this, to enable chaining
912         * @since 2.3
913         */
914        public StrBuilder appendln(int value) {
915            return append(value).appendNewLine();
916        }
917    
918        /**
919         * Appends a long value followed by a new line to the string builder using <code>String.valueOf</code>.
920         *
921         * @param value  the value to append
922         * @return this, to enable chaining
923         * @since 2.3
924         */
925        public StrBuilder appendln(long value) {
926            return append(value).appendNewLine();
927        }
928    
929        /**
930         * Appends a float value followed by a new line to the string builder using <code>String.valueOf</code>.
931         *
932         * @param value  the value to append
933         * @return this, to enable chaining
934         * @since 2.3
935         */
936        public StrBuilder appendln(float value) {
937            return append(value).appendNewLine();
938        }
939    
940        /**
941         * Appends a double value followed by a new line to the string builder using <code>String.valueOf</code>.
942         *
943         * @param value  the value to append
944         * @return this, to enable chaining
945         * @since 2.3
946         */
947        public StrBuilder appendln(double value) {
948            return append(value).appendNewLine();
949        }
950    
951        //-----------------------------------------------------------------------
952        /**
953         * Appends each item in an array to the builder without any separators.
954         * Appending a null array will have no effect.
955         * Each object is appended using {@link #append(Object)}.
956         *
957         * @param array  the array to append
958         * @return this, to enable chaining
959         * @since 2.3
960         */
961        public StrBuilder appendAll(Object[] array) {
962            if (array != null && array.length > 0) {
963                for (int i = 0; i < array.length; i++) {
964                    append(array[i]);
965                }
966            }
967            return this;
968        }
969    
970        /**
971         * Appends each item in a iterable to the builder without any separators.
972         * Appending a null iterable will have no effect.
973         * Each object is appended using {@link #append(Object)}.
974         *
975         * @param iterable  the iterable to append
976         * @return this, to enable chaining
977         * @since 2.3
978         */
979        public StrBuilder appendAll(Iterable<?> iterable) {
980            if (iterable != null) {
981                Iterator<?> it = iterable.iterator();
982                while (it.hasNext()) {
983                    append(it.next());
984                }
985            }
986            return this;
987        }
988    
989        /**
990         * Appends each item in an iterator to the builder without any separators.
991         * Appending a null iterator will have no effect.
992         * Each object is appended using {@link #append(Object)}.
993         *
994         * @param it  the iterator to append
995         * @return this, to enable chaining
996         * @since 2.3
997         */
998        public StrBuilder appendAll(Iterator<?> it) {
999            if (it != null) {
1000                while (it.hasNext()) {
1001                    append(it.next());
1002                }
1003            }
1004            return this;
1005        }
1006    
1007        //-----------------------------------------------------------------------
1008        /**
1009         * Appends an array placing separators between each value, but
1010         * not before the first or after the last.
1011         * Appending a null array will have no effect.
1012         * Each object is appended using {@link #append(Object)}.
1013         *
1014         * @param array  the array to append
1015         * @param separator  the separator to use, null means no separator
1016         * @return this, to enable chaining
1017         */
1018        public StrBuilder appendWithSeparators(Object[] array, String separator) {
1019            if (array != null && array.length > 0) {
1020                separator = (separator == null ? "" : separator);
1021                append(array[0]);
1022                for (int i = 1; i < array.length; i++) {
1023                    append(separator);
1024                    append(array[i]);
1025                }
1026            }
1027            return this;
1028        }
1029    
1030        /**
1031         * Appends a iterable placing separators between each value, but
1032         * not before the first or after the last.
1033         * Appending a null iterable will have no effect.
1034         * Each object is appended using {@link #append(Object)}.
1035         *
1036         * @param iterable  the iterable to append
1037         * @param separator  the separator to use, null means no separator
1038         * @return this, to enable chaining
1039         */
1040        public StrBuilder appendWithSeparators(Iterable<?> iterable, String separator) {
1041            if (iterable != null) {
1042                separator = (separator == null ? "" : separator);
1043                Iterator<?> it = iterable.iterator();
1044                while (it.hasNext()) {
1045                    append(it.next());
1046                    if (it.hasNext()) {
1047                        append(separator);
1048                    }
1049                }
1050            }
1051            return this;
1052        }
1053    
1054        /**
1055         * Appends an iterator placing separators between each value, but
1056         * not before the first or after the last.
1057         * Appending a null iterator will have no effect.
1058         * Each object is appended using {@link #append(Object)}.
1059         *
1060         * @param it  the iterator to append
1061         * @param separator  the separator to use, null means no separator
1062         * @return this, to enable chaining
1063         */
1064        public StrBuilder appendWithSeparators(Iterator<?> it, String separator) {
1065            if (it != null) {
1066                separator = (separator == null ? "" : separator);
1067                while (it.hasNext()) {
1068                    append(it.next());
1069                    if (it.hasNext()) {
1070                        append(separator);
1071                    }
1072                }
1073            }
1074            return this;
1075        }
1076    
1077        //-----------------------------------------------------------------------
1078        /**
1079         * Appends a separator if the builder is currently non-empty.
1080         * Appending a null separator will have no effect.
1081         * The separator is appended using {@link #append(String)}.
1082         * <p>
1083         * This method is useful for adding a separator each time around the
1084         * loop except the first.
1085         * <pre>
1086         * for (Iterator it = list.iterator(); it.hasNext(); ) {
1087         *   appendSeparator(",");
1088         *   append(it.next());
1089         * }
1090         * </pre>
1091         * Note that for this simple example, you should use
1092         * {@link #appendWithSeparators(Iterable, String)}.
1093         * 
1094         * @param separator  the separator to use, null means no separator
1095         * @return this, to enable chaining
1096         * @since 2.3
1097         */
1098        public StrBuilder appendSeparator(String separator) {
1099            return appendSeparator(separator, null);
1100        }
1101    
1102        /**
1103         * Appends one of both separators to the StrBuilder.
1104         * If the builder is currently empty it will append the defaultIfEmpty-separator
1105         * Otherwise it will append the standard-separator
1106         * 
1107         * Appending a null separator will have no effect.
1108         * The separator is appended using {@link #append(String)}.
1109         * <p>
1110         * This method is for example useful for constructing queries
1111         * <pre>
1112         * StrBuilder whereClause = new StrBuilder();
1113         * if(searchCommand.getPriority() != null) {
1114         *  whereClause.appendSeparator(" and", " where");
1115         *  whereClause.append(" priority = ?")
1116         * }
1117         * if(searchCommand.getComponent() != null) {
1118         *  whereClause.appendSeparator(" and", " where");
1119         *  whereClause.append(" component = ?")
1120         * }
1121         * selectClause.append(whereClause)
1122         * </pre>
1123         * 
1124         * @param standard the separator if builder is not empty, null means no separator
1125         * @param defaultIfEmpty the separator if builder is empty, null means no separator
1126         * @return this, to enable chaining
1127         * @since 2.5
1128         */
1129        public StrBuilder appendSeparator(String standard, String defaultIfEmpty) {
1130            String str = isEmpty() ? defaultIfEmpty : standard;
1131            if (str != null) {
1132                append(str);
1133            }
1134            return this;
1135        }
1136    
1137        /**
1138         * Appends a separator if the builder is currently non-empty.
1139         * The separator is appended using {@link #append(char)}.
1140         * <p>
1141         * This method is useful for adding a separator each time around the
1142         * loop except the first.
1143         * <pre>
1144         * for (Iterator it = list.iterator(); it.hasNext(); ) {
1145         *   appendSeparator(',');
1146         *   append(it.next());
1147         * }
1148         * </pre>
1149         * Note that for this simple example, you should use
1150         * {@link #appendWithSeparators(Iterable, String)}.
1151         * 
1152         * @param separator  the separator to use
1153         * @return this, to enable chaining
1154         * @since 2.3
1155         */
1156        public StrBuilder appendSeparator(char separator) {
1157            if (size() > 0) {
1158                append(separator);
1159            }
1160            return this;
1161        }
1162    
1163        /**
1164         * Append one of both separators to the builder
1165         * If the builder is currently empty it will append the defaultIfEmpty-separator
1166         * Otherwise it will append the standard-separator
1167         *
1168         * The separator is appended using {@link #append(char)}.
1169         * @param standard the separator if builder is not empty
1170         * @param defaultIfEmpty the separator if builder is empty
1171         * @return this, to enable chaining
1172         * @since 2.5
1173         */
1174        public StrBuilder appendSeparator(char standard, char defaultIfEmpty) {
1175            if (size() > 0) {
1176                append(standard);
1177            }
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         */
2005        public CharSequence subSequence(int startIndex, int endIndex) {
2006          if (startIndex < 0) {
2007              throw new StringIndexOutOfBoundsException(startIndex);
2008          }
2009          if (endIndex > size) {
2010              throw new StringIndexOutOfBoundsException(endIndex);
2011          }
2012          if (startIndex > endIndex) {
2013              throw new StringIndexOutOfBoundsException(endIndex - startIndex);
2014          }
2015          return substring(startIndex, endIndex);
2016        }
2017    
2018        /**
2019         * Extracts a portion of this string builder as a string.
2020         * 
2021         * @param start  the start index, inclusive, must be valid
2022         * @return the new string
2023         * @throws IndexOutOfBoundsException if the index is invalid
2024         */
2025        public String substring(int start) {
2026            return substring(start, size);
2027        }
2028    
2029        /**
2030         * Extracts a portion of this string builder as a string.
2031         * <p>
2032         * Note: This method treats an endIndex greater than the length of the
2033         * builder as equal to the length of the builder, and continues
2034         * without error, unlike StringBuffer or String.
2035         * 
2036         * @param startIndex  the start index, inclusive, must be valid
2037         * @param endIndex  the end index, exclusive, must be valid except
2038         *  that if too large it is treated as end of string
2039         * @return the new string
2040         * @throws IndexOutOfBoundsException if the index is invalid
2041         */
2042        public String substring(int startIndex, int endIndex) {
2043            endIndex = validateRange(startIndex, endIndex);
2044            return new String(buffer, startIndex, endIndex - startIndex);
2045        }
2046    
2047        /**
2048         * Extracts the leftmost characters from the string builder without
2049         * throwing an exception.
2050         * <p>
2051         * This method extracts the left <code>length</code> characters from
2052         * the builder. If this many characters are not available, the whole
2053         * builder is returned. Thus the returned string may be shorter than the
2054         * length requested.
2055         * 
2056         * @param length  the number of characters to extract, negative returns empty string
2057         * @return the new string
2058         */
2059        public String leftString(int length) {
2060            if (length <= 0) {
2061                return "";
2062            } else if (length >= size) {
2063                return new String(buffer, 0, size);
2064            } else {
2065                return new String(buffer, 0, length);
2066            }
2067        }
2068    
2069        /**
2070         * Extracts the rightmost characters from the string builder without
2071         * throwing an exception.
2072         * <p>
2073         * This method extracts the right <code>length</code> characters from
2074         * the builder. If this many characters are not available, the whole
2075         * builder is returned. Thus the returned string may be shorter than the
2076         * length requested.
2077         * 
2078         * @param length  the number of characters to extract, negative returns empty string
2079         * @return the new string
2080         */
2081        public String rightString(int length) {
2082            if (length <= 0) {
2083                return "";
2084            } else if (length >= size) {
2085                return new String(buffer, 0, size);
2086            } else {
2087                return new String(buffer, size - length, length);
2088            }
2089        }
2090    
2091        /**
2092         * Extracts some characters from the middle of the string builder without
2093         * throwing an exception.
2094         * <p>
2095         * This method extracts <code>length</code> characters from the builder
2096         * at the specified index.
2097         * If the index is negative it is treated as zero.
2098         * If the index is greater than the builder size, it is treated as the builder size.
2099         * If the length is negative, the empty string is returned.
2100         * If insufficient characters are available in the builder, as much as possible is returned.
2101         * Thus the returned string may be shorter than the length requested.
2102         * 
2103         * @param index  the index to start at, negative means zero
2104         * @param length  the number of characters to extract, negative returns empty string
2105         * @return the new string
2106         */
2107        public String midString(int index, int length) {
2108            if (index < 0) {
2109                index = 0;
2110            }
2111            if (length <= 0 || index >= size) {
2112                return "";
2113            }
2114            if (size <= index + length) {
2115                return new String(buffer, index, size - index);
2116            } else {
2117                return new String(buffer, index, length);
2118            }
2119        }
2120    
2121        //-----------------------------------------------------------------------
2122        /**
2123         * Checks if the string builder contains the specified char.
2124         *
2125         * @param ch  the character to find
2126         * @return true if the builder contains the character
2127         */
2128        public boolean contains(char ch) {
2129            char[] thisBuf = buffer;
2130            for (int i = 0; i < this.size; i++) {
2131                if (thisBuf[i] == ch) {
2132                    return true;
2133                }
2134            }
2135            return false;
2136        }
2137    
2138        /**
2139         * Checks if the string builder contains the specified string.
2140         *
2141         * @param str  the string to find
2142         * @return true if the builder contains the string
2143         */
2144        public boolean contains(String str) {
2145            return indexOf(str, 0) >= 0;
2146        }
2147    
2148        /**
2149         * Checks if the string builder contains a string matched using the
2150         * specified matcher.
2151         * <p>
2152         * Matchers can be used to perform advanced searching behaviour.
2153         * For example you could write a matcher to search for the character
2154         * 'a' followed by a number.
2155         *
2156         * @param matcher  the matcher to use, null returns -1
2157         * @return true if the matcher finds a match in the builder
2158         */
2159        public boolean contains(StrMatcher matcher) {
2160            return indexOf(matcher, 0) >= 0;
2161        }
2162    
2163        //-----------------------------------------------------------------------
2164        /**
2165         * Searches the string builder to find the first reference to the specified char.
2166         * 
2167         * @param ch  the character to find
2168         * @return the first index of the character, or -1 if not found
2169         */
2170        public int indexOf(char ch) {
2171            return indexOf(ch, 0);
2172        }
2173    
2174        /**
2175         * Searches the string builder to find the first reference to the specified char.
2176         * 
2177         * @param ch  the character to find
2178         * @param startIndex  the index to start at, invalid index rounded to edge
2179         * @return the first index of the character, or -1 if not found
2180         */
2181        public int indexOf(char ch, int startIndex) {
2182            startIndex = (startIndex < 0 ? 0 : startIndex);
2183            if (startIndex >= size) {
2184                return -1;
2185            }
2186            char[] thisBuf = buffer;
2187            for (int i = startIndex; i < size; i++) {
2188                if (thisBuf[i] == ch) {
2189                    return i;
2190                }
2191            }
2192            return -1;
2193        }
2194    
2195        /**
2196         * Searches the string builder to find the first reference to the specified string.
2197         * <p>
2198         * Note that a null input string will return -1, whereas the JDK throws an exception.
2199         * 
2200         * @param str  the string to find, null returns -1
2201         * @return the first index of the string, or -1 if not found
2202         */
2203        public int indexOf(String str) {
2204            return indexOf(str, 0);
2205        }
2206    
2207        /**
2208         * Searches the string builder to find the first reference to the specified
2209         * string starting searching from the given index.
2210         * <p>
2211         * Note that a null input string will return -1, whereas the JDK throws an exception.
2212         * 
2213         * @param str  the string to find, null returns -1
2214         * @param startIndex  the index to start at, invalid index rounded to edge
2215         * @return the first index of the string, or -1 if not found
2216         */
2217        public int indexOf(String str, int startIndex) {
2218            startIndex = (startIndex < 0 ? 0 : startIndex);
2219            if (str == null || startIndex >= size) {
2220                return -1;
2221            }
2222            int strLen = str.length();
2223            if (strLen == 1) {
2224                return indexOf(str.charAt(0), startIndex);
2225            }
2226            if (strLen == 0) {
2227                return startIndex;
2228            }
2229            if (strLen > size) {
2230                return -1;
2231            }
2232            char[] thisBuf = buffer;
2233            int len = size - strLen + 1;
2234            outer:
2235            for (int i = startIndex; i < len; i++) {
2236                for (int j = 0; j < strLen; j++) {
2237                    if (str.charAt(j) != thisBuf[i + j]) {
2238                        continue outer;
2239                    }
2240                }
2241                return i;
2242            }
2243            return -1;
2244        }
2245    
2246        /**
2247         * Searches the string builder using the matcher to find the first match.
2248         * <p>
2249         * Matchers can be used to perform advanced searching behaviour.
2250         * For example you could write a matcher to find the character 'a'
2251         * followed by a number.
2252         *
2253         * @param matcher  the matcher to use, null returns -1
2254         * @return the first index matched, or -1 if not found
2255         */
2256        public int indexOf(StrMatcher matcher) {
2257            return indexOf(matcher, 0);
2258        }
2259    
2260        /**
2261         * Searches the string builder using the matcher to find the first
2262         * match searching from the given index.
2263         * <p>
2264         * Matchers can be used to perform advanced searching behaviour.
2265         * For example you could write a matcher to find the character 'a'
2266         * followed by a number.
2267         *
2268         * @param matcher  the matcher to use, null returns -1
2269         * @param startIndex  the index to start at, invalid index rounded to edge
2270         * @return the first index matched, or -1 if not found
2271         */
2272        public int indexOf(StrMatcher matcher, int startIndex) {
2273            startIndex = (startIndex < 0 ? 0 : startIndex);
2274            if (matcher == null || startIndex >= size) {
2275                return -1;
2276            }
2277            int len = size;
2278            char[] buf = buffer;
2279            for (int i = startIndex; i < len; i++) {
2280                if (matcher.isMatch(buf, i, startIndex, len) > 0) {
2281                    return i;
2282                }
2283            }
2284            return -1;
2285        }
2286    
2287        //-----------------------------------------------------------------------
2288        /**
2289         * Searches the string builder to find the last reference to the specified char.
2290         * 
2291         * @param ch  the character to find
2292         * @return the last index of the character, or -1 if not found
2293         */
2294        public int lastIndexOf(char ch) {
2295            return lastIndexOf(ch, size - 1);
2296        }
2297    
2298        /**
2299         * Searches the string builder to find the last reference to the specified char.
2300         * 
2301         * @param ch  the character to find
2302         * @param startIndex  the index to start at, invalid index rounded to edge
2303         * @return the last index of the character, or -1 if not found
2304         */
2305        public int lastIndexOf(char ch, int startIndex) {
2306            startIndex = (startIndex >= size ? size - 1 : startIndex);
2307            if (startIndex < 0) {
2308                return -1;
2309            }
2310            for (int i = startIndex; i >= 0; i--) {
2311                if (buffer[i] == ch) {
2312                    return i;
2313                }
2314            }
2315            return -1;
2316        }
2317    
2318        /**
2319         * Searches the string builder to find the last reference to the specified string.
2320         * <p>
2321         * Note that a null input string will return -1, whereas the JDK throws an exception.
2322         * 
2323         * @param str  the string to find, null returns -1
2324         * @return the last index of the string, or -1 if not found
2325         */
2326        public int lastIndexOf(String str) {
2327            return lastIndexOf(str, size - 1);
2328        }
2329    
2330        /**
2331         * Searches the string builder to find the last reference to the specified
2332         * string starting searching from the given index.
2333         * <p>
2334         * Note that a null input string will return -1, whereas the JDK throws an exception.
2335         * 
2336         * @param str  the string to find, null returns -1
2337         * @param startIndex  the index to start at, invalid index rounded to edge
2338         * @return the last index of the string, or -1 if not found
2339         */
2340        public int lastIndexOf(String str, int startIndex) {
2341            startIndex = (startIndex >= size ? size - 1 : startIndex);
2342            if (str == null || startIndex < 0) {
2343                return -1;
2344            }
2345            int strLen = str.length();
2346            if (strLen > 0 && strLen <= size) {
2347                if (strLen == 1) {
2348                    return lastIndexOf(str.charAt(0), startIndex);
2349                }
2350    
2351                outer:
2352                for (int i = startIndex - strLen + 1; i >= 0; i--) {
2353                    for (int j = 0; j < strLen; j++) {
2354                        if (str.charAt(j) != buffer[i + j]) {
2355                            continue outer;
2356                        }
2357                    }
2358                    return i;
2359                }
2360                
2361            } else if (strLen == 0) {
2362                return startIndex;
2363            }
2364            return -1;
2365        }
2366    
2367        /**
2368         * Searches the string builder using the matcher to find the last match.
2369         * <p>
2370         * Matchers can be used to perform advanced searching behaviour.
2371         * For example you could write a matcher to find the character 'a'
2372         * followed by a number.
2373         *
2374         * @param matcher  the matcher to use, null returns -1
2375         * @return the last index matched, or -1 if not found
2376         */
2377        public int lastIndexOf(StrMatcher matcher) {
2378            return lastIndexOf(matcher, size);
2379        }
2380    
2381        /**
2382         * Searches the string builder using the matcher to find the last
2383         * match searching from the given index.
2384         * <p>
2385         * Matchers can be used to perform advanced searching behaviour.
2386         * For example you could write a matcher to find the character 'a'
2387         * followed by a number.
2388         *
2389         * @param matcher  the matcher to use, null returns -1
2390         * @param startIndex  the index to start at, invalid index rounded to edge
2391         * @return the last index matched, or -1 if not found
2392         */
2393        public int lastIndexOf(StrMatcher matcher, int startIndex) {
2394            startIndex = (startIndex >= size ? size - 1 : startIndex);
2395            if (matcher == null || startIndex < 0) {
2396                return -1;
2397            }
2398            char[] buf = buffer;
2399            int endIndex = startIndex + 1;
2400            for (int i = startIndex; i >= 0; i--) {
2401                if (matcher.isMatch(buf, i, 0, endIndex) > 0) {
2402                    return i;
2403                }
2404            }
2405            return -1;
2406        }
2407    
2408        //-----------------------------------------------------------------------
2409        /**
2410         * Creates a tokenizer that can tokenize the contents of this builder.
2411         * <p>
2412         * This method allows the contents of this builder to be tokenized.
2413         * The tokenizer will be setup by default to tokenize on space, tab,
2414         * newline and formfeed (as per StringTokenizer). These values can be
2415         * changed on the tokenizer class, before retrieving the tokens.
2416         * <p>
2417         * The returned tokenizer is linked to this builder. You may intermix
2418         * calls to the buider and tokenizer within certain limits, however
2419         * there is no synchronization. Once the tokenizer has been used once,
2420         * it must be {@link StrTokenizer#reset() reset} to pickup the latest
2421         * changes in the builder. For example:
2422         * <pre>
2423         * StrBuilder b = new StrBuilder();
2424         * b.append("a b ");
2425         * StrTokenizer t = b.asTokenizer();
2426         * String[] tokens1 = t.getTokenArray();  // returns a,b
2427         * b.append("c d ");
2428         * String[] tokens2 = t.getTokenArray();  // returns a,b (c and d ignored)
2429         * t.reset();              // reset causes builder changes to be picked up
2430         * String[] tokens3 = t.getTokenArray();  // returns a,b,c,d
2431         * </pre>
2432         * In addition to simply intermixing appends and tokenization, you can also
2433         * call the set methods on the tokenizer to alter how it tokenizes. Just
2434         * remember to call reset when you want to pickup builder changes.
2435         * <p>
2436         * Calling {@link StrTokenizer#reset(String)} or {@link StrTokenizer#reset(char[])}
2437         * with a non-null value will break the link with the builder.
2438         *
2439         * @return a tokenizer that is linked to this builder
2440         */
2441        public StrTokenizer asTokenizer() {
2442            return new StrBuilderTokenizer();
2443        }
2444    
2445        //-----------------------------------------------------------------------
2446        /**
2447         * Gets the contents of this builder as a Reader.
2448         * <p>
2449         * This method allows the contents of the builder to be read
2450         * using any standard method that expects a Reader.
2451         * <p>
2452         * To use, simply create a <code>StrBuilder</code>, populate it with
2453         * data, call <code>asReader</code>, and then read away.
2454         * <p>
2455         * The internal character array is shared between the builder and the reader.
2456         * This allows you to append to the builder after creating the reader,
2457         * and the changes will be picked up.
2458         * Note however, that no synchronization occurs, so you must perform
2459         * all operations with the builder and the reader in one thread.
2460         * <p>
2461         * The returned reader supports marking, and ignores the flush method.
2462         *
2463         * @return a reader that reads from this builder
2464         */
2465        public Reader asReader() {
2466            return new StrBuilderReader();
2467        }
2468    
2469        //-----------------------------------------------------------------------
2470        /**
2471         * Gets this builder as a Writer that can be written to.
2472         * <p>
2473         * This method allows you to populate the contents of the builder
2474         * using any standard method that takes a Writer.
2475         * <p>
2476         * To use, simply create a <code>StrBuilder</code>,
2477         * call <code>asWriter</code>, and populate away. The data is available
2478         * at any time using the methods of the <code>StrBuilder</code>.
2479         * <p>
2480         * The internal character array is shared between the builder and the writer.
2481         * This allows you to intermix calls that append to the builder and
2482         * write using the writer and the changes will be occur correctly.
2483         * Note however, that no synchronization occurs, so you must perform
2484         * all operations with the builder and the writer in one thread.
2485         * <p>
2486         * The returned writer ignores the close and flush methods.
2487         *
2488         * @return a writer that populates this builder
2489         */
2490        public Writer asWriter() {
2491            return new StrBuilderWriter();
2492        }
2493    
2494        //-----------------------------------------------------------------------
2495    //    /**
2496    //     * Gets a String version of the string builder by calling the internal
2497    //     * constructor of String by reflection.
2498    //     * <p>
2499    //     * WARNING: You must not use the StrBuilder after calling this method
2500    //     * as the buffer is now shared with the String object. To ensure this,
2501    //     * the internal character array is set to null, so you will get
2502    //     * NullPointerExceptions on all method calls.
2503    //     *
2504    //     * @return the builder as a String
2505    //     */
2506    //    public String toSharedString() {
2507    //        try {
2508    //            Constructor con = String.class.getDeclaredConstructor(
2509    //                new Class[] {int.class, int.class, char[].class});
2510    //            con.setAccessible(true);
2511    //            char[] buffer = buf;
2512    //            buf = null;
2513    //            size = -1;
2514    //            nullText = null;
2515    //            return (String) con.newInstance(
2516    //                new Object[] {new Integer(0), new Integer(size), buffer});
2517    //            
2518    //        } catch (Exception ex) {
2519    //            ex.printStackTrace();
2520    //            throw new UnsupportedOperationException("StrBuilder.toSharedString is unsupported: " + ex.getMessage());
2521    //        }
2522    //    }
2523    
2524        //-----------------------------------------------------------------------
2525        /**
2526         * Checks the contents of this builder against another to see if they
2527         * contain the same character content ignoring case.
2528         *
2529         * @param other  the object to check, null returns false
2530         * @return true if the builders contain the same characters in the same order
2531         */
2532        public boolean equalsIgnoreCase(StrBuilder other) {
2533            if (this == other) {
2534                return true;
2535            }
2536            if (this.size != other.size) {
2537                return false;
2538            }
2539            char thisBuf[] = this.buffer;
2540            char otherBuf[] = other.buffer;
2541            for (int i = size - 1; i >= 0; i--) {
2542                char c1 = thisBuf[i];
2543                char c2 = otherBuf[i];
2544                if (c1 != c2 && Character.toUpperCase(c1) != Character.toUpperCase(c2)) {
2545                    return false;
2546                }
2547            }
2548            return true;
2549        }
2550    
2551        /**
2552         * Checks the contents of this builder against another to see if they
2553         * contain the same character content.
2554         *
2555         * @param other  the object to check, null returns false
2556         * @return true if the builders contain the same characters in the same order
2557         */
2558        public boolean equals(StrBuilder other) {
2559            if (this == other) {
2560                return true;
2561            }
2562            if (this.size != other.size) {
2563                return false;
2564            }
2565            char thisBuf[] = this.buffer;
2566            char otherBuf[] = other.buffer;
2567            for (int i = size - 1; i >= 0; i--) {
2568                if (thisBuf[i] != otherBuf[i]) {
2569                    return false;
2570                }
2571            }
2572            return true;
2573        }
2574    
2575        /**
2576         * Checks the contents of this builder against another to see if they
2577         * contain the same character content.
2578         *
2579         * @param obj  the object to check, null returns false
2580         * @return true if the builders contain the same characters in the same order
2581         */
2582        @Override
2583        public boolean equals(Object obj) {
2584            if (obj instanceof StrBuilder) {
2585                return equals((StrBuilder) obj);
2586            }
2587            return false;
2588        }
2589    
2590        /**
2591         * Gets a suitable hash code for this builder.
2592         *
2593         * @return a hash code
2594         */
2595        @Override
2596        public int hashCode() {
2597            char buf[] = buffer;
2598            int hash = 0;
2599            for (int i = size - 1; i >= 0; i--) {
2600                hash = 31 * hash + buf[i];
2601            }
2602            return hash;
2603        }
2604    
2605        //-----------------------------------------------------------------------
2606        /**
2607         * Gets a String version of the string builder, creating a new instance
2608         * each time the method is called.
2609         * <p>
2610         * Note that unlike StringBuffer, the string version returned is
2611         * independent of the string builder.
2612         *
2613         * @return the builder as a String
2614         */
2615        @Override
2616        public String toString() {
2617            return new String(buffer, 0, size);
2618        }
2619    
2620        /**
2621         * Gets a StringBuffer version of the string builder, creating a
2622         * new instance each time the method is called.
2623         *
2624         * @return the builder as a StringBuffer
2625         */
2626        public StringBuffer toStringBuffer() {
2627            return new StringBuffer(size).append(buffer, 0, size);
2628        }
2629    
2630        //-----------------------------------------------------------------------
2631        /**
2632         * Validates parameters defining a range of the builder.
2633         * 
2634         * @param startIndex  the start index, inclusive, must be valid
2635         * @param endIndex  the end index, exclusive, must be valid except
2636         *  that if too large it is treated as end of string
2637         * @return the new string
2638         * @throws IndexOutOfBoundsException if the index is invalid
2639         */
2640        protected int validateRange(int startIndex, int endIndex) {
2641            if (startIndex < 0) {
2642                throw new StringIndexOutOfBoundsException(startIndex);
2643            }
2644            if (endIndex > size) {
2645                endIndex = size;
2646            }
2647            if (startIndex > endIndex) {
2648                throw new StringIndexOutOfBoundsException("end < start");
2649            }
2650            return endIndex;
2651        }
2652    
2653        /**
2654         * Validates parameters defining a single index in the builder.
2655         * 
2656         * @param index  the index, must be valid
2657         * @throws IndexOutOfBoundsException if the index is invalid
2658         */
2659        protected void validateIndex(int index) {
2660            if (index < 0 || index > size) {
2661                throw new StringIndexOutOfBoundsException(index);
2662            }
2663        }
2664    
2665        //-----------------------------------------------------------------------
2666        /**
2667         * Inner class to allow StrBuilder to operate as a tokenizer.
2668         */
2669        class StrBuilderTokenizer extends StrTokenizer {
2670    
2671            /**
2672             * Default constructor.
2673             */
2674            StrBuilderTokenizer() {
2675                super();
2676            }
2677    
2678            /** {@inheritDoc} */
2679            @Override
2680            protected List<String> tokenize(char[] chars, int offset, int count) {
2681                if (chars == null) {
2682                    return super.tokenize(StrBuilder.this.buffer, 0, StrBuilder.this.size());
2683                } else {
2684                    return super.tokenize(chars, offset, count);
2685                }
2686            }
2687    
2688            /** {@inheritDoc} */
2689            @Override
2690            public String getContent() {
2691                String str = super.getContent();
2692                if (str == null) {
2693                    return StrBuilder.this.toString();
2694                } else {
2695                    return str;
2696                }
2697            }
2698        }
2699    
2700        //-----------------------------------------------------------------------
2701        /**
2702         * Inner class to allow StrBuilder to operate as a writer.
2703         */
2704        class StrBuilderReader extends Reader {
2705            /** The current stream position. */
2706            private int pos;
2707            /** The last mark position. */
2708            private int mark;
2709    
2710            /**
2711             * Default constructor.
2712             */
2713            StrBuilderReader() {
2714                super();
2715            }
2716    
2717            /** {@inheritDoc} */
2718            @Override
2719            public void close() {
2720                // do nothing
2721            }
2722    
2723            /** {@inheritDoc} */
2724            @Override
2725            public int read() {
2726                if (ready() == false) {
2727                    return -1;
2728                }
2729                return StrBuilder.this.charAt(pos++);
2730            }
2731    
2732            /** {@inheritDoc} */
2733            @Override
2734            public int read(char b[], int off, int len) {
2735                if (off < 0 || len < 0 || off > b.length ||
2736                        (off + len) > b.length || (off + len) < 0) {
2737                    throw new IndexOutOfBoundsException();
2738                }
2739                if (len == 0) {
2740                    return 0;
2741                }
2742                if (pos >= StrBuilder.this.size()) {
2743                    return -1;
2744                }
2745                if (pos + len > size()) {
2746                    len = StrBuilder.this.size() - pos;
2747                }
2748                StrBuilder.this.getChars(pos, pos + len, b, off);
2749                pos += len;
2750                return len;
2751            }
2752    
2753            /** {@inheritDoc} */
2754            @Override
2755            public long skip(long n) {
2756                if (pos + n > StrBuilder.this.size()) {
2757                    n = StrBuilder.this.size() - pos;
2758                }
2759                if (n < 0) {
2760                    return 0;
2761                }
2762                pos += n;
2763                return n;
2764            }
2765    
2766            /** {@inheritDoc} */
2767            @Override
2768            public boolean ready() {
2769                return pos < StrBuilder.this.size();
2770            }
2771    
2772            /** {@inheritDoc} */
2773            @Override
2774            public boolean markSupported() {
2775                return true;
2776            }
2777    
2778            /** {@inheritDoc} */
2779            @Override
2780            public void mark(int readAheadLimit) {
2781                mark = pos;
2782            }
2783    
2784            /** {@inheritDoc} */
2785            @Override
2786            public void reset() {
2787                pos = mark;
2788            }
2789        }
2790    
2791        //-----------------------------------------------------------------------
2792        /**
2793         * Inner class to allow StrBuilder to operate as a writer.
2794         */
2795        class StrBuilderWriter extends Writer {
2796    
2797            /**
2798             * Default constructor.
2799             */
2800            StrBuilderWriter() {
2801                super();
2802            }
2803    
2804            /** {@inheritDoc} */
2805            @Override
2806            public void close() {
2807                // do nothing
2808            }
2809    
2810            /** {@inheritDoc} */
2811            @Override
2812            public void flush() {
2813                // do nothing
2814            }
2815    
2816            /** {@inheritDoc} */
2817            @Override
2818            public void write(int c) {
2819                StrBuilder.this.append((char) c);
2820            }
2821    
2822            /** {@inheritDoc} */
2823            @Override
2824            public void write(char[] cbuf) {
2825                StrBuilder.this.append(cbuf);
2826            }
2827    
2828            /** {@inheritDoc} */
2829            @Override
2830            public void write(char[] cbuf, int off, int len) {
2831                StrBuilder.this.append(cbuf, off, len);
2832            }
2833    
2834            /** {@inheritDoc} */
2835            @Override
2836            public void write(String str) {
2837                StrBuilder.this.append(str);
2838            }
2839    
2840            /** {@inheritDoc} */
2841            @Override
2842            public void write(String str, int off, int len) {
2843                StrBuilder.this.append(str, off, len);
2844            }
2845        }
2846    
2847    }