All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.trendyol.stove.testing.e2e.rdbms.R2DbcResultMapper.kt Maven / Gradle / Ivy

package com.trendyol.stove.testing.e2e.rdbms

import io.r2dbc.spi.Row
import io.r2dbc.spi.RowMetadata
import kotlin.reflect.KClass
import kotlin.reflect.KMutableProperty
import kotlin.reflect.full.createInstance
import kotlin.reflect.full.memberProperties
import kotlin.reflect.full.primaryConstructor
import kotlin.reflect.jvm.jvmErasure

@PublishedApi
internal fun  mapper(
    row: Row,
    rowMetadata: RowMetadata,
    clazz: KClass
): T =
    when {
        clazz.isData -> dataClassMapper(clazz, rowMetadata, row)
        else -> classMapper(clazz, rowMetadata, row)
    }

private fun  dataClassMapper(
    clazz: KClass,
    rowMetadata: RowMetadata,
    row: Row
): T {
    val constructor = clazz.primaryConstructor!!

    val args =
        constructor.parameters.map { param ->
            val jClazz = rowMetadata.getColumnMetadata(param.name!!).javaType!!
            row.get(param.name!!, jClazz)
        }.toTypedArray()

    return constructor.call(*args)
}

private fun  classMapper(
    clazz: KClass,
    rowMetadata: RowMetadata,
    row: Row
): T {
    val classMemberProperties = clazz.memberProperties.filterIsInstance>()
    val instance = clazz.createInstance()
    classMemberProperties.forEach { memberProperty ->
        val columnTypeOfRow = rowMetadata.getColumnMetadata(memberProperty.name).javaType!!
        val propClazz = memberProperty.returnType.jvmErasure
        val propJClazz = propClazz.java
        when (columnTypeOfRow) {
            propJClazz -> memberProperty.setter.call(instance, row.get(memberProperty.name, columnTypeOfRow))
            else -> {
                if (!checkBothNumeric(columnTypeOfRow, propJClazz)) {
                    throw IllegalStateException(
                        """Sql field and class property objects are being mismatched and not be able to convert each other.
                            | SqlType: $columnTypeOfRow, PropertyType: $propJClazz
                        """.trimMargin()
                    )
                }
                val columnValue = row.get(memberProperty.name, columnTypeOfRow)
                val propertyValue: Any? =
                    try {
                        columnValue?.cast(propClazz)
                    } catch (ex: ClassCastException) {
                        columnValue.convertToNumericOverString(propClazz)
                    }

                memberProperty.setter.call(instance, propertyValue)
            }
        }
    }
    return instance
}

@Suppress("UNCHECKED_CAST")
private fun  Any?.convertToNumericOverString(clazz: KClass): T? {
    if (this == null) return null
    val stringValue = this.toString()
    return when (clazz) {
        Double::class -> stringValue.toDouble()
        Int::class -> stringValue.toInt()
        Long::class -> stringValue.toLong()
        Float::class -> stringValue.toFloat()
        Byte::class -> stringValue.toByte()
        Short::class -> stringValue.toShort()
        else -> error("Numeric conversion unavailable for $clazz")
    } as T
}

private fun  Any.cast(targetClazz: KClass): T = targetClazz.javaObjectType.cast(this)

private val primitiveNumbers: Set> =
    setOf(
        Int::class.javaPrimitiveType as Class<*>,
        Long::class.javaPrimitiveType as Class<*>,
        Float::class.javaPrimitiveType as Class<*>,
        Double::class.javaPrimitiveType as Class<*>,
        Byte::class.javaPrimitiveType as Class<*>,
        Short::class.javaPrimitiveType as Class<*>
    )

private fun Class<*>.isNumericType(): Boolean {
    return if (this.isPrimitive) {
        primitiveNumbers.contains(this)
    } else {
        Number::class.java.isAssignableFrom(this)
    }
}

private fun checkBothNumeric(
    jClazz1: Class<*>,
    jClazz2: Class<*>
): Boolean = jClazz1.isNumericType() && jClazz2.isNumericType()




© 2015 - 2025 Weber Informatics LLC | Privacy Policy