Files
kotlin-fork/libraries/stdlib/js/src/kotlin/text/string.kt
T

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