View Javadoc

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