Laminate refactor
This commit is contained in:
parent
be2daca25e
commit
c01bc36d41
@ -8,26 +8,24 @@ import space.kscience.dataforge.values.Value
|
||||
* A meta laminate consisting of multiple immutable meta layers. For mutable front layer, use [Scheme].
|
||||
* If [layers] list contains a [Laminate] it is flat-mapped.
|
||||
*/
|
||||
public class Laminate(layers: List<Meta>) : TypedMeta<SealedMeta> {
|
||||
public class Laminate internal constructor(public val layers: List<Meta>) : TypedMeta<Laminate> {
|
||||
|
||||
override val value: Value? = layers.firstNotNullOfOrNull { it.value }
|
||||
|
||||
public val layers: List<Meta> = layers.flatMap {
|
||||
if (it is Laminate) {
|
||||
it.layers
|
||||
} else {
|
||||
listOf(it)
|
||||
override val items: Map<NameToken, Laminate> by lazy {
|
||||
layers.map { it.items.keys }.flatten().associateWith { key ->
|
||||
Laminate(layers.mapNotNull { it.items[key] })
|
||||
}
|
||||
}
|
||||
|
||||
override val items: Map<NameToken, SealedMeta> by lazy {
|
||||
layers.map { it.items.keys }.flatten().associateWith { key ->
|
||||
layers.asSequence().map { it.items[key] }.filterNotNull().let(replaceRule)
|
||||
}
|
||||
override fun getMeta(name: Name): Laminate? {
|
||||
val childLayers = layers.mapNotNull { it.getMeta(name) }
|
||||
return if (childLayers.isEmpty()) null else Laminate(childLayers)
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate sealed meta using [mergeRule]
|
||||
* Generate sealed meta by interweaving all layers. If a value is present in at least on layer, it will be present
|
||||
* in the result.
|
||||
*/
|
||||
public fun merge(): SealedMeta {
|
||||
val items = layers.map { it.items.keys }.flatten().associateWith { key ->
|
||||
@ -36,6 +34,17 @@ public class Laminate(layers: List<Meta>) : TypedMeta<SealedMeta> {
|
||||
return SealedMeta(value, items)
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate sealed meta by stacking layers. If node is present in the upper layer, then the lower layers will be
|
||||
* ignored event if they have values that are not present on top layer.
|
||||
*/
|
||||
public fun top(): SealedMeta {
|
||||
val items = layers.map { it.items.keys }.flatten().associateWith { key ->
|
||||
layers.asSequence().map { it.items[key] }.filterNotNull().first().seal()
|
||||
}
|
||||
return SealedMeta(value, items)
|
||||
}
|
||||
|
||||
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)
|
||||
@ -47,7 +56,7 @@ public class Laminate(layers: List<Meta>) : TypedMeta<SealedMeta> {
|
||||
*
|
||||
* TODO add picture
|
||||
*/
|
||||
public val replaceRule: (Sequence<Meta>) -> SealedMeta = { it.first().seal() }
|
||||
public val replaceRule: (Sequence<Meta>) -> SealedMeta? = { it.firstOrNull()?.seal() }
|
||||
|
||||
private fun Sequence<Meta>.merge(): SealedMeta {
|
||||
val value = firstNotNullOfOrNull { it.value }
|
||||
@ -69,12 +78,22 @@ public class Laminate(layers: List<Meta>) : TypedMeta<SealedMeta> {
|
||||
* The values a replaced but meta children are joined
|
||||
* TODO add picture
|
||||
*/
|
||||
public val mergeRule: (Sequence<Meta>) -> TypedMeta<SealedMeta> = { it.merge() }
|
||||
public val mergeRule: (Sequence<Meta>) -> SealedMeta? = { it.merge() }
|
||||
}
|
||||
}
|
||||
|
||||
@Suppress("FunctionName")
|
||||
public fun Laminate(vararg layers: Meta?): Laminate = Laminate(layers.filterNotNull())
|
||||
public fun Laminate(layers: Collection<Meta?>): Laminate {
|
||||
val flatLayers = layers.flatMap {
|
||||
if (it is Laminate) {
|
||||
it.layers
|
||||
} else {
|
||||
listOf(it)
|
||||
}
|
||||
}.filterNotNull()
|
||||
return Laminate(flatLayers)
|
||||
}
|
||||
|
||||
public fun Laminate(vararg layers: Meta?): Laminate = Laminate(listOf(*layers))
|
||||
|
||||
/**
|
||||
* Performance optimized version of get method
|
||||
|
@ -144,9 +144,18 @@ public interface TypedMeta<out M : TypedMeta<M>> : Meta {
|
||||
public val self: M
|
||||
get() = this as M
|
||||
|
||||
override val value: Value?
|
||||
override val items: Map<NameToken, M>
|
||||
|
||||
override fun getMeta(name: Name): M? {
|
||||
tailrec fun M.find(name: Name): M? = if (name.isEmpty()) {
|
||||
this
|
||||
} else {
|
||||
items[name.firstOrNull()!!]?.find(name.cutFirst())
|
||||
}
|
||||
|
||||
return self.find(name)
|
||||
}
|
||||
|
||||
override fun toMeta(): Meta = this
|
||||
}
|
||||
|
||||
|
@ -1,6 +1,7 @@
|
||||
package space.kscience.dataforge.meta
|
||||
|
||||
import kotlinx.serialization.Serializable
|
||||
import space.kscience.dataforge.misc.DFExperimental
|
||||
import space.kscience.dataforge.names.*
|
||||
import space.kscience.dataforge.values.EnumValue
|
||||
import space.kscience.dataforge.values.Value
|
||||
@ -154,8 +155,9 @@ public interface MutableTypedMeta<M : MutableTypedMeta<M>> : TypedMeta<M>, Mutab
|
||||
* Zero-copy attach or replace existing node. Node is used with any additional state, listeners, etc.
|
||||
* In some cases it is possible to have the same node as a child to several others
|
||||
*/
|
||||
@DFExperimental
|
||||
public fun attach(name: Name, node: M)
|
||||
|
||||
override fun getMeta(name: Name): M?
|
||||
override fun getOrCreate(name: Name): M
|
||||
}
|
||||
|
||||
@ -298,13 +300,13 @@ private class MutableMetaImpl(
|
||||
listeners.removeAll { it.owner === owner }
|
||||
}
|
||||
|
||||
|
||||
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) {
|
||||
0 -> error("Can't set a meta with empty name")
|
||||
|
@ -1,5 +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
|
||||
@ -34,7 +35,20 @@ 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?{
|
||||
tailrec fun ObservableMutableMeta.find(name: Name): ObservableMutableMeta? = if (name.isEmpty()) {
|
||||
this
|
||||
} else {
|
||||
items[name.firstOrNull()!!]?.find(name.cutFirst())
|
||||
}
|
||||
|
||||
return find(name)
|
||||
}
|
||||
}
|
||||
|
||||
private class ObservableMetaWrapper(
|
||||
val origin: MutableMeta,
|
||||
@ -59,7 +73,7 @@ private class ObservableMetaWrapper(
|
||||
override val items: Map<NameToken, ObservableMetaWrapper>
|
||||
get() = origin.items.mapValues { ObservableMetaWrapper(it.value) }
|
||||
|
||||
override fun getMeta(name: Name): MutableMeta? = origin.getMeta(name)
|
||||
override fun getMeta(name: Name): ObservableMetaWrapper? = origin.getMeta(name)?.let{ObservableMetaWrapper(it)}
|
||||
|
||||
override var value: Value?
|
||||
get() = origin.value
|
||||
@ -91,6 +105,7 @@ private class ObservableMetaWrapper(
|
||||
return origin.toMeta()
|
||||
}
|
||||
|
||||
@DFExperimental
|
||||
override fun attach(name: Name, node: ObservableMutableMeta) {
|
||||
set(name, node)
|
||||
node.onChange(this) { changeName ->
|
||||
|
@ -8,7 +8,6 @@ import space.kscience.dataforge.values.asValue
|
||||
/**
|
||||
* The meta implementation which is guaranteed to be immutable.
|
||||
*
|
||||
* If the argument is possibly mutable node, it is copied on creation
|
||||
*/
|
||||
@Serializable
|
||||
public class SealedMeta internal constructor(
|
||||
@ -21,7 +20,7 @@ public class SealedMeta internal constructor(
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate sealed node from [this]. If it is already sealed return it as is
|
||||
* Generate sealed node from [this]. If it is already sealed return it as is.
|
||||
*/
|
||||
public fun Meta.seal(): SealedMeta = this as? SealedMeta ?: SealedMeta(
|
||||
value,
|
||||
|
@ -195,7 +195,7 @@ public fun Name.endsWith(name: Name): Boolean =
|
||||
this.length >= name.length && (this == name || tokens.subList(length - name.length, length) == name.tokens)
|
||||
|
||||
/**
|
||||
* if [this] starts with given [head] name, returns the reminder of the name (could be empty). Otherwise returns null
|
||||
* if [this] starts with given [head] name, returns the reminder of the name (could be empty). Otherwise, returns null
|
||||
*/
|
||||
public fun Name.removeHeadOrNull(head: Name): Name? = if (startsWith(head)) {
|
||||
Name(tokens.subList(head.length, length))
|
||||
|
Loading…
Reference in New Issue
Block a user