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

io.micronaut.kotlin.processing.beans.BeanDefinitionProcessor.kt Maven / Gradle / Ivy

There is a newer version: 4.7.10
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.beans

import com.google.devtools.ksp.processing.Resolver
import com.google.devtools.ksp.processing.SymbolProcessor
import com.google.devtools.ksp.processing.SymbolProcessorEnvironment
import com.google.devtools.ksp.symbol.*
import io.micronaut.core.annotation.Generated
import io.micronaut.core.annotation.Vetoed
import io.micronaut.inject.BeanDefinition
import io.micronaut.inject.processing.BeanDefinitionCreator
import io.micronaut.inject.processing.BeanDefinitionCreatorFactory
import io.micronaut.inject.processing.ProcessingException
import io.micronaut.inject.writer.BeanDefinitionVisitor
import io.micronaut.inject.writer.BeanDefinitionWriter
import io.micronaut.kotlin.processing.KotlinOutputVisitor
import io.micronaut.kotlin.processing.visitor.KotlinClassElement
import io.micronaut.kotlin.processing.visitor.KotlinNativeElement
import io.micronaut.kotlin.processing.visitor.KotlinVisitorContext
import java.io.IOException

internal class BeanDefinitionProcessor(private val environment: SymbolProcessorEnvironment): SymbolProcessor {

    private val beanDefinitionMap = mutableMapOf>()
    private var visitorContext : KotlinVisitorContext? = null
    private val processed = HashSet()

    override fun process(resolver: Resolver): List {
        visitorContext = KotlinVisitorContext(environment, resolver)

        val elements = resolver.getAllFiles()
            .flatMap { file: KSFile ->
                file.declarations
            }
            .filterIsInstance()
            .filter { declaration: KSClassDeclaration ->
                declaration.annotations.none { ksAnnotation ->
                    ksAnnotation.annotationType.resolve().declaration.qualifiedName?.asString() == Generated::class.java.name
                }
            }
            .toList()

        try {
            processClassDeclarations(elements, visitorContext!!)
        } catch (e: ProcessingException) {
            handleProcessingException(environment, e)
        }
        return emptyList()
    }

    private fun isVetoed(ksAnnotation: KSAnnotation) =
        ksAnnotation.annotationType.resolve().declaration.qualifiedName?.asString() == Vetoed::class.java.name

    private fun processClassDeclarations(
        elements: List,
        visitorContext: KotlinVisitorContext
    ) {
        for (classDeclaration in elements) {
            if (classDeclaration.classKind != ClassKind.ANNOTATION_CLASS) {
                val classElement =
                    visitorContext.elementFactory.newClassElement(classDeclaration) as KotlinClassElement
                val innerClasses =
                    classDeclaration.declarations
                        .filter { it is KSClassDeclaration }
                        .map { it as KSClassDeclaration }
                        .filter { declaration: KSClassDeclaration ->
                            declaration.annotations.none { ksAnnotation ->
                                isVetoed(ksAnnotation)
                            }
                        }
                        .filter { !it.modifiers.contains(Modifier.INNER) }
                        .toList()
                if (innerClasses.isNotEmpty()) {
                    processClassDeclarations(innerClasses, visitorContext)
                }
                beanDefinitionMap.computeIfAbsent(classElement.name) {
                    val produce = BeanDefinitionCreatorFactory.produce(classElement, visitorContext)
                    val list = ArrayList()
                    for (writer in produce.build()) {
                        if (processed.add(writer.beanDefinitionName)) {
                            writer.visitBeanDefinitionEnd()
                            list.add(writer)
                        }
                    }
                    list
                }
            }
        }
    }

    override fun finish() {
        try {
            val outputVisitor = KotlinOutputVisitor(environment, visitorContext!!)
            var count = 0
            for (writers in beanDefinitionMap.values) {
                for (writer in writers) {
                    processBeanDefinitions(writer, outputVisitor)
                    count++
                }
            }
            if (count > 0) {
                environment.logger.info("Created $count bean definitions")
            }
        } catch (e: ProcessingException) {
            handleProcessingException(environment, e)
        } finally {
            BeanDefinitionWriter.finish()
            beanDefinitionMap.clear()
        }
    }

    companion object Helper {
        fun handleProcessingException(environment: SymbolProcessorEnvironment, e: ProcessingException) {
            val message = e.message
            val originatingNode = (e.originatingElement as KotlinNativeElement).element
            if (message != null) {
                environment.logger.error("Originating element: $originatingNode")
                environment.logger.error(message, originatingNode)
                val cause = e.cause
                if (cause != null) {
                    environment.logger.exception(cause)
                }
            } else {
                environment.logger.error("Unknown error processing element", originatingNode)
                val cause = e.cause
                if (cause != null) {
                    environment.logger.exception(cause)
                } else {
                    environment.logger.exception(e)
                }
            }
        }
    }

    private fun processBeanDefinitions(
        beanDefinitionWriter: BeanDefinitionVisitor,
        outputVisitor: KotlinOutputVisitor,
    ) {
        try {
            if (beanDefinitionWriter.isEnabled) {
                beanDefinitionWriter.accept(outputVisitor)
            }
        } catch (e: IOException) {
            // raise a compile error
            val message = e.message
            error("Unexpected error ${e.javaClass.simpleName}:" + (message ?: e.javaClass.simpleName))
        }
    }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy