2019-01-31 19:03:34 +01:00
|
|
|
@file:JvmName("StringUtils")
|
|
|
|
|
|
|
|
package com.keylesspalace.tusky.util
|
|
|
|
|
2020-04-20 23:06:39 +02:00
|
|
|
import android.text.Spanned
|
|
|
|
import java.util.*
|
2019-01-31 19:03:34 +01:00
|
|
|
|
|
|
|
|
|
|
|
private const val POSSIBLE_CHARS = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
|
|
|
|
|
|
|
fun randomAlphanumericString(count: Int): String {
|
|
|
|
val chars = CharArray(count)
|
|
|
|
val random = Random()
|
|
|
|
for (i in 0 until count) {
|
|
|
|
chars[i] = POSSIBLE_CHARS[random.nextInt(POSSIBLE_CHARS.length)]
|
|
|
|
}
|
|
|
|
return String(chars)
|
|
|
|
}
|
|
|
|
|
|
|
|
// We sort statuses by ID. Something we need to invent some ID for placeholder.
|
|
|
|
// Not sure if inc()/dec() should be made `operator` or not
|
|
|
|
|
|
|
|
/**
|
|
|
|
* "Increment" string so that during sorting it's bigger than [this].
|
|
|
|
*/
|
|
|
|
fun String.inc(): String {
|
|
|
|
// We assume that we will stay in the safe range for now
|
|
|
|
val builder = this.toCharArray()
|
2019-02-05 20:06:00 +01:00
|
|
|
builder[lastIndex] = builder[lastIndex].inc()
|
2019-01-31 19:03:34 +01:00
|
|
|
return String(builder)
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* "Decrement" string so that during sorting it's smaller than [this].
|
|
|
|
*/
|
|
|
|
fun String.dec(): String {
|
2019-02-05 20:06:00 +01:00
|
|
|
if (this.isEmpty()) return this
|
|
|
|
|
2019-01-31 19:03:34 +01:00
|
|
|
val builder = this.toCharArray()
|
|
|
|
var i = builder.lastIndex
|
|
|
|
while (i > 0) {
|
|
|
|
if (builder[i] > '0') {
|
|
|
|
builder[i] = builder[i].dec()
|
2019-02-05 20:06:00 +01:00
|
|
|
return String(builder)
|
2019-01-31 19:03:34 +01:00
|
|
|
} else {
|
|
|
|
builder[i] = 'z'
|
|
|
|
}
|
|
|
|
i--
|
|
|
|
}
|
2019-02-05 20:06:00 +01:00
|
|
|
return if (builder[0] > '1') {
|
|
|
|
builder[0] = builder[0].dec()
|
|
|
|
String(builder)
|
|
|
|
} else {
|
|
|
|
String(builder.copyOfRange(1, builder.size))
|
2019-01-31 19:03:34 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* A < B (strictly) by length and then by content.
|
|
|
|
* Examples:
|
|
|
|
* "abc" < "bcd"
|
|
|
|
* "ab" < "abc"
|
|
|
|
* "cb" < "abc"
|
|
|
|
* not: "ab" < "ab"
|
|
|
|
* not: "abc" > "cb"
|
|
|
|
*/
|
|
|
|
fun String.isLessThan(other: String): Boolean {
|
|
|
|
return when {
|
|
|
|
this.length < other.length -> true
|
|
|
|
this.length > other.length -> false
|
|
|
|
else -> this < other
|
|
|
|
}
|
|
|
|
}
|
2020-04-20 23:06:39 +02:00
|
|
|
|
2021-06-11 20:15:40 +02:00
|
|
|
fun String.idCompareTo(other: String): Int {
|
|
|
|
return when {
|
|
|
|
this === other -> 0
|
|
|
|
this.length < other.length -> -1
|
|
|
|
this.length > other.length -> 1
|
|
|
|
else -> this.compareTo(other)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-04-20 23:06:39 +02:00
|
|
|
fun Spanned.trimTrailingWhitespace(): Spanned {
|
|
|
|
var i = length
|
|
|
|
do {
|
|
|
|
i--
|
|
|
|
} while (i >= 0 && get(i).isWhitespace())
|
|
|
|
return subSequence(0, i + 1) as Spanned
|
|
|
|
}
|
2020-11-04 18:21:41 +01:00
|
|
|
|
|
|
|
/**
|
|
|
|
* BidiFormatter.unicodeWrap is insufficient in some cases (see #1921)
|
|
|
|
* So we force isolation manually
|
|
|
|
* https://unicode.org/reports/tr9/#Explicit_Directional_Isolates
|
|
|
|
*/
|
|
|
|
fun CharSequence.unicodeWrap(): String {
|
|
|
|
return "\u2068${this}\u2069"
|
|
|
|
}
|