Fix IO after refactoring

This commit is contained in:
Alexander Nozik 2020-09-08 16:04:15 +03:00
parent 52a3c8bc6f
commit 8a8484172c
18 changed files with 165 additions and 157 deletions

View File

@ -105,11 +105,13 @@ class FrontMatterEnvelopeFormat(
override fun readPartial(input: Input): PartialEnvelope = override fun readPartial(input: Input): PartialEnvelope =
default.readPartial(input) default.readPartial(input)
override fun Output.writeEnvelope(envelope: Envelope, metaFormatFactory: MetaFormatFactory, formatMeta: Meta): Unit = override fun Output.writeEnvelope(
default.run { writeEnvelope(envelope, metaFormatFactory, formatMeta) } envelope: Envelope,
metaFormatFactory: MetaFormatFactory,
formatMeta: Meta,
): Unit = default.run { writeEnvelope(envelope, metaFormatFactory, formatMeta) }
override fun readObject(input: Input): Envelope = override fun readObject(input: Input): Envelope = default.readObject(input)
default.readObject(input)
} }
} }

View File

@ -30,7 +30,7 @@ public interface IOFormat<T : Any> : MetaRepr {
} }
} }
public fun <T : Any> Input.readWith(format: IOFormat<T>): T = format.run { readObject(this@readWith) } public fun <T : Any> Input.readWith(format: IOFormat<T>): T = format.readObject(this@readWith)
/** /**
* Read given binary as object using given format * Read given binary as object using given format

View File

@ -12,7 +12,7 @@ import hep.dataforge.meta.toJson
import hep.dataforge.meta.toMetaItem import hep.dataforge.meta.toMetaItem
import kotlinx.io.Input import kotlinx.io.Input
import kotlinx.io.Output import kotlinx.io.Output
import kotlinx.io.readByteArray import kotlinx.io.text.readUtf8String
import kotlinx.io.text.writeUtf8String import kotlinx.io.text.writeUtf8String
import kotlinx.serialization.json.Json import kotlinx.serialization.json.Json
import kotlinx.serialization.json.JsonObject import kotlinx.serialization.json.JsonObject
@ -24,7 +24,7 @@ public class JsonMetaFormat(private val json: Json = DEFAULT_JSON) : MetaFormat
override fun writeMeta(output: Output, meta: Meta, descriptor: NodeDescriptor?) { override fun writeMeta(output: Output, meta: Meta, descriptor: NodeDescriptor?) {
val jsonObject = meta.toJson(descriptor) val jsonObject = meta.toJson(descriptor)
output.writeUtf8String(this.json.encodeToString(JsonObject.serializer(), jsonObject)) output.writeUtf8String(json.encodeToString(JsonObject.serializer(), jsonObject))
} }
override fun toMeta(): Meta = Meta { override fun toMeta(): Meta = Meta {
@ -32,7 +32,7 @@ public class JsonMetaFormat(private val json: Json = DEFAULT_JSON) : MetaFormat
} }
override fun readMeta(input: Input, descriptor: NodeDescriptor?): Meta { override fun readMeta(input: Input, descriptor: NodeDescriptor?): Meta {
val str = input.readByteArray().decodeToString() val str = input.readUtf8String()//readByteArray().decodeToString()
val jsonElement = json.parseToJsonElement(str) val jsonElement = json.parseToJsonElement(str)
val item = jsonElement.toMetaItem(descriptor) val item = jsonElement.toMetaItem(descriptor)
return item.node ?: Meta.EMPTY return item.node ?: Meta.EMPTY

View File

@ -18,7 +18,7 @@ import kotlinx.io.*
*/ */
public class TaggedEnvelopeFormat( public class TaggedEnvelopeFormat(
public val io: IOPlugin, public val io: IOPlugin,
public val version: VERSION = VERSION.DF02 public val version: VERSION = VERSION.DF02,
) : EnvelopeFormat { ) : EnvelopeFormat {
// private val metaFormat = io.metaFormat(metaFormatKey) // private val metaFormat = io.metaFormat(metaFormatKey)
@ -67,11 +67,7 @@ public class TaggedEnvelopeFormat(
val metaFormat = io.resolveMetaFormat(tag.metaFormatKey) val metaFormat = io.resolveMetaFormat(tag.metaFormatKey)
?: error("Meta format with key ${tag.metaFormatKey} not found") ?: error("Meta format with key ${tag.metaFormatKey} not found")
val meta: Meta = input.limit(tag.metaSize.toInt()).run { val meta: Meta = metaFormat.readObject(input.limit(tag.metaSize.toInt()))
metaFormat.run {
readObject(input)
}
}
val data = input.readBinary(tag.dataSize.toInt()) val data = input.readBinary(tag.dataSize.toInt())
@ -84,11 +80,8 @@ public class TaggedEnvelopeFormat(
val metaFormat = io.resolveMetaFormat(tag.metaFormatKey) val metaFormat = io.resolveMetaFormat(tag.metaFormatKey)
?: error("Meta format with key ${tag.metaFormatKey} not found") ?: error("Meta format with key ${tag.metaFormatKey} not found")
val meta: Meta = input.limit(tag.metaSize.toInt()).run { val meta: Meta = metaFormat.readObject(input.limit(tag.metaSize.toInt()))
metaFormat.run {
readObject(input)
}
}
return PartialEnvelope(meta, version.tagSize + tag.metaSize, tag.dataSize) return PartialEnvelope(meta, version.tagSize + tag.metaSize, tag.dataSize)
} }
@ -96,7 +89,7 @@ public class TaggedEnvelopeFormat(
private data class Tag( private data class Tag(
val metaFormatKey: Short, val metaFormatKey: Short,
val metaSize: UInt, val metaSize: UInt,
val dataSize: ULong val dataSize: ULong,
) )
public enum class VERSION(public val tagSize: UInt) { public enum class VERSION(public val tagSize: UInt) {
@ -165,13 +158,13 @@ public class TaggedEnvelopeFormat(
override fun readPartial(input: Input): PartialEnvelope = override fun readPartial(input: Input): PartialEnvelope =
default.run { readPartial(input) } default.run { readPartial(input) }
override fun Output.writeEnvelope(envelope: Envelope, metaFormatFactory: MetaFormatFactory, formatMeta: Meta): Unit = override fun Output.writeEnvelope(
default.run { writeEnvelope(envelope, metaFormatFactory, formatMeta) } envelope: Envelope,
metaFormatFactory: MetaFormatFactory,
override fun readObject(input: Input): Envelope = formatMeta: Meta,
default.run { readObject(input) } ): Unit = default.run { writeEnvelope(envelope, metaFormatFactory, formatMeta) }
override fun readObject(input: Input): Envelope = default.readObject(input)
} }
} }

View File

@ -20,7 +20,7 @@ import kotlin.collections.set
*/ */
public class TaglessEnvelopeFormat( public class TaglessEnvelopeFormat(
public val io: IOPlugin, public val io: IOPlugin,
public val meta: Meta = Meta.EMPTY public val meta: Meta = Meta.EMPTY,
) : EnvelopeFormat { ) : EnvelopeFormat {
private val metaStart = meta[META_START_PROPERTY].string ?: DEFAULT_META_START private val metaStart = meta[META_START_PROPERTY].string ?: DEFAULT_META_START
@ -85,13 +85,9 @@ public class TaglessEnvelopeFormat(
val metaFormat = properties[META_TYPE_PROPERTY]?.let { io.resolveMetaFormat(it) } ?: JsonMetaFormat val metaFormat = properties[META_TYPE_PROPERTY]?.let { io.resolveMetaFormat(it) } ?: JsonMetaFormat
val metaSize = properties[META_LENGTH_PROPERTY]?.toInt() val metaSize = properties[META_LENGTH_PROPERTY]?.toInt()
meta = if (metaSize != null) { meta = if (metaSize != null) {
input.limit(metaSize).run { metaFormat.readObject(input.limit(metaSize))
metaFormat.run { readObject(input) }
}
} else { } else {
metaFormat.run { metaFormat.readObject(input)
readObject(input)
}
} }
} }
@ -150,9 +146,7 @@ public class TaglessEnvelopeFormat(
val metaSize = properties[META_LENGTH_PROPERTY]?.toInt() val metaSize = properties[META_LENGTH_PROPERTY]?.toInt()
meta = if (metaSize != null) { meta = if (metaSize != null) {
offset += metaSize.toUInt() offset += metaSize.toUInt()
input.limit(metaSize).run { metaFormat.readObject(input.limit(metaSize))
metaFormat.run { readObject(input) }
}
} else { } else {
error("Can't partially read an envelope with undefined meta size") error("Can't partially read an envelope with undefined meta size")
} }
@ -203,11 +197,13 @@ public class TaglessEnvelopeFormat(
override fun readPartial(input: Input): PartialEnvelope = override fun readPartial(input: Input): PartialEnvelope =
default.run { readPartial(input) } default.run { readPartial(input) }
override fun Output.writeEnvelope(envelope: Envelope, metaFormatFactory: MetaFormatFactory, formatMeta: Meta): Unit = override fun Output.writeEnvelope(
default.run { writeEnvelope(envelope, metaFormatFactory, formatMeta) } envelope: Envelope,
metaFormatFactory: MetaFormatFactory,
formatMeta: Meta,
): Unit = default.run { writeEnvelope(envelope, metaFormatFactory, formatMeta) }
override fun readObject(input: Input): Envelope = override fun readObject(input: Input): Envelope = default.readObject(input)
default.run { readObject(input) }
override fun peekFormat(io: IOPlugin, input: Input): EnvelopeFormat? { override fun peekFormat(io: IOPlugin, input: Input): EnvelopeFormat? {
return try { return try {

View File

@ -23,9 +23,9 @@ class EnvelopeFormatTest {
@Test @Test
fun testTaggedFormat(){ fun testTaggedFormat(){
TaggedEnvelopeFormat.run { TaggedEnvelopeFormat.run {
val byteArray = this.toByteArray(envelope) val byteArray = writeToByteArray(envelope)
//println(byteArray.decodeToString()) //println(byteArray.decodeToString())
val res = readByteArray(byteArray) val res = readFromByteArray(byteArray)
assertEquals(envelope.meta,res.meta) assertEquals(envelope.meta,res.meta)
val double = res.data?.read { val double = res.data?.read {
readDouble() readDouble()
@ -37,9 +37,9 @@ class EnvelopeFormatTest {
@Test @Test
fun testTaglessFormat(){ fun testTaglessFormat(){
TaglessEnvelopeFormat.run { TaglessEnvelopeFormat.run {
val byteArray = toByteArray(envelope) val byteArray = writeToByteArray(envelope)
//println(byteArray.decodeToString()) //println(byteArray.decodeToString())
val res = readByteArray(byteArray) val res = readFromByteArray(byteArray)
assertEquals(envelope.meta,res.meta) assertEquals(envelope.meta,res.meta)
val double = res.data?.read { val double = res.data?.read {
readDouble() readDouble()

View File

@ -1,13 +1,19 @@
package hep.dataforge.io package hep.dataforge.io
import hep.dataforge.meta.* import hep.dataforge.meta.Meta
import hep.dataforge.meta.MetaItem
import hep.dataforge.meta.MetaSerializer
import hep.dataforge.names.Name import hep.dataforge.names.Name
import hep.dataforge.names.toName import hep.dataforge.names.toName
import kotlinx.serialization.ExperimentalSerializationApi import kotlinx.serialization.ExperimentalSerializationApi
import kotlinx.serialization.cbor.Cbor import kotlinx.serialization.cbor.Cbor
import kotlinx.serialization.json.Json
import kotlin.test.Test import kotlin.test.Test
import kotlin.test.assertEquals import kotlin.test.assertEquals
val JSON_PRETTY: Json = Json { prettyPrint = true; useArrayPolymorphism = true }
val JSON_PLAIN: Json = Json { prettyPrint = false; useArrayPolymorphism = true }
class MetaSerializerTest { class MetaSerializerTest {
val meta = Meta { val meta = Meta {
"a" put 22 "a" put 22

View File

@ -3,5 +3,5 @@ package hep.dataforge.io
import kotlinx.io.ByteArrayInput import kotlinx.io.ByteArrayInput
import kotlinx.io.use import kotlinx.io.use
fun <T : Any> IOFormat<T>.toByteArray(obj: T): ByteArray = buildByteArray { writeObject(this, obj) } fun <T : Any> IOFormat<T>.writeToByteArray(obj: T): ByteArray = buildByteArray { writeObject(this, obj) }
fun <T : Any> IOFormat<T>.readByteArray(array: ByteArray): T = ByteArrayInput(array).use { readObject(it) } fun <T : Any> IOFormat<T>.readFromByteArray(array: ByteArray): T = ByteArrayInput(array).use { readObject(it) }

View File

@ -66,7 +66,11 @@ public inline fun <reified T : Any> IOPlugin.resolveIOFormat(): IOFormat<T>? {
* Read file containing meta using given [formatOverride] or file extension to infer meta type. * Read file containing meta using given [formatOverride] or file extension to infer meta type.
* If [path] is a directory search for file starting with `meta` in it * If [path] is a directory search for file starting with `meta` in it
*/ */
public fun IOPlugin.readMetaFile(path: Path, formatOverride: MetaFormat? = null, descriptor: NodeDescriptor? = null): Meta { public fun IOPlugin.readMetaFile(
path: Path,
formatOverride: MetaFormat? = null,
descriptor: NodeDescriptor? = null,
): Meta {
if (!Files.exists(path)) error("Meta file $path does not exist") if (!Files.exists(path)) error("Meta file $path does not exist")
val actualPath: Path = if (Files.isDirectory(path)) { val actualPath: Path = if (Files.isDirectory(path)) {
@ -93,7 +97,7 @@ public fun IOPlugin.writeMetaFile(
path: Path, path: Path,
meta: Meta, meta: Meta,
metaFormat: MetaFormatFactory = JsonMetaFormat, metaFormat: MetaFormatFactory = JsonMetaFormat,
descriptor: NodeDescriptor? = null descriptor: NodeDescriptor? = null,
) { ) {
val actualPath = if (Files.isDirectory(path)) { val actualPath = if (Files.isDirectory(path)) {
path.resolve("@" + metaFormat.name.toString()) path.resolve("@" + metaFormat.name.toString())
@ -146,14 +150,15 @@ public val IOPlugin.Companion.DATA_FILE_NAME: String get() = "@data"
public fun IOPlugin.readEnvelopeFile( public fun IOPlugin.readEnvelopeFile(
path: Path, path: Path,
readNonEnvelopes: Boolean = false, readNonEnvelopes: Boolean = false,
formatPeeker: IOPlugin.(Path) -> EnvelopeFormat? = IOPlugin::peekBinaryFormat formatPeeker: IOPlugin.(Path) -> EnvelopeFormat? = IOPlugin::peekBinaryFormat,
): Envelope? { ): Envelope? {
if (!Files.exists(path)) return null if (!Files.exists(path)) return null
//read two-files directory //read two-files directory
if (Files.isDirectory(path)) { if (Files.isDirectory(path)) {
val metaFile = Files.list(path).asSequence() val metaFile = Files.list(path).asSequence().singleOrNull {
.singleOrNull { it.fileName.toString().startsWith(IOPlugin.META_FILE_NAME) } it.fileName.toString().startsWith(IOPlugin.META_FILE_NAME)
}
val meta = if (metaFile == null) { val meta = if (metaFile == null) {
Meta.EMPTY Meta.EMPTY
@ -196,7 +201,7 @@ public fun IOPlugin.writeEnvelopeFile(
path: Path, path: Path,
envelope: Envelope, envelope: Envelope,
envelopeFormat: EnvelopeFormat = TaggedEnvelopeFormat, envelopeFormat: EnvelopeFormat = TaggedEnvelopeFormat,
metaFormat: MetaFormatFactory? = null metaFormat: MetaFormatFactory? = null,
) { ) {
path.rewrite { path.rewrite {
with(envelopeFormat) { with(envelopeFormat) {
@ -212,7 +217,7 @@ public fun IOPlugin.writeEnvelopeFile(
fun IOPlugin.writeEnvelopeDirectory( fun IOPlugin.writeEnvelopeDirectory(
path: Path, path: Path,
envelope: Envelope, envelope: Envelope,
metaFormat: MetaFormatFactory = JsonMetaFormat metaFormat: MetaFormatFactory = JsonMetaFormat,
) { ) {
if (!Files.exists(path)) { if (!Files.exists(path)) {
Files.createDirectories(path) Files.createDirectories(path)

View File

@ -4,7 +4,7 @@ import hep.dataforge.context.Global
import hep.dataforge.io.Envelope import hep.dataforge.io.Envelope
import hep.dataforge.io.Responder import hep.dataforge.io.Responder
import hep.dataforge.io.TaggedEnvelopeFormat import hep.dataforge.io.TaggedEnvelopeFormat
import hep.dataforge.io.toByteArray import hep.dataforge.io.writeToByteArray
import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.runBlocking import kotlinx.coroutines.runBlocking
import kotlinx.io.writeDouble import kotlinx.io.writeDouble
@ -18,7 +18,7 @@ import kotlin.time.ExperimentalTime
@ExperimentalStdlibApi @ExperimentalStdlibApi
object EchoResponder : Responder { object EchoResponder : Responder {
override suspend fun respond(request: Envelope): Envelope { override suspend fun respond(request: Envelope): Envelope {
val string = TaggedEnvelopeFormat().run { toByteArray(request).decodeToString() } val string = TaggedEnvelopeFormat().run { writeToByteArray(request).decodeToString() }
println("ECHO:") println("ECHO:")
println(string) println(string)
return request return request

View File

@ -4,6 +4,7 @@ package hep.dataforge.meta
import hep.dataforge.meta.JsonMeta.Companion.JSON_ARRAY_KEY import hep.dataforge.meta.JsonMeta.Companion.JSON_ARRAY_KEY
import hep.dataforge.meta.descriptors.ItemDescriptor import hep.dataforge.meta.descriptors.ItemDescriptor
import hep.dataforge.meta.descriptors.ItemDescriptor.Companion.DEFAULT_INDEX_KEY
import hep.dataforge.meta.descriptors.NodeDescriptor import hep.dataforge.meta.descriptors.NodeDescriptor
import hep.dataforge.meta.descriptors.ValueDescriptor import hep.dataforge.meta.descriptors.ValueDescriptor
import hep.dataforge.names.NameToken import hep.dataforge.names.NameToken
@ -75,7 +76,7 @@ private fun Meta.toJsonWithIndex(descriptor: NodeDescriptor?, indexValue: String
if (indexValue != null) { if (indexValue != null) {
val indexKey = descriptor?.indexKey ?: NodeDescriptor.DEFAULT_INDEX_KEY val indexKey = descriptor?.indexKey ?: DEFAULT_INDEX_KEY
elementMap[indexKey] = JsonPrimitive(indexValue) elementMap[indexKey] = JsonPrimitive(indexValue)
} }
@ -158,7 +159,7 @@ public class JsonMeta(private val json: JsonObject, private val descriptor: Node
) )
map[key] = MetaItem.ValueItem(listValue) map[key] = MetaItem.ValueItem(listValue)
} else value.forEachIndexed { index, jsonElement -> } else value.forEachIndexed { index, jsonElement ->
val indexKey = (itemDescriptor as? NodeDescriptor)?.indexKey ?: NodeDescriptor.DEFAULT_INDEX_KEY val indexKey = (itemDescriptor as? NodeDescriptor)?.indexKey ?: DEFAULT_INDEX_KEY
val indexValue: String = (jsonElement as? JsonObject) val indexValue: String = (jsonElement as? JsonObject)
?.get(indexKey)?.jsonPrimitive?.contentOrNull ?.get(indexKey)?.jsonPrimitive?.contentOrNull
?: index.toString() //In case index is non-string, the backward transformation will be broken. ?: index.toString() //In case index is non-string, the backward transformation will be broken.

View File

@ -1,9 +1,12 @@
package hep.dataforge.meta package hep.dataforge.meta
import hep.dataforge.meta.descriptors.* import hep.dataforge.meta.descriptors.Described
import hep.dataforge.meta.descriptors.NodeDescriptor
import hep.dataforge.meta.descriptors.defaultItem
import hep.dataforge.meta.descriptors.get
import hep.dataforge.names.Name import hep.dataforge.names.Name
import hep.dataforge.names.NameToken import hep.dataforge.names.NameToken
import hep.dataforge.names.plus import hep.dataforge.names.asName
/** /**
* A base for delegate-based or descriptor-based scheme. [Scheme] has an empty constructor to simplify usage from [Specification]. * A base for delegate-based or descriptor-based scheme. [Scheme] has an empty constructor to simplify usage from [Specification].
@ -30,21 +33,21 @@ public open class Scheme(
* values if default value is unavailable. * values if default value is unavailable.
* Values from [defaultProvider] completely replace * Values from [defaultProvider] completely replace
*/ */
public open val defaultLayer: Meta get() = DefaultLayer(Name.EMPTY) public open val defaultLayer: Meta get() = DefaultLayer()
override fun toMeta(): Laminate = Laminate(config, defaultLayer) override fun toMeta(): Laminate = Laminate(config, defaultLayer)
private inner class DefaultLayer(val path: Name) : MetaBase() { private inner class DefaultLayer : MetaBase() {
override val items: Map<NameToken, MetaItem<*>> = override val items: Map<NameToken, MetaItem<*>> = buildMap {
(descriptor?.get(path) as? NodeDescriptor)?.items?.entries?.associate { (key, descriptor) -> descriptor?.items?.forEach { (key, itemDescriptor) ->
val token = NameToken(key) val token = NameToken(key)
val fullName = path + token val name = token.asName()
val item: MetaItem<*> = when (descriptor) { val item = defaultProvider.getItem(name) ?: itemDescriptor.defaultItem()
is ValueDescriptor -> getDefaultItem(fullName) ?: descriptor.defaultItem() if (item != null) {
is NodeDescriptor -> MetaItem.NodeItem(DefaultLayer(fullName)) put(token, item)
}
}
} }
token to item
} ?: emptyMap()
} }
} }
@ -82,7 +85,7 @@ public open class MetaScheme(
private val meta: Meta, private val meta: Meta,
override val descriptor: NodeDescriptor? = null, override val descriptor: NodeDescriptor? = null,
config: Config = Config(), config: Config = Config(),
) : Scheme(config, meta::get) { ) : Scheme(config, meta) {
override val defaultLayer: Meta get() = Laminate(meta, descriptor?.defaultItem().node) override val defaultLayer: Meta get() = Laminate(meta, descriptor?.defaultItem().node)
} }

View File

@ -23,12 +23,6 @@ public interface Specification<T : Configurable> {
*/ */
public fun <T : Configurable> Specification<T>.update(config: Config, action: T.() -> Unit): T = wrap(config).apply(action) public fun <T : Configurable> Specification<T>.update(config: Config, action: T.() -> Unit): T = wrap(config).apply(action)
/**
* Wrap a configuration using static meta as default
*/
public fun <T : Configurable> Specification<T>.wrap(config: Config = Config(), default: Meta = Meta.EMPTY): T =
wrap(config, default)
/** /**
* Wrap a configuration using static meta as default * Wrap a configuration using static meta as default
*/ */
@ -37,7 +31,6 @@ public fun <T : Configurable> Specification<T>.wrap(source: Meta): T {
return wrap(source.asConfig(), default) return wrap(source.asConfig(), default)
} }
/** /**
* Apply specified configuration to configurable * Apply specified configuration to configurable
*/ */

View File

@ -3,11 +3,11 @@ package hep.dataforge.meta.descriptors
/** /**
* An object which provides its descriptor * An object which provides its descriptor
*/ */
interface Described { public interface Described {
val descriptor: ItemDescriptor? public val descriptor: ItemDescriptor?
companion object { public companion object {
const val DESCRIPTOR_NODE = "@descriptor" //public const val DESCRIPTOR_NODE: String = "@descriptor"
} }
} }

View File

@ -1,32 +1,48 @@
package hep.dataforge.meta.descriptors package hep.dataforge.meta.descriptors
import hep.dataforge.meta.Laminate import hep.dataforge.meta.Laminate
import hep.dataforge.meta.Meta
import hep.dataforge.meta.MetaBase import hep.dataforge.meta.MetaBase
import hep.dataforge.meta.MetaItem import hep.dataforge.meta.MetaItem
import hep.dataforge.names.NameToken import hep.dataforge.names.NameToken
import hep.dataforge.values.Null
/** /**
* A [Meta] that wraps a descriptor node * A [Meta] that is constructed from [NodeDescriptor]
*/ */
class DescriptorMeta(val descriptor: NodeDescriptor) : MetaBase() { private class DescriptorMeta(val descriptor: NodeDescriptor) : Meta, MetaBase() {
override val items: Map<NameToken, MetaItem<*>> override val items: Map<NameToken, MetaItem<*>>
get() = descriptor.items.entries.associate { entry -> get() = buildMap {
NameToken(entry.key) to entry.value.defaultItem() descriptor.items.forEach { (token, descriptorItem) ->
val item = descriptorItem.defaultItem()
if (item != null) {
put(NameToken(token), item)
}
}
} }
} }
fun NodeDescriptor.buildDefaultMeta() = Laminate(default, DescriptorMeta(this)) /**
* Generate a laminate representing default item set generated by this descriptor
*/
public fun NodeDescriptor.defaultMeta(): Laminate = Laminate(default, DescriptorMeta(this))
fun NodeDescriptor.defaultItem(): MetaItem.NodeItem<*> = /**
MetaItem.NodeItem(buildDefaultMeta()) * Build a default [MetaItem.NodeItem] from this node descriptor
*/
internal fun NodeDescriptor.defaultItem(): MetaItem.NodeItem<*> =
MetaItem.NodeItem(defaultMeta())
fun ValueDescriptor.defaultItem(): MetaItem.ValueItem = MetaItem.ValueItem(default ?: Null) /**
* Build a default [MetaItem.ValueItem] from this descriptor
*/
internal fun ValueDescriptor.defaultItem(): MetaItem.ValueItem? {
return MetaItem.ValueItem(default ?: return null)
}
/** /**
* Build a default [MetaItem] from descriptor. * Build a default [MetaItem] from descriptor.
*/ */
fun ItemDescriptor.defaultItem(): MetaItem<*> { public fun ItemDescriptor.defaultItem(): MetaItem<*>? {
return when (this) { return when (this) {
is ValueDescriptor -> defaultItem() is ValueDescriptor -> defaultItem()
is NodeDescriptor -> defaultItem() is NodeDescriptor -> defaultItem()

View File

@ -4,29 +4,26 @@ import hep.dataforge.meta.*
import hep.dataforge.names.* import hep.dataforge.names.*
import hep.dataforge.values.* import hep.dataforge.values.*
/**
* A common parent for [ValueDescriptor] and [NodeDescriptor]. Describes a single [MetaItem] or a group of same-name-siblings.
*/
@DFBuilder @DFBuilder
sealed class ItemDescriptor(val config: Config) { public sealed class ItemDescriptor(public val config: Config) {
/** /**
* True if same name siblings with this name are allowed * True if same name siblings with this name are allowed
*
* @return
*/ */
var multiple: Boolean by config.boolean(false) public var multiple: Boolean by config.boolean(false)
/** /**
* The item description * The item description text
*
* @return
*/ */
var info: String? by config.string() public var info: String? by config.string()
/** /**
* True if the item is required * True if the item is required
*
* @return
*/ */
abstract var required: Boolean public abstract var required: Boolean
/** /**
@ -34,29 +31,29 @@ sealed class ItemDescriptor(val config: Config) {
* *
* @return * @return
*/ */
var attributes by config.node() public var attributes: Config? by config.node()
}
/** /**
* Configure attributes of the descriptor * An index field by which this node is identified in case of same name siblings construct
*/ */
fun ItemDescriptor.attributes(block: Config.() -> Unit) { public var indexKey: String by config.string(DEFAULT_INDEX_KEY)
(attributes ?: Config().also { this.attributes = it }).apply(block)
}
/** public companion object{
* Set specific attribute in the descriptor public const val DEFAULT_INDEX_KEY: String = "@index"
*/
fun ItemDescriptor.setAttribute(name: Name, value: Any?) {
attributes {
set(name, value)
} }
} }
/**
* Configure attributes of the descriptor, creating an attributes node if needed.
*/
public fun ItemDescriptor.attributes(block: Config.() -> Unit) {
(attributes ?: Config().also { this.attributes = it }).apply(block)
}
/** /**
* Check if given item suits the descriptor * Check if given item suits the descriptor
*/ */
fun ItemDescriptor.validateItem(item: MetaItem<*>?): Boolean { public fun ItemDescriptor.validateItem(item: MetaItem<*>?): Boolean {
if (item == null) return !required if (item == null) return !required
return when (this) { return when (this) {
is ValueDescriptor -> isAllowedValue(item.value ?: return false) is ValueDescriptor -> isAllowedValue(item.value ?: return false)
@ -73,7 +70,7 @@ fun ItemDescriptor.validateItem(item: MetaItem<*>?): Boolean {
* @author Alexander Nozik * @author Alexander Nozik
*/ */
@DFBuilder @DFBuilder
class NodeDescriptor(config: Config = Config()) : ItemDescriptor(config) { public class NodeDescriptor(config: Config = Config()) : ItemDescriptor(config) {
init { init {
config[IS_NODE_KEY] = true config[IS_NODE_KEY] = true
} }
@ -90,14 +87,12 @@ class NodeDescriptor(config: Config = Config()) : ItemDescriptor(config) {
* *
* @return * @return
*/ */
var default by config.node() public var default: Config? by config.node()
/** /**
* An index field by which this node is identified in case of same name siblings construct * The map of children item descriptors (both nodes and values)
*/ */
var indexKey by config.string(DEFAULT_INDEX_KEY) public val items: Map<String, ItemDescriptor>
val items: Map<String, ItemDescriptor>
get() = config.getIndexed(ITEM_KEY).mapValues { (_, item) -> get() = config.getIndexed(ITEM_KEY).mapValues { (_, item) ->
val node = item.node ?: error("Node descriptor must be a node") val node = item.node ?: error("Node descriptor must be a node")
if (node[IS_NODE_KEY].boolean == true) { if (node[IS_NODE_KEY].boolean == true) {
@ -111,7 +106,7 @@ class NodeDescriptor(config: Config = Config()) : ItemDescriptor(config) {
* The map of children node descriptors * The map of children node descriptors
*/ */
@Suppress("UNCHECKED_CAST") @Suppress("UNCHECKED_CAST")
val nodes: Map<String, NodeDescriptor> public val nodes: Map<String, NodeDescriptor>
get() = config.getIndexed(ITEM_KEY).entries.filter { get() = config.getIndexed(ITEM_KEY).entries.filter {
it.value.node[IS_NODE_KEY].boolean == true it.value.node[IS_NODE_KEY].boolean == true
}.associate { (name, item) -> }.associate { (name, item) ->
@ -120,9 +115,9 @@ class NodeDescriptor(config: Config = Config()) : ItemDescriptor(config) {
} }
/** /**
* The list of value descriptors * The list of children value descriptors
*/ */
val values: Map<String, ValueDescriptor> public val values: Map<String, ValueDescriptor>
get() = config.getIndexed(ITEM_KEY).entries.filter { get() = config.getIndexed(ITEM_KEY).entries.filter {
it.value.node[IS_NODE_KEY].boolean != true it.value.node[IS_NODE_KEY].boolean != true
}.associate { (name, item) -> }.associate { (name, item) ->
@ -154,39 +149,43 @@ class NodeDescriptor(config: Config = Config()) : ItemDescriptor(config) {
config[token] = descriptor.config config[token] = descriptor.config
} }
fun item(name: Name, descriptor: ItemDescriptor) { public fun item(name: Name, descriptor: ItemDescriptor) {
buildNode(name.cutLast()).newItem(name.lastOrNull().toString(), descriptor) buildNode(name.cutLast()).newItem(name.lastOrNull().toString(), descriptor)
} }
fun item(name: String, descriptor: ItemDescriptor) { public fun item(name: String, descriptor: ItemDescriptor) {
item(name.toName(), descriptor) item(name.toName(), descriptor)
} }
fun node(name: Name, block: NodeDescriptor.() -> Unit) { /**
* Create and configure a child node descriptor
*/
public fun node(name: Name, block: NodeDescriptor.() -> Unit) {
item(name, NodeDescriptor().apply(block)) item(name, NodeDescriptor().apply(block))
} }
fun node(name: String, block: NodeDescriptor.() -> Unit) { public fun node(name: String, block: NodeDescriptor.() -> Unit) {
node(name.toName(), block) node(name.toName(), block)
} }
fun value(name: Name, block: ValueDescriptor.() -> Unit) { /**
* Create and configure child value descriptor
*/
public fun value(name: Name, block: ValueDescriptor.() -> Unit) {
require(name.length >= 1) { "Name length for value descriptor must be non-empty" } require(name.length >= 1) { "Name length for value descriptor must be non-empty" }
item(name, ValueDescriptor().apply(block)) item(name, ValueDescriptor().apply(block))
} }
fun value(name: String, block: ValueDescriptor.() -> Unit) { public fun value(name: String, block: ValueDescriptor.() -> Unit) {
value(name.toName(), block) value(name.toName(), block)
} }
companion object { public companion object {
val ITEM_KEY = "item".asName() internal val ITEM_KEY: Name = "item".asName()
val IS_NODE_KEY = "@isNode".asName() internal val IS_NODE_KEY: Name = "@isNode".asName()
const val DEFAULT_INDEX_KEY = "@index" public inline operator fun invoke(block: NodeDescriptor.() -> Unit): NodeDescriptor = NodeDescriptor().apply(block)
inline operator fun invoke(block: NodeDescriptor.() -> Unit) = NodeDescriptor().apply(block)
//TODO infer descriptor from spec //TODO infer descriptor from spec
} }
@ -195,7 +194,7 @@ class NodeDescriptor(config: Config = Config()) : ItemDescriptor(config) {
/** /**
* Get a descriptor item associated with given name or null if item for given name not provided * Get a descriptor item associated with given name or null if item for given name not provided
*/ */
operator fun ItemDescriptor.get(name: Name): ItemDescriptor? { public operator fun ItemDescriptor.get(name: Name): ItemDescriptor? {
if (name.isEmpty()) return this if (name.isEmpty()) return this
return when (this) { return when (this) {
is ValueDescriptor -> null // empty name already checked is ValueDescriptor -> null // empty name already checked
@ -203,7 +202,7 @@ operator fun ItemDescriptor.get(name: Name): ItemDescriptor? {
} }
} }
operator fun ItemDescriptor.get(name: String): ItemDescriptor? = get(name.toName()) public operator fun ItemDescriptor.get(name: String): ItemDescriptor? = get(name.toName())
/** /**
* A descriptor for meta value * A descriptor for meta value
@ -213,7 +212,7 @@ operator fun ItemDescriptor.get(name: String): ItemDescriptor? = get(name.toName
* @author Alexander Nozik * @author Alexander Nozik
*/ */
@DFBuilder @DFBuilder
class ValueDescriptor(config: Config = Config()) : ItemDescriptor(config) { public class ValueDescriptor(config: Config = Config()) : ItemDescriptor(config) {
/** /**
* True if the value is required * True if the value is required
@ -227,9 +226,9 @@ class ValueDescriptor(config: Config = Config()) : ItemDescriptor(config) {
* *
* @return * @return
*/ */
var default: Value? by config.value() public var default: Value? by config.value()
fun default(v: Any) { public fun default(v: Any) {
this.default = Value.of(v) this.default = Value.of(v)
} }
@ -238,9 +237,9 @@ class ValueDescriptor(config: Config = Config()) : ItemDescriptor(config) {
* *
* @return * @return
*/ */
var type: List<ValueType>? by config.listValue { ValueType.valueOf(it.string) } public var type: List<ValueType>? by config.listValue { ValueType.valueOf(it.string) }
fun type(vararg t: ValueType) { public fun type(vararg t: ValueType) {
this.type = listOf(*t) this.type = listOf(*t)
} }
@ -251,7 +250,7 @@ class ValueDescriptor(config: Config = Config()) : ItemDescriptor(config) {
* @param value * @param value
* @return * @return
*/ */
fun isAllowedValue(value: Value): Boolean { public fun isAllowedValue(value: Value): Boolean {
return (type?.let { it.contains(ValueType.STRING) || it.contains(value.type) } ?: true) return (type?.let { it.contains(ValueType.STRING) || it.contains(value.type) } ?: true)
&& (allowedValues.isEmpty() || allowedValues.contains(value)) && (allowedValues.isEmpty() || allowedValues.contains(value))
} }
@ -262,7 +261,7 @@ class ValueDescriptor(config: Config = Config()) : ItemDescriptor(config) {
* *
* @return * @return
*/ */
var allowedValues: List<Value> by config.item().convert( public var allowedValues: List<Value> by config.item().convert(
reader = { reader = {
val value = it.value val value = it.value
when { when {
@ -279,7 +278,7 @@ class ValueDescriptor(config: Config = Config()) : ItemDescriptor(config) {
/** /**
* Allow given list of value and forbid others * Allow given list of value and forbid others
*/ */
fun allow(vararg v: Any) { public fun allow(vararg v: Any) {
this.allowedValues = v.map { Value.of(it) } this.allowedValues = v.map { Value.of(it) }
} }
} }
@ -287,7 +286,7 @@ class ValueDescriptor(config: Config = Config()) : ItemDescriptor(config) {
/** /**
* Merge two node descriptors into one using first one as primary * Merge two node descriptors into one using first one as primary
*/ */
operator fun NodeDescriptor.plus(other: NodeDescriptor): NodeDescriptor { public operator fun NodeDescriptor.plus(other: NodeDescriptor): NodeDescriptor {
return NodeDescriptor().apply { return NodeDescriptor().apply {
config.update(other.config) config.update(other.config)
config.update(this@plus.config) config.update(this@plus.config)

View File

@ -36,7 +36,7 @@ class DescriptorTest {
@Test @Test
fun testDefaultMetaNode(){ fun testDefaultMetaNode(){
val meta = descriptor.buildDefaultMeta() val meta = descriptor.defaultMeta()
assertEquals(false, meta["aNode.otherNode.otherValue"].boolean) assertEquals(false, meta["aNode.otherNode.otherValue"].boolean)
} }
} }

View File

@ -1,6 +0,0 @@
package hep.dataforge.meta
import kotlinx.serialization.json.Json
public val JSON_PRETTY: Json = Json { prettyPrint = true; useArrayPolymorphism = true }
public val JSON_PLAIN: Json = Json { prettyPrint = false; useArrayPolymorphism = true }