internal.data.PluginDataImpl.kt Maven / Gradle / Ivy
/*
* Copyright 2019-2020 Mamoe Technologies and contributors.
*
* 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证.
* Use of this source code is governed by the GNU AFFERO GENERAL PUBLIC LICENSE version 3 license that can be found through the following link.
*
* https://github.com/mamoe/mirai/blob/master/LICENSE
*/
@file:Suppress("INVISIBLE_REFERENCE", "INVISIBLE_MEMBER", "EXPOSED_SUPER_CLASS")
package net.mamoe.mirai.console.internal.data
import kotlinx.serialization.KSerializer
import kotlinx.serialization.SerialName
import kotlinx.serialization.builtins.serializer
import kotlinx.serialization.descriptors.SerialDescriptor
import kotlinx.serialization.encoding.CompositeDecoder
import kotlinx.serialization.encoding.Decoder
import kotlinx.serialization.encoding.Encoder
import net.mamoe.mirai.console.data.AbstractPluginData
import net.mamoe.mirai.console.data.AbstractPluginData.ValueNode
import net.mamoe.mirai.console.data.PluginData
import net.mamoe.mirai.console.data.ValueDescription
import net.mamoe.mirai.console.data.ValueName
import net.mamoe.yamlkt.Comment
import net.mamoe.yamlkt.YamlNullableDynamicSerializer
import java.lang.reflect.Constructor
import kotlin.reflect.KAnnotatedElement
/**
* Internal implementation for [PluginData] including:
* - Reflection on Kotlin properties and Java fields
* - Auto-saving
*/
internal abstract class PluginDataImpl {
init {
@Suppress("LeakingThis")
check(this is AbstractPluginData)
}
private fun findNodeInstance(name: String): ValueNode<*>? {
check(this is AbstractPluginData)
return valueNodes.firstOrNull { it.valueName == name }
}
internal open val updaterSerializer: KSerializer = object : KSerializer {
override val descriptor: SerialDescriptor by lazy {
check(this@PluginDataImpl is AbstractPluginData)
kotlinx.serialization.descriptors.buildClassSerialDescriptor((this@PluginDataImpl as PluginData).saveName) {
for (valueNode in valueNodes) valueNode.run {
element(valueName, updaterSerializer.descriptor, annotations = annotations, isOptional = true)
}
}
}
@Suppress("UNCHECKED_CAST")
override fun deserialize(decoder: Decoder) {
val descriptor = descriptor
with(decoder.beginStructure(descriptor)) {
if (decodeSequentially()) {
var index = 0
repeat(decodeCollectionSize(descriptor)) {
val valueName = decodeSerializableElement(descriptor, index++, String.serializer())
val node = findNodeInstance(valueName)
if (node == null) {
decodeSerializableElement(descriptor, index++, YamlNullableDynamicSerializer)
} else {
decodeSerializableElement(descriptor, index++, node.updaterSerializer)
}
}
} else {
outerLoop@ while (true) {
innerLoop@ while (true) {
val index = decodeElementIndex(descriptor)
if (index == CompositeDecoder.DECODE_DONE) {
//check(valueName == null) { "name must be null at this moment." }
break@outerLoop
}
val node = findNodeInstance(descriptor.getElementName(index))
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) {
check(this@PluginDataImpl is AbstractPluginData)
val descriptor = descriptor
with(encoder.beginStructure(descriptor)) {
repeat(descriptor.elementsCount) { index ->
encodeSerializableElement(
descriptor,
index,
valueNodes.find { it.valueName == descriptor.getElementName(index) }?.updaterSerializer
?: error("Cannot find a serializer for ${descriptor.getElementName(index)}"),
Unit
)
}
endStructure(descriptor)
}
}
}
}
internal fun KAnnotatedElement.getAnnotationListForValueSerialization(): List {
return this.annotations.mapNotNull {
when (it) {
is SerialName -> error("@SerialName is not supported on Value. Please use @ValueName instead")
is ValueName -> null
is ValueDescription -> COMMENT_CONSTRUCTOR(it.value)
else -> it
}
}
}
private val COMMENT_CONSTRUCTOR = findAnnotationImplementationClassConstructor()!!
@Suppress("NOTHING_TO_INLINE")
internal inline operator fun Constructor.invoke(vararg args: Any?): T = this.newInstance(*args)
internal inline fun findAnnotationImplementationClassConstructor(): Constructor? {
@Suppress("UNCHECKED_CAST")
return T::class.nestedClasses
.find { it.simpleName?.endsWith("Impl") == true }?.java?.run {
constructors.singleOrNull()
} as Constructor?
}