Skip to content

Commit 4443ae3

Browse files
committed
Kotlin impl
1 parent bdf5985 commit 4443ae3

18 files changed

Lines changed: 224 additions & 140 deletions

File tree

json/json-annotation-processor/src/main/java/ru/tinkoff/kora/json/annotation/processor/GeneratorModuleAnnotationProcessor.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,7 @@ private void generateModule(Element module) throws Exception {
9191
.get()
9292
.getValue();
9393
e.printError(this.processingEnv);
94-
new ProcessingError(Diagnostic.Kind.ERROR, e.getMessage(), module, generatorModuleAnnotation, annotationValue.get(i)).print(processingEnv);
94+
this.processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR, e.getMessage(), module, generatorModuleAnnotation, annotationValue.get(i));
9595
}
9696
}
9797
if (error) {
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
ru.tinkoff.kora.json.annotation.processor.JsonAnnotationProcessor,isolating
2+
ru.tinkoff.kora.json.annotation.processor.GeneratorModuleAnnotationProcessor,isolating
Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
package ru.tinkoff.kora.json.ksp
2+
3+
import com.google.devtools.ksp.processing.Resolver
4+
import com.google.devtools.ksp.processing.SymbolProcessorEnvironment
5+
import com.google.devtools.ksp.processing.SymbolProcessorProvider
6+
import com.google.devtools.ksp.symbol.KSAnnotated
7+
import com.google.devtools.ksp.symbol.KSClassDeclaration
8+
import com.google.devtools.ksp.symbol.KSType
9+
import com.squareup.kotlinpoet.*
10+
import com.squareup.kotlinpoet.ksp.toTypeName
11+
import com.squareup.kotlinpoet.ksp.writeTo
12+
import ru.tinkoff.kora.ksp.common.AnnotationUtils.findAnnotation
13+
import ru.tinkoff.kora.ksp.common.AnnotationUtils.findValueNoDefault
14+
import ru.tinkoff.kora.ksp.common.BaseSymbolProcessor
15+
import ru.tinkoff.kora.ksp.common.CommonClassNames
16+
import ru.tinkoff.kora.ksp.common.KspCommonUtils.addOriginatingKSFile
17+
import ru.tinkoff.kora.ksp.common.KspCommonUtils.generated
18+
import ru.tinkoff.kora.ksp.common.exception.ProcessingErrorException
19+
import ru.tinkoff.kora.ksp.common.generatedClass
20+
21+
class GeneratorModuleProcessorProvider : SymbolProcessorProvider {
22+
override fun create(environment: SymbolProcessorEnvironment) = GeneratorModuleProcessor(environment)
23+
}
24+
25+
class GeneratorModuleProcessor(val env: SymbolProcessorEnvironment) : BaseSymbolProcessor(env) {
26+
val processor = JsonProcessor(env.codeGenerator)
27+
28+
override fun processRound(resolver: Resolver): List<KSAnnotated> {
29+
val modules = resolver.getSymbolsWithAnnotation(CommonClassNames.generatorModule.canonicalName)
30+
for (module in modules) {
31+
try {
32+
this.generateModule(module as KSClassDeclaration)
33+
} catch (e: ProcessingErrorException) {
34+
e.printError(env.logger)
35+
}
36+
}
37+
return listOf()
38+
}
39+
40+
private fun generateModule(module: KSClassDeclaration) {
41+
val generatorModuleAnnotation = module.findAnnotation(CommonClassNames.generatorModule)
42+
val generatorModuleGenerator = generatorModuleAnnotation?.findValueNoDefault<KSType>("generator")
43+
if (generatorModuleGenerator == null || generatorModuleGenerator.toTypeName() != JsonTypes.json) {
44+
return
45+
}
46+
val typesToProcess = generatorModuleAnnotation.findValueNoDefault<List<KSType>>("types")
47+
if (typesToProcess.isNullOrEmpty()) {
48+
return
49+
}
50+
val packageName = module.packageName.asString()
51+
val builder = TypeSpec.interfaceBuilder(module.generatedClass(CommonClassNames.generatorModule))
52+
.addOriginatingKSFile(module)
53+
.addAnnotation(CommonClassNames.module)
54+
.generated(GeneratorModuleProcessor::class)
55+
var error = false
56+
for ((i, jsonType) in typesToProcess.withIndex()) {
57+
val jsonClassDecl = jsonType.declaration as KSClassDeclaration
58+
try {
59+
val readerClassName = ClassName(packageName, module.generatedClass(CommonClassNames.generatorModule) + "_${i}_JsonReader")
60+
val readerMethod = this.generateMapper(module, "reader$i", readerClassName, jsonClassDecl) { target, jsonElement -> processor.generateReader(target, jsonElement) }
61+
builder.addFunction(readerMethod)
62+
63+
val writerClassName = ClassName(packageName, module.generatedClass(CommonClassNames.generatorModule) + "_${i}_JsonWriter")
64+
val writerMethod = this.generateMapper(module, "writer$i", writerClassName, jsonClassDecl) { target, jsonElement -> processor.generateReader(target, jsonElement) }
65+
builder.addFunction(writerMethod)
66+
} catch (e: ProcessingErrorException) {
67+
e.printError(env.logger)
68+
env.logger.error(e.errors[0].message, generatorModuleAnnotation.arguments.firstOrNull { it.name?.asString().equals("types") })
69+
error = true
70+
}
71+
}
72+
if (error) {
73+
return
74+
}
75+
FileSpec.get(packageName, builder.build()).writeTo(env.codeGenerator, false)
76+
}
77+
78+
private fun generateMapper(module: KSClassDeclaration, methodName: String, mapperName: ClassName, jsonClassDecl: KSClassDeclaration, generator: (ClassName, KSClassDeclaration) -> TypeSpec): FunSpec {
79+
val packageName = module.packageName.asString()
80+
val mapper = generator(mapperName, jsonClassDecl)
81+
val mapperBuilder = mapper.toBuilder();
82+
mapperBuilder.originatingElements.clear()
83+
mapperBuilder.addOriginatingKSFile(module)
84+
85+
FileSpec.get(packageName, mapperBuilder.build()).writeTo(env.codeGenerator, false)
86+
87+
return this.mapperMethod(methodName, mapperName, mapper);
88+
}
89+
90+
private fun mapperMethod(methodName: String, mapperName: ClassName, mapper: TypeSpec): FunSpec {
91+
val mapperConstructor = mapper.primaryConstructor!!
92+
return FunSpec.builder(methodName)
93+
.addParameters(mapperConstructor.parameters)
94+
.returns(mapperName)
95+
.addStatement("return %T(%L)", mapperName, mapperConstructor.parameters.map { CodeBlock.of("%N", it.name) }.joinToCode(", "))
96+
.build()
97+
}
98+
}
Lines changed: 26 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,11 @@
11
package ru.tinkoff.kora.json.ksp
22

3-
import com.google.devtools.ksp.getClassDeclarationByName
43
import com.google.devtools.ksp.processing.CodeGenerator
5-
import com.google.devtools.ksp.processing.KSPLogger
6-
import com.google.devtools.ksp.processing.Resolver
74
import com.google.devtools.ksp.symbol.KSClassDeclaration
85
import com.google.devtools.ksp.symbol.Modifier
6+
import com.squareup.kotlinpoet.ClassName
97
import com.squareup.kotlinpoet.FileSpec
8+
import com.squareup.kotlinpoet.TypeSpec
109
import com.squareup.kotlinpoet.ksp.writeTo
1110
import ru.tinkoff.kora.json.ksp.reader.EnumJsonReaderGenerator
1211
import ru.tinkoff.kora.json.ksp.reader.JsonReaderGenerator
@@ -18,63 +17,50 @@ import ru.tinkoff.kora.json.ksp.writer.SealedInterfaceWriterGenerator
1817
import ru.tinkoff.kora.json.ksp.writer.WriterTypeMetaParser
1918

2019
class JsonProcessor(
21-
private val resolver: Resolver,
22-
private val logger: KSPLogger,
2320
private val codeGenerator: CodeGenerator,
24-
private val knownType: KnownType,
2521
) {
26-
private val readerTypeMetaParser = ReaderTypeMetaParser(knownType, logger)
27-
private val writerTypeMetaParser = WriterTypeMetaParser(resolver)
28-
private val writerGenerator = JsonWriterGenerator(resolver)
29-
private val readerGenerator = JsonReaderGenerator(resolver)
22+
private val readerTypeMetaParser = ReaderTypeMetaParser()
23+
private val writerTypeMetaParser = WriterTypeMetaParser()
24+
private val writerGenerator = JsonWriterGenerator()
25+
private val readerGenerator = JsonReaderGenerator()
3026
private val sealedReaderGenerator = SealedInterfaceReaderGenerator()
3127
private val sealedWriterGenerator = SealedInterfaceWriterGenerator()
3228
private val enumJsonReaderGenerator = EnumJsonReaderGenerator()
3329
private val enumJsonWriterGenerator = EnumJsonWriterGenerator()
3430

3531
fun generateReader(jsonClassDeclaration: KSClassDeclaration) {
36-
val packageElement = jsonClassPackage(jsonClassDeclaration)
32+
val packageElement = jsonClassDeclaration.packageName.asString()
3733
val readerClassName = jsonClassDeclaration.jsonReaderName()
38-
val readerDeclaration = resolver.getClassDeclarationByName("$packageElement.$readerClassName")
39-
if (readerDeclaration != null) {
40-
return
41-
}
42-
val readerType = when {
43-
isSealed(jsonClassDeclaration) -> sealedReaderGenerator.generateSealedReader(jsonClassDeclaration)
44-
jsonClassDeclaration.modifiers.contains(Modifier.ENUM) -> enumJsonReaderGenerator.generateEnumReader(jsonClassDeclaration)
34+
val type = generateReader(ClassName(packageElement, readerClassName), jsonClassDeclaration)
35+
FileSpec.get(packageElement, type).writeTo(codeGenerator, false)
36+
}
37+
38+
fun generateReader(target: ClassName, jsonClassDeclaration: KSClassDeclaration): TypeSpec {
39+
return when {
40+
isSealed(jsonClassDeclaration) -> sealedReaderGenerator.generateSealedReader(target, jsonClassDeclaration)
41+
jsonClassDeclaration.modifiers.contains(Modifier.ENUM) -> enumJsonReaderGenerator.generateEnumReader(target, jsonClassDeclaration)
4542
else -> {
4643
val meta = readerTypeMetaParser.parse(jsonClassDeclaration)
47-
readerGenerator.generate(meta)
44+
readerGenerator.generate(target, meta)
4845
}
4946
}
50-
val fileSpec = FileSpec.builder(
51-
packageName = packageElement,
52-
fileName = readerType.name!!
53-
)
54-
fileSpec.addType(readerType)
55-
fileSpec.build().writeTo(codeGenerator = codeGenerator, aggregating = false)
5647
}
5748

5849
fun generateWriter(declaration: KSClassDeclaration) {
59-
val packageElement = jsonClassPackage(declaration)
50+
val packageElement = declaration.packageName.asString()
6051
val writerClassName = declaration.jsonWriterName()
61-
val writerDeclaration = resolver.getClassDeclarationByName("$packageElement.$writerClassName")
62-
if (writerDeclaration != null) {
63-
return
64-
}
65-
val writerType = when {
66-
isSealed(declaration) -> sealedWriterGenerator.generateSealedWriter(declaration)
67-
declaration.modifiers.contains(Modifier.ENUM) -> enumJsonWriterGenerator.generateEnumWriter(declaration)
52+
val type = generateWriter(ClassName(packageElement, writerClassName), declaration)
53+
FileSpec.get(packageElement, type).writeTo(codeGenerator, false)
54+
}
55+
56+
fun generateWriter(target: ClassName, declaration: KSClassDeclaration): TypeSpec {
57+
return when {
58+
isSealed(declaration) -> sealedWriterGenerator.generateSealedWriter(target, declaration)
59+
declaration.modifiers.contains(Modifier.ENUM) -> enumJsonWriterGenerator.generateEnumWriter(target, declaration)
6860
else -> {
6961
val meta = writerTypeMetaParser.parse(declaration)
70-
writerGenerator.generate(meta)
62+
writerGenerator.generate(target, meta)
7163
}
7264
}
73-
val fileSpec = FileSpec.builder(
74-
packageName = packageElement,
75-
fileName = writerType.name!!
76-
)
77-
fileSpec.addType(writerType)
78-
fileSpec.build().writeTo(codeGenerator = codeGenerator, aggregating = false)
7965
}
8066
}

json/json-symbol-processor/src/main/kotlin/ru/tinkoff/kora/json/ksp/JsonSymbolProcessor.kt

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -25,13 +25,7 @@ class JsonSymbolProcessor(
2525
)
2626

2727
override fun processRound(resolver: Resolver): List<KSAnnotated> {
28-
val knownType = KnownType(resolver)
29-
val jsonProcessor = JsonProcessor(
30-
resolver,
31-
kspLogger,
32-
codeGenerator,
33-
knownType
34-
)
28+
val jsonProcessor = JsonProcessor(codeGenerator)
3529
val symbolsToProcess = getSupportedAnnotationTypes().map { resolver.getSymbolsWithAnnotation(it).toList() }.flatten().distinct()
3630
for (it in symbolsToProcess) {
3731
try {

json/json-symbol-processor/src/main/kotlin/ru/tinkoff/kora/json/ksp/KnownType.kt

Lines changed: 19 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -1,42 +1,28 @@
11
package ru.tinkoff.kora.json.ksp
22

3-
import com.google.devtools.ksp.getClassDeclarationByName
4-
import com.google.devtools.ksp.processing.Resolver
53
import com.google.devtools.ksp.symbol.KSType
4+
import com.squareup.kotlinpoet.*
5+
import com.squareup.kotlinpoet.ksp.toTypeName
6+
import java.math.BigInteger
7+
import java.util.*
68

7-
class KnownType(private val resolver: Resolver) {
8-
private val any = resolver.builtIns.anyType
9-
private val nullableAny = resolver.builtIns.anyType.makeNullable()
10-
private val string = resolver.builtIns.stringType
11-
private val nullableString = string.makeNullable()
12-
private val boolean = resolver.builtIns.booleanType
13-
private val nullableBoolean = boolean.makeNullable()
14-
private val integer = resolver.builtIns.intType
15-
private val nullableInteger = integer.makeNullable()
16-
private val bigInteger = resolver.getClassDeclarationByName("java.math.BigInteger")!!.asType(listOf())
17-
private val double = resolver.builtIns.doubleType
18-
private val nullableDouble = double.makeNullable()
19-
private val float = resolver.builtIns.floatType
20-
private val nullableFloat = float.makeNullable()
21-
private val long = resolver.builtIns.longType
22-
private val nullableLong = long.makeNullable()
23-
private val short = resolver.builtIns.shortType
24-
private val nullableShort = short.makeNullable()
25-
private val uuid = resolver.getClassDeclarationByName("java.util.UUID")!!.asType(listOf())
26-
private val binary = resolver.getClassDeclarationByName("kotlin.ByteArray")!!.asType(listOf())
9+
object KnownType {
10+
private val bigInteger = BigInteger::class.asClassName()
11+
private val uuid = UUID::class.asClassName()
12+
private val binary = ByteArray::class.asClassName()
2713

2814
fun detect(type: KSType): KnownTypesEnum? {
29-
return when (type) {
30-
string, nullableString -> KnownTypesEnum.STRING
31-
integer, nullableInteger -> KnownTypesEnum.INTEGER
32-
long, nullableLong -> KnownTypesEnum.LONG
33-
double, nullableDouble -> KnownTypesEnum.DOUBLE
34-
float, nullableFloat -> KnownTypesEnum.FLOAT
35-
short, nullableShort -> KnownTypesEnum.SHORT
36-
bigInteger, bigInteger.makeNullable() -> KnownTypesEnum.BIG_INTEGER
37-
boolean, nullableBoolean -> KnownTypesEnum.BOOLEAN
38-
binary, binary.makeNullable() -> KnownTypesEnum.BINARY
39-
uuid, uuid.makeNullable() -> KnownTypesEnum.UUID
15+
return when (type.toTypeName().copy(nullable = false)) {
16+
STRING -> KnownTypesEnum.STRING
17+
INT -> KnownTypesEnum.INTEGER
18+
LONG -> KnownTypesEnum.LONG
19+
DOUBLE -> KnownTypesEnum.DOUBLE
20+
FLOAT -> KnownTypesEnum.FLOAT
21+
SHORT -> KnownTypesEnum.SHORT
22+
bigInteger -> KnownTypesEnum.BIG_INTEGER
23+
BOOLEAN -> KnownTypesEnum.BOOLEAN
24+
binary -> KnownTypesEnum.BINARY
25+
uuid -> KnownTypesEnum.UUID
4026
else -> null
4127
}
4228
}

json/json-symbol-processor/src/main/kotlin/ru/tinkoff/kora/json/ksp/extension/JsonKoraExtension.kt

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,10 @@ import com.google.devtools.ksp.processing.CodeGenerator
55
import com.google.devtools.ksp.processing.KSPLogger
66
import com.google.devtools.ksp.processing.Resolver
77
import com.google.devtools.ksp.symbol.*
8-
import ru.tinkoff.kora.json.ksp.*
8+
import ru.tinkoff.kora.json.ksp.JsonProcessor
9+
import ru.tinkoff.kora.json.ksp.JsonTypes
10+
import ru.tinkoff.kora.json.ksp.jsonReaderName
11+
import ru.tinkoff.kora.json.ksp.jsonWriterName
912
import ru.tinkoff.kora.json.ksp.reader.ReaderTypeMetaParser
1013
import ru.tinkoff.kora.json.ksp.writer.WriterTypeMetaParser
1114
import ru.tinkoff.kora.kora.app.ksp.extension.ExtensionResult
@@ -21,10 +24,9 @@ class JsonKoraExtension(
2124
) : KoraExtension {
2225
private val jsonWriterErasure = resolver.getClassDeclarationByName(JsonTypes.jsonWriter.canonicalName)!!.asStarProjectedType()
2326
private val jsonReaderErasure = resolver.getClassDeclarationByName(JsonTypes.jsonReader.canonicalName)!!.asStarProjectedType()
24-
private val knownTypes = KnownType(resolver)
25-
private val readerTypeMetaParser: ReaderTypeMetaParser = ReaderTypeMetaParser(knownTypes, kspLogger)
26-
private val writerTypeMetaParser: WriterTypeMetaParser = WriterTypeMetaParser(resolver)
27-
private val processor: JsonProcessor = JsonProcessor(resolver, kspLogger, codeGenerator, knownTypes)
27+
private val readerTypeMetaParser: ReaderTypeMetaParser = ReaderTypeMetaParser()
28+
private val writerTypeMetaParser: WriterTypeMetaParser = WriterTypeMetaParser()
29+
private val processor: JsonProcessor = JsonProcessor(codeGenerator)
2830

2931
override fun getDependencyGenerator(resolver: Resolver, type: KSType, tags: Set<String>): (() -> ExtensionResult)? {
3032
if (tags.isNotEmpty()) return null

json/json-symbol-processor/src/main/kotlin/ru/tinkoff/kora/json/ksp/reader/EnumJsonReaderGenerator.kt

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,18 +8,17 @@ import com.squareup.kotlinpoet.ksp.addOriginatingKSFile
88
import com.squareup.kotlinpoet.ksp.toClassName
99
import com.squareup.kotlinpoet.ksp.toTypeName
1010
import ru.tinkoff.kora.json.ksp.JsonTypes
11-
import ru.tinkoff.kora.json.ksp.jsonReaderName
1211
import ru.tinkoff.kora.ksp.common.AnnotationUtils.isAnnotationPresent
1312
import ru.tinkoff.kora.ksp.common.KspCommonUtils.generated
1413
import ru.tinkoff.kora.ksp.common.KspCommonUtils.toTypeName
1514

1615
class EnumJsonReaderGenerator {
17-
fun generateEnumReader(jsonClassDeclaration: KSClassDeclaration): TypeSpec {
16+
fun generateEnumReader(target: ClassName, jsonClassDeclaration: KSClassDeclaration): TypeSpec {
1817
val className = jsonClassDeclaration.toClassName()
1918
val typeName = jsonClassDeclaration.toTypeName()
2019
val enumType = detectValueType(jsonClassDeclaration)
2120

22-
val typeBuilder = TypeSpec.classBuilder(jsonClassDeclaration.jsonReaderName())
21+
val typeBuilder = TypeSpec.classBuilder(target)
2322
.generated(JsonReaderGenerator::class)
2423
.primaryConstructor(FunSpec.constructorBuilder()
2524
.addParameter("valueReader", JsonTypes.jsonReader.parameterizedBy(enumType.type))

0 commit comments

Comments
 (0)