/* __ *\ ** ________ ___ / / ___ Scala API ** ** / __/ __// _ | / / / _ | (c) 2006-2009, LAMP/EPFL ** ** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** ** /____/\___/_/ |_/____/_/ | | ** ** |/ ** \* */ // $Id$ package scala.collection package mutable import generic._ import compat.Platform.arraycopy import scala.reflect.Manifest /**

* A mutable sequence of characters. This class provides an API compatible * with * java.lang.StringBuilder. *

generic/ * * @author Stephane Micheloud * @author Martin Odersky * @version 2.8 * @since 2.8 */ @serializable @SerialVersionUID(0 - 8525408645367278351L) final class StringBuilder(initCapacity: Int, private val initValue: String) extends Builder[Char, String] with IndexedSeq[Char] { require(initCapacity >= 0) import scala.collection.Seq /** The value is used for character storage. */ private var array = new Array[Char](initCapacity + initValue.length) /** The count is the number of characters used. */ private var count: Int = 0 /** Constructs a string builder with no characters in it and an * initial capacity of 16 characters. */ def this() = this(16, "") /** Constructs a string builder with no characters in it and an * initial capacity specified by the capacity argument. * * @param capacity the initial capacity. * @throws NegativeArraySizeException if the capacity * argument is less than 0. */ def this(capacity: Int) = this(capacity, "") /** Constructs a string builder with initial characters * equal to characters of `str`. */ def this(str: String) = this(16, str) append(initValue) def toArray: Array[Char] = array def length: Int = count def length_=(n: Int) { setLength(n) } /** Clears the builder contents. */ def clear(): Unit = setLength(0) /** Sets the length of the character sequence. * * @param newLength the new length * @throws IndexOutOfBoundsException if the n argument is negative. */ def setLength(n: Int) { require(n >= 0, n) while (count < n) append('\0') count = n } /** Returns the current capacity. The capacity is the amount of storage * available for newly inserted characters, beyond which an allocation * will occur. * * @return the current capacity */ def capacity: Int = array.length /**

* Ensures that the capacity is at least equal to the specified minimum. * If the current capacity is less than the argument, then a new internal * array is allocated with greater capacity. The new capacity is the larger of: *

* *

* If the n argument is non-positive, this * method takes no action and simply returns. *

* * @param n the minimum desired capacity. */ def ensureCapacity(n: Int) { if (n > array.length) { var newsize = array.length * 2 while (n > newsize) newsize = newsize * 2 val newar = new Array[Char](newsize) arraycopy(array, 0, newar, 0, count) array = newar } } /**

* Returns the Char value in this sequence at the specified index. * The first Char value is at index 0, the next at index * 1, and so on, as in array indexing. *

*

* The index argument must be greater than or equal to * 0, and less than the length of this sequence. *

* * @param index the index of the desired Char value. * @return the Char value at the specified index. * @throws IndexOutOfBoundsException if index is * negative or greater than or equal to length(). */ def charAt(index: Int): Char = { if (index < 0 || index >= count) throw new StringIndexOutOfBoundsException(index) array(index) } /** Same as charAt. */ def apply(i: Int): Char = charAt(i) /**

* Removes the Char at the specified position in this * sequence. This sequence is shortened by one Char. *

* * @param index Index of Char to remove * @return This object. * @throws StringIndexOutOfBoundsException if the index * is negative or greater than or equal to length(). */ def deleteCharAt(index: Int): StringBuilder = { if (index < 0 || index >= count) throw new StringIndexOutOfBoundsException(index) arraycopy(array, index + 1, array, index, count - index - 1) count -= 1 this } /**

* The character at the specified index is set to ch. This * sequence is altered to represent a new character sequence that is * identical to the old character sequence, except that it contains the * character ch at position index. *

*

* The index argument must be greater than or equal to * 0, and less than the length of this sequence. *

* * @param index the index of the character to modify. * @param ch the new character. * @throws IndexOutOfBoundsException if index is * negative or greater than or equal to length(). */ def setCharAt(index: Int, ch: Char) { if (index < 0 || index >= count) throw new StringIndexOutOfBoundsException(index) array(index) = ch } /** Same as setCharAt. */ def update(i: Int, c: Char) { setCharAt(i, c) } /** Returns a new String that contains a subsequence of * characters currently contained in this character sequence. The * substring begins at the specified index and extends to the end of * this sequence. * * @param start The beginning index, inclusive. * @return The new string. * @throws StringIndexOutOfBoundsException if start is * less than zero, or greater than the length of this object. */ def substring(start: Int): String = substring(start, count) /** Returns a new String that contains a subsequence of * characters currently contained in this sequence. The * substring begins at the specified start and * extends to the character at index end - 1. * * @param start The beginning index, inclusive. * @param end The ending index, exclusive. * @return The new string. * @throws StringIndexOutOfBoundsException if start * or end are negative or greater than * length(), or start is * greater than end. */ def substring(start: Int, end: Int): String = { if (start < 0) throw new StringIndexOutOfBoundsException(start) if (end > count) throw new StringIndexOutOfBoundsException(end) if (start > end) throw new StringIndexOutOfBoundsException(end - start) new String(array, start, end - start) } def subSequence(start: Int, end: Int): java.lang.CharSequence = substring(start, end) /* Appends the string representation of the Any argument. */ def +=(x: Char): this.type = { append(x); this } def +(x: Char): this.type = { +=(x); this } /**

* Appends the string representation of the Any * argument. *

*

* The argument is converted to a string as if by the method * String.valueOf, and the characters of that * string are then appended to this sequence. *

* * @param x an Any object. * @return a reference to this object. */ def append(x: Any): StringBuilder = append(String.valueOf(x)) /** Appends the specified string to this character sequence. * * @param s a string. * @return a reference to this object. */ def append(s: String): StringBuilder = { val str = if (s == null) "null" else s val len = str.length ensureCapacity(count + len) str.getChars(0, len, array, count) count += len this } /** Appends the specified string builder to this sequence. * * @param sb * @return */ def append(sb: StringBuilder): StringBuilder = if (sb == null) append("null") else { val len = sb.length ensureCapacity(count + len) arraycopy(sb.toArray, 0, array, count, len) count += len this } /**

* Appends the string representation of the Char sequence * argument to this sequence. *

*

* The characters of the sequence argument are appended, in order, * to the contents of this sequence. The length of this sequence * increases by the length of the argument. *

* * @param x the characters to be appended. * @return a reference to this object. */ def appendAll(x: Seq[Char]): StringBuilder = appendAll(x.toArray, 0, x.length) /**

* Appends the string representation of the Char array * argument to this sequence. *

*

* The characters of the array argument are appended, in order, to * the contents of this sequence. The length of this sequence * increases by the length of the argument. *

* * @param x the characters to be appended. * @return a reference to this object. */ def appendAll(x: Array[Char]): StringBuilder = appendAll(x, 0, x.length) /**

* Appends the string representation of a subarray of the * char array argument to this sequence. *

*

* Characters of the Char array x, starting at * index offset, are appended, in order, to the contents * of this sequence. The length of this sequence increases * by the value of len. *

* * @param x the characters to be appended. * @param offset the index of the first Char to append. * @param len the number of Chars to append. * @return a reference to this object. */ def appendAll(x: Array[Char], offset: Int, len: Int): StringBuilder = { ensureCapacity(count + len) arraycopy(x, offset, array, count, len) count += len this } /**

* Appends the string representation of the Boolean * argument to the sequence. *

*

* The argument is converted to a string as if by the method * String.valueOf, and the characters of that * string are then appended to this sequence. *

* * @param x a Boolean. * @return a reference to this object. */ def append(x: Boolean): StringBuilder = append(String.valueOf(x)) def append(x: Byte): StringBuilder = append(String.valueOf(x)) def append(x: Char): StringBuilder = { ensureCapacity(count + 1) array(count) = x count += 1 this } def append(x: Short): StringBuilder = append(String.valueOf(x)) def append(x: Int): StringBuilder = append(String.valueOf(x)) def append(x: Long): StringBuilder = append(String.valueOf(x)) def append(x: Float): StringBuilder = append(String.valueOf(x)) def append(x: Double): StringBuilder = append(String.valueOf(x)) /** Removes the characters in a substring of this sequence. * The substring begins at the specified start and extends to * the character at index end - 1 or to the end of the * sequence if no such character exists. If * start is equal to end, no changes are made. * * @param start The beginning index, inclusive. * @param end The ending index, exclusive. * @return This object. * @throws StringIndexOutOfBoundsException if start * is negative, greater than length(), or * greater than end. */ def delete(start: Int, end: Int): StringBuilder = { if (start < 0 || start > end) throw new StringIndexOutOfBoundsException(start) val end0 = if (end > count) count else end val len = end0 - start if (len > 0) { arraycopy(array, start + len, array, start, count - end0) count -= len } this } /** Replaces the characters in a substring of this sequence * with characters in the specified String. The substring * begins at the specified start and extends to the character * at index end - 1 or to the end of the sequence if no such * character exists. First the characters in the substring are removed and * then the specified String is inserted at start. * * @param start The beginning index, inclusive. * @param end The ending index, exclusive. * @param str String that will replace previous contents. * @return This object. * @throws StringIndexOutOfBoundsException if start * is negative, greater than length(), or * greater than end. */ def replace(start: Int, end: Int, str: String) { if (start < 0 || start > count || start > end) throw new StringIndexOutOfBoundsException(start) val end0 = if (end > count) count else end val len = str.length() val newCount = count + len - (end0 - start) ensureCapacity(newCount) arraycopy(array, end, array, start + len, count - end) str.getChars(0, len, array, start) count = newCount this } /** Inserts the string representation of a subarray of the str * array argument into this sequence. The subarray begins at the specified * offset and extends len chars. * The characters of the subarray are inserted into this sequence at * the position indicated by index. The length of this * sequence increases by len Chars. * * @param index position at which to insert subarray. * @param str a Char array. * @param offset the index of the first char in subarray to * be inserted. * @param len the number of Chars in the subarray to * be inserted. * @return This object * @throws StringIndexOutOfBoundsException if index * is negative or greater than length(), or * offset or len are negative, or * (offset+len) is greater than * str.length. */ def insertAll(index: Int, str: Array[Char], offset: Int, len: Int): StringBuilder = { if (index < 0 || index > count) throw new StringIndexOutOfBoundsException(index) if (offset < 0 || len < 0 || offset > str.length - len) throw new StringIndexOutOfBoundsException( "offset " + offset + ", len " + len + ", str.length " + str.length) ensureCapacity(count + len) arraycopy(array, index, array, index + len, count - index) arraycopy(str, offset, array, index, len) count += len this } /**

* Inserts the string representation of the Any * argument into this character sequence. *

*

* The second argument is converted to a string as if by the method * String.valueOf, and the characters of that * string are then inserted into this sequence at the indicated * offset. *

*

* The offset argument must be greater than or equal to * 0, and less than or equal to the length of this * sequence. *

* * @param offset the offset. * @param x an Any value. * @return a reference to this object. * @throws StringIndexOutOfBoundsException if the offset is invalid. */ def insert(at: Int, x: Any): StringBuilder = insert(at, String.valueOf(x)) /** Inserts the string into this character sequence. * * @param at the offset position. * @param x a string. * @return a reference to this object. * @throws StringIndexOutOfBoundsException if the offset is invalid. */ def insert(at: Int, x: String): StringBuilder = { if (at < 0 || at > count) throw new StringIndexOutOfBoundsException(at) val str = if (x == null) "null" else x val len = str.length ensureCapacity(count + len) arraycopy(array, at, array, at + len, count - at) str.getChars(0, len, array, at) count += len this } /** Inserts the string representation of the Char sequence * argument into this sequence. * * @param at the offset position. * @param x a character sequence. * @return a reference to this object. * @throws StringIndexOutOfBoundsException if the offset is invalid. */ def insertAll(at: Int, x: Seq[Char]): StringBuilder = insertAll(at, x.toArray) /** Inserts the string representation of the Char array * argument into this sequence. * * @param at the offset position. * @param x a character array. * @return a reference to this object. * @throws StringIndexOutOfBoundsException if the offset is invalid. */ def insertAll(at: Int, x: Array[Char]): StringBuilder = { if (at < 0 || at > count) throw new StringIndexOutOfBoundsException(at) val len = x.length ensureCapacity(count + len) arraycopy(array, at, array, at + len, count - at) arraycopy(x, 0, array, at, len) count += len this } /**

* Inserts the string representation of the Boolean argument * into this sequence. *

*

* The offset argument must be greater than or equal to 0, and less than * or equal to the length of this sequence. *

* * @param at the offset position. * @param x a Boolean value. * @return a reference to this object. */ def insert(at: Int, x: Boolean): StringBuilder = insert(at, String.valueOf(x)) /**

* Inserts the string representation of the Byte argument * into this sequence. *

*

* The offset argument must be greater than or equal to 0, and less than * or equal to the length of this sequence. *

* * @param at the offset position. * @param x a Byte value. * @return a reference to this object. */ def insert(at: Int, x: Byte): StringBuilder = insert(at, String.valueOf(x)) /**

* Inserts the string representation of the Char argument * into this sequence. *

*

* The offset argument must be greater than or equal to 0, and less than * or equal to the length of this sequence. *

* * @param at the offset position. * @param x a Char value. * @return a reference to this object. */ def insert(at: Int, x: Char): StringBuilder = { if (at < 0 || at > count) throw new StringIndexOutOfBoundsException(at) ensureCapacity(count + 1) arraycopy(array, at, array, at + 1, count - at) array(at) = x count += 1 this } /**

* Inserts the string representation of the Short argument * into this sequence. *

*

* The offset argument must be greater than or equal to 0, and less than * or equal to the length of this sequence. *

* * @param at the offset position. * @param x a Short value. * @return a reference to this object. */ def insert(at: Int, x: Short): StringBuilder = insert(at, String.valueOf(x)) /**

* Inserts the string representation of the Int argument * into this sequence. *

*

* The offset argument must be greater than or equal to 0, and less than * or equal to the length of this sequence. *

* * @param at the offset position. * @param x a Int value. * @return a reference to this object. */ def insert(at: Int, x: Int): StringBuilder = insert(at, String.valueOf(x)) /**

* Inserts the string representation of the Long argument * into this sequence. *

*

* The offset argument must be greater than or equal to 0, and less than * or equal to the length of this sequence. *

* * @param at the offset position. * @param x a Long value. * @return a reference to this object. */ def insert(at: Int, x: Long): StringBuilder = insert(at, String.valueOf(x)) /**

* Inserts the string representation of the Float argument * into this sequence. *

*

* The offset argument must be greater than or equal to 0, and less than * or equal to the length of this sequence. *

* * @param at the offset position. * @param x a Float value. * @return a reference to this object. */ def insert(at: Int, x: Float): StringBuilder = insert(at, String.valueOf(x)) /**

* Inserts the string representation of the Double argument * into this sequence. *

*

* The offset argument must be greater than or equal to 0, and less than * or equal to the length of this sequence. *

* * @param at the offset position. * @param x a Double value. * @return a reference to this object. */ def insert(at: Int, x: Double): StringBuilder = insert(at, String.valueOf(x)) /**

* Returns the index within this string of the first occurrence of the * specified substring. The integer returned is the smallest value * k such that: *

*
   *  this.toString().startsWith(str, k)
*
*

* is true. *

* * @param str any string. * @return if the string argument occurs as a substring within this * object, then the index of the first character of the first * such substring is returned; if it does not occur as a * substring, -1 is returned. * @throws NullPointerException if str is null. */ def indexOf(str: String): Int = indexOf(str, 0) /**

* Returns the index within this string of the first occurrence of the * specified substring, starting at the specified index. The integer * returned is the smallest value k for which: *

   *    k >= Math.min(fromIndex, str.length()) &&
   *                   this.toString().startsWith(str, k)
*

* If no such value of k exists, then -1 * is returned. *

* * @param str the substring for which to search. * @param fromIndex the index from which to start the search. * @return the index within this string of the first occurrence * of the specified substring, starting at the specified index. */ def indexOf(str: String, fromIndex: Int): Int = indexOfSeq(str.toIndexedSeq, fromIndex) /**

* Returns the index within this string of the rightmost occurrence * of the specified substring. The rightmost empty string "" is * considered to occur at the index value this.length(). * The returned index is the largest value k such that *

*
   *  this.toString().startsWith(str, k)
*
*

* is true. *

* * @param str the substring to search for. * @return if the string argument occurs one or more times as a substring * within this object, then the index of the first character of * the last such substring is returned. If it does not occur as * a substring, -1 is returned. * @throws NullPointerException if str is null. */ def lastIndexOf(str: String): Int = lastIndexOf(str, count) /**

* Returns the index within this string of the last occurrence of the * specified substring. The integer returned is the largest value * k such that: *

val
   *    k <= Math.min(fromIndex, str.length()) &&
   *                   this.toString().startsWith(str, k)
*

* If no such value of k exists, then -1 * is returned. *

* * @param str the substring to search for. * @param fromIndex the index to start the search from. * @return the index within this sequence of the last occurrence * of the specified substring. */ def lastIndexOf(str: String, fromIndex: Int): Int = lastIndexOfSeq(str.toIndexedSeq, fromIndex) /**

* Causes this character sequence to be replaced by the reverse of the * sequence. If there are any surrogate pairs included in the sequence, * these are treated as single characters for the reverse operation. * Thus, the order of the high-low surrogates is never reversed. *

*

* Let n be the character length of this character sequence * (not the length in Char values) just prior to * execution of the reverse method. Then the * character at index k in the new character sequence is * equal to the character at index n-k-1 in the old * character sequence. *

* * @return a reference to this object. */ override def reverse(): StringBuilder = { var hasSurrogate = false val n = count - 1 var j = (n-1) >> 1 while (j >= 0) { val temp = array(j) val temp2 = array(n - j) if (!hasSurrogate) hasSurrogate = (temp >= Character.MIN_HIGH_SURROGATE && temp <= Character.MAX_LOW_SURROGATE) || (temp2 >= Character.MIN_HIGH_SURROGATE && temp2 <= Character.MAX_LOW_SURROGATE) array(j) = temp2 array(n - j) = temp j -= 1 } if (hasSurrogate) { // Reverse back all valid surrogate pairs var i = 0 while (i < count - 1) { val c2 = array(i) if (Character.isLowSurrogate(c2)) { val c1 = array(i + 1) if (Character.isHighSurrogate(c1)) { array(i) = c1; i += 1 array(i) = c2 } } i += 1 } } this } /** Returns a string representing the data in this sequence. * A new String object is allocated and initialized to * contain the character sequence currently represented by this * object. This String is then returned. Subsequent * changes to this sequence do not affect the contents of the * String. * * @return a string representation of this sequence of characters. */ override def toString: String = new String(array, 0, count) def result(): String = toString } object StringBuilder { // method java.util.Arrays.copyOf exists since 1.6 private def copyOf(src: Array[Char], newLength: Int): Array[Char] = { val dest = new Array[Char](newLength) arraycopy(src, 0, dest, 0, Math.min(src.length, newLength)) dest } }