Coverage Summary for Class: DataSourceHelperKt (com.kotlinorm.codegen)

Class Class, % Method, % Branch, % Line, % Instruction, %
DataSourceHelperKt 100% (1/1) 75% (6/8) 44.9% (35/78) 70.8% (46/65) 63.4% (409/645)


 /**
  * Copyright 2022-2025 kronos-orm
  *
  * 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.kotlinorm.codegen
 
 import com.kotlinorm.Kronos
 import com.kotlinorm.beans.logging.log
 import com.kotlinorm.interfaces.KronosDataSourceWrapper
 import java.lang.reflect.Method
 import java.util.*
 import javax.sql.DataSource
 
 fun createWrapper(className: String?, dataSource: DataSource): KronosDataSourceWrapper {
     val className = className ?: {
         Kronos.defaultLogger(dataSource).warn(
             log { +"wrapperClassName is not set, using default: com.kotlinorm.KronosBasicWrapper" }
         )
         "com.kotlinorm.KronosBasicWrapper"
     }()
     return try {
         Class.forName(className)
             .getDeclaredConstructor(dataSource::class.java)
             .newInstance(dataSource) as KronosDataSourceWrapper
     } catch (_: NoSuchMethodException) {
         Class.forName(className)
             .getDeclaredConstructor(DataSource::class.java)
             .newInstance(dataSource) as KronosDataSourceWrapper
     } catch (e: Exception) {
         throw RuntimeException("Failed to create wrapper for $className", e)
     }
 }
 
 fun initialDataSource(config: Map<String, Any?>): DataSource {
     val dataSource =
         Class.forName(
             config["dataSourceClassName"]?.toString() ?: {
                 Kronos.defaultLogger(config).warn(
                     log { +"dataSourceClassName is not set, using default: org.apache.commons.dbcp2.BasicDataSource" }
                 )
                 "org.apache.commons.dbcp2.BasicDataSource"
             }()
         )
             .getDeclaredConstructor()
             .newInstance() as DataSource
 
     dataSource.apply {
         config.entries.forEach { (key, value) ->
             if (key in arrayOf("dataSourceClassName", "wrapperClassName")) return@forEach
             try {
                 // 生成可能的setter方法名(兼容不同命名风格)
                 val methodNames = listOf(
                     "set${key.replaceFirstChar { if (it.isLowerCase()) it.titlecase(Locale.ROOT) else it.toString() }}",
                     "set${key.uppercase(Locale.ROOT)}",
                     "set${key.lowercase(Locale.ROOT)}"
                 )
 
                 val targetMethod = methodNames.firstNotNullOfOrNull { name ->
                     findCompatibleMethod(this.javaClass, name, value)
                 }
 
                 targetMethod?.apply {
                     invoke(dataSource, convertValue(value, targetMethod.parameterTypes[0]))
                 }
                     ?: Kronos.defaultLogger(this).warn(
                         log {
                             "Setter for '$key' not found in ${this::class.java.name}"
                         }
                     )
             } catch (e: Exception) {
                 Kronos.defaultLogger(this).warn(
                     log {
                         "Error setting property '$key': ${e.message?.replace('\n', ' ')}"
                     }
                 )
             }
         }
     }
     return dataSource
 }
 
 private fun findCompatibleMethod(clazz: Class<*>, methodName: String, value: Any?): Method? {
     return try {
         clazz.methods.firstOrNull { method ->
             method.name == methodName &&
                     method.parameterCount == 1 &&
                     isTypeCompatible(method.parameterTypes[0], value)
         }
     } catch (_: Exception) {
         null
     }
 }
 
 private fun isTypeCompatible(targetType: Class<*>, value: Any?): Boolean {
     if (value == null) return !targetType.isPrimitive
     return when (targetType) {
         Int::class.java, Integer.TYPE -> value is Number
         Long::class.java, java.lang.Long.TYPE -> value is Number
         Boolean::class.java, java.lang.Boolean.TYPE -> value is Boolean
         String::class.java -> true
         else -> targetType.isAssignableFrom(value.javaClass)
     }
 }
 
 private fun convertValue(value: Any?, targetType: Class<*>): Any? {
     if (value == null) return null
 
     return when {
         targetType.isAssignableFrom(value.javaClass) -> value
         targetType == Int::class.java || targetType == Integer.TYPE -> (value as? Number)?.toInt()
         targetType == Long::class.java || targetType == java.lang.Long.TYPE -> (value as? Number)?.toLong()
         targetType == Boolean::class.java || targetType == java.lang.Boolean.TYPE -> value.toString().toBoolean()
         targetType == String::class.java -> value.toString()
         targetType.isEnum -> enumValueOfSafe(targetType, value.toString())
         else -> throw IllegalArgumentException("Unsupported type conversion: ${value.javaClass} to $targetType")
     } ?: throw TypeCastException("Cannot convert $value (${value.javaClass}) to $targetType")
 }
 
 private fun enumValueOfSafe(enumClass: Class<*>, value: String): Any {
     return enumClass.enumConstants?.firstOrNull {
         (it as Enum<*>).name.equals(value, ignoreCase = true)
     } ?: throw IllegalArgumentException("Invalid enum value '$value' for ${enumClass.simpleName}")
 }