Moved to K1.3.
Removed MultiNodeItems and replaced same name siblings by queries. Laminate merge.
This commit is contained in:
parent
a7a1f776a7
commit
3033cc6304
10
build.gradle
10
build.gradle
@ -1,12 +1,10 @@
|
||||
buildscript {
|
||||
ext.kotlin_version = '1.3.0-rc-190'
|
||||
ext.serialization_version = '0.8.3-rc13'
|
||||
ext.kotlinx_io_version = '0.1.0-alpha-24-rc13'
|
||||
ext.kotlin_version = '1.3.0'
|
||||
ext.serialization_version = '0.9.0'
|
||||
ext.kotlinx_io_version = '0.1.0-beta-1'
|
||||
|
||||
repositories {
|
||||
jcenter()
|
||||
maven {
|
||||
url = "http://dl.bintray.com/kotlin/kotlin-eap"
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
|
@ -2,10 +2,11 @@ plugins {
|
||||
id 'kotlin-multiplatform'
|
||||
//id 'kotlinx-serialization'
|
||||
}
|
||||
|
||||
repositories {
|
||||
maven { url = 'http://dl.bintray.com/kotlin/kotlin-eap' }
|
||||
mavenCentral()
|
||||
jcenter()
|
||||
}
|
||||
|
||||
kotlin {
|
||||
targets {
|
||||
fromPreset(presets.jvm, 'jvm')
|
||||
|
@ -54,7 +54,7 @@ object BinaryMetaFormat : MetaFormat {
|
||||
}
|
||||
|
||||
override fun read(input: Input): Meta {
|
||||
return (input.readMetaItem() as MetaItem.SingleNodeItem).node
|
||||
return (input.readMetaItem() as MetaItem.NodeItem).node
|
||||
}
|
||||
|
||||
private fun Output.writeChar(char: Char) = writeByte(char.toByte())
|
||||
@ -115,21 +115,14 @@ object BinaryMetaFormat : MetaFormat {
|
||||
writeChar('M')
|
||||
writeInt(meta.items.size)
|
||||
meta.items.forEach { (key, item) ->
|
||||
writeString(key)
|
||||
writeString(key.toString())
|
||||
when (item) {
|
||||
is MetaItem.ValueItem -> {
|
||||
writeValue(item.value)
|
||||
}
|
||||
is MetaItem.SingleNodeItem -> {
|
||||
is MetaItem.NodeItem -> {
|
||||
writeMeta(item.node)
|
||||
}
|
||||
is MetaItem.MultiNodeItem -> {
|
||||
writeChar('#')
|
||||
writeInt(item.nodes.size)
|
||||
item.nodes.forEach {
|
||||
writeMeta(it)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -165,12 +158,7 @@ object BinaryMetaFormat : MetaFormat {
|
||||
set(name, item)
|
||||
}
|
||||
}
|
||||
MetaItem.SingleNodeItem(meta)
|
||||
}
|
||||
'#' -> {
|
||||
val length = readInt()
|
||||
val nodes = (1..length).map { (readMetaItem() as MetaItem.SingleNodeItem).node }
|
||||
MetaItem.MultiNodeItem(nodes)
|
||||
MetaItem.NodeItem(meta)
|
||||
}
|
||||
else -> error("Unknown serialization key character: $keyChar")
|
||||
}
|
||||
|
@ -3,13 +3,14 @@ package hep.dataforge.meta.io
|
||||
import hep.dataforge.meta.Meta
|
||||
import hep.dataforge.meta.MetaItem
|
||||
import hep.dataforge.meta.Value
|
||||
import hep.dataforge.names.NameToken
|
||||
|
||||
/**
|
||||
* Represent any js object as meta
|
||||
*/
|
||||
class JSMeta(val obj: Any) : Meta {
|
||||
override val items: Map<String, MetaItem<out Meta>>
|
||||
get() = listKeys(obj).associateWith { convert(js("obj[it]")) }
|
||||
override val items: Map<NameToken, MetaItem<out Meta>>
|
||||
get() = listKeys(obj).map { NameToken(it) }.associateWith { convert(js("obj[it]")) }
|
||||
|
||||
private fun listKeys(obj: Any): List<String> = js("Object").keys(obj) as List<String>
|
||||
|
||||
@ -22,15 +23,9 @@ class JSMeta(val obj: Any) : Meta {
|
||||
null, isPrimitive(obj), is Number, is String, is Boolean -> MetaItem.ValueItem<JSMeta>(Value.of(obj))
|
||||
isList(obj) -> {
|
||||
val list = obj as List<*>
|
||||
//if first value is primitive, treat as value
|
||||
if (isPrimitive(list.first())) {
|
||||
MetaItem.ValueItem<JSMeta>(Value.of(list))
|
||||
} else {
|
||||
//else treat as meta list
|
||||
MetaItem.MultiNodeItem(list.map { JSMeta(it!!) })
|
||||
}
|
||||
MetaItem.ValueItem<JSMeta>(Value.of(list))
|
||||
}
|
||||
else -> MetaItem.SingleNodeItem(JSMeta(obj))
|
||||
else -> MetaItem.NodeItem(JSMeta(obj))
|
||||
}
|
||||
}
|
||||
}
|
@ -34,13 +34,8 @@ private fun Meta.toJson(): JsonObject {
|
||||
val builder = JsonObject()
|
||||
items.forEach { name, item ->
|
||||
when (item) {
|
||||
is MetaItem.ValueItem -> builder[name] = item.value.toJson()
|
||||
is MetaItem.SingleNodeItem -> builder[name] = item.node.toJson()
|
||||
is MetaItem.MultiNodeItem -> {
|
||||
val array = JsonArray()
|
||||
item.nodes.forEach { array.add(it.toJson()) }
|
||||
builder[name] = array
|
||||
}
|
||||
is MetaItem.ValueItem -> builder[name.toString()] = item.value.toJson()
|
||||
is MetaItem.NodeItem -> builder[name.toString()] = item.node.toJson()
|
||||
}
|
||||
}
|
||||
return builder
|
||||
|
@ -1,10 +1,11 @@
|
||||
plugins {
|
||||
id 'kotlin-multiplatform'
|
||||
}
|
||||
|
||||
repositories {
|
||||
maven { url = 'http://dl.bintray.com/kotlin/kotlin-eap' }
|
||||
mavenCentral()
|
||||
jcenter()
|
||||
}
|
||||
|
||||
kotlin {
|
||||
targets {
|
||||
fromPreset(presets.jvm, 'jvm')
|
||||
|
@ -22,8 +22,7 @@ fun Meta.toConfig(): Config = this as? Config ?: Config().also { builder ->
|
||||
val item = entry.value
|
||||
builder[entry.key] = when (item) {
|
||||
is MetaItem.ValueItem -> MetaItem.ValueItem(item.value)
|
||||
is MetaItem.SingleNodeItem -> MetaItem.SingleNodeItem(item.node.toConfig())
|
||||
is MetaItem.MultiNodeItem -> MetaItem.MultiNodeItem(item.nodes.map { it.toConfig() })
|
||||
is MetaItem.NodeItem -> MetaItem.NodeItem(item.node.toConfig())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,72 @@
|
||||
package hep.dataforge.meta
|
||||
|
||||
import hep.dataforge.names.NameToken
|
||||
|
||||
/**
|
||||
* A meta laminate consisting of multiple immutable meta layers. For mutable front layer, use [StyledConfig].
|
||||
*
|
||||
*
|
||||
*/
|
||||
class Laminate(val layers: List<Meta>) : Meta {
|
||||
|
||||
override val items: Map<NameToken, MetaItem<out Meta>>
|
||||
get() = layers.map { it.items.keys }.flatten().associateWith { key ->
|
||||
layers.asSequence().map { it.items[key] }.filterNotNull().let(replaceRule)
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate sealed meta using [mergeRule]
|
||||
*/
|
||||
fun merge(): SealedMeta {
|
||||
val items = layers.map { it.items.keys }.flatten().associateWith { key ->
|
||||
layers.asSequence().map { it.items[key] }.filterNotNull().merge()
|
||||
}
|
||||
return SealedMeta(items)
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
||||
/**
|
||||
* The default rule which always uses the first found item in sequence alongside with its children.
|
||||
*
|
||||
* TODO add picture
|
||||
*/
|
||||
val replaceRule: (Sequence<MetaItem<*>>) -> MetaItem<SealedMeta> = { it.first().seal() }
|
||||
|
||||
private fun Sequence<MetaItem<*>>.merge(): MetaItem<SealedMeta> {
|
||||
return when {
|
||||
all { it is MetaItem.ValueItem } -> //If all items are values, take first
|
||||
first().seal()
|
||||
all { it is MetaItem.NodeItem } -> {
|
||||
//list nodes in item
|
||||
val nodes = map { it.node }
|
||||
//represent as key->value entries
|
||||
val entries = nodes.flatMap { it.items.entries.asSequence() }
|
||||
//group by keys
|
||||
val groups = entries.groupBy { it.key }
|
||||
// recursively apply the rule
|
||||
val items = groups.mapValues { entry ->
|
||||
entry.value.asSequence().map { it.value }.merge()
|
||||
}
|
||||
MetaItem.NodeItem(SealedMeta(items))
|
||||
|
||||
}
|
||||
else -> map {
|
||||
when (it) {
|
||||
is MetaItem.ValueItem -> MetaItem.NodeItem(buildMeta { Meta.VALUE_KEY to it.value })
|
||||
is MetaItem.NodeItem -> it
|
||||
}
|
||||
}.merge()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* The values a replaced but meta children are joined
|
||||
* TODO add picture
|
||||
*/
|
||||
val mergeRule: (Sequence<MetaItem<*>>) -> MetaItem<SealedMeta> = { it.merge() }
|
||||
}
|
||||
}
|
||||
|
||||
//TODO add custom rules for Laminate merge
|
@ -1,47 +1,57 @@
|
||||
package hep.dataforge.meta
|
||||
|
||||
import hep.dataforge.meta.Meta.Companion.VALUE_KEY
|
||||
import hep.dataforge.meta.MetaItem.NodeItem
|
||||
import hep.dataforge.meta.MetaItem.ValueItem
|
||||
import hep.dataforge.names.Name
|
||||
import hep.dataforge.names.NameToken
|
||||
import hep.dataforge.names.toName
|
||||
|
||||
/**
|
||||
* A member of the meta tree. Could be represented as one of following:
|
||||
* * a value
|
||||
* * a single node
|
||||
* * a list of nodes
|
||||
* * a [ValueItem] (leaf)
|
||||
* * a [NodeItem] (node)
|
||||
*/
|
||||
sealed class MetaItem<M : Meta> {
|
||||
data class ValueItem<M : Meta>(val value: Value) : MetaItem<M>()
|
||||
data class SingleNodeItem<M : Meta>(val node: M) : MetaItem<M>()
|
||||
data class MultiNodeItem<M : Meta>(val nodes: List<M>) : MetaItem<M>()
|
||||
}
|
||||
|
||||
operator fun <M : Meta> List<M>.get(query: String): M? {
|
||||
return if (query.isEmpty()) {
|
||||
first()
|
||||
} else {
|
||||
//TODO add custom key queries
|
||||
get(query.toInt())
|
||||
}
|
||||
data class NodeItem<M : Meta>(val node: M) : MetaItem<M>()
|
||||
}
|
||||
|
||||
/**
|
||||
* Generic meta tree representation. Elements are [MetaItem] objects that could be represented by three different entities:
|
||||
* * [MetaItem.ValueItem] (leaf)
|
||||
* * [MetaItem.SingleNodeItem] single node
|
||||
* * [MetaItem.MultiNodeItem] multi-value node
|
||||
* * [MetaItem.NodeItem] single node
|
||||
*
|
||||
* * Same name siblings are supported via elements with the same [Name] but different queries
|
||||
*/
|
||||
interface Meta {
|
||||
val items: Map<String, MetaItem<out Meta>>
|
||||
}
|
||||
val items: Map<NameToken, MetaItem<out Meta>>
|
||||
|
||||
operator fun Meta.get(name: Name): MetaItem<out Meta>? {
|
||||
return when (name.length) {
|
||||
0 -> error("Can't resolve element from empty name")
|
||||
1 -> items[name.first()!!.body]
|
||||
else -> name.first()!!.let { token -> items[token.body]?.nodes?.get(token.query) }?.get(name.cutFirst())
|
||||
companion object {
|
||||
/**
|
||||
* A key for single value node
|
||||
*/
|
||||
const val VALUE_KEY = "@value"
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Fast [String]-based accessor for item map
|
||||
*/
|
||||
operator fun <T> Map<NameToken, T>.get(body: String, query: String = ""): T? = get(NameToken(body, query))
|
||||
|
||||
operator fun Meta.get(name: Name): MetaItem<out Meta>? {
|
||||
return name.first()?.let { token ->
|
||||
val tail = name.cutFirst()
|
||||
when (tail.length) {
|
||||
0 -> items[token]
|
||||
else -> items[token]?.node?.get(tail)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
operator fun Meta.get(token: NameToken): MetaItem<out Meta>? = items[token]
|
||||
|
||||
//TODO create Java helper for meta operations
|
||||
operator fun Meta.get(key: String): MetaItem<out Meta>? = get(key.toName())
|
||||
|
||||
@ -49,13 +59,15 @@ operator fun Meta.get(key: String): MetaItem<out Meta>? = get(key.toName())
|
||||
* A meta node that ensures that all of its descendants has at least the same type
|
||||
*/
|
||||
abstract class MetaNode<M : MetaNode<M>> : Meta {
|
||||
abstract override val items: Map<String, MetaItem<M>>
|
||||
abstract override val items: Map<NameToken, MetaItem<M>>
|
||||
|
||||
operator fun get(name: Name): MetaItem<M>? {
|
||||
return when (name.length) {
|
||||
0 -> error("Can't resolve element from empty name")
|
||||
1 -> items[name.first()!!.body]
|
||||
else -> name.first()!!.let { token -> items[token.body]?.nodes?.get(token.query) }?.get(name.cutFirst())
|
||||
return name.first()?.let { token ->
|
||||
val tail = name.cutFirst()
|
||||
when (tail.length) {
|
||||
0 -> items[token]
|
||||
else -> items[token]?.node?.get(tail)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -78,34 +90,19 @@ abstract class MetaNode<M : MetaNode<M>> : Meta {
|
||||
*
|
||||
* If the argument is possibly mutable node, it is copied on creation
|
||||
*/
|
||||
class SealedMeta internal constructor(override val items: Map<String, MetaItem<SealedMeta>>) : MetaNode<SealedMeta>() {
|
||||
|
||||
companion object {
|
||||
fun seal(meta: Meta): SealedMeta {
|
||||
val items = if (meta is SealedMeta) {
|
||||
meta.items
|
||||
} else {
|
||||
meta.items.mapValues { entry ->
|
||||
val item = entry.value
|
||||
when (item) {
|
||||
is MetaItem.ValueItem -> MetaItem.ValueItem(item.value)
|
||||
is MetaItem.SingleNodeItem -> MetaItem.SingleNodeItem(seal(item.node))
|
||||
is MetaItem.MultiNodeItem -> MetaItem.MultiNodeItem(item.nodes.map { seal(it) })
|
||||
}
|
||||
}
|
||||
}
|
||||
return SealedMeta(items)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class SealedMeta internal constructor(override val items: Map<NameToken, MetaItem<SealedMeta>>) : MetaNode<SealedMeta>()
|
||||
/**
|
||||
* Generate sealed node from [this]. If it is already sealed return it as is
|
||||
*/
|
||||
fun Meta.seal(): SealedMeta = this as? SealedMeta ?: SealedMeta.seal(this)
|
||||
fun Meta.seal(): SealedMeta = this as? SealedMeta ?: SealedMeta(items.mapValues { entry -> entry.value.seal() })
|
||||
|
||||
fun MetaItem<*>.seal(): MetaItem<SealedMeta> = when (this) {
|
||||
is MetaItem.ValueItem -> MetaItem.ValueItem(value)
|
||||
is MetaItem.NodeItem -> MetaItem.NodeItem(node.seal())
|
||||
}
|
||||
|
||||
object EmptyMeta : Meta {
|
||||
override val items: Map<String, MetaItem<out Meta>> = emptyMap()
|
||||
override val items: Map<NameToken, MetaItem<out Meta>> = emptyMap()
|
||||
}
|
||||
|
||||
/**
|
||||
@ -113,40 +110,23 @@ object EmptyMeta : Meta {
|
||||
*/
|
||||
|
||||
val MetaItem<*>.value
|
||||
get() = (this as? MetaItem.ValueItem)?.value ?: error("Trying to interpret node meta item as value item")
|
||||
get() = (this as? MetaItem.ValueItem)?.value
|
||||
?: (this.node[VALUE_KEY] as? MetaItem.ValueItem)?.value
|
||||
?: error("Trying to interpret node meta item as value item")
|
||||
val MetaItem<*>.string get() = value.string
|
||||
val MetaItem<*>.boolean get() = value.boolean
|
||||
val MetaItem<*>.number get() = value.number
|
||||
val MetaItem<*>.double get() = number.toDouble()
|
||||
val MetaItem<*>.int get() = number.toInt()
|
||||
val MetaItem<*>.long get() = number.toLong()
|
||||
val MetaItem<*>.short get() = number.toShort()
|
||||
|
||||
val <M : Meta> MetaItem<M>.node: M
|
||||
get() = when (this) {
|
||||
is MetaItem.ValueItem -> error("Trying to interpret value meta item as node item")
|
||||
is MetaItem.SingleNodeItem -> node
|
||||
is MetaItem.MultiNodeItem -> nodes.first()
|
||||
is MetaItem.NodeItem -> node
|
||||
}
|
||||
|
||||
/**
|
||||
* Utility method to access item content as list of nodes.
|
||||
* Returns empty list if it is value item.
|
||||
*/
|
||||
val <M : Meta> MetaItem<M>.nodes: List<M>
|
||||
get() = when (this) {
|
||||
is MetaItem.ValueItem -> emptyList()//error("Trying to interpret value meta item as node item")
|
||||
is MetaItem.SingleNodeItem -> listOf(node)
|
||||
is MetaItem.MultiNodeItem -> nodes
|
||||
}
|
||||
|
||||
fun <M : Meta> MetaItem<M>.indexOf(meta: M): Int {
|
||||
return when (this) {
|
||||
is MetaItem.ValueItem -> -1
|
||||
is MetaItem.SingleNodeItem -> if (node == meta) 0 else -1
|
||||
is MetaItem.MultiNodeItem -> nodes.indexOf(meta)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Generic meta-holder object
|
||||
*/
|
||||
|
@ -37,9 +37,8 @@ fun Meta.builder(): MetaBuilder {
|
||||
items.mapValues { entry ->
|
||||
val item = entry.value
|
||||
builder[entry.key] = when (item) {
|
||||
is MetaItem.ValueItem -> MetaItem.ValueItem(item.value)
|
||||
is MetaItem.SingleNodeItem -> MetaItem.SingleNodeItem(item.node.builder())
|
||||
is MetaItem.MultiNodeItem -> MetaItem.MultiNodeItem(item.nodes.map { it.builder() })
|
||||
is MetaItem.ValueItem -> MetaItem.ValueItem<MetaBuilder>(item.value)
|
||||
is MetaItem.NodeItem -> MetaItem.NodeItem(item.node.builder())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,7 @@
|
||||
package hep.dataforge.meta
|
||||
|
||||
import hep.dataforge.names.Name
|
||||
import hep.dataforge.names.NameToken
|
||||
import hep.dataforge.names.plus
|
||||
import hep.dataforge.names.toName
|
||||
|
||||
@ -10,14 +11,16 @@ class MetaListener(val owner: Any? = null, val action: (name: Name, oldItem: Met
|
||||
|
||||
|
||||
interface MutableMeta<M : MutableMeta<M>> : Meta {
|
||||
override val items: Map<String, MetaItem<M>>
|
||||
override val items: Map<NameToken, MetaItem<M>>
|
||||
operator fun set(name: Name, item: MetaItem<M>?)
|
||||
fun onChange(owner: Any? = null, action: (Name, MetaItem<*>?, MetaItem<*>?) -> Unit)
|
||||
fun removeListener(owner: Any)
|
||||
}
|
||||
|
||||
/**
|
||||
* A mutable meta node with attachable change listener
|
||||
* A mutable meta node with attachable change listener.
|
||||
*
|
||||
* Changes in Meta are not thread safe.
|
||||
*/
|
||||
abstract class MutableMetaNode<M : MutableMetaNode<M>> : MetaNode<M>(), MutableMeta<M> {
|
||||
private val listeners = HashSet<MetaListener>()
|
||||
@ -36,26 +39,24 @@ abstract class MutableMetaNode<M : MutableMetaNode<M>> : MetaNode<M>(), MutableM
|
||||
listeners.removeAll { it.owner === owner }
|
||||
}
|
||||
|
||||
private val _items: MutableMap<String, MetaItem<M>> = HashMap()
|
||||
private val _items: MutableMap<NameToken, MetaItem<M>> = HashMap()
|
||||
|
||||
override val items: Map<String, MetaItem<M>>
|
||||
override val items: Map<NameToken, MetaItem<M>>
|
||||
get() = _items
|
||||
|
||||
protected fun itemChanged(name: Name, oldItem: MetaItem<*>?, newItem: MetaItem<*>?) {
|
||||
listeners.forEach { it(name, oldItem, newItem) }
|
||||
}
|
||||
|
||||
protected open fun replaceItem(key: String, oldItem: MetaItem<M>?, newItem: MetaItem<M>?) {
|
||||
protected open fun replaceItem(key: NameToken, oldItem: MetaItem<M>?, newItem: MetaItem<M>?) {
|
||||
if (newItem == null) {
|
||||
_items.remove(key)
|
||||
oldItem?.nodes?.forEach {
|
||||
it.removeListener(this)
|
||||
}
|
||||
oldItem?.node?.removeListener(this)
|
||||
} else {
|
||||
_items[key] = newItem
|
||||
newItem.nodes.forEach {
|
||||
it.onChange(this) { name, oldItem, newItem ->
|
||||
itemChanged(key.toName() + name, oldItem, newItem)
|
||||
if(newItem is MetaItem.NodeItem) {
|
||||
newItem.node.onChange(this) { name, oldChild, newChild ->
|
||||
itemChanged(key + name, oldChild, newChild)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -79,14 +80,13 @@ abstract class MutableMetaNode<M : MutableMetaNode<M>> : MetaNode<M>(), MutableM
|
||||
0 -> error("Can't set meta item for empty name")
|
||||
1 -> {
|
||||
val token = name.first()!!
|
||||
if (token.hasQuery()) TODO("Queries are not supported in set operations on meta")
|
||||
replaceItem(token.body, get(name), item)
|
||||
replaceItem(token, get(name), item)
|
||||
}
|
||||
else -> {
|
||||
val token = name.first()!!
|
||||
//get existing or create new node. Query is ignored for new node
|
||||
val child = this.items[token.body]?.nodes?.get(token.query)
|
||||
?: empty().also { this[token.body.toName()] = MetaItem.SingleNodeItem(it) }
|
||||
val child = this.items[token]?.node
|
||||
?: empty().also { this[token.body.toName()] = MetaItem.NodeItem(it) }
|
||||
child[name.cutFirst()] = item
|
||||
}
|
||||
}
|
||||
@ -99,13 +99,11 @@ fun <M : MutableMeta<M>> M.remove(name: Name) = set(name, null)
|
||||
fun <M : MutableMeta<M>> M.remove(name: String) = remove(name.toName())
|
||||
|
||||
operator fun <M : MutableMeta<M>> M.set(name: Name, value: Value) = set(name, MetaItem.ValueItem(value))
|
||||
operator fun <M : MutableMetaNode<M>> M.set(name: Name, meta: Meta) = set(name, MetaItem.SingleNodeItem(wrap(name, meta)))
|
||||
operator fun <M : MutableMetaNode<M>> M.set(name: Name, metas: List<Meta>) = set(name, MetaItem.MultiNodeItem(metas.map { wrap(name, it) }))
|
||||
|
||||
operator fun <M : MutableMetaNode<M>> M.set(name: Name, meta: Meta) = set(name, MetaItem.NodeItem(wrap(name, meta)))
|
||||
operator fun <M : MutableMeta<M>> M.set(name: String, item: MetaItem<M>) = set(name.toName(), item)
|
||||
operator fun <M : MutableMeta<M>> M.set(name: String, value: Value) = set(name.toName(), MetaItem.ValueItem(value))
|
||||
operator fun <M : MutableMetaNode<M>> M.set(name: String, meta: Meta) = set(name.toName(), meta)
|
||||
operator fun <M : MutableMetaNode<M>> M.set(name: String, metas: List<Meta>) = set(name.toName(), metas)
|
||||
operator fun <M : MutableMeta<M>> M.set(token: NameToken, item: MetaItem<M>?) = set(token.toName(), item)
|
||||
|
||||
|
||||
/**
|
||||
@ -129,19 +127,23 @@ fun <M : MutableMetaNode<M>> M.update(meta: Meta) {
|
||||
meta.items.forEach { entry ->
|
||||
val value = entry.value
|
||||
when (value) {
|
||||
is MetaItem.ValueItem -> this[entry.key] = value.value
|
||||
is MetaItem.SingleNodeItem -> (this[entry.key] as? MetaItem.SingleNodeItem)
|
||||
?.node?.update(value.node) ?: kotlin.run { this[entry.key] = value.node }
|
||||
is MetaItem.MultiNodeItem -> {
|
||||
val existing = this[entry.key]
|
||||
if (existing is MetaItem.MultiNodeItem && existing.nodes.size == value.nodes.size) {
|
||||
existing.nodes.forEachIndexed { index, m ->
|
||||
m.update(value.nodes[index])
|
||||
}
|
||||
} else {
|
||||
this[entry.key] = value.nodes
|
||||
}
|
||||
}
|
||||
is MetaItem.ValueItem -> this[entry.key.toName()] = value.value
|
||||
is MetaItem.NodeItem -> (this[entry.key.toName()] as? MetaItem.NodeItem)?.node?.update(value.node)
|
||||
?: run { this[entry.key.toName()] = value.node }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Same name siblings generation
|
||||
|
||||
fun <M : MutableMetaNode<M>> M.setIndexed(name: Name, metas: Iterable<Meta>, queryFactory: (Int) -> String = { it.toString() }) {
|
||||
val tokens = name.tokens.toMutableList()
|
||||
val last = tokens.last()
|
||||
metas.forEachIndexed { index, meta ->
|
||||
val indexedToken = NameToken(last.body, last.query + queryFactory(index))
|
||||
tokens[tokens.lastIndex] = indexedToken
|
||||
set(Name(tokens), meta)
|
||||
}
|
||||
}
|
||||
|
||||
operator fun <M : MutableMetaNode<M>> M.set(name: Name, metas: Iterable<Meta>) = setIndexed(name, metas)
|
||||
|
@ -4,7 +4,7 @@ package hep.dataforge.meta
|
||||
* Marker interface for specifications
|
||||
*/
|
||||
interface Specification: Configurable{
|
||||
operator fun get(name: String): MetaItem<Config>? = config.get(name)
|
||||
operator fun get(name: String): MetaItem<Config>? = config[name]
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1,6 +1,7 @@
|
||||
package hep.dataforge.meta
|
||||
|
||||
import hep.dataforge.names.Name
|
||||
import hep.dataforge.names.NameToken
|
||||
import hep.dataforge.names.toName
|
||||
|
||||
/**
|
||||
@ -27,12 +28,11 @@ class StyledConfig(val config: Config, style: Meta = EmptyMeta) : Config() {
|
||||
when (item) {
|
||||
null -> config.remove(name)
|
||||
is MetaItem.ValueItem -> config[name] = item.value
|
||||
is MetaItem.SingleNodeItem -> config[name] = item.node
|
||||
is MetaItem.MultiNodeItem -> config[name] = item.nodes
|
||||
is MetaItem.NodeItem -> config[name] = item.node
|
||||
}
|
||||
}
|
||||
|
||||
override val items: Map<String, MetaItem<Config>>
|
||||
override val items: Map<NameToken, MetaItem<Config>>
|
||||
get() = (config.items.keys + style.items.keys).associate { key ->
|
||||
val value = config.items[key]
|
||||
val styleValue = style[key]
|
||||
@ -40,16 +40,12 @@ class StyledConfig(val config: Config, style: Meta = EmptyMeta) : Config() {
|
||||
null -> when (styleValue) {
|
||||
null -> error("Should be unreachable")
|
||||
is MetaItem.ValueItem -> MetaItem.ValueItem(styleValue.value)
|
||||
is MetaItem.SingleNodeItem -> MetaItem.SingleNodeItem<Config>(StyledConfig(config.empty(), styleValue.node))
|
||||
is MetaItem.MultiNodeItem -> MetaItem.MultiNodeItem<Config>(styleValue.nodes.map { StyledConfig(config.empty(), it) })
|
||||
is MetaItem.NodeItem -> MetaItem.NodeItem<Config>(StyledConfig(config.empty(), styleValue.node))
|
||||
}
|
||||
is MetaItem.ValueItem -> MetaItem.ValueItem(value.value)
|
||||
is MetaItem.SingleNodeItem -> MetaItem.SingleNodeItem(
|
||||
is MetaItem.NodeItem -> MetaItem.NodeItem(
|
||||
StyledConfig(value.node, styleValue?.node ?: EmptyMeta)
|
||||
)
|
||||
is MetaItem.MultiNodeItem -> MetaItem.MultiNodeItem(value.nodes.map {
|
||||
StyledConfig(it, styleValue?.node ?: EmptyMeta)
|
||||
})
|
||||
}
|
||||
key to item
|
||||
}
|
||||
|
@ -58,7 +58,7 @@ class Name internal constructor(val tokens: List<NameToken>) {
|
||||
* Following symbols are prohibited in name tokens: `{}.:\`.
|
||||
* A name token could have appendix in square brackets called *query*
|
||||
*/
|
||||
data class NameToken internal constructor(val body: String, val query: String) {
|
||||
data class NameToken(val body: String, val query: String = "") {
|
||||
|
||||
init {
|
||||
if (body.isEmpty()) error("Syntax error: Name token body is empty")
|
||||
@ -108,6 +108,8 @@ fun String.toName(): Name {
|
||||
return Name(tokens.toList())
|
||||
}
|
||||
|
||||
operator fun NameToken.plus(other: Name): Name = Name(listOf(this) + other.tokens)
|
||||
|
||||
operator fun Name.plus(other: Name): Name = Name(this.tokens + other.tokens)
|
||||
|
||||
operator fun Name.plus(other: String): Name = this + other.toName()
|
||||
|
@ -17,7 +17,6 @@ pluginManagement {
|
||||
}
|
||||
|
||||
repositories {
|
||||
maven { url = 'http://dl.bintray.com/kotlin/kotlin-eap' }
|
||||
mavenCentral()
|
||||
maven { url = 'https://plugins.gradle.org/m2/' }
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user