refactor: more secure DataCopy.kt

This commit is contained in:
SOR
2026-07-25 14:36:13 +08:00
parent 1f87f66f62
commit 138024b517
@@ -1,56 +1,39 @@
package com.github.sor2171.backend.entity package com.github.sor2171.backend.entity
import kotlin.reflect.KClass import kotlin.reflect.KClass
import kotlin.reflect.KProperty import kotlin.reflect.KMutableProperty1
import kotlin.reflect.KProperty1
import kotlin.reflect.full.createInstance import kotlin.reflect.full.createInstance
import kotlin.reflect.full.declaredMemberProperties import kotlin.reflect.full.declaredMemberProperties
import kotlin.reflect.full.memberProperties import kotlin.reflect.full.memberProperties
import kotlin.reflect.jvm.javaField import kotlin.reflect.jvm.isAccessible
interface DataCopy { interface DataCopy {
fun <T : Any> toAnotherObject( fun <T : Any> toAnotherObject(
toClass: KClass<T>, toClass: KClass<T>,
otherProperties: Map<String, Any?> otherProperties: Map<String, Any?> = emptyMap()
): T { ): T {
try { @Suppress("UNCHECKED_CAST")
val voProperties = toClass.declaredMemberProperties val sourceProps = this::class.memberProperties
.associate { it.name to (it as KProperty1<Any, *>).get(this) }
return try {
val vo = toClass.createInstance() val vo = toClass.createInstance()
for (voProperty in voProperties) for (prop in toClass.declaredMemberProperties) {
copyFieldData(voProperty, otherProperties, vo) @Suppress("UNCHECKED_CAST")
return vo val mutableProp = prop as? KMutableProperty1<Any, Any?> ?: continue
val value = when {
sourceProps.containsKey(prop.name) -> sourceProps[prop.name]
otherProperties.containsKey(prop.name) -> otherProperties[prop.name]
else -> continue
}
mutableProp.isAccessible = true
mutableProp.set(vo, value)
}
vo
} catch (e: ReflectiveOperationException) { } catch (e: ReflectiveOperationException) {
throw RuntimeException( throw RuntimeException(
"${toClass.simpleName} has no fields.", "Failed to instantiate ${toClass.simpleName}. Does it have a no-arg constructor?", e
e
) )
} }
} }
private fun copyFieldData(
toProperty: KProperty<*>,
otherProperties: Map<String, Any?>,
another: Any
) {
try {
val thisProperty = this::class.memberProperties
.firstOrNull { it.name == toProperty.name }
val value =
if (thisProperty != null) {
thisProperty.getter.call(this)
} else if (otherProperties.containsKey(toProperty.name))
otherProperties[toProperty.name]
else
throw RuntimeException(
"Property ${toProperty.name} not found."
)
val field = toProperty.javaField!!
field.isAccessible = true
field.set(another, value)
} catch (e: Exception) {
e.printStackTrace()
}
}
} }