Skip to content

Commit 8824f7d

Browse files
authored
Merge pull request #510 from k163377/refactor-hasCreatorAnnotation
Refactor KNAI.hasCreatorAnnotation
2 parents 1c112c9 + 78bd9d4 commit 8824f7d

File tree

1 file changed

+45
-52
lines changed

1 file changed

+45
-52
lines changed

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

Lines changed: 45 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -14,12 +14,13 @@ import com.fasterxml.jackson.databind.introspect.NopAnnotationIntrospector
1414
import com.fasterxml.jackson.databind.util.BeanUtil
1515
import java.lang.reflect.Constructor
1616
import java.lang.reflect.Method
17-
import java.util.*
17+
import java.util.Locale
1818
import kotlin.reflect.KClass
1919
import kotlin.reflect.KFunction
2020
import kotlin.reflect.KParameter
2121
import kotlin.reflect.full.companionObject
2222
import kotlin.reflect.full.declaredFunctions
23+
import kotlin.reflect.full.hasAnnotation
2324
import kotlin.reflect.full.memberProperties
2425
import kotlin.reflect.full.primaryConstructor
2526
import kotlin.reflect.jvm.internal.KotlinReflectionInternalError
@@ -59,68 +60,44 @@ internal class KotlinNamesAnnotationIntrospector(val module: KotlinModule, val c
5960
}
6061

6162
@Suppress("UNCHECKED_CAST")
62-
override fun hasCreatorAnnotation(member: Annotated): Boolean {
63+
private fun hasCreatorAnnotation(member: AnnotatedConstructor): Boolean {
6364
// don't add a JsonCreator to any constructor if one is declared already
6465

65-
if (member is AnnotatedConstructor && !member.declaringClass.isEnum) {
66-
// if has parameters, is a Kotlin class, and the parameters all have parameter annotations, then pretend we have a JsonCreator
67-
if (member.parameterCount > 0 && member.declaringClass.isKotlinClass()) {
68-
return cache.checkConstructorIsCreatorAnnotated(member) {
69-
val kClass = cache.kotlinFromJava(member.declaringClass as Class<Any>)
70-
val kConstructor = cache.kotlinFromJava(member.annotated as Constructor<Any>)
66+
val kClass = cache.kotlinFromJava(member.declaringClass as Class<Any>)
67+
.apply { if (this in ignoredClassesForImplyingJsonCreator) return false }
68+
val kConstructor = cache.kotlinFromJava(member.annotated as Constructor<Any>) ?: return false
7169

72-
if (kConstructor != null) {
73-
val isPrimaryConstructor = kClass.primaryConstructor == kConstructor ||
74-
(kClass.primaryConstructor == null && kClass.constructors.size == 1)
70+
// TODO: should we do this check or not? It could cause failures if we miss another way a property could be set
71+
// val requiredProperties = kClass.declaredMemberProperties.filter {!it.returnType.isMarkedNullable }.map { it.name }.toSet()
72+
// val areAllRequiredParametersInConstructor = kConstructor.parameters.all { requiredProperties.contains(it.name) }
7573

76-
val propertyNames = kClass.memberProperties.map { it.name }.toSet()
74+
val propertyNames = kClass.memberProperties.map { it.name }.toSet()
7775

78-
fun KFunction<*>.isPossibleSingleString(): Boolean {
79-
val result = parameters.size == 1 &&
80-
parameters[0].name !in propertyNames &&
81-
parameters[0].type.javaType == String::class.java &&
82-
parameters[0].annotations.none { it.annotationClass.java == JsonProperty::class.java }
83-
return result
84-
}
76+
return when {
77+
kConstructor.isPossibleSingleString(propertyNames) -> false
78+
kConstructor.parameters.any { it.name == null } -> false
79+
!kClass.isPrimaryConstructor(kConstructor) -> false
80+
else -> {
81+
val anyConstructorHasJsonCreator = kClass.constructors
82+
.filterOutSingleStringCallables(propertyNames)
83+
.any { it.hasAnnotation<JsonCreator>() }
8584

86-
fun Collection<KFunction<*>>.filterOutSingleStringCallables(): Collection<KFunction<*>> {
87-
return this.filter { !it.isPossibleSingleString() }
88-
}
85+
val anyCompanionMethodIsJsonCreator = member.type.rawClass.kotlin.companionObject?.declaredFunctions
86+
?.filterOutSingleStringCallables(propertyNames)
87+
?.any { it.hasAnnotation<JsonCreator>() && it.hasAnnotation<JvmStatic>() }
88+
?: false
8989

90-
val anyConstructorHasJsonCreator = kClass.constructors.filterOutSingleStringCallables()
91-
.any { it.annotations.any { it.annotationClass.java == JsonCreator::class.java }
92-
}
93-
94-
val anyCompanionMethodIsJsonCreator = member.type.rawClass.kotlin.companionObject?.declaredFunctions
95-
?.filterOutSingleStringCallables()?.any {
96-
it.annotations.any { it.annotationClass.java == JvmStatic::class.java } &&
97-
it.annotations.any { it.annotationClass.java == JsonCreator::class.java }
98-
} ?: false
99-
100-
// TODO: should we do this check or not? It could cause failures if we miss another way a property could be set
101-
// val requiredProperties = kClass.declaredMemberProperties.filter {!it.returnType.isMarkedNullable }.map { it.name }.toSet()
102-
// val areAllRequiredParametersInConstructor = kConstructor.parameters.all { requiredProperties.contains(it.name) }
103-
104-
val areAllParametersValid = kConstructor.parameters.size == kConstructor.parameters.count { it.name != null }
105-
106-
val isSingleStringConstructor = kConstructor.isPossibleSingleString()
107-
108-
val implyCreatorAnnotation = isPrimaryConstructor
109-
&& !(anyConstructorHasJsonCreator || anyCompanionMethodIsJsonCreator)
110-
&& areAllParametersValid
111-
&& !isSingleStringConstructor
112-
&& kClass !in ignoredClassesForImplyingJsonCreator
113-
114-
implyCreatorAnnotation
115-
} else {
116-
false
117-
}
118-
}
90+
!(anyConstructorHasJsonCreator || anyCompanionMethodIsJsonCreator)
11991
}
12092
}
121-
return false
12293
}
12394

95+
override fun hasCreatorAnnotation(member: Annotated): Boolean =
96+
if (member is AnnotatedConstructor && member.isKotlinConstructorWithParameters())
97+
cache.checkConstructorIsCreatorAnnotated(member) { hasCreatorAnnotation(it) }
98+
else
99+
false
100+
124101
@Suppress("UNCHECKED_CAST")
125102
private fun findKotlinParameterName(param: AnnotatedParameter): String? {
126103
return if (param.declaringClass.isKotlinClass()) {
@@ -165,3 +142,19 @@ internal class KotlinNamesAnnotationIntrospector(val module: KotlinModule, val c
165142
ReplaceWith("with(receiver) { declaringClass.declaredMethods.any { it.name.contains('-') } }")
166143
)
167144
private fun AnnotatedMethod.isInlineClass() = declaringClass.declaredMethods.any { it.name.contains('-') }
145+
146+
// if has parameters, is a Kotlin class, and the parameters all have parameter annotations, then pretend we have a JsonCreator
147+
private fun AnnotatedConstructor.isKotlinConstructorWithParameters(): Boolean =
148+
parameterCount > 0 && declaringClass.isKotlinClass() && !declaringClass.isEnum
149+
150+
private fun KFunction<*>.isPossibleSingleString(propertyNames: Set<String>): Boolean = parameters.size == 1 &&
151+
parameters[0].name !in propertyNames &&
152+
parameters[0].type.javaType == String::class.java &&
153+
!parameters[0].hasAnnotation<JsonProperty>()
154+
155+
private fun Collection<KFunction<*>>.filterOutSingleStringCallables(propertyNames: Set<String>): Collection<KFunction<*>> =
156+
this.filter { !it.isPossibleSingleString(propertyNames) }
157+
158+
private fun KClass<*>.isPrimaryConstructor(kConstructor: KFunction<*>) = this.primaryConstructor.let {
159+
it == kConstructor || (it == null && this.constructors.size == 1)
160+
}

0 commit comments

Comments
 (0)