Skip to content

Commit ffd4dce

Browse files
committed
Fixed rendering of Array types and added value rendering for arrays as well :)
1 parent 2f79074 commit ffd4dce

File tree

12 files changed

+426
-109
lines changed

12 files changed

+426
-109
lines changed

core/generated-sources/src/main/kotlin/org/jetbrains/kotlinx/dataframe/impl/Rendering.kt

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,9 @@ import java.net.URL
1616
import java.time.LocalDateTime
1717
import java.time.LocalTime
1818
import kotlin.reflect.KType
19+
import kotlin.reflect.full.isSubtypeOf
1920
import kotlin.reflect.jvm.jvmErasure
21+
import kotlin.reflect.typeOf
2022

2123
internal fun String.truncate(limit: Int): RenderedContent = if (limit in 1 until length) {
2224
if (limit < 4) RenderedContent.truncatedText("...", this)
@@ -57,6 +59,12 @@ internal fun renderType(type: KType?): String {
5759
else -> {
5860
val fullName = type.jvmErasure.qualifiedName ?: return type.toString()
5961
val name = when {
62+
63+
// catching cases like `typeOf<Array<Int>>().jvmErasure.qualifiedName == "IntArray"`
64+
// https://github.com/Kotlin/dataframe/issues/678
65+
type.isSubtypeOf(typeOf<Array<*>>()) ->
66+
"Array"
67+
6068
type.classifier == URL::class ->
6169
fullName.removePrefix("java.net.")
6270

core/generated-sources/src/main/kotlin/org/jetbrains/kotlinx/dataframe/impl/TypeUtils.kt

Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
@file:OptIn(ExperimentalUnsignedTypes::class)
2+
13
package org.jetbrains.kotlinx.dataframe.impl
24

35
import org.jetbrains.kotlinx.dataframe.AnyFrame
@@ -467,3 +469,104 @@ internal fun nothingType(nullable: Boolean): KType =
467469
} else {
468470
typeOf<List<Nothing>>()
469471
}.arguments.first().type!!
472+
473+
@OptIn(ExperimentalUnsignedTypes::class)
474+
private val primitiveArrayClasses = setOf(
475+
BooleanArray::class,
476+
ByteArray::class,
477+
ShortArray::class,
478+
IntArray::class,
479+
LongArray::class,
480+
FloatArray::class,
481+
DoubleArray::class,
482+
CharArray::class,
483+
484+
UByteArray::class,
485+
UShortArray::class,
486+
UIntArray::class,
487+
ULongArray::class,
488+
)
489+
490+
/**
491+
* Returns `true` if this class is a primitive array class like `XArray`.
492+
*
493+
* Use [KClass.isArray] to also check for `Array<>`.
494+
*/
495+
internal val KClass<*>.isPrimitiveArray: Boolean
496+
get() = this in primitiveArrayClasses
497+
498+
/**
499+
* Returns `true` if this class is an array, either a primitive array like `XArray` or `Array<>`.
500+
*
501+
* Use [KClass.isPrimitiveArray] to only check for primitive arrays.
502+
*/
503+
internal val KClass<*>.isArray: Boolean
504+
get() = this in primitiveArrayClasses ||
505+
this.qualifiedName == Array::class.qualifiedName // instance check fails
506+
507+
/**
508+
* Returns `true` if this type is of a primitive array like `XArray`.
509+
*
510+
* Use [KType.isArray] to also check for `Array<>`.
511+
*/
512+
internal val KType.isPrimitiveArray: Boolean
513+
get() =
514+
if (arguments.isNotEmpty()) {
515+
// Catching https://github.com/Kotlin/dataframe/issues/678
516+
// as typeOf<Array<Int>>().classifier == IntArray::class
517+
false
518+
} else {
519+
(classifier as? KClass<*>)?.isPrimitiveArray == true
520+
}
521+
522+
523+
/**
524+
* Returns `true` if this type is of an array, either a primitive array like `XArray` or `Array<>`.
525+
*
526+
* Use [KType.isPrimitiveArray] to only check for primitive arrays.
527+
*/
528+
internal val KType.isArray: Boolean
529+
get() = (classifier as? KClass<*>)?.isArray == true
530+
531+
/**
532+
* Returns `true` if this object is a primitive array like `XArray`.
533+
*
534+
* Use [Any.isArray] to also check for the `Array<>` object.
535+
*/
536+
internal val Any.isPrimitiveArray: Boolean
537+
get() = this::class.isPrimitiveArray
538+
539+
/**
540+
* Returns `true` if this object is an array, either a primitive array like `XArray` or `Array<>`.
541+
*
542+
* Use [Any.isPrimitiveArray] to only check for primitive arrays.
543+
*/
544+
internal val Any.isArray: Boolean
545+
get() = this::class.isArray
546+
547+
/**
548+
* Returns this array object as a list of values.
549+
*
550+
* @throws IllegalArgumentException if this object is not an array
551+
*/
552+
internal fun Any.asArrayToList(): List<*> {
553+
require(this.isArray) { "Not an array" }
554+
return when (this) {
555+
is BooleanArray -> toList()
556+
is ByteArray -> toList()
557+
is ShortArray -> toList()
558+
is IntArray -> toList()
559+
is LongArray -> toList()
560+
is FloatArray -> toList()
561+
is DoubleArray -> toList()
562+
is CharArray -> toList()
563+
564+
is UByteArray -> toList()
565+
is UShortArray -> toList()
566+
is UIntArray -> toList()
567+
is ULongArray -> toList()
568+
569+
is Array<*> -> toList()
570+
else -> error("Not an array")
571+
}
572+
}

core/generated-sources/src/main/kotlin/org/jetbrains/kotlinx/dataframe/impl/Utils.kt

Lines changed: 44 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -340,32 +340,61 @@ internal fun List<String>.joinToCamelCaseString(): String {
340340
.replaceFirstChar { it.lowercaseChar() }
341341
}
342342

343+
/** Returns `true` if this callable is a getter-like function.
344+
*
345+
* A callable is considered getter-like if it is either a property getter,
346+
* or it's a function with no (type) parameters that starts with "get"/"is". */
343347
internal fun KFunction<*>.isGetterLike(): Boolean =
344-
(name.startsWith("get") || name.startsWith("is")) && valueParameters.isEmpty()
348+
(name.startsWith("get") || name.startsWith("is")) &&
349+
valueParameters.isEmpty() &&
350+
typeParameters.isEmpty()
351+
352+
/** Returns `true` if this callable is a getter-like function.
353+
*
354+
* A callable is considered getter-like if it is either a property getter,
355+
* or it's a function with no (type) parameters that starts with "get"/"is". */
356+
internal fun KProperty<*>.isGetterLike(): Boolean = true
345357

358+
/**
359+
* Returns `true` if this callable is a getter-like function.
360+
*
361+
* A callable is considered getter-like if it is either a property getter,
362+
* or it's a function with no (type) parameters that starts with "get"/"is".
363+
*/
346364
internal fun KCallable<*>.isGetterLike(): Boolean =
347365
when (this) {
348-
is KProperty<*> -> true
366+
is KProperty<*> -> isGetterLike()
349367
is KFunction<*> -> isGetterLike()
350368
else -> false
351369
}
352370

371+
/** Returns the column name for this callable.
372+
* 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.
373+
* Otherwise, the name of the callable is returned with proper getter-trimming iff it's a [KFunction]. */
374+
@PublishedApi
375+
internal val KFunction<*>.columnName: String
376+
get() = findAnnotation<ColumnName>()?.name
377+
?: name
378+
.removePrefix("get")
379+
.removePrefix("is")
380+
.replaceFirstChar { it.lowercase() }
381+
382+
/** Returns the column name for this callable.
383+
* 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.
384+
* Otherwise, the name of the callable is returned with proper getter-trimming iff it's a [KFunction]. */
353385
@PublishedApi
354386
internal val KProperty<*>.columnName: String
355387
get() = findAnnotation<ColumnName>()?.name ?: name
356388

389+
/**
390+
* Returns the column name for this callable.
391+
* If the callable contains the [ColumnName] annotation, its [ColumnName.name] is returned.
392+
* Otherwise, the name of the callable is returned with proper getter-trimming iff it's a [KFunction].
393+
*/
357394
@PublishedApi
358395
internal val KCallable<*>.columnName: String
359-
get() = findAnnotation<ColumnName>()?.name
360-
?: when (this) {
361-
// for defining the column names based on a getter-function, we use the function name minus the get/is prefix
362-
is KFunction<*> ->
363-
name
364-
.removePrefix("get")
365-
.removePrefix("is")
366-
.replaceFirstChar { it.lowercase() }
367-
368-
is KProperty<*> -> this.columnName
369-
370-
else -> name
371-
}
396+
get() = when (this) {
397+
is KFunction<*> -> columnName
398+
is KProperty<*> -> columnName
399+
else -> findAnnotation<ColumnName>()?.name ?: name
400+
}

0 commit comments

Comments
 (0)