Skip to content

Commit e6451b6

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

File tree

1 file changed

+31
-46
lines changed

1 file changed

+31
-46
lines changed

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

+31-46
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,14 @@ 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
15+
import java.lang.reflect.Constructor
1316
import java.util.Locale
1417
import kotlin.reflect.KClass
1518
import kotlin.reflect.KFunction
@@ -18,6 +21,7 @@ import kotlin.reflect.full.declaredFunctions
1821
import kotlin.reflect.full.hasAnnotation
1922
import kotlin.reflect.full.memberProperties
2023
import kotlin.reflect.full.primaryConstructor
24+
import kotlin.reflect.jvm.javaConstructor
2125
import kotlin.reflect.jvm.javaGetter
2226
import kotlin.reflect.jvm.javaType
2327

@@ -84,62 +88,43 @@ internal class KotlinNamesAnnotationIntrospector(
8488
}
8589
} ?: baseType
8690

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) }
91+
override fun findDefaultCreator(
92+
config: MapperConfig<*>,
93+
valueClass: AnnotatedClass,
94+
declaredConstructors: List<PotentialCreator>,
95+
declaredFactories: List<PotentialCreator>
96+
): PotentialCreator? {
97+
val kClass = valueClass.creatableKotlinClass() ?: return null
9698

9799
val propertyNames = kClass.memberProperties.map { it.name }.toSet()
98100

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-
}
101+
val defaultCreator = kClass.let { _ ->
102+
// By default, the primary constructor or the only publicly available constructor may be used
103+
val ctor = kClass.primaryConstructor ?: kClass.constructors.takeIf { it.size == 1 }?.single()
104+
ctor?.takeIf { it.isPossibleCreator(propertyNames) }
115105
}
116-
}
106+
?: return null
117107

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
122-
123-
return JsonCreator.Mode.DEFAULT.takeIf {
124-
cache.checkConstructorIsCreatorAnnotated(ann) { hasCreatorAnnotation(it) }
108+
return declaredConstructors.find {
109+
// To avoid problems with constructors that include `value class` as an argument,
110+
// convert to `KFunction` and compare
111+
cache.kotlinFromJava(it.creator().annotated as Constructor<*>) == defaultCreator
125112
}
126113
}
127114

128115
private fun findKotlinParameterName(param: AnnotatedParameter): String? = cache.findKotlinParameter(param)?.name
129116
}
130117

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>()
118+
// If it is not a Kotlin class or an Enum, Creator is not used
119+
private fun AnnotatedClass.creatableKotlinClass(): KClass<*>? = annotated
120+
.takeIf { it.isKotlinClass() && !it.isEnum }
121+
?.kotlin
139122

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

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

0 commit comments

Comments
 (0)