Fix meta node listeners and attachements

This commit is contained in:
Alexander Nozik 2021-08-14 18:14:37 +03:00
parent ce8074c104
commit a71bb732da
5 changed files with 33 additions and 28 deletions

View File

@ -10,6 +10,7 @@
### Removed ### Removed
### Fixed ### Fixed
- MutableMetaImpl attachment and checks
### Security ### Security
## [0.5.0] ## [0.5.0]

View File

@ -4,7 +4,7 @@ plugins {
allprojects { allprojects {
group = "space.kscience" group = "space.kscience"
version = "0.5.0" version = "0.5.1"
} }
subprojects { subprojects {

View File

@ -261,6 +261,13 @@ public operator fun <M : MutableTypedMeta<M>> MutableTypedMeta<M>.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]. * A general implementation of mutable [Meta] which implements both [MutableTypedMeta] and [ObservableMeta].
* The implementation uses blocking synchronization on mutation on JVM * The implementation uses blocking synchronization on mutation on JVM
@ -280,17 +287,11 @@ private class MutableMetaImpl(
private val children: LinkedHashMap<NameToken, ObservableMutableMeta> = private val children: LinkedHashMap<NameToken, ObservableMutableMeta> =
LinkedHashMap(children.mapValues { (key, meta) -> 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<NameToken, ObservableMutableMeta> get() = children override val items: Map<NameToken, ObservableMutableMeta> get() = children
private fun ObservableMeta.adoptBy(parent: MutableMetaImpl, key: NameToken) {
onChange(parent) { name ->
parent.invalidate(key + name)
}
}
@DFExperimental @DFExperimental
override fun attach(name: Name, node: ObservableMutableMeta) { override fun attach(name: Name, node: ObservableMutableMeta) {
when (name.length) { when (name.length) {
@ -338,9 +339,14 @@ private class MutableMetaImpl(
} }
private fun wrapItem(meta: Meta): 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?) { override fun setMeta(name: Name, node: Meta?) {
val oldItem: ObservableMutableMeta? = get(name) val oldItem: ObservableMutableMeta? = get(name)
if (oldItem != node) { if (oldItem != node) {
@ -348,13 +354,24 @@ private class MutableMetaImpl(
0 -> error("Can't set a meta with empty name") 0 -> error("Can't set a meta with empty name")
1 -> { 1 -> {
val token = name.firstOrNull()!! 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 -> { else -> {
val token = name.firstOrNull()!! 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) { 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) 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) 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<MutableMeta> -> 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 * Create a mutable copy of this meta. The copy is created even if the Meta is already mutable
*/ */

View File

@ -48,7 +48,7 @@ public interface ObservableMutableMeta : ObservableMeta, MutableMeta, MutableTyp
} }
internal abstract class AbstractObservableMeta : ObservableMeta { internal abstract class AbstractObservableMeta : ObservableMeta {
private val listeners = HashSet<MetaListener>() private val listeners = ArrayList<MetaListener>()
override fun invalidate(name: Name) { override fun invalidate(name: Name) {
listeners.forEach { it.callback(this, name) } listeners.forEach { it.callback(this, name) }

View File

@ -68,7 +68,7 @@ private class ObservableMetaWrapper(
override fun attach(name: Name, node: ObservableMutableMeta) { override fun attach(name: Name, node: ObservableMutableMeta) {
set(name, node) set(name, node)
node.onChange(this) { changeName -> node.onChange(this) { changeName ->
setMeta(name + changeName, node[changeName]) setMeta(name + changeName, this[changeName])
} }
} }
} }