se.ansman.kotshi.kapt.KotshiProcessor.kt Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of compiler Show documentation
Show all versions of compiler Show documentation
An annotations processor that generates Moshi adapters from Kotlin data classes
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
}
}