Replace missing query by null in NameToken

This commit is contained in:
Alexander Nozik 2020-07-26 15:00:03 +03:00
parent c3d4836a11
commit 4fde6c4a48
9 changed files with 33 additions and 38 deletions

View File

@ -21,7 +21,7 @@ class MetaSerializerTest {
fun testMetaSerialization() { fun testMetaSerialization() {
val string = JSON_PRETTY.stringify(MetaSerializer, meta) val string = JSON_PRETTY.stringify(MetaSerializer, meta)
val restored = JSON_PLAIN.parse(MetaSerializer, string) val restored = JSON_PLAIN.parse(MetaSerializer, string)
assertEquals(restored, meta) assertEquals(meta, restored)
} }
@Test @Test
@ -29,7 +29,7 @@ class MetaSerializerTest {
val bytes = Cbor.dump(MetaSerializer, meta) val bytes = Cbor.dump(MetaSerializer, meta)
println(bytes.contentToString()) println(bytes.contentToString())
val restored = Cbor.load(MetaSerializer, bytes) val restored = Cbor.load(MetaSerializer, bytes)
assertEquals(restored, meta) assertEquals(meta, restored)
} }
@Test @Test
@ -37,7 +37,7 @@ class MetaSerializerTest {
val name = "a.b.c".toName() val name = "a.b.c".toName()
val string = JSON_PRETTY.stringify(Name.serializer(), name) val string = JSON_PRETTY.stringify(Name.serializer(), name)
val restored = JSON_PLAIN.parse(Name.serializer(), string) val restored = JSON_PLAIN.parse(Name.serializer(), string)
assertEquals(restored, name) assertEquals(name, restored)
} }
@Test @Test

View File

@ -26,7 +26,7 @@ interface Configurable : Described, MutableItemProvider {
fun getDefaultItem(name: Name): MetaItem<*>? = null fun getDefaultItem(name: Name): MetaItem<*>? = null
/** /**
* Check if property with given [name] could be assigned to [value] * Check if property with given [name] could be assigned to [item]
*/ */
fun validateItem(name: Name, item: MetaItem<*>?): Boolean { fun validateItem(name: Name, item: MetaItem<*>?): Boolean {
val descriptor = descriptor?.get(name) val descriptor = descriptor?.get(name)

View File

@ -8,7 +8,7 @@ import kotlin.properties.ReadOnlyProperty
import kotlin.reflect.KProperty import kotlin.reflect.KProperty
/* Meta delegates */ /* Meta delegates */
//TODO to be replaced in 1.4 by interfaces
open class ItemDelegate( open class ItemDelegate(
open val owner: ItemProvider, open val owner: ItemProvider,
val key: Name? = null, val key: Name? = null,

View File

@ -52,7 +52,7 @@ private fun Meta.toJsonWithIndex(descriptor: NodeDescriptor?, indexValue: String
fun addElement(key: String) { fun addElement(key: String) {
val itemDescriptor = descriptor?.items?.get(key) val itemDescriptor = descriptor?.items?.get(key)
val jsonKey = key.toJsonKey(itemDescriptor) val jsonKey = key.toJsonKey(itemDescriptor)
val items: Map<String, MetaItem<*>> = getIndexed(key) val items: Map<String?, MetaItem<*>> = getIndexed(key)
when (items.size) { when (items.size) {
0 -> { 0 -> {
//do nothing //do nothing
@ -94,7 +94,11 @@ fun JsonPrimitive.toValue(descriptor: ValueDescriptor?): Value {
true -> True true -> True
false -> False false -> False
is Number -> NumberValue(body as Number) is Number -> NumberValue(body as Number)
else -> StringValue(content) else -> if (isString) {
StringValue(content)
} else {
content.parseValue()
}
} }
} }
} }
@ -174,7 +178,7 @@ class JsonMeta(val json: JsonObject, val descriptor: NodeDescriptor? = null) : M
override val items: Map<NameToken, MetaItem<JsonMeta>> by lazy(::buildItems) override val items: Map<NameToken, MetaItem<JsonMeta>> by lazy(::buildItems)
companion object{ companion object {
/** /**
* A key representing top-level json array of nodes, which could not be directly represented by a meta node * A key representing top-level json array of nodes, which could not be directly represented by a meta node
*/ */

View File

@ -71,6 +71,7 @@ inline fun <reified M : MutableMeta<M>> M.node(key: Name? = null): ReadWriteProp
item(key).convert(reader = { it?.let { it.node as M } }, writer = { it?.let { MetaItem.NodeItem(it) } }) item(key).convert(reader = { it?.let { it.node as M } }, writer = { it?.let { MetaItem.NodeItem(it) } })
@Deprecated("To be replaced by a converter")
fun <T> MutableItemProvider.item( fun <T> MutableItemProvider.item(
default: T? = null, default: T? = null,
key: Name? = null, key: Name? = null,
@ -85,20 +86,6 @@ fun <T> MutableItemProvider.item(
fun Configurable.value(key: Name? = null): ReadWriteProperty<Any?, Value?> = fun Configurable.value(key: Name? = null): ReadWriteProperty<Any?, Value?> =
item(key).convert(MetaConverter.value) item(key).convert(MetaConverter.value)
fun <T> MutableItemProvider.value(
default: T? = null,
key: Name? = null,
writer: (T) -> Value? = { Value.of(it) },
reader: (Value?) -> T
): ReadWriteProperty<Any?, T> = MutableItemDelegate(
this,
key,
default?.let { MetaItem.of(it) }
).convert(
reader = { reader(it.value) },
writer = { value -> writer(value)?.let { MetaItem.ValueItem(it) } }
)
/* Number delegates*/ /* Number delegates*/
fun MutableItemProvider.int(key: Name? = null): ReadWriteProperty<Any?, Int?> = fun MutableItemProvider.int(key: Name? = null): ReadWriteProperty<Any?, Int?> =

View File

@ -160,7 +160,7 @@ operator fun MutableMeta<*>.set(name: String, metas: Iterable<Meta>): Unit = set
fun <M : MutableMeta<M>> M.append(name: Name, value: Any?) { fun <M : MutableMeta<M>> M.append(name: Name, value: Any?) {
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.last()!!.index val newIndex = name.last()!!.index
if (newIndex.isNotEmpty()) { if (newIndex != null) {
set(name, value) set(name, value)
} else { } else {
val index = (getIndexed(name).keys.mapNotNull { it.toIntOrNull() }.max() ?: -1) + 1 val index = (getIndexed(name).keys.mapNotNull { it.toIntOrNull() }.max() ?: -1) + 1

View File

@ -7,23 +7,25 @@ import hep.dataforge.names.toName
* Get all items matching given name. The index of the last element, if present is used as a [Regex], * Get all items matching given name. The index of the last element, if present is used as a [Regex],
* against which indexes of elements are matched. * against which indexes of elements are matched.
*/ */
fun Meta.getIndexed(name: Name): Map<String, MetaItem<*>> { fun Meta.getIndexed(name: Name): Map<String?, MetaItem<*>> {
val root = when (name.length) { val root = when (name.length) {
0 -> error("Can't use empty name for 'getIndexed'") 0 -> error("Can't use empty name for 'getIndexed'")
1 -> this 1 -> this
else -> this[name.cutLast()].node else -> this[name.cutLast()].node ?: return emptyMap()
} }
val (body, index) = name.last()!! val (body, index) = name.last()!!
return if (index == null) {
root.items.filter { it.key.body == body }.mapKeys { it.key.index }
} else {
val regex = index.toRegex() val regex = index.toRegex()
root.items
return root?.items .filter { it.key.body == body && (regex.matches(it.key.index ?: "")) }
?.filter { it.key.body == body && (index.isEmpty() || regex.matches(it.key.index)) } .mapKeys { it.key.index }
?.mapKeys { it.key.index } }
?: emptyMap()
} }
fun Meta.getIndexed(name: String): Map<String, MetaItem<*>> = this@getIndexed.getIndexed(name.toName()) fun Meta.getIndexed(name: String): Map<String?, MetaItem<*>> = this@getIndexed.getIndexed(name.toName())
/** /**
* Get all items matching given name. * Get all items matching given name.

View File

@ -78,7 +78,7 @@ class Name(val tokens: List<NameToken>) {
* A name token could have appendix in square brackets called *index* * A name token could have appendix in square brackets called *index*
*/ */
@Serializable @Serializable
data class NameToken(val body: String, val index: String = "") { data class NameToken(val body: String, val index: String? = null) {
init { init {
if (body.isEmpty()) error("Syntax error: Name token body is empty") if (body.isEmpty()) error("Syntax error: Name token body is empty")
@ -96,7 +96,7 @@ data class NameToken(val body: String, val index: String = "") {
body.escape() body.escape()
} }
fun hasIndex() = index.isNotEmpty() fun hasIndex() = index != null
@Serializer(NameToken::class) @Serializer(NameToken::class)
companion object : KSerializer<NameToken> { companion object : KSerializer<NameToken> {
@ -150,7 +150,8 @@ fun String.toName(): Name {
} }
else -> when (it) { else -> when (it) {
'.' -> { '.' -> {
yield(NameToken(bodyBuilder.toString(), queryBuilder.toString())) val query = if(queryBuilder.isEmpty()) null else queryBuilder.toString()
yield(NameToken(bodyBuilder.toString(), query))
bodyBuilder = StringBuilder() bodyBuilder = StringBuilder()
queryBuilder = StringBuilder() queryBuilder = StringBuilder()
} }
@ -163,7 +164,8 @@ fun String.toName(): Name {
} }
} }
} }
yield(NameToken(bodyBuilder.toString(), queryBuilder.toString())) val query = if(queryBuilder.isEmpty()) null else queryBuilder.toString()
yield(NameToken(bodyBuilder.toString(), query))
} }
return Name(tokens.toList()) return Name(tokens.toList())
} }
@ -206,7 +208,7 @@ fun Name.withIndex(index: String): Name {
/** /**
* Fast [String]-based accessor for item map * Fast [String]-based accessor for item map
*/ */
operator fun <T> Map<NameToken, T>.get(body: String, query: String = ""): T? = get(NameToken(body, query)) operator fun <T> Map<NameToken, T>.get(body: String, query: String? = null): T? = get(NameToken(body, query))
operator fun <T> Map<Name, T>.get(name: String) = get(name.toName()) operator fun <T> Map<Name, T>.get(name: String) = get(name.toName())
operator fun <T> MutableMap<Name, T>.set(name: String, value: T) = set(name.toName(), value) operator fun <T> MutableMap<Name, T>.set(name: String, value: T) = set(name.toName(), value)

View File

@ -34,7 +34,7 @@ suspend fun Table<Value>.wrap(): Envelope = Envelope {
@ExperimentalIoApi @ExperimentalIoApi
fun TextRows.Companion.readEnvelope(envelope: Envelope): TextRows { fun TextRows.Companion.readEnvelope(envelope: Envelope): TextRows {
val header = envelope.meta.getIndexed("column") val header = envelope.meta.getIndexed("column")
.entries.sortedBy { it.key.toInt() } .entries.sortedBy { it.key?.toInt() }
.map { (_, item) -> .map { (_, item) ->
SimpleColumnHeader(item.node["name"].string!!, Value::class, item.node["meta"].node ?: Meta.EMPTY) SimpleColumnHeader(item.node["name"].string!!, Value::class, item.node["meta"].node ?: Meta.EMPTY)
} }