Fix Scheme special meta implementation
This commit is contained in:
parent
e5f422f9ca
commit
c1065c2885
@ -265,14 +265,7 @@ public operator fun <M : MutableTypedMeta<M>> MutableTypedMeta<M>.set(name: Name
|
|||||||
private class MutableMetaImpl(
|
private class MutableMetaImpl(
|
||||||
value: Value?,
|
value: Value?,
|
||||||
children: Map<NameToken, Meta> = emptyMap()
|
children: Map<NameToken, Meta> = emptyMap()
|
||||||
) : ObservableMutableMeta {
|
) : AbstractObservableMeta(), ObservableMutableMeta {
|
||||||
|
|
||||||
private val listeners = HashSet<MetaListener>()
|
|
||||||
|
|
||||||
override fun invalidate(name: Name) {
|
|
||||||
listeners.forEach { it.callback(this, name) }
|
|
||||||
}
|
|
||||||
|
|
||||||
override var value = value
|
override var value = value
|
||||||
@Synchronized set(value) {
|
@Synchronized set(value) {
|
||||||
val oldValue = field
|
val oldValue = field
|
||||||
@ -289,17 +282,6 @@ private class MutableMetaImpl(
|
|||||||
|
|
||||||
override val items: Map<NameToken, ObservableMutableMeta> get() = children
|
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) {
|
private fun ObservableMeta.adoptBy(parent: MutableMetaImpl, key: NameToken) {
|
||||||
onChange(parent) { name ->
|
onChange(parent) { name ->
|
||||||
parent.invalidate(key + name)
|
parent.invalidate(key + name)
|
||||||
@ -377,10 +359,6 @@ private class MutableMetaImpl(
|
|||||||
invalidate(name)
|
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)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -1,8 +1,6 @@
|
|||||||
package space.kscience.dataforge.meta
|
package space.kscience.dataforge.meta
|
||||||
|
|
||||||
import space.kscience.dataforge.misc.DFExperimental
|
|
||||||
import space.kscience.dataforge.names.*
|
import space.kscience.dataforge.names.*
|
||||||
import space.kscience.dataforge.values.Value
|
|
||||||
import kotlin.jvm.Synchronized
|
import kotlin.jvm.Synchronized
|
||||||
import kotlin.reflect.KProperty1
|
import kotlin.reflect.KProperty1
|
||||||
|
|
||||||
@ -38,7 +36,6 @@ public interface ObservableMeta : Meta {
|
|||||||
public interface ObservableMutableMeta : ObservableMeta, MutableMeta, MutableTypedMeta<ObservableMutableMeta> {
|
public interface ObservableMutableMeta : ObservableMeta, MutableMeta, MutableTypedMeta<ObservableMutableMeta> {
|
||||||
override fun getOrCreate(name: Name): 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()) {
|
tailrec fun ObservableMutableMeta.find(name: Name): ObservableMutableMeta? = if (name.isEmpty()) {
|
||||||
this
|
this
|
||||||
@ -50,10 +47,7 @@ public interface ObservableMutableMeta : ObservableMeta, MutableMeta, MutableTyp
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private class ObservableMetaWrapper(
|
internal abstract class AbstractObservableMeta : ObservableMeta {
|
||||||
val origin: MutableMeta,
|
|
||||||
) : ObservableMutableMeta, Meta by origin {
|
|
||||||
|
|
||||||
private val listeners = HashSet<MetaListener>()
|
private val listeners = HashSet<MetaListener>()
|
||||||
|
|
||||||
override fun invalidate(name: Name) {
|
override fun invalidate(name: Name) {
|
||||||
@ -70,58 +64,11 @@ private class ObservableMetaWrapper(
|
|||||||
listeners.removeAll { it.owner === owner }
|
listeners.removeAll { it.owner === owner }
|
||||||
}
|
}
|
||||||
|
|
||||||
override val items: Map<NameToken, ObservableMetaWrapper>
|
override fun toString(): String = Meta.toString(this)
|
||||||
get() = origin.items.mapValues { ObservableMetaWrapper(it.value) }
|
override fun equals(other: Any?): Boolean = Meta.equals(this, other as? Meta)
|
||||||
|
override fun hashCode(): Int = Meta.hashCode(this)
|
||||||
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])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 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].
|
* Use the value of the property in a [callBack].
|
||||||
* The callback is called once immediately after subscription to pass the initial value.
|
* The callback is called once immediately after subscription to pass the initial value.
|
||||||
|
@ -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())
|
@ -1,8 +1,10 @@
|
|||||||
package space.kscience.dataforge.meta
|
package space.kscience.dataforge.meta
|
||||||
|
|
||||||
import space.kscience.dataforge.meta.descriptors.*
|
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 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].
|
* 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 {
|
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(
|
internal fun wrap(
|
||||||
items: MutableMeta,
|
newMeta: MutableMeta,
|
||||||
preserveDefault: Boolean = false
|
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?) {
|
override fun setValue(name: Name, value: Value?) {
|
||||||
//TODO add validation
|
val valueDescriptor = descriptor?.get(name)
|
||||||
|
if (valueDescriptor?.validate(value) != false) {
|
||||||
meta.setValue(name, value)
|
meta.setValue(name, value)
|
||||||
|
} else error("Value $value is not validated by $valueDescriptor")
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun toMeta(): Laminate = Laminate(meta, descriptor?.defaultNode)
|
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 val descriptor: MetaDescriptor? get() = null
|
||||||
|
|
||||||
override fun empty(): T = builder().also {
|
override fun empty(): T = builder().also {
|
||||||
it.metaDescriptor = descriptor
|
it.descriptor = descriptor
|
||||||
}
|
}
|
||||||
|
|
||||||
@Suppress("OVERRIDE_BY_INLINE")
|
@Suppress("OVERRIDE_BY_INLINE")
|
||||||
|
Loading…
Reference in New Issue
Block a user