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

main.reflection.TypeRef.kt Maven / Gradle / Ivy

There is a newer version: 0.77.0
Show newest version
package de.peekandpoke.ultra.common.reflection

import kotlin.reflect.KClass
import kotlin.reflect.KType
import kotlin.reflect.KTypeProjection
import kotlin.reflect.full.createType

/**
 * Creates a type safe representation of the given [KType]
 *
 * [TypeRef] is a wrapper around KType. It also carries type information in [T].
 *
 * By doing so we can utilize the compiler for checking that reflection types are used correctly.
 */
data class TypeRef internal constructor(val type: KType) {

    companion object {

        /** Cache for [KType] to [TypeRef] */
        private val cachedKTypes = mutableMapOf>()

        /** Cache for [KClass] to [TypeRef] */
        private val cachedNullableKClasses = mutableMapOf, TypeRef<*>>()

        /** Cache for [KClass] to [TypeRef] */
        private val cachedNonNullKClasses = mutableMapOf, TypeRef<*>>()

        fun  createForKType(type: KType): TypeRef {
            @Suppress("UNCHECKED_CAST")
            return cachedKTypes.getOrPut(type) {
                TypeRef(type)
            } as TypeRef
        }

        fun  createForKClass(cls: KClass<*>, nullable: Boolean): TypeRef {

            val cache = if (nullable) {
                cachedNullableKClasses
            } else {
                cachedNonNullKClasses
            }

            @Suppress("UNCHECKED_CAST")
            return cache.getOrPut(cls) {

                val type = cls.createType(
                    arguments = cls.typeParameters.map {
                        KTypeProjection.invariant(kotlin.Any::class.createType())
                    }
                )
                // Return
                createForKType(type)
            } as TypeRef
        }

        val Unit = kType()
        val UnitNull = kType()

        val Any = kType()
        val AnyNull = kType()

        val Boolean = kType()
        val BooleanNull = kType()

        val Byte = kType()
        val ByteNull = kType()

        val Char = kType()
        val CharNull = kType()

        val Double = kType()
        val DoubleNull = kType()

        val Float = kType()
        val FloatNull = kType()

        val Int = kType()
        val IntNull = kType()

        val Long = kType()
        val LongNull = kType()

        val Number = kType()
        val NumberNull = kType()

        val Short = kType()
        val ShortNull = kType()

        val String = kType()
        val StringNull = kType()
    }

    /**
     * Returns a [ReifiedKType] of this TypeRef
     */
    val reified: ReifiedKType by lazy { ReifiedKType(type) }

    /**
     * Converts to a nullable type
     */
    val nullable: TypeRef by lazy(LazyThreadSafetyMode.NONE) {
        @Suppress("RemoveExplicitTypeArguments")
        TypeRef(
            type.classifier!!.createType(
                arguments = type.arguments,
                nullable = true
            )
        )
    }

    /**
     * Wraps the current type as a [List] type
     */
    val list: TypeRef> by lazy(LazyThreadSafetyMode.NONE) {
        @Suppress("RemoveExplicitTypeArguments")
        createForKType>(
            List::class.createType(
                arguments = listOf(KTypeProjection.invariant(type)),
                nullable = false
            )
        )
    }

    /**
     * Wraps the current type with the given generic type [W]
     *
     * E.g. makes a String? type a MyWrapper type
     *
     * When the given type [W] does not have exactly one type parameter an exception is thrown
     */
    inline fun  wrapWith(): TypeRef {
        @Suppress("USELESS_IS_CHECK")
        return wrapWith(cls = W::class, null is W)
    }

    /**
     * Wraps the current type with the given generic type [W]
     *
     * E.g. makes a String? type a MyWrapper type
     *
     * When the given type [W] does not have exactly one type parameter an exception is thrown
     */
    @Suppress("UNCHECKED_CAST")
    fun  wrapWith(cls: KClass, nullable: Boolean): TypeRef {

        if (cls.typeParameters.size != 1) {
            error("Can only wrap with a generic type with exactly one type parameter")
        }

        val cache = if (nullable) {
            nullableWrapCache
        } else {
            nonNullWrapCache
        }

        return cache.getOrPut(cls) {
            createForKType(
                cls.createType(
                    arguments = listOf(KTypeProjection.invariant(type)),
                    nullable = nullable
                )
            )
        } as TypeRef
    }

    /**
     * Internal helper val, that does not carry the correct generic type information.
     *
     * @see [unList] which takes care of the generic type information
     */
    internal val unlisted: TypeRef by lazy(LazyThreadSafetyMode.NONE) {
        if (type.classifier != List::class) {
            error("The current type [$this] must have the classifier List::class")
        }

        @Suppress("RemoveExplicitTypeArguments")
        TypeRef(type.arguments[0].type!!)
    }

    /**
     * Internal cache used by [wrapWith]
     */
    private val nonNullWrapCache = mutableMapOf, TypeRef<*>>()

    /**
     * Internal cache used by [wrapWith]
     */
    private val nullableWrapCache = mutableMapOf, TypeRef<*>>()
}

/**
 * Unwraps a List-type
 *
 * E.g. makes a List-type a String?-type.
 *
 * When the classifier of the current type is not List::class an exception is thrown.
 */
@Suppress("UNCHECKED_CAST")
val  TypeRef>.unList: TypeRef
    get() = unlisted as TypeRef




© 2015 - 2024 Weber Informatics LLC | Privacy Policy