Minor API fixed for Meta

This commit is contained in:
Alexander Nozik 2019-04-02 15:19:52 +03:00
parent 8f5a20bb22
commit 4e679b8971
7 changed files with 64 additions and 21 deletions

View File

@ -69,7 +69,7 @@ open class Context(final override val name: String, val parent: Context? = Globa
override fun listTop(target: String): Sequence<Name> { override fun listTop(target: String): Sequence<Name> {
return when (target) { return when (target) {
Plugin.PLUGIN_TARGET -> plugins.asSequence().map { it.name.toName() } Plugin.PLUGIN_TARGET -> plugins.asSequence().map { it.name.toName() }
Value.TYPE -> properties.asValueSequence().map { it.first } Value.TYPE -> properties.values().map { it.first }
else -> emptySequence() else -> emptySequence()
} }
} }

View File

@ -91,19 +91,19 @@ fun Meta.getAll(name: Name): Map<String, MetaItem<out Meta>> {
fun Meta.getAll(name: String): Map<String, MetaItem<out Meta>> = getAll(name.toName()) fun Meta.getAll(name: String): Map<String, MetaItem<out Meta>> = getAll(name.toName())
/** /**
* Transform meta to sequence of [Name]-[Value] pairs * Get a sequence of [Name]-[Value] pairs
*/ */
fun Meta.asValueSequence(): Sequence<Pair<Name, Value>> { fun Meta.values(): Sequence<Pair<Name, Value>> {
return items.asSequence().flatMap { entry -> return items.asSequence().flatMap { entry ->
val item = entry.value val item = entry.value
when (item) { when (item) {
is ValueItem -> sequenceOf(entry.key.asName() to item.value) is ValueItem -> sequenceOf(entry.key.asName() to item.value)
is NodeItem -> item.node.asValueSequence().map { pair -> (entry.key.asName() + pair.first) to pair.second } is NodeItem -> item.node.values().map { pair -> (entry.key.asName() + pair.first) to pair.second }
} }
} }
} }
operator fun Meta.iterator(): Iterator<Pair<Name, Value>> = asValueSequence().iterator() operator fun Meta.iterator(): Iterator<Pair<Name, Value>> = values().iterator()
/** /**
* A meta node that ensures that all of its descendants has at least the same type * A meta node that ensures that all of its descendants has at least the same type
@ -112,6 +112,27 @@ interface MetaNode<M : MetaNode<M>> : Meta {
override val items: Map<NameToken, MetaItem<M>> override val items: Map<NameToken, MetaItem<M>>
} }
/**
* Get all items matching given name.
*/
fun <M : MetaNode<M>> MetaNode<M>.getAll(name: Name): Map<String, MetaItem<M>> {
val root: MetaNode<M>? = when (name.length) {
0 -> error("Can't use empty name for that")
1 -> this
else -> (this[name.cutLast()] as? NodeItem<M>)?.node
}
val (body, index) = name.last()!!
val regex = index.toRegex()
return root?.items
?.filter { it.key.body == body && (index.isEmpty() || regex.matches(it.key.index)) }
?.mapKeys { it.key.index }
?: emptyMap()
}
fun <M : MetaNode<M>> M.getAll(name: String): Map<String, MetaItem<M>> = getAll(name.toName())
operator fun <M : MetaNode<M>> MetaNode<M>.get(name: Name): MetaItem<M>? { operator fun <M : MetaNode<M>> MetaNode<M>.get(name: Name): MetaItem<M>? {
return name.first()?.let { token -> return name.first()?.let { token ->
val tail = name.cutFirst() val tail = name.cutFirst()
@ -122,7 +143,7 @@ operator fun <M : MetaNode<M>> MetaNode<M>.get(name: Name): MetaItem<M>? {
} }
} }
operator fun <M : MetaNode<M>> MetaNode<M>.get(key: String): MetaItem<M>? = get(key.toName()) operator fun <M : MetaNode<M>> MetaNode<M>?.get(key: String): MetaItem<M>? = this?.let { get(key.toName()) }
/** /**
* Equals and hash code implementation for meta node * Equals and hash code implementation for meta node

View File

@ -1,10 +1,6 @@
package hep.dataforge.meta package hep.dataforge.meta
import hep.dataforge.names.Name import hep.dataforge.names.*
import hep.dataforge.names.NameToken
import hep.dataforge.names.plus
import hep.dataforge.names.toName
import hep.dataforge.names.asName
import hep.dataforge.values.Value import hep.dataforge.values.Value
internal data class MetaListener( internal data class MetaListener(
@ -152,12 +148,12 @@ fun <M : MutableMetaNode<M>> M.update(meta: Meta) {
fun <M : MutableMeta<M>> M.setIndexed( fun <M : MutableMeta<M>> M.setIndexed(
name: Name, name: Name,
items: Iterable<MetaItem<M>>, items: Iterable<MetaItem<M>>,
queryFactory: (Int) -> String = { it.toString() } indexFactory: MetaItem<M>.(index: Int) -> String = { it.toString() }
) { ) {
val tokens = name.tokens.toMutableList() val tokens = name.tokens.toMutableList()
val last = tokens.last() val last = tokens.last()
items.forEachIndexed { index, meta -> items.forEachIndexed { index, meta ->
val indexedToken = NameToken(last.body, last.index + queryFactory(index)) val indexedToken = NameToken(last.body, last.index + meta.indexFactory(index))
tokens[tokens.lastIndex] = indexedToken tokens[tokens.lastIndex] = indexedToken
set(Name(tokens), meta) set(Name(tokens), meta)
} }
@ -166,10 +162,24 @@ fun <M : MutableMeta<M>> M.setIndexed(
fun <M : MutableMetaNode<M>> M.setIndexed( fun <M : MutableMetaNode<M>> M.setIndexed(
name: Name, name: Name,
metas: Iterable<Meta>, metas: Iterable<Meta>,
queryFactory: (Int) -> String = { it.toString() } indexFactory: MetaItem<M>.(index: Int) -> String = { it.toString() }
) { ) {
setIndexed(name, metas.map { MetaItem.NodeItem(wrap(name, it)) }, queryFactory) setIndexed(name, metas.map { MetaItem.NodeItem(wrap(name, it)) }, indexFactory)
} }
operator fun <M : MutableMetaNode<M>> M.set(name: Name, metas: Iterable<Meta>) = setIndexed(name, metas) operator fun <M : MutableMetaNode<M>> M.set(name: Name, metas: Iterable<Meta>) = setIndexed(name, metas)
operator fun <M : MutableMetaNode<M>> M.set(name: String, metas: Iterable<Meta>) = setIndexed(name.toName(), metas) operator fun <M : MutableMetaNode<M>> M.set(name: String, metas: Iterable<Meta>) = setIndexed(name.toName(), metas)
/**
* Append the node with a same-name-sibling, automatically generating numerical index
*/
fun <M : MutableMetaNode<M>> M.append(name: Name, value: Any?) {
require(!name.isEmpty()) { "Name could not be empty for append operation" }
val newIndex = name.last()!!.index
if (newIndex.isNotEmpty()) {
set(name, value)
} else {
val index = (getAll(name).keys.mapNotNull { it.toIntOrNull() }.max() ?: -1) + 1
set(name.withIndex(index.toString()), value)
}
}

View File

@ -106,3 +106,14 @@ fun NameToken.asName() = Name(listOf(this))
val EmptyName = Name(emptyList()) val EmptyName = Name(emptyList())
fun Name.isEmpty(): Boolean = this.length == 0 fun Name.isEmpty(): Boolean = this.length == 0
/**
* Set or replace last token index
*/
fun Name.withIndex(index: String): Name {
val tokens = ArrayList(tokens)
val last = NameToken(tokens.last().body, index)
tokens.removeAt(tokens.size - 1)
tokens.add(last)
return Name(tokens)
}

View File

@ -30,7 +30,7 @@ class MetaBuilderTest {
"b.a[$it]" to it "b.a[$it]" to it
} }
}.seal() }.seal()
assertEquals(10, meta.asValueSequence().count()) assertEquals(10, meta.values().count())
val nodes = meta.getAll("b.a") val nodes = meta.getAll("b.a")

View File

@ -14,7 +14,7 @@ class StyledTest{
} }
} }
}.seal().withStyle() }.seal().withStyle()
assertEquals(10, meta.asValueSequence().count()) assertEquals(10, meta.values().count())
val bNode = meta["b"].node val bNode = meta["b"].node
@ -22,8 +22,8 @@ class StyledTest{
val allNodes = meta.getAll("b.a") val allNodes = meta.getAll("b.a")
assertEquals(3, aNodes?.get("3")?.node["d"]?.int) assertEquals(3, aNodes?.get("3").node["d"].int)
assertEquals(3, allNodes["3"]?.node["d"]?.int) assertEquals(3, allNodes["3"].node["d"].int)
} }
} }

View File

@ -32,7 +32,8 @@ fun Meta.toDynamic(): dynamic {
class DynamicMeta(val obj: dynamic) : Meta { class DynamicMeta(val obj: dynamic) : Meta {
private fun keys() = js("Object.keys(this.obj)") as Array<String> private fun keys() = js("Object.keys(this.obj)") as Array<String>
private fun isArray(obj: dynamic): Boolean = js("Array.isArray(obj)") as Boolean private fun isArray(@Suppress("UNUSED_PARAMETER") obj: dynamic): Boolean =
js("Array.isArray(obj)") as Boolean
private fun asItem(obj: dynamic): MetaItem<DynamicMeta>? { private fun asItem(obj: dynamic): MetaItem<DynamicMeta>? {
if (obj == null) return MetaItem.ValueItem(Null) if (obj == null) return MetaItem.ValueItem(Null)