Skip to content

Commit f1f700b

Browse files
authored
Merge pull request #30 from tobrun/kdz-support-parent-annotations
Enhancements and fixes
2 parents 3745b3c + 1b6c290 commit f1f700b

File tree

4 files changed

+84
-21
lines changed

4 files changed

+84
-21
lines changed

processor/src/main/kotlin/com/tobrun/datacompat/DataCompatProcessor.kt

Lines changed: 42 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ 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.processing.SymbolProcessor
8+
import com.google.devtools.ksp.symbol.ClassKind
89
import com.google.devtools.ksp.symbol.KSAnnotated
910
import com.google.devtools.ksp.symbol.KSClassDeclaration
1011
import com.google.devtools.ksp.symbol.KSPropertyDeclaration
@@ -23,10 +24,12 @@ import com.squareup.kotlinpoet.ParameterSpec
2324
import com.squareup.kotlinpoet.PropertySpec
2425
import com.squareup.kotlinpoet.TypeName
2526
import com.squareup.kotlinpoet.TypeSpec
27+
import com.squareup.kotlinpoet.ksp.toClassName
2628
import com.squareup.kotlinpoet.ksp.toTypeName
2729
import com.squareup.kotlinpoet.ksp.toTypeParameterResolver
2830
import com.squareup.kotlinpoet.ksp.writeTo
2931
import com.tobrun.datacompat.annotation.DataCompat
32+
import java.util.Locale
3033

3134
/**
3235
* [DataCompatProcessor] is a concrete instance of the [SymbolProcessor] interface.
@@ -48,7 +51,8 @@ class DataCompatProcessor(
4851
}
4952

5053
val unableToProcess = annotated.filterNot { it.validate() }
51-
annotated.filter { it is KSClassDeclaration && it.validate() }.forEach { it.accept(Visitor(), Unit) }
54+
annotated.filter { it is KSClassDeclaration && it.validate() }
55+
.forEach { it.accept(Visitor(), Unit) }
5256
return unableToProcess.toList()
5357
}
5458

@@ -62,10 +66,17 @@ class DataCompatProcessor(
6266

6367
// Cleanup class name by dropping Data part
6468
// TODO make this part more flexible with providing name inside the annotation
65-
val className = classDeclaration.simpleName.asString().dropLast(CLASS_NAME_DROP_LAST_CHARACTERS)
69+
val className =
70+
classDeclaration.simpleName.asString().dropLast(CLASS_NAME_DROP_LAST_CHARACTERS)
6671
val classKdoc = classDeclaration.docString
6772
val packageName = classDeclaration.packageName.asString()
6873

74+
val otherAnnotations = classDeclaration.annotations
75+
.filter { it.annotationType.resolve().toString() != DataCompat::class.simpleName }
76+
val implementedInterfaces = classDeclaration
77+
.superTypes
78+
.filter { (it.resolve().declaration as? KSClassDeclaration)?.classKind == ClassKind.INTERFACE }
79+
6980
// Map KSP properties with KoltinPoet TypeNames
7081
val propertyMap = mutableMapOf<KSPropertyDeclaration, TypeName>()
7182
for (property in classDeclaration.getAllProperties()) {
@@ -100,6 +111,18 @@ class DataCompatProcessor(
100111
)
101112
}
102113

114+
otherAnnotations.forEach {
115+
addAnnotation(
116+
it.annotationType.resolve().toClassName()
117+
)
118+
}
119+
120+
implementedInterfaces.forEach {
121+
addSuperinterface(
122+
it.resolve().toClassName()
123+
)
124+
}
125+
103126
// Constructor
104127
val constructorBuilder = FunSpec.constructorBuilder()
105128
constructorBuilder.addModifiers(KModifier.PRIVATE)
@@ -121,11 +144,12 @@ class DataCompatProcessor(
121144
addFunction(
122145
FunSpec.builder("toString")
123146
.addModifiers(KModifier.OVERRIDE)
147+
// using triple quote for long strings
124148
.addStatement(
125149
propertyMap.keys.joinToString(
126-
prefix = "return \"$className(",
150+
prefix = "return \"\"\"$className(",
127151
transform = { "$it=$$it" },
128-
postfix = ")\""
152+
postfix = ")\"\"\".trimIndent()"
129153
)
130154
)
131155
.build()
@@ -188,14 +212,21 @@ class DataCompatProcessor(
188212

189213
var kDocProperty = kdocPropertyList
190214
.filter { it.startsWith("$propertyName ") }
191-
.joinToString { it.substringAfter("$propertyName ").toLowerCase() }
215+
.joinToString {
216+
it.substringAfter("$propertyName ").lowercase(Locale.getDefault())
217+
}
192218

193219
if (kDocProperty.isEmpty()) {
194220
kDocProperty = propertyName
195221
}
196222

197223
builderBuilder.addFunction(
198-
FunSpec.builder("set${propertyName.capitalize()}")
224+
FunSpec
225+
.builder(
226+
"set${propertyName.replaceFirstChar {
227+
if (it.isLowerCase()) it.titlecase(Locale.getDefault()) else it.toString()
228+
}}"
229+
)
199230
.addKdoc(
200231
"""
201232
|Set $kDocProperty
@@ -248,10 +279,12 @@ class DataCompatProcessor(
248279
|
249280
|This is a concrete implementation of the builder design pattern.
250281
|
251-
|${kdocPropertyList.joinToString(
282+
|${
283+
kdocPropertyList.joinToString(
252284
prefix = "$KDOC_PROPERTY_ANNOTATION ",
253285
separator = "\n$KDOC_PROPERTY_ANNOTATION "
254-
)}
286+
)
287+
}
255288
""".trimMargin()
256289
)
257290
builderBuilder.addFunction(buildFunction.build())
@@ -264,7 +297,7 @@ class DataCompatProcessor(
264297
"""
265298
|Creates a [$className] through a DSL-style builder.
266299
|
267-
|@param initializer the intialisation block
300+
|@param initializer the initialisation block
268301
|@return $className
269302
""".trimMargin()
270303
)

processor/src/test/kotlin/com/tobrun/datacompat/DataCompatProcessorTest.kt

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,17 +12,26 @@ import io.kotest.matchers.shouldBe
1212

1313
private val simpleTest = SourceFile.kotlin(
1414
"PersonData.kt",
15-
"""
15+
"""
1616
import com.tobrun.datacompat.annotation.DataCompat
1717
18+
interface EmptyInterface
19+
interface EmptyInterface2
1820
/**
1921
* Represents a person.
2022
* @property name The full name.
2123
* @property nickname The nickname.
2224
* @property age The age.
25+
* @property veryLongAndVeryDetailedDescription The very long and very detailed description.
2326
*/
27+
@Deprecated
2428
@DataCompat
25-
private data class PersonData(val name: String, val nickname: String? = null, val age: Int)
29+
private data class PersonData(
30+
val name: String,
31+
val nickname: String? = null,
32+
val age: Int,
33+
val veryLongAndVeryDetailedDescription: String?
34+
) : EmptyInterface, EmptyInterface2
2635
""".trimIndent()
2736
)
2837

processor/src/test/kotlin/com/tobrun/datacompat/SimpleTestContent.kt

Lines changed: 30 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ internal val expectedSimpleTestContent = """
44
import java.util.Objects
55
import kotlin.Any
66
import kotlin.Boolean
7+
import kotlin.Deprecated
78
import kotlin.Int
89
import kotlin.String
910
import kotlin.Unit
@@ -14,22 +15,28 @@ internal val expectedSimpleTestContent = """
1415
* @property name The full name.
1516
* @property nickname The nickname.
1617
* @property age The age.
18+
* @property veryLongAndVeryDetailedDescription The very long and very detailed description.
1719
*/
20+
@Deprecated
1821
public class Person private constructor(
1922
public val name: String,
2023
public val nickname: String?,
21-
public val age: Int
22-
) {
23-
public override fun toString() = "Person(name=%name, nickname=%nickname, age=%age)"
24+
public val age: Int,
25+
public val veryLongAndVeryDetailedDescription: String?
26+
) : EmptyInterface, EmptyInterface2 {
27+
public override fun toString() = ""${"\""}Person(name=%name, nickname=%nickname, age=%age,
28+
veryLongAndVeryDetailedDescription=%veryLongAndVeryDetailedDescription)""${"\""}.trimIndent()
2429
2530
public override fun equals(other: Any?): Boolean {
2631
if (this === other) return true
2732
if (javaClass != other?.javaClass) return false
2833
other as Person
29-
return name == other.name && nickname == other.nickname && age == other.age
34+
return name == other.name && nickname == other.nickname && age == other.age &&
35+
veryLongAndVeryDetailedDescription == other.veryLongAndVeryDetailedDescription
3036
}
3137
32-
public override fun hashCode(): Int = Objects.hash(name, nickname, age)
38+
public override fun hashCode(): Int = Objects.hash(name, nickname, age,
39+
veryLongAndVeryDetailedDescription)
3340
3441
/**
3542
* Composes and builds a [Person] object.
@@ -39,6 +46,7 @@ internal val expectedSimpleTestContent = """
3946
* @property name The full name.
4047
* @property nickname The nickname.
4148
* @property age The age.
49+
* @property veryLongAndVeryDetailedDescription The very long and very detailed description.
4250
*/
4351
public class Builder {
4452
@set:JvmSynthetic
@@ -49,6 +57,9 @@ internal val expectedSimpleTestContent = """
4957
5058
@set:JvmSynthetic
5159
public var age: Int? = null
60+
61+
@set:JvmSynthetic
62+
public var veryLongAndVeryDetailedDescription: String? = null
5263
5364
/**
5465
* Set the full name.
@@ -82,6 +93,18 @@ internal val expectedSimpleTestContent = """
8293
this.age = age
8394
return this
8495
}
96+
97+
/**
98+
* Set the very long and very detailed description.
99+
*
100+
* @param veryLongAndVeryDetailedDescription the very long and very detailed description.
101+
* @return Builder
102+
*/
103+
public fun setVeryLongAndVeryDetailedDescription(veryLongAndVeryDetailedDescription: String?):
104+
Builder {
105+
this.veryLongAndVeryDetailedDescription = veryLongAndVeryDetailedDescription
106+
return this
107+
}
85108
86109
/**
87110
* Returns a [Person] reference to the object being constructed by the builder.
@@ -97,15 +120,15 @@ internal val expectedSimpleTestContent = """
97120
if (age==null) {
98121
throw IllegalArgumentException("Null age found when building Person.")
99122
}
100-
return Person(name!!, nickname, age!!)
123+
return Person(name!!, nickname, age!!, veryLongAndVeryDetailedDescription)
101124
}
102125
}
103126
}
104127
105128
/**
106129
* Creates a [Person] through a DSL-style builder.
107130
*
108-
* @param initializer the intialisation block
131+
* @param initializer the initialisation block
109132
* @return Person
110133
*/
111134
@JvmSynthetic

processor/src/test/kotlin/com/tobrun/datacompat/TestAnnotation.kt

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,8 @@ package com.tobrun.datacompat
22

33
import com.tschuchort.compiletesting.SourceFile
44

5-
internal const val TEST_ANNOTATION_FILE_NAME = "TestAnnotation.kt"
6-
75
internal val testAnnotation = SourceFile.kotlin(
8-
TEST_ANNOTATION_FILE_NAME,
6+
"TestAnnotation.kt",
97
"""
108
package com.tobrun.datacompat.annotation
119

0 commit comments

Comments
 (0)