
com.github.mvysny.vokdataloader.Utils.kt Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of vok-dataloader Show documentation
Show all versions of vok-dataloader Show documentation
VOK-DataLoader: The Paged/Filtered/Sorted DataLoader API
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