/* __ *\ ** ________ ___ / / ___ Scala API ** ** / __/ __// _ | / / / _ | (c) 2006-2009, LAMP/EPFL ** ** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** ** /____/\___/_/ |_/____/_/ | | ** ** |/ ** \* */ // $Id: StringBuilder.scala 16884 2009-01-09 16:52:09Z cunei $ package scalax.collection.mutable import scalax.collection.generic._ import scalax.runtime._ /**
* A mutable sequence of characters. This class provides an API compatible
* with
* java.lang.StringBuilder
.
*
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
/** Same as ensureCapacity
.
* @deprecated use `ensureCapacity` instead. An assignment is misleading
* because it can never decrease the capacity.
*/
@deprecated def capacity_=(n: Int) { ensureCapacity(n) }
/** * 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: *
*n
argument.
* 2
.
*
* If the n
argument is non-positive, this
* method takes no action and simply returns.
*
* 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.
*
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)
/** Does builder contain an element at given index `idx`?
*/
def isDefinedAt(i: Int): Boolean = 0 <= i && i < count
/**
* Removes the Char
at the specified position in this
* sequence. This sequence is shortened by one Char
.
*
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)
compat.Platform.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.
*
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: Any) { append(x) }
/**
* 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.
*
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)
compat.Platform.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) /* @deprecated use appendAll instead. This method is deprecated * because of the possible confusion with `append(Any)`. */ @deprecated def append(x: Seq[Char]): StringBuilder = appendAll(x) /**
* 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) /** @deprecated use appendAll instead. This method is deprecated * because of the possible confusion with `append(Any)`. */ @deprecated def append(x: Array[Char]): StringBuilder = appendAll(x) /**
* 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
.
*
Char
to append.
* @param len the number of Char
s to append.
* @return a reference to this object.
*/
def appendAll(x: Array[Char], offset: Int, len: Int): StringBuilder = {
ensureCapacity(count + len)
compat.Platform.arraycopy(x, offset, array, count, len)
count += len
this
}
/** @deprecated use appendAll instead. This method is deprecated
* because of the possible confusion with `append(Any, Int, Int)`.
*/
@deprecated def append(x: Array[Char], offset: Int, len: Int): StringBuilder =
appendAll(x, offset, len)
/**
* 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.
*
Boolean
.
* @return a reference to this object.
*/
def append(x: Boolean): StringBuilder = append(String.valueOf(this))
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) {
compat.Platform.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)
compat.Platform.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
char
s.
* The characters of the subarray are inserted into this sequence at
* the position indicated by index
. The length of this
* sequence increases by len
Char
s.
*
* @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 Char
s 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)
compat.Platform.arraycopy(array, index, array, index + len, count - index)
compat.Platform.arraycopy(str, offset, array, index, len)
count += len
this
}
/** @deprecated use insertAll instead. This method is deprecated
* because of the possible confusion with `insert(Int, Any, Int, Int)`.
*/
@deprecated def insert(index: Int, str: Array[Char], offset: Int, len: Int): StringBuilder =
insertAll(index, str, offset, len)
/**
* 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.
*
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)
compat.Platform.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)
/* @deprecated use insertAll instead. This method is deprecated
* because of the possible confusion with `insert(Int, Any)`.
*/
@deprecated def insert(at: Int, x: Seq[Char]): StringBuilder =
insertAll(at, x)
/** 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)
compat.Platform.arraycopy(array, at, array, at + len, count - at)
compat.Platform.arraycopy(x, 0, array, at, len)
count += len
this
}
/* @deprecated use insertAll instead. This method is deprecated
* because of the possible confusion with `insert(Int, Any)`.
*/
@deprecated def insert(at: Int, x: Array[Char]): StringBuilder =
insertAll(at, x)
/**
* 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 aBoolean
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 aByte
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 aChar
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)
compat.Platform.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 aShort
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 aInt
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 aLong
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 aFloat
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 aDouble
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
.
*
-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.
*
* 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.
*
* 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.
*
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)
}
object StringBuilder {
type Array[T] = scala.Array[T] // !!!
// 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)
compat.Platform.arraycopy(src, 0, dest, 0, Math.min(src.length, newLength))
dest
}
private def indexOf(source: Array[Char], sourceOffset: Int, sourceCount: Int,
target: Array[Char], targetOffset: Int, targetCount: Int,
fromIndex: Int): Int =
// todo: There are faster string search algorithms than this!
// we should use at least KMP here.
if (fromIndex >= sourceCount)
if (targetCount == 0) sourceCount else -1
else {
val inx = if (fromIndex < 0) 0 else fromIndex
if (targetCount == 0)
inx
else {
val first = target(targetOffset)
val max = sourceOffset + (sourceCount - targetCount)
var i = sourceOffset + inx
while (i <= max) {
/* Look for first character. */
if (source(i) != first) {
i += 1
while (i <= max && source(i) != first) i += 1
}
/* Found first character, now look at the rest of v2 */
if (i <= max) {
var j = i + 1
val end = j + targetCount - 1
var k = targetOffset + 1
while (j < end && source(j) == target(k)) {
j += 1
k += 1
}
if (j == end) {
/* Found whole string. */
return i - sourceOffset
}
} // if
i += 1
} // while
-1
}
}
private def lastIndexOf(source: Array[Char], sourceOffset: Int, sourceCount: Int,
target: Array[Char], targetOffset: Int, targetCount: Int,
fromIndex: Int): Int = {
val rightIndex = sourceCount - targetCount
if (fromIndex < 0) return -1
val inx = if (fromIndex > rightIndex) rightIndex else fromIndex
// Empty string always matches
if (targetCount == 0) return inx
val strLastIndex = targetOffset + targetCount - 1
val strLastChar = target(strLastIndex)
val min = sourceOffset + targetCount - 1
var i = min + fromIndex
while (true) {
while (i >= min && source(i) != strLastChar) i -= 1
if (i < min) return -1
var j = i - 1
val start = j - (targetCount - 1)
var k = strLastIndex - 1
var outerWhile = false
while (j > start && !outerWhile) {
if (source(j) != target(k)) {
j -= 1
k -= 1
i -= 1
outerWhile = true
}
}
if (!outerWhile) return start - sourceOffset + 1
}
-1
}
}