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.Output
|
||||
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.io.IOFormat.Companion.NAME_KEY
|
||||
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.toMeta
|
||||
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 fun writeMeta(output: Output, meta: Meta, descriptor: NodeDescriptor?) {
|
||||
override fun writeMeta(output: Output, meta: Meta, descriptor: MetaDescriptor?) {
|
||||
val jsonObject = meta.toJson(descriptor)
|
||||
output.writeUtf8String(json.encodeToString(JsonObject.serializer(), jsonObject))
|
||||
output.writeUtf8String(json.encodeToString(JsonElement.serializer(), jsonObject))
|
||||
}
|
||||
|
||||
override fun toMeta(): Meta = Meta {
|
||||
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 jsonElement = json.parseToJsonElement(str)
|
||||
val item = jsonElement.toMeta(descriptor)
|
||||
return item.node ?: Meta.EMPTY
|
||||
return jsonElement.toMeta(descriptor)
|
||||
}
|
||||
|
||||
public companion object : MetaFormatFactory {
|
||||
@ -49,10 +48,10 @@ public class JsonMetaFormat(private val json: Json = DEFAULT_JSON) : MetaFormat
|
||||
|
||||
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) }
|
||||
|
||||
override fun readMeta(input: Input, descriptor: NodeDescriptor?): Meta =
|
||||
override fun readMeta(input: Input, descriptor: MetaDescriptor?): Meta =
|
||||
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 {
|
||||
/**
|
||||
* 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)
|
||||
}
|
||||
|
||||
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 {
|
||||
|
||||
/**
|
||||
|
@ -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]
|
||||
|
||||
|
@ -17,6 +17,8 @@ import kotlin.jvm.Synchronized
|
||||
@Serializable(MutableMetaSerializer::class)
|
||||
public interface MutableMeta : Meta {
|
||||
|
||||
override val items: Map<NameToken, MutableMeta>
|
||||
|
||||
/**
|
||||
* Get or set value of this node
|
||||
*/
|
||||
@ -241,12 +243,12 @@ private class MutableMetaImpl(
|
||||
children: Map<NameToken, Meta> = emptyMap()
|
||||
) : ObservableMutableMeta {
|
||||
|
||||
private val children: LinkedHashMap<NameToken, ObservableMutableMeta> =
|
||||
private val children: LinkedHashMap<NameToken, MutableMetaImpl> =
|
||||
LinkedHashMap(children.mapValues { (key, meta) ->
|
||||
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>()
|
||||
|
||||
@ -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) {
|
||||
0 -> error("Can't set a meta with empty name")
|
||||
0 -> throw IllegalArgumentException("Can't create a node 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)
|
||||
children[name.first()] = newNode
|
||||
newNode.adoptBy(this, name.first())
|
||||
} //do not notify, no value changed
|
||||
else -> getOrCreate(name.first().asName()).getOrCreate(name.cutFirst())
|
||||
}
|
||||
return newNode
|
||||
}
|
||||
|
||||
override fun getOrCreate(name: Name): ObservableMutableMeta =
|
||||
get(name) ?: MutableMetaImpl(null).also { attach(name, it) }
|
||||
override fun getOrCreate(name: Name): ObservableMutableMeta = get(name) ?: createNode(name)
|
||||
|
||||
override fun removeNode(name: Name) {
|
||||
when (name.length) {
|
||||
|
@ -25,7 +25,7 @@ public fun MutableMeta.item(key: Name? = null): MutableMetaDelegate = object : M
|
||||
/* Mutable converters */
|
||||
|
||||
/**
|
||||
* A type converter for a mutable [TypedMetaItem] delegate
|
||||
* A type converter for a [MutableMetaDelegate]
|
||||
*/
|
||||
public fun <R : Any> MutableMetaDelegate.convert(
|
||||
converter: MetaConverter<R>,
|
||||
|
@ -32,11 +32,11 @@ public interface ObservableMeta : Meta {
|
||||
/**
|
||||
* 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>>(
|
||||
val origin: M,
|
||||
) : ObservableMutableMeta, Meta by origin {
|
||||
private class ObservableMetaWrapper(
|
||||
val origin: MutableMeta,
|
||||
) : ObservableMutableMeta, Meta by origin {
|
||||
|
||||
private val listeners = HashSet<MetaListener>()
|
||||
|
||||
@ -54,7 +54,7 @@ private class ObservableMetaWrapper<M : MutableTypedMeta<M>>(
|
||||
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) }
|
||||
|
||||
override var value: Value?
|
||||
@ -64,11 +64,6 @@ private class ObservableMetaWrapper<M : MutableTypedMeta<M>>(
|
||||
changed(Name.EMPTY)
|
||||
}
|
||||
|
||||
override fun attach(name: Name, node: ObservableMutableMeta) {
|
||||
origin.attach(name, node.origin)
|
||||
changed(name)
|
||||
}
|
||||
|
||||
override fun getOrCreate(name: Name): ObservableMutableMeta =
|
||||
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 =
|
||||
(this as? ObservableMeta) ?: ObservableMetaWrapper(self)
|
||||
public fun MutableMeta.asObservable(): ObservableMeta =
|
||||
(this as? ObservableMeta) ?: ObservableMetaWrapper(this)
|
||||
|
||||
|
||||
/**
|
||||
|
@ -1,6 +1,7 @@
|
||||
package space.kscience.dataforge.meta
|
||||
|
||||
import space.kscience.dataforge.meta.descriptors.MetaDescriptor
|
||||
import space.kscience.dataforge.meta.descriptors.get
|
||||
import space.kscience.dataforge.misc.DFExperimental
|
||||
import space.kscience.dataforge.names.toName
|
||||
import space.kscience.dataforge.values.ListValue
|
||||
@ -9,39 +10,45 @@ import space.kscience.dataforge.values.Value
|
||||
/**
|
||||
* Convert meta to map of maps
|
||||
*/
|
||||
public fun Meta.toMap(descriptor: MetaDescriptor? = null): Map<String, Any?> {
|
||||
return items.entries.associate { (token, item) ->
|
||||
token.toString() to when (item) {
|
||||
is MetaItemNode -> item.node.toMap()
|
||||
is MetaItemValue -> item.value.value
|
||||
public fun Meta.toMap(descriptor: MetaDescriptor? = null): Map<String, Any?> = buildMap {
|
||||
items.forEach { (key, child) ->
|
||||
if (child.items.isEmpty()) {
|
||||
//single-value meta is considered a leaf
|
||||
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.
|
||||
*/
|
||||
@DFExperimental
|
||||
public fun Map<String, Any?>.toMeta(descriptor: MetaDescriptor? = null): Meta = Meta {
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
fun toItem(value: Any?): MetaItem = when (value) {
|
||||
is MetaItem -> value
|
||||
is Meta -> MetaItemNode(value)
|
||||
is Map<*, *> -> MetaItemNode((value as Map<String, Any?>).toMeta())
|
||||
else -> MetaItemValue(Value.of(value))
|
||||
fun toMeta(value: Any?): Meta = when (value) {
|
||||
is Meta -> value
|
||||
is Map<*, *> -> (value as Map<String, Any?>).toMeta()
|
||||
else -> Meta(Value.of(value))
|
||||
}
|
||||
|
||||
entries.forEach { (key, value) ->
|
||||
if (value is List<*>) {
|
||||
val items = value.map { toItem(it) }
|
||||
if (items.all { it is MetaItemValue }) {
|
||||
val items = value.map { toMeta(it) }
|
||||
if (items.all { it.isLeaf }) {
|
||||
set(key, ListValue(items.map { it.value!! }))
|
||||
} else {
|
||||
setIndexedItems(key.toName(), value.map { toItem(it) })
|
||||
setIndexedItems(key.toName(), value.map { toMeta(it) })
|
||||
}
|
||||
} 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()
|
||||
|
||||
/**
|
||||
* 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.
|
||||
|
Loading…
Reference in New Issue
Block a user