[WIP] great refactoring in progress

This commit is contained in:
Alexander Nozik 2022-08-08 22:17:06 +03:00
parent 9b1ca8332b
commit c71042ae06
No known key found for this signature in database
GPG Key ID: F7FCF2DD25C71357
11 changed files with 96 additions and 123 deletions

View File

@ -4,6 +4,7 @@ import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.SharedFlow import kotlinx.coroutines.flow.SharedFlow
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import kotlinx.serialization.SerialName import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
import kotlinx.serialization.Transient import kotlinx.serialization.Transient
import space.kscience.dataforge.meta.* import space.kscience.dataforge.meta.*
import space.kscience.dataforge.meta.descriptors.MetaDescriptor import space.kscience.dataforge.meta.descriptors.MetaDescriptor
@ -15,13 +16,14 @@ import space.kscience.visionforge.AbstractVisionGroup.Companion.updateProperties
import kotlin.jvm.Synchronized import kotlin.jvm.Synchronized
@Serializable
public abstract class AbstractVision : Vision { public abstract class AbstractVision : Vision {
@Transient @Transient
override var parent: Vision? = null override var parent: Vision? = null
@SerialName("properties") @SerialName("properties")
internal var _properties: MutableMeta? = null protected var _properties: MutableMeta? = null
protected open val defaultProperties: Meta? get() = descriptor?.defaultNode protected open val defaultProperties: Meta? get() = descriptor?.defaultNode
@ -80,7 +82,7 @@ public abstract class AbstractVision : Vision {
} }
@Transient @Transient
private val _changes = MutableSharedFlow<Name>() private val _changes = MutableSharedFlow<Name>(10)
override val changes: SharedFlow<Name> get() = _changes override val changes: SharedFlow<Name> get() = _changes
override fun invalidate(propertyName: Name) { override fun invalidate(propertyName: Name) {

View File

@ -16,7 +16,7 @@ import kotlin.time.Duration
/** /**
* Create a deep copy of given Vision without external connections. * 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 //Assuming that unrooted visions are already isolated
//TODO replace by efficient deep copy //TODO replace by efficient deep copy
val json = manager.encodeToJsonElement(this) val json = manager.encodeToJsonElement(this)
@ -26,7 +26,7 @@ private fun Vision.deepCopy(): Vision {
/** /**
* An update for a [Vision] * An update for a [Vision]
*/ */
public class VisionChangeBuilder : MutableVisionContainer<Vision> { public class VisionChangeBuilder(private val manager: VisionManager) : MutableVisionContainer<Vision> {
private var reset: Boolean = false private var reset: Boolean = false
private var vision: Vision? = null private var vision: Vision? = null
@ -37,7 +37,7 @@ public class VisionChangeBuilder : MutableVisionContainer<Vision> {
@Synchronized @Synchronized
private fun getOrPutChild(visionName: Name): VisionChangeBuilder = private fun getOrPutChild(visionName: Name): VisionChangeBuilder =
children.getOrPut(visionName) { VisionChangeBuilder() } children.getOrPut(visionName) { VisionChangeBuilder(manager) }
public fun propertyChanged(visionName: Name, propertyName: Name, item: Meta?) { public fun propertyChanged(visionName: Name, propertyName: Name, item: Meta?) {
if (visionName == Name.EMPTY) { if (visionName == Name.EMPTY) {
@ -61,7 +61,7 @@ public class VisionChangeBuilder : MutableVisionContainer<Vision> {
*/ */
public fun deepCopy(): VisionChange = VisionChange( public fun deepCopy(): VisionChange = VisionChange(
reset, reset,
vision?.deepCopy(), vision?.deepCopy(manager),
if (propertyChange.isEmpty()) null else propertyChange.seal(), if (propertyChange.isEmpty()) null else propertyChange.seal(),
if (children.isEmpty()) null else children.mapValues { it.value.deepCopy() } if (children.isEmpty()) null else children.mapValues { it.value.deepCopy() }
) )
@ -81,8 +81,8 @@ public data class VisionChange(
public val children: Map<Name, VisionChange>? = null, public val children: Map<Name, VisionChange>? = null,
) )
public inline fun VisionChange(block: VisionChangeBuilder.() -> Unit): VisionChange = public inline fun VisionManager.VisionChange(block: VisionChangeBuilder.() -> Unit): VisionChange =
VisionChangeBuilder().apply(block).deepCopy() VisionChangeBuilder(this).apply(block).deepCopy()
private fun CoroutineScope.collectChange( private fun CoroutineScope.collectChange(
@ -119,14 +119,15 @@ private fun CoroutineScope.collectChange(
*/ */
public fun Vision.flowChanges( public fun Vision.flowChanges(
collectionDuration: Duration, collectionDuration: Duration,
manager: VisionManager = this.manager,
): Flow<VisionChange> = flow { ): Flow<VisionChange> = flow {
var collector = VisionChangeBuilder() var collector = VisionChangeBuilder(manager)
coroutineScope { coroutineScope {
collectChange(Name.EMPTY, this@flowChanges) { collector } collectChange(Name.EMPTY, this@flowChanges) { collector }
//Send initial vision state //Send initial vision state
val initialChange = VisionChange(vision = deepCopy()) val initialChange = VisionChange(vision = deepCopy(manager))
emit(initialChange) emit(initialChange)
while (currentCoroutineContext().isActive) { while (currentCoroutineContext().isActive) {
@ -137,7 +138,7 @@ public fun Vision.flowChanges(
//emit changes //emit changes
emit(collector.deepCopy()) emit(collector.deepCopy())
//Reset the collector //Reset the collector
collector = VisionChangeBuilder() collector = VisionChangeBuilder(manager)
} }
} }
} }

View File

@ -4,12 +4,6 @@ import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Job import kotlinx.coroutines.Job
import kotlinx.coroutines.flow.* import kotlinx.coroutines.flow.*
import kotlinx.coroutines.launch 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.* import space.kscience.dataforge.names.*
@DslMarker @DslMarker
@ -60,9 +54,9 @@ public inline fun VisionChildren.forEach(block: (NameToken, Vision) -> Unit) {
keys.forEach { block(it, get(it)!!) } keys.forEach { block(it, get(it)!!) }
} }
@Serializable(VisionChildrenContainerSerializer::class)
public interface MutableVisionChildren : VisionChildren, MutableVisionContainer<Vision> { public interface MutableVisionChildren : VisionChildren, MutableVisionContainer<Vision> {
public override val group: MutableVisionGroup?
public override val group: MutableVisionGroup
public operator fun set(token: NameToken, value: Vision?) public operator fun set(token: NameToken, value: Vision?)
@ -83,9 +77,9 @@ public interface MutableVisionChildren : VisionChildren, MutableVisionContainer<
else -> { else -> {
val currentParent = get(name.first()) val currentParent = get(name.first())
if (currentParent != null && currentParent !is MutableVisionGroup) error("Can't assign a child to $currentParent") 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) set(name.first(), it)
} ?: error("Container owner not set") }
parent.children[name.cutFirst()] = child parent.children[name.cutFirst()] = child
} }
} }
@ -113,27 +107,26 @@ public operator fun <V : Vision> MutableVisionContainer<V>.set(
str: String?, vision: V?, str: String?, vision: V?,
): Unit = set(str?.parseAsName(), vision) ): Unit = set(str?.parseAsName(), vision)
internal class VisionChildrenImpl( internal abstract class VisionChildrenImpl(
items: Map<NameToken, Vision>, override val group: MutableVisionGroup,
) : MutableVisionChildren { ) : MutableVisionChildren {
override var group: MutableVisionGroup? = null
internal set
private val items = LinkedHashMap(items)
private val updateJobs = HashMap<NameToken, Job>() private val updateJobs = HashMap<NameToken, Job>()
private val scope: CoroutineScope? get() = group?.manager?.context abstract val items: MutableMap<NameToken, Vision>?
abstract fun buildItems(): MutableMap<NameToken, Vision>
override val keys: Set<NameToken> get() = items.keys private val scope: CoroutineScope get() = group.manager.context
override fun get(token: NameToken): Vision? = items[token] override val keys: Set<NameToken> get() = items?.keys ?: emptySet()
override fun get(token: NameToken): Vision? = items?.get(token)
private val _changes = MutableSharedFlow<Name>() private val _changes = MutableSharedFlow<Name>()
override val changes: SharedFlow<Name> get() = _changes override val changes: SharedFlow<Name> get() = _changes
private fun onChange(name: Name) { private fun onChange(name: Name) {
scope?.launch { scope.launch {
_changes.emit(name) _changes.emit(name)
} }
} }
@ -149,16 +142,16 @@ internal class VisionChildrenImpl(
} }
if (value == null) { if (value == null) {
items.remove(token) items?.remove(token)
} else { } else {
items[token] = value (items ?: buildItems())[token] = value
//check if parent already exists and is different from the current one //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") if (value.parent != null && value.parent != group) error("Can't reassign parent Vision for $value")
//set parent //set parent
value.parent = group value.parent = group
//start update jobs (only if the vision is rooted) //start update jobs (only if the vision is rooted)
scope?.let { scope -> scope.let { scope ->
val job = (value.children as? VisionChildrenImpl)?.changes?.onEach { val job = value.children?.changes?.onEach {
onChange(token + it) onChange(token + it)
}?.launchIn(scope) }?.launchIn(scope)
if (job != null) { if (job != null) {
@ -171,30 +164,30 @@ internal class VisionChildrenImpl(
} }
override fun clear() { override fun clear() {
if (items.isNotEmpty()) { if (!items.isNullOrEmpty()) {
updateJobs.values.forEach { updateJobs.values.forEach {
it.cancel() it.cancel()
} }
updateJobs.clear() updateJobs.clear()
items.clear() items?.clear()
onChange(Name.EMPTY) onChange(Name.EMPTY)
} }
} }
} }
//
internal object VisionChildrenContainerSerializer : KSerializer<MutableVisionChildren> { //internal object VisionChildrenContainerSerializer : KSerializer<MutableVisionChildren> {
private val mapSerializer = serializer<Map<NameToken, Vision>>() // private val mapSerializer = serializer<Map<NameToken, Vision>>()
//
override val descriptor: SerialDescriptor = mapSerializer.descriptor // override val descriptor: SerialDescriptor = mapSerializer.descriptor
//
override fun deserialize(decoder: Decoder): MutableVisionChildren { // override fun deserialize(decoder: Decoder): MutableVisionChildren {
val map = decoder.decodeSerializableValue(mapSerializer) // val map = decoder.decodeSerializableValue(mapSerializer)
return VisionChildrenImpl(map) // return VisionChildrenImpl(map)
} // }
//
override fun serialize(encoder: Encoder, value: MutableVisionChildren) { // override fun serialize(encoder: Encoder, value: MutableVisionChildren) {
val map = value.keys.associateWith { value[it]!! } // val map = value.keys.associateWith { value[it]!! }
encoder.encodeSerializableValue(mapSerializer, map) // encoder.encodeSerializableValue(mapSerializer, map)
} // }
//
} //}

View File

@ -1,10 +1,7 @@
package space.kscience.visionforge package space.kscience.visionforge
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.emptyFlow
import kotlinx.serialization.SerialName import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable import kotlinx.serialization.Serializable
import kotlinx.serialization.Transient
import space.kscience.dataforge.meta.Meta import space.kscience.dataforge.meta.Meta
import space.kscience.dataforge.meta.descriptors.MetaDescriptor import space.kscience.dataforge.meta.descriptors.MetaDescriptor
import space.kscience.dataforge.meta.descriptors.value 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] * A full base implementation for a [Vision]
*/ */
@Serializable
public abstract class AbstractVisionGroup : AbstractVision(), MutableVisionGroup { public abstract class AbstractVisionGroup : AbstractVision(), MutableVisionGroup {
override fun update(change: VisionChange) { override fun update(change: VisionChange) {
@ -49,38 +47,26 @@ public abstract class AbstractVisionGroup : AbstractVision(), MutableVisionGroup
} }
@SerialName("children") @SerialName("children")
protected var _children: MutableVisionChildren? = null protected var _children: MutableMap<NameToken, Vision>? = null
@Transient
override val children: MutableVisionChildren = object : MutableVisionChildren {
@Synchronized init {
fun getOrCreateChildren(): MutableVisionChildren { _children?.forEach { it.value.parent = this }
if (_children == null) { }
_children = VisionChildrenImpl(emptyMap()).apply {
group = this@AbstractVisionGroup override val children: MutableVisionChildren by lazy {
object : VisionChildrenImpl(this){
override val items: MutableMap<NameToken, Vision>?
get() = this@AbstractVisionGroup._children
@Synchronized
override fun buildItems(): MutableMap<NameToken, Vision> {
if (_children == null) {
_children = LinkedHashMap()
} }
return _children!!
} }
return _children!!
}
override val group: MutableVisionGroup get() = this@AbstractVisionGroup
override val keys: Set<NameToken> get() = _children?.keys ?: emptySet()
override val changes: Flow<Name> 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()
} }
} }

View File

@ -2,7 +2,7 @@ plugins {
id("ru.mipt.npm.gradle.mpp") id("ru.mipt.npm.gradle.mpp")
} }
val plotlyVersion = "0.5.0" val plotlyVersion = "0.5.3-dev-1"
kscience { kscience {
useSerialization() useSerialization()

View File

@ -15,6 +15,11 @@ kotlin {
api(project(":visionforge-core")) api(project(":visionforge-core"))
} }
} }
commonTest{
dependencies{
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-test:1.6.4")
}
}
} }
} }

View File

@ -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 companion object {
public const val REFERENCE_CHILD_PROPERTY_PREFIX: String = "@child" public const val REFERENCE_CHILD_PROPERTY_PREFIX: String = "@child"
} }

View File

@ -1,5 +1,9 @@
package space.kscience.visionforge.solid 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.names.asName
import space.kscience.dataforge.values.int import space.kscience.dataforge.values.int
import space.kscience.dataforge.values.string import space.kscience.dataforge.values.string
@ -10,8 +14,8 @@ import kotlin.test.assertEquals
@Suppress("UNUSED_VARIABLE") @Suppress("UNUSED_VARIABLE")
class PropertyTest { class PropertyTest {
@Test @Test
fun testColor(){ fun testColor() {
val box = Box(10.0f, 10.0f,10.0f) val box = Box(10.0f, 10.0f, 10.0f)
box.material { box.material {
//meta["color"] = "pink" //meta["color"] = "pink"
color.set("pink") color.set("pink")
@ -20,14 +24,17 @@ class PropertyTest {
assertEquals("pink", box.color.string) assertEquals("pink", box.color.string)
} }
@OptIn(ExperimentalCoroutinesApi::class)
@Test @Test
fun testColorUpdate(){ fun testColorUpdate() = runTest {
val box = Box(10.0f, 10.0f,10.0f) val box = Box(10.0f, 10.0f, 10.0f)
val c = CompletableDeferred<String?>()
var c: String? = null
box.onPropertyChange { box.onPropertyChange {
if(it == SolidMaterial.MATERIAL_COLOR_KEY){ if (it == SolidMaterial.MATERIAL_COLOR_KEY) {
c = box.color.string c.complete(box.color.string)
} }
} }
@ -35,7 +42,8 @@ class PropertyTest {
color.set("pink") color.set("pink")
} }
assertEquals("pink", c) assertEquals("pink", withTimeout(50) { c.await() })
} }
@Test @Test
@ -53,7 +61,7 @@ class PropertyTest {
@Test @Test
fun testStyleProperty() { fun testStyleProperty() {
var box: Box? = null var box: Box? = null
val group = SolidGroup{ val group = SolidGroup {
styleSheet { styleSheet {
update("testStyle") { update("testStyle") {
"test" put 22 "test" put 22
@ -89,7 +97,7 @@ class PropertyTest {
@Test @Test
fun testReferenceStyleProperty() { fun testReferenceStyleProperty() {
var box: SolidReference? = null var box: SolidReference? = null
val group = SolidGroup{ val group = SolidGroup {
styleSheet { styleSheet {
update("testStyle") { update("testStyle") {
SolidMaterial.MATERIAL_COLOR_KEY put "#555555" SolidMaterial.MATERIAL_COLOR_KEY put "#555555"

View File

@ -2,6 +2,7 @@ package space.kscience.visionforge.solid
import kotlinx.serialization.json.encodeToJsonElement import kotlinx.serialization.json.encodeToJsonElement
import space.kscience.dataforge.misc.DFExperimental import space.kscience.dataforge.misc.DFExperimental
import space.kscience.dataforge.names.get
import space.kscience.visionforge.get import space.kscience.visionforge.get
import space.kscience.visionforge.style import space.kscience.visionforge.style
import space.kscience.visionforge.useStyle import space.kscience.visionforge.useStyle
@ -30,6 +31,7 @@ class SolidReferenceTest {
fun testReferenceSerialization(){ fun testReferenceSerialization(){
val serialized = Solids.jsonForSolids.encodeToJsonElement(groupWithReference) val serialized = Solids.jsonForSolids.encodeToJsonElement(groupWithReference)
val deserialized = Solids.jsonForSolids.decodeFromJsonElement(SolidGroup.serializer(), serialized) 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) assertEquals("blue", (deserialized.children["test"] as Solid).color.string)
} }
} }

View File

@ -11,7 +11,7 @@ import kotlin.test.Test
import kotlin.test.assertEquals import kotlin.test.assertEquals
import kotlin.test.assertTrue import kotlin.test.assertTrue
class VisionUpdateTest { internal class VisionUpdateTest {
val solidManager = Global.fetch(Solids) val solidManager = Global.fetch(Solids)
val visionManager = solidManager.visionManager val visionManager = solidManager.visionManager
@ -20,7 +20,7 @@ class VisionUpdateTest {
val targetVision = SolidGroup { val targetVision = SolidGroup {
box(200,200,200, name = "origin") box(200,200,200, name = "origin")
} }
val dif = VisionChange{ val dif = visionManager.VisionChange{
group ("top") { group ("top") {
color.set(123) color.set(123)
box(100,100,100) box(100,100,100)
@ -36,7 +36,7 @@ class VisionUpdateTest {
@Test @Test
fun testVisionChangeSerialization(){ fun testVisionChangeSerialization(){
val change = VisionChange{ val change = visionManager.VisionChange{
group("top") { group("top") {
color.set(123) color.set(123)
box(100,100,100) box(100,100,100)

View File

@ -2,7 +2,7 @@ plugins {
id("ru.mipt.npm.gradle.mpp") id("ru.mipt.npm.gradle.mpp")
} }
val tablesVersion = "0.2.0-dev-1" val tablesVersion = "0.2.0-dev-3"
kscience { kscience {
useSerialization() useSerialization()