Skip to content
This repository was archived by the owner on Jan 20, 2023. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 0 additions & 26 deletions src/main/kotlin/com/mapk/kmapper/ParameterForMap.kt

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ package com.mapk.kmapper
import com.mapk.annotations.KGetterAlias
import com.mapk.annotations.KGetterIgnore
import com.mapk.core.ArgumentBucket
import com.mapk.core.EnumMapper
import com.mapk.core.KFunctionForCall
import com.mapk.core.getAliasOrName
import com.mapk.core.isUseDefaultArgument
Expand All @@ -13,11 +12,10 @@ import kotlin.reflect.KClass
import kotlin.reflect.KFunction
import kotlin.reflect.KParameter
import kotlin.reflect.KVisibility
import kotlin.reflect.full.isSuperclassOf
import kotlin.reflect.full.memberProperties
import kotlin.reflect.jvm.javaGetter

class KMapper<T : Any> private constructor(
class PlainKMapper<T : Any> private constructor(
private val function: KFunctionForCall<T>,
parameterNameConverter: (String) -> String
) {
Expand All @@ -29,9 +27,9 @@ class KMapper<T : Any> private constructor(
clazz.toKConstructor(), parameterNameConverter
)

private val parameterMap: Map<String, ParameterForMap<*>> = function.parameters
private val parameterMap: Map<String, PlainParameterForMap<*>> = function.parameters
.filter { it.kind != KParameter.Kind.INSTANCE && !it.isUseDefaultArgument() }
.associate { (parameterNameConverter(it.getAliasOrName()!!)) to ParameterForMap.newInstance(it) }
.associate { (parameterNameConverter(it.getAliasOrName()!!)) to PlainParameterForMap.newInstance(it) }

private fun bindArguments(argumentBucket: ArgumentBucket, src: Any) {
src::class.memberProperties.forEach outer@{ property ->
Expand All @@ -51,7 +49,7 @@ class KMapper<T : Any> private constructor(
parameterMap[alias ?: property.name]?.let {
// javaGetterを呼び出す方が高速
javaGetter.isAccessible = true
argumentBucket.putIfAbsent(it.param, javaGetter.invoke(src)?.let { value -> mapObject(it, value) })
argumentBucket.putIfAbsent(it.param, javaGetter.invoke(src)?.let { value -> it.mapObject(value) })
// 終了判定
if (argumentBucket.isInitialized) return
}
Expand All @@ -62,7 +60,7 @@ class KMapper<T : Any> private constructor(
src.forEach { (key, value) ->
parameterMap[key]?.let { param ->
// 取得した内容がnullでなければ適切にmapする
argumentBucket.putIfAbsent(param.param, value?.let { mapObject(param, it) })
argumentBucket.putIfAbsent(param.param, value?.let { param.mapObject(value) })
// 終了判定
if (argumentBucket.isInitialized) return
}
Expand All @@ -71,7 +69,7 @@ class KMapper<T : Any> private constructor(

private fun bindArguments(argumentBucket: ArgumentBucket, srcPair: Pair<*, *>) {
parameterMap[srcPair.first.toString()]?.let {
argumentBucket.putIfAbsent(it.param, srcPair.second?.let { value -> mapObject(it, value) })
argumentBucket.putIfAbsent(it.param, srcPair.second?.let { value -> it.mapObject(value) })
}
}

Expand Down Expand Up @@ -110,22 +108,3 @@ class KMapper<T : Any> private constructor(
return function.call(bucket)
}
}

private fun <T : Any, R : Any> mapObject(param: ParameterForMap<R>, value: T): Any? {
val valueClazz: KClass<*> = value::class

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

val converter: KFunction<*>? = param.getConverter(valueClazz)

return when {
// converterに一致する組み合わせが有れば設定されていればそれを使う
converter != null -> converter.call(value)
// 要求された値がenumかつ元が文字列ならenum mapperでマップ
param.javaClazz.isEnum && value is String -> EnumMapper.getEnum(param.clazz.java, value)
// 要求されているパラメータがStringならtoStringする
param.clazz == String::class -> value.toString()
else -> throw IllegalArgumentException("Can not convert $valueClazz to ${param.clazz}")
}
}
47 changes: 47 additions & 0 deletions src/main/kotlin/com/mapk/kmapper/PlainParameterForMap.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
package com.mapk.kmapper

import com.mapk.core.EnumMapper
import kotlin.reflect.KClass
import kotlin.reflect.KFunction
import kotlin.reflect.KParameter
import kotlin.reflect.full.isSubclassOf
import kotlin.reflect.full.isSuperclassOf

internal class PlainParameterForMap<T : Any> private constructor(val param: KParameter, private val clazz: KClass<T>) {
private val javaClazz: Class<T> by lazy {
clazz.java
}
// リストの長さが小さいと期待されるためこの形で実装しているが、理想的にはmap的なものが使いたい
private val converters: Set<Pair<KClass<*>, KFunction<T>>> by lazy {
convertersFromConstructors(clazz) + convertersFromStaticMethods(clazz) + convertersFromCompanionObject(clazz)
}

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

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

val converter: KFunction<*>? = getConverter(valueClazz)

return when {
// converterに一致する組み合わせが有れば設定されていればそれを使う
converter != null -> converter.call(value)
// 要求された値がenumかつ元が文字列ならenum mapperでマップ
javaClazz.isEnum && value is String -> EnumMapper.getEnum(javaClazz, value)
// 要求されているパラメータがStringならtoStringする
clazz == String::class -> value.toString()
else -> throw IllegalArgumentException("Can not convert $valueClazz to $clazz")
}
}

// 引数の型がconverterに対して入力可能ならconverterを返す
private fun <R : Any> getConverter(input: KClass<out R>): KFunction<T>? =
converters.find { (key, _) -> input.isSubclassOf(key) }?.second

companion object {
fun newInstance(param: KParameter): PlainParameterForMap<*> {
return PlainParameterForMap(param, param.type.classifier as KClass<*>)
}
}
}
8 changes: 4 additions & 4 deletions src/test/kotlin/com/mapk/kmapper/ConverterKMapperTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -32,12 +32,12 @@ private data class BoundStaticMethodConverterSrc(val argument: String)
@DisplayName("コンバータ有りでのマッピングテスト")
class ConverterKMapperTest {
@Nested
@DisplayName("KMapper")
@DisplayName("PlainKMapper")
inner class KMapperTest {
@Test
@DisplayName("コンストラクターでのコンバートテスト")
fun constructorConverterTest() {
val mapper = KMapper(ConstructorConverterDst::class)
val mapper = PlainKMapper(ConstructorConverterDst::class)
val result = mapper.map(mapOf("argument" to 1))

assertEquals(ConstructorConverter(1), result.argument)
Expand All @@ -46,7 +46,7 @@ class ConverterKMapperTest {
@Test
@DisplayName("コンパニオンオブジェクトに定義したコンバータでのコンバートテスト")
fun companionConverterTest() {
val mapper = KMapper(CompanionConverterDst::class)
val mapper = PlainKMapper(CompanionConverterDst::class)
val result = mapper.map(mapOf("argument" to "arg"))

assertEquals("arg", result.argument.arg)
Expand All @@ -55,7 +55,7 @@ class ConverterKMapperTest {
@Test
@DisplayName("スタティックメソッドに定義したコンバータでのコンバートテスト")
fun staticMethodConverterTest() {
val mapper = KMapper(StaticMethodConverterDst::class)
val mapper = PlainKMapper(StaticMethodConverterDst::class)
val result = mapper.map(mapOf("argument" to "1,2,3"))

assertTrue(intArrayOf(1, 2, 3) contentEquals result.argument.arg)
Expand Down
4 changes: 2 additions & 2 deletions src/test/kotlin/com/mapk/kmapper/DefaultArgumentTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,11 @@ class DefaultArgumentTest {
private val src = Src(1, "src")

@Nested
@DisplayName("KMapper")
@DisplayName("PlainKMapper")
inner class KMapperTest {
@Test
fun test() {
val result = KMapper(::Dst).map(src)
val result = PlainKMapper(::Dst).map(src)
assertEquals(Dst(1, "default"), result)
}
}
Expand Down
4 changes: 2 additions & 2 deletions src/test/kotlin/com/mapk/kmapper/EnumMappingTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,9 @@ private class EnumMappingDst(val language: JvmLanguage?)
@DisplayName("文字列 -> Enumのマッピングテスト")
class EnumMappingTest {
@Nested
@DisplayName("KMapper")
@DisplayName("PlainKMapper")
inner class KMapperTest {
private val mapper = KMapper(EnumMappingDst::class)
private val mapper = PlainKMapper(EnumMappingDst::class)

@ParameterizedTest(name = "Non-Null要求")
@EnumSource(value = JvmLanguage::class)
Expand Down
4 changes: 2 additions & 2 deletions src/test/kotlin/com/mapk/kmapper/KGetterIgnoreTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -14,15 +14,15 @@ class KGetterIgnoreTest {
data class Dst(val arg1: Int, val arg2: String, val arg3: Int, val arg4: String)

@Nested
@DisplayName("KMapper")
@DisplayName("PlainKMapper")
inner class KMapperTest {
@Test
@DisplayName("フィールドを無視するテスト")
fun test() {
val src1 = Src1(1, "2-1", 31)
val src2 = Src2("2-2", 32, "4")

val mapper = KMapper(::Dst)
val mapper = PlainKMapper(::Dst)

val dst1 = mapper.map(src1, src2)
val dst2 = mapper.map(src2, src1)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,15 +12,15 @@ private data class BoundSrc(val camel_case: String)
@DisplayName("パラメータ名変換のテスト")
class ParameterNameConverterTest {
@Nested
@DisplayName("KMapper")
@DisplayName("PlainKMapper")
inner class KMapperTest {
@Test
@DisplayName("スネークケースsrc -> キャメルケースdst")
fun test() {
val expected = "snakeCase"
val src = mapOf("camel_case" to expected)

val mapper = KMapper(CamelCaseDst::class) {
val mapper = PlainKMapper(CamelCaseDst::class) {
CaseFormat.LOWER_CAMEL.to(CaseFormat.LOWER_UNDERSCORE, it)
}
val result = mapper.map(src)
Expand Down
6 changes: 3 additions & 3 deletions src/test/kotlin/com/mapk/kmapper/PropertyAliasTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ private data class AliasedSrc(
@DisplayName("エイリアスを貼った場合のテスト")
class PropertyAliasTest {
@Nested
@DisplayName("KMapper")
@DisplayName("PlainKMapper")
inner class KMapperTest {
@Test
@DisplayName("パラメータにエイリアスを貼った場合")
Expand All @@ -32,7 +32,7 @@ class PropertyAliasTest {
"arg3" to 3
)

val result = KMapper(::AliasedDst).map(src)
val result = PlainKMapper(::AliasedDst).map(src)

assertEquals(1.0, result.arg1)
assertEquals(3, result.arg2)
Expand All @@ -42,7 +42,7 @@ class PropertyAliasTest {
@DisplayName("ゲッターにエイリアスを貼った場合")
fun getAliasTest() {
val src = AliasedSrc(1.0, 2)
val result = KMapper(::AliasedDst).map(src)
val result = PlainKMapper(::AliasedDst).map(src)

assertEquals(1.0, result.arg1)
assertEquals(2, result.arg2)
Expand Down
14 changes: 7 additions & 7 deletions src/test/kotlin/com/mapk/kmapper/SimpleKMapperTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -74,14 +74,14 @@ class SimpleKMapperTest {
}

@Nested
@DisplayName("KMapper")
@DisplayName("PlainKMapper")
inner class KMapperTest {
private val mappers: Set<KMapper<out SimpleDst>> = setOf(
KMapper(SimpleDst::class),
KMapper(::SimpleDst),
KMapper((SimpleDst)::factory),
KMapper(::instanceFunction),
KMapper(SimpleDstExt::class)
private val mappers: Set<PlainKMapper<out SimpleDst>> = setOf(
PlainKMapper(SimpleDst::class),
PlainKMapper(::SimpleDst),
PlainKMapper((SimpleDst)::factory),
PlainKMapper(::instanceFunction),
PlainKMapper(SimpleDstExt::class)
)

@Nested
Expand Down
4 changes: 2 additions & 2 deletions src/test/kotlin/com/mapk/kmapper/StringMappingTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,11 @@ private data class BoundMappingSrc(val value: Int)
@DisplayName("文字列に対してtoStringしたものを渡すテスト")
class StringMappingTest {
@Nested
@DisplayName("KMapper")
@DisplayName("PlainKMapper")
inner class KMapperTest {
@Test
fun test() {
val result: StringMappingDst = KMapper(StringMappingDst::class).map("value" to 1)
val result: StringMappingDst = PlainKMapper(StringMappingDst::class).map("value" to 1)
assertEquals("1", result.value)
}
}
Expand Down