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

Commit 6a7eaed

Browse files
authored
Merge pull request #8 from k163377/feature
Modifies.
2 parents d87d4d1 + d24d369 commit 6a7eaed

File tree

8 files changed

+123
-9
lines changed

8 files changed

+123
-9
lines changed

build.gradle.kts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ plugins {
66
}
77

88
group = "com.mapk"
9-
version = "0.1"
9+
version = "0.6"
1010

1111
java {
1212
sourceCompatibility = JavaVersion.VERSION_1_8
@@ -29,6 +29,8 @@ repositories {
2929
dependencies {
3030
implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8")
3131
implementation(kotlin("reflect"))
32+
// https://mvnrepository.com/artifact/org.jetbrains/annotations
33+
implementation(group = "org.jetbrains", name = "annotations", version = "19.0.0")
3234

3335
// https://mvnrepository.com/artifact/org.junit.jupiter/junit-jupiter
3436
testImplementation(group = "org.junit.jupiter", name = "junit-jupiter", version = "5.6.0") {

src/main/java/com/mapk/core/BucketGenerator.java

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,19 +2,24 @@
22

33
import kotlin.Pair;
44
import kotlin.reflect.KParameter;
5+
import org.jetbrains.annotations.NotNull;
6+
import org.jetbrains.annotations.Nullable;
57

68
import java.util.ArrayList;
79
import java.util.List;
810

911
class BucketGenerator {
1012
private final int initializationStatus;
13+
@NotNull
1114
private final List<Integer> initializeMask;
1215
private final int completionValue;
1316

17+
@NotNull
1418
private final KParameter[] keyArray;
19+
@NotNull
1520
private final Object[] valueArray;
1621

17-
BucketGenerator(int capacity, Pair<KParameter, Object> instancePair) {
22+
BucketGenerator(int capacity, @Nullable Pair<KParameter, Object> instancePair) {
1823
keyArray = new KParameter[capacity];
1924
valueArray = new Object[capacity];
2025

@@ -38,6 +43,7 @@ class BucketGenerator {
3843
this.completionValue = completionValue;
3944
}
4045

46+
@NotNull
4147
ArgumentBucket generate() {
4248
return new ArgumentBucket(
4349
keyArray.clone(),

src/main/java/com/mapk/core/EnumMapper.java

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
package com.mapk.core;
22

3+
import org.jetbrains.annotations.NotNull;
4+
import org.jetbrains.annotations.Nullable;
5+
36
public class EnumMapper {
47
/**
58
* Kotlinの型推論バグでクラスからvalueOfが使えないため、ここだけJavaで書いている(型引数もT extends Enumでは書けなかった)
@@ -8,8 +11,9 @@ public class EnumMapper {
811
* @param <T> enumClass
912
* @return Enum.valueOf
1013
*/
14+
@Nullable
1115
@SuppressWarnings({"unchecked", "rawtypes"})
12-
public static <T> T getEnum(Class<T> clazz, String value) {
16+
public static <T> T getEnum(@NotNull Class<T> clazz, @Nullable String value) {
1317
if (value == null || value.isEmpty()) {
1418
return null;
1519
}

src/main/kotlin/com/mapk/annotations/KConverter.kt renamed to src/main/kotlin/com/mapk/annotations/KConstructor.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,4 +3,4 @@ package com.mapk.annotations
33
@Target(AnnotationTarget.CONSTRUCTOR, AnnotationTarget.FUNCTION)
44
@Retention(AnnotationRetention.RUNTIME)
55
@MustBeDocumented
6-
annotation class KConverter
6+
annotation class KConstructor

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

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

3+
import java.util.Objects
34
import kotlin.reflect.KParameter
45

56
class ArgumentBucket internal constructor(
@@ -26,9 +27,7 @@ class ArgumentBucket internal constructor(
2627
return keyArray[key.index] != null
2728
}
2829

29-
override fun containsValue(value: Any?): Boolean {
30-
throw UnsupportedOperationException()
31-
}
30+
override fun containsValue(value: Any?): Boolean = valueArray.any { Objects.equals(value, it) }
3231

3332
override fun get(key: KParameter): Any? = valueArray[key.index]
3433
fun getByIndex(key: Int): Any? =
@@ -42,7 +41,7 @@ class ArgumentBucket internal constructor(
4241
override val keys: MutableSet<KParameter>
4342
get() = keyArray.filterNotNull().toMutableSet()
4443
override val values: MutableCollection<Any?>
45-
get() = throw UnsupportedOperationException()
44+
get() = valueArray.filterIndexed { i, _ -> initializationStatus and initializeMask[i] != 0 }.toMutableList()
4645

4746
fun putIfAbsent(key: KParameter, value: Any?) {
4847
val index = key.index
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
package com.mapk.core
2+
3+
import com.mapk.annotations.KParameterAlias
4+
import kotlin.reflect.KParameter
5+
import kotlin.reflect.full.findAnnotation
6+
7+
/**
8+
* パラメータからエイリアスもしくはプロパティ名を取得する関数
9+
*/
10+
fun KParameter.getAliasOrName(): String? = findAnnotation<KParameterAlias>()?.value ?: name

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

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,15 @@
11
package com.mapk.core
22

3+
import com.mapk.annotations.KConstructor
4+
import kotlin.reflect.KClass
35
import kotlin.reflect.KFunction
46
import kotlin.reflect.KParameter
7+
import kotlin.reflect.full.companionObjectInstance
8+
import kotlin.reflect.full.functions
9+
import kotlin.reflect.full.primaryConstructor
510
import kotlin.reflect.jvm.isAccessible
611

7-
class KFunctionForCall<T>(private val function: KFunction<T>, instance: Any? = null) {
12+
class KFunctionForCall<T>(internal val function: KFunction<T>, instance: Any? = null) {
813
val parameters: List<KParameter> = function.parameters
914
private val generator: BucketGenerator
1015

@@ -28,3 +33,23 @@ class KFunctionForCall<T>(private val function: KFunction<T>, instance: Any? = n
2833
if (argumentBucket.isInitialized) function.call(*argumentBucket.valueArray)
2934
else function.callBy(argumentBucket)
3035
}
36+
37+
@Suppress("UNCHECKED_CAST")
38+
fun <T : Any> KClass<T>.toKConstructor(): KFunctionForCall<T> {
39+
val factoryConstructor: List<KFunctionForCall<T>> =
40+
this.companionObjectInstance?.let { companionObject ->
41+
companionObject::class.functions
42+
.filter { it.annotations.any { annotation -> annotation is KConstructor } }
43+
.map { KFunctionForCall(it, companionObject) as KFunctionForCall<T> }
44+
} ?: emptyList()
45+
46+
val constructors: List<KFunctionForCall<T>> = factoryConstructor + this.constructors
47+
.filter { it.annotations.any { annotation -> annotation is KConstructor } }
48+
.map { KFunctionForCall(it) }
49+
50+
if (constructors.size == 1) return constructors.single()
51+
52+
if (constructors.isEmpty()) return KFunctionForCall(this.primaryConstructor!!)
53+
54+
throw IllegalArgumentException("Find multiple target.")
55+
}
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
package com.mapk.core
2+
3+
import com.mapk.annotations.KConstructor
4+
import kotlin.reflect.KFunction
5+
import kotlin.reflect.full.memberProperties
6+
import kotlin.reflect.full.primaryConstructor
7+
import kotlin.reflect.jvm.isAccessible
8+
import org.junit.jupiter.api.Assertions
9+
import org.junit.jupiter.api.DisplayName
10+
import org.junit.jupiter.api.Test
11+
import org.junit.jupiter.api.assertThrows
12+
13+
@Suppress("UNCHECKED_CAST", "unused")
14+
@DisplayName("クラスからのコンストラクタ抽出関連テスト")
15+
class ToKConstructorTest {
16+
private class SecondaryConstructorDst(val argument: Int) {
17+
@KConstructor
18+
constructor(argument: Number) : this(argument.toInt())
19+
}
20+
21+
class CompanionFactoryDst(val argument: IntArray) {
22+
companion object {
23+
@KConstructor
24+
fun factory(csv: String): CompanionFactoryDst {
25+
return csv.split(",").map { it.toInt() }.toIntArray().let { CompanionFactoryDst(it) }
26+
}
27+
}
28+
}
29+
private class ConstructorDst(val argument: String)
30+
class MultipleConstructorDst @KConstructor constructor(val argument: Int) {
31+
@KConstructor
32+
constructor(argument: String) : this(argument.toInt())
33+
}
34+
35+
private fun <T : Any> KFunctionForCall<T>.getTargetFunction(): KFunction<T> {
36+
return this::class.memberProperties.first { it.name == "function" }.getter.let {
37+
it.isAccessible = true
38+
it.call(this) as KFunction<T>
39+
}
40+
}
41+
42+
@Test
43+
@DisplayName("セカンダリコンストラクタからの取得テスト")
44+
fun testGetFromSecondaryConstructor() {
45+
val function = SecondaryConstructorDst::class.toKConstructor().function
46+
Assertions.assertTrue(function.annotations.any { it is KConstructor })
47+
}
48+
49+
@Test
50+
@DisplayName("ファクトリーメソッドからの取得テスト")
51+
fun testGetFromFactoryMethod() {
52+
val function = CompanionFactoryDst::class.toKConstructor().function
53+
Assertions.assertTrue(function.annotations.any { it is KConstructor })
54+
}
55+
56+
@Test
57+
@DisplayName("無指定でプライマリコンストラクタからの取得テスト")
58+
fun testGetFromPrimaryConstructor() {
59+
val function = ConstructorDst::class.toKConstructor().function
60+
Assertions.assertEquals(ConstructorDst::class.primaryConstructor, function)
61+
}
62+
63+
@Test
64+
@DisplayName("対象を複数指定した場合のテスト")
65+
fun testMultipleDeclareConstructor() {
66+
assertThrows<IllegalArgumentException> { MultipleConstructorDst::class.toKConstructor() }
67+
}
68+
}

0 commit comments

Comments
 (0)