Cover corner cases in JsonMeta
This commit is contained in:
parent
a3479e74f7
commit
c8bd3390cb
@ -10,6 +10,7 @@
|
|||||||
- Kotlin 1.5.10
|
- Kotlin 1.5.10
|
||||||
- Build tools 0.10.0
|
- Build tools 0.10.0
|
||||||
- Relaxed type restriction on `MetaConverter`. Now nullables are available.
|
- Relaxed type restriction on `MetaConverter`. Now nullables are available.
|
||||||
|
- **Huge API-breaking refactoring of Meta**. Meta now can hava both value and children.
|
||||||
|
|
||||||
### Deprecated
|
### Deprecated
|
||||||
- Direct use of `Config`
|
- Direct use of `Config`
|
||||||
@ -17,6 +18,7 @@
|
|||||||
### Removed
|
### Removed
|
||||||
- Public PluginManager mutability
|
- Public PluginManager mutability
|
||||||
- Tables and tables-exposed moved to the separate project `tables.kt`
|
- Tables and tables-exposed moved to the separate project `tables.kt`
|
||||||
|
- BinaryMetaFormat. Use CBOR encoding instead
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
- Proper json array index treatment.
|
- Proper json array index treatment.
|
||||||
|
@ -95,8 +95,8 @@ public open class Context internal constructor(
|
|||||||
|
|
||||||
override fun toMeta(): Meta = Meta {
|
override fun toMeta(): Meta = Meta {
|
||||||
"parent" to parent?.name
|
"parent" to parent?.name
|
||||||
"properties" put properties.layers.firstOrNull()
|
properties.layers.firstOrNull()?.let { set("properties", it) }
|
||||||
"plugins" put plugins.map { it.toMeta().asMetaItem() }
|
"plugins" put plugins.map { it.toMeta() }
|
||||||
}
|
}
|
||||||
|
|
||||||
public companion object {
|
public companion object {
|
||||||
|
@ -1,30 +1,30 @@
|
|||||||
package space.kscience.dataforge.properties
|
package space.kscience.dataforge.properties
|
||||||
|
|
||||||
|
|
||||||
import space.kscience.dataforge.meta.MutableMeta
|
import space.kscience.dataforge.meta.*
|
||||||
import space.kscience.dataforge.meta.get
|
|
||||||
import space.kscience.dataforge.meta.transformations.MetaConverter
|
import space.kscience.dataforge.meta.transformations.MetaConverter
|
||||||
import space.kscience.dataforge.meta.transformations.nullableItemToObject
|
import space.kscience.dataforge.meta.transformations.nullableMetaToObject
|
||||||
import space.kscience.dataforge.meta.transformations.nullableObjectToMetaItem
|
import space.kscience.dataforge.meta.transformations.nullableObjectToMeta
|
||||||
import space.kscience.dataforge.misc.DFExperimental
|
import space.kscience.dataforge.misc.DFExperimental
|
||||||
import space.kscience.dataforge.names.Name
|
import space.kscience.dataforge.names.Name
|
||||||
|
import space.kscience.dataforge.names.startsWith
|
||||||
|
|
||||||
@DFExperimental
|
@DFExperimental
|
||||||
public class MetaProperty<T : Any>(
|
public class MetaProperty<T : Any>(
|
||||||
public val meta: MutableMeta,
|
public val meta: ObservableMutableMeta,
|
||||||
public val name: Name,
|
public val name: Name,
|
||||||
public val converter: MetaConverter<T>,
|
public val converter: MetaConverter<T>,
|
||||||
) : Property<T?> {
|
) : Property<T?> {
|
||||||
|
|
||||||
override var value: T?
|
override var value: T?
|
||||||
get() = converter.nullableItemToObject(meta[name])
|
get() = converter.nullableMetaToObject(meta[name])
|
||||||
set(value) {
|
set(value) {
|
||||||
meta[name] = converter.nullableObjectToMetaItem(value)
|
meta[name] = converter.nullableObjectToMeta(value) ?: Meta.EMPTY
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onChange(owner: Any?, callback: (T?) -> Unit) {
|
override fun onChange(owner: Any?, callback: (T?) -> Unit) {
|
||||||
meta.onChange(owner) { name, oldItem, newItem ->
|
meta.onChange(owner) { name ->
|
||||||
if (name.startsWith(this.name) && oldItem != newItem) callback(converter.nullableItemToObject(newItem))
|
if (name.startsWith(this@MetaProperty.name)) callback(converter.nullableMetaToObject(get(name)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3,6 +3,7 @@ package space.kscience.dataforge.properties
|
|||||||
|
|
||||||
import space.kscience.dataforge.meta.ObservableMeta
|
import space.kscience.dataforge.meta.ObservableMeta
|
||||||
import space.kscience.dataforge.misc.DFExperimental
|
import space.kscience.dataforge.misc.DFExperimental
|
||||||
|
import space.kscience.dataforge.names.startsWith
|
||||||
import space.kscience.dataforge.names.toName
|
import space.kscience.dataforge.names.toName
|
||||||
import kotlin.reflect.KMutableProperty1
|
import kotlin.reflect.KMutableProperty1
|
||||||
|
|
||||||
@ -16,8 +17,8 @@ public fun <P : ObservableMeta, T : Any> P.property(property: KMutableProperty1<
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun onChange(owner: Any?, callback: (T?) -> Unit) {
|
override fun onChange(owner: Any?, callback: (T?) -> Unit) {
|
||||||
this@property.onChange(this) { name, oldItem, newItem ->
|
this@property.onChange(this) { name ->
|
||||||
if (name.startsWith(property.name.toName()) && oldItem != newItem) {
|
if (name.startsWith(property.name.toName())) {
|
||||||
callback(property.get(this@property))
|
callback(property.get(this@property))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -4,7 +4,6 @@ import kotlinx.coroutines.*
|
|||||||
import kotlinx.coroutines.flow.*
|
import kotlinx.coroutines.flow.*
|
||||||
import space.kscience.dataforge.data.Data.Companion.TYPE_OF_NOTHING
|
import space.kscience.dataforge.data.Data.Companion.TYPE_OF_NOTHING
|
||||||
import space.kscience.dataforge.meta.Meta
|
import space.kscience.dataforge.meta.Meta
|
||||||
import space.kscience.dataforge.meta.set
|
|
||||||
import space.kscience.dataforge.names.*
|
import space.kscience.dataforge.names.*
|
||||||
import kotlin.reflect.KType
|
import kotlin.reflect.KType
|
||||||
|
|
||||||
|
@ -54,7 +54,7 @@ public interface GroupRule {
|
|||||||
val data = set.getData(name)
|
val data = set.getData(name)
|
||||||
|
|
||||||
@Suppress("NULLABLE_EXTENSION_OPERATOR_WITH_SAFE_CALL_RECEIVER")
|
@Suppress("NULLABLE_EXTENSION_OPERATOR_WITH_SAFE_CALL_RECEIVER")
|
||||||
val tagValue = data?.meta[key]?.string ?: defaultTagValue
|
val tagValue = data?.meta?.get(key)?.string ?: defaultTagValue
|
||||||
map.getOrPut(tagValue) { ActiveDataTree(set.dataType) }.emit(name, data)
|
map.getOrPut(tagValue) { ActiveDataTree(set.dataType) }.emit(name, data)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -10,43 +10,49 @@ import space.kscience.dataforge.io.MetaFormat
|
|||||||
import space.kscience.dataforge.io.MetaFormatFactory
|
import space.kscience.dataforge.io.MetaFormatFactory
|
||||||
import space.kscience.dataforge.io.readUtf8String
|
import space.kscience.dataforge.io.readUtf8String
|
||||||
import space.kscience.dataforge.io.writeUtf8String
|
import space.kscience.dataforge.io.writeUtf8String
|
||||||
import space.kscience.dataforge.meta.*
|
import space.kscience.dataforge.meta.Meta
|
||||||
import space.kscience.dataforge.meta.descriptors.ItemDescriptor
|
import space.kscience.dataforge.meta.descriptors.MetaDescriptor
|
||||||
import space.kscience.dataforge.meta.descriptors.NodeDescriptor
|
import space.kscience.dataforge.meta.descriptors.get
|
||||||
|
import space.kscience.dataforge.meta.isLeaf
|
||||||
import space.kscience.dataforge.misc.DFExperimental
|
import space.kscience.dataforge.misc.DFExperimental
|
||||||
import space.kscience.dataforge.names.NameToken
|
import space.kscience.dataforge.names.NameToken
|
||||||
import space.kscience.dataforge.names.withIndex
|
import space.kscience.dataforge.names.withIndex
|
||||||
import space.kscience.dataforge.values.ListValue
|
import space.kscience.dataforge.values.ListValue
|
||||||
import space.kscience.dataforge.values.Null
|
import space.kscience.dataforge.values.Null
|
||||||
|
import space.kscience.dataforge.values.Value
|
||||||
import space.kscience.dataforge.values.parseValue
|
import space.kscience.dataforge.values.parseValue
|
||||||
|
import kotlin.collections.component1
|
||||||
|
import kotlin.collections.component2
|
||||||
|
import kotlin.collections.set
|
||||||
|
|
||||||
public fun Meta.toYaml(): YamlMap {
|
public fun Meta.toYaml(): YamlMap {
|
||||||
val map: Map<String, Any?> = items.entries.associate { (key, item) ->
|
val map: Map<String, Any?> = items.entries.associate { (key, item) ->
|
||||||
key.toString() to when (item) {
|
key.toString() to if (item.isLeaf) {
|
||||||
is MetaItemValue -> {
|
item.value?.value
|
||||||
item.value.value
|
} else {
|
||||||
}
|
item.toYaml()
|
||||||
is MetaItemNode -> {
|
|
||||||
item.node.toYaml()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return YamlMap(map)
|
return YamlMap(map)
|
||||||
}
|
}
|
||||||
|
|
||||||
private class YamlMeta(private val yamlMap: YamlMap, private val descriptor: NodeDescriptor? = null) : AbstractTypedMeta() {
|
private class YamlMeta(private val yamlMap: YamlMap, private val descriptor: MetaDescriptor? = null) : Meta {
|
||||||
|
|
||||||
private fun buildItems(): Map<NameToken, MetaItem> {
|
override val value: Value?
|
||||||
val map = LinkedHashMap<NameToken, MetaItem>()
|
get() = yamlMap.getStringOrNull(null)?.parseValue()
|
||||||
|
|
||||||
|
private fun buildItems(): Map<NameToken, Meta> {
|
||||||
|
val map = LinkedHashMap<NameToken, Meta>()
|
||||||
|
|
||||||
yamlMap.content.entries.forEach { (key, value) ->
|
yamlMap.content.entries.forEach { (key, value) ->
|
||||||
val stringKey = key.toString()
|
val stringKey = key.toString()
|
||||||
val itemDescriptor = descriptor?.items?.get(stringKey)
|
val itemDescriptor = descriptor?.get(stringKey)
|
||||||
val token = NameToken(stringKey)
|
val token = NameToken(stringKey)
|
||||||
when (value) {
|
when (value) {
|
||||||
YamlNull -> Null.asMetaItem()
|
YamlNull -> Meta(Null)
|
||||||
is YamlLiteral -> map[token] = value.content.parseValue().asMetaItem()
|
is YamlLiteral -> map[token] = Meta(value.content.parseValue())
|
||||||
is YamlMap -> map[token] = value.toMeta().asMetaItem()
|
is YamlMap -> map[token] = value.toMeta()
|
||||||
is YamlList -> if (value.all { it is YamlLiteral }) {
|
is YamlList -> if (value.all { it is YamlLiteral }) {
|
||||||
val listValue = ListValue(
|
val listValue = ListValue(
|
||||||
value.map {
|
value.map {
|
||||||
@ -54,29 +60,33 @@ private class YamlMeta(private val yamlMap: YamlMap, private val descriptor: Nod
|
|||||||
(it as YamlLiteral).content.parseValue()
|
(it as YamlLiteral).content.parseValue()
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
map[token] = MetaItemValue(listValue)
|
map[token] = Meta(listValue)
|
||||||
} else value.forEachIndexed { index, yamlElement ->
|
} else value.forEachIndexed { index, yamlElement ->
|
||||||
val indexKey = (itemDescriptor as? NodeDescriptor)?.indexKey ?: ItemDescriptor.DEFAULT_INDEX_KEY
|
val indexKey = itemDescriptor?.indexKey
|
||||||
val indexValue: String = (yamlElement as? YamlMap)?.getStringOrNull(indexKey)
|
val indexValue: String = (yamlElement as? YamlMap)?.getStringOrNull(indexKey)
|
||||||
?: 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.
|
||||||
|
|
||||||
val tokenWithIndex = token.withIndex(indexValue)
|
val tokenWithIndex = token.withIndex(indexValue)
|
||||||
map[tokenWithIndex] = yamlElement.toMetaItem(itemDescriptor)
|
map[tokenWithIndex] = yamlElement.toMeta(itemDescriptor)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return map
|
return map
|
||||||
}
|
}
|
||||||
|
|
||||||
override val items: Map<NameToken, MetaItem> get() = buildItems()
|
override val items: Map<NameToken, Meta> get() = buildItems()
|
||||||
|
|
||||||
|
override fun toString(): String = Meta.toString(this)
|
||||||
|
override fun equals(other: Any?): Boolean = Meta.equals(this, other as? Meta)
|
||||||
|
override fun hashCode(): Int = Meta.hashCode(this)
|
||||||
}
|
}
|
||||||
|
|
||||||
public fun YamlElement.toMetaItem(descriptor: ItemDescriptor? = null): MetaItem = when (this) {
|
public fun YamlElement.toMeta(descriptor: MetaDescriptor? = null): Meta = when (this) {
|
||||||
YamlNull -> Null.asMetaItem()
|
YamlNull -> Meta(Null)
|
||||||
is YamlLiteral -> content.parseValue().asMetaItem()
|
is YamlLiteral -> Meta(content.parseValue())
|
||||||
is YamlMap -> toMeta().asMetaItem()
|
is YamlMap -> toMeta()
|
||||||
//We can't return multiple items therefore we create top level node
|
//We can't return multiple items therefore we create top level node
|
||||||
is YamlList -> YamlMap(mapOf("@yamlArray" to this)).toMetaItem(descriptor)
|
is YamlList -> YamlMap(mapOf("@yamlArray" to this)).toMeta(descriptor)
|
||||||
}
|
}
|
||||||
|
|
||||||
public fun YamlMap.toMeta(): Meta = YamlMeta(this)
|
public fun YamlMap.toMeta(): Meta = YamlMeta(this)
|
||||||
@ -88,13 +98,13 @@ public fun YamlMap.toMeta(): Meta = YamlMeta(this)
|
|||||||
@DFExperimental
|
@DFExperimental
|
||||||
public class YamlMetaFormat(private val meta: Meta) : MetaFormat {
|
public class YamlMetaFormat(private val meta: Meta) : MetaFormat {
|
||||||
|
|
||||||
override fun writeMeta(output: Output, meta: Meta, descriptor: NodeDescriptor?) {
|
override fun writeMeta(output: Output, meta: Meta, descriptor: MetaDescriptor?) {
|
||||||
val yaml = meta.toYaml()
|
val yaml = meta.toYaml()
|
||||||
val string = Yaml.encodeToString(yaml)
|
val string = Yaml.encodeToString(yaml)
|
||||||
output.writeUtf8String(string)
|
output.writeUtf8String(string)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun readMeta(input: Input, descriptor: NodeDescriptor?): Meta {
|
override fun readMeta(input: Input, descriptor: MetaDescriptor?): Meta {
|
||||||
val yaml = Yaml.decodeYamlMapFromString(input.readUtf8String())
|
val yaml = Yaml.decodeYamlMapFromString(input.readUtf8String())
|
||||||
return yaml.toMeta()
|
return yaml.toMeta()
|
||||||
}
|
}
|
||||||
@ -113,10 +123,10 @@ public class YamlMetaFormat(private val meta: Meta) : MetaFormat {
|
|||||||
|
|
||||||
private val default = YamlMetaFormat()
|
private val default = YamlMetaFormat()
|
||||||
|
|
||||||
override fun writeMeta(output: Output, meta: Meta, descriptor: NodeDescriptor?): Unit =
|
override fun writeMeta(output: Output, meta: Meta, descriptor: MetaDescriptor?): Unit =
|
||||||
default.writeMeta(output, meta, descriptor)
|
default.writeMeta(output, meta, descriptor)
|
||||||
|
|
||||||
override fun readMeta(input: Input, descriptor: NodeDescriptor?): Meta =
|
override fun readMeta(input: Input, descriptor: MetaDescriptor?): Meta =
|
||||||
default.readMeta(input, descriptor)
|
default.readMeta(input, descriptor)
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,133 +0,0 @@
|
|||||||
package space.kscience.dataforge.io
|
|
||||||
|
|
||||||
import io.ktor.utils.io.core.*
|
|
||||||
import space.kscience.dataforge.context.Context
|
|
||||||
import space.kscience.dataforge.meta.*
|
|
||||||
import space.kscience.dataforge.meta.descriptors.NodeDescriptor
|
|
||||||
import space.kscience.dataforge.values.*
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A DataForge-specific simplified binary format for meta
|
|
||||||
* TODO add description
|
|
||||||
*/
|
|
||||||
public object BinaryMetaFormat : MetaFormat, MetaFormatFactory {
|
|
||||||
override val shortName: String = "bin"
|
|
||||||
override val key: Short = 0x4249//BI
|
|
||||||
|
|
||||||
override fun invoke(meta: Meta, context: Context): MetaFormat = this
|
|
||||||
|
|
||||||
override fun readMeta(input: Input, descriptor: NodeDescriptor?): Meta {
|
|
||||||
return (input.readMetaItem() as MetaItemNode).node
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun Output.writeChar(char: Char) = writeByte(char.code.toByte())
|
|
||||||
|
|
||||||
private fun Output.writeString(str: String) {
|
|
||||||
writeInt(str.length)
|
|
||||||
writeFully(str.encodeToByteArray())
|
|
||||||
}
|
|
||||||
|
|
||||||
public fun Output.writeValue(value: Value): Unit = when (value.type) {
|
|
||||||
ValueType.NUMBER -> when (value.value) {
|
|
||||||
is Short -> {
|
|
||||||
writeChar('s')
|
|
||||||
writeShort(value.short)
|
|
||||||
}
|
|
||||||
is Int -> {
|
|
||||||
writeChar('i')
|
|
||||||
writeInt(value.int)
|
|
||||||
}
|
|
||||||
is Long -> {
|
|
||||||
writeChar('l')
|
|
||||||
writeLong(value.long)
|
|
||||||
}
|
|
||||||
is Float -> {
|
|
||||||
writeChar('f')
|
|
||||||
writeFloat(value.float)
|
|
||||||
}
|
|
||||||
else -> {
|
|
||||||
writeChar('d')
|
|
||||||
writeDouble(value.double)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ValueType.STRING -> {
|
|
||||||
writeChar('S')
|
|
||||||
writeString(value.string)
|
|
||||||
}
|
|
||||||
ValueType.BOOLEAN -> {
|
|
||||||
if (value.boolean) {
|
|
||||||
writeChar('+')
|
|
||||||
} else {
|
|
||||||
writeChar('-')
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ValueType.NULL -> {
|
|
||||||
writeChar('N')
|
|
||||||
}
|
|
||||||
ValueType.LIST -> {
|
|
||||||
writeChar('L')
|
|
||||||
writeInt(value.list.size)
|
|
||||||
value.list.forEach {
|
|
||||||
writeValue(it)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun writeMeta(
|
|
||||||
output: Output,
|
|
||||||
meta: Meta,
|
|
||||||
descriptor: space.kscience.dataforge.meta.descriptors.NodeDescriptor?,
|
|
||||||
) {
|
|
||||||
output.writeChar('M')
|
|
||||||
output.writeInt(meta.items.size)
|
|
||||||
meta.items.forEach { (key, item) ->
|
|
||||||
output.writeString(key.toString())
|
|
||||||
when (item) {
|
|
||||||
is MetaItemValue -> {
|
|
||||||
output.writeValue(item.value)
|
|
||||||
}
|
|
||||||
is MetaItemNode -> {
|
|
||||||
writeObject(output, item.node)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun Input.readString(): String {
|
|
||||||
val length = readInt()
|
|
||||||
val array = readBytes(length)
|
|
||||||
return array.decodeToString()
|
|
||||||
}
|
|
||||||
|
|
||||||
@Suppress("UNCHECKED_CAST")
|
|
||||||
public fun Input.readMetaItem(): TypedMetaItem<MutableMeta> {
|
|
||||||
return when (val keyChar = readByte().toInt().toChar()) {
|
|
||||||
'S' -> MetaItemValue(StringValue(readString()))
|
|
||||||
'N' -> MetaItemValue(Null)
|
|
||||||
'+' -> MetaItemValue(True)
|
|
||||||
'-' -> MetaItemValue(True)
|
|
||||||
's' -> MetaItemValue(NumberValue(readShort()))
|
|
||||||
'i' -> MetaItemValue(NumberValue(readInt()))
|
|
||||||
'l' -> MetaItemValue(NumberValue(readInt()))
|
|
||||||
'f' -> MetaItemValue(NumberValue(readFloat()))
|
|
||||||
'd' -> MetaItemValue(NumberValue(readDouble()))
|
|
||||||
'L' -> {
|
|
||||||
val length = readInt()
|
|
||||||
val list = (1..length).map { (readMetaItem() as MetaItemValue).value }
|
|
||||||
MetaItemValue(Value.of(list))
|
|
||||||
}
|
|
||||||
'M' -> {
|
|
||||||
val length = readInt()
|
|
||||||
val meta = Meta {
|
|
||||||
(1..length).forEach { _ ->
|
|
||||||
val name = readString()
|
|
||||||
val item = readMetaItem()
|
|
||||||
set(name, item)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
MetaItemNode(meta)
|
|
||||||
}
|
|
||||||
else -> error("Unknown serialization key character: $keyChar")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -15,7 +15,7 @@ import space.kscience.dataforge.names.toName
|
|||||||
private class PartDescriptor : Scheme() {
|
private class PartDescriptor : Scheme() {
|
||||||
var offset by int(0)
|
var offset by int(0)
|
||||||
var size by int(0)
|
var size by int(0)
|
||||||
var partMeta by node("meta".toName())
|
var partMeta by item("meta".toName())
|
||||||
|
|
||||||
companion object : SchemeSpec<PartDescriptor>(::PartDescriptor) {
|
companion object : SchemeSpec<PartDescriptor>(::PartDescriptor) {
|
||||||
val MULTIPART_KEY = ENVELOPE_NODE_KEY + "multipart"
|
val MULTIPART_KEY = ENVELOPE_NODE_KEY + "multipart"
|
||||||
@ -86,15 +86,15 @@ public fun EnvelopeBuilder.envelopes(
|
|||||||
public fun Envelope.parts(): EnvelopeParts {
|
public fun Envelope.parts(): EnvelopeParts {
|
||||||
if (data == null) return emptyList()
|
if (data == null) return emptyList()
|
||||||
//TODO add zip folder reader
|
//TODO add zip folder reader
|
||||||
val parts = meta.getIndexed(PARTS_KEY).values.mapNotNull { it.node }.map {
|
val parts = meta.getIndexed(PARTS_KEY).values.map {
|
||||||
PartDescriptor.read(it)
|
PartDescriptor.read(it)
|
||||||
}
|
}
|
||||||
return if (parts.isEmpty()) {
|
return if (parts.isEmpty()) {
|
||||||
listOf(EnvelopePart(data!!, meta[MULTIPART_KEY].node))
|
listOf(EnvelopePart(data!!, meta[MULTIPART_KEY]))
|
||||||
} else {
|
} else {
|
||||||
parts.map {
|
parts.map {
|
||||||
val binary = data!!.view(it.offset, it.size)
|
val binary = data!!.view(it.offset, it.size)
|
||||||
val meta = Laminate(it.partMeta, meta[MULTIPART_KEY].node)
|
val meta = Laminate(it.partMeta, meta[MULTIPART_KEY])
|
||||||
EnvelopePart(binary, meta)
|
EnvelopePart(binary, meta)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -6,13 +6,11 @@ import space.kscience.dataforge.context.Factory
|
|||||||
import space.kscience.dataforge.io.IOFormat.Companion.NAME_KEY
|
import space.kscience.dataforge.io.IOFormat.Companion.NAME_KEY
|
||||||
import space.kscience.dataforge.io.IOFormatFactory.Companion.IO_FORMAT_TYPE
|
import space.kscience.dataforge.io.IOFormatFactory.Companion.IO_FORMAT_TYPE
|
||||||
import space.kscience.dataforge.meta.Meta
|
import space.kscience.dataforge.meta.Meta
|
||||||
import space.kscience.dataforge.meta.MetaItemValue
|
|
||||||
import space.kscience.dataforge.meta.MetaRepr
|
import space.kscience.dataforge.meta.MetaRepr
|
||||||
import space.kscience.dataforge.misc.Named
|
import space.kscience.dataforge.misc.Named
|
||||||
import space.kscience.dataforge.misc.Type
|
import space.kscience.dataforge.misc.Type
|
||||||
import space.kscience.dataforge.names.Name
|
import space.kscience.dataforge.names.Name
|
||||||
import space.kscience.dataforge.names.asName
|
import space.kscience.dataforge.names.asName
|
||||||
import space.kscience.dataforge.values.Value
|
|
||||||
import kotlin.reflect.KType
|
import kotlin.reflect.KType
|
||||||
import kotlin.reflect.typeOf
|
import kotlin.reflect.typeOf
|
||||||
|
|
||||||
@ -117,19 +115,19 @@ public object DoubleIOFormat : IOFormat<Double>, IOFormatFactory<Double> {
|
|||||||
override fun readObject(input: Input): Double = input.readDouble()
|
override fun readObject(input: Input): Double = input.readDouble()
|
||||||
}
|
}
|
||||||
|
|
||||||
public object ValueIOFormat : IOFormat<Value>, IOFormatFactory<Value> {
|
//public object ValueIOFormat : IOFormat<Value>, IOFormatFactory<Value> {
|
||||||
override fun invoke(meta: Meta, context: Context): IOFormat<Value> = this
|
// override fun invoke(meta: Meta, context: Context): IOFormat<Value> = this
|
||||||
|
//
|
||||||
override val name: Name = "value".asName()
|
// override val name: Name = "value".asName()
|
||||||
|
//
|
||||||
override val type: KType get() = typeOf<Value>()
|
// override val type: KType get() = typeOf<Value>()
|
||||||
|
//
|
||||||
override fun writeObject(output: Output, obj: Value) {
|
// override fun writeObject(output: Output, obj: Value) {
|
||||||
BinaryMetaFormat.run { output.writeValue(obj) }
|
// BinaryMetaFormat.run { output.writeValue(obj) }
|
||||||
}
|
// }
|
||||||
|
//
|
||||||
override fun readObject(input: Input): Value {
|
// override fun readObject(input: Input): Value {
|
||||||
return (BinaryMetaFormat.run { input.readMetaItem() } as? MetaItemValue)?.value
|
// return (BinaryMetaFormat.run { input.readMetaItem() } as? MetaItemValue)?.value
|
||||||
?: error("The item is not a value")
|
// ?: error("The item is not a value")
|
||||||
}
|
// }
|
||||||
}
|
//}
|
@ -6,7 +6,9 @@ import space.kscience.dataforge.io.IOFormat.Companion.META_KEY
|
|||||||
import space.kscience.dataforge.io.IOFormat.Companion.NAME_KEY
|
import space.kscience.dataforge.io.IOFormat.Companion.NAME_KEY
|
||||||
import space.kscience.dataforge.io.IOFormatFactory.Companion.IO_FORMAT_TYPE
|
import space.kscience.dataforge.io.IOFormatFactory.Companion.IO_FORMAT_TYPE
|
||||||
import space.kscience.dataforge.io.MetaFormatFactory.Companion.META_FORMAT_TYPE
|
import space.kscience.dataforge.io.MetaFormatFactory.Companion.META_FORMAT_TYPE
|
||||||
import space.kscience.dataforge.meta.*
|
import space.kscience.dataforge.meta.Meta
|
||||||
|
import space.kscience.dataforge.meta.get
|
||||||
|
import space.kscience.dataforge.meta.string
|
||||||
import space.kscience.dataforge.names.Name
|
import space.kscience.dataforge.names.Name
|
||||||
import space.kscience.dataforge.names.toName
|
import space.kscience.dataforge.names.toName
|
||||||
import kotlin.native.concurrent.ThreadLocal
|
import kotlin.native.concurrent.ThreadLocal
|
||||||
@ -19,13 +21,13 @@ public class IOPlugin(meta: Meta) : AbstractPlugin(meta) {
|
|||||||
context.gather<IOFormatFactory<*>>(IO_FORMAT_TYPE).values
|
context.gather<IOFormatFactory<*>>(IO_FORMAT_TYPE).values
|
||||||
}
|
}
|
||||||
|
|
||||||
public fun <T : Any> resolveIOFormat(item: MetaItem, type: KClass<out T>): IOFormat<T>? {
|
public fun <T : Any> resolveIOFormat(item: Meta, type: KClass<out T>): IOFormat<T>? {
|
||||||
val key = item.string ?: item.node[NAME_KEY]?.string ?: error("Format name not defined")
|
val key = item.string ?: item[NAME_KEY]?.string ?: error("Format name not defined")
|
||||||
val name = key.toName()
|
val name = key.toName()
|
||||||
return ioFormatFactories.find { it.name == name }?.let {
|
return ioFormatFactories.find { it.name == name }?.let {
|
||||||
@Suppress("UNCHECKED_CAST")
|
@Suppress("UNCHECKED_CAST")
|
||||||
if (it.type != type) error("Format type ${it.type} is not the same as requested type $type")
|
if (it.type != type) error("Format type ${it.type} is not the same as requested type $type")
|
||||||
else it.invoke(item.node[META_KEY].node ?: Meta.EMPTY, context) as IOFormat<T>
|
else it.invoke(item[META_KEY] ?: Meta.EMPTY, context) as IOFormat<T>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -47,9 +49,9 @@ public class IOPlugin(meta: Meta) : AbstractPlugin(meta) {
|
|||||||
private fun resolveEnvelopeFormat(name: Name, meta: Meta = Meta.EMPTY): EnvelopeFormat? =
|
private fun resolveEnvelopeFormat(name: Name, meta: Meta = Meta.EMPTY): EnvelopeFormat? =
|
||||||
envelopeFormatFactories.find { it.name == name }?.invoke(meta, context)
|
envelopeFormatFactories.find { it.name == name }?.invoke(meta, context)
|
||||||
|
|
||||||
public fun resolveEnvelopeFormat(item: MetaItem): EnvelopeFormat? {
|
public fun resolveEnvelopeFormat(item: Meta): EnvelopeFormat? {
|
||||||
val name = item.string ?: item.node[NAME_KEY]?.string ?: error("Envelope format name not defined")
|
val name = item.string ?: item[NAME_KEY]?.string ?: error("Envelope format name not defined")
|
||||||
val meta = item.node[META_KEY].node ?: Meta.EMPTY
|
val meta = item[META_KEY] ?: Meta.EMPTY
|
||||||
return resolveEnvelopeFormat(name.toName(), meta)
|
return resolveEnvelopeFormat(name.toName(), meta)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -62,7 +64,7 @@ public class IOPlugin(meta: Meta) : AbstractPlugin(meta) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public companion object : PluginFactory<IOPlugin> {
|
public companion object : PluginFactory<IOPlugin> {
|
||||||
public val defaultMetaFormats: List<MetaFormatFactory> = listOf(JsonMetaFormat, BinaryMetaFormat)
|
public val defaultMetaFormats: List<MetaFormatFactory> = listOf(JsonMetaFormat)
|
||||||
public val defaultEnvelopeFormats: List<EnvelopeFormatFactory> =
|
public val defaultEnvelopeFormats: List<EnvelopeFormatFactory> =
|
||||||
listOf(TaggedEnvelopeFormat, TaglessEnvelopeFormat)
|
listOf(TaggedEnvelopeFormat, TaglessEnvelopeFormat)
|
||||||
|
|
||||||
|
@ -7,7 +7,7 @@ import io.ktor.utils.io.core.use
|
|||||||
import space.kscience.dataforge.context.Context
|
import space.kscience.dataforge.context.Context
|
||||||
import space.kscience.dataforge.io.MetaFormatFactory.Companion.META_FORMAT_TYPE
|
import space.kscience.dataforge.io.MetaFormatFactory.Companion.META_FORMAT_TYPE
|
||||||
import space.kscience.dataforge.meta.Meta
|
import space.kscience.dataforge.meta.Meta
|
||||||
import space.kscience.dataforge.meta.descriptors.NodeDescriptor
|
import space.kscience.dataforge.meta.descriptors.MetaDescriptor
|
||||||
import space.kscience.dataforge.misc.Type
|
import space.kscience.dataforge.misc.Type
|
||||||
import space.kscience.dataforge.names.Name
|
import space.kscience.dataforge.names.Name
|
||||||
import space.kscience.dataforge.names.asName
|
import space.kscience.dataforge.names.asName
|
||||||
@ -30,10 +30,10 @@ public interface MetaFormat : IOFormat<Meta> {
|
|||||||
public fun writeMeta(
|
public fun writeMeta(
|
||||||
output: Output,
|
output: Output,
|
||||||
meta: Meta,
|
meta: Meta,
|
||||||
descriptor: NodeDescriptor? = null,
|
descriptor: MetaDescriptor? = null,
|
||||||
)
|
)
|
||||||
|
|
||||||
public fun readMeta(input: Input, descriptor: NodeDescriptor? = null): Meta
|
public fun readMeta(input: Input, descriptor: MetaDescriptor? = null): Meta
|
||||||
}
|
}
|
||||||
|
|
||||||
@Type(META_FORMAT_TYPE)
|
@Type(META_FORMAT_TYPE)
|
||||||
|
@ -2,11 +2,12 @@ package space.kscience.dataforge.io
|
|||||||
|
|
||||||
import kotlinx.serialization.json.*
|
import kotlinx.serialization.json.*
|
||||||
import space.kscience.dataforge.meta.*
|
import space.kscience.dataforge.meta.*
|
||||||
import space.kscience.dataforge.meta.JsonMeta.Companion.JSON_ARRAY_KEY
|
|
||||||
import space.kscience.dataforge.values.ListValue
|
import space.kscience.dataforge.values.ListValue
|
||||||
|
import space.kscience.dataforge.values.double
|
||||||
import kotlin.test.Test
|
import kotlin.test.Test
|
||||||
import kotlin.test.assertEquals
|
import kotlin.test.assertEquals
|
||||||
|
|
||||||
|
|
||||||
fun Meta.toByteArray(format: MetaFormat = JsonMetaFormat) = buildByteArray {
|
fun Meta.toByteArray(format: MetaFormat = JsonMetaFormat) = buildByteArray {
|
||||||
format.writeObject(this@buildByteArray, this@toByteArray)
|
format.writeObject(this@buildByteArray, this@toByteArray)
|
||||||
}
|
}
|
||||||
@ -16,20 +17,6 @@ fun MetaFormat.fromByteArray(packet: ByteArray): Meta {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class MetaFormatTest {
|
class MetaFormatTest {
|
||||||
@Test
|
|
||||||
fun testBinaryMetaFormat() {
|
|
||||||
val meta = Meta {
|
|
||||||
"a" put 22
|
|
||||||
"node" put {
|
|
||||||
"b" put "DDD"
|
|
||||||
"c" put 11.1
|
|
||||||
"array" put doubleArrayOf(1.0, 2.0, 3.0)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
val bytes = meta.toByteArray(BinaryMetaFormat)
|
|
||||||
val result = BinaryMetaFormat.fromByteArray(bytes)
|
|
||||||
assertEquals(meta, result)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun testJsonMetaFormat() {
|
fun testJsonMetaFormat() {
|
||||||
@ -50,36 +37,36 @@ class MetaFormatTest {
|
|||||||
if (meta[it] != result[it]) error("${meta[it]} != ${result[it]}")
|
if (meta[it] != result[it]) error("${meta[it]} != ${result[it]}")
|
||||||
}
|
}
|
||||||
|
|
||||||
assertEquals<Meta>(meta, result)
|
assertEquals(meta, result)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun testJsonToMeta() {
|
fun testJsonToMeta() {
|
||||||
val json = buildJsonArray {
|
val json = buildJsonArray {
|
||||||
//top level array
|
//top level array
|
||||||
add(buildJsonArray {
|
addJsonArray {
|
||||||
add(JsonPrimitive(88))
|
add(88)
|
||||||
add(buildJsonObject {
|
addJsonObject {
|
||||||
put("c", "aasdad")
|
put("c", "aasdad")
|
||||||
put("d", true)
|
put("d", true)
|
||||||
})
|
|
||||||
})
|
|
||||||
add("value")
|
|
||||||
add(buildJsonArray {
|
|
||||||
add(JsonPrimitive(1.0))
|
|
||||||
add(JsonPrimitive(2.0))
|
|
||||||
add(JsonPrimitive(3.0))
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
val meta = json.toMeta().node!!
|
}
|
||||||
|
add("value")
|
||||||
|
addJsonArray {
|
||||||
|
add(1.0)
|
||||||
|
add(2.0)
|
||||||
|
add(3.0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
val meta = json.toMeta()
|
||||||
|
|
||||||
assertEquals(true, meta["$JSON_ARRAY_KEY[0].$JSON_ARRAY_KEY[1].d"].boolean)
|
assertEquals(true, meta["${Meta.JSON_ARRAY_KEY}[0].${Meta.JSON_ARRAY_KEY}[1].d"].boolean)
|
||||||
assertEquals("value", meta["$JSON_ARRAY_KEY[1]"].string)
|
assertEquals("value", meta["${Meta.JSON_ARRAY_KEY}[1]"].string)
|
||||||
assertEquals(listOf(1.0, 2.0, 3.0), meta["$JSON_ARRAY_KEY[2"].value?.list?.map { it.number.toDouble() })
|
assertEquals(listOf(1.0, 2.0, 3.0), meta["${Meta.JSON_ARRAY_KEY}[2]"]?.value?.list?.map { it.double })
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun testJsonStringToMeta(){
|
fun testJsonStringToMeta() {
|
||||||
val jsonString = """
|
val jsonString = """
|
||||||
{
|
{
|
||||||
"comments": [
|
"comments": [
|
||||||
@ -97,8 +84,8 @@ class MetaFormatTest {
|
|||||||
}
|
}
|
||||||
""".trimIndent()
|
""".trimIndent()
|
||||||
val json = Json.parseToJsonElement(jsonString)
|
val json = Json.parseToJsonElement(jsonString)
|
||||||
val meta = json.toMeta().node!!
|
val meta = json.toMeta()
|
||||||
assertEquals(ListValue.EMPTY, meta["comments"].value)
|
assertEquals(ListValue.EMPTY, meta["comments"]?.value)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
@ -5,7 +5,7 @@ import kotlinx.serialization.cbor.Cbor
|
|||||||
import kotlinx.serialization.json.Json
|
import kotlinx.serialization.json.Json
|
||||||
import space.kscience.dataforge.meta.Meta
|
import space.kscience.dataforge.meta.Meta
|
||||||
import space.kscience.dataforge.meta.MetaSerializer
|
import space.kscience.dataforge.meta.MetaSerializer
|
||||||
import space.kscience.dataforge.meta.TypedMetaItem
|
import space.kscience.dataforge.meta.seal
|
||||||
import space.kscience.dataforge.names.Name
|
import space.kscience.dataforge.names.Name
|
||||||
import space.kscience.dataforge.names.toName
|
import space.kscience.dataforge.names.toName
|
||||||
import kotlin.test.Test
|
import kotlin.test.Test
|
||||||
@ -28,7 +28,7 @@ class MetaSerializerTest {
|
|||||||
fun testMetaSerialization() {
|
fun testMetaSerialization() {
|
||||||
val string = JSON_PRETTY.encodeToString(MetaSerializer, meta)
|
val string = JSON_PRETTY.encodeToString(MetaSerializer, meta)
|
||||||
println(string)
|
println(string)
|
||||||
val restored = JSON_PLAIN.decodeFromString(MetaSerializer, string)
|
val restored = JSON_PLAIN.decodeFromString(MetaSerializer, string).seal()
|
||||||
assertEquals(meta, restored)
|
assertEquals(meta, restored)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -52,7 +52,7 @@ class MetaSerializerTest {
|
|||||||
@OptIn(ExperimentalSerializationApi::class)
|
@OptIn(ExperimentalSerializationApi::class)
|
||||||
@Test
|
@Test
|
||||||
fun testMetaItemDescriptor() {
|
fun testMetaItemDescriptor() {
|
||||||
val descriptor = TypedMetaItem.serializer(MetaSerializer).descriptor.getElementDescriptor(0)
|
val descriptor = MetaSerializer.descriptor.getElementDescriptor(0)
|
||||||
println(descriptor)
|
println(descriptor)
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -3,7 +3,7 @@ package space.kscience.dataforge.io
|
|||||||
import io.ktor.utils.io.core.*
|
import io.ktor.utils.io.core.*
|
||||||
import io.ktor.utils.io.streams.asOutput
|
import io.ktor.utils.io.streams.asOutput
|
||||||
import space.kscience.dataforge.meta.Meta
|
import space.kscience.dataforge.meta.Meta
|
||||||
import space.kscience.dataforge.meta.descriptors.NodeDescriptor
|
import space.kscience.dataforge.meta.descriptors.MetaDescriptor
|
||||||
import space.kscience.dataforge.meta.isEmpty
|
import space.kscience.dataforge.meta.isEmpty
|
||||||
import space.kscience.dataforge.misc.DFExperimental
|
import space.kscience.dataforge.misc.DFExperimental
|
||||||
import java.nio.file.Files
|
import java.nio.file.Files
|
||||||
@ -97,7 +97,7 @@ public inline fun <reified T : Any> IOPlugin.resolveIOFormat(): IOFormat<T>? {
|
|||||||
public fun IOPlugin.readMetaFile(
|
public fun IOPlugin.readMetaFile(
|
||||||
path: Path,
|
path: Path,
|
||||||
formatOverride: MetaFormat? = null,
|
formatOverride: MetaFormat? = null,
|
||||||
descriptor: NodeDescriptor? = null,
|
descriptor: MetaDescriptor? = null,
|
||||||
): Meta {
|
): Meta {
|
||||||
if (!Files.exists(path)) error("Meta file $path does not exist")
|
if (!Files.exists(path)) error("Meta file $path does not exist")
|
||||||
|
|
||||||
@ -125,7 +125,7 @@ public fun IOPlugin.writeMetaFile(
|
|||||||
path: Path,
|
path: Path,
|
||||||
meta: Meta,
|
meta: Meta,
|
||||||
metaFormat: MetaFormatFactory = JsonMetaFormat,
|
metaFormat: MetaFormatFactory = JsonMetaFormat,
|
||||||
descriptor: NodeDescriptor? = null,
|
descriptor: MetaDescriptor? = null,
|
||||||
) {
|
) {
|
||||||
val actualPath = if (Files.isDirectory(path)) {
|
val actualPath = if (Files.isDirectory(path)) {
|
||||||
path.resolve("@" + metaFormat.name.toString())
|
path.resolve("@" + metaFormat.name.toString())
|
||||||
|
@ -4,9 +4,13 @@ package space.kscience.dataforge.meta
|
|||||||
|
|
||||||
import kotlinx.serialization.json.*
|
import kotlinx.serialization.json.*
|
||||||
import space.kscience.dataforge.meta.descriptors.MetaDescriptor
|
import space.kscience.dataforge.meta.descriptors.MetaDescriptor
|
||||||
|
import space.kscience.dataforge.meta.descriptors.get
|
||||||
import space.kscience.dataforge.names.NameToken
|
import space.kscience.dataforge.names.NameToken
|
||||||
import space.kscience.dataforge.values.*
|
import space.kscience.dataforge.values.*
|
||||||
|
|
||||||
|
private const val jsonArrayKey: String = "@jsonArray"
|
||||||
|
|
||||||
|
public val Meta.Companion.JSON_ARRAY_KEY: String get() = jsonArrayKey
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param descriptor reserved for custom serialization in future
|
* @param descriptor reserved for custom serialization in future
|
||||||
@ -15,7 +19,7 @@ public fun Value.toJson(descriptor: MetaDescriptor? = null): JsonElement = when
|
|||||||
ValueType.NUMBER -> JsonPrimitive(numberOrNull)
|
ValueType.NUMBER -> JsonPrimitive(numberOrNull)
|
||||||
ValueType.STRING -> JsonPrimitive(string)
|
ValueType.STRING -> JsonPrimitive(string)
|
||||||
ValueType.BOOLEAN -> JsonPrimitive(boolean)
|
ValueType.BOOLEAN -> JsonPrimitive(boolean)
|
||||||
ValueType.LIST -> JsonArray(list.map { it.toJson() })
|
ValueType.LIST -> JsonArray(list.map { it.toJson(descriptor) })
|
||||||
ValueType.NULL -> JsonNull
|
ValueType.NULL -> JsonNull
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -46,7 +50,7 @@ private fun Meta.toJsonWithIndex(descriptor: MetaDescriptor?, index: String?): J
|
|||||||
|
|
||||||
//Add index if needed
|
//Add index if needed
|
||||||
if (index != null) {
|
if (index != null) {
|
||||||
pairs += Meta.INDEX_KEY to JsonPrimitive(index)
|
pairs += (descriptor?.indexKey ?: Meta.INDEX_KEY) to JsonPrimitive(index)
|
||||||
}
|
}
|
||||||
|
|
||||||
//Add value if needed
|
//Add value if needed
|
||||||
@ -59,8 +63,9 @@ private fun Meta.toJsonWithIndex(descriptor: MetaDescriptor?, index: String?): J
|
|||||||
|
|
||||||
public fun Meta.toJson(descriptor: MetaDescriptor? = null): JsonElement = toJsonWithIndex(descriptor, null)
|
public fun Meta.toJson(descriptor: MetaDescriptor? = null): JsonElement = toJsonWithIndex(descriptor, null)
|
||||||
|
|
||||||
public fun JsonObject.toMeta(descriptor: MetaDescriptor? = null): JsonMeta = JsonMeta(this, descriptor)
|
/**
|
||||||
|
* Convert a Json primitive to a [Value]
|
||||||
|
*/
|
||||||
public fun JsonPrimitive.toValue(descriptor: MetaDescriptor?): Value {
|
public fun JsonPrimitive.toValue(descriptor: MetaDescriptor?): Value {
|
||||||
return when (this) {
|
return when (this) {
|
||||||
JsonNull -> Null
|
JsonNull -> Null
|
||||||
@ -68,62 +73,160 @@ public fun JsonPrimitive.toValue(descriptor: MetaDescriptor?): Value {
|
|||||||
if (isString) {
|
if (isString) {
|
||||||
StringValue(content)
|
StringValue(content)
|
||||||
} else {
|
} else {
|
||||||
|
//consider using LazyParse
|
||||||
content.parseValue()
|
content.parseValue()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public fun JsonElement.toMeta(descriptor: MetaDescriptor? = null): TypedMeta<JsonMeta> = JsonMeta(this, descriptor)
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A meta wrapping json object
|
* Turn this [JsonArray] into a [ListValue] with recursion or return null if it contains objects
|
||||||
*/
|
*/
|
||||||
public class JsonMeta(
|
public fun JsonElement.toValueOrNull(descriptor: MetaDescriptor?): Value? = when (this) {
|
||||||
private val json: JsonElement,
|
is JsonPrimitive -> toValue(descriptor)
|
||||||
private val descriptor: MetaDescriptor? = null
|
is JsonObject -> get(Meta.VALUE_KEY)?.toValueOrNull(descriptor)
|
||||||
) : TypedMeta<JsonMeta> {
|
is JsonArray -> {
|
||||||
|
if(isEmpty()) ListValue.EMPTY else {
|
||||||
private val indexName by lazy { descriptor?.indexKey ?: Meta.INDEX_KEY }
|
val values = map { it.toValueOrNull(descriptor) }
|
||||||
|
values.map { it ?: return null }.asValue()
|
||||||
override val value: Value? by lazy {
|
|
||||||
when (json) {
|
|
||||||
is JsonPrimitive -> json.toValue(descriptor)
|
|
||||||
is JsonObject -> json[Meta.VALUE_KEY]?.let { JsonMeta(it).value }
|
|
||||||
is JsonArray -> if (json.all { it is JsonPrimitive }) {
|
|
||||||
//convert array of primitives to ListValue
|
|
||||||
json.map { (it as JsonPrimitive).toValue(descriptor) }.asValue()
|
|
||||||
} else {
|
|
||||||
null
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
override val items: Map<NameToken, JsonMeta> by lazy {
|
|
||||||
when (json) {
|
|
||||||
is JsonPrimitive -> emptyMap()
|
|
||||||
is JsonObject -> json.entries.associate { (name, child) ->
|
|
||||||
val index = (child as? JsonObject)?.get(indexName)?.jsonPrimitive?.content
|
|
||||||
val token = NameToken(name, index)
|
|
||||||
token to JsonMeta(child, descriptor?.children?.get(name))
|
|
||||||
}
|
|
||||||
is JsonArray -> json.mapIndexed { index, child ->
|
|
||||||
//Use explicit index or or order for index
|
|
||||||
val tokenIndex = (child as? JsonObject)?.get(indexName)?.jsonPrimitive?.content ?: index.toString()
|
|
||||||
val token = NameToken(JSON_ARRAY_KEY, tokenIndex)
|
|
||||||
token to JsonMeta(child)
|
|
||||||
}.toMap()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun toString(): String = Meta.toString(this)
|
|
||||||
override fun equals(other: Any?): Boolean = Meta.equals(this, other as? Meta)
|
|
||||||
override fun hashCode(): Int = Meta.hashCode(this)
|
|
||||||
|
|
||||||
public companion object {
|
|
||||||
/**
|
|
||||||
* A key representing top-level json array of nodes, which could not be directly represented by a meta node
|
|
||||||
*/
|
|
||||||
public const val JSON_ARRAY_KEY: String = "@jsonArray"
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fill a mutable map with children produced from [element] with given top level [key]
|
||||||
|
*/
|
||||||
|
private fun MutableMap<NameToken, SealedMeta>.addJsonElement(
|
||||||
|
key: String,
|
||||||
|
element: JsonElement,
|
||||||
|
descriptor: MetaDescriptor?
|
||||||
|
) {
|
||||||
|
when (element) {
|
||||||
|
is JsonPrimitive -> put(NameToken(key), Meta(element.toValue(descriptor)))
|
||||||
|
is JsonArray -> {
|
||||||
|
val value = element.toValueOrNull(descriptor)
|
||||||
|
if (value != null) {
|
||||||
|
put(NameToken(key), Meta(value))
|
||||||
|
} else {
|
||||||
|
val indexKey = descriptor?.indexKey ?: Meta.INDEX_KEY
|
||||||
|
element.forEachIndexed { serial, childElement ->
|
||||||
|
val index = (childElement as? JsonObject)?.get(indexKey)?.jsonPrimitive?.content
|
||||||
|
?: serial.toString()
|
||||||
|
val child: SealedMeta = when (childElement) {
|
||||||
|
is JsonObject -> childElement.toMeta(descriptor)
|
||||||
|
is JsonArray -> {
|
||||||
|
val childValue = childElement.toValueOrNull(null)
|
||||||
|
if (childValue == null) {
|
||||||
|
SealedMeta(null,
|
||||||
|
hashMapOf<NameToken, SealedMeta>().apply {
|
||||||
|
addJsonElement(Meta.JSON_ARRAY_KEY, childElement, null)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
Meta(childValue)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
is JsonPrimitive -> Meta(childElement.toValue(null))
|
||||||
|
}
|
||||||
|
put(NameToken(key, index), child)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
is JsonObject -> {
|
||||||
|
val indexKey = descriptor?.indexKey ?: Meta.INDEX_KEY
|
||||||
|
val index = element[indexKey]?.jsonPrimitive?.content
|
||||||
|
put(NameToken(key, index), element.toMeta(descriptor))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public fun JsonObject.toMeta(descriptor: MetaDescriptor? = null): SealedMeta {
|
||||||
|
val map = LinkedHashMap<NameToken, SealedMeta>()
|
||||||
|
forEach { (key, element) ->
|
||||||
|
if (key != Meta.VALUE_KEY) {
|
||||||
|
map.addJsonElement(key, element, descriptor?.get(key))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return SealedMeta(get(Meta.VALUE_KEY)?.toValueOrNull(descriptor), map)
|
||||||
|
}
|
||||||
|
|
||||||
|
public fun JsonElement.toMeta(descriptor: MetaDescriptor? = null): SealedMeta = when (this) {
|
||||||
|
is JsonPrimitive -> Meta(toValue(descriptor))
|
||||||
|
is JsonObject -> toMeta(descriptor)
|
||||||
|
is JsonArray -> SealedMeta(null,
|
||||||
|
linkedMapOf<NameToken, SealedMeta>().apply {
|
||||||
|
addJsonElement(Meta.JSON_ARRAY_KEY, this@toMeta, null)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
///**
|
||||||
|
// * A meta wrapping json object
|
||||||
|
// */
|
||||||
|
//public class JsonMeta(
|
||||||
|
// private val json: JsonElement,
|
||||||
|
// private val descriptor: MetaDescriptor? = null
|
||||||
|
//) : TypedMeta<JsonMeta> {
|
||||||
|
//
|
||||||
|
// private val indexName by lazy { descriptor?.indexKey ?: Meta.INDEX_KEY }
|
||||||
|
//
|
||||||
|
// override val value: Value? by lazy {
|
||||||
|
// json.toValueOrNull(descriptor)
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// private fun MutableMap<NameToken, JsonMeta>.appendArray(json: JsonArray, key: String) {
|
||||||
|
// json.forEachIndexed { index, child ->
|
||||||
|
// if (child is JsonArray) {
|
||||||
|
// appendArray(child, key)
|
||||||
|
// } else {
|
||||||
|
// //Use explicit index or order for index
|
||||||
|
// val tokenIndex = (child as? JsonObject)
|
||||||
|
// ?.get(indexName)
|
||||||
|
// ?.jsonPrimitive?.content
|
||||||
|
// ?: index.toString()
|
||||||
|
// val token = NameToken(key, tokenIndex)
|
||||||
|
// this[token] = JsonMeta(child)
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// override val items: Map<NameToken, JsonMeta> by lazy {
|
||||||
|
// val map = HashMap<NameToken, JsonMeta>()
|
||||||
|
// when (json) {
|
||||||
|
// is JsonObject -> json.forEach { (name, child) ->
|
||||||
|
// //skip value key
|
||||||
|
// if (name != Meta.VALUE_KEY) {
|
||||||
|
// if (child is JsonArray && child.any { it is JsonObject }) {
|
||||||
|
// map.appendArray(child, name)
|
||||||
|
// } else {
|
||||||
|
//
|
||||||
|
// val index = (child as? JsonObject)?.get(indexName)?.jsonPrimitive?.content
|
||||||
|
// val token = NameToken(name, index)
|
||||||
|
// map[token] = JsonMeta(child, descriptor?.get(name))
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// is JsonArray -> {
|
||||||
|
// //return children only if it is not value
|
||||||
|
// if (value == null) map.appendArray(json, JSON_ARRAY_KEY)
|
||||||
|
// }
|
||||||
|
// else -> {
|
||||||
|
// //do nothing
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// map
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// override fun toString(): String = Meta.toString(this)
|
||||||
|
// override fun equals(other: Any?): Boolean = Meta.equals(this, other as? Meta)
|
||||||
|
// override fun hashCode(): Int = Meta.hashCode(this)
|
||||||
|
//
|
||||||
|
// public companion object {
|
||||||
|
// /**
|
||||||
|
// * A key representing top-level json array of nodes, which could not be directly represented by a meta node
|
||||||
|
// */
|
||||||
|
// public const val JSON_ARRAY_KEY: String = "@jsonArray"
|
||||||
|
// }
|
||||||
|
//}
|
@ -49,7 +49,13 @@ public interface Meta : MetaRepr {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public fun equals(meta1: Meta?, meta2: Meta?): Boolean {
|
public fun equals(meta1: Meta?, meta2: Meta?): Boolean {
|
||||||
return meta1?.value == meta2?.value && meta1?.items == meta2?.items
|
if (meta1 == null && meta2 == null) return true
|
||||||
|
if (meta1 == null || meta2 == null) return false
|
||||||
|
if (meta1.value != meta2.value) return false
|
||||||
|
if (meta1.items.keys != meta2.items.keys) return false
|
||||||
|
return meta1.items.keys.all {
|
||||||
|
equals(meta1[it], meta2[it])
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private val json = Json {
|
private val json = Json {
|
||||||
|
@ -23,7 +23,7 @@ public fun Meta.item(key: Name? = null): MetaDelegate = ReadOnlyProperty { _, pr
|
|||||||
public fun <R : Any> MetaDelegate.convert(
|
public fun <R : Any> MetaDelegate.convert(
|
||||||
converter: MetaConverter<R>,
|
converter: MetaConverter<R>,
|
||||||
): ReadOnlyProperty<Any?, R?> = ReadOnlyProperty { thisRef, property ->
|
): ReadOnlyProperty<Any?, R?> = ReadOnlyProperty { thisRef, property ->
|
||||||
this@convert.getValue(thisRef, property)?.let(converter::itemToObject)
|
this@convert.getValue(thisRef, property)?.let(converter::metaToObject)
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -33,7 +33,7 @@ public fun <R : Any> MetaDelegate.convert(
|
|||||||
converter: MetaConverter<R>,
|
converter: MetaConverter<R>,
|
||||||
default: () -> R,
|
default: () -> R,
|
||||||
): ReadOnlyProperty<Any?, R> = ReadOnlyProperty<Any?, R> { thisRef, property ->
|
): ReadOnlyProperty<Any?, R> = ReadOnlyProperty<Any?, R> { thisRef, property ->
|
||||||
this@convert.getValue(thisRef, property)?.let(converter::itemToObject) ?: default()
|
this@convert.getValue(thisRef, property)?.let(converter::metaToObject) ?: default()
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -1,30 +1,35 @@
|
|||||||
package space.kscience.dataforge.meta
|
package space.kscience.dataforge.meta
|
||||||
|
|
||||||
import kotlinx.serialization.KSerializer
|
import kotlinx.serialization.KSerializer
|
||||||
import kotlinx.serialization.builtins.MapSerializer
|
|
||||||
import kotlinx.serialization.descriptors.SerialDescriptor
|
import kotlinx.serialization.descriptors.SerialDescriptor
|
||||||
import kotlinx.serialization.encoding.Decoder
|
import kotlinx.serialization.encoding.Decoder
|
||||||
import kotlinx.serialization.encoding.Encoder
|
import kotlinx.serialization.encoding.Encoder
|
||||||
|
import kotlinx.serialization.json.JsonDecoder
|
||||||
import kotlinx.serialization.json.JsonElement
|
import kotlinx.serialization.json.JsonElement
|
||||||
import space.kscience.dataforge.names.NameToken
|
import kotlinx.serialization.json.JsonEncoder
|
||||||
import space.kscience.dataforge.names.NameTokenSerializer
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Serialized for meta
|
* Serialized for [Meta]
|
||||||
*/
|
*/
|
||||||
public object MetaSerializer : KSerializer<Meta> {
|
public object MetaSerializer : KSerializer<Meta> {
|
||||||
|
private val genericMetaSerializer = SealedMeta.serializer()
|
||||||
|
|
||||||
private val itemsSerializer: KSerializer<Map<NameToken, Meta>> = MapSerializer(
|
private val jsonSerializer = JsonElement.serializer()
|
||||||
NameTokenSerializer,
|
|
||||||
MetaSerializer
|
|
||||||
)
|
|
||||||
|
|
||||||
override val descriptor: SerialDescriptor = JsonElement.serializer().descriptor
|
override val descriptor: SerialDescriptor = jsonSerializer.descriptor
|
||||||
|
|
||||||
override fun deserialize(decoder: Decoder): Meta = JsonElement.serializer().deserialize(decoder).toMeta()
|
override fun deserialize(decoder: Decoder): Meta = if (decoder is JsonDecoder) {
|
||||||
|
jsonSerializer.deserialize(decoder).toMeta()
|
||||||
|
} else {
|
||||||
|
genericMetaSerializer.deserialize(decoder)
|
||||||
|
}
|
||||||
|
|
||||||
override fun serialize(encoder: Encoder, value: Meta) {
|
override fun serialize(encoder: Encoder, value: Meta) {
|
||||||
JsonElement.serializer().serialize(encoder, value.toJson())
|
if (encoder is JsonEncoder) {
|
||||||
|
jsonSerializer.serialize(encoder, value.toJson())
|
||||||
|
} else {
|
||||||
|
genericMetaSerializer.serialize(encoder, value.seal())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -32,8 +37,8 @@ public object MetaSerializer : KSerializer<Meta> {
|
|||||||
* A serializer for [MutableMeta]
|
* A serializer for [MutableMeta]
|
||||||
*/
|
*/
|
||||||
public object MutableMetaSerializer : KSerializer<MutableMeta> {
|
public object MutableMetaSerializer : KSerializer<MutableMeta> {
|
||||||
override val descriptor: SerialDescriptor = MetaSerializer.descriptor
|
|
||||||
|
|
||||||
|
override val descriptor: SerialDescriptor = MetaSerializer.descriptor
|
||||||
|
|
||||||
override fun deserialize(decoder: Decoder): MutableMeta {
|
override fun deserialize(decoder: Decoder): MutableMeta {
|
||||||
val meta = decoder.decodeSerializableValue(MetaSerializer)
|
val meta = decoder.decodeSerializableValue(MetaSerializer)
|
||||||
|
@ -1,20 +1,27 @@
|
|||||||
package space.kscience.dataforge.meta
|
package space.kscience.dataforge.meta
|
||||||
|
|
||||||
import kotlinx.serialization.Serializable
|
import kotlinx.serialization.Serializable
|
||||||
import space.kscience.dataforge.misc.DFBuilder
|
|
||||||
import space.kscience.dataforge.misc.DFExperimental
|
import space.kscience.dataforge.misc.DFExperimental
|
||||||
import space.kscience.dataforge.names.*
|
import space.kscience.dataforge.names.*
|
||||||
import space.kscience.dataforge.values.EnumValue
|
import space.kscience.dataforge.values.EnumValue
|
||||||
import space.kscience.dataforge.values.Value
|
import space.kscience.dataforge.values.Value
|
||||||
import space.kscience.dataforge.values.asValue
|
import space.kscience.dataforge.values.asValue
|
||||||
|
import kotlin.js.JsName
|
||||||
import kotlin.jvm.Synchronized
|
import kotlin.jvm.Synchronized
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Mark a meta builder
|
||||||
|
*/
|
||||||
|
@DslMarker
|
||||||
|
public annotation class MetaBuilder
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Mutable variant of [Meta]
|
* Mutable variant of [Meta]
|
||||||
* TODO documentation
|
* TODO documentation
|
||||||
*/
|
*/
|
||||||
@Serializable(MutableMetaSerializer::class)
|
@Serializable(MutableMetaSerializer::class)
|
||||||
|
@MetaBuilder
|
||||||
public interface MutableMeta : Meta {
|
public interface MutableMeta : Meta {
|
||||||
|
|
||||||
override val items: Map<NameToken, MutableMeta>
|
override val items: Map<NameToken, MutableMeta>
|
||||||
@ -30,9 +37,9 @@ public interface MutableMeta : Meta {
|
|||||||
public operator fun set(name: Name, meta: Meta)
|
public operator fun set(name: Name, meta: Meta)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Remove a note at a given [name] if it is present
|
* Remove a node at a given [name] if it is present
|
||||||
*/
|
*/
|
||||||
public fun removeNode(name: Name)
|
public fun remove(name: Name)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get existing node or create a new one
|
* Get existing node or create a new one
|
||||||
@ -109,11 +116,17 @@ public interface MutableMeta : Meta {
|
|||||||
toName() put repr.toMeta()
|
toName() put repr.toMeta()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public infix fun String.put(iterable: Iterable<Meta>) {
|
||||||
|
setIndexed(toName(), iterable)
|
||||||
|
}
|
||||||
|
|
||||||
public infix fun String.put(builder: MutableMeta.() -> Unit) {
|
public infix fun String.put(builder: MutableMeta.() -> Unit) {
|
||||||
set(toName(), MutableMeta(builder))
|
set(toName(), MutableMeta(builder))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public fun MutableMeta.getOrCreate(string: String): MutableMeta = getOrCreate(string.toName())
|
||||||
|
|
||||||
@Serializable(MutableMetaSerializer::class)
|
@Serializable(MutableMetaSerializer::class)
|
||||||
public interface MutableTypedMeta<M : MutableTypedMeta<M>> : TypedMeta<M>, MutableMeta {
|
public interface MutableTypedMeta<M : MutableTypedMeta<M>> : TypedMeta<M>, MutableMeta {
|
||||||
/**
|
/**
|
||||||
@ -125,34 +138,59 @@ public interface MutableTypedMeta<M : MutableTypedMeta<M>> : TypedMeta<M>, Mutab
|
|||||||
override fun getOrCreate(name: Name): M
|
override fun getOrCreate(name: Name): M
|
||||||
}
|
}
|
||||||
|
|
||||||
public operator fun MutableMeta.set(key: String, item: Meta?): Unit =
|
public fun <M : MutableTypedMeta<M>> M.getOrCreate(string: String): M = getOrCreate(string.toName())
|
||||||
set(key.toName(), item)
|
|
||||||
|
|
||||||
|
public fun MutableMeta.remove(name: String){
|
||||||
|
remove(name.toName())
|
||||||
|
}
|
||||||
|
|
||||||
public fun MutableMeta.remove(name: Name): Unit = set(name, null)
|
// node setters
|
||||||
|
|
||||||
|
public operator fun MutableMeta.set(name: NameToken, value: Meta): Unit = set(name.asName(), value)
|
||||||
|
public operator fun MutableMeta.set(name: Name, meta: Meta?): Unit {
|
||||||
|
if (meta == null) {
|
||||||
|
remove(name)
|
||||||
|
} else {
|
||||||
|
set(name, meta)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public operator fun MutableMeta.set(key: String, meta: Meta?): Unit {
|
||||||
|
set(key.toName(), meta)
|
||||||
|
}
|
||||||
|
|
||||||
|
//value setters
|
||||||
|
|
||||||
|
public operator fun MutableMeta.set(name: NameToken, value: Value?): Unit = set(name.asName(), value)
|
||||||
|
public operator fun MutableMeta.set(key: String, value: Value?): Unit = set(key.toName(), value)
|
||||||
|
|
||||||
|
public operator fun MutableMeta.set(name: Name, value: String): Unit = set(name, value.asValue())
|
||||||
|
public operator fun MutableMeta.set(name: NameToken, value: String): Unit = set(name.asName(), value.asValue())
|
||||||
|
public operator fun MutableMeta.set(key: String, value: String): Unit = set(key.toName(), value.asValue())
|
||||||
|
|
||||||
|
public operator fun MutableMeta.set(name: Name, value: Boolean): Unit = set(name, value.asValue())
|
||||||
|
public operator fun MutableMeta.set(name: NameToken, value: Boolean): Unit = set(name.asName(), value.asValue())
|
||||||
|
public operator fun MutableMeta.set(key: String, value: Boolean): Unit = set(key.toName(), value.asValue())
|
||||||
|
|
||||||
|
public operator fun MutableMeta.set(name: Name, value: Number): Unit = set(name, value.asValue())
|
||||||
|
public operator fun MutableMeta.set(name: NameToken, value: Number): Unit = set(name.asName(), value.asValue())
|
||||||
|
public operator fun MutableMeta.set(key: String, value: Number): Unit = set(key.toName(), value.asValue())
|
||||||
|
|
||||||
|
public operator fun MutableMeta.set(name: Name, value: List<Value>): Unit = set(name, value.asValue())
|
||||||
|
public operator fun MutableMeta.set(name: NameToken, value: List<Value>): Unit = set(name.asName(), value.asValue())
|
||||||
|
public operator fun MutableMeta.set(key: String, value: List<Value>): Unit = set(key.toName(), value.asValue())
|
||||||
|
|
||||||
|
//public fun MutableMeta.set(key: String, index: String, value: Value?): Unit =
|
||||||
|
// set(key.toName().withIndex(index), value)
|
||||||
|
|
||||||
public fun MutableMeta.remove(name: String): Unit = remove(name.toName())
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Universal unsafe set method
|
* Universal unsafe set method
|
||||||
*/
|
*/
|
||||||
public operator fun MutableMeta.set(name: Name, value: Any?) {
|
public operator fun MutableMeta.set(name: Name, value: Value?) {
|
||||||
when (value) {
|
getOrCreate(name).value = Value.of(value)
|
||||||
null -> remove(name)
|
|
||||||
else -> set(name, Value.of(value))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public operator fun MutableMeta.set(name: NameToken, value: Any?): Unit =
|
|
||||||
set(name.asName(), value)
|
|
||||||
|
|
||||||
public operator fun MutableMeta.set(key: String, value: Any?): Unit =
|
|
||||||
set(key.toName(), value)
|
|
||||||
|
|
||||||
public operator fun MutableMeta.set(key: String, index: String, value: Any?): Unit =
|
|
||||||
set(key.toName().withIndex(index), value)
|
|
||||||
|
|
||||||
|
|
||||||
/* Same name siblings generation */
|
/* Same name siblings generation */
|
||||||
|
|
||||||
public fun MutableMeta.setIndexedItems(
|
public fun MutableMeta.setIndexedItems(
|
||||||
@ -177,10 +215,10 @@ public fun MutableMeta.setIndexed(
|
|||||||
setIndexedItems(name, metas) { item, index -> indexFactory(item, index) }
|
setIndexedItems(name, metas) { item, index -> indexFactory(item, index) }
|
||||||
}
|
}
|
||||||
|
|
||||||
public operator fun <M : MutableTypedMeta<M>> MutableTypedMeta<M>.set(name: Name, metas: Iterable<Meta>): Unit =
|
public operator fun MutableMeta.set(name: Name, metas: Iterable<Meta>): Unit =
|
||||||
setIndexed(name, metas)
|
setIndexed(name, metas)
|
||||||
|
|
||||||
public operator fun <M : MutableTypedMeta<M>> MutableTypedMeta<M>.set(name: String, metas: Iterable<Meta>): Unit =
|
public operator fun MutableMeta.set(name: String, metas: Iterable<Meta>): Unit =
|
||||||
setIndexed(name.toName(), metas)
|
setIndexed(name.toName(), metas)
|
||||||
|
|
||||||
|
|
||||||
@ -190,7 +228,7 @@ public operator fun <M : MutableTypedMeta<M>> MutableTypedMeta<M>.set(name: Stri
|
|||||||
* * node updates node and replaces anything but node
|
* * node updates node and replaces anything but node
|
||||||
* * node list updates node list if number of nodes in the list is the same and replaces anything otherwise
|
* * node list updates node list if number of nodes in the list is the same and replaces anything otherwise
|
||||||
*/
|
*/
|
||||||
public fun <M : MutableTypedMeta<M>> MutableTypedMeta<M>.update(meta: Meta) {
|
public fun MutableMeta.update(meta: Meta) {
|
||||||
meta.valueSequence().forEach { (name, value) ->
|
meta.valueSequence().forEach { (name, value) ->
|
||||||
set(name, value)
|
set(name, value)
|
||||||
}
|
}
|
||||||
@ -217,38 +255,24 @@ public operator fun <M : MutableTypedMeta<M>> MutableTypedMeta<M>.set(name: Name
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
///**
|
|
||||||
// * Create a mutable item provider that uses given provider for default values if those are not found in this provider.
|
|
||||||
// * Changes are propagated only to this provider.
|
|
||||||
// */
|
|
||||||
//public fun <M : MutableTypedMeta<M>> M.withDefault(default: Meta?): MutableTypedMeta<*> =
|
|
||||||
// if (default == null || default.isEmpty()) {
|
|
||||||
// //Optimize for use with empty default
|
|
||||||
// this
|
|
||||||
// } else object : MutableTypedMeta<M> {
|
|
||||||
// override fun set(name: Name, item: MetaItem?) {
|
|
||||||
// this@withDefault.set(name, item)
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// override fun getItem(name: Name): MetaItem? = this@withDefault.getItem(name) ?: default.getItem(name)
|
|
||||||
// }
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A general implementation of mutable [Meta] which implements both [MutableTypedMeta] and [ObservableMeta].
|
* A general implementation of mutable [Meta] which implements both [MutableTypedMeta] and [ObservableMeta].
|
||||||
* The implementation uses blocking synchronization on mutation on JVM
|
* The implementation uses blocking synchronization on mutation on JVM
|
||||||
*/
|
*/
|
||||||
@DFBuilder
|
|
||||||
private class MutableMetaImpl(
|
private class MutableMetaImpl(
|
||||||
override var value: Value?,
|
value: Value?,
|
||||||
children: Map<NameToken, Meta> = emptyMap()
|
children: Map<NameToken, Meta> = emptyMap()
|
||||||
) : ObservableMutableMeta {
|
) : ObservableMutableMeta {
|
||||||
|
|
||||||
private val children: LinkedHashMap<NameToken, MutableMetaImpl> =
|
override var value = value
|
||||||
|
@Synchronized set
|
||||||
|
|
||||||
|
private val children: LinkedHashMap<NameToken, ObservableMutableMeta> =
|
||||||
LinkedHashMap(children.mapValues { (key, meta) ->
|
LinkedHashMap(children.mapValues { (key, meta) ->
|
||||||
MutableMetaImpl(meta.value, meta.items).apply { adoptBy(this, key) }
|
MutableMetaImpl(meta.value, meta.items).apply { adoptBy(this, key) }
|
||||||
})
|
})
|
||||||
|
|
||||||
override val items: Map<NameToken, MutableMetaImpl> get() = children
|
override val items: Map<NameToken, ObservableMutableMeta> get() = children
|
||||||
|
|
||||||
private val listeners = HashSet<MetaListener>()
|
private val listeners = HashSet<MetaListener>()
|
||||||
|
|
||||||
@ -267,60 +291,56 @@ private class MutableMetaImpl(
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private fun adoptBy(parent: MutableMetaImpl, key: NameToken) {
|
private fun ObservableMeta.adoptBy(parent: MutableMetaImpl, key: NameToken) {
|
||||||
onChange(parent) { name ->
|
onChange(parent) { name ->
|
||||||
parent.changed(key + name)
|
parent.changed(key + name)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun attach(name: Name, node: ObservableMutableMeta) {
|
||||||
// fun attach(name: Name, node: MutableMetaImpl) {
|
when (name.length) {
|
||||||
// when (name.length) {
|
0 -> error("Can't set a meta with empty name")
|
||||||
// 0 -> error("Can't set a meta with empty name")
|
1 -> {
|
||||||
// 1 -> {
|
replaceItem(name.first(), get(name), node)
|
||||||
// val key = name.firstOrNull()!!
|
}
|
||||||
// children[key] = node
|
else -> get(name.cutLast())?.attach(name.lastOrNull()!!.asName(), node)
|
||||||
// adoptBy(this, key)
|
}
|
||||||
// changed(name)
|
}
|
||||||
// }
|
|
||||||
// else -> get(name.cutLast())?.attach(name.lastOrNull()!!.asName(), node)
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create and attach empty node
|
* Create and attach empty node
|
||||||
*/
|
*/
|
||||||
private fun createNode(name: Name): ObservableMutableMeta {
|
private fun createNode(name: Name): ObservableMutableMeta = when (name.length) {
|
||||||
val newNode = MutableMetaImpl(null)
|
|
||||||
when (name.length) {
|
|
||||||
0 -> throw IllegalArgumentException("Can't create a node with empty name")
|
0 -> throw IllegalArgumentException("Can't create a node with empty name")
|
||||||
1 -> {
|
1 -> {
|
||||||
|
val newNode = MutableMetaImpl(null)
|
||||||
children[name.first()] = newNode
|
children[name.first()] = newNode
|
||||||
newNode.adoptBy(this, name.first())
|
newNode.adoptBy(this, name.first())
|
||||||
|
newNode
|
||||||
} //do not notify, no value changed
|
} //do not notify, no value changed
|
||||||
else -> getOrCreate(name.first().asName()).getOrCreate(name.cutFirst())
|
else -> getOrCreate(name.first().asName()).getOrCreate(name.cutFirst())
|
||||||
}
|
}
|
||||||
return newNode
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun getOrCreate(name: Name): ObservableMutableMeta = get(name) ?: createNode(name)
|
override fun getOrCreate(name: Name): ObservableMutableMeta = get(name) ?: createNode(name)
|
||||||
|
|
||||||
override fun removeNode(name: Name) {
|
override fun remove(name: Name) {
|
||||||
when (name.length) {
|
when (name.length) {
|
||||||
0 -> error("Can't remove self")
|
0 -> error("Can't remove self")
|
||||||
1 -> if (children.remove(name.firstOrNull()!!) != null) changed(name)
|
1 -> if (children.remove(name.firstOrNull()!!) != null) changed(name)
|
||||||
else -> get(name.cutLast())?.removeNode(name.lastOrNull()!!.asName())
|
else -> get(name.cutLast())?.remove(name.lastOrNull()!!.asName())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Synchronized
|
||||||
private fun replaceItem(
|
private fun replaceItem(
|
||||||
key: NameToken,
|
key: NameToken,
|
||||||
oldItem: ObservableMutableMeta?,
|
oldItem: ObservableMutableMeta?,
|
||||||
newItem: MutableMetaImpl?
|
newItem: ObservableMutableMeta?
|
||||||
) {
|
) {
|
||||||
if (oldItem != newItem) {
|
if (oldItem != newItem) {
|
||||||
if (newItem == null) {
|
if (newItem == null) {
|
||||||
children.remove(key)
|
//remove child and remove stale listener
|
||||||
|
children.remove(key)?.removeListener(this)
|
||||||
} else {
|
} else {
|
||||||
newItem.adoptBy(this, key)
|
newItem.adoptBy(this, key)
|
||||||
children[key] = newItem
|
children[key] = newItem
|
||||||
@ -364,7 +384,7 @@ private class MutableMetaImpl(
|
|||||||
* Append the node with a same-name-sibling, automatically generating numerical index
|
* Append the node with a same-name-sibling, automatically generating numerical index
|
||||||
*/
|
*/
|
||||||
@DFExperimental
|
@DFExperimental
|
||||||
public fun MutableMeta.append(name: Name, value: Any?) {
|
public fun MutableMeta.append(name: Name, value: Value?) {
|
||||||
require(!name.isEmpty()) { "Name could not be empty for append operation" }
|
require(!name.isEmpty()) { "Name could not be empty for append operation" }
|
||||||
val newIndex = name.lastOrNull()!!.index
|
val newIndex = name.lastOrNull()!!.index
|
||||||
if (newIndex != null) {
|
if (newIndex != null) {
|
||||||
@ -376,7 +396,7 @@ public fun MutableMeta.append(name: Name, value: Any?) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@DFExperimental
|
@DFExperimental
|
||||||
public fun MutableMeta.append(name: String, value: Any?): Unit = append(name.toName(), value)
|
public fun MutableMeta.append(name: String, value: Value?): Unit = append(name.toName(), value)
|
||||||
|
|
||||||
///**
|
///**
|
||||||
// * Apply existing node with given [builder] or create a new element with it.
|
// * Apply existing node with given [builder] or create a new element with it.
|
||||||
@ -398,12 +418,15 @@ public fun Meta.toMutableMeta(): ObservableMutableMeta = MutableMetaImpl(value,
|
|||||||
|
|
||||||
public fun Meta.asMutableMeta(): MutableMeta = (this as? MutableMeta) ?: toMutableMeta()
|
public fun Meta.asMutableMeta(): MutableMeta = (this as? MutableMeta) ?: toMutableMeta()
|
||||||
|
|
||||||
|
@JsName("newMutableMeta")
|
||||||
|
public fun MutableMeta(): ObservableMutableMeta = MutableMetaImpl(null)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Build a [MutableMeta] using given transformation
|
* Build a [MutableMeta] using given transformation
|
||||||
*/
|
*/
|
||||||
@Suppress("FunctionName")
|
@Suppress("FunctionName")
|
||||||
public fun MutableMeta(builder: MutableMeta.() -> Unit = {}): ObservableMutableMeta =
|
public inline fun MutableMeta(builder: MutableMeta.() -> Unit = {}): ObservableMutableMeta =
|
||||||
MutableMetaImpl(null).apply(builder)
|
MutableMeta().apply(builder)
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -412,3 +435,31 @@ public fun MutableMeta(builder: MutableMeta.() -> Unit = {}): ObservableMutableM
|
|||||||
*/
|
*/
|
||||||
public inline fun Meta.copy(block: MutableMeta.() -> Unit = {}): Meta =
|
public inline fun Meta.copy(block: MutableMeta.() -> Unit = {}): Meta =
|
||||||
toMutableMeta().apply(block)
|
toMutableMeta().apply(block)
|
||||||
|
|
||||||
|
|
||||||
|
private class MutableMetaWithDefault(val source: MutableMeta, val default: Meta, val name: Name) :
|
||||||
|
MutableMeta by source {
|
||||||
|
override val items: Map<NameToken, MutableMeta>
|
||||||
|
get() = (source.items.keys + default.items.keys).associateWith {
|
||||||
|
MutableMetaWithDefault(source, default, name + it)
|
||||||
|
}
|
||||||
|
|
||||||
|
override var value: Value?
|
||||||
|
get() = source[name]?.value ?: default[name]?.value
|
||||||
|
set(value) {
|
||||||
|
source[name] = value
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun toString(): String = Meta.toString(this)
|
||||||
|
override fun equals(other: Any?): Boolean = Meta.equals(this, other as? Meta)
|
||||||
|
override fun hashCode(): Int = Meta.hashCode(this)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a mutable item provider that uses given provider for default values if those are not found in this provider.
|
||||||
|
* Changes are propagated only to this provider.
|
||||||
|
*/
|
||||||
|
public fun MutableMeta.withDefault(default: Meta?): MutableMeta = if (default == null || default.isEmpty()) {
|
||||||
|
//Optimize for use with empty default
|
||||||
|
this
|
||||||
|
} else MutableMetaWithDefault(this, default, Name.EMPTY)
|
||||||
|
@ -32,10 +32,10 @@ public fun <R : Any> MutableMetaDelegate.convert(
|
|||||||
): ReadWriteProperty<Any?, R?> = object : ReadWriteProperty<Any?, R?> {
|
): ReadWriteProperty<Any?, R?> = object : ReadWriteProperty<Any?, R?> {
|
||||||
|
|
||||||
override fun getValue(thisRef: Any?, property: KProperty<*>): R? =
|
override fun getValue(thisRef: Any?, property: KProperty<*>): R? =
|
||||||
this@convert.getValue(thisRef, property)?.let(converter::itemToObject)
|
this@convert.getValue(thisRef, property)?.let(converter::metaToObject)
|
||||||
|
|
||||||
override fun setValue(thisRef: Any?, property: KProperty<*>, value: R?) {
|
override fun setValue(thisRef: Any?, property: KProperty<*>, value: R?) {
|
||||||
val item = value?.let(converter::objectToMetaItem)
|
val item = value?.let(converter::objectToMeta)
|
||||||
this@convert.setValue(thisRef, property, item)
|
this@convert.setValue(thisRef, property, item)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -46,10 +46,10 @@ public fun <R : Any> MutableMetaDelegate.convert(
|
|||||||
): ReadWriteProperty<Any?, R> = object : ReadWriteProperty<Any?, R> {
|
): ReadWriteProperty<Any?, R> = object : ReadWriteProperty<Any?, R> {
|
||||||
|
|
||||||
override fun getValue(thisRef: Any?, property: KProperty<*>): R =
|
override fun getValue(thisRef: Any?, property: KProperty<*>): R =
|
||||||
this@convert.getValue(thisRef, property)?.let(converter::itemToObject) ?: default()
|
this@convert.getValue(thisRef, property)?.let(converter::metaToObject) ?: default()
|
||||||
|
|
||||||
override fun setValue(thisRef: Any?, property: KProperty<*>, value: R) {
|
override fun setValue(thisRef: Any?, property: KProperty<*>, value: R) {
|
||||||
val item = value.let(converter::objectToMetaItem)
|
val item = value.let(converter::objectToMeta)
|
||||||
this@convert.setValue(thisRef, property, item)
|
this@convert.setValue(thisRef, property, item)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -32,7 +32,7 @@ public interface ObservableMeta : Meta {
|
|||||||
/**
|
/**
|
||||||
* A [Meta] which is both observable and mutable
|
* A [Meta] which is both observable and mutable
|
||||||
*/
|
*/
|
||||||
public interface ObservableMutableMeta : ObservableMeta, MutableMeta, TypedMeta<ObservableMutableMeta>
|
public interface ObservableMutableMeta : ObservableMeta, MutableMeta, MutableTypedMeta<ObservableMutableMeta>
|
||||||
|
|
||||||
private class ObservableMetaWrapper(
|
private class ObservableMetaWrapper(
|
||||||
val origin: MutableMeta,
|
val origin: MutableMeta,
|
||||||
@ -68,8 +68,8 @@ private class ObservableMetaWrapper(
|
|||||||
get(name) ?: ObservableMetaWrapper(origin.getOrCreate(name))
|
get(name) ?: ObservableMetaWrapper(origin.getOrCreate(name))
|
||||||
|
|
||||||
|
|
||||||
override fun removeNode(name: Name) {
|
override fun remove(name: Name) {
|
||||||
origin.removeNode(name)
|
origin.remove(name)
|
||||||
changed(name)
|
changed(name)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -84,10 +84,18 @@ private class ObservableMetaWrapper(
|
|||||||
override fun toMeta(): Meta {
|
override fun toMeta(): Meta {
|
||||||
return origin.toMeta()
|
return origin.toMeta()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun attach(name: Name, node: ObservableMutableMeta) {
|
||||||
|
TODO("Not yet implemented")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public fun MutableMeta.asObservable(): ObservableMeta =
|
/**
|
||||||
(this as? ObservableMeta) ?: ObservableMetaWrapper(this)
|
* Cast this [MutableMeta] to [ObservableMutableMeta] or create an observable wrapper. Only changes made to the result
|
||||||
|
* are guaranteed to be observed.
|
||||||
|
*/
|
||||||
|
public fun MutableMeta.asObservable(): ObservableMutableMeta =
|
||||||
|
(this as? ObservableMutableMeta) ?: ObservableMetaWrapper(this)
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -2,39 +2,43 @@ package space.kscience.dataforge.meta
|
|||||||
|
|
||||||
import space.kscience.dataforge.meta.descriptors.*
|
import space.kscience.dataforge.meta.descriptors.*
|
||||||
import space.kscience.dataforge.names.Name
|
import space.kscience.dataforge.names.Name
|
||||||
|
import space.kscience.dataforge.names.NameToken
|
||||||
|
import space.kscience.dataforge.values.Value
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 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].
|
||||||
* Default item provider and [NodeDescriptor] are optional
|
* Default item provider and [NodeDescriptor] are optional
|
||||||
*/
|
*/
|
||||||
public open class Scheme internal constructor(
|
public open class Scheme(
|
||||||
source: MutableMeta = MutableMeta()
|
source: MutableMeta = MutableMeta()
|
||||||
) : Described, ObservableMutableMeta, Meta by source {
|
) : Described, MutableMeta, ObservableMeta, Meta by source {
|
||||||
|
|
||||||
private var source = source.asObservable()
|
private var source = source.asObservable()
|
||||||
|
|
||||||
final override var descriptor: MetaDescriptor? = null
|
final override var descriptor: MetaDescriptor? = null
|
||||||
internal set
|
internal set
|
||||||
|
|
||||||
|
override var value: Value?
|
||||||
|
get() = source.value
|
||||||
|
set(value) {
|
||||||
|
source.value = value
|
||||||
|
}
|
||||||
|
|
||||||
|
override val items: Map<NameToken, MutableMeta> get() = source.items
|
||||||
|
|
||||||
internal fun wrap(
|
internal fun wrap(
|
||||||
items: MutableMeta,
|
items: MutableMeta,
|
||||||
preserveDefault: Boolean = false
|
preserveDefault: Boolean = false
|
||||||
) {
|
) {
|
||||||
this.source = if (preserveDefault) items.withDefault(this.source) else items
|
this.source = (if (preserveDefault) items.withDefault(this.source) else items).asObservable()
|
||||||
}
|
}
|
||||||
//
|
|
||||||
// /**
|
|
||||||
// * Get a property with default
|
|
||||||
// */
|
|
||||||
// override fun getItem(name: Name): MetaItem? = source[name] ?: descriptor?.get(name)?.defaultValue
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Check if property with given [name] could be assigned to [item]
|
* Check if property with given [name] could be assigned to [meta]
|
||||||
*/
|
*/
|
||||||
public open fun validate(name: Name, item: Meta?): Boolean {
|
public open fun validate(name: Name, meta: Meta?): Boolean {
|
||||||
val descriptor = descriptor?.get(name)
|
val descriptor = descriptor?.get(name)
|
||||||
return descriptor?.validateItem(item) ?: true
|
return descriptor?.validate(meta) ?: true
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -51,11 +55,25 @@ public open class Scheme internal constructor(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun toMeta(): Laminate = Laminate(source, descriptor?.defaultMeta)
|
override fun toMeta(): Laminate = Laminate(source, descriptor?.defaultNode)
|
||||||
|
|
||||||
|
override fun remove(name: Name) {
|
||||||
|
source.remove(name)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getOrCreate(name: Name): MutableMeta = source.getOrCreate(name)
|
||||||
|
|
||||||
|
override fun onChange(owner: Any?, callback: Meta.(name: Name) -> Unit) {
|
||||||
|
source.onChange(owner ?: this, callback)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun removeListener(owner: Any?) {
|
||||||
|
source.removeListener(owner ?: this)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Relocate scheme target onto given [MutableTypedMeta]. Old provider does not get updates anymore.
|
* Relocate scheme target onto given [MutableMeta]. Old provider does not get updates anymore.
|
||||||
* Current state of the scheme used as a default.
|
* Current state of the scheme used as a default.
|
||||||
*/
|
*/
|
||||||
public fun <T : Scheme> T.retarget(provider: MutableMeta): T = apply {
|
public fun <T : Scheme> T.retarget(provider: MutableMeta): T = apply {
|
||||||
@ -74,8 +92,8 @@ public open class SchemeSpec<out T : Scheme>(
|
|||||||
private val builder: () -> T,
|
private val builder: () -> T,
|
||||||
) : Specification<T>, Described {
|
) : Specification<T>, Described {
|
||||||
|
|
||||||
override fun read(items: Meta): T = empty().also {
|
override fun read(source: Meta): T = empty().also {
|
||||||
it.wrap(MutableMeta().withDefault(items))
|
it.wrap(MutableMeta().withDefault(source))
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun write(target: MutableMeta): T = empty().also {
|
override fun write(target: MutableMeta): T = empty().also {
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
package space.kscience.dataforge.meta
|
package space.kscience.dataforge.meta
|
||||||
|
|
||||||
|
import kotlinx.serialization.Serializable
|
||||||
import space.kscience.dataforge.names.NameToken
|
import space.kscience.dataforge.names.NameToken
|
||||||
import space.kscience.dataforge.values.Value
|
import space.kscience.dataforge.values.Value
|
||||||
|
|
||||||
@ -8,6 +9,7 @@ import space.kscience.dataforge.values.Value
|
|||||||
*
|
*
|
||||||
* If the argument is possibly mutable node, it is copied on creation
|
* If the argument is possibly mutable node, it is copied on creation
|
||||||
*/
|
*/
|
||||||
|
@Serializable
|
||||||
public class SealedMeta internal constructor(
|
public class SealedMeta internal constructor(
|
||||||
override val value: Value?,
|
override val value: Value?,
|
||||||
override val items: Map<NameToken, SealedMeta>
|
override val items: Map<NameToken, SealedMeta>
|
||||||
@ -28,6 +30,6 @@ public fun Meta.seal(): SealedMeta = this as? SealedMeta ?: SealedMeta(value, it
|
|||||||
public fun Meta(value: Value): SealedMeta = SealedMeta(value, emptyMap())
|
public fun Meta(value: Value): SealedMeta = SealedMeta(value, emptyMap())
|
||||||
|
|
||||||
@Suppress("FunctionName")
|
@Suppress("FunctionName")
|
||||||
public fun Meta(builder: MutableMeta.() -> Unit): SealedMeta =
|
public inline fun Meta(builder: MutableMeta.() -> Unit): SealedMeta =
|
||||||
MutableMeta(builder).seal()
|
MutableMeta(builder).seal()
|
||||||
|
|
||||||
|
@ -33,15 +33,15 @@ public interface ReadOnlySpecification<out T : Any> {
|
|||||||
*/
|
*/
|
||||||
public interface Specification<out T : Any> : ReadOnlySpecification<T> {
|
public interface Specification<out T : Any> : ReadOnlySpecification<T> {
|
||||||
/**
|
/**
|
||||||
* Wrap [MutableTypedMeta], using it as inner storage (changes to [Specification] are reflected on [MutableTypedMeta]
|
* Wrap [MutableMeta], using it as inner storage (changes to [Specification] are reflected on [MutableMeta]
|
||||||
*/
|
*/
|
||||||
public fun write(target: MutableMeta): T
|
public fun write(target: MutableMeta): T
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Update a [MutableTypedMeta] using given specification
|
* Update a [MutableMeta] using given specification
|
||||||
*/
|
*/
|
||||||
public fun <M : MutableTypedMeta<M>, T : Any> M.update(
|
public fun <T : Any> MutableMeta.update(
|
||||||
spec: Specification<T>,
|
spec: Specification<T>,
|
||||||
action: T.() -> Unit
|
action: T.() -> Unit
|
||||||
): T = spec.write(this).apply(action)
|
): T = spec.write(this).apply(action)
|
||||||
|
@ -5,20 +5,4 @@ package space.kscience.dataforge.meta.descriptors
|
|||||||
*/
|
*/
|
||||||
public interface Described {
|
public interface Described {
|
||||||
public val descriptor: MetaDescriptor?
|
public val descriptor: MetaDescriptor?
|
||||||
|
|
||||||
public companion object {
|
|
||||||
//public const val DESCRIPTOR_NODE: String = "@descriptor"
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
///**
|
|
||||||
// * If meta node supplies explicit descriptor, return it, otherwise try to use descriptor node from meta itself
|
|
||||||
// */
|
|
||||||
//val MetaRepr.descriptor: NodeDescriptor?
|
|
||||||
// get() {
|
|
||||||
// return if (this is Described) {
|
|
||||||
// descriptor
|
|
||||||
// } else {
|
|
||||||
// toMeta()[DESCRIPTOR_NODE].node?.let { NodeDescriptor.wrap(it) }
|
|
||||||
// }
|
|
||||||
// }
|
|
@ -2,6 +2,8 @@ package space.kscience.dataforge.meta.descriptors
|
|||||||
|
|
||||||
import kotlinx.serialization.Serializable
|
import kotlinx.serialization.Serializable
|
||||||
import space.kscience.dataforge.meta.Meta
|
import space.kscience.dataforge.meta.Meta
|
||||||
|
import space.kscience.dataforge.meta.get
|
||||||
|
import space.kscience.dataforge.meta.set
|
||||||
import space.kscience.dataforge.names.*
|
import space.kscience.dataforge.names.*
|
||||||
import space.kscience.dataforge.values.Value
|
import space.kscience.dataforge.values.Value
|
||||||
import space.kscience.dataforge.values.ValueType
|
import space.kscience.dataforge.values.ValueType
|
||||||
@ -12,7 +14,7 @@ import space.kscience.dataforge.values.ValueType
|
|||||||
* @param children child descriptors for this node
|
* @param children child descriptors for this node
|
||||||
* @param multiple True if same name siblings with this name are allowed
|
* @param multiple True if same name siblings with this name are allowed
|
||||||
* @param required True if the item is required
|
* @param required True if the item is required
|
||||||
* @param type list of allowed types for [Meta.value], null if all values are allowed
|
* @param type list of allowed types for [Meta.value], null if all values are allowed. If the type is [ValueType.NULL], no value is allowed for this node.
|
||||||
* @param indexKey An index field by which this node is identified in case of same name siblings construct
|
* @param indexKey An index field by which this node is identified in case of same name siblings construct
|
||||||
* @param defaultValue the default [Meta.value] for the node
|
* @param defaultValue the default [Meta.value] for the node
|
||||||
* @param attributes additional attributes of this descriptor. For example validation and widget parameters
|
* @param attributes additional attributes of this descriptor. For example validation and widget parameters
|
||||||
@ -27,7 +29,13 @@ public data class MetaDescriptor(
|
|||||||
public val indexKey: String = Meta.INDEX_KEY,
|
public val indexKey: String = Meta.INDEX_KEY,
|
||||||
public val defaultValue: Value? = null,
|
public val defaultValue: Value? = null,
|
||||||
public val attributes: Meta = Meta.EMPTY,
|
public val attributes: Meta = Meta.EMPTY,
|
||||||
)
|
) {
|
||||||
|
public companion object {
|
||||||
|
internal const val ALLOWED_VALUES_KEY = "allowedValues"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public val MetaDescriptor.allowedValues: List<Value>? get() = attributes[MetaDescriptor.ALLOWED_VALUES_KEY]?.value?.list
|
||||||
|
|
||||||
public operator fun MetaDescriptor.get(name: Name): MetaDescriptor? = when (name.length) {
|
public operator fun MetaDescriptor.get(name: Name): MetaDescriptor? = when (name.length) {
|
||||||
0 -> this
|
0 -> this
|
||||||
@ -37,24 +45,22 @@ public operator fun MetaDescriptor.get(name: Name): MetaDescriptor? = when (name
|
|||||||
|
|
||||||
public operator fun MetaDescriptor.get(name: String): MetaDescriptor? = get(name.toName())
|
public operator fun MetaDescriptor.get(name: String): MetaDescriptor? = get(name.toName())
|
||||||
|
|
||||||
public class MetaDescriptorBuilder {
|
/**
|
||||||
public var info: String? = null
|
* A node constructed of default values for this descriptor and its children
|
||||||
public var children: MutableMap<String, MetaDescriptor> = hashMapOf()
|
*/
|
||||||
public var multiple: Boolean = false
|
public val MetaDescriptor.defaultNode: Meta
|
||||||
public var required: Boolean = false
|
get() = Meta {
|
||||||
public var type: List<ValueType>? = null
|
defaultValue?.let { defaultValue ->
|
||||||
public var indexKey: String = Meta.INDEX_KEY
|
this.value = defaultValue
|
||||||
public var default: Value? = null
|
}
|
||||||
public var attributes: Meta = Meta.EMPTY
|
children.forEach { (key, descriptor) ->
|
||||||
|
set(key, descriptor.defaultNode)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
internal fun build(): MetaDescriptor = MetaDescriptor(
|
/**
|
||||||
info = info,
|
* Check if given item suits the descriptor
|
||||||
children = children,
|
*/
|
||||||
multiple = multiple,
|
public fun MetaDescriptor.validate(item: Meta?): Boolean = (item != null || !required) &&
|
||||||
required = required,
|
allowedValues?.let { item?.value in it } ?: true &&
|
||||||
type = type,
|
children.all { (key, childDescriptor) -> childDescriptor.validate(item?.get(key)) }
|
||||||
indexKey = indexKey,
|
|
||||||
defaultValue = default,
|
|
||||||
attributes = attributes
|
|
||||||
)
|
|
||||||
}
|
|
@ -0,0 +1,124 @@
|
|||||||
|
package space.kscience.dataforge.meta.descriptors
|
||||||
|
|
||||||
|
import space.kscience.dataforge.meta.Meta
|
||||||
|
import space.kscience.dataforge.meta.MutableMeta
|
||||||
|
import space.kscience.dataforge.meta.get
|
||||||
|
import space.kscience.dataforge.meta.set
|
||||||
|
import space.kscience.dataforge.names.*
|
||||||
|
import space.kscience.dataforge.values.Value
|
||||||
|
import space.kscience.dataforge.values.ValueType
|
||||||
|
import space.kscience.dataforge.values.asValue
|
||||||
|
|
||||||
|
public class MetaDescriptorBuilder {
|
||||||
|
public var info: String? = null
|
||||||
|
public var children: MutableMap<String, MetaDescriptorBuilder> = hashMapOf()
|
||||||
|
public var multiple: Boolean = false
|
||||||
|
public var required: Boolean = false
|
||||||
|
|
||||||
|
public var type: List<ValueType>? = null
|
||||||
|
|
||||||
|
public fun type(primaryType: ValueType, vararg otherTypes: ValueType) {
|
||||||
|
type = listOf(primaryType, *otherTypes)
|
||||||
|
}
|
||||||
|
|
||||||
|
public var indexKey: String = Meta.INDEX_KEY
|
||||||
|
public var default: Value? = null
|
||||||
|
|
||||||
|
public fun default(value: Any?) {
|
||||||
|
default = Value.of(value)
|
||||||
|
}
|
||||||
|
|
||||||
|
public var attributes: MutableMeta = MutableMeta()
|
||||||
|
|
||||||
|
public fun item(name: Name, block: MetaDescriptorBuilder.() -> Unit) {
|
||||||
|
when (name.length) {
|
||||||
|
0 -> apply(block)
|
||||||
|
1 -> {
|
||||||
|
val target = MetaDescriptorBuilder().apply(block)
|
||||||
|
children[name.first().body] = target
|
||||||
|
}
|
||||||
|
else -> {
|
||||||
|
children.getOrPut(name.first().body) { MetaDescriptorBuilder() }.item(name.cutFirst(), block)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public var allowedValues: List<Value>
|
||||||
|
get() = attributes[MetaDescriptor.ALLOWED_VALUES_KEY]?.value?.list ?: emptyList()
|
||||||
|
set(value) {
|
||||||
|
attributes[MetaDescriptor.ALLOWED_VALUES_KEY] = value
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public fun allowedValues(vararg values: Any) {
|
||||||
|
allowedValues = values.map { Value.of(it) }
|
||||||
|
}
|
||||||
|
|
||||||
|
internal fun build(): MetaDescriptor = MetaDescriptor(
|
||||||
|
info = info,
|
||||||
|
children = children.mapValues { it.value.build() },
|
||||||
|
multiple = multiple,
|
||||||
|
required = required,
|
||||||
|
type = type,
|
||||||
|
indexKey = indexKey,
|
||||||
|
defaultValue = default,
|
||||||
|
attributes = attributes
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
public fun MetaDescriptorBuilder.item(name: String, block: MetaDescriptorBuilder.() -> Unit) {
|
||||||
|
item(name.toName(), block)
|
||||||
|
}
|
||||||
|
|
||||||
|
public fun MetaDescriptor(block: MetaDescriptorBuilder.() -> Unit): MetaDescriptor =
|
||||||
|
MetaDescriptorBuilder().apply(block).build()
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create and configure child value descriptor
|
||||||
|
*/
|
||||||
|
public fun MetaDescriptorBuilder.value(
|
||||||
|
name: Name,
|
||||||
|
type: ValueType,
|
||||||
|
vararg additionalTypes: ValueType,
|
||||||
|
block: MetaDescriptorBuilder.() -> Unit
|
||||||
|
) {
|
||||||
|
item(name) {
|
||||||
|
type(type, *additionalTypes)
|
||||||
|
block()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public fun MetaDescriptorBuilder.value(
|
||||||
|
name: String,
|
||||||
|
type: ValueType,
|
||||||
|
vararg additionalTypes: ValueType,
|
||||||
|
block: MetaDescriptorBuilder.() -> Unit
|
||||||
|
) {
|
||||||
|
value(name.toName(), type, additionalTypes = additionalTypes, block)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create and configure child value descriptor
|
||||||
|
*/
|
||||||
|
public fun MetaDescriptorBuilder.node(name: Name, block: MetaDescriptorBuilder.() -> Unit) {
|
||||||
|
item(name) {
|
||||||
|
type(ValueType.NULL)
|
||||||
|
block()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public fun MetaDescriptorBuilder.node(name: String, block: MetaDescriptorBuilder.() -> Unit) {
|
||||||
|
node(name.toName(), block)
|
||||||
|
}
|
||||||
|
|
||||||
|
public inline fun <reified E : Enum<E>> MetaDescriptorBuilder.enum(
|
||||||
|
key: Name,
|
||||||
|
default: E?,
|
||||||
|
crossinline modifier: MetaDescriptorBuilder.() -> Unit = {},
|
||||||
|
): Unit = value(key,ValueType.STRING) {
|
||||||
|
default?.let {
|
||||||
|
this.default = default.asValue()
|
||||||
|
}
|
||||||
|
allowedValues = enumValues<E>().map { it.asValue() }
|
||||||
|
modifier()
|
||||||
|
}
|
@ -1,18 +0,0 @@
|
|||||||
package space.kscience.dataforge.meta.descriptors
|
|
||||||
|
|
||||||
import space.kscience.dataforge.names.Name
|
|
||||||
import space.kscience.dataforge.values.ValueType
|
|
||||||
import space.kscience.dataforge.values.asValue
|
|
||||||
|
|
||||||
public inline fun <reified E : Enum<E>> MetaDescriptorBuilder.enum(
|
|
||||||
key: Name,
|
|
||||||
default: E?,
|
|
||||||
crossinline modifier: MetaDescriptor.() -> Unit = {},
|
|
||||||
): Unit = value(key) {
|
|
||||||
type(ValueType.STRING)
|
|
||||||
default?.let {
|
|
||||||
default(default)
|
|
||||||
}
|
|
||||||
allowedValues = enumValues<E>().map { it.asValue() }
|
|
||||||
modifier()
|
|
||||||
}
|
|
@ -1,124 +1,89 @@
|
|||||||
package space.kscience.dataforge.meta.transformations
|
package space.kscience.dataforge.meta.transformations
|
||||||
|
|
||||||
import space.kscience.dataforge.meta.*
|
import space.kscience.dataforge.meta.*
|
||||||
import space.kscience.dataforge.values.*
|
import space.kscience.dataforge.values.Value
|
||||||
|
import space.kscience.dataforge.values.asValue
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A converter of generic object to and from [TypedMetaItem]
|
* A converter of generic object to and from [Meta]
|
||||||
*/
|
*/
|
||||||
public interface MetaConverter<T> {
|
public interface MetaConverter<T> {
|
||||||
public fun itemToObject(item: MetaItem): T
|
public fun metaToObject(meta: Meta): T?
|
||||||
public fun objectToMetaItem(obj: T): MetaItem
|
public fun objectToMeta(obj: T): Meta
|
||||||
|
|
||||||
public companion object {
|
public companion object {
|
||||||
|
|
||||||
public val item: MetaConverter<MetaItem> = object : MetaConverter<MetaItem> {
|
|
||||||
override fun itemToObject(item: MetaItem): MetaItem = item
|
|
||||||
override fun objectToMetaItem(obj: MetaItem): MetaItem = obj
|
|
||||||
}
|
|
||||||
|
|
||||||
public val meta: MetaConverter<Meta> = object : MetaConverter<Meta> {
|
public val meta: MetaConverter<Meta> = object : MetaConverter<Meta> {
|
||||||
override fun itemToObject(item: MetaItem): Meta = when (item) {
|
override fun metaToObject(meta: Meta): Meta = meta
|
||||||
is MetaItemNode -> item.node
|
override fun objectToMeta(obj: Meta): Meta = obj
|
||||||
is MetaItemValue -> item.value.toMeta()
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun objectToMetaItem(obj: Meta): MetaItem = MetaItemNode(obj)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public val value: MetaConverter<Value> = object : MetaConverter<Value> {
|
public val value: MetaConverter<Value> = object : MetaConverter<Value> {
|
||||||
override fun itemToObject(item: MetaItem): Value = when (item) {
|
override fun metaToObject(meta: Meta): Value? = meta.value
|
||||||
is MetaItemNode -> item.node[Meta.VALUE_KEY].value ?: error("Can't convert node to a value")
|
override fun objectToMeta(obj: Value): Meta = Meta(obj)
|
||||||
is MetaItemValue -> item.value
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun objectToMetaItem(obj: Value): MetaItem = MetaItemValue(obj)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public val string: MetaConverter<String> = object : MetaConverter<String> {
|
public val string: MetaConverter<String> = object : MetaConverter<String> {
|
||||||
override fun itemToObject(item: MetaItem): String = when (item) {
|
override fun metaToObject(meta: Meta): String? = meta.string
|
||||||
is MetaItemNode -> item.node[Meta.VALUE_KEY].value ?: error("Can't convert node to a value")
|
override fun objectToMeta(obj: String): Meta = Meta(obj.asValue())
|
||||||
is MetaItemValue -> item.value
|
|
||||||
}.string
|
|
||||||
|
|
||||||
override fun objectToMetaItem(obj: String): MetaItem = MetaItemValue(obj.asValue())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public val boolean: MetaConverter<Boolean> = object : MetaConverter<Boolean> {
|
public val boolean: MetaConverter<Boolean> = object : MetaConverter<Boolean> {
|
||||||
override fun itemToObject(item: MetaItem): Boolean = when (item) {
|
override fun metaToObject(meta: Meta): Boolean? = meta.boolean
|
||||||
is MetaItemNode -> item.node[Meta.VALUE_KEY].value ?: error("Can't convert node to a value")
|
override fun objectToMeta(obj: Boolean): Meta = Meta(obj.asValue())
|
||||||
is MetaItemValue -> item.value
|
|
||||||
}.boolean
|
|
||||||
|
|
||||||
override fun objectToMetaItem(obj: Boolean): MetaItem = MetaItemValue(obj.asValue())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public val number: MetaConverter<Number> = object : MetaConverter<Number> {
|
public val number: MetaConverter<Number> = object : MetaConverter<Number> {
|
||||||
override fun itemToObject(item: MetaItem): Number = when (item) {
|
override fun metaToObject(meta: Meta): Number? = meta.number
|
||||||
is MetaItemNode -> item.node[Meta.VALUE_KEY].value ?: error("Can't convert node to a value")
|
override fun objectToMeta(obj: Number): Meta = Meta(obj.asValue())
|
||||||
is MetaItemValue -> item.value
|
|
||||||
}.number
|
|
||||||
|
|
||||||
override fun objectToMetaItem(obj: Number): MetaItem = MetaItemValue(obj.asValue())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public val double: MetaConverter<Double> = object : MetaConverter<Double> {
|
public val double: MetaConverter<Double> = object : MetaConverter<Double> {
|
||||||
override fun itemToObject(item: MetaItem): Double = when (item) {
|
override fun metaToObject(meta: Meta): Double? = meta.double
|
||||||
is MetaItemNode -> item.node[Meta.VALUE_KEY].value ?: error("Can't convert node to a value")
|
|
||||||
is MetaItemValue -> item.value
|
|
||||||
}.double
|
|
||||||
|
|
||||||
override fun objectToMetaItem(obj: Double): MetaItem = MetaItemValue(obj.asValue())
|
override fun objectToMeta(obj: Double): Meta = Meta(obj.asValue())
|
||||||
}
|
}
|
||||||
|
|
||||||
public val float: MetaConverter<Float> = object : MetaConverter<Float> {
|
public val float: MetaConverter<Float> = object : MetaConverter<Float> {
|
||||||
override fun itemToObject(item: MetaItem): Float = when (item) {
|
override fun metaToObject(meta: Meta): Float? = meta.float
|
||||||
is MetaItemNode -> item.node[Meta.VALUE_KEY].value ?: error("Can't convert node to a value")
|
|
||||||
is MetaItemValue -> item.value
|
|
||||||
}.float
|
|
||||||
|
|
||||||
override fun objectToMetaItem(obj: Float): MetaItem = MetaItemValue(obj.asValue())
|
override fun objectToMeta(obj: Float): Meta = Meta(obj.asValue())
|
||||||
}
|
}
|
||||||
|
|
||||||
public val int: MetaConverter<Int> = object : MetaConverter<Int> {
|
public val int: MetaConverter<Int> = object : MetaConverter<Int> {
|
||||||
override fun itemToObject(item: MetaItem): Int = when (item) {
|
override fun metaToObject(meta: Meta): Int? = meta.int
|
||||||
is MetaItemNode -> item.node[Meta.VALUE_KEY].value ?: error("Can't convert node to a value")
|
|
||||||
is MetaItemValue -> item.value
|
|
||||||
}.int
|
|
||||||
|
|
||||||
override fun objectToMetaItem(obj: Int): MetaItem = MetaItemValue(obj.asValue())
|
override fun objectToMeta(obj: Int): Meta = Meta(obj.asValue())
|
||||||
}
|
}
|
||||||
|
|
||||||
public val long: MetaConverter<Long> = object : MetaConverter<Long> {
|
public val long: MetaConverter<Long> = object : MetaConverter<Long> {
|
||||||
override fun itemToObject(item: MetaItem): Long = when (item) {
|
override fun metaToObject(meta: Meta): Long? = meta.long
|
||||||
is MetaItemNode -> item.node[Meta.VALUE_KEY].value ?: error("Can't convert node to a value")
|
|
||||||
is MetaItemValue -> item.value
|
|
||||||
}.long
|
|
||||||
|
|
||||||
override fun objectToMetaItem(obj: Long): MetaItem = MetaItemValue(obj.asValue())
|
override fun objectToMeta(obj: Long): Meta = Meta(obj.asValue())
|
||||||
}
|
}
|
||||||
|
|
||||||
public inline fun <reified E : Enum<E>> enum(): MetaConverter<E> = object : MetaConverter<E> {
|
public inline fun <reified E : Enum<E>> enum(): MetaConverter<E> = object : MetaConverter<E> {
|
||||||
@Suppress("USELESS_CAST")
|
@Suppress("USELESS_CAST")
|
||||||
override fun itemToObject(item: MetaItem): E = item.enum<E>() as? E ?: error("The Item is not a Enum")
|
override fun metaToObject(meta: Meta): E = meta.enum<E>() as? E ?: error("The Item is not a Enum")
|
||||||
|
|
||||||
override fun objectToMetaItem(obj: E): MetaItem = MetaItemValue(obj.asValue())
|
override fun objectToMeta(obj: E): Meta = Meta(obj.asValue())
|
||||||
}
|
}
|
||||||
|
|
||||||
public fun <T> valueList(writer: (T) -> Value = { Value.of(it)}, reader: (Value) -> T): MetaConverter<List<T>> =
|
public fun <T> valueList(
|
||||||
|
writer: (T) -> Value = { Value.of(it) },
|
||||||
|
reader: (Value) -> T
|
||||||
|
): MetaConverter<List<T>> =
|
||||||
object : MetaConverter<List<T>> {
|
object : MetaConverter<List<T>> {
|
||||||
override fun itemToObject(item: MetaItem): List<T> =
|
override fun metaToObject(meta: Meta): List<T> =
|
||||||
item.value?.list?.map(reader) ?: error("The item is not a value list")
|
meta.value?.list?.map(reader) ?: error("The item is not a value list")
|
||||||
|
|
||||||
override fun objectToMetaItem(obj: List<T>): MetaItem =
|
override fun objectToMeta(obj: List<T>): Meta = Meta(obj.map(writer).asValue())
|
||||||
MetaItemValue(obj.map(writer).asValue())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public fun <T : Any> MetaConverter<T>.nullableItemToObject(item: MetaItem?): T? = item?.let { itemToObject(it) }
|
public fun <T : Any> MetaConverter<T>.nullableMetaToObject(item: Meta?): T? = item?.let { metaToObject(it) }
|
||||||
public fun <T : Any> MetaConverter<T>.nullableObjectToMetaItem(obj: T?): MetaItem? = obj?.let { objectToMetaItem(it) }
|
public fun <T : Any> MetaConverter<T>.nullableObjectToMeta(obj: T?): Meta? = obj?.let { objectToMeta(it) }
|
||||||
|
|
||||||
public fun <T> MetaConverter<T>.metaToObject(meta: Meta): T = itemToObject(MetaItemNode(meta))
|
public fun <T> MetaConverter<T>.valueToObject(value: Value): T? = metaToObject(Meta(value))
|
||||||
public fun <T> MetaConverter<T>.valueToObject(value: Value): T = itemToObject(MetaItemValue(value))
|
|
||||||
|
@ -13,7 +13,7 @@ public interface TransformationRule {
|
|||||||
/**
|
/**
|
||||||
* Check if this transformation should be applied to a node with given name and value
|
* Check if this transformation should be applied to a node with given name and value
|
||||||
*/
|
*/
|
||||||
public fun matches(name: Name, item: MetaItem?): Boolean
|
public fun matches(name: Name, item: Meta?): Boolean
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Select all items to be transformed. Item could be a value as well as node
|
* Select all items to be transformed. Item could be a value as well as node
|
||||||
@ -26,7 +26,7 @@ public interface TransformationRule {
|
|||||||
/**
|
/**
|
||||||
* Apply transformation for a single item (Node or Value) to the target
|
* Apply transformation for a single item (Node or Value) to the target
|
||||||
*/
|
*/
|
||||||
public fun transformItem(name: Name, item: MetaItem?, target: MutableTypedMeta): Unit
|
public fun transformItem(name: Name, item: Meta?, target: MutableMeta): Unit
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -34,14 +34,14 @@ public interface TransformationRule {
|
|||||||
*/
|
*/
|
||||||
public data class KeepTransformationRule(val selector: (Name) -> Boolean) :
|
public data class KeepTransformationRule(val selector: (Name) -> Boolean) :
|
||||||
TransformationRule {
|
TransformationRule {
|
||||||
override fun matches(name: Name, item: MetaItem?): Boolean {
|
override fun matches(name: Name, item: Meta?): Boolean {
|
||||||
return selector(name)
|
return selector(name)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun selectItems(meta: Meta): Sequence<Name> =
|
override fun selectItems(meta: Meta): Sequence<Name> =
|
||||||
meta.nodeSequence().map { it.first }.filter(selector)
|
meta.nodeSequence().map { it.first }.filter(selector)
|
||||||
|
|
||||||
override fun transformItem(name: Name, item: MetaItem?, target: MutableTypedMeta) {
|
override fun transformItem(name: Name, item: Meta?, target: MutableMeta) {
|
||||||
if (selector(name)) target.set(name, item)
|
if (selector(name)) target.set(name, item)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -51,15 +51,15 @@ public data class KeepTransformationRule(val selector: (Name) -> Boolean) :
|
|||||||
*/
|
*/
|
||||||
public data class SingleItemTransformationRule(
|
public data class SingleItemTransformationRule(
|
||||||
val from: Name,
|
val from: Name,
|
||||||
val transform: MutableTypedMeta.(Name, MetaItem?) -> Unit,
|
val transform: MutableMeta.(Name, Meta?) -> Unit,
|
||||||
) : TransformationRule {
|
) : TransformationRule {
|
||||||
override fun matches(name: Name, item: MetaItem?): Boolean {
|
override fun matches(name: Name, item: Meta?): Boolean {
|
||||||
return name == from
|
return name == from
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun selectItems(meta: Meta): Sequence<Name> = sequenceOf(from)
|
override fun selectItems(meta: Meta): Sequence<Name> = sequenceOf(from)
|
||||||
|
|
||||||
override fun transformItem(name: Name, item: MetaItem?, target: MutableTypedMeta) {
|
override fun transformItem(name: Name, item: Meta?, target: MutableMeta) {
|
||||||
if (name == this.from) {
|
if (name == this.from) {
|
||||||
target.transform(name, item)
|
target.transform(name, item)
|
||||||
}
|
}
|
||||||
@ -68,13 +68,13 @@ public data class SingleItemTransformationRule(
|
|||||||
|
|
||||||
public data class RegexItemTransformationRule(
|
public data class RegexItemTransformationRule(
|
||||||
val from: Regex,
|
val from: Regex,
|
||||||
val transform: MutableTypedMeta.(name: Name, MatchResult, MetaItem?) -> Unit,
|
val transform: MutableMeta.(name: Name, MatchResult, Meta?) -> Unit,
|
||||||
) : TransformationRule {
|
) : TransformationRule {
|
||||||
override fun matches(name: Name, item: MetaItem?): Boolean {
|
override fun matches(name: Name, item: Meta?): Boolean {
|
||||||
return from.matches(name.toString())
|
return from.matches(name.toString())
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun transformItem(name: Name, item: MetaItem?, target: MutableTypedMeta) {
|
override fun transformItem(name: Name, item: Meta?, target: MutableMeta) {
|
||||||
val match = from.matchEntire(name.toString())
|
val match = from.matchEntire(name.toString())
|
||||||
if (match != null) {
|
if (match != null) {
|
||||||
target.transform(name, match, item)
|
target.transform(name, match, item)
|
||||||
@ -105,7 +105,7 @@ public value class MetaTransformation(private val transformations: Collection<Tr
|
|||||||
* Generate an observable configuration that contains only elements defined by transformation rules and changes with the source
|
* Generate an observable configuration that contains only elements defined by transformation rules and changes with the source
|
||||||
*/
|
*/
|
||||||
@DFExperimental
|
@DFExperimental
|
||||||
public fun generate(source: MutableMeta): ObservableMeta = MutableMeta().apply {
|
public fun generate(source: ObservableMeta): ObservableMeta = MutableMeta().apply {
|
||||||
transformations.forEach { rule ->
|
transformations.forEach { rule ->
|
||||||
rule.selectItems(source).forEach { name ->
|
rule.selectItems(source).forEach { name ->
|
||||||
rule.transformItem(name, source[name], this)
|
rule.transformItem(name, source[name], this)
|
||||||
@ -131,8 +131,9 @@ public value class MetaTransformation(private val transformations: Collection<Tr
|
|||||||
/**
|
/**
|
||||||
* Listens for changes in the source node and translates them into second node if transformation set contains a corresponding rule.
|
* Listens for changes in the source node and translates them into second node if transformation set contains a corresponding rule.
|
||||||
*/
|
*/
|
||||||
public fun bind(source: ObservableMeta, target: MutableTypedMeta) {
|
public fun bind(source: ObservableMeta, target: MutableMeta) {
|
||||||
source.onChange(target) { name, _, newItem ->
|
source.onChange(target) { name ->
|
||||||
|
val newItem = source[name]
|
||||||
transformations.forEach { t ->
|
transformations.forEach { t ->
|
||||||
if (t.matches(name, newItem)) {
|
if (t.matches(name, newItem)) {
|
||||||
t.transformItem(name, newItem, target)
|
t.transformItem(name, newItem, target)
|
||||||
@ -172,15 +173,15 @@ public class MetaTransformationBuilder {
|
|||||||
*/
|
*/
|
||||||
public fun keep(regex: String) {
|
public fun keep(regex: String) {
|
||||||
transformations.add(
|
transformations.add(
|
||||||
RegexItemTransformationRule(regex.toRegex()) { name, _, metaItem ->
|
RegexItemTransformationRule(regex.toRegex()) { name, _, Meta ->
|
||||||
set(name, metaItem)
|
set(name, Meta)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Move an item from [from] to [to], optionally applying [operation] it defined
|
* Move an item from [from] to [to], optionally applying [operation] it defined
|
||||||
*/
|
*/
|
||||||
public fun move(from: Name, to: Name, operation: (MetaItem?) -> Any? = { it }) {
|
public fun move(from: Name, to: Name, operation: (Meta?) -> Meta? = { it }) {
|
||||||
transformations.add(
|
transformations.add(
|
||||||
SingleItemTransformationRule(from) { _, item ->
|
SingleItemTransformationRule(from) { _, item ->
|
||||||
set(to, operation(item))
|
set(to, operation(item))
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
package space.kscience.dataforge.values
|
package space.kscience.dataforge.values
|
||||||
|
|
||||||
import kotlinx.serialization.Serializable
|
import kotlinx.serialization.Serializable
|
||||||
|
import kotlin.jvm.JvmInline
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -153,16 +154,10 @@ public class NumberValue(public val number: Number) : Value {
|
|||||||
override fun hashCode(): Int = numberOrNull.hashCode()
|
override fun hashCode(): Int = numberOrNull.hashCode()
|
||||||
}
|
}
|
||||||
|
|
||||||
public class StringValue(public val string: String) : Value {
|
@JvmInline
|
||||||
|
public value class StringValue(public val string: String) : Value {
|
||||||
override val value: Any get() = string
|
override val value: Any get() = string
|
||||||
override val type: ValueType get() = ValueType.STRING
|
override val type: ValueType get() = ValueType.STRING
|
||||||
|
|
||||||
override fun equals(other: Any?): Boolean {
|
|
||||||
return this.string == (other as? Value)?.string
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun hashCode(): Int = string.hashCode()
|
|
||||||
|
|
||||||
override fun toString(): String = string
|
override fun toString(): String = string
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -204,6 +199,9 @@ public class ListValue(override val list: List<Value>) : Value, Iterable<Value>
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public fun ListValue(vararg numbers: Number): ListValue = ListValue(numbers.map{it.asValue()})
|
||||||
|
public fun ListValue(vararg strings: String): ListValue = ListValue(strings.map{it.asValue()})
|
||||||
|
|
||||||
public fun Number.asValue(): Value = NumberValue(this)
|
public fun Number.asValue(): Value = NumberValue(this)
|
||||||
|
|
||||||
public fun Boolean.asValue(): Value = if (this) True else False
|
public fun Boolean.asValue(): Value = if (this) True else False
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
package space.kscience.dataforge.values
|
package space.kscience.dataforge.values
|
||||||
|
|
||||||
import space.kscience.dataforge.meta.Meta
|
import space.kscience.dataforge.meta.Meta
|
||||||
import space.kscience.dataforge.meta.MutableMeta
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Check if value is null
|
* Check if value is null
|
||||||
@ -35,4 +34,4 @@ public val Value.doubleArray: DoubleArray
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public fun Value.toMeta(): MutableMeta = Meta { Meta.VALUE_KEY put this }
|
public fun Value.toMeta(): Meta = Meta(this)
|
@ -1,7 +1,8 @@
|
|||||||
package space.kscience.dataforge.meta
|
package space.kscience.dataforge.meta
|
||||||
|
|
||||||
import kotlinx.serialization.json.*
|
import kotlinx.serialization.json.*
|
||||||
import space.kscience.dataforge.meta.descriptors.NodeDescriptor
|
import space.kscience.dataforge.meta.descriptors.MetaDescriptor
|
||||||
|
import space.kscience.dataforge.meta.descriptors.item
|
||||||
import kotlin.test.Test
|
import kotlin.test.Test
|
||||||
import kotlin.test.assertEquals
|
import kotlin.test.assertEquals
|
||||||
|
|
||||||
@ -30,8 +31,8 @@ class JsonMetaTest {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
val descriptor = NodeDescriptor{
|
val descriptor = MetaDescriptor {
|
||||||
node("nodeArray"){
|
item("nodeArray") {
|
||||||
indexKey = "index"
|
indexKey = "index"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -43,8 +44,17 @@ class JsonMetaTest {
|
|||||||
//println(meta)
|
//println(meta)
|
||||||
val reconstructed = meta.toJson(descriptor)
|
val reconstructed = meta.toJson(descriptor)
|
||||||
println(reconstructed)
|
println(reconstructed)
|
||||||
assertEquals(2,
|
assertEquals(
|
||||||
reconstructed["nodeArray"]?.jsonArray?.get(1)?.jsonObject?.get("index")?.jsonPrimitive?.int)
|
2,
|
||||||
assertEquals(json,reconstructed)
|
reconstructed
|
||||||
|
.jsonObject["nodeArray"]
|
||||||
|
?.jsonArray
|
||||||
|
?.get(1)
|
||||||
|
?.jsonObject
|
||||||
|
?.get("index")
|
||||||
|
?.jsonPrimitive
|
||||||
|
?.int
|
||||||
|
)
|
||||||
|
assertEquals(json, reconstructed)
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,5 +1,6 @@
|
|||||||
package space.kscience.dataforge.meta
|
package space.kscience.dataforge.meta
|
||||||
|
|
||||||
|
import space.kscience.dataforge.values.Value
|
||||||
import space.kscience.dataforge.values.asValue
|
import space.kscience.dataforge.values.asValue
|
||||||
import kotlin.test.Test
|
import kotlin.test.Test
|
||||||
import kotlin.test.assertEquals
|
import kotlin.test.assertEquals
|
||||||
@ -10,7 +11,7 @@ class MetaBuilderTest {
|
|||||||
fun testBuilder() {
|
fun testBuilder() {
|
||||||
val meta = Meta {
|
val meta = Meta {
|
||||||
"a" put 22
|
"a" put 22
|
||||||
"b" put listOf(1, 2, 3)
|
"b" put Value.of(listOf(1, 2, 3))
|
||||||
this["c"] = "myValue".asValue()
|
this["c"] = "myValue".asValue()
|
||||||
"node" put {
|
"node" put {
|
||||||
"e" put 12.2
|
"e" put 12.2
|
||||||
|
@ -6,7 +6,7 @@ import kotlin.test.assertEquals
|
|||||||
class MutableMetaTest{
|
class MutableMetaTest{
|
||||||
@Test
|
@Test
|
||||||
fun testRemove(){
|
fun testRemove(){
|
||||||
val meta = Meta {
|
val meta = MutableMeta {
|
||||||
"aNode" put {
|
"aNode" put {
|
||||||
"innerNode" put {
|
"innerNode" put {
|
||||||
"innerValue" put true
|
"innerValue" put true
|
||||||
|
@ -50,7 +50,7 @@ class SpecificationTest {
|
|||||||
@Test
|
@Test
|
||||||
fun testChildModification() {
|
fun testChildModification() {
|
||||||
val config = MutableMeta()
|
val config = MutableMeta()
|
||||||
val child = config.getChild("child")
|
val child = config.getOrCreate("child")
|
||||||
val scheme = TestScheme.write(child)
|
val scheme = TestScheme.write(child)
|
||||||
scheme.a = 22
|
scheme.a = 22
|
||||||
scheme.b = "test"
|
scheme.b = "test"
|
||||||
@ -61,7 +61,7 @@ class SpecificationTest {
|
|||||||
@Test
|
@Test
|
||||||
fun testChildUpdate() {
|
fun testChildUpdate() {
|
||||||
val config = MutableMeta()
|
val config = MutableMeta()
|
||||||
val child = config.getChild("child")
|
val child = config.getOrCreate("child")
|
||||||
child.update(TestScheme) {
|
child.update(TestScheme) {
|
||||||
a = 22
|
a = 22
|
||||||
b = "test"
|
b = "test"
|
||||||
|
@ -9,16 +9,14 @@ import kotlin.test.assertNotNull
|
|||||||
|
|
||||||
class DescriptorTest {
|
class DescriptorTest {
|
||||||
|
|
||||||
val descriptor = NodeDescriptor {
|
val descriptor = MetaDescriptor {
|
||||||
node("aNode") {
|
node("aNode") {
|
||||||
info = "A root demo node"
|
info = "A root demo node"
|
||||||
value("b") {
|
value("b", ValueType.NUMBER) {
|
||||||
info = "b number value"
|
info = "b number value"
|
||||||
type(ValueType.NUMBER)
|
|
||||||
}
|
}
|
||||||
node("otherNode") {
|
node("otherNode") {
|
||||||
value("otherValue") {
|
value("otherValue", ValueType.BOOLEAN) {
|
||||||
type(ValueType.BOOLEAN)
|
|
||||||
default(false)
|
default(false)
|
||||||
info = "default value"
|
info = "default value"
|
||||||
}
|
}
|
||||||
@ -30,13 +28,13 @@ class DescriptorTest {
|
|||||||
fun testAllowedValues() {
|
fun testAllowedValues() {
|
||||||
val child = descriptor["aNode.b"]
|
val child = descriptor["aNode.b"]
|
||||||
assertNotNull(child)
|
assertNotNull(child)
|
||||||
val allowed = descriptor.nodes["aNode"]?.values?.get("b")?.allowedValues
|
val allowed = descriptor["aNode"]?.get("b")?.allowedValues
|
||||||
assertEquals(emptyList(), allowed)
|
assertEquals(null, allowed)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun testDefaultMetaNode(){
|
fun testDefaultMetaNode() {
|
||||||
val meta = descriptor.defaultMeta
|
val meta = descriptor.defaultNode
|
||||||
assertEquals(false, meta["aNode.otherNode.otherValue"].boolean)
|
assertEquals(false, meta["aNode.otherNode.otherValue"].boolean)
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,8 +1,8 @@
|
|||||||
package space.kscience.dataforge.meta
|
package space.kscience.dataforge.meta
|
||||||
|
|
||||||
import space.kscience.dataforge.names.NameToken
|
import space.kscience.dataforge.names.NameToken
|
||||||
import space.kscience.dataforge.values.Null
|
|
||||||
import space.kscience.dataforge.values.Value
|
import space.kscience.dataforge.values.Value
|
||||||
|
import space.kscience.dataforge.values.asValue
|
||||||
import space.kscience.dataforge.values.isList
|
import space.kscience.dataforge.values.isList
|
||||||
|
|
||||||
|
|
||||||
@ -22,11 +22,6 @@ public fun Value.toDynamic(): dynamic {
|
|||||||
public fun Meta.toDynamic(): dynamic {
|
public fun Meta.toDynamic(): dynamic {
|
||||||
if (this is DynamicMeta) return this.obj
|
if (this is DynamicMeta) return this.obj
|
||||||
|
|
||||||
fun MetaItem.toDynamic(): dynamic = when (this) {
|
|
||||||
is MetaItemValue -> this.value.toDynamic()
|
|
||||||
is MetaItemNode -> this.node.toDynamic()
|
|
||||||
}
|
|
||||||
|
|
||||||
val res = js("{}")
|
val res = js("{}")
|
||||||
this.items.entries.groupBy { it.key.body }.forEach { (key, value) ->
|
this.items.entries.groupBy { it.key.body }.forEach { (key, value) ->
|
||||||
val list = value.map { it.value }
|
val list = value.map { it.value }
|
||||||
@ -38,46 +33,51 @@ public fun Meta.toDynamic(): dynamic {
|
|||||||
return res
|
return res
|
||||||
}
|
}
|
||||||
|
|
||||||
public class DynamicMeta(internal val obj: dynamic) : AbstractTypedMeta() {
|
public class DynamicMeta(internal val obj: dynamic) : Meta {
|
||||||
private fun keys(): Array<String> = js("Object").keys(obj)
|
private fun keys(): Array<String> = js("Object").keys(obj)
|
||||||
|
|
||||||
private fun isArray(@Suppress("UNUSED_PARAMETER") obj: dynamic): Boolean =
|
private fun isArray(@Suppress("UNUSED_PARAMETER") obj: dynamic): Boolean =
|
||||||
js("Array.isArray(obj)") as Boolean
|
js("Array").isArray(obj) as Boolean
|
||||||
|
|
||||||
private fun isPrimitive(obj: dynamic): Boolean =
|
private fun isPrimitive(obj: dynamic): Boolean =
|
||||||
(jsTypeOf(obj) != "object")
|
(jsTypeOf(obj) != "object")
|
||||||
|
|
||||||
@Suppress("UNCHECKED_CAST", "USELESS_CAST")
|
@Suppress("USELESS_CAST")
|
||||||
private fun asItem(obj: dynamic): TypedMetaItem<DynamicMeta>? {
|
override val value: Value?
|
||||||
return when {
|
get() = if (isArray(obj) && (obj as Array<Any?>).all { isPrimitive(it) }) Value.of(obj as Array<Any?>)
|
||||||
obj == null -> MetaItemValue(Null)
|
else when (jsTypeOf(obj)) {
|
||||||
isArray(obj) && (obj as Array<Any?>).all { isPrimitive(it) } -> MetaItemValue(Value.of(obj as Array<Any?>))
|
"boolean" -> (obj as Boolean).asValue()
|
||||||
else -> when (jsTypeOf(obj)) {
|
"number" -> (obj as Number).asValue()
|
||||||
"boolean" -> MetaItemValue(Value.of(obj as Boolean))
|
"string" -> (obj as String).asValue()
|
||||||
"number" -> MetaItemValue(Value.of(obj as Number))
|
|
||||||
"string" -> MetaItemValue(Value.of(obj as String))
|
|
||||||
"object" -> MetaItemNode(DynamicMeta(obj))
|
|
||||||
else -> null
|
else -> null
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override val items: Map<NameToken, TypedMetaItem<DynamicMeta>>
|
override val items: Map<NameToken, Meta>
|
||||||
get() = keys().flatMap<String, Pair<NameToken, TypedMetaItem<DynamicMeta>>> { key ->
|
get() = keys().flatMap<String, Pair<NameToken, Meta>> { key ->
|
||||||
val value = obj[key] ?: return@flatMap emptyList()
|
val value = obj[key] ?: return@flatMap emptyList()
|
||||||
if (isArray(value)) {
|
when {
|
||||||
|
isArray(value) -> {
|
||||||
val array = value as Array<Any?>
|
val array = value as Array<Any?>
|
||||||
return@flatMap if (array.all { isPrimitive(it) }) {
|
if (array.all { isPrimitive(it) }) {
|
||||||
listOf(NameToken(key) to MetaItemValue(Value.of(array)))
|
emptyList()
|
||||||
} else {
|
} else {
|
||||||
array.mapIndexedNotNull { index, it ->
|
array.mapIndexedNotNull { index, it ->
|
||||||
val item = asItem(it) ?: return@mapIndexedNotNull null
|
val item = DynamicMeta(it)
|
||||||
NameToken(key, index.toString()) to item
|
NameToken(key, index.toString()) to item
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
}
|
||||||
val item = asItem(value) ?: return@flatMap emptyList()
|
isPrimitive(obj) -> {
|
||||||
|
emptyList()
|
||||||
|
}
|
||||||
|
else -> {
|
||||||
|
val item = DynamicMeta(value)
|
||||||
listOf(NameToken(key) to item)
|
listOf(NameToken(key) to item)
|
||||||
}
|
}
|
||||||
}.associate { it }
|
}
|
||||||
|
}.toMap()
|
||||||
|
|
||||||
|
override fun toString(): String = Meta.toString(this)
|
||||||
|
override fun equals(other: Any?): Boolean = Meta.equals(this, other as? Meta)
|
||||||
|
override fun hashCode(): Int = Meta.hashCode(this)
|
||||||
}
|
}
|
@ -1,5 +1,6 @@
|
|||||||
package space.kscience.dataforge.meta
|
package space.kscience.dataforge.meta
|
||||||
|
|
||||||
|
import space.kscience.dataforge.values.ListValue
|
||||||
import space.kscience.dataforge.values.int
|
import space.kscience.dataforge.values.int
|
||||||
import kotlin.test.Test
|
import kotlin.test.Test
|
||||||
import kotlin.test.assertEquals
|
import kotlin.test.assertEquals
|
||||||
@ -21,7 +22,7 @@ class DynamicMetaTest {
|
|||||||
val meta = DynamicMeta(d)
|
val meta = DynamicMeta(d)
|
||||||
println(meta)
|
println(meta)
|
||||||
assertEquals(true, meta["ob.booleanNode"].boolean)
|
assertEquals(true, meta["ob.booleanNode"].boolean)
|
||||||
assertEquals(2, meta["array"].value?.list?.get(1)?.int)
|
assertEquals(2, meta["array"]?.value?.list?.get(1)?.int)
|
||||||
assertEquals(4, meta.items.size)
|
assertEquals(4, meta.items.size)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -29,7 +30,7 @@ class DynamicMetaTest {
|
|||||||
fun testMetaToDynamic(){
|
fun testMetaToDynamic(){
|
||||||
val meta = Meta {
|
val meta = Meta {
|
||||||
"a" put 22
|
"a" put 22
|
||||||
"array" put listOf(1, 2, 3)
|
"array" put ListValue(1, 2, 3)
|
||||||
"b" put "myString"
|
"b" put "myString"
|
||||||
"ob" put {
|
"ob" put {
|
||||||
"childNode" put 18
|
"childNode" put 18
|
||||||
|
@ -6,7 +6,7 @@ import space.kscience.dataforge.data.DataTree
|
|||||||
import space.kscience.dataforge.data.GoalExecutionRestriction
|
import space.kscience.dataforge.data.GoalExecutionRestriction
|
||||||
import space.kscience.dataforge.meta.Meta
|
import space.kscience.dataforge.meta.Meta
|
||||||
import space.kscience.dataforge.meta.descriptors.Described
|
import space.kscience.dataforge.meta.descriptors.Described
|
||||||
import space.kscience.dataforge.meta.descriptors.ItemDescriptor
|
import space.kscience.dataforge.meta.descriptors.MetaDescriptor
|
||||||
import space.kscience.dataforge.misc.DFInternal
|
import space.kscience.dataforge.misc.DFInternal
|
||||||
import space.kscience.dataforge.misc.Type
|
import space.kscience.dataforge.misc.Type
|
||||||
import space.kscience.dataforge.names.Name
|
import space.kscience.dataforge.names.Name
|
||||||
@ -47,11 +47,11 @@ public class TaskResultBuilder<T : Any>(
|
|||||||
@DFInternal
|
@DFInternal
|
||||||
public fun <T : Any> Task(
|
public fun <T : Any> Task(
|
||||||
resultType: KType,
|
resultType: KType,
|
||||||
descriptor: ItemDescriptor? = null,
|
descriptor: MetaDescriptor? = null,
|
||||||
builder: suspend TaskResultBuilder<T>.() -> Unit,
|
builder: suspend TaskResultBuilder<T>.() -> Unit,
|
||||||
): Task<T> = object : Task<T> {
|
): Task<T> = object : Task<T> {
|
||||||
|
|
||||||
override val descriptor: ItemDescriptor? = descriptor
|
override val descriptor: MetaDescriptor? = descriptor
|
||||||
|
|
||||||
override suspend fun execute(
|
override suspend fun execute(
|
||||||
workspace: Workspace,
|
workspace: Workspace,
|
||||||
@ -69,6 +69,6 @@ public fun <T : Any> Task(
|
|||||||
@OptIn(DFInternal::class)
|
@OptIn(DFInternal::class)
|
||||||
@Suppress("FunctionName")
|
@Suppress("FunctionName")
|
||||||
public inline fun <reified T : Any> Task(
|
public inline fun <reified T : Any> Task(
|
||||||
descriptor: ItemDescriptor? = null,
|
descriptor: MetaDescriptor? = null,
|
||||||
noinline builder: suspend TaskResultBuilder<T>.() -> Unit,
|
noinline builder: suspend TaskResultBuilder<T>.() -> Unit,
|
||||||
): Task<T> = Task(typeOf<T>(), descriptor, builder)
|
): Task<T> = Task(typeOf<T>(), descriptor, builder)
|
@ -9,7 +9,8 @@ import space.kscience.dataforge.data.DataSetBuilder
|
|||||||
import space.kscience.dataforge.data.DataTree
|
import space.kscience.dataforge.data.DataTree
|
||||||
import space.kscience.dataforge.meta.Meta
|
import space.kscience.dataforge.meta.Meta
|
||||||
import space.kscience.dataforge.meta.MutableMeta
|
import space.kscience.dataforge.meta.MutableMeta
|
||||||
import space.kscience.dataforge.meta.descriptors.NodeDescriptor
|
import space.kscience.dataforge.meta.descriptors.MetaDescriptor
|
||||||
|
import space.kscience.dataforge.meta.descriptors.MetaDescriptorBuilder
|
||||||
import space.kscience.dataforge.misc.DFBuilder
|
import space.kscience.dataforge.misc.DFBuilder
|
||||||
import space.kscience.dataforge.misc.DFExperimental
|
import space.kscience.dataforge.misc.DFExperimental
|
||||||
import space.kscience.dataforge.names.Name
|
import space.kscience.dataforge.names.Name
|
||||||
@ -26,16 +27,16 @@ public interface TaskContainer {
|
|||||||
|
|
||||||
public inline fun <reified T : Any> TaskContainer.registerTask(
|
public inline fun <reified T : Any> TaskContainer.registerTask(
|
||||||
name: String,
|
name: String,
|
||||||
noinline descriptorBuilder: NodeDescriptor.() -> Unit = {},
|
noinline descriptorBuilder: MetaDescriptorBuilder.() -> Unit = {},
|
||||||
noinline builder: suspend TaskResultBuilder<T>.() -> Unit,
|
noinline builder: suspend TaskResultBuilder<T>.() -> Unit,
|
||||||
): Unit = registerTask(name.toName(), Task(NodeDescriptor(descriptorBuilder), builder))
|
): Unit = registerTask(name.toName(), Task(MetaDescriptor(descriptorBuilder), builder))
|
||||||
|
|
||||||
public inline fun <reified T : Any> TaskContainer.task(
|
public inline fun <reified T : Any> TaskContainer.task(
|
||||||
noinline descriptorBuilder: NodeDescriptor.() -> Unit = {},
|
noinline descriptorBuilder: MetaDescriptorBuilder.() -> Unit = {},
|
||||||
noinline builder: suspend TaskResultBuilder<T>.() -> Unit,
|
noinline builder: suspend TaskResultBuilder<T>.() -> Unit,
|
||||||
): PropertyDelegateProvider<Any?, ReadOnlyProperty<Any?, TaskReference<T>>> = PropertyDelegateProvider { _, property ->
|
): PropertyDelegateProvider<Any?, ReadOnlyProperty<Any?, TaskReference<T>>> = PropertyDelegateProvider { _, property ->
|
||||||
val taskName = property.name.toName()
|
val taskName = property.name.toName()
|
||||||
val task = Task(NodeDescriptor(descriptorBuilder), builder)
|
val task = Task(MetaDescriptor(descriptorBuilder), builder)
|
||||||
registerTask(taskName, task)
|
registerTask(taskName, task)
|
||||||
ReadOnlyProperty { _, _ -> TaskReference(taskName, task) }
|
ReadOnlyProperty { _, _ -> TaskReference(taskName, task) }
|
||||||
}
|
}
|
||||||
|
@ -86,7 +86,7 @@ public suspend fun <T : Any> DataSetBuilder<T>.file(
|
|||||||
//otherwise, read as directory
|
//otherwise, read as directory
|
||||||
plugin.run {
|
plugin.run {
|
||||||
val data = readDataDirectory(path, formatResolver)
|
val data = readDataDirectory(path, formatResolver)
|
||||||
val name = data.getMeta()[Envelope.ENVELOPE_NAME_KEY].string
|
val name = data.getMeta()?.get(Envelope.ENVELOPE_NAME_KEY).string
|
||||||
?: path.fileName.toString().replace(".df", "")
|
?: path.fileName.toString().replace(".df", "")
|
||||||
emit(name, data)
|
emit(name, data)
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user