From f0413464a320d3f2089d3cc30a711aed15249d95 Mon Sep 17 00:00:00 2001 From: Alexander Nozik Date: Sat, 1 Jun 2019 08:14:37 +0300 Subject: [PATCH] Name comparison --- .../hep/dataforge/descriptors/Described.kt | 23 ++++++- .../dataforge/descriptors/NodeDescriptor.kt | 2 + .../hep/dataforge/meta/MetaTransformation.kt | 67 ++++++++++++++++--- .../kotlin/hep/dataforge/names/Name.kt | 12 +++- .../kotlin/hep/dataforge/names/NameTest.kt | 13 +++- 5 files changed, 102 insertions(+), 15 deletions(-) diff --git a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/descriptors/Described.kt b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/descriptors/Described.kt index 0ae7a13c..f355c828 100644 --- a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/descriptors/Described.kt +++ b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/descriptors/Described.kt @@ -1,8 +1,29 @@ package hep.dataforge.descriptors +import hep.dataforge.descriptors.Described.Companion.DESCRIPTOR_NODE +import hep.dataforge.meta.Meta +import hep.dataforge.meta.get +import hep.dataforge.meta.node + /** * An object which provides its descriptor */ interface Described { val descriptor: NodeDescriptor -} \ No newline at end of file + + companion object { + const val DESCRIPTOR_NODE = "@descriptor" + } +} + +/** + * If meta node supplies explicit descriptor, return it, otherwise try to use descriptor node from meta itself + */ +val Meta.descriptor: NodeDescriptor? + get() { + return if (this is Described) { + descriptor + } else { + get(DESCRIPTOR_NODE).node?.let { NodeDescriptor.wrap(it) } + } + } \ No newline at end of file diff --git a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/descriptors/NodeDescriptor.kt b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/descriptors/NodeDescriptor.kt index df748acf..a9f73af5 100644 --- a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/descriptors/NodeDescriptor.kt +++ b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/descriptors/NodeDescriptor.kt @@ -126,5 +126,7 @@ class NodeDescriptor(override val config: Config) : Specific { const val VALUE_KEY = "value" override fun wrap(config: Config): NodeDescriptor = NodeDescriptor(config) + + //TODO infer descriptor from spec } } diff --git a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/MetaTransformation.kt b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/MetaTransformation.kt index 22a294b1..ab1a95fa 100644 --- a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/MetaTransformation.kt +++ b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/MetaTransformation.kt @@ -1,6 +1,7 @@ package hep.dataforge.meta import hep.dataforge.names.Name +import hep.dataforge.names.startsWith /** * A transformation for meta item or a group of items @@ -27,17 +28,18 @@ interface TransformationRule { } /** - * A transformation which transforms an element with given [name] to itself. + * A transformation which keeps all elements, matching [selector] unchanged. */ -data class SelfTransformationRule(val name: Name) : TransformationRule { +data class KeepTransformationRule(val selector: (Name) -> Boolean) : TransformationRule { override fun matches(name: Name, item: MetaItem<*>?): Boolean { - return name == name + return selector(name) } - override fun selectItems(meta: Meta): Sequence = sequenceOf(name) + override fun selectItems(meta: Meta): Sequence = + meta.sequence().map { it.first }.filter(selector) override fun > transformItem(name: Name, item: MetaItem<*>?, target: M) { - if (name == this.name) target[name] = item + if (selector(name)) target[name] = item } } @@ -46,8 +48,7 @@ data class SelfTransformationRule(val name: Name) : TransformationRule { */ data class SingleItemTransformationRule( val from: Name, - val to: Name, - val transform: MutableMeta<*>.(MetaItem<*>?) -> Unit + val transform: MutableMeta<*>.(Name, MetaItem<*>?) -> Unit ) : TransformationRule { override fun matches(name: Name, item: MetaItem<*>?): Boolean { return name == from @@ -57,14 +58,29 @@ data class SingleItemTransformationRule( override fun > transformItem(name: Name, item: MetaItem<*>?, target: M) { if (name == this.from) { - target.transform(item) + target.transform(name, item) } } } -class MetaTransformation { - private val transformations = HashSet() +data class RegexpItemTransformationRule( + val from: Regex, + val transform: MutableMeta<*>.(MatchResult, MetaItem<*>?) -> Unit +) : TransformationRule { + override fun matches(name: Name, item: MetaItem<*>?): Boolean { + return from.matches(name.toString()) + } + override fun > transformItem(name: Name, item: MetaItem<*>?, target: M) { + val match = from.matchEntire(name.toString()) + if (match != null) { + target.transform(match, item) + } + } + +} + +inline class MetaTransformation(val transformations: Collection) { /** * Produce new meta using only those items that match transformation rules @@ -89,7 +105,36 @@ class MetaTransformation { } } - companion object{ + /** + * Listens for changes in the source node and translates them into second node if transformation set contains a corresponding rule. + */ + fun > bind(source: MutableMeta<*>, target: M) { + source.onChange(target) { name, oldItem, newItem -> + transformations.forEach { t -> + if (t.matches(name, newItem)) { + t.transformItem(name, newItem, target) + } + } + } + } + + companion object { } +} + +class MetaTransformationBuilder { + val transformations = HashSet() + + fun keep(selector: (Name) -> Boolean) { + transformations.add(KeepTransformationRule(selector)) + } + + fun keep(name: Name) { + keep{it == name} + } + + fun keepNode(name: Name){ + keep{it.startsWith(name)} + } } \ No newline at end of file diff --git a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/names/Name.kt b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/names/Name.kt index d8513e9c..b0aa8a7d 100644 --- a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/names/Name.kt +++ b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/names/Name.kt @@ -122,4 +122,14 @@ fun Name.withIndex(index: String): Name { } operator fun Map.get(name: String) = get(name.toName()) -operator fun MutableMap.set(name: String, value: T) = set(name.toName(), value) \ No newline at end of file +operator fun MutableMap.set(name: String, value: T) = set(name.toName(), value) + +/* Name comparison operations */ + +fun Name.startsWith(token: NameToken): Boolean = first() == token + +fun Name.endsWith(token: NameToken): Boolean = last() == token + +fun Name.startsWith(name: Name): Boolean = tokens.subList(0, name.length) == name.tokens + +fun Name.endsWith(name: Name): Boolean = tokens.subList(length - name.length, length) == name.tokens \ No newline at end of file diff --git a/dataforge-meta/src/commonTest/kotlin/hep/dataforge/names/NameTest.kt b/dataforge-meta/src/commonTest/kotlin/hep/dataforge/names/NameTest.kt index 1302777e..a3116040 100644 --- a/dataforge-meta/src/commonTest/kotlin/hep/dataforge/names/NameTest.kt +++ b/dataforge-meta/src/commonTest/kotlin/hep/dataforge/names/NameTest.kt @@ -1,7 +1,6 @@ package hep.dataforge.names -import kotlin.test.Test -import kotlin.test.assertEquals +import kotlin.test.* class NameTest { @Test @@ -16,4 +15,14 @@ class NameTest { val name2 = "token1".toName() + "token2[2].token3" assertEquals(name1, name2) } + + @Test + fun comparisonTest(){ + val name1 = "token1.token2.token3".toName() + val name2 = "token1.token2".toName() + val name3 = "token3".toName() + assertTrue { name1.startsWith(name2) } + assertTrue { name1.endsWith(name3) } + assertFalse { name1.startsWith(name3) } + } } \ No newline at end of file