Replace EmptyName by Name.Empty

Composite builders for node descriptor
This commit is contained in:
Alexander Nozik 2019-12-24 20:59:14 +03:00
parent 659fded3a5
commit ce8be78549
12 changed files with 81 additions and 54 deletions

View File

@ -72,7 +72,7 @@ class TaglessEnvelopeFormat(
properties[key] = value properties[key] = value
} }
//If can't read line, return envelope without data //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() line = readUtf8Line()
} }
@ -135,7 +135,7 @@ class TaglessEnvelopeFormat(
line = readUtf8Line() line = readUtf8Line()
offset += line.toUtf8Bytes().size.toUInt() offset += line.toUtf8Bytes().size.toUInt()
} catch (ex: EOFException) { } 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 { 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() offset += line.toUtf8Bytes().size.toUInt()
//returning an Envelope without data if end of input is reached //returning an Envelope without data if end of input is reached
} while (!line.startsWith(dataStart)) } while (!line.startsWith(dataStart))

View File

@ -132,7 +132,7 @@ fun IOPlugin.readEnvelopeFile(
return formatPeeker(path)?.let { format -> return formatPeeker(path)?.let { format ->
FileEnvelope(path, format) FileEnvelope(path, format)
} ?: if (readNonEnvelopes) { // if no format accepts file, read it as binary } ?: if (readNonEnvelopes) { // if no format accepts file, read it as binary
SimpleEnvelope(Meta.empty, path.asBinary()) SimpleEnvelope(Meta.EMPTY, path.asBinary())
} else null } else null
} }

View File

@ -1,8 +1,9 @@
package hep.dataforge.descriptors package hep.dataforge.descriptors
import hep.dataforge.meta.* import hep.dataforge.meta.*
import hep.dataforge.names.Name
import hep.dataforge.names.NameToken import hep.dataforge.names.NameToken
import hep.dataforge.names.toName import hep.dataforge.names.asName
import hep.dataforge.values.False import hep.dataforge.values.False
import hep.dataforge.values.True import hep.dataforge.values.True
import hep.dataforge.values.Value import hep.dataforge.values.Value
@ -36,7 +37,7 @@ sealed class ItemDescriptor(override val config: Config) : Specific {
* *
* @return * @return
*/ */
var attributes by node() var attributes by child()
/** /**
* True if the item is required * True if the item is required
@ -66,13 +67,54 @@ class NodeDescriptor(config: Config) : ItemDescriptor(config) {
* *
* @return * @return
*/ */
var default: Config? by node() var default: Config? by child()
/**
* The map of children node descriptors
*/
val nodes: Map<String, NodeDescriptor>
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 * The list of value descriptors
*/ */
val values: Map<String, ValueDescriptor> val values: Map<String, ValueDescriptor>
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")) 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)) value(name, ValueDescriptor { this.name = name }.apply(block))
} }
/** fun value(name: Name, block: ValueDescriptor.() -> Unit) {
* The map of children node descriptors require(name.length >= 1) { "Name length for value descriptor must be non-empty" }
*/ buildNode(name.cutLast()).value(name.last().toString())
val nodes: Map<String, NodeDescriptor>
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))
} }
val items: Map<String, ItemDescriptor> get() = nodes + values val items: Map<String, ItemDescriptor> get() = nodes + values

View File

@ -60,7 +60,7 @@ interface Meta : MetaRepr {
*/ */
const val VALUE_KEY = "@value" const val VALUE_KEY = "@value"
val empty: EmptyMeta = EmptyMeta val EMPTY: EmptyMeta = EmptyMeta
} }
} }

View File

@ -105,7 +105,7 @@ inline fun <reified E : Enum<E>> Configurable.enum(default: E, key: Name? = null
/* Node delegates */ /* Node delegates */
fun Configurable.node(key: Name? = null): MutableNodeDelegate<Config> = MutableNodeDelegate(config, key) fun Configurable.child(key: Name? = null): MutableNodeDelegate<Config> = MutableNodeDelegate(config, key)
fun <T : Specific> Configurable.spec(spec: Specification<T>, key: Name? = null) = fun <T : Specific> Configurable.spec(spec: Specification<T>, key: Name? = null) =
MutableMorphDelegate(config, key) { spec.wrap(it) } MutableMorphDelegate(config, key) { spec.wrap(it) }
@ -133,5 +133,5 @@ fun Configurable.doubleArray(key: Name? = null): ReadWriteDelegateWrapper<Value?
?: doubleArrayOf() ?: doubleArrayOf()
} }
fun <T : Configurable> Configurable.node(key: Name? = null, converter: (Meta) -> T) = fun <T : Configurable> Configurable.child(key: Name? = null, converter: (Meta) -> T) =
MutableMorphDelegate(config, key, converter) MutableMorphDelegate(config, key, converter)

View File

@ -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.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") @JvmName("safeString")
fun Meta.string(default: String, key: String? = null) = fun Meta.string(default: String, key: String? = null) =
@ -400,7 +400,7 @@ fun <M : MutableMeta<M>> M.boolean(default: Boolean? = null, key: Name? = null)
fun <M : MutableMeta<M>> M.number(default: Number? = null, key: Name? = null) = fun <M : MutableMeta<M>> M.number(default: Number? = null, key: Name? = null) =
MutableNumberDelegate(this, key, default) MutableNumberDelegate(this, key, default)
fun <M : MutableMeta<M>> M.node(key: Name? = null) = fun <M : MutableMeta<M>> M.child(key: Name? = null) =
MutableNodeDelegate(this, key) MutableNodeDelegate(this, key)
@JvmName("safeString") @JvmName("safeString")

View File

@ -53,6 +53,8 @@ class Name(val tokens: List<NameToken>) {
companion object { companion object {
const val NAME_SEPARATOR = "." 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. * This operation is rather heavy so it should be used with care in high performance code.
*/ */
fun String.toName(): Name { fun String.toName(): Name {
if (isBlank()) return EmptyName if (isBlank()) return Name.EMPTY
val tokens = sequence { val tokens = sequence {
var bodyBuilder = StringBuilder() var bodyBuilder = StringBuilder()
var queryBuilder = 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. * 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. * 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) 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)) fun NameToken.asName() = Name(listOf(this))
val EmptyName = Name(emptyList())
fun Name.isEmpty(): Boolean = this.length == 0 fun Name.isEmpty(): Boolean = this.length == 0
/** /**

View File

@ -4,7 +4,6 @@ import hep.dataforge.context.*
import hep.dataforge.context.PluginTag.Companion.DATAFORGE_GROUP import hep.dataforge.context.PluginTag.Companion.DATAFORGE_GROUP
import hep.dataforge.meta.EmptyMeta import hep.dataforge.meta.EmptyMeta
import hep.dataforge.meta.Meta import hep.dataforge.meta.Meta
import hep.dataforge.names.EmptyName
import hep.dataforge.names.Name import hep.dataforge.names.Name
import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
@ -24,7 +23,7 @@ interface OutputManager {
operator fun <T : Any> get( operator fun <T : Any> get(
type: KClass<out T>, type: KClass<out T>,
name: Name, name: Name,
stage: Name = EmptyName, stage: Name = Name.EMPTY,
meta: Meta = EmptyMeta meta: Meta = EmptyMeta
): Renderer<T> ): Renderer<T>
} }
@ -39,7 +38,7 @@ val Context.output: OutputManager get() = plugins.get() ?: ConsoleOutputManager(
*/ */
inline operator fun <reified T : Any> OutputManager.get( inline operator fun <reified T : Any> OutputManager.get(
name: Name, name: Name,
stage: Name = EmptyName, stage: Name = Name.EMPTY,
meta: Meta = EmptyMeta meta: Meta = EmptyMeta
): Renderer<T> { ): Renderer<T> {
return get(T::class, name, stage, meta) return get(T::class, name, stage, meta)
@ -48,7 +47,7 @@ inline operator fun <reified T : Any> OutputManager.get(
/** /**
* Directly render an object using the most suitable renderer * 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) get(obj::class, name, stage).render(obj, meta)
/** /**

View File

@ -6,7 +6,10 @@ import hep.dataforge.data.filter
import hep.dataforge.meta.Meta import hep.dataforge.meta.Meta
import hep.dataforge.meta.MetaRepr import hep.dataforge.meta.MetaRepr
import hep.dataforge.meta.buildMeta 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 * 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<Any> abstract fun apply(workspace: Workspace): DataNode<Any>
} }
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<Any> { override fun apply(workspace: Workspace): DataNode<Any> {
val result = workspace.data.filter(filter) val result = workspace.data.filter(filter)
return if (placement.isEmpty()) { 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<Any> = if (placement.isEmpty()) { override fun apply(workspace: Workspace): DataNode<Any> = if (placement.isEmpty()) {
workspace.data workspace.data
} else { } else {
@ -46,7 +49,7 @@ class AllDataDependency(val placement: Name = EmptyName) : Dependency() {
abstract class TaskDependency<out T : Any>( abstract class TaskDependency<out T : Any>(
val meta: Meta, val meta: Meta,
val placement: Name = EmptyName val placement: Name = Name.EMPTY
) : Dependency() { ) : Dependency() {
abstract fun resolveTask(workspace: Workspace): Task<T> abstract fun resolveTask(workspace: Workspace): Task<T>

View File

@ -7,7 +7,6 @@ import hep.dataforge.meta.DFBuilder
import hep.dataforge.meta.Meta import hep.dataforge.meta.Meta
import hep.dataforge.meta.get import hep.dataforge.meta.get
import hep.dataforge.meta.string import hep.dataforge.meta.string
import hep.dataforge.names.EmptyName
import hep.dataforge.names.Name import hep.dataforge.names.Name
import hep.dataforge.names.isEmpty import hep.dataforge.names.isEmpty
import hep.dataforge.names.toName import hep.dataforge.names.toName
@ -57,7 +56,7 @@ class TaskBuilder<R : Any>(val name: Name, val type: KClass<out R>) {
block: TaskEnv.(DataNode<*>) -> DataNode<R> block: TaskEnv.(DataNode<*>) -> DataNode<R>
) { ) {
dataTransforms += DataTransformation(from, to) { context, model, data -> 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) env.block(data)
} }
} }
@ -70,7 +69,7 @@ class TaskBuilder<R : Any>(val name: Name, val type: KClass<out R>) {
) { ) {
dataTransforms += DataTransformation(from, to) { context, model, data -> dataTransforms += DataTransformation(from, to) { context, model, data ->
data.ensureType(inputType) 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)) env.block(data.cast(inputType))
} }
} }

View File

@ -9,7 +9,6 @@ import hep.dataforge.data.DataFilter
import hep.dataforge.data.DataTree import hep.dataforge.data.DataTree
import hep.dataforge.data.DataTreeBuilder import hep.dataforge.data.DataTreeBuilder
import hep.dataforge.meta.* import hep.dataforge.meta.*
import hep.dataforge.names.EmptyName
import hep.dataforge.names.Name import hep.dataforge.names.Name
import hep.dataforge.names.asName import hep.dataforge.names.asName
import hep.dataforge.names.toName import hep.dataforge.names.toName
@ -68,21 +67,21 @@ interface TaskDependencyContainer {
*/ */
fun TaskDependencyContainer.dependsOn( fun TaskDependencyContainer.dependsOn(
name: Name, name: Name,
placement: Name = EmptyName, placement: Name = Name.EMPTY,
meta: Meta = defaultMeta meta: Meta = defaultMeta
): WorkspaceTaskDependency = ): WorkspaceTaskDependency =
WorkspaceTaskDependency(name, meta, placement).also { add(it) } WorkspaceTaskDependency(name, meta, placement).also { add(it) }
fun TaskDependencyContainer.dependsOn( fun TaskDependencyContainer.dependsOn(
name: String, name: String,
placement: Name = EmptyName, placement: Name = Name.EMPTY,
meta: Meta = defaultMeta meta: Meta = defaultMeta
): WorkspaceTaskDependency = ): WorkspaceTaskDependency =
dependsOn(name.toName(), placement, meta) dependsOn(name.toName(), placement, meta)
fun <T : Any> TaskDependencyContainer.dependsOn( fun <T : Any> TaskDependencyContainer.dependsOn(
task: Task<T>, task: Task<T>,
placement: Name = EmptyName, placement: Name = Name.EMPTY,
meta: Meta = defaultMeta meta: Meta = defaultMeta
): DirectTaskDependency<T> = ): DirectTaskDependency<T> =
DirectTaskDependency(task, meta, placement).also { add(it) } DirectTaskDependency(task, meta, placement).also { add(it) }
@ -96,7 +95,7 @@ fun <T : Any> TaskDependencyContainer.dependsOn(
fun <T : Any> TaskDependencyContainer.dependsOn( fun <T : Any> TaskDependencyContainer.dependsOn(
task: Task<T>, task: Task<T>,
placement: Name = EmptyName, placement: Name = Name.EMPTY,
metaBuilder: MetaBuilder.() -> Unit metaBuilder: MetaBuilder.() -> Unit
): DirectTaskDependency<T> = ): DirectTaskDependency<T> =
dependsOn(task, placement, buildMeta(metaBuilder)) dependsOn(task, placement, buildMeta(metaBuilder))
@ -120,7 +119,7 @@ fun TaskDependencyContainer.data(pattern: String? = null, from: String? = null,
/** /**
* Add all data as root node * 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] * A builder for [TaskModel]

View File

@ -5,7 +5,6 @@ import hep.dataforge.context.ContextBuilder
import hep.dataforge.data.DataNode import hep.dataforge.data.DataNode
import hep.dataforge.data.DataTreeBuilder import hep.dataforge.data.DataTreeBuilder
import hep.dataforge.meta.* import hep.dataforge.meta.*
import hep.dataforge.names.EmptyName
import hep.dataforge.names.Name import hep.dataforge.names.Name
import hep.dataforge.names.isEmpty import hep.dataforge.names.isEmpty
import hep.dataforge.names.toName import hep.dataforge.names.toName
@ -32,7 +31,7 @@ fun WorkspaceBuilder.context(name: String = "WORKSPACE", block: ContextBuilder.(
} }
inline fun <reified T : Any> WorkspaceBuilder.data( inline fun <reified T : Any> WorkspaceBuilder.data(
name: Name = EmptyName, name: Name = Name.EMPTY,
noinline block: DataTreeBuilder<T>.() -> Unit noinline block: DataTreeBuilder<T>.() -> Unit
): DataNode<T> { ): DataNode<T> {
val node = DataTreeBuilder(T::class).apply(block) val node = DataTreeBuilder(T::class).apply(block)
@ -47,7 +46,7 @@ inline fun <reified T : Any> WorkspaceBuilder.data(
@JvmName("rawData") @JvmName("rawData")
fun WorkspaceBuilder.data( fun WorkspaceBuilder.data(
name: Name = EmptyName, name: Name = Name.EMPTY,
block: DataTreeBuilder<Any>.() -> Unit block: DataTreeBuilder<Any>.() -> Unit
): DataNode<Any> = data<Any>(name, block) ): DataNode<Any> = data<Any>(name, block)