blob: 061c32919b325545f9788696434dbaef50e8a556 (
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
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
|
import com.google.devtools.ksp.processing.*
import com.google.devtools.ksp.symbol.*
import com.google.devtools.ksp.validate
import java.io.File
import java.io.OutputStream
fun OutputStream.appendText(str: String) {
this.write(str.toByteArray())
}
class BuilderProcessor(
val codeGenerator: CodeGenerator,
val logger: KSPLogger
) : SymbolProcessor {
override fun process(resolver: Resolver): List<KSAnnotated> {
val symbols = resolver.getSymbolsWithAnnotation("com.example.annotation.Builder")
val ret = symbols.filter { !it.validate() }.toList()
symbols
.filter { it is KSClassDeclaration && it.validate() }
.forEach { it.accept(BuilderVisitor(), Unit) }
return ret
}
inner class BuilderVisitor : KSVisitorVoid() {
override fun visitClassDeclaration(classDeclaration: KSClassDeclaration, data: Unit) {
classDeclaration.primaryConstructor!!.accept(this, data)
}
override fun visitFunctionDeclaration(function: KSFunctionDeclaration, data: Unit) {
val parent = function.parentDeclaration as KSClassDeclaration
val packageName = parent.containingFile!!.packageName.asString()
val className = "${parent.simpleName.asString()}Builder"
val file = codeGenerator.createNewFile(Dependencies(true, function.containingFile!!), packageName , className)
file.appendText("package $packageName\n\n")
file.appendText("import HELLO\n\n")
file.appendText("class $className{\n")
function.parameters.forEach {
val name = it.name!!.asString()
val typeName = StringBuilder(it.type.resolve().declaration.qualifiedName?.asString() ?: "<ERROR>")
val typeArgs = it.type.element!!.typeArguments
if (it.type.element!!.typeArguments.isNotEmpty()) {
typeName.append("<")
typeName.append(
typeArgs.map {
val type = it.type?.resolve()
"${it.variance.label} ${type?.declaration?.qualifiedName?.asString() ?: "ERROR"}" +
if (type?.nullability == Nullability.NULLABLE) "?" else ""
}.joinToString(", ")
)
typeName.append(">")
}
file.appendText(" private var $name: $typeName? = null\n")
file.appendText(" internal fun with${name.replaceFirstChar { it.uppercase() } }($name: $typeName): $className {\n")
file.appendText(" this.$name = $name\n")
file.appendText(" return this\n")
file.appendText(" }\n\n")
}
file.appendText(" internal fun build(): ${parent.qualifiedName!!.asString()} {\n")
file.appendText(" return ${parent.qualifiedName!!.asString()}(")
file.appendText(
function.parameters.map {
"${it.name!!.asString()}!!"
}.joinToString(", ")
)
file.appendText(")\n")
file.appendText(" }\n")
file.appendText("}\n")
file.close()
}
}
}
class BuilderProcessorProvider : SymbolProcessorProvider {
override fun create(
environment: SymbolProcessorEnvironment
): SymbolProcessor {
return BuilderProcessor(environment.codeGenerator, environment.logger)
}
}
|