Implement composite Values

This commit is contained in:
Him188 2020-06-21 15:27:19 +08:00
parent 7ac1d94db9
commit 4ce4c025ee
7 changed files with 308 additions and 36 deletions

View File

@ -38,7 +38,7 @@ internal object MiraiConsoleInitializer {
internal object MiraiConsoleBuildConstants { // auto-filled on build (task :mirai-console:fillBuildConstants)
@JvmStatic
val buildDate: Date = Date(1592720608995) // 2020-06-21 14:23:28
val buildDate: Date = Date(1592723625351) // 2020-06-21 15:13:45
const val version: String = "0.5.1"
}

View File

@ -13,11 +13,14 @@ package net.mamoe.mirai.console.setting
import net.mamoe.mirai.console.setting.internal.cast
import net.mamoe.mirai.console.setting.internal.valueFromKTypeImpl
import net.mamoe.mirai.utils.MiraiExperimentalAPI
import kotlin.internal.LowPriorityInOverloadResolution
import kotlin.reflect.KProperty
import kotlin.reflect.KType
import kotlin.reflect.typeOf
// TODO: 2020/6/21 move to JvmPlugin to inherit SettingStorage and CoroutineScope for saving
// Shows public APIs such as deciding when to auto-save.
abstract class Setting : SettingImpl()
@ -37,6 +40,13 @@ internal abstract class SettingImpl {
private val valueNodes: List<Node<*>> = kotlin.run {
TODO("reflection")
}
/**
* flatten
*/
internal fun onValueChanged(value: Value<*>) {
}
}
@ -50,7 +60,7 @@ fun Setting.value(value: Int): IntValue = TODO("codegen")
/**
* Creates a [Value] with [default].
* Creates a [Value] with reified type.
*
* @param T reified param type T.
* Supports only primitives, Kotlin built-in collections,
@ -59,4 +69,7 @@ fun Setting.value(value: Int): IntValue = TODO("codegen")
*/
@LowPriorityInOverloadResolution
@OptIn(ExperimentalStdlibApi::class) // stable in 1.4
inline fun <reified T> Setting.value(default: T): Value<T> = valueFromKTypeImpl(typeOf<T>()).cast()
inline fun <reified T> Setting.valueReified(default: T): Value<T> = valueFromKTypeImpl(typeOf<T>()).cast()
@MiraiExperimentalAPI
fun <T> Setting.valueFromKType(type: KType): Value<T> = valueFromKTypeImpl(type).cast()

View File

@ -102,28 +102,29 @@ interface StringValue : PrimitiveValue<String>
@MiraiExperimentalAPI
interface CompositeValue<T> : Value<T>
/**
* Superclass of [CompositeListValue], [PrimitiveListValue].
*/
interface ListValue<T> : CompositeValue<List<T>>
interface ListValue<E> : CompositeValue<List<E>>
/**
* Elements can by anything, wrapped as [Value].
* @param T is not primitive types.
* @param E is not primitive types.
*/
interface CompositeListValue<T> : ListValue<Value<T>>
interface CompositeListValue<E> : ListValue<E>
/**
* Elements can only be primitives, not wrapped.
* @param T is not primitive types.
* @param E is not primitive types.
*/
interface PrimitiveListValue<T> : ListValue<T>
interface PrimitiveListValue<E> : ListValue<E>
//// region PrimitiveListValue CODEGEN ////
interface PrimitiveIntListValue<T> : PrimitiveListValue<T>
interface PrimitiveLongListValue<T> : PrimitiveListValue<T>
interface PrimitiveIntListValue : PrimitiveListValue<Int>
interface PrimitiveLongListValue : PrimitiveListValue<Long>
// TODO + codegen
//// endregion PrimitiveListValue CODEGEN ////
@ -132,25 +133,25 @@ interface PrimitiveLongListValue<T> : PrimitiveListValue<T>
/**
* Superclass of [CompositeSetValue], [PrimitiveSetValue].
*/
interface SetValue<T> : CompositeValue<Set<T>>
interface SetValue<E> : CompositeValue<Set<E>>
/**
* Elements can by anything, wrapped as [Value].
* @param T is not primitive types.
* @param E is not primitive types.
*/
interface CompositeSetValue<T> : SetValue<Value<T>>
interface CompositeSetValue<E> : SetValue<E>
/**
* Elements can only be primitives, not wrapped.
* @param T is not primitive types.
* @param E is not primitive types.
*/
interface PrimitiveSetValue<T> : SetValue<T>
interface PrimitiveSetValue<E> : SetValue<E>
//// region PrimitiveSetValue CODEGEN ////
interface PrimitiveIntSetValue<T> : PrimitiveSetValue<T>
interface PrimitiveLongSetValue<T> : PrimitiveSetValue<T>
interface PrimitiveIntSetValue : PrimitiveSetValue<Int>
interface PrimitiveLongSetValue : PrimitiveSetValue<Long>
// TODO + codegen
//// endregion PrimitiveSetValue CODEGEN ////
@ -161,7 +162,7 @@ interface PrimitiveLongSetValue<T> : PrimitiveSetValue<T>
*/
interface MapValue<K, V> : CompositeValue<Map<K, V>>
interface CompositeMapValue<K, V> : MapValue<Value<K>, Value<V>>
interface CompositeMapValue<K, V> : MapValue<K, V>
interface PrimitiveMapValue<K, V> : MapValue<K, V>

View File

@ -13,6 +13,7 @@ package net.mamoe.mirai.console.setting.internal
import net.mamoe.mirai.console.setting.Setting
import net.mamoe.mirai.console.setting.Value
import net.mamoe.mirai.console.setting.valueFromKType
import kotlin.reflect.KClass
import kotlin.reflect.KType
@ -31,21 +32,53 @@ internal fun Setting.valueFromKTypeImpl(type: KType): Value<*> {
when {
classifier == 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()) {
// PrimitiveIntIntMap
// ...
TODO()
} else {
return createCompositeMapValueImpl<Any?, Any?>(
kToValue = { valueFromKType(type.arguments[0].type!!) },
vToValue = { valueFromKType(type.arguments[1].type!!) }
)
}
}
classifier == List::class -> {
val elementClass = type.arguments[0].type?.classifier
require(elementClass is KClass<*>)
if (elementClass.isPrimitiveOrBuiltInSerializableValue()) {
// PrimitiveIntList
// ...
TODO()
} else {
return createCompositeListValueImpl<Any?> { valueFromKType(type.arguments[0].type!!) }
}
}
classifier == Set::class -> {
val elementClass = type.arguments[0].type?.classifier
require(elementClass is KClass<*>)
if (elementClass.isPrimitiveOrBuiltInSerializableValue()) {
// PrimitiveIntSet
// ...
TODO()
} else {
return createCompositeSetValueImpl<Any?> { valueFromKType(type.arguments[0].type!!) }
}
}
else -> error("Custom composite value is not supported yet (${classifier.qualifiedName})")
}
}
internal fun KClass<*>.isPrimitiveOrBuiltInSerializableValue(): Boolean {
return false // debug
when (this) {
Byte::class, Short::class, Int::class, Long::class,
Boolean::class,

View File

@ -11,9 +11,7 @@
package net.mamoe.mirai.console.setting.internal
import net.mamoe.mirai.console.setting.CompositeListValue
import net.mamoe.mirai.console.setting.IntValue
import net.mamoe.mirai.console.setting.Value
import net.mamoe.mirai.console.setting.*
internal abstract class IntValueImpl : IntValue {
constructor()
@ -35,13 +33,91 @@ internal abstract class IntValueImpl : IntValue {
protected abstract fun onChanged()
}
internal abstract class CompositeListValueImpl<T>(
val tToValue: (T) -> Value<T>
) : CompositeListValue<T> {
private var _value: List<Value<T>> = mutableListOf()
override var value: List<Value<T>>
get() = _value
set(v) {
_value = v
// type inference bug
internal fun <T> Setting.createCompositeSetValueImpl(tToValue: (T) -> Value<T>): CompositeSetValueImpl<T> {
return object : CompositeSetValueImpl<T>(tToValue) {
override fun onChanged() {
this@createCompositeSetValueImpl.onValueChanged(this)
}
}
}
internal abstract class CompositeSetValueImpl<T>(
tToValue: (T) -> Value<T> // should override onChanged
) : CompositeSetValue<T> {
private val internalSet: MutableSet<Value<T>> = mutableSetOf()
private var _value: Set<T> = internalSet.shadowMap({ it.value }, tToValue).observable { onChanged() }
override var value: Set<T>
get() = _value
set(v) {
if (_value != v) {
onChanged()
_value = v
}
}
protected abstract fun onChanged()
}
// type inference bug
internal fun <T> Setting.createCompositeListValueImpl(tToValue: (T) -> Value<T>): CompositeListValueImpl<T> {
return object : CompositeListValueImpl<T>(tToValue) {
override fun onChanged() {
this@createCompositeListValueImpl.onValueChanged(this)
}
}
}
internal abstract class CompositeListValueImpl<T>(
tToValue: (T) -> Value<T> // should override onChanged
) : CompositeListValue<T> {
private val internalList: MutableList<Value<T>> = mutableListOf()
private var _value: List<T> = internalList.shadowMap({ it.value }, tToValue).observable { onChanged() }
override var value: List<T>
get() = _value
set(v) {
if (_value != v) {
onChanged()
_value = v
}
}
protected abstract fun onChanged()
}
// workaround to a type inference bug
internal fun <K, V> Setting.createCompositeMapValueImpl(
kToValue: (K) -> Value<K>,
vToValue: (V) -> Value<V>
): CompositeMapValueImpl<K, V> {
return object : CompositeMapValueImpl<K, V>(kToValue, vToValue) {
override fun onChanged() {
this@createCompositeMapValueImpl.onValueChanged(this)
}
}
}
internal abstract class CompositeMapValueImpl<K, V>(
kToValue: (K) -> Value<K>, // should override onChanged
vToValue: (V) -> Value<V> // should override onChanged
) : CompositeMapValue<K, V> {
private val internalList: MutableMap<Value<K>, Value<V>> = mutableMapOf()
private var _value: Map<K, V> =
internalList.shadowMap({ it.value }, kToValue, { it.value }, vToValue).observable { onChanged() }
override var value: Map<K, V>
get() = _value
set(v) {
if (_value != v) {
onChanged()
_value = v
}
}
protected abstract fun onChanged()
}

View File

@ -7,6 +7,8 @@
* https://github.com/mamoe/mirai/blob/master/LICENSE
*/
@file:Suppress("DuplicatedCode")
package net.mamoe.mirai.console.setting.internal
import kotlinx.serialization.ImplicitReflectionSerializer
@ -14,7 +16,89 @@ import kotlinx.serialization.serializer
import net.mamoe.yamlkt.Yaml
import kotlin.reflect.KClass
internal fun <E, R> MutableList<E>.shadowMap(transform: (E) -> R, transformBack: (R) -> E): MutableList<R> {
internal inline fun <K, V, KR, VR> MutableMap<K, V>.shadowMap(
crossinline kTransform: (K) -> KR,
crossinline kTransformBack: (KR) -> K,
crossinline vTransform: (V) -> VR,
crossinline vTransformBack: (VR) -> V
): MutableMap<KR, VR> {
return object : MutableMap<KR, VR> {
override val size: Int get() = this@shadowMap.size
override fun containsKey(key: KR): Boolean = this@shadowMap.containsKey(key.let(kTransformBack))
override fun containsValue(value: VR): Boolean = this@shadowMap.containsValue(value.let(vTransformBack))
override fun get(key: KR): VR? = this@shadowMap[key.let(kTransformBack)]?.let(vTransform)
override fun isEmpty(): Boolean = this@shadowMap.isEmpty()
override val entries: MutableSet<MutableMap.MutableEntry<KR, VR>>
get() = this@shadowMap.entries.shadowMap(
transform = { entry: MutableMap.MutableEntry<K, V> ->
object : MutableMap.MutableEntry<KR, VR> {
override val key: KR get() = entry.key.let(kTransform)
override val value: VR get() = entry.value.let(vTransform)
override fun setValue(newValue: VR): VR =
entry.setValue(newValue.let(vTransformBack)).let(vTransform)
}
} as ((MutableMap.MutableEntry<K, V>) -> MutableMap.MutableEntry<KR, VR>), // type inference bug
transformBack = { entry ->
object : MutableMap.MutableEntry<K, V> {
override val key: K get() = entry.key.let(kTransformBack)
override val value: V get() = entry.value.let(vTransformBack)
override fun setValue(newValue: V): V =
entry.setValue(newValue.let(vTransform)).let(vTransformBack)
}
}
)
override val keys: MutableSet<KR>
get() = this@shadowMap.keys.shadowMap(kTransform, kTransformBack)
override val values: MutableCollection<VR>
get() = this@shadowMap.values.shadowMap(vTransform, vTransformBack)
override fun clear() = this@shadowMap.clear()
override fun put(key: KR, value: VR): VR? =
this@shadowMap.put(key.let(kTransformBack), value.let(vTransformBack))?.let(vTransform)
override fun putAll(from: Map<out KR, VR>) {
from.forEach { (kr, vr) ->
this@shadowMap[kr.let(kTransformBack)] = vr.let(vTransformBack)
}
}
override fun remove(key: KR): VR? = this@shadowMap.remove(key.let(kTransformBack))?.let(vTransform)
}
}
internal inline fun <E, R> MutableCollection<E>.shadowMap(
crossinline transform: (E) -> R,
crossinline transformBack: (R) -> E
): MutableCollection<R> {
return object : MutableCollection<R> {
override val size: Int get() = this@shadowMap.size
override fun contains(element: R): Boolean = this@shadowMap.any { it.let(transform) == element }
override fun containsAll(elements: Collection<R>): Boolean = elements.all(::contains)
override fun isEmpty(): Boolean = this@shadowMap.isEmpty()
override fun iterator(): MutableIterator<R> = object : MutableIterator<R> {
private val delegate = this@shadowMap.iterator()
override fun hasNext(): Boolean = delegate.hasNext()
override fun next(): R = delegate.next().let(transform)
override fun remove() = delegate.remove()
}
override fun add(element: R): Boolean = this@shadowMap.add(element.let(transformBack))
override fun addAll(elements: Collection<R>): Boolean = this@shadowMap.addAll(elements.map(transformBack))
override fun clear() = this@shadowMap.clear()
override fun remove(element: R): Boolean = this@shadowMap.removeIf { it.let(transform) == element }
override fun removeAll(elements: Collection<R>): Boolean = elements.all(::remove)
override fun retainAll(elements: Collection<R>): Boolean = this@shadowMap.retainAll(elements.map(transformBack))
}
}
internal inline fun <E, R> MutableList<E>.shadowMap(
crossinline transform: (E) -> R,
crossinline transformBack: (R) -> E
): MutableList<R> {
return object : MutableList<R> {
override val size: Int get() = this@shadowMap.size
@ -78,7 +162,10 @@ internal fun <E, R> MutableList<E>.shadowMap(transform: (E) -> R, transformBack:
}
internal fun <E, R> MutableSet<E>.shadowMap(transform: (E) -> R, transformBack: (R) -> E): MutableSet<R> {
internal inline fun <E, R> MutableSet<E>.shadowMap(
crossinline transform: (E) -> R,
crossinline transformBack: (R) -> E
): MutableSet<R> {
return object : MutableSet<R> {
override val size: Int get() = this@shadowMap.size
@ -102,7 +189,7 @@ internal fun <E, R> MutableSet<E>.shadowMap(transform: (E) -> R, transformBack:
}
}
internal fun <T> dynamicList(supplier: () -> List<T>): List<T> {
internal inline fun <T> dynamicList(crossinline supplier: () -> List<T>): List<T> {
return object : List<T> {
override val size: Int get() = supplier().size
override fun contains(element: T): Boolean = supplier().contains(element)
@ -118,7 +205,7 @@ internal fun <T> dynamicList(supplier: () -> List<T>): List<T> {
}
}
internal fun <T> dynamicSet(supplier: () -> Set<T>): Set<T> {
internal inline fun <T> dynamicSet(crossinline supplier: () -> Set<T>): Set<T> {
return object : Set<T> {
override val size: Int get() = supplier().size
override fun contains(element: T): Boolean = supplier().contains(element)
@ -129,7 +216,7 @@ internal fun <T> dynamicSet(supplier: () -> Set<T>): Set<T> {
}
internal fun <T> dynamicMutableList(supplier: () -> MutableList<T>): MutableList<T> {
internal inline fun <T> dynamicMutableList(crossinline supplier: () -> MutableList<T>): MutableList<T> {
return object : MutableList<T> {
override val size: Int get() = supplier().size
override fun contains(element: T): Boolean = supplier().contains(element)
@ -156,7 +243,7 @@ internal fun <T> dynamicMutableList(supplier: () -> MutableList<T>): MutableList
}
internal fun <T> dynamicMutableSet(supplier: () -> MutableSet<T>): MutableSet<T> {
internal inline fun <T> dynamicMutableSet(crossinline supplier: () -> MutableSet<T>): MutableSet<T> {
return object : MutableSet<T> {
override val size: Int get() = supplier().size
override fun contains(element: T): Boolean = supplier().contains(element)
@ -172,6 +259,23 @@ internal fun <T> dynamicMutableSet(supplier: () -> MutableSet<T>): MutableSet<T>
}
}
@Suppress("UNCHECKED_CAST", "USELESS_CAST") // type inference bug
internal inline fun <K, V> MutableMap<K, V>.observable(crossinline onChanged: () -> Unit): MutableMap<K, V> {
return object : MutableMap<K, V>, Map<K, V> by (this as Map<K, V>) {
override val keys: MutableSet<K>
get() = this@observable.keys.observable(onChanged)
override val values: MutableCollection<V>
get() = this@observable.values.observable(onChanged)
override val entries: MutableSet<MutableMap.MutableEntry<K, V>>
get() = this@observable.entries.observable(onChanged)
override fun clear() = this@observable.clear().also { onChanged() }
override fun put(key: K, value: V): V? = this@observable.put(key, value).also { onChanged() }
override fun putAll(from: Map<out K, V>) = this@observable.putAll(from).also { onChanged() }
override fun remove(key: K): V? = this@observable.remove(key).also { onChanged() }
}
}
internal inline fun <T> MutableList<T>.observable(crossinline onChanged: () -> Unit): MutableList<T> {
return object : MutableList<T> {
override val size: Int get() = this@observable.size
@ -234,6 +338,31 @@ internal inline fun <T> MutableList<T>.observable(crossinline onChanged: () -> U
}
}
internal inline fun <T> MutableCollection<T>.observable(crossinline onChanged: () -> Unit): MutableCollection<T> {
return object : MutableCollection<T> {
override val size: Int get() = this@observable.size
override fun contains(element: T): Boolean = this@observable.contains(element)
override fun containsAll(elements: Collection<T>): Boolean = this@observable.containsAll(elements)
override fun isEmpty(): Boolean = this@observable.isEmpty()
override fun iterator(): MutableIterator<T> = object : MutableIterator<T> {
private val delegate = this@observable.iterator()
override fun hasNext(): Boolean = delegate.hasNext()
override fun next(): T = delegate.next()
override fun remove() = delegate.remove().also { onChanged() }
}
override fun add(element: T): Boolean = this@observable.add(element).also { onChanged() }
override fun addAll(elements: Collection<T>): Boolean = this@observable.addAll(elements).also { onChanged() }
override fun clear() = this@observable.clear().also { onChanged() }
override fun remove(element: T): Boolean = this@observable.remove(element).also { onChanged() }
override fun removeAll(elements: Collection<T>): Boolean =
this@observable.removeAll(elements).also { onChanged() }
override fun retainAll(elements: Collection<T>): Boolean =
this@observable.retainAll(elements).also { onChanged() }
}
}
internal inline fun <T> MutableSet<T>.observable(crossinline onChanged: () -> Unit): MutableSet<T> {
return object : MutableSet<T> {
override val size: Int get() = this@observable.size

View File

@ -0,0 +1,20 @@
/*
* 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
import org.junit.jupiter.api.Test
internal class SettingTest {
@Test
fun testPrimitive() {
}
}