11package tools.jackson.module.kotlin
22
3- import com.fasterxml.jackson.annotation.JsonProperty
4- import com.fasterxml.jackson.annotation.OptBoolean
5- import tools.jackson.databind.DeserializationFeature
6- import tools.jackson.databind.JacksonModule
73import tools.jackson.databind.cfg.MapperConfig
84import tools.jackson.databind.introspect.Annotated
95import tools.jackson.databind.introspect.AnnotatedClass
10- import tools.jackson.databind.introspect.AnnotatedField
11- import tools.jackson.databind.introspect.AnnotatedMember
126import tools.jackson.databind.introspect.AnnotatedMethod
13- import tools.jackson.databind.introspect.AnnotatedParameter
147import tools.jackson.databind.introspect.NopAnnotationIntrospector
158import tools.jackson.databind.jsontype.NamedType
169import tools.jackson.databind.util.Converter
17- import java.lang.reflect.AccessibleObject
18- import java.lang.reflect.Field
19- import java.lang.reflect.Method
20- import kotlin.reflect.KFunction
21- import kotlin.reflect.KMutableProperty1
22- import kotlin.reflect.KParameter
23- import kotlin.reflect.KProperty1
24- import kotlin.reflect.KType
25- import kotlin.reflect.full.createType
26- import kotlin.reflect.full.declaredMemberProperties
27- import kotlin.reflect.full.valueParameters
28- import kotlin.reflect.jvm.javaGetter
29- import kotlin.reflect.jvm.javaSetter
30- import kotlin.reflect.jvm.javaType
31- import kotlin.reflect.jvm.kotlinProperty
3210import kotlin.time.Duration
3311
3412internal class KotlinAnnotationIntrospector (
35- private val context : JacksonModule .SetupContext ,
3613 private val cache : ReflectionCache ,
37- private val nullToEmptyCollection : Boolean ,
38- private val nullToEmptyMap : Boolean ,
39- private val nullIsSameAsDefault : Boolean ,
4014 private val useJavaDurationConversion : Boolean ,
4115) : NopAnnotationIntrospector() {
4216
43- // TODO: implement nullIsSameAsDefault flag, which represents when TRUE that if something has a default value, it can be passed a null to default it
44- // this likely impacts this class to be accurate about what COULD be considered required
45-
46- // If a new isRequired is explicitly specified or the old required is true, those values take precedence.
47- // In other cases, override is done by KotlinModule.
48- private fun JsonProperty.forceRequiredByAnnotation (): Boolean? = when {
49- isRequired != OptBoolean .DEFAULT -> isRequired.asBoolean()
50- required -> true
51- else -> null
52- }
53-
54- private fun AccessibleObject.forceRequiredByAnnotation (): Boolean? =
55- getAnnotation(JsonProperty ::class .java)?.forceRequiredByAnnotation()
56-
57- override fun hasRequiredMarker (
58- cfg : MapperConfig <* >,
59- m : AnnotatedMember
60- ): Boolean? = m.takeIf { it.member.declaringClass.isKotlinClass() }?.let { _ ->
61- cache.javaMemberIsRequired(m) {
62- try {
63- when (m) {
64- is AnnotatedField -> m.hasRequiredMarker()
65- is AnnotatedMethod -> m.hasRequiredMarker()
66- is AnnotatedParameter -> m.hasRequiredMarker()
67- else -> null
68- }
69- } catch (_: UnsupportedOperationException ) {
70- null
71- }
72- }
73- }
74-
7517 override fun findSerializationConverter (config : MapperConfig <* >? , a : Annotated ): Converter <* , * >? = when (a) {
7618 // Find a converter to handle the case where the getter returns an unboxed value from the value class.
7719 is AnnotatedMethod -> a.findValueClassReturnType()?.let {
@@ -105,7 +47,7 @@ internal class KotlinAnnotationIntrospector(
10547 * Subclasses can be detected automatically for sealed classes, since all possible subclasses are known
10648 * at compile-time to Kotlin. This makes [com.fasterxml.jackson.annotation.JsonSubTypes] redundant.
10749 */
108- override fun findSubtypes (cfg : MapperConfig <* >, a : Annotated ): MutableList <NamedType >? = a.rawType
50+ override fun findSubtypes (cfg : MapperConfig <* >, a : Annotated ): MutableList <NamedType >? = a.rawType
10951 .takeIf { it.isKotlinClass() }
11052 ?.let { rawType ->
11153 rawType.kotlin.sealedSubclasses
@@ -114,70 +56,5 @@ internal class KotlinAnnotationIntrospector(
11456 .ifEmpty { null }
11557 }
11658
117- private fun AnnotatedField.hasRequiredMarker (): Boolean? {
118- val field = member as Field
119- return field.forceRequiredByAnnotation()
120- ? : field.kotlinProperty?.returnType?.isRequired()
121- }
122-
123- // Since Kotlin's property has the same Type for each field, getter, and setter,
124- // nullability can be determined from the returnType of KProperty.
125- private fun KProperty1 <* , * >.isRequiredByNullability () = returnType.isRequired()
126-
127- // This could be a setter or a getter of a class property or
128- // a setter-like/getter-like method.
129- private fun AnnotatedMethod.hasRequiredMarker (): Boolean? = this .getRequiredMarkerFromCorrespondingAccessor()
130- ? : this .member.getRequiredMarkerFromAccessorLikeMethod()
131-
132- private fun AnnotatedMethod.getRequiredMarkerFromCorrespondingAccessor (): Boolean? {
133- member.declaringClass.kotlin.declaredMemberProperties.forEach { kProperty ->
134- if (kProperty.javaGetter == this .member || (kProperty as ? KMutableProperty1 )?.javaSetter == this .member) {
135- return member.forceRequiredByAnnotation() ? : kProperty.isRequiredByNullability()
136- }
137- }
138- return null
139- }
140-
141- // Is the member method a regular method of the data class or
142- private fun Method.getRequiredMarkerFromAccessorLikeMethod (): Boolean? = cache.kotlinFromJava(this )?.let { func ->
143- forceRequiredByAnnotation() ? : when {
144- func.isGetterLike() -> func.returnType.isRequired()
145- // If nullToEmpty could be supported for setters,
146- // a branch similar to AnnotatedParameter.hasRequiredMarker should be added.
147- func.isSetterLike() -> func.valueParameters[0 ].isRequired()
148- else -> null
149- }
150- }
151-
152- private fun KFunction <* >.isGetterLike (): Boolean = parameters.size == 1
153- private fun KFunction <* >.isSetterLike (): Boolean = parameters.size == 2 && returnType == UNIT_TYPE
154-
155- private fun AnnotatedParameter.hasRequiredMarker (): Boolean? = getAnnotation(JsonProperty ::class .java)
156- ?.forceRequiredByAnnotation()
157- ? : run {
158- when {
159- nullToEmptyCollection && type.isCollectionLikeType -> false
160- nullToEmptyMap && type.isMapLikeType -> false
161- else -> cache.findKotlinParameter(this )?.isRequired()
162- }
163- }
164-
16559 private fun AnnotatedMethod.findValueClassReturnType () = cache.findValueClassReturnType(this )
166-
167- private fun KParameter.isRequired (): Boolean {
168- val paramType = type
169- val isPrimitive = when (val javaType = paramType.javaType) {
170- is Class <* > -> javaType.isPrimitive
171- else -> false
172- }
173-
174- return ! paramType.isMarkedNullable && ! isOptional && ! isVararg &&
175- ! (isPrimitive && ! context.isEnabled(DeserializationFeature .FAIL_ON_NULL_FOR_PRIMITIVES ))
176- }
177-
178- private fun KType.isRequired (): Boolean = ! isMarkedNullable
179-
180- companion object {
181- val UNIT_TYPE : KType by lazy { Unit ::class .createType() }
182- }
18360}
0 commit comments