Skip to content
This repository was archived by the owner on Jan 20, 2023. It is now read-only.

Commit a751222

Browse files
authored
Merge pull request #30 from k163377/feature
Support for mapping to nested classes.
2 parents 082edec + a2336c0 commit a751222

File tree

9 files changed

+152
-134
lines changed

9 files changed

+152
-134
lines changed

build.gradle.kts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ plugins {
66
}
77

88
group = "com.mapk"
9-
version = "0.25"
9+
version = "0.26"
1010

1111
java {
1212
sourceCompatibility = JavaVersion.VERSION_1_8
@@ -30,7 +30,7 @@ repositories {
3030
dependencies {
3131
implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8")
3232
implementation(kotlin("reflect"))
33-
api("com.github.ProjectMapK:Shared:0.11")
33+
api("com.github.ProjectMapK:Shared:0.12")
3434

3535
// https://mvnrepository.com/artifact/org.junit.jupiter/junit-jupiter
3636
testImplementation(group = "org.junit.jupiter", name = "junit-jupiter", version = "5.6.2") {

src/main/kotlin/com/mapk/kmapper/BoundKMapper.kt

Lines changed: 15 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,11 @@ package com.mapk.kmapper
22

33
import com.mapk.annotations.KGetterAlias
44
import com.mapk.annotations.KGetterIgnore
5-
import com.mapk.core.ArgumentBucket
65
import com.mapk.core.KFunctionForCall
7-
import com.mapk.core.getAliasOrName
8-
import com.mapk.core.isUseDefaultArgument
96
import com.mapk.core.toKConstructor
107
import java.lang.IllegalArgumentException
118
import kotlin.reflect.KClass
129
import kotlin.reflect.KFunction
13-
import kotlin.reflect.KParameter
1410
import kotlin.reflect.KProperty1
1511
import kotlin.reflect.KVisibility
1612
import kotlin.reflect.full.findAnnotation
@@ -23,47 +19,45 @@ class BoundKMapper<S : Any, D : Any> private constructor(
2319
parameterNameConverter: (String) -> String
2420
) {
2521
constructor(function: KFunction<D>, src: KClass<S>, parameterNameConverter: (String) -> String = { it }) : this(
26-
KFunctionForCall(function), src, parameterNameConverter
22+
KFunctionForCall(function, parameterNameConverter), src, parameterNameConverter
2723
)
2824

2925
constructor(clazz: KClass<D>, src: KClass<S>, parameterNameConverter: (String) -> String = { it }) : this(
30-
clazz.toKConstructor(), src, parameterNameConverter
26+
clazz.toKConstructor(parameterNameConverter), src, parameterNameConverter
3127
)
3228

3329
private val parameters: List<BoundParameterForMap<S>>
3430

3531
init {
36-
val srcPropertiesMap: Map<String, KProperty1<S, *>> =
37-
src.memberProperties
38-
.filter {
39-
// アクセス可能かつignoreされてないもののみ抽出
40-
!(it.visibility != KVisibility.PUBLIC) &&
41-
it.getter.annotations.none { annotation -> annotation is KGetterIgnore }
42-
}.associateBy { it.getter.findAnnotation<KGetterAlias>()?.value ?: it.name }
43-
44-
parameters = function.parameters
45-
.filter { it.kind != KParameter.Kind.INSTANCE && !it.isUseDefaultArgument() }
32+
val srcPropertiesMap: Map<String, KProperty1<S, *>> = src.memberProperties
33+
.filter {
34+
// アクセス可能かつignoreされてないもののみ抽出
35+
!(it.visibility != KVisibility.PUBLIC) &&
36+
it.getter.annotations.none { annotation -> annotation is KGetterIgnore }
37+
}.associateBy { it.getter.findAnnotation<KGetterAlias>()?.value ?: it.name }
38+
39+
parameters = function.requiredParameters
4640
.mapNotNull {
47-
val temp = srcPropertiesMap[parameterNameConverter(it.getAliasOrName()!!)]?.let { property ->
41+
val temp = srcPropertiesMap[it.name]?.let { property ->
4842
BoundParameterForMap.newInstance(it, property, parameterNameConverter)
4943
}
5044

5145
// 必須引数に対応するプロパティがsrcに定義されていない場合エラー
5246
if (temp == null && !it.isOptional) {
53-
throw IllegalArgumentException("Property ${it.name!!} is not declared in ${src.jvmName}.")
47+
throw IllegalArgumentException("Property ${it.name} is not declared in ${src.jvmName}.")
5448
}
5549

5650
temp
5751
}
5852
}
5953

6054
fun map(src: S): D {
61-
val bucket: ArgumentBucket = function.getArgumentBucket()
55+
val adaptor = function.getArgumentAdaptor()
6256

6357
parameters.forEach {
64-
bucket.putIfAbsent(it.param, it.map(src))
58+
adaptor.putIfAbsent(it.name, it.map(src))
6559
}
6660

67-
return function.call(bucket)
61+
return function.call(adaptor)
6862
}
6963
}

src/main/kotlin/com/mapk/kmapper/BoundParameterForMap.kt

Lines changed: 16 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,39 +1,39 @@
11
package com.mapk.kmapper
22

33
import com.mapk.core.EnumMapper
4+
import com.mapk.core.ValueParameter
45
import java.lang.IllegalArgumentException
56
import java.lang.reflect.Method
67
import kotlin.reflect.KClass
78
import kotlin.reflect.KFunction
8-
import kotlin.reflect.KParameter
99
import kotlin.reflect.KProperty1
1010
import kotlin.reflect.full.isSubclassOf
1111
import kotlin.reflect.jvm.javaGetter
1212

1313
@Suppress("UNCHECKED_CAST")
1414
internal sealed class BoundParameterForMap<S> {
15-
abstract val param: KParameter
15+
abstract val name: String
1616
protected abstract val propertyGetter: Method
1717

1818
abstract fun map(src: S): Any?
1919

2020
private class Plain<S : Any>(
21-
override val param: KParameter,
21+
override val name: String,
2222
override val propertyGetter: Method
2323
) : BoundParameterForMap<S>() {
2424
override fun map(src: S): Any? = propertyGetter.invoke(src)
2525
}
2626

2727
private class UseConverter<S : Any>(
28-
override val param: KParameter,
28+
override val name: String,
2929
override val propertyGetter: Method,
3030
private val converter: KFunction<*>
3131
) : BoundParameterForMap<S>() {
3232
override fun map(src: S): Any? = converter.call(propertyGetter.invoke(src))
3333
}
3434

3535
private class UseKMapper<S : Any>(
36-
override val param: KParameter,
36+
override val name: String,
3737
override val propertyGetter: Method,
3838
private val kMapper: KMapper<*>
3939
) : BoundParameterForMap<S>() {
@@ -42,31 +42,31 @@ internal sealed class BoundParameterForMap<S> {
4242
}
4343

4444
private class UseBoundKMapper<S : Any, T : Any>(
45-
override val param: KParameter,
45+
override val name: String,
4646
override val propertyGetter: Method,
4747
private val boundKMapper: BoundKMapper<T, *>
4848
) : BoundParameterForMap<S>() {
4949
override fun map(src: S): Any? = boundKMapper.map(propertyGetter.invoke(src) as T)
5050
}
5151

5252
private class ToEnum<S : Any>(
53-
override val param: KParameter,
53+
override val name: String,
5454
override val propertyGetter: Method,
5555
private val paramClazz: Class<*>
5656
) : BoundParameterForMap<S>() {
5757
override fun map(src: S): Any? = EnumMapper.getEnum(paramClazz, propertyGetter.invoke(src) as String)
5858
}
5959

6060
private class ToString<S : Any>(
61-
override val param: KParameter,
61+
override val name: String,
6262
override val propertyGetter: Method
6363
) : BoundParameterForMap<S>() {
6464
override fun map(src: S): String? = propertyGetter.invoke(src).toString()
6565
}
6666

6767
companion object {
6868
fun <S : Any> newInstance(
69-
param: KParameter,
69+
param: ValueParameter<*>,
7070
property: KProperty1<S, *>,
7171
parameterNameConverter: (String) -> String
7272
): BoundParameterForMap<S> {
@@ -75,7 +75,7 @@ internal sealed class BoundParameterForMap<S> {
7575
?: throw IllegalArgumentException("${property.name} does not have getter.")
7676
propertyGetter.isAccessible = true
7777

78-
val paramClazz = param.type.classifier as KClass<*>
78+
val paramClazz = param.requiredClazz
7979
val propertyClazz = property.returnType.classifier as KClass<*>
8080

8181
// コンバータが取れた場合
@@ -86,25 +86,25 @@ internal sealed class BoundParameterForMap<S> {
8686

8787
it.singleOrNull()?.second
8888
}?.let {
89-
return UseConverter(param, propertyGetter, it)
89+
return UseConverter(param.name, propertyGetter, it)
9090
}
9191

9292
if (paramClazz.isSubclassOf(propertyClazz)) {
93-
return Plain(param, propertyGetter)
93+
return Plain(param.name, propertyGetter)
9494
}
9595

9696
val javaClazz = paramClazz.java
9797

9898
return when {
99-
javaClazz.isEnum && propertyClazz == String::class -> ToEnum(param, propertyGetter, javaClazz)
100-
paramClazz == String::class -> ToString(param, propertyGetter)
99+
javaClazz.isEnum && propertyClazz == String::class -> ToEnum(param.name, propertyGetter, javaClazz)
100+
paramClazz == String::class -> ToString(param.name, propertyGetter)
101101
// SrcがMapやPairならKMapperを使わないとマップできない
102102
propertyClazz.isSubclassOf(Map::class) || propertyClazz.isSubclassOf(Pair::class) -> UseKMapper(
103-
param, propertyGetter, KMapper(paramClazz, parameterNameConverter)
103+
param.name, propertyGetter, KMapper(paramClazz, parameterNameConverter)
104104
)
105105
// 何にも当てはまらなければBoundKMapperでマップを試みる
106106
else -> UseBoundKMapper(
107-
param, propertyGetter, BoundKMapper(paramClazz, propertyClazz, parameterNameConverter)
107+
param.name, propertyGetter, BoundKMapper(paramClazz, propertyClazz, parameterNameConverter)
108108
)
109109
}
110110
}

src/main/kotlin/com/mapk/kmapper/KMapper.kt

Lines changed: 34 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -2,17 +2,14 @@ package com.mapk.kmapper
22

33
import com.mapk.annotations.KGetterAlias
44
import com.mapk.annotations.KGetterIgnore
5-
import com.mapk.core.ArgumentBucket
5+
import com.mapk.core.ArgumentAdaptor
66
import com.mapk.core.KFunctionForCall
7-
import com.mapk.core.getAliasOrName
8-
import com.mapk.core.isUseDefaultArgument
97
import com.mapk.core.toKConstructor
108
import java.lang.reflect.Method
119
import java.util.concurrent.ConcurrentHashMap
1210
import java.util.concurrent.ConcurrentMap
1311
import kotlin.reflect.KClass
1412
import kotlin.reflect.KFunction
15-
import kotlin.reflect.KParameter
1613
import kotlin.reflect.KVisibility
1714
import kotlin.reflect.full.memberProperties
1815
import kotlin.reflect.jvm.javaGetter
@@ -22,28 +19,26 @@ class KMapper<T : Any> private constructor(
2219
parameterNameConverter: (String) -> String
2320
) {
2421
constructor(function: KFunction<T>, parameterNameConverter: (String) -> String = { it }) : this(
25-
KFunctionForCall(function), parameterNameConverter
22+
KFunctionForCall(function, parameterNameConverter), parameterNameConverter
2623
)
2724

2825
constructor(clazz: KClass<T>, parameterNameConverter: (String) -> String = { it }) : this(
29-
clazz.toKConstructor(), parameterNameConverter
26+
clazz.toKConstructor(parameterNameConverter), parameterNameConverter
3027
)
3128

32-
private val parameterMap: Map<String, ParameterForMap<*>> = function.parameters
33-
.filter { it.kind != KParameter.Kind.INSTANCE && !it.isUseDefaultArgument() }
34-
.associate {
35-
(parameterNameConverter(it.getAliasOrName()!!)) to ParameterForMap.newInstance(it, parameterNameConverter)
36-
}
29+
private val parameterMap: Map<String, ParameterForMap<*>> = function.requiredParameters.associate {
30+
it.name to ParameterForMap(it, parameterNameConverter)
31+
}
3732

3833
private val getCache: ConcurrentMap<KClass<*>, List<ArgumentBinder>> = ConcurrentHashMap()
3934

40-
private fun bindArguments(argumentBucket: ArgumentBucket, src: Any) {
35+
private fun bindArguments(argumentAdaptor: ArgumentAdaptor, src: Any) {
4136
val clazz = src::class
4237

4338
// キャッシュヒットしたら登録した内容に沿って取得処理を行う
4439
getCache[clazz]?.let { getters ->
4540
// 取得対象フィールドは十分絞り込んでいると考えられるため、終了判定は行わない
46-
getters.forEach { it.bindArgument(src, argumentBucket) }
41+
getters.forEach { it.bindArgument(src, argumentAdaptor) }
4742
return
4843
}
4944

@@ -68,73 +63,75 @@ class KMapper<T : Any> private constructor(
6863

6964
val binder = ArgumentBinder(param, javaGetter)
7065

71-
binder.bindArgument(src, argumentBucket)
66+
binder.bindArgument(src, argumentAdaptor)
7267
tempBinderArrayList.add(binder)
7368
// キャッシュの整合性を保つため、ここでは終了判定を行わない
7469
}
7570
}
7671
getCache.putIfAbsent(clazz, tempBinderArrayList)
7772
}
7873

79-
private fun bindArguments(argumentBucket: ArgumentBucket, src: Map<*, *>) {
74+
private fun bindArguments(argumentAdaptor: ArgumentAdaptor, src: Map<*, *>) {
8075
src.forEach { (key, value) ->
8176
parameterMap[key]?.let { param ->
8277
// 取得した内容がnullでなければ適切にmapする
83-
argumentBucket.putIfAbsent(param.param, value?.let { param.mapObject(value) })
78+
argumentAdaptor.putIfAbsent(param.name, value?.let { param.mapObject(value) })
8479
// 終了判定
85-
if (argumentBucket.isInitialized) return
80+
if (argumentAdaptor.isFullInitialized()) return
8681
}
8782
}
8883
}
8984

90-
private fun bindArguments(argumentBucket: ArgumentBucket, srcPair: Pair<*, *>) {
91-
parameterMap[srcPair.first.toString()]?.let {
92-
argumentBucket.putIfAbsent(it.param, srcPair.second?.let { value -> it.mapObject(value) })
85+
private fun bindArguments(argumentAdaptor: ArgumentAdaptor, srcPair: Pair<*, *>) {
86+
val key = srcPair.first.toString()
87+
88+
parameterMap[key]?.let {
89+
argumentAdaptor.putIfAbsent(key, srcPair.second?.let { value -> it.mapObject(value) })
9390
}
9491
}
9592

9693
fun map(srcMap: Map<String, Any?>): T {
97-
val bucket: ArgumentBucket = function.getArgumentBucket()
98-
bindArguments(bucket, srcMap)
94+
val adaptor: ArgumentAdaptor = function.getArgumentAdaptor()
95+
bindArguments(adaptor, srcMap)
9996

100-
return function.call(bucket)
97+
return function.call(adaptor)
10198
}
10299

103100
fun map(srcPair: Pair<String, Any?>): T {
104-
val bucket: ArgumentBucket = function.getArgumentBucket()
105-
bindArguments(bucket, srcPair)
101+
val adaptor: ArgumentAdaptor = function.getArgumentAdaptor()
102+
bindArguments(adaptor, srcPair)
106103

107-
return function.call(bucket)
104+
return function.call(adaptor)
108105
}
109106

110107
fun map(src: Any): T {
111-
val bucket: ArgumentBucket = function.getArgumentBucket()
112-
bindArguments(bucket, src)
108+
val adaptor: ArgumentAdaptor = function.getArgumentAdaptor()
109+
bindArguments(adaptor, src)
113110

114-
return function.call(bucket)
111+
return function.call(adaptor)
115112
}
116113

117114
fun map(vararg args: Any): T {
118-
val bucket: ArgumentBucket = function.getArgumentBucket()
115+
val adaptor: ArgumentAdaptor = function.getArgumentAdaptor()
119116

120117
listOf(*args).forEach { arg ->
121118
when (arg) {
122-
is Map<*, *> -> bindArguments(bucket, arg)
123-
is Pair<*, *> -> bindArguments(bucket, arg)
124-
else -> bindArguments(bucket, arg)
119+
is Map<*, *> -> bindArguments(adaptor, arg)
120+
is Pair<*, *> -> bindArguments(adaptor, arg)
121+
else -> bindArguments(adaptor, arg)
125122
}
126123
}
127124

128-
return function.call(bucket)
125+
return function.call(adaptor)
129126
}
130127
}
131128

132129
private class ArgumentBinder(private val param: ParameterForMap<*>, private val javaGetter: Method) {
133-
fun bindArgument(src: Any, bucket: ArgumentBucket) {
130+
fun bindArgument(src: Any, adaptor: ArgumentAdaptor) {
134131
// 初期化済みであれば高コストな取得処理は行わない
135-
if (!bucket.containsKey(param.param)) {
132+
if (!adaptor.isInitialized(param.name)) {
136133
// javaGetterを呼び出す方が高速
137-
bucket.putIfAbsent(param.param, javaGetter.invoke(src)?.let { param.mapObject(it) })
134+
adaptor.putIfAbsent(param.name, javaGetter.invoke(src)?.let { param.mapObject(it) })
138135
}
139136
}
140137
}

0 commit comments

Comments
 (0)