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(
|
||||
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)
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -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
|
||||
|
||||
@ -38,7 +36,6 @@ public interface ObservableMeta : Meta {
|
||||
public interface ObservableMutableMeta : ObservableMeta, MutableMeta, MutableTypedMeta<ObservableMutableMeta> {
|
||||
override fun getOrCreate(name: Name): ObservableMutableMeta
|
||||
|
||||
|
||||
override fun getMeta(name: Name): ObservableMutableMeta? {
|
||||
tailrec fun ObservableMutableMeta.find(name: Name): ObservableMutableMeta? = if (name.isEmpty()) {
|
||||
this
|
||||
@ -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 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 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].
|
||||
* 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
|
||||
|
||||
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
|
||||
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")
|
||||
|
Loading…
Reference in New Issue
Block a user