From f05a19aafb7964c1148d19c1d4ec65bb98f38c95 Mon Sep 17 00:00:00 2001 From: Toshiaki Kameyama Date: Fri, 3 Aug 2018 10:25:22 +0900 Subject: [PATCH] JS stdlib: implement String.compareTo with ignoreCase #KT-18067 Fixed --- libraries/stdlib/common/src/kotlin/TextH.kt | 5 +++ libraries/stdlib/js/src/kotlin/string.kt | 31 +++++++++++++++++++ .../stdlib/jvm/src/kotlin/text/StringsJVM.kt | 2 +- libraries/stdlib/test/text/StringTest.kt | 25 +++++++++++++++ 4 files changed, 62 insertions(+), 1 deletion(-) diff --git a/libraries/stdlib/common/src/kotlin/TextH.kt b/libraries/stdlib/common/src/kotlin/TextH.kt index 5a20077168d..7fecd6b8603 100644 --- a/libraries/stdlib/common/src/kotlin/TextH.kt +++ b/libraries/stdlib/common/src/kotlin/TextH.kt @@ -290,6 +290,11 @@ expect fun Int.toString(radix: Int): String @SinceKotlin("1.2") expect fun Long.toString(radix: Int): String +/** + * Compares two strings lexicographically, optionally ignoring case differences. + */ +expect fun String.compareTo(other: String, ignoreCase: Boolean = false): Int + @PublishedApi internal expect fun checkRadix(radix: Int): Int diff --git a/libraries/stdlib/js/src/kotlin/string.kt b/libraries/stdlib/js/src/kotlin/string.kt index bd184ba1840..8ed5aa530fe 100644 --- a/libraries/stdlib/js/src/kotlin/string.kt +++ b/libraries/stdlib/js/src/kotlin/string.kt @@ -73,3 +73,34 @@ public inline val CharSequence.size: Int get() = length @kotlin.internal.InlineOnly internal inline fun String.nativeReplace(pattern: RegExp, replacement: String): String = asDynamic().replace(pattern, replacement) + +public actual fun String.compareTo(other: String, ignoreCase: Boolean): Int { + if (ignoreCase) { + val n1 = this.length + val n2 = other.length + val min = minOf(n1, n2) + if (min == 0) return n1 - n2 + var start = 0 + while (true) { + val end = minOf(start + 16, min) + var s1 = this.substring(start, end) + var s2 = other.substring(start, end) + if (s1 != s2) { + s1 = s1.toUpperCase() + s2 = s2.toUpperCase() + if (s1 != s2) { + s1 = s1.toLowerCase() + s2 = s2.toLowerCase() + if (s1 != s2) { + return s1.compareTo(s2) + } + } + } + if (end == min) break + start = end + } + return n1 - n2 + } else { + return compareTo(other) + } +} \ No newline at end of file diff --git a/libraries/stdlib/jvm/src/kotlin/text/StringsJVM.kt b/libraries/stdlib/jvm/src/kotlin/text/StringsJVM.kt index cf69a4c264b..ad6717c26b9 100644 --- a/libraries/stdlib/jvm/src/kotlin/text/StringsJVM.kt +++ b/libraries/stdlib/jvm/src/kotlin/text/StringsJVM.kt @@ -317,7 +317,7 @@ public inline fun String.codePointCount(beginIndex: Int, endIndex: Int): Int = /** * Compares two strings lexicographically, optionally ignoring case differences. */ -public fun String.compareTo(other: String, ignoreCase: Boolean = false): Int { +public actual fun String.compareTo(other: String, ignoreCase: Boolean): Int { if (ignoreCase) return (this as java.lang.String).compareToIgnoreCase(other) else diff --git a/libraries/stdlib/test/text/StringTest.kt b/libraries/stdlib/test/text/StringTest.kt index a56bf0b9f82..5725bf23eee 100644 --- a/libraries/stdlib/test/text/StringTest.kt +++ b/libraries/stdlib/test/text/StringTest.kt @@ -1336,4 +1336,29 @@ ${" "} assertEquals(" ABC\n \n 123", "ABC\n \n123".prependIndent(" ")) assertEquals(" ", "".prependIndent(" ")) } + + @Test fun testCompareToIgnoreCase() { + assertTrue("ABC".compareTo("ABC", ignoreCase = false) == 0) + assertTrue("ABC".compareTo("ABc", ignoreCase = false) < 0) + assertTrue("ABc".compareTo("ABC", ignoreCase = false) > 0) + assertTrue("ABC".compareTo("ABx", ignoreCase = false) < 0) + assertTrue("ABx".compareTo("ABC", ignoreCase = false) > 0) + assertTrue("[".compareTo("aa", ignoreCase = false) < 0) + assertTrue("".compareTo("", ignoreCase = false) == 0) + assertTrue("".compareTo("A", ignoreCase = false) < 0) + assertTrue("A".compareTo("", ignoreCase = false) > 0) + assertTrue(("A".repeat(16) + "B").compareTo("A".repeat(16) + "b", ignoreCase = false) < 0) + + assertTrue("ABC".compareTo("ABC", ignoreCase = true) == 0) + assertTrue("ABC".compareTo("ABc", ignoreCase = true) == 0) + assertTrue("ABc".compareTo("ABC", ignoreCase = true) == 0) + assertTrue("ABC".compareTo("ABx", ignoreCase = true) < 0) + assertTrue("ABx".compareTo("ABC", ignoreCase = true) > 0) + assertTrue("[".compareTo("aa", ignoreCase = true) < 0) + assertTrue("".compareTo("", ignoreCase = false) == 0) + assertTrue("".compareTo("A", ignoreCase = false) < 0) + assertTrue("A".compareTo("", ignoreCase = false) > 0) + assertTrue(("A".repeat(16) + "B").compareTo("A".repeat(16) + "b", ignoreCase = false) < 0) + } + }