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