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
}
//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))

View File

@ -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
}

View File

@ -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<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
*/
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"))
}
@ -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<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))
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<String, ItemDescriptor> get() = nodes + values

View File

@ -60,7 +60,7 @@ interface Meta : MetaRepr {
*/
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 */
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) =
MutableMorphDelegate(config, key) { spec.wrap(it) }
@ -133,5 +133,5 @@ fun Configurable.doubleArray(key: Name? = null): ReadWriteDelegateWrapper<Value?
?: 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)

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.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 : MutableMeta<M>> M.boolean(default: Boolean? = null, key: Name? = null)
fun <M : MutableMeta<M>> M.number(default: Number? = null, key: Name? = null) =
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)
@JvmName("safeString")

View File

@ -53,6 +53,8 @@ class Name(val tokens: List<NameToken>) {
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
/**

View File

@ -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 <T : Any> get(
type: KClass<out T>,
name: Name,
stage: Name = EmptyName,
stage: Name = Name.EMPTY,
meta: Meta = EmptyMeta
): Renderer<T>
}
@ -39,7 +38,7 @@ val Context.output: OutputManager get() = plugins.get() ?: ConsoleOutputManager(
*/
inline operator fun <reified T : Any> OutputManager.get(
name: Name,
stage: Name = EmptyName,
stage: Name = Name.EMPTY,
meta: Meta = EmptyMeta
): Renderer<T> {
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
*/
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)
/**

View File

@ -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<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> {
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<Any> = if (placement.isEmpty()) {
workspace.data
} else {
@ -46,7 +49,7 @@ class AllDataDependency(val placement: Name = EmptyName) : Dependency() {
abstract class TaskDependency<out T : Any>(
val meta: Meta,
val placement: Name = EmptyName
val placement: Name = Name.EMPTY
) : Dependency() {
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.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<R : Any>(val name: Name, val type: KClass<out R>) {
block: TaskEnv.(DataNode<*>) -> DataNode<R>
) {
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<R : Any>(val name: Name, val type: KClass<out R>) {
) {
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))
}
}

View File

@ -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 <T : Any> TaskDependencyContainer.dependsOn(
task: Task<T>,
placement: Name = EmptyName,
placement: Name = Name.EMPTY,
meta: Meta = defaultMeta
): DirectTaskDependency<T> =
DirectTaskDependency(task, meta, placement).also { add(it) }
@ -96,7 +95,7 @@ fun <T : Any> TaskDependencyContainer.dependsOn(
fun <T : Any> TaskDependencyContainer.dependsOn(
task: Task<T>,
placement: Name = EmptyName,
placement: Name = Name.EMPTY,
metaBuilder: MetaBuilder.() -> Unit
): DirectTaskDependency<T> =
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]

View File

@ -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 <reified T : Any> WorkspaceBuilder.data(
name: Name = EmptyName,
name: Name = Name.EMPTY,
noinline block: DataTreeBuilder<T>.() -> Unit
): DataNode<T> {
val node = DataTreeBuilder(T::class).apply(block)
@ -47,7 +46,7 @@ inline fun <reified T : Any> WorkspaceBuilder.data(
@JvmName("rawData")
fun WorkspaceBuilder.data(
name: Name = EmptyName,
name: Name = Name.EMPTY,
block: DataTreeBuilder<Any>.() -> Unit
): DataNode<Any> = data<Any>(name, block)