diff --git a/build.gradle.kts b/build.gradle.kts index e2482330..35e241e4 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -1,4 +1,4 @@ -val dataforgeVersion by extra("0.1.2") +val dataforgeVersion by extra("0.1.3-dev-1") allprojects { repositories { diff --git a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/Meta.kt b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/Meta.kt index e62c3621..329989de 100644 --- a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/Meta.kt +++ b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/Meta.kt @@ -94,16 +94,31 @@ fun Meta.getAll(name: String): Map> = getAll(name.toN * Get a sequence of [Name]-[Value] pairs */ fun Meta.values(): Sequence> { - return items.asSequence().flatMap { entry -> - val item = entry.value + return items.asSequence().flatMap { (key, item) -> when (item) { - is ValueItem -> sequenceOf(entry.key.asName() to item.value) - is NodeItem -> item.node.values().map { pair -> (entry.key.asName() + pair.first) to pair.second } + is ValueItem -> sequenceOf(key.asName() to item.value) + is NodeItem -> item.node.values().map { pair -> (key.asName() + pair.first) to pair.second } } } } -operator fun Meta.iterator(): Iterator> = values().iterator() +/** + * Get a sequence of all [Name]-[MetaItem] pairs for all items including nodes + */ +fun Meta.sequence(): Sequence>> { + return sequence { + items.forEach { (key, item) -> + yield(key.asName() to item) + if(item is NodeItem<*>) { + yieldAll(item.node.sequence().map { (innerKey, innerItem)-> + (key + innerKey) to innerItem + }) + } + } + } +} + +operator fun Meta.iterator(): Iterator>> = sequence().iterator() /** * A meta node that ensures that all of its descendants has at least the same type diff --git a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/MetaBuilder.kt b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/MetaBuilder.kt index fe6829d4..7debc39b 100644 --- a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/MetaBuilder.kt +++ b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/MetaBuilder.kt @@ -46,4 +46,12 @@ fun Meta.builder(): MetaBuilder { } } -fun buildMeta(builder: MetaBuilder.() -> Unit): MetaBuilder = MetaBuilder().apply(builder) \ No newline at end of file +/** + * Build a [MetaBuilder] using given transformation + */ +fun buildMeta(builder: MetaBuilder.() -> Unit): MetaBuilder = MetaBuilder().apply(builder) + +/** + * Build meta using given source meta as a base + */ +fun buildMeta(source: Meta, builder: MetaBuilder.() -> Unit): MetaBuilder = source.builder().apply(builder) \ No newline at end of file diff --git a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/MetaTransformation.kt b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/MetaTransformation.kt new file mode 100644 index 00000000..0b8321d9 --- /dev/null +++ b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/MetaTransformation.kt @@ -0,0 +1,72 @@ +package hep.dataforge.meta + +import hep.dataforge.names.Name + +/** + * A transformation for meta item or a group of items + */ +interface TransformationRule { + + /** + * Check if this transformation + */ + fun matches(name: Name, item: MetaItem<*>?): Boolean + + /** + * Select all items to be transformed. Item could be a value as well as node + * + * @return a sequence of item paths to be transformed + */ + fun selectItems(meta: Meta): Sequence = + meta.sequence().filter { matches(it.first, it.second) }.map { it.first } + + /** + * Apply transformation for a single item (Node or Value) and return resulting tree with absolute path + */ + fun > transformItem(name: Name, item: MetaItem<*>?, target: M): Unit +} + +data class SelfTransformationRule(val name: Name) : TransformationRule { + override fun matches(name: Name, item: MetaItem<*>?): Boolean { + return name == name + } + + override fun selectItems(meta: Meta): Sequence = sequenceOf(name) + + override fun > transformItem(name: Name, item: MetaItem<*>?, target: M) { + if (name == this.name) target[name] = item + } +} + +data class SingleItemTransformationRule( + val from: Name, + val to: Name, + val transform: MutableMetaNode<*>.(MetaItem<*>?) -> Unit +) : TransformationRule { + override fun matches(name: Name, item: MetaItem<*>?): Boolean { + return name == from + } + + override fun selectItems(meta: Meta): Sequence = sequenceOf(from) + + override fun > transformItem(name: Name, item: MetaItem<*>?, target: M) { + if (name == this.from) { + target.transform(item) + } + } +} + +class MetaTransformation { + private val transformations = HashSet() + + /** + * Produce new meta using only those items that match transformation rules + */ + fun produce(source: Meta): Meta = buildMeta { + transformations.forEach { rule -> + rule.selectItems(source).forEach { name -> + rule.transformItem(name, source[name], this) + } + } + } +} \ No newline at end of file diff --git a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/MutableMeta.kt b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/MutableMeta.kt index 8182495b..2b3e9c7a 100644 --- a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/MutableMeta.kt +++ b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/MutableMeta.kt @@ -96,11 +96,20 @@ fun > MutableMeta.remove(name: Name) = set(name, null) fun > MutableMeta.remove(name: String) = remove(name.toName()) fun > MutableMeta.setValue(name: Name, value: Value) = set(name, MetaItem.ValueItem(value)) -fun > MutableMeta.setItem(name: String, item: MetaItem) = set(name.toName(), item) +//fun > MutableMeta.setItem(name: String, item: MetaItem) = set(name.toName(), item) fun > MutableMeta.setValue(name: String, value: Value) = set(name.toName(), MetaItem.ValueItem(value)) -fun > MutableMeta.setItem(token: NameToken, item: MetaItem?) = set(token.asName(), item) +//fun > MutableMeta.setItem(token: NameToken, item: MetaItem?) = set(token.asName(), item) + +fun > MutableMetaNode.setItem(name: Name, item: MetaItem<*>) { + when (item) { + is MetaItem.ValueItem<*> -> setValue(name, item.value) + is MetaItem.NodeItem<*> -> setNode(name, item.node) + } +} + +fun > MutableMetaNode.setItem(name: String, item: MetaItem<*>) = setItem(name.toName(), item) fun > MutableMetaNode.setNode(name: Name, node: Meta) = set(name, MetaItem.NodeItem(wrap(name, node))) @@ -110,13 +119,10 @@ fun > MutableMetaNode.setNode(name: String, node: Meta /** * Universal set method */ -operator fun > M.set(name: Name, value: Any?) { +operator fun > MutableMetaNode.set(name: Name, value: Any?) { when (value) { null -> remove(name) - is MetaItem<*> -> when (value) { - is MetaItem.ValueItem<*> -> setValue(name, value.value) - is MetaItem.NodeItem<*> -> setNode(name, value.node) - } + is MetaItem<*> -> setItem(name, value) is Meta -> setNode(name, value) is Specific -> setNode(name, value.config) else -> setValue(name, Value.of(value))