org.jetbrains.kotlin.utils.addToStdlib.kt Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of kotlin-compiler-embeddable Show documentation
Show all versions of kotlin-compiler-embeddable Show documentation
the Kotlin compiler embeddable
/*
* Copyright 2010-2020 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 org.jetbrains.kotlin.utils.addToStdlib
import org.jetbrains.kotlin.utils.IDEAPlatforms
import org.jetbrains.kotlin.utils.IDEAPluginsCompatibilityAPI
import java.lang.reflect.Modifier
import java.util.*
import java.util.concurrent.ConcurrentHashMap
import kotlin.reflect.KMutableProperty0
inline fun Sequence<*>.firstIsInstanceOrNull(): T? {
for (element in this) if (element is T) return element
return null
}
inline fun Iterable<*>.firstIsInstanceOrNull(): T? {
for (element in this) if (element is T) return element
return null
}
inline fun Array<*>.firstIsInstanceOrNull(): T? {
for (element in this) if (element is T) return element
return null
}
inline fun Sequence<*>.firstIsInstance(): T {
for (element in this) if (element is T) return element
throw NoSuchElementException("No element of given type found")
}
inline fun Iterable<*>.firstIsInstance(): T {
for (element in this) if (element is T) return element
throw NoSuchElementException("No element of given type found")
}
inline fun Array<*>.firstIsInstance(): T {
for (element in this) if (element is T) return element
throw NoSuchElementException("No element of given type found")
}
inline fun Iterable<*>.filterIsInstanceWithChecker(additionalChecker: (T) -> Boolean): List {
val result = arrayListOf()
for (element in this) {
if (element is T && additionalChecker(element)) {
result += element
}
}
return result
}
inline fun Iterable<*>.lastIsInstanceOrNull(): T? {
when (this) {
is List<*> -> {
for (i in this.indices.reversed()) {
val element = this[i]
if (element is T) return element
}
return null
}
else -> {
return reversed().firstIsInstanceOrNull()
}
}
}
inline fun Iterable.partitionIsInstance(): Pair, List> {
val first = ArrayList()
val second = ArrayList()
for (element in this) {
if (element is R) {
first.add(element)
} else {
second.add(element)
}
}
return Pair(first, second)
}
@Suppress("INVISIBLE_REFERENCE", "INVISIBLE_MEMBER")
@UnsafeCastFunction
inline fun List<*>.castAll(): List<@kotlin.internal.NoInfer T> {
for (element in this) element as T
@Suppress("UNCHECKED_CAST")
return this as List
}
@Suppress("INVISIBLE_REFERENCE", "INVISIBLE_MEMBER")
@UnsafeCastFunction
inline fun Collection<*>.castAll(): Collection<@kotlin.internal.NoInfer T> {
for (element in this) element as T
@Suppress("UNCHECKED_CAST")
return this as Collection
}
fun sequenceOfLazyValues(vararg elements: () -> T): Sequence = elements.asSequence().map { it() }
fun Pair.swap(): Pair = Pair(second, first)
@RequiresOptIn(
message = """
Usage of this function is unsafe because it does not have native compiler support
This means that compiler won't report UNCHECKED_CAST, CAST_NEVER_SUCCEED or similar
diagnostics in case of error cast (which can happen immediately or after some
refactoring of class hierarchy)
Consider using regular `as` and `as?`
""",
level = RequiresOptIn.Level.ERROR
)
annotation class UnsafeCastFunction
@Suppress("INVISIBLE_REFERENCE", "INVISIBLE_MEMBER")
@UnsafeCastFunction
inline fun Any?.safeAs(): @kotlin.internal.NoInfer T? = this as? T
@UnsafeCastFunction
inline fun Any?.cast(): T = this as T
@UnsafeCastFunction
inline fun Any?.assertedCast(message: () -> String): T = this as? T ?: throw AssertionError(message())
fun constant(calculator: () -> T): T {
val cached = constantMap[calculator]
@Suppress("UNCHECKED_CAST")
if (cached != null) return cached as T
// safety check
val fields = calculator::class.java.declaredFields.filter { it.modifiers.and(Modifier.STATIC) == 0 }
assert(fields.isEmpty()) {
"No fields in the passed lambda expected but ${fields.joinToString()} found"
}
val value = calculator()
constantMap[calculator] = value
return value
}
private val constantMap = ConcurrentHashMap, Any>()
fun String.indexOfOrNull(char: Char, startIndex: Int = 0, ignoreCase: Boolean = false): Int? =
indexOf(char, startIndex, ignoreCase).takeIf { it >= 0 }
fun String.lastIndexOfOrNull(char: Char, startIndex: Int = lastIndex, ignoreCase: Boolean = false): Int? =
lastIndexOf(char, startIndex, ignoreCase).takeIf { it >= 0 }
@IDEAPluginsCompatibilityAPI(
IDEAPlatforms._211,
IDEAPlatforms._212,
IDEAPlatforms._213,
message = "Use firstNotNullOfOrNull from stdlib instead",
plugins = "Android plugin in the IDEA, kotlin-ultimate.kotlin-ocswift"
)
inline fun Iterable.firstNotNullResult(transform: (T) -> R?): R? {
for (element in this) {
val result = transform(element)
if (result != null) return result
}
return null
}
@IDEAPluginsCompatibilityAPI(
IDEAPlatforms._211,
IDEAPlatforms._212,
IDEAPlatforms._213,
message = "Use firstNotNullOfOrNull from stdlib instead",
plugins = "Android plugin in the IDEA"
)
inline fun Array.firstNotNullResult(transform: (T) -> R?): R? {
for (element in this) {
val result = transform(element)
if (result != null) return result
}
return null
}
inline fun Iterable.sumByLong(selector: (T) -> Long): Long {
var sum: Long = 0
for (element in this) {
sum += selector(element)
}
return sum
}
inline fun , O> C.ifNotEmpty(body: C.() -> O?): O? = if (isNotEmpty()) this.body() else null
inline fun Array.ifNotEmpty(body: Array.() -> O?): O? = if (isNotEmpty()) this.body() else null
inline fun measureTimeMillisWithResult(block: () -> T): Pair {
val start = System.currentTimeMillis()
val result = block()
return Pair(System.currentTimeMillis() - start, result)
}
fun > Iterable>.flattenTo(c: C): C {
for (element in this) {
c.addAll(element)
}
return c
}
inline fun > Iterable.flatMapToNullable(destination: C, transform: (T) -> Iterable?): C? {
for (element in this) {
val list = transform(element) ?: return null
destination.addAll(list)
}
return destination
}
inline fun Iterable.same(extractor: (T) -> R): Boolean {
val iterator = iterator()
val firstValue = extractor(iterator.next())
while (iterator.hasNext()) {
val item = iterator.next()
val value = extractor(item)
if (value != firstValue) {
return false
}
}
return true
}
inline fun runIf(condition: Boolean, block: () -> R): R? = if (condition) block() else null
inline fun runUnless(condition: Boolean, block: () -> R): R? = if (condition) null else block()
inline fun A.butIf(condition: Boolean, block: (A) -> B): B = if (condition) block(this) else this
inline fun Collection.foldMap(transform: (T) -> R, operation: (R, R) -> R): R {
val iterator = iterator()
var result = transform(iterator.next())
while (iterator.hasNext()) {
result = operation(result, transform(iterator.next()))
}
return result
}
fun MutableList.trimToSize(newSize: Int) {
subList(newSize, size).clear()
}
inline fun MutableMap.getOrPut(key: K, defaultValue: (K) -> VA, postCompute: (VA) -> Unit): V {
val value = get(key)
return if (value == null) {
val answer = defaultValue(key)
put(key, answer)
postCompute(answer)
answer
} else {
value
}
}
fun Set.compactIfPossible(): Set =
when (size) {
0 -> emptySet()
1 -> setOf(single())
else -> this
}
fun Map.compactIfPossible(): Map =
when (size) {
0 -> emptyMap()
1 -> Collections.singletonMap(keys.single(), values.single())
else -> this
}
inline fun R.applyIf(`if`: Boolean, body: R.() -> T): T =
if (`if`) body() else this
inline fun Boolean.ifTrue(body: () -> T?): T? =
if (this) body() else null
inline fun Boolean.ifFalse(body: () -> T?): T? =
if (!this) body() else null
inline fun List.flatGroupBy(keySelector: (T) -> Collection): Map> {
return flatGroupBy(keySelector, keyTransformer = { it }, valueTransformer = { it })
}
inline fun List.flatGroupBy(
keySelector: (T) -> Collection,
keyTransformer: (U) -> K,
valueTransformer: (T) -> V,
): Map> {
val result = mutableMapOf>()
for (element in this) {
val keys = keySelector(element)
val value = valueTransformer(element)
for (key in keys) {
val transformedKey = keyTransformer(key)
// Map.computeIfAbsent is missing in JDK 1.6
var list = result[transformedKey]
if (list == null) {
list = mutableListOf()
result[transformedKey] = list
}
list += value
}
}
return result
}
inline fun List.flatAssociateBy(selector: (T) -> Collection): Map {
return buildMap {
for (value in this@flatAssociateBy) {
for (key in selector(value)) {
put(key, value)
}
}
}
}
fun MutableList.popLast(): E = removeAt(lastIndex)
fun , V> enumMapOf(vararg pairs: Pair): EnumMap = EnumMap(mapOf(*pairs))
fun > enumSetOf(element: T, vararg elements: T): EnumSet = EnumSet.of(element, *elements)
fun shouldNotBeCalled(message: String = "should not be called"): Nothing {
error(message)
}
private inline fun Iterable.zipWithDefault(other: Iterable, leftDefault: () -> T, rightDefault: () -> R): List> {
val leftIterator = this.iterator()
val rightIterator = other.iterator()
return buildList {
while (leftIterator.hasNext() && rightIterator.hasNext()) {
add(leftIterator.next() to rightIterator.next())
}
while (leftIterator.hasNext()) {
add(leftIterator.next() to rightDefault())
}
while (rightIterator.hasNext()) {
add(leftDefault() to rightIterator.next())
}
}
}
fun Iterable.zipWithNulls(other: Iterable): List> {
return zipWithDefault(other, { null }, { null })
}
/**
* Use this function to indicate that some when branch is semantically unreachable
*/
fun unreachableBranch(argument: Any?): Nothing {
error("This argument should've been processed by previous when branches but it wasn't: $argument")
}
/**
* Calls [appendElement] on [buffer] for all the elements, also appending [separator] between them and using the given [prefix]
* and [postfix] if supplied.
*
* If the collection could be huge, you can specify a non-negative value of [limit], in which case only the first [limit]
* elements will be appended, followed by the [truncated] string (which defaults to "...").
*/
fun Iterable.joinToWithBuffer(
buffer: A,
separator: CharSequence = ", ",
prefix: CharSequence = "",
postfix: CharSequence = "",
limit: Int = -1,
truncated: CharSequence = "...",
appendElement: A.(T) -> Unit,
): A {
buffer.append(prefix)
var count = 0
for (element in this) {
if (++count > 1) buffer.append(separator)
if (limit < 0 || count <= limit) {
buffer.appendElement(element)
} else break
}
if (limit in 0.. KMutableProperty0.getOrSetIfNull(compute: () -> V): V =
this.get() ?: compute().also {
this.set(it)
}