Commonize StringBuilder

This commit is contained in:
Abduqodiri Qurbonzoda
2019-11-14 07:36:44 +03:00
parent 1431e27a7b
commit 20d02dd0ee
7 changed files with 1572 additions and 61 deletions
@@ -1,18 +1,32 @@
/* /*
* Copyright 2010-2018 JetBrains s.r.o. and Kotlin Programming Language contributors. * Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/ */
package kotlin.text package kotlin.text
public actual class StringBuilder(content: String = "") : Appendable, CharSequence { /**
actual constructor(capacity: Int) : this() {} * A mutable sequence of characters.
*
* String builder can be used to efficiently perform multiple string manipulation operations.
*/
public actual class StringBuilder actual constructor(content: String) : Appendable, CharSequence {
/**
* Constructs an empty string builder with the specified initial [capacity].
*
* In Kotlin/JS implementation of StringBuilder the initial capacity has no effect on the further performance of operations.
*/
actual constructor(capacity: Int) : this() {
this.asDynamic()._capacity = capacity
}
/** Constructs a string builder that contains the same characters as the specified [content] char sequence. */
actual constructor(content: CharSequence) : this(content.toString()) {} actual constructor(content: CharSequence) : this(content.toString()) {}
/** Constructs an empty string builder. */
actual constructor() : this("") actual constructor() : this("")
private var string: String = content private var string: String = if (content !== undefined) content else ""
actual override val length: Int actual override val length: Int
get() = string.asDynamic().length get() = string.asDynamic().length
@@ -32,23 +46,325 @@ public actual class StringBuilder(content: String = "") : Appendable, CharSequen
return this return this
} }
actual override fun append(csq: CharSequence?, start: Int, end: Int): StringBuilder { @UseExperimental(ExperimentalStdlibApi::class)
string += csq.toString().substring(start, end) actual override fun append(csq: CharSequence?, start: Int, end: Int): StringBuilder = this.appendRange(csq, start, end)
return this
}
actual fun append(obj: Any?): StringBuilder {
string += obj.toString()
return this
}
/**
* Reverses the contents of this string builder and returns this instance.
*
* Surrogate pairs included in this string builder are treated as single characters.
* Therefore, the order of the high-low surrogates is never reversed.
*
* Note that the reverse operation may produce new surrogate pairs that were unpaired low-surrogates and high-surrogates before the operation.
* For example, reversing `"\uDC00\uD800"` produces `"\uD800\uDC00"` which is a valid surrogate pair.
*/
actual fun reverse(): StringBuilder { actual fun reverse(): StringBuilder {
string = string.asDynamic().split("").reverse().join("") var reversed = ""
var index = string.length - 1
while (index >= 0) {
val low = string[index--]
if (low.isLowSurrogate() && index >= 0) {
val high = string[index--]
if (high.isHighSurrogate()) {
reversed = reversed + high + low
} else {
reversed = reversed + low + high
}
} else {
reversed += low
}
}
string = reversed
return this return this
} }
/** /**
* Clears the content of this string builder making it empty. * Appends the string representation of the specified object [value] to this string builder and returns this instance.
*
* The overall effect is exactly as if the [value] were converted to a string by the `value.toString()` method,
* and then that string was appended to this string builder.
*/
actual fun append(value: Any?): StringBuilder {
string += value.toString()
return this
}
/**
* Appends the string representation of the specified boolean [value] to this string builder and returns this instance.
*
* The overall effect is exactly as if the [value] were converted to a string by the `value.toString()` method,
* and then that string was appended to this string builder.
*/
@SinceKotlin("1.3")
// @ExperimentalStdlibApi
actual fun append(value: Boolean): StringBuilder {
string += value
return this
}
/**
* Appends characters in the specified character array [value] to this string builder and returns this instance.
*
* Characters are appended in order, starting at the index 0.
*/
@SinceKotlin("1.3")
@ExperimentalStdlibApi
actual fun append(value: CharArray): StringBuilder {
string += value.concatToString()
return this
}
/**
* Appends the specified string [value] to this string builder and returns this instance.
*/
@SinceKotlin("1.3")
// @ExperimentalStdlibApi
actual fun append(value: String): StringBuilder {
this.string += value
return this
}
/**
* Returns the current capacity of this string builder.
*
* The capacity is the maximum length this string builder can have before an allocation occurs.
*
* In Kotlin/JS implementation of StringBuilder the value returned from this method may not indicate the actual size of the backing storage.
*/
@SinceKotlin("1.3")
@ExperimentalStdlibApi
actual fun capacity(): Int = if (this.asDynamic()._capacity !== undefined) maxOf(this.asDynamic()._capacity, length) else length
/**
* Ensures that the capacity of this string builder is at least equal to the specified [minimumCapacity].
*
* If the current capacity is less than the [minimumCapacity], a new backing storage is allocated with greater capacity.
* Otherwise, this method takes no action and simply returns.
*
* In Kotlin/JS implementation of StringBuilder the size of the backing storage is not extended to comply the given [minimumCapacity],
* thus calling this method has no effect on the further performance of operations.
*/
@SinceKotlin("1.3")
@ExperimentalStdlibApi
actual fun ensureCapacity(minimumCapacity: Int) {
if (minimumCapacity > capacity()) {
this.asDynamic()._capacity = minimumCapacity
}
}
/**
* Returns the index within this string builder of the first occurrence of the specified [string].
*
* Returns `-1` if the specified [string] does not occur in this string builder.
*/
@SinceKotlin("1.3")
@ExperimentalStdlibApi
actual fun indexOf(string: String): Int = this.string.asDynamic().indexOf(string)
/**
* Returns the index within this string builder of the first occurrence of the specified [string],
* starting at the specified [startIndex].
*
* Returns `-1` if the specified [string] does not occur in this string builder starting at the specified [startIndex].
*/
@SinceKotlin("1.3")
@ExperimentalStdlibApi
actual fun indexOf(string: String, startIndex: Int): Int = this.string.asDynamic().indexOf(string, startIndex)
/**
* Returns the index within this string builder of the last occurrence of the specified [string].
* The last occurrence of empty string `""` is considered to be at the index equal to `this.length`.
*
* Returns `-1` if the specified [string] does not occur in this string builder.
*/
@SinceKotlin("1.3")
@ExperimentalStdlibApi
actual fun lastIndexOf(string: String): Int = this.string.asDynamic().lastIndexOf(string)
/**
* Returns the index within this string builder of the last occurrence of the specified [string],
* starting from the specified [startIndex] toward the beginning.
*
* Returns `-1` if the specified [string] does not occur in this string builder starting at the specified [startIndex].
*/
@SinceKotlin("1.3")
@ExperimentalStdlibApi
actual fun lastIndexOf(string: String, startIndex: Int): Int {
if (string.isEmpty() && startIndex < 0) return -1
return this.string.asDynamic().lastIndexOf(string, startIndex)
}
/**
* Inserts the string representation of the specified boolean [value] into this string builder at the specified [index] and returns this instance.
*
* The overall effect is exactly as if the [value] were converted to a string by the `value.toString()` method,
* and then that string was inserted into this string builder at the specified [index].
*
* @throws IndexOutOfBoundsException if [index] is less than zero or greater than the length of this string builder.
*/
@SinceKotlin("1.3")
@ExperimentalStdlibApi
actual fun insert(index: Int, value: Boolean): StringBuilder {
AbstractList.checkPositionIndex(index, length)
string = string.substring(0, index) + value + string.substring(index)
return this
}
/**
* Inserts the specified character [value] into this string builder at the specified [index] and returns this instance.
*
* @throws IndexOutOfBoundsException if [index] is less than zero or greater than the length of this string builder.
*/
@SinceKotlin("1.3")
@ExperimentalStdlibApi
actual fun insert(index: Int, value: Char): StringBuilder {
AbstractList.checkPositionIndex(index, length)
string = string.substring(0, index) + value + string.substring(index)
return this
}
/**
* Inserts characters in the specified character array [value] into this string builder at the specified [index] and returns this instance.
*
* The inserted characters go in same order as in the [value] character array, starting at [index].
*
* @throws IndexOutOfBoundsException if [index] is less than zero or greater than the length of this string builder.
*/
@SinceKotlin("1.3")
@ExperimentalStdlibApi
actual fun insert(index: Int, value: CharArray): StringBuilder {
AbstractList.checkPositionIndex(index, length)
string = string.substring(0, index) + value.concatToString() + string.substring(index)
return this
}
/**
* Inserts characters in the specified character sequence [value] into this string builder at the specified [index] and returns this instance.
*
* The inserted characters go in the same order as in the [value] character sequence, starting at [index].
*
* @param index the position in this string builder to insert at.
* @param value the character sequence from which characters are inserted. If [value] is `null`, then the four characters `"null"` are inserted.
*
* @throws IndexOutOfBoundsException if [index] is less than zero or greater than the length of this string builder.
*/
@SinceKotlin("1.3")
@ExperimentalStdlibApi
actual fun insert(index: Int, value: CharSequence?): StringBuilder {
AbstractList.checkPositionIndex(index, length)
string = string.substring(0, index) + value.toString() + string.substring(index)
return this
}
/**
* Inserts the string representation of the specified object [value] into this string builder at the specified [index] and returns this instance.
*
* The overall effect is exactly as if the [value] were converted to a string by the `value.toString()` method,
* and then that string was inserted into this string builder at the specified [index].
*
* @throws IndexOutOfBoundsException if [index] is less than zero or greater than the length of this string builder.
*/
@SinceKotlin("1.3")
@ExperimentalStdlibApi
actual fun insert(index: Int, value: Any?): StringBuilder {
AbstractList.checkPositionIndex(index, length)
string = string.substring(0, index) + value.toString() + string.substring(index)
return this
}
/**
* Inserts the string [value] into this string builder at the specified [index] and returns this instance.
*
* @throws IndexOutOfBoundsException if [index] is less than zero or greater than the length of this string builder.
*/
@SinceKotlin("1.3")
@ExperimentalStdlibApi
actual fun insert(index: Int, value: String): StringBuilder {
AbstractList.checkPositionIndex(index, length)
this.string = this.string.substring(0, index) + value + this.string.substring(index)
return this
}
/**
* Sets the length of this string builder to the specified [newLength].
*
* If the [newLength] is less than the current length, it is changed to the specified [newLength].
* Otherwise, null characters '\u0000' are appended to this string builder until its length is less than the [newLength].
*
* Note that in Kotlin/JS [set] operator function has non-constant execution time complexity.
* Therefore, increasing length of this string builder and then updating each character by index may slow down your program.
*
* @throws IndexOutOfBoundsException or [IllegalArgumentException] if [newLength] is less than zero.
*/
@SinceKotlin("1.3")
@ExperimentalStdlibApi
actual fun setLength(newLength: Int) {
if (newLength < 0) {
throw IllegalArgumentException("Negative new length: $newLength.")
}
if (newLength <= length) {
string = string.substring(0, newLength)
} else {
for (i in length until newLength) {
string += '\u0000'
}
}
}
/**
* Returns a new [String] that contains characters in this string builder at [startIndex] (inclusive) and up to the [length] (exclusive).
*
* @throws IndexOutOfBoundsException if [startIndex] is less than zero or greater than the length of this string builder.
*/
@SinceKotlin("1.3")
@ExperimentalStdlibApi
actual fun substring(startIndex: Int): String {
AbstractList.checkPositionIndex(startIndex, length)
return string.substring(startIndex)
}
/**
* Returns a new [String] that contains characters in this string builder at [startIndex] (inclusive) and up to the [endIndex] (exclusive).
*
* @throws IndexOutOfBoundsException or [IllegalArgumentException] when [startIndex] or [endIndex] is out of range of this string builder indices or when `startIndex > endIndex`.
*/
@SinceKotlin("1.3")
@ExperimentalStdlibApi
actual fun substring(startIndex: Int, endIndex: Int): String {
AbstractList.checkBoundsIndexes(startIndex, endIndex, length)
return string.substring(startIndex, endIndex)
}
/**
* Attempts to reduce storage used for this string builder.
*
* If the backing storage of this string builder is larger than necessary to hold its current contents,
* then it may be resized to become more space efficient.
* Calling this method may, but is not required to, affect the value of the [capacity] property.
*
* In Kotlin/JS implementation of StringBuilder the size of the backing storage is always equal to the length of the string builder.
*/
@SinceKotlin("1.3")
@ExperimentalStdlibApi
actual fun trimToSize() {
if (this.asDynamic()._capacity !== undefined) {
this.asDynamic()._capacity = length
}
}
override fun toString(): String = string
/**
* Clears the content of this string builder making it empty and returns this instance.
* *
* @sample samples.text.Strings.clearStringBuilder * @sample samples.text.Strings.clearStringBuilder
*/ */
@@ -58,16 +374,340 @@ public actual class StringBuilder(content: String = "") : Appendable, CharSequen
return this return this
} }
override fun toString(): String = string /**
* Sets the character at the specified [index] to the specified [value].
*
* @throws IndexOutOfBoundsException if [index] is out of bounds of this string builder.
*/
@SinceKotlin("1.3")
@ExperimentalStdlibApi
public operator fun set(index: Int, value: Char) {
AbstractList.checkElementIndex(index, length)
string = string.substring(0, index) + value + string.substring(index + 1)
}
/**
* Replaces characters in the specified range of this string builder with characters in the specified string [value] and returns this instance.
*
* @param startIndex the beginning (inclusive) of the range to replace.
* @param endIndex the end (exclusive) of the range to replace.
* @param value the string to replace with.
*
* @throws IndexOutOfBoundsException or [IllegalArgumentException] if [startIndex] is less than zero, greater than the length of this string builder, or `startIndex > endIndex`.
*/
@SinceKotlin("1.3")
@ExperimentalStdlibApi
public fun setRange(startIndex: Int, endIndex: Int, value: String): StringBuilder {
checkReplaceRange(startIndex, endIndex, length)
this.string = this.string.substring(0, startIndex) + value + this.string.substring(endIndex)
return this
}
private fun checkReplaceRange(startIndex: Int, endIndex: Int, length: Int) {
if (startIndex < 0 || startIndex > length) {
throw IndexOutOfBoundsException("startIndex: $startIndex, length: $length")
}
if (startIndex > endIndex) {
throw IllegalArgumentException("startIndex($startIndex) > endIndex($endIndex)")
}
}
/**
* Removes the character at the specified [index] from this string builder and returns this instance.
*
* If the `Char` at the specified [index] is part of a supplementary code point, this method does not remove the entire supplementary character.
*
* @param index the index of `Char` to remove.
*
* @throws IndexOutOfBoundsException if [index] is out of bounds of this string builder.
*/
@SinceKotlin("1.3")
@ExperimentalStdlibApi
public fun deleteAt(index: Int): StringBuilder {
AbstractList.checkElementIndex(index, length)
string = string.substring(0, index) + string.substring(index + 1)
return this
}
/**
* Removes characters in the specified range from this string builder and returns this instance.
*
* @param startIndex the beginning (inclusive) of the range to remove.
* @param endIndex the end (exclusive) of the range to remove.
*
* @throws IndexOutOfBoundsException or [IllegalArgumentException] when [startIndex] is out of range of this string builder indices or when `startIndex > endIndex`.
*/
@SinceKotlin("1.3")
@ExperimentalStdlibApi
public fun deleteRange(startIndex: Int, endIndex: Int): StringBuilder {
checkReplaceRange(startIndex, endIndex, length)
string = string.substring(0, startIndex) + string.substring(endIndex)
return this
}
/**
* Copies characters from this string builder into the [destination] character array.
*
* @param destination the array to copy to.
* @param destinationOffset the position in the array to copy to, 0 by default.
* @param startIndex the beginning (inclusive) of the range to copy, 0 by default.
* @param endIndex the end (exclusive) of the range to copy, length of this string builder by default.
*
* @throws IndexOutOfBoundsException or [IllegalArgumentException] when [startIndex] or [endIndex] is out of range of this string builder indices or when `startIndex > endIndex`.
* @throws IndexOutOfBoundsException when the subrange doesn't fit into the [destination] array starting at the specified [destinationOffset],
* or when that index is out of the [destination] array indices range.
*/
@SinceKotlin("1.3")
@ExperimentalStdlibApi
public fun toCharArray(destination: CharArray, destinationOffset: Int = 0, startIndex: Int = 0, endIndex: Int = this.length) {
AbstractList.checkBoundsIndexes(startIndex, endIndex, length)
AbstractList.checkBoundsIndexes(destinationOffset, destinationOffset + endIndex - startIndex, destination.size)
var dstIndex = destinationOffset
for (index in startIndex until endIndex) {
destination[dstIndex++] = string[index]
}
}
/**
* Appends characters in a subarray of the specified character array [value] to this string builder and returns this instance.
*
* Characters are appended in order, starting at specified [startIndex].
*
* @param value the array from which characters are appended.
* @param startIndex the beginning (inclusive) of the subarray to append.
* @param endIndex the end (exclusive) of the subarray to append.
*
* @throws IndexOutOfBoundsException or [IllegalArgumentException] when [startIndex] or [endIndex] is out of range of the [value] array indices or when `startIndex > endIndex`.
*/
@SinceKotlin("1.3")
@ExperimentalStdlibApi
public fun appendRange(value: CharArray, startIndex: Int, endIndex: Int): StringBuilder {
string += value.concatToString(startIndex, endIndex)
return this
}
/**
* Appends a subsequence of the specified character sequence [value] to this string builder and returns this instance.
*
* @param value the character sequence from which a subsequence is appended. If [value] is `null`,
* then characters are appended as if [value] contained the four characters `"null"`.
* @param startIndex the beginning (inclusive) of the subsequence to append.
* @param endIndex the end (exclusive) of the subsequence to append.
*
* @throws IndexOutOfBoundsException or [IllegalArgumentException] when [startIndex] or [endIndex] is out of range of the [value] character sequence indices or when `startIndex > endIndex`.
*/
@SinceKotlin("1.3")
@ExperimentalStdlibApi
public fun appendRange(value: CharSequence?, startIndex: Int, endIndex: Int): StringBuilder {
val stringCsq = value.toString()
AbstractList.checkBoundsIndexes(startIndex, endIndex, stringCsq.length)
string += stringCsq.substring(startIndex, endIndex)
return this
}
/**
* Inserts characters in a subarray of the specified character array [value] into this string builder at the specified [index] and returns this instance.
*
* The inserted characters go in same order as in the [value] array, starting at [index].
*
* @param index the position in this string builder to insert at.
* @param value the array from which characters are inserted.
* @param startIndex the beginning (inclusive) of the subarray to insert.
* @param endIndex the end (exclusive) of the subarray to insert.
*
* @throws IndexOutOfBoundsException or [IllegalArgumentException] when [startIndex] or [endIndex] is out of range of the [value] array indices or when `startIndex > endIndex`.
* @throws IndexOutOfBoundsException if [index] is less than zero or greater than the length of this string builder.
*/
@SinceKotlin("1.3")
@ExperimentalStdlibApi
public fun insertRange(index: Int, value: CharArray, startIndex: Int, endIndex: Int): StringBuilder {
AbstractList.checkPositionIndex(index, this.length)
string = string.substring(0, index) + value.concatToString(startIndex, endIndex) + string.substring(index)
return this
}
/**
* Inserts characters in a subsequence of the specified character sequence [value] into this string builder at the specified [index] and returns this instance.
*
* The inserted characters go in the same order as in the [value] character sequence, starting at [index].
*
* @param index the position in this string builder to insert at.
* @param value the character sequence from which a subsequence is inserted. If [value] is `null`,
* then characters will be inserted as if [value] contained the four characters `"null"`.
* @param startIndex the beginning (inclusive) of the subsequence to insert.
* @param endIndex the end (exclusive) of the subsequence to insert.
*
* @throws IndexOutOfBoundsException or [IllegalArgumentException] when [startIndex] or [endIndex] is out of range of the [value] character sequence indices or when `startIndex > endIndex`.
* @throws IndexOutOfBoundsException if [index] is less than zero or greater than the length of this string builder.
*/
@SinceKotlin("1.3")
@ExperimentalStdlibApi
public fun insertRange(index: Int, value: CharSequence?, startIndex: Int, endIndex: Int): StringBuilder {
AbstractList.checkPositionIndex(index, length)
val stringCsq = value.toString()
AbstractList.checkBoundsIndexes(startIndex, endIndex, stringCsq.length)
string = string.substring(0, index) + stringCsq.substring(startIndex, endIndex) + string.substring(index)
return this
}
} }
/** /**
* Clears the content of this string builder making it empty. * Clears the content of this string builder making it empty and returns this instance.
* *
* @sample samples.text.Strings.clearStringBuilder * @sample samples.text.Strings.clearStringBuilder
*/ */
@SinceKotlin("1.3") @SinceKotlin("1.3")
@Suppress("EXTENSION_SHADOWED_BY_MEMBER", "NOTHING_TO_INLINE") @Suppress("EXTENSION_SHADOWED_BY_MEMBER", "NOTHING_TO_INLINE")
public actual inline fun StringBuilder.clear(): StringBuilder = this.clear() public actual inline fun StringBuilder.clear(): StringBuilder = this.clear()
/**
* Sets the character at the specified [index] to the specified [value].
*
* @throws IndexOutOfBoundsException if [index] is out of bounds of this string builder.
*/
@SinceKotlin("1.3")
@ExperimentalStdlibApi
@Suppress("EXTENSION_SHADOWED_BY_MEMBER", "NOTHING_TO_INLINE")
public actual inline operator fun StringBuilder.set(index: Int, value: Char) = this.set(index, value)
/**
* Replaces characters in the specified range of this string builder with characters in the specified string [value] and returns this instance.
*
* @param startIndex the beginning (inclusive) of the range to replace.
* @param endIndex the end (exclusive) of the range to replace.
* @param value the string to replace with.
*
* @throws IndexOutOfBoundsException or [IllegalArgumentException] if [startIndex] is less than zero, greater than the length of this string builder, or `startIndex > endIndex`.
*/
@SinceKotlin("1.3")
@ExperimentalStdlibApi
@Suppress("EXTENSION_SHADOWED_BY_MEMBER", "NOTHING_TO_INLINE")
public actual inline fun StringBuilder.setRange(startIndex: Int, endIndex: Int, value: String): StringBuilder =
this.setRange(startIndex, endIndex, value)
/**
* Removes the character at the specified [index] from this string builder and returns this instance.
*
* If the `Char` at the specified [index] is part of a supplementary code point, this method does not remove the entire supplementary character.
*
* @param index the index of `Char` to remove.
*
* @throws IndexOutOfBoundsException if [index] is out of bounds of this string builder.
*/
@SinceKotlin("1.3")
@ExperimentalStdlibApi
@Suppress("EXTENSION_SHADOWED_BY_MEMBER", "NOTHING_TO_INLINE")
public actual inline fun StringBuilder.deleteAt(index: Int): StringBuilder = this.deleteAt(index)
/**
* Removes characters in the specified range from this string builder and returns this instance.
*
* @param startIndex the beginning (inclusive) of the range to remove.
* @param endIndex the end (exclusive) of the range to remove.
*
* @throws IndexOutOfBoundsException or [IllegalArgumentException] when [startIndex] is out of range of this string builder indices or when `startIndex > endIndex`.
*/
@SinceKotlin("1.3")
@ExperimentalStdlibApi
@Suppress("EXTENSION_SHADOWED_BY_MEMBER", "NOTHING_TO_INLINE")
public actual inline fun StringBuilder.deleteRange(startIndex: Int, endIndex: Int): StringBuilder = this.deleteRange(startIndex, endIndex)
/**
* Copies characters from this string builder into the [destination] character array.
*
* @param destination the array to copy to.
* @param destinationOffset the position in the array to copy to, 0 by default.
* @param startIndex the beginning (inclusive) of the range to copy, 0 by default.
* @param endIndex the end (exclusive) of the range to copy, length of this string builder by default.
*
* @throws IndexOutOfBoundsException or [IllegalArgumentException] when [startIndex] or [endIndex] is out of range of this string builder indices or when `startIndex > endIndex`.
* @throws IndexOutOfBoundsException when the subrange doesn't fit into the [destination] array starting at the specified [destinationOffset],
* or when that index is out of the [destination] array indices range.
*/
@SinceKotlin("1.3")
@ExperimentalStdlibApi
@Suppress("EXTENSION_SHADOWED_BY_MEMBER", "NOTHING_TO_INLINE", "ACTUAL_FUNCTION_WITH_DEFAULT_ARGUMENTS")
public actual inline fun StringBuilder.toCharArray(destination: CharArray, destinationOffset: Int = 0, startIndex: Int = 0, endIndex: Int = this.length) =
this.toCharArray(destination, destinationOffset, startIndex, endIndex)
/**
* Appends characters in a subarray of the specified character array [value] to this string builder and returns this instance.
*
* Characters are appended in order, starting at specified [startIndex].
*
* @param value the array from which characters are appended.
* @param startIndex the beginning (inclusive) of the subarray to append.
* @param endIndex the end (exclusive) of the subarray to append.
*
* @throws IndexOutOfBoundsException or [IllegalArgumentException] when [startIndex] or [endIndex] is out of range of the [value] array indices or when `startIndex > endIndex`.
*/
@SinceKotlin("1.3")
@ExperimentalStdlibApi
@Suppress("EXTENSION_SHADOWED_BY_MEMBER", "NOTHING_TO_INLINE")
public actual inline fun StringBuilder.appendRange(value: CharArray, startIndex: Int, endIndex: Int): StringBuilder =
this.appendRange(value, startIndex, endIndex)
/**
* Appends a subsequence of the specified character sequence [value] to this string builder and returns this instance.
*
* @param value the character sequence from which a subsequence is appended. If [value] is `null`,
* then characters are appended as if [value] contained the four characters `"null"`.
* @param startIndex the beginning (inclusive) of the subsequence to append.
* @param endIndex the end (exclusive) of the subsequence to append.
*
* @throws IndexOutOfBoundsException or [IllegalArgumentException] when [startIndex] or [endIndex] is out of range of the [value] character sequence indices or when `startIndex > endIndex`.
*/
@SinceKotlin("1.3")
@ExperimentalStdlibApi
@Suppress("EXTENSION_SHADOWED_BY_MEMBER", "NOTHING_TO_INLINE")
public actual inline fun StringBuilder.appendRange(value: CharSequence?, startIndex: Int, endIndex: Int): StringBuilder =
this.appendRange(value, startIndex, endIndex)
/**
* Inserts characters in a subarray of the specified character array [value] into this string builder at the specified [index] and returns this instance.
*
* The inserted characters go in same order as in the [value] array, starting at [index].
*
* @param index the position in this string builder to insert at.
* @param value the array from which characters are inserted.
* @param startIndex the beginning (inclusive) of the subarray to insert.
* @param endIndex the end (exclusive) of the subarray to insert.
*
* @throws IndexOutOfBoundsException or [IllegalArgumentException] when [startIndex] or [endIndex] is out of range of the [value] array indices or when `startIndex > endIndex`.
* @throws IndexOutOfBoundsException if [index] is less than zero or greater than the length of this string builder.
*/
@SinceKotlin("1.3")
@ExperimentalStdlibApi
@Suppress("EXTENSION_SHADOWED_BY_MEMBER", "NOTHING_TO_INLINE")
public actual inline fun StringBuilder.insertRange(index: Int, value: CharArray, startIndex: Int, endIndex: Int): StringBuilder =
this.insertRange(index, value, startIndex, endIndex)
/**
* Inserts characters in a subsequence of the specified character sequence [value] into this string builder at the specified [index] and returns this instance.
*
* The inserted characters go in the same order as in the [value] character sequence, starting at [index].
*
* @param index the position in this string builder to insert at.
* @param value the character sequence from which a subsequence is inserted. If [value] is `null`,
* then characters will be inserted as if [value] contained the four characters `"null"`.
* @param startIndex the beginning (inclusive) of the subsequence to insert.
* @param endIndex the end (exclusive) of the subsequence to insert.
*
* @throws IndexOutOfBoundsException or [IllegalArgumentException] when [startIndex] or [endIndex] is out of range of the [value] character sequence indices or when `startIndex > endIndex`.
* @throws IndexOutOfBoundsException if [index] is less than zero or greater than the length of this string builder.
*/
@SinceKotlin("1.3")
@ExperimentalStdlibApi
@Suppress("EXTENSION_SHADOWED_BY_MEMBER", "NOTHING_TO_INLINE")
public actual inline fun StringBuilder.insertRange(index: Int, value: CharSequence?, startIndex: Int, endIndex: Int): StringBuilder =
this.insertRange(index, value, startIndex, endIndex)
@@ -9,19 +9,155 @@
package kotlin.text package kotlin.text
/** /**
* Sets the character at the specified [index] to the specified [value]. * Clears the content of this string builder making it empty and returns this instance.
*/
@kotlin.internal.InlineOnly
public inline operator fun StringBuilder.set(index: Int, value: Char): Unit = this.setCharAt(index, value)
/**
* Clears the content of this string builder making it empty.
* *
* @sample samples.text.Strings.clearStringBuilder * @sample samples.text.Strings.clearStringBuilder
*/ */
@SinceKotlin("1.3") @SinceKotlin("1.3")
public actual fun StringBuilder.clear(): StringBuilder = apply { setLength(0) } public actual fun StringBuilder.clear(): StringBuilder = apply { setLength(0) }
/**
* Sets the character at the specified [index] to the specified [value].
*
* @throws IndexOutOfBoundsException if [index] is out of bounds of this string builder.
*/
@kotlin.internal.InlineOnly
public actual inline operator fun StringBuilder.set(index: Int, value: Char): Unit = this.setCharAt(index, value)
/**
* Replaces characters in the specified range of this string builder with characters in the specified string [value] and returns this instance.
*
* @param startIndex the beginning (inclusive) of the range to replace.
* @param endIndex the end (exclusive) of the range to replace.
* @param value the string to replace with.
*
* @throws IndexOutOfBoundsException or [IllegalArgumentException] if [startIndex] is less than zero, greater than the length of this string builder, or `startIndex > endIndex`.
*/
@SinceKotlin("1.3")
@ExperimentalStdlibApi
@kotlin.internal.InlineOnly
public actual inline fun StringBuilder.setRange(startIndex: Int, endIndex: Int, value: String): StringBuilder =
this.replace(startIndex, endIndex, value)
/**
* Removes the character at the specified [index] from this string builder and returns this instance.
*
* If the `Char` at the specified [index] is part of a supplementary code point, this method does not remove the entire supplementary character.
*
* @param index the index of `Char` to remove.
*
* @throws IndexOutOfBoundsException if [index] is out of bounds of this string builder.
*/
@SinceKotlin("1.3")
@ExperimentalStdlibApi
@kotlin.internal.InlineOnly
public actual inline fun StringBuilder.deleteAt(index: Int): StringBuilder = this.deleteCharAt(index)
/**
* Removes characters in the specified range from this string builder and returns this instance.
*
* @param startIndex the beginning (inclusive) of the range to remove.
* @param endIndex the end (exclusive) of the range to remove.
*
* @throws IndexOutOfBoundsException or [IllegalArgumentException] when [startIndex] is out of range of this string builder indices or when `startIndex > endIndex`.
*/
@SinceKotlin("1.3")
@ExperimentalStdlibApi
@kotlin.internal.InlineOnly
public actual inline fun StringBuilder.deleteRange(startIndex: Int, endIndex: Int): StringBuilder = this.delete(startIndex, endIndex)
/**
* Copies characters from this string builder into the [destination] character array.
*
* @param destination the array to copy to.
* @param destinationOffset the position in the array to copy to, 0 by default.
* @param startIndex the beginning (inclusive) of the range to copy, 0 by default.
* @param endIndex the end (exclusive) of the range to copy, length of this string builder by default.
*
* @throws IndexOutOfBoundsException or [IllegalArgumentException] when [startIndex] or [endIndex] is out of range of this string builder indices or when `startIndex > endIndex`.
* @throws IndexOutOfBoundsException when the subrange doesn't fit into the [destination] array starting at the specified [destinationOffset],
* or when that index is out of the [destination] array indices range.
*/
@SinceKotlin("1.3")
@ExperimentalStdlibApi
@kotlin.internal.InlineOnly
@Suppress("ACTUAL_FUNCTION_WITH_DEFAULT_ARGUMENTS")
public actual inline fun StringBuilder.toCharArray(destination: CharArray, destinationOffset: Int = 0, startIndex: Int = 0, endIndex: Int = this.length) =
this.getChars(startIndex, endIndex, destination, destinationOffset)
/**
* Appends characters in a subarray of the specified character array [value] to this string builder and returns this instance.
*
* Characters are appended in order, starting at specified [startIndex].
*
* @param value the array from which characters are appended.
* @param startIndex the beginning (inclusive) of the subarray to append.
* @param endIndex the end (exclusive) of the subarray to append.
*
* @throws IndexOutOfBoundsException or [IllegalArgumentException] when [startIndex] or [endIndex] is out of range of the [value] array indices or when `startIndex > endIndex`.
*/
@SinceKotlin("1.3")
@ExperimentalStdlibApi
@kotlin.internal.InlineOnly
public actual inline fun StringBuilder.appendRange(value: CharArray, startIndex: Int, endIndex: Int): StringBuilder =
this.append(value, startIndex, endIndex - startIndex)
/**
* Appends a subsequence of the specified character sequence [value] to this string builder and returns this instance.
*
* @param value the character sequence from which a subsequence is appended. If [value] is `null`,
* then characters are appended as if [value] contained the four characters `"null"`.
* @param startIndex the beginning (inclusive) of the subsequence to append.
* @param endIndex the end (exclusive) of the subsequence to append.
*
* @throws IndexOutOfBoundsException or [IllegalArgumentException] when [startIndex] or [endIndex] is out of range of the [value] character sequence indices or when `startIndex > endIndex`.
*/
@SinceKotlin("1.3")
@ExperimentalStdlibApi
@kotlin.internal.InlineOnly
public actual inline fun StringBuilder.appendRange(value: CharSequence?, startIndex: Int, endIndex: Int): StringBuilder =
this.append(value, startIndex, endIndex)
/**
* Inserts characters in a subarray of the specified character array [value] into this string builder at the specified [index] and returns this instance.
*
* The inserted characters go in same order as in the [value] array, starting at [index].
*
* @param index the position in this string builder to insert at.
* @param value the array from which characters are inserted.
* @param startIndex the beginning (inclusive) of the subarray to insert.
* @param endIndex the end (exclusive) of the subarray to insert.
*
* @throws IndexOutOfBoundsException or [IllegalArgumentException] when [startIndex] or [endIndex] is out of range of the [value] array indices or when `startIndex > endIndex`.
* @throws IndexOutOfBoundsException if [index] is less than zero or greater than the length of this string builder.
*/
@SinceKotlin("1.3")
@ExperimentalStdlibApi
@kotlin.internal.InlineOnly
public actual inline fun StringBuilder.insertRange(index: Int, value: CharArray, startIndex: Int, endIndex: Int): StringBuilder =
this.insert(index, value, startIndex, endIndex - startIndex)
/**
* Inserts characters in a subsequence of the specified character sequence [value] into this string builder at the specified [index] and returns this instance.
*
* The inserted characters go in the same order as in the [value] character sequence, starting at [index].
*
* @param index the position in this string builder to insert at.
* @param value the character sequence from which a subsequence is inserted. If [value] is `null`,
* then characters will be inserted as if [value] contained the four characters `"null"`.
* @param startIndex the beginning (inclusive) of the subsequence to insert.
* @param endIndex the end (exclusive) of the subsequence to insert.
*
* @throws IndexOutOfBoundsException or [IllegalArgumentException] when [startIndex] or [endIndex] is out of range of the [value] character sequence indices or when `startIndex > endIndex`.
* @throws IndexOutOfBoundsException if [index] is less than zero or greater than the length of this string builder.
*/
@SinceKotlin("1.3")
@ExperimentalStdlibApi
@kotlin.internal.InlineOnly
public actual inline fun StringBuilder.insertRange(index: Int, value: CharSequence?, startIndex: Int, endIndex: Int): StringBuilder =
this.insert(index, value, startIndex, endIndex)
private object SystemProperties { private object SystemProperties {
/** Line separator for current system. */ /** Line separator for current system. */
@JvmField @JvmField
@@ -1,26 +0,0 @@
/*
* Copyright 2010-2018 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package test.text
import kotlin.test.*
class StringBuilderJVMTest() {
@Test fun stringBuildWithInitialCapacity() {
val s = buildString(123) {
assertEquals(123, capacity())
}
assertEquals("", s)
}
@Test fun getAndSetChar() {
val sb = StringBuilder("abc")
sb[1] = 'z'
assertEquals("azc", sb.toString())
assertEquals('c', sb[2])
}
}
@@ -8,30 +8,392 @@
package kotlin.text package kotlin.text
/**
* A mutable sequence of characters.
*
* String builder can be used to efficiently perform multiple string manipulation operations.
*/
expect class StringBuilder : Appendable, CharSequence { expect class StringBuilder : Appendable, CharSequence {
/** Constructs an empty string builder. */
constructor() constructor()
/** Constructs an empty string builder with the specified initial [capacity]. */
constructor(capacity: Int) constructor(capacity: Int)
/** Constructs a string builder that contains the same characters as the specified [content] char sequence. */
constructor(content: CharSequence) constructor(content: CharSequence)
/** Constructs a string builder that contains the same characters as the specified [content] string. */
@SinceKotlin("1.3")
// @ExperimentalStdlibApi
constructor(content: String)
override val length: Int override val length: Int
override operator fun get(index: Int): Char override operator fun get(index: Int): Char
override fun subSequence(startIndex: Int, endIndex: Int): CharSequence override fun subSequence(startIndex: Int, endIndex: Int): CharSequence
fun reverse(): StringBuilder
override fun append(c: Char): StringBuilder override fun append(c: Char): StringBuilder
override fun append(csq: CharSequence?): StringBuilder override fun append(csq: CharSequence?): StringBuilder
override fun append(csq: CharSequence?, start: Int, end: Int): StringBuilder override fun append(csq: CharSequence?, start: Int, end: Int): StringBuilder
fun append(obj: Any?): StringBuilder
/**
* Reverses the contents of this string builder and returns this instance.
*
* Surrogate pairs included in this string builder are treated as single characters.
* Therefore, the order of the high-low surrogates is never reversed.
*
* Note that the reverse operation may produce new surrogate pairs that were unpaired low-surrogates and high-surrogates before the operation.
* For example, reversing `"\uDC00\uD800"` produces `"\uD800\uDC00"` which is a valid surrogate pair.
*/
fun reverse(): StringBuilder
/**
* Appends the string representation of the specified object [value] to this string builder and returns this instance.
*
* The overall effect is exactly as if the [value] were converted to a string by the `value.toString()` method,
* and then that string was appended to this string builder.
*/
fun append(value: Any?): StringBuilder
/**
* Appends the string representation of the specified boolean [value] to this string builder and returns this instance.
*
* The overall effect is exactly as if the [value] were converted to a string by the `value.toString()` method,
* and then that string was appended to this string builder.
*/
@SinceKotlin("1.3")
// @ExperimentalStdlibApi
fun append(value: Boolean): StringBuilder
/**
* Appends characters in the specified character array [value] to this string builder and returns this instance.
*
* Characters are appended in order, starting at the index 0.
*/
@SinceKotlin("1.3")
@ExperimentalStdlibApi
fun append(value: CharArray): StringBuilder
/**
* Appends the specified string [value] to this string builder and returns this instance.
*/
@SinceKotlin("1.3")
// @ExperimentalStdlibApi
fun append(value: String): StringBuilder
/**
* Returns the current capacity of this string builder.
*
* The capacity is the maximum length this string builder can have before an allocation occurs.
*/
@SinceKotlin("1.3")
@ExperimentalStdlibApi
fun capacity(): Int
/**
* Ensures that the capacity of this string builder is at least equal to the specified [minimumCapacity].
*
* If the current capacity is less than the [minimumCapacity], a new backing storage is allocated with greater capacity.
* Otherwise, this method takes no action and simply returns.
*/
@SinceKotlin("1.3")
@ExperimentalStdlibApi
fun ensureCapacity(minimumCapacity: Int)
/**
* Returns the index within this string builder of the first occurrence of the specified [string].
*
* Returns `-1` if the specified [string] does not occur in this string builder.
*/
@SinceKotlin("1.3")
@ExperimentalStdlibApi
fun indexOf(string: String): Int
/**
* Returns the index within this string builder of the first occurrence of the specified [string],
* starting at the specified [startIndex].
*
* Returns `-1` if the specified [string] does not occur in this string builder starting at the specified [startIndex].
*/
@SinceKotlin("1.3")
@ExperimentalStdlibApi
fun indexOf(string: String, startIndex: Int): Int
/**
* Returns the index within this string builder of the last occurrence of the specified [string].
* The last occurrence of empty string `""` is considered to be at the index equal to `this.length`.
*
* Returns `-1` if the specified [string] does not occur in this string builder.
*/
@SinceKotlin("1.3")
@ExperimentalStdlibApi
fun lastIndexOf(string: String): Int
/**
* Returns the index within this string builder of the last occurrence of the specified [string],
* starting from the specified [startIndex] toward the beginning.
*
* Returns `-1` if the specified [string] does not occur in this string builder starting at the specified [startIndex].
*/
@SinceKotlin("1.3")
@ExperimentalStdlibApi
fun lastIndexOf(string: String, startIndex: Int): Int
/**
* Inserts the string representation of the specified boolean [value] into this string builder at the specified [index] and returns this instance.
*
* The overall effect is exactly as if the [value] were converted to a string by the `value.toString()` method,
* and then that string was inserted into this string builder at the specified [index].
*
* @throws IndexOutOfBoundsException if [index] is less than zero or greater than the length of this string builder.
*/
@SinceKotlin("1.3")
@ExperimentalStdlibApi
fun insert(index: Int, value: Boolean): StringBuilder
/**
* Inserts the specified character [value] into this string builder at the specified [index] and returns this instance.
*
* @throws IndexOutOfBoundsException if [index] is less than zero or greater than the length of this string builder.
*/
@SinceKotlin("1.3")
@ExperimentalStdlibApi
fun insert(index: Int, value: Char): StringBuilder
/**
* Inserts characters in the specified character array [value] into this string builder at the specified [index] and returns this instance.
*
* The inserted characters go in same order as in the [value] character array, starting at [index].
*
* @throws IndexOutOfBoundsException if [index] is less than zero or greater than the length of this string builder.
*/
@SinceKotlin("1.3")
@ExperimentalStdlibApi
fun insert(index: Int, value: CharArray): StringBuilder
/**
* Inserts characters in the specified character sequence [value] into this string builder at the specified [index] and returns this instance.
*
* The inserted characters go in the same order as in the [value] character sequence, starting at [index].
*
* @param index the position in this string builder to insert at.
* @param value the character sequence from which characters are inserted. If [value] is `null`, then the four characters `"null"` are inserted.
*
* @throws IndexOutOfBoundsException if [index] is less than zero or greater than the length of this string builder.
*/
@SinceKotlin("1.3")
@ExperimentalStdlibApi
fun insert(index: Int, value: CharSequence?): StringBuilder
/**
* Inserts the string representation of the specified object [value] into this string builder at the specified [index] and returns this instance.
*
* The overall effect is exactly as if the [value] were converted to a string by the `value.toString()` method,
* and then that string was inserted into this string builder at the specified [index].
*
* @throws IndexOutOfBoundsException if [index] is less than zero or greater than the length of this string builder.
*/
@SinceKotlin("1.3")
@ExperimentalStdlibApi
fun insert(index: Int, value: Any?): StringBuilder
/**
* Inserts the string [value] into this string builder at the specified [index] and returns this instance.
*
* @throws IndexOutOfBoundsException if [index] is less than zero or greater than the length of this string builder.
*/
@SinceKotlin("1.3")
@ExperimentalStdlibApi
fun insert(index: Int, value: String): StringBuilder
/**
* Sets the length of this string builder to the specified [newLength].
*
* If the [newLength] is less than the current length, it is changed to the specified [newLength].
* Otherwise, null characters '\u0000' are appended to this string builder until its length is less than the [newLength].
*
* Note that in Kotlin/JS [set] operator function has non-constant execution time complexity.
* Therefore, increasing length of this string builder and then updating each character by index may slow down your program.
*
* @throws IndexOutOfBoundsException or [IllegalArgumentException] if [newLength] is less than zero.
*/
@SinceKotlin("1.3")
@ExperimentalStdlibApi
fun setLength(newLength: Int)
/**
* Returns a new [String] that contains characters in this string builder at [startIndex] (inclusive) and up to the [length] (exclusive).
*
* @throws IndexOutOfBoundsException if [startIndex] is less than zero or greater than the length of this string builder.
*/
@SinceKotlin("1.3")
@ExperimentalStdlibApi
fun substring(startIndex: Int): String
/**
* Returns a new [String] that contains characters in this string builder at [startIndex] (inclusive) and up to the [endIndex] (exclusive).
*
* @throws IndexOutOfBoundsException or [IllegalArgumentException] when [startIndex] or [endIndex] is out of range of this string builder indices or when `startIndex > endIndex`.
*/
@SinceKotlin("1.3")
@ExperimentalStdlibApi
fun substring(startIndex: Int, endIndex: Int): String
/**
* Attempts to reduce storage used for this string builder.
*
* If the backing storage of this string builder is larger than necessary to hold its current contents,
* then it may be resized to become more space efficient.
* Calling this method may, but is not required to, affect the value of the [capacity] property.
*/
@SinceKotlin("1.3")
@ExperimentalStdlibApi
fun trimToSize()
} }
/** /**
* Clears the content of this string builder making it empty. * Clears the content of this string builder making it empty and returns this instance.
* *
* @sample samples.text.Strings.clearStringBuilder * @sample samples.text.Strings.clearStringBuilder
*/ */
@SinceKotlin("1.3") @SinceKotlin("1.3")
public expect fun StringBuilder.clear(): StringBuilder public expect fun StringBuilder.clear(): StringBuilder
/**
* Sets the character at the specified [index] to the specified [value].
*
* @throws IndexOutOfBoundsException if [index] is out of bounds of this string builder.
*/
@SinceKotlin("1.3")
@ExperimentalStdlibApi
public expect operator fun StringBuilder.set(index: Int, value: Char)
/**
* Replaces characters in the specified range of this string builder with characters in the specified string [value] and returns this instance.
*
* @param startIndex the beginning (inclusive) of the range to replace.
* @param endIndex the end (exclusive) of the range to replace.
* @param value the string to replace with.
*
* @throws IndexOutOfBoundsException or [IllegalArgumentException] if [startIndex] is less than zero, greater than the length of this string builder, or `startIndex > endIndex`.
*/
@SinceKotlin("1.3")
@ExperimentalStdlibApi
public expect fun StringBuilder.setRange(startIndex: Int, endIndex: Int, value: String): StringBuilder
/**
* Removes the character at the specified [index] from this string builder and returns this instance.
*
* If the `Char` at the specified [index] is part of a supplementary code point, this method does not remove the entire supplementary character.
*
* @param index the index of `Char` to remove.
*
* @throws IndexOutOfBoundsException if [index] is out of bounds of this string builder.
*/
@SinceKotlin("1.3")
@ExperimentalStdlibApi
public expect fun StringBuilder.deleteAt(index: Int): StringBuilder
/**
* Removes characters in the specified range from this string builder and returns this instance.
*
* @param startIndex the beginning (inclusive) of the range to remove.
* @param endIndex the end (exclusive) of the range to remove.
*
* @throws IndexOutOfBoundsException or [IllegalArgumentException] when [startIndex] is out of range of this string builder indices or when `startIndex > endIndex`.
*/
@SinceKotlin("1.3")
@ExperimentalStdlibApi
public expect fun StringBuilder.deleteRange(startIndex: Int, endIndex: Int): StringBuilder
/**
* Copies characters from this string builder into the [destination] character array.
*
* @param destination the array to copy to.
* @param destinationOffset the position in the array to copy to, 0 by default.
* @param startIndex the beginning (inclusive) of the range to copy, 0 by default.
* @param endIndex the end (exclusive) of the range to copy, length of this string builder by default.
*
* @throws IndexOutOfBoundsException or [IllegalArgumentException] when [startIndex] or [endIndex] is out of range of this string builder indices or when `startIndex > endIndex`.
* @throws IndexOutOfBoundsException when the subrange doesn't fit into the [destination] array starting at the specified [destinationOffset],
* or when that index is out of the [destination] array indices range.
*/
@SinceKotlin("1.3")
@ExperimentalStdlibApi
public expect fun StringBuilder.toCharArray(destination: CharArray, destinationOffset: Int = 0, startIndex: Int = 0, endIndex: Int = this.length)
/**
* Appends characters in a subarray of the specified character array [value] to this string builder and returns this instance.
*
* Characters are appended in order, starting at specified [startIndex].
*
* @param value the array from which characters are appended.
* @param startIndex the beginning (inclusive) of the subarray to append.
* @param endIndex the end (exclusive) of the subarray to append.
*
* @throws IndexOutOfBoundsException or [IllegalArgumentException] when [startIndex] or [endIndex] is out of range of the [value] array indices or when `startIndex > endIndex`.
*/
@SinceKotlin("1.3")
@ExperimentalStdlibApi
public expect fun StringBuilder.appendRange(value: CharArray, startIndex: Int, endIndex: Int): StringBuilder
/**
* Appends a subsequence of the specified character sequence [value] to this string builder and returns this instance.
*
* @param value the character sequence from which a subsequence is appended. If [value] is `null`,
* then characters are appended as if [value] contained the four characters `"null"`.
* @param startIndex the beginning (inclusive) of the subsequence to append.
* @param endIndex the end (exclusive) of the subsequence to append.
*
* @throws IndexOutOfBoundsException or [IllegalArgumentException] when [startIndex] or [endIndex] is out of range of the [value] character sequence indices or when `startIndex > endIndex`.
*/
@SinceKotlin("1.3")
@ExperimentalStdlibApi
public expect fun StringBuilder.appendRange(value: CharSequence?, startIndex: Int, endIndex: Int): StringBuilder
/**
* Inserts characters in a subarray of the specified character array [value] into this string builder at the specified [index] and returns this instance.
*
* The inserted characters go in same order as in the [value] array, starting at [index].
*
* @param index the position in this string builder to insert at.
* @param value the array from which characters are inserted.
* @param startIndex the beginning (inclusive) of the subarray to insert.
* @param endIndex the end (exclusive) of the subarray to insert.
*
* @throws IndexOutOfBoundsException or [IllegalArgumentException] when [startIndex] or [endIndex] is out of range of the [value] array indices or when `startIndex > endIndex`.
* @throws IndexOutOfBoundsException if [index] is less than zero or greater than the length of this string builder.
*/
@SinceKotlin("1.3")
@ExperimentalStdlibApi
public expect fun StringBuilder.insertRange(index: Int, value: CharArray, startIndex: Int, endIndex: Int): StringBuilder
/**
* Inserts characters in a subsequence of the specified character sequence [value] into this string builder at the specified [index] and returns this instance.
*
* The inserted characters go in the same order as in the [value] character sequence, starting at [index].
*
* @param index the position in this string builder to insert at.
* @param value the character sequence from which a subsequence is inserted. If [value] is `null`,
* then characters will be inserted as if [value] contained the four characters `"null"`.
* @param startIndex the beginning (inclusive) of the subsequence to insert.
* @param endIndex the end (exclusive) of the subsequence to insert.
*
* @throws IndexOutOfBoundsException or [IllegalArgumentException] when [startIndex] or [endIndex] is out of range of the [value] character sequence indices or when `startIndex > endIndex`.
* @throws IndexOutOfBoundsException if [index] is less than zero or greater than the length of this string builder.
*/
@SinceKotlin("1.3")
@ExperimentalStdlibApi
public expect fun StringBuilder.insertRange(index: Int, value: CharSequence?, startIndex: Int, endIndex: Int): StringBuilder
@Suppress("EXTENSION_SHADOWED_BY_MEMBER")
@Deprecated("Use append(value: Any?) instead", ReplaceWith("append(value = obj)"), DeprecationLevel.WARNING)
@kotlin.internal.InlineOnly
public inline fun StringBuilder.append(obj: Any?): StringBuilder = this.append(obj)
/** /**
* Builds new string by populating newly created [StringBuilder] using provided [builderAction] * Builds new string by populating newly created [StringBuilder] using provided [builderAction]
* and then converting it to [String]. * and then converting it to [String].
+6 -4
View File
@@ -438,13 +438,14 @@ public fun String.substringAfterLast(delimiter: String, missingDelimiterValue: S
* @param startIndex the index of the first character to be replaced. * @param startIndex the index of the first character to be replaced.
* @param endIndex the index of the first character after the replacement to keep in the string. * @param endIndex the index of the first character after the replacement to keep in the string.
*/ */
@UseExperimental(ExperimentalStdlibApi::class)
public fun CharSequence.replaceRange(startIndex: Int, endIndex: Int, replacement: CharSequence): CharSequence { public fun CharSequence.replaceRange(startIndex: Int, endIndex: Int, replacement: CharSequence): CharSequence {
if (endIndex < startIndex) if (endIndex < startIndex)
throw IndexOutOfBoundsException("End index ($endIndex) is less than start index ($startIndex).") throw IndexOutOfBoundsException("End index ($endIndex) is less than start index ($startIndex).")
val sb = StringBuilder() val sb = StringBuilder()
sb.append(this, 0, startIndex) sb.appendRange(this, 0, startIndex)
sb.append(replacement) sb.append(replacement)
sb.append(this, endIndex, length) sb.appendRange(this, endIndex, length)
return sb return sb
} }
@@ -483,6 +484,7 @@ public inline fun String.replaceRange(range: IntRange, replacement: CharSequence
* *
* [endIndex] is not included in the removed part. * [endIndex] is not included in the removed part.
*/ */
@UseExperimental(ExperimentalStdlibApi::class)
public fun CharSequence.removeRange(startIndex: Int, endIndex: Int): CharSequence { public fun CharSequence.removeRange(startIndex: Int, endIndex: Int): CharSequence {
if (endIndex < startIndex) if (endIndex < startIndex)
throw IndexOutOfBoundsException("End index ($endIndex) is less than start index ($startIndex).") throw IndexOutOfBoundsException("End index ($endIndex) is less than start index ($startIndex).")
@@ -491,8 +493,8 @@ public fun CharSequence.removeRange(startIndex: Int, endIndex: Int): CharSequenc
return this.subSequence(0, length) return this.subSequence(0, length)
val sb = StringBuilder(length - (endIndex - startIndex)) val sb = StringBuilder(length - (endIndex - startIndex))
sb.append(this, 0, startIndex) sb.appendRange(this, 0, startIndex)
sb.append(this, endIndex, length) sb.appendRange(this, endIndex, length)
return sb return sb
} }
+3 -2
View File
@@ -302,6 +302,7 @@ public inline class Duration internal constructor(internal val value: Double) :
* *
* @sample samples.time.Durations.toIsoString * @sample samples.time.Durations.toIsoString
*/ */
@UseExperimental(ExperimentalStdlibApi::class)
public fun toIsoString(): String = buildString { public fun toIsoString(): String = buildString {
if (isNegative()) append('-') if (isNegative()) append('-')
append("PT") append("PT")
@@ -321,8 +322,8 @@ public inline class Duration internal constructor(internal val value: Double) :
append('.') append('.')
val nss = nanoseconds.toString().padStart(9, '0') val nss = nanoseconds.toString().padStart(9, '0')
when { when {
nanoseconds % 1_000_000 == 0 -> append(nss, 0, 3) nanoseconds % 1_000_000 == 0 -> appendRange(nss, 0, 3)
nanoseconds % 1_000 == 0 -> append(nss, 0, 6) nanoseconds % 1_000 == 0 -> appendRange(nss, 0, 6)
else -> append(nss) else -> append(nss)
} }
} }
+397 -1
View File
@@ -5,7 +5,9 @@
package test.text package test.text
import kotlin.random.Random
import kotlin.test.* import kotlin.test.*
import kotlin.text.*
class StringBuilderTest { class StringBuilderTest {
@@ -26,7 +28,7 @@ class StringBuilderTest {
@Test fun append() { @Test fun append() {
// this test is needed for JS implementation // this test is needed for JS implementation
assertEquals("em", buildString { assertEquals("em", buildString {
append("element", 2, 4) appendRange("element", 2, 4)
}) })
} }
@@ -87,4 +89,398 @@ class StringBuilderTest {
assertFailsWith<IndexOutOfBoundsException> { assertEquals('t', sb[-1]) } assertFailsWith<IndexOutOfBoundsException> { assertEquals('t', sb[-1]) }
assertFailsWith<IndexOutOfBoundsException> { assertEquals('t', sb[4]) } assertFailsWith<IndexOutOfBoundsException> { assertEquals('t', sb[4]) }
} }
@Test
fun reverse() {
StringBuilder("my reverse test").let { sb ->
sb.reverse()
assertEquals("tset esrever ym", sb.toString())
sb.append('\uD800')
sb.append('\uDC00')
sb.insert(10, "\uDC01\uD801")
sb.insert(0, "\uD802\uDC02")
sb.reverse()
assertEquals("\uD800\uDC00my re\uD801\uDC01verse test\uD802\uDC02", sb.toString())
}
}
@Test
fun appendChar() {
val times = 100
val expected = "a".repeat(times)
val sb = StringBuilder()
repeat(times) { sb.append('a') }
assertEquals(expected, sb.toString())
sb.append('\uD800')
sb.append('\uDC00')
assertEquals(expected + "\uD800\uDC00", sb.toString())
}
@Test
fun appendInt() {
val times = 100
val expected = (0 until times).fold("") { res, idx -> res + idx }
val sb = StringBuilder()
repeat(times) { sb.append(it) }
assertEquals(expected, sb.toString())
val cornerCase = listOf(0, -1, Int.MIN_VALUE, Int.MAX_VALUE)
val expectedCornerCase = cornerCase.fold("") { res, e -> res + e }
for (int in cornerCase) sb.append(int)
assertEquals(expected + expectedCornerCase, sb.toString())
}
@Test
fun appendBoolean() {
StringBuilder().append(true).append(false).append(true).append(true).let { sb ->
assertEquals("truefalsetruetrue", sb.toString())
}
}
@Test
fun appendString() {
val times = 100
val expected = "foo".repeat(times)
StringBuilder().let { sb ->
repeat(times) { sb.append("foo") }
assertEquals(expected, sb.toString())
}
}
@Test
fun appendAny() {
val myAny = object {
override fun toString(): String = "It's My Any!"
}
StringBuilder().let { sb ->
sb.append(null as Any?)
sb.append(myAny)
assertEquals("nullIt's My Any!", sb.toString())
}
}
@Test
fun appendCharArray() {
StringBuilder().let { sb ->
val times = 100
val expected = "foo".repeat(times)
repeat(times) { sb.append(charArrayOf('f', 'o', 'o')) }
assertEquals(expected, sb.toString())
}
StringBuilder().let { sb ->
val charArray = charArrayOf(
'm', 'y', ' ', 'a', 'p', 'p', 'e', 'n', 'd', ' ', 'c', 'h', 'a', 'r', ' ', 'a', 'r', 'r', 'a', 'y', ' ', 't', 'e', 's', 't'
)
String(charArray, 0, 1)
sb.appendRange(charArray, 0, charArray.size /*25*/)
sb.appendRange(charArray, 0, 9)
sb.appendRange(charArray, 15, 25)
sb.appendRange(charArray, 9, 15)
assertEquals("my append char array testmy appendarray test char ", sb.toString())
assertFails { sb.appendRange(charArrayOf('_', '*', '#'), -1, 0) }
assertFails { sb.appendRange(charArrayOf('_', '*', '#'), 0, 4) }
assertFails { sb.appendRange(charArrayOf('_', '*', '#'), 2, 1) }
assertFails { sb.appendRange(charArrayOf('_', '*', '#'), 2, -1) }
}
}
@Test
fun deleteChar() {
StringBuilder("my delete test").let { sb ->
sb.deleteAt(0)
assertEquals("y delete test", sb.toString())
sb.deleteAt(5)
assertEquals("y delte test", sb.toString())
sb.deleteAt(11)
assertEquals("y delte tes", sb.toString())
assertFailsWith<IndexOutOfBoundsException> { sb.deleteAt(11) }
assertFailsWith<IndexOutOfBoundsException> { sb.deleteAt(-1) }
}
}
@Test
fun deleteSubstring() {
StringBuilder("my delete substring test").let { sb ->
sb.deleteRange(0, 2)
assertEquals(" delete substring test", sb.toString())
sb.deleteRange(7, 17)
assertEquals(" delete test", sb.toString())
sb.deleteRange(8, 12)
assertEquals(" delete ", sb.toString())
sb.deleteRange(8, 12)
assertEquals(" delete ", sb.toString())
assertFails { sb.deleteRange(-1, 1) }
assertFails { sb.deleteRange(0, -1) }
assertFails { sb.deleteRange(2, 1) }
assertFails { sb.deleteRange(sb.length + 1, sb.length + 2) }
}
}
@Test
fun capacityTest() {
assertEquals(100, StringBuilder(100).capacity())
StringBuilder("string builder from string capacity test").let { sb ->
assertTrue(sb.capacity() >= sb.length)
}
StringBuilder().let { sb ->
assertTrue(sb.capacity() >= sb.length)
repeat(Random.nextInt(17, 30)) { sb.append('c') }
assertTrue(sb.capacity() >= sb.length)
repeat(Random.nextInt(35, 62)) { sb.insert(0, "s") }
sb.ensureCapacity(1)
assertTrue(sb.capacity() >= sb.length)
sb.ensureCapacity(sb.length * 10)
assertTrue(sb.capacity() >= sb.length * 10)
}
}
@Test
fun indexOf() {
StringBuilder("my indexOf test").let { sb ->
assertEquals(0, sb.indexOf(""))
assertEquals(5, sb.indexOf("", 5))
assertEquals(sb.length, sb.indexOf("", sb.length))
assertEquals(sb.length, sb.indexOf("", 100)) // Java implementation, should be -1
assertEquals(6, sb.indexOf("e"))
assertEquals(12, sb.indexOf("e", 7))
assertEquals(-1, sb.indexOf("e", 13))
assertEquals(-1, sb.indexOf("e", 100))
assertEquals(11, sb.indexOf("test"))
assertEquals(11, sb.indexOf("test", 11))
assertEquals(-1, sb.indexOf("test", 12))
}
}
@Test
fun lastIndexOf() {
StringBuilder("my lastIndexOf test").let { sb ->
assertEquals(sb.length, sb.lastIndexOf(""))
assertEquals(sb.length, sb.lastIndexOf("", 100))
assertEquals(5, sb.lastIndexOf("", 5))
assertEquals(0, sb.lastIndexOf("", 0))
assertEquals(-1, sb.lastIndexOf("", -100))
assertEquals(16, sb.lastIndexOf("e"))
assertEquals(10, sb.lastIndexOf("e", 15))
assertEquals(-1, sb.lastIndexOf("e", 9))
assertEquals(-1, sb.lastIndexOf("e", -100))
assertEquals(15, sb.lastIndexOf("test"))
assertEquals(15, sb.lastIndexOf("test", 15))
assertEquals(-1, sb.lastIndexOf("test", 14))
}
}
@Test
fun insertBoolean() {
StringBuilder().let { sb ->
sb.insert(0, true)
assertEquals("true", sb.toString())
sb.insert(0, false)
assertEquals("falsetrue", sb.toString())
sb.insert(5, true)
assertEquals("falsetruetrue", sb.toString())
assertFailsWith<IndexOutOfBoundsException> { sb.insert(-1, false) }
assertFailsWith<IndexOutOfBoundsException> { sb.insert(sb.length + 1, false) }
}
}
@Test
fun insertChar() {
StringBuilder("my insert char test").let { sb ->
sb.insert(0, '_')
assertEquals("_my insert char test", sb.toString())
sb.insert(10, 'T')
assertEquals("_my insertT char test", sb.toString())
sb.insert(21, '_')
assertEquals("_my insertT char test_", sb.toString())
assertFailsWith<IndexOutOfBoundsException> { sb.insert(-1, '_') }
assertFailsWith<IndexOutOfBoundsException> { sb.insert(sb.length + 1, '_') }
}
}
@Test
fun insertCharArray() {
StringBuilder("my insert CharArray test").let { sb ->
sb.insert(0, charArrayOf('_'))
assertEquals("_my insert CharArray test", sb.toString())
sb.insert(10, charArrayOf('T'))
assertEquals("_my insertT CharArray test", sb.toString())
sb.insertRange(26, charArrayOf('_', '*', '#'), 0, 1)
assertEquals("_my insertT CharArray test_", sb.toString())
assertFailsWith<IndexOutOfBoundsException> { sb.insert(-1, charArrayOf('_')) }
assertFailsWith<IndexOutOfBoundsException> { sb.insert(sb.length + 1, charArrayOf('_')) }
assertFails { sb.insertRange(0, charArrayOf('_', '*', '#'), -1, 0) }
assertFails { sb.insertRange(0, charArrayOf('_', '*', '#'), 0, 4) }
assertFails { sb.insertRange(0, charArrayOf('_', '*', '#'), 2, 1) }
assertFails { sb.insertRange(0, charArrayOf('_', '*', '#'), 2, -1) }
}
}
@Test
fun insertCharSequence() {
StringBuilder("my insert CharSequence test").let { sb ->
sb.insert(0, "MMM" as CharSequence)
assertEquals("MMMmy insert CharSequence test", sb.toString())
sb.insert(12, StringBuilder("T"))
assertEquals("MMMmy insertT CharSequence test", sb.toString())
sb.insertRange(31, "_*#", 0, 1)
assertEquals("MMMmy insertT CharSequence test_", sb.toString())
sb.insertRange(0, null as CharSequence?, 0, 2)
assertEquals("nuMMMmy insertT CharSequence test_", sb.toString())
assertFailsWith<IndexOutOfBoundsException> { sb.insert(-1, "_" as CharSequence) }
assertFailsWith<IndexOutOfBoundsException> { sb.insert(sb.length + 1, StringBuilder("_")) }
assertFails { sb.insertRange(0, null as CharSequence?, -1, 0) }
assertFails { sb.insertRange(0, null as CharSequence?, 0, 5) }
assertFails { sb.insertRange(0, null as CharSequence?, 2, 1) }
}
}
@Test
fun insertAny() {
val myAny = object {
override fun toString(): String = "It's My Any!"
}
StringBuilder().let { sb ->
sb.insert(0, null as Any?)
sb.insert(2, myAny)
assertEquals("nuIt's My Any!ll", sb.toString())
}
}
@Test
fun insertString() {
StringBuilder("my insert string test").let { sb ->
sb.insert(0, "_")
assertEquals("_my insert string test", sb.toString())
sb.insert(10, "TtT")
assertEquals("_my insertTtT string test", sb.toString())
sb.insert(25, "_!_")
assertEquals("_my insertTtT string test_!_", sb.toString())
}
}
@Test
fun setLength() {
StringBuilder("my setLength test").let { sb ->
sb.setLength(17)
assertEquals("my setLength test", sb.toString())
sb.setLength(0)
assertEquals("", sb.toString())
sb.setLength(5)
assertEquals("\u0000\u0000\u0000\u0000\u0000", sb.toString())
assertFails { sb.setLength(-1) }
}
}
@Test
fun substring() {
StringBuilder("my substring test").let { sb ->
assertEquals("my ", sb.substring(0, 3))
assertEquals("substring", sb.substring(3, 12))
assertEquals("ing test", sb.substring(9))
assertEquals("ing test", sb.substring(9, 17))
assertFails { sb.substring(-1) }
assertFails { sb.substring(0, -1) }
assertFails { sb.substring(0, sb.length + 1) }
assertFails { sb.substring(2, 1) }
}
}
@Test
fun trimToSize() {
StringBuilder("my trimToSize test").let { sb ->
assertEquals(18, sb.length)
assertTrue(sb.capacity() >= sb.length)
sb.append('1')
sb.trimToSize()
assertEquals(19, sb.length)
assertTrue(sb.capacity() >= sb.length)
}
}
@Test
fun set() {
StringBuilder("my set test").let { sb ->
sb[0] = 'M'
assertEquals("My set test", sb.toString())
sb[2] = 'm'
assertEquals("Mymset test", sb.toString())
sb[10] = 'T'
assertEquals("Mymset tesT", sb.toString())
assertFailsWith<IndexOutOfBoundsException> { sb[-1] = '_' }
assertFailsWith<IndexOutOfBoundsException> { sb[sb.length] = '_' }
}
}
@Test
fun setRange() {
StringBuilder("my replace test").let { sb ->
sb.setRange(0, 4, "R")
assertEquals("Replace test", sb.toString())
sb.setRange(7, 7, " empty string")
assertEquals("Replace empty string test", sb.toString())
sb.setRange(20, 25, "")
assertEquals("Replace empty string", sb.toString())
sb.setRange(20, 25, "")
assertEquals("Replace empty string", sb.toString())
assertFails { sb.setRange(-1, 0, "") }
assertFails { sb.setRange(0, -1, "") }
assertFails { sb.setRange(2, 1, "") }
assertFails { sb.setRange(sb.length + 1, sb.length + 2, "") }
}
}
@Test
fun toCharArray() {
StringBuilder("my toCharArray test").let { sb ->
val chars = CharArray(10) { '_' }
sb.toCharArray(chars, 8, 0, 2)
assertEquals("________my", String(chars))
sb.toCharArray(chars, 3, 6, 11)
assertEquals("___harArmy", String(chars))
sb.toCharArray(chars, 0, 16, 19)
assertEquals("estharArmy", String(chars))
sb.setLength(5)
assertFails { sb.toCharArray(chars, -1, 0, 1) }
assertFails { sb.toCharArray(chars, chars.size, 0, 1) }
assertFails { sb.toCharArray(chars, 0, -1, 0) }
assertFails { sb.toCharArray(chars, 0, 0, -1) }
assertFails { sb.toCharArray(chars, 0, 2, 1) }
assertFails { sb.toCharArray(chars, 0, 0, sb.length + 1) }
}
}
} }