Fix Scheme special meta implementation

This commit is contained in:
Alexander Nozik 2021-08-10 13:34:59 +03:00
parent e5f422f9ca
commit c1065c2885
4 changed files with 164 additions and 92 deletions

View File

@ -265,14 +265,7 @@ public operator fun <M : MutableTypedMeta<M>> MutableTypedMeta<M>.set(name: Name
private class MutableMetaImpl(
value: Value?,
children: Map<NameToken, Meta> = emptyMap()
) : ObservableMutableMeta {
private val listeners = HashSet<MetaListener>()
override fun invalidate(name: Name) {
listeners.forEach { it.callback(this, name) }
}
) : AbstractObservableMeta(), ObservableMutableMeta {
override var value = value
@Synchronized set(value) {
val oldValue = field
@ -289,17 +282,6 @@ private class MutableMetaImpl(
override val items: Map<NameToken, ObservableMutableMeta> get() = children
@Synchronized
override fun onChange(owner: Any?, callback: Meta.(name: Name) -> Unit) {
listeners.add(MetaListener(owner, callback))
}
@Synchronized
override fun removeListener(owner: Any?) {
listeners.removeAll { it.owner === owner }
}
private fun ObservableMeta.adoptBy(parent: MutableMetaImpl, key: NameToken) {
onChange(parent) { name ->
parent.invalidate(key + name)
@ -377,10 +359,6 @@ private class MutableMetaImpl(
invalidate(name)
}
}
override fun toString(): String = Meta.toString(this)
override fun equals(other: Any?): Boolean = Meta.equals(this, other as? Meta)
override fun hashCode(): Int = Meta.hashCode(this)
}
/**

View File

@ -1,8 +1,6 @@
package space.kscience.dataforge.meta
import space.kscience.dataforge.misc.DFExperimental
import space.kscience.dataforge.names.*
import space.kscience.dataforge.values.Value
import kotlin.jvm.Synchronized
import kotlin.reflect.KProperty1
@ -35,11 +33,10 @@ public interface ObservableMeta : Meta {
/**
* A [Meta] which is both observable and mutable
*/
public interface ObservableMutableMeta : ObservableMeta, MutableMeta, MutableTypedMeta<ObservableMutableMeta>{
public interface ObservableMutableMeta : ObservableMeta, MutableMeta, MutableTypedMeta<ObservableMutableMeta> {
override fun getOrCreate(name: Name): ObservableMutableMeta
override fun getMeta(name: Name): ObservableMutableMeta?{
override fun getMeta(name: Name): ObservableMutableMeta? {
tailrec fun ObservableMutableMeta.find(name: Name): ObservableMutableMeta? = if (name.isEmpty()) {
this
} else {
@ -50,10 +47,7 @@ public interface ObservableMutableMeta : ObservableMeta, MutableMeta, MutableTyp
}
}
private class ObservableMetaWrapper(
val origin: MutableMeta,
) : ObservableMutableMeta, Meta by origin {
internal abstract class AbstractObservableMeta : ObservableMeta {
private val listeners = HashSet<MetaListener>()
override fun invalidate(name: Name) {
@ -70,58 +64,11 @@ private class ObservableMetaWrapper(
listeners.removeAll { it.owner === owner }
}
override val items: Map<NameToken, ObservableMetaWrapper>
get() = origin.items.mapValues { ObservableMetaWrapper(it.value) }
override fun getMeta(name: Name): ObservableMetaWrapper? = origin.getMeta(name)?.let{ObservableMetaWrapper(it)}
override var value: Value?
get() = origin.value
set(value) {
origin.value = value
invalidate(Name.EMPTY)
}
override fun getOrCreate(name: Name): ObservableMutableMeta =
get(name) ?: ObservableMetaWrapper(origin.getOrCreate(name))
override fun setMeta(name: Name, node: Meta?) {
val oldMeta = get(name)
origin.setMeta(name, node)
//
// if meta is observable propagate changes from it
if (node is ObservableMeta) {
node.onChange(this) { changeName ->
setMeta(name + changeName, node[changeName])
}
}
if (oldMeta != node) {
invalidate(name)
}
}
override fun toMeta(): Meta {
return origin.toMeta()
}
@DFExperimental
override fun attach(name: Name, node: ObservableMutableMeta) {
set(name, node)
node.onChange(this) { changeName ->
setMeta(name + changeName, node[changeName])
}
}
override fun toString(): String = Meta.toString(this)
override fun equals(other: Any?): Boolean = Meta.equals(this, other as? Meta)
override fun hashCode(): Int = Meta.hashCode(this)
}
/**
* Cast this [MutableMeta] to [ObservableMutableMeta] or create an observable wrapper. Only changes made to the result
* are guaranteed to be observed.
*/
public fun MutableMeta.asObservable(): ObservableMutableMeta =
(this as? ObservableMutableMeta) ?: ObservableMetaWrapper(this)
/**
* Use the value of the property in a [callBack].
* The callback is called once immediately after subscription to pass the initial value.

View File

@ -0,0 +1,82 @@
package space.kscience.dataforge.meta
import space.kscience.dataforge.misc.DFExperimental
import space.kscience.dataforge.names.*
import space.kscience.dataforge.values.Value
import kotlin.jvm.Synchronized
/**
* A class that takes [MutableMeta] provider and adds obsevability on top of that
*/
private class ObservableMetaWrapper(
val root: MutableMeta,
val absoluteName: Name,
val listeners: MutableSet<MetaListener>
) : ObservableMutableMeta {
override val items: Map<NameToken, ObservableMutableMeta>
get() = root.items.mapValues {
ObservableMetaWrapper(root, absoluteName + it.key, listeners)
}
override fun getMeta(name: Name): ObservableMutableMeta? =
root.getMeta(name)?.let { ObservableMetaWrapper(root, this.absoluteName + name, listeners) }
@Synchronized
override fun onChange(owner: Any?, callback: Meta.(name: Name) -> Unit) {
listeners.add(
MetaListener(Pair(owner, absoluteName)) { name ->
if (name.startsWith(absoluteName)) {
(this[absoluteName] ?: Meta.EMPTY).callback(name.removeHeadOrNull(absoluteName)!!)
}
}
)
}
override fun removeListener(owner: Any?) {
listeners.removeAll { it.owner === Pair(owner, absoluteName) }
}
override fun invalidate(name: Name) {
listeners.forEach { it.callback(this, name) }
}
override var value: Value?
get() = root.value
set(value) {
root.value = value
invalidate(Name.EMPTY)
}
override fun getOrCreate(name: Name): ObservableMutableMeta =
ObservableMetaWrapper(root, this.absoluteName + name, listeners)
override fun setMeta(name: Name, node: Meta?) {
val oldMeta = get(name)
root.setMeta(absoluteName + name, node)
if (oldMeta != node) {
invalidate(name)
}
}
override fun toMeta(): Meta = root[absoluteName]?.toMeta() ?: Meta.EMPTY
override fun toString(): String = Meta.toString(this)
override fun equals(other: Any?): Boolean = Meta.equals(this, other as? Meta)
override fun hashCode(): Int = Meta.hashCode(this)
@DFExperimental
override fun attach(name: Name, node: ObservableMutableMeta) {
set(name, node)
node.onChange(this) { changeName ->
setMeta(name + changeName, node[changeName])
}
}
}
/**
* Cast this [MutableMeta] to [ObservableMutableMeta] or create an observable wrapper. Only changes made to the result
* are guaranteed to be observed.
*/
public fun MutableMeta.asObservable(): ObservableMutableMeta =
(this as? ObservableMutableMeta) ?: ObservableMetaWrapper(this, Name.EMPTY, hashSetOf())

View File

@ -1,8 +1,10 @@
package space.kscience.dataforge.meta
import space.kscience.dataforge.meta.descriptors.*
import space.kscience.dataforge.names.Name
import space.kscience.dataforge.misc.DFExperimental
import space.kscience.dataforge.names.*
import space.kscience.dataforge.values.Value
import kotlin.jvm.Synchronized
/**
* A base for delegate-based or descriptor-based scheme. [Scheme] has an empty constructor to simplify usage from [Specification].
@ -10,19 +12,23 @@ import space.kscience.dataforge.values.Value
*/
public open class Scheme : Described, MetaRepr, MutableMetaProvider, Configurable {
private var _meta = MutableMeta()
private var targetMeta: MutableMeta = MutableMeta()
final override val meta: ObservableMutableMeta get() = _meta
private var defaultMeta: Meta? = null
internal var metaDescriptor: MetaDescriptor? = null
final override val meta: ObservableMutableMeta = SchemeMeta(Name.EMPTY)
final override val descriptor: MetaDescriptor? get() = metaDescriptor
final override var descriptor: MetaDescriptor? = null
internal set
internal fun wrap(
items: MutableMeta,
newMeta: MutableMeta,
preserveDefault: Boolean = false
) {
_meta = (if (preserveDefault) items.withDefault(meta.seal()) else items).asObservable()
if(preserveDefault){
defaultMeta = targetMeta.seal()
}
targetMeta = newMeta
}
/**
@ -44,11 +50,70 @@ public open class Scheme : Described, MetaRepr, MutableMetaProvider, Configurabl
}
override fun setValue(name: Name, value: Value?) {
//TODO add validation
meta.setValue(name,value)
val valueDescriptor = descriptor?.get(name)
if (valueDescriptor?.validate(value) != false) {
meta.setValue(name, value)
} else error("Value $value is not validated by $valueDescriptor")
}
override fun toMeta(): Laminate = Laminate(meta, descriptor?.defaultNode)
private val listeners = HashSet<MetaListener>()
private inner class SchemeMeta(val pathName: Name) : ObservableMutableMeta {
override var value: Value?
get() = targetMeta[pathName]?.value ?: defaultMeta?.get(pathName)?.value
set(value) {
val oldValue = targetMeta[pathName]?.value
targetMeta[pathName] = value
if (oldValue != value) {
invalidate(pathName)
}
}
override val items: Map<NameToken, ObservableMutableMeta>
get() {
val targetKeys = targetMeta[pathName]?.items?.keys ?: emptySet()
val defaultKeys = defaultMeta?.get(pathName)?.items?.keys ?: emptySet()
return (targetKeys + defaultKeys).associateWith { SchemeMeta(pathName + it) }
}
override fun invalidate(name: Name) {
listeners.forEach { it.callback(this@Scheme.meta, pathName + name) }
}
@Synchronized
override fun onChange(owner: Any?, callback: Meta.(name: Name) -> Unit) {
listeners.add(MetaListener(owner) { changedeName ->
if (changedeName.startsWith(pathName)) {
this@Scheme.meta.callback(changedeName.removeHeadOrNull(pathName)!!)
}
})
}
@Synchronized
override fun removeListener(owner: Any?) {
listeners.removeAll { it.owner === owner }
}
override fun toString(): String = Meta.toString(this)
override fun equals(other: Any?): Boolean = Meta.equals(this, other as? Meta)
override fun hashCode(): Int = Meta.hashCode(this)
override fun setMeta(name: Name, node: Meta?) {
targetMeta.setMeta(name, node)
invalidate(name)
}
override fun getOrCreate(name: Name): ObservableMutableMeta = SchemeMeta(pathName + name)
@DFExperimental
override fun attach(name: Name, node: ObservableMutableMeta) {
TODO("Not yet implemented")
}
}
}
/**
@ -83,7 +148,7 @@ public open class SchemeSpec<out T : Scheme>(
override val descriptor: MetaDescriptor? get() = null
override fun empty(): T = builder().also {
it.metaDescriptor = descriptor
it.descriptor = descriptor
}
@Suppress("OVERRIDE_BY_INLINE")