Skip to content
This repository was archived by the owner on Jan 20, 2023. It is now read-only.

Commit 0c1c158

Browse files
authored
Merge pull request #6 from k163377/feature
Added support for mapping with default arguments.
2 parents f4fe004 + a74776d commit 0c1c158

File tree

4 files changed

+97
-69
lines changed

4 files changed

+97
-69
lines changed
Lines changed: 31 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,33 +1,48 @@
11
package com.mapk.core
22

3+
import kotlin.reflect.KParameter
4+
5+
internal class BucketGenerator(
6+
private val bucket: Array<Any?>,
7+
private val instancePair: Pair<KParameter, Any?>?,
8+
private val initializationStatus: Int,
9+
private val initializeMask: List<Int>
10+
) {
11+
private val completionValue: Int = initializeMask.reduce { l, r -> l or r }
12+
private val bucketSize = bucket.size
13+
14+
fun generate(): ArgumentBucket {
15+
val tempMap = HashMap<KParameter, Any?>(bucketSize, 1.0f)
16+
instancePair?.run { tempMap[this.first] = this.second }
17+
18+
return ArgumentBucket(
19+
bucket.copyOf(),
20+
tempMap,
21+
initializationStatus,
22+
initializeMask,
23+
completionValue
24+
)
25+
}
26+
}
27+
328
class ArgumentBucket internal constructor(
429
internal val bucket: Array<Any?>,
30+
internal val bucketMap: MutableMap<KParameter, Any?>,
531
private var initializationStatus: Int,
632
private val initializeMask: List<Int>,
7-
// clone時の再計算を避けるため1回で済むようにデフォルト値化
8-
private val completionValue: Int = initializeMask.reduce { l, r -> l or r }
9-
) : Cloneable {
33+
private val completionValue: Int
34+
) {
1035
val isInitialized: Boolean get() = initializationStatus == completionValue
11-
val notInitializedParameterIndexes: List<Int> get() = initializeMask.indices.filter {
12-
initializationStatus and initializeMask[it] == 0
13-
}
1436

15-
fun setArgument(argument: Any?, index: Int) {
37+
fun setArgument(kParameter: KParameter, argument: Any?) {
38+
val index = kParameter.index
1639
val temp = initializationStatus or initializeMask[index]
1740

1841
// 先に入ったものを優先するため、初期化済みなら何もしない
1942
if (initializationStatus == temp) return
2043

44+
bucketMap[kParameter] = argument
2145
bucket[index] = argument
2246
initializationStatus = temp
2347
}
24-
25-
public override fun clone(): ArgumentBucket {
26-
return ArgumentBucket(
27-
bucket.copyOf(),
28-
initializationStatus,
29-
initializeMask,
30-
completionValue
31-
)
32-
}
3348
}

src/main/kotlin/com/mapk/core/KFunctionForCall.kt

Lines changed: 16 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -6,36 +6,32 @@ import kotlin.reflect.jvm.isAccessible
66

77
class KFunctionForCall<T>(private val function: KFunction<T>, instance: Any? = null) {
88
val parameters: List<KParameter> = function.parameters
9-
private val originalArgumentBucket: ArgumentBucket
9+
private val generator: BucketGenerator
1010

1111
init {
1212
if (parameters.isEmpty() || (instance != null && parameters.size == 1))
1313
throw IllegalArgumentException("This function is not require arguments.")
1414

1515
// この関数には確実にアクセスするためアクセシビリティ書き換え
1616
function.isAccessible = true
17-
originalArgumentBucket = if (instance != null) {
18-
ArgumentBucket(
19-
Array(parameters.size) { if (it == 0) instance else null },
20-
1,
21-
generateSequence(1) { it.shl(1) }
22-
.take(parameters.size)
23-
.toList()
24-
)
17+
18+
// 初期化処理の共通化のため先に初期化
19+
val tempArray = Array<Any?>(parameters.size) { null }
20+
val maskList = generateSequence(1) { it.shl(1) }.take(parameters.size).toList()
21+
22+
generator = if (instance != null) {
23+
tempArray[0] = instance
24+
25+
// 引数の1番目は初期化済みということでinitializationStatusは1スタート
26+
BucketGenerator(tempArray, parameters.first { it.kind == KParameter.Kind.INSTANCE } to instance, 1, maskList)
2527
} else {
26-
ArgumentBucket(
27-
Array(parameters.size) { null },
28-
0,
29-
generateSequence(1) { it.shl(1) }
30-
.take(parameters.size)
31-
.toList()
32-
)
28+
BucketGenerator(tempArray, null, 0, maskList)
3329
}
3430
}
3531

36-
fun getArgumentBucket(): ArgumentBucket = originalArgumentBucket.clone()
32+
fun getArgumentBucket(): ArgumentBucket = generator.generate()
3733

38-
fun call(argumentBucket: ArgumentBucket): T {
39-
return function.call(*argumentBucket.bucket)
40-
}
34+
fun call(argumentBucket: ArgumentBucket): T =
35+
if (argumentBucket.isInitialized) function.call(*argumentBucket.bucket)
36+
else function.callBy(argumentBucket.bucketMap)
4137
}

src/test/kotlin/com/mapk/core/ArgumentBucketTest.kt

Lines changed: 10 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ package com.mapk.core
22

33
import org.junit.jupiter.api.Assertions.assertEquals
44
import org.junit.jupiter.api.Assertions.assertFalse
5-
import org.junit.jupiter.api.Assertions.assertIterableEquals
65
import org.junit.jupiter.api.Assertions.assertTrue
76
import org.junit.jupiter.api.BeforeEach
87
import org.junit.jupiter.api.DisplayName
@@ -36,36 +35,11 @@ class ArgumentBucketTest {
3635
@Test
3736
@DisplayName("初期化後")
3837
fun isInitialized() {
39-
argumentBucket.setArgument(object {}, 0)
40-
argumentBucket.setArgument(object {}, 1)
41-
argumentBucket.setArgument(object {}, 2)
42-
assertTrue(argumentBucket.isInitialized)
43-
}
44-
}
45-
46-
@Nested
47-
@DisplayName("初期化されていないインデックス取得のテスト")
48-
inner class NotInitializedParameterIndexesTest {
49-
@Test
50-
@DisplayName("何もセットしていない場合")
51-
fun noArguments() {
52-
assertIterableEquals(listOf(0, 1, 2), argumentBucket.notInitializedParameterIndexes)
53-
}
38+
::sampleFunction.parameters.forEach {
39+
argumentBucket.setArgument(it, object {})
40+
}
5441

55-
@Test
56-
@DisplayName("1つセットした場合")
57-
fun singleArgument() {
58-
argumentBucket.setArgument(object {}, 1)
59-
assertIterableEquals(listOf(0, 2), argumentBucket.notInitializedParameterIndexes)
60-
}
61-
62-
@Test
63-
@DisplayName("全てセットした場合")
64-
fun fullArguments() {
65-
argumentBucket.setArgument(object {}, 0)
66-
argumentBucket.setArgument(object {}, 1)
67-
argumentBucket.setArgument(object {}, 2)
68-
assertIterableEquals(emptyList<Any?>(), argumentBucket.notInitializedParameterIndexes)
42+
assertTrue(argumentBucket.isInitialized)
6943
}
7044
}
7145

@@ -75,15 +49,18 @@ class ArgumentBucketTest {
7549
@Test
7650
@DisplayName("正常に追加した場合")
7751
fun setNewArgument() {
78-
argumentBucket.setArgument("argument", 0)
52+
val parameter = ::sampleFunction.parameters.first { it.index == 0 }
53+
argumentBucket.setArgument(parameter, "argument")
7954
assertEquals("argument", argumentBucket.bucket[0])
8055
}
8156

8257
@Test
8358
@DisplayName("同じインデックスに2回追加した場合")
8459
fun setArgumentTwice() {
85-
argumentBucket.setArgument("first", 0)
86-
argumentBucket.setArgument("second", 0)
60+
val parameter = ::sampleFunction.parameters.first { it.index == 0 }
61+
62+
argumentBucket.setArgument(parameter, "first")
63+
argumentBucket.setArgument(parameter, "second")
8764
assertEquals("first", argumentBucket.bucket[0])
8865
}
8966
}

src/test/kotlin/com/mapk/core/KFunctionForCallTest.kt

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
package com.mapk.core
22

3+
import kotlin.reflect.full.functions
4+
import org.junit.jupiter.api.Assertions.assertEquals
35
import org.junit.jupiter.api.DisplayName
46
import org.junit.jupiter.api.Nested
57
import org.junit.jupiter.api.Test
@@ -33,4 +35,42 @@ class KFunctionForCallTest {
3335
assertDoesNotThrow { KFunctionForCall(this::dummy2) }
3436
}
3537
}
38+
39+
@Nested
40+
@DisplayName("呼び出し関連テスト")
41+
inner class CallTest {
42+
@Test
43+
@DisplayName("コンパニオンオブジェクトから取得した場合")
44+
fun fromCompanionObject() {
45+
val function =
46+
Companion::class.functions.find { it.name == (KFunctionForCallTest)::declaredOnCompanionObject.name }!!
47+
48+
val kFunctionForCall = KFunctionForCall(function, Companion)
49+
50+
val bucket = kFunctionForCall.getArgumentBucket()
51+
kFunctionForCall.parameters.forEach { bucket.setArgument(it, it.index) }
52+
val result = kFunctionForCall.call(bucket)
53+
assertEquals("12", result)
54+
}
55+
56+
private fun func(key: String, value: String = "default"): Pair<String, String> = key to value
57+
58+
@Test
59+
@DisplayName("デフォルト値を用いる場合")
60+
fun useDefaultValue() {
61+
val kFunctionForCall = KFunctionForCall(::func)
62+
val argumentBucket = kFunctionForCall.getArgumentBucket()
63+
64+
::func.parameters.forEach { if (!it.isOptional) argumentBucket.setArgument(it, it.name) }
65+
66+
val result = kFunctionForCall.call(argumentBucket)
67+
assertEquals("key" to "default", result)
68+
}
69+
}
70+
71+
companion object {
72+
fun declaredOnCompanionObject(arg1: Any, arg2: Any): String {
73+
return arg1.toString() + arg2.toString()
74+
}
75+
}
3676
}

0 commit comments

Comments
 (0)