Merge pull request #58 from mipt-npm/dev

0.2.0 release
This commit is contained in:
Alexander Nozik 2020-11-28 10:29:09 +03:00 committed by GitHub
commit 6912f26291
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
169 changed files with 5535 additions and 3074 deletions
.gitignoreCHANGELOG.mdbuild.gradle.kts
dataforge-context
dataforge-data
dataforge-io
dataforge-meta

3
.gitignore vendored

@ -6,5 +6,4 @@ out/
build/
!gradle-wrapper.jar
gradle.properties
!gradle-wrapper.jar

39
CHANGELOG.md Normal file

@ -0,0 +1,39 @@
# Changelog
## [Unreleased]
### Added
### Changed
### Deprecated
### Removed
### Fixed
### Security
## [0.2.0]
### Added
### Changed
- Context content resolution refactor
- Kotlin 1.4.10 (build tools 0.6.0)
- Empty query in Name is null instead of ""
- Provider provides an empty map instead of error by default
- Hidden delegates hierarchy in favor of stdlib properties
- Removed io depdendency from `dataforge-output`. Replaced Output by Appendable.
- Configurable is no longer MutableItemProvider. All functionality moved to Scheme.
### Deprecated
- Context activation API
- TextRenderer
### Removed
- Functional server prototype
- `dataforge-output` module
### Fixed
- Global context CoroutineScope resolution
- Library mode compliance
### Security

@ -1,27 +1,24 @@
plugins {
val toolsVersion = "0.5.0"
id("scientifik.mpp") version toolsVersion apply false
id("scientifik.jvm") version toolsVersion apply false
id("scientifik.publish") version toolsVersion apply false
id("org.jetbrains.dokka") version "0.10.1"
id("ru.mipt.npm.project")
}
val dataforgeVersion by extra("0.1.8")
val dataforgeVersion by extra("0.2.0")
val bintrayRepo by extra("dataforge")
val githubProject by extra("dataforge-core")
val spaceRepo by extra("https://maven.jetbrains.space/mipt-npm/p/df/maven")
allprojects {
group = "hep.dataforge"
version = dataforgeVersion
apply<org.jetbrains.dokka.gradle.DokkaPlugin>()
repositories {
mavenLocal()
}
}
subprojects {
apply(plugin = "scientifik.publish")
apply(plugin = "org.jetbrains.dokka")
apply(plugin = "ru.mipt.npm.publish")
}

@ -0,0 +1,334 @@
public abstract class hep/dataforge/context/AbstractPlugin : hep/dataforge/context/Plugin {
public fun <init> ()V
public fun <init> (Lhep/dataforge/meta/Meta;)V
public synthetic fun <init> (Lhep/dataforge/meta/Meta;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
public fun attach (Lhep/dataforge/context/Context;)V
public fun content (Ljava/lang/String;)Ljava/util/Map;
public synthetic fun dependsOn ()Ljava/util/Collection;
public final fun dependsOn ()Ljava/util/List;
public fun detach ()V
public fun getContext ()Lhep/dataforge/context/Context;
public fun getDefaultChainTarget ()Ljava/lang/String;
public fun getDefaultTarget ()Ljava/lang/String;
public fun getLogger ()Lmu/KLogger;
public fun getMeta ()Lhep/dataforge/meta/Meta;
public fun getName ()Lhep/dataforge/names/Name;
protected final fun require (Lhep/dataforge/context/PluginFactory;)Lkotlin/properties/ReadOnlyProperty;
public fun toMeta ()Lhep/dataforge/meta/Meta;
}
public final class hep/dataforge/context/AbstractPluginKt {
public static final fun toMap (Ljava/util/Collection;)Ljava/util/Map;
}
public final class hep/dataforge/context/ClassLoaderPlugin : hep/dataforge/context/AbstractPlugin {
public static final field Companion Lhep/dataforge/context/ClassLoaderPlugin$Companion;
public fun <init> (Ljava/lang/ClassLoader;)V
public fun getTag ()Lhep/dataforge/context/PluginTag;
public final fun services (Lkotlin/reflect/KClass;)Lkotlin/sequences/Sequence;
}
public final class hep/dataforge/context/ClassLoaderPlugin$Companion {
public final fun getDEFAULT ()Lhep/dataforge/context/ClassLoaderPlugin;
}
public final class hep/dataforge/context/ClassLoaderPluginKt {
public static final fun getClassLoaderPlugin (Lhep/dataforge/context/Context;)Lhep/dataforge/context/ClassLoaderPlugin;
}
public class hep/dataforge/context/Context : hep/dataforge/context/Named, hep/dataforge/meta/MetaRepr, hep/dataforge/provider/Provider, kotlinx/coroutines/CoroutineScope {
public static final field Companion Lhep/dataforge/context/Context$Companion;
public static final field PROPERTY_TARGET Ljava/lang/String;
public fun <init> (Lhep/dataforge/names/Name;Lhep/dataforge/context/Context;Lhep/dataforge/meta/Meta;Ljava/util/Set;)V
public synthetic fun <init> (Lhep/dataforge/names/Name;Lhep/dataforge/context/Context;Lhep/dataforge/meta/Meta;Ljava/util/Set;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
public fun close ()V
public fun content (Ljava/lang/String;)Ljava/util/Map;
public final fun content (Ljava/lang/String;Z)Ljava/util/Map;
public fun getCoroutineContext ()Lkotlin/coroutines/CoroutineContext;
public fun getDefaultChainTarget ()Ljava/lang/String;
public fun getDefaultTarget ()Ljava/lang/String;
public final fun getLogger ()Lmu/KLogger;
public final fun getName ()Lhep/dataforge/names/Name;
public final fun getParent ()Lhep/dataforge/context/Context;
public final fun getPlugins ()Lhep/dataforge/context/PluginManager;
public fun toMeta ()Lhep/dataforge/meta/Meta;
}
public final class hep/dataforge/context/Context$Companion {
}
public abstract interface class hep/dataforge/context/ContextAware {
public abstract fun getContext ()Lhep/dataforge/context/Context;
public abstract fun getLogger ()Lmu/KLogger;
}
public final class hep/dataforge/context/ContextAware$DefaultImpls {
public static fun getLogger (Lhep/dataforge/context/ContextAware;)Lmu/KLogger;
}
public final class hep/dataforge/context/ContextBuilder {
public fun <init> ()V
public fun <init> (Lhep/dataforge/context/Context;Ljava/lang/String;)V
public synthetic fun <init> (Lhep/dataforge/context/Context;Ljava/lang/String;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
public final fun build ()Lhep/dataforge/context/Context;
public final fun getName ()Ljava/lang/String;
public final fun plugin (Lhep/dataforge/context/Plugin;)V
public final fun plugin (Lhep/dataforge/context/PluginFactory;Lkotlin/jvm/functions/Function1;)V
public final fun plugin (Lhep/dataforge/context/PluginTag;Lkotlin/jvm/functions/Function1;)V
public final fun plugin (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Lkotlin/jvm/functions/Function1;)V
public static synthetic fun plugin$default (Lhep/dataforge/context/ContextBuilder;Lhep/dataforge/context/PluginFactory;Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)V
public static synthetic fun plugin$default (Lhep/dataforge/context/ContextBuilder;Lhep/dataforge/context/PluginTag;Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)V
public static synthetic fun plugin$default (Lhep/dataforge/context/ContextBuilder;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)V
public final fun properties (Lkotlin/jvm/functions/Function1;)V
public final fun setName (Ljava/lang/String;)V
}
public abstract interface class hep/dataforge/context/Factory {
public abstract fun invoke (Lhep/dataforge/meta/Meta;Lhep/dataforge/context/Context;)Ljava/lang/Object;
}
public final class hep/dataforge/context/Factory$DefaultImpls {
public static synthetic fun invoke$default (Lhep/dataforge/context/Factory;Lhep/dataforge/meta/Meta;Lhep/dataforge/context/Context;ILjava/lang/Object;)Ljava/lang/Object;
}
public final class hep/dataforge/context/Global : hep/dataforge/context/Context {
public static final field INSTANCE Lhep/dataforge/context/Global;
public fun close ()V
public final fun context (Ljava/lang/String;Lhep/dataforge/context/Context;Lkotlin/jvm/functions/Function1;)Lhep/dataforge/context/Context;
public static synthetic fun context$default (Lhep/dataforge/context/Global;Ljava/lang/String;Lhep/dataforge/context/Context;Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)Lhep/dataforge/context/Context;
public final fun getContext (Ljava/lang/String;)Lhep/dataforge/context/Context;
public fun getCoroutineContext ()Lkotlin/coroutines/CoroutineContext;
}
public abstract interface class hep/dataforge/context/Named {
public static final field Companion Lhep/dataforge/context/Named$Companion;
public abstract fun getName ()Lhep/dataforge/names/Name;
}
public final class hep/dataforge/context/Named$Companion {
public final fun nameOf (Ljava/lang/Object;)Lhep/dataforge/names/Name;
}
public final class hep/dataforge/context/NamedKt {
public static final fun isAnonymous (Lhep/dataforge/context/Named;)Z
}
public abstract interface class hep/dataforge/context/Plugin : hep/dataforge/context/ContextAware, hep/dataforge/context/Named, hep/dataforge/meta/MetaRepr, hep/dataforge/provider/Provider {
public static final field Companion Lhep/dataforge/context/Plugin$Companion;
public static final field TARGET Ljava/lang/String;
public abstract fun attach (Lhep/dataforge/context/Context;)V
public abstract fun dependsOn ()Ljava/util/Collection;
public abstract fun detach ()V
public abstract fun getMeta ()Lhep/dataforge/meta/Meta;
public abstract fun getName ()Lhep/dataforge/names/Name;
public abstract fun getTag ()Lhep/dataforge/context/PluginTag;
public abstract fun toMeta ()Lhep/dataforge/meta/Meta;
}
public final class hep/dataforge/context/Plugin$Companion {
public static final field TARGET Ljava/lang/String;
}
public final class hep/dataforge/context/Plugin$DefaultImpls {
public static fun content (Lhep/dataforge/context/Plugin;Ljava/lang/String;)Ljava/util/Map;
public static fun getDefaultChainTarget (Lhep/dataforge/context/Plugin;)Ljava/lang/String;
public static fun getDefaultTarget (Lhep/dataforge/context/Plugin;)Ljava/lang/String;
public static fun getLogger (Lhep/dataforge/context/Plugin;)Lmu/KLogger;
public static fun getName (Lhep/dataforge/context/Plugin;)Lhep/dataforge/names/Name;
public static fun toMeta (Lhep/dataforge/context/Plugin;)Lhep/dataforge/meta/Meta;
}
public abstract interface class hep/dataforge/context/PluginFactory : hep/dataforge/context/Factory {
public static final field Companion Lhep/dataforge/context/PluginFactory$Companion;
public static final field TYPE Ljava/lang/String;
public abstract fun getTag ()Lhep/dataforge/context/PluginTag;
public abstract fun getType ()Lkotlin/reflect/KClass;
}
public final class hep/dataforge/context/PluginFactory$Companion {
public static final field TYPE Ljava/lang/String;
}
public final class hep/dataforge/context/PluginManager : hep/dataforge/context/ContextAware, java/lang/Iterable, kotlin/jvm/internal/markers/KMappedMarker {
public fun <init> (Lhep/dataforge/context/Context;Ljava/util/Set;)V
public final fun fetch (Lhep/dataforge/context/PluginFactory;ZLhep/dataforge/meta/Meta;)Lhep/dataforge/context/Plugin;
public final fun fetch (Lhep/dataforge/context/PluginFactory;ZLkotlin/jvm/functions/Function1;)Lhep/dataforge/context/Plugin;
public static synthetic fun fetch$default (Lhep/dataforge/context/PluginManager;Lhep/dataforge/context/PluginFactory;ZLhep/dataforge/meta/Meta;ILjava/lang/Object;)Lhep/dataforge/context/Plugin;
public static synthetic fun fetch$default (Lhep/dataforge/context/PluginManager;Lhep/dataforge/context/PluginFactory;ZLkotlin/jvm/functions/Function1;ILjava/lang/Object;)Lhep/dataforge/context/Plugin;
public final fun find (ZLkotlin/jvm/functions/Function1;)Lhep/dataforge/context/Plugin;
public static synthetic fun find$default (Lhep/dataforge/context/PluginManager;ZLkotlin/jvm/functions/Function1;ILjava/lang/Object;)Lhep/dataforge/context/Plugin;
public final fun get (Lhep/dataforge/context/PluginTag;Z)Lhep/dataforge/context/Plugin;
public final fun get (Lkotlin/reflect/KClass;Lhep/dataforge/context/PluginTag;Z)Ljava/lang/Object;
public static synthetic fun get$default (Lhep/dataforge/context/PluginManager;Lhep/dataforge/context/PluginTag;ZILjava/lang/Object;)Lhep/dataforge/context/Plugin;
public static synthetic fun get$default (Lhep/dataforge/context/PluginManager;Lkotlin/reflect/KClass;Lhep/dataforge/context/PluginTag;ZILjava/lang/Object;)Ljava/lang/Object;
public fun getContext ()Lhep/dataforge/context/Context;
public fun getLogger ()Lmu/KLogger;
public fun iterator ()Ljava/util/Iterator;
public final fun list (Z)Ljava/util/Collection;
public final fun load (Lhep/dataforge/context/Plugin;)Lhep/dataforge/context/Plugin;
public final fun load (Lhep/dataforge/context/PluginFactory;Lhep/dataforge/meta/Meta;)Lhep/dataforge/context/Plugin;
public final fun load (Lhep/dataforge/context/PluginFactory;Lkotlin/jvm/functions/Function1;)Lhep/dataforge/context/Plugin;
public static synthetic fun load$default (Lhep/dataforge/context/PluginManager;Lhep/dataforge/context/PluginFactory;Lhep/dataforge/meta/Meta;ILjava/lang/Object;)Lhep/dataforge/context/Plugin;
public final fun remove (Lhep/dataforge/context/Plugin;)V
}
public final class hep/dataforge/context/PluginTag : hep/dataforge/meta/MetaRepr {
public static final field Companion Lhep/dataforge/context/PluginTag$Companion;
public static final field DATAFORGE_GROUP Ljava/lang/String;
public fun <init> (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
public synthetic fun <init> (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
public final fun component1 ()Ljava/lang/String;
public final fun component2 ()Ljava/lang/String;
public final fun component3 ()Ljava/lang/String;
public final fun copy (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)Lhep/dataforge/context/PluginTag;
public static synthetic fun copy$default (Lhep/dataforge/context/PluginTag;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;ILjava/lang/Object;)Lhep/dataforge/context/PluginTag;
public fun equals (Ljava/lang/Object;)Z
public final fun getGroup ()Ljava/lang/String;
public final fun getName ()Ljava/lang/String;
public final fun getVersion ()Ljava/lang/String;
public fun hashCode ()I
public final fun matches (Lhep/dataforge/context/PluginTag;)Z
public fun toMeta ()Lhep/dataforge/meta/Meta;
public fun toString ()Ljava/lang/String;
}
public final class hep/dataforge/context/PluginTag$Companion {
public final fun fromString (Ljava/lang/String;)Lhep/dataforge/context/PluginTag;
}
public final class hep/dataforge/context/ResolveKt {
public static final fun gather (Lhep/dataforge/context/Context;Ljava/lang/String;Lkotlin/reflect/KClass;Z)Ljava/util/Map;
public static synthetic fun gather$default (Lhep/dataforge/context/Context;Ljava/lang/String;Lkotlin/reflect/KClass;ZILjava/lang/Object;)Ljava/util/Map;
public static final fun gatherInSequence (Lhep/dataforge/context/Context;Ljava/lang/String;Lkotlin/reflect/KClass;Z)Lkotlin/sequences/Sequence;
public static synthetic fun gatherInSequence$default (Lhep/dataforge/context/Context;Ljava/lang/String;Lkotlin/reflect/KClass;ZILjava/lang/Object;)Lkotlin/sequences/Sequence;
public static final fun getValues (Lkotlin/sequences/Sequence;)Lkotlin/sequences/Sequence;
public static final fun resolve (Lhep/dataforge/context/Context;Ljava/lang/String;Lhep/dataforge/names/Name;Lkotlin/reflect/KClass;)Ljava/lang/Object;
}
public abstract interface annotation class hep/dataforge/descriptors/Attribute : java/lang/annotation/Annotation {
public abstract fun key ()Ljava/lang/String;
public abstract fun value ()Ljava/lang/String;
}
public abstract interface annotation class hep/dataforge/descriptors/Attributes : java/lang/annotation/Annotation {
public abstract fun attrs ()[Lhep/dataforge/descriptors/Attribute;
}
public abstract interface annotation class hep/dataforge/descriptors/ItemDef : java/lang/annotation/Annotation {
public abstract fun info ()Ljava/lang/String;
public abstract fun multiple ()Z
public abstract fun required ()Z
}
public abstract interface annotation class hep/dataforge/descriptors/ValueDef : java/lang/annotation/Annotation {
public abstract fun allowed ()[Ljava/lang/String;
public abstract fun def ()Ljava/lang/String;
public abstract fun enumeration ()Ljava/lang/Class;
public abstract fun type ()[Lhep/dataforge/values/ValueType;
}
public final class hep/dataforge/properties/ConfigProperty : hep/dataforge/properties/Property {
public fun <init> (Lhep/dataforge/meta/Config;Lhep/dataforge/names/Name;Lhep/dataforge/meta/transformations/MetaConverter;)V
public final fun getConfig ()Lhep/dataforge/meta/Config;
public final fun getConverter ()Lhep/dataforge/meta/transformations/MetaConverter;
public final fun getName ()Lhep/dataforge/names/Name;
public fun getValue ()Ljava/lang/Object;
public fun onChange (Ljava/lang/Object;Lkotlin/jvm/functions/Function1;)V
public fun removeChangeListener (Ljava/lang/Object;)V
public fun setValue (Ljava/lang/Object;)V
}
public abstract interface class hep/dataforge/properties/Property {
public abstract fun getValue ()Ljava/lang/Object;
public abstract fun onChange (Ljava/lang/Object;Lkotlin/jvm/functions/Function1;)V
public abstract fun removeChangeListener (Ljava/lang/Object;)V
public abstract fun setValue (Ljava/lang/Object;)V
}
public final class hep/dataforge/properties/Property$DefaultImpls {
public static synthetic fun onChange$default (Lhep/dataforge/properties/Property;Ljava/lang/Object;Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)V
public static synthetic fun removeChangeListener$default (Lhep/dataforge/properties/Property;Ljava/lang/Object;ILjava/lang/Object;)V
}
public final class hep/dataforge/properties/PropertyKt {
public static final fun bind (Lhep/dataforge/properties/Property;Lhep/dataforge/properties/Property;)V
public static final fun mirror (Lhep/dataforge/properties/Property;Lhep/dataforge/properties/Property;Lkotlinx/coroutines/CoroutineScope;)V
public static final fun toFlow (Lhep/dataforge/properties/Property;)Lkotlinx/coroutines/flow/StateFlow;
}
public final class hep/dataforge/provider/DfTypeKt {
public static final fun getDfType (Lkotlin/reflect/KClass;)Ljava/lang/String;
}
public final class hep/dataforge/provider/Path : java/lang/Iterable, kotlin/jvm/internal/markers/KMappedMarker {
public static final field Companion Lhep/dataforge/provider/Path$Companion;
public static final field PATH_SEGMENT_SEPARATOR Ljava/lang/String;
public static final synthetic fun box-impl (Ljava/util/List;)Lhep/dataforge/provider/Path;
public static fun constructor-impl (Ljava/util/List;)Ljava/util/List;
public fun equals (Ljava/lang/Object;)Z
public static fun equals-impl (Ljava/util/List;Ljava/lang/Object;)Z
public static final fun equals-impl0 (Ljava/util/List;Ljava/util/List;)Z
public static final fun getHead-impl (Ljava/util/List;)Lhep/dataforge/provider/PathToken;
public static final fun getLength-impl (Ljava/util/List;)I
public static final fun getTail-e2ET3QM (Ljava/util/List;)Ljava/util/List;
public final fun getTokens ()Ljava/util/List;
public fun hashCode ()I
public static fun hashCode-impl (Ljava/util/List;)I
public fun iterator ()Ljava/util/Iterator;
public static fun iterator-impl (Ljava/util/List;)Ljava/util/Iterator;
public fun toString ()Ljava/lang/String;
public static fun toString-impl (Ljava/util/List;)Ljava/lang/String;
public final synthetic fun unbox-impl ()Ljava/util/List;
}
public final class hep/dataforge/provider/Path$Companion {
public final fun parse-IN54j3k (Ljava/lang/String;)Ljava/util/List;
}
public final class hep/dataforge/provider/PathKt {
public static final fun plus-MQiGgVU (Ljava/util/List;Ljava/util/List;)Ljava/util/List;
public static final fun toPath (Lhep/dataforge/provider/PathToken;)Ljava/util/List;
}
public final class hep/dataforge/provider/PathToken {
public static final field Companion Lhep/dataforge/provider/PathToken$Companion;
public static final field TARGET_SEPARATOR Ljava/lang/String;
public fun <init> (Lhep/dataforge/names/Name;Ljava/lang/String;)V
public synthetic fun <init> (Lhep/dataforge/names/Name;Ljava/lang/String;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
public final fun component1 ()Lhep/dataforge/names/Name;
public final fun component2 ()Ljava/lang/String;
public final fun copy (Lhep/dataforge/names/Name;Ljava/lang/String;)Lhep/dataforge/provider/PathToken;
public static synthetic fun copy$default (Lhep/dataforge/provider/PathToken;Lhep/dataforge/names/Name;Ljava/lang/String;ILjava/lang/Object;)Lhep/dataforge/provider/PathToken;
public fun equals (Ljava/lang/Object;)Z
public final fun getName ()Lhep/dataforge/names/Name;
public final fun getTarget ()Ljava/lang/String;
public fun hashCode ()I
public fun toString ()Ljava/lang/String;
}
public final class hep/dataforge/provider/PathToken$Companion {
public final fun parse (Ljava/lang/String;)Lhep/dataforge/provider/PathToken;
}
public abstract interface class hep/dataforge/provider/Provider {
public abstract fun content (Ljava/lang/String;)Ljava/util/Map;
public abstract fun getDefaultChainTarget ()Ljava/lang/String;
public abstract fun getDefaultTarget ()Ljava/lang/String;
}
public final class hep/dataforge/provider/Provider$DefaultImpls {
public static fun content (Lhep/dataforge/provider/Provider;Ljava/lang/String;)Ljava/util/Map;
public static fun getDefaultChainTarget (Lhep/dataforge/provider/Provider;)Ljava/lang/String;
public static fun getDefaultTarget (Lhep/dataforge/provider/Provider;)Ljava/lang/String;
}
public final class hep/dataforge/provider/ProviderKt {
public static final fun provide-0Dbucg0 (Lhep/dataforge/provider/Provider;Ljava/util/List;Ljava/lang/String;)Ljava/lang/Object;
public static synthetic fun provide-0Dbucg0$default (Lhep/dataforge/provider/Provider;Ljava/util/List;Ljava/lang/String;ILjava/lang/Object;)Ljava/lang/Object;
public static final fun top (Lhep/dataforge/provider/Provider;Ljava/lang/String;Lkotlin/reflect/KClass;)Ljava/util/Map;
}
public abstract interface annotation class hep/dataforge/provider/Type : java/lang/annotation/Annotation {
public abstract fun id ()Ljava/lang/String;
}

@ -1,32 +1,30 @@
import scientifik.useCoroutines
plugins {
id("scientifik.mpp")
id("ru.mipt.npm.mpp")
id("ru.mipt.npm.native")
}
description = "Context and provider definitions"
useCoroutines()
kscience {
useCoroutines()
}
kotlin {
sourceSets {
val commonMain by getting {
dependencies {
api(project(":dataforge-meta"))
api("io.github.microutils:kotlin-logging-common:1.7.9")
api("io.github.microutils:kotlin-logging:1.9.0-dev-npm-2")
}
}
val jvmMain by getting {
dependencies {
api(kotlin("reflect"))
api("io.github.microutils:kotlin-logging:1.7.9")
api("ch.qos.logback:logback-classic:1.2.3")
}
}
val jsMain by getting {
dependencies {
api("io.github.microutils:kotlin-logging-js:1.7.9")
}
}
}

@ -6,7 +6,7 @@ import kotlin.properties.ReadOnlyProperty
import kotlin.reflect.KClass
import kotlin.reflect.KProperty
abstract class AbstractPlugin(override val meta: Meta = Meta.EMPTY) : Plugin {
public abstract class AbstractPlugin(override val meta: Meta = Meta.EMPTY) : Plugin {
private var _context: Context? = null
private val dependencies = ArrayList<PluginFactory<*>>()
@ -30,11 +30,9 @@ abstract class AbstractPlugin(override val meta: Meta = Meta.EMPTY) : Plugin {
dependencies.add(factory)
return PluginDependencyDelegate(factory.type)
}
override fun provideTop(target: String): Map<Name, Any> = emptyMap()
}
fun <T : Named> Collection<T>.toMap(): Map<Name, T> = associate { it.name to it }
public fun <T : Named> Collection<T>.toMap(): Map<Name, T> = associate { it.name to it }
private class PluginDependencyDelegate<P : Plugin>(val type: KClass<out P>) : ReadOnlyProperty<AbstractPlugin, P> {
override fun getValue(thisRef: AbstractPlugin, property: KProperty<*>): P {

@ -1,19 +1,18 @@
package hep.dataforge.context
import hep.dataforge.meta.*
import hep.dataforge.meta.Laminate
import hep.dataforge.meta.Meta
import hep.dataforge.meta.MetaRepr
import hep.dataforge.meta.sequence
import hep.dataforge.names.Name
import hep.dataforge.names.asName
import hep.dataforge.names.plus
import hep.dataforge.provider.Provider
import hep.dataforge.provider.top
import hep.dataforge.values.Value
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Job
import kotlinx.coroutines.SupervisorJob
import mu.KLogger
import mu.KotlinLogging
import kotlin.coroutines.CoroutineContext
import kotlin.jvm.JvmName
/**
* The local environment for anything being done in DataForge framework. Contexts are organized into tree structure with [Global] at the top.
@ -24,73 +23,52 @@ import kotlin.jvm.JvmName
* different plugins with the same interface in different contexts in the hierarchy. The usual behaviour is to use nearest one, but it could
* be overridden by plugin implementation.
*
* Since plugins could contain mutable state, context has two states: active and inactive. No changes are allowed to active context.
* @author Alexander Nozik
*/
open class Context(
public open class Context(
final override val name: Name,
val parent: Context? = Global
public val parent: Context?,
meta: Meta,
plugins: Set<Plugin> = emptySet(),
) : Named, MetaRepr, Provider, CoroutineScope {
private val config = Config()
/**
* Context properties. Working as substitute for environment variables
*/
val properties: Meta = if (parent == null) {
config
private val properties: Laminate = if (parent == null) {
Laminate(meta)
} else {
Laminate(config, parent.properties)
Laminate(meta, parent.properties)
}
/**
* Context logger
*/
val logger: KLogger = KotlinLogging.logger(name.toString())
public val logger: KLogger = KotlinLogging.logger(name.toString())
/**
* A [PluginManager] for current context
*/
val plugins: PluginManager by lazy { PluginManager(this) }
public val plugins: PluginManager by lazy { PluginManager(this, plugins)}
private val activators = HashSet<Any>()
override val defaultTarget: String get() = Plugin.TARGET
/**
* Defines if context is used in any kind of active computations. Active context properties and plugins could not be changed
*/
val isActive: Boolean = activators.isNotEmpty()
override val defaultTarget: String get() = Plugin.PLUGIN_TARGET
override fun provideTop(target: String): Map<Name, Any> {
return when (target) {
Value.TYPE -> properties.sequence().toMap()
Plugin.PLUGIN_TARGET -> plugins.sequence(true).associateBy { it.name }
else -> emptyMap()
public fun content(target: String, inherit: Boolean): Map<Name, Any> {
return if (inherit) {
when (target) {
PROPERTY_TARGET -> properties.sequence().toMap()
Plugin.TARGET -> plugins.list(true).associateBy { it.name }
else -> emptyMap()
}
} else {
when (target) {
PROPERTY_TARGET -> properties.layers.firstOrNull()?.sequence()?.toMap() ?: emptyMap()
Plugin.TARGET -> plugins.list(false).associateBy { it.name }
else -> emptyMap()
}
}
}
/**
* Mark context as active and used by [activator]
*/
fun activate(activator: Any) {
activators.add(activator)
}
/**
* Mark context unused by [activator]
*/
fun deactivate(activator: Any) {
activators.remove(activator)
}
/**
* Change the properties of the context. If active, throw an exception
*/
fun configure(action: Config.() -> Unit) {
if (isActive) error("Can't configure active context")
config.action()
}
override fun content(target: String): Map<Name, Any> = content(target, true)
override val coroutineContext: CoroutineContext by lazy {
(parent ?: Global).coroutineContext.let { parenContext ->
@ -101,81 +79,35 @@ open class Context(
/**
* Detach all plugins and terminate context
*/
open fun close() {
if (isActive) error("Can't close active context")
public open fun close() {
//detach all plugins
plugins.forEach { it.detach() }
}
override fun toMeta(): Meta = Meta {
"parent" to parent?.name
"properties" put properties.seal()
"properties" put properties.layers.firstOrNull()
"plugins" put plugins.map { it.toMeta() }
}
}
fun Context.content(target: String): Map<Name, Any> = content<Any>(target)
/**
* A map of all objects provided by plugins with given target and type
*/
@JvmName("typedContent")
inline fun <reified T : Any> Context.content(target: String): Map<Name, T> =
plugins.flatMap { plugin ->
plugin.top<T>(target).entries.map { (plugin.name + it.key) to it.value }
}.associate { it }
/**
* A global root context. Closing [Global] terminates the framework.
*/
object Global : Context("GLOBAL".asName(), null) {
/**
* Closing all contexts
*
* @throws Exception
*/
override fun close() {
logger.info { "Shutting down GLOBAL" }
for (ctx in contextRegistry.values) {
ctx.close()
}
super.close()
public companion object {
public const val PROPERTY_TARGET: String = "context.property"
}
private val contextRegistry = HashMap<String, Context>()
/**
* Get previously built context
*
* @param name
* @return
*/
fun getContext(name: String): Context? {
return contextRegistry[name]
}
fun context(name: String, parent: Context = this, block: ContextBuilder.() -> Unit = {}): Context =
ContextBuilder(name, parent).apply(block).build()
}
/**
* The interface for something that encapsulated in context
*
* @author Alexander Nozik
* @version $Id: $Id
*/
interface ContextAware {
public interface ContextAware {
/**
* Get context for this object
*
* @return
*/
val context: Context
public val context: Context
val logger: KLogger
public val logger: KLogger
get() = if (this is Named) {
KotlinLogging.logger((context.name + this.name).toString())
} else {

@ -1,44 +1,44 @@
package hep.dataforge.context
import hep.dataforge.meta.DFBuilder
import hep.dataforge.meta.Meta
import hep.dataforge.meta.MetaBuilder
import hep.dataforge.meta.buildMeta
import hep.dataforge.meta.*
import hep.dataforge.names.toName
/**
* A convenience builder for context
*/
@DFBuilder
class ContextBuilder(var name: String = "@anonymous", val parent: Context = Global) {
private val plugins = ArrayList<Plugin>()
public class ContextBuilder(private val parent: Context = Global, public var name: String = "@anonymous") {
private val plugins = HashSet<Plugin>()
private var meta = MetaBuilder()
fun properties(action: MetaBuilder.() -> Unit) {
public fun properties(action: MetaBuilder.() -> Unit) {
meta.action()
}
fun plugin(plugin: Plugin) {
public fun plugin(plugin: Plugin) {
plugins.add(plugin)
}
fun plugin(tag: PluginTag, action: MetaBuilder.() -> Unit = {}) {
plugins.add(PluginRepository.fetch(tag, Meta(action)))
@OptIn(DFExperimental::class)
private fun findPluginFactory(tag: PluginTag): PluginFactory<*> =
parent.gatherInSequence<PluginFactory<*>>(PluginFactory.TYPE).values
.find { it.tag.matches(tag) } ?: error("Can't resolve plugin factory for $tag")
public fun plugin(tag: PluginTag, metaBuilder: MetaBuilder.() -> Unit = {}) {
val factory = findPluginFactory(tag)
val plugin = factory.invoke(Meta(metaBuilder), parent)
plugins.add(plugin)
}
fun plugin(builder: PluginFactory<*>, action: MetaBuilder.() -> Unit = {}) {
public fun plugin(builder: PluginFactory<*>, action: MetaBuilder.() -> Unit = {}) {
plugins.add(builder.invoke(Meta(action)))
}
fun plugin(name: String, group: String = "", version: String = "", action: MetaBuilder.() -> Unit = {}) {
public fun plugin(name: String, group: String = "", version: String = "", action: MetaBuilder.() -> Unit = {}) {
plugin(PluginTag(name, group, version), action)
}
fun build(): Context {
return Context(name.toName(), parent).apply {
this@ContextBuilder.plugins.forEach {
plugins.load(it)
}
}
public fun build(): Context {
return Context(name.toName(), parent, meta.seal(), plugins)
}
}

@ -2,6 +2,6 @@ package hep.dataforge.context
import hep.dataforge.meta.Meta
interface Factory<out T : Any> {
operator fun invoke(meta: Meta = Meta.EMPTY, context: Context = Global): T
public interface Factory<out T : Any> {
public operator fun invoke(meta: Meta = Meta.EMPTY, context: Context = Global): T
}

@ -0,0 +1,48 @@
package hep.dataforge.context
import hep.dataforge.meta.Meta
import hep.dataforge.names.asName
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.SupervisorJob
import kotlin.coroutines.CoroutineContext
import kotlin.native.concurrent.ThreadLocal
/**
* A global root context. Closing [Global] terminates the framework.
*/
@ThreadLocal
public object Global : Context("GLOBAL".asName(), null, Meta.EMPTY) {
override val coroutineContext: CoroutineContext = GlobalScope.coroutineContext + SupervisorJob()
/**
* Closing all contexts
*
* @throws Exception
*/
override fun close() {
logger.info { "Shutting down GLOBAL" }
for (ctx in contextRegistry.values) {
ctx.close()
}
super.close()
}
private val contextRegistry = HashMap<String, Context>()
/**
* Get previously built context
*
* @param name
* @return
*/
public fun getContext(name: String): Context? {
return contextRegistry[name]
}
public fun context(name: String, parent: Context = this, block: ContextBuilder.() -> Unit = {}): Context =
ContextBuilder(parent, name).apply(block).build().also {
contextRegistry[name] = it
}
}

@ -24,16 +24,14 @@ import hep.dataforge.names.isEmpty
*
* @author Alexander Nozik
*/
interface Named {
public interface Named {
/**
* The name of this object instance
*
* @return
*/
val name: Name
public val name: Name
companion object {
public companion object {
/**
* Get the name of given object. If object is Named its name is used,
@ -42,7 +40,7 @@ interface Named {
* @param obj
* @return
*/
fun nameOf(obj: Any): Name {
public fun nameOf(obj: Any): Name {
return if (obj is Named) {
obj.name
} else {
@ -56,5 +54,4 @@ interface Named {
* Check if this object has an empty name and therefore is anonymous.
* @return
*/
val Named.isAnonymous: Boolean
get() = this.name.isEmpty()
public val Named.isAnonymous: Boolean get() = this.name.isEmpty()

@ -1,43 +1,34 @@
package hep.dataforge.context
import hep.dataforge.context.Plugin.Companion.TARGET
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
import hep.dataforge.provider.Type
/**
* The interface to define a Context plugin. A plugin stores all runtime features of a context.
* The plugin is by default configurable and a Provider (both features could be ignored).
* The plugin must in most cases have an empty constructor in order to be able to load it from library.
*
*
* The plugin lifecycle is the following:
*
*
* create - configure - attach - detach - destroy
*
*
* Configuration of attached plugin is possible for a context which is not in a runtime mode, but it is not recommended.
*
* @author Alexander Nozik
*/
interface Plugin : Named, ContextAware, Provider, MetaRepr {
@Type(TARGET)
public interface Plugin : Named, ContextAware, Provider, MetaRepr {
/**
* Get tag for this plugin
*
* @return
*/
val tag: PluginTag
public val tag: PluginTag
val meta: Meta
public val meta: Meta
/**
* The name of this plugin ignoring version and group
*
* @return
*/
override val name: Name get() = tag.name.toName()
@ -45,25 +36,21 @@ interface Plugin : Named, ContextAware, Provider, MetaRepr {
* Plugin dependencies which are required to attach this plugin. Plugin
* dependencies must be initialized and enabled in the Context before this
* plugin is enabled.
*
* @return
*/
fun dependsOn(): Collection<PluginFactory<*>>
public fun dependsOn(): Collection<PluginFactory<*>>
/**
* Start this plugin and attach registration info to the context. This method
* should be called only via PluginManager to avoid dependency issues.
*
* @param context
*/
fun attach(context: Context)
public fun attach(context: Context)
/**
* Stop this plugin and remove registration info from context and other
* plugins. This method should be called only via PluginManager to avoid
* dependency issues.
*/
fun detach()
public fun detach()
override fun toMeta(): Meta = Meta {
"context" put context.name.toString()
@ -72,9 +59,8 @@ interface Plugin : Named, ContextAware, Provider, MetaRepr {
"meta" put meta
}
companion object {
const val PLUGIN_TARGET = "plugin"
public companion object {
public const val TARGET: String = "plugin"
}
}

@ -2,42 +2,62 @@ package hep.dataforge.context
import hep.dataforge.meta.Meta
import hep.dataforge.meta.MetaBuilder
import hep.dataforge.provider.Type
import kotlin.reflect.KClass
@Type(PluginFactory.TYPE)
public interface PluginFactory<T : Plugin> : Factory<T> {
public val tag: PluginTag
public val type: KClass<out T>
public companion object {
public const val TYPE: String = "pluginFactory"
}
}
/**
* The manager for plugin system. Should monitor plugin dependencies and locks.
*
* @property context A context for this plugin manager
* @author Alexander Nozik
*/
class PluginManager(override val context: Context) : ContextAware, Iterable<Plugin> {
public class PluginManager(override val context: Context, plugins: Set<Plugin>) : ContextAware, Iterable<Plugin> {
//TODO refactor to read-only container
/**
* A set of loaded plugins
*/
private val plugins = HashSet<Plugin>()
private val plugins: HashSet<Plugin> = HashSet(plugins)
init {
plugins.forEach { it.attach(context) }
}
/**
* A [PluginManager] of parent context if it is present
*/
private val parent: PluginManager? = context.parent?.plugins
fun sequence(recursive: Boolean): Sequence<Plugin> {
return if (recursive && parent != null) {
plugins.asSequence() + parent.sequence(true)
/**
* List plugins stored in this [PluginManager]. If [inherit] is true, include parent plugins as well
*/
public fun list(inherit: Boolean): Collection<Plugin> {
return if (inherit && parent != null) {
plugins + parent.list(true)
} else {
plugins.asSequence()
plugins
}
}
/**
* Get existing plugin or return null if not present. Only first matching plugin is returned.
* @param recursive search for parent [PluginManager] plugins
* @param inherit search for parent [PluginManager] plugins
* @param predicate condition for the plugin
*/
fun find(recursive: Boolean = true, predicate: (Plugin) -> Boolean): Plugin? = sequence(recursive).find(predicate)
public fun find(inherit: Boolean = true, predicate: (Plugin) -> Boolean): Plugin? =
list(inherit).find(predicate)
/**
* Find a loaded plugin via its tag
@ -45,8 +65,8 @@ class PluginManager(override val context: Context) : ContextAware, Iterable<Plug
* @param tag
* @return
*/
operator fun get(tag: PluginTag, recursive: Boolean = true): Plugin? = find(recursive) { tag.matches(it.tag) }
public operator fun get(tag: PluginTag, inherit: Boolean = true): Plugin? =
find(inherit) { tag.matches(it.tag) }
/**
* Find a loaded plugin via its class. This method does not check if the result is unique and just returns first
@ -60,13 +80,13 @@ class PluginManager(override val context: Context) : ContextAware, Iterable<Plug
* @return
*/
@Suppress("UNCHECKED_CAST")
operator fun <T : Any> get(type: KClass<out T>, tag: PluginTag? = null, recursive: Boolean = true): T? =
public operator fun <T : Any> get(type: KClass<out T>, tag: PluginTag? = null, recursive: Boolean = true): T? =
find(recursive) { type.isInstance(it) && (tag == null || tag.matches(it.tag)) } as T?
inline operator fun <reified T : Any> get(tag: PluginTag? = null, recursive: Boolean = true): T? =
public inline operator fun <reified T : Any> get(tag: PluginTag? = null, recursive: Boolean = true): T? =
get(T::class, tag, recursive)
inline operator fun <reified T : Plugin> get(factory: PluginFactory<T>, recursive: Boolean = true): T? =
public inline operator fun <reified T : Plugin> get(factory: PluginFactory<T>, recursive: Boolean = true): T? =
get(factory.type, factory.tag, recursive)
/**
@ -76,11 +96,9 @@ class PluginManager(override val context: Context) : ContextAware, Iterable<Plug
* @param plugin
* @return
*/
fun <T : Plugin> load(plugin: T): T {
if (context.isActive) error("Can't load plugin into active context")
public fun <T : Plugin> load(plugin: T): T {
if (get(plugin::class, plugin.tag, recursive = false) != null) {
error("Plugin of type ${plugin::class} already exists in ${context.name}")
error("Plugin with tag ${plugin.tag} already exists in ${context.name}")
} else {
for (tag in plugin.dependsOn()) {
fetch(tag, true)
@ -96,18 +114,16 @@ class PluginManager(override val context: Context) : ContextAware, Iterable<Plug
/**
* Load a plugin using its factory
*/
fun <T : Plugin> load(factory: PluginFactory<T>, meta: Meta = Meta.EMPTY): T =
public fun <T : Plugin> load(factory: PluginFactory<T>, meta: Meta = Meta.EMPTY): T =
load(factory(meta, context))
fun <T : Plugin> load(factory: PluginFactory<T>, metaBuilder: MetaBuilder.() -> Unit): T =
public fun <T : Plugin> load(factory: PluginFactory<T>, metaBuilder: MetaBuilder.() -> Unit): T =
load(factory, Meta(metaBuilder))
/**
* Remove a plugin from [PluginManager]
*/
fun remove(plugin: Plugin) {
if (context.isActive) error("Can't remove plugin from active context")
public fun remove(plugin: Plugin) {
if (plugins.contains(plugin)) {
logger.info { "Removing plugin ${plugin.name} from ${context.name}" }
plugin.detach()
@ -117,9 +133,8 @@ class PluginManager(override val context: Context) : ContextAware, Iterable<Plug
/**
* Get an existing plugin with given meta or load new one using provided factory
*
*/
fun <T : Plugin> fetch(factory: PluginFactory<T>, recursive: Boolean = true, meta: Meta = Meta.EMPTY): T {
public fun <T : Plugin> fetch(factory: PluginFactory<T>, recursive: Boolean = true, meta: Meta = Meta.EMPTY): T {
val loaded = get(factory.type, factory.tag, recursive)
return when {
loaded == null -> load(factory(meta, context))
@ -128,10 +143,10 @@ class PluginManager(override val context: Context) : ContextAware, Iterable<Plug
}
}
fun <T : Plugin> fetch(
public fun <T : Plugin> fetch(
factory: PluginFactory<T>,
recursive: Boolean = true,
metaBuilder: MetaBuilder.() -> Unit
metaBuilder: MetaBuilder.() -> Unit,
): T = fetch(factory, recursive, Meta(metaBuilder))
override fun iterator(): Iterator<Plugin> = plugins.iterator()

@ -1,48 +0,0 @@
package hep.dataforge.context
import hep.dataforge.meta.Meta
import kotlin.reflect.KClass
interface PluginFactory<T : Plugin> : Factory<T> {
val tag: PluginTag
val type: KClass<out T>
}
expect object PluginRepository {
fun register(factory: PluginFactory<*>)
/**
* List plugins available in the repository
*/
fun list(): Sequence<PluginFactory<*>>
}
/**
* Fetch specific plugin and instantiate it with given meta
*/
fun PluginRepository.fetch(tag: PluginTag, meta: Meta = Meta.EMPTY): Plugin =
list().find { it.tag.matches(tag) }?.invoke(meta = meta)
?: error("Plugin with tag $tag not found in the repository")
fun <T : Plugin> PluginRepository.register(
tag: PluginTag,
type: KClass<out T>,
constructor: (Context, Meta) -> T
): PluginFactory<T> {
val factory = object : PluginFactory<T> {
override val tag: PluginTag = tag
override val type: KClass<out T> = type
override fun invoke(meta: Meta, context: Context): T = constructor(context, meta)
}
register(factory)
return factory
}
inline fun <reified T : Plugin> PluginRepository.register(tag: PluginTag, noinline constructor: (Context, Meta) -> T) =
register(tag, T::class, constructor)
fun PluginRepository.register(plugin: Plugin) = register(plugin.tag, plugin::class) { _, _ -> plugin }

@ -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))

@ -0,0 +1,96 @@
package hep.dataforge.context
import hep.dataforge.meta.DFExperimental
import hep.dataforge.names.Name
import hep.dataforge.names.plus
import hep.dataforge.provider.Provider
import hep.dataforge.provider.top
import kotlin.reflect.KClass
import kotlin.reflect.cast
/**
* Resolve a specific element in top level elements of the provider and attempt to cast it to the given type
*/
private fun <T : Any> Provider.provide(target: String, name: Name, type: KClass<out T>): T? {
return content(target)[name]?.let { type.cast(it) }
}
/**
* Resolve a top level object with given [target] and [name] in a [Context] own scope or its plugins.
*/
public fun <T : Any> Context.resolve(target: String, name: Name, type: KClass<out T>): T? {
//Try searching for plugin an context property
provide(target, name, type)?.let { return it }
val pluginContent = plugins.mapNotNull { it.provide(target, name, type) }
return if (pluginContent.isEmpty()) {
parent?.resolve<T>(target, name, type)
} else {
pluginContent.single() // throws error in case of name/type conflicts
}
}
/**
* Resolve a top level object with given [target] and [name] in a [Context] own scope or its plugins.
*/
public inline fun <reified T : Any> Context.resolve(target: String, name: Name): T? =
resolve(target, name, T::class)
/**
* Gather a map of all top-level objects with given [target] from context plugins.
* Content from plugins is prefixed by plugin name so name conflicts are impossible
* This operation could be slow in case of large number of plugins
*/
public fun <T : Any> Context.gather(
target: String,
type: KClass<out T>,
inherit: Boolean = true,
): Map<Name, T> = buildMap {
putAll(top(target, type))
plugins.forEach { plugin ->
plugin.top(target, type).forEach { (name, value) ->
if (containsKey(name)) error("Name conflict during gather. An item with name $name could not be gathered from $plugin because key is already present.")
put(plugin.name + name, value)
}
}
if (inherit) {
parent?.gather(target, type, inherit)?.forEach {
//put all values from parent if they are not conflicting
if (!containsKey(it.key)) {
put(it.key, it.value)
}
}
}
}
public inline fun <reified T : Any> Context.gather(target: String, inherit: Boolean = true): Map<Name, T> =
gather(target, T::class, inherit)
/**
* Gather all content from context itself and its plugins in a form of sequence of name-value pairs. Ignores name conflicts.
*
* Adds parent context sequence as well if [inherit] is true
*/
@DFExperimental
public fun <T : Any> Context.gatherInSequence(
target: String,
type: KClass<out T>,
inherit: Boolean = true,
): Sequence<Map.Entry<Name, T>> = sequence {
yieldAll(top(target, type).entries)
plugins.forEach { plugin ->
yieldAll(plugin.top(target, type).mapKeys { plugin.name + it.key }.entries)
}
if (inherit) {
parent?.gather(target, type, inherit)?.let {
yieldAll(it.entries)
}
}
}
@DFExperimental
public inline fun <reified T : Any> Context.gatherInSequence(
target: String,
inherit: Boolean = true,
): Sequence<Map.Entry<Name, T>> = gatherInSequence(target, T::class, inherit)
public val <T> Sequence<Map.Entry<Name, T>>.values: Sequence<T> get() = map { it.value }

@ -0,0 +1,33 @@
package hep.dataforge.properties
import hep.dataforge.meta.Config
import hep.dataforge.meta.DFExperimental
import hep.dataforge.meta.get
import hep.dataforge.meta.transformations.MetaConverter
import hep.dataforge.meta.transformations.nullableItemToObject
import hep.dataforge.meta.transformations.nullableObjectToMetaItem
import hep.dataforge.names.Name
@DFExperimental
public class ConfigProperty<T : Any>(
public val config: Config,
public val name: Name,
public val converter: MetaConverter<T>,
) : Property<T?> {
override var value: T?
get() = converter.nullableItemToObject(config[name])
set(value) {
config.setItem(name,converter.nullableObjectToMetaItem(value))
}
override fun onChange(owner: Any?, callback: (T?) -> Unit) {
config.onChange(owner) { name, oldItem, newItem ->
if (name == this.name && oldItem != newItem) callback(converter.nullableItemToObject(newItem))
}
}
override fun removeChangeListener(owner: Any?) {
config.removeListener(owner)
}
}

@ -0,0 +1,48 @@
package hep.dataforge.properties
import hep.dataforge.meta.DFExperimental
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
@DFExperimental
public interface Property<T> {
public var value: T
public fun onChange(owner: Any? = null, callback: (T) -> Unit)
public fun removeChangeListener(owner: Any? = null)
}
@DFExperimental
@OptIn(ExperimentalCoroutinesApi::class)
public fun <T> Property<T>.toFlow(): StateFlow<T> = MutableStateFlow(value).also { stateFlow ->
onChange {
stateFlow.value = it
}
}
/**
* Reflect all changes in the [source] property onto this property
*
* @return a mirroring job
*/
@DFExperimental
public fun <T> Property<T>.mirror(source: Property<T>, scope: CoroutineScope) {
source.onChange(this) {
this.value = it
}
}
/**
* Bi-directional connection between properties
*/
@DFExperimental
public fun <T> Property<T>.bind(other: Property<T>) {
onChange(other) {
other.value = it
}
other.onChange {
this.value = it
}
}

@ -26,25 +26,25 @@ import hep.dataforge.names.toName
* @author Alexander Nozik
* @version $Id: $Id
*/
inline class Path(val tokens: List<PathToken>) : Iterable<PathToken> {
public inline class Path(public val tokens: List<PathToken>) : Iterable<PathToken> {
val head: PathToken? get() = tokens.firstOrNull()
public val head: PathToken? get() = tokens.firstOrNull()
val length: Int get() = tokens.size
public val length: Int get() = tokens.size
/**
* Returns non-empty optional containing the chain without first segment in case of chain path.
*
* @return
*/
val tail: Path? get() = if (tokens.isEmpty()) null else Path(tokens.drop(1))
public val tail: Path? get() = if (tokens.isEmpty()) null else Path(tokens.drop(1))
override fun iterator(): Iterator<PathToken> = tokens.iterator()
companion object {
const val PATH_SEGMENT_SEPARATOR = "/"
public companion object {
public const val PATH_SEGMENT_SEPARATOR: String = "/"
fun parse(path: String): Path {
public fun parse(path: String): Path {
val head = path.substringBefore(PATH_SEGMENT_SEPARATOR)
val tail = path.substringAfter(PATH_SEGMENT_SEPARATOR)
return PathToken.parse(head).toPath() + parse(tail)
@ -52,18 +52,18 @@ inline class Path(val tokens: List<PathToken>) : Iterable<PathToken> {
}
}
operator fun Path.plus(path: Path) = Path(this.tokens + path.tokens)
public operator fun Path.plus(path: Path): Path = Path(this.tokens + path.tokens)
data class PathToken(val name: Name, val target: String? = null) {
public data class PathToken(val name: Name, val target: String? = null) {
override fun toString(): String = if (target == null) {
name.toString()
} else {
"$target$TARGET_SEPARATOR$name"
}
companion object {
const val TARGET_SEPARATOR = "::"
fun parse(token: String): PathToken {
public companion object {
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()
if (target.contains("[")) TODO("target separators in queries are not supported")
@ -72,4 +72,4 @@ data class PathToken(val name: Name, val target: String? = null) {
}
}
fun PathToken.toPath() = Path(listOf(this))
public fun PathToken.toPath(): Path = Path(listOf(this))

@ -16,41 +16,37 @@
package hep.dataforge.provider
import hep.dataforge.names.Name
import hep.dataforge.names.toName
import kotlin.reflect.KClass
import kotlin.reflect.safeCast
/**
* A marker utility interface for providers.
*
* @author Alexander Nozik
*/
interface Provider {
public interface Provider {
/**
* Default target for this provider
*
* @return
*/
val defaultTarget: String get() = ""
public val defaultTarget: String get() = ""
/**
* Default target for next chain segment
*
* @return
*/
val defaultChainTarget: String get() = ""
public val defaultChainTarget: String get() = ""
/**
* A map of direct children for specific target
*/
fun provideTop(target: String): Map<Name, Any>
public fun content(target: String): Map<Name, Any> = emptyMap()
}
fun Provider.provide(path: Path, targetOverride: String? = null): Any? {
public fun Provider.provide(path: Path, targetOverride: String? = null): Any? {
if (path.length == 0) throw IllegalArgumentException("Can't provide by empty path")
val first = path.first()
val target = targetOverride ?: first.target ?: defaultTarget
val res = provideTop(target)[first.name] ?: return null
val res = content(target)[first.name] ?: return null
return when (path.length) {
1 -> res
else -> {
@ -65,24 +61,29 @@ fun Provider.provide(path: Path, targetOverride: String? = null): Any? {
/**
* Type checked provide
*/
inline fun <reified T : Any> Provider.provide(path: String): T? {
return provide(Path.parse(path)) as? T
public inline fun <reified T : Any> Provider.provide(path: String, targetOverride: String? = null): T? {
return provide(Path.parse(path), targetOverride) as? T
}
//
//inline fun <reified T : Any> Provider.provide(target: String, name: Name): T? {
// return provide(PathToken(name, target).toPath()) as? T
//}
inline fun <reified T : Any> Provider.provide(target: String, name: Name): T? {
return provide(PathToken(name, target).toPath()) as? T
}
inline fun <reified T : Any> Provider.provide(target: String, name: String): T? =
provide(target, name.toName())
//inline fun <reified T : Any> Provider.provide(target: String, name: String): T? =
// provide(target, name.toName())
/**
* Typed top level content
*/
inline fun <reified T : Any> Provider.top(target: String): Map<Name, T> {
return provideTop(target).mapValues {
it.value as? T ?: error("The type of element $it is ${it::class} but ${T::class} is expected")
public fun <T : Any> Provider.top(target: String, type: KClass<out T>): Map<Name, T> {
return content(target).mapValues {
type.safeCast(it.value) ?: error("The type of element $it is ${it::class} but $type is expected")
}
}
/**
* Typed top level content
*/
public inline fun <reified T : Any> Provider.top(target: String): Map<Name, T> = top(target, T::class)

@ -7,4 +7,4 @@ package hep.dataforge.provider
*/
@MustBeDocumented
@Target(AnnotationTarget.CLASS)
annotation class Type(val id: String)
public annotation class Type(val id: String)

@ -11,7 +11,7 @@ class ContextTest {
class DummyPlugin : AbstractPlugin() {
override val tag get() = PluginTag("test")
override fun provideTop(target: String): Map<Name, Any> {
override fun content(target: String): Map<Name, Any> {
return when(target){
"test" -> listOf("a", "b", "c.d").associate { it.toName() to it.toName() }
else -> emptyMap()
@ -21,8 +21,11 @@ class ContextTest {
@Test
fun testPluginManager() {
Global.plugins.load(DummyPlugin())
val members = Global.content<Name>("test")
val context = Global.context("test"){
plugin(DummyPlugin())
}
//Global.plugins.load(DummyPlugin())
val members = context.gather<Name>("test")
assertEquals(3, members.count())
members.forEach {
assertEquals(it.key, it.value.appendLeft("test"))

@ -1,16 +0,0 @@
package hep.dataforge.context
actual object PluginRepository {
private val factories: MutableSet<PluginFactory<*>> = HashSet()
actual fun register(factory: PluginFactory<*>) {
factories.add(factory)
}
/**
* List plugins available in the repository
*/
actual fun list(): Sequence<PluginFactory<*>> = factories.asSequence()
}

@ -0,0 +1,32 @@
package hep.dataforge.properties
import hep.dataforge.meta.DFExperimental
import org.w3c.dom.HTMLInputElement
@DFExperimental
fun HTMLInputElement.bindValue(property: Property<String>) {
if (this.onchange != null) error("Input element already bound")
this.onchange = {
property.value = this.value
Unit
}
property.onChange(this) {
if (value != it) {
value = it
}
}
}
@DFExperimental
fun HTMLInputElement.bindChecked(property: Property<Boolean>) {
if (this.onchange != null) error("Input element already bound")
this.onchange = {
property.value = this.checked
Unit
}
property.onChange(this) {
if (checked != it) {
checked = it
}
}
}

@ -19,24 +19,24 @@ import java.util.*
import kotlin.reflect.KClass
import kotlin.reflect.full.cast
class ClassLoaderPlugin(val classLoader: ClassLoader) : AbstractPlugin() {
public class ClassLoaderPlugin(private val classLoader: ClassLoader) : AbstractPlugin() {
override val tag: PluginTag = PluginTag("classLoader", PluginTag.DATAFORGE_GROUP)
private val serviceCache: MutableMap<Class<*>, ServiceLoader<*>> = HashMap()
fun <T : Any> services(type: KClass<T>): Sequence<T> {
public fun <T : Any> services(type: KClass<T>): Sequence<T> {
return serviceCache.getOrPut(type.java) { ServiceLoader.load(type.java, classLoader) }.asSequence()
.map { type.cast(it) }
}
companion object {
val DEFAULT = ClassLoaderPlugin(Global::class.java.classLoader)
public companion object {
public val DEFAULT: ClassLoaderPlugin = ClassLoaderPlugin(Global::class.java.classLoader)
}
}
val Context.classLoaderPlugin get() = this.plugins.get() ?: ClassLoaderPlugin.DEFAULT
public val Context.classLoaderPlugin: ClassLoaderPlugin get() = this.plugins.get() ?: ClassLoaderPlugin.DEFAULT
inline fun <reified T : Any> Context.services() = classLoaderPlugin.services(T::class)
public inline fun <reified T : Any> Context.services(): Sequence<T> = classLoaderPlugin.services(T::class)
//open class JVMContext(

@ -1,17 +0,0 @@
package hep.dataforge.context
actual object PluginRepository {
private val factories: MutableSet<PluginFactory<*>> = HashSet()
actual fun register(factory: PluginFactory<*>) {
factories.add(factory)
}
/**
* List plugins available in the repository
*/
actual fun list(): Sequence<PluginFactory<*>> =
factories.asSequence() + Global.services()
}

@ -1,44 +0,0 @@
package hep.dataforge.provider
import hep.dataforge.context.Context
import hep.dataforge.context.content
import hep.dataforge.names.Name
import kotlin.reflect.KClass
import kotlin.reflect.full.findAnnotation
/**
*
*/
object Types {
operator fun get(cl: KClass<*>): String {
return cl.findAnnotation<Type>()?.id ?: cl.simpleName ?: ""
}
operator fun get(obj: Any): String {
return get(obj::class)
}
}
/**
* Provide an object with given name inferring target from its type using [Type] annotation
*/
inline fun <reified T : Any> Provider.provideByType(name: String): T? {
val target = Types[T::class]
return provide(target, name)
}
inline fun <reified T : Any> Provider.provideByType(name: Name): T? {
val target = Types[T::class]
return provide(target, name)
}
inline fun <reified T : Any> Provider.top(): Map<Name, T> {
val target = Types[T::class]
return top(target)
}
/**
* A sequences of all objects provided by plugins with given target and type
*/
inline fun <reified T : Any> Context.content(): Map<Name, T> = content<T>(Types[T::class])

@ -0,0 +1,36 @@
package hep.dataforge.provider
import hep.dataforge.context.Context
import hep.dataforge.context.gather
import hep.dataforge.meta.DFExperimental
import hep.dataforge.names.Name
import kotlin.reflect.KClass
import kotlin.reflect.full.findAnnotation
@DFExperimental
public val KClass<*>.dfType: String
get() = findAnnotation<Type>()?.id ?: simpleName ?: ""
/**
* Provide an object with given name inferring target from its type using [Type] annotation
*/
@DFExperimental
public inline fun <reified T : Any> Provider.provideByType(name: String): T? {
val target = T::class.dfType
return provide(target, name)
}
@DFExperimental
public inline fun <reified T : Any> Provider.top(): Map<Name, T> {
val target = T::class.dfType
return top(target)
}
/**
* All objects provided by plugins with given target and type
*/
@DFExperimental
public inline fun <reified T : Any> Context.gather(inherit: Boolean = true): Map<Name, T> =
gather<T>(T::class.dfType, inherit)

@ -0,0 +1,400 @@
public abstract interface class hep/dataforge/data/Action {
public abstract fun invoke (Lhep/dataforge/data/DataNode;Lhep/dataforge/meta/Meta;)Lhep/dataforge/data/DataNode;
public abstract fun isTerminal ()Z
}
public final class hep/dataforge/data/Action$DefaultImpls {
public static fun isTerminal (Lhep/dataforge/data/Action;)Z
}
public final class hep/dataforge/data/ActionEnv {
public fun <init> (Lhep/dataforge/names/Name;Lhep/dataforge/meta/Meta;Lhep/dataforge/meta/Meta;)V
public final fun component1 ()Lhep/dataforge/names/Name;
public final fun component2 ()Lhep/dataforge/meta/Meta;
public final fun component3 ()Lhep/dataforge/meta/Meta;
public final fun copy (Lhep/dataforge/names/Name;Lhep/dataforge/meta/Meta;Lhep/dataforge/meta/Meta;)Lhep/dataforge/data/ActionEnv;
public static synthetic fun copy$default (Lhep/dataforge/data/ActionEnv;Lhep/dataforge/names/Name;Lhep/dataforge/meta/Meta;Lhep/dataforge/meta/Meta;ILjava/lang/Object;)Lhep/dataforge/data/ActionEnv;
public fun equals (Ljava/lang/Object;)Z
public final fun getActionMeta ()Lhep/dataforge/meta/Meta;
public final fun getMeta ()Lhep/dataforge/meta/Meta;
public final fun getName ()Lhep/dataforge/names/Name;
public fun hashCode ()I
public fun toString ()Ljava/lang/String;
}
public final class hep/dataforge/data/ActionKt {
public static final fun then (Lhep/dataforge/data/Action;Lhep/dataforge/data/Action;)Lhep/dataforge/data/Action;
}
public final class hep/dataforge/data/CoroutineMonitor : kotlin/coroutines/CoroutineContext$Element {
public static final field Companion Lhep/dataforge/data/CoroutineMonitor$Companion;
public fun <init> ()V
public final fun finish ()V
public fun fold (Ljava/lang/Object;Lkotlin/jvm/functions/Function2;)Ljava/lang/Object;
public fun get (Lkotlin/coroutines/CoroutineContext$Key;)Lkotlin/coroutines/CoroutineContext$Element;
public fun getKey ()Lkotlin/coroutines/CoroutineContext$Key;
public final fun getStatus ()Ljava/lang/String;
public final fun getTotalWork ()D
public final fun getWorkDone ()D
public fun minusKey (Lkotlin/coroutines/CoroutineContext$Key;)Lkotlin/coroutines/CoroutineContext;
public fun plus (Lkotlin/coroutines/CoroutineContext;)Lkotlin/coroutines/CoroutineContext;
public final fun setStatus (Ljava/lang/String;)V
public final fun setTotalWork (D)V
public final fun setWorkDone (D)V
public final fun start ()V
}
public final class hep/dataforge/data/CoroutineMonitor$Companion : kotlin/coroutines/CoroutineContext$Key {
}
public final class hep/dataforge/data/CoroutineMonitorKt {
public static final fun getDependencies (Lkotlinx/coroutines/Job;)Ljava/util/Collection;
public static final fun getMonitor (Lkotlin/coroutines/CoroutineContext;)Lhep/dataforge/data/CoroutineMonitor;
public static final fun getMonitor (Lkotlinx/coroutines/CoroutineScope;)Lhep/dataforge/data/CoroutineMonitor;
public static final fun getProgress (Lkotlinx/coroutines/Job;)D
public static final fun getStatus (Lkotlinx/coroutines/Job;)Ljava/lang/String;
public static final fun getTotalWork (Lkotlinx/coroutines/Job;)D
public static final fun getWorkDone (Lkotlinx/coroutines/Job;)D
}
public abstract interface class hep/dataforge/data/Data : hep/dataforge/data/Goal, hep/dataforge/meta/MetaRepr {
public static final field Companion Lhep/dataforge/data/Data$Companion;
public static final field TYPE Ljava/lang/String;
public abstract fun getMeta ()Lhep/dataforge/meta/Meta;
public abstract fun getType ()Lkotlin/reflect/KClass;
public abstract fun toMeta ()Lhep/dataforge/meta/Meta;
}
public final class hep/dataforge/data/Data$Companion {
public static final field TYPE Ljava/lang/String;
public final fun invoke (Ljava/lang/String;Lkotlin/reflect/KClass;Lhep/dataforge/meta/Meta;Lkotlin/coroutines/CoroutineContext;Ljava/util/Collection;Lkotlin/jvm/functions/Function2;)Lhep/dataforge/data/Data;
public final fun invoke (Lkotlin/reflect/KClass;Lhep/dataforge/meta/Meta;Lkotlin/coroutines/CoroutineContext;Ljava/util/Collection;Lkotlin/jvm/functions/Function2;)Lhep/dataforge/data/Data;
public static synthetic fun invoke$default (Lhep/dataforge/data/Data$Companion;Ljava/lang/String;Lkotlin/reflect/KClass;Lhep/dataforge/meta/Meta;Lkotlin/coroutines/CoroutineContext;Ljava/util/Collection;Lkotlin/jvm/functions/Function2;ILjava/lang/Object;)Lhep/dataforge/data/Data;
public static synthetic fun invoke$default (Lhep/dataforge/data/Data$Companion;Lkotlin/reflect/KClass;Lhep/dataforge/meta/Meta;Lkotlin/coroutines/CoroutineContext;Ljava/util/Collection;Lkotlin/jvm/functions/Function2;ILjava/lang/Object;)Lhep/dataforge/data/Data;
public final fun static (Ljava/lang/Object;Lhep/dataforge/meta/Meta;)Lhep/dataforge/data/Data;
public static synthetic fun static$default (Lhep/dataforge/data/Data$Companion;Ljava/lang/Object;Lhep/dataforge/meta/Meta;ILjava/lang/Object;)Lhep/dataforge/data/Data;
}
public final class hep/dataforge/data/Data$DefaultImpls {
public static fun toMeta (Lhep/dataforge/data/Data;)Lhep/dataforge/meta/Meta;
}
public final class hep/dataforge/data/DataCastKt {
public static final fun canCast (Lhep/dataforge/data/DataItem;Lkotlin/reflect/KClass;)Z
public static final fun cast (Lhep/dataforge/data/Data;Lkotlin/reflect/KClass;)Lhep/dataforge/data/Data;
public static final fun cast (Lhep/dataforge/data/DataNode;Lkotlin/reflect/KClass;)Lhep/dataforge/data/DataNode;
public static final fun ensureType (Lhep/dataforge/data/DataNode;Lkotlin/reflect/KClass;)V
public static final fun upcast (Lhep/dataforge/data/Data;Lkotlin/reflect/KClass;)Lhep/dataforge/data/Data;
}
public final class hep/dataforge/data/DataFilter : hep/dataforge/meta/Scheme {
public static final field Companion Lhep/dataforge/data/DataFilter$Companion;
public fun <init> ()V
public final fun getFrom ()Ljava/lang/String;
public final fun getPattern ()Ljava/lang/String;
public final fun getTo ()Ljava/lang/String;
public final fun setFrom (Ljava/lang/String;)V
public final fun setPattern (Ljava/lang/String;)V
public final fun setTo (Ljava/lang/String;)V
}
public final class hep/dataforge/data/DataFilter$Companion : hep/dataforge/meta/SchemeSpec {
}
public final class hep/dataforge/data/DataFilterKt {
public static final fun filter (Lhep/dataforge/data/DataNode;Lhep/dataforge/data/DataFilter;)Lhep/dataforge/data/DataNode;
public static final fun filter (Lhep/dataforge/data/DataNode;Lhep/dataforge/meta/Meta;)Lhep/dataforge/data/DataNode;
public static final fun filter (Lhep/dataforge/data/DataNode;Lkotlin/jvm/functions/Function1;)Lhep/dataforge/data/DataNode;
}
public abstract class hep/dataforge/data/DataItem : hep/dataforge/meta/MetaRepr {
public abstract fun getMeta ()Lhep/dataforge/meta/Meta;
public abstract fun getType ()Lkotlin/reflect/KClass;
}
public final class hep/dataforge/data/DataItem$Leaf : hep/dataforge/data/DataItem {
public fun <init> (Lhep/dataforge/data/Data;)V
public final fun getData ()Lhep/dataforge/data/Data;
public fun getMeta ()Lhep/dataforge/meta/Meta;
public fun getType ()Lkotlin/reflect/KClass;
public fun toMeta ()Lhep/dataforge/meta/Meta;
}
public final class hep/dataforge/data/DataItem$Node : hep/dataforge/data/DataItem {
public fun <init> (Lhep/dataforge/data/DataNode;)V
public fun getMeta ()Lhep/dataforge/meta/Meta;
public final fun getNode ()Lhep/dataforge/data/DataNode;
public fun getType ()Lkotlin/reflect/KClass;
public fun toMeta ()Lhep/dataforge/meta/Meta;
}
public final class hep/dataforge/data/DataJVMKt {
public static final fun filterIsInstance (Lhep/dataforge/data/Data;Lkotlin/reflect/KClass;)Lhep/dataforge/data/Data;
public static final fun filterIsInstance (Lhep/dataforge/data/DataItem;Lkotlin/reflect/KClass;)Lhep/dataforge/data/DataItem;
public static final fun filterIsInstance (Lhep/dataforge/data/DataNode;Lkotlin/reflect/KClass;)Lhep/dataforge/data/DataNode;
public static final fun get (Lhep/dataforge/data/Data;)Ljava/lang/Object;
}
public final class hep/dataforge/data/DataKt {
public static final fun map (Lhep/dataforge/data/Data;Lkotlin/reflect/KClass;Lkotlin/coroutines/CoroutineContext;Lhep/dataforge/meta/Meta;Lkotlin/jvm/functions/Function3;)Lhep/dataforge/data/Data;
public static synthetic fun map$default (Lhep/dataforge/data/Data;Lkotlin/reflect/KClass;Lkotlin/coroutines/CoroutineContext;Lhep/dataforge/meta/Meta;Lkotlin/jvm/functions/Function3;ILjava/lang/Object;)Lhep/dataforge/data/Data;
public static final fun reduce (Ljava/util/Map;Lkotlin/reflect/KClass;Lkotlin/coroutines/CoroutineContext;Lhep/dataforge/meta/Meta;Lkotlin/jvm/functions/Function3;)Lhep/dataforge/data/DynamicData;
public static synthetic fun reduce$default (Ljava/util/Map;Lkotlin/reflect/KClass;Lkotlin/coroutines/CoroutineContext;Lhep/dataforge/meta/Meta;Lkotlin/jvm/functions/Function3;ILjava/lang/Object;)Lhep/dataforge/data/DynamicData;
}
public abstract interface class hep/dataforge/data/DataNode : hep/dataforge/meta/MetaRepr {
public static final field Companion Lhep/dataforge/data/DataNode$Companion;
public static final field TYPE Ljava/lang/String;
public abstract fun getItems ()Ljava/util/Map;
public abstract fun getMeta ()Lhep/dataforge/meta/Meta;
public abstract fun getType ()Lkotlin/reflect/KClass;
public abstract fun startAll (Lkotlinx/coroutines/CoroutineScope;)Lkotlinx/coroutines/Job;
public abstract fun toMeta ()Lhep/dataforge/meta/Meta;
}
public final class hep/dataforge/data/DataNode$Companion {
public static final field TYPE Ljava/lang/String;
public final fun builder (Lkotlin/reflect/KClass;)Lhep/dataforge/data/DataTreeBuilder;
public final fun invoke (Lkotlin/reflect/KClass;Lkotlin/jvm/functions/Function1;)Lhep/dataforge/data/DataTree;
}
public final class hep/dataforge/data/DataNode$DefaultImpls {
public static fun startAll (Lhep/dataforge/data/DataNode;Lkotlinx/coroutines/CoroutineScope;)Lkotlinx/coroutines/Job;
public static fun toMeta (Lhep/dataforge/data/DataNode;)Lhep/dataforge/meta/Meta;
}
public final class hep/dataforge/data/DataNodeKt {
public static final fun asSequence (Lhep/dataforge/data/DataNode;)Lkotlin/sequences/Sequence;
public static final fun builder (Lhep/dataforge/data/DataNode;)Lhep/dataforge/data/DataTreeBuilder;
public static final fun dataSequence (Lhep/dataforge/data/DataNode;)Lkotlin/sequences/Sequence;
public static final fun datum (Lhep/dataforge/data/DataTreeBuilder;Lhep/dataforge/names/Name;Lhep/dataforge/data/Data;)V
public static final fun datum (Lhep/dataforge/data/DataTreeBuilder;Ljava/lang/String;Lhep/dataforge/data/Data;)V
public static final fun filter (Lhep/dataforge/data/DataNode;Lkotlin/jvm/functions/Function2;)Lhep/dataforge/data/DataNode;
public static final fun first (Lhep/dataforge/data/DataNode;)Lhep/dataforge/data/Data;
public static final fun get (Lhep/dataforge/data/DataNode;Lhep/dataforge/names/Name;)Lhep/dataforge/data/DataItem;
public static final fun get (Lhep/dataforge/data/DataNode;Ljava/lang/String;)Lhep/dataforge/data/DataItem;
public static final fun getData (Lhep/dataforge/data/DataItem;)Lhep/dataforge/data/Data;
public static final fun getNode (Lhep/dataforge/data/DataItem;)Lhep/dataforge/data/DataNode;
public static final fun iterator (Lhep/dataforge/data/DataNode;)Ljava/util/Iterator;
public static final fun join (Lhep/dataforge/data/DataNode;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
public static final fun node (Lhep/dataforge/data/DataTreeBuilder;Lhep/dataforge/names/Name;Lhep/dataforge/data/DataNode;)V
public static final fun node (Lhep/dataforge/data/DataTreeBuilder;Ljava/lang/String;Lhep/dataforge/data/DataNode;)V
public static final fun static (Lhep/dataforge/data/DataTreeBuilder;Lhep/dataforge/names/Name;Ljava/lang/Object;Lhep/dataforge/meta/Meta;)V
public static final fun static (Lhep/dataforge/data/DataTreeBuilder;Lhep/dataforge/names/Name;Ljava/lang/Object;Lkotlin/jvm/functions/Function1;)V
public static final fun static (Lhep/dataforge/data/DataTreeBuilder;Ljava/lang/String;Ljava/lang/Object;Lkotlin/jvm/functions/Function1;)V
public static synthetic fun static$default (Lhep/dataforge/data/DataTreeBuilder;Lhep/dataforge/names/Name;Ljava/lang/Object;Lhep/dataforge/meta/Meta;ILjava/lang/Object;)V
public static synthetic fun static$default (Lhep/dataforge/data/DataTreeBuilder;Lhep/dataforge/names/Name;Ljava/lang/Object;Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)V
public static synthetic fun static$default (Lhep/dataforge/data/DataTreeBuilder;Ljava/lang/String;Ljava/lang/Object;Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)V
}
public final class hep/dataforge/data/DataTree : hep/dataforge/data/DataNode {
public fun getItems ()Ljava/util/Map;
public fun getMeta ()Lhep/dataforge/meta/Meta;
public fun getType ()Lkotlin/reflect/KClass;
public fun startAll (Lkotlinx/coroutines/CoroutineScope;)Lkotlinx/coroutines/Job;
public fun toMeta ()Lhep/dataforge/meta/Meta;
}
public final class hep/dataforge/data/DataTreeBuilder {
public fun <init> (Lkotlin/reflect/KClass;)V
public final fun build ()Lhep/dataforge/data/DataTree;
public final fun getType ()Lkotlin/reflect/KClass;
public final fun meta (Lhep/dataforge/meta/Meta;)V
public final fun meta (Lkotlin/jvm/functions/Function1;)Lhep/dataforge/meta/MetaBuilder;
public final fun put (Ljava/lang/String;Lhep/dataforge/data/Data;)V
public final fun put (Ljava/lang/String;Lhep/dataforge/data/DataItem;)V
public final fun put (Ljava/lang/String;Lhep/dataforge/data/DataNode;)V
public final fun put (Ljava/lang/String;Lkotlin/jvm/functions/Function1;)V
public final fun set (Lhep/dataforge/names/Name;Lhep/dataforge/data/Data;)V
public final fun set (Lhep/dataforge/names/Name;Lhep/dataforge/data/DataItem;)V
public final fun set (Lhep/dataforge/names/Name;Lhep/dataforge/data/DataNode;)V
public final fun set (Lhep/dataforge/names/Name;Lhep/dataforge/data/DataTreeBuilder;)V
public final fun set (Lhep/dataforge/names/NameToken;Lhep/dataforge/data/Data;)V
public final fun set (Lhep/dataforge/names/NameToken;Lhep/dataforge/data/DataTreeBuilder;)V
public final fun update (Lhep/dataforge/data/DataNode;)V
}
public final class hep/dataforge/data/Dependencies : kotlin/coroutines/CoroutineContext$Element {
public static final field Companion Lhep/dataforge/data/Dependencies$Companion;
public fun <init> (Ljava/util/Collection;)V
public fun fold (Ljava/lang/Object;Lkotlin/jvm/functions/Function2;)Ljava/lang/Object;
public fun get (Lkotlin/coroutines/CoroutineContext$Key;)Lkotlin/coroutines/CoroutineContext$Element;
public fun getKey ()Lkotlin/coroutines/CoroutineContext$Key;
public final fun getValues ()Ljava/util/Collection;
public fun minusKey (Lkotlin/coroutines/CoroutineContext$Key;)Lkotlin/coroutines/CoroutineContext;
public fun plus (Lkotlin/coroutines/CoroutineContext;)Lkotlin/coroutines/CoroutineContext;
}
public final class hep/dataforge/data/Dependencies$Companion : kotlin/coroutines/CoroutineContext$Key {
}
public final class hep/dataforge/data/DynamicData : hep/dataforge/data/DynamicGoal, hep/dataforge/data/Data {
public fun <init> (Lkotlin/reflect/KClass;Lhep/dataforge/meta/Meta;Lkotlin/coroutines/CoroutineContext;Ljava/util/Collection;Lkotlin/jvm/functions/Function2;)V
public synthetic fun <init> (Lkotlin/reflect/KClass;Lhep/dataforge/meta/Meta;Lkotlin/coroutines/CoroutineContext;Ljava/util/Collection;Lkotlin/jvm/functions/Function2;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
public fun getMeta ()Lhep/dataforge/meta/Meta;
public fun getType ()Lkotlin/reflect/KClass;
public fun toMeta ()Lhep/dataforge/meta/Meta;
}
public class hep/dataforge/data/DynamicGoal : hep/dataforge/data/Goal {
public fun <init> (Lkotlin/coroutines/CoroutineContext;Ljava/util/Collection;Lkotlin/jvm/functions/Function2;)V
public synthetic fun <init> (Lkotlin/coroutines/CoroutineContext;Ljava/util/Collection;Lkotlin/jvm/functions/Function2;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
public final fun getBlock ()Lkotlin/jvm/functions/Function2;
public fun getDependencies ()Ljava/util/Collection;
public final fun getResult ()Lkotlinx/coroutines/Deferred;
public fun reset ()V
public fun startAsync (Lkotlinx/coroutines/CoroutineScope;)Lkotlinx/coroutines/Deferred;
}
public final class hep/dataforge/data/FragmentRule {
public field result Lkotlin/jvm/functions/Function2;
public fun <init> (Lhep/dataforge/names/Name;Lhep/dataforge/meta/MetaBuilder;)V
public final fun getMeta ()Lhep/dataforge/meta/MetaBuilder;
public final fun getName ()Lhep/dataforge/names/Name;
public final fun getResult ()Lkotlin/jvm/functions/Function2;
public final fun result (Lkotlin/jvm/functions/Function2;)V
public final fun setMeta (Lhep/dataforge/meta/MetaBuilder;)V
public final fun setResult (Lkotlin/jvm/functions/Function2;)V
}
public abstract interface class hep/dataforge/data/Goal {
public static final field Companion Lhep/dataforge/data/Goal$Companion;
public abstract fun getDependencies ()Ljava/util/Collection;
public abstract fun getResult ()Lkotlinx/coroutines/Deferred;
public abstract fun reset ()V
public abstract fun startAsync (Lkotlinx/coroutines/CoroutineScope;)Lkotlinx/coroutines/Deferred;
}
public final class hep/dataforge/data/Goal$Companion {
}
public final class hep/dataforge/data/GoalKt {
public static final fun await (Lhep/dataforge/data/Goal;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
public static final fun isComplete (Lhep/dataforge/data/Goal;)Z
public static final fun map (Lhep/dataforge/data/Goal;Lkotlin/coroutines/CoroutineContext;Lkotlin/jvm/functions/Function3;)Lhep/dataforge/data/Goal;
public static synthetic fun map$default (Lhep/dataforge/data/Goal;Lkotlin/coroutines/CoroutineContext;Lkotlin/jvm/functions/Function3;ILjava/lang/Object;)Lhep/dataforge/data/Goal;
public static final fun reduce (Ljava/util/Collection;Lkotlin/coroutines/CoroutineContext;Lkotlin/jvm/functions/Function3;)Lhep/dataforge/data/Goal;
public static final fun reduce (Ljava/util/Map;Lkotlin/coroutines/CoroutineContext;Lkotlin/jvm/functions/Function3;)Lhep/dataforge/data/Goal;
public static synthetic fun reduce$default (Ljava/util/Collection;Lkotlin/coroutines/CoroutineContext;Lkotlin/jvm/functions/Function3;ILjava/lang/Object;)Lhep/dataforge/data/Goal;
public static synthetic fun reduce$default (Ljava/util/Map;Lkotlin/coroutines/CoroutineContext;Lkotlin/jvm/functions/Function3;ILjava/lang/Object;)Lhep/dataforge/data/Goal;
}
public abstract interface class hep/dataforge/data/GroupRule {
public static final field Companion Lhep/dataforge/data/GroupRule$Companion;
public abstract fun invoke (Lhep/dataforge/data/DataNode;)Ljava/util/Map;
}
public final class hep/dataforge/data/GroupRule$Companion {
public final fun byMeta (Lhep/dataforge/meta/Meta;)Lhep/dataforge/data/GroupRule;
public final fun byValue (Ljava/lang/String;Ljava/lang/String;)Lhep/dataforge/data/GroupRule;
}
public final class hep/dataforge/data/JoinGroup {
public field result Lkotlin/jvm/functions/Function3;
public fun <init> (Ljava/lang/String;Lhep/dataforge/data/DataNode;)V
public final fun getMeta ()Lhep/dataforge/meta/MetaBuilder;
public final fun getName ()Ljava/lang/String;
public final fun getResult ()Lkotlin/jvm/functions/Function3;
public final fun result (Lkotlin/jvm/functions/Function3;)V
public final fun setMeta (Lhep/dataforge/meta/MetaBuilder;)V
public final fun setName (Ljava/lang/String;)V
public final fun setResult (Lkotlin/jvm/functions/Function3;)V
}
public final class hep/dataforge/data/MapAction : hep/dataforge/data/Action {
public fun <init> (Lkotlin/reflect/KClass;Lkotlin/reflect/KClass;Lkotlin/jvm/functions/Function1;)V
public final fun getInputType ()Lkotlin/reflect/KClass;
public final fun getOutputType ()Lkotlin/reflect/KClass;
public fun invoke (Lhep/dataforge/data/DataNode;Lhep/dataforge/meta/Meta;)Lhep/dataforge/data/DataNode;
public fun isTerminal ()Z
}
public final class hep/dataforge/data/MapActionBuilder {
public field result Lkotlin/jvm/functions/Function3;
public fun <init> (Lhep/dataforge/names/Name;Lhep/dataforge/meta/MetaBuilder;Lhep/dataforge/meta/Meta;)V
public final fun getActionMeta ()Lhep/dataforge/meta/Meta;
public final fun getMeta ()Lhep/dataforge/meta/MetaBuilder;
public final fun getName ()Lhep/dataforge/names/Name;
public final fun getResult ()Lkotlin/jvm/functions/Function3;
public final fun result (Lkotlin/jvm/functions/Function3;)V
public final fun setMeta (Lhep/dataforge/meta/MetaBuilder;)V
public final fun setName (Lhep/dataforge/names/Name;)V
public final fun setResult (Lkotlin/jvm/functions/Function3;)V
}
public final class hep/dataforge/data/NamedData : hep/dataforge/data/Data {
public fun <init> (Ljava/lang/String;Lhep/dataforge/data/Data;)V
public fun getDependencies ()Ljava/util/Collection;
public fun getMeta ()Lhep/dataforge/meta/Meta;
public final fun getName ()Ljava/lang/String;
public fun getResult ()Lkotlinx/coroutines/Deferred;
public fun getType ()Lkotlin/reflect/KClass;
public fun reset ()V
public fun startAsync (Lkotlinx/coroutines/CoroutineScope;)Lkotlinx/coroutines/Deferred;
public fun toMeta ()Lhep/dataforge/meta/Meta;
}
public final class hep/dataforge/data/ReduceAction : hep/dataforge/data/Action {
public fun <init> (Lkotlin/reflect/KClass;Lkotlin/reflect/KClass;Lkotlin/jvm/functions/Function1;)V
public final fun getInputType ()Lkotlin/reflect/KClass;
public final fun getOutputType ()Lkotlin/reflect/KClass;
public fun invoke (Lhep/dataforge/data/DataNode;Lhep/dataforge/meta/Meta;)Lhep/dataforge/data/DataNode;
public fun isTerminal ()Z
}
public final class hep/dataforge/data/ReduceActionKt {
public static final fun get (Ljava/util/Map;Ljava/lang/String;)Ljava/lang/Object;
}
public final class hep/dataforge/data/ReduceGroupBuilder {
public fun <init> (Lhep/dataforge/meta/Meta;)V
public final fun byValue (Ljava/lang/String;Ljava/lang/String;Lkotlin/jvm/functions/Function1;)V
public static synthetic fun byValue$default (Lhep/dataforge/data/ReduceGroupBuilder;Ljava/lang/String;Ljava/lang/String;Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)V
public final fun getActionMeta ()Lhep/dataforge/meta/Meta;
public final fun group (Ljava/lang/String;Lhep/dataforge/data/DataFilter;Lkotlin/jvm/functions/Function1;)V
public final fun group (Ljava/lang/String;Lkotlin/jvm/functions/Function2;Lkotlin/jvm/functions/Function1;)V
public final fun result (Ljava/lang/String;Lkotlin/jvm/functions/Function3;)V
}
public final class hep/dataforge/data/SplitAction : hep/dataforge/data/Action {
public fun <init> (Lkotlin/reflect/KClass;Lkotlin/reflect/KClass;Lkotlin/jvm/functions/Function1;)V
public final fun getInputType ()Lkotlin/reflect/KClass;
public final fun getOutputType ()Lkotlin/reflect/KClass;
public fun invoke (Lhep/dataforge/data/DataNode;Lhep/dataforge/meta/Meta;)Lhep/dataforge/data/DataNode;
public fun isTerminal ()Z
}
public final class hep/dataforge/data/SplitBuilder {
public fun <init> (Lhep/dataforge/names/Name;Lhep/dataforge/meta/Meta;)V
public final fun fragment (Ljava/lang/String;Lkotlin/jvm/functions/Function1;)V
public final fun getMeta ()Lhep/dataforge/meta/Meta;
public final fun getName ()Lhep/dataforge/names/Name;
}
public final class hep/dataforge/data/StaticData : hep/dataforge/data/StaticGoal, hep/dataforge/data/Data {
public fun <init> (Ljava/lang/Object;Lhep/dataforge/meta/Meta;)V
public synthetic fun <init> (Ljava/lang/Object;Lhep/dataforge/meta/Meta;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
public fun getMeta ()Lhep/dataforge/meta/Meta;
public fun getType ()Lkotlin/reflect/KClass;
public fun toMeta ()Lhep/dataforge/meta/Meta;
}
public class hep/dataforge/data/StaticGoal : hep/dataforge/data/Goal {
public fun <init> (Ljava/lang/Object;)V
public fun getDependencies ()Ljava/util/Collection;
public fun getResult ()Lkotlinx/coroutines/Deferred;
public final fun getValue ()Ljava/lang/Object;
public fun reset ()V
public fun startAsync (Lkotlinx/coroutines/CoroutineScope;)Lkotlinx/coroutines/Deferred;
}
public final class hep/dataforge/data/TypeFilteredDataNode : hep/dataforge/data/DataNode {
public fun <init> (Lhep/dataforge/data/DataNode;Lkotlin/reflect/KClass;)V
public fun getItems ()Ljava/util/Map;
public fun getMeta ()Lhep/dataforge/meta/Meta;
public final fun getOrigin ()Lhep/dataforge/data/DataNode;
public fun getType ()Lkotlin/reflect/KClass;
public fun startAll (Lkotlinx/coroutines/CoroutineScope;)Lkotlinx/coroutines/Job;
public fun toMeta ()Lhep/dataforge/meta/Meta;
}

@ -1,29 +1,23 @@
plugins {
id("scientifik.mpp")
id("ru.mipt.npm.mpp")
id("ru.mipt.npm.native")
}
val coroutinesVersion: String = Scientifik.coroutinesVersion
kscience{
useCoroutines()
}
kotlin {
sourceSets {
val commonMain by getting{
commonMain{
dependencies {
api(project(":dataforge-meta"))
api("org.jetbrains.kotlinx:kotlinx-coroutines-core-common:$coroutinesVersion")
}
}
val jvmMain by getting{
dependencies {
api("org.jetbrains.kotlinx:kotlinx-coroutines-core:$coroutinesVersion")
jvmMain{
dependencies{
api(kotlin("reflect"))
}
}
val jsMain by getting{
dependencies {
api("org.jetbrains.kotlinx:kotlinx-coroutines-core-js:$coroutinesVersion")
}
}
}
}

@ -5,23 +5,23 @@ import hep.dataforge.meta.Meta
/**
* A simple data transformation on a data node
*/
interface Action<in T : Any, out R : Any> {
public interface Action<in T : Any, out R : Any> {
/**
* Transform the data in the node, producing a new node. By default it is assumed that all calculations are lazy
* so not actual computation is started at this moment
*/
operator fun invoke(node: DataNode<T>, meta: Meta): DataNode<R>
public operator fun invoke(node: DataNode<T>, meta: Meta): DataNode<R>
/**
* Terminal action is the one that could not be invoked lazily and requires some kind of blocking computation to invoke
*/
val isTerminal: Boolean get() = false
public val isTerminal: Boolean get() = false
}
/**
* Action composition. The result is terminal if one of its parts is terminal
*/
infix fun <T : Any, I : Any, R : Any> Action<T, I>.then(action: Action<I, R>): Action<T, R> {
public infix fun <T : Any, I : Any, R : Any> Action<T, I>.then(action: Action<I, R>): Action<T, R> {
// TODO introduce composite action and add optimize by adding action to the list
return object : Action<T, R> {
override fun invoke(node: DataNode<T>, meta: Meta): DataNode<R> {

@ -1,48 +1,58 @@
package hep.dataforge.data
import hep.dataforge.meta.DFExperimental
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Job
import kotlin.coroutines.CoroutineContext
/**
* A monitor of goal state that could be accessed only form inside the goal
*/
class CoroutineMonitor : CoroutineContext.Element {
@DFExperimental
public class CoroutineMonitor : CoroutineContext.Element {
override val key: CoroutineContext.Key<*> get() = CoroutineMonitor
var totalWork: Double = 1.0
var workDone: Double = 0.0
var status: String = ""
public var totalWork: Double = 1.0
public var workDone: Double = 0.0
public var status: String = ""
/**
* Mark the goal as started
*/
fun start() {
public fun start() {
}
/**
* Mark the goal as completed
*/
fun finish() {
public fun finish() {
workDone = totalWork
}
companion object : CoroutineContext.Key<CoroutineMonitor>
public companion object : CoroutineContext.Key<CoroutineMonitor>
}
class Dependencies(val values: Collection<Job>) : CoroutineContext.Element {
public class Dependencies(public val values: Collection<Job>) : CoroutineContext.Element {
override val key: CoroutineContext.Key<*> get() = Dependencies
companion object : CoroutineContext.Key<Dependencies>
public companion object : CoroutineContext.Key<Dependencies>
}
val CoroutineContext.monitor: CoroutineMonitor? get() = this[CoroutineMonitor]
val CoroutineScope.monitor: CoroutineMonitor? get() = coroutineContext.monitor
@DFExperimental
public val CoroutineContext.monitor: CoroutineMonitor? get() = this[CoroutineMonitor]
@DFExperimental
public val CoroutineScope.monitor: CoroutineMonitor? get() = coroutineContext.monitor
val Job.dependencies: Collection<Job> get() = this[Dependencies]?.values ?: emptyList()
public val Job.dependencies: Collection<Job> get() = this[Dependencies]?.values ?: emptyList()
val Job.totalWork: Double get() = dependencies.sumByDouble { totalWork } + (monitor?.totalWork ?: 0.0)
val Job.workDone: Double get() = dependencies.sumByDouble { workDone } + (monitor?.workDone ?: 0.0)
val Job.status: String get() = monitor?.status ?: ""
val Job.progress: Double get() = workDone / totalWork
@DFExperimental
public val Job.totalWork: Double get() = dependencies.sumByDouble { totalWork } + (monitor?.totalWork ?: 0.0)
@DFExperimental
public val Job.workDone: Double get() = dependencies.sumByDouble { workDone } + (monitor?.workDone ?: 0.0)
@DFExperimental
public val Job.status: String get() = monitor?.status ?: ""
@DFExperimental
public val Job.progress: Double get() = workDone / totalWork

@ -11,15 +11,16 @@ import kotlin.reflect.KClass
/**
* A data element characterized by its meta
*/
interface Data<out T : Any> : Goal<T>, MetaRepr{
public interface Data<out T : Any> : Goal<T>, MetaRepr{
/**
* Type marker for the data. The type is known before the calculation takes place so it could be checked.
*/
val type: KClass<out T>
public val type: KClass<out T>
/**
* Meta for the data
*/
val meta: Meta
public val meta: Meta
override fun toMeta(): Meta = Meta {
"type" put (type.simpleName?:"undefined")
@ -28,10 +29,10 @@ interface Data<out T : Any> : Goal<T>, MetaRepr{
}
}
companion object {
const val TYPE = "data"
public companion object {
public const val TYPE: String = "data"
operator fun <T : Any> invoke(
public operator fun <T : Any> invoke(
type: KClass<out T>,
meta: Meta = Meta.EMPTY,
context: CoroutineContext = EmptyCoroutineContext,
@ -39,14 +40,14 @@ interface Data<out T : Any> : Goal<T>, MetaRepr{
block: suspend CoroutineScope.() -> T
): Data<T> = DynamicData(type, meta, context, dependencies, block)
inline operator fun <reified T : Any> invoke(
public inline operator fun <reified T : Any> invoke(
meta: Meta = Meta.EMPTY,
context: CoroutineContext = EmptyCoroutineContext,
dependencies: Collection<Data<*>> = emptyList(),
noinline block: suspend CoroutineScope.() -> T
): Data<T> = invoke(T::class, meta, context, dependencies, block)
operator fun <T : Any> invoke(
public operator fun <T : Any> invoke(
name: String,
type: KClass<out T>,
meta: Meta = Meta.EMPTY,
@ -55,7 +56,7 @@ interface Data<out T : Any> : Goal<T>, MetaRepr{
block: suspend CoroutineScope.() -> T
): Data<T> = NamedData(name, invoke(type, meta, context, dependencies, block))
inline operator fun <reified T : Any> invoke(
public inline operator fun <reified T : Any> invoke(
name: String,
meta: Meta = Meta.EMPTY,
context: CoroutineContext = EmptyCoroutineContext,
@ -64,13 +65,13 @@ interface Data<out T : Any> : Goal<T>, MetaRepr{
): Data<T> =
invoke(name, T::class, meta, context, dependencies, block)
fun <T : Any> static(value: T, meta: Meta = Meta.EMPTY): Data<T> =
public fun <T : Any> static(value: T, meta: Meta = Meta.EMPTY): Data<T> =
StaticData(value, meta)
}
}
class DynamicData<T : Any>(
public class DynamicData<T : Any>(
override val type: KClass<out T>,
override val meta: Meta = Meta.EMPTY,
context: CoroutineContext = EmptyCoroutineContext,
@ -78,16 +79,16 @@ class DynamicData<T : Any>(
block: suspend CoroutineScope.() -> T
) : Data<T>, DynamicGoal<T>(context, dependencies, block)
class StaticData<T : Any>(
public class StaticData<T : Any>(
value: T,
override val meta: Meta = Meta.EMPTY
) : Data<T>, StaticGoal<T>(value) {
override val type: KClass<out T> get() = value::class
}
class NamedData<out T : Any>(val name: String, data: Data<T>) : Data<T> by data
public class NamedData<out T : Any>(public val name: String, data: Data<T>) : Data<T> by data
fun <T : Any, R : Any> Data<T>.map(
public fun <T : Any, R : Any> Data<T>.map(
outputType: KClass<out R>,
coroutineContext: CoroutineContext = EmptyCoroutineContext,
meta: Meta = this.meta,
@ -100,7 +101,7 @@ fun <T : Any, R : Any> Data<T>.map(
/**
* Create a data pipe
*/
inline fun <T : Any, reified R : Any> Data<T>.map(
public inline fun <T : Any, reified R : Any> Data<T>.map(
coroutineContext: CoroutineContext = EmptyCoroutineContext,
meta: Meta = this.meta,
noinline block: suspend CoroutineScope.(T) -> R
@ -111,7 +112,7 @@ inline fun <T : Any, reified R : Any> Data<T>.map(
/**
* Create a joined data.
*/
inline fun <T : Any, reified R : Any> Collection<Data<T>>.reduce(
public inline fun <T : Any, reified R : Any> Collection<Data<T>>.reduce(
coroutineContext: CoroutineContext = EmptyCoroutineContext,
meta: Meta,
noinline block: suspend CoroutineScope.(Collection<T>) -> R
@ -124,7 +125,7 @@ inline fun <T : Any, reified R : Any> Collection<Data<T>>.reduce(
block(map { run { it.await() } })
}
fun <K, T : Any, R : Any> Map<K, Data<T>>.reduce(
public fun <K, T : Any, R : Any> Map<K, Data<T>>.reduce(
outputType: KClass<out R>,
coroutineContext: CoroutineContext = EmptyCoroutineContext,
meta: Meta,
@ -145,7 +146,7 @@ fun <K, T : Any, R : Any> Map<K, Data<T>>.reduce(
* @param T type of the input goal
* @param R type of the result goal
*/
inline fun <K, T : Any, reified R : Any> Map<K, Data<T>>.reduce(
public inline fun <K, T : Any, reified R : Any> Map<K, Data<T>>.reduce(
coroutineContext: CoroutineContext = EmptyCoroutineContext,
meta: Meta,
noinline block: suspend CoroutineScope.(Map<K, T>) -> R

@ -4,31 +4,29 @@ import hep.dataforge.meta.*
import hep.dataforge.names.toName
class DataFilter : Scheme() {
public class DataFilter : Scheme() {
/**
* A source node for the filter
*/
var from by string()
public var from: String? by string()
/**
* A target placement for the filtered node
*/
var to by string()
public var to: String? by string()
/**
* A regular expression pattern for the filter
*/
var pattern by string(".*")
public var pattern: String by string(".*")
// val prefix by string()
// val suffix by string()
fun isEmpty(): Boolean = config.isEmpty()
companion object : SchemeSpec<DataFilter>(::DataFilter)
public companion object : SchemeSpec<DataFilter>(::DataFilter)
}
/**
* Apply meta-based filter to given data node
*/
fun <T : Any> DataNode<T>.filter(filter: DataFilter): DataNode<T> {
public fun <T : Any> DataNode<T>.filter(filter: DataFilter): DataNode<T> {
val sourceNode = filter.from?.let { get(it.toName()).node } ?: this@filter
val regex = filter.pattern.toRegex()
val targetNode = DataTreeBuilder(type).apply {
@ -46,10 +44,10 @@ fun <T : Any> DataNode<T>.filter(filter: DataFilter): DataNode<T> {
/**
* Filter data using [DataFilter] specification
*/
fun <T : Any> DataNode<T>.filter(filter: Meta): DataNode<T> = filter(DataFilter.wrap(filter))
public fun <T : Any> DataNode<T>.filter(filter: Meta): DataNode<T> = filter(DataFilter.read(filter))
/**
* Filter data using [DataFilter] builder
*/
fun <T : Any> DataNode<T>.filter(filterBuilder: DataFilter.() -> Unit): DataNode<T> =
public fun <T : Any> DataNode<T>.filter(filterBuilder: DataFilter.() -> Unit): DataNode<T> =
filter(DataFilter(filterBuilder))

@ -11,12 +11,12 @@ import kotlin.collections.component2
import kotlin.collections.set
import kotlin.reflect.KClass
sealed class DataItem<out T : Any> : MetaRepr {
abstract val type: KClass<out T>
public sealed class DataItem<out T : Any> : MetaRepr {
public abstract val type: KClass<out T>
abstract val meta: Meta
public abstract val meta: Meta
class Node<out T : Any>(val node: DataNode<T>) : DataItem<T>() {
public class Node<out T : Any>(public val node: DataNode<T>) : DataItem<T>() {
override val type: KClass<out T> get() = node.type
override fun toMeta(): Meta = node.toMeta()
@ -24,7 +24,7 @@ sealed class DataItem<out T : Any> : MetaRepr {
override val meta: Meta get() = node.meta
}
class Leaf<out T : Any>(val data: Data<T>) : DataItem<T>() {
public class Leaf<out T : Any>(public val data: Data<T>) : DataItem<T>() {
override val type: KClass<out T> get() = data.type
override fun toMeta(): Meta = data.toMeta()
@ -36,16 +36,16 @@ sealed class DataItem<out T : Any> : MetaRepr {
/**
* A tree-like data structure grouped into the node. All data inside the node must inherit its type
*/
interface DataNode<out T : Any> : MetaRepr {
public interface DataNode<out T : Any> : MetaRepr {
/**
* The minimal common ancestor to all data in the node
*/
val type: KClass<out T>
public val type: KClass<out T>
val items: Map<NameToken, DataItem<T>>
public val items: Map<NameToken, DataItem<T>>
val meta: Meta
public val meta: Meta
override fun toMeta(): Meta = Meta {
"type" put (type.simpleName ?: "undefined")
@ -60,7 +60,7 @@ interface DataNode<out T : Any> : MetaRepr {
* Start computation for all goals in data node and return a job for the whole node
*/
@Suppress("DeferredResultUnused")
fun CoroutineScope.startAll(): Job = launch {
public fun CoroutineScope.startAll(): Job = launch {
items.values.forEach {
when (it) {
is DataItem.Node<*> -> it.node.run { startAll() }
@ -69,36 +69,36 @@ interface DataNode<out T : Any> : MetaRepr {
}
}
companion object {
const val TYPE = "dataNode"
public companion object {
public const val TYPE: String = "dataNode"
operator fun <T : Any> invoke(type: KClass<out T>, block: DataTreeBuilder<T>.() -> Unit) =
public operator fun <T : Any> invoke(type: KClass<out T>, block: DataTreeBuilder<T>.() -> Unit): DataTree<T> =
DataTreeBuilder(type).apply(block).build()
inline operator fun <reified T : Any> invoke(noinline block: DataTreeBuilder<T>.() -> Unit) =
public inline operator fun <reified T : Any> invoke(noinline block: DataTreeBuilder<T>.() -> Unit): DataTree<T> =
DataTreeBuilder(T::class).apply(block).build()
fun <T : Any> builder(type: KClass<out T>) = DataTreeBuilder(type)
public fun <T : Any> builder(type: KClass<out T>): DataTreeBuilder<T> = DataTreeBuilder(type)
}
}
suspend fun <T: Any> DataNode<T>.join(): Unit = coroutineScope { startAll().join() }
public suspend fun <T: Any> DataNode<T>.join(): Unit = coroutineScope { startAll().join() }
val <T : Any> DataItem<T>?.node: DataNode<T>? get() = (this as? DataItem.Node<T>)?.node
val <T : Any> DataItem<T>?.data: Data<T>? get() = (this as? DataItem.Leaf<T>)?.data
public val <T : Any> DataItem<T>?.node: DataNode<T>? get() = (this as? DataItem.Node<T>)?.node
public val <T : Any> DataItem<T>?.data: Data<T>? get() = (this as? DataItem.Leaf<T>)?.data
operator fun <T : Any> DataNode<T>.get(name: Name): DataItem<T>? = when (name.length) {
public operator fun <T : Any> DataNode<T>.get(name: Name): DataItem<T>? = when (name.length) {
0 -> error("Empty name")
1 -> items[name.first()]
else -> get(name.first()!!.asName()).node?.get(name.cutFirst())
1 -> items[name.firstOrNull()]
else -> get(name.firstOrNull()!!.asName()).node?.get(name.cutFirst())
}
operator fun <T : Any> DataNode<T>.get(name: String): DataItem<T>? = get(name.toName())
public operator fun <T : Any> DataNode<T>.get(name: String): DataItem<T>? = get(name.toName())
/**
* Sequence of all children including nodes
*/
fun <T : Any> DataNode<T>.asSequence(): Sequence<Pair<Name, DataItem<T>>> = sequence {
public fun <T : Any> DataNode<T>.asSequence(): Sequence<Pair<Name, DataItem<T>>> = sequence {
items.forEach { (head, item) ->
yield(head.asName() to item)
if (item is DataItem.Node) {
@ -112,7 +112,7 @@ fun <T : Any> DataNode<T>.asSequence(): Sequence<Pair<Name, DataItem<T>>> = sequ
/**
* Sequence of data entries
*/
fun <T : Any> DataNode<T>.dataSequence(): Sequence<Pair<Name, Data<T>>> = sequence {
public fun <T : Any> DataNode<T>.dataSequence(): Sequence<Pair<Name, Data<T>>> = sequence {
items.forEach { (head, item) ->
when (item) {
is DataItem.Leaf -> yield(head.asName() to item.data)
@ -125,9 +125,9 @@ fun <T : Any> DataNode<T>.dataSequence(): Sequence<Pair<Name, Data<T>>> = sequen
}
}
operator fun <T : Any> DataNode<T>.iterator(): Iterator<Pair<Name, DataItem<T>>> = asSequence().iterator()
public operator fun <T : Any> DataNode<T>.iterator(): Iterator<Pair<Name, DataItem<T>>> = asSequence().iterator()
class DataTree<out T : Any> internal constructor(
public class DataTree<out T : Any> internal constructor(
override val type: KClass<out T>,
override val items: Map<NameToken, DataItem<T>>,
override val meta: Meta
@ -142,17 +142,17 @@ private sealed class DataTreeBuilderItem<out T : Any> {
* A builder for a DataTree.
*/
@DFBuilder
class DataTreeBuilder<T : Any>(val type: KClass<out T>) {
public class DataTreeBuilder<T : Any>(public val type: KClass<out T>) {
private val map = HashMap<NameToken, DataTreeBuilderItem<T>>()
private var meta = MetaBuilder()
operator fun set(token: NameToken, node: DataTreeBuilder<out T>) {
public operator fun set(token: NameToken, node: DataTreeBuilder<out T>) {
if (map.containsKey(token)) error("Tree entry with name $token is not empty")
map[token] = DataTreeBuilderItem.Node(node)
}
operator fun set(token: NameToken, data: Data<T>) {
public operator fun set(token: NameToken, data: Data<T>) {
if (map.containsKey(token)) error("Tree entry with name $token is not empty")
map[token] = DataTreeBuilderItem.Leaf(data)
}
@ -168,30 +168,30 @@ class DataTreeBuilder<T : Any>(val type: KClass<out T>) {
private fun buildNode(name: Name): DataTreeBuilder<T> {
return when (name.length) {
0 -> this
1 -> buildNode(name.first()!!)
else -> buildNode(name.first()!!).buildNode(name.cutFirst())
1 -> buildNode(name.firstOrNull()!!)
else -> buildNode(name.firstOrNull()!!).buildNode(name.cutFirst())
}
}
operator fun set(name: Name, data: Data<T>) {
public operator fun set(name: Name, data: Data<T>) {
when (name.length) {
0 -> error("Can't add data with empty name")
1 -> set(name.first()!!, data)
2 -> buildNode(name.cutLast())[name.last()!!] = data
1 -> set(name.firstOrNull()!!, data)
2 -> buildNode(name.cutLast())[name.lastOrNull()!!] = data
}
}
operator fun set(name: Name, node: DataTreeBuilder<out T>) {
public operator fun set(name: Name, node: DataTreeBuilder<out T>) {
when (name.length) {
0 -> error("Can't add data with empty name")
1 -> set(name.first()!!, node)
2 -> buildNode(name.cutLast())[name.last()!!] = node
1 -> set(name.firstOrNull()!!, node)
2 -> buildNode(name.cutLast())[name.lastOrNull()!!] = node
}
}
operator fun set(name: Name, node: DataNode<T>) = set(name, node.builder())
public operator fun set(name: Name, node: DataNode<T>): Unit = set(name, node.builder())
operator fun set(name: Name, item: DataItem<T>) = when (item) {
public operator fun set(name: Name, item: DataItem<T>): Unit = when (item) {
is DataItem.Node<T> -> set(name, item.node.builder())
is DataItem.Leaf<T> -> set(name, item.data)
}
@ -199,25 +199,25 @@ class DataTreeBuilder<T : Any>(val type: KClass<out T>) {
/**
* Append data to node
*/
infix fun String.put(data: Data<T>) = set(toName(), data)
public infix fun String.put(data: Data<T>): Unit = set(toName(), data)
/**
* Append node
*/
infix fun String.put(node: DataNode<T>) = set(toName(), node)
public infix fun String.put(node: DataNode<T>): Unit = set(toName(), node)
infix fun String.put(item: DataItem<T>) = set(toName(), item)
public infix fun String.put(item: DataItem<T>): Unit = set(toName(), item)
/**
* Build and append node
*/
infix fun String.put(block: DataTreeBuilder<T>.() -> Unit) = set(toName(), DataTreeBuilder(type).apply(block))
public infix fun String.put(block: DataTreeBuilder<T>.() -> Unit): Unit = set(toName(), DataTreeBuilder(type).apply(block))
/**
* Update data with given node data and meta with node meta.
*/
fun update(node: DataNode<T>) {
public fun update(node: DataNode<T>) {
node.dataSequence().forEach {
//TODO check if the place is occupied
this[it.first] = it.second
@ -225,13 +225,13 @@ class DataTreeBuilder<T : Any>(val type: KClass<out T>) {
meta.update(node.meta)
}
fun meta(block: MetaBuilder.() -> Unit) = meta.apply(block)
public fun meta(block: MetaBuilder.() -> Unit): MetaBuilder = meta.apply(block)
fun meta(meta: Meta) {
public fun meta(meta: Meta) {
this.meta = meta.builder()
}
fun build(): DataTree<T> {
public fun build(): DataTree<T> {
val resMap = map.mapValues { (_, value) ->
when (value) {
is DataTreeBuilderItem.Leaf -> DataItem.Leaf(value.value)
@ -242,50 +242,50 @@ class DataTreeBuilder<T : Any>(val type: KClass<out T>) {
}
}
fun <T : Any> DataTreeBuilder<T>.datum(name: Name, data: Data<T>) {
public fun <T : Any> DataTreeBuilder<T>.datum(name: Name, data: Data<T>) {
this[name] = data
}
fun <T : Any> DataTreeBuilder<T>.datum(name: String, data: Data<T>) {
public fun <T : Any> DataTreeBuilder<T>.datum(name: String, data: Data<T>) {
this[name.toName()] = data
}
fun <T : Any> DataTreeBuilder<T>.static(name: Name, data: T, meta: Meta = Meta.EMPTY) {
public fun <T : Any> DataTreeBuilder<T>.static(name: Name, data: T, meta: Meta = Meta.EMPTY) {
this[name] = Data.static(data, meta)
}
fun <T : Any> DataTreeBuilder<T>.static(name: Name, data: T, block: MetaBuilder.() -> Unit = {}) {
public fun <T : Any> DataTreeBuilder<T>.static(name: Name, data: T, block: MetaBuilder.() -> Unit = {}) {
this[name] = Data.static(data, Meta(block))
}
fun <T : Any> DataTreeBuilder<T>.static(name: String, data: T, block: MetaBuilder.() -> Unit = {}) {
public fun <T : Any> DataTreeBuilder<T>.static(name: String, data: T, block: MetaBuilder.() -> Unit = {}) {
this[name.toName()] = Data.static(data, Meta(block))
}
fun <T : Any> DataTreeBuilder<T>.node(name: Name, node: DataNode<T>) {
public fun <T : Any> DataTreeBuilder<T>.node(name: Name, node: DataNode<T>) {
this[name] = node
}
fun <T : Any> DataTreeBuilder<T>.node(name: String, node: DataNode<T>) {
public fun <T : Any> DataTreeBuilder<T>.node(name: String, node: DataNode<T>) {
this[name.toName()] = node
}
inline fun <reified T : Any> DataTreeBuilder<T>.node(name: Name, noinline block: DataTreeBuilder<T>.() -> Unit) {
public inline fun <reified T : Any> DataTreeBuilder<T>.node(name: Name, noinline block: DataTreeBuilder<T>.() -> Unit) {
this[name] = DataNode(T::class, block)
}
inline fun <reified T : Any> DataTreeBuilder<T>.node(name: String, noinline block: DataTreeBuilder<T>.() -> Unit) {
public inline fun <reified T : Any> DataTreeBuilder<T>.node(name: String, noinline block: DataTreeBuilder<T>.() -> Unit) {
this[name.toName()] = DataNode(T::class, block)
}
/**
* Generate a mutable builder from this node. Node content is not changed
*/
fun <T : Any> DataNode<T>.builder(): DataTreeBuilder<T> = DataTreeBuilder(type).apply {
public fun <T : Any> DataNode<T>.builder(): DataTreeBuilder<T> = DataTreeBuilder(type).apply {
dataSequence().forEach { (name, data) -> this[name] = data }
}
fun <T : Any> DataNode<T>.filter(predicate: (Name, Data<T>) -> Boolean): DataNode<T> = DataNode.invoke(type) {
public fun <T : Any> DataNode<T>.filter(predicate: (Name, Data<T>) -> Boolean): DataNode<T> = DataNode.invoke(type) {
dataSequence().forEach { (name, data) ->
if (predicate(name, data)) {
this[name] = data
@ -293,4 +293,4 @@ fun <T : Any> DataNode<T>.filter(predicate: (Name, Data<T>) -> Boolean): DataNod
}
}
fun <T : Any> DataNode<T>.first(): Data<T>? = dataSequence().first().second
public fun <T : Any> DataNode<T>.first(): Data<T>? = dataSequence().first().second

@ -1,37 +1,36 @@
package hep.dataforge.data
import hep.dataforge.meta.DFExperimental
import kotlinx.coroutines.*
import kotlin.coroutines.CoroutineContext
import kotlin.coroutines.EmptyCoroutineContext
interface Goal<out T> {
val dependencies: Collection<Goal<*>>
public interface Goal<out T> {
public val dependencies: Collection<Goal<*>>
/**
* Returns current running coroutine if the goal is started
*/
val result: Deferred<T>?
public val result: Deferred<T>?
/**
* Get ongoing computation or start a new one.
* Does not guarantee thread safety. In case of multi-thread access, could create orphan computations.
*/
fun CoroutineScope.startAsync(): Deferred<T>
public fun CoroutineScope.startAsync(): Deferred<T>
/**
* Reset the computation
*/
fun reset()
public fun reset()
companion object {
}
public companion object
}
suspend fun <T> Goal<T>.await(): T = coroutineScope { startAsync().await() }
public suspend fun <T> Goal<T>.await(): T = coroutineScope { startAsync().await() }
val Goal<*>.isComplete get() = result?.isCompleted ?: false
public val Goal<*>.isComplete: Boolean get() = result?.isCompleted ?: false
open class StaticGoal<T>(val value: T) : Goal<T> {
public open class StaticGoal<T>(public val value: T) : Goal<T> {
override val dependencies: Collection<Goal<*>> get() = emptyList()
override val result: Deferred<T> = CompletableDeferred(value)
@ -42,10 +41,10 @@ open class StaticGoal<T>(val value: T) : Goal<T> {
}
}
open class DynamicGoal<T>(
val coroutineContext: CoroutineContext = EmptyCoroutineContext,
public open class DynamicGoal<T>(
private val coroutineContext: CoroutineContext = EmptyCoroutineContext,
override val dependencies: Collection<Goal<*>> = emptyList(),
val block: suspend CoroutineScope.() -> T
public val block: suspend CoroutineScope.() -> T
) : Goal<T> {
final override var result: Deferred<T>? = null
@ -55,6 +54,7 @@ open class DynamicGoal<T>(
* Get ongoing computation or start a new one.
* Does not guarantee thread safety. In case of multi-thread access, could create orphan computations.
*/
@DFExperimental
override fun CoroutineScope.startAsync(): Deferred<T> {
val startedDependencies = this@DynamicGoal.dependencies.map { goal ->
goal.run { startAsync() }
@ -82,7 +82,7 @@ open class DynamicGoal<T>(
/**
* Create a one-to-one goal based on existing goal
*/
fun <T, R> Goal<T>.map(
public fun <T, R> Goal<T>.map(
coroutineContext: CoroutineContext = EmptyCoroutineContext,
block: suspend CoroutineScope.(T) -> R
): Goal<R> = DynamicGoal(coroutineContext, listOf(this)) {
@ -92,7 +92,7 @@ fun <T, R> Goal<T>.map(
/**
* Create a joining goal.
*/
fun <T, R> Collection<Goal<T>>.reduce(
public fun <T, R> Collection<Goal<T>>.reduce(
coroutineContext: CoroutineContext = EmptyCoroutineContext,
block: suspend CoroutineScope.(Collection<T>) -> R
): Goal<R> = DynamicGoal(coroutineContext, this) {
@ -105,7 +105,7 @@ fun <T, R> Collection<Goal<T>>.reduce(
* @param T type of the input goal
* @param R type of the result goal
*/
fun <K, T, R> Map<K, Goal<T>>.reduce(
public fun <K, T, R> Map<K, Goal<T>>.reduce(
coroutineContext: CoroutineContext = EmptyCoroutineContext,
block: suspend CoroutineScope.(Map<K, T>) -> R
): Goal<R> = DynamicGoal(coroutineContext, this.values) {

@ -19,10 +19,10 @@ import hep.dataforge.meta.Meta
import hep.dataforge.meta.get
import hep.dataforge.meta.string
interface GroupRule {
operator fun <T : Any> invoke(node: DataNode<T>): Map<String, DataNode<T>>
public interface GroupRule {
public operator fun <T : Any> invoke(node: DataNode<T>): Map<String, DataNode<T>>
companion object{
public companion object{
/**
* Create grouping rule that creates groups for different values of value
* field with name [key]
@ -31,7 +31,7 @@ interface GroupRule {
* @param defaultTagValue
* @return
*/
fun byValue(key: String, defaultTagValue: String): GroupRule = object :
public fun byValue(key: String, defaultTagValue: String): GroupRule = object :
GroupRule {
override fun <T : Any> invoke(node: DataNode<T>): Map<String, DataNode<T>> {
val map = HashMap<String, DataTreeBuilder<T>>()
@ -52,7 +52,7 @@ interface GroupRule {
// def = "default",
// info = "Default value which should be used for content in which the grouping value is not presented"
// )
fun byMeta(config: Meta): GroupRule {
public fun byMeta(config: Meta): GroupRule {
//TODO expand grouping options
return config["byValue"]?.string?.let {
byValue(

@ -7,32 +7,31 @@ import kotlin.reflect.KClass
/**
* Action environment includes data name, data meta and action configuration meta
*/
data class ActionEnv(
public data class ActionEnv(
val name: Name,
val meta: Meta,
val actionMeta: Meta
)
/**
* Action environment
*/
@DFBuilder
class MapActionBuilder<T, R>(var name: Name, var meta: MetaBuilder, val actionMeta: Meta) {
lateinit var result: suspend ActionEnv.(T) -> R
public class MapActionBuilder<T, R>(public var name: Name, public var meta: MetaBuilder, public val actionMeta: Meta) {
public lateinit var result: suspend ActionEnv.(T) -> R
/**
* Calculate the result of goal
*/
fun result(f: suspend ActionEnv.(T) -> R) {
public fun result(f: suspend ActionEnv.(T) -> R) {
result = f;
}
}
class MapAction<T : Any, out R : Any>(
val inputType: KClass<T>,
val outputType: KClass<out R>,
public class MapAction<T : Any, out R : Any>(
public val inputType: KClass<T>,
public val outputType: KClass<out R>,
private val block: MapActionBuilder<T, R>.() -> Unit
) : Action<T, R> {
@ -67,7 +66,7 @@ class MapAction<T : Any, out R : Any>(
}
}
inline fun <reified T : Any, reified R : Any> DataNode<T>.map(
public inline fun <reified T : Any, reified R : Any> DataNode<T>.map(
meta: Meta,
noinline action: MapActionBuilder<in T, out R>.() -> Unit
): DataNode<R> = MapAction(T::class, R::class, action).invoke(this, meta)

@ -7,25 +7,25 @@ import hep.dataforge.names.toName
import kotlin.reflect.KClass
class JoinGroup<T : Any, R : Any>(var name: String, internal val node: DataNode<T>) {
public class JoinGroup<T : Any, R : Any>(public var name: String, internal val node: DataNode<T>) {
var meta: MetaBuilder = MetaBuilder()
public var meta: MetaBuilder = MetaBuilder()
lateinit var result: suspend ActionEnv.(Map<Name, T>) -> R
public lateinit var result: suspend ActionEnv.(Map<Name, T>) -> R
fun result(f: suspend ActionEnv.(Map<Name, T>) -> R) {
public fun result(f: suspend ActionEnv.(Map<Name, T>) -> R) {
this.result = f;
}
}
class ReduceGroupBuilder<T : Any, R : Any>(val actionMeta: Meta) {
public class ReduceGroupBuilder<T : Any, R : Any>(public val actionMeta: Meta) {
private val groupRules: MutableList<(DataNode<T>) -> List<JoinGroup<T, R>>> = ArrayList();
/**
* introduce grouping by value name
*/
fun byValue(tag: String, defaultTag: String = "@default", action: JoinGroup<T, R>.() -> Unit) {
public fun byValue(tag: String, defaultTag: String = "@default", action: JoinGroup<T, R>.() -> Unit) {
groupRules += { node ->
GroupRule.byValue(tag, defaultTag).invoke(node).map {
JoinGroup<T, R>(it.key, it.value).apply(action)
@ -36,7 +36,7 @@ class ReduceGroupBuilder<T : Any, R : Any>(val actionMeta: Meta) {
/**
* Add a single fixed group to grouping rules
*/
fun group(groupName: String, filter: DataFilter, action: JoinGroup<T, R>.() -> Unit) {
public fun group(groupName: String, filter: DataFilter, action: JoinGroup<T, R>.() -> Unit) {
groupRules += { node ->
listOf(
JoinGroup<T, R>(groupName, node.filter(filter)).apply(action)
@ -44,7 +44,7 @@ class ReduceGroupBuilder<T : Any, R : Any>(val actionMeta: Meta) {
}
}
fun group(groupName: String, filter: (Name, Data<T>) -> Boolean, action: JoinGroup<T, R>.() -> Unit) {
public fun group(groupName: String, filter: (Name, Data<T>) -> Boolean, action: JoinGroup<T, R>.() -> Unit) {
groupRules += { node ->
listOf(
JoinGroup<T, R>(groupName, node.filter(filter)).apply(action)
@ -55,7 +55,7 @@ class ReduceGroupBuilder<T : Any, R : Any>(val actionMeta: Meta) {
/**
* Apply transformation to the whole node
*/
fun result(resultName: String, f: suspend ActionEnv.(Map<Name, T>) -> R) {
public fun result(resultName: String, f: suspend ActionEnv.(Map<Name, T>) -> R) {
groupRules += { node ->
listOf(JoinGroup<T, R>(resultName, node).apply { result(f) })
}
@ -71,9 +71,9 @@ class ReduceGroupBuilder<T : Any, R : Any>(val actionMeta: Meta) {
/**
* The same rules as for KPipe
*/
class ReduceAction<T : Any, R : Any>(
val inputType: KClass<T>,
val outputType: KClass<out R>,
public class ReduceAction<T : Any, R : Any>(
public val inputType: KClass<T>,
public val outputType: KClass<out R>,
private val action: ReduceGroupBuilder<T, R>.() -> Unit
) : Action<T, R> {
@ -104,4 +104,4 @@ class ReduceAction<T : Any, R : Any>(
}
}
operator fun <T> Map<Name, T>.get(name: String) = get(name.toName())
public operator fun <T> Map<Name, T>.get(name: String): T? = get(name.toName())

@ -10,16 +10,16 @@ import kotlin.collections.set
import kotlin.reflect.KClass
class FragmentRule<T : Any, R : Any>(val name: Name, var meta: MetaBuilder) {
lateinit var result: suspend (T) -> R
public class FragmentRule<T : Any, R : Any>(public val name: Name, public var meta: MetaBuilder) {
public lateinit var result: suspend (T) -> R
fun result(f: suspend (T) -> R) {
public fun result(f: suspend (T) -> R) {
result = f;
}
}
class SplitBuilder<T : Any, R : Any>(val name: Name, val meta: Meta) {
public class SplitBuilder<T : Any, R : Any>(public val name: Name, public val meta: Meta) {
internal val fragments: MutableMap<Name, FragmentRule<T, R>.() -> Unit> = HashMap()
/**
@ -27,14 +27,14 @@ class SplitBuilder<T : Any, R : Any>(val name: Name, val meta: Meta) {
* @param name the name of a fragment
* @param rule the rule to transform fragment name and meta using
*/
fun fragment(name: String, rule: FragmentRule<T, R>.() -> Unit) {
public fun fragment(name: String, rule: FragmentRule<T, R>.() -> Unit) {
fragments[name.toName()] = rule
}
}
class SplitAction<T : Any, R : Any>(
val inputType: KClass<T>,
val outputType: KClass<out R>,
public class SplitAction<T : Any, R : Any>(
public val inputType: KClass<T>,
public val outputType: KClass<out R>,
private val action: SplitBuilder<T, R>.() -> Unit
) : Action<T, R> {

@ -6,7 +6,7 @@ import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Deferred
import kotlin.reflect.KClass
fun <R : Any, T : R> Data<T>.upcast(type: KClass<out R>): Data<R> {
public fun <R : Any, T : R> Data<T>.upcast(type: KClass<out R>): Data<R> {
return object : Data<R> by this {
override val type: KClass<out R> = type
}
@ -15,19 +15,19 @@ fun <R : Any, T : R> Data<T>.upcast(type: KClass<out R>): Data<R> {
/**
* Safe upcast a [Data] to a supertype
*/
inline fun <reified R : Any, T : R> Data<T>.upcast(): Data<R> = upcast(R::class)
public inline fun <reified R : Any, T : R> Data<T>.upcast(): Data<R> = upcast(R::class)
/**
* Check if node could be safely cast to given class
*/
expect fun <R : Any> DataNode<*>.canCast(type: KClass<out R>): Boolean
internal expect fun <R : Any> DataNode<*>.canCast(type: KClass<out R>): Boolean
/**
* Check if data could be safely cast to given class
*/
expect fun <R : Any> Data<*>.canCast(type: KClass<out R>): Boolean
internal expect fun <R : Any> Data<*>.canCast(type: KClass<out R>): Boolean
fun <R : Any> DataItem<*>.canCast(type: KClass<out R>): Boolean = when (this) {
public fun <R : Any> DataItem<*>.canCast(type: KClass<out R>): Boolean = when (this) {
is DataItem.Node -> node.canCast(type)
is DataItem.Leaf -> data.canCast(type)
}
@ -36,7 +36,7 @@ fun <R : Any> DataItem<*>.canCast(type: KClass<out R>): Boolean = when (this) {
* Unsafe cast of data node
*/
@Suppress("UNCHECKED_CAST")
fun <R : Any> Data<*>.cast(type: KClass<out R>): Data<R> {
public fun <R : Any> Data<*>.cast(type: KClass<out R>): Data<R> {
return object : Data<R> {
override val meta: Meta get() = this@cast.meta
override val dependencies: Collection<Goal<*>> get() = this@cast.dependencies
@ -47,10 +47,10 @@ fun <R : Any> Data<*>.cast(type: KClass<out R>): Data<R> {
}
}
inline fun <reified R : Any> Data<*>.cast(): Data<R> = cast(R::class)
public inline fun <reified R : Any> Data<*>.cast(): Data<R> = cast(R::class)
@Suppress("UNCHECKED_CAST")
fun <R : Any> DataNode<*>.cast(type: KClass<out R>): DataNode<R> {
public fun <R : Any> DataNode<*>.cast(type: KClass<out R>): DataNode<R> {
return object : DataNode<R> {
override val meta: Meta get() = this@cast.meta
override val type: KClass<out R> = type
@ -58,12 +58,12 @@ fun <R : Any> DataNode<*>.cast(type: KClass<out R>): DataNode<R> {
}
}
inline fun <reified R : Any> DataNode<*>.cast(): DataNode<R> = cast(R::class)
public inline fun <reified R : Any> DataNode<*>.cast(): DataNode<R> = cast(R::class)
/**
* Check that node is compatible with given type meaning that each element could be cast to the type
*/
fun <T : Any> DataNode<*>.ensureType(type: KClass<out T>) {
public fun <T : Any> DataNode<*>.ensureType(type: KClass<out T>) {
if (!canCast(type)) {
error("$type expected, but $type received")
}

@ -5,12 +5,10 @@ import kotlin.reflect.KClass
/**
* Check that node is compatible with given type meaning that each element could be cast to the type
*/
actual fun <R : Any> DataNode<*>.canCast(type: KClass<out R>): Boolean {
//Not supported in js yet
return true
internal actual fun <R : Any> DataNode<*>.canCast(type: KClass<out R>): Boolean {
return this.type == type
}
actual fun <R : Any> Data<*>.canCast(type: KClass<out R>): Boolean {
//Not supported in js yet
return true
internal actual fun <R : Any> Data<*>.canCast(type: KClass<out R>): Boolean {
return this.type == type
}

@ -8,7 +8,7 @@ import kotlin.reflect.KClass
/**
* A zero-copy data node wrapper that returns only children with appropriate type.
*/
class TypeFilteredDataNode<out T : Any>(val origin: DataNode<*>, override val type: KClass<out T>) : DataNode<T> {
public class TypeFilteredDataNode<out T : Any>(public val origin: DataNode<*>, override val type: KClass<out T>) : DataNode<T> {
override val meta: Meta get() = origin.meta
override val items: Map<NameToken, DataItem<T>> by lazy {
origin.items.mapNotNull { (key, item) ->

@ -3,33 +3,32 @@ 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
*/
fun <T : Any> Data<T>.get(): T = runBlocking { await() }
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
*/
actual fun <R : Any> DataNode<*>.canCast(type: KClass<out R>): Boolean =
type.isSuperclassOf(type)
internal actual fun <R : Any> DataNode<*>.canCast(type: KClass<out R>): Boolean =
type.isSubclassOf(this.type)
actual fun <R : Any> Data<*>.canCast(type: KClass<out R>): Boolean =
internal actual fun <R : Any> Data<*>.canCast(type: KClass<out R>): Boolean =
this.type.isSubclassOf(type)
/**
* Cast the node to given type if the cast is possible or return null
*/
fun <R : Any> Data<*>.filterIsInstance(type: KClass<out R>): Data<R>? =
public fun <R : Any> Data<*>.filterIsInstance(type: KClass<out R>): Data<R>? =
if (canCast(type)) cast(type) else null
/**
* Filter a node by data and node type. Resulting node and its subnodes is guaranteed to have border type [type],
* but could contain empty nodes
*/
fun <R : Any> DataNode<*>.filterIsInstance(type: KClass<out R>): DataNode<R> {
public fun <R : Any> DataNode<*>.filterIsInstance(type: KClass<out R>): DataNode<R> {
return when {
canCast(type) -> cast(type)
this is TypeFilteredDataNode -> origin.filterIsInstance(type)
@ -40,10 +39,10 @@ fun <R : Any> DataNode<*>.filterIsInstance(type: KClass<out R>): DataNode<R> {
/**
* Filter all elements of given data item that could be cast to given type. If no elements are available, return null.
*/
fun <R : Any> DataItem<*>?.filterIsInstance(type: KClass<out R>): DataItem<R>? = when (this) {
public fun <R : Any> DataItem<*>?.filterIsInstance(type: KClass<out R>): DataItem<R>? = when (this) {
null -> null
is DataItem.Node -> DataItem.Node(this.node.filterIsInstance(type))
is DataItem.Leaf -> this.data.filterIsInstance(type)?.let { DataItem.Leaf(it) }
}
inline fun <reified R : Any> DataItem<*>?.filterIsInstance(): DataItem<R>? = this@filterIsInstance.filterIsInstance(R::class)
public inline fun <reified R : Any> DataItem<*>?.filterIsInstance(): DataItem<R>? = this@filterIsInstance.filterIsInstance(R::class)

@ -0,0 +1,14 @@
package hep.dataforge.data
import kotlin.reflect.KClass
/**
* 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 {
return this.type == type
}
internal actual fun <R : Any> Data<*>.canCast(type: KClass<out R>): Boolean {
return this.type == type
}

@ -0,0 +1,469 @@
public final class hep/dataforge/io/BinaryMetaFormat : hep/dataforge/io/MetaFormat, hep/dataforge/io/MetaFormatFactory {
public static final field INSTANCE Lhep/dataforge/io/BinaryMetaFormat;
public fun getKey ()S
public fun getName ()Lhep/dataforge/names/Name;
public fun getShortName ()Ljava/lang/String;
public fun getType ()Lkotlin/reflect/KClass;
public fun invoke (Lhep/dataforge/meta/Meta;Lhep/dataforge/context/Context;)Lhep/dataforge/io/MetaFormat;
public synthetic fun invoke (Lhep/dataforge/meta/Meta;Lhep/dataforge/context/Context;)Ljava/lang/Object;
public fun readMeta (Lkotlinx/io/Input;Lhep/dataforge/meta/descriptors/NodeDescriptor;)Lhep/dataforge/meta/Meta;
public final fun readMetaItem (Lkotlinx/io/Input;)Lhep/dataforge/meta/MetaItem;
public fun readObject (Lkotlinx/io/Input;)Lhep/dataforge/meta/Meta;
public synthetic fun readObject (Lkotlinx/io/Input;)Ljava/lang/Object;
public fun toMeta ()Lhep/dataforge/meta/Meta;
public fun writeMeta (Lkotlinx/io/Output;Lhep/dataforge/meta/Meta;Lhep/dataforge/meta/descriptors/NodeDescriptor;)V
public fun writeObject (Lkotlinx/io/Output;Lhep/dataforge/meta/Meta;)V
public synthetic fun writeObject (Lkotlinx/io/Output;Ljava/lang/Object;)V
public final fun writeValue (Lkotlinx/io/Output;Lhep/dataforge/values/Value;)V
}
public final class hep/dataforge/io/BinaryView : kotlinx/io/Binary {
public fun <init> (Lkotlinx/io/Binary;II)V
public fun getSize ()I
public fun read (IILkotlin/jvm/functions/Function1;)Ljava/lang/Object;
}
public abstract interface class hep/dataforge/io/Consumer {
public abstract fun consume (Lhep/dataforge/io/Envelope;)V
}
public final class hep/dataforge/io/DoubleIOFormat : hep/dataforge/io/IOFormat, hep/dataforge/io/IOFormatFactory {
public static final field INSTANCE Lhep/dataforge/io/DoubleIOFormat;
public fun getName ()Lhep/dataforge/names/Name;
public fun getType ()Lkotlin/reflect/KClass;
public fun invoke (Lhep/dataforge/meta/Meta;Lhep/dataforge/context/Context;)Lhep/dataforge/io/IOFormat;
public synthetic fun invoke (Lhep/dataforge/meta/Meta;Lhep/dataforge/context/Context;)Ljava/lang/Object;
public fun readObject (Lkotlinx/io/Input;)Ljava/lang/Double;
public synthetic fun readObject (Lkotlinx/io/Input;)Ljava/lang/Object;
public fun toMeta ()Lhep/dataforge/meta/Meta;
public fun writeObject (Lkotlinx/io/Output;D)V
public synthetic fun writeObject (Lkotlinx/io/Output;Ljava/lang/Object;)V
}
public abstract interface class hep/dataforge/io/Envelope {
public static final field Companion Lhep/dataforge/io/Envelope$Companion;
public abstract fun getData ()Lkotlinx/io/Binary;
public abstract fun getMeta ()Lhep/dataforge/meta/Meta;
}
public final class hep/dataforge/io/Envelope$Companion {
public final fun getENVELOPE_DATA_ID_KEY ()Lhep/dataforge/names/Name;
public final fun getENVELOPE_DATA_TYPE_KEY ()Lhep/dataforge/names/Name;
public final fun getENVELOPE_DESCRIPTION_KEY ()Lhep/dataforge/names/Name;
public final fun getENVELOPE_NAME_KEY ()Lhep/dataforge/names/Name;
public final fun getENVELOPE_NODE_KEY ()Lhep/dataforge/names/Name;
public final fun getENVELOPE_TYPE_KEY ()Lhep/dataforge/names/Name;
public final fun invoke (Lkotlin/jvm/functions/Function1;)Lhep/dataforge/io/Envelope;
}
public final class hep/dataforge/io/EnvelopeBuilder : hep/dataforge/io/Envelope {
public fun <init> ()V
public final fun data (Lkotlin/jvm/functions/Function1;)V
public fun getData ()Lkotlinx/io/Binary;
public final fun getDataID ()Ljava/lang/String;
public final fun getDataType ()Ljava/lang/String;
public final fun getDescription ()Ljava/lang/String;
public fun getMeta ()Lhep/dataforge/meta/Meta;
public final fun getName ()Ljava/lang/String;
public final fun getType ()Ljava/lang/String;
public final fun meta (Lkotlin/jvm/functions/Function1;)V
public final fun seal ()Lhep/dataforge/io/Envelope;
public fun setData (Lkotlinx/io/Binary;)V
public final fun setDataID (Ljava/lang/String;)V
public final fun setDataType (Ljava/lang/String;)V
public final fun setDescription (Ljava/lang/String;)V
public fun setMeta (Lhep/dataforge/meta/Meta;)V
public final fun setName (Ljava/lang/String;)V
public final fun setType (Ljava/lang/String;)V
}
public abstract interface class hep/dataforge/io/EnvelopeFormat : hep/dataforge/io/IOFormat {
public abstract fun getDefaultMetaFormat ()Lhep/dataforge/io/MetaFormatFactory;
public abstract fun readObject (Lkotlinx/io/Input;)Lhep/dataforge/io/Envelope;
public abstract fun readPartial (Lkotlinx/io/Input;)Lhep/dataforge/io/PartialEnvelope;
public abstract fun writeEnvelope (Lkotlinx/io/Output;Lhep/dataforge/io/Envelope;Lhep/dataforge/io/MetaFormatFactory;Lhep/dataforge/meta/Meta;)V
public abstract fun writeObject (Lkotlinx/io/Output;Lhep/dataforge/io/Envelope;)V
}
public final class hep/dataforge/io/EnvelopeFormat$DefaultImpls {
public static fun getDefaultMetaFormat (Lhep/dataforge/io/EnvelopeFormat;)Lhep/dataforge/io/MetaFormatFactory;
public static synthetic fun writeEnvelope$default (Lhep/dataforge/io/EnvelopeFormat;Lkotlinx/io/Output;Lhep/dataforge/io/Envelope;Lhep/dataforge/io/MetaFormatFactory;Lhep/dataforge/meta/Meta;ILjava/lang/Object;)V
public static fun writeObject (Lhep/dataforge/io/EnvelopeFormat;Lkotlinx/io/Output;Lhep/dataforge/io/Envelope;)V
}
public abstract interface class hep/dataforge/io/EnvelopeFormatFactory : hep/dataforge/io/EnvelopeFormat, hep/dataforge/io/IOFormatFactory {
public static final field Companion Lhep/dataforge/io/EnvelopeFormatFactory$Companion;
public static final field ENVELOPE_FORMAT_TYPE Ljava/lang/String;
public abstract fun getName ()Lhep/dataforge/names/Name;
public abstract fun getType ()Lkotlin/reflect/KClass;
public abstract fun invoke (Lhep/dataforge/meta/Meta;Lhep/dataforge/context/Context;)Lhep/dataforge/io/EnvelopeFormat;
public abstract fun peekFormat (Lhep/dataforge/io/IOPlugin;Lkotlinx/io/Input;)Lhep/dataforge/io/EnvelopeFormat;
}
public final class hep/dataforge/io/EnvelopeFormatFactory$Companion {
public static final field ENVELOPE_FORMAT_TYPE Ljava/lang/String;
}
public final class hep/dataforge/io/EnvelopeFormatFactory$DefaultImpls {
public static fun getDefaultMetaFormat (Lhep/dataforge/io/EnvelopeFormatFactory;)Lhep/dataforge/io/MetaFormatFactory;
public static fun getName (Lhep/dataforge/io/EnvelopeFormatFactory;)Lhep/dataforge/names/Name;
public static fun getType (Lhep/dataforge/io/EnvelopeFormatFactory;)Lkotlin/reflect/KClass;
public static fun toMeta (Lhep/dataforge/io/EnvelopeFormatFactory;)Lhep/dataforge/meta/Meta;
public static fun writeObject (Lhep/dataforge/io/EnvelopeFormatFactory;Lkotlinx/io/Output;Lhep/dataforge/io/Envelope;)V
}
public final class hep/dataforge/io/EnvelopeFormatKt {
public static final fun read (Lhep/dataforge/io/EnvelopeFormat;Lkotlinx/io/Input;)Lhep/dataforge/io/Envelope;
}
public final class hep/dataforge/io/EnvelopeKt {
public static final fun contentEquals (Lhep/dataforge/io/Envelope;Lhep/dataforge/io/Envelope;)Z
public static final fun dataEquals (Lhep/dataforge/io/Envelope;Lhep/dataforge/io/Envelope;)Z
public static final fun getDataID (Lhep/dataforge/io/Envelope;)Ljava/lang/String;
public static final fun getDataType (Lhep/dataforge/io/Envelope;)Ljava/lang/String;
public static final fun getDescription (Lhep/dataforge/io/Envelope;)Ljava/lang/String;
public static final fun getType (Lhep/dataforge/io/Envelope;)Ljava/lang/String;
public static final fun metaEquals (Lhep/dataforge/io/Envelope;Lhep/dataforge/io/Envelope;)Z
public static final fun withMetaLayers (Lhep/dataforge/io/Envelope;[Lhep/dataforge/meta/Meta;)Lhep/dataforge/io/Envelope;
}
public final class hep/dataforge/io/EnvelopePart {
public fun <init> (Lkotlinx/io/Binary;Lhep/dataforge/meta/Meta;)V
public final fun component1 ()Lkotlinx/io/Binary;
public final fun component2 ()Lhep/dataforge/meta/Meta;
public final fun copy (Lkotlinx/io/Binary;Lhep/dataforge/meta/Meta;)Lhep/dataforge/io/EnvelopePart;
public static synthetic fun copy$default (Lhep/dataforge/io/EnvelopePart;Lkotlinx/io/Binary;Lhep/dataforge/meta/Meta;ILjava/lang/Object;)Lhep/dataforge/io/EnvelopePart;
public fun equals (Ljava/lang/Object;)Z
public final fun getBinary ()Lkotlinx/io/Binary;
public final fun getDescription ()Lhep/dataforge/meta/Meta;
public fun hashCode ()I
public fun toString ()Ljava/lang/String;
}
public final class hep/dataforge/io/EnvelopePartsKt {
public static final fun envelope (Lhep/dataforge/io/EnvelopePart;Lhep/dataforge/io/EnvelopeFormat;)Lhep/dataforge/io/Envelope;
public static final fun envelope (Lhep/dataforge/io/EnvelopePart;Lhep/dataforge/io/IOPlugin;)Lhep/dataforge/io/Envelope;
public static final fun envelopes (Lhep/dataforge/io/EnvelopeBuilder;Ljava/util/List;Lhep/dataforge/io/EnvelopeFormat;Ljava/lang/String;)V
public static synthetic fun envelopes$default (Lhep/dataforge/io/EnvelopeBuilder;Ljava/util/List;Lhep/dataforge/io/EnvelopeFormat;Ljava/lang/String;ILjava/lang/Object;)V
public static final fun getName (Lhep/dataforge/io/EnvelopePart;)Ljava/lang/String;
public static final fun multipart (Lhep/dataforge/io/EnvelopeBuilder;Ljava/util/List;Ljava/lang/String;)V
public static synthetic fun multipart$default (Lhep/dataforge/io/EnvelopeBuilder;Ljava/util/List;Ljava/lang/String;ILjava/lang/Object;)V
public static final fun parts (Lhep/dataforge/io/Envelope;)Ljava/util/List;
}
public final class hep/dataforge/io/FileIOKt {
public static final fun append (Ljava/nio/file/Path;Lkotlin/jvm/functions/Function1;)V
public static final fun getDATA_FILE_NAME (Lhep/dataforge/io/IOPlugin$Companion;)Ljava/lang/String;
public static final fun getMETA_FILE_NAME (Lhep/dataforge/io/IOPlugin$Companion;)Ljava/lang/String;
public static final fun peekBinaryFormat (Lhep/dataforge/io/IOPlugin;Ljava/nio/file/Path;)Lhep/dataforge/io/EnvelopeFormat;
public static final fun read (Ljava/nio/file/Path;Lkotlin/jvm/functions/Function1;)Ljava/lang/Object;
public static final fun readEnvelope (Ljava/nio/file/Path;Lhep/dataforge/io/EnvelopeFormat;)Lhep/dataforge/io/Envelope;
public static final fun readEnvelopeFile (Lhep/dataforge/io/IOPlugin;Ljava/nio/file/Path;ZLkotlin/jvm/functions/Function2;)Lhep/dataforge/io/Envelope;
public static synthetic fun readEnvelopeFile$default (Lhep/dataforge/io/IOPlugin;Ljava/nio/file/Path;ZLkotlin/jvm/functions/Function2;ILjava/lang/Object;)Lhep/dataforge/io/Envelope;
public static final fun readMetaFile (Lhep/dataforge/io/IOPlugin;Ljava/nio/file/Path;Lhep/dataforge/io/MetaFormat;Lhep/dataforge/meta/descriptors/NodeDescriptor;)Lhep/dataforge/meta/Meta;
public static synthetic fun readMetaFile$default (Lhep/dataforge/io/IOPlugin;Ljava/nio/file/Path;Lhep/dataforge/io/MetaFormat;Lhep/dataforge/meta/descriptors/NodeDescriptor;ILjava/lang/Object;)Lhep/dataforge/meta/Meta;
public static final fun rewrite (Ljava/nio/file/Path;Lkotlin/jvm/functions/Function1;)V
public static final fun write (Ljava/nio/file/Path;Lkotlin/jvm/functions/Function1;)V
public static final fun writeEnvelopeDirectory (Lhep/dataforge/io/IOPlugin;Ljava/nio/file/Path;Lhep/dataforge/io/Envelope;Lhep/dataforge/io/MetaFormatFactory;)V
public static synthetic fun writeEnvelopeDirectory$default (Lhep/dataforge/io/IOPlugin;Ljava/nio/file/Path;Lhep/dataforge/io/Envelope;Lhep/dataforge/io/MetaFormatFactory;ILjava/lang/Object;)V
public static final fun writeEnvelopeFile (Lhep/dataforge/io/IOPlugin;Ljava/nio/file/Path;Lhep/dataforge/io/Envelope;Lhep/dataforge/io/EnvelopeFormat;Lhep/dataforge/io/MetaFormatFactory;)V
public static synthetic fun writeEnvelopeFile$default (Lhep/dataforge/io/IOPlugin;Ljava/nio/file/Path;Lhep/dataforge/io/Envelope;Lhep/dataforge/io/EnvelopeFormat;Lhep/dataforge/io/MetaFormatFactory;ILjava/lang/Object;)V
public static final fun writeMetaFile (Lhep/dataforge/io/IOPlugin;Ljava/nio/file/Path;Lhep/dataforge/meta/Meta;Lhep/dataforge/io/MetaFormatFactory;Lhep/dataforge/meta/descriptors/NodeDescriptor;)V
public static synthetic fun writeMetaFile$default (Lhep/dataforge/io/IOPlugin;Ljava/nio/file/Path;Lhep/dataforge/meta/Meta;Lhep/dataforge/io/MetaFormatFactory;Lhep/dataforge/meta/descriptors/NodeDescriptor;ILjava/lang/Object;)V
public static final fun writeToFile (Lhep/dataforge/io/IOFormat;Ljava/nio/file/Path;Ljava/lang/Object;)V
}
public abstract interface class hep/dataforge/io/IOFormat : hep/dataforge/meta/MetaRepr {
public static final field Companion Lhep/dataforge/io/IOFormat$Companion;
public abstract fun readObject (Lkotlinx/io/Input;)Ljava/lang/Object;
public abstract fun writeObject (Lkotlinx/io/Output;Ljava/lang/Object;)V
}
public final class hep/dataforge/io/IOFormat$Companion {
public final fun getMETA_KEY ()Lhep/dataforge/names/Name;
public final fun getNAME_KEY ()Lhep/dataforge/names/Name;
}
public abstract interface class hep/dataforge/io/IOFormatFactory : hep/dataforge/context/Factory, hep/dataforge/context/Named, hep/dataforge/meta/MetaRepr {
public static final field Companion Lhep/dataforge/io/IOFormatFactory$Companion;
public static final field IO_FORMAT_TYPE Ljava/lang/String;
public abstract fun getType ()Lkotlin/reflect/KClass;
public abstract fun toMeta ()Lhep/dataforge/meta/Meta;
}
public final class hep/dataforge/io/IOFormatFactory$Companion {
public static final field IO_FORMAT_TYPE Ljava/lang/String;
}
public final class hep/dataforge/io/IOFormatFactory$DefaultImpls {
public static fun toMeta (Lhep/dataforge/io/IOFormatFactory;)Lhep/dataforge/meta/Meta;
}
public final class hep/dataforge/io/IOFormatKt {
public static final fun fill (Lkotlinx/io/pool/ObjectPool;Lkotlin/jvm/functions/Function1;)Ljava/nio/ByteBuffer;
public static final fun readWith (Lkotlinx/io/Binary;Lhep/dataforge/io/IOFormat;)Ljava/lang/Object;
public static final fun readWith (Lkotlinx/io/Input;Lhep/dataforge/io/IOFormat;)Ljava/lang/Object;
public static final fun toBinary (Lhep/dataforge/io/IOFormat;Ljava/lang/Object;)Lkotlinx/io/Binary;
public static final fun writeWith (Lkotlinx/io/Output;Lhep/dataforge/io/IOFormat;Ljava/lang/Object;)V
}
public final class hep/dataforge/io/IOPlugin : hep/dataforge/context/AbstractPlugin {
public static final field Companion Lhep/dataforge/io/IOPlugin$Companion;
public fun <init> (Lhep/dataforge/meta/Meta;)V
public fun content (Ljava/lang/String;)Ljava/util/Map;
public final fun getEnvelopeFormatFactories ()Ljava/util/Collection;
public final fun getIoFormatFactories ()Ljava/util/Collection;
public final fun getMetaFormatFactories ()Ljava/util/Collection;
public fun getTag ()Lhep/dataforge/context/PluginTag;
public final fun resolveEnvelopeFormat (Lhep/dataforge/meta/MetaItem;)Lhep/dataforge/io/EnvelopeFormat;
public final fun resolveIOFormat (Lhep/dataforge/meta/MetaItem;Lkotlin/reflect/KClass;)Lhep/dataforge/io/IOFormat;
public final fun resolveMetaFormat (Ljava/lang/String;Lhep/dataforge/meta/Meta;)Lhep/dataforge/io/MetaFormat;
public final fun resolveMetaFormat (SLhep/dataforge/meta/Meta;)Lhep/dataforge/io/MetaFormat;
public static synthetic fun resolveMetaFormat$default (Lhep/dataforge/io/IOPlugin;Ljava/lang/String;Lhep/dataforge/meta/Meta;ILjava/lang/Object;)Lhep/dataforge/io/MetaFormat;
public static synthetic fun resolveMetaFormat$default (Lhep/dataforge/io/IOPlugin;SLhep/dataforge/meta/Meta;ILjava/lang/Object;)Lhep/dataforge/io/MetaFormat;
}
public final class hep/dataforge/io/IOPlugin$Companion : hep/dataforge/context/PluginFactory {
public final fun getDefaultEnvelopeFormats ()Ljava/util/List;
public final fun getDefaultMetaFormats ()Ljava/util/List;
public fun getTag ()Lhep/dataforge/context/PluginTag;
public fun getType ()Lkotlin/reflect/KClass;
public fun invoke (Lhep/dataforge/meta/Meta;Lhep/dataforge/context/Context;)Lhep/dataforge/io/IOPlugin;
public synthetic fun invoke (Lhep/dataforge/meta/Meta;Lhep/dataforge/context/Context;)Ljava/lang/Object;
}
public final class hep/dataforge/io/IOPluginKt {
public static final fun getIo (Lhep/dataforge/context/Context;)Lhep/dataforge/io/IOPlugin;
}
public final class hep/dataforge/io/IoMiscKt {
public static final fun Binary (ILkotlin/jvm/functions/Function1;)Lkotlinx/io/Binary;
public static synthetic fun Binary$default (ILkotlin/jvm/functions/Function1;ILjava/lang/Object;)Lkotlinx/io/Binary;
public static final fun buildByteArray (ILkotlin/jvm/functions/Function1;)[B
public static synthetic fun buildByteArray$default (ILkotlin/jvm/functions/Function1;ILjava/lang/Object;)[B
public static final fun get (Lkotlinx/io/Binary;Lkotlin/ranges/IntRange;)Lhep/dataforge/io/BinaryView;
public static final fun readRawString (Lkotlinx/io/Input;I)Ljava/lang/String;
public static final fun view (Lkotlinx/io/Binary;II)Lhep/dataforge/io/BinaryView;
public static final fun writeRawString (Lkotlinx/io/Output;Ljava/lang/String;)V
}
public final class hep/dataforge/io/JsonMetaFormat : hep/dataforge/io/MetaFormat {
public static final field Companion Lhep/dataforge/io/JsonMetaFormat$Companion;
public fun <init> ()V
public fun <init> (Lkotlinx/serialization/json/Json;)V
public synthetic fun <init> (Lkotlinx/serialization/json/Json;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
public fun readMeta (Lkotlinx/io/Input;Lhep/dataforge/meta/descriptors/NodeDescriptor;)Lhep/dataforge/meta/Meta;
public fun readObject (Lkotlinx/io/Input;)Lhep/dataforge/meta/Meta;
public synthetic fun readObject (Lkotlinx/io/Input;)Ljava/lang/Object;
public fun toMeta ()Lhep/dataforge/meta/Meta;
public fun writeMeta (Lkotlinx/io/Output;Lhep/dataforge/meta/Meta;Lhep/dataforge/meta/descriptors/NodeDescriptor;)V
public fun writeObject (Lkotlinx/io/Output;Lhep/dataforge/meta/Meta;)V
public synthetic fun writeObject (Lkotlinx/io/Output;Ljava/lang/Object;)V
}
public final class hep/dataforge/io/JsonMetaFormat$Companion : hep/dataforge/io/MetaFormatFactory {
public final fun getDEFAULT_JSON ()Lkotlinx/serialization/json/Json;
public fun getKey ()S
public fun getName ()Lhep/dataforge/names/Name;
public fun getShortName ()Ljava/lang/String;
public fun getType ()Lkotlin/reflect/KClass;
public fun invoke (Lhep/dataforge/meta/Meta;Lhep/dataforge/context/Context;)Lhep/dataforge/io/MetaFormat;
public synthetic fun invoke (Lhep/dataforge/meta/Meta;Lhep/dataforge/context/Context;)Ljava/lang/Object;
public fun readMeta (Lkotlinx/io/Input;Lhep/dataforge/meta/descriptors/NodeDescriptor;)Lhep/dataforge/meta/Meta;
public fun readObject (Lkotlinx/io/Input;)Lhep/dataforge/meta/Meta;
public synthetic fun readObject (Lkotlinx/io/Input;)Ljava/lang/Object;
public fun toMeta ()Lhep/dataforge/meta/Meta;
public fun writeMeta (Lkotlinx/io/Output;Lhep/dataforge/meta/Meta;Lhep/dataforge/meta/descriptors/NodeDescriptor;)V
public fun writeObject (Lkotlinx/io/Output;Lhep/dataforge/meta/Meta;)V
public synthetic fun writeObject (Lkotlinx/io/Output;Ljava/lang/Object;)V
}
public final class hep/dataforge/io/ListIOFormat : hep/dataforge/io/IOFormat {
public fun <init> (Lhep/dataforge/io/IOFormat;)V
public final fun getFormat ()Lhep/dataforge/io/IOFormat;
public synthetic fun readObject (Lkotlinx/io/Input;)Ljava/lang/Object;
public fun readObject (Lkotlinx/io/Input;)Ljava/util/List;
public fun toMeta ()Lhep/dataforge/meta/Meta;
public synthetic fun writeObject (Lkotlinx/io/Output;Ljava/lang/Object;)V
public fun writeObject (Lkotlinx/io/Output;Ljava/util/List;)V
}
public abstract interface class hep/dataforge/io/MetaFormat : hep/dataforge/io/IOFormat {
public abstract fun readMeta (Lkotlinx/io/Input;Lhep/dataforge/meta/descriptors/NodeDescriptor;)Lhep/dataforge/meta/Meta;
public abstract fun readObject (Lkotlinx/io/Input;)Lhep/dataforge/meta/Meta;
public abstract fun writeMeta (Lkotlinx/io/Output;Lhep/dataforge/meta/Meta;Lhep/dataforge/meta/descriptors/NodeDescriptor;)V
public abstract fun writeObject (Lkotlinx/io/Output;Lhep/dataforge/meta/Meta;)V
}
public final class hep/dataforge/io/MetaFormat$DefaultImpls {
public static synthetic fun readMeta$default (Lhep/dataforge/io/MetaFormat;Lkotlinx/io/Input;Lhep/dataforge/meta/descriptors/NodeDescriptor;ILjava/lang/Object;)Lhep/dataforge/meta/Meta;
public static fun readObject (Lhep/dataforge/io/MetaFormat;Lkotlinx/io/Input;)Lhep/dataforge/meta/Meta;
public static synthetic fun writeMeta$default (Lhep/dataforge/io/MetaFormat;Lkotlinx/io/Output;Lhep/dataforge/meta/Meta;Lhep/dataforge/meta/descriptors/NodeDescriptor;ILjava/lang/Object;)V
public static fun writeObject (Lhep/dataforge/io/MetaFormat;Lkotlinx/io/Output;Lhep/dataforge/meta/Meta;)V
}
public abstract interface class hep/dataforge/io/MetaFormatFactory : hep/dataforge/io/IOFormatFactory, hep/dataforge/io/MetaFormat {
public static final field Companion Lhep/dataforge/io/MetaFormatFactory$Companion;
public static final field META_FORMAT_TYPE Ljava/lang/String;
public abstract fun getKey ()S
public abstract fun getName ()Lhep/dataforge/names/Name;
public abstract fun getShortName ()Ljava/lang/String;
public abstract fun getType ()Lkotlin/reflect/KClass;
public abstract fun invoke (Lhep/dataforge/meta/Meta;Lhep/dataforge/context/Context;)Lhep/dataforge/io/MetaFormat;
}
public final class hep/dataforge/io/MetaFormatFactory$Companion {
public static final field META_FORMAT_TYPE Ljava/lang/String;
}
public final class hep/dataforge/io/MetaFormatFactory$DefaultImpls {
public static fun getKey (Lhep/dataforge/io/MetaFormatFactory;)S
public static fun getName (Lhep/dataforge/io/MetaFormatFactory;)Lhep/dataforge/names/Name;
public static fun getType (Lhep/dataforge/io/MetaFormatFactory;)Lkotlin/reflect/KClass;
public static fun readObject (Lhep/dataforge/io/MetaFormatFactory;Lkotlinx/io/Input;)Lhep/dataforge/meta/Meta;
public static fun toMeta (Lhep/dataforge/io/MetaFormatFactory;)Lhep/dataforge/meta/Meta;
public static fun writeObject (Lhep/dataforge/io/MetaFormatFactory;Lkotlinx/io/Output;Lhep/dataforge/meta/Meta;)V
}
public final class hep/dataforge/io/MetaFormatKt {
public static final fun parse (Lhep/dataforge/io/MetaFormat;Ljava/lang/String;)Lhep/dataforge/meta/Meta;
public static final fun parse (Lhep/dataforge/io/MetaFormatFactory;Ljava/lang/String;Lhep/dataforge/meta/Meta;)Lhep/dataforge/meta/Meta;
public static final fun toString (Lhep/dataforge/meta/Meta;Lhep/dataforge/io/MetaFormat;)Ljava/lang/String;
public static final fun toString (Lhep/dataforge/meta/Meta;Lhep/dataforge/io/MetaFormatFactory;)Ljava/lang/String;
}
public final class hep/dataforge/io/PartialEnvelope {
public synthetic fun <init> (Lhep/dataforge/meta/Meta;ILkotlin/ULong;Lkotlin/jvm/internal/DefaultConstructorMarker;)V
public final fun component1 ()Lhep/dataforge/meta/Meta;
public final fun component2-pVg5ArA ()I
public final fun component3-6VbMDqA ()Lkotlin/ULong;
public final fun copy-BMK4sig (Lhep/dataforge/meta/Meta;ILkotlin/ULong;)Lhep/dataforge/io/PartialEnvelope;
public static synthetic fun copy-BMK4sig$default (Lhep/dataforge/io/PartialEnvelope;Lhep/dataforge/meta/Meta;ILkotlin/ULong;ILjava/lang/Object;)Lhep/dataforge/io/PartialEnvelope;
public fun equals (Ljava/lang/Object;)Z
public final fun getDataOffset-pVg5ArA ()I
public final fun getDataSize-6VbMDqA ()Lkotlin/ULong;
public final fun getMeta ()Lhep/dataforge/meta/Meta;
public fun hashCode ()I
public fun toString ()Ljava/lang/String;
}
public final class hep/dataforge/io/ProxyEnvelope : hep/dataforge/io/Envelope {
public fun <init> (Lhep/dataforge/io/Envelope;[Lhep/dataforge/meta/Meta;)V
public fun getData ()Lkotlinx/io/Binary;
public fun getMeta ()Lhep/dataforge/meta/Laminate;
public synthetic fun getMeta ()Lhep/dataforge/meta/Meta;
public final fun getSource ()Lhep/dataforge/io/Envelope;
}
public abstract interface class hep/dataforge/io/Responder {
public abstract fun respond (Lhep/dataforge/io/Envelope;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
}
public final class hep/dataforge/io/SimpleEnvelope : hep/dataforge/io/Envelope {
public fun <init> (Lhep/dataforge/meta/Meta;Lkotlinx/io/Binary;)V
public fun getData ()Lkotlinx/io/Binary;
public fun getMeta ()Lhep/dataforge/meta/Meta;
}
public final class hep/dataforge/io/StreamsIOKt {
public static final fun read (Ljava/io/InputStream;ILkotlin/jvm/functions/Function1;)Ljava/lang/Object;
public static final fun read (Ljava/io/InputStream;Lkotlin/jvm/functions/Function1;)Ljava/lang/Object;
public static final fun readBlocking (Ljava/io/InputStream;Lkotlin/jvm/functions/Function1;)Ljava/lang/Object;
public static final fun write (Ljava/io/OutputStream;Lkotlin/jvm/functions/Function1;)V
}
public final class hep/dataforge/io/TaggedEnvelopeFormat : hep/dataforge/io/EnvelopeFormat {
public static final field Companion Lhep/dataforge/io/TaggedEnvelopeFormat$Companion;
public fun <init> (Lhep/dataforge/io/IOPlugin;Lhep/dataforge/io/TaggedEnvelopeFormat$VERSION;)V
public synthetic fun <init> (Lhep/dataforge/io/IOPlugin;Lhep/dataforge/io/TaggedEnvelopeFormat$VERSION;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
public fun getDefaultMetaFormat ()Lhep/dataforge/io/MetaFormatFactory;
public final fun getIo ()Lhep/dataforge/io/IOPlugin;
public final fun getVersion ()Lhep/dataforge/io/TaggedEnvelopeFormat$VERSION;
public fun readObject (Lkotlinx/io/Input;)Lhep/dataforge/io/Envelope;
public synthetic fun readObject (Lkotlinx/io/Input;)Ljava/lang/Object;
public fun readPartial (Lkotlinx/io/Input;)Lhep/dataforge/io/PartialEnvelope;
public fun toMeta ()Lhep/dataforge/meta/Meta;
public fun writeEnvelope (Lkotlinx/io/Output;Lhep/dataforge/io/Envelope;Lhep/dataforge/io/MetaFormatFactory;Lhep/dataforge/meta/Meta;)V
public fun writeObject (Lkotlinx/io/Output;Lhep/dataforge/io/Envelope;)V
public synthetic fun writeObject (Lkotlinx/io/Output;Ljava/lang/Object;)V
}
public final class hep/dataforge/io/TaggedEnvelopeFormat$Companion : hep/dataforge/io/EnvelopeFormatFactory {
public fun getDefaultMetaFormat ()Lhep/dataforge/io/MetaFormatFactory;
public fun getName ()Lhep/dataforge/names/Name;
public fun getType ()Lkotlin/reflect/KClass;
public fun invoke (Lhep/dataforge/meta/Meta;Lhep/dataforge/context/Context;)Lhep/dataforge/io/EnvelopeFormat;
public synthetic fun invoke (Lhep/dataforge/meta/Meta;Lhep/dataforge/context/Context;)Ljava/lang/Object;
public fun peekFormat (Lhep/dataforge/io/IOPlugin;Lkotlinx/io/Input;)Lhep/dataforge/io/EnvelopeFormat;
public fun readObject (Lkotlinx/io/Input;)Lhep/dataforge/io/Envelope;
public synthetic fun readObject (Lkotlinx/io/Input;)Ljava/lang/Object;
public fun readPartial (Lkotlinx/io/Input;)Lhep/dataforge/io/PartialEnvelope;
public fun toMeta ()Lhep/dataforge/meta/Meta;
public fun writeEnvelope (Lkotlinx/io/Output;Lhep/dataforge/io/Envelope;Lhep/dataforge/io/MetaFormatFactory;Lhep/dataforge/meta/Meta;)V
public fun writeObject (Lkotlinx/io/Output;Lhep/dataforge/io/Envelope;)V
public synthetic fun writeObject (Lkotlinx/io/Output;Ljava/lang/Object;)V
}
public final class hep/dataforge/io/TaggedEnvelopeFormat$VERSION : java/lang/Enum {
public static final field DF02 Lhep/dataforge/io/TaggedEnvelopeFormat$VERSION;
public static final field DF03 Lhep/dataforge/io/TaggedEnvelopeFormat$VERSION;
public final fun getTagSize-pVg5ArA ()I
public static fun valueOf (Ljava/lang/String;)Lhep/dataforge/io/TaggedEnvelopeFormat$VERSION;
public static fun values ()[Lhep/dataforge/io/TaggedEnvelopeFormat$VERSION;
}
public final class hep/dataforge/io/TaglessEnvelopeFormat : hep/dataforge/io/EnvelopeFormat {
public static final field Companion Lhep/dataforge/io/TaglessEnvelopeFormat$Companion;
public static final field DATA_LENGTH_PROPERTY Ljava/lang/String;
public static final field DATA_START_PROPERTY Ljava/lang/String;
public static final field DEFAULT_DATA_START Ljava/lang/String;
public static final field DEFAULT_META_START Ljava/lang/String;
public static final field META_LENGTH_PROPERTY Ljava/lang/String;
public static final field META_START_PROPERTY Ljava/lang/String;
public static final field META_TYPE_PROPERTY Ljava/lang/String;
public static final field TAGLESS_ENVELOPE_HEADER Ljava/lang/String;
public static final field TAGLESS_ENVELOPE_TYPE Ljava/lang/String;
public static final field code I
public fun <init> (Lhep/dataforge/io/IOPlugin;Lhep/dataforge/meta/Meta;)V
public synthetic fun <init> (Lhep/dataforge/io/IOPlugin;Lhep/dataforge/meta/Meta;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
public fun getDefaultMetaFormat ()Lhep/dataforge/io/MetaFormatFactory;
public final fun getIo ()Lhep/dataforge/io/IOPlugin;
public final fun getMeta ()Lhep/dataforge/meta/Meta;
public fun readObject (Lkotlinx/io/Input;)Lhep/dataforge/io/Envelope;
public synthetic fun readObject (Lkotlinx/io/Input;)Ljava/lang/Object;
public fun readPartial (Lkotlinx/io/Input;)Lhep/dataforge/io/PartialEnvelope;
public fun toMeta ()Lhep/dataforge/meta/Meta;
public fun writeEnvelope (Lkotlinx/io/Output;Lhep/dataforge/io/Envelope;Lhep/dataforge/io/MetaFormatFactory;Lhep/dataforge/meta/Meta;)V
public fun writeObject (Lkotlinx/io/Output;Lhep/dataforge/io/Envelope;)V
public synthetic fun writeObject (Lkotlinx/io/Output;Ljava/lang/Object;)V
}
public final class hep/dataforge/io/TaglessEnvelopeFormat$Companion : hep/dataforge/io/EnvelopeFormatFactory {
public fun getDefaultMetaFormat ()Lhep/dataforge/io/MetaFormatFactory;
public fun getName ()Lhep/dataforge/names/Name;
public fun getType ()Lkotlin/reflect/KClass;
public fun invoke (Lhep/dataforge/meta/Meta;Lhep/dataforge/context/Context;)Lhep/dataforge/io/EnvelopeFormat;
public synthetic fun invoke (Lhep/dataforge/meta/Meta;Lhep/dataforge/context/Context;)Ljava/lang/Object;
public fun peekFormat (Lhep/dataforge/io/IOPlugin;Lkotlinx/io/Input;)Lhep/dataforge/io/EnvelopeFormat;
public fun readObject (Lkotlinx/io/Input;)Lhep/dataforge/io/Envelope;
public synthetic fun readObject (Lkotlinx/io/Input;)Ljava/lang/Object;
public fun readPartial (Lkotlinx/io/Input;)Lhep/dataforge/io/PartialEnvelope;
public fun toMeta ()Lhep/dataforge/meta/Meta;
public fun writeEnvelope (Lkotlinx/io/Output;Lhep/dataforge/io/Envelope;Lhep/dataforge/io/MetaFormatFactory;Lhep/dataforge/meta/Meta;)V
public fun writeObject (Lkotlinx/io/Output;Lhep/dataforge/io/Envelope;)V
public synthetic fun writeObject (Lkotlinx/io/Output;Ljava/lang/Object;)V
}
public final class hep/dataforge/io/ValueIOFormat : hep/dataforge/io/IOFormat, hep/dataforge/io/IOFormatFactory {
public static final field INSTANCE Lhep/dataforge/io/ValueIOFormat;
public fun getName ()Lhep/dataforge/names/Name;
public fun getType ()Lkotlin/reflect/KClass;
public fun invoke (Lhep/dataforge/meta/Meta;Lhep/dataforge/context/Context;)Lhep/dataforge/io/IOFormat;
public synthetic fun invoke (Lhep/dataforge/meta/Meta;Lhep/dataforge/context/Context;)Ljava/lang/Object;
public fun readObject (Lkotlinx/io/Input;)Lhep/dataforge/values/Value;
public synthetic fun readObject (Lkotlinx/io/Input;)Ljava/lang/Object;
public fun toMeta ()Lhep/dataforge/meta/Meta;
public fun writeObject (Lkotlinx/io/Output;Lhep/dataforge/values/Value;)V
public synthetic fun writeObject (Lkotlinx/io/Output;Ljava/lang/Object;)V
}

@ -1,17 +1,17 @@
import scientifik.DependencySourceSet.TEST
import scientifik.useSerialization
plugins {
id("scientifik.mpp")
id("ru.mipt.npm.mpp")
id("ru.mipt.npm.native")
}
description = "IO module"
useSerialization(sourceSet = TEST){
cbor()
kscience {
useSerialization(sourceSet = ru.mipt.npm.gradle.DependencySourceSet.TEST) {
cbor()
}
}
val ioVersion by rootProject.extra("0.2.0-npm-dev-7")
val ioVersion by rootProject.extra("0.2.0-npm-dev-11")
kotlin {
sourceSets {

@ -0,0 +1,59 @@
public final class hep/dataforge/io/yaml/FrontMatterEnvelopeFormat : hep/dataforge/io/EnvelopeFormat {
public static final field Companion Lhep/dataforge/io/yaml/FrontMatterEnvelopeFormat$Companion;
public static final field SEPARATOR Ljava/lang/String;
public fun <init> (Lhep/dataforge/io/IOPlugin;Lhep/dataforge/meta/Meta;)V
public synthetic fun <init> (Lhep/dataforge/io/IOPlugin;Lhep/dataforge/meta/Meta;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
public fun getDefaultMetaFormat ()Lhep/dataforge/io/MetaFormatFactory;
public fun readObject (Lkotlinx/io/Input;)Lhep/dataforge/io/Envelope;
public synthetic fun readObject (Lkotlinx/io/Input;)Ljava/lang/Object;
public fun readPartial (Lkotlinx/io/Input;)Lhep/dataforge/io/PartialEnvelope;
public fun toMeta ()Lhep/dataforge/meta/Meta;
public fun writeEnvelope (Lkotlinx/io/Output;Lhep/dataforge/io/Envelope;Lhep/dataforge/io/MetaFormatFactory;Lhep/dataforge/meta/Meta;)V
public fun writeObject (Lkotlinx/io/Output;Lhep/dataforge/io/Envelope;)V
public synthetic fun writeObject (Lkotlinx/io/Output;Ljava/lang/Object;)V
}
public final class hep/dataforge/io/yaml/FrontMatterEnvelopeFormat$Companion : hep/dataforge/io/EnvelopeFormatFactory {
public fun getDefaultMetaFormat ()Lhep/dataforge/io/MetaFormatFactory;
public fun getName ()Lhep/dataforge/names/Name;
public fun getType ()Lkotlin/reflect/KClass;
public fun invoke (Lhep/dataforge/meta/Meta;Lhep/dataforge/context/Context;)Lhep/dataforge/io/EnvelopeFormat;
public synthetic fun invoke (Lhep/dataforge/meta/Meta;Lhep/dataforge/context/Context;)Ljava/lang/Object;
public fun peekFormat (Lhep/dataforge/io/IOPlugin;Lkotlinx/io/Input;)Lhep/dataforge/io/EnvelopeFormat;
public fun readObject (Lkotlinx/io/Input;)Lhep/dataforge/io/Envelope;
public synthetic fun readObject (Lkotlinx/io/Input;)Ljava/lang/Object;
public fun readPartial (Lkotlinx/io/Input;)Lhep/dataforge/io/PartialEnvelope;
public fun toMeta ()Lhep/dataforge/meta/Meta;
public fun writeEnvelope (Lkotlinx/io/Output;Lhep/dataforge/io/Envelope;Lhep/dataforge/io/MetaFormatFactory;Lhep/dataforge/meta/Meta;)V
public fun writeObject (Lkotlinx/io/Output;Lhep/dataforge/io/Envelope;)V
public synthetic fun writeObject (Lkotlinx/io/Output;Ljava/lang/Object;)V
}
public final class hep/dataforge/io/yaml/YamlMetaFormat : hep/dataforge/io/MetaFormat {
public static final field Companion Lhep/dataforge/io/yaml/YamlMetaFormat$Companion;
public fun <init> (Lhep/dataforge/meta/Meta;)V
public fun readMeta (Lkotlinx/io/Input;Lhep/dataforge/meta/descriptors/NodeDescriptor;)Lhep/dataforge/meta/Meta;
public fun readObject (Lkotlinx/io/Input;)Lhep/dataforge/meta/Meta;
public synthetic fun readObject (Lkotlinx/io/Input;)Ljava/lang/Object;
public fun toMeta ()Lhep/dataforge/meta/Meta;
public fun writeMeta (Lkotlinx/io/Output;Lhep/dataforge/meta/Meta;Lhep/dataforge/meta/descriptors/NodeDescriptor;)V
public fun writeObject (Lkotlinx/io/Output;Lhep/dataforge/meta/Meta;)V
public synthetic fun writeObject (Lkotlinx/io/Output;Ljava/lang/Object;)V
}
public final class hep/dataforge/io/yaml/YamlMetaFormat$Companion : hep/dataforge/io/MetaFormatFactory {
public fun getKey ()S
public fun getName ()Lhep/dataforge/names/Name;
public fun getShortName ()Ljava/lang/String;
public fun getType ()Lkotlin/reflect/KClass;
public fun invoke (Lhep/dataforge/meta/Meta;Lhep/dataforge/context/Context;)Lhep/dataforge/io/MetaFormat;
public synthetic fun invoke (Lhep/dataforge/meta/Meta;Lhep/dataforge/context/Context;)Ljava/lang/Object;
public fun readMeta (Lkotlinx/io/Input;Lhep/dataforge/meta/descriptors/NodeDescriptor;)Lhep/dataforge/meta/Meta;
public fun readObject (Lkotlinx/io/Input;)Lhep/dataforge/meta/Meta;
public synthetic fun readObject (Lkotlinx/io/Input;)Ljava/lang/Object;
public fun toMeta ()Lhep/dataforge/meta/Meta;
public fun writeMeta (Lkotlinx/io/Output;Lhep/dataforge/meta/Meta;Lhep/dataforge/meta/descriptors/NodeDescriptor;)V
public fun writeObject (Lkotlinx/io/Output;Lhep/dataforge/meta/Meta;)V
public synthetic fun writeObject (Lkotlinx/io/Output;Ljava/lang/Object;)V
}

@ -1,13 +1,13 @@
import scientifik.useSerialization
plugins {
id("scientifik.jvm")
id("ru.mipt.npm.jvm")
}
description = "YAML meta IO"
useSerialization{
yaml()
kscience {
useSerialization {
yaml()
}
}
dependencies {

@ -9,20 +9,19 @@ import hep.dataforge.meta.Meta
import kotlinx.io.*
import kotlinx.io.text.readUtf8Line
import kotlinx.io.text.writeUtf8String
import kotlinx.serialization.toUtf8Bytes
@DFExperimental
class FrontMatterEnvelopeFormat(
val io: IOPlugin,
val meta: Meta = Meta.EMPTY
public class FrontMatterEnvelopeFormat(
private val io: IOPlugin,
private val meta: Meta = Meta.EMPTY,
) : EnvelopeFormat {
override fun Input.readPartial(): PartialEnvelope {
var line: String = ""
override fun readPartial(input: Input): PartialEnvelope {
var line = ""
var offset = 0u
do {
line = readUtf8Line() //?: error("Input does not contain front matter separator")
offset += line.toUtf8Bytes().size.toUInt()
line = input.readUtf8Line() //?: error("Input does not contain front matter separator")
offset += line.toByteArray().size.toUInt()
} while (!line.startsWith(SEPARATOR))
val readMetaFormat =
@ -32,22 +31,21 @@ class FrontMatterEnvelopeFormat(
//TODO replace by preview
val meta = Binary {
do {
line = readUtf8Line()
line = input.readUtf8Line()
writeUtf8String(line + "\r\n")
offset += line.toUtf8Bytes().size.toUInt()
offset += line.toByteArray().size.toUInt()
} while (!line.startsWith(SEPARATOR))
}.read {
readMetaFormat.run {
readMeta()
}
readMetaFormat.readMeta(input)
}
return PartialEnvelope(meta, offset, null)
}
override fun Input.readObject(): Envelope {
var line: String = ""
override fun readObject(input: Input): Envelope {
var line = ""
do {
line = readUtf8Line() //?: error("Input does not contain front matter separator")
line = input.readUtf8Line() //?: error("Input does not contain front matter separator")
} while (!line.startsWith(SEPARATOR))
val readMetaFormat =
@ -56,26 +54,29 @@ class FrontMatterEnvelopeFormat(
val meta = Binary {
do {
writeUtf8String(readUtf8Line() + "\r\n")
writeUtf8String(input.readUtf8Line() + "\r\n")
} while (!line.startsWith(SEPARATOR))
}.read {
readMetaFormat.run {
readMeta()
}
readMetaFormat.readMeta(input)
}
val bytes = readByteArray()
val bytes = input.readByteArray()
val data = bytes.asBinary()
return SimpleEnvelope(meta, data)
}
override fun Output.writeEnvelope(envelope: Envelope, metaFormatFactory: MetaFormatFactory, formatMeta: Meta) {
val metaFormat = metaFormatFactory(formatMeta, io.context)
writeRawString("$SEPARATOR\r\n")
metaFormat.run { writeObject(envelope.meta) }
writeRawString("$SEPARATOR\r\n")
override fun writeEnvelope(
output: Output,
envelope: Envelope,
metaFormatFactory: MetaFormatFactory,
formatMeta: Meta,
) {
val metaFormat = metaFormatFactory(formatMeta, this@FrontMatterEnvelopeFormat.io.context)
output.writeRawString("${hep.dataforge.io.yaml.FrontMatterEnvelopeFormat.Companion.SEPARATOR}\r\n")
metaFormat.run { this.writeObject(output, envelope.meta) }
output.writeRawString("${hep.dataforge.io.yaml.FrontMatterEnvelopeFormat.Companion.SEPARATOR}\r\n")
//Printing data
envelope.data?.let { data ->
writeBinary(data)
output.writeBinary(data)
}
}
@ -84,8 +85,8 @@ class FrontMatterEnvelopeFormat(
META_KEY put meta
}
companion object : EnvelopeFormatFactory {
const val SEPARATOR = "---"
public companion object : EnvelopeFormatFactory {
public const val SEPARATOR = "---"
private val metaTypeRegex = "---(\\w*)\\s*".toRegex()
@ -106,14 +107,18 @@ class FrontMatterEnvelopeFormat(
private val default by lazy { invoke() }
override fun Input.readPartial(): PartialEnvelope =
default.run { readPartial() }
override fun readPartial(input: Input): PartialEnvelope =
default.readPartial(input)
override fun Output.writeEnvelope(envelope: Envelope, metaFormatFactory: MetaFormatFactory, formatMeta: Meta) =
default.run { writeEnvelope(envelope, metaFormatFactory, formatMeta) }
override fun writeEnvelope(
output: Output,
envelope: Envelope,
metaFormatFactory: MetaFormatFactory,
formatMeta: Meta,
): Unit = default.writeEnvelope(output, envelope, metaFormatFactory, formatMeta)
override fun Input.readObject(): Envelope =
default.run { readObject() }
override fun readObject(input: Input): Envelope = default.readObject(input)
}
}

@ -16,17 +16,20 @@ import kotlinx.io.asInputStream
import kotlinx.io.text.writeUtf8String
import org.yaml.snakeyaml.Yaml
/**
* Represent meta as Yaml
*/
@DFExperimental
class YamlMetaFormat(val meta: Meta) : MetaFormat {
public class YamlMetaFormat(private val meta: Meta) : MetaFormat {
private val yaml = Yaml()
override fun Output.writeMeta(meta: Meta, descriptor: NodeDescriptor?) {
override fun writeMeta(output: Output, meta: Meta, descriptor: NodeDescriptor?) {
val string = yaml.dump(meta.toMap(descriptor))
writeUtf8String(string)
output.writeUtf8String(string)
}
override fun Input.readMeta(descriptor: NodeDescriptor?): Meta {
val map: Map<String, Any?> = yaml.load(asInputStream())
override fun readMeta(input: Input, descriptor: NodeDescriptor?): Meta {
val map: Map<String, Any?> = yaml.load(input.asInputStream())
return map.toMeta(descriptor)
}
@ -35,19 +38,19 @@ class YamlMetaFormat(val meta: Meta) : MetaFormat {
META_KEY put meta
}
companion object : MetaFormatFactory {
public companion object : MetaFormatFactory {
override fun invoke(meta: Meta, context: Context): MetaFormat = YamlMetaFormat(meta)
override val shortName = "yaml"
override val shortName: String = "yaml"
override val key: Short = 0x594d //YM
private val default = YamlMetaFormat()
override fun Output.writeMeta(meta: Meta, descriptor: NodeDescriptor?) =
default.run { writeMeta(meta, descriptor) }
override fun writeMeta(output: Output, meta: Meta, descriptor: NodeDescriptor?): Unit =
default.writeMeta(output, meta, descriptor)
override fun Input.readMeta(descriptor: NodeDescriptor?): Meta =
default.run { readMeta(descriptor) }
override fun readMeta(input: kotlinx.io.Input, descriptor: NodeDescriptor?): Meta =
default.readMeta(input, descriptor)
}
}

@ -11,14 +11,18 @@ import kotlinx.io.*
import kotlinx.io.text.readUtf8String
import kotlinx.io.text.writeUtf8String
object BinaryMetaFormat : MetaFormat, MetaFormatFactory {
/**
* A DataForge-specific simplified binary format for meta
* TODO add description
*/
public object BinaryMetaFormat : MetaFormat, MetaFormatFactory {
override val shortName: String = "bin"
override val key: Short = 0x4249//BI
override fun invoke(meta: Meta, context: Context): MetaFormat = this
override fun Input.readMeta(descriptor: NodeDescriptor?): Meta {
return (readMetaItem() as MetaItem.NodeItem).node
override fun readMeta(input: Input, descriptor: NodeDescriptor?): Meta {
return (input.readMetaItem() as MetaItem.NodeItem).node
}
private fun Output.writeChar(char: Char) = writeByte(char.toByte())
@ -28,7 +32,7 @@ object BinaryMetaFormat : MetaFormat, MetaFormatFactory {
writeUtf8String(str)
}
fun Output.writeValue(value: Value) {
public fun Output.writeValue(value: Value) {
if (value.isList()) {
writeChar('L')
writeInt(value.list.size)
@ -75,17 +79,21 @@ object BinaryMetaFormat : MetaFormat, MetaFormatFactory {
}
}
override fun Output.writeMeta(meta: Meta, descriptor: NodeDescriptor?) {
writeChar('M')
writeInt(meta.items.size)
override fun writeMeta(
output: kotlinx.io.Output,
meta: hep.dataforge.meta.Meta,
descriptor: hep.dataforge.meta.descriptors.NodeDescriptor?
) {
output.writeChar('M')
output.writeInt(meta.items.size)
meta.items.forEach { (key, item) ->
writeString(key.toString())
output.writeString(key.toString())
when (item) {
is MetaItem.ValueItem -> {
writeValue(item.value)
output.writeValue(item.value)
}
is MetaItem.NodeItem -> {
writeObject(item.node)
writeObject(output, item.node)
}
}
}
@ -97,7 +105,7 @@ object BinaryMetaFormat : MetaFormat, MetaFormatFactory {
}
@Suppress("UNCHECKED_CAST")
fun Input.readMetaItem(): MetaItem<MetaBuilder> {
public fun Input.readMetaItem(): MetaItem<MetaBuilder> {
return when (val keyChar = readByte().toChar()) {
'S' -> MetaItem.ValueItem(StringValue(readString()))
'N' -> MetaItem.ValueItem(Null)

@ -0,0 +1,11 @@
package hep.dataforge.io
import hep.dataforge.meta.DFExperimental
/**
* A fire-and-forget consumer of messages
*/
@DFExperimental
public interface Consumer {
public fun consume(message: Envelope): Unit
}

@ -4,64 +4,66 @@ import hep.dataforge.meta.Laminate
import hep.dataforge.meta.Meta
import hep.dataforge.meta.get
import hep.dataforge.meta.string
import hep.dataforge.names.Name
import hep.dataforge.names.asName
import hep.dataforge.names.plus
import kotlinx.io.Binary
interface Envelope {
val meta: Meta
val data: Binary?
public interface Envelope {
public val meta: Meta
public val data: Binary?
companion object {
public companion object {
/**
* meta keys
*/
val ENVELOPE_NODE_KEY = "@envelope".asName()
val ENVELOPE_TYPE_KEY = ENVELOPE_NODE_KEY + "type"
val ENVELOPE_DATA_TYPE_KEY = ENVELOPE_NODE_KEY + "dataType"
val ENVELOPE_DATA_ID_KEY = ENVELOPE_NODE_KEY + "dataID"
val ENVELOPE_DESCRIPTION_KEY = ENVELOPE_NODE_KEY + "description"
val ENVELOPE_NAME_KEY = ENVELOPE_NODE_KEY + "name"
public val ENVELOPE_NODE_KEY: Name = "@envelope".asName()
public val ENVELOPE_TYPE_KEY: Name = ENVELOPE_NODE_KEY + "type"
public val ENVELOPE_DATA_TYPE_KEY: Name = ENVELOPE_NODE_KEY + "dataType"
public val ENVELOPE_DATA_ID_KEY: Name = ENVELOPE_NODE_KEY + "dataID"
public val ENVELOPE_DESCRIPTION_KEY: Name = ENVELOPE_NODE_KEY + "description"
public val ENVELOPE_NAME_KEY: Name = ENVELOPE_NODE_KEY + "name"
//const val ENVELOPE_TIME_KEY = "@envelope.time"
/**
* Build a static envelope using provided builder
*/
inline operator fun invoke(block: EnvelopeBuilder.() -> Unit) = EnvelopeBuilder().apply(block).build()
public inline operator fun invoke(block: EnvelopeBuilder.() -> Unit): Envelope =
EnvelopeBuilder().apply(block).seal()
}
}
class SimpleEnvelope(override val meta: Meta, override val data: Binary?) : Envelope
public class SimpleEnvelope(override val meta: Meta, override val data: Binary?) : Envelope
/**
* The purpose of the envelope
*
*/
val Envelope.type: String? get() = meta[Envelope.ENVELOPE_TYPE_KEY].string
public val Envelope.type: String? get() = meta[Envelope.ENVELOPE_TYPE_KEY].string
/**
* The type of data encoding
*
*/
val Envelope.dataType: String? get() = meta[Envelope.ENVELOPE_DATA_TYPE_KEY].string
public val Envelope.dataType: String? get() = meta[Envelope.ENVELOPE_DATA_TYPE_KEY].string
/**
* Textual user friendly description
*
*/
val Envelope.description: String? get() = meta[Envelope.ENVELOPE_DESCRIPTION_KEY].string
public val Envelope.description: String? get() = meta[Envelope.ENVELOPE_DESCRIPTION_KEY].string
/**
* An optional unique identifier that is used for data comparison. Data without identifier could not be compared to another data.
*/
val Envelope.dataID: String? get() = meta[Envelope.ENVELOPE_DATA_ID_KEY].string
public val Envelope.dataID: String? get() = meta[Envelope.ENVELOPE_DATA_ID_KEY].string
fun Envelope.metaEquals(other: Envelope): Boolean = this.meta == other.meta
public fun Envelope.metaEquals(other: Envelope): Boolean = this.meta == other.meta
fun Envelope.dataEquals(other: Envelope): Boolean = this.dataID != null && this.dataID == other.dataID
public fun Envelope.dataEquals(other: Envelope): Boolean = this.dataID != null && this.dataID == other.dataID
fun Envelope.contentEquals(other: Envelope): Boolean {
public fun Envelope.contentEquals(other: Envelope): Boolean {
return (this === other || (metaEquals(other) && dataEquals(other)))
}
@ -69,7 +71,7 @@ fun Envelope.contentEquals(other: Envelope): Boolean {
/**
* An envelope, which wraps existing envelope and adds one or several additional layers of meta
*/
class ProxyEnvelope(val source: Envelope, vararg meta: Meta) : Envelope {
public class ProxyEnvelope(public val source: Envelope, vararg meta: Meta) : Envelope {
override val meta: Laminate = Laminate(*meta, source.meta)
override val data: Binary? get() = source.data
}
@ -77,7 +79,7 @@ class ProxyEnvelope(val source: Envelope, vararg meta: Meta) : Envelope {
/**
* Add few meta layers to existing envelope (on top of existing meta)
*/
fun Envelope.withMetaLayers(vararg layers: Meta): Envelope {
public fun Envelope.withMetaLayers(vararg layers: Meta): Envelope {
return when {
layers.isEmpty() -> this
this is ProxyEnvelope -> ProxyEnvelope(source, *layers, *this.meta.layers.toTypedArray())

@ -3,42 +3,44 @@ package hep.dataforge.io
import hep.dataforge.meta.*
import kotlinx.io.*
class EnvelopeBuilder {
public class EnvelopeBuilder : Envelope {
private val metaBuilder = MetaBuilder()
var data: Binary? = null
fun meta(block: MetaBuilder.() -> Unit) {
override var data: Binary? = null
override var meta: Meta
get() = metaBuilder
set(value) {
metaBuilder.update(value)
}
public fun meta(block: MetaBuilder.() -> Unit) {
metaBuilder.apply(block)
}
fun meta(meta: Meta) {
metaBuilder.update(meta)
}
/**
* The general purpose of the envelope
*/
var type by metaBuilder.string(key = Envelope.ENVELOPE_TYPE_KEY)
var dataType by metaBuilder.string(key = Envelope.ENVELOPE_DATA_TYPE_KEY)
public var type: String? by metaBuilder.string(key = Envelope.ENVELOPE_TYPE_KEY)
public var dataType: String? by metaBuilder.string(key = Envelope.ENVELOPE_DATA_TYPE_KEY)
/**
* Data unique identifier to bypass identity checks
*/
var dataID by metaBuilder.string(key = Envelope.ENVELOPE_DATA_ID_KEY)
var description by metaBuilder.string(key = Envelope.ENVELOPE_DESCRIPTION_KEY)
var name by metaBuilder.string(key = Envelope.ENVELOPE_NAME_KEY)
public var dataID: String? by metaBuilder.string(key = Envelope.ENVELOPE_DATA_ID_KEY)
public var description: String? by metaBuilder.string(key = Envelope.ENVELOPE_DESCRIPTION_KEY)
public var name: String? by metaBuilder.string(key = Envelope.ENVELOPE_NAME_KEY)
/**
* Construct a data binary from given builder
*/
@OptIn(ExperimentalIoApi::class)
fun data(block: Output.() -> Unit) {
public fun data(block: Output.() -> Unit) {
val arrayBuilder = ByteArrayOutput()
arrayBuilder.block()
data = arrayBuilder.toByteArray().asBinary()
}
fun build() = SimpleEnvelope(metaBuilder.seal(), data)
public fun seal(): Envelope = SimpleEnvelope(metaBuilder.seal(), data)
}

@ -13,31 +13,29 @@ import kotlin.reflect.KClass
/**
* A partially read envelope with meta, but without data
*/
@ExperimentalUnsignedTypes
data class PartialEnvelope(val meta: Meta, val dataOffset: UInt, val dataSize: ULong?)
public data class PartialEnvelope(val meta: Meta, val dataOffset: UInt, val dataSize: ULong?)
interface EnvelopeFormat : IOFormat<Envelope> {
val defaultMetaFormat: MetaFormatFactory get() = JsonMetaFormat
public interface EnvelopeFormat : IOFormat<Envelope> {
public val defaultMetaFormat: MetaFormatFactory get() = JsonMetaFormat
fun Input.readPartial(): PartialEnvelope
public fun readPartial(input: Input): PartialEnvelope
fun Output.writeEnvelope(
public fun writeEnvelope(
output: Output,
envelope: Envelope,
metaFormatFactory: MetaFormatFactory = defaultMetaFormat,
formatMeta: Meta = Meta.EMPTY
formatMeta: Meta = Meta.EMPTY,
)
override fun Input.readObject(): Envelope
override fun readObject(input: Input): Envelope
override fun Output.writeObject(obj: Envelope): Unit = writeEnvelope(obj)
override fun writeObject(output: Output, obj: Envelope): Unit = writeEnvelope(output, obj)
}
fun EnvelopeFormat.readPartial(input: Input) = input.readPartial()
fun EnvelopeFormat.read(input: Input) = input.readObject()
public fun EnvelopeFormat.read(input: Input): Envelope = readObject(input)
@Type(ENVELOPE_FORMAT_TYPE)
interface EnvelopeFormatFactory : IOFormatFactory<Envelope>, EnvelopeFormat {
public interface EnvelopeFormatFactory : IOFormatFactory<Envelope>, EnvelopeFormat {
override val name: Name get() = "envelope".asName()
override val type: KClass<out Envelope> get() = Envelope::class
@ -47,9 +45,9 @@ interface EnvelopeFormatFactory : IOFormatFactory<Envelope>, EnvelopeFormat {
* Try to infer specific format from input and return null if the attempt is failed.
* This method does **not** return Input into initial state.
*/
fun peekFormat(io: IOPlugin, input: Input): EnvelopeFormat?
public fun peekFormat(io: IOPlugin, input: Input): EnvelopeFormat?
companion object {
const val ENVELOPE_FORMAT_TYPE = "io.format.envelope"
public companion object {
public const val ENVELOPE_FORMAT_TYPE: String = "io.format.envelope"
}
}

@ -31,11 +31,11 @@ private class PartDescriptor : Scheme() {
}
}
data class EnvelopePart(val binary: Binary, val description: Meta?)
public data class EnvelopePart(val binary: Binary, val description: Meta?)
typealias EnvelopeParts = List<EnvelopePart>
public typealias EnvelopeParts = List<EnvelopePart>
fun EnvelopeBuilder.multipart(
public fun EnvelopeBuilder.multipart(
parts: EnvelopeParts,
separator: String = DEFAULT_MULTIPART_DATA_SEPARATOR
) {
@ -69,7 +69,7 @@ fun EnvelopeBuilder.multipart(
}
}
fun EnvelopeBuilder.envelopes(
public fun EnvelopeBuilder.envelopes(
envelopes: List<Envelope>,
format: EnvelopeFormat = TaggedEnvelopeFormat,
separator: String = DEFAULT_MULTIPART_DATA_SEPARATOR
@ -84,11 +84,11 @@ fun EnvelopeBuilder.envelopes(
multipart(parts, separator)
}
fun Envelope.parts(): EnvelopeParts {
public fun Envelope.parts(): EnvelopeParts {
if (data == null) return emptyList()
//TODO add zip folder reader
val parts = meta.getIndexed(PARTS_KEY).values.mapNotNull { it.node }.map {
PartDescriptor.wrap(it)
PartDescriptor.read(it)
}
return if (parts.isEmpty()) {
listOf(EnvelopePart(data!!, meta[MULTIPART_KEY].node))
@ -101,14 +101,14 @@ fun Envelope.parts(): EnvelopeParts {
}
}
fun EnvelopePart.envelope(format: EnvelopeFormat): Envelope = binary.readWith(format)
public fun EnvelopePart.envelope(format: EnvelopeFormat): Envelope = binary.readWith(format)
val EnvelopePart.name: String? get() = description?.get("name").string
public val EnvelopePart.name: String? get() = description?.get("name").string
/**
* Represent envelope part by an envelope
*/
fun EnvelopePart.envelope(plugin: IOPlugin): Envelope {
public fun EnvelopePart.envelope(plugin: IOPlugin): Envelope {
val formatItem = description?.get(PART_FORMAT_KEY)
return if (formatItem != null) {
val format: EnvelopeFormat = plugin.resolveEnvelopeFormat(formatItem)

@ -20,41 +20,42 @@ import kotlin.reflect.KClass
/**
* And interface for reading and writing objects into with IO streams
*/
interface IOFormat<T : Any> : MetaRepr {
fun Output.writeObject(obj: T)
fun Input.readObject(): T
public interface IOFormat<T : Any> : MetaRepr {
public fun writeObject(output: Output, obj: T)
public fun readObject(input: Input): T
companion object{
val NAME_KEY = "name".asName()
val META_KEY = "meta".asName()
public companion object {
public val NAME_KEY: Name = "name".asName()
public val META_KEY: Name = "meta".asName()
}
}
fun <T : Any> Input.readWith(format: IOFormat<T>): T = format.run { readObject() }
public fun <T : Any> Input.readWith(format: IOFormat<T>): T = format.readObject(this@readWith)
/**
* Read given binary as object using given format
*/
fun <T : Any> Binary.readWith(format: IOFormat<T>): T = read {
public fun <T : Any> Binary.readWith(format: IOFormat<T>): T = read {
readWith(format)
}
fun <T : Any> Output.writeWith(format: IOFormat<T>, obj: T) = format.run { writeObject(obj) }
public fun <T : Any> Output.writeWith(format: IOFormat<T>, obj: T): Unit =
format.run { writeObject(this@writeWith, obj) }
class ListIOFormat<T : Any>(val format: IOFormat<T>) : IOFormat<List<T>> {
override fun Output.writeObject(obj: List<T>) {
writeInt(obj.size)
format.run {
public class ListIOFormat<T : Any>(public val format: IOFormat<T>) : IOFormat<List<T>> {
override fun writeObject(output: Output, obj: List<T>) {
output.writeInt(obj.size)
this.format.run {
obj.forEach {
writeObject(it)
writeObject(output, it)
}
}
}
override fun Input.readObject(): List<T> {
val size = readInt()
override fun readObject(input: Input): List<T> {
val size = input.readInt()
return format.run {
List(size) { readObject() }
List(size) { readObject(input) }
}
}
@ -64,9 +65,9 @@ class ListIOFormat<T : Any>(val format: IOFormat<T>) : IOFormat<List<T>> {
}
}
val <T : Any> IOFormat<T>.list get() = ListIOFormat(this)
//public val <T : Any> IOFormat<T>.list: ListIOFormat<T> get() = ListIOFormat(this)
fun ObjectPool<Buffer>.fill(block: Buffer.() -> Unit): Buffer {
public fun ObjectPool<Buffer>.fill(block: Buffer.() -> Unit): Buffer {
val buffer = borrow()
return try {
buffer.apply(block)
@ -77,50 +78,50 @@ fun ObjectPool<Buffer>.fill(block: Buffer.() -> Unit): Buffer {
}
@Type(IO_FORMAT_TYPE)
interface IOFormatFactory<T : Any> : Factory<IOFormat<T>>, Named, MetaRepr {
public interface IOFormatFactory<T : Any> : Factory<IOFormat<T>>, Named, MetaRepr {
/**
* Explicit type for dynamic type checks
*/
val type: KClass<out T>
public val type: KClass<out T>
override fun toMeta(): Meta = Meta {
NAME_KEY put name.toString()
}
companion object {
const val IO_FORMAT_TYPE = "io.format"
public companion object {
public const val IO_FORMAT_TYPE: String = "io.format"
}
}
fun <T : Any> IOFormat<T>.toBinary(obj: T): Binary = Binary { writeObject(obj) }
public fun <T : Any> IOFormat<T>.toBinary(obj: T): Binary = Binary { writeObject(this, obj) }
object DoubleIOFormat : IOFormat<Double>, IOFormatFactory<Double> {
public object DoubleIOFormat : IOFormat<Double>, IOFormatFactory<Double> {
override fun invoke(meta: Meta, context: Context): IOFormat<Double> = this
override val name: Name = "double".asName()
override val type: KClass<out Double> get() = Double::class
override fun Output.writeObject(obj: Double) {
writeDouble(obj)
override fun writeObject(output: Output, obj: kotlin.Double) {
output.writeDouble(obj)
}
override fun Input.readObject(): Double = readDouble()
override fun readObject(input: Input): Double = input.readDouble()
}
object ValueIOFormat : IOFormat<Value>, IOFormatFactory<Value> {
public object ValueIOFormat : IOFormat<Value>, IOFormatFactory<Value> {
override fun invoke(meta: Meta, context: Context): IOFormat<Value> = this
override val name: Name = "value".asName()
override val type: KClass<out Value> get() = Value::class
override fun Output.writeObject(obj: Value) {
BinaryMetaFormat.run { writeValue(obj) }
override fun writeObject(output: Output, obj: Value) {
BinaryMetaFormat.run { output.writeValue(obj) }
}
override fun Input.readObject(): Value {
return (BinaryMetaFormat.run { readMetaItem() } as? MetaItem.ValueItem)?.value
override fun readObject(input: Input): Value {
return (BinaryMetaFormat.run { input.readMetaItem() } as? MetaItem.ValueItem)?.value
?: error("The item is not a value")
}
}

@ -11,14 +11,14 @@ import hep.dataforge.names.Name
import hep.dataforge.names.toName
import kotlin.reflect.KClass
class IOPlugin(meta: Meta) : AbstractPlugin(meta) {
public class IOPlugin(meta: Meta) : AbstractPlugin(meta) {
override val tag: PluginTag get() = Companion.tag
val ioFormatFactories by lazy {
context.content<IOFormatFactory<*>>(IO_FORMAT_TYPE).values
public val ioFormatFactories: Collection<IOFormatFactory<*>> by lazy {
context.gather<IOFormatFactory<*>>(IO_FORMAT_TYPE).values
}
fun <T : Any> resolveIOFormat(item: MetaItem<*>, type: KClass<out T>): IOFormat<T>? {
public fun <T : Any> resolveIOFormat(item: MetaItem<*>, type: KClass<out T>): IOFormat<T>? {
val key = item.string ?: item.node[NAME_KEY]?.string ?: error("Format name not defined")
val name = key.toName()
return ioFormatFactories.find { it.name == name }?.let {
@ -29,40 +29,40 @@ class IOPlugin(meta: Meta) : AbstractPlugin(meta) {
}
val metaFormatFactories by lazy {
context.content<MetaFormatFactory>(META_FORMAT_TYPE).values
public val metaFormatFactories: Collection<MetaFormatFactory> by lazy {
context.gather<MetaFormatFactory>(META_FORMAT_TYPE).values
}
fun resolveMetaFormat(key: Short, meta: Meta = Meta.EMPTY): MetaFormat? =
public fun resolveMetaFormat(key: Short, meta: Meta = Meta.EMPTY): MetaFormat? =
metaFormatFactories.find { it.key == key }?.invoke(meta)
fun resolveMetaFormat(name: String, meta: Meta = Meta.EMPTY): MetaFormat? =
public fun resolveMetaFormat(name: String, meta: Meta = Meta.EMPTY): MetaFormat? =
metaFormatFactories.find { it.shortName == name }?.invoke(meta)
val envelopeFormatFactories by lazy {
context.content<EnvelopeFormatFactory>(ENVELOPE_FORMAT_TYPE).values
public val envelopeFormatFactories: Collection<EnvelopeFormatFactory> by lazy {
context.gather<EnvelopeFormatFactory>(ENVELOPE_FORMAT_TYPE).values
}
fun resolveEnvelopeFormat(name: Name, meta: Meta = Meta.EMPTY): EnvelopeFormat? =
private fun resolveEnvelopeFormat(name: Name, meta: Meta = Meta.EMPTY): EnvelopeFormat? =
envelopeFormatFactories.find { it.name == name }?.invoke(meta, context)
fun resolveEnvelopeFormat(item: MetaItem<*>): EnvelopeFormat? {
public fun resolveEnvelopeFormat(item: MetaItem<*>): EnvelopeFormat? {
val name = item.string ?: item.node[NAME_KEY]?.string ?: error("Envelope format name not defined")
val meta = item.node[META_KEY].node ?: Meta.EMPTY
return resolveEnvelopeFormat(name.toName(), meta)
}
override fun provideTop(target: String): Map<Name, Any> {
override fun content(target: String): Map<Name, Any> {
return when (target) {
META_FORMAT_TYPE -> defaultMetaFormats.toMap()
ENVELOPE_FORMAT_TYPE -> defaultEnvelopeFormats.toMap()
else -> super.provideTop(target)
else -> super.content(target)
}
}
companion object : PluginFactory<IOPlugin> {
val defaultMetaFormats: List<MetaFormatFactory> = listOf(JsonMetaFormat, BinaryMetaFormat)
val defaultEnvelopeFormats = listOf(TaggedEnvelopeFormat, TaglessEnvelopeFormat)
public companion object : PluginFactory<IOPlugin> {
public val defaultMetaFormats: List<MetaFormatFactory> = listOf(JsonMetaFormat, BinaryMetaFormat)
public val defaultEnvelopeFormats: List<EnvelopeFormatFactory> = listOf(TaggedEnvelopeFormat, TaglessEnvelopeFormat)
override val tag: PluginTag = PluginTag("io", group = PluginTag.DATAFORGE_GROUP)
@ -71,4 +71,4 @@ class IOPlugin(meta: Meta) : AbstractPlugin(meta) {
}
}
val Context.io: IOPlugin get() = plugins.fetch(IOPlugin)
public val Context.io: IOPlugin get() = plugins.fetch(IOPlugin)

@ -12,45 +12,46 @@ import hep.dataforge.meta.toJson
import hep.dataforge.meta.toMetaItem
import kotlinx.io.Input
import kotlinx.io.Output
import kotlinx.io.readByteArray
import kotlinx.io.text.readUtf8String
import kotlinx.io.text.writeUtf8String
import kotlinx.serialization.UnstableDefault
import kotlinx.serialization.json.Json
import kotlinx.serialization.json.JsonObjectSerializer
import kotlinx.serialization.json.JsonObject
@OptIn(UnstableDefault::class)
class JsonMetaFormat(private val json: Json = DEFAULT_JSON) : MetaFormat {
/**
* A Json format for Meta representation
*/
public class JsonMetaFormat(private val json: Json = DEFAULT_JSON) : MetaFormat {
override fun Output.writeMeta(meta: Meta, descriptor: NodeDescriptor?) {
override fun writeMeta(output: Output, meta: Meta, descriptor: NodeDescriptor?) {
val jsonObject = meta.toJson(descriptor)
writeUtf8String(json.stringify(JsonObjectSerializer, jsonObject))
output.writeUtf8String(json.encodeToString(JsonObject.serializer(), jsonObject))
}
override fun toMeta(): Meta = Meta{
override fun toMeta(): Meta = Meta {
NAME_KEY put name.toString()
}
override fun Input.readMeta(descriptor: NodeDescriptor?): Meta {
val str = readByteArray().decodeToString()
val jsonElement = json.parseJson(str)
override fun readMeta(input: Input, descriptor: NodeDescriptor?): Meta {
val str = input.readUtf8String()//readByteArray().decodeToString()
val jsonElement = json.parseToJsonElement(str)
val item = jsonElement.toMetaItem(descriptor)
return item.node ?: Meta.EMPTY
}
companion object : MetaFormatFactory {
val DEFAULT_JSON = Json { prettyPrint = true }
public companion object : MetaFormatFactory {
public val DEFAULT_JSON: Json = Json { prettyPrint = true }
override fun invoke(meta: Meta, context: Context): MetaFormat = default
override val shortName = "json"
override val shortName: String = "json"
override val key: Short = 0x4a53//"JS"
private val default = JsonMetaFormat()
override fun Output.writeMeta(meta: Meta, descriptor: NodeDescriptor?) =
default.run { writeMeta(meta, descriptor) }
override fun writeMeta(output: Output, meta: Meta, descriptor: NodeDescriptor?): Unit =
default.run { writeMeta(output, meta, descriptor) }
override fun Input.readMeta(descriptor: NodeDescriptor?): Meta =
default.run { readMeta(descriptor) }
override fun readMeta(input: Input, descriptor: NodeDescriptor?): Meta =
default.run { readMeta(input, descriptor) }
}
}

@ -17,46 +17,50 @@ import kotlin.reflect.KClass
/**
* A format for meta serialization
*/
public interface MetaFormat : IOFormat<Meta> {
interface MetaFormat : IOFormat<Meta> {
override fun Output.writeObject(obj: Meta) {
writeMeta(obj, null)
override fun writeObject(output: Output, obj: Meta) {
writeMeta(output, obj, null)
}
override fun Input.readObject(): Meta = readMeta()
override fun readObject(input: Input): Meta = readMeta(input)
fun Output.writeMeta(meta: Meta, descriptor: NodeDescriptor? = null)
fun Input.readMeta(descriptor: NodeDescriptor? = null): Meta
public fun writeMeta(
output: Output,
meta: Meta,
descriptor: NodeDescriptor? = null,
)
public fun readMeta(input: Input, descriptor: NodeDescriptor? = null): Meta
}
@Type(META_FORMAT_TYPE)
interface MetaFormatFactory : IOFormatFactory<Meta>, MetaFormat {
val shortName: String
public interface MetaFormatFactory : IOFormatFactory<Meta>, MetaFormat {
public val shortName: String
override val name: Name get() = "meta".asName() + shortName
override val type: KClass<out Meta> get() = Meta::class
val key: Short get() = name.hashCode().toShort()
public val key: Short get() = name.hashCode().toShort()
override operator fun invoke(meta: Meta, context: Context): MetaFormat
companion object {
const val META_FORMAT_TYPE = "io.format.meta"
public companion object {
public const val META_FORMAT_TYPE: String = "io.format.meta"
}
}
fun Meta.toString(format: MetaFormat): String = buildByteArray {
format.run { writeObject(this@toString) }
public fun Meta.toString(format: MetaFormat): String = buildByteArray {
format.run { writeObject(this@buildByteArray, this@toString) }
}.decodeToString()
fun Meta.toString(formatFactory: MetaFormatFactory): String = toString(formatFactory())
public fun Meta.toString(formatFactory: MetaFormatFactory): String = toString(formatFactory())
fun MetaFormat.parse(str: String): Meta {
return ByteArrayInput(str.encodeToByteArray()).use { it.readObject() }
public fun MetaFormat.parse(str: String): Meta {
return ByteArrayInput(str.encodeToByteArray()).use { readObject(it) }
}
fun MetaFormatFactory.parse(str: String, formatMeta: Meta): Meta = invoke(formatMeta).parse(str)
public fun MetaFormatFactory.parse(str: String, formatMeta: Meta): Meta = invoke(formatMeta).parse(str)

@ -1,8 +1,12 @@
package hep.dataforge.io
interface Responder {
/**
* An object that could respond to external messages asynchronously
*/
public interface Responder {
/**
* Send a request and wait for response for this specific request
*/
suspend fun respond(request: Envelope): Envelope
}
public suspend fun respond(request: Envelope): Envelope
}

@ -12,9 +12,13 @@ import hep.dataforge.names.plus
import hep.dataforge.names.toName
import kotlinx.io.*
class TaggedEnvelopeFormat(
val io: IOPlugin,
val version: VERSION = VERSION.DF02
/**
* A streaming-friendly envelope format with a short binary tag.
* TODO add description
*/
public class TaggedEnvelopeFormat(
public val io: IOPlugin,
public val version: VERSION = VERSION.DF02,
) : EnvelopeFormat {
// private val metaFormat = io.metaFormat(metaFormatKey)
@ -37,18 +41,23 @@ class TaggedEnvelopeFormat(
writeRawString(END_SEQUENCE)
}
override fun Output.writeEnvelope(envelope: Envelope, metaFormatFactory: MetaFormatFactory, formatMeta: Meta) {
val metaFormat = metaFormatFactory.invoke(formatMeta, io.context)
override fun writeEnvelope(
output: Output,
envelope: Envelope,
metaFormatFactory: MetaFormatFactory,
formatMeta: Meta,
) {
val metaFormat = metaFormatFactory.invoke(formatMeta, this@TaggedEnvelopeFormat.io.context)
val metaBytes = metaFormat.toBinary(envelope.meta)
val actualSize: ULong = (envelope.data?.size ?: 0).toULong()
val tag = Tag(metaFormatFactory.key, metaBytes.size.toUInt() + 2u, actualSize)
writeBinary(tag.toBinary())
writeBinary(metaBytes)
writeRawString("\r\n")
output.writeBinary(tag.toBinary())
output.writeBinary(metaBytes)
output.writeRawString("\r\n")
envelope.data?.let {
writeBinary(it)
output.writeBinary(it)
}
flush()
output.flush()
}
/**
@ -57,34 +66,27 @@ class TaggedEnvelopeFormat(
* @param input an input to read from
* @param formats a collection of meta formats to resolve
*/
override fun Input.readObject(): Envelope {
val tag = readTag(version)
override fun readObject(input: Input): Envelope {
val tag = input.readTag(this.version)
val metaFormat = io.resolveMetaFormat(tag.metaFormatKey)
?: error("Meta format with key ${tag.metaFormatKey} not found")
val meta: Meta = limit(tag.metaSize.toInt()).run {
metaFormat.run {
readObject()
}
}
val meta: Meta = metaFormat.readObject(input.limit(tag.metaSize.toInt()))
val data = readBinary(tag.dataSize.toInt())
val data = input.readBinary(tag.dataSize.toInt())
return SimpleEnvelope(meta, data)
}
override fun Input.readPartial(): PartialEnvelope {
val tag = readTag(version)
override fun readPartial(input: Input): PartialEnvelope {
val tag = input.readTag(this.version)
val metaFormat = io.resolveMetaFormat(tag.metaFormatKey)
?: error("Meta format with key ${tag.metaFormatKey} not found")
val meta: Meta = limit(tag.metaSize.toInt()).run {
metaFormat.run {
readObject()
}
}
val meta: Meta = metaFormat.readObject(input.limit(tag.metaSize.toInt()))
return PartialEnvelope(meta, version.tagSize + tag.metaSize, tag.dataSize)
}
@ -92,10 +94,10 @@ class TaggedEnvelopeFormat(
private data class Tag(
val metaFormatKey: Short,
val metaSize: UInt,
val dataSize: ULong
val dataSize: ULong,
)
enum class VERSION(val tagSize: UInt) {
public enum class VERSION(public val tagSize: UInt) {
DF02(20u),
DF03(24u)
}
@ -107,7 +109,7 @@ class TaggedEnvelopeFormat(
}
}
companion object : EnvelopeFormatFactory {
public companion object : EnvelopeFormatFactory {
private const val START_SEQUENCE = "#~"
private const val END_SEQUENCE = "~#\r\n"
@ -158,16 +160,24 @@ class TaggedEnvelopeFormat(
private val default by lazy { invoke() }
override fun Input.readPartial(): PartialEnvelope =
default.run { readPartial() }
override fun Output.writeEnvelope(envelope: Envelope, metaFormatFactory: MetaFormatFactory, formatMeta: Meta) =
default.run { writeEnvelope(envelope, metaFormatFactory, formatMeta) }
override fun Input.readObject(): Envelope =
default.run { readObject() }
override fun readPartial(input: Input): PartialEnvelope =
default.run { readPartial(input) }
override fun writeEnvelope(
output: Output,
envelope: Envelope,
metaFormatFactory: MetaFormatFactory,
formatMeta: Meta,
): Unit = default.run {
writeEnvelope(
output,
envelope,
metaFormatFactory,
formatMeta
)
}
override fun readObject(input: Input): Envelope = default.readObject(input)
}
}

@ -7,15 +7,20 @@ import hep.dataforge.meta.Meta
import hep.dataforge.meta.get
import hep.dataforge.meta.isEmpty
import hep.dataforge.meta.string
import hep.dataforge.names.Name
import hep.dataforge.names.asName
import kotlinx.io.*
import kotlinx.io.text.readUtf8Line
import kotlinx.io.text.writeUtf8String
import kotlin.collections.set
class TaglessEnvelopeFormat(
val io: IOPlugin,
val meta: Meta = Meta.EMPTY
/**
* A text envelope format with human-readable tag.
* TODO add description
*/
public class TaglessEnvelopeFormat(
public val io: IOPlugin,
public val meta: Meta = Meta.EMPTY,
) : EnvelopeFormat {
private val metaStart = meta[META_START_PROPERTY].string ?: DEFAULT_META_START
@ -25,39 +30,46 @@ class TaglessEnvelopeFormat(
writeUtf8String("#? $key: $value;\r\n")
}
override fun Output.writeEnvelope(envelope: Envelope, metaFormatFactory: MetaFormatFactory, formatMeta: Meta) {
val metaFormat = metaFormatFactory(formatMeta, io.context)
override fun writeEnvelope(
output: Output,
envelope: Envelope,
metaFormatFactory: MetaFormatFactory,
formatMeta: Meta
) {
val metaFormat = metaFormatFactory(formatMeta, this.io.context)
//printing header
writeRawString(TAGLESS_ENVELOPE_HEADER + "\r\n")
output.writeRawString(TAGLESS_ENVELOPE_HEADER + "\r\n")
//printing all properties
writeProperty(META_TYPE_PROPERTY, metaFormatFactory.shortName)
output.writeProperty(META_TYPE_PROPERTY,
metaFormatFactory.shortName)
//TODO add optional metaFormat properties
val actualSize: Int = envelope.data?.size ?: 0
writeProperty(DATA_LENGTH_PROPERTY, actualSize)
output.writeProperty(DATA_LENGTH_PROPERTY, actualSize)
//Printing meta
if (!envelope.meta.isEmpty()) {
val metaBytes = metaFormat.toBinary(envelope.meta)
writeProperty(META_LENGTH_PROPERTY, metaBytes.size + 2)
writeUtf8String(metaStart + "\r\n")
writeBinary(metaBytes)
writeRawString("\r\n")
output.writeProperty(META_LENGTH_PROPERTY,
metaBytes.size + 2)
output.writeUtf8String(this.metaStart + "\r\n")
output.writeBinary(metaBytes)
output.writeRawString("\r\n")
}
//Printing data
envelope.data?.let { data ->
writeUtf8String(dataStart + "\r\n")
writeBinary(data)
output.writeUtf8String(this.dataStart + "\r\n")
output.writeBinary(data)
}
}
override fun Input.readObject(): Envelope {
override fun readObject(input: Input): Envelope {
var line: String
do {
line = readUtf8Line() // ?: error("Input does not contain tagless envelope header")
line = input.readUtf8Line() // ?: error("Input does not contain tagless envelope header")
} while (!line.startsWith(TAGLESS_ENVELOPE_HEADER))
val properties = HashMap<String, String>()
@ -70,8 +82,8 @@ class TaglessEnvelopeFormat(
properties[key] = value
}
//If can't read line, return envelope without data
if (exhausted()) return SimpleEnvelope(Meta.EMPTY, null)
line = readUtf8Line()
if (input.exhausted()) return SimpleEnvelope(Meta.EMPTY, null)
line = input.readUtf8Line()
}
var meta: Meta = Meta.EMPTY
@ -80,19 +92,15 @@ class TaglessEnvelopeFormat(
val metaFormat = properties[META_TYPE_PROPERTY]?.let { io.resolveMetaFormat(it) } ?: JsonMetaFormat
val metaSize = properties[META_LENGTH_PROPERTY]?.toInt()
meta = if (metaSize != null) {
limit(metaSize).run {
metaFormat.run { readObject() }
}
metaFormat.readObject(input.limit(metaSize))
} else {
metaFormat.run {
readObject()
}
metaFormat.readObject(input)
}
}
do {
try {
line = readUtf8Line()
line = input.readUtf8Line()
} catch (ex: EOFException) {
//returning an Envelope without data if end of input is reached
return SimpleEnvelope(meta, null)
@ -100,24 +108,24 @@ class TaglessEnvelopeFormat(
} while (!line.startsWith(dataStart))
val data: Binary? = if (properties.containsKey(DATA_LENGTH_PROPERTY)) {
readBinary(properties[DATA_LENGTH_PROPERTY]!!.toInt())
input.readBinary(properties[DATA_LENGTH_PROPERTY]!!.toInt())
// val bytes = ByteArray(properties[DATA_LENGTH_PROPERTY]!!.toInt())
// readByteArray(bytes)
// bytes.asBinary()
} else {
Binary {
copyTo(this)
input.copyTo(this)
}
}
return SimpleEnvelope(meta, data)
}
override fun Input.readPartial(): PartialEnvelope {
override fun readPartial(input: Input): PartialEnvelope {
var offset = 0u
var line: String
do {
line = readUtf8Line()// ?: error("Input does not contain tagless envelope header")
line = input.readUtf8Line()// ?: error("Input does not contain tagless envelope header")
offset += line.encodeToByteArray().size.toUInt()
} while (!line.startsWith(TAGLESS_ENVELOPE_HEADER))
val properties = HashMap<String, String>()
@ -131,7 +139,7 @@ class TaglessEnvelopeFormat(
properties[key] = value
}
try {
line = readUtf8Line()
line = input.readUtf8Line()
offset += line.encodeToByteArray().size.toUInt()
} catch (ex: EOFException) {
return PartialEnvelope(Meta.EMPTY, offset.toUInt(), 0.toULong())
@ -145,16 +153,14 @@ class TaglessEnvelopeFormat(
val metaSize = properties[META_LENGTH_PROPERTY]?.toInt()
meta = if (metaSize != null) {
offset += metaSize.toUInt()
limit(metaSize).run {
metaFormat.run { readObject() }
}
metaFormat.readObject(input.limit(metaSize))
} else {
error("Can't partially read an envelope with undefined meta size")
}
}
do {
line = readUtf8Line() //?: return PartialEnvelope(Meta.EMPTY, offset.toUInt(), 0.toULong())
line = input.readUtf8Line() //?: return PartialEnvelope(Meta.EMPTY, offset.toUInt(), 0.toULong())
offset += line.encodeToByteArray().size.toUInt()
//returning an Envelope without data if end of input is reached
} while (!line.startsWith(dataStart))
@ -168,26 +174,26 @@ class TaglessEnvelopeFormat(
META_KEY put meta
}
companion object : EnvelopeFormatFactory {
public companion object : EnvelopeFormatFactory {
private val propertyPattern = "#\\?\\s*(?<key>[\\w.]*)\\s*:\\s*(?<value>[^;]*);?".toRegex()
private val propertyPattern = "#\\?\\s*([\\w.]*)\\s*:\\s*([^;]*);?".toRegex()
const val META_TYPE_PROPERTY = "metaType"
const val META_LENGTH_PROPERTY = "metaLength"
const val DATA_LENGTH_PROPERTY = "dataLength"
public const val META_TYPE_PROPERTY: String = "metaType"
public const val META_LENGTH_PROPERTY: String = "metaLength"
public const val DATA_LENGTH_PROPERTY: String = "dataLength"
const val TAGLESS_ENVELOPE_TYPE = "tagless"
public const val TAGLESS_ENVELOPE_TYPE: String = "tagless"
const val TAGLESS_ENVELOPE_HEADER = "#~DFTL~#"
const val META_START_PROPERTY = "metaSeparator"
const val DEFAULT_META_START = "#~META~#"
const val DATA_START_PROPERTY = "dataSeparator"
const val DEFAULT_DATA_START = "#~DATA~#"
public const val TAGLESS_ENVELOPE_HEADER: String = "#~DFTL~#"
public const val META_START_PROPERTY: String = "metaSeparator"
public const val DEFAULT_META_START: String = "#~META~#"
public const val DATA_START_PROPERTY: String = "dataSeparator"
public const val DEFAULT_DATA_START: String = "#~DATA~#"
const val code: Int = 0x4446544c //DFTL
public const val code: Int = 0x4446544c //DFTL
override val name = TAGLESS_ENVELOPE_TYPE.asName()
override val name: Name = TAGLESS_ENVELOPE_TYPE.asName()
override fun invoke(meta: Meta, context: Context): EnvelopeFormat {
return TaglessEnvelopeFormat(context.io, meta)
@ -195,14 +201,24 @@ class TaglessEnvelopeFormat(
private val default by lazy { invoke() }
override fun Input.readPartial(): PartialEnvelope =
default.run { readPartial() }
override fun readPartial(input: Input): PartialEnvelope =
default.run { readPartial(input) }
override fun Output.writeEnvelope(envelope: Envelope, metaFormatFactory: MetaFormatFactory, formatMeta: Meta) =
default.run { writeEnvelope(envelope, metaFormatFactory, formatMeta) }
override fun writeEnvelope(
output: Output,
envelope: Envelope,
metaFormatFactory: MetaFormatFactory,
formatMeta: Meta,
): Unit = default.run {
writeEnvelope(
output,
envelope,
metaFormatFactory,
formatMeta
)
}
override fun Input.readObject(): Envelope =
default.run { readObject() }
override fun readObject(input: Input): Envelope = default.readObject(input)
override fun peekFormat(io: IOPlugin, input: Input): EnvelopeFormat? {
return try {

@ -1,75 +0,0 @@
package hep.dataforge.io.functions
import hep.dataforge.context.ContextAware
import hep.dataforge.io.IOFormat
import hep.dataforge.io.IOPlugin
import hep.dataforge.meta.Meta
import hep.dataforge.meta.get
import hep.dataforge.names.asName
import hep.dataforge.names.plus
import kotlin.reflect.KClass
/**
* A server that could produce asynchronous function values
*/
interface FunctionServer : ContextAware {
/**
* Call a function with given name and descriptor
*/
suspend fun <T : Any, R : Any> call(meta: Meta, arg: T, inputType: KClass<out T>, outputType: KClass<out R>): R
suspend fun <T : Any, R : Any> callMany(
meta: Meta,
arg: List<T>,
inputType: KClass<out T>,
outputType: KClass<out R>
): List<R> = List(arg.size) {
call<T, R>(meta, arg[it], inputType, outputType)
}
/**
* Get a generic suspended function with given name and descriptor
*/
fun <T : Any, R : Any> function(
meta: Meta,
inputType: KClass<out T>,
outputType: KClass<out R>
): (suspend (T) -> R) = { call(meta, it, inputType, outputType) }
companion object {
const val FUNCTION_NAME_KEY = "function"
val FORMAT_KEY = "format".asName()
val INPUT_FORMAT_KEY = FORMAT_KEY + "input"
val OUTPUT_FORMAT_KEY = FORMAT_KEY + "output"
}
}
suspend inline fun <reified T : Any, reified R : Any> FunctionServer.call(meta: Meta, arg: T) =
call(meta, arg, T::class, R::class)
suspend inline fun <reified T : Any, reified R : Any> FunctionServer.callMany(meta: Meta, arg: List<T>) =
callMany(meta, arg, T::class, R::class)
inline fun <reified T : Any, reified R : Any> FunctionServer.function(meta: Meta) =
function(meta, T::class, R::class)
fun <T : Any> IOPlugin.getInputFormat(meta: Meta, type: KClass<out T>): IOFormat<T> {
return meta[FunctionServer.INPUT_FORMAT_KEY]?.let {
resolveIOFormat<T>(it, type)
} ?: error("Input format not resolved")
}
fun <R : Any> IOPlugin.getOutputFormat(meta: Meta, type: KClass<out R>): IOFormat<R> {
return meta[FunctionServer.OUTPUT_FORMAT_KEY]?.let {
resolveIOFormat<R>(it, type)
} ?: error("Input format not resolved")
}
inline fun <reified T : Any> IOPlugin.getInputFormat(meta: Meta): IOFormat<T> =
getInputFormat(meta, T::class)
inline fun <reified R : Any> IOPlugin.getOutputFormat(meta: Meta): IOFormat<R> =
getOutputFormat(meta, R::class)

@ -1,98 +0,0 @@
package hep.dataforge.io.functions
import hep.dataforge.context.Context
import hep.dataforge.context.ContextAware
import hep.dataforge.io.*
import hep.dataforge.meta.Meta
import hep.dataforge.meta.get
import hep.dataforge.meta.int
import kotlin.reflect.KClass
class RemoteFunctionClient(override val context: Context, val responder: Responder) : FunctionServer, ContextAware {
private fun <T : Any> IOPlugin.encodeOne(
meta: Meta,
value: T,
valueType: KClass<out T> = value::class
): Envelope = Envelope.invoke {
meta(meta)
type = REQUEST_TYPE
data {
val inputFormat: IOFormat<T> = getInputFormat(meta, valueType)
inputFormat.run {
writeObject(value)
}
}
}
private fun <T : Any> IOPlugin.encodeMany(
meta: Meta,
values: List<T>,
valueType: KClass<out T>
): Envelope = Envelope.invoke {
meta(meta)
type = REQUEST_TYPE
meta {
SIZE_KEY put values.size
}
data {
val inputFormat: IOFormat<T> = getInputFormat(meta, valueType)
inputFormat.run {
values.forEach {
writeObject(it)
}
}
}
}
private fun <R : Any> IOPlugin.decode(envelope: Envelope, valueType: KClass<out R>): List<R> {
require(envelope.type == RESPONSE_TYPE) { "Unexpected message type: ${envelope.type}" }
val size = envelope.meta[SIZE_KEY].int ?: 1
return if (size == 0) {
emptyList()
} else {
val outputFormat: IOFormat<R> = getOutputFormat(envelope.meta, valueType)
envelope.data?.read {
List<R>(size) {
outputFormat.run {
readObject()
}
}
} ?: error("Message does not contain data")
}
}
private val plugin by lazy {
context.plugins.load(IOPlugin)
}
override suspend fun <T : Any, R : Any> call(
meta: Meta,
arg: T,
inputType: KClass<out T>,
outputType: KClass<out R>
): R = plugin.run {
val request = encodeOne(meta, arg)
val response = responder.respond(request)
return decode<R>(response, outputType).first()
}
override suspend fun <T : Any, R : Any> callMany(
meta: Meta,
arg: List<T>,
inputType: KClass<out T>,
outputType: KClass<out R>
): List<R> = plugin.run {
val request = encodeMany(meta, arg, inputType)
val response = responder.respond(request)
return decode<R>(response, outputType)
}
companion object {
const val REQUEST_TYPE = "function.request"
const val RESPONSE_TYPE = "function.response"
const val SIZE_KEY = "size"
}
}

@ -1,58 +0,0 @@
package hep.dataforge.io.functions
import hep.dataforge.context.Context
import hep.dataforge.context.ContextAware
import hep.dataforge.io.Envelope
import hep.dataforge.io.IOPlugin
import hep.dataforge.io.Responder
import hep.dataforge.io.type
import hep.dataforge.meta.get
import hep.dataforge.meta.int
class RemoteFunctionServer(
override val context: Context,
val functionServer: FunctionServer
) : ContextAware, Responder {
private val plugin by lazy {
context.plugins.load(IOPlugin)
}
override suspend fun respond(request: Envelope): Envelope {
require(request.type == RemoteFunctionClient.REQUEST_TYPE) { "Unexpected message type: ${request.type}" }
val inputFormat = plugin.getInputFormat<Any>(request.meta)
val outputFormat = plugin.getOutputFormat<Any>(request.meta)
val size = request.meta[RemoteFunctionClient.SIZE_KEY].int ?: 1
val input = request.data?.read {
inputFormat.run {
List(size) {
readObject()
}
}
} ?: error("Input is empty")
val output = functionServer.callMany<Any, Any>(
request.meta,
input
)
return Envelope.invoke {
meta {
meta(request.meta)
}
type = RemoteFunctionClient.RESPONSE_TYPE
data {
outputFormat.run {
output.forEach {
writeObject(it)
}
}
}
}
}
}

@ -3,26 +3,26 @@ package hep.dataforge.io
import kotlinx.io.*
import kotlin.math.min
fun Output.writeRawString(str: String) {
public fun Output.writeRawString(str: String) {
str.forEach { writeByte(it.toByte()) }
}
fun Input.readRawString(size: Int): String {
public fun Input.readRawString(size: Int): String {
val array = CharArray(size) { readByte().toChar() }
return String(array)
return array.concatToString()
}
inline fun buildByteArray(expectedSize: Int = 16, block: Output.() -> Unit): ByteArray =
public inline fun buildByteArray(expectedSize: Int = 16, block: Output.() -> Unit): ByteArray =
ByteArrayOutput(expectedSize).apply(block).toByteArray()
@Suppress("FunctionName")
inline fun Binary(expectedSize: Int = 16, block: Output.() -> Unit): Binary =
public inline fun Binary(expectedSize: Int = 16, block: Output.() -> Unit): Binary =
buildByteArray(expectedSize, block).asBinary()
/**
* View section of a [Binary] as an independent binary
*/
class BinaryView(private val source: Binary, private val start: Int, override val size: Int) : Binary {
public class BinaryView(private val source: Binary, private val start: Int, override val size: Int) : Binary {
init {
require(start > 0)
@ -34,6 +34,6 @@ class BinaryView(private val source: Binary, private val start: Int, override va
}
}
fun Binary.view(start: Int, size: Int) = BinaryView(this, start, size)
public fun Binary.view(start: Int, size: Int): BinaryView = BinaryView(this, start, size)
operator fun Binary.get(range: IntRange) = view(range.first, range.last - range.first)
public operator fun Binary.get(range: IntRange): BinaryView = view(range.first, range.last - range.first)

@ -23,9 +23,9 @@ class EnvelopeFormatTest {
@Test
fun testTaggedFormat(){
TaggedEnvelopeFormat.run {
val byteArray = this.toByteArray(envelope)
val byteArray = writeToByteArray(envelope)
//println(byteArray.decodeToString())
val res = readByteArray(byteArray)
val res = readFromByteArray(byteArray)
assertEquals(envelope.meta,res.meta)
val double = res.data?.read {
readDouble()
@ -37,9 +37,9 @@ class EnvelopeFormatTest {
@Test
fun testTaglessFormat(){
TaglessEnvelopeFormat.run {
val byteArray = toByteArray(envelope)
val byteArray = writeToByteArray(envelope)
//println(byteArray.decodeToString())
val res = readByteArray(byteArray)
val res = readFromByteArray(byteArray)
assertEquals(envelope.meta,res.meta)
val double = res.data?.read {
readDouble()

@ -3,18 +3,16 @@ package hep.dataforge.io
import hep.dataforge.meta.*
import hep.dataforge.meta.JsonMeta.Companion.JSON_ARRAY_KEY
import kotlinx.io.asBinary
import kotlinx.serialization.json.JsonPrimitive
import kotlinx.serialization.json.json
import kotlinx.serialization.json.jsonArray
import kotlinx.serialization.json.*
import kotlin.test.Test
import kotlin.test.assertEquals
fun Meta.toByteArray(format: MetaFormat = JsonMetaFormat) = buildByteArray {
format.run { writeObject(this@toByteArray) }
format.writeObject(this@buildByteArray, this@toByteArray)
}
fun MetaFormat.fromByteArray(packet: ByteArray): Meta {
return packet.asBinary().read { readObject() }
return packet.asBinary().read { readObject(this) }
}
class MetaFormatTest {
@ -57,21 +55,21 @@ class MetaFormatTest {
@Test
fun testJsonToMeta() {
val json = jsonArray {
val json = buildJsonArray {
//top level array
+jsonArray {
+JsonPrimitive(88)
+json {
"c" to "aasdad"
"d" to true
}
}
+"value"
+jsonArray {
+JsonPrimitive(1.0)
+JsonPrimitive(2.0)
+JsonPrimitive(3.0)
}
add(buildJsonArray {
add(JsonPrimitive(88))
add(buildJsonObject {
put("c", "aasdad")
put("d", true)
})
})
add("value")
add(buildJsonArray {
add(JsonPrimitive(1.0))
add(JsonPrimitive(2.0))
add(JsonPrimitive(3.0))
})
}
val meta = json.toMetaItem().node!!

@ -1,12 +1,19 @@
package hep.dataforge.io
import hep.dataforge.meta.*
import hep.dataforge.meta.Meta
import hep.dataforge.meta.MetaItem
import hep.dataforge.meta.MetaSerializer
import hep.dataforge.names.Name
import hep.dataforge.names.toName
import kotlinx.serialization.ExperimentalSerializationApi
import kotlinx.serialization.cbor.Cbor
import kotlinx.serialization.json.Json
import kotlin.test.Test
import kotlin.test.assertEquals
val JSON_PRETTY: Json = Json { prettyPrint = true; useArrayPolymorphism = true }
val JSON_PLAIN: Json = Json { prettyPrint = false; useArrayPolymorphism = true }
class MetaSerializerTest {
val meta = Meta {
"a" put 22
@ -19,29 +26,33 @@ class MetaSerializerTest {
@Test
fun testMetaSerialization() {
val string = JSON_PRETTY.stringify(MetaSerializer, meta)
val restored = JSON_PLAIN.parse(MetaSerializer, string)
assertEquals(restored, meta)
val string = JSON_PRETTY.encodeToString(MetaSerializer, meta)
println(string)
val restored = JSON_PLAIN.decodeFromString(MetaSerializer, string)
assertEquals(meta, restored)
}
@OptIn(ExperimentalSerializationApi::class)
@Test
fun testCborSerialization() {
val bytes = Cbor.dump(MetaSerializer, meta)
println(bytes.contentToString())
val restored = Cbor.load(MetaSerializer, bytes)
assertEquals(restored, meta)
val bytes = Cbor.encodeToByteArray(MetaSerializer, meta)
println(bytes.decodeToString())
val restored = Cbor.decodeFromByteArray(MetaSerializer, bytes)
assertEquals(meta, restored)
}
@Test
fun testNameSerialization() {
val name = "a.b.c".toName()
val string = JSON_PRETTY.stringify(Name.serializer(), name)
val restored = JSON_PLAIN.parse(Name.serializer(), string)
assertEquals(restored, name)
val string = JSON_PRETTY.encodeToString(Name.serializer(), name)
val restored = JSON_PLAIN.decodeFromString(Name.serializer(), string)
assertEquals(name, restored)
}
@OptIn(ExperimentalSerializationApi::class)
@Test
fun testMetaItemDescriptor() {
val descriptor = MetaItem.serializer(MetaSerializer).descriptor.getElementDescriptor(0)
println(descriptor)
}
}

@ -3,5 +3,5 @@ package hep.dataforge.io
import kotlinx.io.ByteArrayInput
import kotlinx.io.use
fun <T : Any> IOFormat<T>.toByteArray(obj: T): ByteArray = buildByteArray { writeObject(obj) }
fun <T : Any> IOFormat<T>.readByteArray(array: ByteArray): T = ByteArrayInput(array).use { it.readObject() }
fun <T : Any> IOFormat<T>.writeToByteArray(obj: T): ByteArray = buildByteArray { writeObject(this, obj) }
fun <T : Any> IOFormat<T>.readFromByteArray(array: ByteArray): T = ByteArrayInput(array).use { readObject(it) }

@ -11,12 +11,12 @@ import java.nio.file.StandardOpenOption
import kotlin.reflect.full.isSuperclassOf
import kotlin.streams.asSequence
fun <R> Path.read(block: Input.() -> R): R = asBinary().read(block = block)
public fun <R> Path.read(block: Input.() -> R): R = asBinary().read(block = block)
/**
* Write a live output to a newly created file. If file does not exist, throws error
*/
fun Path.write(block: Output.() -> Unit): Unit {
public fun Path.write(block: Output.() -> Unit): Unit {
val stream = Files.newOutputStream(this, StandardOpenOption.WRITE, StandardOpenOption.CREATE_NEW)
stream.asOutput().use(block)
}
@ -24,7 +24,7 @@ fun Path.write(block: Output.() -> Unit): Unit {
/**
* Create a new file or append to exiting one with given output [block]
*/
fun Path.append(block: Output.() -> Unit): Unit {
public fun Path.append(block: Output.() -> Unit): Unit {
val stream = Files.newOutputStream(
this,
StandardOpenOption.WRITE, StandardOpenOption.APPEND, StandardOpenOption.CREATE
@ -35,7 +35,7 @@ fun Path.append(block: Output.() -> Unit): Unit {
/**
* Create a new file or replace existing one using given output [block]
*/
fun Path.rewrite(block: Output.() -> Unit): Unit {
public fun Path.rewrite(block: Output.() -> Unit): Unit {
val stream = Files.newOutputStream(
this,
StandardOpenOption.WRITE, StandardOpenOption.TRUNCATE_EXISTING, StandardOpenOption.CREATE
@ -43,9 +43,9 @@ fun Path.rewrite(block: Output.() -> Unit): Unit {
stream.asOutput().use(block)
}
fun Path.readEnvelope(format: EnvelopeFormat): Envelope {
public fun Path.readEnvelope(format: EnvelopeFormat): Envelope {
val partialEnvelope: PartialEnvelope = asBinary().read {
format.run { readPartial() }
format.run { readPartial(this@read) }
}
val offset: Int = partialEnvelope.dataOffset.toInt()
val size: Int = partialEnvelope.dataSize?.toInt() ?: (Files.size(this).toInt() - offset)
@ -58,7 +58,7 @@ fun Path.readEnvelope(format: EnvelopeFormat): Envelope {
*/
@Suppress("UNCHECKED_CAST")
@DFExperimental
inline fun <reified T : Any> IOPlugin.resolveIOFormat(): IOFormat<T>? {
public inline fun <reified T : Any> IOPlugin.resolveIOFormat(): IOFormat<T>? {
return ioFormatFactories.find { it.type.isSuperclassOf(T::class) } as IOFormat<T>?
}
@ -66,7 +66,11 @@ inline fun <reified T : Any> IOPlugin.resolveIOFormat(): IOFormat<T>? {
* Read file containing meta using given [formatOverride] or file extension to infer meta type.
* If [path] is a directory search for file starting with `meta` in it
*/
fun IOPlugin.readMetaFile(path: Path, formatOverride: MetaFormat? = null, descriptor: NodeDescriptor? = null): Meta {
public fun IOPlugin.readMetaFile(
path: Path,
formatOverride: MetaFormat? = null,
descriptor: NodeDescriptor? = null,
): Meta {
if (!Files.exists(path)) error("Meta file $path does not exist")
val actualPath: Path = if (Files.isDirectory(path)) {
@ -80,7 +84,7 @@ fun IOPlugin.readMetaFile(path: Path, formatOverride: MetaFormat? = null, descri
val metaFormat = formatOverride ?: resolveMetaFormat(extension) ?: error("Can't resolve meta format $extension")
return metaFormat.run {
actualPath.read {
readMeta(descriptor)
readMeta(this, descriptor)
}
}
}
@ -89,11 +93,11 @@ fun IOPlugin.readMetaFile(path: Path, formatOverride: MetaFormat? = null, descri
* Write meta to file using [metaFormat]. If [path] is a directory, write a file with name equals name of [metaFormat].
* Like "meta.json"
*/
fun IOPlugin.writeMetaFile(
public fun IOPlugin.writeMetaFile(
path: Path,
meta: Meta,
metaFormat: MetaFormatFactory = JsonMetaFormat,
descriptor: NodeDescriptor? = null
descriptor: NodeDescriptor? = null,
) {
val actualPath = if (Files.isDirectory(path)) {
path.resolve("@" + metaFormat.name.toString())
@ -102,7 +106,7 @@ fun IOPlugin.writeMetaFile(
}
metaFormat.run {
actualPath.write {
writeMeta(meta, descriptor)
writeMeta(this, meta, descriptor)
}
}
}
@ -111,7 +115,7 @@ fun IOPlugin.writeMetaFile(
* Return inferred [EnvelopeFormat] if only one format could read given file. If no format accepts file, return null. If
* multiple formats accepts file, throw an error.
*/
fun IOPlugin.peekBinaryFormat(path: Path): EnvelopeFormat? {
public fun IOPlugin.peekBinaryFormat(path: Path): EnvelopeFormat? {
val binary = path.asBinary()
val formats = envelopeFormatFactories.mapNotNull { factory ->
binary.read {
@ -126,8 +130,8 @@ fun IOPlugin.peekBinaryFormat(path: Path): EnvelopeFormat? {
}
}
val IOPlugin.Companion.META_FILE_NAME: String get() = "@meta"
val IOPlugin.Companion.DATA_FILE_NAME: String get() = "@data"
public val IOPlugin.Companion.META_FILE_NAME: String get() = "@meta"
public val IOPlugin.Companion.DATA_FILE_NAME: String get() = "@data"
/**
* Read and envelope from file if the file exists, return null if file does not exist.
@ -143,17 +147,18 @@ val IOPlugin.Companion.DATA_FILE_NAME: String get() = "@data"
* Return null otherwise.
*/
@DFExperimental
fun IOPlugin.readEnvelopeFile(
public fun IOPlugin.readEnvelopeFile(
path: Path,
readNonEnvelopes: Boolean = false,
formatPeeker: IOPlugin.(Path) -> EnvelopeFormat? = IOPlugin::peekBinaryFormat
formatPeeker: IOPlugin.(Path) -> EnvelopeFormat? = IOPlugin::peekBinaryFormat,
): Envelope? {
if (!Files.exists(path)) return null
//read two-files directory
if (Files.isDirectory(path)) {
val metaFile = Files.list(path).asSequence()
.singleOrNull { it.fileName.toString().startsWith(IOPlugin.META_FILE_NAME) }
val metaFile = Files.list(path).asSequence().singleOrNull {
it.fileName.toString().startsWith(IOPlugin.META_FILE_NAME)
}
val meta = if (metaFile == null) {
Meta.EMPTY
@ -182,9 +187,9 @@ fun IOPlugin.readEnvelopeFile(
/**
* Write a binary into file. Throws an error if file already exists
*/
fun <T : Any> IOFormat<T>.writeToFile(path: Path, obj: T) {
public fun <T : Any> IOFormat<T>.writeToFile(path: Path, obj: T) {
path.write {
writeObject(obj)
writeObject(this, obj)
}
}
@ -192,16 +197,14 @@ fun <T : Any> IOFormat<T>.writeToFile(path: Path, obj: T) {
* Write envelope file to given [path] using [envelopeFormat] and optional [metaFormat]
*/
@DFExperimental
fun IOPlugin.writeEnvelopeFile(
public fun IOPlugin.writeEnvelopeFile(
path: Path,
envelope: Envelope,
envelopeFormat: EnvelopeFormat = TaggedEnvelopeFormat,
metaFormat: MetaFormatFactory? = null
metaFormat: MetaFormatFactory? = null,
) {
path.rewrite {
with(envelopeFormat) {
writeEnvelope(envelope, metaFormat ?: envelopeFormat.defaultMetaFormat)
}
envelopeFormat.writeEnvelope(this, envelope, metaFormat ?: envelopeFormat.defaultMetaFormat)
}
}
@ -209,10 +212,10 @@ fun IOPlugin.writeEnvelopeFile(
* Write separate meta and data files to given directory [path]
*/
@DFExperimental
fun IOPlugin.writeEnvelopeDirectory(
public fun IOPlugin.writeEnvelopeDirectory(
path: Path,
envelope: Envelope,
metaFormat: MetaFormatFactory = JsonMetaFormat
metaFormat: MetaFormatFactory = JsonMetaFormat,
) {
if (!Files.exists(path)) {
Files.createDirectories(path)

@ -1,28 +0,0 @@
package hep.dataforge.io
import hep.dataforge.io.functions.FunctionServer
import hep.dataforge.io.functions.function
import hep.dataforge.meta.Meta
import hep.dataforge.names.Name
import kotlin.reflect.KClass
import kotlin.reflect.full.isSuperclassOf
fun IOPlugin.resolveIOFormatName(type: KClass<*>): Name {
return ioFormatFactories.find { it.type.isSuperclassOf(type) }?.name
?: error("Can't resolve IOFormat for type $type")
}
inline fun <reified T : Any, reified R : Any> IOPlugin.generateFunctionMeta(functionName: String): Meta = Meta {
FunctionServer.FUNCTION_NAME_KEY put functionName
FunctionServer.INPUT_FORMAT_KEY put resolveIOFormatName(T::class).toString()
FunctionServer.OUTPUT_FORMAT_KEY put resolveIOFormatName(R::class).toString()
}
inline fun <reified T : Any, reified R : Any> FunctionServer.function(
functionName: String
): (suspend (T) -> R) {
val plugin = context.plugins.get<IOPlugin>() ?: error("IO plugin not loaded")
val meta = plugin.generateFunctionMeta<T, R>(functionName)
return function(meta)
}

@ -1,4 +1,4 @@
package hep.dataforge.io.tcp
package hep.dataforge.io
import kotlinx.io.*
import kotlinx.io.buffer.Buffer
@ -30,16 +30,16 @@ private class BlockingStreamInput(val source: InputStream) : Input() {
}
}
fun <R> InputStream.read(size: Int, block: Input.() -> R): R {
public fun <R> InputStream.read(size: Int, block: Input.() -> R): R {
val buffer = ByteArray(size)
read(buffer)
return buffer.asBinary().read(block = block)
}
fun <R> InputStream.read(block: Input.() -> R): R = asInput().block()
public fun <R> InputStream.read(block: Input.() -> R): R = asInput().block()
fun <R> InputStream.readBlocking(block: Input.() -> R): R = BlockingStreamInput(this).block()
public fun <R> InputStream.readBlocking(block: Input.() -> R): R = BlockingStreamInput(this).block()
inline fun OutputStream.write(block: Output.() -> Unit) {
public inline fun OutputStream.write(block: Output.() -> Unit) {
asOutput().block()
}

@ -1,65 +0,0 @@
package hep.dataforge.io.tcp
import hep.dataforge.context.Context
import hep.dataforge.context.ContextAware
import hep.dataforge.io.*
import hep.dataforge.meta.Meta
import kotlinx.coroutines.asCoroutineDispatcher
import kotlinx.coroutines.withContext
import java.net.Socket
import java.util.concurrent.Executors
import kotlin.time.ExperimentalTime
@ExperimentalTime
class EnvelopeClient(
override val context: Context,
val host: String,
val port: Int,
formatFactory: EnvelopeFormatFactory = TaggedEnvelopeFormat,
formatMeta: Meta = Meta.EMPTY
) : Responder, ContextAware {
private val dispatcher = Executors.newSingleThreadExecutor().asCoroutineDispatcher()
private val format = formatFactory(formatMeta, context = context)
// private var socket: SocketChannel? = null
//
// private fun getSocket(): Socket {
// val socket = socket ?: Socket(host, port).also { this.socket = it }
// return if (socket.isConnected) {
// socket
// } else {
// Socket(host, port).also { this.socket = it }
// }
// }
suspend fun close() {
try {
respond(
Envelope {
type = EnvelopeServer.SHUTDOWN_ENVELOPE_TYPE
}
)
} catch (ex: Exception) {
logger.error { ex }
}
}
override suspend fun respond(request: Envelope): Envelope = withContext(dispatcher) {
//val address = InetSocketAddress(host,port)
val socket = Socket(host, port)
val inputStream = socket.getInputStream()
val outputStream = socket.getOutputStream()
format.run {
outputStream.write {
writeObject(request)
}
logger.debug { "Sent request with type ${request.type} to ${socket.remoteSocketAddress}" }
val res = inputStream.readBlocking { readObject() }
logger.debug { "Received response with type ${res.type} from ${socket.remoteSocketAddress}" }
return@withContext res
}
}
}

@ -1,103 +0,0 @@
package hep.dataforge.io.tcp
import hep.dataforge.context.Context
import hep.dataforge.context.ContextAware
import hep.dataforge.io.EnvelopeFormatFactory
import hep.dataforge.io.Responder
import hep.dataforge.io.TaggedEnvelopeFormat
import hep.dataforge.io.type
import hep.dataforge.meta.Meta
import kotlinx.coroutines.*
import java.net.ServerSocket
import java.net.Socket
import kotlin.concurrent.thread
class EnvelopeServer(
override val context: Context,
val port: Int,
val responder: Responder,
val scope: CoroutineScope,
formatFactory: EnvelopeFormatFactory = TaggedEnvelopeFormat,
formatMeta: Meta = Meta.EMPTY
) : ContextAware {
private var job: Job? = null
private val format = formatFactory(formatMeta, context = context)
fun start() {
if (job == null) {
logger.info { "Starting envelope server on port $port" }
job = scope.launch(Dispatchers.IO) {
val serverSocket = ServerSocket(port)
//TODO add handshake and format negotiation
while (isActive && !serverSocket.isClosed) {
val socket = serverSocket.accept()
logger.info { "Accepted connection from ${socket.remoteSocketAddress}" }
readSocket(socket)
}
}
}
}
fun stop() {
logger.info { "Stopping envelope server on port $port" }
job?.cancel()
job = null
}
// private fun CoroutineScope.readSocket(socket: Socket) {
// launch(Dispatchers.IO) {
// val input = socket.getInputStream().asInput()
// val output = socket.getOutputStream().asOutput()
// format.run {
// while (isActive && socket.isConnected) {
// val request = input.readThis()
// logger.debug { "Accepted request with type ${request.type} from ${socket.remoteSocketAddress}" }
// if (request.type == SHUTDOWN_ENVELOPE_TYPE) {
// //Echo shutdown command
// logger.info { "Accepted graceful shutdown signal from ${socket.inetAddress}" }
// socket.close()
// cancel("Graceful connection shutdown requested by client")
// }
// val response = responder.respond(request)
// output.writeThis(response)
// }
// }
// }
// }
private fun readSocket(socket: Socket) {
thread {
val inputStream = socket.getInputStream()
val outputStream = socket.getOutputStream()
format.run {
while (socket.isConnected) {
val request = inputStream.readBlocking { readObject() }
logger.debug { "Accepted request with type ${request.type} from ${socket.remoteSocketAddress}" }
if (request.type == SHUTDOWN_ENVELOPE_TYPE) {
//Echo shutdown command
outputStream.write {
writeObject(request)
}
logger.info { "Accepted graceful shutdown signal from ${socket.inetAddress}" }
socket.close()
return@thread
// cancel("Graceful connection shutdown requested by client")
}
runBlocking {
val response = responder.respond(request)
outputStream.write {
writeObject(response)
}
logger.debug { "Sent response with type ${response.type} to ${socket.remoteSocketAddress}" }
}
}
}
}
}
companion object {
const val SHUTDOWN_ENVELOPE_TYPE = "@shutdown"
}
}

@ -1,70 +0,0 @@
package hep.dataforge.io.tcp
import hep.dataforge.context.Global
import hep.dataforge.io.Envelope
import hep.dataforge.io.Responder
import hep.dataforge.io.TaggedEnvelopeFormat
import hep.dataforge.io.toByteArray
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.runBlocking
import kotlinx.io.writeDouble
import org.junit.jupiter.api.AfterAll
import org.junit.jupiter.api.BeforeAll
import org.junit.jupiter.api.Timeout
import kotlin.test.Test
import kotlin.test.assertEquals
import kotlin.time.ExperimentalTime
@ExperimentalStdlibApi
object EchoResponder : Responder {
override suspend fun respond(request: Envelope): Envelope {
val string = TaggedEnvelopeFormat().run { toByteArray(request).decodeToString() }
println("ECHO:")
println(string)
return request
}
}
@ExperimentalTime
@ExperimentalStdlibApi
class EnvelopeServerTest {
companion object {
@JvmStatic
val echoEnvelopeServer = EnvelopeServer(Global, 7778, EchoResponder, GlobalScope)
@BeforeAll
@JvmStatic
fun start() {
echoEnvelopeServer.start()
}
@AfterAll
@JvmStatic
fun close() {
echoEnvelopeServer.stop()
}
}
@Test
@Timeout(1)
fun doEchoTest() {
val request = Envelope {
type = "test.echo"
meta {
"test.value" put 22
}
data {
writeDouble(22.7)
}
}
val client = EnvelopeClient(Global, host = "localhost", port = 7778)
runBlocking {
val response = client.respond(request)
assertEquals(request.meta, response.meta)
// assertEquals(request.data?.toBytes()?.decodeToString(), response.data?.toBytes()?.decodeToString())
client.close()
}
}
}

File diff suppressed because it is too large Load Diff

@ -1,9 +1,12 @@
import scientifik.useSerialization
plugins {
id("scientifik.mpp")
id("ru.mipt.npm.mpp")
id("ru.mipt.npm.native")
}
useSerialization()
kscience {
useSerialization{
json()
}
}
description = "Meta definition and basic operations on meta"

@ -4,25 +4,30 @@ import hep.dataforge.names.Name
import hep.dataforge.names.NameToken
import hep.dataforge.names.asName
import hep.dataforge.names.plus
import kotlinx.serialization.*
import kotlinx.serialization.KSerializer
import kotlinx.serialization.Serializable
import kotlinx.serialization.descriptors.SerialDescriptor
import kotlinx.serialization.encoding.Decoder
import kotlinx.serialization.encoding.Encoder
import kotlin.collections.set
//TODO add validator to configuration
data class MetaListener(
public data class MetaListener(
val owner: Any? = null,
val action: (name: Name, oldItem: MetaItem<*>?, newItem: MetaItem<*>?) -> Unit
)
interface ObservableMeta : Meta {
fun onChange(owner: Any?, action: (name: Name, oldItem: MetaItem<*>?, newItem: MetaItem<*>?) -> Unit)
fun removeListener(owner: Any?)
public interface ObservableMeta : Meta {
public fun onChange(owner: Any?, action: (name: Name, oldItem: MetaItem<*>?, newItem: MetaItem<*>?) -> Unit)
public fun removeListener(owner: Any?)
}
/**
* Mutable meta representing object state
*/
@Serializable
class Config : AbstractMutableMeta<Config>(), ObservableMeta {
@Serializable(Config.Companion::class)
public class Config() : AbstractMutableMeta<Config>(), ObservableMeta {
private val listeners = HashSet<MetaListener>()
@ -46,12 +51,12 @@ class Config : AbstractMutableMeta<Config>(), ObservableMeta {
override fun replaceItem(key: NameToken, oldItem: MetaItem<Config>?, newItem: MetaItem<Config>?) {
if (newItem == null) {
_items.remove(key)
children.remove(key)
if (oldItem != null && oldItem is MetaItem.NodeItem<Config>) {
oldItem.node.removeListener(this)
}
} else {
_items[key] = newItem
children[key] = newItem
if (newItem is MetaItem.NodeItem) {
newItem.node.onChange(this) { name, oldChild, newChild ->
itemChanged(key + name, oldChild, newChild)
@ -68,10 +73,9 @@ class Config : AbstractMutableMeta<Config>(), ObservableMeta {
override fun empty(): Config = Config()
@Serializer(Config::class)
companion object ConfigSerializer : KSerializer<Config> {
public companion object ConfigSerializer : KSerializer<Config> {
fun empty(): Config = Config()
public fun empty(): Config = Config()
override val descriptor: SerialDescriptor get() = MetaSerializer.descriptor
override fun deserialize(decoder: Decoder): Config {
@ -84,9 +88,9 @@ class Config : AbstractMutableMeta<Config>(), ObservableMeta {
}
}
operator fun Config.get(token: NameToken): MetaItem<Config>? = items[token]
public operator fun Config.get(token: NameToken): MetaItem<Config>? = items[token]
fun Meta.asConfig(): Config = this as? Config ?: Config().also { builder ->
public fun Meta.asConfig(): Config = this as? Config ?: Config().also { builder ->
this.items.mapValues { entry ->
val item = entry.value
builder[entry.key.asName()] = when (item) {

@ -1,115 +1,24 @@
package hep.dataforge.meta
import hep.dataforge.meta.descriptors.*
import hep.dataforge.names.Name
import hep.dataforge.names.asName
import hep.dataforge.names.toName
import hep.dataforge.values.Value
import kotlin.properties.ReadWriteProperty
import kotlin.reflect.KProperty
/**
* A container that holds a [Config] and a default item provider.
* Default item provider could be use for example to reference parent configuration.
* It is not possible to know if some property is declared by provider just by looking on [Configurable],
* this information should be provided externally.
* A container that holds a [Config].
*/
interface Configurable : Described, MutableItemProvider {
public interface Configurable {
/**
* Backing config
*/
val config: Config
/**
* Default meta item provider
*/
fun getDefaultItem(name: Name): MetaItem<*>? = null
/**
* Check if property with given [name] could be assigned to [value]
*/
fun validateItem(name: Name, item: MetaItem<*>?): Boolean {
val descriptor = descriptor?.get(name)
return descriptor?.validateItem(item) ?: true
}
override val descriptor: NodeDescriptor? get() = null
/**
* Get a property with default
*/
override fun getItem(name: Name): MetaItem<*>? =
config[name] ?: getDefaultItem(name) ?: descriptor?.get(name)?.defaultItem()
/**
* Set a configurable property
*/
override fun setItem(name: Name, item: MetaItem<*>?) {
if (validateItem(name, item)) {
config.setItem(name, item)
} else {
error("Validation failed for property $name with value $item")
}
}
public val config: Config
}
/**
* Reset the property to its default value
*/
fun Configurable.resetProperty(name: Name) {
setItem(name, null)
}
fun Configurable.getItem(key: String) = getItem(key.toName())
fun Configurable.setItem(name: Name, value: Value?) = setItem(name, value?.let { MetaItem.ValueItem(value) })
fun Configurable.setItem(name: Name, meta: Meta?) = setItem(name, meta?.let { MetaItem.NodeItem(meta) })
fun Configurable.setItem(key: String, item: MetaItem<*>?) = setItem(key.toName(), item)
fun Configurable.setItem(key: String, value: Value?) = setItem(key, value?.let { MetaItem.ValueItem(value) })
fun Configurable.setItem(key: String, meta: Meta?) = setItem(key, meta?.let { MetaItem.NodeItem(meta) })
fun <T : Configurable> T.configure(meta: Meta): T = this.apply { config.update(meta) }
public fun <T : Configurable> T.configure(meta: Meta): T = this.apply { config.update(meta) }
@DFBuilder
inline fun <T : Configurable> T.configure(action: Config.() -> Unit): T = apply { config.apply(action) }
public inline fun <T : Configurable> T.configure(action: Config.() -> Unit): T = apply { config.apply(action) }
/* Node delegates */
fun Configurable.config(key: Name? = null): ReadWriteProperty<Any?, Config?> =
config.node(key)
fun MutableItemProvider.node(key: Name? = null): ReadWriteProperty<Any?, Meta?> = item(key).convert(
reader = { it.node },
writer = { it?.let { MetaItem.NodeItem(it) } }
)
fun <T : Configurable> Configurable.spec(
spec: Specification<T>, key: Name? = null
): ReadWriteProperty<Any?, T?> = object : ReadWriteProperty<Any?, T?> {
override fun getValue(thisRef: Any?, property: KProperty<*>): T? {
val name = key ?: property.name.asName()
return config[name].node?.let { spec.wrap(it) }
}
override fun setValue(thisRef: Any?, property: KProperty<*>, value: T?) {
val name = key ?: property.name.asName()
config[name] = value?.config
}
}
fun <T : Configurable> Configurable.spec(
spec: Specification<T>, default: T, key: Name? = null
): ReadWriteProperty<Any?, T> = object : ReadWriteProperty<Any?, T> {
override fun getValue(thisRef: Any?, property: KProperty<*>): T {
val name = key ?: property.name.asName()
return config[name].node?.let { spec.wrap(it) } ?: default
}
override fun setValue(thisRef: Any?, property: KProperty<*>, value: T) {
val name = key ?: property.name.asName()
config[name] = value.config
}
}
public fun Configurable.config(key: Name? = null): ReadWriteProperty<Any?, Config?> = config.node(key)

@ -5,62 +5,107 @@ import hep.dataforge.names.Name
import hep.dataforge.names.asName
import hep.dataforge.values.Value
import kotlin.properties.ReadOnlyProperty
import kotlin.reflect.KProperty
/* Meta delegates */
open class ItemDelegate(
open val owner: ItemProvider,
val key: Name? = null,
open val default: MetaItem<*>? = null
) : ReadOnlyProperty<Any?, MetaItem<*>?> {
override fun getValue(thisRef: Any?, property: KProperty<*>): MetaItem<*>? {
return owner.getItem(key ?: property.name.asName()) ?: default
}
}
public typealias ItemDelegate = ReadOnlyProperty<Any?, MetaItem<*>?>
fun ItemProvider.item(key: Name? = null): ItemDelegate = ItemDelegate(this, key)
public fun ItemProvider.item(key: Name? = null): ItemDelegate = ReadOnlyProperty { _, property ->
getItem(key ?: property.name.asName())
}
//TODO add caching for sealed nodes
//Read-only delegates for Metas
/**
* Apply a converter to this delegate creating a delegate with a custom type
*/
public fun <R : Any> ItemDelegate.convert(
converter: MetaConverter<R>,
): ReadOnlyProperty<Any?, R?> = ReadOnlyProperty { thisRef, property ->
this@convert.getValue(thisRef, property)?.let(converter::itemToObject)
}
/*
*
*/
public fun <R : Any> ItemDelegate.convert(
converter: MetaConverter<R>,
default: () -> R,
): ReadOnlyProperty<Any?, R> = ReadOnlyProperty<Any?, R> { thisRef, property ->
this@convert.getValue(thisRef, property)?.let(converter::itemToObject) ?: default()
}
/**
* A converter with a custom reader transformation
*/
public fun <R> ItemDelegate.convert(
reader: (MetaItem<*>?) -> R,
): ReadOnlyProperty<Any?, R> = ReadOnlyProperty<Any?, R> { thisRef, property ->
this@convert.getValue(thisRef, property).let(reader)
}
/* Read-only delegates for [ItemProvider] */
/**
* A property delegate that uses custom key
*/
fun ItemProvider.value(key: Name? = null): ReadOnlyProperty<Any?, Value?> =
public fun ItemProvider.value(key: Name? = null): ReadOnlyProperty<Any?, Value?> =
item(key).convert(MetaConverter.value)
fun ItemProvider.string(key: Name? = null): ReadOnlyProperty<Any?, String?> =
public fun ItemProvider.string(key: Name? = null): ReadOnlyProperty<Any?, String?> =
item(key).convert(MetaConverter.string)
fun ItemProvider.boolean(key: Name? = null): ReadOnlyProperty<Any?, Boolean?> =
public fun ItemProvider.boolean(key: Name? = null): ReadOnlyProperty<Any?, Boolean?> =
item(key).convert(MetaConverter.boolean)
fun ItemProvider.number(key: Name? = null): ReadOnlyProperty<Any?, Number?> =
public fun ItemProvider.number(key: Name? = null): ReadOnlyProperty<Any?, Number?> =
item(key).convert(MetaConverter.number)
fun ItemProvider.node(key: Name? = null): ReadOnlyProperty<Any?, Meta?> =
public fun ItemProvider.double(key: Name? = null): ReadOnlyProperty<Any?, Double?> =
item(key).convert(MetaConverter.double)
public fun ItemProvider.float(key: Name? = null): ReadOnlyProperty<Any?, Float?> =
item(key).convert(MetaConverter.float)
public fun ItemProvider.int(key: Name? = null): ReadOnlyProperty<Any?, Int?> =
item(key).convert(MetaConverter.int)
public fun ItemProvider.long(key: Name? = null): ReadOnlyProperty<Any?, Long?> =
item(key).convert(MetaConverter.long)
public fun ItemProvider.node(key: Name? = null): ReadOnlyProperty<Any?, Meta?> =
item(key).convert(MetaConverter.meta)
fun ItemProvider.string(default: String, key: Name? = null): ReadOnlyProperty<Any?, String> =
public fun ItemProvider.string(default: String, key: Name? = null): ReadOnlyProperty<Any?, String> =
item(key).convert(MetaConverter.string) { default }
fun ItemProvider.boolean(default: Boolean, key: Name? = null): ReadOnlyProperty<Any?, Boolean> =
public fun ItemProvider.boolean(default: Boolean, key: Name? = null): ReadOnlyProperty<Any?, Boolean> =
item(key).convert(MetaConverter.boolean) { default }
fun ItemProvider.number(default: Number, key: Name? = null): ReadOnlyProperty<Any?, Number> =
public fun ItemProvider.number(default: Number, key: Name? = null): ReadOnlyProperty<Any?, Number> =
item(key).convert(MetaConverter.number) { default }
inline fun <reified E : Enum<E>> ItemProvider.enum(default: E, key: Name? = null): ReadOnlyProperty<Any?, E> =
public fun ItemProvider.double(default: Double, key: Name? = null): ReadOnlyProperty<Any?, Double> =
item(key).convert(MetaConverter.double) { default }
public fun ItemProvider.float(default: Float, key: Name? = null): ReadOnlyProperty<Any?, Float> =
item(key).convert(MetaConverter.float) { default }
public fun ItemProvider.int(default: Int, key: Name? = null): ReadOnlyProperty<Any?, Int> =
item(key).convert(MetaConverter.int) { default }
public fun ItemProvider.long(default: Long, key: Name? = null): ReadOnlyProperty<Any?, Long> =
item(key).convert(MetaConverter.long) { default }
public inline fun <reified E : Enum<E>> ItemProvider.enum(default: E, key: Name? = null): ReadOnlyProperty<Any?, E> =
item(key).convert(MetaConverter.enum()) { default }
fun ItemProvider.string(key: Name? = null, default: () -> String): ReadOnlyProperty<Any?, String> =
public fun ItemProvider.string(key: Name? = null, default: () -> String): ReadOnlyProperty<Any?, String> =
item(key).convert(MetaConverter.string, default)
fun ItemProvider.boolean(key: Name? = null, default: () -> Boolean): ReadOnlyProperty<Any?, Boolean> =
public fun ItemProvider.boolean(key: Name? = null, default: () -> Boolean): ReadOnlyProperty<Any?, Boolean> =
item(key).convert(MetaConverter.boolean, default)
fun ItemProvider.number(key: Name? = null, default: () -> Number): ReadOnlyProperty<Any?, Number> =
public fun ItemProvider.number(key: Name? = null, default: () -> Number): ReadOnlyProperty<Any?, Number> =
item(key).convert(MetaConverter.number, default)

@ -4,6 +4,7 @@ package hep.dataforge.meta
import hep.dataforge.meta.JsonMeta.Companion.JSON_ARRAY_KEY
import hep.dataforge.meta.descriptors.ItemDescriptor
import hep.dataforge.meta.descriptors.ItemDescriptor.Companion.DEFAULT_INDEX_KEY
import hep.dataforge.meta.descriptors.NodeDescriptor
import hep.dataforge.meta.descriptors.ValueDescriptor
import hep.dataforge.names.NameToken
@ -15,7 +16,7 @@ import kotlinx.serialization.json.*
/**
* @param descriptor reserved for custom serialization in future
*/
fun Value.toJson(descriptor: ValueDescriptor? = null): JsonElement {
public fun Value.toJson(descriptor: ValueDescriptor? = null): JsonElement {
return if (isList()) {
JsonArray(list.map { it.toJson() })
} else {
@ -52,7 +53,7 @@ private fun Meta.toJsonWithIndex(descriptor: NodeDescriptor?, indexValue: String
fun addElement(key: String) {
val itemDescriptor = descriptor?.items?.get(key)
val jsonKey = key.toJsonKey(itemDescriptor)
val items: Map<String, MetaItem<*>> = getIndexed(key)
val items: Map<String?, MetaItem<*>> = getIndexed(key)
when (items.size) {
0 -> {
//do nothing
@ -61,9 +62,9 @@ private fun Meta.toJsonWithIndex(descriptor: NodeDescriptor?, indexValue: String
elementMap[jsonKey] = items.values.first().toJsonElement(itemDescriptor, null)
}
else -> {
val array = jsonArray {
val array = buildJsonArray {
items.forEach { (index, item) ->
+item.toJsonElement(itemDescriptor, index)
add(item.toJsonElement(itemDescriptor, index))
}
}
elementMap[jsonKey] = array
@ -75,32 +76,31 @@ private fun Meta.toJsonWithIndex(descriptor: NodeDescriptor?, indexValue: String
if (indexValue != null) {
val indexKey = descriptor?.indexKey ?: NodeDescriptor.DEFAULT_INDEX_KEY
val indexKey = descriptor?.indexKey ?: DEFAULT_INDEX_KEY
elementMap[indexKey] = JsonPrimitive(indexValue)
}
return JsonObject(elementMap)
}
fun Meta.toJson(descriptor: NodeDescriptor? = null): JsonObject = toJsonWithIndex(descriptor, null)
public fun Meta.toJson(descriptor: NodeDescriptor? = null): JsonObject = toJsonWithIndex(descriptor, null)
fun JsonObject.toMeta(descriptor: NodeDescriptor? = null): JsonMeta = JsonMeta(this, descriptor)
public fun JsonObject.toMeta(descriptor: NodeDescriptor? = null): JsonMeta = JsonMeta(this, descriptor)
fun JsonPrimitive.toValue(descriptor: ValueDescriptor?): Value {
public fun JsonPrimitive.toValue(descriptor: ValueDescriptor?): Value {
return when (this) {
JsonNull -> Null
is JsonLiteral -> {
when (body) {
true -> True
false -> False
is Number -> NumberValue(body as Number)
else -> StringValue(content)
else -> {
if (isString) {
StringValue(content)
} else {
content.parseValue()
}
}
}
}
fun JsonElement.toMetaItem(descriptor: ItemDescriptor? = null): MetaItem<JsonMeta> = when (this) {
public fun JsonElement.toMetaItem(descriptor: ItemDescriptor? = null): MetaItem<JsonMeta> = when (this) {
is JsonPrimitive -> {
val value = this.toValue(descriptor as? ValueDescriptor)
MetaItem.ValueItem(value)
@ -122,7 +122,7 @@ fun JsonElement.toMetaItem(descriptor: ItemDescriptor? = null): MetaItem<JsonMet
MetaItem.ValueItem(value)
} else {
//We can't return multiple items therefore we create top level node
json { JSON_ARRAY_KEY to this@toMetaItem }.toMetaItem(descriptor)
buildJsonObject { put(JSON_ARRAY_KEY, this@toMetaItem) }.toMetaItem(descriptor)
}
}
}
@ -130,7 +130,7 @@ fun JsonElement.toMetaItem(descriptor: ItemDescriptor? = null): MetaItem<JsonMet
/**
* A meta wrapping json object
*/
class JsonMeta(val json: JsonObject, val descriptor: NodeDescriptor? = null) : MetaBase() {
public class JsonMeta(private val json: JsonObject, private val descriptor: NodeDescriptor? = null) : MetaBase() {
private fun buildItems(): Map<NameToken, MetaItem<JsonMeta>> {
val map = LinkedHashMap<NameToken, MetaItem<JsonMeta>>()
@ -159,9 +159,9 @@ class JsonMeta(val json: JsonObject, val descriptor: NodeDescriptor? = null) : M
)
map[key] = MetaItem.ValueItem(listValue)
} else value.forEachIndexed { index, jsonElement ->
val indexKey = (itemDescriptor as? NodeDescriptor)?.indexKey ?: NodeDescriptor.DEFAULT_INDEX_KEY
val indexKey = (itemDescriptor as? NodeDescriptor)?.indexKey ?: DEFAULT_INDEX_KEY
val indexValue: String = (jsonElement as? JsonObject)
?.get(indexKey)?.contentOrNull
?.get(indexKey)?.jsonPrimitive?.contentOrNull
?: index.toString() //In case index is non-string, the backward transformation will be broken.
val token = key.withIndex(indexValue)
@ -174,10 +174,10 @@ class JsonMeta(val json: JsonObject, val descriptor: NodeDescriptor? = null) : M
override val items: Map<NameToken, MetaItem<JsonMeta>> by lazy(::buildItems)
companion object{
public companion object {
/**
* A key representing top-level json array of nodes, which could not be directly represented by a meta node
*/
const val JSON_ARRAY_KEY = "@jsonArray"
public const val JSON_ARRAY_KEY: String = "@jsonArray"
}
}

@ -7,9 +7,9 @@ import hep.dataforge.names.NameToken
* A meta laminate consisting of multiple immutable meta layers. For mutable front layer, use [Scheme].
* If [layers] list contains a [Laminate] it is flat-mapped.
*/
class Laminate(layers: List<Meta>) : MetaBase() {
public class Laminate(layers: List<Meta>) : MetaBase() {
val layers: List<Meta> = layers.flatMap {
public val layers: List<Meta> = layers.flatMap {
if (it is Laminate) {
it.layers
} else {
@ -17,8 +17,6 @@ class Laminate(layers: List<Meta>) : MetaBase() {
}
}
constructor(vararg layers: Meta?) : this(layers.filterNotNull())
override val items: Map<NameToken, MetaItem<Meta>> by lazy {
layers.map { it.items.keys }.flatten().associateWith { key ->
layers.asSequence().map { it.items[key] }.filterNotNull().let(replaceRule)
@ -28,21 +26,21 @@ class Laminate(layers: List<Meta>) : MetaBase() {
/**
* Generate sealed meta using [mergeRule]
*/
fun merge(): SealedMeta {
public fun merge(): SealedMeta {
val items = layers.map { it.items.keys }.flatten().associateWith { key ->
layers.asSequence().map { it.items[key] }.filterNotNull().merge()
}
return SealedMeta(items)
}
companion object {
public companion object {
/**
* The default rule which always uses the first found item in sequence alongside with its children.
*
* TODO add picture
*/
val replaceRule: (Sequence<MetaItem<*>>) -> MetaItem<SealedMeta> = { it.first().seal() }
public val replaceRule: (Sequence<MetaItem<*>>) -> MetaItem<SealedMeta> = { it.first().seal() }
private fun Sequence<MetaItem<*>>.merge(): MetaItem<SealedMeta> {
return when {
@ -76,14 +74,17 @@ class Laminate(layers: List<Meta>) : MetaBase() {
* The values a replaced but meta children are joined
* TODO add picture
*/
val mergeRule: (Sequence<MetaItem<*>>) -> MetaItem<SealedMeta> = { it.merge() }
public val mergeRule: (Sequence<MetaItem<*>>) -> MetaItem<SealedMeta> = { it.merge() }
}
}
@Suppress("FunctionName")
public fun Laminate(vararg layers: Meta?): Laminate = Laminate(layers.filterNotNull())
/**
* Performance optimized version of get method
*/
fun Laminate.getFirst(name: Name): MetaItem<*>? {
public fun Laminate.getFirst(name: Name): MetaItem<*>? {
layers.forEach { layer ->
layer[name]?.let { return it }
}
@ -93,11 +94,11 @@ fun Laminate.getFirst(name: Name): MetaItem<*>? {
/**
* Create a new [Laminate] adding given layer to the top
*/
fun Laminate.withTop(meta: Meta): Laminate = Laminate(listOf(meta) + layers)
public fun Laminate.withTop(meta: Meta): Laminate = Laminate(listOf(meta) + layers)
/**
* Create a new [Laminate] adding given layer to the bottom
*/
fun Laminate.withBottom(meta: Meta): Laminate = Laminate(layers + meta)
public fun Laminate.withBottom(meta: Meta): Laminate = Laminate(layers + meta)
//TODO add custom rules for Laminate merge

@ -4,8 +4,12 @@ import hep.dataforge.meta.Meta.Companion.VALUE_KEY
import hep.dataforge.meta.MetaItem.NodeItem
import hep.dataforge.meta.MetaItem.ValueItem
import hep.dataforge.names.*
import hep.dataforge.values.*
import kotlinx.serialization.*
import hep.dataforge.values.EnumValue
import hep.dataforge.values.Null
import hep.dataforge.values.Value
import hep.dataforge.values.boolean
import kotlinx.serialization.Serializable
import kotlinx.serialization.json.Json
/**
@ -13,28 +17,17 @@ import kotlinx.serialization.*
* * a [ValueItem] (leaf)
* * a [NodeItem] (node)
*/
@Serializable
sealed class MetaItem<out M : Meta> {
@Serializable(MetaItemSerializer::class)
public sealed class MetaItem<out M : Meta>() {
abstract override fun equals(other: Any?): Boolean
abstract override fun hashCode(): Int
@Serializable
class ValueItem(val value: Value) : MetaItem<Nothing>() {
@Serializable(MetaItemSerializer::class)
public class ValueItem(public val value: Value) : MetaItem<Nothing>() {
override fun toString(): String = value.toString()
@Serializer(ValueItem::class)
companion object : KSerializer<ValueItem> {
override val descriptor: SerialDescriptor get() = ValueSerializer.descriptor
override fun deserialize(decoder: Decoder): ValueItem = ValueItem(ValueSerializer.deserialize(decoder))
override fun serialize(encoder: Encoder, value: ValueItem) {
ValueSerializer.serialize(encoder, value.value)
}
}
override fun equals(other: Any?): Boolean {
return this.value == (other as? ValueItem)?.value
}
@ -44,29 +37,18 @@ sealed class MetaItem<out M : Meta> {
}
}
@Serializable
class NodeItem<M : Meta>(@Serializable(MetaSerializer::class) val node: M) : MetaItem<M>() {
@Serializable(MetaItemSerializer::class)
public class NodeItem<M : Meta>(public val node: M) : MetaItem<M>() {
//Fixing serializer for node could cause class cast problems, but it should not since Meta descendants are not serializeable
override fun toString(): String = node.toString()
@Serializer(NodeItem::class)
companion object : KSerializer<NodeItem<out Meta>> {
override val descriptor: SerialDescriptor get() = MetaSerializer.descriptor
override fun deserialize(decoder: Decoder): NodeItem<*> = NodeItem(MetaSerializer.deserialize(decoder))
override fun serialize(encoder: Encoder, value: NodeItem<*>) {
MetaSerializer.serialize(encoder, value.node)
}
}
override fun equals(other: Any?): Boolean = node == (other as? NodeItem<*>)?.node
override fun hashCode(): Int = node.hashCode()
}
companion object {
fun of(arg: Any?): MetaItem<*> {
public companion object {
public fun of(arg: Any?): MetaItem<*> {
return when (arg) {
null -> ValueItem(Null)
is MetaItem<*> -> arg
@ -77,16 +59,24 @@ sealed class MetaItem<out M : Meta> {
}
}
public fun Value.asMetaItem(): ValueItem = ValueItem(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.
* Meaning that two states with the same meta are equal.
*/
interface MetaRepr {
fun toMeta(): Meta
@Serializable(MetaSerializer::class)
public interface MetaRepr {
public fun toMeta(): Meta
}
interface ItemProvider{
fun getItem(name: Name): MetaItem<*>?
public fun interface ItemProvider {
public fun getItem(name: Name): MetaItem<*>?
public companion object {
public val EMPTY: ItemProvider = ItemProvider { null }
}
}
/**
@ -96,15 +86,15 @@ interface ItemProvider{
*
* * Same name siblings are supported via elements with the same [Name] but different queries
*/
interface Meta : MetaRepr, ItemProvider {
public interface Meta : MetaRepr, ItemProvider {
/**
* Top level items of meta tree
*/
val items: Map<NameToken, MetaItem<*>>
public val items: Map<NameToken, MetaItem<*>>
override fun getItem(name: Name): MetaItem<*>? {
if (name.isEmpty()) return NodeItem(this)
return name.first()?.let { token ->
return name.firstOrNull()?.let { token ->
val tail = name.cutFirst()
when (tail.length) {
0 -> items[token]
@ -121,15 +111,15 @@ interface Meta : MetaRepr, ItemProvider {
override fun toString(): String
companion object {
const val TYPE = "meta"
public companion object {
public const val TYPE: String = "meta"
/**
* A key for single value node
*/
const val VALUE_KEY = "@value"
public const val VALUE_KEY: String = "@value"
val EMPTY: Meta = object: MetaBase() {
public val EMPTY: Meta = object : MetaBase() {
override val items: Map<NameToken, MetaItem<*>> = emptyMap()
}
}
@ -142,19 +132,19 @@ interface Meta : MetaRepr, ItemProvider {
*
* If [name] is empty return current [Meta] as a [NodeItem]
*/
operator fun Meta?.get(name: Name): MetaItem<*>? = this?.getItem(name)
public operator fun Meta?.get(name: Name): MetaItem<*>? = this?.getItem(name)
operator fun Meta?.get(token: NameToken): MetaItem<*>? = this?.items?.get(token)
public operator fun Meta?.get(token: NameToken): MetaItem<*>? = this?.items?.get(token)
/**
* Parse [Name] from [key] using full name notation and pass it to [Meta.get]
*/
operator fun Meta?.get(key: String): MetaItem<*>? = get(key.toName())
public operator fun Meta?.get(key: String): MetaItem<*>? = get(key.toName())
/**
* Get a sequence of [Name]-[Value] pairs
*/
fun Meta.values(): Sequence<Pair<Name, Value>> {
public fun Meta.values(): Sequence<Pair<Name, Value>> {
return items.asSequence().flatMap { (key, item) ->
when (item) {
is ValueItem -> sequenceOf(key.asName() to item.value)
@ -166,7 +156,7 @@ fun Meta.values(): Sequence<Pair<Name, Value>> {
/**
* Get a sequence of all [Name]-[MetaItem] pairs for all items including nodes
*/
fun Meta.sequence(): Sequence<Pair<Name, MetaItem<*>>> {
public fun Meta.sequence(): Sequence<Pair<Name, MetaItem<*>>> {
return sequence {
items.forEach { (key, item) ->
yield(key.asName() to item)
@ -179,33 +169,33 @@ fun Meta.sequence(): Sequence<Pair<Name, MetaItem<*>>> {
}
}
operator fun Meta.iterator(): Iterator<Pair<Name, MetaItem<*>>> = sequence().iterator()
public operator fun Meta.iterator(): Iterator<Pair<Name, MetaItem<*>>> = sequence().iterator()
/**
* A meta node that ensures that all of its descendants has at least the same type
*/
interface MetaNode<out M : MetaNode<M>> : Meta {
public interface MetaNode<out M : MetaNode<M>> : Meta {
override val items: Map<NameToken, MetaItem<M>>
}
/**
* The same as [Meta.get], but with specific node type
*/
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")
(this as Meta).get(name) as MetaItem<M>? // Do not change
}
operator fun <M : MetaNode<M>> M?.get(key: String): MetaItem<M>? = this[key.toName()]
public operator fun <M : MetaNode<M>> M?.get(key: String): MetaItem<M>? = this[key.toName()]
operator fun <M : MetaNode<M>> M?.get(key: NameToken): MetaItem<M>? = this[key.asName()]
public operator fun <M : MetaNode<M>> M?.get(key: NameToken): MetaItem<M>? = this[key.asName()]
/**
* Equals, hashcode and to string for any meta
*/
abstract class MetaBase : Meta {
public abstract class MetaBase : Meta {
override fun equals(other: Any?): Boolean = if (other is Meta) {
this.items == other.items
@ -215,30 +205,33 @@ abstract class MetaBase : Meta {
override fun hashCode(): Int = items.hashCode()
override fun toString(): String = JSON_PRETTY.stringify(MetaSerializer, this)
override fun toString(): String = Json {
prettyPrint = true
useArrayPolymorphism = true
}.encodeToString(MetaSerializer, this)
}
/**
* Equals and hash code implementation for meta node
*/
abstract class AbstractMetaNode<M : MetaNode<M>> : MetaNode<M>, MetaBase()
public abstract class AbstractMetaNode<M : MetaNode<M>> : MetaNode<M>, MetaBase()
/**
* The meta implementation which is guaranteed to be immutable.
*
* If the argument is possibly mutable node, it is copied on creation
*/
class SealedMeta internal constructor(
override val items: Map<NameToken, MetaItem<SealedMeta>>
public class SealedMeta internal constructor(
override val items: Map<NameToken, MetaItem<SealedMeta>>,
) : AbstractMetaNode<SealedMeta>()
/**
* Generate sealed node from [this]. If it is already sealed return it as is
*/
fun Meta.seal(): SealedMeta = this as? SealedMeta ?: SealedMeta(items.mapValues { entry -> entry.value.seal() })
public fun Meta.seal(): SealedMeta = this as? SealedMeta ?: SealedMeta(items.mapValues { entry -> entry.value.seal() })
@Suppress("UNCHECKED_CAST")
fun MetaItem<*>.seal(): MetaItem<SealedMeta> = when (this) {
public fun MetaItem<*>.seal(): MetaItem<SealedMeta> = when (this) {
is ValueItem -> this
is NodeItem -> NodeItem(node.seal())
}
@ -246,32 +239,32 @@ fun MetaItem<*>.seal(): MetaItem<SealedMeta> = when (this) {
/**
* Unsafe methods to access values and nodes directly from [MetaItem]
*/
val MetaItem<*>?.value: Value?
public val MetaItem<*>?.value: Value?
get() = (this as? ValueItem)?.value
?: (this?.node?.get(VALUE_KEY) as? ValueItem)?.value
val MetaItem<*>?.string get() = value?.string
val MetaItem<*>?.boolean get() = value?.boolean
val MetaItem<*>?.number get() = value?.number
val MetaItem<*>?.double get() = number?.toDouble()
val MetaItem<*>?.float get() = number?.toFloat()
val MetaItem<*>?.int get() = number?.toInt()
val MetaItem<*>?.long get() = number?.toLong()
val MetaItem<*>?.short get() = number?.toShort()
public val MetaItem<*>?.string: String? get() = value?.string
public val MetaItem<*>?.boolean: Boolean? get() = value?.boolean
public val MetaItem<*>?.number: Number? get() = value?.number
public val MetaItem<*>?.double: Double? get() = number?.toDouble()
public val MetaItem<*>?.float: Float? get() = number?.toFloat()
public val MetaItem<*>?.int: Int? get() = number?.toInt()
public val MetaItem<*>?.long: Long? get() = number?.toLong()
public val MetaItem<*>?.short: Short? get() = number?.toShort()
inline fun <reified E : Enum<E>> MetaItem<*>?.enum(): E? = if (this is ValueItem && this.value is EnumValue<*>) {
public inline fun <reified E : Enum<E>> MetaItem<*>?.enum(): E? = if (this is ValueItem && this.value is EnumValue<*>) {
this.value.value as E
} else {
string?.let { enumValueOf<E>(it) }
}
val MetaItem<*>?.stringList get() = value?.list?.map { it.string } ?: emptyList()
public val MetaItem<*>.stringList: List<String>? get() = value?.list?.map { it.string }
val <M : Meta> MetaItem<M>?.node: M?
public val <M : Meta> MetaItem<M>?.node: M?
get() = when (this) {
null -> null
is ValueItem -> null//error("Trying to interpret value meta item as node item")
is NodeItem -> node
}
fun Meta.isEmpty() = this === Meta.EMPTY || this.items.isEmpty()
public fun Meta.isEmpty(): Boolean = this === Meta.EMPTY || this.items.isEmpty()

@ -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>) {
this@MetaBuilder[this] = value.toList()
public infix fun String.put(value: Iterable<Meta>) {
set(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>) {
this@MetaBuilder[this] = value.toList()
public infix fun Name.put(value: Iterable<Meta>) {
set(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)

@ -1,28 +1,68 @@
package hep.dataforge.meta
import hep.dataforge.names.NameToken
import kotlinx.serialization.*
import hep.dataforge.values.ValueSerializer
import kotlinx.serialization.ExperimentalSerializationApi
import kotlinx.serialization.InternalSerializationApi
import kotlinx.serialization.KSerializer
import kotlinx.serialization.builtins.MapSerializer
import kotlinx.serialization.json.JsonInput
import kotlinx.serialization.json.JsonObjectSerializer
import kotlinx.serialization.json.JsonOutput
import kotlinx.serialization.descriptors.*
import kotlinx.serialization.encoding.*
import kotlinx.serialization.json.JsonDecoder
import kotlinx.serialization.json.JsonEncoder
import kotlinx.serialization.json.JsonObject
@OptIn(ExperimentalSerializationApi::class)
public object MetaItemSerializer : KSerializer<MetaItem<*>> {
@OptIn(InternalSerializationApi::class)
override val descriptor: SerialDescriptor = buildClassSerialDescriptor("hep.dataforge.meta.MetaItem") {
element<Boolean>("isNode")
element("content", buildSerialDescriptor("MetaItem.content", PolymorphicKind.SEALED))
}
override fun deserialize(decoder: Decoder): MetaItem<*> {
decoder.decodeStructure(descriptor) {
//Force strict serialization order
require(decodeElementIndex(descriptor) == 0) { "Node flag must be first item serialized" }
val isNode = decodeBooleanElement(descriptor, 0)
require(decodeElementIndex(descriptor) == 1) { "Missing MetaItem content" }
val item = if (isNode) {
decodeSerializableElement(descriptor,1, MetaSerializer).asMetaItem()
} else {
decodeSerializableElement(descriptor,1,ValueSerializer).asMetaItem()
}
require(decodeElementIndex(descriptor) == CompositeDecoder.DECODE_DONE){"Serialized MetaItem contains additional fields"}
return item
}
}
override fun serialize(encoder: Encoder, value: MetaItem<*>) {
encoder.encodeStructure(descriptor) {
encodeBooleanElement(descriptor, 0, value is MetaItem.NodeItem)
when (value) {
is MetaItem.ValueItem -> encodeSerializableElement(descriptor, 1, ValueSerializer, value.value)
is MetaItem.NodeItem -> encodeSerializableElement(descriptor, 1, MetaSerializer, value.node)
}
}
}
}
/**
* Serialized for meta
*/
@Serializer(Meta::class)
object MetaSerializer : KSerializer<Meta> {
private val mapSerializer = MapSerializer(
NameToken.serializer(),
MetaItem.serializer(MetaSerializer)
public object MetaSerializer : KSerializer<Meta> {
private val mapSerializer: KSerializer<Map<NameToken, MetaItem<Meta>>> = MapSerializer(
NameToken,
MetaItemSerializer//MetaItem.serializer(MetaSerializer)
)
override val descriptor: SerialDescriptor get() = mapSerializer.descriptor
override fun deserialize(decoder: Decoder): Meta {
return if (decoder is JsonInput) {
JsonObjectSerializer.deserialize(decoder).toMeta()
return if (decoder is JsonDecoder) {
JsonObject.serializer().deserialize(decoder).toMeta()
} else {
object : MetaBase() {
override val items: Map<NameToken, MetaItem<*>> = mapSerializer.deserialize(decoder)
@ -31,8 +71,8 @@ object MetaSerializer : KSerializer<Meta> {
}
override fun serialize(encoder: Encoder, value: Meta) {
if (encoder is JsonOutput) {
JsonObjectSerializer.serialize(encoder, value.toJson())
if (encoder is JsonEncoder) {
JsonObject.serializer().serialize(encoder, value.toJson())
} else {
mapSerializer.serialize(encoder, value.items)
}

@ -0,0 +1,18 @@
package hep.dataforge.meta
import hep.dataforge.names.Name
import hep.dataforge.names.NameToken
/**
* Meta object with default provider for items not present in the initial meta.
*/
public class MetaWithDefault(public val meta: Meta, public val default: ItemProvider) : MetaBase() {
override val items: Map<NameToken, MetaItem<*>>
get() = meta.items
override fun getItem(name: Name): MetaItem<*>? {
return meta[name] ?: default.getItem(name)
}
}
public fun Meta.withDefault(default: ItemProvider): MetaWithDefault = MetaWithDefault(this, default)

@ -5,163 +5,189 @@ import hep.dataforge.names.Name
import hep.dataforge.names.asName
import hep.dataforge.values.DoubleArrayValue
import hep.dataforge.values.Value
import hep.dataforge.values.stringList
import hep.dataforge.values.asValue
import hep.dataforge.values.doubleArray
import kotlin.properties.ReadWriteProperty
import kotlin.reflect.KProperty
/* Read-write delegates */
open class MutableItemDelegate(
override val owner: MutableItemProvider,
key: Name? = null,
default: MetaItem<*>? = null
) : ItemDelegate(owner, key, default), ReadWriteProperty<Any?, MetaItem<*>?> {
public typealias MutableItemDelegate = ReadWriteProperty<Any?, MetaItem<*>?>
public fun MutableItemProvider.item(key: Name? = null): MutableItemDelegate = object : MutableItemDelegate {
override fun getValue(thisRef: Any?, property: KProperty<*>): MetaItem<*>? {
return getItem(key ?: property.name.asName())
}
override fun setValue(thisRef: Any?, property: KProperty<*>, value: MetaItem<*>?) {
val name = key ?: property.name.asName()
owner.setItem(name, value)
setItem(name, value)
}
}
fun MutableItemProvider.item(key: Name? = null): MutableItemDelegate =
MutableItemDelegate(this, key)
/* Mutable converters */
//Read-write delegates
/**
* A type converter for a mutable [MetaItem] delegate
*/
public fun <R : Any> MutableItemDelegate.convert(
converter: MetaConverter<R>,
): ReadWriteProperty<Any?, R?> = object : ReadWriteProperty<Any?, R?> {
override fun getValue(thisRef: Any?, property: KProperty<*>): R? =
this@convert.getValue(thisRef, property)?.let(converter::itemToObject)
override fun setValue(thisRef: Any?, property: KProperty<*>, value: R?) {
val item = value?.let(converter::objectToMetaItem)
this@convert.setValue(thisRef, property, item)
}
}
public fun <R : Any> MutableItemDelegate.convert(
converter: MetaConverter<R>,
default: () -> R,
): ReadWriteProperty<Any?, R> = object : ReadWriteProperty<Any?, R> {
override fun getValue(thisRef: Any?, property: KProperty<*>): R =
this@convert.getValue(thisRef, property)?.let(converter::itemToObject) ?: default()
override fun setValue(thisRef: Any?, property: KProperty<*>, value: R) {
val item = value.let(converter::objectToMetaItem)
this@convert.setValue(thisRef, property, item)
}
}
public fun <R> MutableItemDelegate.convert(
reader: (MetaItem<*>?) -> R,
writer: (R) -> MetaItem<*>?,
): ReadWriteProperty<Any?, R> = object : ReadWriteProperty<Any?, R> {
override fun getValue(thisRef: Any?, property: KProperty<*>): R =
this@convert.getValue(thisRef, property).let(reader)
override fun setValue(thisRef: Any?, property: KProperty<*>, value: R) {
val item = value?.let(writer)
this@convert.setValue(thisRef, property, item)
}
}
/* Read-write delegates for [MutableItemProvider] */
/**
* A property delegate that uses custom key
*/
fun MutableItemProvider.value(key: Name? = null): ReadWriteProperty<Any?, Value?> =
public fun MutableItemProvider.value(key: Name? = null): ReadWriteProperty<Any?, Value?> =
item(key).convert(MetaConverter.value)
fun MutableItemProvider.string(key: Name? = null): ReadWriteProperty<Any?, String?> =
public fun MutableItemProvider.string(key: Name? = null): ReadWriteProperty<Any?, String?> =
item(key).convert(MetaConverter.string)
fun MutableItemProvider.boolean(key: Name? = null): ReadWriteProperty<Any?, Boolean?> =
public fun MutableItemProvider.boolean(key: Name? = null): ReadWriteProperty<Any?, Boolean?> =
item(key).convert(MetaConverter.boolean)
fun MutableItemProvider.number(key: Name? = null): ReadWriteProperty<Any?, Number?> =
public fun MutableItemProvider.number(key: Name? = null): ReadWriteProperty<Any?, Number?> =
item(key).convert(MetaConverter.number)
fun MutableItemProvider.string(default: String, key: Name? = null): ReadWriteProperty<Any?, String> =
public fun MutableItemProvider.string(default: String, key: Name? = null): ReadWriteProperty<Any?, String> =
item(key).convert(MetaConverter.string) { default }
fun MutableItemProvider.boolean(default: Boolean, key: Name? = null): ReadWriteProperty<Any?, Boolean> =
public fun MutableItemProvider.boolean(default: Boolean, key: Name? = null): ReadWriteProperty<Any?, Boolean> =
item(key).convert(MetaConverter.boolean) { default }
fun MutableItemProvider.number(default: Number, key: Name? = null): ReadWriteProperty<Any?, Number> =
public fun MutableItemProvider.number(default: Number, key: Name? = null): ReadWriteProperty<Any?, Number> =
item(key).convert(MetaConverter.number) { default }
fun MutableItemProvider.value(key: Name? = null, default: () -> Value): ReadWriteProperty<Any?, Value> =
public fun MutableItemProvider.value(key: Name? = null, default: () -> Value): ReadWriteProperty<Any?, Value> =
item(key).convert(MetaConverter.value, default)
fun MutableItemProvider.string(key: Name? = null, default: () -> String): ReadWriteProperty<Any?, String> =
public fun MutableItemProvider.string(key: Name? = null, default: () -> String): ReadWriteProperty<Any?, String> =
item(key).convert(MetaConverter.string, default)
fun MutableItemProvider.boolean(key: Name? = null, default: () -> Boolean): ReadWriteProperty<Any?, Boolean> =
public fun MutableItemProvider.boolean(key: Name? = null, default: () -> Boolean): ReadWriteProperty<Any?, Boolean> =
item(key).convert(MetaConverter.boolean, default)
fun MutableItemProvider.number(key: Name? = null, default: () -> Number): ReadWriteProperty<Any?, Number> =
public fun MutableItemProvider.number(key: Name? = null, default: () -> Number): ReadWriteProperty<Any?, Number> =
item(key).convert(MetaConverter.number, default)
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 }
inline fun <reified M : MutableMeta<M>> M.node(key: Name? = null): ReadWriteProperty<Any?, M?> =
public inline fun <reified M : MutableMeta<M>> M.node(key: Name? = null): ReadWriteProperty<Any?, M?> =
item(key).convert(reader = { it?.let { it.node as M } }, writer = { it?.let { MetaItem.NodeItem(it) } })
/* Number delegates */
fun <T> MutableItemProvider.item(
default: T? = null,
key: Name? = null,
writer: (T) -> MetaItem<*>? = { MetaItem.of(it) },
reader: (MetaItem<*>?) -> T
): ReadWriteProperty<Any?, T> = MutableItemDelegate(
this,
key,
default?.let { MetaItem.of(it) }
).convert(reader = reader, writer = writer)
fun Configurable.value(key: Name? = null): ReadWriteProperty<Any?, Value?> =
item(key).convert(MetaConverter.value)
fun <T> MutableItemProvider.value(
default: T? = null,
key: Name? = null,
writer: (T) -> Value? = { Value.of(it) },
reader: (Value?) -> T
): ReadWriteProperty<Any?, T> = MutableItemDelegate(
this,
key,
default?.let { MetaItem.of(it) }
).convert(
reader = { reader(it.value) },
writer = { value -> writer(value)?.let { MetaItem.ValueItem(it) } }
)
/* Number delegates*/
fun MutableItemProvider.int(key: Name? = null): ReadWriteProperty<Any?, Int?> =
public fun MutableItemProvider.int(key: Name? = null): ReadWriteProperty<Any?, Int?> =
item(key).convert(MetaConverter.int)
fun MutableItemProvider.double(key: Name? = null): ReadWriteProperty<Any?, Double?> =
public fun MutableItemProvider.double(key: Name? = null): ReadWriteProperty<Any?, Double?> =
item(key).convert(MetaConverter.double)
fun MutableItemProvider.long(key: Name? = null): ReadWriteProperty<Any?, Long?> =
public fun MutableItemProvider.long(key: Name? = null): ReadWriteProperty<Any?, Long?> =
item(key).convert(MetaConverter.long)
fun MutableItemProvider.float(key: Name? = null): ReadWriteProperty<Any?, Float?> =
public fun MutableItemProvider.float(key: Name? = null): ReadWriteProperty<Any?, Float?> =
item(key).convert(MetaConverter.float)
/* Safe number delegates*/
fun MutableItemProvider.int(default: Int, key: Name? = null): ReadWriteProperty<Any?, Int> =
public fun MutableItemProvider.int(default: Int, key: Name? = null): ReadWriteProperty<Any?, Int> =
item(key).convert(MetaConverter.int) { default }
fun MutableItemProvider.double(default: Double, key: Name? = null): ReadWriteProperty<Any?, Double> =
public fun MutableItemProvider.double(default: Double, key: Name? = null): ReadWriteProperty<Any?, Double> =
item(key).convert(MetaConverter.double) { default }
fun MutableItemProvider.long(default: Long, key: Name? = null): ReadWriteProperty<Any?, Long> =
public fun MutableItemProvider.long(default: Long, key: Name? = null): ReadWriteProperty<Any?, Long> =
item(key).convert(MetaConverter.long) { default }
fun MutableItemProvider.float(default: Float, key: Name? = null): ReadWriteProperty<Any?, Float> =
public fun MutableItemProvider.float(default: Float, key: Name? = null): ReadWriteProperty<Any?, Float> =
item(key).convert(MetaConverter.float) { default }
/*
* Extra delegates for special cases
*/
fun MutableItemProvider.stringList(vararg strings: String, key: Name? = null): ReadWriteProperty<Any?, List<String>> =
item(listOf(*strings), key) {
it?.value?.stringList ?: emptyList()
}
/* Extra delegates for special cases */
fun MutableItemProvider.stringListOrNull(
vararg strings: String,
key: Name? = null
): ReadWriteProperty<Any?, List<String>?> =
item(listOf(*strings), key) {
it?.value?.stringList
}
public fun MutableItemProvider.stringList(
vararg default: String,
key: Name? = null,
): ReadWriteProperty<Any?, List<String>> = item(key).convert(
reader = { it?.stringList ?: listOf(*default) },
writer = { it.map { str -> str.asValue() }.asValue().asMetaItem() }
)
fun MutableItemProvider.numberList(vararg numbers: Number, key: Name? = null): ReadWriteProperty<Any?, List<Number>> =
item(listOf(*numbers), key) { item ->
item?.value?.list?.map { it.number } ?: emptyList()
}
public fun MutableItemProvider.stringList(
key: Name? = null,
): ReadWriteProperty<Any?, List<String>?> = item(key).convert(
reader = { it?.stringList },
writer = { it?.map { str -> str.asValue() }?.asValue()?.asMetaItem() }
)
/**
* A special delegate for double arrays
*/
fun MutableItemProvider.doubleArray(vararg doubles: Double, key: Name? = null): ReadWriteProperty<Any?, DoubleArray> =
item(doubleArrayOf(*doubles), key) {
(it.value as? DoubleArrayValue)?.value
?: it?.value?.list?.map { value -> value.number.toDouble() }?.toDoubleArray()
?: doubleArrayOf()
}
public fun MutableItemProvider.numberList(
vararg default: Number,
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() }
)
fun <T> MutableItemProvider.listValue(
/* A special delegate for double arrays */
public fun MutableItemProvider.doubleArray(
vararg default: Double,
key: Name? = null,
): ReadWriteProperty<Any?, DoubleArray> = item(key).convert(
reader = { it?.value?.doubleArray ?: doubleArrayOf(*default) },
writer = { DoubleArrayValue(it).asMetaItem() }
)
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))

@ -0,0 +1,88 @@
package hep.dataforge.meta
import hep.dataforge.names.*
import hep.dataforge.values.Value
import kotlin.properties.ReadWriteProperty
public interface MutableItemProvider : ItemProvider {
public fun setItem(name: Name, item: MetaItem<*>?)
}
public fun MutableItemProvider.getItem(key: String): MetaItem<*>? = getItem(key.toName())
public fun MutableItemProvider.setValue(name: Name, value: Value?): Unit =
setItem(name, value?.let { MetaItem.ValueItem(value) })
public fun MutableItemProvider.setNode(name: Name, meta: Meta?): Unit =
setItem(name, meta?.let { MetaItem.NodeItem(meta) })
public fun MutableItemProvider.setItem(key: String, item: MetaItem<*>?): Unit = setItem(key.toName(), item)
public fun MutableItemProvider.setValue(key: String, value: Value?): Unit =
setItem(key, value?.let { MetaItem.ValueItem(value) })
public fun MutableItemProvider.setNode(key: String, meta: Meta?): Unit =
setItem(key, meta?.let { MetaItem.NodeItem(meta) })
public fun MutableItemProvider.node(key: Name? = null): ReadWriteProperty<Any?, Meta?> = item(key).convert(
reader = { it.node },
writer = { it?.let { MetaItem.NodeItem(it) } }
)
@Suppress("NOTHING_TO_INLINE")
public inline fun MutableItemProvider.remove(name: Name): Unit = setItem(name, null)
@Suppress("NOTHING_TO_INLINE")
public inline fun MutableItemProvider.remove(name: String): Unit = remove(name.toName())
/**
* Universal unsafe set method
*/
public operator fun MutableItemProvider.set(name: Name, value: Any?) {
when (value) {
null -> remove(name)
is MetaItem<*> -> setItem(name, value)
is Meta -> setNode(name, value)
is Configurable -> setNode(name, value.config)
else -> setValue(name, Value.of(value))
}
}
public operator fun MutableItemProvider.set(name: NameToken, value: Any?): Unit =
set(name.asName(), value)
public operator fun MutableItemProvider.set(key: String, value: Any?): Unit =
set(key.toName(), value)
public operator fun MutableItemProvider.set(key: String, index: String, value: Any?): Unit =
set(key.toName().withIndex(index), value)
/* Same name siblings generation */
public fun MutableItemProvider.setIndexedItems(
name: Name,
items: Iterable<MetaItem<*>>,
indexFactory: (MetaItem<*>, index: Int) -> String = { _, index -> index.toString() }
) {
val tokens = name.tokens.toMutableList()
val last = tokens.last()
items.forEachIndexed { index, meta ->
val indexedToken = NameToken(last.body, last.index + indexFactory(meta, index))
tokens[tokens.lastIndex] = indexedToken
setItem(Name(tokens), meta)
}
}
public fun MutableItemProvider.setIndexed(
name: Name,
metas: Iterable<Meta>,
indexFactory: (Meta, index: Int) -> String = { _, index -> index.toString() }
) {
setIndexedItems(name, metas.map { MetaItem.NodeItem(it) }) { item, index -> indexFactory(item.node!!, index) }
}
public operator fun MutableItemProvider.set(name: Name, metas: Iterable<Meta>): Unit = setIndexed(name, metas)
public operator fun MutableItemProvider.set(name: String, metas: Iterable<Meta>): Unit = setIndexed(name.toName(), metas)

@ -1,13 +1,8 @@
package hep.dataforge.meta
import hep.dataforge.names.*
import hep.dataforge.values.Value
interface MutableItemProvider : ItemProvider {
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,19 +13,19 @@ 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> {
protected val _items: MutableMap<NameToken, MetaItem<M>> = LinkedHashMap()
public abstract class AbstractMutableMeta<M : MutableMeta<M>> : AbstractMetaNode<M>(), MutableMeta<M> {
protected val children: MutableMap<NameToken, MetaItem<M>> = LinkedHashMap()
override val items: Map<NameToken, MetaItem<M>>
get() = _items
get() = children
//protected abstract fun itemChanged(name: Name, oldItem: MetaItem<*>?, newItem: MetaItem<*>?)
protected open fun replaceItem(key: NameToken, oldItem: MetaItem<M>?, newItem: MetaItem<M>?) {
if (newItem == null) {
_items.remove(key)
children.remove(key)
} else {
_items[key] = newItem
children[key] = newItem
}
//itemChanged(key.asName(), oldItem, newItem)
}
@ -56,12 +51,12 @@ abstract class AbstractMutableMeta<M : MutableMeta<M>> : AbstractMetaNode<M>(),
when (name.length) {
0 -> error("Can't setValue meta item for empty name")
1 -> {
val token = name.first()!!
val token = name.firstOrNull()!!
@Suppress("UNCHECKED_CAST") val oldItem: MetaItem<M>? = get(name) as? MetaItem<M>
replaceItem(token, oldItem, wrapItem(item))
}
else -> {
val token = name.first()!!
val token = name.firstOrNull()!!
//get existing or create new node. Query is ignored for new node
if (items[token] == null) {
replaceItem(token, null, MetaItem.NodeItem(empty()))
@ -72,52 +67,13 @@ abstract class AbstractMutableMeta<M : MutableMeta<M>> : AbstractMetaNode<M>(),
}
}
@Suppress("NOTHING_TO_INLINE")
inline fun MutableMeta<*>.remove(name: Name) = setItem(name, null)
@Suppress("NOTHING_TO_INLINE")
inline fun MutableMeta<*>.remove(name: String) = remove(name.toName())
operator fun MutableMeta<*>.set(name: Name, item: MetaItem<*>?) = setItem(name, item)
fun MutableMeta<*>.setValue(name: Name, value: Value) = setItem(name, MetaItem.ValueItem(value))
fun MutableMeta<*>.setValue(name: String, value: Value) = set(name.toName(), value)
fun MutableMeta<*>.setItem(name: String, item: MetaItem<*>?) = setItem(name.toName(), item)
fun MutableMeta<*>.setNode(name: Name, node: Meta) =
setItem(name, MetaItem.NodeItem(node))
fun MutableMeta<*>.setNode(name: String, node: Meta) = setNode(name.toName(), node)
/**
* Universal unsafe set method
*/
operator fun MutableMeta<*>.set(name: Name, value: Any?) {
when (value) {
null -> remove(name)
is MetaItem<*> -> setItem(name, value)
is Meta -> setNode(name, value)
is Configurable -> setNode(name, value.config)
else -> setValue(name, Value.of(value))
}
}
operator fun MutableMeta<*>.set(name: NameToken, value: Any?) = set(name.asName(), value)
operator fun MutableMeta<*>.set(key: String, value: Any?) = set(key.toName(), value)
operator fun MutableMeta<*>.set(key: String, index: String, value: Any?) = set(key.toName().withIndex(index), value)
/**
* Update existing mutable node with another node. The rules are following:
* * value replaces anything
* * 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)
@ -127,54 +83,27 @@ fun <M : MutableMeta<M>> M.update(meta: Meta) {
}
}
/* Same name siblings generation */
fun MutableMeta<*>.setIndexedItems(
name: Name,
items: Iterable<MetaItem<*>>,
indexFactory: (MetaItem<*>, index: Int) -> String = { _, index -> index.toString() }
) {
val tokens = name.tokens.toMutableList()
val last = tokens.last()
items.forEachIndexed { index, meta ->
val indexedToken = NameToken(last.body, last.index + indexFactory(meta, index))
tokens[tokens.lastIndex] = indexedToken
setItem(Name(tokens), meta)
}
}
fun MutableMeta<*>.setIndexed(
name: Name,
metas: Iterable<Meta>,
indexFactory: (Meta, index: Int) -> String = { _, index -> index.toString() }
) {
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)
/**
* 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.last()!!.index
if (newIndex.isNotEmpty()) {
val newIndex = name.lastOrNull()!!.index
if (newIndex != null) {
set(name, value)
} else {
val index = (getIndexed(name).keys.mapNotNull { it.toIntOrNull() }.max() ?: -1) + 1
val index = (getIndexed(name).keys.mapNotNull { it.toIntOrNull() }.maxOrNull() ?: -1) + 1
set(name.withIndex(index.toString()), value)
}
}
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

@ -3,28 +3,54 @@ package hep.dataforge.meta
import hep.dataforge.meta.descriptors.*
import hep.dataforge.names.Name
import hep.dataforge.names.NameToken
import hep.dataforge.names.plus
import hep.dataforge.names.asName
/**
* A base for delegate-based or descriptor-based scheme. [Scheme] has an empty constructor to simplify usage from [Specification].
* Default item provider and [NodeDescriptor] are optional
*/
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 = Config(),
internal var default: ItemProvider? = null,
descriptor: NodeDescriptor? = null,
) : Configurable, MutableItemProvider, Described, MetaRepr {
override var config: Config = config
internal set(value) {
//Fix problem with `init` blocks in specifications
field = value.apply { update(field) }
}
override var descriptor: NodeDescriptor? = descriptor
internal set
public fun getDefaultItem(name: Name): MetaItem<*>? {
return default?.getItem(name) ?: descriptor?.get(name)?.defaultItem()
}
//constructor(config: Config, default: Meta) : this(config, { default[it] })
constructor(config: Config) : this(config, { null })
/**
* Get a property with default
*/
override fun getItem(name: Name): MetaItem<*>? = config[name] ?: getDefaultItem(name)
final override var config: Config = Config()
internal set
/**
* Check if property with given [name] could be assigned to [item]
*/
public fun validateItem(name: Name, item: MetaItem<*>?): Boolean {
val descriptor = descriptor?.get(name)
return descriptor?.validateItem(item) ?: true
}
var defaultProvider: (Name) -> MetaItem<*>? = { null }
internal set
override fun getDefaultItem(name: Name): MetaItem<*>? {
return defaultProvider(name) ?: descriptor?.get(name)?.defaultItem()
/**
* Set a configurable property
*/
override fun setItem(name: Name, item: MetaItem<*>?) {
if (validateItem(name, item)) {
config.setItem(name, item)
} else {
error("Validation failed for property $name with value $item")
}
}
/**
@ -32,56 +58,73 @@ 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() = object : MetaBase() {
override val items: Map<NameToken, MetaItem<*>> = buildMap {
descriptor?.items?.forEach { (key, itemDescriptor) ->
val token = NameToken(key)
val name = token.asName()
val item = default?.getItem(name) ?: itemDescriptor.defaultItem()
if (item != null) {
put(token, item)
}
}
}
}
override fun toMeta(): Laminate = Laminate(config, defaultLayer)
private inner class DefaultLayer(val path: Name) : MetaBase() {
override val items: Map<NameToken, MetaItem<*>> =
(descriptor?.get(path) as? NodeDescriptor)?.items?.entries?.associate { (key, descriptor) ->
val token = NameToken(key)
val fullName = path + token
val item: MetaItem<*> = when (descriptor) {
is ValueDescriptor -> getDefaultItem(fullName) ?: descriptor.defaultItem()
is NodeDescriptor -> MetaItem.NodeItem(DefaultLayer(fullName))
}
token to item
} ?: emptyMap()
}
public fun isEmpty(): Boolean = config.isEmpty()
}
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, descriptor: NodeDescriptor?) -> T,
) : Specification<T>, Described {
override fun wrap(config: Config, defaultProvider: (Name) -> MetaItem<*>?): T {
return empty().apply {
public constructor(emptyBuilder: () -> T) : this({ config: Config, defaultProvider: ItemProvider, descriptor: NodeDescriptor? ->
emptyBuilder().apply {
this.config = config
this.defaultProvider = defaultProvider
this.default = defaultProvider
this.descriptor = descriptor
}
})
override fun read(meta: Meta, defaultProvider: ItemProvider): T =
builder(Config(), meta.withDefault(defaultProvider), descriptor)
override fun wrap(config: Config, defaultProvider: ItemProvider): T {
return builder(config, defaultProvider, descriptor)
}
//TODO Generate descriptor from Scheme class
override val descriptor: NodeDescriptor? get() = null
@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,
override val descriptor: NodeDescriptor? = null,
config: Config = Config()
) : Scheme(config, meta::get) {
override val defaultLayer: Meta get() = Laminate(meta, descriptor?.defaultItem().node)
///**
// * A scheme that uses [Meta] as a default layer
// */
//public open class MetaScheme(
// private val meta: Meta,
// override val descriptor: NodeDescriptor? = null,
// config: Config = Config(),
//) : Scheme(config, meta) {
// override val defaultLayer: Meta get() = Laminate(meta, descriptor?.defaultItem().node)
//}
public fun Meta.asScheme(): Scheme = Scheme().apply {
config = this@asScheme.asConfig()
}
fun Meta.asScheme() =
MetaScheme(this)
fun <T : Configurable> Meta.toScheme(spec: Specification<T>, block: T.() -> Unit) = spec.wrap(this).apply(block)
public fun <T : MutableItemProvider> Meta.toScheme(spec: Specification<T>, block: T.() -> Unit = {}): T =
spec.read(this).apply(block)

@ -1,67 +1,102 @@
package hep.dataforge.meta
import hep.dataforge.names.Name
import hep.dataforge.names.asName
import kotlin.jvm.JvmName
import kotlin.properties.ReadWriteProperty
import kotlin.reflect.KProperty
/**
* Allows to apply custom configuration in a type safe way to simple untyped configuration.
* By convention [Scheme] companion should inherit this class
*
*/
interface Specification<T : Configurable> {
fun empty() = wrap()
public interface Specification<T : MutableItemProvider> {
/**
* Read generic read-only meta with this [Specification] producing instance of desired type.
*/
public fun read(meta: Meta, defaultProvider: ItemProvider = ItemProvider.EMPTY): T
/**
* Wrap generic configuration producing instance of desired type
* Wrap mutable [Config], using it as inner storage (changes to [Specification] are reflected on [Config]
*/
fun wrap(config: Config = Config(), defaultProvider: (Name) -> MetaItem<*>? = { null }): T
public fun wrap(config: Config, defaultProvider: ItemProvider = ItemProvider.EMPTY): T =
read(config as Meta, defaultProvider)
operator fun invoke(action: T.() -> Unit): T = empty().apply(action)
/**
* Generate an empty object
*/
public fun empty(): T = read(Meta.EMPTY)
/**
* A convenience method to use specifications in builders
*/
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)
/**
* 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] }
/**
* Wrap a configuration using static meta as default
*/
fun <T : Configurable> Specification<T>.wrap(source: Meta): T {
val default = source.seal()
return wrap(source.asConfig(), default)
}
public fun <T : MutableItemProvider> Specification<T>.update(meta: Meta, action: T.() -> Unit): T =
read(meta).apply(action)
/**
* Apply specified configuration to configurable
*/
fun <T : Configurable, C : Configurable, S : Specification<C>> T.configure(spec: S, action: C.() -> Unit) =
apply { spec.update(config, action) }
public fun <T : MetaRepr, C : MutableItemProvider, S : Specification<C>> T.configure(spec: S, action: C.() -> Unit): T =
apply { spec.update(toMeta(), action) }
/**
* Update configuration using given specification
*/
fun <C : Configurable, S : Specification<C>> Configurable.update(spec: S, action: C.() -> Unit) =
public fun <C : MutableItemProvider, 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 : MutableItemProvider, 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 : MutableItemProvider> 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 : MutableItemProvider> MetaItem<Config>.spec(spec: Specification<T>): T? = node?.let { spec.wrap(it) }
public fun <T : Scheme> MutableItemProvider.spec(
spec: Specification<T>, key: Name? = null,
): ReadWriteProperty<Any?, T?> = object : ReadWriteProperty<Any?, T?> {
override fun getValue(thisRef: Any?, property: KProperty<*>): T? {
val name = key ?: property.name.asName()
return getItem(name).node?.let { spec.read(it) }
}
override fun setValue(thisRef: Any?, property: KProperty<*>, value: T?) {
val name = key ?: property.name.asName()
setItem(name, value?.toMeta()?.asMetaItem())
}
}
public fun <T : Scheme> MutableItemProvider.spec(
spec: Specification<T>,
default: T,
key: Name? = null,
): ReadWriteProperty<Any?, T> = object : ReadWriteProperty<Any?, T> {
override fun getValue(thisRef: Any?, property: KProperty<*>): T {
val name = key ?: property.name.asName()
return getItem(name).node?.let { spec.read(it) } ?: default
}
override fun setValue(thisRef: Any?, property: KProperty<*>, value: T) {
val name = key ?: property.name.asName()
setItem(name, value.toMeta().asMetaItem())
}
}

@ -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

@ -3,11 +3,11 @@ package hep.dataforge.meta.descriptors
/**
* An object which provides its descriptor
*/
interface Described {
val descriptor: ItemDescriptor?
public interface Described {
public val descriptor: ItemDescriptor?
companion object {
const val DESCRIPTOR_NODE = "@descriptor"
public companion object {
//public const val DESCRIPTOR_NODE: String = "@descriptor"
}
}

@ -1,32 +1,48 @@
package hep.dataforge.meta.descriptors
import hep.dataforge.meta.Laminate
import hep.dataforge.meta.Meta
import hep.dataforge.meta.MetaBase
import hep.dataforge.meta.MetaItem
import hep.dataforge.names.NameToken
import hep.dataforge.values.Null
/**
* A [Meta] that wraps a descriptor node
* A [Meta] that is constructed from [NodeDescriptor]
*/
class DescriptorMeta(val descriptor: NodeDescriptor) : MetaBase() {
private class DescriptorMeta(val descriptor: NodeDescriptor) : Meta, MetaBase() {
override val items: Map<NameToken, MetaItem<*>>
get() = descriptor.items.entries.associate { entry ->
NameToken(entry.key) to entry.value.defaultItem()
get() = buildMap {
descriptor.items.forEach { (token, descriptorItem) ->
val item = descriptorItem.defaultItem()
if (item != null) {
put(NameToken(token), item)
}
}
}
}
fun NodeDescriptor.buildDefaultMeta() = Laminate(default, DescriptorMeta(this))
/**
* Generate a laminate representing default item set generated by this descriptor
*/
public fun NodeDescriptor.defaultMeta(): Laminate = Laminate(default, DescriptorMeta(this))
fun NodeDescriptor.defaultItem(): MetaItem.NodeItem<*> =
MetaItem.NodeItem(buildDefaultMeta())
/**
* Build a default [MetaItem.NodeItem] from this node descriptor
*/
internal fun NodeDescriptor.defaultItem(): MetaItem.NodeItem<*> =
MetaItem.NodeItem(defaultMeta())
fun ValueDescriptor.defaultItem(): MetaItem.ValueItem = MetaItem.ValueItem(default ?: Null)
/**
* Build a default [MetaItem.ValueItem] from this descriptor
*/
internal fun ValueDescriptor.defaultItem(): MetaItem.ValueItem? {
return MetaItem.ValueItem(default ?: return null)
}
/**
* Build a default [MetaItem] from descriptor.
*/
fun ItemDescriptor.defaultItem(): MetaItem<*> {
public fun ItemDescriptor.defaultItem(): MetaItem<*>? {
return when (this) {
is ValueDescriptor -> defaultItem()
is NodeDescriptor -> defaultItem()

@ -4,29 +4,26 @@ import hep.dataforge.meta.*
import hep.dataforge.names.*
import hep.dataforge.values.*
/**
* A common parent for [ValueDescriptor] and [NodeDescriptor]. Describes a single [MetaItem] or a group of same-name-siblings.
*/
@DFBuilder
sealed class ItemDescriptor(val config: Config) {
public sealed class ItemDescriptor(public val config: Config) {
/**
* True if same name siblings with this name are allowed
*
* @return
*/
var multiple: Boolean by config.boolean(false)
public var multiple: Boolean by config.boolean(false)
/**
* The item description
*
* @return
* The item description text
*/
var info: String? by config.string()
public var info: String? by config.string()
/**
* True if the item is required
*
* @return
*/
abstract var required: Boolean
public abstract var required: Boolean
/**
@ -34,29 +31,29 @@ sealed class ItemDescriptor(val config: Config) {
*
* @return
*/
var attributes by config.node()
}
public var attributes: Config? by config.node()
/**
* Configure attributes of the descriptor
*/
fun ItemDescriptor.attributes(block: Config.() -> Unit) {
(attributes ?: Config().also { this.attributes = it }).apply(block)
}
/**
* An index field by which this node is identified in case of same name siblings construct
*/
public var indexKey: String by config.string(DEFAULT_INDEX_KEY)
/**
* Set specific attribute in the descriptor
*/
fun ItemDescriptor.setAttribute(name: Name, value: Any?) {
attributes {
set(name, value)
public companion object{
public const val DEFAULT_INDEX_KEY: String = "@index"
}
}
/**
* Configure attributes of the descriptor, creating an attributes node if needed.
*/
public fun ItemDescriptor.attributes(block: Config.() -> Unit) {
(attributes ?: Config().also { this.attributes = it }).apply(block)
}
/**
* Check if given item suits the descriptor
*/
fun ItemDescriptor.validateItem(item: MetaItem<*>?): Boolean {
public fun ItemDescriptor.validateItem(item: MetaItem<*>?): Boolean {
if (item == null) return !required
return when (this) {
is ValueDescriptor -> isAllowedValue(item.value ?: return false)
@ -73,7 +70,7 @@ fun ItemDescriptor.validateItem(item: MetaItem<*>?): Boolean {
* @author Alexander Nozik
*/
@DFBuilder
class NodeDescriptor(config: Config = Config()) : ItemDescriptor(config) {
public class NodeDescriptor(config: Config = Config()) : ItemDescriptor(config) {
init {
config[IS_NODE_KEY] = true
}
@ -90,14 +87,12 @@ class NodeDescriptor(config: Config = Config()) : ItemDescriptor(config) {
*
* @return
*/
var default by config.node()
public var default: Config? by config.node()
/**
* An index field by which this node is identified in case of same name siblings construct
* The map of children item descriptors (both nodes and values)
*/
var indexKey by config.string(DEFAULT_INDEX_KEY)
val items: Map<String, ItemDescriptor>
public val items: Map<String, ItemDescriptor>
get() = config.getIndexed(ITEM_KEY).mapValues { (_, item) ->
val node = item.node ?: error("Node descriptor must be a node")
if (node[IS_NODE_KEY].boolean == true) {
@ -111,7 +106,7 @@ class NodeDescriptor(config: Config = Config()) : ItemDescriptor(config) {
* The map of children node descriptors
*/
@Suppress("UNCHECKED_CAST")
val nodes: Map<String, NodeDescriptor>
public val nodes: Map<String, NodeDescriptor>
get() = config.getIndexed(ITEM_KEY).entries.filter {
it.value.node[IS_NODE_KEY].boolean == true
}.associate { (name, item) ->
@ -120,9 +115,9 @@ class NodeDescriptor(config: Config = Config()) : ItemDescriptor(config) {
}
/**
* The list of value descriptors
* The list of children value descriptors
*/
val values: Map<String, ValueDescriptor>
public val values: Map<String, ValueDescriptor>
get() = config.getIndexed(ITEM_KEY).entries.filter {
it.value.node[IS_NODE_KEY].boolean != true
}.associate { (name, item) ->
@ -141,7 +136,7 @@ class NodeDescriptor(config: Config = Config()) : ItemDescriptor(config) {
}
NodeDescriptor(config)
}
else -> buildNode(name.first()?.asName()!!).buildNode(name.cutFirst())
else -> buildNode(name.firstOrNull()?.asName()!!).buildNode(name.cutFirst())
}
}
@ -154,39 +149,43 @@ class NodeDescriptor(config: Config = Config()) : ItemDescriptor(config) {
config[token] = descriptor.config
}
fun item(name: Name, descriptor: ItemDescriptor) {
buildNode(name.cutLast()).newItem(name.last().toString(), descriptor)
public fun item(name: Name, descriptor: ItemDescriptor) {
buildNode(name.cutLast()).newItem(name.lastOrNull().toString(), descriptor)
}
fun item(name: String, descriptor: ItemDescriptor) {
public fun item(name: String, descriptor: ItemDescriptor) {
item(name.toName(), descriptor)
}
fun node(name: Name, block: NodeDescriptor.() -> Unit) {
/**
* Create and configure a child node descriptor
*/
public fun node(name: Name, block: NodeDescriptor.() -> Unit) {
item(name, NodeDescriptor().apply(block))
}
fun node(name: String, block: NodeDescriptor.() -> Unit) {
public fun node(name: String, block: NodeDescriptor.() -> Unit) {
node(name.toName(), block)
}
fun value(name: Name, block: ValueDescriptor.() -> Unit) {
/**
* Create and configure child value descriptor
*/
public fun value(name: Name, block: ValueDescriptor.() -> Unit) {
require(name.length >= 1) { "Name length for value descriptor must be non-empty" }
item(name, ValueDescriptor().apply(block))
}
fun value(name: String, block: ValueDescriptor.() -> Unit) {
public fun value(name: String, block: ValueDescriptor.() -> Unit) {
value(name.toName(), block)
}
companion object {
public companion object {
val ITEM_KEY = "item".asName()
val IS_NODE_KEY = "@isNode".asName()
internal val ITEM_KEY: Name = "item".asName()
internal val IS_NODE_KEY: Name = "@isNode".asName()
const val DEFAULT_INDEX_KEY = "@index"
inline operator fun invoke(block: NodeDescriptor.() -> Unit) = NodeDescriptor().apply(block)
public inline operator fun invoke(block: NodeDescriptor.() -> Unit): NodeDescriptor = NodeDescriptor().apply(block)
//TODO infer descriptor from spec
}
@ -195,15 +194,15 @@ class NodeDescriptor(config: Config = Config()) : ItemDescriptor(config) {
/**
* Get a descriptor item associated with given name or null if item for given name not provided
*/
operator fun ItemDescriptor.get(name: Name): ItemDescriptor? {
public operator fun ItemDescriptor.get(name: Name): ItemDescriptor? {
if (name.isEmpty()) return this
return when (this) {
is ValueDescriptor -> null // empty name already checked
is NodeDescriptor -> items[name.first()!!.toString()]?.get(name.cutFirst())
is NodeDescriptor -> items[name.firstOrNull()!!.toString()]?.get(name.cutFirst())
}
}
operator fun ItemDescriptor.get(name: String): ItemDescriptor? = get(name.toName())
public operator fun ItemDescriptor.get(name: String): ItemDescriptor? = get(name.toName())
/**
* A descriptor for meta value
@ -213,7 +212,7 @@ operator fun ItemDescriptor.get(name: String): ItemDescriptor? = get(name.toName
* @author Alexander Nozik
*/
@DFBuilder
class ValueDescriptor(config: Config = Config()) : ItemDescriptor(config) {
public class ValueDescriptor(config: Config = Config()) : ItemDescriptor(config) {
/**
* True if the value is required
@ -227,9 +226,9 @@ class ValueDescriptor(config: Config = Config()) : ItemDescriptor(config) {
*
* @return
*/
var default: Value? by config.value()
public var default: Value? by config.value()
fun default(v: Any) {
public fun default(v: Any) {
this.default = Value.of(v)
}
@ -238,9 +237,9 @@ class ValueDescriptor(config: Config = Config()) : ItemDescriptor(config) {
*
* @return
*/
var type: List<ValueType>? by config.listValue { ValueType.valueOf(it.string) }
public var type: List<ValueType>? by config.listValue { ValueType.valueOf(it.string) }
fun type(vararg t: ValueType) {
public fun type(vararg t: ValueType) {
this.type = listOf(*t)
}
@ -251,7 +250,7 @@ class ValueDescriptor(config: Config = Config()) : ItemDescriptor(config) {
* @param value
* @return
*/
fun isAllowedValue(value: Value): Boolean {
public fun isAllowedValue(value: Value): Boolean {
return (type?.let { it.contains(ValueType.STRING) || it.contains(value.type) } ?: true)
&& (allowedValues.isEmpty() || allowedValues.contains(value))
}
@ -262,7 +261,7 @@ class ValueDescriptor(config: Config = Config()) : ItemDescriptor(config) {
*
* @return
*/
var allowedValues: List<Value> by config.item().convert(
public var allowedValues: List<Value> by config.item().convert(
reader = {
val value = it.value
when {
@ -279,7 +278,7 @@ class ValueDescriptor(config: Config = Config()) : ItemDescriptor(config) {
/**
* Allow given list of value and forbid others
*/
fun allow(vararg v: Any) {
public fun allow(vararg v: Any) {
this.allowedValues = v.map { Value.of(it) }
}
}
@ -287,7 +286,7 @@ class ValueDescriptor(config: Config = Config()) : ItemDescriptor(config) {
/**
* Merge two node descriptors into one using first one as primary
*/
operator fun NodeDescriptor.plus(other: NodeDescriptor): NodeDescriptor {
public operator fun NodeDescriptor.plus(other: NodeDescriptor): NodeDescriptor {
return NodeDescriptor().apply {
config.update(other.config)
config.update(this@plus.config)

@ -0,0 +1,36 @@
package hep.dataforge.meta
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.
*/
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
else -> this[name.cutLast()].node ?: return emptyMap()
}
val (body, index) = name.lastOrNull()!!
return if (index == null) {
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 ?: "")) }
.mapKeys { it.key.index }
}
}
public fun Meta.getIndexed(name: String): Map<String?, MetaItem<*>> = this@getIndexed.getIndexed(name.toName())
/**
* Get all items matching given name.
*/
@Suppress("UNCHECKED_CAST")
public fun <M : MetaNode<M>> M.getIndexed(name: Name): Map<String, MetaItem<M>> =
(this as Meta).getIndexed(name) as Map<String, MetaItem<M>>
public fun <M : MetaNode<M>> M.getIndexed(name: String): Map<String, MetaItem<M>> =
getIndexed(name.toName())

@ -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

Some files were not shown because too many files have changed in this diff Show More