Skip to content

Commit

Permalink
Add better support for Kotlin types
Browse files Browse the repository at this point in the history
  • Loading branch information
seongahjo committed Jan 17, 2024
1 parent af7f818 commit a6ef9a6
Show file tree
Hide file tree
Showing 7 changed files with 122 additions and 51 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -25,10 +25,11 @@ import com.navercorp.fixturemonkey.api.property.PropertyGenerator
import com.navercorp.fixturemonkey.api.type.Types
import com.navercorp.fixturemonkey.kotlin.property.InterfaceKFunctionProperty
import com.navercorp.fixturemonkey.kotlin.type.getPropertyName
import com.navercorp.fixturemonkey.kotlin.type.isKotlinClass
import com.navercorp.fixturemonkey.kotlin.type.kotlinMemberFunctions
import org.apiguardian.api.API
import org.apiguardian.api.API.Status
import kotlin.reflect.KParameter.Kind.INSTANCE
import kotlin.reflect.full.memberFunctions
import kotlin.reflect.full.memberProperties
import kotlin.reflect.jvm.javaMethod
import kotlin.reflect.jvm.javaType
Expand All @@ -43,7 +44,7 @@ class InterfaceKFunctionPropertyGenerator : PropertyGenerator {
val type = Types.getActualType(property.type)

if (type.isKotlinClass()) {
val methods = type.kotlin.memberFunctions
val methods = type.kotlinMemberFunctions()
.filter { it.parameters.none { parameter -> parameter.kind != INSTANCE } }
.filter { !DATA_CLASS_METHOD_NAMES.contains(it.name) }
.filter { it.returnType.javaType != Void.TYPE }
Expand Down Expand Up @@ -71,15 +72,10 @@ class InterfaceKFunctionPropertyGenerator : PropertyGenerator {
return JAVA_METHOD_PROPERTY_GENERATOR.generateChildProperties(property)
}

private fun Class<*>.isKotlinClass(): Boolean =
declaredAnnotations.any { it.annotationClass.java.name == METADATA_FQN_NAME }

companion object {
private val JAVA_METHOD_PROPERTY_GENERATOR =
NoArgumentInterfaceJavaMethodPropertyGenerator()

private val DATA_CLASS_METHOD_NAMES = setOf("toString", "hashCode")

private const val METADATA_FQN_NAME = "kotlin.Metadata"
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,8 @@ import com.navercorp.fixturemonkey.kotlin.introspector.CompanionObjectFactoryMet
import com.navercorp.fixturemonkey.kotlin.introspector.KotlinPropertyArbitraryIntrospector
import com.navercorp.fixturemonkey.kotlin.property.KotlinPropertyGenerator
import com.navercorp.fixturemonkey.kotlin.type.actualType
import com.navercorp.fixturemonkey.kotlin.type.cachedKotlin
import com.navercorp.fixturemonkey.kotlin.type.cachedMemberFunctions
import com.navercorp.fixturemonkey.kotlin.type.declaredConstructor
import com.navercorp.fixturemonkey.kotlin.type.toTypeReference
import java.lang.reflect.AnnotatedType
Expand All @@ -55,7 +57,6 @@ import kotlin.reflect.KFunction
import kotlin.reflect.KParameter
import kotlin.reflect.KProperty
import kotlin.reflect.full.companionObject
import kotlin.reflect.full.memberFunctions
import kotlin.reflect.jvm.isAccessible
import kotlin.reflect.jvm.javaType

Expand Down Expand Up @@ -157,8 +158,8 @@ class KotlinInstantiatorProcessor :
val factoryMethodName = instantiator.factoryMethodName
val inputParameterTypes = instantiator.inputParameterTypes.map { it.type.actualType() }
.toTypedArray()
val kotlinType = type.kotlin
val companionMethod = kotlinType.companionObject?.memberFunctions
val kotlinType = type.cachedKotlin()
val companionMethod = kotlinType.companionObject?.cachedMemberFunctions()
?.findDeclaredMemberFunction(factoryMethodName, inputParameterTypes)
?: throw IllegalArgumentException("Given type $kotlinType has no static factory method.")

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import com.navercorp.fixturemonkey.api.generator.ArbitraryGeneratorContext
import com.navercorp.fixturemonkey.api.introspector.ArbitraryIntrospector
import com.navercorp.fixturemonkey.api.introspector.ArbitraryIntrospectorResult
import com.navercorp.fixturemonkey.api.type.Types
import com.navercorp.fixturemonkey.kotlin.type.cachedKotlin
import kotlin.reflect.KFunction
import kotlin.reflect.full.companionObjectInstance

Expand All @@ -31,7 +32,7 @@ class CompanionObjectFactoryMethodIntrospector(
) : ArbitraryIntrospector {
override fun introspect(context: ArbitraryGeneratorContext): ArbitraryIntrospectorResult {
val type = Types.getActualType(context.resolvedType)
val kotlinType = type.kotlin
val kotlinType = type.cachedKotlin()
val propertyNames = context.children.map { it.objectProperty.property.name }

return ArbitraryIntrospectorResult(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import com.navercorp.fixturemonkey.api.generator.ArbitraryGeneratorContext
import com.navercorp.fixturemonkey.api.introspector.ArbitraryIntrospector
import com.navercorp.fixturemonkey.api.introspector.ArbitraryIntrospectorResult
import com.navercorp.fixturemonkey.api.type.Types
import com.navercorp.fixturemonkey.kotlin.type.cachedKotlin
import kotlin.reflect.KMutableProperty
import kotlin.reflect.KVisibility
import kotlin.reflect.full.createInstance
Expand All @@ -30,7 +31,7 @@ import kotlin.reflect.full.declaredMemberProperties
class KotlinPropertyArbitraryIntrospector : ArbitraryIntrospector {
override fun introspect(context: ArbitraryGeneratorContext): ArbitraryIntrospectorResult {
val type = Types.getActualType(context.resolvedType)
val kotlinType = type.kotlin
val kotlinType = type.cachedKotlin()

val generated = if (context.generated != CombinableArbitrary.NOT_GENERATED) {
context.generated
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
/*
* Fixture Monkey
*
* Copyright (c) 2021-present NAVER Corp.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package com.navercorp.fixturemonkey.kotlin.type

import com.navercorp.fixturemonkey.api.container.ConcurrentLruCache
import kotlin.reflect.KClass
import kotlin.reflect.KFunction
import kotlin.reflect.full.memberFunctions

private val CONSTRUCTORS = ConcurrentLruCache<KClass<*>, Collection<KFunction<*>>>(2048)
private val KOTLIN_TYPES = ConcurrentLruCache<Class<*>, KClass<*>>(2048)
private val MEMBER_FUNCTIONS = ConcurrentLruCache<KClass<*>, Collection<KFunction<*>>>(2048)
fun Class<*>.declaredKotlinConstructors(): Collection<KFunction<*>> =
CONSTRUCTORS.computeIfAbsent(this.cachedKotlin()) { it.constructors }

fun Class<*>.cachedKotlin(): KClass<*> = KOTLIN_TYPES.computeIfAbsent(this) { it.kotlin }

fun Class<*>.kotlinMemberFunctions(): Collection<KFunction<*>> =
MEMBER_FUNCTIONS.computeIfAbsent(this.cachedKotlin()) { cachedKotlin().memberFunctions }

fun KClass<*>.cachedMemberFunctions(): Collection<KFunction<*>> =
MEMBER_FUNCTIONS.computeIfAbsent(this) { this.memberFunctions }
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
/*
* Fixture Monkey
*
* Copyright (c) 2021-present NAVER Corp.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package com.navercorp.fixturemonkey.kotlin.type

import com.navercorp.fixturemonkey.api.type.TypeReference
import com.navercorp.fixturemonkey.api.type.Types
import java.lang.reflect.AnnotatedType
import java.lang.reflect.Type
import kotlin.reflect.KType
import kotlin.reflect.jvm.javaType

/**
* Provides a collection of extension functions for Type,
* facilitating the conversion and handling of types in Kotlin reflection.
*
* These utilities enhance the interoperability between Kotlin's type system and Java's
* type system, particularly focusing on [TypeReference].
*/
fun Type.actualType(): Class<*> = Types.getActualType(this)

fun <T> Class<T>.toAnnotatedType(): AnnotatedType = Types.generateAnnotatedTypeWithoutAnnotation(this)

fun Type.toAnnotatedType(): AnnotatedType = Types.generateAnnotatedTypeWithoutAnnotation(this)

fun <T> Class<T>.toTypeReference(): TypeReference<T> = object : TypeReference<T>() {
override fun getType(): Type {
return this@toTypeReference
}

override fun getAnnotatedType(): AnnotatedType {
return Types.generateAnnotatedTypeWithoutAnnotation(this@toTypeReference)
}
}

fun Type.toTypeReference(): TypeReference<*> = object : TypeReference<Any?>() {
override fun getType(): Type {
return this@toTypeReference
}

override fun getAnnotatedType(): AnnotatedType {
return Types.generateAnnotatedTypeWithoutAnnotation(this@toTypeReference)
}
}

fun KType.toTypeReference(): TypeReference<*> = object : TypeReference<Any?>() {
override fun getType(): Type {
return this@toTypeReference.javaType
}

override fun getAnnotatedType(): AnnotatedType {
return this@toTypeReference.javaType.toAnnotatedType()
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@
package com.navercorp.fixturemonkey.kotlin.type

import com.navercorp.fixturemonkey.api.type.AnnotatedTypes
import com.navercorp.fixturemonkey.api.type.TypeReference
import com.navercorp.fixturemonkey.api.type.Types
import org.apiguardian.api.API
import java.lang.reflect.AnnotatedParameterizedType
Expand All @@ -30,13 +29,11 @@ import java.lang.reflect.TypeVariable
import kotlin.reflect.KFunction
import kotlin.reflect.KParameter
import kotlin.reflect.KProperty
import kotlin.reflect.KType
import kotlin.reflect.full.findAnnotations
import kotlin.reflect.jvm.javaField
import kotlin.reflect.jvm.javaType

@API(since = "0.4.0", status = API.Status.MAINTAINED)
@OptIn(ExperimentalStdlibApi::class)
fun getAnnotatedType(ownerType: AnnotatedType, kProperty: KProperty<*>): AnnotatedType {
val type = kProperty.returnType.javaType
val annotations = mutableSetOf<Annotation>()
Expand Down Expand Up @@ -138,45 +135,11 @@ fun KFunction<*>.getPropertyName(): String {
}
}

fun Type.actualType(): Class<*> = Types.getActualType(this)

fun <T> Class<T>.toAnnotatedType(): AnnotatedType = Types.generateAnnotatedTypeWithoutAnnotation(this)

fun Type.toAnnotatedType(): AnnotatedType = Types.generateAnnotatedTypeWithoutAnnotation(this)

fun <T> Class<T>.toTypeReference(): TypeReference<T> = object : TypeReference<T>() {
override fun getType(): Type {
return this@toTypeReference
}

override fun getAnnotatedType(): AnnotatedType {
return Types.generateAnnotatedTypeWithoutAnnotation(this@toTypeReference)
}
}

fun Type.toTypeReference(): TypeReference<*> = object : TypeReference<Any?>() {
override fun getType(): Type {
return this@toTypeReference
}

override fun getAnnotatedType(): AnnotatedType {
return Types.generateAnnotatedTypeWithoutAnnotation(this@toTypeReference)
}
}

fun KType.toTypeReference(): TypeReference<*> = object : TypeReference<Any?>() {
override fun getType(): Type {
return this@toTypeReference.javaType
}

override fun getAnnotatedType(): AnnotatedType {
return this@toTypeReference.javaType.toAnnotatedType()
}
}
fun Class<*>.isKotlinClass(): Boolean = declaredAnnotations.any { it.annotationClass.java.name == METADATA_FQN_NAME }

fun Class<*>.declaredConstructor(
vararg arguments: Class<*>,
): KFunction<*> = this.kotlin.constructors
): KFunction<*> = this.declaredKotlinConstructors()
.firstOrNull { constructor ->
Types.isAssignableTypes(
arguments,
Expand All @@ -185,3 +148,5 @@ fun Class<*>.declaredConstructor(
.toTypedArray(),
)
} ?: this.kotlin.constructors.first()

private const val METADATA_FQN_NAME = "kotlin.Metadata"

0 comments on commit a6ef9a6

Please sign in to comment.