aboutsummaryrefslogtreecommitdiff
path: root/examples/playground/test-processor/src/main/kotlin/JavaBuilderProcessor.kt
blob: a3eb1f157b525d177fb6c725cb3e0f157afb80c1 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
import com.example.annotation.Builder
import com.squareup.javapoet.*
import javax.annotation.processing.AbstractProcessor
import javax.annotation.processing.RoundEnvironment
import javax.lang.model.SourceVersion
import javax.lang.model.element.ExecutableElement
import javax.lang.model.element.Modifier
import javax.lang.model.element.PackageElement
import javax.lang.model.element.TypeElement

class JavaBuilderProcessor : AbstractProcessor() {

    override fun getSupportedSourceVersion(): SourceVersion {
        return SourceVersion.latestSupported()
    }
    override fun getSupportedAnnotationTypes(): MutableSet<String> {
        return mutableSetOf("com.example.annotation.Builder")
    }

    private fun processType(typeElement: TypeElement) {
        val typeSpecBuilder = TypeSpec.classBuilder("${typeElement.simpleName}Builder")
        val constructor = typeElement.enclosedElements.filterIsInstance<ExecutableElement>()
                .filter { it.simpleName.toString() == "<init>" }
                .first()

        constructor.parameters.forEach {
            typeSpecBuilder.addField(ClassName.get(it.asType()), it.simpleName.toString(), Modifier.PRIVATE)
            val parameterSpec = ParameterSpec.builder(ClassName.get(it.asType()), it.simpleName.toString()).build()
            typeSpecBuilder.addMethod(
                    MethodSpec.methodBuilder("with${it.simpleName.toString().replaceFirstChar { it.uppercase() } }")
                            .addParameter(parameterSpec)
                            .addModifiers(Modifier.PUBLIC)
                            .addStatement("this.\$N = \$N", it.simpleName, it.simpleName)
                            .addStatement("return this")
                            .returns(ClassName.bestGuess("${typeElement.qualifiedName}Builder"))
                            .build()
            )
        }
        val statements = constructor.parameters.map { it.simpleName.toString() }.joinToString(", ")
        typeSpecBuilder.addMethod(
                MethodSpec.methodBuilder("build")
                        .addModifiers(Modifier.PUBLIC)
                        .returns(ClassName.get(typeElement.asType()))
                        .addStatement("return new ${typeElement.simpleName}($statements)")
                        .build()
        )
        val javaFileBuilder = JavaFile.builder((typeElement.enclosingElement as PackageElement).qualifiedName.toString(), typeSpecBuilder.build())
        javaFileBuilder.skipJavaLangImports(true).build().writeTo(processingEnv.filer)
    }

    override fun process(annotations: MutableSet<out TypeElement>?, roundEnv: RoundEnvironment?): Boolean {
        val elements = roundEnv!!.getElementsAnnotatedWith(Builder::class.java)
        elements.filterIsInstance<TypeElement>()
                .map { processType(it) }
        return true
    }

}