Align JS String.equals/compareTo(ignoreCase) behavior with JVM #KT-48999
This commit is contained in:
committed by
Space
parent
b5f74ef9a6
commit
f8bcba0b76
@@ -35,6 +35,9 @@ internal actual external fun String.nativeLastIndexOf(str: String, fromIndex: In
|
|||||||
/**
|
/**
|
||||||
* Returns `true` if this string is equal to [other], optionally ignoring character case.
|
* Returns `true` if this string is equal to [other], optionally ignoring character case.
|
||||||
*
|
*
|
||||||
|
* Two strings are considered to be equal if they have the same length and the same character at the same index.
|
||||||
|
* If [ignoreCase] is true, the result of `Char.uppercaseChar().lowercaseChar()` on each character is compared.
|
||||||
|
*
|
||||||
* @param ignoreCase `true` to ignore character case when comparing strings. By default `false`.
|
* @param ignoreCase `true` to ignore character case when comparing strings. By default `false`.
|
||||||
*/
|
*/
|
||||||
@Suppress("ACTUAL_FUNCTION_WITH_DEFAULT_ARGUMENTS")
|
@Suppress("ACTUAL_FUNCTION_WITH_DEFAULT_ARGUMENTS")
|
||||||
@@ -475,6 +478,11 @@ internal fun compareToIgnoreCase(thiz: String, other: String): Int {
|
|||||||
1
|
1
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Compares two strings lexicographically, optionally ignoring case differences.
|
||||||
|
*
|
||||||
|
* If [ignoreCase] is true, the result of `Char.uppercaseChar().lowercaseChar()` on each character is compared.
|
||||||
|
*/
|
||||||
@Suppress("ACTUAL_FUNCTION_WITH_DEFAULT_ARGUMENTS")
|
@Suppress("ACTUAL_FUNCTION_WITH_DEFAULT_ARGUMENTS")
|
||||||
public actual fun String.compareTo(other: String, ignoreCase: Boolean = false): Int {
|
public actual fun String.compareTo(other: String, ignoreCase: Boolean = false): Int {
|
||||||
return if (!ignoreCase) this.compareTo(other)
|
return if (!ignoreCase) this.compareTo(other)
|
||||||
|
|||||||
@@ -270,12 +270,17 @@ expect fun String.replaceFirst(oldValue: String, newValue: String, ignoreCase: B
|
|||||||
/**
|
/**
|
||||||
* Returns `true` if this string is equal to [other], optionally ignoring character case.
|
* Returns `true` if this string is equal to [other], optionally ignoring character case.
|
||||||
*
|
*
|
||||||
|
* Two strings are considered to be equal if they have the same length and the same character at the same index.
|
||||||
|
* If [ignoreCase] is true, the result of `Char.uppercaseChar().lowercaseChar()` on each character is compared.
|
||||||
|
*
|
||||||
* @param ignoreCase `true` to ignore character case when comparing strings. By default `false`.
|
* @param ignoreCase `true` to ignore character case when comparing strings. By default `false`.
|
||||||
*/
|
*/
|
||||||
expect fun String?.equals(other: String?, ignoreCase: Boolean = false): Boolean
|
expect fun String?.equals(other: String?, ignoreCase: Boolean = false): Boolean
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Compares two strings lexicographically, optionally ignoring case differences.
|
* 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")
|
@SinceKotlin("1.2")
|
||||||
expect fun String.compareTo(other: String, ignoreCase: Boolean = false): Int
|
expect fun String.compareTo(other: String, ignoreCase: Boolean = false): Int
|
||||||
|
|||||||
@@ -244,6 +244,11 @@ public inline fun String.match(regex: String): Array<String>? = asDynamic().matc
|
|||||||
@kotlin.internal.InlineOnly
|
@kotlin.internal.InlineOnly
|
||||||
internal inline fun String.nativeReplace(pattern: RegExp, replacement: String): String = asDynamic().replace(pattern, replacement)
|
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")
|
@SinceKotlin("1.2")
|
||||||
@Suppress("ACTUAL_FUNCTION_WITH_DEFAULT_ARGUMENTS")
|
@Suppress("ACTUAL_FUNCTION_WITH_DEFAULT_ARGUMENTS")
|
||||||
public actual fun String.compareTo(other: String, ignoreCase: Boolean = false): Int {
|
public actual fun String.compareTo(other: String, ignoreCase: Boolean = false): Int {
|
||||||
@@ -252,24 +257,23 @@ public actual fun String.compareTo(other: String, ignoreCase: Boolean = false):
|
|||||||
val n2 = other.length
|
val n2 = other.length
|
||||||
val min = minOf(n1, n2)
|
val min = minOf(n1, n2)
|
||||||
if (min == 0) return n1 - n2
|
if (min == 0) return n1 - n2
|
||||||
var start = 0
|
for (index in 0 until min) {
|
||||||
while (true) {
|
var thisChar = this[index]
|
||||||
val end = minOf(start + 16, min)
|
var otherChar = other[index]
|
||||||
var s1 = this.substring(start, end)
|
|
||||||
var s2 = other.substring(start, end)
|
if (thisChar != otherChar) {
|
||||||
if (s1 != s2) {
|
thisChar = thisChar.uppercaseChar()
|
||||||
s1 = s1.uppercase()
|
otherChar = otherChar.uppercaseChar()
|
||||||
s2 = s2.uppercase()
|
|
||||||
if (s1 != s2) {
|
if (thisChar != otherChar) {
|
||||||
s1 = s1.lowercase()
|
thisChar = thisChar.lowercaseChar()
|
||||||
s2 = s2.lowercase()
|
otherChar = otherChar.lowercaseChar()
|
||||||
if (s1 != s2) {
|
|
||||||
return s1.compareTo(s2)
|
if (thisChar != otherChar) {
|
||||||
|
return thisChar.compareTo(otherChar)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (end == min) break
|
|
||||||
start = end
|
|
||||||
}
|
}
|
||||||
return n1 - n2
|
return n1 - n2
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -61,19 +61,33 @@ public fun String.matches(regex: String): Boolean {
|
|||||||
*/
|
*/
|
||||||
public actual fun CharSequence.isBlank(): Boolean = length == 0 || indices.all { this[it].isWhitespace() }
|
public actual fun CharSequence.isBlank(): Boolean = length == 0 || indices.all { this[it].isWhitespace() }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns `true` if this string is equal to [other], optionally ignoring character case.
|
||||||
|
*
|
||||||
|
* Two strings are considered to be equal if they have the same length and the same character at the same index.
|
||||||
|
* If [ignoreCase] is true, the result of `Char.uppercaseChar().lowercaseChar()` on each character is compared.
|
||||||
|
*
|
||||||
|
* @param ignoreCase `true` to ignore character case when comparing strings. By default `false`.
|
||||||
|
*/
|
||||||
@Suppress("ACTUAL_FUNCTION_WITH_DEFAULT_ARGUMENTS")
|
@Suppress("ACTUAL_FUNCTION_WITH_DEFAULT_ARGUMENTS")
|
||||||
public actual fun String?.equals(other: String?, ignoreCase: Boolean = false): Boolean =
|
public actual fun String?.equals(other: String?, ignoreCase: Boolean = false): Boolean {
|
||||||
when {
|
if (this == null) return other == null
|
||||||
this == null -> other == null
|
if (other == null) return false
|
||||||
!ignoreCase -> this == other
|
if (!ignoreCase) return this == other
|
||||||
other == null -> false
|
|
||||||
else -> {
|
if (this.length != other.length) return false
|
||||||
val thisLower = this.lowercase()
|
|
||||||
val otherLower = other.lowercase()
|
for (index in 0 until this.length) {
|
||||||
thisLower == otherLower || (thisLower.uppercase() == otherLower.uppercase())
|
val thisChar = this[index]
|
||||||
|
val otherChar = other[index]
|
||||||
|
if (!thisChar.equals(otherChar, ignoreCase)) {
|
||||||
|
return false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@Suppress("ACTUAL_FUNCTION_WITH_DEFAULT_ARGUMENTS")
|
@Suppress("ACTUAL_FUNCTION_WITH_DEFAULT_ARGUMENTS")
|
||||||
public actual fun CharSequence.regionMatches(thisOffset: Int, other: CharSequence, otherOffset: Int, length: Int, ignoreCase: Boolean = false): Boolean =
|
public actual fun CharSequence.regionMatches(thisOffset: Int, other: CharSequence, otherOffset: Int, length: Int, ignoreCase: Boolean = false): Boolean =
|
||||||
|
|||||||
@@ -44,6 +44,9 @@ internal actual inline fun String.nativeLastIndexOf(str: String, fromIndex: Int)
|
|||||||
/**
|
/**
|
||||||
* Returns `true` if this string is equal to [other], optionally ignoring character case.
|
* Returns `true` if this string is equal to [other], optionally ignoring character case.
|
||||||
*
|
*
|
||||||
|
* Two strings are considered to be equal if they have the same length and the same character at the same index.
|
||||||
|
* If [ignoreCase] is true, the result of `Char.uppercaseChar().lowercaseChar()` on each character is compared.
|
||||||
|
*
|
||||||
* @param ignoreCase `true` to ignore character case when comparing strings. By default `false`.
|
* @param ignoreCase `true` to ignore character case when comparing strings. By default `false`.
|
||||||
*/
|
*/
|
||||||
@Suppress("ACTUAL_FUNCTION_WITH_DEFAULT_ARGUMENTS")
|
@Suppress("ACTUAL_FUNCTION_WITH_DEFAULT_ARGUMENTS")
|
||||||
@@ -540,6 +543,8 @@ public inline fun String.codePointCount(beginIndex: Int, endIndex: Int): Int =
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Compares two strings lexicographically, optionally ignoring case differences.
|
* Compares two strings lexicographically, optionally ignoring case differences.
|
||||||
|
*
|
||||||
|
* If [ignoreCase] is true, the result of `Char.uppercaseChar().lowercaseChar()` on each character is compared.
|
||||||
*/
|
*/
|
||||||
@Suppress("ACTUAL_FUNCTION_WITH_DEFAULT_ARGUMENTS")
|
@Suppress("ACTUAL_FUNCTION_WITH_DEFAULT_ARGUMENTS")
|
||||||
public actual fun String.compareTo(other: String, ignoreCase: Boolean = false): Int {
|
public actual fun String.compareTo(other: String, ignoreCase: Boolean = false): Int {
|
||||||
|
|||||||
@@ -225,13 +225,9 @@ public inline operator fun Char.plus(other: String): String = this.toString() +
|
|||||||
/**
|
/**
|
||||||
* Returns `true` if this character is equal to the [other] character, optionally ignoring character case.
|
* Returns `true` if this character is equal to the [other] character, optionally ignoring character case.
|
||||||
*
|
*
|
||||||
|
* Two characters are considered equal ignoring case if `Char.uppercaseChar().lowercaseChar()` on each character produces the same result.
|
||||||
|
*
|
||||||
* @param ignoreCase `true` to ignore character case when comparing characters. By default `false`.
|
* @param ignoreCase `true` to ignore character case when comparing characters. By default `false`.
|
||||||
*
|
|
||||||
* Two characters are considered the same ignoring case if at least one of the following is `true`:
|
|
||||||
* - The two characters are the same (as compared by the == operator)
|
|
||||||
* - Applying the method [uppercaseChar] to each character produces the same result
|
|
||||||
* - Applying the method [lowercaseChar] to each character produces the same result
|
|
||||||
*
|
|
||||||
* @sample samples.text.Chars.equals
|
* @sample samples.text.Chars.equals
|
||||||
*/
|
*/
|
||||||
public fun Char.equals(other: Char, ignoreCase: Boolean = false): Boolean {
|
public fun Char.equals(other: Char, ignoreCase: Boolean = false): Boolean {
|
||||||
|
|||||||
@@ -841,6 +841,11 @@ class StringTest {
|
|||||||
assertFalse("sample".equals(null, ignoreCase = true))
|
assertFalse("sample".equals(null, ignoreCase = true))
|
||||||
assertTrue(null.equals(null, ignoreCase = true))
|
assertTrue(null.equals(null, ignoreCase = true))
|
||||||
assertTrue(null.equals(null, ignoreCase = false))
|
assertTrue(null.equals(null, ignoreCase = false))
|
||||||
|
|
||||||
|
// Tests that characters are compared one by one
|
||||||
|
val s1 = "\uFB00" // uppercase() == "FF"
|
||||||
|
val s2 = "\u0066\u0066" // "ff"
|
||||||
|
assertFalse(s1.equals(s2, ignoreCase = true))
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test fun compareToIgnoreCase() {
|
@Test fun compareToIgnoreCase() {
|
||||||
@@ -898,6 +903,11 @@ class StringTest {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Tests that characters are compared one by one
|
||||||
|
val s1 = "\uFB00" // uppercase().lowercase() == "ff"
|
||||||
|
val s2 = "\u0067" // "g"
|
||||||
|
assertCompareResult(GT, s1, s2, ignoreCase = true)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -429,12 +429,17 @@ actual fun String.replaceFirst(oldValue: String, newValue: String, ignoreCase: B
|
|||||||
/**
|
/**
|
||||||
* Returns `true` if this string is equal to [other], optionally ignoring character case.
|
* Returns `true` if this string is equal to [other], optionally ignoring character case.
|
||||||
*
|
*
|
||||||
|
* Two strings are considered to be equal if they have the same length and the same character at the same index.
|
||||||
|
* If [ignoreCase] is true, the result of `Char.uppercaseChar().lowercaseChar()` on each character is compared.
|
||||||
|
*
|
||||||
* @param ignoreCase `true` to ignore character case when comparing strings. By default `false`.
|
* @param ignoreCase `true` to ignore character case when comparing strings. By default `false`.
|
||||||
*/
|
*/
|
||||||
actual fun String?.equals(other: String?, ignoreCase: Boolean): Boolean = TODO("Wasm stdlib: Text")
|
actual fun String?.equals(other: String?, ignoreCase: Boolean): Boolean = TODO("Wasm stdlib: Text")
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Compares two strings lexicographically, optionally ignoring case differences.
|
* 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")
|
@SinceKotlin("1.2")
|
||||||
actual fun String.compareTo(other: String, ignoreCase: Boolean): Int = TODO("Wasm stdlib: Text")
|
actual fun String.compareTo(other: String, ignoreCase: Boolean): Int = TODO("Wasm stdlib: Text")
|
||||||
|
|||||||
Reference in New Issue
Block a user