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