feature/root #69

Merged
altavir merged 23 commits from feature/root into dev 2021-09-11 19:14:21 +03:00
2 changed files with 114 additions and 116 deletions
Showing only changes of commit 1b2d61008f - Show all commits

View File

@ -1,15 +1,9 @@
package ru.mipt.npm.root package ru.mipt.npm.root
import kotlinx.serialization.* import kotlinx.serialization.SerialName
import kotlinx.serialization.descriptors.SerialDescriptor import kotlinx.serialization.Serializable
import kotlinx.serialization.encoding.Decoder import kotlinx.serialization.json.JsonElement
import kotlinx.serialization.encoding.Encoder import kotlinx.serialization.json.decodeFromJsonElement
import kotlinx.serialization.json.Json
import kotlinx.serialization.json.JsonDecoder
import kotlinx.serialization.json.JsonObject
import kotlinx.serialization.modules.SerializersModule
import kotlinx.serialization.modules.polymorphic
import kotlinx.serialization.modules.subclass
@Serializable @Serializable
@SerialName("TGeoManager") @SerialName("TGeoManager")
@ -26,113 +20,10 @@ public class TGeoManager : TNamed() {
/** /**
* Load Json encoded TGeoManager * Load Json encoded TGeoManager
*/ */
public fun decodeFromJson(jsonObject: JsonObject): TGeoManager = TODO() public fun decodeFromJson(jsonElement: JsonElement): TGeoManager =
rootJson().decodeFromJsonElement(jsonElement)
public fun decodeFromString(string: String): TGeoManager = public fun decodeFromString(string: String): TGeoManager =
Root().decodeFromString(serializer(), string) rootJson().decodeFromString(serializer(), string)
} }
} }
@OptIn(ExperimentalSerializationApi::class)
private class RootJsonSerializer<T>(private val tSerializer: KSerializer<T>) : KSerializer<T> {
private val refCache: HashMap<UInt, TObject> = HashMap()
override val descriptor: SerialDescriptor get() = tSerializer.descriptor
override fun deserialize(decoder: Decoder): T {
val input = decoder as JsonDecoder
val element = input.decodeJsonElement()
return input.json.decodeFromJsonElement(tSerializer, transformDeserialize(element))
}
override fun serialize(encoder: Encoder, value: T) {
tSerializer.serialize(encoder, value)
}
override fun <T> decodeFromString(deserializer: DeserializationStrategy<T>, string: String): T {
val match = refRegex.matchEntire(string)
return if (match != null) {
//Do unref
val ref = match.value.toUIntOrNull() ?: error("Ref value is not a number")
val refValue = refCache[ref] ?: error("Reference $ref unresolved")
refValue as T //TODO research means to make it safe
} else {
val res = rootJson.decodeFromString(deserializer, string)
val uid = (res as? TObject)?.fUniqueID
if (uid != null && refCache[uid] == null) {
refCache[uid] = res
}
res
}
}
override fun <T> encodeToString(serializer: SerializationStrategy<T>, value: T): String =
rootJson.encodeToString(serializer, value)
companion object {
val refRegex = """\{\s*"${"\\$"}ref"\s*:\s*(\d*)}""".toRegex()
val rootSerializersModule = SerializersModule {
polymorphic(TGeoShape::class) {
subclass(TGeoBBox.serializer())
subclass(TGeoCompositeShape.serializer())
subclass(TGeoXtru.serializer())
subclass(TGeoTube.serializer())
subclass(TGeoTubeSeg.serializer())
subclass(TGeoShapeAssembly.serializer())
}
polymorphic(TGeoMatrix::class) {
subclass(TGeoIdentity.serializer())
subclass(TGeoHMatrix.serializer())
subclass(TGeoTranslation.serializer())
subclass(TGeoRotation.serializer())
subclass(TGeoCombiTrans.serializer())
}
polymorphic(TObject::class) {
subclass(TGeoBBox.serializer())
subclass(TGeoCompositeShape.serializer())
subclass(TGeoXtru.serializer())
subclass(TGeoTube.serializer())
subclass(TGeoTubeSeg.serializer())
subclass(TGeoShapeAssembly.serializer())
subclass(TGeoIdentity.serializer())
subclass(TGeoHMatrix.serializer())
subclass(TGeoTranslation.serializer())
subclass(TGeoRotation.serializer())
subclass(TGeoCombiTrans.serializer())
subclass(TGeoMaterial.serializer())
subclass(TGeoMixture.serializer())
subclass(TGeoMedium.serializer())
subclass(TGeoNode.serializer())
subclass(TGeoNodeMatrix.serializer())
subclass(TGeoVolume.serializer())
subclass(TGeoVolumeAssembly.serializer())
}
polymorphic(TGeoNode::class, TGeoNode.serializer()) {
subclass(TGeoNodeMatrix.serializer())
}
polymorphic(TGeoVolume::class, TGeoVolume.serializer()) {
subclass(TGeoVolumeAssembly.serializer())
}
}
val rootJson: Json = Json {
encodeDefaults = true
ignoreUnknownKeys = true
classDiscriminator = "_typename"
serializersModule = rootSerializersModule
}
}
}

View File

@ -0,0 +1,107 @@
package ru.mipt.npm.root
import kotlinx.serialization.KSerializer
import kotlinx.serialization.descriptors.SerialDescriptor
import kotlinx.serialization.encoding.Decoder
import kotlinx.serialization.encoding.Encoder
import kotlinx.serialization.json.*
import kotlinx.serialization.modules.PolymorphicModuleBuilder
import kotlinx.serialization.modules.SerializersModule
import kotlinx.serialization.modules.polymorphic
import kotlinx.serialization.modules.subclass
private typealias RefCache = MutableList<TObject>
private class RootJsonSerializer<T : TObject>(
private val refCache: RefCache,
private val tSerializer: KSerializer<T>
) : KSerializer<T> {
override val descriptor: SerialDescriptor get() = tSerializer.descriptor
override fun deserialize(decoder: Decoder): T {
val input = decoder as JsonDecoder
val element = input.decodeJsonElement()
val refId = (element as? JsonObject)?.get("\$ref")?.jsonPrimitive?.int
return if (refId != null) {
//Do unref
val refValue = refCache[refId]
refValue as T //TODO research means to make it safe
} else {
val res = input.json.decodeFromJsonElement(tSerializer, element)
//val uid = res.fUniqueID
refCache.add(res)
res
}
}
override fun serialize(encoder: Encoder, value: T) {
tSerializer.serialize(encoder, value)
}
}
private fun <T : TObject> KSerializer<T>.unref(refCache: RefCache): RootJsonSerializer<T> =
RootJsonSerializer(refCache, this)
private fun PolymorphicModuleBuilder<TGeoShape>.shapes(refCache: RefCache) {
subclass(TGeoBBox.serializer().unref(refCache))
subclass(TGeoCompositeShape.serializer().unref(refCache))
subclass(TGeoXtru.serializer().unref(refCache))
subclass(TGeoTube.serializer().unref(refCache))
subclass(TGeoTubeSeg.serializer().unref(refCache))
subclass(TGeoShapeAssembly.serializer().unref(refCache))
}
private fun PolymorphicModuleBuilder<TGeoMatrix>.matrices(refCache: RefCache) {
subclass(TGeoIdentity.serializer().unref(refCache))
subclass(TGeoHMatrix.serializer().unref(refCache))
subclass(TGeoTranslation.serializer().unref(refCache))
subclass(TGeoRotation.serializer().unref(refCache))
subclass(TGeoCombiTrans.serializer().unref(refCache))
}
/**
* Create an instance of Json with unfolding Root references. This instance could not be reused because of the cache.
*/
internal fun rootJson(): Json {
val refCache = ArrayList<TObject>(4096)
return Json {
encodeDefaults = true
ignoreUnknownKeys = true
classDiscriminator = "_typename"
serializersModule = SerializersModule {
polymorphic(TGeoShape::class) {
default { TGeoBBox.serializer().unref(refCache) }
shapes(refCache)
}
polymorphic(TGeoMatrix::class) {
matrices(refCache)
}
polymorphic(TObject::class) {
shapes(refCache)
matrices(refCache)
subclass(TGeoMaterial.serializer().unref(refCache))
subclass(TGeoMixture.serializer().unref(refCache))
subclass(TGeoMedium.serializer().unref(refCache))
subclass(TGeoNode.serializer().unref(refCache))
subclass(TGeoNodeMatrix.serializer().unref(refCache))
subclass(TGeoVolume.serializer().unref(refCache))
subclass(TGeoVolumeAssembly.serializer().unref(refCache))
}
polymorphic(TGeoNode::class, TGeoNode.serializer().unref(refCache)) {
subclass(TGeoNodeMatrix.serializer().unref(refCache))
}
polymorphic(TGeoVolume::class, TGeoVolume.serializer().unref(refCache)) {
subclass(TGeoVolumeAssembly.serializer().unref(refCache))
}
}
}
}