Skip to content

Commit

Permalink
Fixed rendering of Array types and added value rendering for arrays a…
Browse files Browse the repository at this point in the history
…s well :)
  • Loading branch information
Jolanrensen committed Apr 25, 2024
1 parent 2f79074 commit ffd4dce
Show file tree
Hide file tree
Showing 12 changed files with 426 additions and 109 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,9 @@ import java.net.URL
import java.time.LocalDateTime
import java.time.LocalTime
import kotlin.reflect.KType
import kotlin.reflect.full.isSubtypeOf
import kotlin.reflect.jvm.jvmErasure
import kotlin.reflect.typeOf

internal fun String.truncate(limit: Int): RenderedContent = if (limit in 1 until length) {
if (limit < 4) RenderedContent.truncatedText("...", this)
Expand Down Expand Up @@ -57,6 +59,12 @@ internal fun renderType(type: KType?): String {
else -> {
val fullName = type.jvmErasure.qualifiedName ?: return type.toString()
val name = when {

// catching cases like `typeOf<Array<Int>>().jvmErasure.qualifiedName == "IntArray"`
// https://github.com/Kotlin/dataframe/issues/678
type.isSubtypeOf(typeOf<Array<*>>()) ->
"Array"

type.classifier == URL::class ->
fullName.removePrefix("java.net.")

Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
@file:OptIn(ExperimentalUnsignedTypes::class)

package org.jetbrains.kotlinx.dataframe.impl

import org.jetbrains.kotlinx.dataframe.AnyFrame
Expand Down Expand Up @@ -467,3 +469,104 @@ internal fun nothingType(nullable: Boolean): KType =
} else {
typeOf<List<Nothing>>()
}.arguments.first().type!!

@OptIn(ExperimentalUnsignedTypes::class)
private val primitiveArrayClasses = setOf(
BooleanArray::class,
ByteArray::class,
ShortArray::class,
IntArray::class,
LongArray::class,
FloatArray::class,
DoubleArray::class,
CharArray::class,

UByteArray::class,
UShortArray::class,
UIntArray::class,
ULongArray::class,
)

/**
* Returns `true` if this class is a primitive array class like `XArray`.
*
* Use [KClass.isArray] to also check for `Array<>`.
*/
internal val KClass<*>.isPrimitiveArray: Boolean
get() = this in primitiveArrayClasses

/**
* Returns `true` if this class is an array, either a primitive array like `XArray` or `Array<>`.
*
* Use [KClass.isPrimitiveArray] to only check for primitive arrays.
*/
internal val KClass<*>.isArray: Boolean
get() = this in primitiveArrayClasses ||
this.qualifiedName == Array::class.qualifiedName // instance check fails

/**
* Returns `true` if this type is of a primitive array like `XArray`.
*
* Use [KType.isArray] to also check for `Array<>`.
*/
internal val KType.isPrimitiveArray: Boolean
get() =
if (arguments.isNotEmpty()) {
// Catching https://github.com/Kotlin/dataframe/issues/678
// as typeOf<Array<Int>>().classifier == IntArray::class
false
} else {
(classifier as? KClass<*>)?.isPrimitiveArray == true
}


/**
* Returns `true` if this type is of an array, either a primitive array like `XArray` or `Array<>`.
*
* Use [KType.isPrimitiveArray] to only check for primitive arrays.
*/
internal val KType.isArray: Boolean
get() = (classifier as? KClass<*>)?.isArray == true

/**
* Returns `true` if this object is a primitive array like `XArray`.
*
* Use [Any.isArray] to also check for the `Array<>` object.
*/
internal val Any.isPrimitiveArray: Boolean
get() = this::class.isPrimitiveArray

/**
* Returns `true` if this object is an array, either a primitive array like `XArray` or `Array<>`.
*
* Use [Any.isPrimitiveArray] to only check for primitive arrays.
*/
internal val Any.isArray: Boolean
get() = this::class.isArray

/**
* Returns this array object as a list of values.
*
* @throws IllegalArgumentException if this object is not an array
*/
internal fun Any.asArrayToList(): List<*> {
require(this.isArray) { "Not an array" }
return when (this) {
is BooleanArray -> toList()
is ByteArray -> toList()
is ShortArray -> toList()
is IntArray -> toList()
is LongArray -> toList()
is FloatArray -> toList()
is DoubleArray -> toList()
is CharArray -> toList()

is UByteArray -> toList()
is UShortArray -> toList()
is UIntArray -> toList()
is ULongArray -> toList()

is Array<*> -> toList()
else -> error("Not an array")
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -340,32 +340,61 @@ internal fun List<String>.joinToCamelCaseString(): String {
.replaceFirstChar { it.lowercaseChar() }
}

/** Returns `true` if this callable is a getter-like function.
*
* A callable is considered getter-like if it is either a property getter,
* or it's a function with no (type) parameters that starts with "get"/"is". */
internal fun KFunction<*>.isGetterLike(): Boolean =
(name.startsWith("get") || name.startsWith("is")) && valueParameters.isEmpty()
(name.startsWith("get") || name.startsWith("is")) &&
valueParameters.isEmpty() &&
typeParameters.isEmpty()

/** Returns `true` if this callable is a getter-like function.
*
* A callable is considered getter-like if it is either a property getter,
* or it's a function with no (type) parameters that starts with "get"/"is". */
internal fun KProperty<*>.isGetterLike(): Boolean = true

/**
* Returns `true` if this callable is a getter-like function.
*
* A callable is considered getter-like if it is either a property getter,
* or it's a function with no (type) parameters that starts with "get"/"is".
*/
internal fun KCallable<*>.isGetterLike(): Boolean =
when (this) {
is KProperty<*> -> true
is KProperty<*> -> isGetterLike()
is KFunction<*> -> isGetterLike()
else -> false
}

/** Returns the column name for this callable.
* If the callable contains the [ColumnName][org.jetbrains.kotlinx.dataframe.annotations.ColumnName] annotation, its [ColumnName.name][org.jetbrains.kotlinx.dataframe.annotations.ColumnName.name] is returned.
* Otherwise, the name of the callable is returned with proper getter-trimming iff it's a [KFunction]. */
@PublishedApi
internal val KFunction<*>.columnName: String
get() = findAnnotation<ColumnName>()?.name
?: name
.removePrefix("get")
.removePrefix("is")
.replaceFirstChar { it.lowercase() }

/** Returns the column name for this callable.
* If the callable contains the [ColumnName][org.jetbrains.kotlinx.dataframe.annotations.ColumnName] annotation, its [ColumnName.name][org.jetbrains.kotlinx.dataframe.annotations.ColumnName.name] is returned.
* Otherwise, the name of the callable is returned with proper getter-trimming iff it's a [KFunction]. */
@PublishedApi
internal val KProperty<*>.columnName: String
get() = findAnnotation<ColumnName>()?.name ?: name

/**
* Returns the column name for this callable.
* If the callable contains the [ColumnName] annotation, its [ColumnName.name] is returned.
* Otherwise, the name of the callable is returned with proper getter-trimming iff it's a [KFunction].
*/
@PublishedApi
internal val KCallable<*>.columnName: String
get() = findAnnotation<ColumnName>()?.name
?: when (this) {
// for defining the column names based on a getter-function, we use the function name minus the get/is prefix
is KFunction<*> ->
name
.removePrefix("get")
.removePrefix("is")
.replaceFirstChar { it.lowercase() }

is KProperty<*> -> this.columnName

else -> name
}
get() = when (this) {
is KFunction<*> -> columnName
is KProperty<*> -> columnName
else -> findAnnotation<ColumnName>()?.name ?: name
}
Loading

0 comments on commit ffd4dce

Please sign in to comment.