Java Source Code: org.apache.commons.lang.text.StrBuilder


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