From c71042ae06346fbe362426145a243febba2bee83 Mon Sep 17 00:00:00 2001 From: Alexander Nozik Date: Mon, 8 Aug 2022 22:17:06 +0300 Subject: [PATCH] [WIP] great refactoring in progress --- .../kscience/visionforge/AbstractVision.kt | 6 +- .../kscience/visionforge/VisionChange.kt | 19 ++--- .../kscience/visionforge/VisionContainer.kt | 79 +++++++++---------- .../space/kscience/visionforge/VisionGroup.kt | 46 ++++------- visionforge-plotly/build.gradle.kts | 2 +- visionforge-solid/build.gradle.kts | 5 ++ .../visionforge/solid/SolidReference.kt | 24 ------ .../visionforge/solid/PropertyTest.kt | 28 ++++--- .../visionforge/solid/SolidReferenceTest.kt | 2 + .../visionforge/solid/VisionUpdateTest.kt | 6 +- visionforge-tables/build.gradle.kts | 2 +- 11 files changed, 96 insertions(+), 123 deletions(-) diff --git a/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/AbstractVision.kt b/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/AbstractVision.kt index f6b38880..0e0e043d 100644 --- a/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/AbstractVision.kt +++ b/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/AbstractVision.kt @@ -4,6 +4,7 @@ import kotlinx.coroutines.flow.MutableSharedFlow import kotlinx.coroutines.flow.SharedFlow import kotlinx.coroutines.launch import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable import kotlinx.serialization.Transient import space.kscience.dataforge.meta.* import space.kscience.dataforge.meta.descriptors.MetaDescriptor @@ -15,13 +16,14 @@ import space.kscience.visionforge.AbstractVisionGroup.Companion.updateProperties import kotlin.jvm.Synchronized +@Serializable public abstract class AbstractVision : Vision { @Transient override var parent: Vision? = null @SerialName("properties") - internal var _properties: MutableMeta? = null + protected var _properties: MutableMeta? = null protected open val defaultProperties: Meta? get() = descriptor?.defaultNode @@ -80,7 +82,7 @@ public abstract class AbstractVision : Vision { } @Transient - private val _changes = MutableSharedFlow() + private val _changes = MutableSharedFlow(10) override val changes: SharedFlow get() = _changes override fun invalidate(propertyName: Name) { diff --git a/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/VisionChange.kt b/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/VisionChange.kt index a4667b2f..33bbd5ca 100644 --- a/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/VisionChange.kt +++ b/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/VisionChange.kt @@ -16,7 +16,7 @@ import kotlin.time.Duration /** * Create a deep copy of given Vision without external connections. */ -private fun Vision.deepCopy(): Vision { +private fun Vision.deepCopy(manager: VisionManager): Vision { //Assuming that unrooted visions are already isolated //TODO replace by efficient deep copy val json = manager.encodeToJsonElement(this) @@ -26,7 +26,7 @@ private fun Vision.deepCopy(): Vision { /** * An update for a [Vision] */ -public class VisionChangeBuilder : MutableVisionContainer { +public class VisionChangeBuilder(private val manager: VisionManager) : MutableVisionContainer { private var reset: Boolean = false private var vision: Vision? = null @@ -37,7 +37,7 @@ public class VisionChangeBuilder : MutableVisionContainer { @Synchronized private fun getOrPutChild(visionName: Name): VisionChangeBuilder = - children.getOrPut(visionName) { VisionChangeBuilder() } + children.getOrPut(visionName) { VisionChangeBuilder(manager) } public fun propertyChanged(visionName: Name, propertyName: Name, item: Meta?) { if (visionName == Name.EMPTY) { @@ -61,7 +61,7 @@ public class VisionChangeBuilder : MutableVisionContainer { */ public fun deepCopy(): VisionChange = VisionChange( reset, - vision?.deepCopy(), + vision?.deepCopy(manager), if (propertyChange.isEmpty()) null else propertyChange.seal(), if (children.isEmpty()) null else children.mapValues { it.value.deepCopy() } ) @@ -81,8 +81,8 @@ public data class VisionChange( public val children: Map? = null, ) -public inline fun VisionChange(block: VisionChangeBuilder.() -> Unit): VisionChange = - VisionChangeBuilder().apply(block).deepCopy() +public inline fun VisionManager.VisionChange(block: VisionChangeBuilder.() -> Unit): VisionChange = + VisionChangeBuilder(this).apply(block).deepCopy() private fun CoroutineScope.collectChange( @@ -119,14 +119,15 @@ private fun CoroutineScope.collectChange( */ public fun Vision.flowChanges( collectionDuration: Duration, + manager: VisionManager = this.manager, ): Flow = flow { - var collector = VisionChangeBuilder() + var collector = VisionChangeBuilder(manager) coroutineScope { collectChange(Name.EMPTY, this@flowChanges) { collector } //Send initial vision state - val initialChange = VisionChange(vision = deepCopy()) + val initialChange = VisionChange(vision = deepCopy(manager)) emit(initialChange) while (currentCoroutineContext().isActive) { @@ -137,7 +138,7 @@ public fun Vision.flowChanges( //emit changes emit(collector.deepCopy()) //Reset the collector - collector = VisionChangeBuilder() + collector = VisionChangeBuilder(manager) } } } diff --git a/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/VisionContainer.kt b/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/VisionContainer.kt index 81f7d9da..4ce69047 100644 --- a/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/VisionContainer.kt +++ b/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/VisionContainer.kt @@ -4,12 +4,6 @@ import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Job import kotlinx.coroutines.flow.* import kotlinx.coroutines.launch -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.serializer import space.kscience.dataforge.names.* @DslMarker @@ -60,9 +54,9 @@ public inline fun VisionChildren.forEach(block: (NameToken, Vision) -> Unit) { keys.forEach { block(it, get(it)!!) } } -@Serializable(VisionChildrenContainerSerializer::class) public interface MutableVisionChildren : VisionChildren, MutableVisionContainer { - public override val group: MutableVisionGroup? + + public override val group: MutableVisionGroup public operator fun set(token: NameToken, value: Vision?) @@ -83,9 +77,9 @@ public interface MutableVisionChildren : VisionChildren, MutableVisionContainer< else -> { val currentParent = get(name.first()) if (currentParent != null && currentParent !is MutableVisionGroup) error("Can't assign a child to $currentParent") - val parent: MutableVisionGroup = currentParent as? MutableVisionGroup ?: group?.createGroup().also { + val parent: MutableVisionGroup = currentParent as? MutableVisionGroup ?: group.createGroup().also { set(name.first(), it) - } ?: error("Container owner not set") + } parent.children[name.cutFirst()] = child } } @@ -113,27 +107,26 @@ public operator fun MutableVisionContainer.set( str: String?, vision: V?, ): Unit = set(str?.parseAsName(), vision) -internal class VisionChildrenImpl( - items: Map, +internal abstract class VisionChildrenImpl( + override val group: MutableVisionGroup, ) : MutableVisionChildren { - override var group: MutableVisionGroup? = null - internal set - - private val items = LinkedHashMap(items) private val updateJobs = HashMap() - private val scope: CoroutineScope? get() = group?.manager?.context + abstract val items: MutableMap? + abstract fun buildItems(): MutableMap - override val keys: Set get() = items.keys + private val scope: CoroutineScope get() = group.manager.context - override fun get(token: NameToken): Vision? = items[token] + override val keys: Set get() = items?.keys ?: emptySet() + + override fun get(token: NameToken): Vision? = items?.get(token) private val _changes = MutableSharedFlow() override val changes: SharedFlow get() = _changes private fun onChange(name: Name) { - scope?.launch { + scope.launch { _changes.emit(name) } } @@ -149,16 +142,16 @@ internal class VisionChildrenImpl( } if (value == null) { - items.remove(token) + items?.remove(token) } else { - items[token] = value + (items ?: buildItems())[token] = value //check if parent already exists and is different from the current one if (value.parent != null && value.parent != group) error("Can't reassign parent Vision for $value") //set parent value.parent = group //start update jobs (only if the vision is rooted) - scope?.let { scope -> - val job = (value.children as? VisionChildrenImpl)?.changes?.onEach { + scope.let { scope -> + val job = value.children?.changes?.onEach { onChange(token + it) }?.launchIn(scope) if (job != null) { @@ -171,30 +164,30 @@ internal class VisionChildrenImpl( } override fun clear() { - if (items.isNotEmpty()) { + if (!items.isNullOrEmpty()) { updateJobs.values.forEach { it.cancel() } updateJobs.clear() - items.clear() + items?.clear() onChange(Name.EMPTY) } } } - -internal object VisionChildrenContainerSerializer : KSerializer { - private val mapSerializer = serializer>() - - override val descriptor: SerialDescriptor = mapSerializer.descriptor - - override fun deserialize(decoder: Decoder): MutableVisionChildren { - val map = decoder.decodeSerializableValue(mapSerializer) - return VisionChildrenImpl(map) - } - - override fun serialize(encoder: Encoder, value: MutableVisionChildren) { - val map = value.keys.associateWith { value[it]!! } - encoder.encodeSerializableValue(mapSerializer, map) - } - -} +// +//internal object VisionChildrenContainerSerializer : KSerializer { +// private val mapSerializer = serializer>() +// +// override val descriptor: SerialDescriptor = mapSerializer.descriptor +// +// override fun deserialize(decoder: Decoder): MutableVisionChildren { +// val map = decoder.decodeSerializableValue(mapSerializer) +// return VisionChildrenImpl(map) +// } +// +// override fun serialize(encoder: Encoder, value: MutableVisionChildren) { +// val map = value.keys.associateWith { value[it]!! } +// encoder.encodeSerializableValue(mapSerializer, map) +// } +// +//} diff --git a/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/VisionGroup.kt b/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/VisionGroup.kt index f87650a7..9535fbd9 100644 --- a/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/VisionGroup.kt +++ b/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/VisionGroup.kt @@ -1,10 +1,7 @@ package space.kscience.visionforge -import kotlinx.coroutines.flow.Flow -import kotlinx.coroutines.flow.emptyFlow import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable -import kotlinx.serialization.Transient import space.kscience.dataforge.meta.Meta import space.kscience.dataforge.meta.descriptors.MetaDescriptor import space.kscience.dataforge.meta.descriptors.value @@ -33,6 +30,7 @@ public val Vision.children: VisionChildren? get() = (this as? VisionGroup)?.chil /** * A full base implementation for a [Vision] */ +@Serializable public abstract class AbstractVisionGroup : AbstractVision(), MutableVisionGroup { override fun update(change: VisionChange) { @@ -49,38 +47,26 @@ public abstract class AbstractVisionGroup : AbstractVision(), MutableVisionGroup } @SerialName("children") - protected var _children: MutableVisionChildren? = null + protected var _children: MutableMap? = null - @Transient - override val children: MutableVisionChildren = object : MutableVisionChildren { - @Synchronized - fun getOrCreateChildren(): MutableVisionChildren { - if (_children == null) { - _children = VisionChildrenImpl(emptyMap()).apply { - group = this@AbstractVisionGroup + init { + _children?.forEach { it.value.parent = this } + } + + override val children: MutableVisionChildren by lazy { + object : VisionChildrenImpl(this){ + override val items: MutableMap? + get() = this@AbstractVisionGroup._children + + @Synchronized + override fun buildItems(): MutableMap { + if (_children == null) { + _children = LinkedHashMap() } + return _children!! } - return _children!! - } - override val group: MutableVisionGroup get() = this@AbstractVisionGroup - - override val keys: Set get() = _children?.keys ?: emptySet() - override val changes: Flow get() = _children?.changes ?: emptyFlow() - - override fun get(token: NameToken): Vision? = _children?.get(token) - - override fun set(token: NameToken, value: Vision?) { - getOrCreateChildren()[token] = value - } - - override fun set(name: Name?, child: Vision?) { - getOrCreateChildren()[name] = child - } - - override fun clear() { - _children?.clear() } } diff --git a/visionforge-plotly/build.gradle.kts b/visionforge-plotly/build.gradle.kts index caeb4e52..1c1ed308 100644 --- a/visionforge-plotly/build.gradle.kts +++ b/visionforge-plotly/build.gradle.kts @@ -2,7 +2,7 @@ plugins { id("ru.mipt.npm.gradle.mpp") } -val plotlyVersion = "0.5.0" +val plotlyVersion = "0.5.3-dev-1" kscience { useSerialization() diff --git a/visionforge-solid/build.gradle.kts b/visionforge-solid/build.gradle.kts index e00830d5..ce669659 100644 --- a/visionforge-solid/build.gradle.kts +++ b/visionforge-solid/build.gradle.kts @@ -15,6 +15,11 @@ kotlin { api(project(":visionforge-core")) } } + commonTest{ + dependencies{ + implementation("org.jetbrains.kotlinx:kotlinx-coroutines-test:1.6.4") + } + } } } diff --git a/visionforge-solid/src/commonMain/kotlin/space/kscience/visionforge/solid/SolidReference.kt b/visionforge-solid/src/commonMain/kotlin/space/kscience/visionforge/solid/SolidReference.kt index 3e34212a..aa79734f 100644 --- a/visionforge-solid/src/commonMain/kotlin/space/kscience/visionforge/solid/SolidReference.kt +++ b/visionforge-solid/src/commonMain/kotlin/space/kscience/visionforge/solid/SolidReference.kt @@ -60,30 +60,6 @@ public class SolidReference( } } -// override fun getPropertyValue( -// name: Name, -// inherit: Boolean, -// includeStyles: Boolean, -// includeDefaults: Boolean, -// ): Value? { -// meta?.getValue(name)?.let { return it } -// if (includeStyles) { -// getStyleProperty(name)?.value?.let { return it } -// } -// prototype.getPropertyValue(name, inherit, includeStyles, includeDefaults)?.let { return it } -// if (inherit) { -// parent?.getPropertyValue(name, inherit, includeStyles, includeDefaults)?.let { return it } -// } -// return null -// } -// -// override fun getProperty( -// name: Name, -// inherit: Boolean, -// includeStyles: Boolean, -// includeDefaults: Boolean, -// ): MutableMeta = VisionProperties(this, name, descriptor[name], inherit, includeStyles, prototype.meta) - public companion object { public const val REFERENCE_CHILD_PROPERTY_PREFIX: String = "@child" } diff --git a/visionforge-solid/src/commonTest/kotlin/space/kscience/visionforge/solid/PropertyTest.kt b/visionforge-solid/src/commonTest/kotlin/space/kscience/visionforge/solid/PropertyTest.kt index 688712e6..bb6f5de1 100644 --- a/visionforge-solid/src/commonTest/kotlin/space/kscience/visionforge/solid/PropertyTest.kt +++ b/visionforge-solid/src/commonTest/kotlin/space/kscience/visionforge/solid/PropertyTest.kt @@ -1,5 +1,9 @@ package space.kscience.visionforge.solid +import kotlinx.coroutines.CompletableDeferred +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.test.runTest +import kotlinx.coroutines.withTimeout import space.kscience.dataforge.names.asName import space.kscience.dataforge.values.int import space.kscience.dataforge.values.string @@ -10,8 +14,8 @@ import kotlin.test.assertEquals @Suppress("UNUSED_VARIABLE") class PropertyTest { @Test - fun testColor(){ - val box = Box(10.0f, 10.0f,10.0f) + fun testColor() { + val box = Box(10.0f, 10.0f, 10.0f) box.material { //meta["color"] = "pink" color.set("pink") @@ -20,14 +24,17 @@ class PropertyTest { assertEquals("pink", box.color.string) } + @OptIn(ExperimentalCoroutinesApi::class) @Test - fun testColorUpdate(){ - val box = Box(10.0f, 10.0f,10.0f) + fun testColorUpdate() = runTest { + val box = Box(10.0f, 10.0f, 10.0f) + + val c = CompletableDeferred() + - var c: String? = null box.onPropertyChange { - if(it == SolidMaterial.MATERIAL_COLOR_KEY){ - c = box.color.string + if (it == SolidMaterial.MATERIAL_COLOR_KEY) { + c.complete(box.color.string) } } @@ -35,7 +42,8 @@ class PropertyTest { color.set("pink") } - assertEquals("pink", c) + assertEquals("pink", withTimeout(50) { c.await() }) + } @Test @@ -53,7 +61,7 @@ class PropertyTest { @Test fun testStyleProperty() { var box: Box? = null - val group = SolidGroup{ + val group = SolidGroup { styleSheet { update("testStyle") { "test" put 22 @@ -89,7 +97,7 @@ class PropertyTest { @Test fun testReferenceStyleProperty() { var box: SolidReference? = null - val group = SolidGroup{ + val group = SolidGroup { styleSheet { update("testStyle") { SolidMaterial.MATERIAL_COLOR_KEY put "#555555" diff --git a/visionforge-solid/src/commonTest/kotlin/space/kscience/visionforge/solid/SolidReferenceTest.kt b/visionforge-solid/src/commonTest/kotlin/space/kscience/visionforge/solid/SolidReferenceTest.kt index 4726f246..f74426ba 100644 --- a/visionforge-solid/src/commonTest/kotlin/space/kscience/visionforge/solid/SolidReferenceTest.kt +++ b/visionforge-solid/src/commonTest/kotlin/space/kscience/visionforge/solid/SolidReferenceTest.kt @@ -2,6 +2,7 @@ package space.kscience.visionforge.solid import kotlinx.serialization.json.encodeToJsonElement import space.kscience.dataforge.misc.DFExperimental +import space.kscience.dataforge.names.get import space.kscience.visionforge.get import space.kscience.visionforge.style import space.kscience.visionforge.useStyle @@ -30,6 +31,7 @@ class SolidReferenceTest { fun testReferenceSerialization(){ val serialized = Solids.jsonForSolids.encodeToJsonElement(groupWithReference) val deserialized = Solids.jsonForSolids.decodeFromJsonElement(SolidGroup.serializer(), serialized) + assertEquals(groupWithReference.items["test"]?.color.string, deserialized.items["test"]?.color.string) assertEquals("blue", (deserialized.children["test"] as Solid).color.string) } } \ No newline at end of file diff --git a/visionforge-solid/src/commonTest/kotlin/space/kscience/visionforge/solid/VisionUpdateTest.kt b/visionforge-solid/src/commonTest/kotlin/space/kscience/visionforge/solid/VisionUpdateTest.kt index 3fbf3ad4..026499f4 100644 --- a/visionforge-solid/src/commonTest/kotlin/space/kscience/visionforge/solid/VisionUpdateTest.kt +++ b/visionforge-solid/src/commonTest/kotlin/space/kscience/visionforge/solid/VisionUpdateTest.kt @@ -11,7 +11,7 @@ import kotlin.test.Test import kotlin.test.assertEquals import kotlin.test.assertTrue -class VisionUpdateTest { +internal class VisionUpdateTest { val solidManager = Global.fetch(Solids) val visionManager = solidManager.visionManager @@ -20,7 +20,7 @@ class VisionUpdateTest { val targetVision = SolidGroup { box(200,200,200, name = "origin") } - val dif = VisionChange{ + val dif = visionManager.VisionChange{ group ("top") { color.set(123) box(100,100,100) @@ -36,7 +36,7 @@ class VisionUpdateTest { @Test fun testVisionChangeSerialization(){ - val change = VisionChange{ + val change = visionManager.VisionChange{ group("top") { color.set(123) box(100,100,100) diff --git a/visionforge-tables/build.gradle.kts b/visionforge-tables/build.gradle.kts index 4326da36..d4e86bf1 100644 --- a/visionforge-tables/build.gradle.kts +++ b/visionforge-tables/build.gradle.kts @@ -2,7 +2,7 @@ plugins { id("ru.mipt.npm.gradle.mpp") } -val tablesVersion = "0.2.0-dev-1" +val tablesVersion = "0.2.0-dev-3" kscience { useSerialization()