This code is intended to generate a class (domain) from a class with class with similar/identical properties (api). Motivation is to speed development time when using clean architecture.
import org.joda.time.DateTime
import org.joda.time.DateTimeZone
import javax.inject.Inject
import kotlin.reflect.KClass
import kotlin.reflect.KParameter
import kotlin.reflect.full.declaredMemberProperties
import kotlin.reflect.full.findAnnotation
class DomainApiReflectionAdapter<DOMAIN : Any, API : Any> constructor(
val clazz: KClass<DOMAIN>
) {
fun fromApi(apiModel: API): DOMAIN {
return apiToDomain(apiModel, clazz)
}
inline fun <API : Any, DOMAIN : Any> apiToDomain(apiModel: API, clazz: KClass<DOMAIN>): DOMAIN {
val factory = Factory(clazz)
val apiProperties = toPropMap(apiModel)
val constructorMap = mutableMapOf<KParameter, Any?>()
for (param in factory.params) {
param.findAnnotation<AdapterValues>()?.let {
val iclass = param.type.classifier as KClass<*>
println(iclass)
constructorMap[param] = innerFactory.simpleConstruct(apiProperties)
} ?: {
if (param.name in apiProperties) {
constructorMap[param] = when {
param.type.classifier == List::class -> {
val itype = param.type.arguments[0].type!!.classifier as KClass<*>
(apiProperties[param.name] as List<*>).map {
it?.toDomain(itype)
}
}
param.type.classifier == DateTime::class -> DateTime(apiProperties[param.name] as Long, DateTimeZone.forID("UTC"))
else -> apiProperties[param.name]
}
}
}()
}
return factory.construct(constructorMap)
}
fun <K : Any> toPropMap(model: K): Map<String, Any?> {
return model.javaClass.kotlin.declaredMemberProperties
.map { prop ->
prop.name to prop.get(model)
}.toMap()
}
companion object {
inline fun <reified DOMAIN : Any, API : Any> instance(): DomainApiReflectionAdapter<DOMAIN, API> {
return DomainApiReflectionAdapter(DOMAIN::class)
}
}
inner class Factory<DOMAIN : Any>(clazz: KClass<DOMAIN>) {
private val constructor = clazz.constructors.first()
val params = constructor.parameters
fun construct(map: Map<KParameter, Any?>): DOMAIN {
return constructor.callBy(map)
}
fun simpleConstruct(apiProperties: Map<String, Any?>): DOMAIN {
val innerMap = mutableMapOf<KParameter, Any?>()
for (ip in params) {
innerMap[ip] = apiProperties[ip.name]
}
return construct(innerMap)
}
}
}
fun <DOMAIN : Any, API : Any> API.toDomain(clazz: KClass<DOMAIN>): DOMAIN {
val adapter = DomainApiReflectionAdapter<DOMAIN, API>(clazz)
return adapter.fromApi(this)
}
abstract class Adapter<A, B> : (B) -> A
@Target(AnnotationTarget.VALUE_PARAMETER)
annotation class AdapterClass()
@Target(AnnotationTarget.VALUE_PARAMETER)
annotation class AdapterValues(val parameters: Array<String>)