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].
|
* 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.
|
* 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 }
|
override val value: Value? = layers.firstNotNullOfOrNull { it.value }
|
||||||
|
|
||||||
public val layers: List<Meta> = layers.flatMap {
|
override val items: Map<NameToken, Laminate> by lazy {
|
||||||
if (it is Laminate) {
|
layers.map { it.items.keys }.flatten().associateWith { key ->
|
||||||
it.layers
|
Laminate(layers.mapNotNull { it.items[key] })
|
||||||
} else {
|
|
||||||
listOf(it)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override val items: Map<NameToken, SealedMeta> by lazy {
|
override fun getMeta(name: Name): Laminate? {
|
||||||
layers.map { it.items.keys }.flatten().associateWith { key ->
|
val childLayers = layers.mapNotNull { it.getMeta(name) }
|
||||||
layers.asSequence().map { it.items[key] }.filterNotNull().let(replaceRule)
|
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 {
|
public fun merge(): SealedMeta {
|
||||||
val items = layers.map { it.items.keys }.flatten().associateWith { key ->
|
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)
|
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 toString(): String = Meta.toString(this)
|
||||||
override fun equals(other: Any?): Boolean = Meta.equals(this, other as? Meta)
|
override fun equals(other: Any?): Boolean = Meta.equals(this, other as? Meta)
|
||||||
override fun hashCode(): Int = Meta.hashCode(this)
|
override fun hashCode(): Int = Meta.hashCode(this)
|
||||||
@ -47,7 +56,7 @@ public class Laminate(layers: List<Meta>) : TypedMeta<SealedMeta> {
|
|||||||
*
|
*
|
||||||
* TODO add picture
|
* 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 {
|
private fun Sequence<Meta>.merge(): SealedMeta {
|
||||||
val value = firstNotNullOfOrNull { it.value }
|
val value = firstNotNullOfOrNull { it.value }
|
||||||
@ -61,7 +70,7 @@ public class Laminate(layers: List<Meta>) : TypedMeta<SealedMeta> {
|
|||||||
val items = groups.mapValues { entry ->
|
val items = groups.mapValues { entry ->
|
||||||
entry.value.asSequence().map { it.value }.merge()
|
entry.value.asSequence().map { it.value }.merge()
|
||||||
}
|
}
|
||||||
return SealedMeta(value,items)
|
return SealedMeta(value, items)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -69,12 +78,22 @@ public class Laminate(layers: List<Meta>) : TypedMeta<SealedMeta> {
|
|||||||
* The values a replaced but meta children are joined
|
* The values a replaced but meta children are joined
|
||||||
* TODO add picture
|
* 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(layers: Collection<Meta?>): Laminate {
|
||||||
public fun Laminate(vararg layers: Meta?): Laminate = Laminate(layers.filterNotNull())
|
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
|
* Performance optimized version of get method
|
||||||
|
@ -144,9 +144,18 @@ public interface TypedMeta<out M : TypedMeta<M>> : Meta {
|
|||||||
public val self: M
|
public val self: M
|
||||||
get() = this as M
|
get() = this as M
|
||||||
|
|
||||||
override val value: Value?
|
|
||||||
override val items: Map<NameToken, M>
|
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
|
override fun toMeta(): Meta = this
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
package space.kscience.dataforge.meta
|
package space.kscience.dataforge.meta
|
||||||
|
|
||||||
import kotlinx.serialization.Serializable
|
import kotlinx.serialization.Serializable
|
||||||
|
import space.kscience.dataforge.misc.DFExperimental
|
||||||
import space.kscience.dataforge.names.*
|
import space.kscience.dataforge.names.*
|
||||||
import space.kscience.dataforge.values.EnumValue
|
import space.kscience.dataforge.values.EnumValue
|
||||||
import space.kscience.dataforge.values.Value
|
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.
|
* 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
|
* 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)
|
public fun attach(name: Name, node: M)
|
||||||
|
override fun getMeta(name: Name): M?
|
||||||
override fun getOrCreate(name: Name): M
|
override fun getOrCreate(name: Name): M
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -298,13 +300,13 @@ private class MutableMetaImpl(
|
|||||||
listeners.removeAll { it.owner === owner }
|
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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@DFExperimental
|
||||||
override fun attach(name: Name, node: ObservableMutableMeta) {
|
override fun attach(name: Name, node: ObservableMutableMeta) {
|
||||||
when (name.length) {
|
when (name.length) {
|
||||||
0 -> error("Can't set a meta with empty name")
|
0 -> error("Can't set a meta with empty name")
|
||||||
|
@ -1,5 +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 space.kscience.dataforge.values.Value
|
||||||
import kotlin.jvm.Synchronized
|
import kotlin.jvm.Synchronized
|
||||||
@ -34,7 +35,20 @@ public interface ObservableMeta : Meta {
|
|||||||
/**
|
/**
|
||||||
* A [Meta] which is both observable and mutable
|
* 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(
|
private class ObservableMetaWrapper(
|
||||||
val origin: MutableMeta,
|
val origin: MutableMeta,
|
||||||
@ -59,7 +73,7 @@ private class ObservableMetaWrapper(
|
|||||||
override val items: Map<NameToken, ObservableMetaWrapper>
|
override val items: Map<NameToken, ObservableMetaWrapper>
|
||||||
get() = origin.items.mapValues { ObservableMetaWrapper(it.value) }
|
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?
|
override var value: Value?
|
||||||
get() = origin.value
|
get() = origin.value
|
||||||
@ -91,6 +105,7 @@ private class ObservableMetaWrapper(
|
|||||||
return origin.toMeta()
|
return origin.toMeta()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@DFExperimental
|
||||||
override fun attach(name: Name, node: ObservableMutableMeta) {
|
override fun attach(name: Name, node: ObservableMutableMeta) {
|
||||||
set(name, node)
|
set(name, node)
|
||||||
node.onChange(this) { changeName ->
|
node.onChange(this) { changeName ->
|
||||||
|
@ -8,7 +8,6 @@ import space.kscience.dataforge.values.asValue
|
|||||||
/**
|
/**
|
||||||
* The meta implementation which is guaranteed to be immutable.
|
* The meta implementation which is guaranteed to be immutable.
|
||||||
*
|
*
|
||||||
* If the argument is possibly mutable node, it is copied on creation
|
|
||||||
*/
|
*/
|
||||||
@Serializable
|
@Serializable
|
||||||
public class SealedMeta internal constructor(
|
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(
|
public fun Meta.seal(): SealedMeta = this as? SealedMeta ?: SealedMeta(
|
||||||
value,
|
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)
|
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)) {
|
public fun Name.removeHeadOrNull(head: Name): Name? = if (startsWith(head)) {
|
||||||
Name(tokens.subList(head.length, length))
|
Name(tokens.subList(head.length, length))
|
||||||
|
Loading…
Reference in New Issue
Block a user