You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

99 lines
3.6 KiB
Kotlin

package me.eater.threedom.kapt
import org.yanex.takenoko.*
import java.io.File
import javax.annotation.processing.*
import javax.lang.model.SourceVersion
import javax.lang.model.element.Element
import javax.lang.model.element.TypeElement
import javax.tools.Diagnostic.Kind.ERROR
@SupportedSourceVersion(SourceVersion.RELEASE_8)
@SupportedAnnotationTypes("me.eater.threedom.kapt.EventName")
@SupportedOptions(EventNameProcessor.KAPT_KOTLIN_GENERATED_OPTION_NAME, "org.gradle.annotation.processing.aggregating")
class EventNameProcessor : AbstractProcessor() {
companion object {
const val KAPT_KOTLIN_GENERATED_OPTION_NAME = "kapt.kotlin.generated"
}
override fun process(annotations: MutableSet<out TypeElement>?, roundEnv: RoundEnvironment): Boolean {
val annotatedElements = roundEnv.getElementsAnnotatedWith(EventName::class.java)
if (annotatedElements.isEmpty()) return false
val kaptKotlinGeneratedDir = processingEnv.options[KAPT_KOTLIN_GENERATED_OPTION_NAME] ?: run {
processingEnv.messager.printMessage(ERROR, "Can't find the target directory for generated Kotlin files.")
return false
}
val generatedKtFile = kotlinFile("me.eater.threedom.generated") {
objectDeclaration("EventNames") {
val eventNames = mutableMapOf<String, String>()
for (element in annotatedElements) {
val typeElement = element.toTypeElementOrNull() ?: continue
val eventName = typeElement.getAnnotation(EventName::class.java).eventName
if (eventName in eventNames) {
processingEnv.messager.printMessage(
ERROR,
"Class ${eventNames[eventName]} already uses the event name '${eventName}'",
element
)
continue
}
eventNames[typeElement.qualifiedName.toString()] = eventName
property(eventName) {
initializer(typeElement.qualifiedName.toString() + "::class")
}
}
property("EVENT_MAPPING") {
initializer(
"mapOf(\n${eventNames.map { (k, v) -> " \"" + k.replace('"', '\"') + "\" to \"" + v.replace('"', '\"') + '"' }
.joinToString(",\n")}\n)"
)
}
function("getEventName") {
param<String>("eventClass")
returnType<String>()
body {
append("return EVENT_MAPPING[eventClass] ?: eventClass")
}
}
function(
"getEventName", INLINE
) {
typeParam("reified T")
returnType<String>()
body {
append("return getEventName(T::class.java.name)")
}
}
}
}
File("$kaptKotlinGeneratedDir/me/eater/threedom/generated", "EventNames.kt").apply {
parentFile.mkdirs()
writeText(generatedKtFile.accept(PrettyPrinter(PrettyPrinterConfiguration())))
}
return true
}
fun Element.toTypeElementOrNull(): TypeElement? {
if (this !is TypeElement) {
processingEnv.messager.printMessage(ERROR, "Invalid element type, class expected", this)
return null
}
return this
}
}