Skip to content

Commit c45300d

Browse files
authored
GH-97 Support Kotlin properties (Resolve #97)
* Add KPropertyMember. * Add AnnotationResolver. * Fix typo. * Rename methods to resolve conversation. * Mega fix B) * Format code. * Simplify code changes. * Fix typo.
1 parent 5769ffa commit c45300d

File tree

20 files changed

+491
-231
lines changed

20 files changed

+491
-231
lines changed

cdn-kt/pom.xml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,11 @@
4444
<artifactId>kotlin-stdlib-jdk8</artifactId>
4545
<version>${kotlin.version}</version>
4646
</dependency>
47+
<dependency>
48+
<groupId>org.jetbrains.kotlin</groupId>
49+
<artifactId>kotlin-reflect</artifactId>
50+
<version>${kotlin.version}</version>
51+
</dependency>
4752

4853
<!-- Tests -->
4954
<dependency>

cdn-kt/src/main/kotlin/net/dzikoysk/cdn/KCdn.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,9 +22,9 @@ import kotlin.reflect.KClass
2222

2323
class KCdn(private val cdn: Cdn) {
2424

25-
fun parse(source: String): KConfiguration = KConfiguration(cdn.load(source))
25+
fun parse(source: String): KConfiguration = KConfiguration(cdn.load { source } )
2626

27-
fun <T : Any> parse(scheme: KClass<T>, source: String): T = cdn.load(source, scheme.java)
27+
fun <T : Any> parse(scheme: KClass<T>, source: String): T = cdn.load( { source } , scheme.java)
2828

2929
inline fun <reified T : Any> parseAs(source: String): T = this.parse(T::class, source)
3030

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
package net.dzikoysk.cdn
2+
3+
import net.dzikoysk.cdn.annotation.KotlinMemberResolver
4+
5+
fun CdnSettings.registerKotlinModule() : CdnSettings {
6+
this.withAnnotationResolver(KotlinMemberResolver())
7+
return this
8+
}
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
package net.dzikoysk.cdn.annotation
2+
3+
import panda.std.stream.PandaStream
4+
import java.lang.reflect.AnnotatedType
5+
import kotlin.reflect.KFunction
6+
import kotlin.reflect.jvm.*
7+
8+
class KFunctionMember(
9+
private val instance: Any,
10+
private val getter: KFunction<*>,
11+
private val setter: KFunction<*>,
12+
private val propertyName: String
13+
) : AnnotatedMember {
14+
15+
override fun setValue(value: Any?) {
16+
setter.call(instance, value)
17+
}
18+
19+
override fun getValue(): Any? {
20+
return getter.call(instance)
21+
}
22+
23+
override fun isAnnotationPresent(annotation: Class<out Annotation>?): Boolean {
24+
return getter.annotations
25+
.map { it.javaClass }
26+
.contains(annotation)
27+
}
28+
29+
override fun <A : Annotation?> getAnnotationsByType(annotation: Class<A>?): MutableList<A>? {
30+
return PandaStream.of(getter.annotations)
31+
.`is`(annotation)
32+
.toList()
33+
}
34+
35+
override fun <A : Annotation?> getAnnotation(annotation: Class<A>?): A? {
36+
return PandaStream.of(getter.annotations)
37+
.`is`(annotation)
38+
.head()
39+
.orNull
40+
}
41+
42+
override fun getAnnotatedType(): AnnotatedType? = getter.javaMethod?.annotatedReturnType
43+
44+
override fun getType(): Class<*>? = getter.javaMethod?.returnType
45+
46+
override fun getName(): String = propertyName
47+
48+
override fun getInstance(): Any = instance
49+
50+
}
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
package net.dzikoysk.cdn.annotation
2+
3+
import panda.std.stream.PandaStream
4+
import java.lang.reflect.AnnotatedType
5+
import kotlin.reflect.KMutableProperty
6+
import kotlin.reflect.jvm.javaField
7+
import kotlin.reflect.jvm.javaGetter
8+
9+
class KPropertyMember(private val instance: Any, private val property: KMutableProperty<*>) : AnnotatedMember {
10+
11+
override fun setValue(value: Any?) {
12+
property.setter.call(instance, value)
13+
}
14+
15+
override fun getValue(): Any? {
16+
return property.getter.call(instance)
17+
}
18+
19+
override fun isAnnotationPresent(annotation: Class<out Annotation>?): Boolean {
20+
return property.annotations
21+
.map { it.javaClass }
22+
.contains(annotation)
23+
}
24+
25+
override fun <A : Annotation?> getAnnotationsByType(annotation: Class<A>?): MutableList<A>? {
26+
return PandaStream.of(property.javaField?.annotations)
27+
.flatMap { it.toList() }
28+
.`is`(annotation)
29+
.toList()
30+
}
31+
32+
override fun <A : Annotation?> getAnnotation(annotation: Class<A>?): A? {
33+
return PandaStream.of(property.javaField?.annotations)
34+
.flatMap { it.toList() }
35+
.`is`(annotation)
36+
.head()
37+
.orNull
38+
}
39+
40+
override fun getAnnotatedType(): AnnotatedType? = property.javaGetter?.annotatedReturnType
41+
42+
override fun getType(): Class<*>? = property.javaField?.type
43+
44+
override fun getName(): String = property.name
45+
46+
override fun getInstance(): Any = instance
47+
48+
}
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
package net.dzikoysk.cdn.annotation
2+
3+
import panda.utilities.StringUtils
4+
import java.lang.reflect.Field
5+
import kotlin.reflect.KMutableProperty
6+
import kotlin.reflect.full.functions
7+
import kotlin.reflect.full.memberProperties
8+
9+
class KotlinMemberResolver : MemberResolver {
10+
11+
override fun fromField(instance: Any, field: Field): AnnotatedMember {
12+
return FieldMember(instance, field)
13+
}
14+
15+
override fun fromProperty(instance: Any, propertyName: String) : AnnotatedMember {
16+
val find = instance::class.memberProperties.find { it.name == propertyName }
17+
18+
if (find !is KMutableProperty<*>) {
19+
val getter = instance::class.functions.find { it.name == "get" + StringUtils.capitalize(propertyName) }
20+
val setter = instance::class.functions.find { it.name == "set" + StringUtils.capitalize(propertyName) }
21+
22+
if (getter == null || setter == null) {
23+
throw NoSuchMethodException()
24+
}
25+
26+
return KFunctionMember(instance, getter, setter, propertyName)
27+
}
28+
29+
return KPropertyMember(instance, find)
30+
}
31+
32+
override fun getProperties(instance: Any): List<AnnotatedMember> {
33+
return instance::class.memberProperties
34+
.filterIsInstance<KMutableProperty<*>>()
35+
.map { KPropertyMember(instance, it) }
36+
}
37+
38+
}

cdn-kt/src/test/kotlin/net/dzikoysk/cdn/kt/KotlinConfiguration.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ import net.dzikoysk.cdn.entity.Description
2020

2121
class KotlinConfiguration {
2222

23-
@get:Description("# Description")
23+
@Description("# Description")
2424
var key = "value"
2525

2626
}

cdn-kt/src/test/kotlin/net/dzikoysk/cdn/kt/KotlinReaderTest.kt

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,20 +17,24 @@
1717
package net.dzikoysk.cdn.kt
1818

1919
import net.dzikoysk.cdn.CdnFactory
20+
import net.dzikoysk.cdn.registerKotlinModule
2021
import org.junit.jupiter.api.Assertions.assertEquals
2122
import org.junit.jupiter.api.Test
2223

2324
class KotlinReaderTest {
2425

2526
@Test
2627
fun `should load configuration in kotlin`() {
27-
val configuration = CdnFactory.createStandard().load("key: custom", KotlinConfiguration::class.java)
28-
assertEquals("custom", configuration.key)
28+
val configuration = CdnFactory.createStandard().settings
29+
.registerKotlinModule()
30+
.build()
31+
.load( { "key: custom" }, KotlinConfiguration::class.java)
2932

33+
assertEquals("custom", configuration.key)
3034
assertEquals("""
3135
# Description
3236
key: custom
33-
""".trimIndent(), CdnFactory.createStandard().render(configuration))
37+
""".trimIndent(), CdnFactory.createStandard().settings.registerKotlinModule().build().render(configuration))
3438
}
3539

3640
}

cdn/src/main/java/net/dzikoysk/cdn/CdnDeserializer.java

Lines changed: 6 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -16,16 +16,16 @@
1616

1717
package net.dzikoysk.cdn;
1818

19+
import net.dzikoysk.cdn.annotation.AnnotatedMember;
1920
import net.dzikoysk.cdn.entity.Contextual;
2021
import net.dzikoysk.cdn.entity.DeserializationHandler;
2122
import net.dzikoysk.cdn.model.Element;
2223
import net.dzikoysk.cdn.model.Section;
2324
import net.dzikoysk.cdn.serialization.Deserializer;
24-
import net.dzikoysk.cdn.shared.AnnotatedMember;
25-
import net.dzikoysk.cdn.shared.AnnotatedMember.FieldMember;
26-
import net.dzikoysk.cdn.shared.AnnotatedMember.MethodMember;
2725
import panda.std.Option;
2826
import panda.utilities.ObjectUtils;
27+
import panda.utilities.StringUtils;
28+
2929
import java.lang.reflect.Field;
3030
import java.lang.reflect.Method;
3131

@@ -57,35 +57,16 @@ private Object deserializeToSection(Section source, Object instance) throws Refl
5757
deserializeField(source, instance, field);
5858
}
5959

60-
for (Method method : instance.getClass().getMethods()) {
61-
deserializeMethod(source, instance, method);
60+
for (AnnotatedMember annotatedMember : settings.getAnnotationResolver().getProperties(instance)) {
61+
deserializeMember(source, annotatedMember);
6262
}
6363

6464
return instance;
6565
}
6666

6767
private void deserializeField(Section source, Object instance, Field field) throws ReflectiveOperationException {
6868
if (!CdnUtils.isIgnored(field)) {
69-
deserializeMember(source, new FieldMember(instance, field));
70-
}
71-
}
72-
73-
private void deserializeMethod(Section source, Object instance, Method setter) throws ReflectiveOperationException {
74-
try {
75-
if (!setter.getName().startsWith("set")) {
76-
return;
77-
}
78-
79-
Method getter = instance.getClass().getMethod("get" + setter.getName().substring(3));
80-
81-
if (CdnUtils.isIgnored(getter)) {
82-
return;
83-
}
84-
85-
deserializeMember(source, new MethodMember(instance, setter, getter));
86-
}
87-
catch (NoSuchMethodException ignored) {
88-
// cannot set this property, ignore
69+
deserializeMember(source, settings.getAnnotationResolver().fromField(instance, field));
8970
}
9071
}
9172

cdn/src/main/java/net/dzikoysk/cdn/CdnSerializer.java

Lines changed: 10 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -21,14 +21,14 @@
2121
import net.dzikoysk.cdn.model.Configuration;
2222
import net.dzikoysk.cdn.model.Section;
2323
import net.dzikoysk.cdn.serialization.Serializer;
24-
import net.dzikoysk.cdn.shared.AnnotatedMember;
25-
import net.dzikoysk.cdn.shared.AnnotatedMember.FieldMember;
26-
import net.dzikoysk.cdn.shared.AnnotatedMember.MethodMember;
24+
import net.dzikoysk.cdn.annotation.AnnotatedMember;
25+
import panda.std.stream.PandaStream;
26+
import panda.utilities.StringUtils;
27+
2728
import java.lang.reflect.Field;
2829
import java.lang.reflect.Method;
2930
import java.util.Arrays;
3031
import java.util.List;
31-
import java.util.stream.Collectors;
3232

3333
public final class CdnSerializer {
3434

@@ -54,42 +54,24 @@ public <S extends Section> S serialize(Object entity, S output) throws Reflectiv
5454
serializeField(entity, field, output);
5555
}
5656

57-
for (Method method : template.getMethods()) {
58-
serializeMethod(entity, method, output);
57+
for (AnnotatedMember annotatedMember : settings.getAnnotationResolver().getProperties(entity)) {
58+
serializeMember(annotatedMember, output);
5959
}
6060

6161
return output;
6262
}
6363

6464
private void serializeField(Object entity, Field field, Section output) throws ReflectiveOperationException {
6565
if (!CdnUtils.isIgnored(field)) {
66-
serializeMember(new FieldMember(entity, field), output);
67-
}
68-
}
69-
70-
private void serializeMethod(Object entity, Method getter, Section output) throws ReflectiveOperationException {
71-
if (CdnUtils.isIgnored(getter)) {
72-
return;
73-
}
74-
75-
try {
76-
if (!getter.getName().startsWith("get")) {
77-
return;
78-
}
79-
80-
Method setter = entity.getClass().getMethod("set" + getter.getName().substring(3), getter.getReturnType());
81-
serializeMember(new MethodMember(entity, setter, getter), output);
82-
}
83-
catch (NoSuchMethodException ignored) {
84-
// cannot set this property, ignore
66+
serializeMember(settings.getAnnotationResolver().fromField(entity, field), output);
8567
}
8668
}
8769

8870
private void serializeMember(AnnotatedMember member, Section output) throws ReflectiveOperationException {
8971
Object propertyValue = member.getValue();
90-
List<String> description = Arrays.stream(member.getAnnotationsByType(Description.class))
91-
.flatMap(annotation -> Arrays.stream(annotation.value()))
92-
.collect(Collectors.toList());
72+
List<String> description = PandaStream.of(member.getAnnotationsByType(Description.class))
73+
.flatMap(annotation -> Arrays.asList(annotation.value()))
74+
.toList();
9375

9476
if (member.isAnnotationPresent(Contextual.class)) {
9577
Section section = new Section(description, CdnConstants.OBJECT_SEPARATOR, member.getName());

0 commit comments

Comments
 (0)