From cc32906fc9877c9b980654b7996ddd9e573bb169 Mon Sep 17 00:00:00 2001 From: tursom Date: Mon, 12 Apr 2021 11:22:54 +0800 Subject: [PATCH] update --- settings.gradle.kts | 1 + .../main/kotlin/cn/tursom/core/TextColor.kt | 123 +++++ .../src/main/kotlin/cn/tursom/core/Tools.kt | 29 +- ts-core/ts-clone/build.gradle.kts | 1 + .../cn/tursom/core/clone/InstantAllocator.kt | 55 ++ .../main/kotlin/cn/tursom/core/clone/clone.kt | 492 ++++++------------ .../cn/tursom/core/clone/cloneBackend.kt | 270 ++++++++++ .../cn/tursom/core/datastruct/ArrayMap.kt | 4 +- .../tursom/core/datastruct/DefaultValueMap.kt | 22 +- .../core/datastruct/DefaultValueMutableMap.kt | 30 +- .../core/datastruct/KPropertyValueMap.kt | 4 +- .../core/datastruct/ParallelArrayMap.kt | 22 +- .../cn/tursom/core/delegation/delegations.kt | 102 +--- .../ts-observer/build.gradle.kts | 32 ++ .../core/delegation/observer}/Listenable.kt | 2 +- .../observer}/ListenableListener.kt | 2 +- .../ListenableMutableDelegatedField.kt | 5 +- .../core/delegation/observer}/Listener.kt | 2 +- .../observer}/UnmonitoredFieldException.kt | 2 +- .../core/delegation/observer/delegations.kt | 109 ++++ .../main/kotlin/cn/tursom/core/encrypt/ECC.kt | 4 +- ts-core/ts-log/build.gradle.kts | 6 +- 22 files changed, 807 insertions(+), 512 deletions(-) create mode 100644 ts-core/src/main/kotlin/cn/tursom/core/TextColor.kt create mode 100644 ts-core/ts-clone/src/main/kotlin/cn/tursom/core/clone/InstantAllocator.kt create mode 100644 ts-core/ts-clone/src/main/kotlin/cn/tursom/core/clone/cloneBackend.kt create mode 100644 ts-core/ts-delegation/ts-observer/build.gradle.kts rename ts-core/ts-delegation/{src/main/kotlin/cn/tursom/core/delegation => ts-observer/src/main/kotlin/cn/tursom/core/delegation/observer}/Listenable.kt (89%) rename ts-core/ts-delegation/{src/main/kotlin/cn/tursom/core/delegation => ts-observer/src/main/kotlin/cn/tursom/core/delegation/observer}/ListenableListener.kt (84%) rename ts-core/ts-delegation/{src/main/kotlin/cn/tursom/core/delegation => ts-observer/src/main/kotlin/cn/tursom/core/delegation/observer}/ListenableMutableDelegatedField.kt (89%) rename ts-core/ts-delegation/{src/main/kotlin/cn/tursom/core/delegation => ts-observer/src/main/kotlin/cn/tursom/core/delegation/observer}/Listener.kt (83%) rename ts-core/ts-delegation/{src/main/kotlin/cn/tursom/core/delegation => ts-observer/src/main/kotlin/cn/tursom/core/delegation/observer}/UnmonitoredFieldException.kt (90%) create mode 100644 ts-core/ts-delegation/ts-observer/src/main/kotlin/cn/tursom/core/delegation/observer/delegations.kt diff --git a/settings.gradle.kts b/settings.gradle.kts index fc338bc..9bf43b5 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -7,6 +7,7 @@ include("ts-core:ts-pool") include("ts-core:ts-hash") include("ts-core:ts-log") include("ts-core:ts-delegation") +include("ts-core:ts-delegation:ts-observer") include("ts-core:ts-clone") include("ts-core:ts-mail") include("ts-core:ts-coroutine") diff --git a/ts-core/src/main/kotlin/cn/tursom/core/TextColor.kt b/ts-core/src/main/kotlin/cn/tursom/core/TextColor.kt new file mode 100644 index 0000000..32accfe --- /dev/null +++ b/ts-core/src/main/kotlin/cn/tursom/core/TextColor.kt @@ -0,0 +1,123 @@ +package cn.tursom.core + +object TextColor { + const val reset = "\u001b[0m" + + enum class DisplayType(val code: Int) { + DEFAULT(0), HIGHLIGHT(1), INTENSITY(2), ITALIC(3), UNDERLINE(4), + SLOW_BLINK(5), RAPID_BLINK(6), REVERSE(7), INVISIBLE(8), CROSSED_OUT(9), + UNDERLINE_OFF(24), BLINK_OFF(25), REVERSE_OFF(27), INVISIBLE_OFF(28), CROSSED_OUT_OFF(29), + OVER_LINE(53), OVER_LINE_OFF(55); + + val strCode = "\u001b[${code}m" + } + + fun textColor(displayType: DisplayType, textColor: Int, backgroundColor: Int) = + "\u001B[${displayType.code};$textColor;${backgroundColor}m" + + fun rgbTextColor(r: Int, g: Int, b: Int, displayType: DisplayType = DisplayType.DEFAULT) = + "\u001B[${displayType.code};38;2;$r;$g;${b}m" + + fun rgbBackgroundColor(r: Int, g: Int, b: Int, displayType: DisplayType = DisplayType.DEFAULT) = + "\u001B[${displayType.code};48;2;$r;$g;${b}m" + + const val black = "\u001b[30m" + const val red = "\u001b[31m" + const val green = "\u001b[32m" + const val yellow = "\u001b[33m" + const val blue = "\u001b[34m" + const val magenta = "\u001b[35m" + const val cyan = "\u001b[36m" + const val white = "\u001b[37m" + + const val brightBlack = "\u001b[30;1m" + const val brightRed = "\u001b[31;1m" + const val brightGreen = "\u001b[32;1m" + const val brightYellow = "\u001b[33;1m" + const val brightBlue = "\u001b[34;1m" + const val brightMagenta = "\u001b[35;1m" + const val brightCyan = "\u001b[36;1m" + const val brightWhite = "\u001b[37;1m" + + val textColor = Array(256) { + "\u001b[38;5;${it}m" + } + + fun textColor(color: Int) = textColor[color] + fun textColor(color: Int, displayType: DisplayType = DisplayType.DEFAULT) = + "\u001B[${displayType.code};38;5;${color}m" + + const val blackBackground = "\u001b[40m" + const val redBackground = "\u001b[41m" + const val greenBackground = "\u001b[42m" + const val yellowBackground = "\u001b[43m" + const val blueBackground = "\u001b[44m" + const val magentaBackground = "\u001b[45m" + const val cyanBackground = "\u001b[46m" + const val whiteBackground = "\u001b[47m" + + const val brightBlackBackground = "\u001b[40;1m" + const val brightRedBackground = "\u001b[41;1m" + const val brightGreenBackground = "\u001b[42;1m" + const val brightYellowBackground = "\u001b[43;1m" + const val brightBlueBackground = "\u001b[44;1m" + const val brightMagentaBackground = "\u001b[45;1m" + const val brightCyanBackground = "\u001b[46;1m" + const val brightWhiteBackground = "\u001b[47;1m" + + val backgroundColor = Array(256) { + "\u001b[48;5;${it}m" + } + + const val bold = "\u001b[1m" + const val underline = "\u001b[4m" + const val reverseColor = "\u001b[7m" + + const val up = "\u001b[1A" + const val down = "\u001b[1B" + const val left = "\u001b[1C" + const val right = "\u001b[1D" + + const val downToNextLine = "\u001b[1E" + const val upToPrevLine = "\u001b[1F" + + fun up(step: Int) = "\u001b[${step}A" + fun down(step: Int) = "\u001b[${step}B" + fun left(step: Int) = "\u001b[${step}C" + fun right(step: Int) = "\u001b[${step}D" + + fun downToNextLine(step: Int) = "\u001b[${step}E" + fun upToPrevLine(step: Int) = "\u001b[${step}F" + + fun jumpToLine(line: Int) = "\u001b[${line}G" + fun jump(line: Int, row: Int) = "\u001b[${line};${row}H" + + const val cleanScreenToEnd = "\u001b[0J" + const val cleanScreenFromStart = "\u001b[1J" + const val cleanScreen = "\u001b[2J" + + const val cleanLineToEnd = "\u001b[0K" + const val cleanLineFromStart = "\u001b[1K" + const val cleanLine = "\u001b[2K" + + const val savePosition = "\u001b[s" + const val loadPosition = "\u001b[u" + + fun rgbTo8Color(R: Int, G: Int, B: Int): Int { + //8色化处理,取RGB的高1位相与。 + val r1 = R shr 5 and 0x4 + val g1 = G shr 6 and 0x2 + val b1 = B shr 7 + return (r1 or g1 or b1) + 1 + } + + fun rgbTo16Color(R: Int, G: Int, B: Int): Int { + //16色化处理,取R、G的高1位和B的高2位相与 + val r1 = R shr 4 and 0x8 + val g1 = G shr 5 and 0x4 + val b1 = B shr 6 and 0x3 + return (r1 or g1 or b1) + 1 + } + + fun rgbTo256Color(r: Int, g: Int, b: Int): Int = ((r / 32 shl 5) + (g / 32 shl 2) + b / 64) and 0xFF +} diff --git a/ts-core/src/main/kotlin/cn/tursom/core/Tools.kt b/ts-core/src/main/kotlin/cn/tursom/core/Tools.kt index bdfd008..e4ddb08 100644 --- a/ts-core/src/main/kotlin/cn/tursom/core/Tools.kt +++ b/ts-core/src/main/kotlin/cn/tursom/core/Tools.kt @@ -53,8 +53,12 @@ object Utils { .create() } + @Suppress("SpellCheckingInspection") internal val UPPER_HEX_ARRAY = "0123456789ABCDEF".toCharArray() + + @Suppress("SpellCheckingInspection") internal val LOWER_HEX_ARRAY = "0123456789abcdef".toCharArray() + val md5 by lazy { MessageDigest.getInstance("MD5")!! } val sha256 by lazy { MessageDigest.getInstance("SHA-256")!! } val sha by lazy { MessageDigest.getInstance("SHA")!! } @@ -62,6 +66,7 @@ object Utils { val sha384 by lazy { MessageDigest.getInstance("SHA-384")!! } val sha512 by lazy { MessageDigest.getInstance("SHA-512")!! } + @Suppress("SpellCheckingInspection") internal val DIGITS = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz".toCharArray() val receiverField: Field by lazy { @@ -135,7 +140,7 @@ inline fun T?.checkNull(ifNull: () -> Exception): T { } } -fun String.emptyToNull() = if (isEmpty()) null else this +fun String.emptyToNull() = ifEmpty { null } inline fun getClazz() = T::class.java @@ -251,7 +256,7 @@ fun A.changeAnnotationValue(field: KProperty1, v val h = Proxy.getInvocationHandler(this) val memberValuesField = h.javaClass.getDeclaredField("memberValues") memberValuesField.isAccessible = true - val memberValues = memberValuesField[h].cast>() + val memberValues = memberValuesField[h].uncheckedCast>() memberValues[field.name] = value true } catch (e: Exception) { @@ -366,7 +371,7 @@ val KProperty<*>.receiver: Any? val KProperty<*>.owner: Class<*>? get() = try { - Utils.ownerField.get(this)?.cast>() + Utils.ownerField.get(this)?.uncheckedCast>() } catch (e: Exception) { null } ?: javaClass.getFieldForAll("owner")?.let { @@ -406,16 +411,6 @@ fun Any.serialize(): ByteArray { return outputStream.toByteArray() } - -inline infix fun String.ifEmpty(ifEmpty: () -> String) = if (isNotEmpty()) this else ifEmpty() -inline infix fun String.ifBlank(ifBlank: () -> String) = if (isNotBlank()) this else ifBlank() - -@JvmName("ifEmptyNullable") -inline fun String.ifEmpty(ifEmpty: () -> String?) = if (isNotEmpty()) this else ifEmpty() - -@JvmName("ifBlankNullable") -inline fun String.ifBlank(ifBlank: () -> String?) = if (isNotBlank()) this else ifBlank() - /** * 使用 condition 做条件判断,如果返回 true 则使用 then 生成结果,否则范湖自身 */ @@ -452,9 +447,9 @@ inline val KClass<*>.companionObjectInstanceOrNull: Any? null } -inline val Map.notNullKey get() = cast>() -inline val Map.notNullValue get() = cast>() -inline val Map.notNullEntry get() = cast>() +inline val Map.notNullKey get() = uncheckedCast>() +inline val Map.notNullValue get() = uncheckedCast>() +inline val Map.notNullEntry get() = uncheckedCast>() inline val Map.filterNullKey get() = filter { it.key != null }.notNullKey inline val Map.filterNullValue get() = filter { it.value != null }.notNullValue @@ -466,7 +461,7 @@ val KClass.allMemberProperties: List> !it.java.isInterface } while (superClass != null) { - propertiesList.addAll(superClass.memberProperties.cast()) + propertiesList.addAll(superClass.memberProperties.uncheckedCast()) superClass = superClass.superclasses.firstOrNull { !it.java.isInterface } diff --git a/ts-core/ts-clone/build.gradle.kts b/ts-core/ts-clone/build.gradle.kts index e284acb..24cfaa9 100644 --- a/ts-core/ts-clone/build.gradle.kts +++ b/ts-core/ts-clone/build.gradle.kts @@ -6,6 +6,7 @@ plugins { dependencies { implementation(project(":")) implementation(project(":ts-core")) + implementation(project(":ts-core:ts-log")) implementation(project(":ts-core:ts-datastruct")) } diff --git a/ts-core/ts-clone/src/main/kotlin/cn/tursom/core/clone/InstantAllocator.kt b/ts-core/ts-clone/src/main/kotlin/cn/tursom/core/clone/InstantAllocator.kt new file mode 100644 index 0000000..929472a --- /dev/null +++ b/ts-core/ts-clone/src/main/kotlin/cn/tursom/core/clone/InstantAllocator.kt @@ -0,0 +1,55 @@ +package cn.tursom.core.clone + +import cn.tursom.core.Unsafe +import cn.tursom.core.uncheckedCast +import java.util.concurrent.ConcurrentHashMap +import kotlin.reflect.KClass + +object InstantAllocator { + enum class AllocateFunction { INSTANCE, UNSAFE, KOBJECT, NONE } + + private val allocateFunctionMap = ConcurrentHashMap, AllocateFunction>() + + @Throws(NoSuchMethodException::class) + operator fun invoke(clazz: Class, unsafe: Boolean = true): T = get(clazz, unsafe) + + @Throws(NoSuchMethodException::class) + inline operator fun invoke(unsafe: Boolean = true): T = get(T::class.java, unsafe) + + @Throws(NoSuchMethodException::class) + operator fun get(clazz: KClass, unsafe: Boolean = true): T = get(clazz.java, unsafe) + + @Throws(NoSuchMethodException::class) + operator fun get(clazz: Class, unsafe: Boolean = true): T { + return when (allocateFunctionMap[clazz]) { + null -> try { + val newInstance = clazz.newInstance() + allocateFunctionMap[clazz] = AllocateFunction.INSTANCE + newInstance + } catch (e: Exception) { + val kClass = clazz.kotlin + val objectInstance = kClass.objectInstance + if (objectInstance != null) { + allocateFunctionMap[clazz] = AllocateFunction.KOBJECT + objectInstance + } else if (unsafe) try { + allocateFunctionMap[clazz] = AllocateFunction.UNSAFE + Unsafe.unsafe.allocateInstance(clazz).uncheckedCast() + } catch (e: Exception) { + allocateFunctionMap[clazz] = AllocateFunction.NONE + throw NoSuchMethodException("${clazz.name}:()") + } else { + throw NoSuchMethodException("${clazz.name}:()") + } + } + AllocateFunction.INSTANCE -> clazz.newInstance() + AllocateFunction.UNSAFE -> if (unsafe) { + Unsafe.unsafe.allocateInstance(clazz).uncheckedCast() + } else { + throw NoSuchMethodException("${clazz.name}:()") + } + AllocateFunction.KOBJECT -> clazz.kotlin.objectInstance!! + AllocateFunction.NONE -> throw NoSuchMethodException("${clazz.name}:()") + } + } +} \ No newline at end of file diff --git a/ts-core/ts-clone/src/main/kotlin/cn/tursom/core/clone/clone.kt b/ts-core/ts-clone/src/main/kotlin/cn/tursom/core/clone/clone.kt index 5ffc187..360d31d 100644 --- a/ts-core/ts-clone/src/main/kotlin/cn/tursom/core/clone/clone.kt +++ b/ts-core/ts-clone/src/main/kotlin/cn/tursom/core/clone/clone.kt @@ -1,110 +1,22 @@ -@file:Suppress("unused") - package cn.tursom.core.clone -import cn.tursom.core.Unsafe -import cn.tursom.core.cast +import cn.tursom.core.* import cn.tursom.core.datastruct.ArrayMap -import cn.tursom.core.datastruct.KPropertyValueMap -import cn.tursom.core.datastruct.ReadWriteMap -import cn.tursom.core.datastruct.SoftArrayMap -import cn.tursom.core.final +import cn.tursom.log.impl.Slf4jImpl import kotlin.reflect.KClass -import kotlin.reflect.KMutableProperty1 import kotlin.reflect.KProperty1 import kotlin.reflect.full.findAnnotation -import kotlin.reflect.full.memberProperties -import kotlin.reflect.jvm.isAccessible -import kotlin.reflect.jvm.javaField -import kotlin.reflect.jvm.javaType +/** + * clone 使用的日志对象 + */ +private val logger = Slf4jImpl.getLogger() +/** + * clone 使用的对象属性类型 + */ typealias Property = KProperty1 -val Any.valueMap: Map - get() = if (this is Map<*, *>) { - cast() - } else { - KPropertyValueMap(this) - } - -private val injectMapCache = ReadWriteMap, ArrayMap?>>(SoftArrayMap(HashMap())) - -val KClass.injectMap: ArrayMap?> - @Suppress("UNCHECKED_CAST") - get() = let { - var valueMap = injectMapCache[it] as ArrayMap?>? - if (valueMap == null) { - val properties = it.memberProperties - valueMap = ArrayMap(properties.size) - (properties as Collection>).forEach { property -> - property.isAccessible = true - valueMap[property.name] = property - } - injectMapCache[it] = valueMap as ArrayMap?> - } - valueMap.copy() - } - -private val T.injectMap: ArrayMap?> - @Suppress("UNCHECKED_CAST") - get() = (this::class as KClass).injectMap - -private val Array, Property?>>.injectMap get() = iterator().injectMap -private val Iterator, Property?>>.injectMap - get() = let { - val valueMap = ArrayMap?>() - @Suppress("UNCHECKED_CAST") - forEach { (k, field) -> - field?.isAccessible = true - valueMap[k.name] = field - } - //logger.trace("Iterator.injectMap: {}", valueMap) - valueMap - } - -private fun T.injectMap(clazz: KClass<*>?): ArrayMap?> = this::class - .cast>() - .injectMap - .also { - if (clazz == null) return@also - val clazzThis = this::class.java - - fun import(relation: Relation, property: Property) { - if (relation.clazz != clazz) return - //logger.trace("relation {} to {}", relation.property, property.name) - it[relation.property] = when { - relation.skip -> null - relation.handler.isEmpty() -> property - else -> try { - val handler = clazzThis.getDeclaredMethod(relation.handler, relation.handleClass.java) - handler.isAccessible = true - object : KMutableProperty1, KProperty1 by property { - override val setter: KMutableProperty1.Setter get() = TODO() - override fun set(receiver: T, value: Any?) = - handler(this@injectMap, value).inject(receiver, property) - } - } catch (e: Exception) { - //logger.warn("an exception caused on generate inject handler", e) - null - } - } - } - - fun parseAnnotation(annotation: Annotation, property: Property) { - when (annotation) { - is Relation -> import(annotation, property) - is Relations -> annotation.relations.forEach { relation -> import(relation, property) } - } - } - - this::class.memberProperties.cast>>().forEach { property -> - property.annotations.forEach { annotation -> parseAnnotation(annotation, property) } - (property.javaField ?: return@forEach).annotations.forEach { annotation -> - parseAnnotation(annotation, property) - } - } - } /** * 用于形式化的将List中的数据映射到实体类上 @@ -122,7 +34,7 @@ private fun T.injectMap(clazz: KClass<*>?): ArrayMap List.clone( unsafe: Boolean = true, - vararg relation: Pair, Property?> + vararg relation: Pair, Property?>, ): List { val list = ArrayList(size) val memberMap = T::class.injectMap @@ -131,25 +43,28 @@ inline fun List.clone( } forEach { it ?: return@forEach - val target = instance(unsafe) ?: return@forEach - list.add(it.inject(target, memberMap.iterator())) + try { + val target = InstantAllocator[T::class.java, unsafe] + list.add(it.inject(target, memberMap.iterator())) + } catch (e: Exception) { + } } return list } inline fun List.clone( unsafe: Boolean = true, - vararg relation: Property? -): T = clone(instance(unsafe)!!, relation.iterator()) + vararg relation: Property?, +): T = clone(InstantAllocator[T::class.java, unsafe], relation.iterator()) fun List.clone( target: T, - vararg relation: Property? + vararg relation: Property?, ): T = clone(target, relation.iterator()) fun List.clone( target: T, - relation: Iterator?> + relation: Iterator?>, ): T = relation.mapIndexed { index, kProperty1 -> (kProperty1 ?: return@mapIndexed null).name to this[index] }.clone(target) @@ -157,29 +72,30 @@ fun List.clone( /** * 新建并拷贝 + * @author 王景阔 * 创建类型 T 的实例 * 并将对象两个的所有同名字段拷贝进新建的实例中 * @return 新建的实例 * @param unsafe 是否允许使用 Unsafe 创建对象,unsafe 不需调用构造函数,可以在没有默认构造函数的情况下生成对象 */ -inline fun Any.clone(unsafe: Boolean = true): T = clone(instance(unsafe)!!) +inline fun Any.clone(unsafe: Boolean = true): T = clone(InstantAllocator[T::class.java, unsafe]) -fun Any.clone(clazz: Class, unsafe: Boolean = true): T = clone(instance(unsafe, clazz)!!) +fun Any.clone(clazz: Class, unsafe: Boolean = true): T = clone(InstantAllocator[clazz, unsafe]) @JvmName("unsafeClone") inline fun Any.clone( unsafe: Boolean = true, - vararg relation: Pair?> -): T = clone(instance(unsafe)!!, relation.iterator()) + vararg relation: Pair?>, +): T = clone(InstantAllocator[T::class.java, unsafe], relation.iterator()) inline fun S.clone( unsafe: Boolean = true, - vararg relation: Pair, Property?> -): T = clone(instance(unsafe)!!, relation.iterator()) + vararg relation: Pair, Property?>, +): T = clone(InstantAllocator[T::class.java, unsafe], relation.iterator()) fun Any.cloneMap(): Map = when (this) { - is Map<*, *> -> @Suppress("UNCHECKED_CAST") (this as Map).mapKeys { it.key.toString() } + is Map<*, *> -> uncheckedCast>().mapKeys { it.key.toString() } is Iterator<*> -> { val valueMap = HashMap() (this as Iterator).forEach { @@ -201,22 +117,22 @@ fun Any.clone(target: T): T = apply { injectWithoutProperty(target) }. fun S.clone( target: T, - iterator: Iterator, Property?>> + iterator: Iterator, Property?>>, ): T = apply { injectWithoutProperty(target) }.checkPropertyClone(target) { valueMap - //.also { logger.trace("clone {} into {}, value map:{}", this, target, it) } + .also { logger.trace("clone {} into {}, value map:{}", this, target, it) } .clone(target, iterator.injectMap.iterator(), this::class) } fun Any.clone( target: T, - vararg relation: Pair?> + vararg relation: Pair?>, ): T = clone(target, relation.iterator()) @JvmName("unsafeClone") fun Any.clone( target: T, - iterator: Iterator?>> + iterator: Iterator?>>, ): T = apply { injectWithoutProperty(target) }.checkPropertyClone(target) { valueMap.clone(target, iterator, this::class) } @@ -225,9 +141,9 @@ fun Any.clone( fun Map.clone( target: T, iterator: Iterator?>>, - clazz: KClass<*>? = null + clazz: KClass<*>? = null, ): T { - val memberMap = target.injectMap(clazz) as MutableMap?> + val memberMap = target.injectMap(clazz) iterator.forEach { (k, v) -> memberMap[k] = v } @@ -238,9 +154,9 @@ fun Map.clone( fun Map.clone( target: T, iterator: Iterator, Property?>>, - clazz: KClass<*>? = null + clazz: KClass<*>? = null, ): T { - val memberMap = target.injectMap(clazz) as MutableMap?> + val memberMap = target.injectMap(clazz) iterator.forEach { (k, v) -> memberMap[k.get(target)?.toString() ?: return@forEach] = v } @@ -251,13 +167,13 @@ fun Map.clone( fun Map.clone( target: T, iterator: Iterator?>>, - clazz: KClass<*>? = null + clazz: KClass<*>? = null, ): T { val memberMap = target.injectMap(clazz) iterator.forEach { (k, v) -> memberMap[k ?: return@forEach] = v } - //logger.trace("inject {} into {}, mapping: {}", this, target, memberMap) + logger.trace("inject {} into {}, mapping: {}", this, target, memberMap) return inject(target, memberMap.iterator()) } @@ -268,229 +184,6 @@ fun Any.checkPropertyClone(target: T, ifClone: () -> T): T = target } -//fun Any.checkPropertyClone(targetClass: KClass, ifClone: () -> T): T { -// if (targetClass.findAnnotation()?.classList?.contains(this::class) != true) -// ifClone() -//} - -fun Any.injectWithoutProperty(target: T): T { - fun parseAnnotation(relation: Relation, property: KProperty1) { - if (relation.property.isEmpty() && relation.clazz.java.isInstance(this)) try { - val handler = target::class.java.getDeclaredMethod(relation.handler, relation.clazz.java) - handler.isAccessible = true - handler(target, this)?.inject(target, property) - } catch (e: Exception) { - //logger.warn("an exception caused on global inject", e) - } - } - - target::class.memberProperties - .cast>>() - .forEach { property -> - property.annotations.forEach { annotation -> - when (annotation) { - is Relation -> parseAnnotation(annotation, property) - is Relations -> annotation.relations.forEach { parseAnnotation(it, property) } - } - } - property.javaField?.annotations?.forEach { annotation -> - when (annotation) { - is Relation -> parseAnnotation(annotation, property) - is Relations -> annotation.relations.forEach { parseAnnotation(it, property) } - } - } - } - return target -} - -fun Any.inject( - target: T, - iterator: Iterator?>> -): T = apply { injectWithoutProperty(target) }.checkPropertyClone(target) { valueMap.inject(target, iterator) } - -@JvmName("injectMap") -fun Any.inject( - target: T, - iterator: Iterator?>> -): T = apply { injectWithoutProperty(target) }.checkPropertyClone(target) { valueMap.inject(target, iterator) } - -fun Map.inject( - target: T, - iterator: Iterator?>> -): T { - iterator.forEach { (k, t) -> - val value = this[k] ?: return@forEach - value.inject(target, t ?: return@forEach) - } - return target -} - -fun Map.p2pInject( - target: T, - iterator: Iterator, Property?>> -): T { - iterator.forEach { (k, t) -> - val value = this[k(target)?.toString() ?: return@forEach] ?: return@forEach - value.inject(target, t ?: return@forEach) - } - return target -} - -@JvmName("injectMap") -fun Map.inject( - target: T, - iterator: Iterator?>> -): T { - iterator.forEach { (k, t) -> - val value = this[k] ?: return@forEach - value.inject(target, t ?: return@forEach) - } - return target -} - -fun Any.inject(target: T, property: Property) { - //logger.trace("inject {} into {}.{}", this, +{ target::class.simpleName }, property.name) - when (property) { - is KMutableProperty1<*, *> -> { - @Suppress("UNCHECKED_CAST") - property as KMutableProperty1 - property.isAccessible = true - try { - property.set(target, cast(this, property.returnType.javaType.cast()) ?: return) - } catch (e: ClassCastException) { - //logger.trace("inject failed", e) - } - } - else -> { - val field = property.javaField ?: return - field.isAccessible = true - field.final = false - try { - field.set(target, cast(this, field.type) ?: return) - } catch (e: Exception) { - //logger.trace("inject failed", e) - } - } - } -} - -fun cast(source: Any, target: Class<*>): Any? = if (target.isInstance(source)) { - source -} else when (target) { - Byte::class.java -> if (source is Number) source.toByte() else source.toString().toByteOrNull() - Char::class.java -> if (source is Number) source.toChar() else source.toString().toIntOrNull()?.toChar() - Short::class.java -> if (source is Number) source.toShort() else source.toString().toShortOrNull() - Int::class.java -> if (source is Number) source.toInt() else source.toString().toIntOrNull() - Long::class.java -> if (source is Number) source.toLong() else source.toString().toLongOrNull() - Float::class.java -> if (source is Number) source.toFloat() else source.toString().toFloatOrNull() - Double::class.java -> if (source is Number) source.toDouble() else source.toString().toDoubleOrNull() - Boolean::class.java -> if (source is Number) source != 0 else source.toString().toBoolean() - - java.lang.Byte::class.java -> if (source is Number) source.toByte() else source.toString().toByteOrNull() - java.lang.Character::class.java -> if (source is Number) source.toChar() else source.toString().toIntOrNull() - ?.toChar() - java.lang.Short::class.java -> if (source is Number) source.toShort() else source.toString().toShortOrNull() - java.lang.Integer::class.java -> if (source is Number) source.toInt() else source.toString().toIntOrNull() - java.lang.Long::class.java -> if (source is Number) source.toLong() else source.toString().toLongOrNull() - java.lang.Float::class.java -> if (source is Number) source.toFloat() else source.toString().toFloatOrNull() - java.lang.Double::class.java -> if (source is Number) source.toDouble() else source.toString().toDoubleOrNull() - java.lang.Boolean::class.java -> if (source is Number) source != 0 else source.toString().toBoolean() - - String::class.java -> source.toString() - - else -> source -} - -fun T.read(source: Any?): T = source?.clone(this) ?: this - -fun T.read( - source: S?, - vararg relation: Pair, Property?> -): T = source?.clone(this, relation.iterator()) ?: this - -fun T.read( - source: Map?, - vararg relation: Pair?> -): T = source?.clone(this, relation.iterator()) ?: this - - -fun T.p2pRead( - source: Map?, - vararg relation: Pair, Property?> -): T { - //logger.trace("p2p read, source:{}, relation: {}", source, relation) - return source?.p2pInject(this, relation.iterator()) ?: this -} - -fun T.p2pRead( - source: Map? -): T { - source ?: return this - val properties = this::class.memberProperties.cast>>() - val relation = properties.mapNotNull { property -> - val key = property.javaField?.getAnnotation(Key::class.java)?.key ?: return@mapNotNull null - val keyProperty = properties.find { it.name == key } ?: return@mapNotNull null - keyProperty to property - } - //logger.trace("p2p read, source:{}, relation: {}", source, relation) - return source.p2pInject(this, relation.iterator()) -} - -fun T.p2pRead( - source: Any? -): T { - source ?: return this - val properties = this::class.memberProperties.cast>>() - properties.forEach { property -> - val keyAnnotation = property.javaField?.getAnnotation(Key::class.java) ?: return@forEach - if (keyAnnotation.handler.isEmpty()) return@forEach - if (keyAnnotation.clazz.isInstance(source).not()) return@forEach - val handler = this::class.java.getDeclaredMethod(keyAnnotation.handler, keyAnnotation.clazz.java) - (handler(this, source) ?: return@forEach).inject(this, property) - } - return this -} - -fun List.read( - source: List, - vararg relation: Pair, Property?> -): List { - val memberMap = this[0].injectMap as MutableMap?> - relation.forEach { (k, v) -> - memberMap[k.name] = v - } - return source.mapIndexed { index, it -> it.inject(this[index], memberMap.iterator()) } -} - -inline fun read(vararg values: Any, unsafe: Boolean = true): T { - val instance = instance(unsafe)!! - values.forEach { - it.clone(instance) - } - return instance -} - -inline fun read(value: Any, unsafe: Boolean = true): T = value.clone(unsafe) - -fun T.readWithoutProperty(vararg values: Any): T { - values.forEach { - it.injectWithoutProperty(this) - } - return this -} - -inline fun instance(unsafe: Boolean = true) = instance(unsafe, T::class.java) - -fun instance(unsafe: Boolean = true, clazz: Class): T? = try { - clazz.newInstance()!! -} catch (e: Exception) { - if (unsafe) { - @Suppress("UNCHECKED_CAST") - Unsafe.unsafe.allocateInstance(clazz) as T? - } else { - null - } -} inline fun , V> Iterator.mapIndexed(action: (Int, T) -> Pair?): Map { val map = ArrayMap() @@ -502,6 +195,113 @@ inline fun , V> Iterator.mapIndexed(action: (Int, T) -> return map } -fun T.mapRead(map: Map, key: KProperty1): T { - return read(map[key.get(this)?.toString() ?: return this] ?: return this) +fun T.set(field: KProperty1, value: F): T { + (value as Any?)?.inject(this, field as Property) + return this +} + +fun T.deepClone(): T = when (this) { + is Char, is Boolean, is Byte, is Short, is Int, is Long, is Float, is Double, is Class<*> -> this + is CharArray -> copyOf().uncheckedCast() + is BooleanArray -> copyOf().uncheckedCast() + is ByteArray -> copyOf().uncheckedCast() + is ShortArray -> copyOf().uncheckedCast() + is IntArray -> copyOf().uncheckedCast() + is LongArray -> copyOf().uncheckedCast() + is FloatArray -> copyOf().uncheckedCast() + is DoubleArray -> copyOf().uncheckedCast() + else -> deepClone(HashMap()) +} + +private fun T.deepClone(clonedMap: MutableMap): T = + clonedMap[this]?.uncheckedCast() ?: when (this) { + is Char, is Boolean, is Byte, is Short, is Int, is Long, is Float, is Double, is Class<*> -> this + is CharArray -> copyOf().uncheckedCast() + is BooleanArray -> copyOf().uncheckedCast() + is ByteArray -> copyOf().uncheckedCast() + is ShortArray -> copyOf().uncheckedCast() + is IntArray -> copyOf().uncheckedCast() + is LongArray -> copyOf().uncheckedCast() + is FloatArray -> copyOf().uncheckedCast() + is DoubleArray -> copyOf().uncheckedCast() + is Array<*> -> { + val instance = java.lang.reflect.Array.newInstance(javaClass.componentType, size).uncheckedCast>() + forEachIndexed { index, it -> + instance[index] = it?.deepClone(clonedMap) + } + instance.uncheckedCast() + } + else -> { + val clazz = javaClass + val newInstance = unsafeInstance(clazz)!! + clonedMap[this] = newInstance + clazz.forAllFields { field -> + if (field.isStatic()) return@forAllFields + field.isAccessible = true + field.set(newInstance, field.get(this)?.deepClone(clonedMap)) + } + newInstance + } + } + +fun Any?.details(name: String = "", skipStatic: Boolean = true) = buildString { + this@details.details(HashMap(), this, "", name, skipStatic) +} + +private fun Any?.details( + map: MutableMap, + builder: StringBuilder, + indentation: String, + name: String = "", + skipStatic: Boolean = true, + directValue: String = "|- ", + objectValue: String = "/- ", + hint: String = "${TextColor.red}|${TextColor.reset} ", + tmpIndentation: String = "", + type: String? = null, +) { + when (this) { + null, is Char, is Boolean, is Byte, is Short, is Int, is Long, is Float, is Double, is Class<*>, is String -> + builder.append("$indentation$tmpIndentation$directValue$name(${type ?: this?.javaClass?.name ?: "null"}): $this\n") + is CharArray -> builder.append("$indentation$tmpIndentation$directValue$name(CharArray): ${String(this)}\n") + is BooleanArray -> builder.append("$indentation$tmpIndentation$directValue$name(BooleanArray): ${asList()}\n") + is ByteArray -> builder.append("$indentation$tmpIndentation$directValue$name(ByteArray): 0x${toHexString()}\n") + is ShortArray -> builder.append("$indentation$tmpIndentation$directValue$name(ShortArray): ${asList()}\n") + is IntArray -> builder.append("$indentation$tmpIndentation$directValue$name(IntArray): ${asList()}\n") + is LongArray -> builder.append("$indentation$tmpIndentation$directValue$name(LongArray): ${asList()}\n") + is FloatArray -> builder.append("$indentation$tmpIndentation$directValue$name(FloatArray): ${asList()}\n") + is DoubleArray -> builder.append("$indentation$tmpIndentation$directValue$name(DoubleArray): ${asList()}\n") + is Array<*> -> { + builder.append("$indentation$tmpIndentation$objectValue$name: (Array<${javaClass.componentType.name}>, ${hashCode()})\n") + if (this !in map) { + map[this] = hashCode() + val newIndentation = "$indentation$hint" + forEachIndexed { index, it -> + val newTmpIndentation = "|- ${name.ifEmpty { "array" }}[$index]" + it.details( + map, builder, newIndentation, + skipStatic = skipStatic, + directValue = " = ", + objectValue = " = /- ", + hint = "${TextColor.red}|${TextColor.reset} ${" ".repeat(newTmpIndentation.length + 1)}${TextColor.red}|${TextColor.reset} ", + tmpIndentation = newTmpIndentation + ) + } + } + } + in map -> builder.append("$indentation$tmpIndentation$directValue$name${if (name.isNotEmpty()) ": " else ""}(${javaClass.name}, ${map[this]}, generated)\n") + else -> { + map[this] = this.hashCode() + builder.append("$indentation$tmpIndentation$objectValue$name${if (name.isNotEmpty()) ": " else ""}(${javaClass.name}, ${hashCode()})\n") + val newIndentation = "$indentation$hint" + javaClass.forAllFields { field -> + if (skipStatic && field.isStatic()) { + return@forAllFields + } + field.isAccessible = true + val value = field.get(this) + value.details(map, builder, newIndentation, field.name, skipStatic, type = field.type.name) + } + } + } } \ No newline at end of file diff --git a/ts-core/ts-clone/src/main/kotlin/cn/tursom/core/clone/cloneBackend.kt b/ts-core/ts-clone/src/main/kotlin/cn/tursom/core/clone/cloneBackend.kt new file mode 100644 index 0000000..fa81114 --- /dev/null +++ b/ts-core/ts-clone/src/main/kotlin/cn/tursom/core/clone/cloneBackend.kt @@ -0,0 +1,270 @@ +package cn.tursom.core.clone + +import cn.tursom.core.Unsafe +import cn.tursom.core.allMemberProperties +import cn.tursom.core.datastruct.KPropertyValueMap +import cn.tursom.core.datastruct.SoftArrayMap +import cn.tursom.core.unaryPlus +import cn.tursom.core.uncheckedCast +import cn.tursom.log.impl.Slf4jImpl +import java.util.concurrent.ConcurrentHashMap +import kotlin.collections.component1 +import kotlin.collections.component2 +import kotlin.collections.set +import kotlin.reflect.* +import kotlin.reflect.full.memberProperties +import kotlin.reflect.jvm.isAccessible +import kotlin.reflect.jvm.javaField +import kotlin.reflect.jvm.javaType + +private val logger = Slf4jImpl().logger + +val Any.valueMap: Map + get() = if (this is Map<*, *>) { + uncheckedCast() + } else { + KPropertyValueMap(this) + } + +private val injectMapCache = SoftArrayMap, Map?>>(ConcurrentHashMap()) + +val KClass.injectMap: MutableMap?> + @Suppress("UNCHECKED_CAST") + get() { + var injectMap = injectMapCache[this] as Map?>? + if (injectMap == null) { + injectMap = allMemberProperties.associateByTo(HashMap()) { property -> + property.isAccessible = true + property.name + } + injectMapCache[this] = injectMap as Map?> + } + return HashMap(injectMap) + } + +internal val T.injectMap: MutableMap?> + get() = this::class.uncheckedCast>().injectMap + +internal val Iterator, Property?>>.injectMap + get() = let { + val valueMap = HashMap?>() + @Suppress("UNCHECKED_CAST") + forEach { (k, field) -> + field?.isAccessible = true + valueMap[k.name] = field + } + logger.trace("Iterator.injectMap: {}", valueMap) + valueMap + } + +internal fun T.injectMap(targetClazz: KClass<*>?): MutableMap?> = this::class + .uncheckedCast>() + .injectMap + .also { injectMap -> + if (targetClazz == null) return@also + val clazzThis = this::class.java + + fun import(relation: Relation, property: Property) { + if (relation.clazz != targetClazz) return + var propertyName = relation.property + if (propertyName.isBlank()) { + propertyName = property.name + } + logger.trace("relation {} to {}", propertyName, property.name) + injectMap[propertyName] = when { + relation.skip -> null + relation.handler.isEmpty() -> property + else -> try { + val handler = clazzThis.getDeclaredMethod(relation.handler, relation.handleClass.java) + handler.isAccessible = true + object : KMutableProperty1, KProperty1 by property { + override val setter: KMutableProperty1.Setter + get() = object : KMutableProperty1.Setter, KCallable by property.uncheckedCast() { + override val isExternal: Boolean get() = false + override val isInfix: Boolean get() = false + override val isInline: Boolean get() = false + override val isOperator: Boolean get() = false + override val isSuspend: Boolean get() = false + override val property: KProperty get() = property + override fun invoke(receiver: T, value: Any?) = + handler(this@injectMap, value).inject(receiver, property) + } + + override fun set(receiver: T, value: Any?) = try { + handler(this@injectMap, value).inject(receiver, property) + } catch (e: Exception) { + logger.trace("", e) + } + } + } catch (e: Exception) { + logger.warn("an exception caused on generate inject handler", e) + null + } + } + } + + fun parseAnnotation(annotation: Annotation, property: Property) { + when (annotation) { + is Relation -> import(annotation, property) + is Relations -> annotation.relations.forEach { relation -> import(relation, property) } + } + } + + this::class.memberProperties.uncheckedCast>>().forEach { property -> + property.annotations.forEach { annotation -> parseAnnotation(annotation, property) } + (property.javaField ?: return@forEach).annotations.forEach { annotation -> + parseAnnotation(annotation, property) + } + } + } + +fun Any.injectWithoutProperty(target: T): T { + fun parseAnnotation(relation: Relation, property: KProperty1) { + if (relation.property.isEmpty() && relation.clazz.java.isInstance(this)) try { + val handler = target::class.java.getDeclaredMethod(relation.handler, relation.clazz.java) + handler.isAccessible = true + handler(target, this)?.inject(target, property) + } catch (e: Exception) { + logger.trace("an exception caused on global inject", e) + } + } + + target::class.memberProperties + .uncheckedCast>>() + .forEach { property -> + property.annotations.forEach { annotation -> + when (annotation) { + is Relation -> parseAnnotation(annotation, property) + is Relations -> annotation.relations.forEach { parseAnnotation(it, property) } + } + } + property.javaField?.annotations?.forEach { annotation -> + when (annotation) { + is Relation -> parseAnnotation(annotation, property) + is Relations -> annotation.relations.forEach { parseAnnotation(it, property) } + } + } + } + return target +} + +fun Any.inject( + target: T, + iterator: Iterator?>>, +): T = apply { injectWithoutProperty(target) }.checkPropertyClone(target) { valueMap.inject(target, iterator) } + +@JvmName("injectMap") +fun Any.inject( + target: T, + iterator: Iterator?>>, +): T = apply { injectWithoutProperty(target) }.checkPropertyClone(target) { valueMap.inject(target, iterator) } + +fun Map.inject( + target: T, + iterator: Iterator?>>, +): T { + iterator.forEach { (k, t) -> + val value = this[k] ?: return@forEach + value.inject(target, t ?: return@forEach) + } + return target +} + +/** + * point to point inject + * 通过指定映射关系,从本map中取出数据并注入到目标对象中 + * + */ +fun Map.p2pInject( + target: T, + iterator: Iterator, Property?>>, +): T { + iterator.forEach { (k, t) -> + val value = this[k(target)?.toString() ?: return@forEach] ?: return@forEach + value.inject(target, t ?: return@forEach) + } + return target +} + +@JvmName("injectMap") +fun Map.inject( + target: T, + iterator: Iterator?>>, +): T { + iterator.forEach { (k, t) -> + val value = this[k] ?: return@forEach + value.inject(target, t ?: return@forEach) + } + return target +} + +fun Any.inject(target: T, property: Property) { + logger.trace("inject {} into {}.{}", this, +{ target::class.simpleName }, property.name) + when (property) { + is KMutableProperty1<*, *> -> { + @Suppress("UNCHECKED_CAST") + property as KMutableProperty1 + property.isAccessible = true + try { + property.set(target, cast(this, property.returnType.javaType.uncheckedCast()) ?: return) + } catch (e: ClassCastException) { + if (logger.isTraceEnabled) { + logger.trace("inject {} failed", property.name, e) + } + } catch (e: Exception) { + logger.error("inject {} failed", property.name, e) + } + } + else -> { + val field = property.javaField ?: return + field.isAccessible = true + try { + field.set(target, cast(this, field.type) ?: return) + } catch (e: ClassCastException) { + if (logger.isTraceEnabled) { + logger.trace("inject {} failed", property.name, e) + } + } catch (e: Exception) { + logger.error("inject {} failed", property.name, e) + } + } + } +} + +fun cast(source: Any, target: Class<*>): Any? = if (target.isInstance(source)) { + source +} else when (target) { + Byte::class.java -> if (source is Number) source.toByte() else source.toString().toByteOrNull() + Char::class.java -> if (source is Number) source.toChar() else source.toString().toIntOrNull()?.toChar() + Short::class.java -> if (source is Number) source.toShort() else source.toString().toShortOrNull() + Int::class.java -> if (source is Number) source.toInt() else source.toString().toIntOrNull() + Long::class.java -> if (source is Number) source.toLong() else source.toString().toLongOrNull() + Float::class.java -> if (source is Number) source.toFloat() else source.toString().toFloatOrNull() + Double::class.java -> if (source is Number) source.toDouble() else source.toString().toDoubleOrNull() + Boolean::class.java -> if (source is Number) source != 0 else source.toString().toBoolean() + + java.lang.Byte::class.java -> if (source is Number) source.toByte() else source.toString().toByteOrNull() + java.lang.Character::class.java -> if (source is Number) source.toChar() else source.toString().toIntOrNull() + ?.toChar() + java.lang.Short::class.java -> if (source is Number) source.toShort() else source.toString().toShortOrNull() + java.lang.Integer::class.java -> if (source is Number) source.toInt() else source.toString().toIntOrNull() + java.lang.Long::class.java -> if (source is Number) source.toLong() else source.toString().toLongOrNull() + java.lang.Float::class.java -> if (source is Number) source.toFloat() else source.toString().toFloatOrNull() + java.lang.Double::class.java -> if (source is Number) source.toDouble() else source.toString().toDoubleOrNull() + java.lang.Boolean::class.java -> if (source is Number) source != 0 else source.toString().toBoolean() + + String::class.java -> source.toString() + + else -> source +} + +inline fun instance(unsafe: Boolean = true) = instance(unsafe, T::class.java) + +fun instance(unsafe: Boolean = true, clazz: Class): T = InstantAllocator[clazz, unsafe] + +fun unsafeInstance(clazz: Class, unsafe: Boolean = true): T? = if (unsafe) { + @Suppress("UNCHECKED_CAST") + Unsafe.unsafe.allocateInstance(clazz) as T? +} else { + null +} \ No newline at end of file diff --git a/ts-core/ts-datastruct/src/main/kotlin/cn/tursom/core/datastruct/ArrayMap.kt b/ts-core/ts-datastruct/src/main/kotlin/cn/tursom/core/datastruct/ArrayMap.kt index aa529c8..47e6a7b 100644 --- a/ts-core/ts-datastruct/src/main/kotlin/cn/tursom/core/datastruct/ArrayMap.kt +++ b/ts-core/ts-datastruct/src/main/kotlin/cn/tursom/core/datastruct/ArrayMap.kt @@ -1,6 +1,6 @@ package cn.tursom.core.datastruct -import cn.tursom.core.cast +import cn.tursom.core.uncheckedCast @Suppress("MemberVisibilityCanBePrivate") open class ArrayMap(initialCapacity: Int = 16) : SimpMap { @@ -149,7 +149,7 @@ open class ArrayMap(initialCapacity: Int = 16) : SimpMap { override fun setValue(newValue: V): V = value.also { value = newValue } override fun compareTo(other: K): Int { return if (key is Comparable<*>) { - key.cast>().compareTo(other) + key.uncheckedCast>().compareTo(other) } else { -1 } diff --git a/ts-core/ts-datastruct/src/main/kotlin/cn/tursom/core/datastruct/DefaultValueMap.kt b/ts-core/ts-datastruct/src/main/kotlin/cn/tursom/core/datastruct/DefaultValueMap.kt index 1a9b6ad..9672873 100644 --- a/ts-core/ts-datastruct/src/main/kotlin/cn/tursom/core/datastruct/DefaultValueMap.kt +++ b/ts-core/ts-datastruct/src/main/kotlin/cn/tursom/core/datastruct/DefaultValueMap.kt @@ -1,11 +1,11 @@ package cn.tursom.core.datastruct -import cn.tursom.core.cast +import cn.tursom.core.uncheckedCast class DefaultValueMap( private val map: Map, - private val defaultValue: (K) -> V -) : Map by map.cast() { + private val defaultValue: (K) -> V, +) : Map by map.uncheckedCast() { override val entries: Set> get() = Entry() override val values: Collection get() = Values() @@ -21,8 +21,8 @@ class DefaultValueMap( } inner class Entry( - private val entries: Set> = map.entries - ) : Set> by entries.cast() { + private val entries: Set> = map.entries, + ) : Set> by entries.uncheckedCast() { override val size: Int get() = entries.count { it.value != null } override fun isEmpty(): Boolean = entries.firstOrNull { it.value != null } == null override fun iterator(): Iterator> = EntryIterator() @@ -32,7 +32,7 @@ class DefaultValueMap( private var next: Map.Entry? = null override fun hasNext(): Boolean { while (iterator.hasNext()) { - next = iterator.next().cast() + next = iterator.next().uncheckedCast() if (next?.value != null) { return true } @@ -44,13 +44,13 @@ class DefaultValueMap( next != null -> next hasNext() -> next else -> throw NoSuchElementException() - }.cast() + }.uncheckedCast() } } inner class Values( - private val values: Collection = map.values - ) : Collection by values.cast() { + private val values: Collection = map.values, + ) : Collection by values.uncheckedCast() { override val size: Int get() = values.count { it != null } override fun isEmpty(): Boolean = values.first { it != null } == null override fun iterator(): Iterator = ValuesIterator() @@ -61,7 +61,7 @@ class DefaultValueMap( override fun hasNext(): Boolean { while (iterator.hasNext()) { - next = iterator.next().cast() + next = iterator.next().uncheckedCast() if (next != null) { return true } @@ -73,7 +73,7 @@ class DefaultValueMap( next != null -> next hasNext() -> next else -> throw NoSuchElementException() - }.cast() + }.uncheckedCast() } } } diff --git a/ts-core/ts-datastruct/src/main/kotlin/cn/tursom/core/datastruct/DefaultValueMutableMap.kt b/ts-core/ts-datastruct/src/main/kotlin/cn/tursom/core/datastruct/DefaultValueMutableMap.kt index 456f610..adc74cc 100644 --- a/ts-core/ts-datastruct/src/main/kotlin/cn/tursom/core/datastruct/DefaultValueMutableMap.kt +++ b/ts-core/ts-datastruct/src/main/kotlin/cn/tursom/core/datastruct/DefaultValueMutableMap.kt @@ -1,11 +1,11 @@ package cn.tursom.core.datastruct -import cn.tursom.core.cast +import cn.tursom.core.uncheckedCast class DefaultValueMutableMap( private val map: MutableMap, - private val defaultValue: (K) -> V -) : MutableMap by map.cast() { + private val defaultValue: (K) -> V, +) : MutableMap by map.uncheckedCast() { override val entries: MutableSet> get() = Entry() override val values: MutableCollection get() = Values() @@ -19,19 +19,19 @@ class DefaultValueMutableMap( } inner class Entry( - private val entries: MutableSet> = map.entries - ) : MutableSet> by entries.cast() { + private val entries: MutableSet> = map.entries, + ) : MutableSet> by entries.uncheckedCast() { override val size: Int get() = entries.count { it.value != null } override fun isEmpty(): Boolean = entries.firstOrNull { it.value != null } == null override fun iterator(): MutableIterator> = EntryIterator() inner class EntryIterator( - private val iterator: MutableIterator> = entries.iterator() - ) : MutableIterator> by iterator.cast() { + private val iterator: MutableIterator> = entries.iterator(), + ) : MutableIterator> by iterator.uncheckedCast() { private var next: Map.Entry? = null override fun hasNext(): Boolean { while (iterator.hasNext()) { - next = iterator.next().cast() + next = iterator.next().uncheckedCast() if (next?.value != null) { return true } @@ -43,25 +43,25 @@ class DefaultValueMutableMap( next != null -> next hasNext() -> next else -> throw NoSuchElementException() - }.cast() + }.uncheckedCast() } } inner class Values( - private val values: MutableCollection = map.values - ) : MutableCollection by values.cast() { + private val values: MutableCollection = map.values, + ) : MutableCollection by values.uncheckedCast() { override val size: Int get() = values.count { it != null } override fun isEmpty(): Boolean = values.first { it != null } == null override fun iterator(): MutableIterator = ValuesIterator() inner class ValuesIterator( - private val iterator: MutableIterator = values.iterator() - ) : MutableIterator by iterator.cast() { + private val iterator: MutableIterator = values.iterator(), + ) : MutableIterator by iterator.uncheckedCast() { private var next: V? = null override fun hasNext(): Boolean { while (iterator.hasNext()) { - next = iterator.next().cast() + next = iterator.next().uncheckedCast() if (next != null) { return true } @@ -73,7 +73,7 @@ class DefaultValueMutableMap( next != null -> next hasNext() -> next else -> throw NoSuchElementException() - }.cast() + }.uncheckedCast() } } } \ No newline at end of file diff --git a/ts-core/ts-datastruct/src/main/kotlin/cn/tursom/core/datastruct/KPropertyValueMap.kt b/ts-core/ts-datastruct/src/main/kotlin/cn/tursom/core/datastruct/KPropertyValueMap.kt index 7d64468..03e0d93 100644 --- a/ts-core/ts-datastruct/src/main/kotlin/cn/tursom/core/datastruct/KPropertyValueMap.kt +++ b/ts-core/ts-datastruct/src/main/kotlin/cn/tursom/core/datastruct/KPropertyValueMap.kt @@ -1,6 +1,6 @@ package cn.tursom.core.datastruct -import cn.tursom.core.cast +import cn.tursom.core.uncheckedCast import kotlin.reflect.KClass import kotlin.reflect.KProperty1 import kotlin.reflect.full.memberProperties @@ -23,7 +23,7 @@ class KPropertyValueMap(val target: Any) : Map { operator fun get(clazz: KClass<*>): SoftArrayMap> { var map = propertiesMapCache[clazz] if (map == null) { - map = clazz.memberProperties.cast>>().let { properties -> + map = clazz.memberProperties.uncheckedCast>>().let { properties -> val valueMap = SoftArrayMap>(properties.size) properties.forEach { it.isAccessible = true diff --git a/ts-core/ts-datastruct/src/main/kotlin/cn/tursom/core/datastruct/ParallelArrayMap.kt b/ts-core/ts-datastruct/src/main/kotlin/cn/tursom/core/datastruct/ParallelArrayMap.kt index 029b6b3..2675c08 100644 --- a/ts-core/ts-datastruct/src/main/kotlin/cn/tursom/core/datastruct/ParallelArrayMap.kt +++ b/ts-core/ts-datastruct/src/main/kotlin/cn/tursom/core/datastruct/ParallelArrayMap.kt @@ -1,6 +1,6 @@ package cn.tursom.core.datastruct -import cn.tursom.core.cast +import cn.tursom.core.uncheckedCast @Suppress("MemberVisibilityCanBePrivate") open class ParallelArrayMap(initialCapacity: Int = 16) : SimpMap { @@ -17,8 +17,8 @@ open class ParallelArrayMap(initialCapacity: Int = 16) : SimpMap { @Suppress("LeakingThis") override val entries: MutableSet> = EntrySet(this) - override val keys: MutableSet get() = arrValue.asList().subList(0, end).toMutableSet().cast() - override val values: MutableCollection get() = arrValue.asList().cast>().subList(0, end) + override val keys: MutableSet get() = arrValue.asList().subList(0, end).toMutableSet().uncheckedCast() + override val values: MutableCollection get() = arrValue.asList().uncheckedCast>().subList(0, end) /** * @param key 查找的键 @@ -59,7 +59,7 @@ open class ParallelArrayMap(initialCapacity: Int = 16) : SimpMap { fun setByIndex(index: Int, value: V): V? { val oldValue = arrValue[end] arrValue[index] = value - return oldValue.cast() + return oldValue.uncheckedCast() } override fun delete(key: K): V? { @@ -72,7 +72,7 @@ open class ParallelArrayMap(initialCapacity: Int = 16) : SimpMap { System.arraycopy(arr, index + 1, arr, index, end - index - 1) System.arraycopy(arrValue, index + 1, arrValue, index, end - index - 1) end-- - return oldValue.cast() + return oldValue.uncheckedCast() } override fun clear() { @@ -83,7 +83,7 @@ open class ParallelArrayMap(initialCapacity: Int = 16) : SimpMap { return if (end <= 0) { null } else { - arrValue[0].cast() + arrValue[0].uncheckedCast() } } @@ -109,7 +109,7 @@ open class ParallelArrayMap(initialCapacity: Int = 16) : SimpMap { return if (index < 0 || index >= end) { null } else { - arr[index].cast() + arr[index].uncheckedCast() } } @@ -117,7 +117,7 @@ open class ParallelArrayMap(initialCapacity: Int = 16) : SimpMap { return if (index < 0 || index >= end) { null } else { - arrValue[index].cast() + arrValue[index].uncheckedCast() } } @@ -194,10 +194,10 @@ open class ParallelArrayMap(initialCapacity: Int = 16) : SimpMap { val map: ParallelArrayMap, val index: Int ) : MutableMap.MutableEntry { - override val key: K get() = map.getKeyByIndex(index).cast() - override val value: V get() = map.getByIndex(index).cast() + override val key: K get() = map.getKeyByIndex(index).uncheckedCast() + override val value: V get() = map.getByIndex(index).uncheckedCast() override fun setValue(newValue: V): V { - return map.setByIndex(index, newValue).cast() + return map.setByIndex(index, newValue).uncheckedCast() } } } diff --git a/ts-core/ts-delegation/src/main/kotlin/cn/tursom/core/delegation/delegations.kt b/ts-core/ts-delegation/src/main/kotlin/cn/tursom/core/delegation/delegations.kt index 43a959a..9438171 100644 --- a/ts-core/ts-delegation/src/main/kotlin/cn/tursom/core/delegation/delegations.kt +++ b/ts-core/ts-delegation/src/main/kotlin/cn/tursom/core/delegation/delegations.kt @@ -2,8 +2,10 @@ package cn.tursom.core.delegation -import cn.tursom.core.* -import java.util.concurrent.ConcurrentLinkedQueue +import cn.tursom.core.SimpThreadLocal +import cn.tursom.core.castOrNull +import cn.tursom.core.receiver +import cn.tursom.core.uncheckedCast import java.util.concurrent.Executor import java.util.concurrent.locks.Lock import java.util.concurrent.locks.ReadWriteLock @@ -126,99 +128,3 @@ fun MutableDelegatedField.getter(getter: MutableDelegatedField MutableDelegatedField.withExecutor(executor: Executor): MutableDelegatedField = ExecutorMutableDelegatedField(this, executor) - -@Listenable -fun KMutableProperty0.listenable(): MutableDelegatedField { - isAccessible = true - val delegate = getDelegate() - return if (delegate is MutableDelegatedField<*, *> && delegate[ListenableMutableDelegatedField] == true) { - @OptIn(UncheckedCast::class) - delegate.cast() - } else { - @OptIn(UncheckedCast::class) - (ListenableMutableDelegatedField(KPropertyMutableDelegatedField(cast()))) - } -} - -@Listenable -fun listenable(initValue: V): MutableDelegatedField = ListenableMutableDelegatedField( - MutableDelegatedFieldValue(initValue) -) - -@Listenable -fun MutableDelegatedField.listenable(): MutableDelegatedField = - ListenableMutableDelegatedField(this) - -@JvmName("listenable1") -@Listenable -fun listenable(delegatedField: MutableDelegatedField): MutableDelegatedField = - ListenableMutableDelegatedField(delegatedField) - - -infix operator fun ListenableListener.plus(listener: T.(old: V, new: V) -> Unit): Listener = - addListener(listener) - -@OptIn(Listenable::class) -fun KProperty0.getListenableMutableDelegatedField(): ListenableMutableDelegatedField? { - isAccessible = true - var delegate = getDelegate() ?: getDelegate(receiver, name) - if (delegate is DelegatedField<*, *>) { - while (true) { - if (delegate is ListenableMutableDelegatedField<*, *>) { - @OptIn(UncheckedCast::class) - return delegate.cast() - } - if (delegate is DecoratorDelegatedField<*, *>) { - delegate = delegate.delegatedField - } else { - break - } - } - } else if (delegate is KProperty0<*> && delegate != this) { - @OptIn(UncheckedCast::class) - return delegate.cast>().getListenableMutableDelegatedField() - } - return null -} - -inline fun T.addChangeListener( - property: T.() -> KProperty0, -): ListenableListener { - val kProperty0 = property() - - @OptIn(Listenable::class, UncheckedCast::class) - val delegatedField = kProperty0 - .getListenableMutableDelegatedField() - .cast?>() - ?: throw UnmonitoredFieldException(kProperty0.toString()) - return object : ListenableListener { - private val selfList = ConcurrentLinkedQueue>() - override fun addListener(listener: T.(old: V, new: V) -> Unit): Listener { - @OptIn(Listenable::class) - val listener1 = delegatedField.addListener(listener) - selfList.add(listener1) - return listener1 - } - - override fun catch(handler: T.(old: V, new: V, e: Throwable) -> Unit): ListenableListener { - selfList.forEach { - it.catch(handler) - } - return this - } - - override fun cancel(): Boolean { - selfList.forEach { - it.cancel() - } - return true - } - - override fun finally(handler: T.(old: V, new: V) -> Unit): Listener { - selfList.forEach { - it.finally(handler) - } - return this - } - } -} diff --git a/ts-core/ts-delegation/ts-observer/build.gradle.kts b/ts-core/ts-delegation/ts-observer/build.gradle.kts new file mode 100644 index 0000000..ca4f534 --- /dev/null +++ b/ts-core/ts-delegation/ts-observer/build.gradle.kts @@ -0,0 +1,32 @@ +plugins { + kotlin("jvm") + `maven-publish` +} + +dependencies { + implementation(project(":ts-core")) + implementation(project(":ts-core:ts-delegation")) +} + +@kotlin.Suppress("UNCHECKED_CAST") +(rootProject.ext["excludeTest"] as (Project, TaskContainer) -> Unit)(project, tasks) + +tasks.register("install") { + finalizedBy(tasks["publishToMavenLocal"]) +} + +publishing { + publications { + create("maven") { + groupId = project.group.toString() + artifactId = project.name + version = project.version.toString() + + from(components["java"]) + try { + artifact(tasks["sourcesJar"]) + } catch (e: Exception) { + } + } + } +} diff --git a/ts-core/ts-delegation/src/main/kotlin/cn/tursom/core/delegation/Listenable.kt b/ts-core/ts-delegation/ts-observer/src/main/kotlin/cn/tursom/core/delegation/observer/Listenable.kt similarity index 89% rename from ts-core/ts-delegation/src/main/kotlin/cn/tursom/core/delegation/Listenable.kt rename to ts-core/ts-delegation/ts-observer/src/main/kotlin/cn/tursom/core/delegation/observer/Listenable.kt index 51fc5d0..0b0934b 100644 --- a/ts-core/ts-delegation/src/main/kotlin/cn/tursom/core/delegation/Listenable.kt +++ b/ts-core/ts-delegation/ts-observer/src/main/kotlin/cn/tursom/core/delegation/observer/Listenable.kt @@ -1,4 +1,4 @@ -package cn.tursom.core.delegation +package cn.tursom.core.delegation.observer /** * 标识一个属性可以被指定的 FieldChangeListener 监听 diff --git a/ts-core/ts-delegation/src/main/kotlin/cn/tursom/core/delegation/ListenableListener.kt b/ts-core/ts-delegation/ts-observer/src/main/kotlin/cn/tursom/core/delegation/observer/ListenableListener.kt similarity index 84% rename from ts-core/ts-delegation/src/main/kotlin/cn/tursom/core/delegation/ListenableListener.kt rename to ts-core/ts-delegation/ts-observer/src/main/kotlin/cn/tursom/core/delegation/observer/ListenableListener.kt index b9a27d0..741af94 100644 --- a/ts-core/ts-delegation/src/main/kotlin/cn/tursom/core/delegation/ListenableListener.kt +++ b/ts-core/ts-delegation/ts-observer/src/main/kotlin/cn/tursom/core/delegation/observer/ListenableListener.kt @@ -1,4 +1,4 @@ -package cn.tursom.core.delegation +package cn.tursom.core.delegation.observer interface ListenableListener : Listener { infix fun addListener(listener: T.(old: V, new: V) -> Unit): Listener diff --git a/ts-core/ts-delegation/src/main/kotlin/cn/tursom/core/delegation/ListenableMutableDelegatedField.kt b/ts-core/ts-delegation/ts-observer/src/main/kotlin/cn/tursom/core/delegation/observer/ListenableMutableDelegatedField.kt similarity index 89% rename from ts-core/ts-delegation/src/main/kotlin/cn/tursom/core/delegation/ListenableMutableDelegatedField.kt rename to ts-core/ts-delegation/ts-observer/src/main/kotlin/cn/tursom/core/delegation/observer/ListenableMutableDelegatedField.kt index 4b99ebd..f3f25b8 100644 --- a/ts-core/ts-delegation/src/main/kotlin/cn/tursom/core/delegation/ListenableMutableDelegatedField.kt +++ b/ts-core/ts-delegation/ts-observer/src/main/kotlin/cn/tursom/core/delegation/observer/ListenableMutableDelegatedField.kt @@ -1,5 +1,8 @@ -package cn.tursom.core.delegation +package cn.tursom.core.delegation.observer +import cn.tursom.core.delegation.DecoratorMutableDelegatedField +import cn.tursom.core.delegation.DelegatedFieldAttachmentKey +import cn.tursom.core.delegation.MutableDelegatedField import cn.tursom.core.uncheckedCast import java.util.concurrent.ConcurrentLinkedDeque import kotlin.reflect.KProperty diff --git a/ts-core/ts-delegation/src/main/kotlin/cn/tursom/core/delegation/Listener.kt b/ts-core/ts-delegation/ts-observer/src/main/kotlin/cn/tursom/core/delegation/observer/Listener.kt similarity index 83% rename from ts-core/ts-delegation/src/main/kotlin/cn/tursom/core/delegation/Listener.kt rename to ts-core/ts-delegation/ts-observer/src/main/kotlin/cn/tursom/core/delegation/observer/Listener.kt index f2636b3..ccad2fb 100644 --- a/ts-core/ts-delegation/src/main/kotlin/cn/tursom/core/delegation/Listener.kt +++ b/ts-core/ts-delegation/ts-observer/src/main/kotlin/cn/tursom/core/delegation/observer/Listener.kt @@ -1,4 +1,4 @@ -package cn.tursom.core.delegation +package cn.tursom.core.delegation.observer interface Listener { fun cancel(): Boolean diff --git a/ts-core/ts-delegation/src/main/kotlin/cn/tursom/core/delegation/UnmonitoredFieldException.kt b/ts-core/ts-delegation/ts-observer/src/main/kotlin/cn/tursom/core/delegation/observer/UnmonitoredFieldException.kt similarity index 90% rename from ts-core/ts-delegation/src/main/kotlin/cn/tursom/core/delegation/UnmonitoredFieldException.kt rename to ts-core/ts-delegation/ts-observer/src/main/kotlin/cn/tursom/core/delegation/observer/UnmonitoredFieldException.kt index 0eec731..dd4bb63 100644 --- a/ts-core/ts-delegation/src/main/kotlin/cn/tursom/core/delegation/UnmonitoredFieldException.kt +++ b/ts-core/ts-delegation/ts-observer/src/main/kotlin/cn/tursom/core/delegation/observer/UnmonitoredFieldException.kt @@ -1,4 +1,4 @@ -package cn.tursom.core.delegation +package cn.tursom.core.delegation.observer class UnmonitoredFieldException : Exception { constructor() : super() diff --git a/ts-core/ts-delegation/ts-observer/src/main/kotlin/cn/tursom/core/delegation/observer/delegations.kt b/ts-core/ts-delegation/ts-observer/src/main/kotlin/cn/tursom/core/delegation/observer/delegations.kt new file mode 100644 index 0000000..792a967 --- /dev/null +++ b/ts-core/ts-delegation/ts-observer/src/main/kotlin/cn/tursom/core/delegation/observer/delegations.kt @@ -0,0 +1,109 @@ +@file:Suppress("unused") + +package cn.tursom.core.delegation.observer + +import cn.tursom.core.UncheckedCast +import cn.tursom.core.cast +import cn.tursom.core.delegation.* +import cn.tursom.core.receiver +import java.util.concurrent.ConcurrentLinkedQueue +import kotlin.reflect.KMutableProperty0 +import kotlin.reflect.KProperty0 +import kotlin.reflect.jvm.isAccessible + + +@Listenable +fun KMutableProperty0.listenable(): MutableDelegatedField { + isAccessible = true + val delegate = getDelegate() + return if (delegate is MutableDelegatedField<*, *> && delegate[ListenableMutableDelegatedField] == true) { + @OptIn(UncheckedCast::class) + delegate.cast() + } else { + @OptIn(UncheckedCast::class) + (ListenableMutableDelegatedField(KPropertyMutableDelegatedField(cast()))) + } +} + +@Listenable +fun listenable(initValue: V): MutableDelegatedField = ListenableMutableDelegatedField( + MutableDelegatedFieldValue(initValue) +) + +@Listenable +fun MutableDelegatedField.listenable(): MutableDelegatedField = + ListenableMutableDelegatedField(this) + +@JvmName("listenable1") +@Listenable +fun listenable(delegatedField: MutableDelegatedField): MutableDelegatedField = + ListenableMutableDelegatedField(delegatedField) + + +infix operator fun ListenableListener.plus(listener: T.(old: V, new: V) -> Unit): Listener = + addListener(listener) + +@OptIn(Listenable::class) +fun KProperty0.getListenableMutableDelegatedField(): ListenableMutableDelegatedField? { + isAccessible = true + var delegate = getDelegate() ?: getDelegate(receiver, name) + if (delegate is DelegatedField<*, *>) { + while (true) { + if (delegate is ListenableMutableDelegatedField<*, *>) { + @OptIn(UncheckedCast::class) + return delegate.cast() + } + if (delegate is DecoratorDelegatedField<*, *>) { + delegate = delegate.delegatedField + } else { + break + } + } + } else if (delegate is KProperty0<*> && delegate != this) { + @OptIn(UncheckedCast::class) + return delegate.cast>().getListenableMutableDelegatedField() + } + return null +} + +inline fun T.addChangeListener( + property: T.() -> KProperty0, +): ListenableListener { + val kProperty0 = property() + + @OptIn(Listenable::class, UncheckedCast::class) + val delegatedField = kProperty0 + .getListenableMutableDelegatedField() + .cast?>() + ?: throw UnmonitoredFieldException(kProperty0.toString()) + return object : ListenableListener { + private val selfList = ConcurrentLinkedQueue>() + override fun addListener(listener: T.(old: V, new: V) -> Unit): Listener { + @OptIn(Listenable::class) + val listener1 = delegatedField.addListener(listener) + selfList.add(listener1) + return listener1 + } + + override fun catch(handler: T.(old: V, new: V, e: Throwable) -> Unit): ListenableListener { + selfList.forEach { + it.catch(handler) + } + return this + } + + override fun cancel(): Boolean { + selfList.forEach { + it.cancel() + } + return true + } + + override fun finally(handler: T.(old: V, new: V) -> Unit): Listener { + selfList.forEach { + it.finally(handler) + } + return this + } + } +} \ No newline at end of file diff --git a/ts-core/ts-encrypt/src/main/kotlin/cn/tursom/core/encrypt/ECC.kt b/ts-core/ts-encrypt/src/main/kotlin/cn/tursom/core/encrypt/ECC.kt index 891f257..1ca6f46 100644 --- a/ts-core/ts-encrypt/src/main/kotlin/cn/tursom/core/encrypt/ECC.kt +++ b/ts-core/ts-encrypt/src/main/kotlin/cn/tursom/core/encrypt/ECC.kt @@ -1,7 +1,7 @@ package cn.tursom.core.encrypt import cn.tursom.core.Unsafe -import cn.tursom.core.cast +import cn.tursom.core.uncheckedCast import java.security.KeyFactory import java.security.KeyPair import java.security.KeyPairGenerator @@ -72,7 +72,7 @@ class ECC( val standardCurveLineSet by lazy { try { Unsafe { - Class.forName("sun.security.ec.CurveDB")["nameMap"].cast>().keys + Class.forName("sun.security.ec.CurveDB")["nameMap"].uncheckedCast>().keys } } catch (e: Exception) { emptySet() diff --git a/ts-core/ts-log/build.gradle.kts b/ts-core/ts-log/build.gradle.kts index cd403e4..8dd694c 100644 --- a/ts-core/ts-log/build.gradle.kts +++ b/ts-core/ts-log/build.gradle.kts @@ -5,9 +5,9 @@ plugins { dependencies { implementation(project(":")) - implementation(group = "org.slf4j", name = "slf4j-api", version = "1.7.29") - implementation(group = "ch.qos.logback", name = "logback-core", version = "1.2.3") - implementation(group = "ch.qos.logback", name = "logback-classic", version = "1.2.3") + api(group = "org.slf4j", name = "slf4j-api", version = "1.7.29") + api(group = "ch.qos.logback", name = "logback-core", version = "1.2.3") + api(group = "ch.qos.logback", name = "logback-classic", version = "1.2.3") } @kotlin.Suppress("UNCHECKED_CAST")