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