WIP full refactor of Meta
This commit is contained in:
parent
9f5b010847
commit
a3479e74f7
@ -6,11 +6,11 @@ package space.kscience.dataforge.io
|
|||||||
import io.ktor.utils.io.core.Input
|
import io.ktor.utils.io.core.Input
|
||||||
import io.ktor.utils.io.core.Output
|
import io.ktor.utils.io.core.Output
|
||||||
import kotlinx.serialization.json.Json
|
import kotlinx.serialization.json.Json
|
||||||
import kotlinx.serialization.json.JsonObject
|
import kotlinx.serialization.json.JsonElement
|
||||||
import space.kscience.dataforge.context.Context
|
import space.kscience.dataforge.context.Context
|
||||||
import space.kscience.dataforge.io.IOFormat.Companion.NAME_KEY
|
import space.kscience.dataforge.io.IOFormat.Companion.NAME_KEY
|
||||||
import space.kscience.dataforge.meta.Meta
|
import space.kscience.dataforge.meta.Meta
|
||||||
import space.kscience.dataforge.meta.descriptors.NodeDescriptor
|
import space.kscience.dataforge.meta.descriptors.MetaDescriptor
|
||||||
import space.kscience.dataforge.meta.toJson
|
import space.kscience.dataforge.meta.toJson
|
||||||
import space.kscience.dataforge.meta.toMeta
|
import space.kscience.dataforge.meta.toMeta
|
||||||
import kotlin.reflect.KType
|
import kotlin.reflect.KType
|
||||||
@ -23,20 +23,19 @@ public class JsonMetaFormat(private val json: Json = DEFAULT_JSON) : MetaFormat
|
|||||||
|
|
||||||
override val type: KType get() = typeOf<Meta>()
|
override val type: KType get() = typeOf<Meta>()
|
||||||
|
|
||||||
override fun writeMeta(output: Output, meta: Meta, descriptor: NodeDescriptor?) {
|
override fun writeMeta(output: Output, meta: Meta, descriptor: MetaDescriptor?) {
|
||||||
val jsonObject = meta.toJson(descriptor)
|
val jsonObject = meta.toJson(descriptor)
|
||||||
output.writeUtf8String(json.encodeToString(JsonObject.serializer(), jsonObject))
|
output.writeUtf8String(json.encodeToString(JsonElement.serializer(), jsonObject))
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun toMeta(): Meta = Meta {
|
override fun toMeta(): Meta = Meta {
|
||||||
NAME_KEY put name.toString()
|
NAME_KEY put name.toString()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun readMeta(input: Input, descriptor: NodeDescriptor?): Meta {
|
override fun readMeta(input: Input, descriptor: MetaDescriptor?): Meta {
|
||||||
val str = input.readUtf8String()//readByteArray().decodeToString()
|
val str = input.readUtf8String()//readByteArray().decodeToString()
|
||||||
val jsonElement = json.parseToJsonElement(str)
|
val jsonElement = json.parseToJsonElement(str)
|
||||||
val item = jsonElement.toMeta(descriptor)
|
return jsonElement.toMeta(descriptor)
|
||||||
return item.node ?: Meta.EMPTY
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public companion object : MetaFormatFactory {
|
public companion object : MetaFormatFactory {
|
||||||
@ -49,10 +48,10 @@ public class JsonMetaFormat(private val json: Json = DEFAULT_JSON) : MetaFormat
|
|||||||
|
|
||||||
private val default = JsonMetaFormat()
|
private val default = JsonMetaFormat()
|
||||||
|
|
||||||
override fun writeMeta(output: Output, meta: Meta, descriptor: NodeDescriptor?): Unit =
|
override fun writeMeta(output: Output, meta: Meta, descriptor: MetaDescriptor?): Unit =
|
||||||
default.run { writeMeta(output, meta, descriptor) }
|
default.run { writeMeta(output, meta, descriptor) }
|
||||||
|
|
||||||
override fun readMeta(input: Input, descriptor: NodeDescriptor?): Meta =
|
override fun readMeta(input: Input, descriptor: MetaDescriptor?): Meta =
|
||||||
default.run { readMeta(input, descriptor) }
|
default.run { readMeta(input, descriptor) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -116,6 +116,10 @@ public class JsonMeta(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
|
|
||||||
public companion object {
|
public companion object {
|
||||||
/**
|
/**
|
||||||
* A key representing top-level json array of nodes, which could not be directly represented by a meta node
|
* A key representing top-level json array of nodes, which could not be directly represented by a meta node
|
||||||
|
@ -36,6 +36,10 @@ public class Laminate(layers: List<Meta>) : TypedMeta<SealedMeta> {
|
|||||||
return SealedMeta(value, items)
|
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)
|
||||||
|
|
||||||
public companion object {
|
public companion object {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -63,6 +63,11 @@ public interface Meta : MetaRepr {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* True if this [Meta] does not have children
|
||||||
|
*/
|
||||||
|
public val Meta.isLeaf: Boolean get() = items.isEmpty()
|
||||||
|
|
||||||
|
|
||||||
public operator fun Meta.get(token: NameToken): Meta? = items[token]
|
public operator fun Meta.get(token: NameToken): Meta? = items[token]
|
||||||
|
|
||||||
|
@ -17,6 +17,8 @@ import kotlin.jvm.Synchronized
|
|||||||
@Serializable(MutableMetaSerializer::class)
|
@Serializable(MutableMetaSerializer::class)
|
||||||
public interface MutableMeta : Meta {
|
public interface MutableMeta : Meta {
|
||||||
|
|
||||||
|
override val items: Map<NameToken, MutableMeta>
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get or set value of this node
|
* Get or set value of this node
|
||||||
*/
|
*/
|
||||||
@ -241,12 +243,12 @@ private class MutableMetaImpl(
|
|||||||
children: Map<NameToken, Meta> = emptyMap()
|
children: Map<NameToken, Meta> = emptyMap()
|
||||||
) : ObservableMutableMeta {
|
) : ObservableMutableMeta {
|
||||||
|
|
||||||
private val children: LinkedHashMap<NameToken, ObservableMutableMeta> =
|
private val children: LinkedHashMap<NameToken, MutableMetaImpl> =
|
||||||
LinkedHashMap(children.mapValues { (key, meta) ->
|
LinkedHashMap(children.mapValues { (key, meta) ->
|
||||||
MutableMetaImpl(meta.value, meta.items).apply { adoptBy(this, key) }
|
MutableMetaImpl(meta.value, meta.items).apply { adoptBy(this, key) }
|
||||||
})
|
})
|
||||||
|
|
||||||
override val items: Map<NameToken, ObservableMutableMeta> get() = children
|
override val items: Map<NameToken, MutableMetaImpl> get() = children
|
||||||
|
|
||||||
private val listeners = HashSet<MetaListener>()
|
private val listeners = HashSet<MetaListener>()
|
||||||
|
|
||||||
@ -271,21 +273,37 @@ private class MutableMetaImpl(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun attach(name: Name, node: ObservableMutableMeta) {
|
|
||||||
|
// fun attach(name: Name, node: MutableMetaImpl) {
|
||||||
|
// when (name.length) {
|
||||||
|
// 0 -> error("Can't set a meta with empty name")
|
||||||
|
// 1 -> {
|
||||||
|
// val key = name.firstOrNull()!!
|
||||||
|
// children[key] = node
|
||||||
|
// adoptBy(this, key)
|
||||||
|
// changed(name)
|
||||||
|
// }
|
||||||
|
// else -> get(name.cutLast())?.attach(name.lastOrNull()!!.asName(), node)
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create and attach empty node
|
||||||
|
*/
|
||||||
|
private fun createNode(name: Name): ObservableMutableMeta {
|
||||||
|
val newNode = MutableMetaImpl(null)
|
||||||
when (name.length) {
|
when (name.length) {
|
||||||
0 -> error("Can't set a meta with empty name")
|
0 -> throw IllegalArgumentException("Can't create a node with empty name")
|
||||||
1 -> {
|
1 -> {
|
||||||
val key = name.firstOrNull()!!
|
children[name.first()] = newNode
|
||||||
children[key] = node
|
newNode.adoptBy(this, name.first())
|
||||||
adoptBy(this, key)
|
} //do not notify, no value changed
|
||||||
changed(name)
|
else -> getOrCreate(name.first().asName()).getOrCreate(name.cutFirst())
|
||||||
}
|
|
||||||
else -> get(name.cutLast())?.attach(name.lastOrNull()!!.asName(), node)
|
|
||||||
}
|
}
|
||||||
|
return newNode
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getOrCreate(name: Name): ObservableMutableMeta =
|
override fun getOrCreate(name: Name): ObservableMutableMeta = get(name) ?: createNode(name)
|
||||||
get(name) ?: MutableMetaImpl(null).also { attach(name, it) }
|
|
||||||
|
|
||||||
override fun removeNode(name: Name) {
|
override fun removeNode(name: Name) {
|
||||||
when (name.length) {
|
when (name.length) {
|
||||||
|
@ -25,7 +25,7 @@ public fun MutableMeta.item(key: Name? = null): MutableMetaDelegate = object : M
|
|||||||
/* Mutable converters */
|
/* Mutable converters */
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A type converter for a mutable [TypedMetaItem] delegate
|
* A type converter for a [MutableMetaDelegate]
|
||||||
*/
|
*/
|
||||||
public fun <R : Any> MutableMetaDelegate.convert(
|
public fun <R : Any> MutableMetaDelegate.convert(
|
||||||
converter: MetaConverter<R>,
|
converter: MetaConverter<R>,
|
||||||
|
@ -32,10 +32,10 @@ public interface ObservableMeta : Meta {
|
|||||||
/**
|
/**
|
||||||
* A [Meta] which is both observable and mutable
|
* A [Meta] which is both observable and mutable
|
||||||
*/
|
*/
|
||||||
public interface ObservableMutableMeta : ObservableMeta, MutableTypedMeta<ObservableMutableMeta>
|
public interface ObservableMutableMeta : ObservableMeta, MutableMeta, TypedMeta<ObservableMutableMeta>
|
||||||
|
|
||||||
private class ObservableMetaWrapper<M : MutableTypedMeta<M>>(
|
private class ObservableMetaWrapper(
|
||||||
val origin: M,
|
val origin: MutableMeta,
|
||||||
) : ObservableMutableMeta, Meta by origin {
|
) : ObservableMutableMeta, Meta by origin {
|
||||||
|
|
||||||
private val listeners = HashSet<MetaListener>()
|
private val listeners = HashSet<MetaListener>()
|
||||||
@ -54,7 +54,7 @@ private class ObservableMetaWrapper<M : MutableTypedMeta<M>>(
|
|||||||
listeners.removeAll { it.owner === owner }
|
listeners.removeAll { it.owner === owner }
|
||||||
}
|
}
|
||||||
|
|
||||||
override val items: Map<NameToken, ObservableMetaWrapper<M>>
|
override val items: Map<NameToken, ObservableMetaWrapper>
|
||||||
get() = origin.items.mapValues { ObservableMetaWrapper(it.value) }
|
get() = origin.items.mapValues { ObservableMetaWrapper(it.value) }
|
||||||
|
|
||||||
override var value: Value?
|
override var value: Value?
|
||||||
@ -64,11 +64,6 @@ private class ObservableMetaWrapper<M : MutableTypedMeta<M>>(
|
|||||||
changed(Name.EMPTY)
|
changed(Name.EMPTY)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun attach(name: Name, node: ObservableMutableMeta) {
|
|
||||||
origin.attach(name, node.origin)
|
|
||||||
changed(name)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun getOrCreate(name: Name): ObservableMutableMeta =
|
override fun getOrCreate(name: Name): ObservableMutableMeta =
|
||||||
get(name) ?: ObservableMetaWrapper(origin.getOrCreate(name))
|
get(name) ?: ObservableMetaWrapper(origin.getOrCreate(name))
|
||||||
|
|
||||||
@ -91,8 +86,8 @@ private class ObservableMetaWrapper<M : MutableTypedMeta<M>>(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public fun <M : MutableTypedMeta<M>> MutableTypedMeta<M>.asObservable(): ObservableMeta =
|
public fun MutableMeta.asObservable(): ObservableMeta =
|
||||||
(this as? ObservableMeta) ?: ObservableMetaWrapper(self)
|
(this as? ObservableMeta) ?: ObservableMetaWrapper(this)
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
package space.kscience.dataforge.meta
|
package space.kscience.dataforge.meta
|
||||||
|
|
||||||
import space.kscience.dataforge.meta.descriptors.MetaDescriptor
|
import space.kscience.dataforge.meta.descriptors.MetaDescriptor
|
||||||
|
import space.kscience.dataforge.meta.descriptors.get
|
||||||
import space.kscience.dataforge.misc.DFExperimental
|
import space.kscience.dataforge.misc.DFExperimental
|
||||||
import space.kscience.dataforge.names.toName
|
import space.kscience.dataforge.names.toName
|
||||||
import space.kscience.dataforge.values.ListValue
|
import space.kscience.dataforge.values.ListValue
|
||||||
@ -9,39 +10,45 @@ import space.kscience.dataforge.values.Value
|
|||||||
/**
|
/**
|
||||||
* Convert meta to map of maps
|
* Convert meta to map of maps
|
||||||
*/
|
*/
|
||||||
public fun Meta.toMap(descriptor: MetaDescriptor? = null): Map<String, Any?> {
|
public fun Meta.toMap(descriptor: MetaDescriptor? = null): Map<String, Any?> = buildMap {
|
||||||
return items.entries.associate { (token, item) ->
|
items.forEach { (key, child) ->
|
||||||
token.toString() to when (item) {
|
if (child.items.isEmpty()) {
|
||||||
is MetaItemNode -> item.node.toMap()
|
//single-value meta is considered a leaf
|
||||||
is MetaItemValue -> item.value.value
|
put(key.toString(), child.value)
|
||||||
|
} else {
|
||||||
|
//if child contains both children and value, then value will be placed into `@value` key
|
||||||
|
put(key.toString(), child.toMap(descriptor?.get(key.body)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (value != null) {
|
||||||
|
put(Meta.VALUE_KEY, value)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Convert map of maps to meta. This method will recognize [TypedMetaItem], [Map]<String,Any?> and [List] of all mentioned above as value.
|
* Convert map of maps to meta. This method will recognize [Meta], [Map]<String,Any?> and [List] of all mentioned above as value.
|
||||||
* All other values will be converted to values.
|
* All other values will be converted to values.
|
||||||
*/
|
*/
|
||||||
@DFExperimental
|
@DFExperimental
|
||||||
public fun Map<String, Any?>.toMeta(descriptor: MetaDescriptor? = null): Meta = Meta {
|
public fun Map<String, Any?>.toMeta(descriptor: MetaDescriptor? = null): Meta = Meta {
|
||||||
@Suppress("UNCHECKED_CAST")
|
@Suppress("UNCHECKED_CAST")
|
||||||
fun toItem(value: Any?): MetaItem = when (value) {
|
fun toMeta(value: Any?): Meta = when (value) {
|
||||||
is MetaItem -> value
|
is Meta -> value
|
||||||
is Meta -> MetaItemNode(value)
|
is Map<*, *> -> (value as Map<String, Any?>).toMeta()
|
||||||
is Map<*, *> -> MetaItemNode((value as Map<String, Any?>).toMeta())
|
else -> Meta(Value.of(value))
|
||||||
else -> MetaItemValue(Value.of(value))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
entries.forEach { (key, value) ->
|
entries.forEach { (key, value) ->
|
||||||
if (value is List<*>) {
|
if (value is List<*>) {
|
||||||
val items = value.map { toItem(it) }
|
val items = value.map { toMeta(it) }
|
||||||
if (items.all { it is MetaItemValue }) {
|
if (items.all { it.isLeaf }) {
|
||||||
set(key, ListValue(items.map { it.value!! }))
|
set(key, ListValue(items.map { it.value!! }))
|
||||||
} else {
|
} else {
|
||||||
setIndexedItems(key.toName(), value.map { toItem(it) })
|
setIndexedItems(key.toName(), value.map { toMeta(it) })
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
set(key, toItem(value))
|
set(key, toMeta(value))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -74,6 +74,11 @@ public fun Name.lastOrNull(): NameToken? = tokens.lastOrNull()
|
|||||||
*/
|
*/
|
||||||
public fun Name.firstOrNull(): NameToken? = tokens.firstOrNull()
|
public fun Name.firstOrNull(): NameToken? = tokens.firstOrNull()
|
||||||
|
|
||||||
|
/**
|
||||||
|
* First token or throw exception
|
||||||
|
*/
|
||||||
|
public fun Name.first(): NameToken = tokens.first()
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Convert a [String] to name parsing it and extracting name tokens and index syntax.
|
* Convert a [String] to name parsing it and extracting name tokens and index syntax.
|
||||||
|
Loading…
Reference in New Issue
Block a user