Skip to content

Commit 1373c75

Browse files
committed
add Value Creators
1 parent 49ff7a2 commit 1373c75

File tree

3 files changed

+122
-0
lines changed

3 files changed

+122
-0
lines changed
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
package com.fasterxml.jackson.module.kotlin
2+
3+
import kotlin.reflect.KFunction
4+
import kotlin.reflect.jvm.isAccessible
5+
6+
internal class ConstructorValueCreator<T>(override val callable: KFunction<T>) : ValueCreator<T>() {
7+
override val accessible: Boolean = callable.isAccessible
8+
9+
init {
10+
callable.isAccessible = true
11+
}
12+
}
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
package com.fasterxml.jackson.module.kotlin
2+
3+
import kotlin.reflect.KFunction
4+
import kotlin.reflect.KParameter
5+
import kotlin.reflect.full.extensionReceiverParameter
6+
import kotlin.reflect.full.instanceParameter
7+
import kotlin.reflect.jvm.isAccessible
8+
9+
internal class MethodValueCreator<T> private constructor(
10+
override val callable: KFunction<T>,
11+
override val accessible: Boolean,
12+
val companionObjectInstance: Any
13+
) : ValueCreator<T>() {
14+
val instanceParameter: KParameter = callable.instanceParameter!!
15+
16+
companion object {
17+
fun <T> of(callable: KFunction<T>): MethodValueCreator<T>? {
18+
// we shouldn't have an instance or receiver parameter and if we do, just go with default Java-ish behavior
19+
if (callable.extensionReceiverParameter != null) return null
20+
21+
val possibleCompanion = callable.instanceParameter!!.type.erasedType().kotlin
22+
23+
// abort, we have some unknown case here
24+
if (!possibleCompanion.isCompanion) return null
25+
26+
val (companionObjectInstance: Any, accessible: Boolean) = try {
27+
// throws ex
28+
val instance = possibleCompanion.objectInstance!!
29+
// If an instance of the companion object can be obtained, accessibility depends on the KFunction
30+
instance to callable.isAccessible
31+
} catch (ex: IllegalAccessException) {
32+
// fallback for when an odd access exception happens through Kotlin reflection
33+
possibleCompanion.java.enclosingClass.fields
34+
.firstOrNull { it.type.kotlin.isCompanion }
35+
?.let {
36+
it.isAccessible = true
37+
38+
// If the instance of the companion object cannot be obtained, accessibility will always be false
39+
it.get(null) to false
40+
} ?: throw ex
41+
}
42+
43+
return MethodValueCreator(callable, accessible, companionObjectInstance)
44+
}
45+
}
46+
}
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
package com.fasterxml.jackson.module.kotlin
2+
3+
import com.fasterxml.jackson.databind.DeserializationContext
4+
import com.fasterxml.jackson.databind.MapperFeature
5+
import com.fasterxml.jackson.databind.introspect.AnnotatedConstructor
6+
import com.fasterxml.jackson.databind.introspect.AnnotatedMethod
7+
import com.fasterxml.jackson.databind.introspect.AnnotatedWithParams
8+
import java.lang.reflect.Constructor
9+
import java.lang.reflect.Method
10+
import kotlin.reflect.KFunction
11+
import kotlin.reflect.KParameter
12+
import kotlin.reflect.full.valueParameters
13+
14+
/**
15+
* A class that abstracts the creation of instances by calling KFunction.
16+
* @see KotlinValueInstantiator
17+
*/
18+
internal sealed class ValueCreator<T> {
19+
/**
20+
* Function to be call.
21+
*/
22+
protected abstract val callable: KFunction<T>
23+
24+
/**
25+
* Initial value for accessibility by reflection.
26+
*/
27+
protected abstract val accessible: Boolean
28+
29+
/**
30+
* ValueParameters of the KFunction to be called.
31+
*/
32+
val valueParameters: List<KParameter> by lazy { callable.valueParameters }
33+
34+
/**
35+
* Checking process to see if access from context is possible.
36+
* @throws IllegalAccessException
37+
*/
38+
fun checkAccessibility(ctxt: DeserializationContext) {
39+
if ((!accessible && ctxt.config.isEnabled(MapperFeature.CAN_OVERRIDE_ACCESS_MODIFIERS)) ||
40+
(accessible && ctxt.config.isEnabled(MapperFeature.OVERRIDE_PUBLIC_ACCESS_MODIFIERS))) {
41+
return
42+
}
43+
44+
throw IllegalAccessException("Cannot access to function or companion object instance, target: $callable")
45+
}
46+
47+
/**
48+
* Function call with default values enabled.
49+
*/
50+
fun callBy(args: Map<KParameter, Any?>): T = callable.callBy(args)
51+
52+
companion object {
53+
@Suppress("UNCHECKED_CAST")
54+
fun of(
55+
_withArgsCreator: AnnotatedWithParams, cache: ReflectionCache
56+
): ValueCreator<*>? = when (_withArgsCreator) {
57+
is AnnotatedConstructor -> cache.kotlinFromJava(_withArgsCreator.annotated as Constructor<Any>)
58+
?.let { ConstructorValueCreator(it) }
59+
is AnnotatedMethod -> cache.kotlinFromJava(_withArgsCreator.annotated as Method)
60+
?.let { MethodValueCreator.of(it) }
61+
else -> throw IllegalStateException("Expected a constructor or method to create a Kotlin object, instead found ${_withArgsCreator.annotated.javaClass.name}")
62+
} // we cannot reflect this method so do the default Java-ish behavior
63+
}
64+
}

0 commit comments

Comments
 (0)