mirror of
https://github.com/InsanusMokrassar/MicroUtils.git
synced 2024-06-28 20:37:35 +00:00
194 lines
9.3 KiB
Kotlin
194 lines
9.3 KiB
Kotlin
|
package dev.inmo.micro_utils.repos.generator
|
||
|
|
||
|
import com.google.devtools.ksp.KspExperimental
|
||
|
import com.google.devtools.ksp.getAnnotationsByType
|
||
|
import com.google.devtools.ksp.isAnnotationPresent
|
||
|
import com.google.devtools.ksp.processing.CodeGenerator
|
||
|
import com.google.devtools.ksp.processing.Resolver
|
||
|
import com.google.devtools.ksp.processing.SymbolProcessor
|
||
|
import com.google.devtools.ksp.symbol.KSAnnotated
|
||
|
import com.google.devtools.ksp.symbol.KSClassDeclaration
|
||
|
import com.google.devtools.ksp.symbol.KSPropertyDeclaration
|
||
|
import com.google.devtools.ksp.symbol.KSType
|
||
|
import com.squareup.kotlinpoet.AnnotationSpec
|
||
|
import com.squareup.kotlinpoet.ClassName
|
||
|
import com.squareup.kotlinpoet.CodeBlock
|
||
|
import com.squareup.kotlinpoet.FileSpec
|
||
|
import com.squareup.kotlinpoet.FunSpec
|
||
|
import com.squareup.kotlinpoet.KModifier
|
||
|
import com.squareup.kotlinpoet.ParameterSpec
|
||
|
import com.squareup.kotlinpoet.PropertySpec
|
||
|
import com.squareup.kotlinpoet.TypeSpec
|
||
|
import com.squareup.kotlinpoet.asTypeName
|
||
|
import com.squareup.kotlinpoet.ksp.toAnnotationSpec
|
||
|
import com.squareup.kotlinpoet.ksp.toClassName
|
||
|
import com.squareup.kotlinpoet.ksp.toTypeName
|
||
|
import dev.inmo.micro_utils.repos.annotations.GenerateCRUDModel
|
||
|
import dev.inmo.micro_utils.repos.annotations.GenerateCRUDModelExcludeOverride
|
||
|
import kotlinx.serialization.SerialName
|
||
|
import kotlinx.serialization.Serializable
|
||
|
import java.io.File
|
||
|
import kotlin.reflect.KProperty1
|
||
|
import kotlin.reflect.full.memberProperties
|
||
|
|
||
|
class Processor(
|
||
|
private val codeGenerator: CodeGenerator
|
||
|
) : SymbolProcessor {
|
||
|
@OptIn(KspExperimental::class)
|
||
|
override fun process(resolver: Resolver): List<KSAnnotated> {
|
||
|
resolver.getSymbolsWithAnnotation(
|
||
|
GenerateCRUDModel::class.qualifiedName!!
|
||
|
).filterIsInstance<KSClassDeclaration>().forEach { ksClassDeclaration ->
|
||
|
val ksFile = ksClassDeclaration.containingFile ?: return@forEach
|
||
|
FileSpec.builder(
|
||
|
ksClassDeclaration.packageName.asString(),
|
||
|
"GeneratedModels${ksFile.fileName.removeSuffix(".kt")}"
|
||
|
).apply {
|
||
|
val annotation = ksClassDeclaration.getAnnotationsByType(GenerateCRUDModel::class).first()
|
||
|
addFileComment(
|
||
|
"""
|
||
|
THIS CODE HAVE BEEN GENERATED AUTOMATICALLY
|
||
|
TO REGENERATE IT JUST DELETE FILE
|
||
|
ORIGINAL FILE: ${ksFile.fileName}
|
||
|
""".trimIndent()
|
||
|
)
|
||
|
val newName = "New${ksClassDeclaration.simpleName.getShortName()}"
|
||
|
val registeredName = "Registered${ksClassDeclaration.simpleName.getShortName()}"
|
||
|
|
||
|
val allKSClassProperties = ksClassDeclaration.getAllProperties()
|
||
|
val excludedKSClassProperties = allKSClassProperties.filter {
|
||
|
it.isAnnotationPresent(GenerateCRUDModelExcludeOverride::class)
|
||
|
}
|
||
|
val excludedKSClassPropertiesNames = excludedKSClassProperties.map { it.simpleName.asString() }
|
||
|
val ksClassProperties = allKSClassProperties.filter {
|
||
|
it !in excludedKSClassProperties
|
||
|
}
|
||
|
val ksClassPropertiesNames = ksClassProperties.map { it.simpleName.asString() }
|
||
|
val newNewType = TypeSpec.classBuilder(newName).apply {
|
||
|
val typeBuilder = this
|
||
|
addSuperinterface(ksClassDeclaration.toClassName())
|
||
|
addModifiers(KModifier.DATA)
|
||
|
if (annotation.serializable) {
|
||
|
addAnnotation(Serializable::class)
|
||
|
if (annotation.generateSerialName) {
|
||
|
addAnnotation(AnnotationSpec.get(SerialName(newName)))
|
||
|
}
|
||
|
}
|
||
|
primaryConstructor(
|
||
|
FunSpec.constructorBuilder().apply {
|
||
|
ksClassProperties.forEach {
|
||
|
addParameter(it.simpleName.getShortName(), it.type.toTypeName())
|
||
|
typeBuilder.addProperty(
|
||
|
PropertySpec.builder(it.simpleName.getShortName(), it.type.toTypeName(), KModifier.OVERRIDE).apply {
|
||
|
initializer(it.simpleName.getShortName())
|
||
|
}.build()
|
||
|
)
|
||
|
}
|
||
|
}.build()
|
||
|
)
|
||
|
}.build()
|
||
|
addType(
|
||
|
newNewType
|
||
|
)
|
||
|
|
||
|
val registeredSupertypes = ksClassDeclaration.annotations.filter {
|
||
|
it.shortName.asString() == GenerateCRUDModel::class.simpleName &&
|
||
|
it.annotationType.resolve().declaration.qualifiedName ?.asString() == GenerateCRUDModel::class.qualifiedName
|
||
|
}.flatMap {
|
||
|
(it.arguments.first().value as List<KSType>).map { it.declaration as KSClassDeclaration }
|
||
|
}.toList()
|
||
|
|
||
|
|
||
|
val registeredTypesProperties: List<KSPropertyDeclaration> = registeredSupertypes.flatMap { registeredType ->
|
||
|
registeredType.getAllProperties()
|
||
|
}.filter {
|
||
|
it.simpleName.asString() !in excludedKSClassPropertiesNames && it.getAnnotationsByType(GenerateCRUDModelExcludeOverride::class).none()
|
||
|
}
|
||
|
val allProperties: List<KSPropertyDeclaration> = ksClassProperties.toList() + registeredTypesProperties
|
||
|
val propertiesToOverrideInRegistered = allProperties.distinctBy { it.simpleName.asString() }.sortedBy { property ->
|
||
|
val name = property.simpleName.asString()
|
||
|
|
||
|
ksClassPropertiesNames.indexOf(name).takeIf { it > -1 } ?.let {
|
||
|
it + allProperties.size
|
||
|
} ?: allProperties.indexOfFirst { it.simpleName.asString() == name }
|
||
|
}
|
||
|
|
||
|
val newRegisteredType = TypeSpec.classBuilder(registeredName).apply {
|
||
|
val typeBuilder = this
|
||
|
addSuperinterface(ksClassDeclaration.toClassName())
|
||
|
if (annotation.serializable) {
|
||
|
addAnnotation(Serializable::class)
|
||
|
|
||
|
if (annotation.generateSerialName) {
|
||
|
addAnnotation(
|
||
|
AnnotationSpec.get(SerialName(registeredName))
|
||
|
)
|
||
|
}
|
||
|
}
|
||
|
addSuperinterfaces(registeredSupertypes.map { it.toClassName() })
|
||
|
addModifiers(KModifier.DATA)
|
||
|
primaryConstructor(
|
||
|
FunSpec.constructorBuilder().apply {
|
||
|
propertiesToOverrideInRegistered.forEach {
|
||
|
addParameter(
|
||
|
ParameterSpec.builder(it.simpleName.getShortName(), it.type.toTypeName()).apply {
|
||
|
annotations += it.annotations.map { it.toAnnotationSpec() }
|
||
|
}.build()
|
||
|
)
|
||
|
typeBuilder.addProperty(
|
||
|
PropertySpec.builder(it.simpleName.getShortName(), it.type.toTypeName(), KModifier.OVERRIDE).apply {
|
||
|
initializer(it.simpleName.getShortName())
|
||
|
}.build()
|
||
|
)
|
||
|
}
|
||
|
}.build()
|
||
|
)
|
||
|
}.build()
|
||
|
addType(
|
||
|
newRegisteredType
|
||
|
)
|
||
|
|
||
|
addFunction(
|
||
|
FunSpec.builder("asNew").apply {
|
||
|
receiver(ksClassDeclaration.toClassName())
|
||
|
addCode(
|
||
|
CodeBlock.of(
|
||
|
"return ${newNewType.name}(${newNewType.propertySpecs.joinToString { it.name }})"
|
||
|
)
|
||
|
)
|
||
|
returns(ClassName(packageName, newNewType.name!!))
|
||
|
}.build()
|
||
|
)
|
||
|
|
||
|
addFunction(
|
||
|
FunSpec.builder("asRegistered").apply {
|
||
|
receiver(ksClassDeclaration.toClassName())
|
||
|
(registeredTypesProperties.filter { it.simpleName.asString() !in ksClassPropertiesNames }).forEach {
|
||
|
addParameter(it.simpleName.asString(), it.type.toTypeName())
|
||
|
}
|
||
|
addCode(
|
||
|
CodeBlock.of(
|
||
|
"return ${newRegisteredType.name}(${newRegisteredType.propertySpecs.joinToString { it.name }})"
|
||
|
)
|
||
|
)
|
||
|
returns(ClassName(packageName, newRegisteredType.name!!))
|
||
|
}.build()
|
||
|
)
|
||
|
}.build().let {
|
||
|
File(
|
||
|
File(ksFile.filePath).parent,
|
||
|
"GeneratedModels${ksFile.fileName}"
|
||
|
).takeIf { !it.exists() } ?.apply {
|
||
|
parentFile.mkdirs()
|
||
|
|
||
|
writer().use { writer ->
|
||
|
it.writeTo(writer)
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return emptyList()
|
||
|
}
|
||
|
}
|