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

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 {
/**
* 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)
}
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 {
/**

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]

View File

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

View File

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

View File

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

View File

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

View File

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