diff --git a/cern-root-loader/src/commonMain/kotlin/ru/mipt/npm/root/TGeoManager.kt b/cern-root-loader/src/commonMain/kotlin/ru/mipt/npm/root/TGeoManager.kt index acf4d712..3b3eecf5 100644 --- a/cern-root-loader/src/commonMain/kotlin/ru/mipt/npm/root/TGeoManager.kt +++ b/cern-root-loader/src/commonMain/kotlin/ru/mipt/npm/root/TGeoManager.kt @@ -1,15 +1,9 @@ package ru.mipt.npm.root -import kotlinx.serialization.* -import kotlinx.serialization.descriptors.SerialDescriptor -import kotlinx.serialization.encoding.Decoder -import kotlinx.serialization.encoding.Encoder -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 +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable +import kotlinx.serialization.json.JsonElement +import kotlinx.serialization.json.decodeFromJsonElement @Serializable @SerialName("TGeoManager") @@ -26,113 +20,10 @@ public class TGeoManager : TNamed() { /** * 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 = - Root().decodeFromString(serializer(), string) + rootJson().decodeFromString(serializer(), string) } } - -@OptIn(ExperimentalSerializationApi::class) -private class RootJsonSerializer(private val tSerializer: KSerializer) : KSerializer { - - private val refCache: HashMap = 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 decodeFromString(deserializer: DeserializationStrategy, 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 encodeToString(serializer: SerializationStrategy, 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 - } - - } - -} diff --git a/cern-root-loader/src/commonMain/kotlin/ru/mipt/npm/root/rootJson.kt b/cern-root-loader/src/commonMain/kotlin/ru/mipt/npm/root/rootJson.kt new file mode 100644 index 00000000..f0b25d18 --- /dev/null +++ b/cern-root-loader/src/commonMain/kotlin/ru/mipt/npm/root/rootJson.kt @@ -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 + +private class RootJsonSerializer( + private val refCache: RefCache, + private val tSerializer: KSerializer +) : KSerializer { + + 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 KSerializer.unref(refCache: RefCache): RootJsonSerializer = + RootJsonSerializer(refCache, this) + + +private fun PolymorphicModuleBuilder.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.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(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)) + } + } + } +} \ No newline at end of file