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

Commit 7f4ff40

Browse files
authored
Merge pull request #26 from k163377/feature
Improved memory usage efficiency and update libraries.
2 parents 7995a8a + 2345dbb commit 7f4ff40

File tree

7 files changed

+125
-52
lines changed

7 files changed

+125
-52
lines changed

.idea/compiler.xml

Lines changed: 6 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

.idea/misc.xml

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

build.gradle.kts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
plugins {
22
id("maven")
33
id("java")
4-
id("org.jetbrains.kotlin.jvm") version "1.3.71"
4+
id("org.jetbrains.kotlin.jvm") version "1.3.72"
55
id("org.jlleitschuh.gradle.ktlint") version "9.2.1"
66
}
77

88
group = "com.mapk"
9-
version = "0.22"
9+
version = "0.23"
1010

1111
java {
1212
sourceCompatibility = JavaVersion.VERSION_1_8
@@ -18,7 +18,7 @@ buildscript {
1818
}
1919

2020
dependencies {
21-
classpath(kotlin("gradle-plugin", version = "1.3.71"))
21+
classpath(kotlin("gradle-plugin"))
2222
}
2323
}
2424

@@ -30,10 +30,10 @@ repositories {
3030
dependencies {
3131
implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8")
3232
implementation(kotlin("reflect"))
33-
api("com.github.ProjectMapK:Shared:0.10")
33+
api("com.github.ProjectMapK:Shared:0.11")
3434

3535
// https://mvnrepository.com/artifact/org.junit.jupiter/junit-jupiter
36-
testImplementation(group = "org.junit.jupiter", name = "junit-jupiter", version = "5.6.1") {
36+
testImplementation(group = "org.junit.jupiter", name = "junit-jupiter", version = "5.6.2") {
3737
exclude(group = "org.junit.vintage", module = "junit-vintage-engine")
3838
}
3939
// 現状プロパティ名の変換はテストでしか使っていないのでtestImplementation

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ import kotlin.reflect.jvm.jvmName
2020
class BoundKMapper<S : Any, D : Any> private constructor(
2121
private val function: KFunctionForCall<D>,
2222
src: KClass<S>,
23-
parameterNameConverter: (String) -> String = { it }
23+
parameterNameConverter: (String) -> String
2424
) {
2525
constructor(function: KFunction<D>, src: KClass<S>, parameterNameConverter: (String) -> String = { it }) : this(
2626
KFunctionForCall(function), src, parameterNameConverter
@@ -45,7 +45,7 @@ class BoundKMapper<S : Any, D : Any> private constructor(
4545
.filter { it.kind != KParameter.Kind.INSTANCE && !it.isUseDefaultArgument() }
4646
.mapNotNull {
4747
val temp = srcPropertiesMap[parameterNameConverter(it.getAliasOrName()!!)]?.let { property ->
48-
BoundParameterForMap(it, property)
48+
BoundParameterForMap.newInstance(it, property)
4949
}
5050

5151
// 必須引数に対応するプロパティがsrcに定義されていない場合エラー

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

Lines changed: 64 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -2,41 +2,83 @@ package com.mapk.kmapper
22

33
import com.mapk.core.EnumMapper
44
import java.lang.IllegalArgumentException
5+
import java.lang.reflect.Method
56
import kotlin.reflect.KClass
7+
import kotlin.reflect.KFunction
68
import kotlin.reflect.KParameter
79
import kotlin.reflect.KProperty1
810
import kotlin.reflect.full.isSubclassOf
911
import kotlin.reflect.jvm.javaGetter
1012

1113
@Suppress("UNCHECKED_CAST")
12-
internal class BoundParameterForMap<S : Any>(val param: KParameter, property: KProperty1<S, *>) {
13-
val map: (S) -> Any?
14+
internal sealed class BoundParameterForMap<S> {
15+
abstract val param: KParameter
16+
protected abstract val propertyGetter: Method
1417

15-
init {
16-
// ゲッターが無いならエラー
17-
val propertyGetter = property.javaGetter
18-
?: throw IllegalArgumentException("${property.name} does not have getter.")
19-
propertyGetter.isAccessible = true
18+
abstract fun map(src: S): Any?
2019

21-
val paramClazz = param.type.classifier as KClass<*>
22-
val propertyClazz = property.returnType.classifier as KClass<*>
20+
private class Plain<S : Any>(
21+
override val param: KParameter,
22+
override val propertyGetter: Method
23+
) : BoundParameterForMap<S>() {
24+
override fun map(src: S): Any? = propertyGetter.invoke(src)
25+
}
26+
27+
private class UseConverter<S : Any>(
28+
override val param: KParameter,
29+
override val propertyGetter: Method,
30+
private val converter: KFunction<*>
31+
) : BoundParameterForMap<S>() {
32+
override fun map(src: S): Any? = converter.call(propertyGetter.invoke(src))
33+
}
34+
35+
private class ToEnum<S : Any>(
36+
override val param: KParameter,
37+
override val propertyGetter: Method,
38+
private val paramClazz: Class<*>
39+
) : BoundParameterForMap<S>() {
40+
override fun map(src: S): Any? = EnumMapper.getEnum(paramClazz, propertyGetter.invoke(src) as String)
41+
}
42+
43+
private class ToString<S : Any>(
44+
override val param: KParameter,
45+
override val propertyGetter: Method
46+
) : BoundParameterForMap<S>() {
47+
override fun map(src: S): String? = propertyGetter.invoke(src).toString()
48+
}
49+
50+
companion object {
51+
fun <S : Any> newInstance(param: KParameter, property: KProperty1<S, *>): BoundParameterForMap<S> {
52+
// ゲッターが無いならエラー
53+
val propertyGetter = property.javaGetter
54+
?: throw IllegalArgumentException("${property.name} does not have getter.")
55+
propertyGetter.isAccessible = true
56+
57+
val paramClazz = param.type.classifier as KClass<*>
58+
val propertyClazz = property.returnType.classifier as KClass<*>
2359

24-
val converter = paramClazz.getConverters()
25-
.filter { (key, _) -> propertyClazz.isSubclassOf(key) }
26-
.let {
27-
if (1 < it.size) throw IllegalArgumentException("${param.name} has multiple converter. $it")
60+
// コンバータが取れた場合
61+
paramClazz.getConverters()
62+
.filter { (key, _) -> propertyClazz.isSubclassOf(key) }
63+
.let {
64+
if (1 < it.size) throw IllegalArgumentException("${param.name} has multiple converter. $it")
2865

29-
it.singleOrNull()?.second
66+
it.singleOrNull()?.second
67+
}?.let {
68+
return UseConverter(param, propertyGetter, it)
69+
}
70+
71+
if (paramClazz.isSubclassOf(propertyClazz)) {
72+
return Plain(param, propertyGetter)
3073
}
3174

32-
map = when {
33-
converter != null -> { { converter.call(propertyGetter.invoke(it)) } }
34-
paramClazz.isSubclassOf(propertyClazz) -> { { propertyGetter.invoke(it) } }
35-
paramClazz.java.isEnum && propertyClazz == String::class -> { {
36-
EnumMapper.getEnum(paramClazz.java, propertyGetter.invoke(it) as String)
37-
} }
38-
paramClazz == String::class -> { { propertyGetter.invoke(it).toString() } }
39-
else -> throw IllegalArgumentException("Can not convert $propertyClazz to $paramClazz")
75+
val javaClazz = paramClazz.java
76+
77+
return when {
78+
javaClazz.isEnum && propertyClazz == String::class -> ToEnum(param, propertyGetter, javaClazz)
79+
paramClazz == String::class -> ToString(param, propertyGetter)
80+
else -> throw IllegalArgumentException("Can not convert $propertyClazz to $paramClazz")
81+
}
4082
}
4183
}
4284
}

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

Lines changed: 18 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -33,19 +33,19 @@ class KMapper<T : Any> private constructor(
3333
.filter { it.kind != KParameter.Kind.INSTANCE && !it.isUseDefaultArgument() }
3434
.associate { (parameterNameConverter(it.getAliasOrName()!!)) to ParameterForMap.newInstance(it) }
3535

36-
private val getCache: ConcurrentMap<KClass<*>, List<(Any, ArgumentBucket) -> Unit>> = ConcurrentHashMap()
36+
private val getCache: ConcurrentMap<KClass<*>, List<ArgumentBinder>> = ConcurrentHashMap()
3737

3838
private fun bindArguments(argumentBucket: ArgumentBucket, src: Any) {
3939
val clazz = src::class
4040

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

48-
val tempCacheArrayList = ArrayList<(Any, ArgumentBucket) -> Unit>()
48+
val tempBinderArrayList = ArrayList<ArgumentBinder>()
4949

5050
src::class.memberProperties.forEach outer@{ property ->
5151
// propertyが公開されていない場合は処理を行わない
@@ -64,19 +64,14 @@ class KMapper<T : Any> private constructor(
6464
parameterMap[alias ?: property.name]?.let { param ->
6565
javaGetter.isAccessible = true
6666

67-
val tempCache = { value: Any, bucket: ArgumentBucket ->
68-
// 初期化済みであれば高コストな取得処理は行わない
69-
if (!bucket.containsKey(param.param)) {
70-
// javaGetterを呼び出す方が高速
71-
bucket.putIfAbsent(param.param, javaGetter.invoke(value)?.let { param.mapObject(it) })
72-
}
73-
}
74-
tempCache(src, argumentBucket)
75-
tempCacheArrayList.add(tempCache)
67+
val binder = ArgumentBinder(param, javaGetter)
68+
69+
binder.bindArgument(src, argumentBucket)
70+
tempBinderArrayList.add(binder)
7671
// キャッシュの整合性を保つため、ここでは終了判定を行わない
7772
}
7873
}
79-
getCache.putIfAbsent(clazz, tempCacheArrayList)
74+
getCache.putIfAbsent(clazz, tempBinderArrayList)
8075
}
8176

8277
private fun bindArguments(argumentBucket: ArgumentBucket, src: Map<*, *>) {
@@ -131,3 +126,13 @@ class KMapper<T : Any> private constructor(
131126
return function.call(bucket)
132127
}
133128
}
129+
130+
private class ArgumentBinder(private val param: ParameterForMap<*>, private val javaGetter: Method) {
131+
fun bindArgument(value: Any, bucket: ArgumentBucket) {
132+
// 初期化済みであれば高コストな取得処理は行わない
133+
if (!bucket.containsKey(param.param)) {
134+
// javaGetterを呼び出す方が高速
135+
bucket.putIfAbsent(param.param, javaGetter.invoke(value)?.let { param.mapObject(it) })
136+
}
137+
}
138+
}

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

Lines changed: 29 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -15,33 +15,33 @@ internal class ParameterForMap<T : Any> private constructor(val param: KParamete
1515
// リストの長さが小さいと期待されるためこの形で実装しているが、理想的にはmap的なものが使いたい
1616
private val converters: Set<Pair<KClass<*>, KFunction<T>>> = clazz.getConverters()
1717

18-
private val convertCache: ConcurrentMap<KClass<*>, (Any) -> Any?> = ConcurrentHashMap()
18+
private val convertCache: ConcurrentMap<KClass<*>, ParameterProcessor> = ConcurrentHashMap()
1919

2020
fun <U : Any> mapObject(value: U): Any? {
2121
val valueClazz: KClass<*> = value::class
2222

2323
// 取得方法のキャッシュが有ればそれを用いる
24-
convertCache[valueClazz]?.let { return it(value) }
24+
convertCache[valueClazz]?.let { return it.process(value) }
2525

2626
// パラメータに対してvalueが代入可能(同じもしくは親クラス)であればそのまま用いる
2727
if (clazz.isSuperclassOf(valueClazz)) {
28-
convertCache.putIfAbsent(valueClazz) { it }
28+
convertCache.putIfAbsent(valueClazz, ParameterProcessor.Plain)
2929
return value
3030
}
3131

3232
val converter: KFunction<*>? = converters.getConverter(valueClazz)
3333

34-
val lambda: (Any) -> Any? = when {
34+
val processor: ParameterProcessor = when {
3535
// converterに一致する組み合わせが有れば設定されていればそれを使う
36-
converter != null -> { { converter.call(it) } }
36+
converter != null -> ParameterProcessor.UseConverter(converter)
3737
// 要求された値がenumかつ元が文字列ならenum mapperでマップ
38-
javaClazz.isEnum && value is String -> { { EnumMapper.getEnum(javaClazz, it as String) } }
38+
javaClazz.isEnum && value is String -> ParameterProcessor.ToEnum(javaClazz)
3939
// 要求されているパラメータがStringならtoStringする
40-
clazz == String::class -> { { it.toString() } }
40+
clazz == String::class -> ParameterProcessor.ToString
4141
else -> throw IllegalArgumentException("Can not convert $valueClazz to $clazz")
4242
}
43-
convertCache.putIfAbsent(valueClazz, lambda)
44-
return lambda(value)
43+
convertCache.putIfAbsent(valueClazz, processor)
44+
return processor.process(value)
4545
}
4646

4747
companion object {
@@ -50,3 +50,23 @@ internal class ParameterForMap<T : Any> private constructor(val param: KParamete
5050
}
5151
}
5252
}
53+
54+
private sealed class ParameterProcessor {
55+
abstract fun process(value: Any): Any?
56+
57+
object Plain : ParameterProcessor() {
58+
override fun process(value: Any): Any? = value
59+
}
60+
61+
class UseConverter(private val converter: KFunction<*>) : ParameterProcessor() {
62+
override fun process(value: Any): Any? = converter.call(value)
63+
}
64+
65+
class ToEnum(private val javaClazz: Class<*>) : ParameterProcessor() {
66+
override fun process(value: Any): Any? = EnumMapper.getEnum(javaClazz, value as String)
67+
}
68+
69+
object ToString : ParameterProcessor() {
70+
override fun process(value: Any): Any? = value.toString()
71+
}
72+
}

0 commit comments

Comments
 (0)