WIP full refactor of Meta

This commit is contained in:
Alexander Nozik 2021-07-25 12:57:10 +03:00
parent 9f5b010847
commit a3479e74f7
9 changed files with 86 additions and 49 deletions

View File

@ -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) }
} }
} }

View File

@ -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

View File

@ -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 {
/** /**

View File

@ -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]

View File

@ -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) {

View File

@ -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>,

View File

@ -32,11 +32,11 @@ 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)
/** /**

View File

@ -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))
} }
} }
} }

View File

@ -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.