diff --git a/build.gradle.kts b/build.gradle.kts index 8a465a5..3087eb3 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -33,12 +33,16 @@ dependencies { testImplementation(group = "org.junit.jupiter", name = "junit-jupiter", version = "5.6.0") { exclude(group = "org.junit.vintage", module = "junit-vintage-engine") } + // 現状プロパティ名の変換はテストでしか使っていないのでtestImplementation + // https://mvnrepository.com/artifact/com.google.guava/guava + testImplementation(group = "com.google.guava", name = "guava", version = "28.2-jre") } tasks.compileKotlin { dependsOn("ktlintFormat") kotlinOptions { jvmTarget = "1.8" + allWarningsAsErrors = true } } diff --git a/src/main/kotlin/com/wrongwrong/mapk/core/CompanionKFunction.kt b/src/main/kotlin/com/wrongwrong/mapk/core/KFunctionWithInstance.kt similarity index 68% rename from src/main/kotlin/com/wrongwrong/mapk/core/CompanionKFunction.kt rename to src/main/kotlin/com/wrongwrong/mapk/core/KFunctionWithInstance.kt index 564c8b3..d3fa059 100644 --- a/src/main/kotlin/com/wrongwrong/mapk/core/CompanionKFunction.kt +++ b/src/main/kotlin/com/wrongwrong/mapk/core/KFunctionWithInstance.kt @@ -3,11 +3,17 @@ package com.wrongwrong.mapk.core import kotlin.reflect.KFunction import kotlin.reflect.KParameter import kotlin.reflect.full.instanceParameter +import kotlin.reflect.jvm.isAccessible -internal class CompanionKFunction( +internal class KFunctionWithInstance( private val function: KFunction, private val instance: Any ) : KFunction by function { + init { + // このインスタンスを生成している時点でfunctionにアクセスしたい状況なので、アクセシビリティはここでセットする + function.isAccessible = true + } + private val instanceParam by lazy { mapOf(function.instanceParameter!! to instance) } override val parameters: List by lazy { diff --git a/src/main/kotlin/com/wrongwrong/mapk/core/KMapper.kt b/src/main/kotlin/com/wrongwrong/mapk/core/KMapper.kt index e526d2b..1083db6 100644 --- a/src/main/kotlin/com/wrongwrong/mapk/core/KMapper.kt +++ b/src/main/kotlin/com/wrongwrong/mapk/core/KMapper.kt @@ -5,7 +5,6 @@ import com.wrongwrong.mapk.annotations.KPropertyAlias import com.wrongwrong.mapk.annotations.KPropertyIgnore import kotlin.reflect.KClass import kotlin.reflect.KFunction -import kotlin.reflect.KParameter import kotlin.reflect.KProperty1 import kotlin.reflect.KVisibility import kotlin.reflect.full.companionObjectInstance @@ -20,16 +19,12 @@ class KMapper(private val function: KFunction, propertyNameConverter getTarget(clazz), propertyNameConverter ) - private val parameters: Set> + private val parameters: Set> = function.parameters + .map { ParameterForMap.newInstance(it, propertyNameConverter) } + .toSet() init { - val params: List = function.parameters - - if (params.isEmpty()) throw IllegalArgumentException("This function is not require arguments.") - - parameters = params - .map { ParameterForMap.newInstance(it, propertyNameConverter) } - .toSet() + if (parameters.isEmpty()) throw IllegalArgumentException("This function is not require arguments.") // private関数に対してもマッピングできなければ何かと不都合があるため、accessibleは書き換える function.isAccessible = true @@ -44,6 +39,12 @@ class KMapper(private val function: KFunction, propertyNameConverter }.let { function.callBy(it) } } + fun map(srcPair: Pair): T = parameters + .single { it.name == srcPair.first } + .let { + function.callBy(mapOf(it.param to srcPair.second?.let { value -> mapObject(it, value) })) + } + fun map(src: Any): T { val srcMap: Map> = src::class.memberProperties.filterTargets().associate { property -> @@ -117,11 +118,7 @@ internal fun getTarget(clazz: KClass): KFunction { clazz.companionObjectInstance?.let { companionObject -> companionObject::class.functions .filter { it.annotations.any { annotation -> annotation is KConstructor } } - .map { - // isAccessibleの書き換えはKotlinの都合で先に行う必要が有る - it.isAccessible = true - CompanionKFunction(it, companionObject) as KFunction - } + .map { KFunctionWithInstance(it, companionObject) as KFunction } } ?: emptyList() val constructors: List> = factoryConstructor + clazz.constructors diff --git a/src/main/kotlin/com/wrongwrong/mapk/core/ParameterForMap.kt b/src/main/kotlin/com/wrongwrong/mapk/core/ParameterForMap.kt index a6e5bc0..8df3d96 100644 --- a/src/main/kotlin/com/wrongwrong/mapk/core/ParameterForMap.kt +++ b/src/main/kotlin/com/wrongwrong/mapk/core/ParameterForMap.kt @@ -66,9 +66,7 @@ private fun creatorsFromCompanionObject(clazz: KClass): Set annotation is KConverter } } .map { function -> - // isAccessibleの書き換えはKotlinの都合で先に行う必要が有る - function.isAccessible = true - val func: KFunction = CompanionKFunction(function, companionObject) as KFunction + val func: KFunction = KFunctionWithInstance(function, companionObject) as KFunction (func.parameters.single().type.classifier as KClass<*>) to func }.toSet() diff --git a/src/test/kotlin/mapk/core/EnumMappingTest.kt b/src/test/kotlin/mapk/core/EnumMappingTest.kt new file mode 100644 index 0000000..8edd826 --- /dev/null +++ b/src/test/kotlin/mapk/core/EnumMappingTest.kt @@ -0,0 +1,28 @@ +@file:Suppress("unused") + +package mapk.core + +import com.wrongwrong.mapk.core.KMapper +import org.junit.jupiter.api.Assertions.assertEquals +import org.junit.jupiter.api.DisplayName +import org.junit.jupiter.params.ParameterizedTest +import org.junit.jupiter.params.provider.EnumSource + +enum class JvmLanguage { + Java, Scala, Groovy, Kotlin +} + +private class EnumMappingDst(val language: JvmLanguage?) + +@DisplayName("文字列 -> Enumのマッピングテスト") +class EnumMappingTest { + private val mapper = KMapper(EnumMappingDst::class) + + @ParameterizedTest(name = "Non-Null要求") + @EnumSource(value = JvmLanguage::class) + fun test(language: JvmLanguage) { + val result = mapper.map("language" to language.name) + + assertEquals(language, result.language) + } +} diff --git a/src/test/kotlin/mapk/core/PropertyNameConverterTest.kt b/src/test/kotlin/mapk/core/PropertyNameConverterTest.kt new file mode 100644 index 0000000..9f48670 --- /dev/null +++ b/src/test/kotlin/mapk/core/PropertyNameConverterTest.kt @@ -0,0 +1,24 @@ +package mapk.core + +import com.google.common.base.CaseFormat +import com.wrongwrong.mapk.core.KMapper +import org.junit.jupiter.api.Assertions.assertEquals +import org.junit.jupiter.api.DisplayName +import org.junit.jupiter.api.Test + +private class CamelCaseDst(val camelCase: String) + +@DisplayName("プロパティ名変換のテスト") +class PropertyNameConverterTest { + @Test + @DisplayName("スネークケースsrc -> キャメルケースdst") + fun test() { + val expected = "snakeCase" + val src = mapOf("camel_case" to expected) + + val mapper = KMapper(CamelCaseDst::class) { CaseFormat.LOWER_CAMEL.to(CaseFormat.LOWER_UNDERSCORE, it) } + val result = mapper.map(src) + + assertEquals(expected, result.camelCase) + } +} diff --git a/src/test/kotlin/mapk/core/StringMappingTest.kt b/src/test/kotlin/mapk/core/StringMappingTest.kt new file mode 100644 index 0000000..cb3b2a7 --- /dev/null +++ b/src/test/kotlin/mapk/core/StringMappingTest.kt @@ -0,0 +1,17 @@ +package mapk.core + +import com.wrongwrong.mapk.core.KMapper +import org.junit.jupiter.api.Assertions.assertEquals +import org.junit.jupiter.api.DisplayName +import org.junit.jupiter.api.Test + +private data class StringMappingDst(val value: String) + +@DisplayName("文字列に対してtoStringしたものを渡すテスト") +class StringMappingTest { + @Test + fun test() { + val result: StringMappingDst = KMapper(StringMappingDst::class).map("value" to 1) + assertEquals("1", result.value) + } +}