dataforge-meta cleanup

This commit is contained in:
Alexander Nozik 2020-09-07 22:43:39 +03:00
parent 87af89b47d
commit 52a3c8bc6f
20 changed files with 206 additions and 270 deletions

View File

@ -2,7 +2,6 @@ package hep.dataforge.context
import hep.dataforge.meta.Meta
import hep.dataforge.meta.MetaRepr
import hep.dataforge.meta.buildMeta
import hep.dataforge.names.Name
import hep.dataforge.names.toName
import hep.dataforge.provider.Provider

View File

@ -2,7 +2,6 @@ package hep.dataforge.context
import hep.dataforge.meta.Meta
import hep.dataforge.meta.MetaRepr
import hep.dataforge.meta.buildMeta
/**
* The tag which contains information about name, group and version of some
@ -10,7 +9,7 @@ import hep.dataforge.meta.buildMeta
*
* @author Alexander Nozik
*/
data class PluginTag(
public data class PluginTag(
val name: String,
val group: String = "",
val version: String = ""
@ -22,7 +21,7 @@ data class PluginTag(
* @param otherTag
* @return
*/
fun matches(otherTag: PluginTag): Boolean {
public fun matches(otherTag: PluginTag): Boolean {
return matchesName(otherTag) && matchesGroup(otherTag)
}
@ -42,9 +41,9 @@ data class PluginTag(
"version" put version
}
companion object {
public companion object {
const val DATAFORGE_GROUP = "hep.dataforge"
public const val DATAFORGE_GROUP: String = "hep.dataforge"
/**
* Build new PluginTag from standard string representation
@ -52,7 +51,7 @@ data class PluginTag(
* @param tag
* @return
*/
fun fromString(tag: String): PluginTag {
public fun fromString(tag: String): PluginTag {
val sepIndex = tag.indexOf(":")
return if (sepIndex >= 0) {
PluginTag(group = tag.substring(0, sepIndex), name = tag.substring(sepIndex + 1))

View File

@ -3,7 +3,6 @@ package hep.dataforge.data
import kotlinx.coroutines.runBlocking
import kotlin.reflect.KClass
import kotlin.reflect.full.isSubclassOf
import kotlin.reflect.full.isSuperclassOf
/**
* Block the thread and get data content
@ -14,7 +13,7 @@ public fun <T : Any> Data<T>.get(): T = runBlocking { await() }
* Check that node is compatible with given type meaning that each element could be cast to the type
*/
internal actual fun <R : Any> DataNode<*>.canCast(type: KClass<out R>): Boolean =
this.type.isSubclassOf(type)
type.isSubclassOf(this.type)
internal actual fun <R : Any> Data<*>.canCast(type: KClass<out R>): Boolean =
this.type.isSubclassOf(type)

View File

@ -12,6 +12,7 @@ import kotlinx.serialization.Serializer
import kotlinx.serialization.descriptors.SerialDescriptor
import kotlinx.serialization.encoding.Decoder
import kotlinx.serialization.encoding.Encoder
import kotlinx.serialization.json.Json
/**
@ -86,7 +87,7 @@ public sealed class MetaItem<out M : Meta> {
}
public fun Value.asMetaItem(): ValueItem = ValueItem(this)
public fun <M:Meta> M.asMetaItem(): NodeItem<M> = NodeItem(this)
public fun <M : Meta> M.asMetaItem(): NodeItem<M> = NodeItem(this)
/**
* The object that could be represented as [Meta]. Meta provided by [toMeta] method should fully represent object state.
@ -96,8 +97,12 @@ public interface MetaRepr {
public fun toMeta(): Meta
}
public interface ItemProvider{
public fun interface ItemProvider {
public fun getItem(name: Name): MetaItem<*>?
public companion object {
public val EMPTY: ItemProvider = ItemProvider { null }
}
}
/**
@ -107,6 +112,7 @@ public interface ItemProvider{
*
* * Same name siblings are supported via elements with the same [Name] but different queries
*/
@Serializable(MetaSerializer::class)
public interface Meta : MetaRepr, ItemProvider {
/**
* Top level items of meta tree
@ -140,7 +146,7 @@ public interface Meta : MetaRepr, ItemProvider {
*/
public const val VALUE_KEY: String = "@value"
public val EMPTY: Meta = object: MetaBase() {
public val EMPTY: Meta = object : MetaBase() {
override val items: Map<NameToken, MetaItem<*>> = emptyMap()
}
}
@ -202,7 +208,7 @@ public interface MetaNode<out M : MetaNode<M>> : Meta {
/**
* The same as [Meta.get], but with specific node type
*/
public operator fun <M : MetaNode<M>> M?.get(name: Name): MetaItem<M>? = if( this == null) {
public operator fun <M : MetaNode<M>> M?.get(name: Name): MetaItem<M>? = if (this == null) {
null
} else {
@Suppress("UNCHECKED_CAST", "ReplaceGetOrSet")
@ -226,7 +232,10 @@ public abstract class MetaBase : Meta {
override fun hashCode(): Int = items.hashCode()
override fun toString(): String = JSON_PRETTY.encodeToString(MetaSerializer, this)
override fun toString(): String = Json {
prettyPrint = true
useArrayPolymorphism = true
}.encodeToString(MetaSerializer, this)
}
/**
@ -240,7 +249,7 @@ public abstract class AbstractMetaNode<M : MetaNode<M>> : MetaNode<M>, MetaBase(
* If the argument is possibly mutable node, it is copied on creation
*/
public class SealedMeta internal constructor(
override val items: Map<NameToken, MetaItem<SealedMeta>>
override val items: Map<NameToken, MetaItem<SealedMeta>>,
) : AbstractMetaNode<SealedMeta>()
/**

View File

@ -11,113 +11,109 @@ import kotlin.jvm.JvmName
* DSL builder for meta. Is not intended to store mutable state
*/
@DFBuilder
class MetaBuilder : AbstractMutableMeta<MetaBuilder>() {
public class MetaBuilder : AbstractMutableMeta<MetaBuilder>() {
override fun wrapNode(meta: Meta): MetaBuilder = if (meta is MetaBuilder) meta else meta.builder()
override fun empty(): MetaBuilder = MetaBuilder()
infix fun String.put(item: MetaItem<*>?) {
public infix fun String.put(item: MetaItem<*>?) {
set(this, item)
}
infix fun String.put(value: Value?) {
public infix fun String.put(value: Value?) {
set(this, value)
}
infix fun String.put(string: String?) {
public infix fun String.put(string: String?) {
set(this, string?.asValue())
}
infix fun String.put(number: Number?) {
public infix fun String.put(number: Number?) {
set(this, number?.asValue())
}
infix fun String.put(boolean: Boolean?) {
public infix fun String.put(boolean: Boolean?) {
set(this, boolean?.asValue())
}
infix fun String.put(enum: Enum<*>) {
public infix fun String.put(enum: Enum<*>) {
set(this, EnumValue(enum))
}
@JvmName("putValues")
infix fun String.put(iterable: Iterable<Value>) {
public infix fun String.put(iterable: Iterable<Value>) {
set(this, iterable.asValue())
}
@JvmName("putNumbers")
infix fun String.put(iterable: Iterable<Number>) {
public infix fun String.put(iterable: Iterable<Number>) {
set(this, iterable.map { it.asValue() }.asValue())
}
@JvmName("putStrings")
infix fun String.put(iterable: Iterable<String>) {
public infix fun String.put(iterable: Iterable<String>) {
set(this, iterable.map { it.asValue() }.asValue())
}
infix fun String.put(array: DoubleArray) {
public infix fun String.put(array: DoubleArray) {
set(this, array.asValue())
}
infix fun String.putValue(any: Any?) {
set(this, Value.of(any))
}
infix fun String.put(meta: Meta?) {
public infix fun String.put(meta: Meta?) {
this@MetaBuilder[this] = meta
}
infix fun String.put(repr: MetaRepr?) {
public infix fun String.put(repr: MetaRepr?) {
set(this, repr?.toMeta())
}
@JvmName("putMetas")
infix fun String.put(value: Iterable<Meta>) {
public infix fun String.put(value: Iterable<Meta>) {
this@MetaBuilder[this] = value.toList()
}
infix fun String.put(metaBuilder: MetaBuilder.() -> Unit) {
public infix fun String.put(metaBuilder: MetaBuilder.() -> Unit) {
this@MetaBuilder[this] = MetaBuilder().apply(metaBuilder)
}
infix fun Name.put(value: Value?) {
public infix fun Name.put(value: Value?) {
set(this, value)
}
infix fun Name.put(string: String?) {
public infix fun Name.put(string: String?) {
set(this, string?.asValue())
}
infix fun Name.put(number: Number?) {
public infix fun Name.put(number: Number?) {
set(this, number?.asValue())
}
infix fun Name.put(boolean: Boolean?) {
public infix fun Name.put(boolean: Boolean?) {
set(this, boolean?.asValue())
}
infix fun Name.put(enum: Enum<*>) {
public infix fun Name.put(enum: Enum<*>) {
set(this, EnumValue(enum))
}
@JvmName("putValues")
infix fun Name.put(iterable: Iterable<Value>) {
public infix fun Name.put(iterable: Iterable<Value>) {
set(this, iterable.asValue())
}
infix fun Name.put(meta: Meta?) {
public infix fun Name.put(meta: Meta?) {
this@MetaBuilder[this] = meta
}
infix fun Name.put(repr: MetaRepr?) {
public infix fun Name.put(repr: MetaRepr?) {
set(this, repr?.toMeta())
}
@JvmName("putMetas")
infix fun Name.put(value: Iterable<Meta>) {
public infix fun Name.put(value: Iterable<Meta>) {
this@MetaBuilder[this] = value.toList()
}
infix fun Name.put(metaBuilder: MetaBuilder.() -> Unit) {
public infix fun Name.put(metaBuilder: MetaBuilder.() -> Unit) {
this@MetaBuilder[this] = MetaBuilder().apply(metaBuilder)
}
}
@ -125,7 +121,7 @@ class MetaBuilder : AbstractMutableMeta<MetaBuilder>() {
/**
* For safety, builder always copies the initial meta even if it is builder itself
*/
fun Meta.builder(): MetaBuilder {
public fun Meta.builder(): MetaBuilder {
return MetaBuilder().also { builder ->
items.mapValues { entry ->
val item = entry.value
@ -140,16 +136,10 @@ fun Meta.builder(): MetaBuilder {
/**
* Create a deep copy of this meta and apply builder to it
*/
fun Meta.edit(builder: MetaBuilder.() -> Unit): MetaBuilder = builder().apply(builder)
/**
* Build a [MetaBuilder] using given transformation
*/
@Deprecated("To be replaced with fake constructor", ReplaceWith("Meta"))
fun buildMeta(builder: MetaBuilder.() -> Unit): MetaBuilder = MetaBuilder().apply(builder)
public fun Meta.edit(builder: MetaBuilder.() -> Unit): MetaBuilder = builder().apply(builder)
/**
* Build a [MetaBuilder] using given transformation
*/
@Suppress("FunctionName")
fun Meta(builder: MetaBuilder.() -> Unit): MetaBuilder = MetaBuilder().apply(builder)
public fun Meta(builder: MetaBuilder.() -> Unit): MetaBuilder = MetaBuilder().apply(builder)

View File

@ -1,6 +1,7 @@
package hep.dataforge.meta
import hep.dataforge.names.NameToken
import kotlinx.serialization.ExperimentalSerializationApi
import kotlinx.serialization.KSerializer
import kotlinx.serialization.Serializer
import kotlinx.serialization.builtins.MapSerializer
@ -11,12 +12,13 @@ import kotlinx.serialization.json.JsonDecoder
import kotlinx.serialization.json.JsonEncoder
import kotlinx.serialization.json.JsonObject
/**
* Serialized for meta
*/
@OptIn(ExperimentalSerializationApi::class)
@Serializer(Meta::class)
object MetaSerializer : KSerializer<Meta> {
public object MetaSerializer : KSerializer<Meta> {
private val mapSerializer = MapSerializer(
NameToken.serializer(),
MetaItem.serializer(MetaSerializer)

View File

@ -14,8 +14,7 @@ import kotlin.reflect.KProperty
public typealias MutableItemDelegate = ReadWriteProperty<Any?, MetaItem<*>?>
public fun MutableItemProvider.item(key: Name? = null): MutableItemDelegate = object :MutableItemDelegate {
public fun MutableItemProvider.item(key: Name? = null): MutableItemDelegate = object : MutableItemDelegate {
override fun getValue(thisRef: Any?, property: KProperty<*>): MetaItem<*>? {
return getItem(key ?: property.name.asName())
}
@ -24,7 +23,6 @@ public fun MutableItemProvider.item(key: Name? = null): MutableItemDelegate = ob
val name = key ?: property.name.asName()
setItem(name, value)
}
//MutableItemDelegate(this, key)
}
/* Mutable converters */
@ -111,7 +109,10 @@ public fun MutableItemProvider.boolean(key: Name? = null, default: () -> Boolean
public fun MutableItemProvider.number(key: Name? = null, default: () -> Number): ReadWriteProperty<Any?, Number> =
item(key).convert(MetaConverter.number, default)
public inline fun <reified E : Enum<E>> MutableItemProvider.enum(default: E, key: Name? = null): ReadWriteProperty<Any?, E> =
public inline fun <reified E : Enum<E>> MutableItemProvider.enum(
default: E,
key: Name? = null,
): ReadWriteProperty<Any?, E> =
item(key).convert(MetaConverter.enum()) { default }
public inline fun <reified M : MutableMeta<M>> M.node(key: Name? = null): ReadWriteProperty<Any?, M?> =
@ -155,14 +156,14 @@ public fun MutableItemProvider.float(default: Float, key: Name? = null): ReadWri
public fun MutableItemProvider.stringList(
vararg default: String,
key: Name? = null
key: Name? = null,
): ReadWriteProperty<Any?, List<String>> = item(key).convert(
reader = { it?.stringList ?: listOf(*default) },
writer = { it.map { str -> str.asValue() }.asValue().asMetaItem() }
)
public fun MutableItemProvider.stringList(
key: Name? = null
key: Name? = null,
): ReadWriteProperty<Any?, List<String>?> = item(key).convert(
reader = { it?.stringList },
writer = { it?.map { str -> str.asValue() }?.asValue()?.asMetaItem() }
@ -170,7 +171,7 @@ public fun MutableItemProvider.stringList(
public fun MutableItemProvider.numberList(
vararg default: Number,
key: Name? = null
key: Name? = null,
): ReadWriteProperty<Any?, List<Number>> = item(key).convert(
reader = { it?.value?.list?.map { value -> value.number } ?: listOf(*default) },
writer = { it.map { num -> num.asValue() }.asValue().asMetaItem() }
@ -181,8 +182,8 @@ public fun MutableItemProvider.numberList(
public fun MutableItemProvider.doubleArray(
vararg default: Double,
key: Name? = null
): ReadWriteProperty<Any?, DoubleArray> =item(key).convert(
key: Name? = null,
): ReadWriteProperty<Any?, DoubleArray> = item(key).convert(
reader = { it?.value?.doubleArray ?: doubleArrayOf(*default) },
writer = { DoubleArrayValue(it).asMetaItem() }
)
@ -190,5 +191,5 @@ public fun MutableItemProvider.doubleArray(
public fun <T> MutableItemProvider.listValue(
key: Name? = null,
writer: (T) -> Value = { Value.of(it) },
reader: (Value) -> T
reader: (Value) -> T,
): ReadWriteProperty<Any?, List<T>?> = item(key).convert(MetaConverter.valueList(writer, reader))

View File

@ -3,11 +3,11 @@ package hep.dataforge.meta
import hep.dataforge.names.*
import hep.dataforge.values.Value
interface MutableItemProvider : ItemProvider {
fun setItem(name: Name, item: MetaItem<*>?)
public interface MutableItemProvider : ItemProvider {
public fun setItem(name: Name, item: MetaItem<*>?)
}
interface MutableMeta<out M : MutableMeta<M>> : MetaNode<M>, MutableItemProvider {
public interface MutableMeta<out M : MutableMeta<M>> : MetaNode<M>, MutableItemProvider {
override val items: Map<NameToken, MetaItem<M>>
// fun onChange(owner: Any? = null, action: (Name, MetaItem<*>?, MetaItem<*>?) -> Unit)
// fun removeListener(owner: Any? = null)
@ -18,7 +18,7 @@ interface MutableMeta<out M : MutableMeta<M>> : MetaNode<M>, MutableItemProvider
*
* Changes in Meta are not thread safe.
*/
abstract class AbstractMutableMeta<M : MutableMeta<M>> : AbstractMetaNode<M>(), MutableMeta<M> {
public abstract class AbstractMutableMeta<M : MutableMeta<M>> : AbstractMetaNode<M>(), MutableMeta<M> {
protected val _items: MutableMap<NameToken, MetaItem<M>> = LinkedHashMap()
override val items: Map<NameToken, MetaItem<M>>
@ -74,28 +74,28 @@ abstract class AbstractMutableMeta<M : MutableMeta<M>> : AbstractMetaNode<M>(),
@Suppress("NOTHING_TO_INLINE")
inline fun MutableMeta<*>.remove(name: Name) = setItem(name, null)
public inline fun MutableMeta<*>.remove(name: Name): Unit = setItem(name, null)
@Suppress("NOTHING_TO_INLINE")
inline fun MutableMeta<*>.remove(name: String) = remove(name.toName())
public inline fun MutableMeta<*>.remove(name: String): Unit = remove(name.toName())
operator fun MutableMeta<*>.set(name: Name, item: MetaItem<*>?) = setItem(name, item)
public operator fun MutableMeta<*>.set(name: Name, item: MetaItem<*>?): Unit = setItem(name, item)
fun MutableMeta<*>.setValue(name: Name, value: Value) = setItem(name, MetaItem.ValueItem(value))
public fun MutableMeta<*>.setValue(name: Name, value: Value): Unit = setItem(name, MetaItem.ValueItem(value))
fun MutableMeta<*>.setValue(name: String, value: Value) = set(name.toName(), value)
public fun MutableMeta<*>.setValue(name: String, value: Value): Unit = set(name.toName(), value)
fun MutableMeta<*>.setItem(name: String, item: MetaItem<*>?) = setItem(name.toName(), item)
public fun MutableMeta<*>.setItem(name: String, item: MetaItem<*>?): Unit = setItem(name.toName(), item)
fun MutableMeta<*>.setNode(name: Name, node: Meta) =
public fun MutableMeta<*>.setNode(name: Name, node: Meta): Unit =
setItem(name, MetaItem.NodeItem(node))
fun MutableMeta<*>.setNode(name: String, node: Meta) = setNode(name.toName(), node)
public fun MutableMeta<*>.setNode(name: String, node: Meta): Unit = setNode(name.toName(), node)
/**
* Universal unsafe set method
*/
operator fun MutableMeta<*>.set(name: Name, value: Any?) {
public operator fun MutableMeta<*>.set(name: Name, value: Any?) {
when (value) {
null -> remove(name)
is MetaItem<*> -> setItem(name, value)
@ -105,11 +105,14 @@ operator fun MutableMeta<*>.set(name: Name, value: Any?) {
}
}
operator fun MutableMeta<*>.set(name: NameToken, value: Any?) = set(name.asName(), value)
public operator fun MutableMeta<*>.set(name: NameToken, value: Any?): Unit =
set(name.asName(), value)
operator fun MutableMeta<*>.set(key: String, value: Any?) = set(key.toName(), value)
public operator fun MutableMeta<*>.set(key: String, value: Any?): Unit =
set(key.toName(), value)
operator fun MutableMeta<*>.set(key: String, index: String, value: Any?) = set(key.toName().withIndex(index), value)
public operator fun MutableMeta<*>.set(key: String, index: String, value: Any?): Unit =
set(key.toName().withIndex(index), value)
/**
* Update existing mutable node with another node. The rules are following:
@ -117,7 +120,7 @@ operator fun MutableMeta<*>.set(key: String, index: String, value: Any?) = set(k
* * 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
*/
fun <M : MutableMeta<M>> M.update(meta: Meta) {
public fun <M : MutableMeta<M>> M.update(meta: Meta) {
meta.items.forEach { entry ->
when (val value = entry.value) {
is MetaItem.ValueItem -> setValue(entry.key.asName(), value.value)
@ -129,7 +132,7 @@ fun <M : MutableMeta<M>> M.update(meta: Meta) {
/* Same name siblings generation */
fun MutableMeta<*>.setIndexedItems(
public fun MutableMeta<*>.setIndexedItems(
name: Name,
items: Iterable<MetaItem<*>>,
indexFactory: (MetaItem<*>, index: Int) -> String = { _, index -> index.toString() }
@ -143,7 +146,7 @@ fun MutableMeta<*>.setIndexedItems(
}
}
fun MutableMeta<*>.setIndexed(
public fun MutableMeta<*>.setIndexed(
name: Name,
metas: Iterable<Meta>,
indexFactory: (Meta, index: Int) -> String = { _, index -> index.toString() }
@ -151,13 +154,13 @@ fun MutableMeta<*>.setIndexed(
setIndexedItems(name, metas.map { MetaItem.NodeItem(it) }) { item, index -> indexFactory(item.node!!, index) }
}
operator fun MutableMeta<*>.set(name: Name, metas: Iterable<Meta>): Unit = setIndexed(name, metas)
operator fun MutableMeta<*>.set(name: String, metas: Iterable<Meta>): Unit = setIndexed(name.toName(), metas)
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)
/**
* Append the node with a same-name-sibling, automatically generating numerical index
*/
fun <M : MutableMeta<M>> M.append(name: Name, value: Any?) {
public fun <M : MutableMeta<M>> M.append(name: Name, value: Any?) {
require(!name.isEmpty()) { "Name could not be empty for append operation" }
val newIndex = name.lastOrNull()!!.index
if (newIndex != null) {
@ -168,13 +171,13 @@ fun <M : MutableMeta<M>> M.append(name: Name, value: Any?) {
}
}
fun <M : MutableMeta<M>> M.append(name: String, value: Any?) = append(name.toName(), value)
public fun <M : MutableMeta<M>> M.append(name: String, value: Any?): Unit = append(name.toName(), value)
/**
* Apply existing node with given [builder] or create a new element with it.
*/
@DFExperimental
fun <M : AbstractMutableMeta<M>> M.edit(name: Name, builder: M.() -> Unit) {
public fun <M : AbstractMutableMeta<M>> M.edit(name: Name, builder: M.() -> Unit) {
val item = when (val existingItem = get(name)) {
null -> empty().also { set(name, it) }
is MetaItem.NodeItem<M> -> existingItem.node

View File

@ -8,23 +8,21 @@ import hep.dataforge.names.plus
/**
* A base for delegate-based or descriptor-based scheme. [Scheme] has an empty constructor to simplify usage from [Specification].
*/
open class Scheme() : Configurable, Described, MetaRepr {
constructor(config: Config, defaultProvider: (Name) -> MetaItem<*>?) : this() {
this.config = config
this.defaultProvider = defaultProvider
}
public open class Scheme(
config: Config,
defaultProvider: ItemProvider,
) : Configurable, Described, MetaRepr {
//constructor(config: Config, default: Meta) : this(config, { default[it] })
constructor(config: Config) : this(config, { null })
final override var config: Config = Config()
override var config: Config = config
internal set
var defaultProvider: (Name) -> MetaItem<*>? = { null }
public var defaultProvider: ItemProvider = defaultProvider
internal set
public constructor() : this(Config(), ItemProvider { null })
override fun getDefaultItem(name: Name): MetaItem<*>? {
return defaultProvider(name) ?: descriptor?.get(name)?.defaultItem()
return defaultProvider.getItem(name) ?: descriptor?.get(name)?.defaultItem()
}
/**
@ -32,7 +30,7 @@ open class Scheme() : Configurable, Described, MetaRepr {
* values if default value is unavailable.
* Values from [defaultProvider] completely replace
*/
open val defaultLayer: Meta get() = DefaultLayer(Name.EMPTY)
public open val defaultLayer: Meta get() = DefaultLayer(Name.EMPTY)
override fun toMeta(): Laminate = Laminate(config, defaultLayer)
@ -50,38 +48,45 @@ open class Scheme() : Configurable, Described, MetaRepr {
}
}
inline operator fun <T : Scheme> T.invoke(block: T.() -> Unit) = apply(block)
/**
* A shortcut to edit a [Scheme] object in-place
*/
public inline operator fun <T : Scheme> T.invoke(block: T.() -> Unit): T = apply(block)
/**
* A specification for simplified generation of wrappers
*/
open class SchemeSpec<T : Scheme>(val builder: () -> T) :
Specification<T> {
override fun empty(): T = builder()
public open class SchemeSpec<T : Scheme>(
private val builder: (config: Config, defaultProvider: ItemProvider) -> T,
) : Specification<T> {
override fun wrap(config: Config, defaultProvider: (Name) -> MetaItem<*>?): T {
return empty().apply {
public constructor(emptyBuilder: () -> T) : this({ config: Config, defaultProvider: ItemProvider ->
emptyBuilder().apply {
this.config = config
this.defaultProvider = defaultProvider
}
}
})
override fun empty(): T = builder(Config(), ItemProvider.EMPTY)
override fun wrap(config: Config, defaultProvider: ItemProvider): T = builder(config, defaultProvider)
@Suppress("OVERRIDE_BY_INLINE")
final override inline operator fun invoke(action: T.() -> Unit) = empty().apply(action)
final override inline operator fun invoke(action: T.() -> Unit): T = empty().apply(action)
}
/**
* A scheme that uses [Meta] as a default layer
*/
open class MetaScheme(
val meta: Meta,
public open class MetaScheme(
private val meta: Meta,
override val descriptor: NodeDescriptor? = null,
config: Config = Config()
config: Config = Config(),
) : Scheme(config, meta::get) {
override val defaultLayer: Meta get() = Laminate(meta, descriptor?.defaultItem().node)
}
fun Meta.asScheme() =
MetaScheme(this)
public fun Meta.asScheme(): MetaScheme = MetaScheme(this)
fun <T : Configurable> Meta.toScheme(spec: Specification<T>, block: T.() -> Unit) = spec.wrap(this).apply(block)
public fun <T : Configurable> Meta.toScheme(spec: Specification<T>, block: T.() -> Unit): T =
spec.wrap(this).apply(block)

View File

@ -1,6 +1,5 @@
package hep.dataforge.meta
import hep.dataforge.names.Name
import kotlin.jvm.JvmName
/**
@ -8,32 +7,32 @@ import kotlin.jvm.JvmName
* By convention [Scheme] companion should inherit this class
*
*/
interface Specification<T : Configurable> {
fun empty() = wrap()
public interface Specification<T : Configurable> {
public fun empty(): T = wrap()
/**
* Wrap generic configuration producing instance of desired type
*/
fun wrap(config: Config = Config(), defaultProvider: (Name) -> MetaItem<*>? = { null }): T
public fun wrap(config: Config = Config(), defaultProvider: ItemProvider = ItemProvider{ null }): T
operator fun invoke(action: T.() -> Unit): T = empty().apply(action)
public operator fun invoke(action: T.() -> Unit): T = empty().apply(action)
}
/**
* Update given configuration using given type as a builder
*/
fun <T : Configurable> Specification<T>.update(config: Config, action: T.() -> Unit): T = wrap(config).apply(action)
public fun <T : Configurable> Specification<T>.update(config: Config, action: T.() -> Unit): T = wrap(config).apply(action)
/**
* Wrap a configuration using static meta as default
*/
fun <T : Configurable> Specification<T>.wrap(config: Config = Config(), default: Meta = Meta.EMPTY): T =
wrap(config) { default[it] }
public fun <T : Configurable> Specification<T>.wrap(config: Config = Config(), default: Meta = Meta.EMPTY): T =
wrap(config, default)
/**
* Wrap a configuration using static meta as default
*/
fun <T : Configurable> Specification<T>.wrap(source: Meta): T {
public fun <T : Configurable> Specification<T>.wrap(source: Meta): T {
val default = source.seal()
return wrap(source.asConfig(), default)
}
@ -42,26 +41,26 @@ fun <T : Configurable> Specification<T>.wrap(source: Meta): T {
/**
* Apply specified configuration to configurable
*/
fun <T : Configurable, C : Configurable, S : Specification<C>> T.configure(spec: S, action: C.() -> Unit) =
public fun <T : Configurable, C : Configurable, S : Specification<C>> T.configure(spec: S, action: C.() -> Unit): T =
apply { spec.update(config, action) }
/**
* Update configuration using given specification
*/
fun <C : Configurable, S : Specification<C>> Configurable.update(spec: S, action: C.() -> Unit) =
public fun <C : Configurable, S : Specification<C>> Configurable.update(spec: S, action: C.() -> Unit): Configurable =
apply { spec.update(config, action) }
/**
* Create a style based on given specification
*/
fun <C : Configurable, S : Specification<C>> S.createStyle(action: C.() -> Unit): Meta =
public fun <C : Configurable, S : Specification<C>> S.createStyle(action: C.() -> Unit): Meta =
Config().also { update(it, action) }
fun <T : Configurable> MetaItem<*>.spec(spec: Specification<T>): T? = node?.let {
public fun <T : Configurable> MetaItem<*>.spec(spec: Specification<T>): T? = node?.let {
spec.wrap(
Config(), it
)
}
@JvmName("configSpec")
fun <T : Configurable> MetaItem<Config>.spec(spec: Specification<T>): T? = node?.let { spec.wrap(it) }
public fun <T : Configurable> MetaItem<Config>.spec(spec: Specification<T>): T? = node?.let { spec.wrap(it) }

View File

@ -4,8 +4,8 @@ package hep.dataforge.meta
* General marker for dataforge builders
*/
@DslMarker
annotation class DFBuilder
public annotation class DFBuilder
@RequiresOptIn(level = RequiresOptIn.Level.WARNING)
@Retention(AnnotationRetention.BINARY)
annotation class DFExperimental
public annotation class DFExperimental

View File

@ -6,7 +6,7 @@ import hep.dataforge.names.*
* 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.
*/
fun Meta.getIndexed(name: Name): Map<String?, MetaItem<*>> {
public fun Meta.getIndexed(name: Name): Map<String?, MetaItem<*>> {
val root = when (name.length) {
0 -> error("Can't use empty name for 'getIndexed'")
1 -> this
@ -18,19 +18,19 @@ fun Meta.getIndexed(name: Name): Map<String?, MetaItem<*>> {
root.items.filter { it.key.body == body }.mapKeys { it.key.index }
} else {
val regex = index.toRegex()
root.items
.filter { it.key.body == body && (regex.matches(it.key.index ?: "")) }
root.items.filter { it.key.body == body && (regex.matches(it.key.index ?: "")) }
.mapKeys { it.key.index }
}
}
fun Meta.getIndexed(name: String): Map<String?, MetaItem<*>> = this@getIndexed.getIndexed(name.toName())
public fun Meta.getIndexed(name: String): Map<String?, MetaItem<*>> = this@getIndexed.getIndexed(name.toName())
/**
* Get all items matching given name.
*/
@Suppress("UNCHECKED_CAST")
fun <M : MetaNode<M>> M.getIndexed(name: Name): Map<String, MetaItem<M>> =
public fun <M : MetaNode<M>> M.getIndexed(name: Name): Map<String, MetaItem<M>> =
(this as Meta).getIndexed(name) as Map<String, MetaItem<M>>
fun <M : MetaNode<M>> M.getIndexed(name: String): Map<String, MetaItem<M>> = getIndexed(name.toName())
public fun <M : MetaNode<M>> M.getIndexed(name: String): Map<String, MetaItem<M>> =
getIndexed(name.toName())

View File

@ -8,7 +8,7 @@ import hep.dataforge.values.Value
/**
* Convert meta to map of maps
*/
fun Meta.toMap(descriptor: NodeDescriptor? = null): Map<String, Any?> {
public fun Meta.toMap(descriptor: NodeDescriptor? = null): Map<String, Any?> {
return items.entries.associate { (token, item) ->
token.toString() to when (item) {
is MetaItem.NodeItem -> item.node.toMap()
@ -22,7 +22,7 @@ fun Meta.toMap(descriptor: NodeDescriptor? = null): Map<String, Any?> {
* All other values will be converted to values.
*/
@DFExperimental
fun Map<String, Any?>.toMeta(descriptor: NodeDescriptor? = null): Meta = Meta {
public fun Map<String, Any?>.toMeta(descriptor: NodeDescriptor? = null): Meta = Meta {
@Suppress("UNCHECKED_CAST")
fun toItem(value: Any?): MetaItem<*> = when (value) {
is MetaItem<*> -> value

View File

@ -1,76 +0,0 @@
package hep.dataforge.meta
import kotlinx.serialization.InternalSerializationApi
import kotlinx.serialization.builtins.DoubleArraySerializer
import kotlinx.serialization.builtins.serializer
import kotlinx.serialization.descriptors.*
import kotlinx.serialization.encoding.CompositeDecoder
import kotlinx.serialization.encoding.CompositeEncoder
import kotlinx.serialization.encoding.Decoder
import kotlinx.serialization.encoding.Encoder
import kotlinx.serialization.json.Json
fun ClassSerialDescriptorBuilder.boolean(name: String, isOptional: Boolean = false, vararg annotations: Annotation) =
element(name, Boolean.serializer().descriptor, isOptional = isOptional, annotations = annotations.toList())
fun ClassSerialDescriptorBuilder.string(name: String, isOptional: Boolean = false, vararg annotations: Annotation) =
element(name, String.serializer().descriptor, isOptional = isOptional, annotations = annotations.toList())
fun ClassSerialDescriptorBuilder.int(name: String, isOptional: Boolean = false, vararg annotations: Annotation) =
element(name, Int.serializer().descriptor, isOptional = isOptional, annotations = annotations.toList())
fun ClassSerialDescriptorBuilder.double(name: String, isOptional: Boolean = false, vararg annotations: Annotation) =
element(name, Double.serializer().descriptor, isOptional = isOptional, annotations = annotations.toList())
fun ClassSerialDescriptorBuilder.float(name: String, isOptional: Boolean = false, vararg annotations: Annotation) =
element(name, Float.serializer().descriptor, isOptional = isOptional, annotations = annotations.toList())
fun ClassSerialDescriptorBuilder.long(name: String, isOptional: Boolean = false, vararg annotations: Annotation) =
element(name, Long.serializer().descriptor, isOptional = isOptional, annotations = annotations.toList())
fun ClassSerialDescriptorBuilder.doubleArray(
name: String,
isOptional: Boolean = false,
vararg annotations: Annotation,
) =
element(name, DoubleArraySerializer().descriptor, isOptional = isOptional, annotations = annotations.toList())
@OptIn(InternalSerializationApi::class)
inline fun <reified E : Enum<E>> ClassSerialDescriptorBuilder.enum(
name: String,
isOptional: Boolean = false,
vararg annotations: Annotation,
) {
val enumDescriptor = buildSerialDescriptor(serialName, SerialKind.ENUM) {
enumValues<E>().forEach {
val fqn = "$serialName.${it.name}"
val enumMemberDescriptor = buildSerialDescriptor(fqn, StructureKind.OBJECT)
element(it.name, enumMemberDescriptor)
}
}
element(name, enumDescriptor, isOptional = isOptional, annotations = annotations.toList())
}
@DFExperimental
inline fun <R> Decoder.decodeStructure(
desc: SerialDescriptor,
crossinline block: CompositeDecoder.() -> R,
): R {
val decoder = beginStructure(desc)
val res = decoder.block()
decoder.endStructure(desc)
return res
}
@DFExperimental
inline fun Encoder.encodeStructure(
desc: SerialDescriptor,
block: CompositeEncoder.() -> Unit,
) {
val encoder = beginStructure(desc)
encoder.block()
encoder.endStructure(desc)
}
val JSON_PRETTY = Json { prettyPrint = true; useArrayPolymorphism = true }
val JSON_PLAIN = Json { prettyPrint = false; useArrayPolymorphism = true }

View File

@ -10,7 +10,7 @@ import kotlinx.serialization.Serializable
*
*/
@Serializable
enum class ValueType {
public enum class ValueType {
NUMBER, STRING, BOOLEAN, NULL
}
@ -19,44 +19,44 @@ enum class ValueType {
*
* Value can represent a list of value objects.
*/
interface Value {
@Serializable(ValueSerializer::class)
public interface Value {
/**
* Get raw value of this value
*/
val value: Any?
public val value: Any?
/**
* The type of the value
*/
val type: ValueType
public val type: ValueType
/**
* get this value represented as Number
*/
val number: Number
public val number: Number
/**
* get this value represented as String
*/
val string: String
public val string: String
/**
* get this value represented as List
*/
val list: List<Value>
get() = if (this == Null) emptyList() else listOf(this)
public val list: List<Value> get() = if (this == Null) emptyList() else listOf(this)
override fun equals(other: Any?): Boolean
override fun hashCode(): Int
companion object {
const val TYPE = "value"
public companion object {
public const val TYPE: String = "value"
/**
* Convert object to value
*/
fun of(value: Any?): Value {
public fun of(value: Any?): Value {
return when (value) {
null -> Null
is Value -> value
@ -89,7 +89,7 @@ interface Value {
/**
* A singleton null value
*/
object Null : Value {
public object Null : Value {
override val value: Any? get() = null
override val type: ValueType get() = ValueType.NULL
override val number: Number get() = Double.NaN
@ -104,7 +104,7 @@ object Null : Value {
/**
* Singleton true value
*/
object True : Value {
public object True : Value {
override val value: Any? get() = true
override val type: ValueType get() = ValueType.BOOLEAN
override val number: Number get() = 1.0
@ -119,7 +119,7 @@ object True : Value {
/**
* Singleton false value
*/
object False : Value {
public object False : Value {
override val value: Any? get() = false
override val type: ValueType get() = ValueType.BOOLEAN
override val number: Number get() = -1.0
@ -131,7 +131,7 @@ object False : Value {
override fun hashCode(): Int = -1
}
class NumberValue(override val number: Number) : Value {
public class NumberValue(override val number: Number) : Value {
override val value: Any? get() = number
override val type: ValueType get() = ValueType.NUMBER
override val string: String get() = number.toString()
@ -154,7 +154,7 @@ class NumberValue(override val number: Number) : Value {
override fun toString(): String = value.toString()
}
class StringValue(override val string: String) : Value {
public class StringValue(override val string: String) : Value {
override val value: Any? get() = string
override val type: ValueType get() = ValueType.STRING
override val number: Number get() = string.toDouble()
@ -168,7 +168,7 @@ class StringValue(override val string: String) : Value {
override fun toString(): String = "\"${value.toString()}\""
}
class EnumValue<E : Enum<*>>(override val value: E) : Value {
public class EnumValue<E : Enum<*>>(override val value: E) : Value {
override val type: ValueType get() = ValueType.STRING
override val number: Number get() = value.ordinal
override val string: String get() = value.name
@ -182,7 +182,7 @@ class EnumValue<E : Enum<*>>(override val value: E) : Value {
override fun toString(): String = value.toString()
}
class ListValue(override val list: List<Value>) : Value {
public class ListValue(override val list: List<Value>) : Value {
init {
require(list.isNotEmpty()) { "Can't create list value from empty list" }
}
@ -210,34 +210,34 @@ class ListValue(override val list: List<Value>) : Value {
}
fun Number.asValue(): Value = NumberValue(this)
public fun Number.asValue(): Value = NumberValue(this)
fun Boolean.asValue(): Value = if (this) True else False
public fun Boolean.asValue(): Value = if (this) True else False
fun String.asValue(): Value = StringValue(this)
public fun String.asValue(): Value = StringValue(this)
fun Iterable<Value>.asValue(): Value {
public fun Iterable<Value>.asValue(): Value {
val list = toList()
return if (list.isEmpty()) Null else ListValue(this.toList())
}
fun IntArray.asValue(): Value = if (isEmpty()) Null else ListValue(map { NumberValue(it) })
public fun IntArray.asValue(): Value = if (isEmpty()) Null else ListValue(map { NumberValue(it) })
fun LongArray.asValue(): Value = if (isEmpty()) Null else ListValue(map { NumberValue(it) })
public fun LongArray.asValue(): Value = if (isEmpty()) Null else ListValue(map { NumberValue(it) })
fun ShortArray.asValue(): Value = if (isEmpty()) Null else ListValue(map { NumberValue(it) })
public fun ShortArray.asValue(): Value = if (isEmpty()) Null else ListValue(map { NumberValue(it) })
fun FloatArray.asValue(): Value = if (isEmpty()) Null else ListValue(map { NumberValue(it) })
public fun FloatArray.asValue(): Value = if (isEmpty()) Null else ListValue(map { NumberValue(it) })
fun ByteArray.asValue(): Value = if (isEmpty()) Null else ListValue(map { NumberValue(it) })
public fun ByteArray.asValue(): Value = if (isEmpty()) Null else ListValue(map { NumberValue(it) })
fun <E : Enum<E>> E.asValue(): Value = EnumValue(this)
public fun <E : Enum<E>> E.asValue(): Value = EnumValue(this)
/**
* Create Value from String using closest match conversion
*/
fun String.parseValue(): Value {
public fun String.parseValue(): Value {
//Trying to get integer
if (isEmpty() || this == Null.string) {

View File

@ -1,25 +1,25 @@
package hep.dataforge.values
import hep.dataforge.meta.boolean
import hep.dataforge.meta.enum
import hep.dataforge.meta.string
import kotlinx.serialization.ExperimentalSerializationApi
import kotlinx.serialization.KSerializer
import kotlinx.serialization.Serializer
import kotlinx.serialization.builtins.ListSerializer
import kotlinx.serialization.descriptors.SerialDescriptor
import kotlinx.serialization.descriptors.buildClassSerialDescriptor
import kotlinx.serialization.descriptors.element
import kotlinx.serialization.encoding.Decoder
import kotlinx.serialization.encoding.Encoder
@OptIn(ExperimentalSerializationApi::class)
@Serializer(Value::class)
object ValueSerializer : KSerializer<Value> {
public object ValueSerializer : KSerializer<Value> {
private val listSerializer by lazy { ListSerializer(ValueSerializer) }
override val descriptor: SerialDescriptor =
buildClassSerialDescriptor("hep.dataforge.values.Value") {
boolean("isList")
enum<ValueType>("valueType")
string("value")
element<Boolean>("isList")
element<ValueType>("valueType")
element<String>("value")
}
private fun Decoder.decodeValue(): Value {

View File

@ -4,7 +4,7 @@ package hep.dataforge.values
/**
* A value built from string which content and type are parsed on-demand
*/
class LazyParsedValue(override val string: String) : Value {
public class LazyParsedValue(override val string: String) : Value {
private val parsedValue by lazy { string.parseValue() }
override val value: Any? get() = parsedValue.value
@ -18,12 +18,12 @@ class LazyParsedValue(override val string: String) : Value {
override fun hashCode(): Int = string.hashCode()
}
fun String.lazyParseValue(): LazyParsedValue = LazyParsedValue(this)
public fun String.lazyParseValue(): LazyParsedValue = LazyParsedValue(this)
/**
* A performance optimized version of list value for doubles
*/
class DoubleArrayValue(override val value: DoubleArray) : Value {
public class DoubleArrayValue(override val value: DoubleArray) : Value {
override val type: ValueType get() = ValueType.NUMBER
override val number: Double get() = value.first()
override val string: String get() = value.first().toString()
@ -46,4 +46,4 @@ class DoubleArrayValue(override val value: DoubleArray) : Value {
override fun toString(): String = list.joinToString (prefix = "[", postfix = "]")
}
fun DoubleArray.asValue(): Value = if(isEmpty()) Null else DoubleArrayValue(this)
public fun DoubleArray.asValue(): Value = if(isEmpty()) Null else DoubleArrayValue(this)

View File

@ -1,32 +1,33 @@
package hep.dataforge.values
import hep.dataforge.meta.Meta
import hep.dataforge.meta.MetaBuilder
/**
* Check if value is null
*/
fun Value.isNull(): Boolean = this == Null
public fun Value.isNull(): Boolean = this == Null
/**
* Check if value is list
*/
fun Value.isList(): Boolean = this.list.size > 1
public fun Value.isList(): Boolean = this.list.size > 1
val Value.boolean
public val Value.boolean
get() = this == True
|| this.list.firstOrNull() == True
|| (type == ValueType.STRING && string.toBoolean())
val Value.int get() = number.toInt()
val Value.double get() = number.toDouble()
val Value.float get() = number.toFloat()
val Value.short get() = number.toShort()
val Value.long get() = number.toLong()
public val Value.int: Int get() = number.toInt()
public val Value.double: Double get() = number.toDouble()
public val Value.float: Float get() = number.toFloat()
public val Value.short: Short get() = number.toShort()
public val Value.long: Long get() = number.toLong()
val Value.stringList: List<String> get() = list.map { it.string }
public val Value.stringList: List<String> get() = list.map { it.string }
val Value.doubleArray: DoubleArray
public val Value.doubleArray: DoubleArray
get() = if (this is DoubleArrayValue) {
value
} else {
@ -34,4 +35,4 @@ val Value.doubleArray: DoubleArray
}
fun Value.toMeta() = Meta { Meta.VALUE_KEY put this }
public fun Value.toMeta(): MetaBuilder = Meta { Meta.VALUE_KEY put this }

View File

@ -1,18 +1,17 @@
package hep.dataforge.meta
import hep.dataforge.names.Name
import kotlin.test.Test
import kotlin.test.assertEquals
class SpecificationTest {
class TestStyled(config: Config, defaultProvider: (Name) -> MetaItem<*>?) :
class TestStyled(config: Config, defaultProvider: ItemProvider) :
Scheme(config, defaultProvider) {
var list by numberList(1, 2, 3)
companion object : Specification<TestStyled> {
override fun wrap(
config: Config,
defaultProvider: (Name) -> MetaItem<*>?
defaultProvider: ItemProvider
): TestStyled = TestStyled(config, defaultProvider)
}
}

View File

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