[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.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<Name>()
private val _changes = MutableSharedFlow<Name>(10)
override val changes: SharedFlow<Name> get() = _changes
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.
*/
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<Vision> {
public class VisionChangeBuilder(private val manager: VisionManager) : MutableVisionContainer<Vision> {
private var reset: Boolean = false
private var vision: Vision? = null
@ -37,7 +37,7 @@ public class VisionChangeBuilder : MutableVisionContainer<Vision> {
@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<Vision> {
*/
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<Name, VisionChange>? = 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<VisionChange> = 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)
}
}
}

View File

@ -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<Vision> {
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 <V : Vision> MutableVisionContainer<V>.set(
str: String?, vision: V?,
): Unit = set(str?.parseAsName(), vision)
internal class VisionChildrenImpl(
items: Map<NameToken, Vision>,
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<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>()
override val changes: SharedFlow<Name> 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<MutableVisionChildren> {
private val mapSerializer = serializer<Map<NameToken, Vision>>()
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<MutableVisionChildren> {
// private val mapSerializer = serializer<Map<NameToken, Vision>>()
//
// 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)
// }
//
//}

View File

@ -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<NameToken, Vision>? = 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<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")
}
val plotlyVersion = "0.5.0"
val plotlyVersion = "0.5.3-dev-1"
kscience {
useSerialization()

View File

@ -15,6 +15,11 @@ kotlin {
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 const val REFERENCE_CHILD_PROPERTY_PREFIX: String = "@child"
}

View File

@ -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<String?>()
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"

View File

@ -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)
}
}

View File

@ -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)

View File

@ -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()