temporal solution of generating problem

This commit is contained in:
InsanusMokrassar 2023-02-26 14:37:41 +06:00
parent d2e6d2ec80
commit 8215f9d2c6
4 changed files with 178 additions and 148 deletions

View File

@ -89,17 +89,17 @@ publishing {
} }
if (project.hasProperty("signing.gnupg.keyName")) { if (project.hasProperty("signing.gnupg.keyName")) {
apply plugin: 'signing' // apply plugin: 'signing'
//
signing { // signing {
useGpgCmd() // useGpgCmd()
//
sign publishing.publications // sign publishing.publications
} // }
//
task signAll { // task signAll {
tasks.withType(Sign).forEach { // tasks.withType(Sign).forEach {
dependsOn(it) // dependsOn(it)
} // }
} // }
} }

View File

@ -8,8 +8,13 @@ import com.google.devtools.ksp.processing.Resolver
import com.google.devtools.ksp.processing.SymbolProcessor import com.google.devtools.ksp.processing.SymbolProcessor
import com.google.devtools.ksp.symbol.KSAnnotated import com.google.devtools.ksp.symbol.KSAnnotated
import com.google.devtools.ksp.symbol.KSClassDeclaration import com.google.devtools.ksp.symbol.KSClassDeclaration
import com.google.devtools.ksp.symbol.KSClassifierReference
import com.google.devtools.ksp.symbol.KSPropertyDeclaration import com.google.devtools.ksp.symbol.KSPropertyDeclaration
import com.google.devtools.ksp.symbol.KSReferenceElement
import com.google.devtools.ksp.symbol.KSType import com.google.devtools.ksp.symbol.KSType
import com.google.devtools.ksp.symbol.KSTypeAlias
import com.google.devtools.ksp.symbol.KSValueArgument
import com.google.devtools.ksp.symbol.Nullability
import com.squareup.kotlinpoet.AnnotationSpec import com.squareup.kotlinpoet.AnnotationSpec
import com.squareup.kotlinpoet.ClassName import com.squareup.kotlinpoet.ClassName
import com.squareup.kotlinpoet.CodeBlock import com.squareup.kotlinpoet.CodeBlock
@ -18,6 +23,7 @@ import com.squareup.kotlinpoet.FunSpec
import com.squareup.kotlinpoet.KModifier import com.squareup.kotlinpoet.KModifier
import com.squareup.kotlinpoet.ParameterSpec import com.squareup.kotlinpoet.ParameterSpec
import com.squareup.kotlinpoet.PropertySpec import com.squareup.kotlinpoet.PropertySpec
import com.squareup.kotlinpoet.TypeName
import com.squareup.kotlinpoet.TypeSpec import com.squareup.kotlinpoet.TypeSpec
import com.squareup.kotlinpoet.asTypeName import com.squareup.kotlinpoet.asTypeName
import com.squareup.kotlinpoet.ksp.toAnnotationSpec import com.squareup.kotlinpoet.ksp.toAnnotationSpec
@ -31,163 +37,181 @@ import java.io.File
import kotlin.reflect.KProperty1 import kotlin.reflect.KProperty1
import kotlin.reflect.full.memberProperties import kotlin.reflect.full.memberProperties
private fun KSClassifierReference.quilifiedName(): String = "${qualifier ?.let { "${it.quilifiedName()}." } ?: ""}${referencedName()}"
class Processor( class Processor(
private val codeGenerator: CodeGenerator private val codeGenerator: CodeGenerator
) : SymbolProcessor { ) : SymbolProcessor {
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()
)
}
}
}
@OptIn(KspExperimental::class) @OptIn(KspExperimental::class)
override fun process(resolver: Resolver): List<KSAnnotated> { override fun process(resolver: Resolver): List<KSAnnotated> {
resolver.getSymbolsWithAnnotation( val toRetry = resolver.getSymbolsWithAnnotation(
GenerateCRUDModel::class.qualifiedName!! GenerateCRUDModel::class.qualifiedName!!
).filterIsInstance<KSClassDeclaration>().forEach { ksClassDeclaration -> ).filterIsInstance<KSClassDeclaration>().filterNot { ksClassDeclaration ->
val ksFile = ksClassDeclaration.containingFile ?: return@forEach val ksFile = ksClassDeclaration.containingFile ?: return@filterNot false
FileSpec.builder( runCatching {
ksClassDeclaration.packageName.asString(), FileSpec.builder(
"GeneratedModels${ksFile.fileName.removeSuffix(".kt")}" ksClassDeclaration.packageName.asString(),
).apply { "GeneratedModels${ksFile.fileName.removeSuffix(".kt")}"
val annotation = ksClassDeclaration.getAnnotationsByType(GenerateCRUDModel::class).first() ).apply {
addFileComment( val annotation = ksClassDeclaration.getAnnotationsByType(GenerateCRUDModel::class).first()
""" addFileComment(
"""
THIS CODE HAVE BEEN GENERATED AUTOMATICALLY THIS CODE HAVE BEEN GENERATED AUTOMATICALLY
TO REGENERATE IT JUST DELETE FILE TO REGENERATE IT JUST DELETE FILE
ORIGINAL FILE: ${ksFile.fileName} ORIGINAL FILE: ${ksFile.fileName}
""".trimIndent() """.trimIndent()
) )
val newName = "New${ksClassDeclaration.simpleName.getShortName()}" val newName = "New${ksClassDeclaration.simpleName.getShortName()}"
val registeredName = "Registered${ksClassDeclaration.simpleName.getShortName()}" val registeredName = "Registered${ksClassDeclaration.simpleName.getShortName()}"
val allKSClassProperties = ksClassDeclaration.getAllProperties() val allKSClassProperties = ksClassDeclaration.getAllProperties()
val excludedKSClassProperties = allKSClassProperties.filter { val excludedKSClassProperties = allKSClassProperties.filter {
it.isAnnotationPresent(GenerateCRUDModelExcludeOverride::class) 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( val excludedKSClassPropertiesNames = excludedKSClassProperties.map { it.simpleName.asString() }
FunSpec.constructorBuilder().apply { val ksClassProperties = allKSClassProperties.filter {
ksClassProperties.forEach { it !in excludedKSClassProperties
addParameter(it.simpleName.getShortName(), it.type.toTypeName()) }
typeBuilder.addProperty( val ksClassPropertiesNames = ksClassProperties.map { it.simpleName.asString() }
PropertySpec.builder(it.simpleName.getShortName(), it.type.toTypeName(), KModifier.OVERRIDE).apply { val newNewType = TypeSpec.classBuilder(newName).apply {
initializer(it.simpleName.getShortName()) val typeBuilder = this
}.build() 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.typeName)
typeBuilder.addProperty(
PropertySpec.builder(it.simpleName.getShortName(), it.typeName, 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.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
)
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() }.build()
) )
}.build()
addType(
newNewType
)
val registeredSupertypes = ksClassDeclaration.annotations.filter { addFunction(
it.shortName.asString() == GenerateCRUDModel::class.simpleName && FunSpec.builder("asRegistered").apply {
it.annotationType.resolve().declaration.qualifiedName ?.asString() == GenerateCRUDModel::class.qualifiedName receiver(ksClassDeclaration.toClassName())
}.flatMap { (registeredTypesProperties.filter { it.simpleName.asString() !in ksClassPropertiesNames }).forEach {
(it.arguments.first().value as List<KSType>).map { it.declaration as KSClassDeclaration } addParameter(it.simpleName.asString(), it.typeName)
}.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()
)
} }
addCode(
CodeBlock.of(
"return ${newRegisteredType.name}(${newRegisteredType.propertySpecs.joinToString { it.name }})"
)
)
returns(ClassName(packageName, newRegisteredType.name!!))
}.build() }.build()
) )
}.build() }.build().let {
addType( File(
newRegisteredType File(ksFile.filePath).parent,
) "GeneratedModels${ksFile.fileName}"
).takeIf { !it.exists() } ?.apply {
parentFile.mkdirs()
addFunction( writer().use { writer ->
FunSpec.builder("asNew").apply { it.writeTo(writer)
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)
} }
} }
} }.isSuccess
} }.toList()
return emptyList() return toRetry
} }
} }

View File

@ -13,6 +13,7 @@ import kotlinx.serialization.Serializable
public data class NewTest( public data class NewTest(
public override val property1: String, public override val property1: String,
public override val property2: Int, public override val property2: Int,
public override val parent: ParentTypeId?,
) : Test ) : Test
@Serializable @Serializable
@ -21,8 +22,10 @@ public data class RegisteredTest(
public override val id: TestId, public override val id: TestId,
public override val property1: String, public override val property1: String,
public override val property2: Int, public override val property2: Int,
public override val parent: ParentTypeId?,
) : Test, IRegisteredTest ) : Test, IRegisteredTest
public fun Test.asNew(): NewTest = NewTest(property1, property2) public fun Test.asNew(): NewTest = NewTest(property1, property2, parent)
public fun Test.asRegistered(id: TestId): RegisteredTest = RegisteredTest(id, property1, property2) public fun Test.asRegistered(id: TestId): RegisteredTest = RegisteredTest(id, property1, property2,
parent)

View File

@ -11,10 +11,13 @@ value class TestId(
val long: Long val long: Long
) )
typealias ParentTypeId = TestId
@GenerateCRUDModel(IRegisteredTest::class) @GenerateCRUDModel(IRegisteredTest::class)
sealed interface Test { sealed interface Test {
val property1: String val property1: String
val property2: Int val property2: Int
val parent: ParentTypeId?
@GenerateCRUDModelExcludeOverride @GenerateCRUDModelExcludeOverride
val excludedProperty: String val excludedProperty: String