com.faendir.kotlin.autodsl.kapt.KaptSourceInfoResolver.kt Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of processor Show documentation
Show all versions of processor Show documentation
Auto-generates DSL for your Kotlin projects using annotations.
@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)
}