Optimized type projections in meta. Set operations are now non-generic

This commit is contained in:
Alexander Nozik 2019-06-01 16:04:45 +03:00
parent f0413464a3
commit d08e1ee57d
11 changed files with 117 additions and 98 deletions

View File

@ -90,6 +90,7 @@ object BinaryMetaFormat : MetaFormat {
return readText(max = length) return readText(max = length)
} }
@Suppress("UNCHECKED_CAST")
private fun Input.readMetaItem(): MetaItem<MetaBuilder> { private fun Input.readMetaItem(): MetaItem<MetaBuilder> {
val keyChar = readByte().toChar() val keyChar = readByte().toChar()
return when (keyChar) { return when (keyChar) {
@ -119,6 +120,6 @@ object BinaryMetaFormat : MetaFormat {
MetaItem.NodeItem(meta) MetaItem.NodeItem(meta)
} }
else -> error("Unknown serialization key character: $keyChar") else -> error("Unknown serialization key character: $keyChar")
} } as MetaItem<MetaBuilder>
} }
} }

View File

@ -49,8 +49,7 @@ fun Value.toJson(): JsonElement {
fun Meta.toJson(): JsonObject { fun Meta.toJson(): JsonObject {
val map = this.items.mapValues { entry -> val map = this.items.mapValues { entry ->
val value = entry.value when (val value = entry.value) {
when (value) {
is MetaItem.ValueItem -> value.value.toJson() is MetaItem.ValueItem -> value.value.toJson()
is MetaItem.NodeItem -> value.node.toJson() is MetaItem.NodeItem -> value.node.toJson()
} }
@ -70,8 +69,9 @@ class JsonMeta(val json: JsonObject) : Meta {
} }
} }
@Suppress("UNCHECKED_CAST")
private operator fun MutableMap<String, MetaItem<JsonMeta>>.set(key: String, value: JsonElement) = when (value) { private operator fun MutableMap<String, MetaItem<JsonMeta>>.set(key: String, value: JsonElement) = when (value) {
is JsonPrimitive -> this[key] = MetaItem.ValueItem(value.toValue()) is JsonPrimitive -> this[key] = MetaItem.ValueItem(value.toValue()) as MetaItem<JsonMeta>
is JsonObject -> this[key] = MetaItem.NodeItem(value.toMeta()) is JsonObject -> this[key] = MetaItem.NodeItem(value.toMeta())
is JsonArray -> { is JsonArray -> {
when { when {
@ -82,12 +82,12 @@ class JsonMeta(val json: JsonObject) : Meta {
(it as JsonPrimitive).toValue() (it as JsonPrimitive).toValue()
} }
) )
this[key] = MetaItem.ValueItem(listValue) this[key] = MetaItem.ValueItem(listValue) as MetaItem<JsonMeta>
} }
else -> value.forEachIndexed { index, jsonElement -> else -> value.forEachIndexed { index, jsonElement ->
when (jsonElement) { when (jsonElement) {
is JsonObject -> this["$key[$index]"] = MetaItem.NodeItem(JsonMeta(jsonElement)) is JsonObject -> this["$key[$index]"] = MetaItem.NodeItem(JsonMeta(jsonElement))
is JsonPrimitive -> this["$key[$index]"] = MetaItem.ValueItem(jsonElement.toValue()) is JsonPrimitive -> this["$key[$index]"] = MetaItem.ValueItem(jsonElement.toValue()) as MetaItem<JsonMeta>
is JsonArray -> TODO("Nested arrays not supported") is JsonArray -> TODO("Nested arrays not supported")
} }
} }

View File

@ -1,6 +1,5 @@
package hep.dataforge.meta package hep.dataforge.meta
import hep.dataforge.names.Name
import hep.dataforge.names.NameToken import hep.dataforge.names.NameToken
import hep.dataforge.names.asName import hep.dataforge.names.asName
@ -9,12 +8,12 @@ import hep.dataforge.names.asName
/** /**
* Mutable meta representing object state * Mutable meta representing object state
*/ */
open class Config : AbstractMutableMeta<Config>() { class Config : AbstractMutableMeta<Config>() {
/** /**
* Attach configuration node instead of creating one * Attach configuration node instead of creating one
*/ */
override fun wrap(name: Name, meta: Meta): Config = meta.toConfig() override fun wrapNode(meta: Meta): Config = meta.toConfig()
override fun empty(): Config = Config() override fun empty(): Config = Config()
@ -29,7 +28,7 @@ fun Meta.toConfig(): Config = this as? Config ?: Config().also { builder ->
this.items.mapValues { entry -> this.items.mapValues { entry ->
val item = entry.value val item = entry.value
builder[entry.key.asName()] = when (item) { builder[entry.key.asName()] = when (item) {
is MetaItem.ValueItem -> MetaItem.ValueItem(item.value) is MetaItem.ValueItem -> item.value
is MetaItem.NodeItem -> MetaItem.NodeItem(item.node.toConfig()) is MetaItem.NodeItem -> MetaItem.NodeItem(item.node.toConfig())
} }
} }

View File

@ -14,8 +14,8 @@ import hep.dataforge.values.boolean
* * a [ValueItem] (leaf) * * a [ValueItem] (leaf)
* * a [NodeItem] (node) * * a [NodeItem] (node)
*/ */
sealed class MetaItem<M : Meta> { sealed class MetaItem<out M : Meta> {
data class ValueItem<M : Meta>(val value: Value) : MetaItem<M>() data class ValueItem(val value: Value) : MetaItem<Nothing>()
data class NodeItem<M : Meta>(val node: M) : MetaItem<M>() data class NodeItem<M : Meta>(val node: M) : MetaItem<M>()
} }
@ -35,7 +35,7 @@ interface MetaRepr {
* * Same name siblings are supported via elements with the same [Name] but different queries * * Same name siblings are supported via elements with the same [Name] but different queries
*/ */
interface Meta : MetaRepr { interface Meta : MetaRepr {
val items: Map<NameToken, MetaItem<out Meta>> val items: Map<NameToken, MetaItem<*>>
override fun toMeta(): Meta = this override fun toMeta(): Meta = this
@ -50,12 +50,7 @@ interface Meta : MetaRepr {
/* Get operations*/ /* Get operations*/
/** operator fun Meta?.get(name: Name): MetaItem<*>? {
* 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>? {
if (this == null) return null if (this == null) return null
return name.first()?.let { token -> return name.first()?.let { token ->
val tail = name.cutFirst() val tail = name.cutFirst()
@ -66,13 +61,13 @@ operator fun Meta?.get(name: Name): MetaItem<out Meta>? {
} }
} }
operator fun Meta?.get(token: NameToken): MetaItem<out Meta>? = this?.items?.get(token) operator fun Meta?.get(token: NameToken): MetaItem<*>? = this?.items?.get(token)
operator fun Meta?.get(key: String): MetaItem<out Meta>? = get(key.toName()) operator fun Meta?.get(key: String): MetaItem<*>? = get(key.toName())
/** /**
* Get all items matching given name. * Get all items matching given name.
*/ */
fun Meta.getAll(name: Name): Map<String, MetaItem<out Meta>> { fun Meta.getAll(name: Name): Map<String, MetaItem<*>> {
val root = when (name.length) { val root = when (name.length) {
0 -> error("Can't use empty name for that") 0 -> error("Can't use empty name for that")
1 -> this 1 -> this
@ -88,7 +83,7 @@ fun Meta.getAll(name: Name): Map<String, MetaItem<out Meta>> {
?: emptyMap() ?: emptyMap()
} }
fun Meta.getAll(name: String): Map<String, MetaItem<out Meta>> = getAll(name.toName()) fun Meta.getAll(name: String): Map<String, MetaItem<*>> = getAll(name.toName())
/** /**
* Get a sequence of [Name]-[Value] pairs * Get a sequence of [Name]-[Value] pairs
@ -109,8 +104,8 @@ fun Meta.sequence(): Sequence<Pair<Name, MetaItem<*>>> {
return sequence { return sequence {
items.forEach { (key, item) -> items.forEach { (key, item) ->
yield(key.asName() to item) yield(key.asName() to item)
if(item is NodeItem<*>) { if (item is NodeItem<*>) {
yieldAll(item.node.sequence().map { (innerKey, innerItem)-> yieldAll(item.node.sequence().map { (innerKey, innerItem) ->
(key + innerKey) to innerItem (key + innerKey) to innerItem
}) })
} }
@ -130,7 +125,7 @@ interface MetaNode<M : MetaNode<M>> : Meta {
/** /**
* Get all items matching given name. * Get all items matching given name.
*/ */
fun <M : MetaNode<M>> MetaNode<M>.getAll(name: Name): Map<String, MetaItem<M>> { fun <M : MetaNode<M>> M.getAll(name: Name): Map<String, MetaItem<M>> {
val root: MetaNode<M>? = when (name.length) { val root: MetaNode<M>? = when (name.length) {
0 -> error("Can't use empty name for that") 0 -> error("Can't use empty name for that")
1 -> this 1 -> this
@ -158,7 +153,11 @@ operator fun <M : MetaNode<M>> MetaNode<M>.get(name: Name): MetaItem<M>? {
} }
} }
operator fun <M : MetaNode<M>> MetaNode<M>?.get(key: String): MetaItem<M>? = this?.let { get(key.toName()) } operator fun <M : MetaNode<M>> MetaNode<M>?.get(key: String): MetaItem<M>? = if (this == null) {
null
} else {
this[key.toName()]
}
/** /**
* Equals and hash code implementation for meta node * Equals and hash code implementation for meta node
@ -189,13 +188,14 @@ class SealedMeta internal constructor(override val items: Map<NameToken, MetaIte
*/ */
fun Meta.seal(): SealedMeta = this as? SealedMeta ?: SealedMeta(items.mapValues { entry -> entry.value.seal() }) fun Meta.seal(): SealedMeta = this as? SealedMeta ?: SealedMeta(items.mapValues { entry -> entry.value.seal() })
@Suppress("UNCHECKED_CAST")
fun MetaItem<*>.seal(): MetaItem<SealedMeta> = when (this) { fun MetaItem<*>.seal(): MetaItem<SealedMeta> = when (this) {
is MetaItem.ValueItem -> MetaItem.ValueItem(value) is ValueItem -> this as MetaItem<SealedMeta>
is MetaItem.NodeItem -> MetaItem.NodeItem(node.seal()) is NodeItem -> NodeItem(node.seal())
} }
object EmptyMeta : Meta { object EmptyMeta : Meta {
override val items: Map<NameToken, MetaItem<out Meta>> = emptyMap() override val items: Map<NameToken, MetaItem<*>> = emptyMap()
} }
/** /**
@ -203,8 +203,8 @@ object EmptyMeta : Meta {
*/ */
val MetaItem<*>?.value val MetaItem<*>?.value
get() = (this as? MetaItem.ValueItem)?.value get() = (this as? ValueItem)?.value
?: (this?.node?.get(VALUE_KEY) as? MetaItem.ValueItem)?.value ?: (this?.node?.get(VALUE_KEY) as? ValueItem)?.value
val MetaItem<*>?.string get() = value?.string val MetaItem<*>?.string get() = value?.string
val MetaItem<*>?.boolean get() = value?.boolean val MetaItem<*>?.boolean get() = value?.boolean
@ -226,8 +226,8 @@ val MetaItem<*>?.stringList get() = value?.list?.map { it.string } ?: emptyList(
val <M : Meta> MetaItem<M>?.node: M? val <M : Meta> MetaItem<M>?.node: M?
get() = when (this) { get() = when (this) {
null -> null null -> null
is MetaItem.ValueItem -> error("Trying to interpret value meta item as node item") is ValueItem -> error("Trying to interpret value meta item as node item")
is MetaItem.NodeItem -> node is NodeItem -> node
} }
/** /**

View File

@ -1,6 +1,5 @@
package hep.dataforge.meta package hep.dataforge.meta
import hep.dataforge.names.Name
import hep.dataforge.names.asName import hep.dataforge.names.asName
import hep.dataforge.values.Value import hep.dataforge.values.Value
@ -8,7 +7,7 @@ import hep.dataforge.values.Value
* DSL builder for meta. Is not intended to store mutable state * DSL builder for meta. Is not intended to store mutable state
*/ */
class MetaBuilder : AbstractMutableMeta<MetaBuilder>() { class MetaBuilder : AbstractMutableMeta<MetaBuilder>() {
override fun wrap(name: Name, meta: Meta): MetaBuilder = meta.builder() override fun wrapNode(meta: Meta): MetaBuilder = meta.builder()
override fun empty(): MetaBuilder = MetaBuilder() override fun empty(): MetaBuilder = MetaBuilder()
infix fun String.to(value: Any) { infix fun String.to(value: Any) {
@ -39,7 +38,7 @@ fun Meta.builder(): MetaBuilder {
items.mapValues { entry -> items.mapValues { entry ->
val item = entry.value val item = entry.value
builder[entry.key.asName()] = when (item) { builder[entry.key.asName()] = when (item) {
is MetaItem.ValueItem -> MetaItem.ValueItem<MetaBuilder>(item.value) is MetaItem.ValueItem -> item.value
is MetaItem.NodeItem -> MetaItem.NodeItem(item.node.builder()) is MetaItem.NodeItem -> MetaItem.NodeItem(item.node.builder())
} }
} }

View File

@ -80,6 +80,9 @@ data class RegexpItemTransformationRule(
} }
/**
* A set of [TransformationRule] to either transform static meta or create dynamically updated [MutableMeta]
*/
inline class MetaTransformation(val transformations: Collection<TransformationRule>) { inline class MetaTransformation(val transformations: Collection<TransformationRule>) {
/** /**
@ -109,7 +112,7 @@ inline class MetaTransformation(val transformations: Collection<TransformationRu
* Listens for changes in the source node and translates them into second node if transformation set contains a corresponding rule. * Listens for changes in the source node and translates them into second node if transformation set contains a corresponding rule.
*/ */
fun <M : MutableMeta<M>> bind(source: MutableMeta<*>, target: M) { fun <M : MutableMeta<M>> bind(source: MutableMeta<*>, target: M) {
source.onChange(target) { name, oldItem, newItem -> source.onChange(target) { name, _, newItem ->
transformations.forEach { t -> transformations.forEach { t ->
if (t.matches(name, newItem)) { if (t.matches(name, newItem)) {
t.transformItem(name, newItem, target) t.transformItem(name, newItem, target)
@ -123,18 +126,29 @@ inline class MetaTransformation(val transformations: Collection<TransformationRu
} }
} }
/**
* A builder for a set of transformation rules
*/
class MetaTransformationBuilder { class MetaTransformationBuilder {
val transformations = HashSet<TransformationRule>() val transformations = HashSet<TransformationRule>()
/**
* Keep all items with name satisfying the criteria
*/
fun keep(selector: (Name) -> Boolean) { fun keep(selector: (Name) -> Boolean) {
transformations.add(KeepTransformationRule(selector)) transformations.add(KeepTransformationRule(selector))
} }
/**
* Keep specific item (including its descendants)
*/
fun keep(name: Name) { fun keep(name: Name) {
keep{it == name} keep{it == name}
} }
fun keepNode(name: Name){ fun move(from: Name, to: Name){
keep{it.startsWith(name)} transformations.add(
SingleItemTransformationRule(from){ _, item -> setItem(to, item)}
)
} }
} }

View File

@ -10,15 +10,8 @@ internal data class MetaListener(
interface MutableMeta<M : MutableMeta<M>> : MetaNode<M> { interface MutableMeta<M : MutableMeta<M>> : MetaNode<M> {
/**
* Transform given meta to node type of this meta tree
* @param name the name of the node where meta should be attached. Needed for correct assignment validators and styles
* @param meta the node itself
*/
fun wrap(name: Name, meta: Meta): M
override val items: Map<NameToken, MetaItem<M>> override val items: Map<NameToken, MetaItem<M>>
operator fun set(name: Name, item: MetaItem<M>?) operator fun set(name: Name, item: MetaItem<*>?)
fun onChange(owner: Any? = null, action: (Name, MetaItem<*>?, MetaItem<*>?) -> Unit) fun onChange(owner: Any? = null, action: (Name, MetaItem<*>?, MetaItem<*>?) -> Unit)
fun removeListener(owner: Any? = null) fun removeListener(owner: Any? = null)
} }
@ -69,59 +62,70 @@ abstract class AbstractMutableMeta<M : MutableMeta<M>> : AbstractMetaNode<M>(),
itemChanged(key.asName(), oldItem, newItem) itemChanged(key.asName(), oldItem, newItem)
} }
@Suppress("UNCHECKED_CAST")
protected fun wrapItem(item: MetaItem<*>?): MetaItem<M>? = when (item) {
null -> null
is MetaItem.ValueItem -> item as MetaItem<M>
is MetaItem.NodeItem<*> -> MetaItem.NodeItem(wrapNode(item.node))
}
/**
* Transform given meta to node type of this meta tree
*/
protected abstract fun wrapNode(meta: Meta): M
/** /**
* Create empty node * Create empty node
*/ */
internal abstract fun empty(): M internal abstract fun empty(): M
override operator fun set(name: Name, item: MetaItem<M>?) { override operator fun set(name: Name, item: MetaItem<*>?) {
when (name.length) { when (name.length) {
0 -> error("Can't setValue meta item for empty name") 0 -> error("Can't setValue meta item for empty name")
1 -> { 1 -> {
val token = name.first()!! val token = name.first()!!
replaceItem(token, get(name), item) replaceItem(token, get(name), wrapItem(item))
} }
else -> { else -> {
val token = name.first()!! val token = name.first()!!
//get existing or create new node. Query is ignored for new node //get existing or create new node. Query is ignored for new node
val child = this.items[token]?.node if(items[token] == null){
?: empty().also { this[token.body.toName()] = MetaItem.NodeItem(it) } replaceItem(token,null, MetaItem.NodeItem(empty()))
child[name.cutFirst()] = item }
items[token]?.node!![name.cutFirst()] = item
} }
} }
} }
} }
fun <M : MutableMeta<M>> MutableMeta<M>.remove(name: Name) = set(name, null) fun MutableMeta<*>.remove(name: Name) = set(name, null)
fun <M : MutableMeta<M>> MutableMeta<M>.remove(name: String) = remove(name.toName()) fun MutableMeta<*>.remove(name: String) = remove(name.toName())
fun <M : MutableMeta<M>> MutableMeta<M>.setValue(name: Name, value: Value) = fun MutableMeta<*>.setValue(name: Name, value: Value) =
set(name, MetaItem.ValueItem(value)) set(name, MetaItem.ValueItem(value))
fun <M : MutableMeta<M>> MutableMeta<M>.setValue(name: String, value: Value) = fun MutableMeta<*>.setValue(name: String, value: Value) =
set(name.toName(), MetaItem.ValueItem(value)) set(name.toName(), MetaItem.ValueItem(value))
//fun <M : MutableMeta<M>> MutableMeta<M>.setItem(token: NameToken, item: MetaItem<M>?) = set(token.asName(), item) fun MutableMeta<*>.setItem(name: Name, item: MetaItem<*>?) {
//fun <M : MutableMeta<M>> MutableMeta<M>.setItem(name: String, item: MetaItem<M>) = set(name.toName(), item)
fun <M : MutableMeta<M>> MutableMeta<M>.setItem(name: Name, item: MetaItem<*>) {
when (item) { when (item) {
is MetaItem.ValueItem<*> -> setValue(name, item.value) null -> remove(name)
is MetaItem.ValueItem -> setValue(name, item.value)
is MetaItem.NodeItem<*> -> setNode(name, item.node) is MetaItem.NodeItem<*> -> setNode(name, item.node)
} }
} }
fun <M : MutableMeta<M>> MutableMeta<M>.setItem(name: String, item: MetaItem<*>) = setItem(name.toName(), item) fun MutableMeta<*>.setItem(name: String, item: MetaItem<*>?) = setItem(name.toName(), item)
fun <M : MutableMeta<M>> MutableMeta<M>.setNode(name: Name, node: Meta) = fun MutableMeta<*>.setNode(name: Name, node: Meta) =
set(name, MetaItem.NodeItem(wrap(name, node))) set(name, MetaItem.NodeItem(node))
fun <M : MutableMeta<M>> MutableMeta<M>.setNode(name: String, node: Meta) = setNode(name.toName(), node) fun MutableMeta<*>.setNode(name: String, node: Meta) = setNode(name.toName(), node)
/** /**
* Universal set method * Universal set method
*/ */
operator fun <M : MutableMeta<M>> MutableMeta<M>.set(name: Name, value: Any?) { operator fun MutableMeta<*>.set(name: Name, value: Any?) {
when (value) { when (value) {
null -> remove(name) null -> remove(name)
is MetaItem<*> -> setItem(name, value) is MetaItem<*> -> setItem(name, value)
@ -131,9 +135,9 @@ operator fun <M : MutableMeta<M>> MutableMeta<M>.set(name: Name, value: Any?) {
} }
} }
operator fun <M : MutableMeta<M>> M.set(name: NameToken, value: Any?) = set(name.asName(), value) operator fun MutableMeta<*>.set(name: NameToken, value: Any?) = set(name.asName(), value)
operator fun <M : MutableMeta<M>> M.set(key: String, value: Any?) = set(key.toName(), value) operator fun MutableMeta<*>.set(key: String, value: Any?) = set(key.toName(), value)
/** /**
* Update existing mutable node with another node. The rules are following: * Update existing mutable node with another node. The rules are following:
@ -143,8 +147,7 @@ operator fun <M : MutableMeta<M>> M.set(key: String, value: Any?) = set(key.toNa
*/ */
fun <M : MutableMeta<M>> M.update(meta: Meta) { fun <M : MutableMeta<M>> M.update(meta: Meta) {
meta.items.forEach { entry -> meta.items.forEach { entry ->
val value = entry.value when (val value = entry.value) {
when (value) {
is MetaItem.ValueItem -> setValue(entry.key.asName(), value.value) is MetaItem.ValueItem -> setValue(entry.key.asName(), value.value)
is MetaItem.NodeItem -> (this[entry.key.asName()] as? MetaItem.NodeItem)?.node?.update(value.node) is MetaItem.NodeItem -> (this[entry.key.asName()] as? MetaItem.NodeItem)?.node?.update(value.node)
?: run { setNode(entry.key.asName(), value.node) } ?: run { setNode(entry.key.asName(), value.node) }
@ -154,10 +157,10 @@ fun <M : MutableMeta<M>> M.update(meta: Meta) {
/* Same name siblings generation */ /* Same name siblings generation */
fun <M : MutableMeta<M>> M.setIndexedItems( fun MutableMeta<*>.setIndexedItems(
name: Name, name: Name,
items: Iterable<MetaItem<M>>, items: Iterable<MetaItem<*>>,
indexFactory: MetaItem<M>.(index: Int) -> String = { it.toString() } indexFactory: MetaItem<*>.(index: Int) -> String = { it.toString() }
) { ) {
val tokens = name.tokens.toMutableList() val tokens = name.tokens.toMutableList()
val last = tokens.last() val last = tokens.last()
@ -168,21 +171,21 @@ fun <M : MutableMeta<M>> M.setIndexedItems(
} }
} }
fun <M : MutableMeta<M>> M.setIndexed( fun MutableMeta<*>.setIndexed(
name: Name, name: Name,
metas: Iterable<Meta>, metas: Iterable<Meta>,
indexFactory: MetaItem<M>.(index: Int) -> String = { it.toString() } indexFactory: MetaItem<*>.(index: Int) -> String = { it.toString() }
) { ) {
setIndexedItems(name, metas.map { MetaItem.NodeItem(wrap(name, it)) }, indexFactory) setIndexedItems(name, metas.map { MetaItem.NodeItem(it) }, indexFactory)
} }
operator fun <M : MutableMeta<M>> M.set(name: Name, metas: Iterable<Meta>) = setIndexed(name, metas) operator fun MutableMeta<*>.set(name: Name, metas: Iterable<Meta>): Unit = setIndexed(name, metas)
operator fun <M : MutableMeta<M>> M.set(name: String, metas: Iterable<Meta>) = setIndexed(name.toName(), metas) operator fun MutableMeta<*>.set(name: String, metas: Iterable<Meta>): Unit = setIndexed(name.toName(), metas)
/** /**
* Append the node with a same-name-sibling, automatically generating numerical index * Append the node with a same-name-sibling, automatically generating numerical index
*/ */
fun <M : MutableMeta<M>> M.append(name: Name, value: Any?) { fun MutableMeta<*>.append(name: Name, value: Any?) {
require(!name.isEmpty()) { "Name could not be empty for append operation" } require(!name.isEmpty()) { "Name could not be empty for append operation" }
val newIndex = name.last()!!.index val newIndex = name.last()!!.index
if (newIndex.isNotEmpty()) { if (newIndex.isNotEmpty()) {
@ -193,4 +196,4 @@ fun <M : MutableMeta<M>> M.append(name: Name, value: Any?) {
} }
} }
fun <M : MutableMeta<M>> M.append(name: String, value: Any?) = append(name.toName(), value) fun MutableMeta<*>.append(name: String, value: Any?) = append(name.toName(), value)

View File

@ -12,14 +12,11 @@ import kotlin.reflect.KProperty
* @param style - the style * @param style - the style
*/ */
class Styled(val base: Meta, val style: Config = Config().empty()) : AbstractMutableMeta<Styled>() { class Styled(val base: Meta, val style: Config = Config().empty()) : AbstractMutableMeta<Styled>() {
override fun wrap(name: Name, meta: Meta): Styled { override fun wrapNode(meta: Meta): Styled = Styled(meta)
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
}
override fun empty(): Styled { override fun empty(): Styled = Styled(EmptyMeta)
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
}
@Suppress("UNCHECKED_CAST")
override val items: Map<NameToken, MetaItem<Styled>> override val items: Map<NameToken, MetaItem<Styled>>
get() = (base.items.keys + style.items.keys).associate { key -> get() = (base.items.keys + style.items.keys).associate { key ->
val value = base.items[key] val value = base.items[key]
@ -27,10 +24,10 @@ class Styled(val base: Meta, val style: Config = Config().empty()) : AbstractMut
val item: MetaItem<Styled> = when (value) { val item: MetaItem<Styled> = when (value) {
null -> when (styleValue) { null -> when (styleValue) {
null -> error("Should be unreachable") null -> error("Should be unreachable")
is MetaItem.ValueItem -> MetaItem.ValueItem(styleValue.value)
is MetaItem.NodeItem -> MetaItem.NodeItem(Styled(style.empty(), styleValue.node)) is MetaItem.NodeItem -> MetaItem.NodeItem(Styled(style.empty(), styleValue.node))
else -> styleValue.value as MetaItem<Styled>
} }
is MetaItem.ValueItem -> MetaItem.ValueItem(value.value) is MetaItem.ValueItem -> value as MetaItem<Styled>
is MetaItem.NodeItem -> MetaItem.NodeItem( is MetaItem.NodeItem -> MetaItem.NodeItem(
Styled(value.node, styleValue?.node ?: Config.empty()) Styled(value.node, styleValue?.node ?: Config.empty())
) )
@ -38,7 +35,7 @@ class Styled(val base: Meta, val style: Config = Config().empty()) : AbstractMut
key to item key to item
} }
override fun set(name: Name, item: MetaItem<Styled>?) { override fun set(name: Name, item: MetaItem<*>?) {
if (item == null) { if (item == null) {
style.remove(name) style.remove(name)
} else { } else {

View File

@ -121,6 +121,11 @@ fun Name.withIndex(index: String): Name {
return Name(tokens) return Name(tokens)
} }
/**
* 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 <T> Map<Name, T>.get(name: String) = get(name.toName()) operator fun <T> Map<Name, T>.get(name: String) = get(name.toName())
operator fun <T> MutableMap<Name, T>.set(name: String, value: T) = set(name.toName(), value) operator fun <T> MutableMap<Name, T>.set(name: String, value: T) = set(name.toName(), value)

View File

@ -35,12 +35,13 @@ class DynamicMeta(val obj: dynamic) : Meta {
private fun isArray(@Suppress("UNUSED_PARAMETER") obj: dynamic): Boolean = private fun isArray(@Suppress("UNUSED_PARAMETER") obj: dynamic): Boolean =
js("Array.isArray(obj)") as Boolean js("Array.isArray(obj)") as Boolean
@Suppress("UNCHECKED_CAST")
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) as MetaItem<DynamicMeta>
return when (jsTypeOf(obj)) { return when (jsTypeOf(obj as? Any)) {
"boolean" -> MetaItem.ValueItem(Value.of(obj as Boolean)) "boolean" -> MetaItem.ValueItem(Value.of(obj as Boolean)) as MetaItem<DynamicMeta>
"number" -> MetaItem.ValueItem(Value.of(obj as Number)) "number" -> MetaItem.ValueItem(Value.of(obj as Number)) as MetaItem<DynamicMeta>
"string" -> MetaItem.ValueItem(Value.of(obj as String)) "string" -> MetaItem.ValueItem(Value.of(obj as String)) as MetaItem<DynamicMeta>
"object" -> MetaItem.NodeItem(DynamicMeta(obj)) "object" -> MetaItem.NodeItem(DynamicMeta(obj))
else -> null else -> null
} }

View File

@ -61,13 +61,13 @@ class SimpleWorkspaceTest {
allData() allData()
} }
joinByGroup<Int, Double> { context -> joinByGroup<Int, Double> { context ->
group("even", filter = { name, data -> name.toString().toInt() % 2 == 0 }) { group("even", filter = { name, _ -> name.toString().toInt() % 2 == 0 }) {
result { data -> result { data ->
context.logger.info { "Starting even" } context.logger.info { "Starting even" }
data.values.average() data.values.average()
} }
} }
group("odd", filter = { name, data -> name.toString().toInt() % 2 == 1 }) { group("odd", filter = { name, _ -> name.toString().toInt() % 2 == 1 }) {
result { data -> result { data ->
context.logger.info { "Starting odd" } context.logger.info { "Starting odd" }
data.values.average() data.values.average()