From ce8be7854901190b0d2396078f47f93fb4719326 Mon Sep 17 00:00:00 2001 From: Alexander Nozik Date: Tue, 24 Dec 2019 20:59:14 +0300 Subject: [PATCH] Replace EmptyName by Name.Empty Composite builders for node descriptor --- .../hep/dataforge/io/TaglessEnvelopeFormat.kt | 6 +- .../jvmMain/kotlin/hep/dataforge/io/fileIO.kt | 2 +- .../dataforge/descriptors/ItemDescriptor.kt | 70 +++++++++++++------ .../kotlin/hep/dataforge/meta/Meta.kt | 2 +- .../hep/dataforge/meta/configDelegates.kt | 4 +- .../hep/dataforge/meta/metaDelegates.kt | 4 +- .../kotlin/hep/dataforge/names/Name.kt | 8 +-- .../hep/dataforge/output/OutputManager.kt | 7 +- .../hep/dataforge/workspace/Dependency.kt | 11 +-- .../hep/dataforge/workspace/TaskBuilder.kt | 5 +- .../hep/dataforge/workspace/TaskModel.kt | 11 ++- .../dataforge/workspace/WorkspaceBuilder.kt | 5 +- 12 files changed, 81 insertions(+), 54 deletions(-) diff --git a/dataforge-io/src/commonMain/kotlin/hep/dataforge/io/TaglessEnvelopeFormat.kt b/dataforge-io/src/commonMain/kotlin/hep/dataforge/io/TaglessEnvelopeFormat.kt index 5b22ae8c..0ed5214b 100644 --- a/dataforge-io/src/commonMain/kotlin/hep/dataforge/io/TaglessEnvelopeFormat.kt +++ b/dataforge-io/src/commonMain/kotlin/hep/dataforge/io/TaglessEnvelopeFormat.kt @@ -72,7 +72,7 @@ class TaglessEnvelopeFormat( properties[key] = value } //If can't read line, return envelope without data - if (eof()) return SimpleEnvelope(Meta.empty, null) + if (eof()) return SimpleEnvelope(Meta.EMPTY, null) line = readUtf8Line() } @@ -135,7 +135,7 @@ class TaglessEnvelopeFormat( line = readUtf8Line() offset += line.toUtf8Bytes().size.toUInt() } catch (ex: EOFException) { - return PartialEnvelope(Meta.empty, offset.toUInt(), 0.toULong()) + return PartialEnvelope(Meta.EMPTY, offset.toUInt(), 0.toULong()) } } @@ -155,7 +155,7 @@ class TaglessEnvelopeFormat( } do { - line = readUtf8Line() ?: return PartialEnvelope(Meta.empty, offset.toUInt(), 0.toULong()) + line = readUtf8Line() ?: return PartialEnvelope(Meta.EMPTY, offset.toUInt(), 0.toULong()) offset += line.toUtf8Bytes().size.toUInt() //returning an Envelope without data if end of input is reached } while (!line.startsWith(dataStart)) diff --git a/dataforge-io/src/jvmMain/kotlin/hep/dataforge/io/fileIO.kt b/dataforge-io/src/jvmMain/kotlin/hep/dataforge/io/fileIO.kt index 4e60707f..fb693057 100644 --- a/dataforge-io/src/jvmMain/kotlin/hep/dataforge/io/fileIO.kt +++ b/dataforge-io/src/jvmMain/kotlin/hep/dataforge/io/fileIO.kt @@ -132,7 +132,7 @@ fun IOPlugin.readEnvelopeFile( return formatPeeker(path)?.let { format -> FileEnvelope(path, format) } ?: if (readNonEnvelopes) { // if no format accepts file, read it as binary - SimpleEnvelope(Meta.empty, path.asBinary()) + SimpleEnvelope(Meta.EMPTY, path.asBinary()) } else null } diff --git a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/descriptors/ItemDescriptor.kt b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/descriptors/ItemDescriptor.kt index 79c17e47..3d6e7744 100644 --- a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/descriptors/ItemDescriptor.kt +++ b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/descriptors/ItemDescriptor.kt @@ -1,8 +1,9 @@ package hep.dataforge.descriptors import hep.dataforge.meta.* +import hep.dataforge.names.Name import hep.dataforge.names.NameToken -import hep.dataforge.names.toName +import hep.dataforge.names.asName import hep.dataforge.values.False import hep.dataforge.values.True import hep.dataforge.values.Value @@ -36,7 +37,7 @@ sealed class ItemDescriptor(override val config: Config) : Specific { * * @return */ - var attributes by node() + var attributes by child() /** * True if the item is required @@ -66,13 +67,54 @@ class NodeDescriptor(config: Config) : ItemDescriptor(config) { * * @return */ - var default: Config? by node() + var default: Config? by child() + + /** + * The map of children node descriptors + */ + val nodes: Map + get() = config.getIndexed(NODE_KEY.asName()).entries.associate { (name, node) -> + name to wrap(node.node ?: error("Node descriptor must be a node")) + } + + + fun node(name: String, descriptor: NodeDescriptor) { + if (items.keys.contains(name)) error("The key $name already exists in descriptor") + val token = NameToken(NODE_KEY, name) + config[token] = descriptor.config + } + + + fun node(name: String, block: NodeDescriptor.() -> Unit) { + val token = NameToken(NODE_KEY, name) + if (config[token] == null) { + config[token] = NodeDescriptor(block) + } else { + NodeDescriptor.update(config[token].node ?: error("Node expected"), block) + } + } + + private fun buildNode(name: Name): NodeDescriptor { + return when (name.length) { + 0 -> this + 1 -> { + val token = NameToken(NODE_KEY, name.toString()) + val config: Config = config[token].node ?: Config().also { config[token] = it } + wrap(config) + } + else -> buildNode(name.first()?.asName()!!).buildNode(name.cutFirst()) + } + } + + fun node(name: Name, block: NodeDescriptor.() -> Unit) { + buildNode(name).apply(block) + } /** * The list of value descriptors */ val values: Map - get() = config.getIndexed(VALUE_KEY.toName()).entries.associate { (name, node) -> + get() = config.getIndexed(VALUE_KEY.asName()).entries.associate { (name, node) -> name to ValueDescriptor.wrap(node.node ?: error("Value descriptor must be a node")) } @@ -89,23 +131,9 @@ class NodeDescriptor(config: Config) : ItemDescriptor(config) { value(name, ValueDescriptor { this.name = name }.apply(block)) } - /** - * The map of children node descriptors - */ - val nodes: Map - get() = config.getIndexed(NODE_KEY.toName()).entries.associate { (name, node) -> - name to wrap(node.node ?: error("Node descriptor must be a node")) - } - - - fun node(name: String, descriptor: NodeDescriptor) { - if (items.keys.contains(name)) error("The key $name already exists in descriptor") - val token = NameToken(NODE_KEY, name) - config[token] = descriptor.config - } - - fun node(name: String, block: NodeDescriptor.() -> Unit) { - node(name, invoke { this.name = name }.apply(block)) + fun value(name: Name, block: ValueDescriptor.() -> Unit) { + require(name.length >= 1) { "Name length for value descriptor must be non-empty" } + buildNode(name.cutLast()).value(name.last().toString()) } val items: Map get() = nodes + values 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 57364593..7b980137 100644 --- a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/Meta.kt +++ b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/Meta.kt @@ -60,7 +60,7 @@ interface Meta : MetaRepr { */ const val VALUE_KEY = "@value" - val empty: EmptyMeta = EmptyMeta + val EMPTY: EmptyMeta = EmptyMeta } } diff --git a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/configDelegates.kt b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/configDelegates.kt index 34fa0e71..e3dbc9aa 100644 --- a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/configDelegates.kt +++ b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/configDelegates.kt @@ -105,7 +105,7 @@ inline fun > Configurable.enum(default: E, key: Name? = null /* Node delegates */ -fun Configurable.node(key: Name? = null): MutableNodeDelegate = MutableNodeDelegate(config, key) +fun Configurable.child(key: Name? = null): MutableNodeDelegate = MutableNodeDelegate(config, key) fun Configurable.spec(spec: Specification, key: Name? = null) = MutableMorphDelegate(config, key) { spec.wrap(it) } @@ -133,5 +133,5 @@ fun Configurable.doubleArray(key: Name? = null): ReadWriteDelegateWrapper Configurable.node(key: Name? = null, converter: (Meta) -> T) = +fun Configurable.child(key: Name? = null, converter: (Meta) -> T) = MutableMorphDelegate(config, key, converter) diff --git a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/metaDelegates.kt b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/metaDelegates.kt index 65e49da2..0921eae8 100644 --- a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/metaDelegates.kt +++ b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/metaDelegates.kt @@ -144,7 +144,7 @@ fun Meta.boolean(default: Boolean? = null, key: String? = null) = BooleanDelegat fun Meta.number(default: Number? = null, key: String? = null) = NumberDelegate(this, key, default) -fun Meta.node(key: String? = null) = ChildDelegate(this, key) { it } +fun Meta.child(key: String? = null) = ChildDelegate(this, key) { it } @JvmName("safeString") fun Meta.string(default: String, key: String? = null) = @@ -400,7 +400,7 @@ fun > M.boolean(default: Boolean? = null, key: Name? = null) fun > M.number(default: Number? = null, key: Name? = null) = MutableNumberDelegate(this, key, default) -fun > M.node(key: Name? = null) = +fun > M.child(key: Name? = null) = MutableNodeDelegate(this, key) @JvmName("safeString") 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 084f27df..3c8d93d5 100644 --- a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/names/Name.kt +++ b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/names/Name.kt @@ -53,6 +53,8 @@ class Name(val tokens: List) { companion object { const val NAME_SEPARATOR = "." + + val EMPTY = Name(emptyList()) } } @@ -87,7 +89,7 @@ data class NameToken(val body: String, val index: String = "") { * This operation is rather heavy so it should be used with care in high performance code. */ fun String.toName(): Name { - if (isBlank()) return EmptyName + if (isBlank()) return Name.EMPTY val tokens = sequence { var bodyBuilder = StringBuilder() var queryBuilder = StringBuilder() @@ -139,7 +141,7 @@ fun String.toName(): Name { * Convert the [String] to a [Name] by simply wrapping it in a single name token without parsing. * The input string could contain dots and braces, but they are just escaped, not parsed. */ -fun String.asName(): Name = if (isBlank()) EmptyName else NameToken(this).asName() +fun String.asName(): Name = if (isBlank()) Name.EMPTY else NameToken(this).asName() operator fun NameToken.plus(other: Name): Name = Name(listOf(this) + other.tokens) @@ -153,8 +155,6 @@ fun Name.appendLeft(other: String): Name = NameToken(other) + this fun NameToken.asName() = Name(listOf(this)) -val EmptyName = Name(emptyList()) - fun Name.isEmpty(): Boolean = this.length == 0 /** diff --git a/dataforge-output/src/commonMain/kotlin/hep/dataforge/output/OutputManager.kt b/dataforge-output/src/commonMain/kotlin/hep/dataforge/output/OutputManager.kt index c05d5bb4..e88b29a5 100644 --- a/dataforge-output/src/commonMain/kotlin/hep/dataforge/output/OutputManager.kt +++ b/dataforge-output/src/commonMain/kotlin/hep/dataforge/output/OutputManager.kt @@ -4,7 +4,6 @@ import hep.dataforge.context.* import hep.dataforge.context.PluginTag.Companion.DATAFORGE_GROUP import hep.dataforge.meta.EmptyMeta import hep.dataforge.meta.Meta -import hep.dataforge.names.EmptyName import hep.dataforge.names.Name import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.Dispatchers @@ -24,7 +23,7 @@ interface OutputManager { operator fun get( type: KClass, name: Name, - stage: Name = EmptyName, + stage: Name = Name.EMPTY, meta: Meta = EmptyMeta ): Renderer } @@ -39,7 +38,7 @@ val Context.output: OutputManager get() = plugins.get() ?: ConsoleOutputManager( */ inline operator fun OutputManager.get( name: Name, - stage: Name = EmptyName, + stage: Name = Name.EMPTY, meta: Meta = EmptyMeta ): Renderer { return get(T::class, name, stage, meta) @@ -48,7 +47,7 @@ inline operator fun OutputManager.get( /** * Directly render an object using the most suitable renderer */ -fun OutputManager.render(obj: Any, name: Name, stage: Name = EmptyName, meta: Meta = EmptyMeta) = +fun OutputManager.render(obj: Any, name: Name, stage: Name = Name.EMPTY, meta: Meta = EmptyMeta) = get(obj::class, name, stage).render(obj, meta) /** diff --git a/dataforge-workspace/src/commonMain/kotlin/hep/dataforge/workspace/Dependency.kt b/dataforge-workspace/src/commonMain/kotlin/hep/dataforge/workspace/Dependency.kt index ddb53d5d..72402a17 100644 --- a/dataforge-workspace/src/commonMain/kotlin/hep/dataforge/workspace/Dependency.kt +++ b/dataforge-workspace/src/commonMain/kotlin/hep/dataforge/workspace/Dependency.kt @@ -6,7 +6,10 @@ import hep.dataforge.data.filter import hep.dataforge.meta.Meta import hep.dataforge.meta.MetaRepr import hep.dataforge.meta.buildMeta -import hep.dataforge.names.* +import hep.dataforge.names.Name +import hep.dataforge.names.asName +import hep.dataforge.names.isEmpty +import hep.dataforge.names.plus /** * A dependency of the task which allows to lazily create a data tree for single dependency @@ -15,7 +18,7 @@ sealed class Dependency : MetaRepr { abstract fun apply(workspace: Workspace): DataNode } -class DataDependency(val filter: DataFilter, val placement: Name = EmptyName) : Dependency() { +class DataDependency(val filter: DataFilter, val placement: Name = Name.EMPTY) : Dependency() { override fun apply(workspace: Workspace): DataNode { val result = workspace.data.filter(filter) return if (placement.isEmpty()) { @@ -31,7 +34,7 @@ class DataDependency(val filter: DataFilter, val placement: Name = EmptyName) : } } -class AllDataDependency(val placement: Name = EmptyName) : Dependency() { +class AllDataDependency(val placement: Name = Name.EMPTY) : Dependency() { override fun apply(workspace: Workspace): DataNode = if (placement.isEmpty()) { workspace.data } else { @@ -46,7 +49,7 @@ class AllDataDependency(val placement: Name = EmptyName) : Dependency() { abstract class TaskDependency( val meta: Meta, - val placement: Name = EmptyName + val placement: Name = Name.EMPTY ) : Dependency() { abstract fun resolveTask(workspace: Workspace): Task diff --git a/dataforge-workspace/src/commonMain/kotlin/hep/dataforge/workspace/TaskBuilder.kt b/dataforge-workspace/src/commonMain/kotlin/hep/dataforge/workspace/TaskBuilder.kt index 6ae15fa3..87736c01 100644 --- a/dataforge-workspace/src/commonMain/kotlin/hep/dataforge/workspace/TaskBuilder.kt +++ b/dataforge-workspace/src/commonMain/kotlin/hep/dataforge/workspace/TaskBuilder.kt @@ -7,7 +7,6 @@ import hep.dataforge.meta.DFBuilder import hep.dataforge.meta.Meta import hep.dataforge.meta.get import hep.dataforge.meta.string -import hep.dataforge.names.EmptyName import hep.dataforge.names.Name import hep.dataforge.names.isEmpty import hep.dataforge.names.toName @@ -57,7 +56,7 @@ class TaskBuilder(val name: Name, val type: KClass) { block: TaskEnv.(DataNode<*>) -> DataNode ) { dataTransforms += DataTransformation(from, to) { context, model, data -> - val env = TaskEnv(EmptyName, model.meta, context, data) + val env = TaskEnv(Name.EMPTY, model.meta, context, data) env.block(data) } } @@ -70,7 +69,7 @@ class TaskBuilder(val name: Name, val type: KClass) { ) { dataTransforms += DataTransformation(from, to) { context, model, data -> data.ensureType(inputType) - val env = TaskEnv(EmptyName, model.meta, context, data) + val env = TaskEnv(Name.EMPTY, model.meta, context, data) env.block(data.cast(inputType)) } } diff --git a/dataforge-workspace/src/commonMain/kotlin/hep/dataforge/workspace/TaskModel.kt b/dataforge-workspace/src/commonMain/kotlin/hep/dataforge/workspace/TaskModel.kt index 45b07fca..ede9efaa 100644 --- a/dataforge-workspace/src/commonMain/kotlin/hep/dataforge/workspace/TaskModel.kt +++ b/dataforge-workspace/src/commonMain/kotlin/hep/dataforge/workspace/TaskModel.kt @@ -9,7 +9,6 @@ import hep.dataforge.data.DataFilter import hep.dataforge.data.DataTree import hep.dataforge.data.DataTreeBuilder import hep.dataforge.meta.* -import hep.dataforge.names.EmptyName import hep.dataforge.names.Name import hep.dataforge.names.asName import hep.dataforge.names.toName @@ -68,21 +67,21 @@ interface TaskDependencyContainer { */ fun TaskDependencyContainer.dependsOn( name: Name, - placement: Name = EmptyName, + placement: Name = Name.EMPTY, meta: Meta = defaultMeta ): WorkspaceTaskDependency = WorkspaceTaskDependency(name, meta, placement).also { add(it) } fun TaskDependencyContainer.dependsOn( name: String, - placement: Name = EmptyName, + placement: Name = Name.EMPTY, meta: Meta = defaultMeta ): WorkspaceTaskDependency = dependsOn(name.toName(), placement, meta) fun TaskDependencyContainer.dependsOn( task: Task, - placement: Name = EmptyName, + placement: Name = Name.EMPTY, meta: Meta = defaultMeta ): DirectTaskDependency = DirectTaskDependency(task, meta, placement).also { add(it) } @@ -96,7 +95,7 @@ fun TaskDependencyContainer.dependsOn( fun TaskDependencyContainer.dependsOn( task: Task, - placement: Name = EmptyName, + placement: Name = Name.EMPTY, metaBuilder: MetaBuilder.() -> Unit ): DirectTaskDependency = dependsOn(task, placement, buildMeta(metaBuilder)) @@ -120,7 +119,7 @@ fun TaskDependencyContainer.data(pattern: String? = null, from: String? = null, /** * Add all data as root node */ -fun TaskDependencyContainer.allData(to: Name = EmptyName) = AllDataDependency(to).also { add(it) } +fun TaskDependencyContainer.allData(to: Name = Name.EMPTY) = AllDataDependency(to).also { add(it) } /** * A builder for [TaskModel] diff --git a/dataforge-workspace/src/commonMain/kotlin/hep/dataforge/workspace/WorkspaceBuilder.kt b/dataforge-workspace/src/commonMain/kotlin/hep/dataforge/workspace/WorkspaceBuilder.kt index b8f3ffa0..7b831a50 100644 --- a/dataforge-workspace/src/commonMain/kotlin/hep/dataforge/workspace/WorkspaceBuilder.kt +++ b/dataforge-workspace/src/commonMain/kotlin/hep/dataforge/workspace/WorkspaceBuilder.kt @@ -5,7 +5,6 @@ import hep.dataforge.context.ContextBuilder import hep.dataforge.data.DataNode import hep.dataforge.data.DataTreeBuilder import hep.dataforge.meta.* -import hep.dataforge.names.EmptyName import hep.dataforge.names.Name import hep.dataforge.names.isEmpty import hep.dataforge.names.toName @@ -32,7 +31,7 @@ fun WorkspaceBuilder.context(name: String = "WORKSPACE", block: ContextBuilder.( } inline fun WorkspaceBuilder.data( - name: Name = EmptyName, + name: Name = Name.EMPTY, noinline block: DataTreeBuilder.() -> Unit ): DataNode { val node = DataTreeBuilder(T::class).apply(block) @@ -47,7 +46,7 @@ inline fun WorkspaceBuilder.data( @JvmName("rawData") fun WorkspaceBuilder.data( - name: Name = EmptyName, + name: Name = Name.EMPTY, block: DataTreeBuilder.() -> Unit ): DataNode = data(name, block)