Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Kotlin - How to get KClass<*> annotation parameter from annotation processor

I have the following annotation:

@Target(AnnotationTarget.CLASS)
@Retention(AnnotationRetention.SOURCE)
@MustBeDocumented
annotation class Model(
    val owner: KClass<*>,
    val consumer: KClass<*>
)

@Model(DataOwner::class, DataConsumer::class)
interface Student {
    val name: String
    val group: Group
}

I need to get the value of owner and consumer in my annotation processor.

I've tried this approach:

private inline fun <reified T> findAnnotationValue(
    element: Element,
    annotationClass: KClass<*>,
    valueName: String
): T? {
    return element.annotationMirrors.map {
        it to it.annotationType.asElement() as TypeElement
    }.firstOrNull { (_, element) ->
        element.qualifiedName.contentEquals(annotationClass.qualifiedName)
    }?.let { (mirror, _) ->
        extractValue(mirror, valueName)
    }
}

private inline fun <reified T> extractValue(
    annotationMirror: AnnotationMirror,
    valueName: String
): T? {
    return annotationMirror.elementValues.toList()
        .firstOrNull { (key, _) ->
            key.simpleName.contentEquals(valueName)
        }?.let { (_, value) ->
            value.value as T
        }
}


val ownerClass: KClass<*> = findAnnotationValue(
    element,
    Model::class,
    "owner"
)

But it gave me this error:

e: [kapt] An exception occurred: java.lang.ClassCastException: com.sun.tools.javac.code.Type$ClassType cannot be cast to kotlin.reflect.KClass

I also tried this:

val ownerClass: KClass<*> = element.getAnnotation(Model::class.java).owner

But it gave me this error:

e: [kapt] An exception occurred: javax.lang.model.type.MirroredTypeException: Attempt to access Class object for TypeMirror inc.ahmedmourad.systems.tutors.domain.model.DataOwner

inc.ahmedmourad.systems.tutors.domain.model.DataOwner is the owner value passed to the annotation.

So this's where i'm stuck right now, any help is appreciated. Thank you!

like image 453
Ahmed Mourad Avatar asked Oct 24 '25 12:10

Ahmed Mourad


1 Answers

If you are using KSP then the solution can look something like this:

Using the experimental APIs and assuming the KClass you reference in the annotation is available:

resolver.getSymbolsWithAnnotation("com.example.Model")
    .map {
        val consumerType = try {
            it.getAnnotationsByType(Model::class).first().consumer
            null
        } catch (e: KSTypeNotPresentException) {
            e.ksType
        }
        val consumerDeclaration = consumerType!!.declaration as KSClassDeclaration // etc
    }

If you can't do this, then maintaining a KSP view of the annotation is more flexible:

resolver.getSymbolsWithAnnotation("com.example.Model")
    .map { ksAnnotated ->
        val args = ksAnnotated.annotations.first {
            it.shortName.asString() == "Model" && it.annotationType.resolve().declaration.qualifiedName?.asString() == "com.example.Model"
        }.arguments

        val consumerType = args.first { it.name?.asString() == "owner" }.value as KSType
        val consumerDeclaration = consumerType.declaration as KSClassDeclaration // etc 
    }
like image 88
3 revsDavid Rawson Avatar answered Oct 27 '25 02:10

3 revsDavid Rawson