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

io.micronaut.kotlin.processing.annotation.KotlinAnnotationMetadataBuilder.kt Maven / Gradle / Ivy

There is a newer version: 4.7.5
Show newest version
/*
 * Copyright 2017-2022 original authors
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package io.micronaut.kotlin.processing.annotation

import com.google.devtools.ksp.getClassDeclarationByName
import com.google.devtools.ksp.isConstructor
import com.google.devtools.ksp.isDefault
import com.google.devtools.ksp.processing.Resolver
import com.google.devtools.ksp.processing.SymbolProcessorEnvironment
import com.google.devtools.ksp.symbol.ClassKind
import com.google.devtools.ksp.symbol.KSAnnotated
import com.google.devtools.ksp.symbol.KSAnnotation
import com.google.devtools.ksp.symbol.KSClassDeclaration
import com.google.devtools.ksp.symbol.KSDeclaration
import com.google.devtools.ksp.symbol.KSFunctionDeclaration
import com.google.devtools.ksp.symbol.KSNode
import com.google.devtools.ksp.symbol.KSPropertyDeclaration
import com.google.devtools.ksp.symbol.KSPropertyGetter
import com.google.devtools.ksp.symbol.KSPropertySetter
import com.google.devtools.ksp.symbol.KSType
import com.google.devtools.ksp.symbol.KSTypeReference
import com.google.devtools.ksp.symbol.KSValueParameter
import com.google.devtools.ksp.symbol.KSVisitor
import com.google.devtools.ksp.symbol.Location
import com.google.devtools.ksp.symbol.Origin
import io.micronaut.context.annotation.Property
import io.micronaut.core.annotation.AnnotationClassValue
import io.micronaut.core.annotation.AnnotationMetadata
import io.micronaut.core.annotation.AnnotationUtil
import io.micronaut.core.annotation.AnnotationValue
import io.micronaut.core.reflect.ClassUtils
import io.micronaut.core.util.ArrayUtils
import io.micronaut.core.util.StringUtils
import io.micronaut.inject.annotation.AbstractAnnotationMetadataBuilder
import io.micronaut.inject.annotation.MutableAnnotationMetadata
import io.micronaut.inject.visitor.VisitorContext
import io.micronaut.kotlin.processing.getBinaryName
import io.micronaut.kotlin.processing.getClassDeclaration
import io.micronaut.kotlin.processing.visitor.KotlinVisitorContext
import java.lang.annotation.Repeatable
import java.lang.annotation.RetentionPolicy
import java.util.*

internal class KotlinAnnotationMetadataBuilder(
    private val symbolProcessorEnvironment: SymbolProcessorEnvironment,
    var resolver: Resolver,
    private val visitorContext: KotlinVisitorContext
) : AbstractAnnotationMetadataBuilder() {

    companion object {
        private fun getTypeForAnnotation(annotationMirror: KSAnnotation, visitorContext: KotlinVisitorContext): KSClassDeclaration {
            return annotationMirror.annotationType.resolve().declaration.getClassDeclaration(visitorContext)
        }
        fun getAnnotationTypeName(resolver: Resolver, annotationMirror: KSAnnotation, visitorContext: KotlinVisitorContext): String {
            val type = getTypeForAnnotation(annotationMirror, visitorContext)
            return type.getBinaryName(resolver, visitorContext)
        }
    }

    override fun isRepeatableAnnotationContainer(annotationValue: AnnotationValue<*>?): Boolean {
        val repeatableAnnotations = annotationValue!!.getAnnotations(AnnotationMetadata.VALUE_MEMBER)
        return repeatableAnnotations.isNotEmpty() && repeatableAnnotations.all { isRepeatableAnnotation(it) }
    }

    private fun isRepeatableAnnotation(value: AnnotationValue): Boolean {
        val annotationMirror = getAnnotationMirror(value.annotationName).orElse(null)
        if (annotationMirror == null) {
            return ClassUtils.forName(value.annotationName, null)
                .map { it -> it.annotations.any { it.annotationClass.java == Repeatable::class.java } }
                .orElse(false)
        }
        return getRepeatableContainerNameForType(annotationMirror) != null
    }

    override fun getTypeForAnnotation(annotationMirror: KSAnnotation): KSAnnotated {
        return KotlinAnnotationType(annotationMirror, Companion.getTypeForAnnotation(annotationMirror, visitorContext))
    }

    override fun hasAnnotation(element: KSAnnotated, annotation: Class): Boolean {
        return hasAnnotation(element, annotation.name)
    }

    override fun hasAnnotation(element: KSAnnotated, annotation: String): Boolean {
        return element.annotations.map {
            it.annotationType.resolve().declaration.qualifiedName
        }.any {
            it?.asString() == annotation
        }
    }

    override fun hasAnnotations(element: KSAnnotated): Boolean {
        var annotated = element
        if (annotated is KotlinAnnotationType) {
            annotated = annotated.type
        }
        return if (annotated is KSPropertyDeclaration) {
            annotated.annotations.iterator().hasNext() ||
                    annotated.getter?.annotations?.iterator()?.hasNext() ?: false
        } else {
            annotated.annotations.iterator().hasNext()
        }
    }

    override fun getAnnotationTypeName(annotationMirror: KSAnnotation): String {
        return Companion.getAnnotationTypeName(resolver, annotationMirror, visitorContext)
    }

    override fun getElementName(element: KSAnnotated): String {
        var annotated = element
        if (annotated is KotlinAnnotationType) {
            annotated = annotated.type
        }
        if (annotated is KSDeclaration) {
            return if (annotated is KSClassDeclaration) {
                annotated.qualifiedName!!.asString()
            } else {
                annotated.simpleName.asString()
            }
        }
        TODO("Not yet implemented")
    }

    override fun getAnnotationsForType(element: KSAnnotated): MutableList {
        val annotationMirrors : MutableList = mutableListOf()

        var annotated = element
        if (annotated is KotlinAnnotationType) {
            annotated = annotated.type
        }
        when (annotated) {
            is KSValueParameter -> {
                // fuse annotations for setter and property
                val parent = annotated.parent
                if (parent is KSPropertySetter) {
                    val property = parent.parent
                    if (property is KSPropertyDeclaration) {
                        annotationMirrors.addAll(property.annotations)
                    }
                    annotationMirrors.addAll(parent.annotations)
                }
                annotationMirrors.addAll(annotated.annotations)
            }

            is KSPropertyGetter, is KSPropertySetter -> {
                val property = annotated.parent
                if (property is KSPropertyDeclaration) {
                    annotationMirrors.addAll(property.annotations)
                }
                annotationMirrors.addAll(annotated.annotations)
            }

            is KSPropertyDeclaration -> {
                val parent : KSClassDeclaration? = findClassDeclaration(annotated)
                if (parent is KSClassDeclaration) {
                    if (parent.classKind == ClassKind.ANNOTATION_CLASS) {
                        annotationMirrors.addAll(annotated.annotations)
                        val getter = annotated.getter
                        if (getter != null) {
                            annotationMirrors.addAll(getter.annotations)
                        }
                    }
                }
                annotationMirrors.addAll(annotated.annotations)
            }

            else -> {
                annotationMirrors.addAll(annotated.annotations)
            }
        }
        val expanded : MutableList = mutableListOf()
        for (ann in annotationMirrors) {
            val annotationName = getAnnotationTypeName(ann)
            var repeateable = false
            var hasOtherMembers = false
            for (arg in ann.arguments) {
                if ("value" == arg.name?.asString()) {
                    val value = arg.value
                    if (value is Iterable<*>) {
                        for (nested in value) {
                            if (nested is KSAnnotation) {
                                val repeatableName = getRepeatableName(nested)
                                if (repeatableName != null && repeatableName == annotationName) {
                                    expanded.add(nested)
                                    repeateable = true
                                }
                            }
                        }
                    }
                } else {
                    hasOtherMembers = true
                }
            }

            if (!repeateable || hasOtherMembers) {
                expanded.add(ann)
            }
        }
        return expanded
    }

    private fun findClassDeclaration(element: KSPropertyDeclaration): KSClassDeclaration? {
        var parent = element.parent
        while (parent != null) {
           if (parent is KSClassDeclaration) {
               return parent
           }
           parent = parent.parent
        }
        return null
    }

    override fun postProcess(annotationMetadata: MutableAnnotationMetadata, element: KSAnnotated) {
        var annotated = element
        if (annotated is KotlinAnnotationType) {
            annotated = annotated.type
        }
        if (annotated is KSValueParameter) {
            handleNullability(annotated.type.resolve(), annotationMetadata)
        } else if (annotated is KSFunctionDeclaration) {
            val ksType = annotated.returnType?.resolve()
            if (ksType != null) {
                handleNullability(ksType, annotationMetadata)
            }
        } else if (annotated is KSPropertyDeclaration) {
            handleNullability(annotated.type.resolve(), annotationMetadata)
        } else if (annotated is KSPropertySetter) {
            if (!annotationMetadata.hasAnnotation(JvmField::class.java) && (annotationMetadata.hasStereotype(AnnotationUtil.QUALIFIER) || annotationMetadata.hasAnnotation(Property::class.java))) {
                // implicitly inject
                annotationMetadata.addDeclaredAnnotation(AnnotationUtil.INJECT, emptyMap())
            }
        }
    }

    private fun handleNullability(
        ksType: KSType,
        annotationMetadata: MutableAnnotationMetadata
    ) {
        if (ksType.isMarkedNullable) {
            // explicitly allowed to be null so add nullable
            annotationMetadata.addDeclaredAnnotation(AnnotationUtil.NULLABLE, emptyMap())
        } else {
            // with Kotlin the default is not null, so we must store in the metadata
            // that the element is not nullable
            annotationMetadata.addDeclaredAnnotation(AnnotationUtil.NON_NULL, emptyMap())
        }
    }

    override fun buildHierarchy(
        element: KSAnnotated,
        inheritTypeAnnotations: Boolean,
        declaredOnly: Boolean
    ): MutableList {
        var annotated = element
        if (annotated is KotlinAnnotationType) {
            annotated = annotated.type
        }
        if (declaredOnly) {
            return mutableListOf(annotated)
        }
        when (annotated) {

            is KSValueParameter -> {
                val parent = annotated.parent
                return if (parent is KSFunctionDeclaration) {
                    if (parent.isConstructor()) {
                        mutableListOf(annotated)
                    } else {
                        val parameters = parent.parameters
                        val parameterIndex =
                            parameters.indexOf(parameters.find { it.name!!.asString() == annotated.name!!.asString() })
                        methodsHierarchy(parent)
                            .map { if (it == parent) annotated else it.parameters[parameterIndex] }
                            .toMutableList()
                    }
                } else { // Setter
                    mutableListOf(annotated)
                }
            }

            is KSClassDeclaration -> {
                val hierarchy = mutableListOf()
                if (annotated.classKind == ClassKind.ANNOTATION_CLASS) {
                    hierarchy.add(annotated)
                } else {
                    visitorContext.nativeElementsHelper.populateTypeHierarchy(annotated, hierarchy)
                }
                return hierarchy as MutableList
            }

            is KSFunctionDeclaration -> {
                val hierarchy = mutableListOf()
                hierarchy.addAll(methodsHierarchy(annotated))
                return hierarchy
            }

            else -> {
                return mutableListOf(annotated)
            }
        }
    }

    private fun methodsHierarchy(element: KSFunctionDeclaration): List =
        if (element.isConstructor()) {
            listOf(element)
        } else {
            val hierarchy = mutableListOf()
            hierarchy.addAll(visitorContext.nativeElementsHelper.findOverriddenMethods(element))
            hierarchy.add(element)
            hierarchy
        }

    override fun readAnnotationRawValues(
        originatingElement: KSAnnotated,
        annotationName: String,
        member: KSAnnotated,
        memberName: String,
        annotationValue: Any,
        annotationValues: MutableMap
    ) {
        if (!annotationValues.containsKey(memberName)) {
            val value = readAnnotationValue(originatingElement, member, annotationName, memberName, annotationValue)
            if (value != null) {
                validateAnnotationValue(originatingElement, annotationName, member, memberName, value)
                annotationValues[memberName] = value
            }
        }
    }

    override fun isValidationRequired(member: KSAnnotated?): Boolean {
        if (member != null) {
            return member.annotations.any {
                val name = it.annotationType.resolve().declaration.qualifiedName?.asString()
                if (name != null) {
                    return name.startsWith("jakarta.validation")
                } else {
                    return false
                }
            }
        }
        return false
    }

    override fun addError(originatingElement: KSAnnotated, error: String) {
        symbolProcessorEnvironment.logger.error(error, originatingElement)
    }

    override fun addWarning(originatingElement: KSAnnotated, warning: String) {
        symbolProcessorEnvironment.logger.warn(warning, originatingElement)
    }

    override fun readAnnotationValue(
        originatingElement: KSAnnotated,
        member: KSAnnotated,
        annotationName: String,
        memberName: String,
        annotationValue: Any
    ): Any? {
        val property = member as KSPropertyDeclaration
        return when (annotationValue) {
            is Collection<*> -> {
                toArray(annotationValue, member, property.type)
            }
            is Array<*> -> {
                toArray(annotationValue.toList(), member, property.type)
            }
            else -> {
                if (isEvaluatedExpression(annotationValue)) {
                    return buildEvaluatedExpressionReference(
                        originatingElement,
                        annotationName,
                        memberName,
                        annotationValue
                    )
                } else {
                    return readAnnotationValue(originatingElement, annotationValue)
                }
            }
        }
    }

    private fun toArray(
        annotationValue: Collection<*>,
        element: KSAnnotated,
        type: KSTypeReference
    ): Array? {
        var valueType = Any::class.java
        if (annotationValue.isEmpty()) {
            val arrayType = type.resolve()
            if (arrayType.declaration.qualifiedName?.asString() == "kotlin.Array") {
                val className = arrayType.arguments[0].type!!.resolve().declaration.getBinaryName(resolver, visitorContext);
                val optionalClassName = ClassUtils.forName(className, javaClass.classLoader)
                if (optionalClassName.isPresent) {
                    valueType = optionalClassName.get() as Class
                }
            }
        }
        val collection = annotationValue.mapNotNull {
            val v = readAnnotationValue(element, it)
            if (v != null) {
                valueType = v.javaClass
            }
            v
        } // annotation values can't be null
        return ArrayUtils.toArray(collection, valueType)
    }

    override fun readAnnotationDefaultValues(
        annotationName: String,
        annotationType: KSAnnotated
    ): MutableMap {
        return if (annotationType is KotlinAnnotationType) {
            val map = mutableMapOf()
            annotationType.type.getAllProperties().forEach { prop ->
                val argument = annotationType.mirror.defaultArguments.find { it.name == prop.simpleName }
                if (argument?.value != null && argument.isDefault()) {
                    val value = argument.value!!
                    if (value !is String || !StringUtils.isEmpty(value)) {
                        map[prop] = value
                    }
                }
            }
            map
        } else {
            mutableMapOf()
        }
    }

    override fun getOriginatingClassName(orginatingElement: KSAnnotated): String? {
        var annotated = orginatingElement
        if (annotated is KotlinAnnotationType) {
            annotated = annotated.type
        }
        val binaryName = if (annotated is KSClassDeclaration) {
            annotated.getBinaryName(resolver, visitorContext)
        } else {
            val classDeclaration = annotated.getClassDeclaration(visitorContext)
            classDeclaration.getBinaryName(resolver, visitorContext)
        }
        return if (binaryName != Object::javaClass.name) {
            binaryName
        } else {
            null
        }
    }

    override fun readAnnotationRawValues(annotationMirror: KSAnnotation): MutableMap {
        val map = mutableMapOf()
        val declaration = annotationMirror.annotationType.resolve().declaration.getClassDeclaration(visitorContext)
        declaration.getAllProperties().forEach { prop ->
            val argument = annotationMirror.arguments.find { it.name == prop.simpleName }
            if (argument?.value != null && !argument.isDefault()) {
                val value = argument.value!!
                map[prop] = value
            }
        }
        return map
    }

    override fun  getAnnotationValues(
        originatingElement: KSAnnotated,
        member: KSAnnotated?,
        annotationType: Class
    ): Optional> {
        val annotationMirrors: MutableList = (member as KSPropertyDeclaration).getter!!.annotations.toMutableList()
        annotationMirrors.addAll(member.annotations.toList())
        val annotationName = annotationType.name
        for (annotationMirror in annotationMirrors) {
            if (annotationMirror.annotationType.resolve().declaration.qualifiedName?.asString() == annotationName) {
                val values: Map = readAnnotationRawValues(annotationMirror)
                val converted: MutableMap = mutableMapOf()
                for ((key, value1) in values) {
                    var value = value1!!
                    val memberName = key.simpleName.asString()
                    if (isEvaluatedExpression(value)) {
                        value = buildEvaluatedExpressionReference(
                            originatingElement,
                            annotationName,
                            memberName,
                            value
                        )
                    }
                    readAnnotationRawValues(
                        originatingElement,
                        annotationName,
                        key,
                        memberName,
                        value,
                        converted
                    )
                }
                return Optional.of(
                    AnnotationValue.builder(annotationType).members(converted).build()
                )
            }
        }
        return Optional.empty()
    }

    override fun getRepeatableName(annotationMirror: KSAnnotation): String? {
        val repeatableContainer =
            getRepeatableContainerNameForType(annotationMirror.annotationType.getClassDeclaration(visitorContext))
        return repeatableContainer
    }

    override fun getRepeatableContainerNameForType(annotationType: KSAnnotated): String? {
        val name = java.lang.annotation.Repeatable::class.java.name
        val repeatable = annotationType.annotations.find {
            it.annotationType.resolve().declaration.qualifiedName?.asString() == name
        }
        if (repeatable != null) {
            val value = repeatable.arguments.find { it.name?.asString() == "value" }?.value
            if (value != null) {
                val declaration = (value as KSType).declaration.getClassDeclaration(visitorContext)
                return declaration.getBinaryName(resolver, visitorContext)
            }
        }
        return null
    }

    override fun findRepeatableContainerNameForType(annotationName: String): String? {
        val container = super.findRepeatableContainerNameForType(annotationName)
        if (container == null) {
            return ClassUtils.forName(annotationName, null)
            .flatMap {
                for (annotation in it.annotations) {
                    if (annotation.annotationClass.java == Repeatable::class.java) {
                        return@flatMap Optional.of(
                            (annotation as Repeatable).value.java.name
                        )
                    }
                }
                return@flatMap Optional.empty()
            }
            .orElse(null)
        }
        return container
    }

    override fun getAnnotationMirror(annotationName: String): Optional {
        return Optional.ofNullable(resolver.getClassDeclarationByName(annotationName))
    }

    override fun getAnnotationMember(annotationElement: KSAnnotated, member: CharSequence): KSAnnotated? {
        var annotated = annotationElement
        if (annotated is KotlinAnnotationType) {
            annotated = annotated.type
        }
        if (annotated is KSClassDeclaration) {
            return annotated.getAllProperties().find { it.simpleName.asString() == member }
        }
        throw IllegalStateException("Unknown annotation element: $annotated")
    }

    override fun getAnnotationMemberName(member: KSAnnotated): String {
        var annotated = member
        if (annotated is KotlinAnnotationType) {
            annotated = annotated.type
        }
        if (annotated is KSPropertyDeclaration) {
            return annotated.simpleName.asString()
        }
        throw IllegalStateException("Unknown annotation member element: $annotated")
    }

    override fun getVisitorContext(): VisitorContext {
        return visitorContext
    }

    override fun getRetentionPolicy(annotation: KSAnnotated): RetentionPolicy {
        var retention = annotation.annotations.find {
            getAnnotationTypeName(it) == java.lang.annotation.Retention::class.java.name
        }
        if (retention != null) {
            val value = retention.arguments.find { it.name?.asString() == "value" }?.value
            if (value is KSType) {
                return toRetentionPolicy(value)
            }
        } else {
            retention = annotation.annotations.find {
                getAnnotationTypeName(it) == Retention::class.java.name
            }
            if (retention != null) {
                val value = retention.arguments.find { it.name?.asString() == "value" }?.value
                if (value is KSType) {
                    return toJavaRetentionPolicy(value)
                }
            }
        }
        return RetentionPolicy.RUNTIME
    }

    private fun toRetentionPolicy(value: KSType) =
        RetentionPolicy.valueOf(value.declaration.qualifiedName!!.getShortName())

    private fun toJavaRetentionPolicy(value: KSType) =
        when (AnnotationRetention.valueOf(value.declaration.qualifiedName!!.getShortName())) {
            AnnotationRetention.RUNTIME -> {
                RetentionPolicy.RUNTIME
            }

            AnnotationRetention.SOURCE -> {
                RetentionPolicy.SOURCE
            }

            AnnotationRetention.BINARY -> {
                RetentionPolicy.CLASS
            }
    }

    private fun readAnnotationValue(originatingElement: KSAnnotated, value: Any?): Any? {
        if (value == null) {
            return null
        }// (originatingElement as KSPropertyDeclaration).type
        if (value is KSType) {
            val declaration = value.declaration
            if (declaration is KSClassDeclaration) {
                if (declaration.classKind == ClassKind.ENUM_ENTRY) {
                    return declaration.qualifiedName?.getShortName()
                }
                if (declaration.classKind == ClassKind.CLASS ||
                    declaration.classKind == ClassKind.INTERFACE ||
                    declaration.classKind == ClassKind.ENUM_CLASS ||
                    declaration.classKind == ClassKind.ANNOTATION_CLASS) {
                    return AnnotationClassValue(declaration.getBinaryName(resolver, visitorContext))
                }
            }
        }
        if (value is KSAnnotation) {
            return readNestedAnnotationValue(originatingElement, value)
        }

         return value
    }

    private data class KotlinAnnotationType(
        var mirror: KSAnnotation,
        var type: KSClassDeclaration,
        override val annotations: Sequence = type.annotations,
        override val location: Location = type.location,
        override val origin: Origin = type.origin,
        override val parent: KSNode? = type.parent
    ) : KSAnnotated {
        override fun  accept(visitor: KSVisitor, data: D): R {
            return type.accept(visitor, data);
        }
    }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy