From e39f79e4ab702226b0b125e1eb9fe4b4392fa25a Mon Sep 17 00:00:00 2001 From: Alexander Nozik Date: Tue, 24 Aug 2021 16:04:21 +0300 Subject: [PATCH] Root parsing complete --- .../kotlin/ru/mipt/npm/root/JsonRootObject.kt | 26 +++ .../kotlin/ru/mipt/npm/root/TGeoNode.kt | 8 +- .../kotlin/ru/mipt/npm/root/TGeoShape.kt | 6 +- .../kotlin/ru/mipt/npm/root/TGeoVolume.kt | 34 ++-- .../kotlin/ru/mipt/npm/root/TObject.kt | 8 +- .../kotlin/ru/mipt/npm/root/rootJson.kt | 149 +++++++++--------- .../mipt/npm/root/{loadBM@N.kt => loadBMN.kt} | 8 +- 7 files changed, 143 insertions(+), 96 deletions(-) create mode 100644 cern-root-loader/src/commonMain/kotlin/ru/mipt/npm/root/JsonRootObject.kt rename cern-root-loader/src/jvmTest/kotlin/ru/mipt/npm/root/{loadBM@N.kt => loadBMN.kt} (78%) diff --git a/cern-root-loader/src/commonMain/kotlin/ru/mipt/npm/root/JsonRootObject.kt b/cern-root-loader/src/commonMain/kotlin/ru/mipt/npm/root/JsonRootObject.kt new file mode 100644 index 00000000..bb5fd1cb --- /dev/null +++ b/cern-root-loader/src/commonMain/kotlin/ru/mipt/npm/root/JsonRootObject.kt @@ -0,0 +1,26 @@ +package ru.mipt.npm.root + +import kotlinx.serialization.KSerializer +import kotlinx.serialization.Serializable +import kotlinx.serialization.descriptors.SerialDescriptor +import kotlinx.serialization.encoding.Decoder +import kotlinx.serialization.encoding.Encoder +import kotlinx.serialization.json.JsonElement + +@Serializable(JsonRootSerializer::class) +public class JsonRootObject(public val element: JsonElement): TObject() + +public object JsonRootSerializer: KSerializer{ + private val jsonElementSerializer = JsonElement.serializer() + + override val descriptor: SerialDescriptor + get() = jsonElementSerializer.descriptor + + override fun deserialize(decoder: Decoder): JsonRootObject { + return JsonRootObject(decoder.decodeSerializableValue(jsonElementSerializer)) + } + + override fun serialize(encoder: Encoder, value: JsonRootObject) { + encoder.encodeSerializableValue(jsonElementSerializer, value.element) + } +} \ No newline at end of file diff --git a/cern-root-loader/src/commonMain/kotlin/ru/mipt/npm/root/TGeoNode.kt b/cern-root-loader/src/commonMain/kotlin/ru/mipt/npm/root/TGeoNode.kt index f507aeb2..9e6f47f1 100644 --- a/cern-root-loader/src/commonMain/kotlin/ru/mipt/npm/root/TGeoNode.kt +++ b/cern-root-loader/src/commonMain/kotlin/ru/mipt/npm/root/TGeoNode.kt @@ -7,12 +7,14 @@ import kotlinx.serialization.Serializable @Serializable @SerialName("TGeoNode") public open class TGeoNode : TNamed() { - //val fGeoAtt: UInt + public val fGeoAtt: UInt = 0u + @Contextual public val fVolume: TGeoVolume? = null - @Contextual - public val fMother: TGeoVolume? = null +// @Contextual +// public val fMother: TGeoVolume? = null + public val fNumber: Int = 0 public val fNovlp: Int = 0 public val fOverlaps: IntArray = intArrayOf() diff --git a/cern-root-loader/src/commonMain/kotlin/ru/mipt/npm/root/TGeoShape.kt b/cern-root-loader/src/commonMain/kotlin/ru/mipt/npm/root/TGeoShape.kt index c986f899..26cf0bab 100644 --- a/cern-root-loader/src/commonMain/kotlin/ru/mipt/npm/root/TGeoShape.kt +++ b/cern-root-loader/src/commonMain/kotlin/ru/mipt/npm/root/TGeoShape.kt @@ -126,4 +126,8 @@ public class TGeoShapeAssembly( @Contextual public val fVolume: TGeoVolumeAssembly, public val fBBoxOK: Boolean = true -) : TGeoBBox() \ No newline at end of file +) : TGeoBBox() + +public class TGeoShapeRef(provider: () -> TGeoShape) : TGeoShape() { + public val value: TGeoShape by lazy(provider) +} \ No newline at end of file diff --git a/cern-root-loader/src/commonMain/kotlin/ru/mipt/npm/root/TGeoVolume.kt b/cern-root-loader/src/commonMain/kotlin/ru/mipt/npm/root/TGeoVolume.kt index afa7c7d9..480c1ca7 100644 --- a/cern-root-loader/src/commonMain/kotlin/ru/mipt/npm/root/TGeoVolume.kt +++ b/cern-root-loader/src/commonMain/kotlin/ru/mipt/npm/root/TGeoVolume.kt @@ -7,29 +7,35 @@ import kotlinx.serialization.Serializable @Serializable @SerialName("TGeoVolume") public open class TGeoVolume : TNamed() { - // "fGeoAtt" : 3084, -// "fLineColor" : 3, -// "fLineStyle" : 1, -// "fLineWidth" : 1, -// "fFillColor" : 19, -// "fFillStyle" : 1001, - @Contextual - public lateinit var fNodes: TObjArray - internal set + public val fGeoAtt: UInt = 0u + public val fLineColor: Int = 2 + public val fLineStyle: Int? = null + public val fLineWidth: UInt = 1u + public val fFillColor: Int? = null + public val fFillStyle: Int? = null @Contextual - public lateinit var fShape: TGeoShape - internal set + public val fNodes: TObjArray? = null @Contextual - public lateinit var fMedium: TGeoMedium - internal set + public val fShape: TGeoShape? = null + + @Contextual + public val fMedium: TGeoMedium? = null public val fNumber: Int = 1 public val fNtotal: Int = 1 public val fRefCount: Int = 1 } +public class TGeoVolumeRef(provider: () -> TGeoVolume) : TGeoVolume() { + public val value: TGeoVolume by lazy(provider) +} + @Serializable @SerialName("TGeoVolumeAssembly") -public class TGeoVolumeAssembly : TGeoVolume() \ No newline at end of file +public open class TGeoVolumeAssembly : TGeoVolume() + +public class TGeoVolumeAssemblyRef(provider: () -> TGeoVolumeAssembly) : TGeoVolumeAssembly() { + public val value: TGeoVolumeAssembly by lazy(provider) +} \ No newline at end of file diff --git a/cern-root-loader/src/commonMain/kotlin/ru/mipt/npm/root/TObject.kt b/cern-root-loader/src/commonMain/kotlin/ru/mipt/npm/root/TObject.kt index f70cc589..50738d11 100644 --- a/cern-root-loader/src/commonMain/kotlin/ru/mipt/npm/root/TObject.kt +++ b/cern-root-loader/src/commonMain/kotlin/ru/mipt/npm/root/TObject.kt @@ -1,5 +1,6 @@ package ru.mipt.npm.root +import kotlinx.serialization.Contextual import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable @@ -15,9 +16,10 @@ public open class TNamed : TObject() { public val fTitle: String = "" } + @Serializable @SerialName("TObjArray") -public class TObjArray(public val arr: List): TObject() { +public class TObjArray(public val arr: List<@Contextual TObject>): TObject() { public companion object{ public val empty: TObjArray = TObjArray(emptyList()) } @@ -25,8 +27,8 @@ public class TObjArray(public val arr: List): TObject() { @Serializable @SerialName("TList") -public class TList(public val arr: List): TObject() +public class TList(public val arr: List<@Contextual TObject>): TObject() @Serializable @SerialName("THashList") -public class THashList(public val arr: List): TObject() \ No newline at end of file +public class THashList(public val arr: List<@Contextual TObject>): TObject() \ No newline at end of file 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 index 33b8b6c2..8416368a 100644 --- 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 @@ -1,12 +1,10 @@ package ru.mipt.npm.root -import kotlinx.serialization.DeserializationStrategy import kotlinx.serialization.ExperimentalSerializationApi import kotlinx.serialization.KSerializer import kotlinx.serialization.encoding.Decoder import kotlinx.serialization.json.* import kotlinx.serialization.modules.* -import kotlin.reflect.KClass /** @@ -24,8 +22,8 @@ private object RootDecoder { private class RootUnrefSerializer( private val tSerializer: KSerializer, - private val refCache: MutableList,// = ArrayList(4096) - private val counter: ReferenceCounter + private val refCache: List,// = ArrayList(4096) + //private val counter: ReferenceCounter ) : KSerializer by tSerializer { override fun deserialize(decoder: Decoder): T { @@ -33,67 +31,74 @@ private object RootDecoder { val element = input.decodeJsonElement() val refId = (element as? JsonObject)?.get("\$ref")?.jsonPrimitive?.int val ref = if (refId != null) { - //Do unref - refCache[refId] - } else { - refCache[counter.value].also { - counter.increment() + //println("Substituting ref $refId") + //Forward ref for shapes + when (tSerializer.descriptor.serialName) { + "TGeoShape" -> return TGeoShapeRef{ + //Should be not null at the moment of actualization + refCache[refId].value as TGeoShape + } as T + "TGeoVolumeAssembly" -> return TGeoVolumeAssemblyRef{ + //Should be not null at the moment of actualization + refCache[refId].value as TGeoVolumeAssembly + } as T + //Do unref + else -> refCache[refId] } + } else { + refCache.find { it.element == element } ?: error("Element '$element' not found in the cache") + } + + return ref.getOrPutValue { +// val actualTypeName = it.jsonObject["_typename"]?.jsonPrimitive?.content + input.json.decodeFromJsonElement(tSerializer, it) } - return ref.value(tSerializer) as T //TODO research means to make it safe } } - private fun KSerializer.unref(refCache: MutableList, counter: ReferenceCounter): KSerializer = - RootUnrefSerializer(this, refCache, counter) + private fun KSerializer.unref(refCache: List): KSerializer = + RootUnrefSerializer(this, refCache) @OptIn(ExperimentalSerializationApi::class) fun unrefSerializersModule( - refCache: MutableList, counter: ReferenceCounter + refCache: List ): SerializersModule = SerializersModule { - val collector = this - val unrefCollector = object : SerializersModuleCollector { + include(serializersModule) - override fun contextual( - kClass: KClass, - provider: (typeArgumentsSerializers: List>) -> KSerializer<*> - ) { - collector.contextual(kClass) { provider(it).unref(refCache, counter) } - } + contextual(TGeoManager.serializer().unref(refCache)) + contextual(TObjArray.serializer().unref(refCache)) + contextual(TGeoVolumeAssembly.serializer().unref(refCache)) + contextual(TGeoShapeAssembly.serializer().unref(refCache)) + contextual(TGeoRotation.serializer().unref(refCache)) + contextual(TGeoMedium.serializer().unref(refCache)) + contextual(TGeoVolume.serializer().unref(refCache)) + contextual(TGeoMatrix.serializer().unref(refCache)) + contextual(TGeoNodeMatrix.serializer().unref(refCache)) + contextual(TGeoShape.serializer().unref(refCache)) + contextual(TObject.serializer().unref(refCache)) - override fun polymorphic( - baseClass: KClass, - actualClass: KClass, - actualSerializer: KSerializer - ) { - collector.polymorphic(baseClass, actualClass, actualSerializer.unref(refCache, counter)) - } - override fun polymorphicDefault( - baseClass: KClass, - defaultSerializerProvider: (className: String?) -> DeserializationStrategy? - ) { - collector.polymorphicDefault(baseClass) { - (defaultSerializerProvider(it) as KSerializer).unref(refCache, counter) - } - } + polymorphicDefault(TGeoShape::class) { + TGeoShape.serializer().unref(refCache) + } + + polymorphicDefault(TGeoMatrix::class) { + TGeoMatrix.serializer().unref(refCache) } - serializersModule.dumpTo(unrefCollector) } /** * Create an instance of Json with unfolding Root references. This instance could not be reused because of the cache. */ - private fun unrefJson(refCache: MutableList, counter: ReferenceCounter): Json = Json { + private fun unrefJson(refCache: MutableList): Json = Json { encodeDefaults = true ignoreUnknownKeys = true classDiscriminator = "_typename" - serializersModule = unrefSerializersModule(refCache, counter) + serializersModule = unrefSerializersModule(refCache) } fun decode(sourceDeserializer: KSerializer, source: JsonElement): TObject { - val counter = ReferenceCounter() val refCache = ArrayList() fun fillCache(element: JsonElement) { @@ -112,34 +117,35 @@ private object RootDecoder { } } else -> { + //ignore primitives } } } fillCache(source) - return unrefJson(refCache, counter).decodeFromJsonElement(sourceDeserializer.unref(refCache, counter), source) + return unrefJson(refCache).decodeFromJsonElement(sourceDeserializer.unref(refCache), source) } - class ReferenceCounter(var value: Int = 0) { - fun increment() { - value += 1 - } +// class ReferenceCounter(var value: Int = 0) { +// fun increment() { +// value += 1 +// } +// +// override fun toString(): String = value.toString() +// } - override fun toString(): String = value.toString() - } + class RefEntry(val element: JsonElement) { - class RefEntry(val obj: JsonObject) { + var value: Any? = null - private var cachedValue: Any? = null - - fun value(serializer: KSerializer<*>): Any { - if (cachedValue == null) { - cachedValue = json.decodeFromJsonElement(serializer, obj) + fun getOrPutValue(builder: (JsonElement) -> T): T { + if (value == null) { + value = builder(element) } - return cachedValue!! + return value as T } - override fun toString(): String = obj.toString() + override fun toString(): String = element.toString() } private fun PolymorphicModuleBuilder.shapes() { @@ -166,27 +172,9 @@ private object RootDecoder { } private val serializersModule = SerializersModule { - contextual(TGeoManager::class) { TGeoManager.serializer() } - contextual(TObjArray::class) { TObjArray.serializer() } - contextual(TGeoVolumeAssembly::class) { TGeoVolumeAssembly.serializer() } - contextual(TGeoShapeAssembly::class) { TGeoShapeAssembly.serializer() } - contextual(TGeoRotation::class) { TGeoRotation.serializer() } - contextual(TGeoMedium::class) { TGeoMedium.serializer() } - - polymorphic(TGeoShape::class) { - default { TGeoBBox.serializer() } - shapes() - } - - polymorphic(TGeoMatrix::class) { - matrices() - } - - polymorphic(TGeoBoolNode::class) { - boolNodes() - } polymorphic(TObject::class) { + default { JsonRootSerializer } subclass(TObjArray.serializer()) shapes() @@ -202,6 +190,19 @@ private object RootDecoder { subclass(TGeoNodeMatrix.serializer()) subclass(TGeoVolume.serializer()) subclass(TGeoVolumeAssembly.serializer()) + subclass(TGeoManager.serializer()) + } + + polymorphic(TGeoShape::class) { + shapes() + } + + polymorphic(TGeoMatrix::class) { + matrices() + } + + polymorphic(TGeoBoolNode::class) { + boolNodes() } polymorphic(TGeoNode::class, TGeoNode.serializer()) { diff --git a/cern-root-loader/src/jvmTest/kotlin/ru/mipt/npm/root/loadBM@N.kt b/cern-root-loader/src/jvmTest/kotlin/ru/mipt/npm/root/loadBMN.kt similarity index 78% rename from cern-root-loader/src/jvmTest/kotlin/ru/mipt/npm/root/loadBM@N.kt rename to cern-root-loader/src/jvmTest/kotlin/ru/mipt/npm/root/loadBMN.kt index 2c2aa86c..763b7e45 100644 --- a/cern-root-loader/src/jvmTest/kotlin/ru/mipt/npm/root/loadBM@N.kt +++ b/cern-root-loader/src/jvmTest/kotlin/ru/mipt/npm/root/loadBMN.kt @@ -1,6 +1,8 @@ package ru.mipt.npm.root import kotlinx.serialization.json.* +import java.time.Duration +import kotlin.system.measureTimeMillis private fun JsonElement.countTypes(): Sequence = sequence { val json = this@countTypes @@ -27,5 +29,9 @@ fun main() { println(it) } - val geo = TObject.decodeFromString(TGeoManager.serializer(), string) + val time = measureTimeMillis { + val geo = TObject.decodeFromString(TGeoManager.serializer(), string) + } + + println(Duration.ofMillis(time)) } \ No newline at end of file