Name revolution

This commit is contained in:
Alexander Nozik 2021-07-31 15:02:11 +03:00
parent 4a76063093
commit 8763d63e28
42 changed files with 298 additions and 296 deletions

View File

@ -11,6 +11,7 @@
- Build tools 0.10.0
- Relaxed type restriction on `MetaConverter`. Now nullables are available.
- **Huge API-breaking refactoring of Meta**. Meta now can hava both value and children.
- **API breaking** `String.toName()` is replaced by `Name.parse()`
### Deprecated
- Direct use of `Config`

View File

@ -4,7 +4,7 @@ plugins {
allprojects {
group = "space.kscience"
version = "0.5.0-dev-3"
version = "0.5.0-dev-4"
}
subprojects {

View File

@ -7,8 +7,9 @@ import space.kscience.dataforge.meta.toMutableMeta
import space.kscience.dataforge.misc.DFBuilder
import space.kscience.dataforge.misc.DFExperimental
import space.kscience.dataforge.names.Name
import space.kscience.dataforge.names.NameToken
import space.kscience.dataforge.names.asName
import space.kscience.dataforge.names.plus
import space.kscience.dataforge.names.toName
import kotlin.collections.component1
import kotlin.collections.component2
import kotlin.collections.set
@ -30,7 +31,7 @@ public class ContextBuilder internal constructor(
}
public fun name(string: String) {
this.name = string.toName()
this.name = Name.parse(string)
}
@OptIn(DFExperimental::class)
@ -63,7 +64,7 @@ public class ContextBuilder internal constructor(
}
public fun build(): Context {
val contextName = name ?: "@auto[${hashCode().toUInt().toString(16)}]".toName()
val contextName = name ?: NameToken("@auto",hashCode().toUInt().toString(16)).asName()
val plugins = HashMap<PluginTag, Plugin>()
fun addPlugin(factory: PluginFactory<*>, meta: Meta) {

View File

@ -6,7 +6,6 @@ import space.kscience.dataforge.meta.MetaRepr
import space.kscience.dataforge.misc.Named
import space.kscience.dataforge.misc.Type
import space.kscience.dataforge.names.Name
import space.kscience.dataforge.names.toName
import space.kscience.dataforge.provider.Provider
/**
@ -31,7 +30,7 @@ public interface Plugin : Named, ContextAware, Provider, MetaRepr {
/**
* The name of this plugin ignoring version and group
*/
override val name: Name get() = tag.name.toName()
override val name: Name get() = Name.parse(tag.name)
/**
* Plugin dependencies which are required to attach this plugin. Plugin

View File

@ -1,14 +1,14 @@
package space.kscience.dataforge.properties
import space.kscience.dataforge.meta.ObservableMeta
import space.kscience.dataforge.meta.Scheme
import space.kscience.dataforge.misc.DFExperimental
import space.kscience.dataforge.names.Name
import space.kscience.dataforge.names.startsWith
import space.kscience.dataforge.names.toName
import kotlin.reflect.KMutableProperty1
@DFExperimental
public fun <P : ObservableMeta, T : Any> P.property(property: KMutableProperty1<P, T?>): Property<T?> =
public fun <S : Scheme, T : Any> S.property(property: KMutableProperty1<S, T?>): Property<T?> =
object : Property<T?> {
override var value: T?
get() = property.get(this@property)
@ -17,15 +17,15 @@ public fun <P : ObservableMeta, T : Any> P.property(property: KMutableProperty1<
}
override fun onChange(owner: Any?, callback: (T?) -> Unit) {
this@property.onChange(this) { name ->
if (name.startsWith(property.name.toName())) {
this@property.meta.onChange(this) { name ->
if (name.startsWith(Name.parse(property.name))) {
callback(property.get(this@property))
}
}
}
override fun removeChangeListener(owner: Any?) {
this@property.removeListener(this@property)
this@property.meta.removeListener(this@property)
}
}

View File

@ -16,7 +16,6 @@
package space.kscience.dataforge.provider
import space.kscience.dataforge.names.Name
import space.kscience.dataforge.names.toName
import kotlin.jvm.JvmInline
/**
@ -63,7 +62,7 @@ public data class PathToken(val name: Name, val target: String? = null) {
public const val TARGET_SEPARATOR: String = "::"
public fun parse(token: String): PathToken {
val target = token.substringBefore(TARGET_SEPARATOR, "")
val name = token.substringAfter(TARGET_SEPARATOR).toName()
val name = Name.parse(token.substringAfter(TARGET_SEPARATOR))
if (target.contains("[")) TODO("target separators in queries are not supported")
return PathToken(name, target)
}

View File

@ -2,7 +2,6 @@ package space.kscience.dataforge.context
import space.kscience.dataforge.names.Name
import space.kscience.dataforge.names.appendLeft
import space.kscience.dataforge.names.toName
import kotlin.test.Test
import kotlin.test.assertEquals
@ -12,17 +11,16 @@ class ContextTest {
override val tag get() = PluginTag("test")
override fun content(target: String): Map<Name, Any> {
return when(target){
"test" -> listOf("a", "b", "c.d").associate { it.toName() to it.toName() }
return when (target) {
"test" -> listOf("a", "b", "c.d").associate { Name.parse(it) to Name.parse(it) }
else -> emptyMap()
}
}
}
@Test
fun testPluginManager() {
val context = Global.buildContext{
val context = Global.buildContext {
name("test")
plugin(DummyPlugin())
}

View File

@ -14,7 +14,7 @@ internal class TestScheme : Scheme() {
}
@DFExperimental
class ItemPropertiesTest {
class MetaPropertiesTest {
@Test
fun testBinding() {
val scheme = TestScheme.empty()

View File

@ -11,7 +11,6 @@ import space.kscience.dataforge.misc.DFBuilder
import space.kscience.dataforge.misc.DFExperimental
import space.kscience.dataforge.misc.DFInternal
import space.kscience.dataforge.names.Name
import space.kscience.dataforge.names.toName
import kotlin.reflect.KType
import kotlin.reflect.typeOf
@ -95,7 +94,7 @@ internal class ReduceAction<T : Any, R : Any>(
val groupMeta = group.meta
val env = ActionEnv(groupName.toName(), groupMeta, meta)
val env = ActionEnv(Name.parse(groupName), groupMeta, meta)
@OptIn(DFInternal::class) val res: Data<R> = dataFlow.reduceToData(
outputType,
meta = groupMeta

View File

@ -12,7 +12,6 @@ import space.kscience.dataforge.meta.toMutableMeta
import space.kscience.dataforge.misc.DFExperimental
import space.kscience.dataforge.misc.DFInternal
import space.kscience.dataforge.names.Name
import space.kscience.dataforge.names.toName
import kotlin.collections.set
import kotlin.reflect.KType
import kotlin.reflect.typeOf
@ -36,7 +35,7 @@ public class SplitBuilder<T : Any, R : Any>(public val name: Name, public val me
* @param rule the rule to transform fragment name and meta using
*/
public fun fragment(name: String, rule: FragmentRule<T, R>.() -> Unit) {
fragments[name.toName()] = rule
fragments[Name.parse(name)] = rule
}
}

View File

@ -115,4 +115,4 @@ public suspend inline fun <reified T : Any> ActiveDataTree<T>.emit(
public suspend inline fun <reified T : Any> ActiveDataTree<T>.emit(
name: String,
noinline block: suspend ActiveDataTree<T>.() -> Unit,
): Unit = emit(name.toName(), ActiveDataTree(typeOf<T>(), block))
): Unit = emit(Name.parse(name), ActiveDataTree(typeOf<T>(), block))

View File

@ -8,7 +8,6 @@ import space.kscience.dataforge.meta.MutableMeta
import space.kscience.dataforge.misc.DFExperimental
import space.kscience.dataforge.names.Name
import space.kscience.dataforge.names.plus
import space.kscience.dataforge.names.toName
import kotlin.reflect.KType
public interface DataSetBuilder<in T : Any> {
@ -39,17 +38,17 @@ public interface DataSetBuilder<in T : Any> {
/**
* Append data to node
*/
public suspend infix fun String.put(data: Data<T>): Unit = emit(toName(), data)
public suspend infix fun String.put(data: Data<T>): Unit = emit(Name.parse(this), data)
/**
* Append node
*/
public suspend infix fun String.put(dataSet: DataSet<T>): Unit = emit(toName(), dataSet)
public suspend infix fun String.put(dataSet: DataSet<T>): Unit = emit(Name.parse(this), dataSet)
/**
* Build and append node
*/
public suspend infix fun String.put(block: suspend DataSetBuilder<T>.() -> Unit): Unit = emit(toName(), block)
public suspend infix fun String.put(block: suspend DataSetBuilder<T>.() -> Unit): Unit = emit(Name.parse(this), block)
}
private class SubSetBuilder<in T : Any>(
@ -77,15 +76,15 @@ public suspend fun <T : Any> DataSetBuilder<T>.emit(name: Name, block: suspend D
public suspend fun <T : Any> DataSetBuilder<T>.emit(name: String, data: Data<T>) {
emit(name.toName(), data)
emit(Name.parse(name), data)
}
public suspend fun <T : Any> DataSetBuilder<T>.emit(name: String, set: DataSet<T>) {
this.emit(name.toName(), set)
this.emit(Name.parse(name), set)
}
public suspend fun <T : Any> DataSetBuilder<T>.emit(name: String, block: suspend DataSetBuilder<T>.() -> Unit): Unit =
this@emit.emit(name.toName(), block)
this@emit.emit(Name.parse(name), block)
public suspend fun <T : Any> DataSetBuilder<T>.emit(data: NamedData<T>) {
emit(data.name, data.data)
@ -115,17 +114,25 @@ public suspend inline fun <reified T : Any> DataSetBuilder<T>.produce(
/**
* Emit a static data with the fixed value
*/
public suspend inline fun <reified T : Any> DataSetBuilder<T>.static(name: String, data: T, meta: Meta = Meta.EMPTY): Unit =
public suspend inline fun <reified T : Any> DataSetBuilder<T>.static(
name: String,
data: T,
meta: Meta = Meta.EMPTY
): Unit =
emit(name, Data.static(data, meta))
public suspend inline fun <reified T : Any> DataSetBuilder<T>.static(name: Name, data: T, meta: Meta = Meta.EMPTY): Unit =
public suspend inline fun <reified T : Any> DataSetBuilder<T>.static(
name: Name,
data: T,
meta: Meta = Meta.EMPTY
): Unit =
emit(name, Data.static(data, meta))
public suspend inline fun <reified T : Any> DataSetBuilder<T>.static(
name: String,
data: T,
mutableMeta: MutableMeta.() -> Unit,
): Unit = emit(name.toName(), Data.static(data, Meta(mutableMeta)))
): Unit = emit(Name.parse(name), Data.static(data, Meta(mutableMeta)))
/**
* Update data with given node data and meta with node meta.

View File

@ -57,7 +57,7 @@ public interface DataTree<out T : Any> : DataSet<T> {
}
}
public suspend fun <T: Any> DataSet<T>.getData(name: String): Data<T>? = getData(name.toName())
public suspend fun <T: Any> DataSet<T>.getData(name: String): Data<T>? = getData(Name.parse(name))
/**
* Get a [DataTreeItem] with given [name] or null if the item does not exist

View File

@ -5,7 +5,10 @@ import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.mapNotNull
import space.kscience.dataforge.misc.DFExperimental
import space.kscience.dataforge.names.*
import space.kscience.dataforge.names.Name
import space.kscience.dataforge.names.isEmpty
import space.kscience.dataforge.names.plus
import space.kscience.dataforge.names.removeHeadOrNull
import kotlin.reflect.KType
@ -64,7 +67,7 @@ public fun <T : Any> DataSet<T>.branch(branchName: Name): DataSet<T> = if (branc
override val updates: Flow<Name> get() = this@branch.updates.mapNotNull { it.removeHeadOrNull(branchName) }
}
public fun <T : Any> DataSet<T>.branch(branchName: String): DataSet<T> = this@branch.branch(branchName.toName())
public fun <T : Any> DataSet<T>.branch(branchName: String): DataSet<T> = this@branch.branch(Name.parse(branchName))
@DFExperimental
public suspend fun <T : Any> DataSet<T>.rootData(): Data<T>? = getData(Name.EMPTY)

View File

@ -6,7 +6,6 @@ import kotlinx.coroutines.flow.map
import space.kscience.dataforge.misc.DFExperimental
import space.kscience.dataforge.names.Name
import space.kscience.dataforge.names.matches
import space.kscience.dataforge.names.toName
import kotlin.reflect.KType
import kotlin.reflect.full.isSubtypeOf
import kotlin.reflect.typeOf
@ -61,4 +60,4 @@ public suspend fun <R : Any> DataSet<*>.selectOne(type: KType, name: Name): Name
public suspend inline fun <reified R : Any> DataSet<*>.selectOne(name: Name): NamedData<R>? = selectOne(typeOf<R>(), name)
public suspend inline fun <reified R : Any> DataSet<*>.selectOne(name: String): NamedData<R>? =
selectOne(typeOf<R>(), name.toName())
selectOne(typeOf<R>(), Name.parse(name))

View File

@ -3,7 +3,7 @@ package space.kscience.dataforge.data
import kotlinx.coroutines.*
import kotlinx.coroutines.flow.collect
import space.kscience.dataforge.misc.DFExperimental
import space.kscience.dataforge.names.toName
import space.kscience.dataforge.names.asName
import kotlin.test.Test
import kotlin.test.assertEquals
@ -72,7 +72,7 @@ internal class DataTreeBuilderTest {
}
}
val rootNode = ActiveDataTree<Int> {
setAndObserve("sub".toName(), subNode)
setAndObserve("sub".asName(), subNode)
}
launch {

View File

@ -10,12 +10,11 @@ import space.kscience.dataforge.io.PartDescriptor.Companion.SEPARATOR_KEY
import space.kscience.dataforge.meta.*
import space.kscience.dataforge.names.asName
import space.kscience.dataforge.names.plus
import space.kscience.dataforge.names.toName
private class PartDescriptor : Scheme() {
var offset by int(0)
var size by int(0)
var partMeta by item("meta".toName())
var partMeta by item("meta".asName())
companion object : SchemeSpec<PartDescriptor>(::PartDescriptor) {
val MULTIPART_KEY = ENVELOPE_NODE_KEY + "multipart"

View File

@ -10,7 +10,7 @@ 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.toName
import kotlin.native.concurrent.ThreadLocal
import kotlin.reflect.KClass
@ -23,7 +23,7 @@ public class IOPlugin(meta: Meta) : AbstractPlugin(meta) {
public fun <T : Any> resolveIOFormat(item: Meta, type: KClass<out T>): IOFormat<T>? {
val key = item.string ?: item[NAME_KEY]?.string ?: error("Format name not defined")
val name = key.toName()
val name = Name.parse(key)
return ioFormatFactories.find { it.name == name }?.let {
@Suppress("UNCHECKED_CAST")
if (it.type != type) error("Format type ${it.type} is not the same as requested type $type")
@ -52,7 +52,7 @@ public class IOPlugin(meta: Meta) : AbstractPlugin(meta) {
public fun resolveEnvelopeFormat(item: Meta): EnvelopeFormat? {
val name = item.string ?: item[NAME_KEY]?.string ?: error("Envelope format name not defined")
val meta = item[META_KEY] ?: Meta.EMPTY
return resolveEnvelopeFormat(name.toName(), meta)
return resolveEnvelopeFormat(Name.parse(name), meta)
}
override fun content(target: String): Map<Name, Any> {

View File

@ -10,7 +10,7 @@ import space.kscience.dataforge.meta.get
import space.kscience.dataforge.meta.string
import space.kscience.dataforge.names.Name
import space.kscience.dataforge.names.plus
import space.kscience.dataforge.names.toName
/**
* A streaming-friendly envelope format with a short binary tag.
@ -121,7 +121,7 @@ public class TaggedEnvelopeFormat(
override fun invoke(meta: Meta, context: Context): EnvelopeFormat {
val io = context.io
val metaFormatName = meta["name"].string?.toName() ?: JsonMetaFormat.name
val metaFormatName = meta["name"].string?.let { Name.parse(it) } ?: JsonMetaFormat.name
//Check if appropriate factory exists
io.metaFormatFactories.find { it.name == metaFormatName } ?: error("Meta format could not be resolved")

View File

@ -7,7 +7,6 @@ import space.kscience.dataforge.meta.Meta
import space.kscience.dataforge.meta.MetaSerializer
import space.kscience.dataforge.meta.seal
import space.kscience.dataforge.names.Name
import space.kscience.dataforge.names.toName
import kotlin.test.Test
import kotlin.test.assertEquals
@ -43,7 +42,7 @@ class MetaSerializerTest {
@Test
fun testNameSerialization() {
val name = "a.b.c".toName()
val name = Name.parse("a.b.c")
val string = JSON_PRETTY.encodeToString(Name.serializer(), name)
val restored = JSON_PLAIN.decodeFromString(Name.serializer(), string)
assertEquals(name, restored)

View File

@ -16,6 +16,13 @@ public interface MetaRepr {
public fun toMeta(): Meta
}
/**
* A container for meta nodes
*/
public fun interface MetaProvider {
public fun getMeta(name: Name): Meta?
}
/**
* A meta node
* TODO add documentation
@ -23,10 +30,12 @@ public interface MetaRepr {
*/
@Type(Meta.TYPE)
@Serializable(MetaSerializer::class)
public interface Meta : MetaRepr {
public interface Meta : MetaRepr, MetaProvider {
public val value: Value?
public val items: Map<NameToken, Meta>
override fun getMeta(name: Name): Meta? = find(name)
override fun toMeta(): Meta = this
override fun toString(): String
@ -66,6 +75,12 @@ public interface Meta : MetaRepr {
public fun toString(meta: Meta): String = json.encodeToString(MetaSerializer, meta)
public val EMPTY: Meta = SealedMeta(null, emptyMap())
private tailrec fun Meta.find(name: Name): Meta? = if (name.isEmpty()) {
this
} else {
items[name.firstOrNull()!!]?.find(name.cutFirst())
}
}
}
@ -82,16 +97,12 @@ public operator fun Meta.get(token: NameToken): Meta? = items[token]
*
* If [name] is empty return current [Meta]
*/
public tailrec operator fun Meta.get(name: Name): Meta? = if (name.isEmpty()) {
this
} else {
get(name.firstOrNull()!!)?.get(name.cutFirst())
}
public operator fun Meta.get(name: Name): Meta? = getMeta(name)
/**
* Parse [Name] from [key] using full name notation and pass it to [Meta.get]
*/
public operator fun Meta.get(key: String): Meta? = this[key.toName()]
public operator fun Meta.get(key: String): Meta? = this[Name.parse(key)]
/**
* Get all items matching given name. The index of the last element, if present is used as a [Regex],
@ -155,7 +166,7 @@ public tailrec operator fun <M : TypedMeta<M>> TypedMeta<M>.get(name: Name): M?
/**
* Parse [Name] from [key] using full name notation and pass it to [TypedMeta.get]
*/
public operator fun <M : TypedMeta<M>> TypedMeta<M>.get(key: String): M? = this[key.toName()]
public operator fun <M : TypedMeta<M>> TypedMeta<M>.get(key: String): M? = this[Name.parse(key)]
/**
@ -197,7 +208,7 @@ public fun Meta.isEmpty(): Boolean = this === Meta.EMPTY || (value == null && it
public fun <M : TypedMeta<M>> TypedMeta<M>.getIndexed(name: Name): Map<String?, M> =
(this as Meta).getIndexed(name) as Map<String?, M>
public fun <M : TypedMeta<M>> TypedMeta<M>.getIndexed(name: String): Map<String?, Meta> = getIndexed(name.toName())
public fun <M : TypedMeta<M>> TypedMeta<M>.getIndexed(name: String): Map<String?, Meta> = getIndexed(Name.parse(name))
public val Meta?.string: String? get() = this?.value?.string

View File

@ -10,8 +10,8 @@ import kotlin.properties.ReadOnlyProperty
public typealias MetaDelegate = ReadOnlyProperty<Any?, Meta?>
public fun Meta.item(key: Name? = null): MetaDelegate = ReadOnlyProperty { _, property ->
get(key ?: property.name.asName())
public fun MetaProvider.item(key: Name? = null): MetaDelegate = ReadOnlyProperty { _, property ->
getMeta(key ?: property.name.asName())
}
//TODO add caching for sealed nodes
@ -50,62 +50,62 @@ public fun <R> MetaDelegate.convert(
/**
* A property delegate that uses custom key
*/
public fun Meta.value(key: Name? = null): ReadOnlyProperty<Any?, Value?> =
public fun MetaProvider.value(key: Name? = null): ReadOnlyProperty<Any?, Value?> =
item(key).convert(MetaConverter.value)
public fun Meta.string(key: Name? = null): ReadOnlyProperty<Any?, String?> =
public fun MetaProvider.string(key: Name? = null): ReadOnlyProperty<Any?, String?> =
item(key).convert(MetaConverter.string)
public fun Meta.boolean(key: Name? = null): ReadOnlyProperty<Any?, Boolean?> =
public fun MetaProvider.boolean(key: Name? = null): ReadOnlyProperty<Any?, Boolean?> =
item(key).convert(MetaConverter.boolean)
public fun Meta.number(key: Name? = null): ReadOnlyProperty<Any?, Number?> =
public fun MetaProvider.number(key: Name? = null): ReadOnlyProperty<Any?, Number?> =
item(key).convert(MetaConverter.number)
public fun Meta.double(key: Name? = null): ReadOnlyProperty<Any?, Double?> =
public fun MetaProvider.double(key: Name? = null): ReadOnlyProperty<Any?, Double?> =
item(key).convert(MetaConverter.double)
public fun Meta.float(key: Name? = null): ReadOnlyProperty<Any?, Float?> =
public fun MetaProvider.float(key: Name? = null): ReadOnlyProperty<Any?, Float?> =
item(key).convert(MetaConverter.float)
public fun Meta.int(key: Name? = null): ReadOnlyProperty<Any?, Int?> =
public fun MetaProvider.int(key: Name? = null): ReadOnlyProperty<Any?, Int?> =
item(key).convert(MetaConverter.int)
public fun Meta.long(key: Name? = null): ReadOnlyProperty<Any?, Long?> =
public fun MetaProvider.long(key: Name? = null): ReadOnlyProperty<Any?, Long?> =
item(key).convert(MetaConverter.long)
public fun Meta.node(key: Name? = null): ReadOnlyProperty<Any?, Meta?> =
public fun MetaProvider.node(key: Name? = null): ReadOnlyProperty<Any?, Meta?> =
item(key).convert(MetaConverter.meta)
public fun Meta.string(default: String, key: Name? = null): ReadOnlyProperty<Any?, String> =
public fun MetaProvider.string(default: String, key: Name? = null): ReadOnlyProperty<Any?, String> =
item(key).convert(MetaConverter.string) { default }
public fun Meta.boolean(default: Boolean, key: Name? = null): ReadOnlyProperty<Any?, Boolean> =
public fun MetaProvider.boolean(default: Boolean, key: Name? = null): ReadOnlyProperty<Any?, Boolean> =
item(key).convert(MetaConverter.boolean) { default }
public fun Meta.number(default: Number, key: Name? = null): ReadOnlyProperty<Any?, Number> =
public fun MetaProvider.number(default: Number, key: Name? = null): ReadOnlyProperty<Any?, Number> =
item(key).convert(MetaConverter.number) { default }
public fun Meta.double(default: Double, key: Name? = null): ReadOnlyProperty<Any?, Double> =
public fun MetaProvider.double(default: Double, key: Name? = null): ReadOnlyProperty<Any?, Double> =
item(key).convert(MetaConverter.double) { default }
public fun Meta.float(default: Float, key: Name? = null): ReadOnlyProperty<Any?, Float> =
public fun MetaProvider.float(default: Float, key: Name? = null): ReadOnlyProperty<Any?, Float> =
item(key).convert(MetaConverter.float) { default }
public fun Meta.int(default: Int, key: Name? = null): ReadOnlyProperty<Any?, Int> =
public fun MetaProvider.int(default: Int, key: Name? = null): ReadOnlyProperty<Any?, Int> =
item(key).convert(MetaConverter.int) { default }
public fun Meta.long(default: Long, key: Name? = null): ReadOnlyProperty<Any?, Long> =
public fun MetaProvider.long(default: Long, key: Name? = null): ReadOnlyProperty<Any?, Long> =
item(key).convert(MetaConverter.long) { default }
public inline fun <reified E : Enum<E>> Meta.enum(default: E, key: Name? = null): ReadOnlyProperty<Any?, E> =
public inline fun <reified E : Enum<E>> MetaProvider.enum(default: E, key: Name? = null): ReadOnlyProperty<Any?, E> =
item(key).convert(MetaConverter.enum()) { default }
public fun Meta.string(key: Name? = null, default: () -> String): ReadOnlyProperty<Any?, String> =
public fun MetaProvider.string(key: Name? = null, default: () -> String): ReadOnlyProperty<Any?, String> =
item(key).convert(MetaConverter.string, default)
public fun Meta.boolean(key: Name? = null, default: () -> Boolean): ReadOnlyProperty<Any?, Boolean> =
public fun MetaProvider.boolean(key: Name? = null, default: () -> Boolean): ReadOnlyProperty<Any?, Boolean> =
item(key).convert(MetaConverter.boolean, default)
public fun Meta.number(key: Name? = null, default: () -> Number): ReadOnlyProperty<Any?, Number> =
public fun MetaProvider.number(key: Name? = null, default: () -> Number): ReadOnlyProperty<Any?, Number> =
item(key).convert(MetaConverter.number, default)

View File

@ -16,13 +16,17 @@ import kotlin.jvm.Synchronized
@DslMarker
public annotation class MetaBuilder
public interface MutableMetaProvider : MetaProvider {
public fun setMeta(name: Name, node: Meta?)
}
/**
* Mutable variant of [Meta]
* TODO documentation
*/
@Serializable(MutableMetaSerializer::class)
@MetaBuilder
public interface MutableMeta : Meta {
public interface MutableMeta : Meta, MutableMetaProvider {
override val items: Map<NameToken, MutableMeta>
@ -36,6 +40,14 @@ public interface MutableMeta : Meta {
*/
public operator fun set(name: Name, meta: Meta)
override fun setMeta(name: Name, node: Meta?) {
if (node == null) {
remove(name)
} else {
set(name, node)
}
}
/**
* Remove a node at a given [name] if it is present
*/
@ -85,47 +97,47 @@ public interface MutableMeta : Meta {
}
public infix fun String.put(meta: Meta) {
toName() put meta
Name.parse(this) put meta
}
public infix fun String.put(value: Value?) {
set(toName(), value)
set(Name.parse(this), value)
}
public infix fun String.put(string: String) {
set(toName(), string.asValue())
set(Name.parse(this), string.asValue())
}
public infix fun String.put(number: Number) {
set(toName(), number.asValue())
set(Name.parse(this), number.asValue())
}
public infix fun String.put(boolean: Boolean) {
set(toName(), boolean.asValue())
set(Name.parse(this), boolean.asValue())
}
public infix fun String.put(enum: Enum<*>) {
set(toName(), EnumValue(enum))
set(Name.parse(this), EnumValue(enum))
}
public infix fun String.put(array: DoubleArray) {
set(toName(), array.asValue())
set(Name.parse(this), array.asValue())
}
public infix fun String.put(repr: MetaRepr) {
toName() put repr.toMeta()
Name.parse(this) put repr.toMeta()
}
public infix fun String.put(iterable: Iterable<Meta>) {
setIndexed(toName(), iterable)
setIndexed(Name.parse(this), iterable)
}
public infix fun String.put(builder: MutableMeta.() -> Unit) {
set(toName(), MutableMeta(builder))
set(Name.parse(this), MutableMeta(builder))
}
}
public fun MutableMeta.getOrCreate(string: String): MutableMeta = getOrCreate(string.toName())
public fun MutableMeta.getOrCreate(key: String): MutableMeta = getOrCreate(Name.parse(key))
@Serializable(MutableMetaSerializer::class)
public interface MutableTypedMeta<M : MutableTypedMeta<M>> : TypedMeta<M>, MutableMeta {
@ -138,47 +150,37 @@ public interface MutableTypedMeta<M : MutableTypedMeta<M>> : TypedMeta<M>, Mutab
override fun getOrCreate(name: Name): M
}
public fun <M : MutableTypedMeta<M>> M.getOrCreate(string: String): M = getOrCreate(string.toName())
public fun <M : MutableTypedMeta<M>> M.getOrCreate(key: String): M = getOrCreate(Name.parse(key))
public fun MutableMeta.remove(name: String){
remove(name.toName())
public fun MutableMeta.remove(key: String) {
remove(Name.parse(key))
}
// 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)
}
public operator fun MutableMetaProvider.set(Key: NameToken, value: Meta): Unit = setMeta(Key.asName(), value)
public operator fun MutableMetaProvider.set(key: String, value: Meta): Unit = setMeta(Name.parse(key), value)
//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(key: String, value: Value?): Unit = set(Name.parse(key), 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(key: String, value: String): Unit = set(Name.parse(key), 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(key: String, value: Boolean): Unit = set(Name.parse(key), 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(key: String, value: Number): Unit = set(Name.parse(key), 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 operator fun MutableMeta.set(key: String, value: List<Value>): Unit = set(Name.parse(key), value.asValue())
//public fun MutableMeta.set(key: String, index: String, value: Value?): Unit =
// set(key.toName().withIndex(index), value)
@ -218,8 +220,8 @@ public fun MutableMeta.setIndexed(
public operator fun MutableMeta.set(name: Name, metas: Iterable<Meta>): Unit =
setIndexed(name, metas)
public operator fun MutableMeta.set(name: String, metas: Iterable<Meta>): Unit =
setIndexed(name.toName(), metas)
public operator fun MutableMeta.set(key: String, metas: Iterable<Meta>): Unit =
setIndexed(Name.parse(key), metas)
/**
@ -396,13 +398,13 @@ public fun MutableMeta.append(name: Name, meta: Meta) {
}
@DFExperimental
public fun MutableMeta.append(name: String, meta: Meta): Unit = append(name.toName(), meta)
public fun MutableMeta.append(key: String, meta: Meta): Unit = append(Name.parse(key), meta)
@DFExperimental
public fun MutableMeta.append(name: Name, value: Value): Unit = append(name, Meta(value))
@DFExperimental
public fun MutableMeta.append(name: String, value: Value): Unit = append(name.toName(), value)
public fun MutableMeta.append(key: String, value: Value): Unit = append(Name.parse(key), value)
///**
// * Apply existing node with given [builder] or create a new element with it.
@ -443,8 +445,9 @@ public inline fun Meta.copy(block: MutableMeta.() -> Unit = {}): Meta =
toMutableMeta().apply(block)
private class MutableMetaWithDefault(val source: MutableMeta, val default: Meta, val name: Name) :
MutableMeta by source {
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)
@ -456,6 +459,8 @@ private class MutableMetaWithDefault(val source: MutableMeta, val default: Meta,
source[name] = value
}
override fun getMeta(name: Name): Meta? = source.getMeta(name) ?: default.getMeta(name)
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)

View File

@ -11,14 +11,14 @@ import kotlin.reflect.KProperty
public typealias MutableMetaDelegate = ReadWriteProperty<Any?, Meta?>
public fun MutableMeta.item(key: Name? = null): MutableMetaDelegate = object : MutableMetaDelegate {
public fun MutableMetaProvider.item(key: Name? = null): MutableMetaDelegate = object : MutableMetaDelegate {
override fun getValue(thisRef: Any?, property: KProperty<*>): Meta? {
return get(key ?: property.name.asName())
return getMeta(key ?: property.name.asName())
}
override fun setValue(thisRef: Any?, property: KProperty<*>, value: Meta?) {
val name = key ?: property.name.asName()
set(name, value)
setMeta(name, value)
}
}
@ -74,40 +74,40 @@ public fun <R> MutableMetaDelegate.convert(
/**
* A property delegate that uses custom key
*/
public fun MutableMeta.value(key: Name? = null): ReadWriteProperty<Any?, Value?> =
public fun MutableMetaProvider.value(key: Name? = null): ReadWriteProperty<Any?, Value?> =
item(key).convert(MetaConverter.value)
public fun MutableMeta.string(key: Name? = null): ReadWriteProperty<Any?, String?> =
public fun MutableMetaProvider.string(key: Name? = null): ReadWriteProperty<Any?, String?> =
item(key).convert(MetaConverter.string)
public fun MutableMeta.boolean(key: Name? = null): ReadWriteProperty<Any?, Boolean?> =
public fun MutableMetaProvider.boolean(key: Name? = null): ReadWriteProperty<Any?, Boolean?> =
item(key).convert(MetaConverter.boolean)
public fun MutableMeta.number(key: Name? = null): ReadWriteProperty<Any?, Number?> =
public fun MutableMetaProvider.number(key: Name? = null): ReadWriteProperty<Any?, Number?> =
item(key).convert(MetaConverter.number)
public fun MutableMeta.string(default: String, key: Name? = null): ReadWriteProperty<Any?, String> =
public fun MutableMetaProvider.string(default: String, key: Name? = null): ReadWriteProperty<Any?, String> =
item(key).convert(MetaConverter.string) { default }
public fun MutableMeta.boolean(default: Boolean, key: Name? = null): ReadWriteProperty<Any?, Boolean> =
public fun MutableMetaProvider.boolean(default: Boolean, key: Name? = null): ReadWriteProperty<Any?, Boolean> =
item(key).convert(MetaConverter.boolean) { default }
public fun MutableMeta.number(default: Number, key: Name? = null): ReadWriteProperty<Any?, Number> =
public fun MutableMetaProvider.number(default: Number, key: Name? = null): ReadWriteProperty<Any?, Number> =
item(key).convert(MetaConverter.number) { default }
public fun MutableMeta.value(key: Name? = null, default: () -> Value): ReadWriteProperty<Any?, Value> =
public fun MutableMetaProvider.value(key: Name? = null, default: () -> Value): ReadWriteProperty<Any?, Value> =
item(key).convert(MetaConverter.value, default)
public fun MutableMeta.string(key: Name? = null, default: () -> String): ReadWriteProperty<Any?, String> =
public fun MutableMetaProvider.string(key: Name? = null, default: () -> String): ReadWriteProperty<Any?, String> =
item(key).convert(MetaConverter.string, default)
public fun MutableMeta.boolean(key: Name? = null, default: () -> Boolean): ReadWriteProperty<Any?, Boolean> =
public fun MutableMetaProvider.boolean(key: Name? = null, default: () -> Boolean): ReadWriteProperty<Any?, Boolean> =
item(key).convert(MetaConverter.boolean, default)
public fun MutableMeta.number(key: Name? = null, default: () -> Number): ReadWriteProperty<Any?, Number> =
public fun MutableMetaProvider.number(key: Name? = null, default: () -> Number): ReadWriteProperty<Any?, Number> =
item(key).convert(MetaConverter.number, default)
public inline fun <reified E : Enum<E>> MutableMeta.enum(
public inline fun <reified E : Enum<E>> MutableMetaProvider.enum(
default: E,
key: Name? = null,
): ReadWriteProperty<Any?, E> =
@ -115,37 +115,37 @@ public inline fun <reified E : Enum<E>> MutableMeta.enum(
/* Number delegates */
public fun MutableMeta.int(key: Name? = null): ReadWriteProperty<Any?, Int?> =
public fun MutableMetaProvider.int(key: Name? = null): ReadWriteProperty<Any?, Int?> =
item(key).convert(MetaConverter.int)
public fun MutableMeta.double(key: Name? = null): ReadWriteProperty<Any?, Double?> =
public fun MutableMetaProvider.double(key: Name? = null): ReadWriteProperty<Any?, Double?> =
item(key).convert(MetaConverter.double)
public fun MutableMeta.long(key: Name? = null): ReadWriteProperty<Any?, Long?> =
public fun MutableMetaProvider.long(key: Name? = null): ReadWriteProperty<Any?, Long?> =
item(key).convert(MetaConverter.long)
public fun MutableMeta.float(key: Name? = null): ReadWriteProperty<Any?, Float?> =
public fun MutableMetaProvider.float(key: Name? = null): ReadWriteProperty<Any?, Float?> =
item(key).convert(MetaConverter.float)
/* Safe number delegates*/
public fun MutableMeta.int(default: Int, key: Name? = null): ReadWriteProperty<Any?, Int> =
public fun MutableMetaProvider.int(default: Int, key: Name? = null): ReadWriteProperty<Any?, Int> =
item(key).convert(MetaConverter.int) { default }
public fun MutableMeta.double(default: Double, key: Name? = null): ReadWriteProperty<Any?, Double> =
public fun MutableMetaProvider.double(default: Double, key: Name? = null): ReadWriteProperty<Any?, Double> =
item(key).convert(MetaConverter.double) { default }
public fun MutableMeta.long(default: Long, key: Name? = null): ReadWriteProperty<Any?, Long> =
public fun MutableMetaProvider.long(default: Long, key: Name? = null): ReadWriteProperty<Any?, Long> =
item(key).convert(MetaConverter.long) { default }
public fun MutableMeta.float(default: Float, key: Name? = null): ReadWriteProperty<Any?, Float> =
public fun MutableMetaProvider.float(default: Float, key: Name? = null): ReadWriteProperty<Any?, Float> =
item(key).convert(MetaConverter.float) { default }
/* Extra delegates for special cases */
public fun MutableMeta.stringList(
public fun MutableMetaProvider.stringList(
vararg default: String,
key: Name? = null,
): ReadWriteProperty<Any?, List<String>> = item(key).convert(
@ -153,14 +153,14 @@ public fun MutableMeta.stringList(
writer = { Meta(it.map { str -> str.asValue() }.asValue()) }
)
public fun MutableMeta.stringList(
public fun MutableMetaProvider.stringList(
key: Name? = null,
): ReadWriteProperty<Any?, List<String>?> = item(key).convert(
reader = { it?.stringList },
writer = { it?.map { str -> str.asValue() }?.asValue()?.let { Meta(it) } }
)
public fun MutableMeta.numberList(
public fun MutableMetaProvider.numberList(
vararg default: Number,
key: Name? = null,
): ReadWriteProperty<Any?, List<Number>> = item(key).convert(
@ -171,7 +171,7 @@ public fun MutableMeta.numberList(
/* A special delegate for double arrays */
public fun MutableMeta.doubleArray(
public fun MutableMetaProvider.doubleArray(
vararg default: Double,
key: Name? = null,
): ReadWriteProperty<Any?, DoubleArray> = item(key).convert(
@ -179,7 +179,7 @@ public fun MutableMeta.doubleArray(
writer = { Meta(DoubleArrayValue(it)) }
)
public fun <T> MutableMeta.listValue(
public fun <T> MutableMetaProvider.listValue(
key: Name? = null,
writer: (T) -> Value = { Value.of(it) },
reader: (Value) -> T,

View File

@ -1,9 +1,6 @@
package space.kscience.dataforge.meta
import space.kscience.dataforge.names.Name
import space.kscience.dataforge.names.NameToken
import space.kscience.dataforge.names.asName
import space.kscience.dataforge.names.startsWith
import space.kscience.dataforge.names.*
import space.kscience.dataforge.values.Value
import kotlin.jvm.Synchronized
import kotlin.reflect.KProperty1
@ -57,6 +54,8 @@ private class ObservableMetaWrapper(
override val items: Map<NameToken, ObservableMetaWrapper>
get() = origin.items.mapValues { ObservableMetaWrapper(it.value) }
override fun getMeta(name: Name): Meta? = origin.getMeta(name)
override var value: Value?
get() = origin.value
set(value) {
@ -86,7 +85,10 @@ private class ObservableMetaWrapper(
}
override fun attach(name: Name, node: ObservableMutableMeta) {
TODO("Not yet implemented")
set(name, node)
node.onChange(this) { changeName ->
setMeta(name + changeName, node[changeName])
}
}
}
@ -104,14 +106,14 @@ public fun MutableMeta.asObservable(): ObservableMutableMeta =
*
* Optional [owner] property is used for
*/
public fun <O : ObservableMeta, T> O.useProperty(
property: KProperty1<O, T>,
public fun <S : Scheme, T> S.useProperty(
property: KProperty1<S, T>,
owner: Any? = null,
callBack: O.(T) -> Unit,
callBack: S.(T) -> Unit,
) {
//Pass initial value.
callBack(property.get(this))
onChange(owner) { name ->
meta.onChange(owner) { name ->
if (name.startsWith(property.name.asName())) {
callBack(property.get(this@useProperty))
}

View File

@ -2,35 +2,24 @@ package space.kscience.dataforge.meta
import space.kscience.dataforge.meta.descriptors.*
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].
* Default item provider and [NodeDescriptor] are optional
* Default item provider and [MetaDescriptor] are optional
*/
public open class Scheme(
source: MutableMeta = MutableMeta()
) : Described, MutableMeta, ObservableMeta, Meta by source {
public open class Scheme : Described, MetaRepr, MutableMetaProvider {
private var source = source.asObservable()
public var meta: ObservableMutableMeta = MutableMeta()
private set
final override var descriptor: MetaDescriptor? = null
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(
items: MutableMeta,
preserveDefault: Boolean = false
) {
this.source = (if (preserveDefault) items.withDefault(this.source) else items).asObservable()
meta = (if (preserveDefault) items.withDefault(meta.seal()) else items).asObservable()
}
/**
@ -41,35 +30,17 @@ public open class Scheme(
return descriptor?.validate(meta) ?: true
}
/**
* Set a configurable property
*/
override fun set(name: Name, meta: Meta) {
val oldItem = source[name]
if (oldItem != meta) {
if (validate(name, meta)) {
source[name] = meta
} else {
error("Validation failed for property $name with value $meta")
}
override fun getMeta(name: Name): Meta? = meta.getMeta(name)
override fun setMeta(name: Name, node: Meta?) {
if (validate(name, meta)) {
meta.setMeta(name, node)
} else {
error("Validation failed for node $node at $name")
}
}
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)
}
override fun toMeta(): Laminate = Laminate(meta, descriptor?.defaultNode)
}
/**

View File

@ -77,6 +77,11 @@ public fun <T : Scheme> MutableMeta.spec(
}
}
public fun <T : Scheme> Scheme.spec(
spec: Specification<T>,
key: Name? = null,
): ReadWriteProperty<Any?, T> = meta.spec(spec, key)
/**
* A delegate that uses a [Specification] to wrap a list of child providers.
* If children are mutable, the changes in list elements are reflected on them.

View File

@ -43,7 +43,7 @@ public operator fun MetaDescriptor.get(name: Name): MetaDescriptor? = when (name
else -> get(name.firstOrNull()!!.asName())?.get(name.cutFirst())
}
public operator fun MetaDescriptor.get(name: String): MetaDescriptor? = get(name.toName())
public operator fun MetaDescriptor.get(name: String): MetaDescriptor? = get(Name.parse(name))
/**
* A node constructed of default values for this descriptor and its children

View File

@ -4,10 +4,22 @@ 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.names.Name
import space.kscience.dataforge.names.cutFirst
import space.kscience.dataforge.names.first
import space.kscience.dataforge.names.length
import space.kscience.dataforge.values.Value
import space.kscience.dataforge.values.ValueType
import space.kscience.dataforge.values.asValue
import kotlin.collections.List
import kotlin.collections.MutableMap
import kotlin.collections.emptyList
import kotlin.collections.getOrPut
import kotlin.collections.hashMapOf
import kotlin.collections.listOf
import kotlin.collections.map
import kotlin.collections.mapValues
import kotlin.collections.set
public class MetaDescriptorBuilder {
public var info: String? = null
@ -67,7 +79,7 @@ public class MetaDescriptorBuilder {
}
public fun MetaDescriptorBuilder.item(name: String, block: MetaDescriptorBuilder.() -> Unit) {
item(name.toName(), block)
item(Name.parse(name), block)
}
public fun MetaDescriptor(block: MetaDescriptorBuilder.() -> Unit): MetaDescriptor =
@ -94,7 +106,7 @@ public fun MetaDescriptorBuilder.value(
vararg additionalTypes: ValueType,
block: MetaDescriptorBuilder.() -> Unit
) {
value(name.toName(), type, additionalTypes = additionalTypes, block)
value(Name.parse(name), type, additionalTypes = additionalTypes, block)
}
/**
@ -108,7 +120,7 @@ public fun MetaDescriptorBuilder.node(name: Name, block: MetaDescriptorBuilder.(
}
public fun MetaDescriptorBuilder.node(name: String, block: MetaDescriptorBuilder.() -> Unit) {
node(name.toName(), block)
node(Name.parse(name), block)
}
public inline fun <reified E : Enum<E>> MetaDescriptorBuilder.enum(

View File

@ -3,7 +3,7 @@ package space.kscience.dataforge.meta
import space.kscience.dataforge.meta.descriptors.MetaDescriptor
import space.kscience.dataforge.meta.descriptors.get
import space.kscience.dataforge.misc.DFExperimental
import space.kscience.dataforge.names.toName
import space.kscience.dataforge.names.Name
import space.kscience.dataforge.values.ListValue
import space.kscience.dataforge.values.Value
@ -28,7 +28,7 @@ public fun Meta.toMap(descriptor: MetaDescriptor? = null): Map<String, Any?> = b
/**
* Convert map of maps to meta. This method will recognize [Meta], [Map]<String,Any?> and [List] of all mentioned above as value.
* All other values will be converted to values.
* All other values will be converted to [Value].
*/
@DFExperimental
public fun Map<String, Any?>.toMeta(descriptor: MetaDescriptor? = null): Meta = Meta {
@ -45,7 +45,7 @@ public fun Map<String, Any?>.toMeta(descriptor: MetaDescriptor? = null): Meta =
if (items.all { it.isLeaf }) {
set(key, ListValue(items.map { it.value!! }))
} else {
setIndexedItems(key.toName(), value.map { toMeta(it) })
setIndexedItems(Name.parse(key), value.map { toMeta(it) })
}
} else {
set(key, toMeta(value))

View File

@ -42,7 +42,7 @@ public data class KeepTransformationRule(val selector: (Name) -> Boolean) :
meta.nodeSequence().map { it.first }.filter(selector)
override fun transformItem(name: Name, item: Meta?, target: MutableMeta) {
if (selector(name)) target.set(name, item)
if (selector(name)) target.setMeta(name, item)
}
}
@ -174,7 +174,7 @@ public class MetaTransformationBuilder {
public fun keep(regex: String) {
transformations.add(
RegexItemTransformationRule(regex.toRegex()) { name, _, Meta ->
set(name, Meta)
setMeta(name, Meta)
})
}
@ -184,7 +184,7 @@ public class MetaTransformationBuilder {
public fun move(from: Name, to: Name, operation: (Meta?) -> Meta? = { it }) {
transformations.add(
SingleItemTransformationRule(from) { _, item ->
set(to, operation(item))
setMeta(to, operation(item))
}
)
}

View File

@ -47,6 +47,61 @@ public class Name(public val tokens: List<NameToken>) {
public val MATCH_ALL_TOKEN: NameToken = NameToken("**")
public val EMPTY: Name = Name(emptyList())
/**
* Convert a [String] to name parsing it and extracting name tokens and index syntax.
* This operation is rather heavy so it should be used with care in high performance code.
*/
public fun parse(string: String): Name{
if (string.isBlank()) return Name.EMPTY
val tokens = sequence {
var bodyBuilder = StringBuilder()
var queryBuilder = StringBuilder()
var bracketCount: Int = 0
var escape: Boolean = false
fun queryOn() = bracketCount > 0
for (it in string) {
when {
escape -> {
if (queryOn()) {
queryBuilder.append(it)
} else {
bodyBuilder.append(it)
}
escape = false
}
it == '\\' -> {
escape = true
}
queryOn() -> {
when (it) {
'[' -> bracketCount++
']' -> bracketCount--
}
if (queryOn()) queryBuilder.append(it)
}
else -> when (it) {
'.' -> {
val query = if (queryBuilder.isEmpty()) null else queryBuilder.toString()
yield(NameToken(bodyBuilder.toString(), query))
bodyBuilder = StringBuilder()
queryBuilder = StringBuilder()
}
'[' -> bracketCount++
']' -> error("Syntax error: closing bracket ] not have not matching open bracket")
else -> {
if (queryBuilder.isNotEmpty()) error("Syntax error: only name end and name separator are allowed after index")
bodyBuilder.append(it)
}
}
}
}
val query = if (queryBuilder.isEmpty()) null else queryBuilder.toString()
yield(NameToken(bodyBuilder.toString(), query))
}
return Name(tokens.toList())
}
}
}
@ -80,61 +135,6 @@ public fun Name.firstOrNull(): NameToken? = tokens.firstOrNull()
public fun Name.first(): NameToken = tokens.first()
/**
* Convert a [String] to name parsing it and extracting name tokens and index syntax.
* This operation is rather heavy so it should be used with care in high performance code.
*/
public fun String.toName(): Name {
if (isBlank()) return Name.EMPTY
val tokens = sequence {
var bodyBuilder = StringBuilder()
var queryBuilder = StringBuilder()
var bracketCount: Int = 0
var escape: Boolean = false
fun queryOn() = bracketCount > 0
for (it in this@toName) {
when {
escape -> {
if (queryOn()) {
queryBuilder.append(it)
} else {
bodyBuilder.append(it)
}
escape = false
}
it == '\\' -> {
escape = true
}
queryOn() -> {
when (it) {
'[' -> bracketCount++
']' -> bracketCount--
}
if (queryOn()) queryBuilder.append(it)
}
else -> when (it) {
'.' -> {
val query = if (queryBuilder.isEmpty()) null else queryBuilder.toString()
yield(NameToken(bodyBuilder.toString(), query))
bodyBuilder = StringBuilder()
queryBuilder = StringBuilder()
}
'[' -> bracketCount++
']' -> error("Syntax error: closing bracket ] not have not matching open bracket")
else -> {
if (queryBuilder.isNotEmpty()) error("Syntax error: only name end and name separator are allowed after index")
bodyBuilder.append(it)
}
}
}
}
val query = if (queryBuilder.isEmpty()) null else queryBuilder.toString()
yield(NameToken(bodyBuilder.toString(), query))
}
return Name(tokens.toList())
}
/**
* Convert the [String] to a [Name] by simply wrapping it in a single name token without parsing.
* The input string could contain dots and braces, but they are just escaped, not parsed.
@ -145,7 +145,7 @@ public operator fun NameToken.plus(other: Name): Name = Name(listOf(this) + othe
public operator fun Name.plus(other: Name): Name = Name(this.tokens + other.tokens)
public operator fun Name.plus(other: String): Name = this + other.toName()
public operator fun Name.plus(other: String): Name = this + Name.parse(other)
public operator fun Name.plus(other: NameToken): Name = Name(tokens + other)
@ -174,8 +174,8 @@ public fun Name.withIndex(index: String): Name {
* Fast [String]-based accessor for item map
*/
public operator fun <T> Map<NameToken, T>.get(body: String, query: String? = null): T? = get(NameToken(body, query))
public operator fun <T> Map<Name, T>.get(name: String): T? = get(name.toName())
public operator fun <T> MutableMap<Name, T>.set(name: String, value: T): Unit = set(name.toName(), value)
public operator fun <T> Map<Name, T>.get(name: String): T? = get(Name.parse(name))
public operator fun <T> MutableMap<Name, T>.set(name: String, value: T): Unit = set(Name.parse(name), value)
/* Name comparison operations */

View File

@ -12,7 +12,7 @@ public object NameSerializer : KSerializer<Name> {
PrimitiveSerialDescriptor("space.kscience.dataforge.names.Name", PrimitiveKind.STRING)
override fun deserialize(decoder: Decoder): Name {
return decoder.decodeString().toName()
return Name.parse(decoder.decodeString())
}
override fun serialize(encoder: Encoder, value: Name) {
@ -25,7 +25,7 @@ public object NameTokenSerializer: KSerializer<NameToken> {
PrimitiveSerialDescriptor("space.kscience.dataforge.names.NameToken", PrimitiveKind.STRING)
override fun deserialize(decoder: Decoder): NameToken {
return decoder.decodeString().toName().firstOrNull()!!
return Name.parse(decoder.decodeString()).firstOrNull()!!
}
override fun serialize(

View File

@ -44,4 +44,4 @@ public fun Name.matches(pattern: Name): Boolean = when {
}
@OptIn(DFExperimental::class)
public fun Name.matches(pattern: String): Boolean = matches(pattern.toName())
public fun Name.matches(pattern: String): Boolean = matches(Name.parse(pattern))

View File

@ -30,7 +30,7 @@ class MetaDelegateTest {
fun delegateTest() {
val testObject = TestScheme.empty()
testObject.set("myValue","theString".asValue())
testObject.meta["myValue"] = "theString".asValue()
testObject.enumValue = TestEnum.NO
testObject.inner = InnerScheme { innerValue = "ddd" }

View File

@ -10,7 +10,7 @@ import kotlin.test.assertTrue
class NameMatchTest {
@Test
fun matchWildCards() {
val theName = "a.b.c.d".toName()
val theName = Name.parse("a.b.c.d")
assertTrue { theName.matches("a.b.**") }
assertTrue { theName.matches("a.*.c.**") }
assertTrue { theName.matches("**.d") }
@ -22,7 +22,7 @@ class NameMatchTest {
@Test
fun matchPattern() {
val theName = "a[dd+2].b[13].c.d[\"d\"]".toName()
val theName = Name.parse("a[dd+2].b[13].c.d[\"d\"]")
assertTrue { theName.matches("a[.*].b[.*].c[.*].d[.*]") }
assertTrue { theName.matches("a[.*].b[.*].c.d[.*]") }
assertFalse { theName.matches("a[.*].b[.*].*.d") }

View File

@ -9,7 +9,7 @@ class NameSerializationTest {
@Test
fun testNameSerialization() {
val name = "aaa.bbb.ccc".toName()
val name = Name.parse("aaa.bbb.ccc")
val json = Json.encodeToJsonElement(Name.serializer(), name)
println(json)
val reconstructed = Json.decodeFromJsonElement(Name.serializer(), json)

View File

@ -8,22 +8,22 @@ import kotlin.test.assertTrue
class NameTest {
@Test
fun simpleName() {
val name = "token1.token2.token3".toName()
val name = Name.parse("token1.token2.token3")
assertEquals("token2", name[1].toString())
}
@Test
fun equalityTest() {
val name1 = "token1.token2[2].token3".toName()
val name2 = "token1".toName() + "token2[2].token3"
val name1 = Name.parse("token1.token2[2].token3")
val name2 = "token1".asName() + "token2[2].token3"
assertEquals(name1, name2)
}
@Test
fun comparisonTest(){
val name1 = "token1.token2.token3".toName()
val name2 = "token1.token2".toName()
val name3 = "token3".toName()
val name1 = Name.parse("token1.token2.token3")
val name2 = Name.parse("token1.token2")
val name3 = Name.parse("token3")
assertTrue { name1.startsWith(name2) }
assertTrue { name1.endsWith(name3) }
assertFalse { name1.startsWith(name3) }
@ -31,11 +31,11 @@ class NameTest {
@Test
fun escapeTest(){
val escapedName = "token\\.one.token2".toName()
val escapedName = Name.parse("token\\.one.token2")
val unescapedName = "token\\.one.token2".asName()
assertEquals(2, escapedName.length)
assertEquals(1, unescapedName.length)
assertEquals(escapedName, escapedName.toString().toName())
assertEquals(escapedName, Name.parse(escapedName.toString()))
}
}

View File

@ -1,6 +1,5 @@
package space.kscience.dataforge.meta
import space.kscience.dataforge.misc.DFExperimental
import space.kscience.dataforge.names.NameToken
import space.kscience.dataforge.values.Value
import space.kscience.dataforge.values.asValue
@ -8,7 +7,6 @@ import space.kscience.dataforge.values.isList
//TODO add Meta wrapper for dynamic
@DFExperimental
public fun Value.toDynamic(): dynamic {
return if (isList()) {
list.map { it.toDynamic() }.toTypedArray().asDynamic()
@ -20,7 +18,6 @@ public fun Value.toDynamic(): dynamic {
/**
* Represent or copy this [Meta] to dynamic object to be passed to JS libraries
*/
@DFExperimental
public fun Meta.toDynamic(): dynamic {
if (this is DynamicMeta) return this.obj
if(items.isEmpty()) return value?.toDynamic()
@ -36,7 +33,6 @@ public fun Meta.toDynamic(): dynamic {
return res
}
@DFExperimental
public class DynamicMeta(internal val obj: dynamic) : TypedMeta<DynamicMeta> {
private fun keys(): Array<String> = js("Object").keys(obj) as Array<String>

View File

@ -5,7 +5,6 @@ import space.kscience.dataforge.meta.Meta
import space.kscience.dataforge.meta.MutableMeta
import space.kscience.dataforge.misc.Type
import space.kscience.dataforge.names.Name
import space.kscience.dataforge.names.toName
import space.kscience.dataforge.provider.Provider
@ -28,7 +27,7 @@ public interface Workspace : ContextAware, Provider {
override fun content(target: String): Map<Name, Any> {
return when (target) {
"target", Meta.TYPE -> targets.mapKeys { it.key.toName() }
"target", Meta.TYPE -> targets.mapKeys { Name.parse(it.key)}
Task.TYPE -> tasks
//Data.TYPE -> data.flow().toMap()
else -> emptyMap()
@ -50,10 +49,10 @@ public interface Workspace : ContextAware, Provider {
}
public suspend fun Workspace.produce(task: String, target: String): TaskResult<*> =
produce(task.toName(), targets[target] ?: error("Target with key $target not found in $this"))
produce(Name.parse(task), targets[target] ?: error("Target with key $target not found in $this"))
public suspend fun Workspace.produce(task: String, meta: Meta): TaskResult<*> =
produce(task.toName(), meta)
produce(Name.parse(task), meta)
public suspend fun Workspace.produce(task: String, block: MutableMeta.() -> Unit = {}): TaskResult<*> =
produce(task, Meta(block))

View File

@ -14,7 +14,6 @@ import space.kscience.dataforge.meta.descriptors.MetaDescriptorBuilder
import space.kscience.dataforge.misc.DFBuilder
import space.kscience.dataforge.misc.DFExperimental
import space.kscience.dataforge.names.Name
import space.kscience.dataforge.names.toName
import kotlin.properties.PropertyDelegateProvider
import kotlin.properties.ReadOnlyProperty
@ -29,13 +28,13 @@ public inline fun <reified T : Any> TaskContainer.registerTask(
name: String,
noinline descriptorBuilder: MetaDescriptorBuilder.() -> Unit = {},
noinline builder: suspend TaskResultBuilder<T>.() -> Unit,
): Unit = registerTask(name.toName(), Task(MetaDescriptor(descriptorBuilder), builder))
): Unit = registerTask(Name.parse(name), Task(MetaDescriptor(descriptorBuilder), builder))
public inline fun <reified T : Any> TaskContainer.task(
noinline descriptorBuilder: MetaDescriptorBuilder.() -> Unit = {},
noinline builder: suspend TaskResultBuilder<T>.() -> Unit,
): PropertyDelegateProvider<Any?, ReadOnlyProperty<Any?, TaskReference<T>>> = PropertyDelegateProvider { _, property ->
val taskName = property.name.toName()
val taskName = Name.parse(property.name)
val task = Task(MetaDescriptor(descriptorBuilder), builder)
registerTask(taskName, task)
ReadOnlyProperty { _, _ -> TaskReference(taskName, task) }

View File

@ -7,7 +7,6 @@ import space.kscience.dataforge.context.PluginFactory
import space.kscience.dataforge.context.PluginTag
import space.kscience.dataforge.data.*
import space.kscience.dataforge.meta.Meta
import space.kscience.dataforge.names.toName
import kotlin.reflect.KClass
import kotlin.test.Test
import kotlin.test.assertEquals
@ -25,7 +24,7 @@ class DataPropagationTestPlugin : WorkspacePlugin() {
val singleData by task<Int> {
workspace.data.select<Int>().getData("myData[12]".toName())?.let {
workspace.data.select<Int>().getData("myData[12]")?.let {
emit("result", it)
}
}