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

com.github.mvysny.vokdataloader.Utils.kt Maven / Gradle / Ivy

The newest version!
package com.github.mvysny.vokdataloader

import java.beans.Introspector
import java.beans.PropertyDescriptor
import java.lang.reflect.Method
import java.math.BigDecimal
import java.math.BigInteger
import java.text.BreakIterator
import java.util.*
import kotlin.Comparator

/**
 * Returns a getter method for given [propertyName] for this class. Fails if there is no such property, or if the
 * property is write-only (it doesn't have a getter).
 */
public fun Class<*>.getGetter(propertyName: String): Method {
    val properties: Array = Introspector.getBeanInfo(this).propertyDescriptors

    // it.name.substringBefore('-') explanation:
    // Kotlin inline classes mangle property names to make them inaccessible from Java.
    // See https://gitlab.com/mvysny/vok-dataloader/-/issues/7 for more details
    val propertyDescriptor: PropertyDescriptor = properties.firstOrNull { it.name.substringBefore('-') == propertyName }
            ?: throw IllegalStateException("Bean $this has no property $propertyName. Available properties: ${properties.map { it.name }}")

    return propertyDescriptor.readMethod
            ?: throw IllegalStateException("Bean $this has no readMethod for property $propertyDescriptor")
}

/**
 * Returns the number of items in this range. Returns 0 for empty range.
 */
public val IntRange.length: Int get() {
    if (isEmpty()) return 0
    val len = endInclusive - start + 1
    return if (len < 0) Int.MAX_VALUE else len
}

/**
 * Returns the number of items in this range. Returns 0 for empty range.
 */
public val LongRange.length: Long get() {
    if (isEmpty()) return 0
    val len = endInclusive - start + 1
    return if (len < 0) Long.MAX_VALUE else len
}

/**
 * Returns a range that is an intersection of this range and [other].
 */
public fun IntRange.intersection(other: IntRange): IntRange {
    if (this.first > other.last || this.last < other.first) {
        return IntRange.EMPTY
    }
    if (contains(other)) {
        return other
    }
    if (other.contains(this)) {
        return this
    }
    val s: Int = this.first.coerceAtLeast(other.first)
    val e: Int = this.last.coerceAtMost(other.last)
    assert(s <= e)
    return s..e
}

/**
 * Checks whether this range fully contains the [other] range.
 */
public operator fun > ClosedRange.contains(other: ClosedRange): Boolean =
        other.isEmpty() || (start <= other.start && endInclusive >= other.endInclusive)

/**
 * Returns a range that is an intersection of this range and [other].
 */
public fun LongRange.intersection(other: LongRange): LongRange {
    if (this.first > other.last || this.last < other.first) {
        return LongRange.EMPTY
    }
    if (contains(other)) {
        return other
    }
    if (other.contains(this)) {
        return this
    }
    val s: Long = this.first.coerceAtLeast(other.first)
    val e: Long = this.last.coerceAtMost(other.last)
    assert(s <= e)
    return s..e
}

public val ClosedRange.longRange: LongRange get() = start.toLong()..endInclusive.toLong()

public val ClosedRange.intRange: IntRange get() =
    start.coerceIn(Int.MIN_VALUE.toLong(), Int.MAX_VALUE.toLong()).toInt()..
        endInclusive.coerceIn(Int.MIN_VALUE.toLong(), Int.MAX_VALUE.toLong()).toInt()

/**
 * Returns a view of the portion of this list between the specified [indexRange].
 * The returned list is backed by this list, so non-structural changes in the returned list are reflected in this list, and vice-versa.
 *
 * Structural changes in the base list make the behavior of the view undefined.
 */
public fun  List.subList(indexRange: IntRange): List {
    if (indexRange.isEmpty()) {
        val index: Int = indexRange.first.coerceIn(0..size)
        return subList(index, index)
    }
    return subList(indexRange.first, if (indexRange.last == Int.MAX_VALUE) Int.MAX_VALUE else indexRange.last + 1)
}

/**
 * Splits text into words, no spaces. Optionally returns the punctuation characters.
 * Uses [BreakIterator.getWordInstance] - see Javadoc for [BreakIterator] for more details.
 * @receiver the text to split..
 * @param punctuation defaults to false. If false, punctuation is not returned.
 * @return a list of words, never null, may be empty.
 */
public fun String.splitToWords(punctuation: Boolean = false, locale: Locale = Locale.getDefault()): LinkedList {
    val bi: BreakIterator = BreakIterator.getWordInstance(locale)
    bi.setText(this)
    val result = LinkedList()
    while (true) {
        val current: Int = bi.current()
        val next: Int = bi.next()
        if (next == BreakIterator.DONE) {
            break
        }
        val word: String = substring(current, next).trim()
        if (word.isEmpty()) {
            continue
        }
        val c: Int = word.codePointAt(0)
        if (punctuation || Character.isAlphabetic(c) || Character.isDigit(c)) {
            result.add(word)
        }
    }
    return result
}

/**
 * Returns a [Comparator] which moves null values upfront. Shields the
 * receiver comparator from receiving null values.
 */
public fun  Comparator.nullsFirst(): Comparator = Comparator { o1: T?, o2: T? ->
    when {
        o1 == null && o2 == null -> 0
        o1 == null && o2 != null -> -1
        o1 != null && o2 == null -> 1
        else -> [email protected](o1, o2)
    }
}

/**
 * Converts this number to [BigDecimal].
 */
public fun Number.toBigDecimal(): BigDecimal = when (this) {
    is BigDecimal -> this
    is BigInteger -> BigDecimal(this)
    is Long -> BigDecimal(this)
    is Int -> BigDecimal(this)
    is Short -> BigDecimal(toInt())
    is Byte -> BigDecimal(toInt())
    else -> BigDecimal.valueOf(toDouble())
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy