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