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

se.ansman.kotshi.kapt.KotshiProcessor.kt Maven / Gradle / Ivy

Go to download

An annotations processor that generates Moshi adapters from Kotlin data classes

There is a newer version: 3.0.0
Show newest version
package se.ansman.kotshi.kapt

import com.google.auto.service.AutoService
import com.google.common.collect.ImmutableList
import com.google.common.collect.ImmutableSetMultimap
import com.google.common.collect.Multimaps
import com.google.common.collect.SetMultimap
import com.squareup.kotlinpoet.asClassName
import com.squareup.kotlinpoet.metadata.classinspectors.ElementsClassInspector
import net.ltgt.gradle.incap.IncrementalAnnotationProcessor
import net.ltgt.gradle.incap.IncrementalAnnotationProcessorType.AGGREGATING
import se.ansman.kotshi.Errors
import se.ansman.kotshi.Options
import se.ansman.kotshi.model.GeneratedAdapter
import se.ansman.kotshi.model.GeneratedAnnotation
import javax.annotation.processing.*
import javax.lang.model.SourceVersion
import javax.lang.model.element.Element
import javax.lang.model.element.TypeElement
import javax.lang.model.util.Elements
import javax.lang.model.util.Types

@AutoService(Processor::class)
@IncrementalAnnotationProcessor(AGGREGATING)
class KotshiProcessor : AbstractProcessor() {
    private var createAnnotationsUsingConstructor: Boolean? = null
    private var useLegacyDataClassRenderer: Boolean = false
    private var generatedAnnotation: GeneratedAnnotation? = null
    private lateinit var elements: Elements
    private lateinit var types: Types
    private lateinit var metadataAccessor: MetadataAccessor
    private lateinit var steps: ImmutableList

    override fun getSupportedSourceVersion(): SourceVersion = SourceVersion.latestSupported()

    private fun initSteps(): Iterable {
        val adapters: MutableList = mutableListOf()
        return listOf(
            AdaptersProcessingStep(
                processor = this,
                metadataAccessor = metadataAccessor,
                messager = processingEnv.messager,
                filer = processingEnv.filer,
                adapters = adapters,
                types = types,
                elements = processingEnv.elementUtils,
                generatedAnnotation = generatedAnnotation,
                createAnnotationsUsingConstructor = createAnnotationsUsingConstructor,
                useLegacyDataClassRenderer = useLegacyDataClassRenderer,
            ),
            FactoryProcessingStep(
                processor = this,
                messager = processingEnv.messager,
                filer = processingEnv.filer,
                types = processingEnv.typeUtils,
                elements = processingEnv.elementUtils,
                generatedAnnotation = generatedAnnotation,
                generatedAdapters = adapters,
                metadataAccessor = metadataAccessor,
                createAnnotationsUsingConstructor = createAnnotationsUsingConstructor,
            )
        )
    }

    @Synchronized
    override fun init(processingEnv: ProcessingEnvironment) {
        super.init(processingEnv)
        createAnnotationsUsingConstructor = processingEnv.options[Options.createAnnotationsUsingConstructor]?.toBooleanStrict()
        useLegacyDataClassRenderer = processingEnv.options[Options.useLegacyDataClassRenderer]?.toBooleanStrict() ?: useLegacyDataClassRenderer
        generatedAnnotation = processingEnv.options[Options.generatedAnnotation]
            ?.let { name ->
                Options.possibleGeneratedAnnotations[name] ?: run {
                    processingEnv.messager.logKotshiError(Errors.invalidGeneratedAnnotation(name), element = null)
                    null
                }
            }
            ?.let { GeneratedAnnotation(it, KotshiProcessor::class.asClassName()) }
        elements = processingEnv.elementUtils
        types = processingEnv.typeUtils
        metadataAccessor = MetadataAccessor(ElementsClassInspector.create(elements, processingEnv.typeUtils))
        steps = ImmutableList.copyOf(initSteps())
    }

    private fun getSupportedAnnotationClasses(): Set> =
        steps.flatMapTo(mutableSetOf()) { it.annotations }

    /**
     * Returns the set of supported annotation types as a  collected from registered
     * [processing steps][ProcessingStep].
     */
    override fun getSupportedAnnotationTypes(): Set =
        getSupportedAnnotationClasses().mapTo(mutableSetOf()) { it.canonicalName }

    override fun getSupportedOptions(): Set = setOf("kotshi.createAnnotationsUsingConstructor")

    override fun process(annotations: Set, roundEnv: RoundEnvironment): Boolean {
        if (!roundEnv.processingOver()) {
            process(validElements(roundEnv), roundEnv)
        }
        return false
    }

    private fun validElements(roundEnv: RoundEnvironment): ImmutableSetMultimap, Element> {
        val validElements = ImmutableSetMultimap.builder, Element>()
        for (annotationClass in getSupportedAnnotationClasses()) {
            validElements.putAll(annotationClass, roundEnv.getElementsAnnotatedWith(annotationClass))
        }
        return validElements.build()
    }

    /** Processes the valid elements, including those previously deferred by each step.  */
    private fun process(validElements: ImmutableSetMultimap, Element>, roundEnv: RoundEnvironment) {
        for (step in steps) {
            val stepElements = Multimaps.filterKeys(validElements) { it in step.annotations }
            if (!stepElements.isEmpty) {
                step.process(stepElements, roundEnv)
            }
        }
    }

    interface ProcessingStep {
        val annotations: Set>
        fun process(elementsByAnnotation: SetMultimap, Element>, roundEnv: RoundEnvironment)
    }

    abstract class GeneratingProcessingStep : ProcessingStep {
        protected abstract val filer: Filer
        protected abstract val processor: KotshiProcessor
    }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy