diff --git a/CHANGELOG.md b/CHANGELOG.md index 3a874fa5..b0063e5c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,7 @@ ### Removed ### Fixed +- MutableMetaImpl attachment and checks ### Security ## [0.5.0] diff --git a/build.gradle.kts b/build.gradle.kts index 82f63b33..d49db3c4 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -4,7 +4,7 @@ plugins { allprojects { group = "space.kscience" - version = "0.5.0" + version = "0.5.1" } subprojects { diff --git a/dataforge-meta/src/commonMain/kotlin/space/kscience/dataforge/meta/MutableMeta.kt b/dataforge-meta/src/commonMain/kotlin/space/kscience/dataforge/meta/MutableMeta.kt index 527b6cbe..0c2c653e 100644 --- a/dataforge-meta/src/commonMain/kotlin/space/kscience/dataforge/meta/MutableMeta.kt +++ b/dataforge-meta/src/commonMain/kotlin/space/kscience/dataforge/meta/MutableMeta.kt @@ -261,6 +261,13 @@ public operator fun > MutableTypedMeta.set(name: Name } } +private fun ObservableMeta.adoptBy(parent: MutableMetaImpl, key: NameToken) { + if (this === parent) error("Can't attach a node to itself") + onChange(parent) { name -> + parent.invalidate(key + name) + } +} + /** * A general implementation of mutable [Meta] which implements both [MutableTypedMeta] and [ObservableMeta]. * The implementation uses blocking synchronization on mutation on JVM @@ -280,17 +287,11 @@ private class MutableMetaImpl( private val children: LinkedHashMap = LinkedHashMap(children.mapValues { (key, meta) -> - MutableMetaImpl(meta.value, meta.items).apply { adoptBy(this, key) } + MutableMetaImpl(meta.value, meta.items).also { it.adoptBy(this, key) } }) override val items: Map get() = children - private fun ObservableMeta.adoptBy(parent: MutableMetaImpl, key: NameToken) { - onChange(parent) { name -> - parent.invalidate(key + name) - } - } - @DFExperimental override fun attach(name: Name, node: ObservableMutableMeta) { when (name.length) { @@ -338,9 +339,14 @@ private class MutableMetaImpl( } private fun wrapItem(meta: Meta): MutableMetaImpl = - MutableMetaImpl(meta.value, meta.items.mapValuesTo(LinkedHashMap()) { wrapItem(it.value) }) - + meta as? MutableMetaImpl ?: MutableMetaImpl( + meta.value, + meta.items.mapValuesTo(LinkedHashMap()) { + wrapItem(it.value) + } + ) + @Synchronized override fun setMeta(name: Name, node: Meta?) { val oldItem: ObservableMutableMeta? = get(name) if (oldItem != node) { @@ -348,13 +354,24 @@ private class MutableMetaImpl( 0 -> error("Can't set a meta with empty name") 1 -> { val token = name.firstOrNull()!! - replaceItem(token, oldItem, node?.let { wrapItem(node) }) + //remove child and invalidate if argument is null + if (node == null) { + children.remove(token)?.removeListener(this) + // old item is not null otherwise we can't be here + invalidate(name) + } else { + val newNode = wrapItem(node) + newNode.adoptBy(this, token) + children[token] = newNode + } } else -> { val token = name.firstOrNull()!! - //get existing or create new node. Index is ignored for new node + //get existing or create new node. if (items[token] == null) { - replaceItem(token, null, MutableMetaImpl(null)) + val newNode = MutableMetaImpl(null) + newNode.adoptBy(this, token) + children[token] = newNode } items[token]?.setMeta(name.cutFirst(), node) } @@ -384,19 +401,6 @@ public fun MutableMeta.append(name: Name, value: Value): Unit = append(name, Met public fun MutableMeta.append(key: String, value: Value): Unit = append(Name.parse(key), value) -///** -// * Apply existing node with given [builder] or create a new element with it. -// */ -//@DFExperimental -//public fun MutableMeta.edit(name: Name, builder: MutableMeta.() -> Unit) { -// val item = when (val existingItem = get(name)) { -// null -> MutableMeta().also { set(name, it) } -// is MetaItemNode -> existingItem.node -// else -> error("Can't edit value meta item") -// } -// item.apply(builder) -//} - /** * Create a mutable copy of this meta. The copy is created even if the Meta is already mutable */ diff --git a/dataforge-meta/src/commonMain/kotlin/space/kscience/dataforge/meta/ObservableMeta.kt b/dataforge-meta/src/commonMain/kotlin/space/kscience/dataforge/meta/ObservableMeta.kt index 73ec03b6..f2b80417 100644 --- a/dataforge-meta/src/commonMain/kotlin/space/kscience/dataforge/meta/ObservableMeta.kt +++ b/dataforge-meta/src/commonMain/kotlin/space/kscience/dataforge/meta/ObservableMeta.kt @@ -48,7 +48,7 @@ public interface ObservableMutableMeta : ObservableMeta, MutableMeta, MutableTyp } internal abstract class AbstractObservableMeta : ObservableMeta { - private val listeners = HashSet() + private val listeners = ArrayList() override fun invalidate(name: Name) { listeners.forEach { it.callback(this, name) } diff --git a/dataforge-meta/src/commonMain/kotlin/space/kscience/dataforge/meta/ObservableMetaWrapper.kt b/dataforge-meta/src/commonMain/kotlin/space/kscience/dataforge/meta/ObservableMetaWrapper.kt index 53e427ef..b19235b6 100644 --- a/dataforge-meta/src/commonMain/kotlin/space/kscience/dataforge/meta/ObservableMetaWrapper.kt +++ b/dataforge-meta/src/commonMain/kotlin/space/kscience/dataforge/meta/ObservableMetaWrapper.kt @@ -68,7 +68,7 @@ private class ObservableMetaWrapper( override fun attach(name: Name, node: ObservableMutableMeta) { set(name, node) node.onChange(this) { changeName -> - setMeta(name + changeName, node[changeName]) + setMeta(name + changeName, this[changeName]) } } }