@@ -3,32 +3,127 @@ package tools.jackson.module.kotlin
3
3
import com.fasterxml.jackson.annotation.JsonProperty
4
4
import com.fasterxml.jackson.annotation.JsonSetter
5
5
import com.fasterxml.jackson.annotation.Nulls
6
+ import tools.jackson.databind.DeserializationFeature
7
+ import tools.jackson.databind.JacksonModule
6
8
import tools.jackson.databind.JavaType
7
9
import tools.jackson.databind.cfg.MapperConfig
8
10
import tools.jackson.databind.introspect.Annotated
9
11
import tools.jackson.databind.introspect.AnnotatedClass
12
+ import tools.jackson.databind.introspect.AnnotatedField
10
13
import tools.jackson.databind.introspect.AnnotatedMember
11
14
import tools.jackson.databind.introspect.AnnotatedMethod
12
15
import tools.jackson.databind.introspect.AnnotatedParameter
13
16
import tools.jackson.databind.introspect.NopAnnotationIntrospector
14
17
import tools.jackson.databind.introspect.PotentialCreator
15
18
import java.lang.reflect.Constructor
19
+ import java.lang.reflect.Field
20
+ import java.lang.reflect.Method
16
21
import java.util.Locale
17
22
import kotlin.collections.getOrNull
18
23
import kotlin.reflect.KClass
19
24
import kotlin.reflect.KFunction
25
+ import kotlin.reflect.KMutableProperty1
20
26
import kotlin.reflect.KParameter
27
+ import kotlin.reflect.KProperty1
28
+ import kotlin.reflect.KType
29
+ import kotlin.reflect.full.createType
30
+ import kotlin.reflect.full.declaredMemberProperties
21
31
import kotlin.reflect.full.hasAnnotation
22
32
import kotlin.reflect.full.memberProperties
23
33
import kotlin.reflect.full.primaryConstructor
34
+ import kotlin.reflect.full.valueParameters
24
35
import kotlin.reflect.jvm.javaGetter
36
+ import kotlin.reflect.jvm.javaSetter
25
37
import kotlin.reflect.jvm.javaType
38
+ import kotlin.reflect.jvm.kotlinProperty
26
39
27
40
internal class KotlinNamesAnnotationIntrospector (
41
+ private val context : JacksonModule .SetupContext ,
28
42
private val cache : ReflectionCache ,
43
+ private val nullToEmptyCollection : Boolean ,
44
+ private val nullToEmptyMap : Boolean ,
45
+ private val nullIsSameAsDefault : Boolean ,
29
46
private val strictNullChecks : Boolean ,
30
47
private val kotlinPropertyNameAsImplicitName : Boolean
31
48
) : NopAnnotationIntrospector() {
49
+ private fun KType.isRequired (): Boolean = ! isMarkedNullable
50
+
51
+ // Since Kotlin's property has the same Type for each field, getter, and setter,
52
+ // nullability can be determined from the returnType of KProperty.
53
+ private fun KProperty1 <* , * >.isRequiredByNullability () = returnType.isRequired()
54
+
55
+ private fun KParameter.isRequired (): Boolean {
56
+ val paramType = type
57
+ val isPrimitive = when (val javaType = paramType.javaType) {
58
+ is Class <* > -> javaType.isPrimitive
59
+ else -> false
60
+ }
61
+
62
+ return ! paramType.isMarkedNullable && ! isOptional && ! isVararg &&
63
+ ! (isPrimitive && ! context.isEnabled(DeserializationFeature .FAIL_ON_NULL_FOR_PRIMITIVES ))
64
+ }
65
+
66
+ private fun AnnotatedField.hasRequiredMarker (): Boolean? {
67
+ val field = member as Field
68
+ return field.kotlinProperty?.returnType?.isRequired()
69
+ }
70
+
71
+ private fun KFunction <* >.isGetterLike (): Boolean = parameters.size == 1
72
+ private fun KFunction <* >.isSetterLike (): Boolean = parameters.size == 2 && returnType == UNIT_TYPE
73
+
74
+ private fun AnnotatedMethod.getRequiredMarkerFromCorrespondingAccessor (): Boolean? {
75
+ member.declaringClass.kotlin.declaredMemberProperties.forEach { kProperty ->
76
+ if (kProperty.javaGetter == this .member || (kProperty as ? KMutableProperty1 )?.javaSetter == this .member) {
77
+ return kProperty.isRequiredByNullability()
78
+ }
79
+ }
80
+ return null
81
+ }
82
+
83
+ // Is the member method a regular method of the data class or
84
+ private fun Method.getRequiredMarkerFromAccessorLikeMethod (): Boolean? = cache.kotlinFromJava(this )?.let { func ->
85
+ when {
86
+ func.isGetterLike() -> func.returnType.isRequired()
87
+ // If nullToEmpty could be supported for setters,
88
+ // a branch similar to AnnotatedParameter.hasRequiredMarker should be added.
89
+ func.isSetterLike() -> func.valueParameters[0 ].isRequired()
90
+ else -> null
91
+ }
92
+ }
93
+
94
+ // This could be a setter or a getter of a class property or
95
+ // a setter-like/getter-like method.
96
+ private fun AnnotatedMethod.hasRequiredMarker (): Boolean? = this .getRequiredMarkerFromCorrespondingAccessor()
97
+ ? : this .member.getRequiredMarkerFromAccessorLikeMethod()
98
+
99
+ // TODO: implement nullIsSameAsDefault flag, which represents when TRUE that if something has a default value, it can be passed a null to default it
100
+ // this likely impacts this class to be accurate about what COULD be considered required
101
+ private fun AnnotatedParameter.hasRequiredMarker (): Boolean? = when {
102
+ nullToEmptyCollection && type.isCollectionLikeType -> false
103
+ nullToEmptyMap && type.isMapLikeType -> false
104
+ else -> cache.findKotlinParameter(this )?.isRequired()
105
+ }
106
+
107
+ override fun hasRequiredMarker (
108
+ cfg : MapperConfig <* >,
109
+ m : AnnotatedMember
110
+ ): Boolean? = m.takeIf { it.member.declaringClass.isKotlinClass() }?.let { _ ->
111
+ println (m)
112
+
113
+ cache.javaMemberIsRequired(m) {
114
+ try {
115
+ when (m) {
116
+ is AnnotatedField -> m.hasRequiredMarker()
117
+ is AnnotatedMethod -> m.hasRequiredMarker()
118
+ is AnnotatedParameter -> m.hasRequiredMarker()
119
+ else -> null
120
+ }
121
+ } catch (_: UnsupportedOperationException ) {
122
+ null
123
+ }
124
+ }
125
+ }
126
+
32
127
private fun getterNameFromJava (member : AnnotatedMethod ): String? {
33
128
val name = member.name
34
129
@@ -123,6 +218,10 @@ internal class KotlinNamesAnnotationIntrospector(
123
218
124
219
private fun findKotlinParameter (param : Annotated ) = (param as ? AnnotatedParameter )
125
220
?.let { cache.findKotlinParameter(it) }
221
+
222
+ companion object {
223
+ val UNIT_TYPE : KType by lazy { Unit ::class .createType() }
224
+ }
126
225
}
127
226
128
227
private fun KParameter.markedNonNullAt (index : Int ) = type.arguments.getOrNull(index)?.type?.isMarkedNullable == false
0 commit comments