Fix IO after refactoring
This commit is contained in:
parent
52a3c8bc6f
commit
8a8484172c
@ -105,11 +105,13 @@ class FrontMatterEnvelopeFormat(
|
||||
override fun readPartial(input: Input): PartialEnvelope =
|
||||
default.readPartial(input)
|
||||
|
||||
override fun Output.writeEnvelope(envelope: Envelope, metaFormatFactory: MetaFormatFactory, formatMeta: Meta): Unit =
|
||||
default.run { writeEnvelope(envelope, metaFormatFactory, formatMeta) }
|
||||
override fun Output.writeEnvelope(
|
||||
envelope: Envelope,
|
||||
metaFormatFactory: MetaFormatFactory,
|
||||
formatMeta: Meta,
|
||||
): Unit = default.run { writeEnvelope(envelope, metaFormatFactory, formatMeta) }
|
||||
|
||||
override fun readObject(input: Input): Envelope =
|
||||
default.readObject(input)
|
||||
override fun readObject(input: Input): Envelope = default.readObject(input)
|
||||
|
||||
}
|
||||
}
|
@ -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
|
||||
|
@ -12,7 +12,7 @@ import hep.dataforge.meta.toJson
|
||||
import hep.dataforge.meta.toMetaItem
|
||||
import kotlinx.io.Input
|
||||
import kotlinx.io.Output
|
||||
import kotlinx.io.readByteArray
|
||||
import kotlinx.io.text.readUtf8String
|
||||
import kotlinx.io.text.writeUtf8String
|
||||
import kotlinx.serialization.json.Json
|
||||
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?) {
|
||||
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 {
|
||||
@ -32,7 +32,7 @@ public class JsonMetaFormat(private val json: Json = DEFAULT_JSON) : MetaFormat
|
||||
}
|
||||
|
||||
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 item = jsonElement.toMetaItem(descriptor)
|
||||
return item.node ?: Meta.EMPTY
|
||||
|
@ -18,7 +18,7 @@ import kotlinx.io.*
|
||||
*/
|
||||
public class TaggedEnvelopeFormat(
|
||||
public val io: IOPlugin,
|
||||
public val version: VERSION = VERSION.DF02
|
||||
public val version: VERSION = VERSION.DF02,
|
||||
) : EnvelopeFormat {
|
||||
|
||||
// private val metaFormat = io.metaFormat(metaFormatKey)
|
||||
@ -67,11 +67,7 @@ public class TaggedEnvelopeFormat(
|
||||
val metaFormat = io.resolveMetaFormat(tag.metaFormatKey)
|
||||
?: error("Meta format with key ${tag.metaFormatKey} not found")
|
||||
|
||||
val meta: Meta = input.limit(tag.metaSize.toInt()).run {
|
||||
metaFormat.run {
|
||||
readObject(input)
|
||||
}
|
||||
}
|
||||
val meta: Meta = metaFormat.readObject(input.limit(tag.metaSize.toInt()))
|
||||
|
||||
val data = input.readBinary(tag.dataSize.toInt())
|
||||
|
||||
@ -84,11 +80,8 @@ public class TaggedEnvelopeFormat(
|
||||
val metaFormat = io.resolveMetaFormat(tag.metaFormatKey)
|
||||
?: error("Meta format with key ${tag.metaFormatKey} not found")
|
||||
|
||||
val meta: Meta = input.limit(tag.metaSize.toInt()).run {
|
||||
metaFormat.run {
|
||||
readObject(input)
|
||||
}
|
||||
}
|
||||
val meta: Meta = metaFormat.readObject(input.limit(tag.metaSize.toInt()))
|
||||
|
||||
|
||||
return PartialEnvelope(meta, version.tagSize + tag.metaSize, tag.dataSize)
|
||||
}
|
||||
@ -96,7 +89,7 @@ public class TaggedEnvelopeFormat(
|
||||
private data class Tag(
|
||||
val metaFormatKey: Short,
|
||||
val metaSize: UInt,
|
||||
val dataSize: ULong
|
||||
val dataSize: ULong,
|
||||
)
|
||||
|
||||
public enum class VERSION(public val tagSize: UInt) {
|
||||
@ -165,13 +158,13 @@ public class TaggedEnvelopeFormat(
|
||||
override fun readPartial(input: Input): PartialEnvelope =
|
||||
default.run { readPartial(input) }
|
||||
|
||||
override fun Output.writeEnvelope(envelope: Envelope, metaFormatFactory: MetaFormatFactory, formatMeta: Meta): Unit =
|
||||
default.run { writeEnvelope(envelope, metaFormatFactory, formatMeta) }
|
||||
|
||||
override fun readObject(input: Input): Envelope =
|
||||
default.run { readObject(input) }
|
||||
|
||||
override fun Output.writeEnvelope(
|
||||
envelope: Envelope,
|
||||
metaFormatFactory: MetaFormatFactory,
|
||||
formatMeta: Meta,
|
||||
): Unit = default.run { writeEnvelope(envelope, metaFormatFactory, formatMeta) }
|
||||
|
||||
override fun readObject(input: Input): Envelope = default.readObject(input)
|
||||
}
|
||||
|
||||
}
|
@ -20,7 +20,7 @@ import kotlin.collections.set
|
||||
*/
|
||||
public class TaglessEnvelopeFormat(
|
||||
public val io: IOPlugin,
|
||||
public val meta: Meta = Meta.EMPTY
|
||||
public val meta: Meta = Meta.EMPTY,
|
||||
) : EnvelopeFormat {
|
||||
|
||||
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 metaSize = properties[META_LENGTH_PROPERTY]?.toInt()
|
||||
meta = if (metaSize != null) {
|
||||
input.limit(metaSize).run {
|
||||
metaFormat.run { readObject(input) }
|
||||
}
|
||||
metaFormat.readObject(input.limit(metaSize))
|
||||
} else {
|
||||
metaFormat.run {
|
||||
readObject(input)
|
||||
}
|
||||
metaFormat.readObject(input)
|
||||
}
|
||||
}
|
||||
|
||||
@ -150,9 +146,7 @@ public class TaglessEnvelopeFormat(
|
||||
val metaSize = properties[META_LENGTH_PROPERTY]?.toInt()
|
||||
meta = if (metaSize != null) {
|
||||
offset += metaSize.toUInt()
|
||||
input.limit(metaSize).run {
|
||||
metaFormat.run { readObject(input) }
|
||||
}
|
||||
metaFormat.readObject(input.limit(metaSize))
|
||||
} else {
|
||||
error("Can't partially read an envelope with undefined meta size")
|
||||
}
|
||||
@ -203,11 +197,13 @@ public class TaglessEnvelopeFormat(
|
||||
override fun readPartial(input: Input): PartialEnvelope =
|
||||
default.run { readPartial(input) }
|
||||
|
||||
override fun Output.writeEnvelope(envelope: Envelope, metaFormatFactory: MetaFormatFactory, formatMeta: Meta): Unit =
|
||||
default.run { writeEnvelope(envelope, metaFormatFactory, formatMeta) }
|
||||
override fun Output.writeEnvelope(
|
||||
envelope: Envelope,
|
||||
metaFormatFactory: MetaFormatFactory,
|
||||
formatMeta: Meta,
|
||||
): Unit = default.run { writeEnvelope(envelope, metaFormatFactory, formatMeta) }
|
||||
|
||||
override fun readObject(input: Input): Envelope =
|
||||
default.run { readObject(input) }
|
||||
override fun readObject(input: Input): Envelope = default.readObject(input)
|
||||
|
||||
override fun peekFormat(io: IOPlugin, input: Input): EnvelopeFormat? {
|
||||
return try {
|
||||
|
@ -23,9 +23,9 @@ class EnvelopeFormatTest {
|
||||
@Test
|
||||
fun testTaggedFormat(){
|
||||
TaggedEnvelopeFormat.run {
|
||||
val byteArray = this.toByteArray(envelope)
|
||||
val byteArray = writeToByteArray(envelope)
|
||||
//println(byteArray.decodeToString())
|
||||
val res = readByteArray(byteArray)
|
||||
val res = readFromByteArray(byteArray)
|
||||
assertEquals(envelope.meta,res.meta)
|
||||
val double = res.data?.read {
|
||||
readDouble()
|
||||
@ -37,9 +37,9 @@ class EnvelopeFormatTest {
|
||||
@Test
|
||||
fun testTaglessFormat(){
|
||||
TaglessEnvelopeFormat.run {
|
||||
val byteArray = toByteArray(envelope)
|
||||
val byteArray = writeToByteArray(envelope)
|
||||
//println(byteArray.decodeToString())
|
||||
val res = readByteArray(byteArray)
|
||||
val res = readFromByteArray(byteArray)
|
||||
assertEquals(envelope.meta,res.meta)
|
||||
val double = res.data?.read {
|
||||
readDouble()
|
||||
|
@ -1,13 +1,19 @@
|
||||
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.toName
|
||||
import kotlinx.serialization.ExperimentalSerializationApi
|
||||
import kotlinx.serialization.cbor.Cbor
|
||||
import kotlinx.serialization.json.Json
|
||||
import kotlin.test.Test
|
||||
import kotlin.test.assertEquals
|
||||
|
||||
val JSON_PRETTY: Json = Json { prettyPrint = true; useArrayPolymorphism = true }
|
||||
val JSON_PLAIN: Json = Json { prettyPrint = false; useArrayPolymorphism = true }
|
||||
|
||||
class MetaSerializerTest {
|
||||
val meta = Meta {
|
||||
"a" put 22
|
||||
|
@ -3,5 +3,5 @@ package hep.dataforge.io
|
||||
import kotlinx.io.ByteArrayInput
|
||||
import kotlinx.io.use
|
||||
|
||||
fun <T : Any> IOFormat<T>.toByteArray(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>.writeToByteArray(obj: T): ByteArray = buildByteArray { writeObject(this, obj) }
|
||||
fun <T : Any> IOFormat<T>.readFromByteArray(array: ByteArray): T = ByteArrayInput(array).use { readObject(it) }
|
@ -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.
|
||||
* 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")
|
||||
|
||||
val actualPath: Path = if (Files.isDirectory(path)) {
|
||||
@ -93,7 +97,7 @@ public fun IOPlugin.writeMetaFile(
|
||||
path: Path,
|
||||
meta: Meta,
|
||||
metaFormat: MetaFormatFactory = JsonMetaFormat,
|
||||
descriptor: NodeDescriptor? = null
|
||||
descriptor: NodeDescriptor? = null,
|
||||
) {
|
||||
val actualPath = if (Files.isDirectory(path)) {
|
||||
path.resolve("@" + metaFormat.name.toString())
|
||||
@ -146,14 +150,15 @@ public val IOPlugin.Companion.DATA_FILE_NAME: String get() = "@data"
|
||||
public fun IOPlugin.readEnvelopeFile(
|
||||
path: Path,
|
||||
readNonEnvelopes: Boolean = false,
|
||||
formatPeeker: IOPlugin.(Path) -> EnvelopeFormat? = IOPlugin::peekBinaryFormat
|
||||
formatPeeker: IOPlugin.(Path) -> EnvelopeFormat? = IOPlugin::peekBinaryFormat,
|
||||
): Envelope? {
|
||||
if (!Files.exists(path)) return null
|
||||
|
||||
//read two-files directory
|
||||
if (Files.isDirectory(path)) {
|
||||
val metaFile = Files.list(path).asSequence()
|
||||
.singleOrNull { it.fileName.toString().startsWith(IOPlugin.META_FILE_NAME) }
|
||||
val metaFile = Files.list(path).asSequence().singleOrNull {
|
||||
it.fileName.toString().startsWith(IOPlugin.META_FILE_NAME)
|
||||
}
|
||||
|
||||
val meta = if (metaFile == null) {
|
||||
Meta.EMPTY
|
||||
@ -196,7 +201,7 @@ public fun IOPlugin.writeEnvelopeFile(
|
||||
path: Path,
|
||||
envelope: Envelope,
|
||||
envelopeFormat: EnvelopeFormat = TaggedEnvelopeFormat,
|
||||
metaFormat: MetaFormatFactory? = null
|
||||
metaFormat: MetaFormatFactory? = null,
|
||||
) {
|
||||
path.rewrite {
|
||||
with(envelopeFormat) {
|
||||
@ -212,7 +217,7 @@ public fun IOPlugin.writeEnvelopeFile(
|
||||
fun IOPlugin.writeEnvelopeDirectory(
|
||||
path: Path,
|
||||
envelope: Envelope,
|
||||
metaFormat: MetaFormatFactory = JsonMetaFormat
|
||||
metaFormat: MetaFormatFactory = JsonMetaFormat,
|
||||
) {
|
||||
if (!Files.exists(path)) {
|
||||
Files.createDirectories(path)
|
||||
|
@ -4,7 +4,7 @@ import hep.dataforge.context.Global
|
||||
import hep.dataforge.io.Envelope
|
||||
import hep.dataforge.io.Responder
|
||||
import hep.dataforge.io.TaggedEnvelopeFormat
|
||||
import hep.dataforge.io.toByteArray
|
||||
import hep.dataforge.io.writeToByteArray
|
||||
import kotlinx.coroutines.GlobalScope
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import kotlinx.io.writeDouble
|
||||
@ -18,7 +18,7 @@ import kotlin.time.ExperimentalTime
|
||||
@ExperimentalStdlibApi
|
||||
object EchoResponder : Responder {
|
||||
override suspend fun respond(request: Envelope): Envelope {
|
||||
val string = TaggedEnvelopeFormat().run { toByteArray(request).decodeToString() }
|
||||
val string = TaggedEnvelopeFormat().run { writeToByteArray(request).decodeToString() }
|
||||
println("ECHO:")
|
||||
println(string)
|
||||
return request
|
||||
|
@ -4,6 +4,7 @@ package hep.dataforge.meta
|
||||
|
||||
import hep.dataforge.meta.JsonMeta.Companion.JSON_ARRAY_KEY
|
||||
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.ValueDescriptor
|
||||
import hep.dataforge.names.NameToken
|
||||
@ -75,7 +76,7 @@ private fun Meta.toJsonWithIndex(descriptor: NodeDescriptor?, indexValue: String
|
||||
|
||||
|
||||
if (indexValue != null) {
|
||||
val indexKey = descriptor?.indexKey ?: NodeDescriptor.DEFAULT_INDEX_KEY
|
||||
val indexKey = descriptor?.indexKey ?: DEFAULT_INDEX_KEY
|
||||
elementMap[indexKey] = JsonPrimitive(indexValue)
|
||||
}
|
||||
|
||||
@ -158,7 +159,7 @@ public class JsonMeta(private val json: JsonObject, private val descriptor: Node
|
||||
)
|
||||
map[key] = MetaItem.ValueItem(listValue)
|
||||
} 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)
|
||||
?.get(indexKey)?.jsonPrimitive?.contentOrNull
|
||||
?: index.toString() //In case index is non-string, the backward transformation will be broken.
|
||||
|
@ -1,9 +1,12 @@
|
||||
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.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].
|
||||
@ -30,21 +33,21 @@ public open class Scheme(
|
||||
* values if default value is unavailable.
|
||||
* 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)
|
||||
|
||||
private inner class DefaultLayer(val path: Name) : MetaBase() {
|
||||
override val items: Map<NameToken, MetaItem<*>> =
|
||||
(descriptor?.get(path) as? NodeDescriptor)?.items?.entries?.associate { (key, descriptor) ->
|
||||
private inner class DefaultLayer : MetaBase() {
|
||||
override val items: Map<NameToken, MetaItem<*>> = buildMap {
|
||||
descriptor?.items?.forEach { (key, itemDescriptor) ->
|
||||
val token = NameToken(key)
|
||||
val fullName = path + token
|
||||
val item: MetaItem<*> = when (descriptor) {
|
||||
is ValueDescriptor -> getDefaultItem(fullName) ?: descriptor.defaultItem()
|
||||
is NodeDescriptor -> MetaItem.NodeItem(DefaultLayer(fullName))
|
||||
val name = token.asName()
|
||||
val item = defaultProvider.getItem(name) ?: itemDescriptor.defaultItem()
|
||||
if (item != null) {
|
||||
put(token, item)
|
||||
}
|
||||
}
|
||||
}
|
||||
token to item
|
||||
} ?: emptyMap()
|
||||
}
|
||||
}
|
||||
|
||||
@ -82,7 +85,7 @@ public open class MetaScheme(
|
||||
private val meta: Meta,
|
||||
override val descriptor: NodeDescriptor? = null,
|
||||
config: Config = Config(),
|
||||
) : Scheme(config, meta::get) {
|
||||
) : Scheme(config, meta) {
|
||||
override val defaultLayer: Meta get() = Laminate(meta, descriptor?.defaultItem().node)
|
||||
}
|
||||
|
||||
|
@ -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)
|
||||
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
@ -37,7 +31,6 @@ public fun <T : Configurable> Specification<T>.wrap(source: Meta): T {
|
||||
return wrap(source.asConfig(), default)
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Apply specified configuration to configurable
|
||||
*/
|
||||
|
@ -3,11 +3,11 @@ package hep.dataforge.meta.descriptors
|
||||
/**
|
||||
* An object which provides its descriptor
|
||||
*/
|
||||
interface Described {
|
||||
val descriptor: ItemDescriptor?
|
||||
public interface Described {
|
||||
public val descriptor: ItemDescriptor?
|
||||
|
||||
companion object {
|
||||
const val DESCRIPTOR_NODE = "@descriptor"
|
||||
public companion object {
|
||||
//public const val DESCRIPTOR_NODE: String = "@descriptor"
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,32 +1,48 @@
|
||||
package hep.dataforge.meta.descriptors
|
||||
|
||||
import hep.dataforge.meta.Laminate
|
||||
import hep.dataforge.meta.Meta
|
||||
import hep.dataforge.meta.MetaBase
|
||||
import hep.dataforge.meta.MetaItem
|
||||
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<*>>
|
||||
get() = descriptor.items.entries.associate { entry ->
|
||||
NameToken(entry.key) to entry.value.defaultItem()
|
||||
get() = buildMap {
|
||||
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.
|
||||
*/
|
||||
fun ItemDescriptor.defaultItem(): MetaItem<*> {
|
||||
public fun ItemDescriptor.defaultItem(): MetaItem<*>? {
|
||||
return when (this) {
|
||||
is ValueDescriptor -> defaultItem()
|
||||
is NodeDescriptor -> defaultItem()
|
||||
|
@ -4,29 +4,26 @@ import hep.dataforge.meta.*
|
||||
import hep.dataforge.names.*
|
||||
import hep.dataforge.values.*
|
||||
|
||||
/**
|
||||
* A common parent for [ValueDescriptor] and [NodeDescriptor]. Describes a single [MetaItem] or a group of same-name-siblings.
|
||||
*/
|
||||
@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
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
var multiple: Boolean by config.boolean(false)
|
||||
public var multiple: Boolean by config.boolean(false)
|
||||
|
||||
/**
|
||||
* The item description
|
||||
*
|
||||
* @return
|
||||
* The item description text
|
||||
*/
|
||||
var info: String? by config.string()
|
||||
public var info: String? by config.string()
|
||||
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
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) {
|
||||
(attributes ?: Config().also { this.attributes = it }).apply(block)
|
||||
}
|
||||
public var indexKey: String by config.string(DEFAULT_INDEX_KEY)
|
||||
|
||||
/**
|
||||
* Set specific attribute in the descriptor
|
||||
*/
|
||||
fun ItemDescriptor.setAttribute(name: Name, value: Any?) {
|
||||
attributes {
|
||||
set(name, value)
|
||||
public companion object{
|
||||
public const val DEFAULT_INDEX_KEY: String = "@index"
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
fun ItemDescriptor.validateItem(item: MetaItem<*>?): Boolean {
|
||||
public fun ItemDescriptor.validateItem(item: MetaItem<*>?): Boolean {
|
||||
if (item == null) return !required
|
||||
return when (this) {
|
||||
is ValueDescriptor -> isAllowedValue(item.value ?: return false)
|
||||
@ -73,7 +70,7 @@ fun ItemDescriptor.validateItem(item: MetaItem<*>?): Boolean {
|
||||
* @author Alexander Nozik
|
||||
*/
|
||||
@DFBuilder
|
||||
class NodeDescriptor(config: Config = Config()) : ItemDescriptor(config) {
|
||||
public class NodeDescriptor(config: Config = Config()) : ItemDescriptor(config) {
|
||||
init {
|
||||
config[IS_NODE_KEY] = true
|
||||
}
|
||||
@ -90,14 +87,12 @@ class NodeDescriptor(config: Config = Config()) : ItemDescriptor(config) {
|
||||
*
|
||||
* @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)
|
||||
|
||||
val items: Map<String, ItemDescriptor>
|
||||
public val items: Map<String, ItemDescriptor>
|
||||
get() = config.getIndexed(ITEM_KEY).mapValues { (_, item) ->
|
||||
val node = item.node ?: error("Node descriptor must be a node")
|
||||
if (node[IS_NODE_KEY].boolean == true) {
|
||||
@ -111,7 +106,7 @@ class NodeDescriptor(config: Config = Config()) : ItemDescriptor(config) {
|
||||
* The map of children node descriptors
|
||||
*/
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
val nodes: Map<String, NodeDescriptor>
|
||||
public val nodes: Map<String, NodeDescriptor>
|
||||
get() = config.getIndexed(ITEM_KEY).entries.filter {
|
||||
it.value.node[IS_NODE_KEY].boolean == true
|
||||
}.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 {
|
||||
it.value.node[IS_NODE_KEY].boolean != true
|
||||
}.associate { (name, item) ->
|
||||
@ -154,39 +149,43 @@ class NodeDescriptor(config: Config = Config()) : ItemDescriptor(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)
|
||||
}
|
||||
|
||||
fun item(name: String, descriptor: ItemDescriptor) {
|
||||
public fun item(name: String, descriptor: ItemDescriptor) {
|
||||
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))
|
||||
}
|
||||
|
||||
fun node(name: String, block: NodeDescriptor.() -> Unit) {
|
||||
public fun node(name: String, block: NodeDescriptor.() -> Unit) {
|
||||
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" }
|
||||
item(name, ValueDescriptor().apply(block))
|
||||
}
|
||||
|
||||
fun value(name: String, block: ValueDescriptor.() -> Unit) {
|
||||
public fun value(name: String, block: ValueDescriptor.() -> Unit) {
|
||||
value(name.toName(), block)
|
||||
}
|
||||
|
||||
companion object {
|
||||
public companion object {
|
||||
|
||||
val ITEM_KEY = "item".asName()
|
||||
val IS_NODE_KEY = "@isNode".asName()
|
||||
internal val ITEM_KEY: Name = "item".asName()
|
||||
internal val IS_NODE_KEY: Name = "@isNode".asName()
|
||||
|
||||
const val DEFAULT_INDEX_KEY = "@index"
|
||||
|
||||
inline operator fun invoke(block: NodeDescriptor.() -> Unit) = NodeDescriptor().apply(block)
|
||||
public inline operator fun invoke(block: NodeDescriptor.() -> Unit): NodeDescriptor = NodeDescriptor().apply(block)
|
||||
|
||||
//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
|
||||
*/
|
||||
operator fun ItemDescriptor.get(name: Name): ItemDescriptor? {
|
||||
public operator fun ItemDescriptor.get(name: Name): ItemDescriptor? {
|
||||
if (name.isEmpty()) return this
|
||||
return when (this) {
|
||||
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
|
||||
@ -213,7 +212,7 @@ operator fun ItemDescriptor.get(name: String): ItemDescriptor? = get(name.toName
|
||||
* @author Alexander Nozik
|
||||
*/
|
||||
@DFBuilder
|
||||
class ValueDescriptor(config: Config = Config()) : ItemDescriptor(config) {
|
||||
public class ValueDescriptor(config: Config = Config()) : ItemDescriptor(config) {
|
||||
|
||||
/**
|
||||
* True if the value is required
|
||||
@ -227,9 +226,9 @@ class ValueDescriptor(config: Config = Config()) : ItemDescriptor(config) {
|
||||
*
|
||||
* @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)
|
||||
}
|
||||
|
||||
@ -238,9 +237,9 @@ class ValueDescriptor(config: Config = Config()) : ItemDescriptor(config) {
|
||||
*
|
||||
* @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)
|
||||
}
|
||||
|
||||
@ -251,7 +250,7 @@ class ValueDescriptor(config: Config = Config()) : ItemDescriptor(config) {
|
||||
* @param value
|
||||
* @return
|
||||
*/
|
||||
fun isAllowedValue(value: Value): Boolean {
|
||||
public fun isAllowedValue(value: Value): Boolean {
|
||||
return (type?.let { it.contains(ValueType.STRING) || it.contains(value.type) } ?: true)
|
||||
&& (allowedValues.isEmpty() || allowedValues.contains(value))
|
||||
}
|
||||
@ -262,7 +261,7 @@ class ValueDescriptor(config: Config = Config()) : ItemDescriptor(config) {
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
var allowedValues: List<Value> by config.item().convert(
|
||||
public var allowedValues: List<Value> by config.item().convert(
|
||||
reader = {
|
||||
val value = it.value
|
||||
when {
|
||||
@ -279,7 +278,7 @@ class ValueDescriptor(config: Config = Config()) : ItemDescriptor(config) {
|
||||
/**
|
||||
* 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) }
|
||||
}
|
||||
}
|
||||
@ -287,7 +286,7 @@ class ValueDescriptor(config: Config = Config()) : ItemDescriptor(config) {
|
||||
/**
|
||||
* 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 {
|
||||
config.update(other.config)
|
||||
config.update(this@plus.config)
|
||||
|
@ -36,7 +36,7 @@ class DescriptorTest {
|
||||
|
||||
@Test
|
||||
fun testDefaultMetaNode(){
|
||||
val meta = descriptor.buildDefaultMeta()
|
||||
val meta = descriptor.defaultMeta()
|
||||
assertEquals(false, meta["aNode.otherNode.otherValue"].boolean)
|
||||
}
|
||||
}
|
@ -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 }
|
Loading…
Reference in New Issue
Block a user