diff --git a/build.gradle.kts b/build.gradle.kts index 2ed2a7c..feb3af9 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -6,7 +6,7 @@ plugins { } group = "com.mapk" -version = "0.25" +version = "0.26" java { sourceCompatibility = JavaVersion.VERSION_1_8 @@ -30,7 +30,7 @@ repositories { dependencies { implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8") implementation(kotlin("reflect")) - api("com.github.ProjectMapK:Shared:0.11") + api("com.github.ProjectMapK:Shared:0.12") // https://mvnrepository.com/artifact/org.junit.jupiter/junit-jupiter testImplementation(group = "org.junit.jupiter", name = "junit-jupiter", version = "5.6.2") { diff --git a/src/main/kotlin/com/mapk/kmapper/BoundKMapper.kt b/src/main/kotlin/com/mapk/kmapper/BoundKMapper.kt index 287c54f..ec67db6 100644 --- a/src/main/kotlin/com/mapk/kmapper/BoundKMapper.kt +++ b/src/main/kotlin/com/mapk/kmapper/BoundKMapper.kt @@ -2,15 +2,11 @@ package com.mapk.kmapper import com.mapk.annotations.KGetterAlias import com.mapk.annotations.KGetterIgnore -import com.mapk.core.ArgumentBucket import com.mapk.core.KFunctionForCall -import com.mapk.core.getAliasOrName -import com.mapk.core.isUseDefaultArgument import com.mapk.core.toKConstructor import java.lang.IllegalArgumentException import kotlin.reflect.KClass import kotlin.reflect.KFunction -import kotlin.reflect.KParameter import kotlin.reflect.KProperty1 import kotlin.reflect.KVisibility import kotlin.reflect.full.findAnnotation @@ -23,34 +19,32 @@ class BoundKMapper private constructor( parameterNameConverter: (String) -> String ) { constructor(function: KFunction, src: KClass, parameterNameConverter: (String) -> String = { it }) : this( - KFunctionForCall(function), src, parameterNameConverter + KFunctionForCall(function, parameterNameConverter), src, parameterNameConverter ) constructor(clazz: KClass, src: KClass, parameterNameConverter: (String) -> String = { it }) : this( - clazz.toKConstructor(), src, parameterNameConverter + clazz.toKConstructor(parameterNameConverter), src, parameterNameConverter ) private val parameters: List> init { - val srcPropertiesMap: Map> = - src.memberProperties - .filter { - // アクセス可能かつignoreされてないもののみ抽出 - !(it.visibility != KVisibility.PUBLIC) && - it.getter.annotations.none { annotation -> annotation is KGetterIgnore } - }.associateBy { it.getter.findAnnotation()?.value ?: it.name } - - parameters = function.parameters - .filter { it.kind != KParameter.Kind.INSTANCE && !it.isUseDefaultArgument() } + val srcPropertiesMap: Map> = src.memberProperties + .filter { + // アクセス可能かつignoreされてないもののみ抽出 + !(it.visibility != KVisibility.PUBLIC) && + it.getter.annotations.none { annotation -> annotation is KGetterIgnore } + }.associateBy { it.getter.findAnnotation()?.value ?: it.name } + + parameters = function.requiredParameters .mapNotNull { - val temp = srcPropertiesMap[parameterNameConverter(it.getAliasOrName()!!)]?.let { property -> + val temp = srcPropertiesMap[it.name]?.let { property -> BoundParameterForMap.newInstance(it, property, parameterNameConverter) } // 必須引数に対応するプロパティがsrcに定義されていない場合エラー if (temp == null && !it.isOptional) { - throw IllegalArgumentException("Property ${it.name!!} is not declared in ${src.jvmName}.") + throw IllegalArgumentException("Property ${it.name} is not declared in ${src.jvmName}.") } temp @@ -58,12 +52,12 @@ class BoundKMapper private constructor( } fun map(src: S): D { - val bucket: ArgumentBucket = function.getArgumentBucket() + val adaptor = function.getArgumentAdaptor() parameters.forEach { - bucket.putIfAbsent(it.param, it.map(src)) + adaptor.putIfAbsent(it.name, it.map(src)) } - return function.call(bucket) + return function.call(adaptor) } } diff --git a/src/main/kotlin/com/mapk/kmapper/BoundParameterForMap.kt b/src/main/kotlin/com/mapk/kmapper/BoundParameterForMap.kt index adac56a..3516e12 100644 --- a/src/main/kotlin/com/mapk/kmapper/BoundParameterForMap.kt +++ b/src/main/kotlin/com/mapk/kmapper/BoundParameterForMap.kt @@ -1,31 +1,31 @@ package com.mapk.kmapper import com.mapk.core.EnumMapper +import com.mapk.core.ValueParameter import java.lang.IllegalArgumentException import java.lang.reflect.Method import kotlin.reflect.KClass import kotlin.reflect.KFunction -import kotlin.reflect.KParameter import kotlin.reflect.KProperty1 import kotlin.reflect.full.isSubclassOf import kotlin.reflect.jvm.javaGetter @Suppress("UNCHECKED_CAST") internal sealed class BoundParameterForMap { - abstract val param: KParameter + abstract val name: String protected abstract val propertyGetter: Method abstract fun map(src: S): Any? private class Plain( - override val param: KParameter, + override val name: String, override val propertyGetter: Method ) : BoundParameterForMap() { override fun map(src: S): Any? = propertyGetter.invoke(src) } private class UseConverter( - override val param: KParameter, + override val name: String, override val propertyGetter: Method, private val converter: KFunction<*> ) : BoundParameterForMap() { @@ -33,7 +33,7 @@ internal sealed class BoundParameterForMap { } private class UseKMapper( - override val param: KParameter, + override val name: String, override val propertyGetter: Method, private val kMapper: KMapper<*> ) : BoundParameterForMap() { @@ -42,7 +42,7 @@ internal sealed class BoundParameterForMap { } private class UseBoundKMapper( - override val param: KParameter, + override val name: String, override val propertyGetter: Method, private val boundKMapper: BoundKMapper ) : BoundParameterForMap() { @@ -50,7 +50,7 @@ internal sealed class BoundParameterForMap { } private class ToEnum( - override val param: KParameter, + override val name: String, override val propertyGetter: Method, private val paramClazz: Class<*> ) : BoundParameterForMap() { @@ -58,7 +58,7 @@ internal sealed class BoundParameterForMap { } private class ToString( - override val param: KParameter, + override val name: String, override val propertyGetter: Method ) : BoundParameterForMap() { override fun map(src: S): String? = propertyGetter.invoke(src).toString() @@ -66,7 +66,7 @@ internal sealed class BoundParameterForMap { companion object { fun newInstance( - param: KParameter, + param: ValueParameter<*>, property: KProperty1, parameterNameConverter: (String) -> String ): BoundParameterForMap { @@ -75,7 +75,7 @@ internal sealed class BoundParameterForMap { ?: throw IllegalArgumentException("${property.name} does not have getter.") propertyGetter.isAccessible = true - val paramClazz = param.type.classifier as KClass<*> + val paramClazz = param.requiredClazz val propertyClazz = property.returnType.classifier as KClass<*> // コンバータが取れた場合 @@ -86,25 +86,25 @@ internal sealed class BoundParameterForMap { it.singleOrNull()?.second }?.let { - return UseConverter(param, propertyGetter, it) + return UseConverter(param.name, propertyGetter, it) } if (paramClazz.isSubclassOf(propertyClazz)) { - return Plain(param, propertyGetter) + return Plain(param.name, propertyGetter) } val javaClazz = paramClazz.java return when { - javaClazz.isEnum && propertyClazz == String::class -> ToEnum(param, propertyGetter, javaClazz) - paramClazz == String::class -> ToString(param, propertyGetter) + javaClazz.isEnum && propertyClazz == String::class -> ToEnum(param.name, propertyGetter, javaClazz) + paramClazz == String::class -> ToString(param.name, propertyGetter) // SrcがMapやPairならKMapperを使わないとマップできない propertyClazz.isSubclassOf(Map::class) || propertyClazz.isSubclassOf(Pair::class) -> UseKMapper( - param, propertyGetter, KMapper(paramClazz, parameterNameConverter) + param.name, propertyGetter, KMapper(paramClazz, parameterNameConverter) ) // 何にも当てはまらなければBoundKMapperでマップを試みる else -> UseBoundKMapper( - param, propertyGetter, BoundKMapper(paramClazz, propertyClazz, parameterNameConverter) + param.name, propertyGetter, BoundKMapper(paramClazz, propertyClazz, parameterNameConverter) ) } } diff --git a/src/main/kotlin/com/mapk/kmapper/KMapper.kt b/src/main/kotlin/com/mapk/kmapper/KMapper.kt index 5d89c6d..3677df3 100644 --- a/src/main/kotlin/com/mapk/kmapper/KMapper.kt +++ b/src/main/kotlin/com/mapk/kmapper/KMapper.kt @@ -2,17 +2,14 @@ package com.mapk.kmapper import com.mapk.annotations.KGetterAlias import com.mapk.annotations.KGetterIgnore -import com.mapk.core.ArgumentBucket +import com.mapk.core.ArgumentAdaptor import com.mapk.core.KFunctionForCall -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 import kotlin.reflect.KVisibility import kotlin.reflect.full.memberProperties import kotlin.reflect.jvm.javaGetter @@ -22,28 +19,26 @@ class KMapper private constructor( parameterNameConverter: (String) -> String ) { constructor(function: KFunction, parameterNameConverter: (String) -> String = { it }) : this( - KFunctionForCall(function), parameterNameConverter + KFunctionForCall(function, parameterNameConverter), parameterNameConverter ) constructor(clazz: KClass, parameterNameConverter: (String) -> String = { it }) : this( - clazz.toKConstructor(), parameterNameConverter + clazz.toKConstructor(parameterNameConverter), parameterNameConverter ) - private val parameterMap: Map> = function.parameters - .filter { it.kind != KParameter.Kind.INSTANCE && !it.isUseDefaultArgument() } - .associate { - (parameterNameConverter(it.getAliasOrName()!!)) to ParameterForMap.newInstance(it, parameterNameConverter) - } + private val parameterMap: Map> = function.requiredParameters.associate { + it.name to ParameterForMap(it, parameterNameConverter) + } private val getCache: ConcurrentMap, List> = ConcurrentHashMap() - private fun bindArguments(argumentBucket: ArgumentBucket, src: Any) { + private fun bindArguments(argumentAdaptor: ArgumentAdaptor, src: Any) { val clazz = src::class // キャッシュヒットしたら登録した内容に沿って取得処理を行う getCache[clazz]?.let { getters -> // 取得対象フィールドは十分絞り込んでいると考えられるため、終了判定は行わない - getters.forEach { it.bindArgument(src, argumentBucket) } + getters.forEach { it.bindArgument(src, argumentAdaptor) } return } @@ -68,7 +63,7 @@ class KMapper private constructor( val binder = ArgumentBinder(param, javaGetter) - binder.bindArgument(src, argumentBucket) + binder.bindArgument(src, argumentAdaptor) tempBinderArrayList.add(binder) // キャッシュの整合性を保つため、ここでは終了判定を行わない } @@ -76,65 +71,67 @@ class KMapper private constructor( getCache.putIfAbsent(clazz, tempBinderArrayList) } - private fun bindArguments(argumentBucket: ArgumentBucket, src: Map<*, *>) { + private fun bindArguments(argumentAdaptor: ArgumentAdaptor, src: Map<*, *>) { src.forEach { (key, value) -> parameterMap[key]?.let { param -> // 取得した内容がnullでなければ適切にmapする - argumentBucket.putIfAbsent(param.param, value?.let { param.mapObject(value) }) + argumentAdaptor.putIfAbsent(param.name, value?.let { param.mapObject(value) }) // 終了判定 - if (argumentBucket.isInitialized) return + if (argumentAdaptor.isFullInitialized()) return } } } - private fun bindArguments(argumentBucket: ArgumentBucket, srcPair: Pair<*, *>) { - parameterMap[srcPair.first.toString()]?.let { - argumentBucket.putIfAbsent(it.param, srcPair.second?.let { value -> it.mapObject(value) }) + private fun bindArguments(argumentAdaptor: ArgumentAdaptor, srcPair: Pair<*, *>) { + val key = srcPair.first.toString() + + parameterMap[key]?.let { + argumentAdaptor.putIfAbsent(key, srcPair.second?.let { value -> it.mapObject(value) }) } } fun map(srcMap: Map): T { - val bucket: ArgumentBucket = function.getArgumentBucket() - bindArguments(bucket, srcMap) + val adaptor: ArgumentAdaptor = function.getArgumentAdaptor() + bindArguments(adaptor, srcMap) - return function.call(bucket) + return function.call(adaptor) } fun map(srcPair: Pair): T { - val bucket: ArgumentBucket = function.getArgumentBucket() - bindArguments(bucket, srcPair) + val adaptor: ArgumentAdaptor = function.getArgumentAdaptor() + bindArguments(adaptor, srcPair) - return function.call(bucket) + return function.call(adaptor) } fun map(src: Any): T { - val bucket: ArgumentBucket = function.getArgumentBucket() - bindArguments(bucket, src) + val adaptor: ArgumentAdaptor = function.getArgumentAdaptor() + bindArguments(adaptor, src) - return function.call(bucket) + return function.call(adaptor) } fun map(vararg args: Any): T { - val bucket: ArgumentBucket = function.getArgumentBucket() + val adaptor: ArgumentAdaptor = function.getArgumentAdaptor() listOf(*args).forEach { arg -> when (arg) { - is Map<*, *> -> bindArguments(bucket, arg) - is Pair<*, *> -> bindArguments(bucket, arg) - else -> bindArguments(bucket, arg) + is Map<*, *> -> bindArguments(adaptor, arg) + is Pair<*, *> -> bindArguments(adaptor, arg) + else -> bindArguments(adaptor, arg) } } - return function.call(bucket) + return function.call(adaptor) } } private class ArgumentBinder(private val param: ParameterForMap<*>, private val javaGetter: Method) { - fun bindArgument(src: Any, bucket: ArgumentBucket) { + fun bindArgument(src: Any, adaptor: ArgumentAdaptor) { // 初期化済みであれば高コストな取得処理は行わない - if (!bucket.containsKey(param.param)) { + if (!adaptor.isInitialized(param.name)) { // javaGetterを呼び出す方が高速 - bucket.putIfAbsent(param.param, javaGetter.invoke(src)?.let { param.mapObject(it) }) + adaptor.putIfAbsent(param.name, javaGetter.invoke(src)?.let { param.mapObject(it) }) } } } diff --git a/src/main/kotlin/com/mapk/kmapper/ParameterForMap.kt b/src/main/kotlin/com/mapk/kmapper/ParameterForMap.kt index 92c5a27..3842f67 100644 --- a/src/main/kotlin/com/mapk/kmapper/ParameterForMap.kt +++ b/src/main/kotlin/com/mapk/kmapper/ParameterForMap.kt @@ -1,18 +1,20 @@ package com.mapk.kmapper import com.mapk.core.EnumMapper +import com.mapk.core.ValueParameter import java.util.concurrent.ConcurrentHashMap import java.util.concurrent.ConcurrentMap import kotlin.reflect.KClass import kotlin.reflect.KFunction -import kotlin.reflect.KParameter import kotlin.reflect.full.isSuperclassOf -internal class ParameterForMap private constructor( - val param: KParameter, - private val clazz: KClass, +internal class ParameterForMap( + param: ValueParameter, private val parameterNameConverter: (String) -> String ) { + val name: String = param.name + private val clazz: KClass = param.requiredClazz + private val javaClazz: Class by lazy { clazz.java } @@ -52,12 +54,6 @@ internal class ParameterForMap private constructor( convertCache.putIfAbsent(valueClazz, processor) return processor.process(value) } - - companion object { - fun newInstance(param: KParameter, parameterNameConverter: (String) -> String): ParameterForMap<*> { - return ParameterForMap(param, param.type.classifier as KClass<*>, parameterNameConverter) - } - } } private sealed class ParameterProcessor { diff --git a/src/main/kotlin/com/mapk/kmapper/ParameterUtils.kt b/src/main/kotlin/com/mapk/kmapper/ParameterUtils.kt index 25c395d..9dd4d97 100644 --- a/src/main/kotlin/com/mapk/kmapper/ParameterUtils.kt +++ b/src/main/kotlin/com/mapk/kmapper/ParameterUtils.kt @@ -3,9 +3,9 @@ package com.mapk.kmapper import com.mapk.annotations.KConverter import com.mapk.conversion.KConvertBy import com.mapk.core.KFunctionWithInstance +import com.mapk.core.ValueParameter import kotlin.reflect.KClass import kotlin.reflect.KFunction -import kotlin.reflect.KParameter import kotlin.reflect.full.companionObjectInstance import kotlin.reflect.full.findAnnotation import kotlin.reflect.full.functions @@ -54,7 +54,7 @@ private fun convertersFromCompanionObject(clazz: KClass): Set, KFunction<*>>> { +internal fun ValueParameter.getConverters(): Set, KFunction<*>>> { return annotations.mapNotNull { paramAnnotation -> paramAnnotation.annotationClass .findAnnotation() diff --git a/src/main/kotlin/com/mapk/kmapper/PlainKMapper.kt b/src/main/kotlin/com/mapk/kmapper/PlainKMapper.kt index 871990d..b1ab323 100644 --- a/src/main/kotlin/com/mapk/kmapper/PlainKMapper.kt +++ b/src/main/kotlin/com/mapk/kmapper/PlainKMapper.kt @@ -2,15 +2,12 @@ package com.mapk.kmapper import com.mapk.annotations.KGetterAlias import com.mapk.annotations.KGetterIgnore -import com.mapk.core.ArgumentBucket +import com.mapk.core.ArgumentAdaptor import com.mapk.core.KFunctionForCall -import com.mapk.core.getAliasOrName -import com.mapk.core.isUseDefaultArgument import com.mapk.core.toKConstructor import java.lang.reflect.Method import kotlin.reflect.KClass import kotlin.reflect.KFunction -import kotlin.reflect.KParameter import kotlin.reflect.KVisibility import kotlin.reflect.full.memberProperties import kotlin.reflect.jvm.javaGetter @@ -20,21 +17,18 @@ class PlainKMapper private constructor( parameterNameConverter: (String) -> String ) { constructor(function: KFunction, parameterNameConverter: (String) -> String = { it }) : this( - KFunctionForCall(function), parameterNameConverter + KFunctionForCall(function, parameterNameConverter), parameterNameConverter ) constructor(clazz: KClass, parameterNameConverter: (String) -> String = { it }) : this( - clazz.toKConstructor(), parameterNameConverter + clazz.toKConstructor(parameterNameConverter), parameterNameConverter ) - private val parameterMap: Map> = function.parameters - .filter { it.kind != KParameter.Kind.INSTANCE && !it.isUseDefaultArgument() } - .associate { - (parameterNameConverter(it.getAliasOrName()!!)) to - PlainParameterForMap.newInstance(it, parameterNameConverter) - } + private val parameterMap: Map> = function.requiredParameters.associate { + it.name to PlainParameterForMap(it, parameterNameConverter) + } - private fun bindArguments(argumentBucket: ArgumentBucket, src: Any) { + private fun bindArguments(argumentAdaptor: ArgumentAdaptor, src: Any) { src::class.memberProperties.forEach outer@{ property -> // propertyが公開されていない場合は処理を行わない if (property.visibility != KVisibility.PUBLIC) return@outer @@ -48,66 +42,69 @@ class PlainKMapper private constructor( if (it is KGetterIgnore) return@outer // ignoreされている場合は処理を行わない if (it is KGetterAlias) alias = it.value } + alias = alias ?: property.name - parameterMap[alias ?: property.name]?.let { + parameterMap[alias!!]?.let { // javaGetterを呼び出す方が高速 javaGetter.isAccessible = true - argumentBucket.putIfAbsent(it.param, javaGetter.invoke(src)?.let { value -> it.mapObject(value) }) + argumentAdaptor.putIfAbsent(alias!!, javaGetter.invoke(src)?.let { value -> it.mapObject(value) }) // 終了判定 - if (argumentBucket.isInitialized) return + if (argumentAdaptor.isFullInitialized()) return } } } - private fun bindArguments(argumentBucket: ArgumentBucket, src: Map<*, *>) { + private fun bindArguments(argumentAdaptor: ArgumentAdaptor, src: Map<*, *>) { src.forEach { (key, value) -> parameterMap[key]?.let { param -> // 取得した内容がnullでなければ適切にmapする - argumentBucket.putIfAbsent(param.param, value?.let { param.mapObject(value) }) + argumentAdaptor.putIfAbsent(key as String, value?.let { param.mapObject(value) }) // 終了判定 - if (argumentBucket.isInitialized) return + if (argumentAdaptor.isFullInitialized()) return } } } - private fun bindArguments(argumentBucket: ArgumentBucket, srcPair: Pair<*, *>) { - parameterMap[srcPair.first.toString()]?.let { - argumentBucket.putIfAbsent(it.param, srcPair.second?.let { value -> it.mapObject(value) }) + private fun bindArguments(argumentBucket: ArgumentAdaptor, srcPair: Pair<*, *>) { + val key = srcPair.first.toString() + + parameterMap[key]?.let { + argumentBucket.putIfAbsent(key, srcPair.second?.let { value -> it.mapObject(value) }) } } fun map(srcMap: Map): T { - val bucket: ArgumentBucket = function.getArgumentBucket() - bindArguments(bucket, srcMap) + val adaptor = function.getArgumentAdaptor() + bindArguments(adaptor, srcMap) - return function.call(bucket) + return function.call(adaptor) } fun map(srcPair: Pair): T { - val bucket: ArgumentBucket = function.getArgumentBucket() - bindArguments(bucket, srcPair) + val adaptor = function.getArgumentAdaptor() + bindArguments(adaptor, srcPair) - return function.call(bucket) + return function.call(adaptor) } fun map(src: Any): T { - val bucket: ArgumentBucket = function.getArgumentBucket() - bindArguments(bucket, src) + val adaptor = function.getArgumentAdaptor() + bindArguments(adaptor, src) - return function.call(bucket) + return function.call(adaptor) } fun map(vararg args: Any): T { - val bucket: ArgumentBucket = function.getArgumentBucket() + val adaptor = function.getArgumentAdaptor() listOf(*args).forEach { arg -> when (arg) { - is Map<*, *> -> bindArguments(bucket, arg) - is Pair<*, *> -> bindArguments(bucket, arg) - else -> bindArguments(bucket, arg) + is Map<*, *> -> bindArguments(adaptor, arg) + is Pair<*, *> -> bindArguments(adaptor, arg) + else -> bindArguments(adaptor, arg) } } - return function.call(bucket) + return function.call(adaptor) } } diff --git a/src/main/kotlin/com/mapk/kmapper/PlainParameterForMap.kt b/src/main/kotlin/com/mapk/kmapper/PlainParameterForMap.kt index cc934e0..eab051e 100644 --- a/src/main/kotlin/com/mapk/kmapper/PlainParameterForMap.kt +++ b/src/main/kotlin/com/mapk/kmapper/PlainParameterForMap.kt @@ -1,16 +1,17 @@ package com.mapk.kmapper import com.mapk.core.EnumMapper +import com.mapk.core.ValueParameter import kotlin.reflect.KClass import kotlin.reflect.KFunction -import kotlin.reflect.KParameter import kotlin.reflect.full.isSuperclassOf -internal class PlainParameterForMap private constructor( - val param: KParameter, - private val clazz: KClass, +internal class PlainParameterForMap( + param: ValueParameter, private val parameterNameConverter: (String) -> String ) { + private val clazz: KClass = param.requiredClazz + private val javaClazz: Class by lazy { clazz.java } @@ -38,10 +39,4 @@ internal class PlainParameterForMap private constructor( else -> PlainKMapper(clazz, parameterNameConverter).map(value, PARAMETER_DUMMY) } } - - companion object { - fun newInstance(param: KParameter, parameterNameConverter: (String) -> String): PlainParameterForMap<*> { - return PlainParameterForMap(param, param.type.classifier as KClass<*>, parameterNameConverter) - } - } } diff --git a/src/test/kotlin/com/mapk/kmapper/KParameterFlattenTest.kt b/src/test/kotlin/com/mapk/kmapper/KParameterFlattenTest.kt new file mode 100644 index 0000000..844fd0c --- /dev/null +++ b/src/test/kotlin/com/mapk/kmapper/KParameterFlattenTest.kt @@ -0,0 +1,39 @@ +package com.mapk.kmapper + +import com.mapk.annotations.KParameterFlatten +import java.time.LocalDateTime +import org.junit.jupiter.api.Assertions.assertEquals +import org.junit.jupiter.api.DisplayName +import org.junit.jupiter.api.Test + +@DisplayName("KParameterFlattenテスト") +class KParameterFlattenTest { + data class InnerDst(val fooFoo: Int, val barBar: String) + data class Dst(@KParameterFlatten val bazBaz: InnerDst, val quxQux: LocalDateTime) + + private val expected = Dst(InnerDst(1, "str"), LocalDateTime.MIN) + + data class Src(val bazBazFooFoo: Int, val bazBazBarBar: String, val quxQux: LocalDateTime, val quuxQuux: Boolean) + private val src = Src(1, "str", LocalDateTime.MIN, false) + + @Test + @DisplayName("BoundKMapper") + fun boundKMapperTest() { + val result = BoundKMapper(::Dst, Src::class).map(src) + assertEquals(expected, result) + } + + @Test + @DisplayName("KMapper") + fun kMapperTest() { + val result = KMapper(::Dst).map(src) + assertEquals(expected, result) + } + + @Test + @DisplayName("PlainKMapper") + fun plainKMapperTest() { + val result = PlainKMapper(::Dst).map(src) + assertEquals(expected, result) + } +}