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