|
1 | 1 | package com.wrongwrong.mapk.core |
2 | 2 |
|
3 | 3 | import com.wrongwrong.mapk.annotations.KConstructor |
| 4 | +import com.wrongwrong.mapk.annotations.KGetterAlias |
4 | 5 | import com.wrongwrong.mapk.annotations.KPropertyAlias |
5 | 6 | import com.wrongwrong.mapk.annotations.KPropertyIgnore |
| 7 | +import java.lang.reflect.Method |
6 | 8 | import kotlin.reflect.KClass |
7 | 9 | import kotlin.reflect.KFunction |
8 | | -import kotlin.reflect.KProperty1 |
| 10 | +import kotlin.reflect.KParameter |
9 | 11 | import kotlin.reflect.KVisibility |
10 | 12 | import kotlin.reflect.full.companionObjectInstance |
| 13 | +import kotlin.reflect.full.findAnnotation |
11 | 14 | import kotlin.reflect.full.functions |
12 | 15 | import kotlin.reflect.full.isSuperclassOf |
13 | 16 | import kotlin.reflect.full.memberProperties |
14 | 17 | import kotlin.reflect.full.primaryConstructor |
15 | | -import kotlin.reflect.jvm.isAccessible |
| 18 | +import kotlin.reflect.jvm.javaGetter |
| 19 | + |
| 20 | +class KMapper<T : Any> private constructor( |
| 21 | + private val function: KFunctionForCall<T>, |
| 22 | + propertyNameConverter: (String) -> String = { it } |
| 23 | +) { |
| 24 | + constructor(function: KFunction<T>, propertyNameConverter: (String) -> String = { it }) : this( |
| 25 | + KFunctionForCall(function), propertyNameConverter |
| 26 | + ) |
16 | 27 |
|
17 | | -class KMapper<T : Any>(private val function: KFunction<T>, propertyNameConverter: (String) -> String = { it }) { |
18 | 28 | constructor(clazz: KClass<T>, propertyNameConverter: (String) -> String = { it }) : this( |
19 | 29 | getTarget(clazz), propertyNameConverter |
20 | 30 | ) |
21 | 31 |
|
22 | | - private val parameters: Set<ParameterForMap<*>> = function.parameters |
23 | | - .map { ParameterForMap.newInstance(it, propertyNameConverter) } |
24 | | - .toSet() |
| 32 | + private val parameterMap: Map<String, ParameterForMap<*>> = function.parameters |
| 33 | + .filter { it.kind != KParameter.Kind.INSTANCE } |
| 34 | + .associate { |
| 35 | + (it.findAnnotation<KPropertyAlias>()?.value ?: propertyNameConverter(it.name!!)) to |
| 36 | + ParameterForMap.newInstance(it) |
| 37 | + } |
25 | 38 |
|
26 | 39 | init { |
27 | | - if (parameters.isEmpty()) throw IllegalArgumentException("This function is not require arguments.") |
28 | | - |
29 | | - // private関数に対してもマッピングできなければ何かと不都合があるため、accessibleは書き換える |
30 | | - function.isAccessible = true |
| 40 | + if (parameterMap.isEmpty()) throw IllegalArgumentException("This function is not require arguments.") |
31 | 41 | } |
32 | 42 |
|
33 | | - fun map(srcMap: Map<String, Any?>): T { |
34 | | - return parameters.associate { |
35 | | - // 取得した内容がnullでなければ適切にmapする |
36 | | - it.param to srcMap.getValue(it.name)?.let { value -> |
37 | | - mapObject(it, value) |
| 43 | + private fun bindParameters(targetArray: Array<Any?>, src: Any) { |
| 44 | + src::class.memberProperties.forEach { property -> |
| 45 | + val javaGetter: Method? = property.javaGetter |
| 46 | + if (javaGetter != null && property.visibility == KVisibility.PUBLIC && property.annotations.none { annotation -> annotation is KPropertyIgnore }) { |
| 47 | + parameterMap[property.findAnnotation<KGetterAlias>()?.value ?: property.name]?.let { |
| 48 | + // javaGetterを呼び出す方が高速 |
| 49 | + javaGetter.isAccessible = true |
| 50 | + targetArray[it.index] = javaGetter.invoke(src)?.let { value -> mapObject(it, value) } |
| 51 | + } |
38 | 52 | } |
39 | | - }.let { function.callBy(it) } |
| 53 | + } |
40 | 54 | } |
41 | 55 |
|
42 | | - fun map(srcPair: Pair<String, Any?>): T = parameters |
43 | | - .single { it.name == srcPair.first } |
44 | | - .let { |
45 | | - function.callBy(mapOf(it.param to srcPair.second?.let { value -> mapObject(it, value) })) |
| 56 | + private fun bindParameters(targetArray: Array<Any?>, src: Map<*, *>) { |
| 57 | + src.forEach { (key, value) -> |
| 58 | + parameterMap[key]?.let { param -> |
| 59 | + // 取得した内容がnullでなければ適切にmapする |
| 60 | + targetArray[param.index] = value?.let { mapObject(param, it) } |
| 61 | + } |
46 | 62 | } |
| 63 | + } |
47 | 64 |
|
48 | | - fun map(src: Any): T { |
49 | | - val srcMap: Map<String, KProperty1.Getter<*, *>> = |
50 | | - src::class.memberProperties.filterTargets().associate { property -> |
51 | | - val getter = property.getAccessibleGetter() |
| 65 | + private fun bindParameters(targetArray: Array<Any?>, srcPair: Pair<*, *>) { |
| 66 | + parameterMap.getValue(srcPair.first.toString()).let { |
| 67 | + targetArray[it.index] = srcPair.second?.let { value -> mapObject(it, value) } |
| 68 | + } |
| 69 | + } |
52 | 70 |
|
53 | | - val key = getter.annotations |
54 | | - .find { it is KPropertyAlias } |
55 | | - ?.let { (it as KPropertyAlias).value } |
56 | | - ?: property.name |
| 71 | + fun map(srcMap: Map<String, Any?>): T { |
| 72 | + val array: Array<Any?> = function.argumentArray |
| 73 | + bindParameters(array, srcMap) |
| 74 | + return function.call(array) |
| 75 | + } |
57 | 76 |
|
58 | | - key to getter |
59 | | - } |
| 77 | + fun map(srcPair: Pair<String, Any?>): T { |
| 78 | + val array: Array<Any?> = function.argumentArray |
| 79 | + bindParameters(array, srcPair) |
| 80 | + return function.call(array) |
| 81 | + } |
60 | 82 |
|
61 | | - return parameters.associate { |
62 | | - // 取得した内容がnullでなければ適切にmapする |
63 | | - it.param to srcMap.getValue(it.name).call(src)?.let { value -> |
64 | | - mapObject(it, value) |
65 | | - } |
66 | | - }.let { function.callBy(it) } |
| 83 | + fun map(src: Any): T { |
| 84 | + val array: Array<Any?> = function.argumentArray |
| 85 | + bindParameters(array, src) |
| 86 | + return function.call(array) |
67 | 87 | } |
68 | 88 |
|
69 | 89 | fun map(vararg args: Any): T { |
70 | | - val srcMap: Map<String, () -> Any?> = listOf(*args) |
71 | | - .map { arg -> |
72 | | - when (arg) { |
73 | | - is Map<*, *> -> arg.entries.associate { (key, value) -> |
74 | | - (key as String) to { value } |
75 | | - } |
76 | | - is Pair<*, *> -> mapOf(arg.first as String to { arg.second }) |
77 | | - else -> { |
78 | | - arg::class.memberProperties.filterTargets().associate { property -> |
79 | | - val getter = property.getAccessibleGetter() |
80 | | - |
81 | | - val key = getter.annotations |
82 | | - .find { it is KPropertyAlias } |
83 | | - ?.let { (it as KPropertyAlias).value } |
84 | | - ?: property.name |
85 | | - |
86 | | - key to { getter.call(arg) } |
87 | | - } |
88 | | - } |
89 | | - } |
90 | | - }.reduce { acc, map -> |
91 | | - acc + map |
92 | | - } |
| 90 | + val array: Array<Any?> = function.argumentArray |
93 | 91 |
|
94 | | - return parameters.associate { |
95 | | - // 取得した内容がnullでなければ適切にmapする |
96 | | - it.param to srcMap.getValue(it.name)()?.let { value -> |
97 | | - mapObject(it, value) |
| 92 | + listOf(*args).forEach { arg -> |
| 93 | + when (arg) { |
| 94 | + is Map<*, *> -> bindParameters(array, arg) |
| 95 | + is Pair<*, *> -> bindParameters(array, arg) |
| 96 | + else -> bindParameters(array, arg) |
98 | 97 | } |
99 | | - }.let { function.callBy(it) } |
100 | | - } |
101 | | -} |
| 98 | + } |
102 | 99 |
|
103 | | -private fun Collection<KProperty1<*, *>>.filterTargets(): Collection<KProperty1<*, *>> { |
104 | | - return filter { |
105 | | - it.visibility == KVisibility.PUBLIC && it.annotations.none { annotation -> annotation is KPropertyIgnore } |
| 100 | + return function.call(array) |
106 | 101 | } |
107 | 102 | } |
108 | 103 |
|
109 | | -private fun KProperty1<*, *>.getAccessibleGetter(): KProperty1.Getter<*, *> { |
110 | | - // アクセス制限の有るクラスではpublicなプロパティでもゲッターにアクセスできない場合が有るため、アクセス可能にして使う |
111 | | - getter.isAccessible = true |
112 | | - return getter |
113 | | -} |
114 | | - |
115 | 104 | @Suppress("UNCHECKED_CAST") |
116 | | -internal fun <T : Any> getTarget(clazz: KClass<T>): KFunction<T> { |
117 | | - val factoryConstructor: List<KFunction<T>> = |
| 105 | +internal fun <T : Any> getTarget(clazz: KClass<T>): KFunctionForCall<T> { |
| 106 | + val factoryConstructor: List<KFunctionForCall<T>> = |
118 | 107 | clazz.companionObjectInstance?.let { companionObject -> |
119 | 108 | companionObject::class.functions |
120 | 109 | .filter { it.annotations.any { annotation -> annotation is KConstructor } } |
121 | | - .map { KFunctionWithInstance(it, companionObject) as KFunction<T> } |
| 110 | + .map { KFunctionForCall(it, companionObject) as KFunctionForCall<T> } |
122 | 111 | } ?: emptyList() |
123 | 112 |
|
124 | | - val constructors: List<KFunction<T>> = factoryConstructor + clazz.constructors |
| 113 | + val constructors: List<KFunctionForCall<T>> = factoryConstructor + clazz.constructors |
125 | 114 | .filter { it.annotations.any { annotation -> annotation is KConstructor } } |
| 115 | + .map { KFunctionForCall(it) } |
126 | 116 |
|
127 | 117 | if (constructors.size == 1) return constructors.single() |
128 | 118 |
|
129 | | - if (constructors.isEmpty()) return clazz.primaryConstructor!! |
| 119 | + if (constructors.isEmpty()) return KFunctionForCall(clazz.primaryConstructor!!) |
130 | 120 |
|
131 | 121 | throw IllegalArgumentException("Find multiple target.") |
132 | 122 | } |
|
0 commit comments