diff --git a/dataforge-meta/src/commonMain/kotlin/space/kscience/dataforge/meta/Laminate.kt b/dataforge-meta/src/commonMain/kotlin/space/kscience/dataforge/meta/Laminate.kt
index 217efa95..30f6d542 100644
--- a/dataforge-meta/src/commonMain/kotlin/space/kscience/dataforge/meta/Laminate.kt
+++ b/dataforge-meta/src/commonMain/kotlin/space/kscience/dataforge/meta/Laminate.kt
@@ -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) : TypedMeta {
+public class Laminate internal constructor(public val layers: List) : TypedMeta {
override val value: Value? = layers.firstNotNullOfOrNull { it.value }
- public val layers: List = layers.flatMap {
- if (it is Laminate) {
- it.layers
- } else {
- listOf(it)
+ override val items: Map by lazy {
+ layers.map { it.items.keys }.flatten().associateWith { key ->
+ Laminate(layers.mapNotNull { it.items[key] })
}
}
- override val items: Map 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) : TypedMeta {
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) : TypedMeta {
*
* TODO add picture
*/
- public val replaceRule: (Sequence) -> SealedMeta = { it.first().seal() }
+ public val replaceRule: (Sequence) -> SealedMeta? = { it.firstOrNull()?.seal() }
private fun Sequence.merge(): SealedMeta {
val value = firstNotNullOfOrNull { it.value }
@@ -61,7 +70,7 @@ public class Laminate(layers: List) : TypedMeta {
val items = groups.mapValues { entry ->
entry.value.asSequence().map { it.value }.merge()
}
- return SealedMeta(value,items)
+ return SealedMeta(value, items)
}
@@ -69,12 +78,22 @@ public class Laminate(layers: List) : TypedMeta {
* The values a replaced but meta children are joined
* TODO add picture
*/
- public val mergeRule: (Sequence) -> TypedMeta = { it.merge() }
+ public val mergeRule: (Sequence) -> SealedMeta? = { it.merge() }
}
}
-@Suppress("FunctionName")
-public fun Laminate(vararg layers: Meta?): Laminate = Laminate(layers.filterNotNull())
+public fun Laminate(layers: Collection): 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
diff --git a/dataforge-meta/src/commonMain/kotlin/space/kscience/dataforge/meta/Meta.kt b/dataforge-meta/src/commonMain/kotlin/space/kscience/dataforge/meta/Meta.kt
index 21f9e5e3..2e2e4a10 100644
--- a/dataforge-meta/src/commonMain/kotlin/space/kscience/dataforge/meta/Meta.kt
+++ b/dataforge-meta/src/commonMain/kotlin/space/kscience/dataforge/meta/Meta.kt
@@ -144,9 +144,18 @@ public interface TypedMeta> : Meta {
public val self: M
get() = this as M
- override val value: Value?
override val items: Map
+ 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
}
diff --git a/dataforge-meta/src/commonMain/kotlin/space/kscience/dataforge/meta/MutableMeta.kt b/dataforge-meta/src/commonMain/kotlin/space/kscience/dataforge/meta/MutableMeta.kt
index bb23049a..f626448f 100644
--- a/dataforge-meta/src/commonMain/kotlin/space/kscience/dataforge/meta/MutableMeta.kt
+++ b/dataforge-meta/src/commonMain/kotlin/space/kscience/dataforge/meta/MutableMeta.kt
@@ -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> : TypedMeta, 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")
diff --git a/dataforge-meta/src/commonMain/kotlin/space/kscience/dataforge/meta/ObservableMeta.kt b/dataforge-meta/src/commonMain/kotlin/space/kscience/dataforge/meta/ObservableMeta.kt
index 26cc1cd7..c5f6b4d7 100644
--- a/dataforge-meta/src/commonMain/kotlin/space/kscience/dataforge/meta/ObservableMeta.kt
+++ b/dataforge-meta/src/commonMain/kotlin/space/kscience/dataforge/meta/ObservableMeta.kt
@@ -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
+public interface ObservableMutableMeta : ObservableMeta, MutableMeta, MutableTypedMeta{
+ 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
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 ->
diff --git a/dataforge-meta/src/commonMain/kotlin/space/kscience/dataforge/meta/SealedMeta.kt b/dataforge-meta/src/commonMain/kotlin/space/kscience/dataforge/meta/SealedMeta.kt
index 8cda90e9..df9afc12 100644
--- a/dataforge-meta/src/commonMain/kotlin/space/kscience/dataforge/meta/SealedMeta.kt
+++ b/dataforge-meta/src/commonMain/kotlin/space/kscience/dataforge/meta/SealedMeta.kt
@@ -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,
diff --git a/dataforge-meta/src/commonMain/kotlin/space/kscience/dataforge/names/Name.kt b/dataforge-meta/src/commonMain/kotlin/space/kscience/dataforge/names/Name.kt
index 4ff779a0..7bf8d14e 100644
--- a/dataforge-meta/src/commonMain/kotlin/space/kscience/dataforge/names/Name.kt
+++ b/dataforge-meta/src/commonMain/kotlin/space/kscience/dataforge/names/Name.kt
@@ -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))