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