All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.jetbrains.kotlin.utils.addToStdlib.kt Maven / Gradle / Ivy

There is a newer version: 2.1.0-RC
Show newest version
/*
 * 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

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..