Skip to content
This repository was archived by the owner on Jan 20, 2023. It is now read-only.
2 changes: 1 addition & 1 deletion build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ plugins {
}

group = "com.mapk"
version = "0.21"
version = "0.22"

java {
sourceCompatibility = JavaVersion.VERSION_1_8
Expand Down
33 changes: 28 additions & 5 deletions src/main/kotlin/com/mapk/kmapper/KMapper.kt
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ import com.mapk.core.getAliasOrName
import com.mapk.core.isUseDefaultArgument
import com.mapk.core.toKConstructor
import java.lang.reflect.Method
import java.util.concurrent.ConcurrentHashMap
import java.util.concurrent.ConcurrentMap
import kotlin.reflect.KClass
import kotlin.reflect.KFunction
import kotlin.reflect.KParameter
Expand All @@ -31,7 +33,20 @@ class KMapper<T : Any> private constructor(
.filter { it.kind != KParameter.Kind.INSTANCE && !it.isUseDefaultArgument() }
.associate { (parameterNameConverter(it.getAliasOrName()!!)) to ParameterForMap.newInstance(it) }

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

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

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

val tempCacheArrayList = ArrayList<(Any, ArgumentBucket) -> Unit>()

src::class.memberProperties.forEach outer@{ property ->
// propertyが公開されていない場合は処理を行わない
if (property.visibility != KVisibility.PUBLIC) return@outer
Expand All @@ -46,14 +61,22 @@ class KMapper<T : Any> private constructor(
if (it is KGetterAlias) alias = it.value
}

parameterMap[alias ?: property.name]?.let {
// javaGetterを呼び出す方が高速
parameterMap[alias ?: property.name]?.let { param ->
javaGetter.isAccessible = true
argumentBucket.putIfAbsent(it.param, javaGetter.invoke(src)?.let { value -> it.mapObject(value) })
// 終了判定
if (argumentBucket.isInitialized) return

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

private fun bindArguments(argumentBucket: ArgumentBucket, src: Map<*, *>) {
Expand Down
8 changes: 5 additions & 3 deletions src/main/kotlin/com/mapk/kmapper/ParameterForMap.kt
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package com.mapk.kmapper

import com.mapk.core.EnumMapper
import java.util.concurrent.ConcurrentHashMap
import java.util.concurrent.ConcurrentMap
import kotlin.reflect.KClass
import kotlin.reflect.KFunction
import kotlin.reflect.KParameter
Expand All @@ -13,7 +15,7 @@ internal class ParameterForMap<T : Any> private constructor(val param: KParamete
// リストの長さが小さいと期待されるためこの形で実装しているが、理想的にはmap的なものが使いたい
private val converters: Set<Pair<KClass<*>, KFunction<T>>> = clazz.getConverters()

private val convertCache: MutableMap<KClass<*>, (Any) -> Any?> = HashMap()
private val convertCache: ConcurrentMap<KClass<*>, (Any) -> Any?> = ConcurrentHashMap()

fun <U : Any> mapObject(value: U): Any? {
val valueClazz: KClass<*> = value::class
Expand All @@ -23,7 +25,7 @@ internal class ParameterForMap<T : Any> private constructor(val param: KParamete

// パラメータに対してvalueが代入可能(同じもしくは親クラス)であればそのまま用いる
if (clazz.isSuperclassOf(valueClazz)) {
convertCache[valueClazz] = { value }
convertCache.putIfAbsent(valueClazz) { it }
return value
}

Expand All @@ -38,7 +40,7 @@ internal class ParameterForMap<T : Any> private constructor(val param: KParamete
clazz == String::class -> { { it.toString() } }
else -> throw IllegalArgumentException("Can not convert $valueClazz to $clazz")
}
convertCache[valueClazz] = lambda
convertCache.putIfAbsent(valueClazz, lambda)
return lambda(value)
}

Expand Down