Skip to content

Instantly share code, notes, and snippets.

@CanerPatir
Created March 11, 2020 13:10
Show Gist options
  • Select an option

  • Save CanerPatir/f29c3305af47749e1675cd9aa0a782da to your computer and use it in GitHub Desktop.

Select an option

Save CanerPatir/f29c3305af47749e1675cd9aa0a782da to your computer and use it in GitHub Desktop.
package com.trendyol.inventorycenterapi.helper
import com.trendyol.inventorycenterapi.domain.inventory.CurrencyCode
import java.lang.reflect.*
import java.time.Instant
import kotlin.random.Random
import kotlin.reflect.full.createType
import kotlin.reflect.KClass
import kotlin.reflect.KType
import kotlin.reflect.KTypeProjection
import kotlin.reflect.KVariance
class Randomizer {
class NoUsableConstructor() : Error()
companion object {
inline fun <reified T: Any> makeRandomInstance(): T {
return makeRandomInstance(T::class, getKType<T>()) as T
}
private var random: Random = Random
fun makeRandomInstance(type: KType): Any? {
return makeRandomInstance(type.classifier as KClass<*>, type)
}
fun makeRandomInstance(clazz: KClass<*>, type: KType): Any? {
if(type.isMarkedNullable && random.nextBoolean()) {
return null
}
val primitive = makeStandardInstanceOrNull(clazz, type)
if (primitive != null) {
return primitive
}
val constructors = clazz.constructors.shuffled(random)
for (constructor in constructors) {
try {
val arguments = constructor.parameters
.map { makeRandomInstance(it.type) }
.toTypedArray()
return constructor.call(*arguments)
} catch (e: Throwable) {
e.printStackTrace()
// no-op. We catch any possible error here that might occur during class creation
}
}
throw NoUsableConstructor()
}
private fun makeStandardInstanceOrNull(clazz: KClass<*>, type: KType) = when (clazz) {
Any::class -> "Anything" // We should randomize among some types
Int::class -> random.nextInt()
Long::class -> random.nextLong()
Double::class -> random.nextDouble()
Float::class -> random.nextFloat()
Char::class -> makeRandomChar()
String::class -> makeRandomString()
Instant::class -> Instant.now()
List::class, Collection::class -> makeRandomList(type)
Map::class -> makeRandomMap(type)
CurrencyCode::class -> CurrencyCode.TRY
else -> null
}
private fun makeRandomList(type: KType): List<Any?> {
val numOfElements = random.nextInt(10)
val elemType = type.arguments[0].type!!
return (1..numOfElements)
.map { makeRandomInstance(elemType) }
}
private fun makeRandomMap(type: KType): Map<Any?, Any?> {
val numOfElements = random.nextInt(10)
val keyType = type.arguments[0].type!!
val valType = type.arguments[1].type!!
val keys = (1..numOfElements)
.map { makeRandomInstance(keyType) }
val values = (1..numOfElements)
.map { makeRandomInstance(valType) }
return keys.zip(values).toMap()
}
private fun makeRandomChar() = ('A'..'z').random(random)
private fun makeRandomString() = (1..random.nextInt(100))
.map { makeRandomChar() }
.joinToString(separator = "") { "$it" }
}
}
// getKType
// --- Interface ---
inline fun <reified T : Any> getKType(): KType =
object : SuperTypeTokenHolder<T>() {}.getKTypeImpl()
// --- Implementation ---
@Suppress("unused")
open class SuperTypeTokenHolder<T>
fun SuperTypeTokenHolder<*>.getKTypeImpl(): KType =
javaClass.genericSuperclass.toKType().arguments.single().type!!
fun KClass<*>.toInvariantFlexibleProjection(arguments: List<KTypeProjection> = emptyList()): KTypeProjection {
// TODO: there should be an API in kotlin-reflect which creates KType instances corresponding to flexible types
// Currently we always produce a non-null type, which is obviously wrong
val args = if (java.isArray()) listOf(java.componentType.kotlin.toInvariantFlexibleProjection()) else arguments
return KTypeProjection.invariant(createType(args, nullable = false))
}
fun Type.toKTypeProjection(): KTypeProjection = when (this) {
is Class<*> -> this.kotlin.toInvariantFlexibleProjection()
is ParameterizedType -> {
val erasure = (rawType as Class<*>).kotlin
erasure.toInvariantFlexibleProjection((erasure.typeParameters.zip(actualTypeArguments).map { (parameter, argument) ->
val projection = argument.toKTypeProjection()
projection.takeIf {
// Get rid of use-site projections on arguments, where the corresponding parameters already have a declaration-site projection
parameter.variance == KVariance.INVARIANT || parameter.variance != projection.variance
} ?: KTypeProjection.invariant(projection.type!!)
}))
}
is WildcardType -> when {
lowerBounds.isNotEmpty() -> KTypeProjection.contravariant(lowerBounds.single().toKType())
upperBounds.isNotEmpty() -> KTypeProjection.covariant(upperBounds.single().toKType())
// This looks impossible to obtain through Java reflection API, but someone may construct and pass such an instance here anyway
else -> KTypeProjection.STAR
}
is GenericArrayType -> Array<Any>::class.toInvariantFlexibleProjection(listOf(genericComponentType.toKTypeProjection()))
is TypeVariable<*> -> TODO() // TODO
else -> throw IllegalArgumentException("Unsupported type: $this")
}
fun Type.toKType(): KType = toKTypeProjection().type!!
@berkkulaksiz
Copy link

is TypeVariable<*> -> TODO() // TODO

How can i implement this case? Have you any thoughts?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment