Skip to content

Commit f0920c6

Browse files
committed
Migrated to implement findDefaultCreator
1 parent da5657d commit f0920c6

File tree

4 files changed

+70
-78
lines changed

4 files changed

+70
-78
lines changed

src/main/kotlin/io/github/projectmapk/jackson/module/kogera/annotationIntrospector/KotlinFallbackAnnotationIntrospector.kt

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package io.github.projectmapk.jackson.module.kogera.annotationIntrospector
22

3+
import com.fasterxml.jackson.annotation.JsonProperty
34
import com.fasterxml.jackson.annotation.JsonSetter
45
import com.fasterxml.jackson.annotation.Nulls
56
import com.fasterxml.jackson.databind.JavaType
@@ -11,11 +12,14 @@ import com.fasterxml.jackson.databind.introspect.AnnotatedMember
1112
import com.fasterxml.jackson.databind.introspect.AnnotatedMethod
1213
import com.fasterxml.jackson.databind.introspect.AnnotatedParameter
1314
import com.fasterxml.jackson.databind.introspect.NopAnnotationIntrospector
15+
import com.fasterxml.jackson.databind.introspect.PotentialCreator
1416
import com.fasterxml.jackson.databind.util.Converter
1517
import io.github.projectmapk.jackson.module.kogera.JSON_K_UNBOX_CLASS
1618
import io.github.projectmapk.jackson.module.kogera.KOTLIN_DURATION_CLASS
1719
import io.github.projectmapk.jackson.module.kogera.ReflectionCache
1820
import io.github.projectmapk.jackson.module.kogera.isUnboxableValueClass
21+
import io.github.projectmapk.jackson.module.kogera.jmClass.JmClass
22+
import io.github.projectmapk.jackson.module.kogera.jmClass.JmConstructor
1923
import io.github.projectmapk.jackson.module.kogera.jmClass.JmValueParameter
2024
import io.github.projectmapk.jackson.module.kogera.ser.KotlinDurationValueToJavaDurationConverter
2125
import io.github.projectmapk.jackson.module.kogera.ser.KotlinToJavaDurationConverter
@@ -120,6 +124,38 @@ internal class KotlinFallbackAnnotationIntrospector(
120124
}
121125
}
122126
?: super.findSetterInfo(ann)
127+
128+
// If it is not a Kotlin class or an Enum, Creator is not used
129+
private fun AnnotatedClass.creatableKotlinClass(): JmClass? = annotated
130+
.takeIf { !it.isEnum }
131+
?.let { cache.getJmClass(it) }
132+
133+
override fun findDefaultCreator(
134+
config: MapperConfig<*>,
135+
valueClass: AnnotatedClass,
136+
declaredConstructors: List<PotentialCreator>,
137+
declaredFactories: List<PotentialCreator>
138+
): PotentialCreator? {
139+
val jmClass = valueClass.creatableKotlinClass() ?: return null
140+
val primarilyConstructor = jmClass.primarilyConstructor()
141+
?.takeIf { it.valueParameters.isNotEmpty() }
142+
?: return null
143+
val isPossiblySingleString = isPossiblySingleString(primarilyConstructor, jmClass)
144+
145+
for (it in declaredConstructors) {
146+
val javaConstructor = it.creator().annotated as Constructor<*>
147+
148+
if (primarilyConstructor.isMetadataFor(javaConstructor)) {
149+
if (isPossibleSingleString(isPossiblySingleString, javaConstructor)) {
150+
break
151+
} else {
152+
return it
153+
}
154+
}
155+
}
156+
157+
return null
158+
}
123159
}
124160

125161
private fun JmValueParameter.isNullishTypeAt(index: Int): Boolean = arguments.getOrNull(index)?.let {
@@ -130,3 +166,15 @@ private fun JmValueParameter.isNullishTypeAt(index: Int): Boolean = arguments.ge
130166
private fun JmValueParameter.requireStrictNullCheck(type: JavaType): Boolean =
131167
((type.isArrayType || type.isCollectionLikeType) && !this.isNullishTypeAt(0)) ||
132168
(type.isMapLikeType && !this.isNullishTypeAt(1))
169+
170+
private fun JmClass.primarilyConstructor() = constructors.find { !it.isSecondary } ?: constructors.singleOrNull()
171+
172+
private fun isPossiblySingleString(
173+
jmConstructor: JmConstructor,
174+
jmClass: JmClass
175+
) = jmConstructor.valueParameters.singleOrNull()?.let { it.isString && it.name !in jmClass.propertyNameSet } == true
176+
177+
private fun isPossibleSingleString(
178+
isPossiblySingleString: Boolean,
179+
javaConstructor: Constructor<*>
180+
): Boolean = isPossiblySingleString && javaConstructor.parameters[0].annotations.none { it is JsonProperty }

src/main/kotlin/io/github/projectmapk/jackson/module/kogera/annotationIntrospector/KotlinPrimaryAnnotationIntrospector.kt

Lines changed: 0 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,7 @@
11
package io.github.projectmapk.jackson.module.kogera.annotationIntrospector
22

3-
import com.fasterxml.jackson.annotation.JsonCreator
4-
import com.fasterxml.jackson.annotation.JsonProperty
53
import com.fasterxml.jackson.databind.JavaType
6-
import com.fasterxml.jackson.databind.cfg.MapperConfig
74
import com.fasterxml.jackson.databind.introspect.Annotated
8-
import com.fasterxml.jackson.databind.introspect.AnnotatedConstructor
95
import com.fasterxml.jackson.databind.introspect.AnnotatedField
106
import com.fasterxml.jackson.databind.introspect.AnnotatedMember
117
import com.fasterxml.jackson.databind.introspect.AnnotatedMethod
@@ -14,17 +10,13 @@ import com.fasterxml.jackson.databind.introspect.NopAnnotationIntrospector
1410
import com.fasterxml.jackson.databind.jsontype.NamedType
1511
import io.github.projectmapk.jackson.module.kogera.JSON_PROPERTY_CLASS
1612
import io.github.projectmapk.jackson.module.kogera.ReflectionCache
17-
import io.github.projectmapk.jackson.module.kogera.hasCreatorAnnotation
1813
import io.github.projectmapk.jackson.module.kogera.jmClass.JmClass
1914
import io.github.projectmapk.jackson.module.kogera.jmClass.JmProperty
20-
import io.github.projectmapk.jackson.module.kogera.jmClass.JmValueParameter
2115
import io.github.projectmapk.jackson.module.kogera.reconstructClass
2216
import io.github.projectmapk.jackson.module.kogera.toSignature
2317
import kotlinx.metadata.isNullable
2418
import java.lang.reflect.Constructor
25-
import java.lang.reflect.Executable
2619
import java.lang.reflect.Method
27-
import java.lang.reflect.Modifier
2820

2921
// AnnotationIntrospector that overrides the behavior of the default AnnotationIntrospector
3022
// (in most cases, JacksonAnnotationIntrospector).
@@ -115,54 +107,4 @@ internal class KotlinPrimaryAnnotationIntrospector(
115107
override fun findSubtypes(a: Annotated): List<NamedType>? = cache.getJmClass(a.rawType)?.let { jmClass ->
116108
jmClass.sealedSubclasses.map { NamedType(it.reconstructClass()) }.ifEmpty { null }
117109
}
118-
119-
// Return Mode.DEFAULT if ann is a Primary Constructor and the condition is satisfied.
120-
// Currently, there is no way to define the priority of a Creator,
121-
// so the presence or absence of a JsonCreator is included in the decision.
122-
// The reason for overriding the JacksonAnnotationIntrospector is to reduce overhead.
123-
// In rare cases, a problem may occur,
124-
// but it is assumed that the problem can be solved by adjusting the order of module registration.
125-
override fun findCreatorAnnotation(config: MapperConfig<*>, ann: Annotated): JsonCreator.Mode? {
126-
(ann as? AnnotatedConstructor)?.takeIf { 0 < it.parameterCount } ?: return null
127-
128-
val declaringClass = ann.declaringClass
129-
val jmClass = declaringClass.takeIf { !it.isEnum }
130-
?.let { cache.getJmClass(it) }
131-
?: return null
132-
133-
return JsonCreator.Mode.DEFAULT
134-
.takeIf { ann.annotated.isPrimarilyConstructorOf(jmClass) && !hasCreator(declaringClass, jmClass) }
135-
}
136-
}
137-
138-
private fun Constructor<*>.isPrimarilyConstructorOf(jmClass: JmClass): Boolean = jmClass.findJmConstructor(this)
139-
?.let { !it.isSecondary || jmClass.constructors.size == 1 } == true
140-
141-
private fun isPossibleSingleString(
142-
kotlinParams: List<JmValueParameter>,
143-
javaFunction: Executable,
144-
propertyNames: Set<String>
145-
): Boolean = kotlinParams.size == 1 &&
146-
kotlinParams[0].let { it.name !in propertyNames && it.isString } &&
147-
javaFunction.parameters[0].annotations.none { it is JsonProperty }
148-
149-
private fun hasCreatorConstructor(clazz: Class<*>, jmClass: JmClass, propertyNames: Set<String>): Boolean {
150-
val kmConstructorMap = jmClass.constructors.associateBy { it.signature?.descriptor }
151-
152-
return clazz.constructors.any { constructor ->
153-
val kmConstructor = kmConstructorMap[constructor.toSignature().descriptor] ?: return@any false
154-
155-
!isPossibleSingleString(kmConstructor.valueParameters, constructor, propertyNames) &&
156-
constructor.hasCreatorAnnotation()
157-
}
158-
}
159-
160-
// In the original, `isPossibleSingleString` comparison was disabled,
161-
// and if enabled, the behavior would have changed, so the comparison is skipped.
162-
private fun hasCreatorFunction(clazz: Class<*>): Boolean = clazz.declaredMethods
163-
.any { Modifier.isStatic(it.modifiers) && it.hasCreatorAnnotation() }
164-
165-
private fun hasCreator(clazz: Class<*>, jmClass: JmClass): Boolean {
166-
val propertyNames = jmClass.propertyNameSet
167-
return hasCreatorConstructor(clazz, jmClass, propertyNames) || hasCreatorFunction(clazz)
168110
}

src/main/kotlin/io/github/projectmapk/jackson/module/kogera/jmClass/JmClass.kt

Lines changed: 2 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
package io.github.projectmapk.jackson.module.kogera.jmClass
22

33
import io.github.projectmapk.jackson.module.kogera.reconstructClassOrNull
4-
import io.github.projectmapk.jackson.module.kogera.toDescBuilder
54
import io.github.projectmapk.jackson.module.kogera.toKmClass
65
import io.github.projectmapk.jackson.module.kogera.toSignature
76
import kotlinx.metadata.ClassKind
@@ -106,25 +105,8 @@ private class JmClassImpl(
106105
companionPropName?.let { JmClass.CompanionObject(clazz, it) }
107106
}
108107

109-
override fun findJmConstructor(constructor: Constructor<*>): JmConstructor? {
110-
val descHead = constructor.parameterTypes.toDescBuilder()
111-
val len = descHead.length
112-
val desc = CharArray(len + 1).apply {
113-
descHead.getChars(0, len, this, 0)
114-
this[len] = 'V'
115-
}.let { String(it) }
116-
117-
// Only constructors that take a value class as an argument have a DefaultConstructorMarker on the Signature.
118-
val valueDesc = descHead
119-
.replace(len - 1, len, "Lkotlin/jvm/internal/DefaultConstructorMarker;)V")
120-
.toString()
121-
122-
// Constructors always have the same name, so only desc is compared
123-
return constructors.find {
124-
val targetDesc = it.signature?.descriptor
125-
targetDesc == desc || targetDesc == valueDesc
126-
}
127-
}
108+
override fun findJmConstructor(constructor: Constructor<*>): JmConstructor? =
109+
constructors.find { it.isMetadataFor(constructor) }
128110

129111
// Field name always matches property name
130112
override fun findPropertyByField(field: Field): JmProperty? = allPropsMap[field.name]

src/main/kotlin/io/github/projectmapk/jackson/module/kogera/jmClass/JmConstructor.kt

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
11
package io.github.projectmapk.jackson.module.kogera.jmClass
22

3+
import io.github.projectmapk.jackson.module.kogera.toDescBuilder
34
import kotlinx.metadata.KmConstructor
45
import kotlinx.metadata.isSecondary
56
import kotlinx.metadata.jvm.JvmMethodSignature
67
import kotlinx.metadata.jvm.signature
8+
import java.lang.reflect.Constructor
79

810
internal data class JmConstructor(
911
val isSecondary: Boolean,
@@ -15,4 +17,22 @@ internal data class JmConstructor(
1517
signature = constructor.signature,
1618
valueParameters = constructor.valueParameters.map { JmValueParameter(it) }
1719
)
20+
21+
// Only constructors that take a value class as an argument have a DefaultConstructorMarker on the Signature.
22+
private fun StringBuilder.valueDesc(len: Int) =
23+
replace(len - 1, len, "Lkotlin/jvm/internal/DefaultConstructorMarker;)V").toString()
24+
25+
fun isMetadataFor(constructor: Constructor<*>): Boolean {
26+
val targetDesc = signature?.descriptor
27+
28+
val descHead = constructor.parameterTypes.toDescBuilder()
29+
val len = descHead.length
30+
val desc = CharArray(len + 1).apply {
31+
descHead.getChars(0, len, this, 0)
32+
this[len] = 'V'
33+
}.let { String(it) }
34+
35+
// Constructors always have the same name, so only desc is compared
36+
return targetDesc == desc || targetDesc == descHead.valueDesc(len)
37+
}
1838
}

0 commit comments

Comments
 (0)