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
4 changes: 4 additions & 0 deletions build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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<T>(
internal class KFunctionWithInstance<T>(
private val function: KFunction<T>,
private val instance: Any
) : KFunction<T> by function {
init {
// このインスタンスを生成している時点でfunctionにアクセスしたい状況なので、アクセシビリティはここでセットする
function.isAccessible = true
}

private val instanceParam by lazy { mapOf(function.instanceParameter!! to instance) }

override val parameters: List<KParameter> by lazy {
Expand Down
25 changes: 11 additions & 14 deletions src/main/kotlin/com/wrongwrong/mapk/core/KMapper.kt
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -20,16 +19,12 @@ class KMapper<T : Any>(private val function: KFunction<T>, propertyNameConverter
getTarget(clazz), propertyNameConverter
)

private val parameters: Set<ParameterForMap<*>>
private val parameters: Set<ParameterForMap<*>> = function.parameters
.map { ParameterForMap.newInstance(it, propertyNameConverter) }
.toSet()

init {
val params: List<KParameter> = 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
Expand All @@ -44,6 +39,12 @@ class KMapper<T : Any>(private val function: KFunction<T>, propertyNameConverter
}.let { function.callBy(it) }
}

fun map(srcPair: Pair<String, Any?>): 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<String, KProperty1.Getter<*, *>> =
src::class.memberProperties.filterTargets().associate { property ->
Expand Down Expand Up @@ -117,11 +118,7 @@ internal fun <T : Any> getTarget(clazz: KClass<T>): KFunction<T> {
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<T>
}
.map { KFunctionWithInstance(it, companionObject) as KFunction<T> }
} ?: emptyList()

val constructors: List<KFunction<T>> = factoryConstructor + clazz.constructors
Expand Down
4 changes: 1 addition & 3 deletions src/main/kotlin/com/wrongwrong/mapk/core/ParameterForMap.kt
Original file line number Diff line number Diff line change
Expand Up @@ -66,9 +66,7 @@ private fun <T : Any> creatorsFromCompanionObject(clazz: KClass<T>): Set<Pair<KC
companionObject::class.functions
.filter { it.annotations.any { annotation -> annotation is KConverter } }
.map { function ->
// isAccessibleの書き換えはKotlinの都合で先に行う必要が有る
function.isAccessible = true
val func: KFunction<T> = CompanionKFunction(function, companionObject) as KFunction<T>
val func: KFunction<T> = KFunctionWithInstance(function, companionObject) as KFunction<T>

(func.parameters.single().type.classifier as KClass<*>) to func
}.toSet()
Expand Down
28 changes: 28 additions & 0 deletions src/test/kotlin/mapk/core/EnumMappingTest.kt
Original file line number Diff line number Diff line change
@@ -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)
}
}
24 changes: 24 additions & 0 deletions src/test/kotlin/mapk/core/PropertyNameConverterTest.kt
Original file line number Diff line number Diff line change
@@ -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)
}
}
17 changes: 17 additions & 0 deletions src/test/kotlin/mapk/core/StringMappingTest.kt
Original file line number Diff line number Diff line change
@@ -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)
}
}