stdlib: Improve character API.

The patch adds methods for surrogate characters processing and Unicode
category support needed for regular expression library.
This commit is contained in:
Ilya Matveev
2017-06-08 14:04:23 +07:00
committed by ilmat192
parent 96e807697a
commit e64a5f42f0
11 changed files with 385 additions and 10 deletions
+4 -1
View File
@@ -163,7 +163,6 @@ enum CharacterClass {
OTHER_SYMBOL = 28,
/**
* Unicode category constant Pi.
*
*/
INITIAL_QUOTE_PUNCTUATION = 29,
/**
@@ -973,6 +972,10 @@ KChar Kotlin_Char_toUpperCase(KChar ch) {
return towupper_Konan(ch);
}
KInt Kotlin_Char_getType(KChar ch) {
return getType(ch);
}
constexpr KInt digits[] = {
0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
-1, -1, -1, -1, -1, -1, -1,
@@ -172,6 +172,12 @@ external public fun String.toUpperCase(): String
@Suppress("NOTHING_TO_INLINE")
external public inline fun String.toLowerCase(): String
/**
* Returns an array containing all characters of the specified string.
*/
@SymbolName("Kotlin_String_toCharArray")
external public fun String.toCharArray() : CharArray
/**
* Returns a copy of this string having its first letter uppercased, or the original string,
* if it's empty or already starts with an upper case letter.
+15
View File
@@ -106,6 +106,21 @@ public final class Char : Comparable<Char> {
*/
public const val MAX_SURROGATE: Char = MAX_LOW_SURROGATE
/**
* The minimum value of a supplementary code point, `\u0x10000`. Kotlin/Native specific.
*/
public const val MIN_SUPPLEMENTARY_CODE_POINT: Int = 0x10000
/**
* The minimum value of a Unicode code point. Kotlin/Native specific.
*/
public const val MIN_CODE_POINT = 0x000000
/**
* The maximum value of a Unicode code point. Kotlin/Native specific.
*/
public const val MAX_CODE_POINT = 0X10FFFF
/**
* The minimum radix available for conversion to and from strings.
*/
@@ -1275,6 +1275,17 @@ public fun <T : Comparable<T>> MutableList<T>.sortDescending(): Unit {
sortWith(reverseOrder())
}
/**
* Replaces each element in the list with a result of a transformation specified.
*/
public fun <T> MutableList<T>.replaceAll(transformation: (T) -> T) {
val it = listIterator()
while (it.hasNext()) {
val element = it.next()
it.set(transformation(element))
}
}
/**
* Returns a list of all elements sorted according to their natural sort order.
*/
@@ -16,6 +16,8 @@
package kotlin.text
import kotlin.IllegalArgumentException
/**
* Returns `true` if this character (Unicode code point) is defined in Unicode.
*/
@@ -102,6 +104,15 @@ internal fun digitOf(char: Char, radix: Int): Int = digitOfChecked(char, checkRa
@SymbolName("Kotlin_Char_digitOfChecked")
external internal fun digitOfChecked(char: Char, radix: Int): Int
/**
* Returns a value indicating a character's general category.
*/
public val Char.category: CharCategory get() = CharCategory.valueOf(getType())
/** Retrun a Unicode category of the character as an Int. */
@SymbolName("Kotlin_Char_getType")
external internal fun Char.getType(): Int
/**
* Checks whether the given [radix] is valid radix for string to number and number to string conversion.
*/
@@ -112,3 +123,48 @@ internal fun checkRadix(radix: Int): Int {
}
return radix
}
// Char.Compaion methods. Konan specific.
// TODO: Make public when supplementary codepoints are supported.
/** Converts a unicode code point to lower case. */
internal fun Char.Companion.toLowerCase(codePoint: Int): Int =
if (codePoint < MIN_SUPPLEMENTARY_CODE_POINT) {
codePoint.toChar().toLowerCase().toInt()
} else {
codePoint // TODO: Implement this transformation for supplementary codepoints.
}
/** Converts a unicode code point to upper case. */
internal fun Char.Companion.toUpperCase(codePoint: Int): Int =
if (codePoint < MIN_SUPPLEMENTARY_CODE_POINT) {
codePoint.toChar().toUpperCase().toInt()
} else {
codePoint // TODO: Implement this transformation for supplementary codepoints.
}
/** Converts a surrogate pair to a unicode code point. Doesn't validate that the characters are a valid surrogate pair. */
public fun Char.Companion.toCodePoint(high: Char, low: Char): Int =
(((high - MIN_HIGH_SURROGATE) shl 10) or (low - MIN_LOW_SURROGATE)) + 0x10000
/** Checks if the codepoint specified is a supplementary codepoint or not. */
public fun Char.Companion.isSupplementaryCodePoint(codepoint: Int): Boolean =
codepoint in MIN_SUPPLEMENTARY_CODE_POINT..MAX_CODE_POINT
public fun Char.Companion.isSurrogatePair(high: Char, low: Char): Boolean = high.isHighSurrogate() && low.isLowSurrogate()
/**
* Converts the codepoint specified to a char array. If the codepoint is not supplementary, the method will
* return an array with one element otherwise it will return an array A with a high surrogate in A[0] and
* a low surrogate in A[1].
*/
public fun Char.Companion.toChars(codePoint: Int): CharArray =
when {
codePoint in 0 until MIN_SUPPLEMENTARY_CODE_POINT -> charArrayOf(codePoint.toChar())
codePoint in MIN_SUPPLEMENTARY_CODE_POINT..MAX_CODE_POINT -> {
val low = ((codePoint - 0x10000) and 0x3FF) + MIN_LOW_SURROGATE.toInt()
val high = (((codePoint - 0x10000) ushr 10) and 0x3FF) + MIN_HIGH_SURROGATE.toInt()
charArrayOf(high.toChar(), low.toChar())
}
else -> throw IllegalArgumentException()
}
@@ -0,0 +1,171 @@
package kotlin.text
// The values duplicate constants defined in KString.cpp.
/**
* Represents the character general category in the Unicode specification.
*/
public enum class CharCategory(public val value: Int, public val code: String) {
/**
* General category "Cn" in the Unicode specification.
*/
UNASSIGNED(0, "Cn"),
/**
* General category "Lu" in the Unicode specification.
*/
UPPERCASE_LETTER(1, "Lu"),
/**
* General category "Ll" in the Unicode specification.
*/
LOWERCASE_LETTER(2, "Ll"),
/**
* General category "Lt" in the Unicode specification.
*/
TITLECASE_LETTER(3, "Lt"),
/**
* General category "Lm" in the Unicode specification.
*/
MODIFIER_LETTER(4, "Lm"),
/**
* General category "Lo" in the Unicode specification.
*/
OTHER_LETTER(5, "Lo"),
/**
* General category "Mn" in the Unicode specification.
*/
NON_SPACING_MARK(6, "Mn"),
/**
* General category "Me" in the Unicode specification.
*/
ENCLOSING_MARK(7, "Me"),
/**
* General category "Mc" in the Unicode specification.
*/
COMBINING_SPACING_MARK(8, "Mc"),
/**
* General category "Nd" in the Unicode specification.
*/
DECIMAL_DIGIT_NUMBER(9, "Nd"),
/**
* General category "Nl" in the Unicode specification.
*/
LETTER_NUMBER(10, "Nl"),
/**
* General category "No" in the Unicode specification.
*/
OTHER_NUMBER(11, "No"),
/**
* General category "Zs" in the Unicode specification.
*/
SPACE_SEPARATOR(12, "Zs"),
/**
* General category "Zl" in the Unicode specification.
*/
LINE_SEPARATOR(13, "Zl"),
/**
* General category "Zp" in the Unicode specification.
*/
PARAGRAPH_SEPARATOR(14, "Zp"),
/**
* General category "Cc" in the Unicode specification.
*/
CONTROL(15, "Cc"),
/**
* General category "Cf" in the Unicode specification.
*/
FORMAT(16, "Cf"),
/**
* General category "Co" in the Unicode specification.
*/
PRIVATE_USE(18, "Co"),
/**
* General category "Cs" in the Unicode specification.
*/
SURROGATE(19, "Cs"),
/**
* General category "Pd" in the Unicode specification.
*/
DASH_PUNCTUATION(20, "Pd"),
/**
* General category "Ps" in the Unicode specification.
*/
START_PUNCTUATION(21, "Ps"),
/**
* General category "Pe" in the Unicode specification.
*/
END_PUNCTUATION(22, "Pe"),
/**
* General category "Pc" in the Unicode specification.
*/
CONNECTOR_PUNCTUATION(23, "Pc"),
/**
* General category "Po" in the Unicode specification.
*/
OTHER_PUNCTUATION(24, "Po"),
/**
* General category "Sm" in the Unicode specification.
*/
MATH_SYMBOL(25, "Sm"),
/**
* General category "Sc" in the Unicode specification.
*/
CURRENCY_SYMBOL(26, "Sc"),
/**
* General category "Sk" in the Unicode specification.
*/
MODIFIER_SYMBOL(27, "Sk"),
/**
* General category "So" in the Unicode specification.
*/
OTHER_SYMBOL(28, "So"),
/**
* General category "Pi" in the Unicode specification.
*/
INITIAL_QUOTE_PUNCTUATION(29, "Pi"),
/**
* General category "Pf" in the Unicode specification.
*/
FINAL_QUOTE_PUNCTUATION(30, "Pf");
/**
* Returns `true` if [char] character belongs to this category.
*/
public operator fun contains(char: Char): Boolean = char.getType() == this.value
public companion object {
public fun valueOf(category: Int): CharCategory =
when {
category >=0 && category <= 16 -> values()[category]
category >= 18 && category <= 30 -> values()[category - 1]
else -> throw IllegalArgumentException("Category #$category is not defined.")
}
}
}
@@ -26,9 +26,6 @@ external fun toUtf8Array(string: String, start: Int, size: Int) : ByteArray
@SymbolName("Kotlin_String_fromCharArray")
external fun fromCharArray(array: CharArray, start: Int, size: Int) : String
@SymbolName("Kotlin_String_toCharArray")
external fun toCharArray(string: String) : CharArray
/**
* Builds new string by populating newly created [StringBuilder] using provided [builderAction]
* and then converting it to [String].
@@ -59,7 +56,7 @@ class StringBuilder private constructor (
constructor(capacity: Int) : this(CharArray(capacity))
constructor(string: String) : this(toCharArray(string)) {
constructor(string: String) : this(string.toCharArray()) {
length = array.size
}
@@ -217,7 +214,7 @@ class StringBuilder private constructor (
return this
}
fun insert(index: Int, string: String): StringBuilder = insert(index, toCharArray(string))
fun insert(index: Int, string: String): StringBuilder = insert(index, string.toCharArray())
fun insert(index: Int, value: Boolean) = insert(index, value.toString())
fun insert(index: Int, value: Byte) = insert(index, value.toString())
@@ -260,7 +257,7 @@ class StringBuilder private constructor (
return this
}
fun append(it: String): StringBuilder = append(toCharArray(it))
fun append(it: String): StringBuilder = append(it.toCharArray())
fun append(it: Boolean) = append(it.toString())
fun append(it: Byte) = append(it.toString())