339 lines
13 KiB
Kotlin
339 lines
13 KiB
Kotlin
/*
|
|
* Copyright 2010-2021 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 kotlin.text
|
|
|
|
import kotlin.js.RegExp
|
|
|
|
/**
|
|
* Converts the characters in the specified array to a string.
|
|
*/
|
|
@SinceKotlin("1.2")
|
|
@Deprecated("Use CharArray.concatToString() instead", ReplaceWith("chars.concatToString()"))
|
|
@DeprecatedSinceKotlin(warningSince = "1.4", errorSince = "1.5")
|
|
public actual fun String(chars: CharArray): String {
|
|
var result = ""
|
|
for (char in chars) {
|
|
result += char
|
|
}
|
|
return result
|
|
}
|
|
|
|
/**
|
|
* Converts the characters from a portion of the specified array to a string.
|
|
*
|
|
* @throws IndexOutOfBoundsException if either [offset] or [length] are less than zero
|
|
* or `offset + length` is out of [chars] array bounds.
|
|
*/
|
|
@SinceKotlin("1.2")
|
|
@Deprecated("Use CharArray.concatToString(startIndex, endIndex) instead", ReplaceWith("chars.concatToString(offset, offset + length)"))
|
|
@DeprecatedSinceKotlin(warningSince = "1.4", errorSince = "1.5")
|
|
public actual fun String(chars: CharArray, offset: Int, length: Int): String {
|
|
if (offset < 0 || length < 0 || chars.size - offset < length)
|
|
throw IndexOutOfBoundsException("size: ${chars.size}; offset: $offset; length: $length")
|
|
var result = ""
|
|
for (index in offset until offset + length) {
|
|
result += chars[index]
|
|
}
|
|
return result
|
|
}
|
|
|
|
/**
|
|
* Concatenates characters in this [CharArray] into a String.
|
|
*/
|
|
@SinceKotlin("1.4")
|
|
@WasExperimental(ExperimentalStdlibApi::class)
|
|
public actual fun CharArray.concatToString(): String {
|
|
var result = ""
|
|
for (char in this) {
|
|
result += char
|
|
}
|
|
return result
|
|
}
|
|
|
|
/**
|
|
* Concatenates characters in this [CharArray] or its subrange into a String.
|
|
*
|
|
* @param startIndex the beginning (inclusive) of the subrange of characters, 0 by default.
|
|
* @param endIndex the end (exclusive) of the subrange of characters, size of this array by default.
|
|
*
|
|
* @throws IndexOutOfBoundsException if [startIndex] is less than zero or [endIndex] is greater than the size of this array.
|
|
* @throws IllegalArgumentException if [startIndex] is greater than [endIndex].
|
|
*/
|
|
@SinceKotlin("1.4")
|
|
@WasExperimental(ExperimentalStdlibApi::class)
|
|
@Suppress("ACTUAL_FUNCTION_WITH_DEFAULT_ARGUMENTS")
|
|
public actual fun CharArray.concatToString(startIndex: Int = 0, endIndex: Int = this.size): String {
|
|
AbstractList.checkBoundsIndexes(startIndex, endIndex, this.size)
|
|
var result = ""
|
|
for (index in startIndex until endIndex) {
|
|
result += this[index]
|
|
}
|
|
return result
|
|
}
|
|
|
|
/**
|
|
* Returns a [CharArray] containing characters of this string.
|
|
*/
|
|
@SinceKotlin("1.4")
|
|
@WasExperimental(ExperimentalStdlibApi::class)
|
|
public actual fun String.toCharArray(): CharArray {
|
|
return CharArray(length) { get(it) }
|
|
}
|
|
|
|
/**
|
|
* Returns a [CharArray] containing characters of this string or its substring.
|
|
*
|
|
* @param startIndex the beginning (inclusive) of the substring, 0 by default.
|
|
* @param endIndex the end (exclusive) of the substring, length of this string by default.
|
|
*
|
|
* @throws IndexOutOfBoundsException if [startIndex] is less than zero or [endIndex] is greater than the length of this string.
|
|
* @throws IllegalArgumentException if [startIndex] is greater than [endIndex].
|
|
*/
|
|
@SinceKotlin("1.4")
|
|
@WasExperimental(ExperimentalStdlibApi::class)
|
|
@Suppress("ACTUAL_FUNCTION_WITH_DEFAULT_ARGUMENTS")
|
|
public actual fun String.toCharArray(startIndex: Int = 0, endIndex: Int = this.length): CharArray {
|
|
AbstractList.checkBoundsIndexes(startIndex, endIndex, length)
|
|
return CharArray(endIndex - startIndex) { get(startIndex + it) }
|
|
}
|
|
|
|
/**
|
|
* Decodes a string from the bytes in UTF-8 encoding in this array.
|
|
*
|
|
* Malformed byte sequences are replaced by the replacement char `\uFFFD`.
|
|
*/
|
|
@SinceKotlin("1.4")
|
|
@WasExperimental(ExperimentalStdlibApi::class)
|
|
public actual fun ByteArray.decodeToString(): String {
|
|
return decodeUtf8(this, 0, size, false)
|
|
}
|
|
|
|
/**
|
|
* Decodes a string from the bytes in UTF-8 encoding in this array or its subrange.
|
|
*
|
|
* @param startIndex the beginning (inclusive) of the subrange to decode, 0 by default.
|
|
* @param endIndex the end (exclusive) of the subrange to decode, size of this array by default.
|
|
* @param throwOnInvalidSequence specifies whether to throw an exception on malformed byte sequence or replace it by the replacement char `\uFFFD`.
|
|
*
|
|
* @throws IndexOutOfBoundsException if [startIndex] is less than zero or [endIndex] is greater than the size of this array.
|
|
* @throws IllegalArgumentException if [startIndex] is greater than [endIndex].
|
|
* @throws CharacterCodingException if the byte array contains malformed UTF-8 byte sequence and [throwOnInvalidSequence] is true.
|
|
*/
|
|
@SinceKotlin("1.4")
|
|
@WasExperimental(ExperimentalStdlibApi::class)
|
|
@Suppress("ACTUAL_FUNCTION_WITH_DEFAULT_ARGUMENTS")
|
|
public actual fun ByteArray.decodeToString(
|
|
startIndex: Int = 0,
|
|
endIndex: Int = this.size,
|
|
throwOnInvalidSequence: Boolean = false
|
|
): String {
|
|
AbstractList.checkBoundsIndexes(startIndex, endIndex, this.size)
|
|
return decodeUtf8(this, startIndex, endIndex, throwOnInvalidSequence)
|
|
}
|
|
|
|
/**
|
|
* Encodes this string to an array of bytes in UTF-8 encoding.
|
|
*
|
|
* Any malformed char sequence is replaced by the replacement byte sequence.
|
|
*/
|
|
@SinceKotlin("1.4")
|
|
@WasExperimental(ExperimentalStdlibApi::class)
|
|
public actual fun String.encodeToByteArray(): ByteArray {
|
|
return encodeUtf8(this, 0, length, false)
|
|
}
|
|
|
|
/**
|
|
* Encodes this string or its substring to an array of bytes in UTF-8 encoding.
|
|
*
|
|
* @param startIndex the beginning (inclusive) of the substring to encode, 0 by default.
|
|
* @param endIndex the end (exclusive) of the substring to encode, length of this string by default.
|
|
* @param throwOnInvalidSequence specifies whether to throw an exception on malformed char sequence or replace.
|
|
*
|
|
* @throws IndexOutOfBoundsException if [startIndex] is less than zero or [endIndex] is greater than the length of this string.
|
|
* @throws IllegalArgumentException if [startIndex] is greater than [endIndex].
|
|
* @throws CharacterCodingException if this string contains malformed char sequence and [throwOnInvalidSequence] is true.
|
|
*/
|
|
@SinceKotlin("1.4")
|
|
@WasExperimental(ExperimentalStdlibApi::class)
|
|
@Suppress("ACTUAL_FUNCTION_WITH_DEFAULT_ARGUMENTS")
|
|
public actual fun String.encodeToByteArray(
|
|
startIndex: Int = 0,
|
|
endIndex: Int = this.length,
|
|
throwOnInvalidSequence: Boolean = false
|
|
): ByteArray {
|
|
AbstractList.checkBoundsIndexes(startIndex, endIndex, length)
|
|
return encodeUtf8(this, startIndex, endIndex, throwOnInvalidSequence)
|
|
}
|
|
|
|
/**
|
|
* Returns a copy of this string converted to upper case using the rules of the default locale.
|
|
*/
|
|
@Deprecated("Use uppercase() instead.", ReplaceWith("uppercase()"))
|
|
@DeprecatedSinceKotlin(warningSince = "1.5")
|
|
@kotlin.internal.InlineOnly
|
|
public actual inline fun String.toUpperCase(): String = asDynamic().toUpperCase()
|
|
|
|
/**
|
|
* Returns a copy of this string converted to upper case using Unicode mapping rules of the invariant locale.
|
|
*
|
|
* This function supports one-to-many and many-to-one character mapping,
|
|
* thus the length of the returned string can be different from the length of the original string.
|
|
*
|
|
* @sample samples.text.Strings.uppercase
|
|
*/
|
|
@SinceKotlin("1.5")
|
|
@WasExperimental(ExperimentalStdlibApi::class)
|
|
@kotlin.internal.InlineOnly
|
|
public actual inline fun String.uppercase(): String = asDynamic().toUpperCase()
|
|
|
|
/**
|
|
* Returns a copy of this string converted to lower case using the rules of the default locale.
|
|
*/
|
|
@Deprecated("Use lowercase() instead.", ReplaceWith("lowercase()"))
|
|
@DeprecatedSinceKotlin(warningSince = "1.5")
|
|
@kotlin.internal.InlineOnly
|
|
public actual inline fun String.toLowerCase(): String = asDynamic().toLowerCase()
|
|
|
|
/**
|
|
* Returns a copy of this string converted to lower case using Unicode mapping rules of the invariant locale.
|
|
*
|
|
* This function supports one-to-many and many-to-one character mapping,
|
|
* thus the length of the returned string can be different from the length of the original string.
|
|
*
|
|
* @sample samples.text.Strings.lowercase
|
|
*/
|
|
@SinceKotlin("1.5")
|
|
@WasExperimental(ExperimentalStdlibApi::class)
|
|
@kotlin.internal.InlineOnly
|
|
public actual inline fun String.lowercase(): String = asDynamic().toLowerCase()
|
|
|
|
@kotlin.internal.InlineOnly
|
|
internal actual inline fun String.nativeIndexOf(str: String, fromIndex: Int): Int = asDynamic().indexOf(str, fromIndex)
|
|
|
|
@kotlin.internal.InlineOnly
|
|
internal actual inline fun String.nativeLastIndexOf(str: String, fromIndex: Int): Int = asDynamic().lastIndexOf(str, fromIndex)
|
|
|
|
@kotlin.internal.InlineOnly
|
|
@kotlin.js.JsPolyfill("""
|
|
if (typeof String.prototype.startsWith === "undefined") {
|
|
Object.defineProperty(String.prototype, "startsWith", {
|
|
value: function (searchString, position) {
|
|
position = position || 0;
|
|
return this.lastIndexOf(searchString, position) === position;
|
|
}
|
|
});
|
|
}
|
|
""")
|
|
internal inline fun String.nativeStartsWith(s: String, position: Int): Boolean = asDynamic().startsWith(s, position)
|
|
|
|
@kotlin.internal.InlineOnly
|
|
@kotlin.js.JsPolyfill("""
|
|
if (typeof String.prototype.endsWith === "undefined") {
|
|
Object.defineProperty(String.prototype, "endsWith", {
|
|
value: function (searchString, position) {
|
|
var subjectString = this.toString();
|
|
if (position === undefined || position > subjectString.length) {
|
|
position = subjectString.length;
|
|
}
|
|
position -= searchString.length;
|
|
var lastIndex = subjectString.indexOf(searchString, position);
|
|
return lastIndex !== -1 && lastIndex === position;
|
|
}
|
|
});
|
|
}
|
|
""")
|
|
internal inline fun String.nativeEndsWith(s: String): Boolean = asDynamic().endsWith(s)
|
|
|
|
@kotlin.internal.InlineOnly
|
|
public actual inline fun String.substring(startIndex: Int): String = asDynamic().substring(startIndex)
|
|
|
|
@kotlin.internal.InlineOnly
|
|
public actual inline fun String.substring(startIndex: Int, endIndex: Int): String = asDynamic().substring(startIndex, endIndex)
|
|
|
|
@Deprecated("Use String.plus() instead", ReplaceWith("this + str"))
|
|
@DeprecatedSinceKotlin(warningSince = "1.6")
|
|
@kotlin.internal.InlineOnly
|
|
public inline fun String.concat(str: String): String = asDynamic().concat(str)
|
|
|
|
@Deprecated("Use Regex.findAll() instead or invoke matches() on String dynamically: this.asDynamic().match(regex)")
|
|
@DeprecatedSinceKotlin(warningSince = "1.6")
|
|
@kotlin.internal.InlineOnly
|
|
public inline fun String.match(regex: String): Array<String>? = asDynamic().match(regex)
|
|
|
|
//native public fun String.trim(): String
|
|
//TODO: String.replace to implement effective trimLeading and trimTrailing
|
|
|
|
@kotlin.internal.InlineOnly
|
|
internal inline fun String.nativeReplace(pattern: RegExp, replacement: String): String = asDynamic().replace(pattern, replacement)
|
|
|
|
/**
|
|
* Compares two strings lexicographically, optionally ignoring case differences.
|
|
*
|
|
* If [ignoreCase] is true, the result of `Char.uppercaseChar().lowercaseChar()` on each character is compared.
|
|
*/
|
|
@SinceKotlin("1.2")
|
|
@Suppress("ACTUAL_FUNCTION_WITH_DEFAULT_ARGUMENTS")
|
|
public actual fun String.compareTo(other: String, ignoreCase: Boolean = false): Int {
|
|
if (ignoreCase) {
|
|
val n1 = this.length
|
|
val n2 = other.length
|
|
val min = minOf(n1, n2)
|
|
if (min == 0) return n1 - n2
|
|
for (index in 0 until min) {
|
|
var thisChar = this[index]
|
|
var otherChar = other[index]
|
|
|
|
if (thisChar != otherChar) {
|
|
thisChar = thisChar.uppercaseChar()
|
|
otherChar = otherChar.uppercaseChar()
|
|
|
|
if (thisChar != otherChar) {
|
|
thisChar = thisChar.lowercaseChar()
|
|
otherChar = otherChar.lowercaseChar()
|
|
|
|
if (thisChar != otherChar) {
|
|
return thisChar.compareTo(otherChar)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return n1 - n2
|
|
} else {
|
|
return compareTo(other)
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Returns `true` if the contents of this char sequence are equal to the contents of the specified [other],
|
|
* i.e. both char sequences contain the same number of the same characters in the same order.
|
|
*
|
|
* @sample samples.text.Strings.contentEquals
|
|
*/
|
|
@SinceKotlin("1.5")
|
|
public actual infix fun CharSequence?.contentEquals(other: CharSequence?): Boolean = contentEqualsImpl(other)
|
|
|
|
/**
|
|
* Returns `true` if the contents of this char sequence are equal to the contents of the specified [other], optionally ignoring case difference.
|
|
*
|
|
* @param ignoreCase `true` to ignore character case when comparing contents.
|
|
*
|
|
* @sample samples.text.Strings.contentEquals
|
|
*/
|
|
@SinceKotlin("1.5")
|
|
public actual fun CharSequence?.contentEquals(other: CharSequence?, ignoreCase: Boolean): Boolean {
|
|
return if (ignoreCase)
|
|
this.contentEqualsIgnoreCaseImpl(other)
|
|
else
|
|
this.contentEqualsImpl(other)
|
|
}
|
|
|
|
|
|
private val STRING_CASE_INSENSITIVE_ORDER = Comparator<String> { a, b -> a.compareTo(b, ignoreCase = true) }
|
|
|
|
@SinceKotlin("1.2")
|
|
public actual val String.Companion.CASE_INSENSITIVE_ORDER: Comparator<String>
|
|
get() = STRING_CASE_INSENSITIVE_ORDER
|