Skip to content

Commit 7d90528

Browse files
committed
Optimize the search process for creators
Changed to implement findDefaultCreator. Fixes #805.
1 parent e48cfb4 commit 7d90528

File tree

1 file changed

+28
-47
lines changed

1 file changed

+28
-47
lines changed

src/main/kotlin/com/fasterxml/jackson/module/kotlin/KotlinNamesAnnotationIntrospector.kt

+28-47
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,13 @@ import com.fasterxml.jackson.annotation.JsonProperty
55
import com.fasterxml.jackson.databind.JavaType
66
import com.fasterxml.jackson.databind.cfg.MapperConfig
77
import com.fasterxml.jackson.databind.introspect.Annotated
8+
import com.fasterxml.jackson.databind.introspect.AnnotatedClass
89
import com.fasterxml.jackson.databind.introspect.AnnotatedConstructor
910
import com.fasterxml.jackson.databind.introspect.AnnotatedMember
1011
import com.fasterxml.jackson.databind.introspect.AnnotatedMethod
1112
import com.fasterxml.jackson.databind.introspect.AnnotatedParameter
1213
import com.fasterxml.jackson.databind.introspect.NopAnnotationIntrospector
14+
import com.fasterxml.jackson.databind.introspect.PotentialCreator
1315
import java.util.Locale
1416
import kotlin.reflect.KClass
1517
import kotlin.reflect.KFunction
@@ -18,6 +20,7 @@ import kotlin.reflect.full.declaredFunctions
1820
import kotlin.reflect.full.hasAnnotation
1921
import kotlin.reflect.full.memberProperties
2022
import kotlin.reflect.full.primaryConstructor
23+
import kotlin.reflect.jvm.javaConstructor
2124
import kotlin.reflect.jvm.javaGetter
2225
import kotlin.reflect.jvm.javaType
2326

@@ -84,62 +87,40 @@ internal class KotlinNamesAnnotationIntrospector(
8487
}
8588
} ?: baseType
8689

87-
private fun hasCreatorAnnotation(member: AnnotatedConstructor): Boolean {
88-
// don't add a JsonCreator to any constructor if one is declared already
89-
90-
val kClass = member.declaringClass.kotlin
91-
val kConstructor = cache.kotlinFromJava(member.annotated) ?: return false
92-
93-
// TODO: should we do this check or not? It could cause failures if we miss another way a property could be set
94-
// val requiredProperties = kClass.declaredMemberProperties.filter {!it.returnType.isMarkedNullable }.map { it.name }.toSet()
95-
// val areAllRequiredParametersInConstructor = kConstructor.parameters.all { requiredProperties.contains(it.name) }
90+
override fun findDefaultCreator(
91+
config: MapperConfig<*>,
92+
valueClass: AnnotatedClass,
93+
declaredConstructors: List<PotentialCreator>,
94+
declaredFactories: List<PotentialCreator>
95+
): PotentialCreator? {
96+
val kClass = valueClass.creatableKotlinClass() ?: return null
9697

9798
val propertyNames = kClass.memberProperties.map { it.name }.toSet()
9899

99-
return when {
100-
kConstructor.isPossibleSingleString(propertyNames) -> false
101-
kConstructor.parameters.any { it.name == null } -> false
102-
!kClass.isPrimaryConstructor(kConstructor) -> false
103-
else -> {
104-
val anyConstructorHasJsonCreator = kClass.constructors
105-
.filterOutSingleStringCallables(propertyNames)
106-
.any { it.hasAnnotation<JsonCreator>() }
107-
108-
val anyCompanionMethodIsJsonCreator = member.type.rawClass.kotlin.companionObject?.declaredFunctions
109-
?.filterOutSingleStringCallables(propertyNames)
110-
?.any { it.hasAnnotation<JsonCreator>() && it.hasAnnotation<JvmStatic>() }
111-
?: false
112-
113-
!(anyConstructorHasJsonCreator || anyCompanionMethodIsJsonCreator)
114-
}
100+
val defaultCreator = kClass.let { _ ->
101+
// By default, the primary constructor or the only publicly available constructor may be used
102+
val ctor = kClass.primaryConstructor ?: kClass.constructors.takeIf { it.size == 1 }?.single()
103+
ctor?.takeIf { it.isPossibleCreator(propertyNames) }
115104
}
116-
}
117-
118-
// TODO: possible work around for JsonValue class that requires the class constructor to have the JsonCreator(DELEGATED) set?
119-
// since we infer the creator at times for these methods, the wrong mode could be implied.
120-
override fun findCreatorAnnotation(config: MapperConfig<*>, ann: Annotated): JsonCreator.Mode? {
121-
if (ann !is AnnotatedConstructor || !ann.isKotlinConstructorWithParameters()) return null
105+
?.javaConstructor
106+
?: return null
122107

123-
return JsonCreator.Mode.DEFAULT.takeIf {
124-
cache.checkConstructorIsCreatorAnnotated(ann) { hasCreatorAnnotation(it) }
125-
}
108+
return declaredConstructors.find { it.creator().annotated == defaultCreator }
126109
}
127110

128111
private fun findKotlinParameterName(param: AnnotatedParameter): String? = cache.findKotlinParameter(param)?.name
129112
}
130113

131-
// if has parameters, is a Kotlin class, and the parameters all have parameter annotations, then pretend we have a JsonCreator
132-
private fun AnnotatedConstructor.isKotlinConstructorWithParameters(): Boolean =
133-
parameterCount > 0 && declaringClass.isKotlinClass() && !declaringClass.isEnum
134-
135-
private fun KFunction<*>.isPossibleSingleString(propertyNames: Set<String>): Boolean = parameters.size == 1 &&
136-
parameters[0].name !in propertyNames &&
137-
parameters[0].type.javaType == String::class.java &&
138-
!parameters[0].hasAnnotation<JsonProperty>()
114+
// If it is not a Kotlin class or an Enum, Creator is not used
115+
private fun AnnotatedClass.creatableKotlinClass(): KClass<*>? = annotated
116+
.takeIf { it.isKotlinClass() && !it.isEnum }
117+
?.kotlin
139118

140-
private fun Collection<KFunction<*>>.filterOutSingleStringCallables(propertyNames: Set<String>): Collection<KFunction<*>> =
141-
this.filter { !it.isPossibleSingleString(propertyNames) }
119+
private fun KFunction<*>.isPossibleCreator(propertyNames: Set<String>): Boolean = 0 < parameters.size
120+
&& !isPossibleSingleString(propertyNames)
121+
&& parameters.none { it.name == null }
142122

143-
private fun KClass<*>.isPrimaryConstructor(kConstructor: KFunction<*>) = this.primaryConstructor.let {
144-
it == kConstructor || (it == null && this.constructors.size == 1)
145-
}
123+
private fun KFunction<*>.isPossibleSingleString(propertyNames: Set<String>): Boolean = parameters.size == 1 &&
124+
parameters[0].name !in propertyNames &&
125+
parameters[0].type.javaType == String::class.java &&
126+
!parameters[0].hasAnnotation<JsonProperty>()

0 commit comments

Comments
 (0)