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

com.faendir.kotlin.autodsl.kapt.KaptSourceInfoResolver.kt Maven / Gradle / Ivy

There is a newer version: 2.4.0
Show newest version
@file:OptIn(KotlinPoetMetadataPreview::class)

package com.faendir.kotlin.autodsl.kapt

import com.faendir.kotlin.autodsl.SourceInfoResolver
import com.faendir.kotlin.autodsl.nonnull
import com.google.devtools.ksp.symbol.ClassKind
import com.squareup.kotlinpoet.*
import com.squareup.kotlinpoet.metadata.*
import kotlinx.metadata.Flag
import kotlinx.metadata.KmClass
import kotlinx.metadata.KmConstructor
import kotlinx.metadata.KmValueParameter
import javax.annotation.processing.ProcessingEnvironment
import javax.annotation.processing.RoundEnvironment
import javax.lang.model.element.ElementKind
import javax.lang.model.element.ExecutableElement
import javax.lang.model.element.TypeElement
import javax.lang.model.element.VariableElement
import javax.lang.model.type.DeclaredType
import javax.lang.model.type.MirroredTypeException
import kotlin.reflect.KClass
import kotlin.reflect.KProperty1

class KaptSourceInfoResolver(private val processingEnv: ProcessingEnvironment, private val roundEnv: RoundEnvironment) :
    SourceInfoResolver {
    override fun getClassesWithAnnotation(annotation: KClass): List =
        roundEnv.getElementsAnnotatedWith(annotation.java).filterIsInstance().map { Type(it) }

    override fun getClassesWithAnnotation(annotation: Type): List =
        roundEnv.getElementsAnnotatedWith(annotation.element).filterIsInstance().map { Type(it) }

    override fun Type.getClassKind(): ClassKind = when {
        kmClass.isEnum -> ClassKind.ENUM_CLASS
        kmClass.isAnnotation -> ClassKind.ANNOTATION_CLASS
        kmClass.isInterface -> ClassKind.INTERFACE
        kmClass.isEnumEntry -> ClassKind.ENUM_ENTRY
        kmClass.isClass -> ClassKind.CLASS
        kmClass.isObject -> ClassKind.OBJECT
        else -> throw IllegalArgumentException()
    }

    override fun  Annotated.getAnnotationTypeProperty(annotation: KClass, property: KProperty1>): ClassName? = try {
        getAnnotation(annotation)?.let(property)?.asClassName()
    } catch (e: MirroredTypeException) {
        (e.typeMirror.asTypeName() as? ClassName)
    }?.mapToKotlin()

    override fun Annotated.hasAnnotation(annotation: KClass): Boolean = getAnnotation(annotation) != null

    override fun  Annotated.getAnnotationProperty(annotation: KClass, property: KProperty1): V? =
        getAnnotation(annotation)?.let(property)

    override fun Type.isAbstract(): Boolean = Flag.IS_ABSTRACT(kmClass.flags)

    override fun Type.getConstructors(): List {
        val constructorElements = element.enclosedElements.filterIsInstance().filter { it.kind == ElementKind.CONSTRUCTOR }.toMutableList()
        return kmClass.constructors.associateWith { kmConstructor ->
            constructorElements.first { element ->
                element.parameters.size == kmConstructor.valueParameters.size && (element.parameters zip kmConstructor.valueParameters).all { (e, k) ->
                    val eType = e.asType().asTypeName().mapToKotlin()
                    val kType = k.type?.asTypeName()?.nonnull
                    if (eType is ParameterizedTypeName && kType is ParameterizedTypeName) {
                        //Invariant kotlin parameters are variant in java, just check erased type
                        eType.rawType == kType.rawType
                    } else {
                        eType == kType
                    }
                }
            }
        }.map { (kmConstructor, element) -> Constructor(element, kmConstructor) }
    }

    override fun Constructor.isAccessible(): Boolean = Flag.IS_PUBLIC(kmConstructor.flags) || Flag.IS_INTERNAL(kmConstructor.flags)

    override fun Type.getPrimaryConstructor(): Constructor? = getConstructors().find { it.kmConstructor.isPrimary }

    override fun Constructor.isValid(): Boolean = true

    override fun Constructor.getParameters(): List =
        (element.parameters zip kmConstructor.valueParameters).map { (element, kmValueParameter) -> Parameter(element, kmValueParameter) }

    override fun Type.asClassName(): ClassName = kmClass.asClassName()

    override fun Parameter.getTypeDeclaration(): Type? {
        val element = processingEnv.typeUtils.asElement(element.asType()) as? TypeElement
        val kmType = try {
            element?.toKmClass()
        } catch (e: IllegalStateException) {
            null
        }
        return if (element != null && kmType != null) Type(element, kmType) else null
    }

    override fun Parameter.getTypeArguments(): List =
        (element.asType() as DeclaredType).typeArguments.mapNotNull {
            try {
                Type(processingEnv.typeUtils.asElement(it) as TypeElement)
            } catch (e: Exception) {
                null
            }
        }

    override fun Parameter.getTypeName(): TypeName = kmValueParameter.type!!.asTypeName()

    override fun Parameter.getName(): String = kmValueParameter.name

    override fun Parameter.hasDefault(): Boolean = kmValueParameter.declaresDefaultValue

    override fun Parameter.getDoc(): String? = processingEnv.elementUtils.getDocComment(element)
}

interface Annotated {
    fun  getAnnotation(annotation: KClass): T?
}

class Type(internal val element: TypeElement, internal val kmClass: KmClass) : Annotated {
    constructor(element: TypeElement) : this(element, element.toKmClass())

    override fun  getAnnotation(annotation: KClass): T? = element.getAnnotation(annotation.java)
}

class Constructor(internal val element: ExecutableElement, internal val kmConstructor: KmConstructor) : Annotated {
    override fun  getAnnotation(annotation: KClass): T? = element.getAnnotation(annotation.java)
}

class Parameter(internal val element: VariableElement, internal val kmValueParameter: KmValueParameter) : Annotated {
    override fun  getAnnotation(annotation: KClass): T? = element.getAnnotation(annotation.java)
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy