Json meta format fix

This commit is contained in:
Alexander Nozik 2019-03-19 10:57:41 +03:00
parent 9fd89a5b60
commit ce0e5d4f24
3 changed files with 85 additions and 102 deletions

View File

@ -3,6 +3,7 @@ package hep.dataforge.meta.io
import hep.dataforge.meta.Meta import hep.dataforge.meta.Meta
import hep.dataforge.meta.MetaItem import hep.dataforge.meta.MetaItem
import hep.dataforge.names.NameToken import hep.dataforge.names.NameToken
import hep.dataforge.names.toName
import hep.dataforge.values.* import hep.dataforge.values.*
import kotlinx.io.core.Input import kotlinx.io.core.Input
import kotlinx.io.core.Output import kotlinx.io.core.Output
@ -22,10 +23,10 @@ object JsonMetaFormat : MetaFormat {
val str = input.readText() val str = input.readText()
val json = Json.plain.parseJson(str) val json = Json.plain.parseJson(str)
if(json is JsonObject) { if (json is JsonObject) {
return json.toMeta() return json.toMeta()
} else { } else {
TODO("non-object root") TODO("Non-object root not supported")
} }
} }
} }
@ -51,34 +52,47 @@ fun Meta.toJson(): JsonObject {
} }
fun JsonElement.toMetaItem() = when (this) {
is JsonPrimitive -> MetaItem.ValueItem<JsonMeta>(this.toValue())
is JsonObject -> MetaItem.NodeItem(this.toMeta())
is JsonArray -> {
if (this.all { it is JsonPrimitive }) {
val value = ListValue(this.map { (it as JsonPrimitive).toValue() })
MetaItem.ValueItem<JsonMeta>(value)
} else {
TODO("mixed nodes json")
}
}
}
fun JsonObject.toMeta() = JsonMeta(this) fun JsonObject.toMeta() = JsonMeta(this)
private fun JsonPrimitive.toValue(): Value {
return when (this) {
JsonNull -> Null
else -> this.content.parseValue() // Optimize number and boolean parsing
}
}
class JsonMeta(val json: JsonObject) : Meta { class JsonMeta(val json: JsonObject) : Meta {
override val items: Map<NameToken, MetaItem<out Meta>> by lazy {
json.mapKeys { NameToken(it.key) }.mapValues { entry -> private fun JsonPrimitive.toValue(): Value {
entry.value.toMetaItem() return when (this) {
JsonNull -> Null
else -> this.content.parseValue() // Optimize number and boolean parsing
} }
} }
private operator fun MutableMap<String, MetaItem<JsonMeta>>.set(key: String, value: JsonElement) = when (value) {
is JsonPrimitive -> this[key] = MetaItem.ValueItem(value.toValue())
is JsonObject -> this[key] = MetaItem.NodeItem(value.toMeta())
is JsonArray -> {
when {
value.all { it is JsonPrimitive } -> {
val listValue = ListValue(
value.map {
//We already checked that all values are primitives
(it as JsonPrimitive).toValue()
}
)
this[key] = MetaItem.ValueItem(listValue)
}
else -> value.forEachIndexed { index, jsonElement ->
when (jsonElement) {
is JsonObject -> this["$key[$index]"] = MetaItem.NodeItem(JsonMeta(jsonElement))
is JsonPrimitive -> this["$key[$index]"] = MetaItem.ValueItem(jsonElement.toValue())
is JsonArray -> TODO("Nested arrays not supported")
}
}
}
}
}
override val items: Map<NameToken, MetaItem<JsonMeta>> by lazy {
val map = HashMap<String, MetaItem<JsonMeta>>()
json.forEach { (key, value) -> map[key] = value }
map.mapKeys { it.key.toName().first()!! }
}
} }
class JsonMetaFormatFactory : MetaFormatFactory { class JsonMetaFormatFactory : MetaFormatFactory {

View File

@ -21,10 +21,8 @@
*/ */
package hep.dataforge.descriptors package hep.dataforge.descriptors
import hep.dataforge.Named
import hep.dataforge.meta.* import hep.dataforge.meta.*
import hep.dataforge.names.Name import hep.dataforge.names.toName
import java.util.*
/** /**
* Descriptor for meta node. Could contain additional information for viewing * Descriptor for meta node. Could contain additional information for viewing
@ -32,7 +30,14 @@ import java.util.*
* *
* @author Alexander Nozik * @author Alexander Nozik
*/ */
open class NodeDescriptor(val meta: Meta) : MetaRepr { class NodeDescriptor(override val config: Config) : Specification {
/**
* The name of this node
*
* @return
*/
var name: String by string { error("Anonymous descriptors are not allowed") }
/** /**
* True if multiple children with this nodes name are allowed. Anonymous * True if multiple children with this nodes name are allowed. Anonymous
@ -40,103 +45,47 @@ open class NodeDescriptor(val meta: Meta) : MetaRepr {
* *
* @return * @return
*/ */
val multiple: Boolean by meta.boolean(false) var multiple: Boolean by boolean(false)
/** /**
* True if the node is required * True if the node is required
* *
* @return * @return
*/ */
val required: Boolean by meta.boolean(false) var required: Boolean by boolean(false)
/** /**
* The node description * The node description
* *
* @return * @return
*/ */
open val info: String by meta.string("") var info: String? by string()
/** /**
* A list of tags for this node. Tags used to customize node usage * A list of tags for this node. Tags used to customize node usage
* *
* @return * @return
*/ */
val tags: List<String> by customValue(def = emptyList()) { it.list.map { it.string } } var tags: List<String> by value().map { value ->
value?.list?.map { it.string } ?: emptyList()
/** }
* The name of this node
*
* @return
*/
override val name: String by stringValue(def = meta.name)
/** /**
* The list of value descriptors * The list of value descriptors
*
* @return
*/ */
fun valueDescriptors(): Map<String, ValueDescriptor> { val values: Map<String, ValueDescriptor>
val map = HashMap<String, ValueDescriptor>() get() = config.getAll("value".toName()).entries.associate { (name, node) ->
if (meta.hasMeta("value")) { name to ValueDescriptor.wrap(node.node ?: error("Value descriptor must be a node"))
for (valueNode in meta.getMetaList("value")) {
val vd = ValueDescriptor(valueNode)
map[vd.name] = vd
}
} }
return map
}
/**
* The child node descriptor for given name. Name syntax is supported.
*
* @param name
* @return
*/
fun getNodeDescriptor(name: String): NodeDescriptor? {
return getNodeDescriptor(Name.of(name))
}
fun getNodeDescriptor(name: Name): NodeDescriptor? {
return if (name.length == 1) {
childrenDescriptors()[name.unescaped]
} else {
getNodeDescriptor(name.cutLast())?.getNodeDescriptor(name.last)
}
}
/**
* The value descriptor for given value name. Name syntax is supported.
*
* @param name
* @return
*/
fun getValueDescriptor(name: String): ValueDescriptor? {
return getValueDescriptor(Name.of(name))
}
fun getValueDescriptor(name: Name): ValueDescriptor? {
return if (name.length == 1) {
valueDescriptors()[name.unescaped]
} else {
getNodeDescriptor(name.cutLast())?.getValueDescriptor(name.last)
}
}
/** /**
* The map of children node descriptors * The map of children node descriptors
*
* @return
*/ */
fun childrenDescriptors(): Map<String, NodeDescriptor> { val nodes: Map<String, NodeDescriptor>
val map = HashMap<String, NodeDescriptor>() get() = config.getAll("node".toName()).entries.associate { (name, node) ->
if (meta.hasMeta("node")) { name to NodeDescriptor.wrap(node.node ?: error("Node descriptor must be a node"))
for (node in meta.getMetaList("node")) {
val nd = NodeDescriptor(node)
map[nd.name] = nd
}
} }
return map
}
/** /**
* Check if this node has default * Check if this node has default
@ -173,15 +122,14 @@ open class NodeDescriptor(val meta: Meta) : MetaRepr {
*/ */
val key: String by stringValue(def = "") val key: String by stringValue(def = "")
override fun toMeta(): Meta {
return meta
}
fun builder(): DescriptorBuilder = DescriptorBuilder(this.name, Configuration(this.meta)) fun builder(): DescriptorBuilder = DescriptorBuilder(this.name, Configuration(this.meta))
//override val descriptor: NodeDescriptor = empty("descriptor") //override val descriptor: NodeDescriptor = empty("descriptor")
companion object { companion object : SpecificationCompanion<NodeDescriptor> {
override fun wrap(config: Config): NodeDescriptor = NodeDescriptor(config)
fun empty(nodeName: String): NodeDescriptor { fun empty(nodeName: String): NodeDescriptor {
return NodeDescriptor(Meta.buildEmpty(nodeName)) return NodeDescriptor(Meta.buildEmpty(nodeName))

View File

@ -0,0 +1,21 @@
package hep.dataforge.meta
fun Meta.toDynamic(): dynamic {
fun MetaItem<*>.toDynamic(): dynamic = when (this) {
is MetaItem.ValueItem -> this.value.value.asDynamic()
is MetaItem.NodeItem -> this.node.toDynamic()
}
val res = js("{}")
this.items.entries.groupBy { it.key.body }.forEach { (key, value) ->
val list = value.map { it.value }
res[key] = when (list.size) {
1 -> list.first().toDynamic()
else -> list.map { it.toDynamic() }
}
}
return res
}