diff --git a/backend/codegen/src/main/kotlin/net/mamoe/mirai/console/codegen/ValueSettingCodegen.kt b/backend/codegen/src/main/kotlin/net/mamoe/mirai/console/codegen/ValueSettingCodegen.kt index b1cecf118..7382bd31b 100644 --- a/backend/codegen/src/main/kotlin/net/mamoe/mirai/console/codegen/ValueSettingCodegen.kt +++ b/backend/codegen/src/main/kotlin/net/mamoe/mirai/console/codegen/ValueSettingCodegen.kt @@ -22,9 +22,7 @@ object ValueSettingCodegen { object PrimitiveValuesCodegen : RegionCodegen("Value.kt"), DefaultInvoke { @JvmStatic fun main(args: Array) = super.startIndependent() - - override val defaultInvokeArgs: List - get() = KtPrimitives + KtString + override val defaultInvokeArgs: List = KtPrimitives + KtString override fun StringBuilder.apply(ktType: KtType) { @Suppress("ClassName") @@ -42,7 +40,6 @@ object ValueSettingCodegen { object BuiltInSerializerConstantsPrimitivesCodegen : RegionCodegen("_Setting.value.kt"), DefaultInvoke { @JvmStatic fun main(args: Array) = super.startIndependent() - override val defaultInvokeArgs: List = KtPrimitives + KtString override fun StringBuilder.apply(ktType: KtType) { @@ -60,14 +57,12 @@ object ValueSettingCodegen { object PrimitiveValuesImplCodegen : RegionCodegen("_PrimitiveValueDeclarations.kt"), DefaultInvoke { @JvmStatic fun main(args: Array) = super.startIndependent() - - override val defaultInvokeArgs: List - get() = KtPrimitives + KtString + override val defaultInvokeArgs: List = KtPrimitives + KtString override fun StringBuilder.apply(ktType: KtType) { appendKCode( """ -internal abstract class ${ktType.standardName}ValueImpl : ${ktType.standardName}Value, SerializerAwareValue<${ktType.standardName}>, KSerializer { +internal abstract class ${ktType.standardName}ValueImpl : ${ktType.standardName}Value, SerializerAwareValue<${ktType.standardName}>, KSerializer, AbstractValueImpl<${ktType.standardName}> { constructor() constructor(default: ${ktType.standardName}) { _value = default @@ -89,8 +84,13 @@ internal abstract class ${ktType.standardName}ValueImpl : ${ktType.standardName} final override val serializer: KSerializer get() = this final override val descriptor: SerialDescriptor get() = BuiltInSerializerConstants.${ktType.standardName}SerializerDescriptor final override fun serialize(encoder: Encoder, value: Unit) = ${ktType.standardName}.serializer().serialize(encoder, this.value) - final override fun deserialize(decoder: Decoder) { - value = ${ktType.standardName}.serializer().deserialize(decoder) + final override fun deserialize(decoder: Decoder) = setValueBySerializer(${ktType.standardName}.serializer().deserialize(decoder)) + override fun toString(): String = _value${if (ktType != KtString) "?.toString()" else ""} ?: "${ktType.standardName}Value.value not yet initialized." + override fun equals(other: Any?): Boolean = other is ${ktType.standardName}ValueImpl && other::class.java == this::class.java && other._value == this._value + override fun hashCode(): Int { + val value = _value + return if (value == null) 1 + else value.hashCode() * 31 } } """ @@ -102,9 +102,7 @@ internal abstract class ${ktType.standardName}ValueImpl : ${ktType.standardName} object Setting_value_PrimitivesImplCodegen : RegionCodegen("_Setting.value.kt"), DefaultInvoke { @JvmStatic fun main(args: Array) = super.startIndependent() - - override val defaultInvokeArgs: List - get() = KtPrimitives + KtString + override val defaultInvokeArgs: List = KtPrimitives + KtString override fun StringBuilder.apply(ktType: KtType) { appendKCode( @@ -127,9 +125,7 @@ internal fun Setting.${ktType.lowerCaseName}ValueImpl(): SerializerAwareValue<${ object Setting_valueImplPrimitiveCodegen : RegionCodegen("_Setting.value.kt"), DefaultInvoke { @JvmStatic fun main(args: Array) = super.startIndependent() - - override val defaultInvokeArgs: List - get() = KtPrimitives + KtString + override val defaultInvokeArgs: List = KtPrimitives + KtString override fun StringBuilder.apply(ktType: KtType) { appendKCode( diff --git a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/setting/Setting.kt b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/setting/Setting.kt index d09acd8db..5c71ceab0 100644 --- a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/setting/Setting.kt +++ b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/setting/Setting.kt @@ -15,6 +15,7 @@ import kotlinx.serialization.* import kotlinx.serialization.builtins.MapSerializer import kotlinx.serialization.builtins.serializer import net.mamoe.mirai.console.setting.internal.cast +import net.mamoe.mirai.console.setting.internal.isOdd import net.mamoe.mirai.console.setting.internal.valueFromKTypeImpl import net.mamoe.mirai.console.setting.internal.valueImpl import net.mamoe.mirai.utils.MiraiExperimentalAPI @@ -22,7 +23,7 @@ import net.mamoe.yamlkt.Yaml import net.mamoe.yamlkt.YamlConfiguration import net.mamoe.yamlkt.YamlConfiguration.ListSerialization.FLOW_SEQUENCE import net.mamoe.yamlkt.YamlConfiguration.MapSerialization.FLOW_MAP -import java.util.* +import net.mamoe.yamlkt.YamlNullableDynamicSerializer import kotlin.internal.LowPriorityInOverloadResolution import kotlin.reflect.KProperty import kotlin.reflect.KType @@ -39,7 +40,7 @@ abstract class Setting : SettingImpl() { property: KProperty<*> ): SerializerAwareValue { val name = property.serialName - valueNodes.put(name, Node(name, this, this.serializer)) + valueNodes.add(Node(name, this, this.serializer)) return this } @@ -55,29 +56,82 @@ internal val KProperty<*>.serialName: String get() = this.findAnnotation? = valueNodes[name] + internal fun findNodeInstance(name: String): Node<*>? = valueNodes.firstOrNull { it.serialName == name } - internal class Node( + internal data class Node( val serialName: String, val value: Value, val updaterSerializer: KSerializer ) - internal val valueNodes: MutableMap> = Collections.synchronizedMap(mutableMapOf()) + internal val valueNodes: MutableList> = mutableListOf() internal open val updaterSerializer: KSerializer by lazy { - val actual = MapSerializer(String.serializer(), String.serializer()) - object : KSerializer { - override val descriptor: SerialDescriptor - get() = actual.descriptor + override val descriptor: SerialDescriptor get() = settingUpdaterSerializerDescriptor + @Suppress("UNCHECKED_CAST") override fun deserialize(decoder: Decoder) { - actual.deserialize(decoder) + val descriptor = descriptor + with(decoder.beginStructure(descriptor, *settingUpdaterSerializerTypeArguments)) { + if (decodeSequentially()) { + var index = 0 + repeat(decodeCollectionSize(descriptor)) { + val serialName = decodeSerializableElement(descriptor, index++, String.serializer()) + val node = findNodeInstance(serialName) + if (node == null) { + decodeSerializableElement(descriptor, index++, YamlNullableDynamicSerializer) + } else { + decodeSerializableElement(descriptor, index++, node.updaterSerializer) + } + } + } else { + outerLoop@ while (true) { + var serialName: String? = null + innerLoop@ while (true) { + val index = decodeElementIndex(descriptor) + if (index == CompositeDecoder.READ_DONE) { + check(serialName == null) { "name must be null at this moment." } + break@outerLoop + } + + if (!index.isOdd()) { // key + check(serialName == null) { "name must be null at this moment" } + serialName = decodeSerializableElement(descriptor, index, String.serializer()) + } else { + check(serialName != null) { "name must not be null at this moment" } + + val node = findNodeInstance(serialName) + if (node == null) { + decodeSerializableElement(descriptor, index, YamlNullableDynamicSerializer) + } else { + decodeSerializableElement(descriptor, index, node.updaterSerializer) + } + + + break@innerLoop + } + } + + } + } + endStructure(descriptor) + } } + @Suppress("UNCHECKED_CAST") override fun serialize(encoder: Encoder, value: Unit) { - TODO() + val descriptor = descriptor + with(encoder.beginCollection(descriptor, valueNodes.size, *settingUpdaterSerializerTypeArguments)) { + var index = 0 + + // val vSerializer = settingUpdaterSerializerTypeArguments[1] as KSerializer + valueNodes.forEach { (serialName, _, valueSerializer) -> + encodeSerializableElement(descriptor, index++, String.serializer(), serialName) + encodeSerializableElement(descriptor, index++, valueSerializer, Unit) + } + endStructure(descriptor) + } } } @@ -91,6 +145,13 @@ internal abstract class SettingImpl { } companion object { + + private val ABSENT_STUB = Any() + + private val settingUpdaterSerializerTypeArguments = arrayOf(String.serializer(), YamlNullableDynamicSerializer) + private val settingUpdaterSerializerDescriptor = + MapSerializer(settingUpdaterSerializerTypeArguments[0], settingUpdaterSerializerTypeArguments[1]).descriptor + val allFlow = Yaml( YamlConfiguration( nonStrictNullability = true, @@ -124,7 +185,8 @@ fun Setting.value(default: Int): SerializerAwareValue = valueImpl(default) @LowPriorityInOverloadResolution @MiraiExperimentalAPI @OptIn(ExperimentalStdlibApi::class) // stable in 1.4 -inline fun Setting.valueReified(default: T): SerializableValue = valueFromKTypeImpl(typeOf()).cast() +inline fun Setting.valueReified(default: T): SerializerAwareValue = + valueFromKTypeImpl(typeOf()).cast() @MiraiExperimentalAPI -fun Setting.valueFromKType(type: KType): SerializableValue = valueFromKTypeImpl(type).cast() \ No newline at end of file +fun Setting.valueFromKType(type: KType): SerializerAwareValue = valueFromKTypeImpl(type).cast() \ No newline at end of file diff --git a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/setting/Value.kt b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/setting/Value.kt index 241cd2060..6e73d9289 100644 --- a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/setting/Value.kt +++ b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/setting/Value.kt @@ -15,6 +15,7 @@ import kotlinx.serialization.BinaryFormat import kotlinx.serialization.KSerializer import kotlinx.serialization.StringFormat import net.mamoe.mirai.console.setting.internal.map +import net.mamoe.mirai.console.setting.internal.setValueBySerializer import net.mamoe.mirai.utils.MiraiExperimentalAPI import kotlin.reflect.KProperty @@ -36,17 +37,22 @@ interface Value { * Typically returned by [Setting.value] functions. */ class SerializableValue( - delegate: Value, + private val delegate: Value, /** * The serializer used to update and dump [delegate] */ override val serializer: KSerializer -) : Value by delegate, SerializerAwareValue +) : Value by delegate, SerializerAwareValue { + override fun toString(): String = delegate.toString() +} fun Value.serializableValueWith( serializer: KSerializer ): SerializableValue { - return SerializableValue(this, serializer.map(serializer = { this.value }, deserializer = { this.value = it })) + return SerializableValue( + this, + serializer.map(serializer = { this.value }, deserializer = { this.setValueBySerializer(it) }) + ) } /** @@ -136,7 +142,6 @@ interface BooleanValue : PrimitiveValue */ interface StringValue : PrimitiveValue - //// endregion PrimitiveValues CODEGEN //// diff --git a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/setting/internal/CompositeValueImpl.kt b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/setting/internal/CompositeValueImpl.kt index 1b611fe9d..a3e666b92 100644 --- a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/setting/internal/CompositeValueImpl.kt +++ b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/setting/internal/CompositeValueImpl.kt @@ -25,7 +25,7 @@ internal fun Setting.createCompositeSetValueImpl(tToValue: (T) -> Value): internal abstract class CompositeSetValueImpl( tToValue: (T) -> Value // should override onChanged -) : CompositeSetValue { +) : CompositeSetValue, AbstractValueImpl>() { private val internalSet: MutableSet> = mutableSetOf() private var _value: Set = internalSet.shadowMap({ it.value }, tToValue).observable { onChanged() } @@ -34,12 +34,30 @@ internal abstract class CompositeSetValueImpl( get() = _value set(v) { if (_value != v) { + @Suppress("LocalVariableName") + val _value = _value as MutableSet + _value.clear() + _value.addAll(v) onChanged() - _value = v } } + override fun setValueBySerializer(value: Set) { + val thisValue = this.value + if (!thisValue.tryPatch(value)) { + this.value = value // deep set + } + } + protected abstract fun onChanged() + override fun toString(): String = _value.toString() + override fun equals(other: Any?): Boolean = + other is CompositeSetValueImpl<*> && other::class.java == this::class.java && other._value == this._value + + override fun hashCode(): Int { + val value = _value + return value.hashCode() * 31 + super.hashCode() + } } @@ -54,21 +72,39 @@ internal fun Setting.createCompositeListValueImpl(tToValue: (T) -> Value) internal abstract class CompositeListValueImpl( tToValue: (T) -> Value // should override onChanged -) : CompositeListValue { +) : CompositeListValue, AbstractValueImpl>() { private val internalList: MutableList> = mutableListOf() - private var _value: List = internalList.shadowMap({ it.value }, tToValue).observable { onChanged() } + private val _value: List = internalList.shadowMap({ it.value }, tToValue).observable { onChanged() } override var value: List get() = _value set(v) { if (_value != v) { + @Suppress("LocalVariableName") + val _value = _value as MutableList + _value.clear() + _value.addAll(v) onChanged() - _value = v } } + override fun setValueBySerializer(value: List) { + val thisValue = this.value + if (!thisValue.tryPatch(value)) { + this.value = value // deep set + } + } + protected abstract fun onChanged() + override fun toString(): String = _value.toString() + override fun equals(other: Any?): Boolean = + other is CompositeListValueImpl<*> && other::class.java == this::class.java && other._value == this._value + + override fun hashCode(): Int { + val value = _value + return value.hashCode() * 31 + super.hashCode() + } } // workaround to a type inference bug @@ -77,28 +113,85 @@ internal fun Setting.createCompositeMapValueImpl( vToValue: (V) -> Value ): CompositeMapValueImpl { return object : CompositeMapValueImpl(kToValue, vToValue) { - override fun onChanged() { - this@createCompositeMapValueImpl.onValueChanged(this) - } + override fun onChanged() = this@createCompositeMapValueImpl.onValueChanged(this) } } internal abstract class CompositeMapValueImpl( kToValue: (K) -> Value, // should override onChanged vToValue: (V) -> Value // should override onChanged -) : CompositeMapValue { +) : CompositeMapValue, AbstractValueImpl>() { private val internalList: MutableMap, Value> = mutableMapOf() - private var _value: Map = + private var _value: MutableMap = internalList.shadowMap({ it.value }, kToValue, { it.value }, vToValue).observable { onChanged() } override var value: Map get() = _value set(v) { if (_value != v) { + @Suppress("LocalVariableName") + val _value = _value + _value.clear() + _value.putAll(v) onChanged() - _value = v } } + override fun setValueBySerializer(value: Map) { + val thisValue = this.value as MutableMap + if (!thisValue.tryPatch(value)) { + this.value = value // deep set + } + } + protected abstract fun onChanged() + override fun toString(): String = _value.toString() + override fun equals(other: Any?): Boolean = + other is CompositeMapValueImpl<*, *> && other::class.java == this::class.java && other._value == this._value + + override fun hashCode(): Int { + val value = _value + return value.hashCode() * 31 + super.hashCode() + } +} + +internal fun MutableMap.patchImpl(_new: Map) { + val new = _new.toMutableMap() + val iterator = this.iterator() + for (entry in iterator) { + val newValue = new.remove(entry.key) + + if (newValue != null) { + // has replacer + if (entry.value?.tryPatch(newValue) != true) { + // patch not supported, or old value is null + entry.setValue(newValue) + } // else: patched, no remove + } else { + // no replacer + iterator.remove() + } + } + putAll(new) +} + +internal fun , E> C.patchImpl(_new: Collection) { + this.retainAll(_new) +} + +/** + * True if successfully patched + */ +@Suppress("UNCHECKED_CAST") +internal fun Any.tryPatch(any: Any): Boolean = when { + this is MutableCollection<*> && any is Collection<*> -> { + (this as MutableCollection).patchImpl(any as Collection) + true + } + this is MutableMap<*, *> && any is Map<*, *> -> { + (this as MutableMap).patchImpl(any as Map) + true + } + this is Value<*> && any is Value<*> -> any.value?.let { otherValue -> this.value?.tryPatch(otherValue) } == true + else -> false } \ No newline at end of file diff --git a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/setting/internal/Setting.value composite impl.kt b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/setting/internal/Setting.value composite impl.kt index 4c2ecabb8..4ada0dc61 100644 --- a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/setting/internal/Setting.value composite impl.kt +++ b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/setting/internal/Setting.value composite impl.kt @@ -13,7 +13,7 @@ package net.mamoe.mirai.console.setting.internal import kotlinx.serialization.* import kotlinx.serialization.builtins.* -import net.mamoe.mirai.console.setting.SerializableValue +import net.mamoe.mirai.console.setting.SerializerAwareValue import net.mamoe.mirai.console.setting.Setting import net.mamoe.mirai.console.setting.serializableValueWith import net.mamoe.mirai.console.setting.valueFromKType @@ -22,43 +22,52 @@ import net.mamoe.yamlkt.YamlNullableDynamicSerializer import kotlin.reflect.KClass import kotlin.reflect.KType +private val primitiveCollectionsImplemented by lazy { + false +} @PublishedApi @Suppress("UnsafeCall", "SMARTCAST_IMPOSSIBLE", "UNCHECKED_CAST") -internal fun Setting.valueFromKTypeImpl(type: KType): SerializableValue<*> { +internal fun Setting.valueFromKTypeImpl(type: KType): SerializerAwareValue<*> { val classifier = type.classifier require(classifier is KClass<*>) if (classifier.isPrimitiveOrBuiltInSerializableValue()) { - TODO("是基础类型, 可以直接创建 ValueImpl. ") + return valueImplPrimitive(classifier) as SerializerAwareValue<*> } // 复合类型 when (classifier) { - Map::class -> { + MutableMap::class, + Map::class + -> { val keyClass = type.arguments[0].type?.classifier require(keyClass is KClass<*>) val valueClass = type.arguments[1].type?.classifier require(valueClass is KClass<*>) - if (keyClass.isPrimitiveOrBuiltInSerializableValue() && valueClass.isPrimitiveOrBuiltInSerializableValue()) { + if (primitiveCollectionsImplemented && keyClass.isPrimitiveOrBuiltInSerializableValue() && valueClass.isPrimitiveOrBuiltInSerializableValue()) { // PrimitiveIntIntMap // ... TODO() } else { return createCompositeMapValueImpl( - kToValue = { valueFromKType(type.arguments[0].type!!) }, - vToValue = { valueFromKType(type.arguments[1].type!!) } + kToValue = { k -> valueFromKType(type.arguments[0].type!!).also { it.value = k } }, + vToValue = { v -> valueFromKType(type.arguments[1].type!!).also { it.value = v } } ).serializableValueWith(serializerMirai(type) as KSerializer>) // erased } } - List::class -> { + Collection::class, + MutableCollection::class, + MutableList::class, + List::class + -> { val elementClass = type.arguments[0].type?.classifier require(elementClass is KClass<*>) - if (elementClass.isPrimitiveOrBuiltInSerializableValue()) { + if (primitiveCollectionsImplemented && elementClass.isPrimitiveOrBuiltInSerializableValue()) { // PrimitiveIntList // ... TODO() @@ -67,11 +76,13 @@ internal fun Setting.valueFromKTypeImpl(type: KType): SerializableValue<*> { .serializableValueWith(serializerMirai(type) as KSerializer>) } } - Set::class -> { + MutableSet::class, + Set::class + -> { val elementClass = type.arguments[0].type?.classifier require(elementClass is KClass<*>) - if (elementClass.isPrimitiveOrBuiltInSerializableValue()) { + if (primitiveCollectionsImplemented && elementClass.isPrimitiveOrBuiltInSerializableValue()) { // PrimitiveIntSet // ... TODO() @@ -85,7 +96,6 @@ internal fun Setting.valueFromKTypeImpl(type: KType): SerializableValue<*> { } internal fun KClass<*>.isPrimitiveOrBuiltInSerializableValue(): Boolean { - return false // debug when (this) { Byte::class, Short::class, Int::class, Long::class, Boolean::class, diff --git a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/setting/internal/_PrimitiveValueDeclarations.kt b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/setting/internal/_PrimitiveValueDeclarations.kt new file mode 100644 index 000000000..cb2898a6f --- /dev/null +++ b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/setting/internal/_PrimitiveValueDeclarations.kt @@ -0,0 +1,346 @@ +/* + * Copyright 2020 Mamoe Technologies and contributors. + * + * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. + * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. + * + * https://github.com/mamoe/mirai/blob/master/LICENSE + */ + +package net.mamoe.mirai.console.setting.internal + +import kotlinx.serialization.Decoder +import kotlinx.serialization.Encoder +import kotlinx.serialization.KSerializer +import kotlinx.serialization.SerialDescriptor +import kotlinx.serialization.builtins.serializer +import net.mamoe.mirai.console.setting.* + +/** + * The super class to all ValueImpl s + */ +internal abstract class AbstractValueImpl : Value { + open fun setValueBySerializer(value: T) { + this.value = value + } +} + +internal fun Value.setValueBySerializer(value: T) = (this as AbstractValueImpl).setValueBySerializer(value) + +//// region PrimitiveValuesImpl CODEGEN //// + +internal abstract class ByteValueImpl : ByteValue, SerializerAwareValue, KSerializer, + AbstractValueImpl { + constructor() + constructor(default: Byte) { + _value = default + } + + private var _value: Byte? = null + + final override var value: Byte + get() = _value ?: error("ByteValue.value should be initialized before get.") + set(v) { + if (v != this._value) { + this._value = v + onChanged() + } + } + + protected abstract fun onChanged() + + final override val serializer: KSerializer get() = this + final override val descriptor: SerialDescriptor get() = BuiltInSerializerConstants.ByteSerializerDescriptor + final override fun serialize(encoder: Encoder, value: Unit) = Byte.serializer().serialize(encoder, this.value) + final override fun deserialize(decoder: Decoder) = setValueBySerializer(Byte.serializer().deserialize(decoder)) + override fun toString(): String = _value?.toString() ?: "ByteValue.value not yet initialized." + override fun equals(other: Any?): Boolean = + other is ByteValueImpl && other::class.java == this::class.java && other._value == this._value + + override fun hashCode(): Int { + val value = _value + return if (value == null) 1 + else value.hashCode() * 31 + } +} + +internal abstract class ShortValueImpl : ShortValue, SerializerAwareValue, KSerializer, + AbstractValueImpl { + constructor() + constructor(default: Short) { + _value = default + } + + private var _value: Short? = null + + final override var value: Short + get() = _value ?: error("ShortValue.value should be initialized before get.") + set(v) { + if (v != this._value) { + this._value = v + onChanged() + } + } + + protected abstract fun onChanged() + + final override val serializer: KSerializer get() = this + final override val descriptor: SerialDescriptor get() = BuiltInSerializerConstants.ShortSerializerDescriptor + final override fun serialize(encoder: Encoder, value: Unit) = Short.serializer().serialize(encoder, this.value) + final override fun deserialize(decoder: Decoder) = setValueBySerializer(Short.serializer().deserialize(decoder)) + override fun toString(): String = _value?.toString() ?: "ShortValue.value not yet initialized." + override fun equals(other: Any?): Boolean = + other is ShortValueImpl && other::class.java == this::class.java && other._value == this._value + + override fun hashCode(): Int { + val value = _value + return if (value == null) 1 + else value.hashCode() * 31 + } +} + +internal abstract class IntValueImpl : IntValue, SerializerAwareValue, KSerializer, AbstractValueImpl { + constructor() + constructor(default: Int) { + _value = default + } + + private var _value: Int? = null + + final override var value: Int + get() = _value ?: error("IntValue.value should be initialized before get.") + set(v) { + if (v != this._value) { + this._value = v + onChanged() + } + } + + protected abstract fun onChanged() + + final override val serializer: KSerializer get() = this + final override val descriptor: SerialDescriptor get() = BuiltInSerializerConstants.IntSerializerDescriptor + final override fun serialize(encoder: Encoder, value: Unit) = Int.serializer().serialize(encoder, this.value) + final override fun deserialize(decoder: Decoder) = setValueBySerializer(Int.serializer().deserialize(decoder)) + override fun toString(): String = _value?.toString() ?: "IntValue.value not yet initialized." + override fun equals(other: Any?): Boolean = + other is IntValueImpl && other::class.java == this::class.java && other._value == this._value + + override fun hashCode(): Int { + val value = _value + return if (value == null) 1 + else value.hashCode() * 31 + } +} + +internal abstract class LongValueImpl : LongValue, SerializerAwareValue, KSerializer, + AbstractValueImpl { + constructor() + constructor(default: Long) { + _value = default + } + + private var _value: Long? = null + + final override var value: Long + get() = _value ?: error("LongValue.value should be initialized before get.") + set(v) { + if (v != this._value) { + this._value = v + onChanged() + } + } + + protected abstract fun onChanged() + + final override val serializer: KSerializer get() = this + final override val descriptor: SerialDescriptor get() = BuiltInSerializerConstants.LongSerializerDescriptor + final override fun serialize(encoder: Encoder, value: Unit) = Long.serializer().serialize(encoder, this.value) + final override fun deserialize(decoder: Decoder) = setValueBySerializer(Long.serializer().deserialize(decoder)) + override fun toString(): String = _value?.toString() ?: "LongValue.value not yet initialized." + override fun equals(other: Any?): Boolean = + other is LongValueImpl && other::class.java == this::class.java && other._value == this._value + + override fun hashCode(): Int { + val value = _value + return if (value == null) 1 + else value.hashCode() * 31 + } +} + +internal abstract class FloatValueImpl : FloatValue, SerializerAwareValue, KSerializer, + AbstractValueImpl { + constructor() + constructor(default: Float) { + _value = default + } + + private var _value: Float? = null + + final override var value: Float + get() = _value ?: error("FloatValue.value should be initialized before get.") + set(v) { + if (v != this._value) { + this._value = v + onChanged() + } + } + + protected abstract fun onChanged() + + final override val serializer: KSerializer get() = this + final override val descriptor: SerialDescriptor get() = BuiltInSerializerConstants.FloatSerializerDescriptor + final override fun serialize(encoder: Encoder, value: Unit) = Float.serializer().serialize(encoder, this.value) + final override fun deserialize(decoder: Decoder) = setValueBySerializer(Float.serializer().deserialize(decoder)) + override fun toString(): String = _value?.toString() ?: "FloatValue.value not yet initialized." + override fun equals(other: Any?): Boolean = + other is FloatValueImpl && other::class.java == this::class.java && other._value == this._value + + override fun hashCode(): Int { + val value = _value + return if (value == null) 1 + else value.hashCode() * 31 + } +} + +internal abstract class DoubleValueImpl : DoubleValue, SerializerAwareValue, KSerializer, + AbstractValueImpl { + constructor() + constructor(default: Double) { + _value = default + } + + private var _value: Double? = null + + final override var value: Double + get() = _value ?: error("DoubleValue.value should be initialized before get.") + set(v) { + if (v != this._value) { + this._value = v + onChanged() + } + } + + protected abstract fun onChanged() + + final override val serializer: KSerializer get() = this + final override val descriptor: SerialDescriptor get() = BuiltInSerializerConstants.DoubleSerializerDescriptor + final override fun serialize(encoder: Encoder, value: Unit) = Double.serializer().serialize(encoder, this.value) + final override fun deserialize(decoder: Decoder) = setValueBySerializer(Double.serializer().deserialize(decoder)) + override fun toString(): String = _value?.toString() ?: "DoubleValue.value not yet initialized." + override fun equals(other: Any?): Boolean = + other is DoubleValueImpl && other::class.java == this::class.java && other._value == this._value + + override fun hashCode(): Int { + val value = _value + return if (value == null) 1 + else value.hashCode() * 31 + } +} + +internal abstract class CharValueImpl : CharValue, SerializerAwareValue, KSerializer, + AbstractValueImpl { + constructor() + constructor(default: Char) { + _value = default + } + + private var _value: Char? = null + + final override var value: Char + get() = _value ?: error("CharValue.value should be initialized before get.") + set(v) { + if (v != this._value) { + this._value = v + onChanged() + } + } + + protected abstract fun onChanged() + + final override val serializer: KSerializer get() = this + final override val descriptor: SerialDescriptor get() = BuiltInSerializerConstants.CharSerializerDescriptor + final override fun serialize(encoder: Encoder, value: Unit) = Char.serializer().serialize(encoder, this.value) + final override fun deserialize(decoder: Decoder) = setValueBySerializer(Char.serializer().deserialize(decoder)) + override fun toString(): String = _value?.toString() ?: "CharValue.value not yet initialized." + override fun equals(other: Any?): Boolean = + other is CharValueImpl && other::class.java == this::class.java && other._value == this._value + + override fun hashCode(): Int { + val value = _value + return if (value == null) 1 + else value.hashCode() * 31 + } +} + +internal abstract class BooleanValueImpl : BooleanValue, SerializerAwareValue, KSerializer, + AbstractValueImpl { + constructor() + constructor(default: Boolean) { + _value = default + } + + private var _value: Boolean? = null + + final override var value: Boolean + get() = _value ?: error("BooleanValue.value should be initialized before get.") + set(v) { + if (v != this._value) { + this._value = v + onChanged() + } + } + + protected abstract fun onChanged() + + final override val serializer: KSerializer get() = this + final override val descriptor: SerialDescriptor get() = BuiltInSerializerConstants.BooleanSerializerDescriptor + final override fun serialize(encoder: Encoder, value: Unit) = Boolean.serializer().serialize(encoder, this.value) + final override fun deserialize(decoder: Decoder) = setValueBySerializer(Boolean.serializer().deserialize(decoder)) + override fun toString(): String = _value?.toString() ?: "BooleanValue.value not yet initialized." + override fun equals(other: Any?): Boolean = + other is BooleanValueImpl && other::class.java == this::class.java && other._value == this._value + + override fun hashCode(): Int { + val value = _value + return if (value == null) 1 + else value.hashCode() * 31 + } +} + +internal abstract class StringValueImpl : StringValue, SerializerAwareValue, KSerializer, + AbstractValueImpl { + constructor() + constructor(default: String) { + _value = default + } + + private var _value: String? = null + + final override var value: String + get() = _value ?: error("StringValue.value should be initialized before get.") + set(v) { + if (v != this._value) { + this._value = v + onChanged() + } + } + + protected abstract fun onChanged() + + final override val serializer: KSerializer get() = this + final override val descriptor: SerialDescriptor get() = BuiltInSerializerConstants.StringSerializerDescriptor + final override fun serialize(encoder: Encoder, value: Unit) = String.serializer().serialize(encoder, this.value) + final override fun deserialize(decoder: Decoder) = setValueBySerializer(String.serializer().deserialize(decoder)) + override fun toString(): String = _value ?: "StringValue.value not yet initialized." + override fun equals(other: Any?): Boolean = + other is StringValueImpl && other::class.java == this::class.java && other._value == this._value + + override fun hashCode(): Int { + val value = _value + return if (value == null) 1 + else value.hashCode() * 31 + } +} + +//// endregion PrimitiveValuesImpl CODEGEN //// diff --git a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/setting/internal/_PrimitiveValueDeclareations.kt b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/setting/internal/_PrimitiveValueDeclareations.kt deleted file mode 100644 index cb9bee61b..000000000 --- a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/setting/internal/_PrimitiveValueDeclareations.kt +++ /dev/null @@ -1,52 +0,0 @@ -/* - * Copyright 2020 Mamoe Technologies and contributors. - * - * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. - * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. - * - * https://github.com/mamoe/mirai/blob/master/LICENSE - */ - -package net.mamoe.mirai.console.setting.internal - -import kotlinx.serialization.Decoder -import kotlinx.serialization.Encoder -import kotlinx.serialization.KSerializer -import kotlinx.serialization.SerialDescriptor -import kotlinx.serialization.builtins.serializer -import net.mamoe.mirai.console.setting.IntValue -import net.mamoe.mirai.console.setting.SerializerAwareValue - - -//// region PrimitiveValues CODEGEN //// - -// TODO: 2020/6/21 CODEGEN - -internal abstract class IntValueImpl : IntValue, SerializerAwareValue, KSerializer { - constructor() - constructor(default: Int) { - _value = default - } - - private var _value: Int? = null - - final override var value: Int - get() = _value ?: error("IntValue.value should be initialized before get.") - set(v) { - if (v != this._value) { - this._value = v - onChanged() - } - } - - protected abstract fun onChanged() - - final override val serializer: KSerializer get() = this - final override val descriptor: SerialDescriptor get() = BuiltInSerializerConstants.IntSerializerDescriptor - final override fun serialize(encoder: Encoder, value: Unit) = Int.serializer().serialize(encoder, this.value) - final override fun deserialize(decoder: Decoder) { - value = Int.serializer().deserialize(decoder) - } -} - -//// endregion PrimitiveValues CODEGEN //// diff --git a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/setting/internal/_Setting.value.kt b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/setting/internal/_Setting.value.kt index 4d01fbfe5..3b34fe942 100644 --- a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/setting/internal/_Setting.value.kt +++ b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/setting/internal/_Setting.value.kt @@ -12,10 +12,11 @@ package net.mamoe.mirai.console.setting.internal import kotlinx.serialization.builtins.serializer import net.mamoe.mirai.console.setting.SerializerAwareValue import net.mamoe.mirai.console.setting.Setting +import kotlin.reflect.KClass internal object BuiltInSerializerConstants { -//// region BuiltInSerializerConstants primitives CODEGEN //// + //// region BuiltInSerializerConstantsPrimitives CODEGEN //// @JvmStatic val ByteSerializerDescriptor = Byte.serializer().descriptor @@ -44,13 +45,55 @@ internal object BuiltInSerializerConstants { @JvmStatic val StringSerializerDescriptor = String.serializer().descriptor - -//// endregion BuiltInSerializerConstants primitives CODEGEN //// + //// endregion BuiltInSerializerConstantsPrimitives CODEGEN //// } -//// region Setting.value primitives impl CODEGEN //// +@Suppress("UNCHECKED_CAST") +internal fun Setting.valueImplPrimitive(kClass: KClass): SerializerAwareValue? { + return when (kClass) { + //// region Setting_valueImplPrimitive CODEGEN //// -// TODO: 2020/6/21 CODEGEN + Byte::class -> byteValueImpl() + Short::class -> shortValueImpl() + Int::class -> intValueImpl() + Long::class -> longValueImpl() + Float::class -> floatValueImpl() + Double::class -> doubleValueImpl() + Char::class -> charValueImpl() + Boolean::class -> booleanValueImpl() + String::class -> stringValueImpl() + + //// endregion Setting_valueImplPrimitive CODEGEN //// + else -> error("Internal error: unexpected type passed: ${kClass.qualifiedName}") + } as SerializerAwareValue? +} + + +//// region Setting_value_PrimitivesImpl CODEGEN //// + +internal fun Setting.valueImpl(default: Byte): SerializerAwareValue { + return object : ByteValueImpl(default) { + override fun onChanged() = this@valueImpl.onValueChanged(this) + } +} + +internal fun Setting.byteValueImpl(): SerializerAwareValue { + return object : ByteValueImpl() { + override fun onChanged() = this@byteValueImpl.onValueChanged(this) + } +} + +internal fun Setting.valueImpl(default: Short): SerializerAwareValue { + return object : ShortValueImpl(default) { + override fun onChanged() = this@valueImpl.onValueChanged(this) + } +} + +internal fun Setting.shortValueImpl(): SerializerAwareValue { + return object : ShortValueImpl() { + override fun onChanged() = this@shortValueImpl.onValueChanged(this) + } +} internal fun Setting.valueImpl(default: Int): SerializerAwareValue { return object : IntValueImpl(default) { @@ -58,4 +101,82 @@ internal fun Setting.valueImpl(default: Int): SerializerAwareValue { } } -//// endregion Setting.value primitives impl CODEGEN //// +internal fun Setting.intValueImpl(): SerializerAwareValue { + return object : IntValueImpl() { + override fun onChanged() = this@intValueImpl.onValueChanged(this) + } +} + +internal fun Setting.valueImpl(default: Long): SerializerAwareValue { + return object : LongValueImpl(default) { + override fun onChanged() = this@valueImpl.onValueChanged(this) + } +} + +internal fun Setting.longValueImpl(): SerializerAwareValue { + return object : LongValueImpl() { + override fun onChanged() = this@longValueImpl.onValueChanged(this) + } +} + +internal fun Setting.valueImpl(default: Float): SerializerAwareValue { + return object : FloatValueImpl(default) { + override fun onChanged() = this@valueImpl.onValueChanged(this) + } +} + +internal fun Setting.floatValueImpl(): SerializerAwareValue { + return object : FloatValueImpl() { + override fun onChanged() = this@floatValueImpl.onValueChanged(this) + } +} + +internal fun Setting.valueImpl(default: Double): SerializerAwareValue { + return object : DoubleValueImpl(default) { + override fun onChanged() = this@valueImpl.onValueChanged(this) + } +} + +internal fun Setting.doubleValueImpl(): SerializerAwareValue { + return object : DoubleValueImpl() { + override fun onChanged() = this@doubleValueImpl.onValueChanged(this) + } +} + +internal fun Setting.valueImpl(default: Char): SerializerAwareValue { + return object : CharValueImpl(default) { + override fun onChanged() = this@valueImpl.onValueChanged(this) + } +} + +internal fun Setting.charValueImpl(): SerializerAwareValue { + return object : CharValueImpl() { + override fun onChanged() = this@charValueImpl.onValueChanged(this) + } +} + +internal fun Setting.valueImpl(default: Boolean): SerializerAwareValue { + return object : BooleanValueImpl(default) { + override fun onChanged() = this@valueImpl.onValueChanged(this) + } +} + +internal fun Setting.booleanValueImpl(): SerializerAwareValue { + return object : BooleanValueImpl() { + override fun onChanged() = this@booleanValueImpl.onValueChanged(this) + } +} + +internal fun Setting.valueImpl(default: String): SerializerAwareValue { + return object : StringValueImpl(default) { + override fun onChanged() = this@valueImpl.onValueChanged(this) + } +} + +internal fun Setting.stringValueImpl(): SerializerAwareValue { + return object : StringValueImpl() { + override fun onChanged() = this@stringValueImpl.onValueChanged(this) + } +} + +//// endregion Setting_value_PrimitivesImpl CODEGEN //// diff --git a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/setting/internal/collectionUtil.kt b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/setting/internal/collectionUtil.kt index 75208385a..c691f1cd3 100644 --- a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/setting/internal/collectionUtil.kt +++ b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/setting/internal/collectionUtil.kt @@ -16,6 +16,8 @@ import kotlinx.serialization.serializer import net.mamoe.yamlkt.Yaml import kotlin.reflect.KClass +// TODO: 2020/6/24 优化性能: 引入一个 comparator 之类来替代将 Int 包装为 Value 后进行 containsKey 比较的方法 + internal inline fun MutableMap.shadowMap( crossinline kTransform: (K) -> KR, crossinline kTransformBack: (KR) -> K, @@ -37,6 +39,13 @@ internal inline fun MutableMap.shadowMap( override val value: VR get() = entry.value.let(vTransform) override fun setValue(newValue: VR): VR = entry.setValue(newValue.let(vTransformBack)).let(vTransform) + + override fun hashCode(): Int = 17 * 31 + (key?.hashCode() ?: 0) + (value?.hashCode() ?: 0) + override fun toString(): String = "$key=$value" + override fun equals(other: Any?): Boolean { + if (other == null || other !is Map.Entry<*, *>) return false + return other.key == key && other.value == value + } } } as ((MutableMap.MutableEntry) -> MutableMap.MutableEntry), // type inference bug transformBack = { entry -> @@ -45,6 +54,13 @@ internal inline fun MutableMap.shadowMap( override val value: V get() = entry.value.let(vTransformBack) override fun setValue(newValue: V): V = entry.setValue(newValue.let(vTransform)).let(vTransformBack) + + override fun hashCode(): Int = 17 * 31 + (key?.hashCode() ?: 0) + (value?.hashCode() ?: 0) + override fun toString(): String = "$key=$value" + override fun equals(other: Any?): Boolean { + if (other == null || other !is Map.Entry<*, *>) return false + return other.key == key && other.value == value + } } } ) @@ -64,6 +80,8 @@ internal inline fun MutableMap.shadowMap( } override fun remove(key: KR): VR? = this@shadowMap.remove(key.let(kTransformBack))?.let(vTransform) + override fun toString(): String = this@shadowMap.toString() + override fun hashCode(): Int = this@shadowMap.hashCode() } } @@ -82,6 +100,8 @@ internal inline fun MutableCollection.shadowMap( override fun hasNext(): Boolean = delegate.hasNext() override fun next(): R = delegate.next().let(transform) override fun remove() = delegate.remove() + override fun toString(): String = delegate.toString() + override fun hashCode(): Int = delegate.hashCode() } override fun add(element: R): Boolean = this@shadowMap.add(element.let(transformBack)) @@ -92,6 +112,8 @@ internal inline fun MutableCollection.shadowMap( override fun remove(element: R): Boolean = this@shadowMap.removeIf { it.let(transform) == element } override fun removeAll(elements: Collection): Boolean = elements.all(::remove) override fun retainAll(elements: Collection): Boolean = this@shadowMap.retainAll(elements.map(transformBack)) + override fun toString(): String = this@shadowMap.toString() + override fun hashCode(): Int = this@shadowMap.hashCode() } } @@ -112,6 +134,8 @@ internal inline fun MutableList.shadowMap( override fun hasNext(): Boolean = delegate.hasNext() override fun next(): R = delegate.next().let(transform) override fun remove() = delegate.remove() + override fun toString(): String = delegate.toString() + override fun hashCode(): Int = delegate.hashCode() } override fun lastIndexOf(element: R): Int = this@shadowMap.indexOfLast { it.let(transform) == element } @@ -134,6 +158,8 @@ internal inline fun MutableList.shadowMap( override fun next(): R = delegate.next().let(transform) override fun remove() = delegate.remove() override fun set(element: R) = delegate.set(element.let(transformBack)) + override fun toString(): String = delegate.toString() + override fun hashCode(): Int = delegate.hashCode() } override fun listIterator(index: Int): MutableListIterator = object : MutableListIterator { @@ -147,6 +173,8 @@ internal inline fun MutableList.shadowMap( override fun next(): R = delegate.next().let(transform) override fun remove() = delegate.remove() override fun set(element: R) = delegate.set(element.let(transformBack)) + override fun toString(): String = delegate.toString() + override fun hashCode(): Int = delegate.hashCode() } override fun remove(element: R): Boolean = this@shadowMap.removeIf { it.let(transform) == element } @@ -158,6 +186,9 @@ internal inline fun MutableList.shadowMap( override fun subList(fromIndex: Int, toIndex: Int): MutableList = this@shadowMap.subList(fromIndex, toIndex).map(transform).toMutableList() + + override fun toString(): String = this@shadowMap.toString() + override fun hashCode(): Int = this@shadowMap.hashCode() } } @@ -177,6 +208,8 @@ internal inline fun MutableSet.shadowMap( override fun hasNext(): Boolean = delegate.hasNext() override fun next(): R = delegate.next().let(transform) override fun remove() = delegate.remove() + override fun toString(): String = delegate.toString() + override fun hashCode(): Int = delegate.hashCode() } override fun add(element: R): Boolean = this@shadowMap.add(element.let(transformBack)) @@ -186,6 +219,8 @@ internal inline fun MutableSet.shadowMap( override fun remove(element: R): Boolean = this@shadowMap.removeIf { it.let(transform) == element } override fun removeAll(elements: Collection): Boolean = elements.all(::remove) override fun retainAll(elements: Collection): Boolean = this@shadowMap.retainAll(elements.map(transformBack)) + override fun toString(): String = this@shadowMap.toString() + override fun hashCode(): Int = this@shadowMap.hashCode() } } @@ -202,6 +237,8 @@ internal inline fun dynamicList(crossinline supplier: () -> List): List = supplier().listIterator() override fun listIterator(index: Int): ListIterator = supplier().listIterator(index) override fun subList(fromIndex: Int, toIndex: Int): List = supplier().subList(fromIndex, toIndex) + override fun toString(): String = supplier().toString() + override fun hashCode(): Int = supplier().hashCode() } } @@ -212,6 +249,8 @@ internal inline fun dynamicSet(crossinline supplier: () -> Set): Set { override fun containsAll(elements: Collection): Boolean = supplier().containsAll(elements) override fun isEmpty(): Boolean = supplier().isEmpty() override fun iterator(): Iterator = supplier().iterator() + override fun toString(): String = supplier().toString() + override fun hashCode(): Int = supplier().hashCode() } } @@ -239,6 +278,8 @@ internal inline fun dynamicMutableList(crossinline supplier: () -> MutableLi override fun retainAll(elements: Collection): Boolean = supplier().retainAll(elements) override fun set(index: Int, element: T): T = supplier().set(index, element) override fun subList(fromIndex: Int, toIndex: Int): MutableList = supplier().subList(fromIndex, toIndex) + override fun toString(): String = supplier().toString() + override fun hashCode(): Int = supplier().hashCode() } } @@ -256,6 +297,8 @@ internal inline fun dynamicMutableSet(crossinline supplier: () -> MutableSet override fun remove(element: T): Boolean = supplier().remove(element) override fun removeAll(elements: Collection): Boolean = supplier().removeAll(elements) override fun retainAll(elements: Collection): Boolean = supplier().retainAll(elements) + override fun toString(): String = supplier().toString() + override fun hashCode(): Int = supplier().hashCode() } } @@ -273,6 +316,8 @@ internal inline fun MutableMap.observable(crossinline onChanged: () override fun put(key: K, value: V): V? = this@observable.put(key, value).also { onChanged() } override fun putAll(from: Map) = this@observable.putAll(from).also { onChanged() } override fun remove(key: K): V? = this@observable.remove(key).also { onChanged() } + override fun toString(): String = this@observable.toString() + override fun hashCode(): Int = this@observable.hashCode() } } @@ -289,6 +334,8 @@ internal inline fun MutableList.observable(crossinline onChanged: () -> U override fun hasNext(): Boolean = delegate.hasNext() override fun next(): T = delegate.next() override fun remove() = delegate.remove().also { onChanged() } + override fun toString(): String = delegate.toString() + override fun hashCode(): Int = delegate.hashCode() } override fun lastIndexOf(element: T): Int = this@observable.lastIndexOf(element) @@ -310,6 +357,8 @@ internal inline fun MutableList.observable(crossinline onChanged: () -> U override fun next(): T = delegate.next() override fun remove() = delegate.remove().also { onChanged() } override fun set(element: T) = delegate.set(element).also { onChanged() } + override fun toString(): String = delegate.toString() + override fun hashCode(): Int = delegate.hashCode() } override fun listIterator(index: Int): MutableListIterator = object : MutableListIterator { @@ -323,6 +372,8 @@ internal inline fun MutableList.observable(crossinline onChanged: () -> U override fun next(): T = delegate.next() override fun remove() = delegate.remove().also { onChanged() } override fun set(element: T) = delegate.set(element).also { onChanged() } + override fun toString(): String = delegate.toString() + override fun hashCode(): Int = delegate.hashCode() } override fun remove(element: T): Boolean = this@observable.remove(element).also { onChanged() } @@ -335,6 +386,8 @@ internal inline fun MutableList.observable(crossinline onChanged: () -> U override fun set(index: Int, element: T): T = this@observable.set(index, element).also { onChanged() } override fun subList(fromIndex: Int, toIndex: Int): MutableList = this@observable.subList(fromIndex, toIndex) + override fun toString(): String = this@observable.toString() + override fun hashCode(): Int = this@observable.hashCode() } } @@ -349,6 +402,8 @@ internal inline fun MutableCollection.observable(crossinline onChanged: ( override fun hasNext(): Boolean = delegate.hasNext() override fun next(): T = delegate.next() override fun remove() = delegate.remove().also { onChanged() } + override fun toString(): String = delegate.toString() + override fun hashCode(): Int = delegate.hashCode() } override fun add(element: T): Boolean = this@observable.add(element).also { onChanged() } @@ -360,6 +415,9 @@ internal inline fun MutableCollection.observable(crossinline onChanged: ( override fun retainAll(elements: Collection): Boolean = this@observable.retainAll(elements).also { onChanged() } + + override fun toString(): String = this@observable.toString() + override fun hashCode(): Int = this@observable.hashCode() } } @@ -374,6 +432,8 @@ internal inline fun MutableSet.observable(crossinline onChanged: () -> Un override fun hasNext(): Boolean = delegate.hasNext() override fun next(): T = delegate.next() override fun remove() = delegate.remove().also { onChanged() } + override fun toString(): String = delegate.toString() + override fun hashCode(): Int = delegate.hashCode() } override fun add(element: T): Boolean = this@observable.add(element).also { onChanged() } @@ -385,6 +445,9 @@ internal inline fun MutableSet.observable(crossinline onChanged: () -> Un override fun retainAll(elements: Collection): Boolean = this@observable.retainAll(elements).also { onChanged() } + + override fun toString(): String = this@observable.toString() + override fun hashCode(): Int = this@observable.hashCode() } } diff --git a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/setting/internal/serializerUtil.kt b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/setting/internal/serializerUtil.kt index 454544929..7887e4052 100644 --- a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/setting/internal/serializerUtil.kt +++ b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/setting/internal/serializerUtil.kt @@ -15,6 +15,8 @@ import kotlin.reflect.full.findAnnotation internal val KProperty<*>.serialNameOrPropertyName: String get() = this.findAnnotation()?.value ?: this.name +internal fun Int.isOdd() = this and 0b1 != 0 + internal inline fun KSerializer.bind( crossinline setter: (E) -> Unit, crossinline getter: () -> E diff --git a/backend/mirai-console/src/test/kotlin/net/mamoe/mirai/console/setting/SettingTest.kt b/backend/mirai-console/src/test/kotlin/net/mamoe/mirai/console/setting/SettingTest.kt index a0abf50e5..893bcd131 100644 --- a/backend/mirai-console/src/test/kotlin/net/mamoe/mirai/console/setting/SettingTest.kt +++ b/backend/mirai-console/src/test/kotlin/net/mamoe/mirai/console/setting/SettingTest.kt @@ -9,21 +9,121 @@ package net.mamoe.mirai.console.setting -import net.mamoe.yamlkt.Yaml +import kotlinx.serialization.UnstableDefault +import kotlinx.serialization.json.Json +import kotlinx.serialization.json.JsonConfiguration import org.junit.jupiter.api.Test +import kotlin.test.assertEquals +import kotlin.test.assertSame internal class SettingTest { class MySetting : Setting() { - val int by value(1) + var int by value(1) val map by valueReified(mapOf("" to "")) - val map2 by valueReified(mapOf("" to mapOf("" to mapOf("" to "")))) + val map2 by valueReified(mutableMapOf("" to mutableMapOf("" to ""))) + } + + @OptIn(UnstableDefault::class) + private val jsonPrettyPrint = Json(JsonConfiguration(prettyPrint = true)) + private val json = Json(JsonConfiguration.Stable) + + @Test + fun testStringify() { + val setting = MySetting() + + var string = json.stringify(setting.updaterSerializer, Unit) + assertEquals("""{"int":1,"map":{},"map2":{}}""", string) + + setting.int = 2 + + string = json.stringify(setting.updaterSerializer, Unit) + assertEquals("""{"int":2,"map":{},"map2":{}}""", string) } @Test - fun testPrimitive() { + fun testParseUpdate() { val setting = MySetting() - val string = Yaml.nonStrict.stringify(setting.updaterSerializer, Unit) - println(string) + + assertEquals(1, setting.int) + + json.parse( + setting.updaterSerializer, """ + {"int":3,"map":{},"map2":{}} + """.trimIndent() + ) + + assertEquals(3, setting.int) + } + + @Test + fun testNestedParseUpdate() { + val setting = MySetting() + + fun delegation() = setting.map + + val refBefore = setting.map + fun reference() = refBefore + + assertEquals(mapOf(), delegation()) // delegation + + json.parse( + setting.updaterSerializer, """ + {"int":1,"map":{"t":"test"},"map2":{}} + """.trimIndent() + ) + + assertEquals(mapOf("t" to "test").toString(), delegation().toString()) + assertEquals(mapOf("t" to "test").toString(), reference().toString()) + + assertSame(reference(), delegation()) // check shadowing + } + + @Test + fun testDeepNestedParseUpdate() { + val setting = MySetting() + + fun delegation() = setting.map2 + + val refBefore = setting.map2 + fun reference() = refBefore + + assertEquals(mutableMapOf(), delegation()) // delegation + + json.parse( + setting.updaterSerializer, """ + {"int":1,"map":{},"map2":{"t":{"f":"test"}}} + """.trimIndent() + ) + + assertEquals(mapOf("t" to mapOf("f" to "test")).toString(), delegation().toString()) + assertEquals(mapOf("t" to mapOf("f" to "test")).toString(), reference().toString()) + + assertSame(reference(), delegation()) // check shadowing + } + + @Test + fun testDeepNestedTrackingParseUpdate() { + val setting = MySetting() + + setting.map2["t"] = mutableMapOf() + + fun delegation() = setting.map2["t"]!! + + val refBefore = setting.map2["t"]!! + fun reference() = refBefore + + assertEquals(mutableMapOf(), delegation()) // delegation + + json.parse( + setting.updaterSerializer, """ + {"int":1,"map":{},"map2":{"t":{"f":"test"}}} + """.trimIndent() + ) + + assertEquals(mapOf("f" to "test").toString(), delegation().toString()) + assertEquals(mapOf("f" to "test").toString(), reference().toString()) + + assertSame(reference(), delegation()) // check shadowing } } \ No newline at end of file