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