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

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

/*
 * Copyright 2017-2023 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.symbol.KSAnnotated
import com.google.devtools.ksp.symbol.KSAnnotation
import io.micronaut.inject.annotation.AbstractAnnotationMetadataBuilder.CachedAnnotationMetadata
import io.micronaut.inject.ast.ClassElement
import io.micronaut.inject.ast.Element
import io.micronaut.inject.ast.FieldElement
import io.micronaut.inject.ast.GenericElement
import io.micronaut.inject.ast.GenericPlaceholderElement
import io.micronaut.inject.ast.MethodElement
import io.micronaut.inject.ast.PackageElement
import io.micronaut.inject.ast.ParameterElement
import io.micronaut.inject.ast.WildcardElement
import io.micronaut.inject.ast.annotation.AbstractElementAnnotationMetadataFactory
import io.micronaut.inject.ast.annotation.ElementAnnotationMetadata
import io.micronaut.inject.ast.annotation.ElementAnnotationMetadataFactory
import io.micronaut.inject.processing.ProcessingException
import io.micronaut.kotlin.processing.visitor.AbstractKotlinElement
import io.micronaut.kotlin.processing.visitor.AbstractKotlinMethodElement
import io.micronaut.kotlin.processing.visitor.KotlinClassNativeElement
import io.micronaut.kotlin.processing.visitor.KotlinClassElement
import io.micronaut.kotlin.processing.visitor.KotlinFieldElement
import io.micronaut.kotlin.processing.visitor.KotlinGenericPlaceholderElement
import io.micronaut.kotlin.processing.visitor.KotlinParameterElement
import io.micronaut.kotlin.processing.visitor.KotlinTypeArgumentElement
import io.micronaut.kotlin.processing.visitor.KotlinWildcardElement

internal class KotlinElementAnnotationMetadataFactory(
    isReadOnly: Boolean,
    metadataBuilder: KotlinAnnotationMetadataBuilder
) : AbstractElementAnnotationMetadataFactory(
    isReadOnly,
    metadataBuilder
) {

    private val empty: KSAnnotated = KotlinAnnotations(emptySequence())

    override fun readOnly(): ElementAnnotationMetadataFactory {
        return KotlinElementAnnotationMetadataFactory(
            true,
            metadataBuilder as KotlinAnnotationMetadataBuilder
        )
    }

    override fun getNativeElement(element: Element?): KSAnnotated {
        return if (element is AbstractKotlinElement<*>) element.nativeType.element else empty
    }

    override fun buildGenericTypeAnnotations(element: GenericElement?): ElementAnnotationMetadata {
        if (element is KotlinTypeArgumentElement) {
            return buildTypeAnnotationsForTypeArgument(element)
        }
        return super.buildGenericTypeAnnotations(element)
    }

    private fun buildTypeAnnotationsForTypeArgument(
        element: KotlinTypeArgumentElement
    ): AbstractElementAnnotationMetadata {
        return object : AbstractElementAnnotationMetadata() {

            override fun lookup(): CachedAnnotationMetadata {
                if (element.genericNativeType.owner == null) {
                    throw ProcessingException(element, "Type annotations require the owner element to be specified!")
                }
                return metadataBuilder.lookupOrBuild(
                    element.genericNativeType,
                    element.genericNativeType.declaration
                )
            }

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

    override fun lookupForClass(classElement: ClassElement?): CachedAnnotationMetadata {
        val kotlinClassElement = classElement as KotlinClassElement
        return metadataBuilder.lookupOrBuild(
            getClassDefinitionCacheKey(kotlinClassElement),
            classElement.declaration
        )
    }

    override fun lookupTypeAnnotationsForClass(classElement: ClassElement): CachedAnnotationMetadata? {
        val kotlinClassElement = classElement as KotlinClassElement
        if (kotlinClassElement.nativeType.type == null) {
            throw ProcessingException(classElement, "Type annotations aren't supported!")
        }
        if (kotlinClassElement.nativeType.owner == null) {
            throw ProcessingException(classElement, "Type annotations require the owner element to be specified!")
        }
        return metadataBuilder.lookupOrBuild(
            kotlinClassElement.nativeType,
            KotlinAnnotations(kotlinClassElement.kotlinType.annotations)
        )
    }

    override fun lookupTypeAnnotationsForGenericPlaceholder(placeholderElement: GenericPlaceholderElement): CachedAnnotationMetadata? {
        val kotlinPlaceholderElement = placeholderElement as KotlinGenericPlaceholderElement
        if (kotlinPlaceholderElement.genericNativeType.owner == null) {
            throw ProcessingException(placeholderElement, "Type annotations an a generic placeholder require the owner element to be specified!")
        }
        return metadataBuilder.lookupOrBuild(
            kotlinPlaceholderElement.genericNativeType,
            kotlinPlaceholderElement.genericNativeType.declaration
        )
    }

    override fun lookupTypeAnnotationsForWildcard(wildcardElement: WildcardElement): CachedAnnotationMetadata? {
        val kotlinWildcardElement = wildcardElement as KotlinWildcardElement
        if (kotlinWildcardElement.genericNativeType.owner == null) {
            throw ProcessingException(wildcardElement, "Type annotations on a wildcard require the owner element to be specified!")
        }
        return metadataBuilder.lookupOrBuild(
            kotlinWildcardElement.genericNativeType,
            kotlinWildcardElement.genericNativeType.declaration
        )
    }

    override fun lookupForPackage(packageElement: PackageElement?): CachedAnnotationMetadata? {
        return metadataBuilder.lookupOrBuildForType(getNativeElement(packageElement))
    }

    override fun lookupForParameter(parameterElement: ParameterElement): CachedAnnotationMetadata? {
        val kotlinParameterElement = parameterElement as KotlinParameterElement
        val owner = kotlinParameterElement.methodElement.owningType as KotlinClassElement
        return metadataBuilder.lookupOrBuild(
            Key3(
                getClassDefinitionCacheKey(owner),
                kotlinParameterElement.methodElement.nativeType,
                kotlinParameterElement.nativeType
            ),
            kotlinParameterElement.nativeType.element
        )
    }

    override fun lookupForField(fieldElement: FieldElement): CachedAnnotationMetadata? {
        val kotlinFieldElement = fieldElement as KotlinFieldElement
        val owner = kotlinFieldElement.owningType as KotlinClassElement
        return metadataBuilder.lookupOrBuild(
            Key2(
                getClassDefinitionCacheKey(owner),
                kotlinFieldElement.nativeType,
            ),
            kotlinFieldElement.nativeType.element
        )
    }

    override fun lookupForMethod(methodElement: MethodElement): CachedAnnotationMetadata? {
        val kotlinMethodElement = methodElement as AbstractKotlinMethodElement<*>
        val owner = kotlinMethodElement.owningType as KotlinClassElement
        return metadataBuilder.lookupOrBuild(
            Key2(
                getClassDefinitionCacheKey(owner),
                kotlinMethodElement.nativeType,
            ),
            kotlinMethodElement.nativeType.element
        )
    }

    private fun getClassDefinitionCacheKey(kotlinClassElement: KotlinClassElement) =
        KotlinClassNativeElement(
            kotlinClassElement.nativeType.declaration,
            null, // Strip the type for the cache key
        )

    data class Key2(private val v1: Any, private val v2: Any)

    data class Key3(private val v1: Any, private val v2: Any, private val v3: Any)

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy