Skip to content

Commit

Permalink
Merge pull request #34 from tobrun/kdz-revisit-default-annotation
Browse files Browse the repository at this point in the history
Use another approach for default annotation
  • Loading branch information
kiryldz authored Mar 14, 2023
2 parents 37349dd + 375000c commit 3c42494
Show file tree
Hide file tree
Showing 3 changed files with 57 additions and 18 deletions.
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,8 @@ And you will have to include the required dependencies:

```groovy
dependencies {
implementation 'com.github.tobrun.kotlin-data-compat:annotation:0.4.0'
ksp 'com.github.tobrun.kotlin-data-compat:processor:0.4.0'
implementation 'com.github.tobrun.kotlin-data-compat:annotation:0.4.1'
ksp 'com.github.tobrun.kotlin-data-compat:processor:0.4.1'
}
```

Expand Down
2 changes: 1 addition & 1 deletion gradle/wrapper/gradle-wrapper.properties
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-7.1-bin.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-7.6.1-bin.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,9 @@ import com.google.devtools.ksp.processing.SymbolProcessor
import com.google.devtools.ksp.symbol.ClassKind
import com.google.devtools.ksp.symbol.KSAnnotated
import com.google.devtools.ksp.symbol.KSClassDeclaration
import com.google.devtools.ksp.symbol.KSNode
import com.google.devtools.ksp.symbol.KSPropertyDeclaration
import com.google.devtools.ksp.symbol.KSValueParameter
import com.google.devtools.ksp.symbol.KSVisitorVoid
import com.google.devtools.ksp.symbol.Modifier
import com.google.devtools.ksp.validate
Expand Down Expand Up @@ -45,19 +47,63 @@ class DataCompatProcessor(

override fun process(resolver: Resolver): List<KSAnnotated> {
logger.info("DataCompat: process")
val annotated = resolver.getSymbolsWithAnnotation(DataCompat::class.qualifiedName!!, true)
if (annotated.count() == 0) {
val dataCompatAnnotated = resolver.getSymbolsWithAnnotation(DataCompat::class.qualifiedName!!, true)
if (dataCompatAnnotated.count() == 0) {
logger.info("DataCompat: No DataCompat annotations found for processing")
return emptyList()
}

val unableToProcess = annotated.filterNot { it.validate() }
annotated.filter { it is KSClassDeclaration && it.validate() }
.forEach { it.accept(Visitor(), Unit) }
// symbols returned by
// resolver.getSymbolsWithAnnotation(DataCompat::class.qualifiedName!!, true)
// don't expose their annotations.
// Therefore we can't access the @Default annotation required to build the code
// for default values.
val classToDefaultValuesMap =
mutableMapOf<KSClassDeclaration, MutableMap<String, String?>>()
val imports = ArrayList<String>()
val symbolsWithDefaultAnnotation =
resolver.getSymbolsWithAnnotation(Default::class.qualifiedName!!, true)
symbolsWithDefaultAnnotation.forEach { annotatedProperty ->
// since KSP 1.8.10-1.0.9 annotatedProperty are returned ONLY as KSValueParameter;
// for previous KSP versions they were also returned as KSPropertyDeclaration
if (annotatedProperty is KSValueParameter) {
val parentClass = annotatedProperty.findParentClass()!!
var defaultValueMap = classToDefaultValuesMap[parentClass]
if (defaultValueMap == null) {
defaultValueMap = mutableMapOf()
}
val defaultAnnotationsParams = annotatedProperty.annotations.firstOrNull()?.arguments
val defaultValue = defaultAnnotationsParams?.first()
defaultValueMap[annotatedProperty.name!!.getShortName()] =
defaultValue?.value as? String?
defaultAnnotationsParams?.getOrNull(1)?.value?.let {
imports.addAll(it as ArrayList<String>)
}
classToDefaultValuesMap[parentClass] = defaultValueMap
}
}

val unableToProcess = dataCompatAnnotated.filterNot { it.validate() }
dataCompatAnnotated.filter { it is KSClassDeclaration && it.validate() }
.forEach { it.accept(Visitor(classToDefaultValuesMap, imports), Unit) }
return unableToProcess.toList()
}

private inner class Visitor : KSVisitorVoid() {
private fun KSNode.findParentClass(): KSClassDeclaration? {
var currentParent: KSNode? = parent
while (currentParent !is KSClassDeclaration) {
currentParent = parent?.parent
if (currentParent == null) {
return null
}
}
return currentParent
}

private inner class Visitor(
private val defaultValuesMap: Map<KSClassDeclaration, MutableMap<String, String?>>,
private val imports: List<String>
) : KSVisitorVoid() {

@Suppress("LongMethod", "MaxLineLength", "ComplexMethod")
override fun visitClassDeclaration(classDeclaration: KSClassDeclaration, data: Unit) {
Expand All @@ -77,7 +123,6 @@ class DataCompatProcessor(
val implementedInterfaces = classDeclaration
.superTypes
.filter { (it.resolve().declaration as? KSClassDeclaration)?.classKind == ClassKind.INTERFACE }
val imports = ArrayList<String>()

// Map KSP properties with KoltinPoet TypeNames
val propertyMap = mutableMapOf<KSPropertyDeclaration, TypeName>()
Expand Down Expand Up @@ -220,19 +265,13 @@ class DataCompatProcessor(
val builderBuilder = TypeSpec.classBuilder("Builder")
for (property in propertyMap) {
val propertyName = property.key.toString()
val defaultAnnotationsParams = property.key.annotations
.firstOrNull { it.annotationType.resolve().toString() == Default::class.simpleName }
?.arguments
val defaultValue = defaultAnnotationsParams?.first()
defaultAnnotationsParams?.getOrNull(1)?.value?.let {
imports.addAll(it as ArrayList<String>)
}

val nullableType = property.value.copy(nullable = true)
builderBuilder.addProperty(
PropertySpec.builder(propertyName, nullableType)
.initializer(
CodeBlock.builder()
.add((defaultValue?.value as? String?) ?: "null")
.add(defaultValuesMap[classDeclaration]?.get(propertyName) ?: "null")
.build()
)
.addAnnotation(
Expand Down

0 comments on commit 3c42494

Please sign in to comment.