diff --git a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/data/PluginDataExtensions.kt b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/data/PluginDataExtensions.kt index 8523353bd..4681f1394 100644 --- a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/data/PluginDataExtensions.kt +++ b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/data/PluginDataExtensions.kt @@ -1,15 +1,63 @@ -@file:Suppress("unused", "INAPPLICABLE_JVM_NAME") +@file:Suppress("unused", "INAPPLICABLE_JVM_NAME", "INVISIBLE_REFERENCE", "INVISIBLE_MEMBER") package net.mamoe.mirai.console.data import net.mamoe.mirai.console.data.PluginDataExtensions.withDefault import net.mamoe.mirai.console.internal.data.ShadowMap +import net.mamoe.mirai.console.util.ConsoleExperimentalAPI +import kotlin.internal.LowPriorityInOverloadResolution /** * [PluginData] 相关一些扩展 */ public object PluginDataExtensions { + @ConsoleExperimentalAPI + public open class NotNullMap internal constructor( + private val delegate: Map + ) : Map by delegate { + override fun get(key: K): V = + delegate[key] ?: error("Internal error: delegate[key] returned null for NotNullMap.get") + + @Deprecated( + "getOrDefault on NotNullMap always returns the value in the map, and defaultValue will never be returned.", + level = DeprecationLevel.WARNING, + replaceWith = ReplaceWith("this.get(key)") + ) + override fun getOrDefault(key: K, defaultValue: V): V { + return super.getOrDefault(key, defaultValue) + } + } + + @Suppress("DELEGATED_MEMBER_HIDES_SUPERTYPE_OVERRIDE") // as designed + public class NotNullMutableMap internal constructor( + private val delegate: MutableMap + ) : MutableMap by delegate, NotNullMap(delegate) { + override fun get(key: K): V = + delegate[key] ?: error("Internal error: delegate[key] returned null for NotNullMutableMap.get") + + @Deprecated( + "getOrDefault on NotNullMutableMap always returns the value in the map, and defaultValue will never be returned.", + level = DeprecationLevel.WARNING, + replaceWith = ReplaceWith("this.get(key)") + ) + override fun getOrDefault(key: K, defaultValue: V): V { + return super.getOrDefault(key, defaultValue) + } + + override fun put(key: K, value: V): V { + return delegate.put(key, value) + ?: error("Internal error: delegate.put(key, value) returned null for NotNullMutableMap.put") + } + + @Deprecated( + "putIfAbsent on NotNullMutableMap always does nothing.", + level = DeprecationLevel.WARNING, + replaceWith = ReplaceWith("") + ) + override fun putIfAbsent(key: K, value: V): Nothing? = null + } + /** * 创建一个代理对象, 当 [Map.get] 返回 `null` 时先放入一个 [LinkedHashMap], 再从 [this] 中取出链接自动保存的 [LinkedHashMap]. ([MutableMap.getOrPut] 的替代) * @@ -17,7 +65,7 @@ public object PluginDataExtensions { */ @JvmName("withEmptyDefaultMapImmutable") @JvmStatic - public fun SerializerAwareValue>>.withEmptyDefault(): SerializerAwareValue>> { + public fun SerializerAwareValue>>.withEmptyDefault(): SerializerAwareValue>> { return this.withDefault { LinkedHashMap() } } @@ -27,7 +75,7 @@ public object PluginDataExtensions { */ @JvmName("withEmptyDefaultMap") @JvmStatic - public fun SerializerAwareValue>>.withEmptyDefault(): SerializerAwareValue>> { + public fun SerializerAwareValue>>.withEmptyDefault(): SerializerAwareValue>> { return this.withDefault { LinkedHashMap() } } @@ -38,7 +86,7 @@ public object PluginDataExtensions { */ @JvmName("withEmptyDefaultListImmutable") @JvmStatic - public fun SerializerAwareValue>>.withEmptyDefault(): SerializerAwareValue>> { + public fun SerializerAwareValue>>.withEmptyDefault(): SerializerAwareValue>> { return this.withDefault { ArrayList() } } @@ -48,7 +96,7 @@ public object PluginDataExtensions { */ @JvmName("withEmptyDefaultList") @JvmStatic - public fun SerializerAwareValue>>.withEmptyDefault(): SerializerAwareValue>> { + public fun SerializerAwareValue>>.withEmptyDefault(): SerializerAwareValue>> { return this.withDefault { ArrayList() } } @@ -59,7 +107,7 @@ public object PluginDataExtensions { */ @JvmName("withEmptyDefaultSetImmutable") @JvmStatic - public fun SerializerAwareValue>>.withEmptyDefault(): SerializerAwareValue>> { + public fun SerializerAwareValue>>.withEmptyDefault(): SerializerAwareValue>> { return this.withDefault { LinkedHashSet() } } @@ -69,7 +117,7 @@ public object PluginDataExtensions { */ @JvmName("withEmptyDefaultSet") @JvmStatic - public fun SerializerAwareValue>>.withEmptyDefault(): SerializerAwareValue>> { + public fun SerializerAwareValue>>.withEmptyDefault(): SerializerAwareValue>> { return this.withDefault { LinkedHashSet() } } @@ -78,15 +126,47 @@ public object PluginDataExtensions { * 创建一个代理对象, 当 [Map.get] 返回 `null` 时先调用 [defaultValueComputer] 并放入 [Map], 再返回调用的返回值 */ @JvmStatic + @JvmName("withDefaultMapImmutableNotNull") + public fun SerializerAwareValue>.withDefault(defaultValueComputer: (K) -> V): SerializerAwareValue> { + @Suppress("UNCHECKED_CAST") // magic + return (this as SerializerAwareValue>).withDefault(defaultValueComputer) as SerializerAwareValue> + } + + /** + * 创建一个代理对象, 当 [Map.get] 返回 `null` 时先调用 [defaultValueComputer] 并放入 [Map], 再返回调用的返回值 + */ + @JvmStatic + @LowPriorityInOverloadResolution @JvmName("withDefaultMapImmutable") public fun SerializerAwareValue>.withDefault(defaultValueComputer: (K) -> V): SerializerAwareValue> { @Suppress("UNCHECKED_CAST") // magic return (this as SerializerAwareValue>).withDefault(defaultValueComputer) as SerializerAwareValue> } + @JvmStatic + @JvmName("withDefaultMapNotNull") + public fun SerializerAwareValue>.withDefault(defaultValueComputer: (K) -> V): SerializerAwareValue> { + val origin = this + + @Suppress("UNCHECKED_CAST") + return SerializableValue( + object : CompositeMapValue { + private val instance = NotNullMutableMap(createDelegateInstance(origin, defaultValueComputer)) + + override var value: Map + get() = instance + set(value) { + origin.value = value as MutableMap // erased cast + } + } as Value>, // erased cast + this.serializer + ) + } + /** * 创建一个代理对象, 当 [Map.get] 返回 `null` 时先调用 [defaultValueComputer] 并放入 [Map], 再返回调用的返回值 */ + @LowPriorityInOverloadResolution @JvmStatic @JvmName("withDefaultMap") public fun SerializerAwareValue>.withDefault(defaultValueComputer: (K) -> V): SerializerAwareValue> { @@ -95,24 +175,7 @@ public object PluginDataExtensions { @Suppress("UNCHECKED_CAST") return SerializableValue( object : CompositeMapValue { - private val instance = object : MutableMap, AbstractMap() { - override val entries: MutableSet> get() = origin.value.entries - override val keys: MutableSet get() = origin.value.keys - override val values: MutableCollection get() = origin.value.values - override fun clear() = origin.value.clear() - override fun putAll(from: Map) = origin.value.putAll(from) - override fun remove(key: K): V? = origin.value.remove(key) - override fun put(key: K, value: V): V? = origin.value.put(key, value) - - override fun get(key: K): V? { - // the only difference - val result = origin.value[key] - if (result != null) return result - put(key, defaultValueComputer(key)) - return origin.value[key] - } - } - + private val instance = createDelegateInstance(origin, defaultValueComputer) override var value: Map get() = instance set(value) { @@ -123,6 +186,57 @@ public object PluginDataExtensions { ) } + private fun createDelegateInstance( + origin: SerializerAwareValue>, + defaultValueComputer: (K) -> V + ): MutableMap { + return object : MutableMap, AbstractMap() { + override val entries: MutableSet> get() = origin.value.entries + override val keys: MutableSet get() = origin.value.keys + override val values: MutableCollection get() = origin.value.values + override fun clear() = origin.value.clear() + override fun putAll(from: Map) = origin.value.putAll(from) + override fun remove(key: K): V? = origin.value.remove(key) + override fun put(key: K, value: V): V? = origin.value.put(key, value) + + override fun get(key: K): V? { + // the only difference + val result = origin.value[key] + if (result != null) return result + put(key, defaultValueComputer(key)) + return origin.value[key] + } + } + } + + + /** + * 替换 [MutableMap] 的 key + */ + @JvmName("mapKeysNotNull") + @JvmStatic + public fun SerializerAwareValue>.mapKeys( + oldToNew: (OldK) -> NewK, + newToOld: (NewK) -> OldK, + ): SerializerAwareValue> { + val origin = this + + @Suppress("UNCHECKED_CAST") + return SerializableValue( + object : CompositeMapValue { + private val instance = + NotNullMutableMap(ShadowMap({ origin.value }, oldToNew, newToOld, { it }, { it })) + + override var value: Map + get() = instance + set(value) { + origin.value = + value.mapKeysTo(NotNullMutableMap(LinkedHashMap())) { it.key.let(newToOld) } // erased cast + } + } as Value>, // erased cast + this.serializer + ) + } /** * 替换 [MutableMap] 的 key @@ -177,4 +291,72 @@ public object PluginDataExtensions { this.serializer ) } -} \ No newline at end of file + + /** + * 替换 [Map] 的 key + */ + @JvmName("mapKeysImmutableNotNull") + @JvmStatic + public fun SerializerAwareValue>.mapKeys( + oldToNew: (OldK) -> NewK, + newToOld: (NewK) -> OldK, + ): SerializerAwareValue> { + val origin = this + + @Suppress("UNCHECKED_CAST") + return SerializableValue( + object : CompositeMapValue { + // casting Map to MutableMap is OK here, as we don't call mutable functions + private val instance = + NotNullMap(ShadowMap({ origin.value as MutableMap }, oldToNew, newToOld, { it }, { it })) + + override var value: Map + get() = instance + set(value) { + origin.value = + value.mapKeysTo(NotNullMutableMap(LinkedHashMap())) { it.key.let(newToOld) } // erased cast + } + } as Value>, // erased cast + this.serializer + ) + } +} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/internal/data/builtins/BotManagerImpl.kt b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/internal/data/builtins/BotManagerImpl.kt index 0cdf45731..3abac0c9a 100644 --- a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/internal/data/builtins/BotManagerImpl.kt +++ b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/internal/data/builtins/BotManagerImpl.kt @@ -44,8 +44,10 @@ internal object ManagersConfig : AutoSavePluginConfig() { 管理员列表 """ ) - private val managers by value>>().withEmptyDefault() - .mapKeys(Bot::getInstance, Bot::id) + private val managers + by value>>() + .withEmptyDefault() + .mapKeys(Bot::getInstance, Bot::id) - internal operator fun get(bot: Bot): MutableSet = managers[bot]!! + internal operator fun get(bot: Bot): MutableSet = managers[bot] } \ No newline at end of file