2023-02-25 13:56:12 +00:00
|
|
|
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
|
2023-02-26 08:37:41 +00:00
|
|
|
import com.google.devtools.ksp.symbol.KSClassifierReference
|
2023-02-25 13:56:12 +00:00
|
|
|
import com.google.devtools.ksp.symbol.KSPropertyDeclaration
|
2023-02-26 08:37:41 +00:00
|
|
|
import com.google.devtools.ksp.symbol.KSReferenceElement
|
2023-02-25 13:56:12 +00:00
|
|
|
import com.google.devtools.ksp.symbol.KSType
|
2023-02-26 08:37:41 +00:00
|
|
|
import com.google.devtools.ksp.symbol.KSTypeAlias
|
|
|
|
import com.google.devtools.ksp.symbol.KSValueArgument
|
|
|
|
import com.google.devtools.ksp.symbol.Nullability
|
2023-02-25 13:56:12 +00:00
|
|
|
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
|
2023-02-26 08:37:41 +00:00
|
|
|
import com.squareup.kotlinpoet.TypeName
|
2023-02-25 13:56:12 +00:00
|
|
|
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
|
|
|
|
|
2023-02-26 08:37:41 +00:00
|
|
|
private fun KSClassifierReference.quilifiedName(): String = "${qualifier ?.let { "${it.quilifiedName()}." } ?: ""}${referencedName()}"
|
|
|
|
|
2023-02-25 13:56:12 +00:00
|
|
|
class Processor(
|
|
|
|
private val codeGenerator: CodeGenerator
|
|
|
|
) : SymbolProcessor {
|
2023-02-26 08:37:41 +00:00
|
|
|
private val KSPropertyDeclaration.typeName: TypeName
|
|
|
|
get() {
|
|
|
|
return runCatching {
|
|
|
|
type.toTypeName()
|
|
|
|
}.getOrElse {
|
|
|
|
val element = type.element as KSClassifierReference
|
|
|
|
(type.element as KSClassifierReference).let {
|
|
|
|
ClassName(
|
|
|
|
element.qualifier ?.quilifiedName() ?: "",
|
|
|
|
element.referencedName()
|
|
|
|
)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2023-02-25 13:56:12 +00:00
|
|
|
@OptIn(KspExperimental::class)
|
|
|
|
override fun process(resolver: Resolver): List<KSAnnotated> {
|
2023-02-26 08:37:41 +00:00
|
|
|
val toRetry = resolver.getSymbolsWithAnnotation(
|
2023-02-25 13:56:12 +00:00
|
|
|
GenerateCRUDModel::class.qualifiedName!!
|
2023-02-26 08:37:41 +00:00
|
|
|
).filterIsInstance<KSClassDeclaration>().filterNot { ksClassDeclaration ->
|
|
|
|
val ksFile = ksClassDeclaration.containingFile ?: return@filterNot false
|
|
|
|
runCatching {
|
|
|
|
FileSpec.builder(
|
|
|
|
ksClassDeclaration.packageName.asString(),
|
|
|
|
"GeneratedModels${ksFile.fileName.removeSuffix(".kt")}"
|
|
|
|
).apply {
|
|
|
|
val annotation = ksClassDeclaration.getAnnotationsByType(GenerateCRUDModel::class).first()
|
|
|
|
addFileComment(
|
|
|
|
"""
|
2023-02-25 13:56:12 +00:00
|
|
|
THIS CODE HAVE BEEN GENERATED AUTOMATICALLY
|
|
|
|
TO REGENERATE IT JUST DELETE FILE
|
|
|
|
ORIGINAL FILE: ${ksFile.fileName}
|
|
|
|
""".trimIndent()
|
2023-02-26 08:37:41 +00:00
|
|
|
)
|
|
|
|
val newName = "New${ksClassDeclaration.simpleName.getShortName()}"
|
|
|
|
val registeredName = "Registered${ksClassDeclaration.simpleName.getShortName()}"
|
2023-02-25 13:56:12 +00:00
|
|
|
|
2023-02-26 08:37:41 +00:00
|
|
|
val allKSClassProperties = ksClassDeclaration.getAllProperties()
|
|
|
|
val excludedKSClassProperties = allKSClassProperties.filter {
|
|
|
|
it.isAnnotationPresent(GenerateCRUDModelExcludeOverride::class)
|
2023-02-25 13:56:12 +00:00
|
|
|
}
|
2023-02-26 08:37:41 +00:00
|
|
|
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)))
|
2023-02-25 13:56:12 +00:00
|
|
|
}
|
2023-02-26 08:37:41 +00:00
|
|
|
}
|
|
|
|
primaryConstructor(
|
|
|
|
FunSpec.constructorBuilder().apply {
|
|
|
|
ksClassProperties.forEach {
|
2023-06-20 14:20:47 +00:00
|
|
|
addParameter(
|
|
|
|
ParameterSpec.builder(it.simpleName.getShortName(), it.typeName).apply {
|
|
|
|
annotations += it.annotations.map { it.toAnnotationSpec() }
|
|
|
|
}.build()
|
|
|
|
)
|
2023-02-26 08:37:41 +00:00
|
|
|
typeBuilder.addProperty(
|
|
|
|
PropertySpec.builder(it.simpleName.getShortName(), it.typeName, KModifier.OVERRIDE).apply {
|
|
|
|
initializer(it.simpleName.getShortName())
|
|
|
|
}.build()
|
|
|
|
)
|
|
|
|
}
|
|
|
|
}.build()
|
|
|
|
)
|
|
|
|
}.build()
|
|
|
|
addType(
|
|
|
|
newNewType
|
2023-02-25 13:56:12 +00:00
|
|
|
)
|
|
|
|
|
2023-02-26 08:37:41 +00:00
|
|
|
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()
|
2023-02-25 13:56:12 +00:00
|
|
|
|
|
|
|
|
2023-02-26 08:37:41 +00:00
|
|
|
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()
|
2023-02-25 13:56:12 +00:00
|
|
|
|
2023-02-26 08:37:41 +00:00
|
|
|
ksClassPropertiesNames.indexOf(name).takeIf { it > -1 } ?.let {
|
|
|
|
it + allProperties.size
|
|
|
|
} ?: allProperties.indexOfFirst { it.simpleName.asString() == name }
|
|
|
|
}
|
2023-02-25 13:56:12 +00:00
|
|
|
|
2023-02-26 08:37:41 +00:00
|
|
|
val newRegisteredType = TypeSpec.classBuilder(registeredName).apply {
|
|
|
|
val typeBuilder = this
|
|
|
|
addSuperinterface(ksClassDeclaration.toClassName())
|
|
|
|
if (annotation.serializable) {
|
|
|
|
addAnnotation(Serializable::class)
|
2023-02-25 13:56:12 +00:00
|
|
|
|
2023-02-26 08:37:41 +00:00
|
|
|
if (annotation.generateSerialName) {
|
|
|
|
addAnnotation(
|
|
|
|
AnnotationSpec.get(SerialName(registeredName))
|
2023-02-25 13:56:12 +00:00
|
|
|
)
|
|
|
|
}
|
2023-02-26 08:37:41 +00:00
|
|
|
}
|
|
|
|
addSuperinterfaces(registeredSupertypes.map { it.toClassName() })
|
|
|
|
addModifiers(KModifier.DATA)
|
|
|
|
primaryConstructor(
|
|
|
|
FunSpec.constructorBuilder().apply {
|
|
|
|
propertiesToOverrideInRegistered.forEach {
|
|
|
|
addParameter(
|
|
|
|
ParameterSpec.builder(it.simpleName.getShortName(), it.typeName).apply {
|
|
|
|
annotations += it.annotations.map { it.toAnnotationSpec() }
|
|
|
|
}.build()
|
|
|
|
)
|
|
|
|
typeBuilder.addProperty(
|
|
|
|
PropertySpec.builder(it.simpleName.getShortName(), it.typeName, KModifier.OVERRIDE).apply {
|
|
|
|
initializer(it.simpleName.getShortName())
|
|
|
|
}.build()
|
|
|
|
)
|
|
|
|
}
|
|
|
|
}.build()
|
|
|
|
)
|
|
|
|
}.build()
|
|
|
|
addType(
|
|
|
|
newRegisteredType
|
2023-02-25 13:56:12 +00:00
|
|
|
)
|
|
|
|
|
2023-02-26 08:37:41 +00:00
|
|
|
addFunction(
|
|
|
|
FunSpec.builder("asNew").apply {
|
|
|
|
receiver(ksClassDeclaration.toClassName())
|
|
|
|
addCode(
|
|
|
|
CodeBlock.of(
|
|
|
|
"return ${newNewType.name}(${newNewType.propertySpecs.joinToString { it.name }})"
|
|
|
|
)
|
2023-02-25 13:56:12 +00:00
|
|
|
)
|
2023-02-26 08:37:41 +00:00
|
|
|
returns(ClassName(packageName, newNewType.name!!))
|
|
|
|
}.build()
|
|
|
|
)
|
2023-02-25 13:56:12 +00:00
|
|
|
|
2023-02-26 08:37:41 +00:00
|
|
|
addFunction(
|
|
|
|
FunSpec.builder("asRegistered").apply {
|
|
|
|
receiver(ksClassDeclaration.toClassName())
|
|
|
|
(registeredTypesProperties.filter { it.simpleName.asString() !in ksClassPropertiesNames }).forEach {
|
|
|
|
addParameter(it.simpleName.asString(), it.typeName)
|
|
|
|
}
|
|
|
|
addCode(
|
|
|
|
CodeBlock.of(
|
|
|
|
"return ${newRegisteredType.name}(${newRegisteredType.propertySpecs.joinToString { it.name }})"
|
|
|
|
)
|
2023-02-25 13:56:12 +00:00
|
|
|
)
|
2023-02-26 08:37:41 +00:00
|
|
|
returns(ClassName(packageName, newRegisteredType.name!!))
|
|
|
|
}.build()
|
|
|
|
)
|
|
|
|
}.build().let {
|
|
|
|
File(
|
|
|
|
File(ksFile.filePath).parent,
|
|
|
|
"GeneratedModels${ksFile.fileName}"
|
|
|
|
).takeIf { !it.exists() } ?.apply {
|
|
|
|
parentFile.mkdirs()
|
2023-02-25 13:56:12 +00:00
|
|
|
|
2023-02-26 08:37:41 +00:00
|
|
|
writer().use { writer ->
|
|
|
|
it.writeTo(writer)
|
|
|
|
}
|
2023-02-25 13:56:12 +00:00
|
|
|
}
|
|
|
|
}
|
2023-02-26 08:37:41 +00:00
|
|
|
}.isSuccess
|
|
|
|
}.toList()
|
2023-02-25 13:56:12 +00:00
|
|
|
|
2023-02-26 08:37:41 +00:00
|
|
|
return toRetry
|
2023-02-25 13:56:12 +00:00
|
|
|
}
|
|
|
|
}
|