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

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

The newest version!
package com.faendir.kotlin.autodsl.kapt

import com.faendir.kotlin.autodsl.SourceInfoResolver
import com.faendir.kotlin.autodsl.nonnull
import com.faendir.kotlin.autodsl.toRawType
import com.faendir.kotlin.autodsl.withoutAnnotations
import com.google.devtools.ksp.symbol.ClassKind
import com.squareup.kotlinpoet.ClassName
import com.squareup.kotlinpoet.FunSpec
import com.squareup.kotlinpoet.KModifier
import com.squareup.kotlinpoet.LambdaTypeName
import com.squareup.kotlinpoet.ParameterSpec
import com.squareup.kotlinpoet.ParameterizedTypeName
import com.squareup.kotlinpoet.TypeName
import com.squareup.kotlinpoet.TypeSpec
import com.squareup.kotlinpoet.asClassName
import com.squareup.kotlinpoet.asTypeName
import com.squareup.kotlinpoet.metadata.classinspectors.ElementsClassInspector
import com.squareup.kotlinpoet.metadata.specs.toTypeSpec
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, it.toTypeSpec()) }

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

    override fun Type.getClassKind(): ClassKind = when (typeSpec.kind) {
        TypeSpec.Kind.CLASS -> {
            when {
                typeSpec.modifiers.contains(KModifier.ENUM) -> ClassKind.ENUM_CLASS
                typeSpec.modifiers.contains(KModifier.ANNOTATION) -> ClassKind.ANNOTATION_CLASS
                element.kind == ElementKind.ENUM_CONSTANT -> ClassKind.ENUM_ENTRY
                else -> ClassKind.CLASS
            }
        }

        TypeSpec.Kind.OBJECT -> ClassKind.OBJECT
        TypeSpec.Kind.INTERFACE -> ClassKind.INTERFACE
    }

    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 = typeSpec.modifiers.contains(KModifier.ABSTRACT)

    override fun Type.getConstructors(): List {
        val constructorElements = element.enclosedElements.filterIsInstance().filter { it.kind == ElementKind.CONSTRUCTOR }.toMutableList()
        return (listOfNotNull(typeSpec.primaryConstructor?.let { it to true }) + typeSpec.funSpecs.filter { it.isConstructor }
            .map { it to false }).map { (constructorSpec, isPrimary) ->
            val element = constructorElements.first { element ->
                element.parameters.size == constructorSpec.parameters.size && (element.parameters zip constructorSpec.parameters).all { (e, k) ->
                    val eType = e.asType().asTypeName().mapToKotlin()
                    val kType = k.type.nonnull
                    if (eType is ParameterizedTypeName && kType is ParameterizedTypeName) {
                        //Invariant kotlin parameters are variant in java, just check erased type
                        eType.rawType == kType.rawType
                    } else if (eType is ParameterizedTypeName && kType is LambdaTypeName) {
                        eType.typeArguments.map { it.toRawType() } == listOfNotNull(kType.receiver) + kType.parameters.map { it.type.withoutAnnotations() } + kType.returnType
                    } else {
                        eType == kType
                    }
                }
            }
            Constructor(element, constructorSpec, isPrimary)
        }
    }

    override fun Constructor.isAccessible(): Boolean = KModifier.PRIVATE !in constructorSpec.modifiers && KModifier.PROTECTED !in constructorSpec.modifiers

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

    override fun Constructor.isValid(): Boolean = true

    override fun Constructor.getParameters(): List =
        (element.parameters zip constructorSpec.parameters).map { (element, parameterSpec) ->
            Parameter(
                element,
                parameterSpec.toBuilder(type = parameterSpec.type.withoutAnnotations()).build()
            )
        }

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

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

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

    override fun Parameter.getTypeName(): TypeName = parameterSpec.type

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

    override fun Parameter.hasDefault(): Boolean = parameterSpec.defaultValue != null

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

    private val classInspector = ElementsClassInspector.create(lenient = true, processingEnv.elementUtils, processingEnv.typeUtils)
    private fun TypeElement.toTypeSpec() = toTypeSpec(lenient = true, classInspector)
}

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

class Type(internal val element: TypeElement, internal val typeSpec: TypeSpec) : Annotated {
    override fun  getAnnotation(annotation: KClass): T? = element.getAnnotation(annotation.java)

    override fun toString(): String {
        return element.toString()
    }
}

class Constructor(internal val element: ExecutableElement, internal val constructorSpec: FunSpec, internal val isPrimary: Boolean) : Annotated {
    override fun  getAnnotation(annotation: KClass): T? = element.getAnnotation(annotation.java)
}

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




© 2015 - 2024 Weber Informatics LLC | Privacy Policy