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/ build/
!gradle-wrapper.jar !gradle-wrapper.jar
gradle.properties

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 { plugins {
val toolsVersion = "0.5.0" id("ru.mipt.npm.project")
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"
} }
val dataforgeVersion by extra("0.1.8") val dataforgeVersion by extra("0.2.0")
val bintrayRepo by extra("dataforge") val bintrayRepo by extra("dataforge")
val githubProject by extra("dataforge-core") val githubProject by extra("dataforge-core")
val spaceRepo by extra("https://maven.jetbrains.space/mipt-npm/p/df/maven")
allprojects { allprojects {
group = "hep.dataforge" group = "hep.dataforge"
version = dataforgeVersion version = dataforgeVersion
apply<org.jetbrains.dokka.gradle.DokkaPlugin>()
repositories { repositories {
mavenLocal() mavenLocal()
} }
} }
subprojects { subprojects {
apply(plugin = "scientifik.publish") apply(plugin = "ru.mipt.npm.publish")
apply(plugin = "org.jetbrains.dokka")
} }

@ -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 { plugins {
id("scientifik.mpp") id("ru.mipt.npm.mpp")
id("ru.mipt.npm.native")
} }
description = "Context and provider definitions" description = "Context and provider definitions"
kscience {
useCoroutines() useCoroutines()
}
kotlin { kotlin {
sourceSets { sourceSets {
val commonMain by getting { val commonMain by getting {
dependencies { dependencies {
api(project(":dataforge-meta")) 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 { val jvmMain by getting {
dependencies { dependencies {
api(kotlin("reflect")) api(kotlin("reflect"))
api("io.github.microutils:kotlin-logging:1.7.9")
api("ch.qos.logback:logback-classic:1.2.3") api("ch.qos.logback:logback-classic:1.2.3")
} }
} }
val jsMain by getting { val jsMain by getting {
dependencies { 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.KClass
import kotlin.reflect.KProperty 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 var _context: Context? = null
private val dependencies = ArrayList<PluginFactory<*>>() private val dependencies = ArrayList<PluginFactory<*>>()
@ -30,11 +30,9 @@ abstract class AbstractPlugin(override val meta: Meta = Meta.EMPTY) : Plugin {
dependencies.add(factory) dependencies.add(factory)
return PluginDependencyDelegate(factory.type) 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> { private class PluginDependencyDelegate<P : Plugin>(val type: KClass<out P>) : ReadOnlyProperty<AbstractPlugin, P> {
override fun getValue(thisRef: AbstractPlugin, property: KProperty<*>): P { override fun getValue(thisRef: AbstractPlugin, property: KProperty<*>): P {

@ -1,19 +1,18 @@
package hep.dataforge.context 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.Name
import hep.dataforge.names.asName
import hep.dataforge.names.plus import hep.dataforge.names.plus
import hep.dataforge.provider.Provider import hep.dataforge.provider.Provider
import hep.dataforge.provider.top
import hep.dataforge.values.Value
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Job import kotlinx.coroutines.Job
import kotlinx.coroutines.SupervisorJob import kotlinx.coroutines.SupervisorJob
import mu.KLogger import mu.KLogger
import mu.KotlinLogging import mu.KotlinLogging
import kotlin.coroutines.CoroutineContext 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. * 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 * 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. * 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, final override val name: Name,
val parent: Context? = Global public val parent: Context?,
meta: Meta,
plugins: Set<Plugin> = emptySet(),
) : Named, MetaRepr, Provider, CoroutineScope { ) : Named, MetaRepr, Provider, CoroutineScope {
private val config = Config()
/** /**
* Context properties. Working as substitute for environment variables * Context properties. Working as substitute for environment variables
*/ */
val properties: Meta = if (parent == null) { private val properties: Laminate = if (parent == null) {
config Laminate(meta)
} else { } else {
Laminate(config, parent.properties) Laminate(meta, parent.properties)
} }
/** /**
* Context logger * Context logger
*/ */
val logger: KLogger = KotlinLogging.logger(name.toString()) public val logger: KLogger = KotlinLogging.logger(name.toString())
/** /**
* A [PluginManager] for current context * 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
/** public fun content(target: String, inherit: Boolean): Map<Name, Any> {
* Defines if context is used in any kind of active computations. Active context properties and plugins could not be changed return if (inherit) {
*/ when (target) {
val isActive: Boolean = activators.isNotEmpty() PROPERTY_TARGET -> properties.sequence().toMap()
Plugin.TARGET -> plugins.list(true).associateBy { it.name }
override val defaultTarget: String get() = Plugin.PLUGIN_TARGET else -> emptyMap()
}
override fun provideTop(target: String): Map<Name, Any> { } else {
return when (target) { when (target) {
Value.TYPE -> properties.sequence().toMap() PROPERTY_TARGET -> properties.layers.firstOrNull()?.sequence()?.toMap() ?: emptyMap()
Plugin.PLUGIN_TARGET -> plugins.sequence(true).associateBy { it.name } Plugin.TARGET -> plugins.list(false).associateBy { it.name }
else -> emptyMap() else -> emptyMap()
}
} }
} }
/** override fun content(target: String): Map<Name, Any> = content(target, true)
* 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 val coroutineContext: CoroutineContext by lazy { override val coroutineContext: CoroutineContext by lazy {
(parent ?: Global).coroutineContext.let { parenContext -> (parent ?: Global).coroutineContext.let { parenContext ->
@ -101,81 +79,35 @@ open class Context(
/** /**
* Detach all plugins and terminate context * Detach all plugins and terminate context
*/ */
open fun close() { public open fun close() {
if (isActive) error("Can't close active context")
//detach all plugins //detach all plugins
plugins.forEach { it.detach() } plugins.forEach { it.detach() }
} }
override fun toMeta(): Meta = Meta { override fun toMeta(): Meta = Meta {
"parent" to parent?.name "parent" to parent?.name
"properties" put properties.seal() "properties" put properties.layers.firstOrNull()
"plugins" put plugins.map { it.toMeta() } "plugins" put plugins.map { it.toMeta() }
} }
}
fun Context.content(target: String): Map<Name, Any> = content<Any>(target) public companion object {
public const val PROPERTY_TARGET: String = "context.property"
/**
* 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()
} }
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 * The interface for something that encapsulated in context
* *
* @author Alexander Nozik
* @version $Id: $Id
*/ */
interface ContextAware { public interface ContextAware {
/** /**
* Get context for this object * Get context for this object
* *
* @return * @return
*/ */
val context: Context public val context: Context
val logger: KLogger public val logger: KLogger
get() = if (this is Named) { get() = if (this is Named) {
KotlinLogging.logger((context.name + this.name).toString()) KotlinLogging.logger((context.name + this.name).toString())
} else { } else {

@ -1,44 +1,44 @@
package hep.dataforge.context package hep.dataforge.context
import hep.dataforge.meta.DFBuilder import hep.dataforge.meta.*
import hep.dataforge.meta.Meta
import hep.dataforge.meta.MetaBuilder
import hep.dataforge.meta.buildMeta
import hep.dataforge.names.toName import hep.dataforge.names.toName
/** /**
* A convenience builder for context * A convenience builder for context
*/ */
@DFBuilder @DFBuilder
class ContextBuilder(var name: String = "@anonymous", val parent: Context = Global) { public class ContextBuilder(private val parent: Context = Global, public var name: String = "@anonymous") {
private val plugins = ArrayList<Plugin>() private val plugins = HashSet<Plugin>()
private var meta = MetaBuilder() private var meta = MetaBuilder()
fun properties(action: MetaBuilder.() -> Unit) { public fun properties(action: MetaBuilder.() -> Unit) {
meta.action() meta.action()
} }
fun plugin(plugin: Plugin) { public fun plugin(plugin: Plugin) {
plugins.add(plugin) plugins.add(plugin)
} }
fun plugin(tag: PluginTag, action: MetaBuilder.() -> Unit = {}) { @OptIn(DFExperimental::class)
plugins.add(PluginRepository.fetch(tag, Meta(action))) 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))) 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) plugin(PluginTag(name, group, version), action)
} }
fun build(): Context { public fun build(): Context {
return Context(name.toName(), parent).apply { return Context(name.toName(), parent, meta.seal(), plugins)
this@ContextBuilder.plugins.forEach {
plugins.load(it)
}
}
} }
} }

@ -2,6 +2,6 @@ package hep.dataforge.context
import hep.dataforge.meta.Meta import hep.dataforge.meta.Meta
interface Factory<out T : Any> { public interface Factory<out T : Any> {
operator fun invoke(meta: Meta = Meta.EMPTY, context: Context = Global): T 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 * @author Alexander Nozik
*/ */
interface Named { public interface Named {
/** /**
* The name of this object instance * 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, * Get the name of given object. If object is Named its name is used,
@ -42,7 +40,7 @@ interface Named {
* @param obj * @param obj
* @return * @return
*/ */
fun nameOf(obj: Any): Name { public fun nameOf(obj: Any): Name {
return if (obj is Named) { return if (obj is Named) {
obj.name obj.name
} else { } else {
@ -56,5 +54,4 @@ interface Named {
* Check if this object has an empty name and therefore is anonymous. * Check if this object has an empty name and therefore is anonymous.
* @return * @return
*/ */
val Named.isAnonymous: Boolean public val Named.isAnonymous: Boolean get() = this.name.isEmpty()
get() = this.name.isEmpty()

@ -1,43 +1,34 @@
package hep.dataforge.context package hep.dataforge.context
import hep.dataforge.context.Plugin.Companion.TARGET
import hep.dataforge.meta.Meta import hep.dataforge.meta.Meta
import hep.dataforge.meta.MetaRepr import hep.dataforge.meta.MetaRepr
import hep.dataforge.meta.buildMeta
import hep.dataforge.names.Name import hep.dataforge.names.Name
import hep.dataforge.names.toName import hep.dataforge.names.toName
import hep.dataforge.provider.Provider 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 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 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 must in most cases have an empty constructor in order to be able to load it from library.
* *
*
* The plugin lifecycle is the following: * The plugin lifecycle is the following:
* *
*
* create - configure - attach - detach - destroy * 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 * 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 * The name of this plugin ignoring version and group
*
* @return
*/ */
override val name: Name get() = tag.name.toName() 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 * Plugin dependencies which are required to attach this plugin. Plugin
* dependencies must be initialized and enabled in the Context before this * dependencies must be initialized and enabled in the Context before this
* plugin is enabled. * 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 * Start this plugin and attach registration info to the context. This method
* should be called only via PluginManager to avoid dependency issues. * 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 * Stop this plugin and remove registration info from context and other
* plugins. This method should be called only via PluginManager to avoid * plugins. This method should be called only via PluginManager to avoid
* dependency issues. * dependency issues.
*/ */
fun detach() public fun detach()
override fun toMeta(): Meta = Meta { override fun toMeta(): Meta = Meta {
"context" put context.name.toString() "context" put context.name.toString()
@ -72,9 +59,8 @@ interface Plugin : Named, ContextAware, Provider, MetaRepr {
"meta" put meta "meta" put meta
} }
companion object { public companion object {
public const val TARGET: String = "plugin"
const val PLUGIN_TARGET = "plugin"
} }
} }

@ -2,42 +2,62 @@ package hep.dataforge.context
import hep.dataforge.meta.Meta import hep.dataforge.meta.Meta
import hep.dataforge.meta.MetaBuilder import hep.dataforge.meta.MetaBuilder
import hep.dataforge.provider.Type
import kotlin.reflect.KClass 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. * The manager for plugin system. Should monitor plugin dependencies and locks.
* *
* @property context A context for this plugin manager * @property context A context for this plugin manager
* @author Alexander Nozik * @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 * 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 * A [PluginManager] of parent context if it is present
*/ */
private val parent: PluginManager? = context.parent?.plugins private val parent: PluginManager? = context.parent?.plugins
/**
fun sequence(recursive: Boolean): Sequence<Plugin> { * List plugins stored in this [PluginManager]. If [inherit] is true, include parent plugins as well
return if (recursive && parent != null) { */
plugins.asSequence() + parent.sequence(true) public fun list(inherit: Boolean): Collection<Plugin> {
return if (inherit && parent != null) {
plugins + parent.list(true)
} else { } else {
plugins.asSequence() plugins
} }
} }
/** /**
* Get existing plugin or return null if not present. Only first matching plugin is returned. * 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 * @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 * Find a loaded plugin via its tag
@ -45,8 +65,8 @@ class PluginManager(override val context: Context) : ContextAware, Iterable<Plug
* @param tag * @param tag
* @return * @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 * 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 * @return
*/ */
@Suppress("UNCHECKED_CAST") @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? 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) 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) get(factory.type, factory.tag, recursive)
/** /**
@ -76,11 +96,9 @@ class PluginManager(override val context: Context) : ContextAware, Iterable<Plug
* @param plugin * @param plugin
* @return * @return
*/ */
fun <T : Plugin> load(plugin: T): T { public fun <T : Plugin> load(plugin: T): T {
if (context.isActive) error("Can't load plugin into active context")
if (get(plugin::class, plugin.tag, recursive = false) != null) { 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 { } else {
for (tag in plugin.dependsOn()) { for (tag in plugin.dependsOn()) {
fetch(tag, true) fetch(tag, true)
@ -96,18 +114,16 @@ class PluginManager(override val context: Context) : ContextAware, Iterable<Plug
/** /**
* Load a plugin using its factory * 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)) 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)) load(factory, Meta(metaBuilder))
/** /**
* Remove a plugin from [PluginManager] * Remove a plugin from [PluginManager]
*/ */
fun remove(plugin: Plugin) { public fun remove(plugin: Plugin) {
if (context.isActive) error("Can't remove plugin from active context")
if (plugins.contains(plugin)) { if (plugins.contains(plugin)) {
logger.info { "Removing plugin ${plugin.name} from ${context.name}" } logger.info { "Removing plugin ${plugin.name} from ${context.name}" }
plugin.detach() 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 * 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) val loaded = get(factory.type, factory.tag, recursive)
return when { return when {
loaded == null -> load(factory(meta, context)) 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>, factory: PluginFactory<T>,
recursive: Boolean = true, recursive: Boolean = true,
metaBuilder: MetaBuilder.() -> Unit metaBuilder: MetaBuilder.() -> Unit,
): T = fetch(factory, recursive, Meta(metaBuilder)) ): T = fetch(factory, recursive, Meta(metaBuilder))
override fun iterator(): Iterator<Plugin> = plugins.iterator() 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.Meta
import hep.dataforge.meta.MetaRepr import hep.dataforge.meta.MetaRepr
import hep.dataforge.meta.buildMeta
/** /**
* The tag which contains information about name, group and version of some * The tag which contains information about name, group and version of some
@ -10,7 +9,7 @@ import hep.dataforge.meta.buildMeta
* *
* @author Alexander Nozik * @author Alexander Nozik
*/ */
data class PluginTag( public data class PluginTag(
val name: String, val name: String,
val group: String = "", val group: String = "",
val version: String = "" val version: String = ""
@ -22,7 +21,7 @@ data class PluginTag(
* @param otherTag * @param otherTag
* @return * @return
*/ */
fun matches(otherTag: PluginTag): Boolean { public fun matches(otherTag: PluginTag): Boolean {
return matchesName(otherTag) && matchesGroup(otherTag) return matchesName(otherTag) && matchesGroup(otherTag)
} }
@ -42,9 +41,9 @@ data class PluginTag(
"version" put version "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 * Build new PluginTag from standard string representation
@ -52,7 +51,7 @@ data class PluginTag(
* @param tag * @param tag
* @return * @return
*/ */
fun fromString(tag: String): PluginTag { public fun fromString(tag: String): PluginTag {
val sepIndex = tag.indexOf(":") val sepIndex = tag.indexOf(":")
return if (sepIndex >= 0) { return if (sepIndex >= 0) {
PluginTag(group = tag.substring(0, sepIndex), name = tag.substring(sepIndex + 1)) 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 * @author Alexander Nozik
* @version $Id: $Id * @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. * Returns non-empty optional containing the chain without first segment in case of chain path.
* *
* @return * @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() override fun iterator(): Iterator<PathToken> = tokens.iterator()
companion object { public companion object {
const val PATH_SEGMENT_SEPARATOR = "/" 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 head = path.substringBefore(PATH_SEGMENT_SEPARATOR)
val tail = path.substringAfter(PATH_SEGMENT_SEPARATOR) val tail = path.substringAfter(PATH_SEGMENT_SEPARATOR)
return PathToken.parse(head).toPath() + parse(tail) 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) { override fun toString(): String = if (target == null) {
name.toString() name.toString()
} else { } else {
"$target$TARGET_SEPARATOR$name" "$target$TARGET_SEPARATOR$name"
} }
companion object { public companion object {
const val TARGET_SEPARATOR = "::" public const val TARGET_SEPARATOR: String = "::"
fun parse(token: String): PathToken { public fun parse(token: String): PathToken {
val target = token.substringBefore(TARGET_SEPARATOR, "") val target = token.substringBefore(TARGET_SEPARATOR, "")
val name = token.substringAfter(TARGET_SEPARATOR).toName() val name = token.substringAfter(TARGET_SEPARATOR).toName()
if (target.contains("[")) TODO("target separators in queries are not supported") 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 package hep.dataforge.provider
import hep.dataforge.names.Name import hep.dataforge.names.Name
import hep.dataforge.names.toName import kotlin.reflect.KClass
import kotlin.reflect.safeCast
/** /**
* A marker utility interface for providers. * A marker utility interface for providers.
* *
* @author Alexander Nozik * @author Alexander Nozik
*/ */
interface Provider { public interface Provider {
/** /**
* Default target for this provider * Default target for this provider
*
* @return
*/ */
val defaultTarget: String get() = "" public val defaultTarget: String get() = ""
/** /**
* Default target for next chain segment * Default target for next chain segment
*
* @return
*/ */
val defaultChainTarget: String get() = "" public val defaultChainTarget: String get() = ""
/** /**
* A map of direct children for specific target * 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") if (path.length == 0) throw IllegalArgumentException("Can't provide by empty path")
val first = path.first() val first = path.first()
val target = targetOverride ?: first.target ?: defaultTarget 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) { return when (path.length) {
1 -> res 1 -> res
else -> { else -> {
@ -65,24 +61,29 @@ fun Provider.provide(path: Path, targetOverride: String? = null): Any? {
/** /**
* Type checked provide * Type checked provide
*/ */
inline fun <reified T : Any> Provider.provide(path: String): T? { public inline fun <reified T : Any> Provider.provide(path: String, targetOverride: String? = null): T? {
return provide(Path.parse(path)) as? 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? { //inline fun <reified T : Any> Provider.provide(target: String, name: String): T? =
return provide(PathToken(name, target).toPath()) as? 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 * Typed top level content
*/ */
inline fun <reified T : Any> Provider.top(target: String): Map<Name, T> { public fun <T : Any> Provider.top(target: String, type: KClass<out T>): Map<Name, T> {
return provideTop(target).mapValues { return content(target).mapValues {
it.value as? T ?: error("The type of element $it is ${it::class} but ${T::class} is expected") 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 @MustBeDocumented
@Target(AnnotationTarget.CLASS) @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() { class DummyPlugin : AbstractPlugin() {
override val tag get() = PluginTag("test") 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){ return when(target){
"test" -> listOf("a", "b", "c.d").associate { it.toName() to it.toName() } "test" -> listOf("a", "b", "c.d").associate { it.toName() to it.toName() }
else -> emptyMap() else -> emptyMap()
@ -21,8 +21,11 @@ class ContextTest {
@Test @Test
fun testPluginManager() { fun testPluginManager() {
Global.plugins.load(DummyPlugin()) val context = Global.context("test"){
val members = Global.content<Name>("test") plugin(DummyPlugin())
}
//Global.plugins.load(DummyPlugin())
val members = context.gather<Name>("test")
assertEquals(3, members.count()) assertEquals(3, members.count())
members.forEach { members.forEach {
assertEquals(it.key, it.value.appendLeft("test")) 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.KClass
import kotlin.reflect.full.cast 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) override val tag: PluginTag = PluginTag("classLoader", PluginTag.DATAFORGE_GROUP)
private val serviceCache: MutableMap<Class<*>, ServiceLoader<*>> = HashMap() 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() return serviceCache.getOrPut(type.java) { ServiceLoader.load(type.java, classLoader) }.asSequence()
.map { type.cast(it) } .map { type.cast(it) }
} }
companion object { public companion object {
val DEFAULT = ClassLoaderPlugin(Global::class.java.classLoader) 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( //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 { plugins {
id("scientifik.mpp") id("ru.mipt.npm.mpp")
id("ru.mipt.npm.native")
} }
val coroutinesVersion: String = Scientifik.coroutinesVersion kscience{
useCoroutines()
}
kotlin { kotlin {
sourceSets { sourceSets {
val commonMain by getting{ commonMain{
dependencies { dependencies {
api(project(":dataforge-meta")) api(project(":dataforge-meta"))
api("org.jetbrains.kotlinx:kotlinx-coroutines-core-common:$coroutinesVersion")
} }
} }
jvmMain{
val jvmMain by getting{ dependencies{
dependencies {
api("org.jetbrains.kotlinx:kotlinx-coroutines-core:$coroutinesVersion")
api(kotlin("reflect")) 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 * 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 * 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 * 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 * 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 * 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 // TODO introduce composite action and add optimize by adding action to the list
return object : Action<T, R> { return object : Action<T, R> {
override fun invoke(node: DataNode<T>, meta: Meta): DataNode<R> { override fun invoke(node: DataNode<T>, meta: Meta): DataNode<R> {

@ -1,48 +1,58 @@
package hep.dataforge.data package hep.dataforge.data
import hep.dataforge.meta.DFExperimental
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Job import kotlinx.coroutines.Job
import kotlin.coroutines.CoroutineContext import kotlin.coroutines.CoroutineContext
/** /**
* A monitor of goal state that could be accessed only form inside the goal * 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 override val key: CoroutineContext.Key<*> get() = CoroutineMonitor
var totalWork: Double = 1.0 public var totalWork: Double = 1.0
var workDone: Double = 0.0 public var workDone: Double = 0.0
var status: String = "" public var status: String = ""
/** /**
* Mark the goal as started * Mark the goal as started
*/ */
fun start() { public fun start() {
} }
/** /**
* Mark the goal as completed * Mark the goal as completed
*/ */
fun finish() { public fun finish() {
workDone = totalWork 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 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] @DFExperimental
val CoroutineScope.monitor: CoroutineMonitor? get() = coroutineContext.monitor 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) @DFExperimental
val Job.workDone: Double get() = dependencies.sumByDouble { workDone } + (monitor?.workDone ?: 0.0) public val Job.totalWork: Double get() = dependencies.sumByDouble { totalWork } + (monitor?.totalWork ?: 0.0)
val Job.status: String get() = monitor?.status ?: "" @DFExperimental
val Job.progress: Double get() = workDone / totalWork 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 * 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. * 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 * Meta for the data
*/ */
val meta: Meta public val meta: Meta
override fun toMeta(): Meta = Meta { override fun toMeta(): Meta = Meta {
"type" put (type.simpleName?:"undefined") "type" put (type.simpleName?:"undefined")
@ -28,10 +29,10 @@ interface Data<out T : Any> : Goal<T>, MetaRepr{
} }
} }
companion object { public companion object {
const val TYPE = "data" public const val TYPE: String = "data"
operator fun <T : Any> invoke( public operator fun <T : Any> invoke(
type: KClass<out T>, type: KClass<out T>,
meta: Meta = Meta.EMPTY, meta: Meta = Meta.EMPTY,
context: CoroutineContext = EmptyCoroutineContext, context: CoroutineContext = EmptyCoroutineContext,
@ -39,14 +40,14 @@ interface Data<out T : Any> : Goal<T>, MetaRepr{
block: suspend CoroutineScope.() -> T block: suspend CoroutineScope.() -> T
): Data<T> = DynamicData(type, meta, context, dependencies, block) ): 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, meta: Meta = Meta.EMPTY,
context: CoroutineContext = EmptyCoroutineContext, context: CoroutineContext = EmptyCoroutineContext,
dependencies: Collection<Data<*>> = emptyList(), dependencies: Collection<Data<*>> = emptyList(),
noinline block: suspend CoroutineScope.() -> T noinline block: suspend CoroutineScope.() -> T
): Data<T> = invoke(T::class, meta, context, dependencies, block) ): Data<T> = invoke(T::class, meta, context, dependencies, block)
operator fun <T : Any> invoke( public operator fun <T : Any> invoke(
name: String, name: String,
type: KClass<out T>, type: KClass<out T>,
meta: Meta = Meta.EMPTY, meta: Meta = Meta.EMPTY,
@ -55,7 +56,7 @@ interface Data<out T : Any> : Goal<T>, MetaRepr{
block: suspend CoroutineScope.() -> T block: suspend CoroutineScope.() -> T
): Data<T> = NamedData(name, invoke(type, meta, context, dependencies, block)) ): 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, name: String,
meta: Meta = Meta.EMPTY, meta: Meta = Meta.EMPTY,
context: CoroutineContext = EmptyCoroutineContext, context: CoroutineContext = EmptyCoroutineContext,
@ -64,13 +65,13 @@ interface Data<out T : Any> : Goal<T>, MetaRepr{
): Data<T> = ): Data<T> =
invoke(name, T::class, meta, context, dependencies, block) 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) StaticData(value, meta)
} }
} }
class DynamicData<T : Any>( public class DynamicData<T : Any>(
override val type: KClass<out T>, override val type: KClass<out T>,
override val meta: Meta = Meta.EMPTY, override val meta: Meta = Meta.EMPTY,
context: CoroutineContext = EmptyCoroutineContext, context: CoroutineContext = EmptyCoroutineContext,
@ -78,16 +79,16 @@ class DynamicData<T : Any>(
block: suspend CoroutineScope.() -> T block: suspend CoroutineScope.() -> T
) : Data<T>, DynamicGoal<T>(context, dependencies, block) ) : Data<T>, DynamicGoal<T>(context, dependencies, block)
class StaticData<T : Any>( public class StaticData<T : Any>(
value: T, value: T,
override val meta: Meta = Meta.EMPTY override val meta: Meta = Meta.EMPTY
) : Data<T>, StaticGoal<T>(value) { ) : Data<T>, StaticGoal<T>(value) {
override val type: KClass<out T> get() = value::class 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>, outputType: KClass<out R>,
coroutineContext: CoroutineContext = EmptyCoroutineContext, coroutineContext: CoroutineContext = EmptyCoroutineContext,
meta: Meta = this.meta, meta: Meta = this.meta,
@ -100,7 +101,7 @@ fun <T : Any, R : Any> Data<T>.map(
/** /**
* Create a data pipe * 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, coroutineContext: CoroutineContext = EmptyCoroutineContext,
meta: Meta = this.meta, meta: Meta = this.meta,
noinline block: suspend CoroutineScope.(T) -> R noinline block: suspend CoroutineScope.(T) -> R
@ -111,7 +112,7 @@ inline fun <T : Any, reified R : Any> Data<T>.map(
/** /**
* Create a joined data. * 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, coroutineContext: CoroutineContext = EmptyCoroutineContext,
meta: Meta, meta: Meta,
noinline block: suspend CoroutineScope.(Collection<T>) -> R 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() } }) 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>, outputType: KClass<out R>,
coroutineContext: CoroutineContext = EmptyCoroutineContext, coroutineContext: CoroutineContext = EmptyCoroutineContext,
meta: Meta, 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 T type of the input goal
* @param R type of the result 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, coroutineContext: CoroutineContext = EmptyCoroutineContext,
meta: Meta, meta: Meta,
noinline block: suspend CoroutineScope.(Map<K, T>) -> R noinline block: suspend CoroutineScope.(Map<K, T>) -> R

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

@ -11,12 +11,12 @@ import kotlin.collections.component2
import kotlin.collections.set import kotlin.collections.set
import kotlin.reflect.KClass import kotlin.reflect.KClass
sealed class DataItem<out T : Any> : MetaRepr { public sealed class DataItem<out T : Any> : MetaRepr {
abstract val type: KClass<out T> 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 val type: KClass<out T> get() = node.type
override fun toMeta(): Meta = node.toMeta() override fun toMeta(): Meta = node.toMeta()
@ -24,7 +24,7 @@ sealed class DataItem<out T : Any> : MetaRepr {
override val meta: Meta get() = node.meta 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 val type: KClass<out T> get() = data.type
override fun toMeta(): Meta = data.toMeta() 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 * 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 * 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 { override fun toMeta(): Meta = Meta {
"type" put (type.simpleName ?: "undefined") "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 * Start computation for all goals in data node and return a job for the whole node
*/ */
@Suppress("DeferredResultUnused") @Suppress("DeferredResultUnused")
fun CoroutineScope.startAll(): Job = launch { public fun CoroutineScope.startAll(): Job = launch {
items.values.forEach { items.values.forEach {
when (it) { when (it) {
is DataItem.Node<*> -> it.node.run { startAll() } is DataItem.Node<*> -> it.node.run { startAll() }
@ -69,36 +69,36 @@ interface DataNode<out T : Any> : MetaRepr {
} }
} }
companion object { public companion object {
const val TYPE = "dataNode" 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() 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() 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 public 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>?.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") 0 -> error("Empty name")
1 -> items[name.first()] 1 -> items[name.firstOrNull()]
else -> get(name.first()!!.asName()).node?.get(name.cutFirst()) 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 * 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) -> items.forEach { (head, item) ->
yield(head.asName() to item) yield(head.asName() to item)
if (item is DataItem.Node) { 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 * 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) -> items.forEach { (head, item) ->
when (item) { when (item) {
is DataItem.Leaf -> yield(head.asName() to item.data) 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 type: KClass<out T>,
override val items: Map<NameToken, DataItem<T>>, override val items: Map<NameToken, DataItem<T>>,
override val meta: Meta override val meta: Meta
@ -142,17 +142,17 @@ private sealed class DataTreeBuilderItem<out T : Any> {
* A builder for a DataTree. * A builder for a DataTree.
*/ */
@DFBuilder @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 val map = HashMap<NameToken, DataTreeBuilderItem<T>>()
private var meta = MetaBuilder() 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") if (map.containsKey(token)) error("Tree entry with name $token is not empty")
map[token] = DataTreeBuilderItem.Node(node) 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") if (map.containsKey(token)) error("Tree entry with name $token is not empty")
map[token] = DataTreeBuilderItem.Leaf(data) 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> { private fun buildNode(name: Name): DataTreeBuilder<T> {
return when (name.length) { return when (name.length) {
0 -> this 0 -> this
1 -> buildNode(name.first()!!) 1 -> buildNode(name.firstOrNull()!!)
else -> buildNode(name.first()!!).buildNode(name.cutFirst()) 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) { when (name.length) {
0 -> error("Can't add data with empty name") 0 -> error("Can't add data with empty name")
1 -> set(name.first()!!, data) 1 -> set(name.firstOrNull()!!, data)
2 -> buildNode(name.cutLast())[name.last()!!] = 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) { when (name.length) {
0 -> error("Can't add data with empty name") 0 -> error("Can't add data with empty name")
1 -> set(name.first()!!, node) 1 -> set(name.firstOrNull()!!, node)
2 -> buildNode(name.cutLast())[name.last()!!] = 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.Node<T> -> set(name, item.node.builder())
is DataItem.Leaf<T> -> set(name, item.data) 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 * 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 * 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 * 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. * 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 { node.dataSequence().forEach {
//TODO check if the place is occupied //TODO check if the place is occupied
this[it.first] = it.second this[it.first] = it.second
@ -225,13 +225,13 @@ class DataTreeBuilder<T : Any>(val type: KClass<out T>) {
meta.update(node.meta) 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() this.meta = meta.builder()
} }
fun build(): DataTree<T> { public fun build(): DataTree<T> {
val resMap = map.mapValues { (_, value) -> val resMap = map.mapValues { (_, value) ->
when (value) { when (value) {
is DataTreeBuilderItem.Leaf -> DataItem.Leaf(value.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 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 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) 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)) 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)) 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 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 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) 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) this[name.toName()] = DataNode(T::class, block)
} }
/** /**
* Generate a mutable builder from this node. Node content is not changed * 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 } 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) -> dataSequence().forEach { (name, data) ->
if (predicate(name, data)) { if (predicate(name, data)) {
this[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 package hep.dataforge.data
import hep.dataforge.meta.DFExperimental
import kotlinx.coroutines.* import kotlinx.coroutines.*
import kotlin.coroutines.CoroutineContext import kotlin.coroutines.CoroutineContext
import kotlin.coroutines.EmptyCoroutineContext import kotlin.coroutines.EmptyCoroutineContext
interface Goal<out T> { public interface Goal<out T> {
val dependencies: Collection<Goal<*>> public val dependencies: Collection<Goal<*>>
/** /**
* Returns current running coroutine if the goal is started * 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. * Get ongoing computation or start a new one.
* Does not guarantee thread safety. In case of multi-thread access, could create orphan computations. * 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 * 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 dependencies: Collection<Goal<*>> get() = emptyList()
override val result: Deferred<T> = CompletableDeferred(value) override val result: Deferred<T> = CompletableDeferred(value)
@ -42,10 +41,10 @@ open class StaticGoal<T>(val value: T) : Goal<T> {
} }
} }
open class DynamicGoal<T>( public open class DynamicGoal<T>(
val coroutineContext: CoroutineContext = EmptyCoroutineContext, private val coroutineContext: CoroutineContext = EmptyCoroutineContext,
override val dependencies: Collection<Goal<*>> = emptyList(), override val dependencies: Collection<Goal<*>> = emptyList(),
val block: suspend CoroutineScope.() -> T public val block: suspend CoroutineScope.() -> T
) : Goal<T> { ) : Goal<T> {
final override var result: Deferred<T>? = null final override var result: Deferred<T>? = null
@ -55,6 +54,7 @@ open class DynamicGoal<T>(
* Get ongoing computation or start a new one. * Get ongoing computation or start a new one.
* Does not guarantee thread safety. In case of multi-thread access, could create orphan computations. * Does not guarantee thread safety. In case of multi-thread access, could create orphan computations.
*/ */
@DFExperimental
override fun CoroutineScope.startAsync(): Deferred<T> { override fun CoroutineScope.startAsync(): Deferred<T> {
val startedDependencies = this@DynamicGoal.dependencies.map { goal -> val startedDependencies = this@DynamicGoal.dependencies.map { goal ->
goal.run { startAsync() } goal.run { startAsync() }
@ -82,7 +82,7 @@ open class DynamicGoal<T>(
/** /**
* Create a one-to-one goal based on existing goal * 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, coroutineContext: CoroutineContext = EmptyCoroutineContext,
block: suspend CoroutineScope.(T) -> R block: suspend CoroutineScope.(T) -> R
): Goal<R> = DynamicGoal(coroutineContext, listOf(this)) { ): Goal<R> = DynamicGoal(coroutineContext, listOf(this)) {
@ -92,7 +92,7 @@ fun <T, R> Goal<T>.map(
/** /**
* Create a joining goal. * Create a joining goal.
*/ */
fun <T, R> Collection<Goal<T>>.reduce( public fun <T, R> Collection<Goal<T>>.reduce(
coroutineContext: CoroutineContext = EmptyCoroutineContext, coroutineContext: CoroutineContext = EmptyCoroutineContext,
block: suspend CoroutineScope.(Collection<T>) -> R block: suspend CoroutineScope.(Collection<T>) -> R
): Goal<R> = DynamicGoal(coroutineContext, this) { ): Goal<R> = DynamicGoal(coroutineContext, this) {
@ -105,7 +105,7 @@ fun <T, R> Collection<Goal<T>>.reduce(
* @param T type of the input goal * @param T type of the input goal
* @param R type of the result 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, coroutineContext: CoroutineContext = EmptyCoroutineContext,
block: suspend CoroutineScope.(Map<K, T>) -> R block: suspend CoroutineScope.(Map<K, T>) -> R
): Goal<R> = DynamicGoal(coroutineContext, this.values) { ): Goal<R> = DynamicGoal(coroutineContext, this.values) {

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

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

@ -7,25 +7,25 @@ import hep.dataforge.names.toName
import kotlin.reflect.KClass 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; 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(); private val groupRules: MutableList<(DataNode<T>) -> List<JoinGroup<T, R>>> = ArrayList();
/** /**
* introduce grouping by value name * 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 -> groupRules += { node ->
GroupRule.byValue(tag, defaultTag).invoke(node).map { GroupRule.byValue(tag, defaultTag).invoke(node).map {
JoinGroup<T, R>(it.key, it.value).apply(action) 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 * 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 -> groupRules += { node ->
listOf( listOf(
JoinGroup<T, R>(groupName, node.filter(filter)).apply(action) 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 -> groupRules += { node ->
listOf( listOf(
JoinGroup<T, R>(groupName, node.filter(filter)).apply(action) 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 * 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 -> groupRules += { node ->
listOf(JoinGroup<T, R>(resultName, node).apply { result(f) }) 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 * The same rules as for KPipe
*/ */
class ReduceAction<T : Any, R : Any>( public class ReduceAction<T : Any, R : Any>(
val inputType: KClass<T>, public val inputType: KClass<T>,
val outputType: KClass<out R>, public val outputType: KClass<out R>,
private val action: ReduceGroupBuilder<T, R>.() -> Unit private val action: ReduceGroupBuilder<T, R>.() -> Unit
) : Action<T, R> { ) : 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 import kotlin.reflect.KClass
class FragmentRule<T : Any, R : Any>(val name: Name, var meta: MetaBuilder) { public class FragmentRule<T : Any, R : Any>(public val name: Name, public var meta: MetaBuilder) {
lateinit var result: suspend (T) -> R public lateinit var result: suspend (T) -> R
fun result(f: suspend (T) -> R) { public fun result(f: suspend (T) -> R) {
result = f; 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() 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 name the name of a fragment
* @param rule the rule to transform fragment name and meta using * @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 fragments[name.toName()] = rule
} }
} }
class SplitAction<T : Any, R : Any>( public class SplitAction<T : Any, R : Any>(
val inputType: KClass<T>, public val inputType: KClass<T>,
val outputType: KClass<out R>, public val outputType: KClass<out R>,
private val action: SplitBuilder<T, R>.() -> Unit private val action: SplitBuilder<T, R>.() -> Unit
) : Action<T, R> { ) : Action<T, R> {

@ -6,7 +6,7 @@ import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Deferred import kotlinx.coroutines.Deferred
import kotlin.reflect.KClass 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 { return object : Data<R> by this {
override val type: KClass<out R> = type 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 * 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 * 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 * 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.Node -> node.canCast(type)
is DataItem.Leaf -> data.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 * Unsafe cast of data node
*/ */
@Suppress("UNCHECKED_CAST") @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> { return object : Data<R> {
override val meta: Meta get() = this@cast.meta override val meta: Meta get() = this@cast.meta
override val dependencies: Collection<Goal<*>> get() = this@cast.dependencies 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") @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> { return object : DataNode<R> {
override val meta: Meta get() = this@cast.meta override val meta: Meta get() = this@cast.meta
override val type: KClass<out R> = type 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 * 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)) { if (!canCast(type)) {
error("$type expected, but $type received") 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 * 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 { internal actual fun <R : Any> DataNode<*>.canCast(type: KClass<out R>): Boolean {
//Not supported in js yet return this.type == type
return true
} }
actual fun <R : Any> Data<*>.canCast(type: KClass<out R>): Boolean { internal actual fun <R : Any> Data<*>.canCast(type: KClass<out R>): Boolean {
//Not supported in js yet return this.type == type
return true
} }

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

@ -3,33 +3,32 @@ package hep.dataforge.data
import kotlinx.coroutines.runBlocking import kotlinx.coroutines.runBlocking
import kotlin.reflect.KClass import kotlin.reflect.KClass
import kotlin.reflect.full.isSubclassOf import kotlin.reflect.full.isSubclassOf
import kotlin.reflect.full.isSuperclassOf
/** /**
* Block the thread and get data content * 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 * 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 = internal actual fun <R : Any> DataNode<*>.canCast(type: KClass<out R>): Boolean =
type.isSuperclassOf(type) 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) this.type.isSubclassOf(type)
/** /**
* Cast the node to given type if the cast is possible or return null * 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 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], * 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 * 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 { return when {
canCast(type) -> cast(type) canCast(type) -> cast(type)
this is TypeFilteredDataNode -> origin.filterIsInstance(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. * 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 null -> null
is DataItem.Node -> DataItem.Node(this.node.filterIsInstance(type)) is DataItem.Node -> DataItem.Node(this.node.filterIsInstance(type))
is DataItem.Leaf -> this.data.filterIsInstance(type)?.let { DataItem.Leaf(it) } 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 { plugins {
id("scientifik.mpp") id("ru.mipt.npm.mpp")
id("ru.mipt.npm.native")
} }
description = "IO module" description = "IO module"
useSerialization(sourceSet = TEST){ kscience {
cbor() 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 { kotlin {
sourceSets { 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 { plugins {
id("scientifik.jvm") id("ru.mipt.npm.jvm")
} }
description = "YAML meta IO" description = "YAML meta IO"
useSerialization{ kscience {
yaml() useSerialization {
yaml()
}
} }
dependencies { dependencies {

@ -9,20 +9,19 @@ import hep.dataforge.meta.Meta
import kotlinx.io.* import kotlinx.io.*
import kotlinx.io.text.readUtf8Line import kotlinx.io.text.readUtf8Line
import kotlinx.io.text.writeUtf8String import kotlinx.io.text.writeUtf8String
import kotlinx.serialization.toUtf8Bytes
@DFExperimental @DFExperimental
class FrontMatterEnvelopeFormat( public class FrontMatterEnvelopeFormat(
val io: IOPlugin, private val io: IOPlugin,
val meta: Meta = Meta.EMPTY private val meta: Meta = Meta.EMPTY,
) : EnvelopeFormat { ) : EnvelopeFormat {
override fun Input.readPartial(): PartialEnvelope { override fun readPartial(input: Input): PartialEnvelope {
var line: String = "" var line = ""
var offset = 0u var offset = 0u
do { do {
line = readUtf8Line() //?: error("Input does not contain front matter separator") line = input.readUtf8Line() //?: error("Input does not contain front matter separator")
offset += line.toUtf8Bytes().size.toUInt() offset += line.toByteArray().size.toUInt()
} while (!line.startsWith(SEPARATOR)) } while (!line.startsWith(SEPARATOR))
val readMetaFormat = val readMetaFormat =
@ -32,22 +31,21 @@ class FrontMatterEnvelopeFormat(
//TODO replace by preview //TODO replace by preview
val meta = Binary { val meta = Binary {
do { do {
line = readUtf8Line() line = input.readUtf8Line()
writeUtf8String(line + "\r\n") writeUtf8String(line + "\r\n")
offset += line.toUtf8Bytes().size.toUInt() offset += line.toByteArray().size.toUInt()
} while (!line.startsWith(SEPARATOR)) } while (!line.startsWith(SEPARATOR))
}.read { }.read {
readMetaFormat.run { readMetaFormat.readMeta(input)
readMeta()
}
} }
return PartialEnvelope(meta, offset, null) return PartialEnvelope(meta, offset, null)
} }
override fun Input.readObject(): Envelope { override fun readObject(input: Input): Envelope {
var line: String = "" var line = ""
do { 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)) } while (!line.startsWith(SEPARATOR))
val readMetaFormat = val readMetaFormat =
@ -56,26 +54,29 @@ class FrontMatterEnvelopeFormat(
val meta = Binary { val meta = Binary {
do { do {
writeUtf8String(readUtf8Line() + "\r\n") writeUtf8String(input.readUtf8Line() + "\r\n")
} while (!line.startsWith(SEPARATOR)) } while (!line.startsWith(SEPARATOR))
}.read { }.read {
readMetaFormat.run { readMetaFormat.readMeta(input)
readMeta()
}
} }
val bytes = readByteArray() val bytes = input.readByteArray()
val data = bytes.asBinary() val data = bytes.asBinary()
return SimpleEnvelope(meta, data) return SimpleEnvelope(meta, data)
} }
override fun Output.writeEnvelope(envelope: Envelope, metaFormatFactory: MetaFormatFactory, formatMeta: Meta) { override fun writeEnvelope(
val metaFormat = metaFormatFactory(formatMeta, io.context) output: Output,
writeRawString("$SEPARATOR\r\n") envelope: Envelope,
metaFormat.run { writeObject(envelope.meta) } metaFormatFactory: MetaFormatFactory,
writeRawString("$SEPARATOR\r\n") 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 //Printing data
envelope.data?.let { data -> envelope.data?.let { data ->
writeBinary(data) output.writeBinary(data)
} }
} }
@ -84,8 +85,8 @@ class FrontMatterEnvelopeFormat(
META_KEY put meta META_KEY put meta
} }
companion object : EnvelopeFormatFactory { public companion object : EnvelopeFormatFactory {
const val SEPARATOR = "---" public const val SEPARATOR = "---"
private val metaTypeRegex = "---(\\w*)\\s*".toRegex() private val metaTypeRegex = "---(\\w*)\\s*".toRegex()
@ -106,14 +107,18 @@ class FrontMatterEnvelopeFormat(
private val default by lazy { invoke() } private val default by lazy { invoke() }
override fun Input.readPartial(): PartialEnvelope = override fun readPartial(input: Input): PartialEnvelope =
default.run { readPartial() } default.readPartial(input)
override fun Output.writeEnvelope(envelope: Envelope, metaFormatFactory: MetaFormatFactory, formatMeta: Meta) = override fun writeEnvelope(
default.run { writeEnvelope(envelope, metaFormatFactory, formatMeta) } 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 kotlinx.io.text.writeUtf8String
import org.yaml.snakeyaml.Yaml import org.yaml.snakeyaml.Yaml
/**
* Represent meta as Yaml
*/
@DFExperimental @DFExperimental
class YamlMetaFormat(val meta: Meta) : MetaFormat { public class YamlMetaFormat(private val meta: Meta) : MetaFormat {
private val yaml = Yaml() 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)) val string = yaml.dump(meta.toMap(descriptor))
writeUtf8String(string) output.writeUtf8String(string)
} }
override fun Input.readMeta(descriptor: NodeDescriptor?): Meta { override fun readMeta(input: Input, descriptor: NodeDescriptor?): Meta {
val map: Map<String, Any?> = yaml.load(asInputStream()) val map: Map<String, Any?> = yaml.load(input.asInputStream())
return map.toMeta(descriptor) return map.toMeta(descriptor)
} }
@ -35,19 +38,19 @@ class YamlMetaFormat(val meta: Meta) : MetaFormat {
META_KEY put meta META_KEY put meta
} }
companion object : MetaFormatFactory { public companion object : MetaFormatFactory {
override fun invoke(meta: Meta, context: Context): MetaFormat = YamlMetaFormat(meta) 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 override val key: Short = 0x594d //YM
private val default = YamlMetaFormat() private val default = YamlMetaFormat()
override fun Output.writeMeta(meta: Meta, descriptor: NodeDescriptor?) = override fun writeMeta(output: Output, meta: Meta, descriptor: NodeDescriptor?): Unit =
default.run { writeMeta(meta, descriptor) } default.writeMeta(output, meta, descriptor)
override fun Input.readMeta(descriptor: NodeDescriptor?): Meta = override fun readMeta(input: kotlinx.io.Input, descriptor: NodeDescriptor?): Meta =
default.run { readMeta(descriptor) } default.readMeta(input, descriptor)
} }
} }

@ -11,14 +11,18 @@ import kotlinx.io.*
import kotlinx.io.text.readUtf8String import kotlinx.io.text.readUtf8String
import kotlinx.io.text.writeUtf8String 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 shortName: String = "bin"
override val key: Short = 0x4249//BI override val key: Short = 0x4249//BI
override fun invoke(meta: Meta, context: Context): MetaFormat = this override fun invoke(meta: Meta, context: Context): MetaFormat = this
override fun Input.readMeta(descriptor: NodeDescriptor?): Meta { override fun readMeta(input: Input, descriptor: NodeDescriptor?): Meta {
return (readMetaItem() as MetaItem.NodeItem).node return (input.readMetaItem() as MetaItem.NodeItem).node
} }
private fun Output.writeChar(char: Char) = writeByte(char.toByte()) private fun Output.writeChar(char: Char) = writeByte(char.toByte())
@ -28,7 +32,7 @@ object BinaryMetaFormat : MetaFormat, MetaFormatFactory {
writeUtf8String(str) writeUtf8String(str)
} }
fun Output.writeValue(value: Value) { public fun Output.writeValue(value: Value) {
if (value.isList()) { if (value.isList()) {
writeChar('L') writeChar('L')
writeInt(value.list.size) writeInt(value.list.size)
@ -75,17 +79,21 @@ object BinaryMetaFormat : MetaFormat, MetaFormatFactory {
} }
} }
override fun Output.writeMeta(meta: Meta, descriptor: NodeDescriptor?) { override fun writeMeta(
writeChar('M') output: kotlinx.io.Output,
writeInt(meta.items.size) meta: hep.dataforge.meta.Meta,
descriptor: hep.dataforge.meta.descriptors.NodeDescriptor?
) {
output.writeChar('M')
output.writeInt(meta.items.size)
meta.items.forEach { (key, item) -> meta.items.forEach { (key, item) ->
writeString(key.toString()) output.writeString(key.toString())
when (item) { when (item) {
is MetaItem.ValueItem -> { is MetaItem.ValueItem -> {
writeValue(item.value) output.writeValue(item.value)
} }
is MetaItem.NodeItem -> { is MetaItem.NodeItem -> {
writeObject(item.node) writeObject(output, item.node)
} }
} }
} }
@ -97,7 +105,7 @@ object BinaryMetaFormat : MetaFormat, MetaFormatFactory {
} }
@Suppress("UNCHECKED_CAST") @Suppress("UNCHECKED_CAST")
fun Input.readMetaItem(): MetaItem<MetaBuilder> { public fun Input.readMetaItem(): MetaItem<MetaBuilder> {
return when (val keyChar = readByte().toChar()) { return when (val keyChar = readByte().toChar()) {
'S' -> MetaItem.ValueItem(StringValue(readString())) 'S' -> MetaItem.ValueItem(StringValue(readString()))
'N' -> MetaItem.ValueItem(Null) '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.Meta
import hep.dataforge.meta.get import hep.dataforge.meta.get
import hep.dataforge.meta.string import hep.dataforge.meta.string
import hep.dataforge.names.Name
import hep.dataforge.names.asName import hep.dataforge.names.asName
import hep.dataforge.names.plus import hep.dataforge.names.plus
import kotlinx.io.Binary import kotlinx.io.Binary
interface Envelope { public interface Envelope {
val meta: Meta public val meta: Meta
val data: Binary? public val data: Binary?
companion object { public companion object {
/** /**
* meta keys * meta keys
*/ */
val ENVELOPE_NODE_KEY = "@envelope".asName() public val ENVELOPE_NODE_KEY: Name = "@envelope".asName()
val ENVELOPE_TYPE_KEY = ENVELOPE_NODE_KEY + "type" public val ENVELOPE_TYPE_KEY: Name = ENVELOPE_NODE_KEY + "type"
val ENVELOPE_DATA_TYPE_KEY = ENVELOPE_NODE_KEY + "dataType" public val ENVELOPE_DATA_TYPE_KEY: Name = ENVELOPE_NODE_KEY + "dataType"
val ENVELOPE_DATA_ID_KEY = ENVELOPE_NODE_KEY + "dataID" public val ENVELOPE_DATA_ID_KEY: Name = ENVELOPE_NODE_KEY + "dataID"
val ENVELOPE_DESCRIPTION_KEY = ENVELOPE_NODE_KEY + "description" public val ENVELOPE_DESCRIPTION_KEY: Name = ENVELOPE_NODE_KEY + "description"
val ENVELOPE_NAME_KEY = ENVELOPE_NODE_KEY + "name" public val ENVELOPE_NAME_KEY: Name = ENVELOPE_NODE_KEY + "name"
//const val ENVELOPE_TIME_KEY = "@envelope.time" //const val ENVELOPE_TIME_KEY = "@envelope.time"
/** /**
* Build a static envelope using provided builder * 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 * 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 * 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 * 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. * 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))) 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 * 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 meta: Laminate = Laminate(*meta, source.meta)
override val data: Binary? get() = source.data 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) * 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 { return when {
layers.isEmpty() -> this layers.isEmpty() -> this
this is ProxyEnvelope -> ProxyEnvelope(source, *layers, *this.meta.layers.toTypedArray()) this is ProxyEnvelope -> ProxyEnvelope(source, *layers, *this.meta.layers.toTypedArray())

@ -3,42 +3,44 @@ package hep.dataforge.io
import hep.dataforge.meta.* import hep.dataforge.meta.*
import kotlinx.io.* import kotlinx.io.*
class EnvelopeBuilder { public class EnvelopeBuilder : Envelope {
private val metaBuilder = MetaBuilder() 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) metaBuilder.apply(block)
} }
fun meta(meta: Meta) {
metaBuilder.update(meta)
}
/** /**
* The general purpose of the envelope * The general purpose of the envelope
*/ */
var type by metaBuilder.string(key = Envelope.ENVELOPE_TYPE_KEY) public var type: String? by metaBuilder.string(key = Envelope.ENVELOPE_TYPE_KEY)
var dataType by metaBuilder.string(key = Envelope.ENVELOPE_DATA_TYPE_KEY) public var dataType: String? by metaBuilder.string(key = Envelope.ENVELOPE_DATA_TYPE_KEY)
/** /**
* Data unique identifier to bypass identity checks * Data unique identifier to bypass identity checks
*/ */
var dataID by metaBuilder.string(key = Envelope.ENVELOPE_DATA_ID_KEY) public var dataID: String? by metaBuilder.string(key = Envelope.ENVELOPE_DATA_ID_KEY)
var description by metaBuilder.string(key = Envelope.ENVELOPE_DESCRIPTION_KEY) public var description: String? by metaBuilder.string(key = Envelope.ENVELOPE_DESCRIPTION_KEY)
var name by metaBuilder.string(key = Envelope.ENVELOPE_NAME_KEY) public var name: String? by metaBuilder.string(key = Envelope.ENVELOPE_NAME_KEY)
/** /**
* Construct a data binary from given builder * Construct a data binary from given builder
*/ */
@OptIn(ExperimentalIoApi::class) @OptIn(ExperimentalIoApi::class)
fun data(block: Output.() -> Unit) { public fun data(block: Output.() -> Unit) {
val arrayBuilder = ByteArrayOutput() val arrayBuilder = ByteArrayOutput()
arrayBuilder.block() arrayBuilder.block()
data = arrayBuilder.toByteArray().asBinary() 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 * A partially read envelope with meta, but without data
*/ */
@ExperimentalUnsignedTypes public data class PartialEnvelope(val meta: Meta, val dataOffset: UInt, val dataSize: ULong?)
data class PartialEnvelope(val meta: Meta, val dataOffset: UInt, val dataSize: ULong?)
interface EnvelopeFormat : IOFormat<Envelope> { public interface EnvelopeFormat : IOFormat<Envelope> {
val defaultMetaFormat: MetaFormatFactory get() = JsonMetaFormat 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, envelope: Envelope,
metaFormatFactory: MetaFormatFactory = defaultMetaFormat, 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() public fun EnvelopeFormat.read(input: Input): Envelope = readObject(input)
fun EnvelopeFormat.read(input: Input) = input.readObject()
@Type(ENVELOPE_FORMAT_TYPE) @Type(ENVELOPE_FORMAT_TYPE)
interface EnvelopeFormatFactory : IOFormatFactory<Envelope>, EnvelopeFormat { public interface EnvelopeFormatFactory : IOFormatFactory<Envelope>, EnvelopeFormat {
override val name: Name get() = "envelope".asName() override val name: Name get() = "envelope".asName()
override val type: KClass<out Envelope> get() = Envelope::class 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. * Try to infer specific format from input and return null if the attempt is failed.
* This method does **not** return Input into initial state. * 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 { public companion object {
const val ENVELOPE_FORMAT_TYPE = "io.format.envelope" 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, parts: EnvelopeParts,
separator: String = DEFAULT_MULTIPART_DATA_SEPARATOR separator: String = DEFAULT_MULTIPART_DATA_SEPARATOR
) { ) {
@ -69,7 +69,7 @@ fun EnvelopeBuilder.multipart(
} }
} }
fun EnvelopeBuilder.envelopes( public fun EnvelopeBuilder.envelopes(
envelopes: List<Envelope>, envelopes: List<Envelope>,
format: EnvelopeFormat = TaggedEnvelopeFormat, format: EnvelopeFormat = TaggedEnvelopeFormat,
separator: String = DEFAULT_MULTIPART_DATA_SEPARATOR separator: String = DEFAULT_MULTIPART_DATA_SEPARATOR
@ -84,11 +84,11 @@ fun EnvelopeBuilder.envelopes(
multipart(parts, separator) multipart(parts, separator)
} }
fun Envelope.parts(): EnvelopeParts { public fun Envelope.parts(): EnvelopeParts {
if (data == null) return emptyList() if (data == null) return emptyList()
//TODO add zip folder reader //TODO add zip folder reader
val parts = meta.getIndexed(PARTS_KEY).values.mapNotNull { it.node }.map { val parts = meta.getIndexed(PARTS_KEY).values.mapNotNull { it.node }.map {
PartDescriptor.wrap(it) PartDescriptor.read(it)
} }
return if (parts.isEmpty()) { return if (parts.isEmpty()) {
listOf(EnvelopePart(data!!, meta[MULTIPART_KEY].node)) listOf(EnvelopePart(data!!, meta[MULTIPART_KEY].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 * 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) val formatItem = description?.get(PART_FORMAT_KEY)
return if (formatItem != null) { return if (formatItem != null) {
val format: EnvelopeFormat = plugin.resolveEnvelopeFormat(formatItem) 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 * And interface for reading and writing objects into with IO streams
*/ */
interface IOFormat<T : Any> : MetaRepr { public interface IOFormat<T : Any> : MetaRepr {
fun Output.writeObject(obj: T) public fun writeObject(output: Output, obj: T)
fun Input.readObject(): T public fun readObject(input: Input): T
companion object{ public companion object {
val NAME_KEY = "name".asName() public val NAME_KEY: Name = "name".asName()
val META_KEY = "meta".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 * 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) 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>> { public class ListIOFormat<T : Any>(public val format: IOFormat<T>) : IOFormat<List<T>> {
override fun Output.writeObject(obj: List<T>) { override fun writeObject(output: Output, obj: List<T>) {
writeInt(obj.size) output.writeInt(obj.size)
format.run { this.format.run {
obj.forEach { obj.forEach {
writeObject(it) writeObject(output, it)
} }
} }
} }
override fun Input.readObject(): List<T> { override fun readObject(input: Input): List<T> {
val size = readInt() val size = input.readInt()
return format.run { 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() val buffer = borrow()
return try { return try {
buffer.apply(block) buffer.apply(block)
@ -77,50 +78,50 @@ fun ObjectPool<Buffer>.fill(block: Buffer.() -> Unit): Buffer {
} }
@Type(IO_FORMAT_TYPE) @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 * Explicit type for dynamic type checks
*/ */
val type: KClass<out T> public val type: KClass<out T>
override fun toMeta(): Meta = Meta { override fun toMeta(): Meta = Meta {
NAME_KEY put name.toString() NAME_KEY put name.toString()
} }
companion object { public companion object {
const val IO_FORMAT_TYPE = "io.format" 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 fun invoke(meta: Meta, context: Context): IOFormat<Double> = this
override val name: Name = "double".asName() override val name: Name = "double".asName()
override val type: KClass<out Double> get() = Double::class override val type: KClass<out Double> get() = Double::class
override fun Output.writeObject(obj: Double) { override fun writeObject(output: Output, obj: kotlin.Double) {
writeDouble(obj) 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 fun invoke(meta: Meta, context: Context): IOFormat<Value> = this
override val name: Name = "value".asName() override val name: Name = "value".asName()
override val type: KClass<out Value> get() = Value::class override val type: KClass<out Value> get() = Value::class
override fun Output.writeObject(obj: Value) { override fun writeObject(output: Output, obj: Value) {
BinaryMetaFormat.run { writeValue(obj) } BinaryMetaFormat.run { output.writeValue(obj) }
} }
override fun Input.readObject(): Value { override fun readObject(input: Input): Value {
return (BinaryMetaFormat.run { readMetaItem() } as? MetaItem.ValueItem)?.value return (BinaryMetaFormat.run { input.readMetaItem() } as? MetaItem.ValueItem)?.value
?: error("The item is not a value") ?: error("The item is not a value")
} }
} }

@ -11,14 +11,14 @@ import hep.dataforge.names.Name
import hep.dataforge.names.toName import hep.dataforge.names.toName
import kotlin.reflect.KClass import kotlin.reflect.KClass
class IOPlugin(meta: Meta) : AbstractPlugin(meta) { public class IOPlugin(meta: Meta) : AbstractPlugin(meta) {
override val tag: PluginTag get() = Companion.tag override val tag: PluginTag get() = Companion.tag
val ioFormatFactories by lazy { public val ioFormatFactories: Collection<IOFormatFactory<*>> by lazy {
context.content<IOFormatFactory<*>>(IO_FORMAT_TYPE).values 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 key = item.string ?: item.node[NAME_KEY]?.string ?: error("Format name not defined")
val name = key.toName() val name = key.toName()
return ioFormatFactories.find { it.name == name }?.let { return ioFormatFactories.find { it.name == name }?.let {
@ -29,40 +29,40 @@ class IOPlugin(meta: Meta) : AbstractPlugin(meta) {
} }
val metaFormatFactories by lazy { public val metaFormatFactories: Collection<MetaFormatFactory> by lazy {
context.content<MetaFormatFactory>(META_FORMAT_TYPE).values 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) 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) metaFormatFactories.find { it.shortName == name }?.invoke(meta)
val envelopeFormatFactories by lazy { public val envelopeFormatFactories: Collection<EnvelopeFormatFactory> by lazy {
context.content<EnvelopeFormatFactory>(ENVELOPE_FORMAT_TYPE).values 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) 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 name = item.string ?: item.node[NAME_KEY]?.string ?: error("Envelope format name not defined")
val meta = item.node[META_KEY].node ?: Meta.EMPTY val meta = item.node[META_KEY].node ?: Meta.EMPTY
return resolveEnvelopeFormat(name.toName(), meta) return resolveEnvelopeFormat(name.toName(), meta)
} }
override fun provideTop(target: String): Map<Name, Any> { override fun content(target: String): Map<Name, Any> {
return when (target) { return when (target) {
META_FORMAT_TYPE -> defaultMetaFormats.toMap() META_FORMAT_TYPE -> defaultMetaFormats.toMap()
ENVELOPE_FORMAT_TYPE -> defaultEnvelopeFormats.toMap() ENVELOPE_FORMAT_TYPE -> defaultEnvelopeFormats.toMap()
else -> super.provideTop(target) else -> super.content(target)
} }
} }
companion object : PluginFactory<IOPlugin> { public companion object : PluginFactory<IOPlugin> {
val defaultMetaFormats: List<MetaFormatFactory> = listOf(JsonMetaFormat, BinaryMetaFormat) public val defaultMetaFormats: List<MetaFormatFactory> = listOf(JsonMetaFormat, BinaryMetaFormat)
val defaultEnvelopeFormats = listOf(TaggedEnvelopeFormat, TaglessEnvelopeFormat) public val defaultEnvelopeFormats: List<EnvelopeFormatFactory> = listOf(TaggedEnvelopeFormat, TaglessEnvelopeFormat)
override val tag: PluginTag = PluginTag("io", group = PluginTag.DATAFORGE_GROUP) 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 hep.dataforge.meta.toMetaItem
import kotlinx.io.Input import kotlinx.io.Input
import kotlinx.io.Output import kotlinx.io.Output
import kotlinx.io.readByteArray import kotlinx.io.text.readUtf8String
import kotlinx.io.text.writeUtf8String import kotlinx.io.text.writeUtf8String
import kotlinx.serialization.UnstableDefault
import kotlinx.serialization.json.Json 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) 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() NAME_KEY put name.toString()
} }
override fun Input.readMeta(descriptor: NodeDescriptor?): Meta { override fun readMeta(input: Input, descriptor: NodeDescriptor?): Meta {
val str = readByteArray().decodeToString() val str = input.readUtf8String()//readByteArray().decodeToString()
val jsonElement = json.parseJson(str) val jsonElement = json.parseToJsonElement(str)
val item = jsonElement.toMetaItem(descriptor) val item = jsonElement.toMetaItem(descriptor)
return item.node ?: Meta.EMPTY return item.node ?: Meta.EMPTY
} }
companion object : MetaFormatFactory { public companion object : MetaFormatFactory {
val DEFAULT_JSON = Json { prettyPrint = true } public val DEFAULT_JSON: Json = Json { prettyPrint = true }
override fun invoke(meta: Meta, context: Context): MetaFormat = default override fun invoke(meta: Meta, context: Context): MetaFormat = default
override val shortName = "json" override val shortName: String = "json"
override val key: Short = 0x4a53//"JS" override val key: Short = 0x4a53//"JS"
private val default = JsonMetaFormat() private val default = JsonMetaFormat()
override fun Output.writeMeta(meta: Meta, descriptor: NodeDescriptor?) = override fun writeMeta(output: Output, meta: Meta, descriptor: NodeDescriptor?): Unit =
default.run { writeMeta(meta, descriptor) } default.run { writeMeta(output, meta, descriptor) }
override fun Input.readMeta(descriptor: NodeDescriptor?): Meta = override fun readMeta(input: Input, descriptor: NodeDescriptor?): Meta =
default.run { readMeta(descriptor) } default.run { readMeta(input, descriptor) }
} }
} }

@ -17,46 +17,50 @@ import kotlin.reflect.KClass
/** /**
* A format for meta serialization * A format for meta serialization
*/ */
public interface MetaFormat : IOFormat<Meta> {
interface MetaFormat : IOFormat<Meta> { override fun writeObject(output: Output, obj: Meta) {
writeMeta(output, obj, null)
override fun Output.writeObject(obj: Meta) {
writeMeta(obj, null)
} }
override fun Input.readObject(): Meta = readMeta() override fun readObject(input: Input): Meta = readMeta(input)
fun Output.writeMeta(meta: Meta, descriptor: NodeDescriptor? = null) public fun writeMeta(
fun Input.readMeta(descriptor: NodeDescriptor? = null): Meta output: Output,
meta: Meta,
descriptor: NodeDescriptor? = null,
)
public fun readMeta(input: Input, descriptor: NodeDescriptor? = null): Meta
} }
@Type(META_FORMAT_TYPE) @Type(META_FORMAT_TYPE)
interface MetaFormatFactory : IOFormatFactory<Meta>, MetaFormat { public interface MetaFormatFactory : IOFormatFactory<Meta>, MetaFormat {
val shortName: String public val shortName: String
override val name: Name get() = "meta".asName() + shortName override val name: Name get() = "meta".asName() + shortName
override val type: KClass<out Meta> get() = Meta::class 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 override operator fun invoke(meta: Meta, context: Context): MetaFormat
companion object { public companion object {
const val META_FORMAT_TYPE = "io.format.meta" public const val META_FORMAT_TYPE: String = "io.format.meta"
} }
} }
fun Meta.toString(format: MetaFormat): String = buildByteArray { public fun Meta.toString(format: MetaFormat): String = buildByteArray {
format.run { writeObject(this@toString) } format.run { writeObject(this@buildByteArray, this@toString) }
}.decodeToString() }.decodeToString()
fun Meta.toString(formatFactory: MetaFormatFactory): String = toString(formatFactory()) public fun Meta.toString(formatFactory: MetaFormatFactory): String = toString(formatFactory())
fun MetaFormat.parse(str: String): Meta { public fun MetaFormat.parse(str: String): Meta {
return ByteArrayInput(str.encodeToByteArray()).use { it.readObject() } 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 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 * 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 hep.dataforge.names.toName
import kotlinx.io.* import kotlinx.io.*
class TaggedEnvelopeFormat( /**
val io: IOPlugin, * A streaming-friendly envelope format with a short binary tag.
val version: VERSION = VERSION.DF02 * TODO add description
*/
public class TaggedEnvelopeFormat(
public val io: IOPlugin,
public val version: VERSION = VERSION.DF02,
) : EnvelopeFormat { ) : EnvelopeFormat {
// private val metaFormat = io.metaFormat(metaFormatKey) // private val metaFormat = io.metaFormat(metaFormatKey)
@ -37,18 +41,23 @@ class TaggedEnvelopeFormat(
writeRawString(END_SEQUENCE) writeRawString(END_SEQUENCE)
} }
override fun Output.writeEnvelope(envelope: Envelope, metaFormatFactory: MetaFormatFactory, formatMeta: Meta) { override fun writeEnvelope(
val metaFormat = metaFormatFactory.invoke(formatMeta, io.context) output: Output,
envelope: Envelope,
metaFormatFactory: MetaFormatFactory,
formatMeta: Meta,
) {
val metaFormat = metaFormatFactory.invoke(formatMeta, this@TaggedEnvelopeFormat.io.context)
val metaBytes = metaFormat.toBinary(envelope.meta) val metaBytes = metaFormat.toBinary(envelope.meta)
val actualSize: ULong = (envelope.data?.size ?: 0).toULong() val actualSize: ULong = (envelope.data?.size ?: 0).toULong()
val tag = Tag(metaFormatFactory.key, metaBytes.size.toUInt() + 2u, actualSize) val tag = Tag(metaFormatFactory.key, metaBytes.size.toUInt() + 2u, actualSize)
writeBinary(tag.toBinary()) output.writeBinary(tag.toBinary())
writeBinary(metaBytes) output.writeBinary(metaBytes)
writeRawString("\r\n") output.writeRawString("\r\n")
envelope.data?.let { 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 input an input to read from
* @param formats a collection of meta formats to resolve * @param formats a collection of meta formats to resolve
*/ */
override fun Input.readObject(): Envelope { override fun readObject(input: Input): Envelope {
val tag = readTag(version) val tag = input.readTag(this.version)
val metaFormat = io.resolveMetaFormat(tag.metaFormatKey) val metaFormat = io.resolveMetaFormat(tag.metaFormatKey)
?: error("Meta format with key ${tag.metaFormatKey} not found") ?: error("Meta format with key ${tag.metaFormatKey} not found")
val meta: Meta = limit(tag.metaSize.toInt()).run { val meta: Meta = metaFormat.readObject(input.limit(tag.metaSize.toInt()))
metaFormat.run {
readObject()
}
}
val data = readBinary(tag.dataSize.toInt()) val data = input.readBinary(tag.dataSize.toInt())
return SimpleEnvelope(meta, data) return SimpleEnvelope(meta, data)
} }
override fun Input.readPartial(): PartialEnvelope { override fun readPartial(input: Input): PartialEnvelope {
val tag = readTag(version) val tag = input.readTag(this.version)
val metaFormat = io.resolveMetaFormat(tag.metaFormatKey) val metaFormat = io.resolveMetaFormat(tag.metaFormatKey)
?: error("Meta format with key ${tag.metaFormatKey} not found") ?: error("Meta format with key ${tag.metaFormatKey} not found")
val meta: Meta = limit(tag.metaSize.toInt()).run { val meta: Meta = metaFormat.readObject(input.limit(tag.metaSize.toInt()))
metaFormat.run {
readObject()
}
}
return PartialEnvelope(meta, version.tagSize + tag.metaSize, tag.dataSize) return PartialEnvelope(meta, version.tagSize + tag.metaSize, tag.dataSize)
} }
@ -92,10 +94,10 @@ class TaggedEnvelopeFormat(
private data class Tag( private data class Tag(
val metaFormatKey: Short, val metaFormatKey: Short,
val metaSize: UInt, 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), DF02(20u),
DF03(24u) DF03(24u)
} }
@ -107,7 +109,7 @@ class TaggedEnvelopeFormat(
} }
} }
companion object : EnvelopeFormatFactory { public companion object : EnvelopeFormatFactory {
private const val START_SEQUENCE = "#~" private const val START_SEQUENCE = "#~"
private const val END_SEQUENCE = "~#\r\n" private const val END_SEQUENCE = "~#\r\n"
@ -158,16 +160,24 @@ class TaggedEnvelopeFormat(
private val default by lazy { invoke() } private val default by lazy { invoke() }
override fun Input.readPartial(): PartialEnvelope = override fun readPartial(input: Input): PartialEnvelope =
default.run { readPartial() } default.run { readPartial(input) }
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 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.get
import hep.dataforge.meta.isEmpty import hep.dataforge.meta.isEmpty
import hep.dataforge.meta.string import hep.dataforge.meta.string
import hep.dataforge.names.Name
import hep.dataforge.names.asName import hep.dataforge.names.asName
import kotlinx.io.* import kotlinx.io.*
import kotlinx.io.text.readUtf8Line import kotlinx.io.text.readUtf8Line
import kotlinx.io.text.writeUtf8String import kotlinx.io.text.writeUtf8String
import kotlin.collections.set import kotlin.collections.set
class TaglessEnvelopeFormat( /**
val io: IOPlugin, * A text envelope format with human-readable tag.
val meta: Meta = Meta.EMPTY * TODO add description
*/
public class TaglessEnvelopeFormat(
public val io: IOPlugin,
public val meta: Meta = Meta.EMPTY,
) : EnvelopeFormat { ) : EnvelopeFormat {
private val metaStart = meta[META_START_PROPERTY].string ?: DEFAULT_META_START private val metaStart = meta[META_START_PROPERTY].string ?: DEFAULT_META_START
@ -25,39 +30,46 @@ class TaglessEnvelopeFormat(
writeUtf8String("#? $key: $value;\r\n") writeUtf8String("#? $key: $value;\r\n")
} }
override fun Output.writeEnvelope(envelope: Envelope, metaFormatFactory: MetaFormatFactory, formatMeta: Meta) { override fun writeEnvelope(
val metaFormat = metaFormatFactory(formatMeta, io.context) output: Output,
envelope: Envelope,
metaFormatFactory: MetaFormatFactory,
formatMeta: Meta
) {
val metaFormat = metaFormatFactory(formatMeta, this.io.context)
//printing header //printing header
writeRawString(TAGLESS_ENVELOPE_HEADER + "\r\n") output.writeRawString(TAGLESS_ENVELOPE_HEADER + "\r\n")
//printing all properties //printing all properties
writeProperty(META_TYPE_PROPERTY, metaFormatFactory.shortName) output.writeProperty(META_TYPE_PROPERTY,
metaFormatFactory.shortName)
//TODO add optional metaFormat properties //TODO add optional metaFormat properties
val actualSize: Int = envelope.data?.size ?: 0 val actualSize: Int = envelope.data?.size ?: 0
writeProperty(DATA_LENGTH_PROPERTY, actualSize) output.writeProperty(DATA_LENGTH_PROPERTY, actualSize)
//Printing meta //Printing meta
if (!envelope.meta.isEmpty()) { if (!envelope.meta.isEmpty()) {
val metaBytes = metaFormat.toBinary(envelope.meta) val metaBytes = metaFormat.toBinary(envelope.meta)
writeProperty(META_LENGTH_PROPERTY, metaBytes.size + 2) output.writeProperty(META_LENGTH_PROPERTY,
writeUtf8String(metaStart + "\r\n") metaBytes.size + 2)
writeBinary(metaBytes) output.writeUtf8String(this.metaStart + "\r\n")
writeRawString("\r\n") output.writeBinary(metaBytes)
output.writeRawString("\r\n")
} }
//Printing data //Printing data
envelope.data?.let { data -> envelope.data?.let { data ->
writeUtf8String(dataStart + "\r\n") output.writeUtf8String(this.dataStart + "\r\n")
writeBinary(data) output.writeBinary(data)
} }
} }
override fun Input.readObject(): Envelope { override fun readObject(input: Input): Envelope {
var line: String var line: String
do { 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)) } while (!line.startsWith(TAGLESS_ENVELOPE_HEADER))
val properties = HashMap<String, String>() val properties = HashMap<String, String>()
@ -70,8 +82,8 @@ class TaglessEnvelopeFormat(
properties[key] = value properties[key] = value
} }
//If can't read line, return envelope without data //If can't read line, return envelope without data
if (exhausted()) return SimpleEnvelope(Meta.EMPTY, null) if (input.exhausted()) return SimpleEnvelope(Meta.EMPTY, null)
line = readUtf8Line() line = input.readUtf8Line()
} }
var meta: Meta = Meta.EMPTY var meta: Meta = Meta.EMPTY
@ -80,19 +92,15 @@ class TaglessEnvelopeFormat(
val metaFormat = properties[META_TYPE_PROPERTY]?.let { io.resolveMetaFormat(it) } ?: JsonMetaFormat val metaFormat = properties[META_TYPE_PROPERTY]?.let { io.resolveMetaFormat(it) } ?: JsonMetaFormat
val metaSize = properties[META_LENGTH_PROPERTY]?.toInt() val metaSize = properties[META_LENGTH_PROPERTY]?.toInt()
meta = if (metaSize != null) { meta = if (metaSize != null) {
limit(metaSize).run { metaFormat.readObject(input.limit(metaSize))
metaFormat.run { readObject() }
}
} else { } else {
metaFormat.run { metaFormat.readObject(input)
readObject()
}
} }
} }
do { do {
try { try {
line = readUtf8Line() line = input.readUtf8Line()
} catch (ex: EOFException) { } catch (ex: EOFException) {
//returning an Envelope without data if end of input is reached //returning an Envelope without data if end of input is reached
return SimpleEnvelope(meta, null) return SimpleEnvelope(meta, null)
@ -100,24 +108,24 @@ class TaglessEnvelopeFormat(
} while (!line.startsWith(dataStart)) } while (!line.startsWith(dataStart))
val data: Binary? = if (properties.containsKey(DATA_LENGTH_PROPERTY)) { 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()) // val bytes = ByteArray(properties[DATA_LENGTH_PROPERTY]!!.toInt())
// readByteArray(bytes) // readByteArray(bytes)
// bytes.asBinary() // bytes.asBinary()
} else { } else {
Binary { Binary {
copyTo(this) input.copyTo(this)
} }
} }
return SimpleEnvelope(meta, data) return SimpleEnvelope(meta, data)
} }
override fun Input.readPartial(): PartialEnvelope { override fun readPartial(input: Input): PartialEnvelope {
var offset = 0u var offset = 0u
var line: String var line: String
do { 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() offset += line.encodeToByteArray().size.toUInt()
} while (!line.startsWith(TAGLESS_ENVELOPE_HEADER)) } while (!line.startsWith(TAGLESS_ENVELOPE_HEADER))
val properties = HashMap<String, String>() val properties = HashMap<String, String>()
@ -131,7 +139,7 @@ class TaglessEnvelopeFormat(
properties[key] = value properties[key] = value
} }
try { try {
line = readUtf8Line() line = input.readUtf8Line()
offset += line.encodeToByteArray().size.toUInt() offset += line.encodeToByteArray().size.toUInt()
} catch (ex: EOFException) { } catch (ex: EOFException) {
return PartialEnvelope(Meta.EMPTY, offset.toUInt(), 0.toULong()) return PartialEnvelope(Meta.EMPTY, offset.toUInt(), 0.toULong())
@ -145,16 +153,14 @@ class TaglessEnvelopeFormat(
val metaSize = properties[META_LENGTH_PROPERTY]?.toInt() val metaSize = properties[META_LENGTH_PROPERTY]?.toInt()
meta = if (metaSize != null) { meta = if (metaSize != null) {
offset += metaSize.toUInt() offset += metaSize.toUInt()
limit(metaSize).run { metaFormat.readObject(input.limit(metaSize))
metaFormat.run { readObject() }
}
} else { } else {
error("Can't partially read an envelope with undefined meta size") error("Can't partially read an envelope with undefined meta size")
} }
} }
do { 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() offset += line.encodeToByteArray().size.toUInt()
//returning an Envelope without data if end of input is reached //returning an Envelope without data if end of input is reached
} while (!line.startsWith(dataStart)) } while (!line.startsWith(dataStart))
@ -168,26 +174,26 @@ class TaglessEnvelopeFormat(
META_KEY put meta 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" public const val META_TYPE_PROPERTY: String = "metaType"
const val META_LENGTH_PROPERTY = "metaLength" public const val META_LENGTH_PROPERTY: String = "metaLength"
const val DATA_LENGTH_PROPERTY = "dataLength" 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~#" public const val TAGLESS_ENVELOPE_HEADER: String = "#~DFTL~#"
const val META_START_PROPERTY = "metaSeparator" public const val META_START_PROPERTY: String = "metaSeparator"
const val DEFAULT_META_START = "#~META~#" public const val DEFAULT_META_START: String = "#~META~#"
const val DATA_START_PROPERTY = "dataSeparator" public const val DATA_START_PROPERTY: String = "dataSeparator"
const val DEFAULT_DATA_START = "#~DATA~#" 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 { override fun invoke(meta: Meta, context: Context): EnvelopeFormat {
return TaglessEnvelopeFormat(context.io, meta) return TaglessEnvelopeFormat(context.io, meta)
@ -195,14 +201,24 @@ class TaglessEnvelopeFormat(
private val default by lazy { invoke() } private val default by lazy { invoke() }
override fun Input.readPartial(): PartialEnvelope = override fun readPartial(input: Input): PartialEnvelope =
default.run { readPartial() } default.run { readPartial(input) }
override fun Output.writeEnvelope(envelope: Envelope, metaFormatFactory: MetaFormatFactory, formatMeta: Meta) = override fun writeEnvelope(
default.run { writeEnvelope(envelope, metaFormatFactory, formatMeta) } output: Output,
envelope: Envelope,
metaFormatFactory: MetaFormatFactory,
formatMeta: Meta,
): Unit = default.run {
writeEnvelope(
output,
envelope,
metaFormatFactory,
formatMeta
)
}
override fun Input.readObject(): Envelope = override fun readObject(input: Input): Envelope = default.readObject(input)
default.run { readObject() }
override fun peekFormat(io: IOPlugin, input: Input): EnvelopeFormat? { override fun peekFormat(io: IOPlugin, input: Input): EnvelopeFormat? {
return try { 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 kotlinx.io.*
import kotlin.math.min import kotlin.math.min
fun Output.writeRawString(str: String) { public fun Output.writeRawString(str: String) {
str.forEach { writeByte(it.toByte()) } str.forEach { writeByte(it.toByte()) }
} }
fun Input.readRawString(size: Int): String { public fun Input.readRawString(size: Int): String {
val array = CharArray(size) { readByte().toChar() } 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() ByteArrayOutput(expectedSize).apply(block).toByteArray()
@Suppress("FunctionName") @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() buildByteArray(expectedSize, block).asBinary()
/** /**
* View section of a [Binary] as an independent binary * 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 { init {
require(start > 0) 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 @Test
fun testTaggedFormat(){ fun testTaggedFormat(){
TaggedEnvelopeFormat.run { TaggedEnvelopeFormat.run {
val byteArray = this.toByteArray(envelope) val byteArray = writeToByteArray(envelope)
//println(byteArray.decodeToString()) //println(byteArray.decodeToString())
val res = readByteArray(byteArray) val res = readFromByteArray(byteArray)
assertEquals(envelope.meta,res.meta) assertEquals(envelope.meta,res.meta)
val double = res.data?.read { val double = res.data?.read {
readDouble() readDouble()
@ -37,9 +37,9 @@ class EnvelopeFormatTest {
@Test @Test
fun testTaglessFormat(){ fun testTaglessFormat(){
TaglessEnvelopeFormat.run { TaglessEnvelopeFormat.run {
val byteArray = toByteArray(envelope) val byteArray = writeToByteArray(envelope)
//println(byteArray.decodeToString()) //println(byteArray.decodeToString())
val res = readByteArray(byteArray) val res = readFromByteArray(byteArray)
assertEquals(envelope.meta,res.meta) assertEquals(envelope.meta,res.meta)
val double = res.data?.read { val double = res.data?.read {
readDouble() readDouble()

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

@ -1,12 +1,19 @@
package hep.dataforge.io 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.Name
import hep.dataforge.names.toName import hep.dataforge.names.toName
import kotlinx.serialization.ExperimentalSerializationApi
import kotlinx.serialization.cbor.Cbor import kotlinx.serialization.cbor.Cbor
import kotlinx.serialization.json.Json
import kotlin.test.Test import kotlin.test.Test
import kotlin.test.assertEquals import kotlin.test.assertEquals
val JSON_PRETTY: Json = Json { prettyPrint = true; useArrayPolymorphism = true }
val JSON_PLAIN: Json = Json { prettyPrint = false; useArrayPolymorphism = true }
class MetaSerializerTest { class MetaSerializerTest {
val meta = Meta { val meta = Meta {
"a" put 22 "a" put 22
@ -19,29 +26,33 @@ class MetaSerializerTest {
@Test @Test
fun testMetaSerialization() { fun testMetaSerialization() {
val string = JSON_PRETTY.stringify(MetaSerializer, meta) val string = JSON_PRETTY.encodeToString(MetaSerializer, meta)
val restored = JSON_PLAIN.parse(MetaSerializer, string) println(string)
assertEquals(restored, meta) val restored = JSON_PLAIN.decodeFromString(MetaSerializer, string)
assertEquals(meta, restored)
} }
@OptIn(ExperimentalSerializationApi::class)
@Test @Test
fun testCborSerialization() { fun testCborSerialization() {
val bytes = Cbor.dump(MetaSerializer, meta) val bytes = Cbor.encodeToByteArray(MetaSerializer, meta)
println(bytes.contentToString()) println(bytes.decodeToString())
val restored = Cbor.load(MetaSerializer, bytes) val restored = Cbor.decodeFromByteArray(MetaSerializer, bytes)
assertEquals(restored, meta) assertEquals(meta, restored)
} }
@Test @Test
fun testNameSerialization() { fun testNameSerialization() {
val name = "a.b.c".toName() val name = "a.b.c".toName()
val string = JSON_PRETTY.stringify(Name.serializer(), name) val string = JSON_PRETTY.encodeToString(Name.serializer(), name)
val restored = JSON_PLAIN.parse(Name.serializer(), string) val restored = JSON_PLAIN.decodeFromString(Name.serializer(), string)
assertEquals(restored, name) assertEquals(name, restored)
} }
@OptIn(ExperimentalSerializationApi::class)
@Test @Test
fun testMetaItemDescriptor() { fun testMetaItemDescriptor() {
val descriptor = MetaItem.serializer(MetaSerializer).descriptor.getElementDescriptor(0) 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.ByteArrayInput
import kotlinx.io.use import kotlinx.io.use
fun <T : Any> IOFormat<T>.toByteArray(obj: T): ByteArray = buildByteArray { writeObject(obj) } fun <T : Any> IOFormat<T>.writeToByteArray(obj: T): ByteArray = buildByteArray { writeObject(this, obj) }
fun <T : Any> IOFormat<T>.readByteArray(array: ByteArray): T = ByteArrayInput(array).use { it.readObject() } 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.reflect.full.isSuperclassOf
import kotlin.streams.asSequence 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 * 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) val stream = Files.newOutputStream(this, StandardOpenOption.WRITE, StandardOpenOption.CREATE_NEW)
stream.asOutput().use(block) 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] * 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( val stream = Files.newOutputStream(
this, this,
StandardOpenOption.WRITE, StandardOpenOption.APPEND, StandardOpenOption.CREATE 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] * 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( val stream = Files.newOutputStream(
this, this,
StandardOpenOption.WRITE, StandardOpenOption.TRUNCATE_EXISTING, StandardOpenOption.CREATE StandardOpenOption.WRITE, StandardOpenOption.TRUNCATE_EXISTING, StandardOpenOption.CREATE
@ -43,9 +43,9 @@ fun Path.rewrite(block: Output.() -> Unit): Unit {
stream.asOutput().use(block) stream.asOutput().use(block)
} }
fun Path.readEnvelope(format: EnvelopeFormat): Envelope { public fun Path.readEnvelope(format: EnvelopeFormat): Envelope {
val partialEnvelope: PartialEnvelope = asBinary().read { val partialEnvelope: PartialEnvelope = asBinary().read {
format.run { readPartial() } format.run { readPartial(this@read) }
} }
val offset: Int = partialEnvelope.dataOffset.toInt() val offset: Int = partialEnvelope.dataOffset.toInt()
val size: Int = partialEnvelope.dataSize?.toInt() ?: (Files.size(this).toInt() - offset) val size: Int = partialEnvelope.dataSize?.toInt() ?: (Files.size(this).toInt() - offset)
@ -58,7 +58,7 @@ fun Path.readEnvelope(format: EnvelopeFormat): Envelope {
*/ */
@Suppress("UNCHECKED_CAST") @Suppress("UNCHECKED_CAST")
@DFExperimental @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>? 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. * 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 * 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") if (!Files.exists(path)) error("Meta file $path does not exist")
val actualPath: Path = if (Files.isDirectory(path)) { 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") val metaFormat = formatOverride ?: resolveMetaFormat(extension) ?: error("Can't resolve meta format $extension")
return metaFormat.run { return metaFormat.run {
actualPath.read { 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]. * Write meta to file using [metaFormat]. If [path] is a directory, write a file with name equals name of [metaFormat].
* Like "meta.json" * Like "meta.json"
*/ */
fun IOPlugin.writeMetaFile( public fun IOPlugin.writeMetaFile(
path: Path, path: Path,
meta: Meta, meta: Meta,
metaFormat: MetaFormatFactory = JsonMetaFormat, metaFormat: MetaFormatFactory = JsonMetaFormat,
descriptor: NodeDescriptor? = null descriptor: NodeDescriptor? = null,
) { ) {
val actualPath = if (Files.isDirectory(path)) { val actualPath = if (Files.isDirectory(path)) {
path.resolve("@" + metaFormat.name.toString()) path.resolve("@" + metaFormat.name.toString())
@ -102,7 +106,7 @@ fun IOPlugin.writeMetaFile(
} }
metaFormat.run { metaFormat.run {
actualPath.write { 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 * 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. * 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 binary = path.asBinary()
val formats = envelopeFormatFactories.mapNotNull { factory -> val formats = envelopeFormatFactories.mapNotNull { factory ->
binary.read { binary.read {
@ -126,8 +130,8 @@ fun IOPlugin.peekBinaryFormat(path: Path): EnvelopeFormat? {
} }
} }
val IOPlugin.Companion.META_FILE_NAME: String get() = "@meta" public val IOPlugin.Companion.META_FILE_NAME: String get() = "@meta"
val IOPlugin.Companion.DATA_FILE_NAME: String get() = "@data" 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. * 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. * Return null otherwise.
*/ */
@DFExperimental @DFExperimental
fun IOPlugin.readEnvelopeFile( public fun IOPlugin.readEnvelopeFile(
path: Path, path: Path,
readNonEnvelopes: Boolean = false, readNonEnvelopes: Boolean = false,
formatPeeker: IOPlugin.(Path) -> EnvelopeFormat? = IOPlugin::peekBinaryFormat formatPeeker: IOPlugin.(Path) -> EnvelopeFormat? = IOPlugin::peekBinaryFormat,
): Envelope? { ): Envelope? {
if (!Files.exists(path)) return null if (!Files.exists(path)) return null
//read two-files directory //read two-files directory
if (Files.isDirectory(path)) { if (Files.isDirectory(path)) {
val metaFile = Files.list(path).asSequence() val metaFile = Files.list(path).asSequence().singleOrNull {
.singleOrNull { it.fileName.toString().startsWith(IOPlugin.META_FILE_NAME) } it.fileName.toString().startsWith(IOPlugin.META_FILE_NAME)
}
val meta = if (metaFile == null) { val meta = if (metaFile == null) {
Meta.EMPTY Meta.EMPTY
@ -182,9 +187,9 @@ fun IOPlugin.readEnvelopeFile(
/** /**
* Write a binary into file. Throws an error if file already exists * 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 { 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] * Write envelope file to given [path] using [envelopeFormat] and optional [metaFormat]
*/ */
@DFExperimental @DFExperimental
fun IOPlugin.writeEnvelopeFile( public fun IOPlugin.writeEnvelopeFile(
path: Path, path: Path,
envelope: Envelope, envelope: Envelope,
envelopeFormat: EnvelopeFormat = TaggedEnvelopeFormat, envelopeFormat: EnvelopeFormat = TaggedEnvelopeFormat,
metaFormat: MetaFormatFactory? = null metaFormat: MetaFormatFactory? = null,
) { ) {
path.rewrite { path.rewrite {
with(envelopeFormat) { envelopeFormat.writeEnvelope(this, envelope, metaFormat ?: envelopeFormat.defaultMetaFormat)
writeEnvelope(envelope, metaFormat ?: envelopeFormat.defaultMetaFormat)
}
} }
} }
@ -209,10 +212,10 @@ fun IOPlugin.writeEnvelopeFile(
* Write separate meta and data files to given directory [path] * Write separate meta and data files to given directory [path]
*/ */
@DFExperimental @DFExperimental
fun IOPlugin.writeEnvelopeDirectory( public fun IOPlugin.writeEnvelopeDirectory(
path: Path, path: Path,
envelope: Envelope, envelope: Envelope,
metaFormat: MetaFormatFactory = JsonMetaFormat metaFormat: MetaFormatFactory = JsonMetaFormat,
) { ) {
if (!Files.exists(path)) { if (!Files.exists(path)) {
Files.createDirectories(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.*
import kotlinx.io.buffer.Buffer 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) val buffer = ByteArray(size)
read(buffer) read(buffer)
return buffer.asBinary().read(block = block) 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() 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 { 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" 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.NameToken
import hep.dataforge.names.asName import hep.dataforge.names.asName
import hep.dataforge.names.plus 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 //TODO add validator to configuration
data class MetaListener( public data class MetaListener(
val owner: Any? = null, val owner: Any? = null,
val action: (name: Name, oldItem: MetaItem<*>?, newItem: MetaItem<*>?) -> Unit val action: (name: Name, oldItem: MetaItem<*>?, newItem: MetaItem<*>?) -> Unit
) )
interface ObservableMeta : Meta { public interface ObservableMeta : Meta {
fun onChange(owner: Any?, action: (name: Name, oldItem: MetaItem<*>?, newItem: MetaItem<*>?) -> Unit) public fun onChange(owner: Any?, action: (name: Name, oldItem: MetaItem<*>?, newItem: MetaItem<*>?) -> Unit)
fun removeListener(owner: Any?) public fun removeListener(owner: Any?)
} }
/** /**
* Mutable meta representing object state * Mutable meta representing object state
*/ */
@Serializable @Serializable(Config.Companion::class)
class Config : AbstractMutableMeta<Config>(), ObservableMeta { public class Config() : AbstractMutableMeta<Config>(), ObservableMeta {
private val listeners = HashSet<MetaListener>() 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>?) { override fun replaceItem(key: NameToken, oldItem: MetaItem<Config>?, newItem: MetaItem<Config>?) {
if (newItem == null) { if (newItem == null) {
_items.remove(key) children.remove(key)
if (oldItem != null && oldItem is MetaItem.NodeItem<Config>) { if (oldItem != null && oldItem is MetaItem.NodeItem<Config>) {
oldItem.node.removeListener(this) oldItem.node.removeListener(this)
} }
} else { } else {
_items[key] = newItem children[key] = newItem
if (newItem is MetaItem.NodeItem) { if (newItem is MetaItem.NodeItem) {
newItem.node.onChange(this) { name, oldChild, newChild -> newItem.node.onChange(this) { name, oldChild, newChild ->
itemChanged(key + name, oldChild, newChild) itemChanged(key + name, oldChild, newChild)
@ -68,10 +73,9 @@ class Config : AbstractMutableMeta<Config>(), ObservableMeta {
override fun empty(): Config = Config() override fun empty(): Config = Config()
@Serializer(Config::class) public companion object ConfigSerializer : KSerializer<Config> {
companion object ConfigSerializer : KSerializer<Config> {
fun empty(): Config = Config() public fun empty(): Config = Config()
override val descriptor: SerialDescriptor get() = MetaSerializer.descriptor override val descriptor: SerialDescriptor get() = MetaSerializer.descriptor
override fun deserialize(decoder: Decoder): Config { 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 -> this.items.mapValues { entry ->
val item = entry.value val item = entry.value
builder[entry.key.asName()] = when (item) { builder[entry.key.asName()] = when (item) {

@ -1,115 +1,24 @@
package hep.dataforge.meta package hep.dataforge.meta
import hep.dataforge.meta.descriptors.*
import hep.dataforge.names.Name 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.properties.ReadWriteProperty
import kotlin.reflect.KProperty
/** /**
* A container that holds a [Config] and a default item provider. * A container that holds a [Config].
* 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.
*/ */
interface Configurable : Described, MutableItemProvider { public interface Configurable {
/** /**
* Backing config * Backing config
*/ */
val config: Config public 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")
}
}
} }
/**
* Reset the property to its default value
*/
fun Configurable.resetProperty(name: Name) {
setItem(name, null)
}
fun Configurable.getItem(key: String) = getItem(key.toName()) public fun <T : Configurable> T.configure(meta: Meta): T = this.apply { config.update(meta) }
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) }
@DFBuilder @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 */ /* Node delegates */
fun Configurable.config(key: Name? = null): ReadWriteProperty<Any?, Config?> = public fun Configurable.config(key: Name? = null): ReadWriteProperty<Any?, Config?> = config.node(key)
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
}
}

@ -5,62 +5,107 @@ import hep.dataforge.names.Name
import hep.dataforge.names.asName import hep.dataforge.names.asName
import hep.dataforge.values.Value import hep.dataforge.values.Value
import kotlin.properties.ReadOnlyProperty import kotlin.properties.ReadOnlyProperty
import kotlin.reflect.KProperty
/* Meta delegates */ /* Meta delegates */
open class ItemDelegate( public typealias ItemDelegate = ReadOnlyProperty<Any?, MetaItem<*>?>
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
}
}
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 //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 * 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) 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) 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) 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) 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) 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 } 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 } 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 } 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 } 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) 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) 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) 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.JsonMeta.Companion.JSON_ARRAY_KEY
import hep.dataforge.meta.descriptors.ItemDescriptor 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.NodeDescriptor
import hep.dataforge.meta.descriptors.ValueDescriptor import hep.dataforge.meta.descriptors.ValueDescriptor
import hep.dataforge.names.NameToken import hep.dataforge.names.NameToken
@ -15,7 +16,7 @@ import kotlinx.serialization.json.*
/** /**
* @param descriptor reserved for custom serialization in future * @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()) { return if (isList()) {
JsonArray(list.map { it.toJson() }) JsonArray(list.map { it.toJson() })
} else { } else {
@ -52,7 +53,7 @@ private fun Meta.toJsonWithIndex(descriptor: NodeDescriptor?, indexValue: String
fun addElement(key: String) { fun addElement(key: String) {
val itemDescriptor = descriptor?.items?.get(key) val itemDescriptor = descriptor?.items?.get(key)
val jsonKey = key.toJsonKey(itemDescriptor) val jsonKey = key.toJsonKey(itemDescriptor)
val items: Map<String, MetaItem<*>> = getIndexed(key) val items: Map<String?, MetaItem<*>> = getIndexed(key)
when (items.size) { when (items.size) {
0 -> { 0 -> {
//do nothing //do nothing
@ -61,9 +62,9 @@ private fun Meta.toJsonWithIndex(descriptor: NodeDescriptor?, indexValue: String
elementMap[jsonKey] = items.values.first().toJsonElement(itemDescriptor, null) elementMap[jsonKey] = items.values.first().toJsonElement(itemDescriptor, null)
} }
else -> { else -> {
val array = jsonArray { val array = buildJsonArray {
items.forEach { (index, item) -> items.forEach { (index, item) ->
+item.toJsonElement(itemDescriptor, index) add(item.toJsonElement(itemDescriptor, index))
} }
} }
elementMap[jsonKey] = array elementMap[jsonKey] = array
@ -75,32 +76,31 @@ private fun Meta.toJsonWithIndex(descriptor: NodeDescriptor?, indexValue: String
if (indexValue != null) { if (indexValue != null) {
val indexKey = descriptor?.indexKey ?: NodeDescriptor.DEFAULT_INDEX_KEY val indexKey = descriptor?.indexKey ?: DEFAULT_INDEX_KEY
elementMap[indexKey] = JsonPrimitive(indexValue) elementMap[indexKey] = JsonPrimitive(indexValue)
} }
return JsonObject(elementMap) 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) { return when (this) {
JsonNull -> Null JsonNull -> Null
is JsonLiteral -> { else -> {
when (body) { if (isString) {
true -> True StringValue(content)
false -> False } else {
is Number -> NumberValue(body as Number) content.parseValue()
else -> StringValue(content)
} }
} }
} }
} }
fun JsonElement.toMetaItem(descriptor: ItemDescriptor? = null): MetaItem<JsonMeta> = when (this) { public fun JsonElement.toMetaItem(descriptor: ItemDescriptor? = null): MetaItem<JsonMeta> = when (this) {
is JsonPrimitive -> { is JsonPrimitive -> {
val value = this.toValue(descriptor as? ValueDescriptor) val value = this.toValue(descriptor as? ValueDescriptor)
MetaItem.ValueItem(value) MetaItem.ValueItem(value)
@ -122,7 +122,7 @@ fun JsonElement.toMetaItem(descriptor: ItemDescriptor? = null): MetaItem<JsonMet
MetaItem.ValueItem(value) MetaItem.ValueItem(value)
} else { } else {
//We can't return multiple items therefore we create top level node //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 * 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>> { private fun buildItems(): Map<NameToken, MetaItem<JsonMeta>> {
val map = LinkedHashMap<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) map[key] = MetaItem.ValueItem(listValue)
} else value.forEachIndexed { index, jsonElement -> } 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) 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. ?: index.toString() //In case index is non-string, the backward transformation will be broken.
val token = key.withIndex(indexValue) 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) 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 * 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]. * 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. * 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) { if (it is Laminate) {
it.layers it.layers
} else { } 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 { override val items: Map<NameToken, MetaItem<Meta>> by lazy {
layers.map { it.items.keys }.flatten().associateWith { key -> layers.map { it.items.keys }.flatten().associateWith { key ->
layers.asSequence().map { it.items[key] }.filterNotNull().let(replaceRule) layers.asSequence().map { it.items[key] }.filterNotNull().let(replaceRule)
@ -28,21 +26,21 @@ class Laminate(layers: List<Meta>) : MetaBase() {
/** /**
* Generate sealed meta using [mergeRule] * Generate sealed meta using [mergeRule]
*/ */
fun merge(): SealedMeta { public fun merge(): SealedMeta {
val items = layers.map { it.items.keys }.flatten().associateWith { key -> val items = layers.map { it.items.keys }.flatten().associateWith { key ->
layers.asSequence().map { it.items[key] }.filterNotNull().merge() layers.asSequence().map { it.items[key] }.filterNotNull().merge()
} }
return SealedMeta(items) return SealedMeta(items)
} }
companion object { public companion object {
/** /**
* The default rule which always uses the first found item in sequence alongside with its children. * The default rule which always uses the first found item in sequence alongside with its children.
* *
* TODO add picture * 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> { private fun Sequence<MetaItem<*>>.merge(): MetaItem<SealedMeta> {
return when { return when {
@ -76,14 +74,17 @@ class Laminate(layers: List<Meta>) : MetaBase() {
* The values a replaced but meta children are joined * The values a replaced but meta children are joined
* TODO add picture * 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 * Performance optimized version of get method
*/ */
fun Laminate.getFirst(name: Name): MetaItem<*>? { public fun Laminate.getFirst(name: Name): MetaItem<*>? {
layers.forEach { layer -> layers.forEach { layer ->
layer[name]?.let { return it } 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 * 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 * 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 //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.NodeItem
import hep.dataforge.meta.MetaItem.ValueItem import hep.dataforge.meta.MetaItem.ValueItem
import hep.dataforge.names.* import hep.dataforge.names.*
import hep.dataforge.values.* import hep.dataforge.values.EnumValue
import kotlinx.serialization.* 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 [ValueItem] (leaf)
* * a [NodeItem] (node) * * a [NodeItem] (node)
*/ */
@Serializable @Serializable(MetaItemSerializer::class)
sealed class MetaItem<out M : Meta> { public sealed class MetaItem<out M : Meta>() {
abstract override fun equals(other: Any?): Boolean abstract override fun equals(other: Any?): Boolean
abstract override fun hashCode(): Int abstract override fun hashCode(): Int
@Serializable @Serializable(MetaItemSerializer::class)
class ValueItem(val value: Value) : MetaItem<Nothing>() { public class ValueItem(public val value: Value) : MetaItem<Nothing>() {
override fun toString(): String = value.toString() 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 { override fun equals(other: Any?): Boolean {
return this.value == (other as? ValueItem)?.value return this.value == (other as? ValueItem)?.value
} }
@ -44,29 +37,18 @@ sealed class MetaItem<out M : Meta> {
} }
} }
@Serializable @Serializable(MetaItemSerializer::class)
class NodeItem<M : Meta>(@Serializable(MetaSerializer::class) val node: M) : MetaItem<M>() { 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 //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() 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 equals(other: Any?): Boolean = node == (other as? NodeItem<*>)?.node
override fun hashCode(): Int = node.hashCode() override fun hashCode(): Int = node.hashCode()
} }
companion object { public companion object {
fun of(arg: Any?): MetaItem<*> { public fun of(arg: Any?): MetaItem<*> {
return when (arg) { return when (arg) {
null -> ValueItem(Null) null -> ValueItem(Null)
is MetaItem<*> -> arg 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. * 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. * Meaning that two states with the same meta are equal.
*/ */
interface MetaRepr { @Serializable(MetaSerializer::class)
fun toMeta(): Meta public interface MetaRepr {
public fun toMeta(): Meta
} }
interface ItemProvider{ public fun interface ItemProvider {
fun getItem(name: Name): MetaItem<*>? 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 * * 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 * Top level items of meta tree
*/ */
val items: Map<NameToken, MetaItem<*>> public val items: Map<NameToken, MetaItem<*>>
override fun getItem(name: Name): MetaItem<*>? { override fun getItem(name: Name): MetaItem<*>? {
if (name.isEmpty()) return NodeItem(this) if (name.isEmpty()) return NodeItem(this)
return name.first()?.let { token -> return name.firstOrNull()?.let { token ->
val tail = name.cutFirst() val tail = name.cutFirst()
when (tail.length) { when (tail.length) {
0 -> items[token] 0 -> items[token]
@ -121,15 +111,15 @@ interface Meta : MetaRepr, ItemProvider {
override fun toString(): String override fun toString(): String
companion object { public companion object {
const val TYPE = "meta" public const val TYPE: String = "meta"
/** /**
* A key for single value node * 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() 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] * 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] * 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 * 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) -> return items.asSequence().flatMap { (key, item) ->
when (item) { when (item) {
is ValueItem -> sequenceOf(key.asName() to item.value) 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 * 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 { return sequence {
items.forEach { (key, item) -> items.forEach { (key, item) ->
yield(key.asName() to 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 * 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>> override val items: Map<NameToken, MetaItem<M>>
} }
/** /**
* The same as [Meta.get], but with specific node type * 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 null
} else { } else {
@Suppress("UNCHECKED_CAST", "ReplaceGetOrSet") @Suppress("UNCHECKED_CAST", "ReplaceGetOrSet")
(this as Meta).get(name) as MetaItem<M>? // Do not change (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 * 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) { override fun equals(other: Any?): Boolean = if (other is Meta) {
this.items == other.items this.items == other.items
@ -215,30 +205,33 @@ abstract class MetaBase : Meta {
override fun hashCode(): Int = items.hashCode() 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 * 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. * The meta implementation which is guaranteed to be immutable.
* *
* If the argument is possibly mutable node, it is copied on creation * If the argument is possibly mutable node, it is copied on creation
*/ */
class SealedMeta internal constructor( public class SealedMeta internal constructor(
override val items: Map<NameToken, MetaItem<SealedMeta>> override val items: Map<NameToken, MetaItem<SealedMeta>>,
) : AbstractMetaNode<SealedMeta>() ) : AbstractMetaNode<SealedMeta>()
/** /**
* Generate sealed node from [this]. If it is already sealed return it as is * 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") @Suppress("UNCHECKED_CAST")
fun MetaItem<*>.seal(): MetaItem<SealedMeta> = when (this) { public fun MetaItem<*>.seal(): MetaItem<SealedMeta> = when (this) {
is ValueItem -> this is ValueItem -> this
is NodeItem -> NodeItem(node.seal()) 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] * Unsafe methods to access values and nodes directly from [MetaItem]
*/ */
val MetaItem<*>?.value: Value? public val MetaItem<*>?.value: Value?
get() = (this as? ValueItem)?.value get() = (this as? ValueItem)?.value
?: (this?.node?.get(VALUE_KEY) as? ValueItem)?.value ?: (this?.node?.get(VALUE_KEY) as? ValueItem)?.value
val MetaItem<*>?.string get() = value?.string public val MetaItem<*>?.string: String? get() = value?.string
val MetaItem<*>?.boolean get() = value?.boolean public val MetaItem<*>?.boolean: Boolean? get() = value?.boolean
val MetaItem<*>?.number get() = value?.number public val MetaItem<*>?.number: Number? get() = value?.number
val MetaItem<*>?.double get() = number?.toDouble() public val MetaItem<*>?.double: Double? get() = number?.toDouble()
val MetaItem<*>?.float get() = number?.toFloat() public val MetaItem<*>?.float: Float? get() = number?.toFloat()
val MetaItem<*>?.int get() = number?.toInt() public val MetaItem<*>?.int: Int? get() = number?.toInt()
val MetaItem<*>?.long get() = number?.toLong() public val MetaItem<*>?.long: Long? get() = number?.toLong()
val MetaItem<*>?.short get() = number?.toShort() 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 this.value.value as E
} else { } else {
string?.let { enumValueOf<E>(it) } 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) { get() = when (this) {
null -> null null -> null
is ValueItem -> null//error("Trying to interpret value meta item as node item") is ValueItem -> null//error("Trying to interpret value meta item as node item")
is NodeItem -> node 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 * DSL builder for meta. Is not intended to store mutable state
*/ */
@DFBuilder @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 wrapNode(meta: Meta): MetaBuilder = if (meta is MetaBuilder) meta else meta.builder()
override fun empty(): MetaBuilder = MetaBuilder() override fun empty(): MetaBuilder = MetaBuilder()
infix fun String.put(item: MetaItem<*>?) { public infix fun String.put(item: MetaItem<*>?) {
set(this, item) set(this, item)
} }
infix fun String.put(value: Value?) { public infix fun String.put(value: Value?) {
set(this, value) set(this, value)
} }
infix fun String.put(string: String?) { public infix fun String.put(string: String?) {
set(this, string?.asValue()) set(this, string?.asValue())
} }
infix fun String.put(number: Number?) { public infix fun String.put(number: Number?) {
set(this, number?.asValue()) set(this, number?.asValue())
} }
infix fun String.put(boolean: Boolean?) { public infix fun String.put(boolean: Boolean?) {
set(this, boolean?.asValue()) set(this, boolean?.asValue())
} }
infix fun String.put(enum: Enum<*>) { public infix fun String.put(enum: Enum<*>) {
set(this, EnumValue(enum)) set(this, EnumValue(enum))
} }
@JvmName("putValues") @JvmName("putValues")
infix fun String.put(iterable: Iterable<Value>) { public infix fun String.put(iterable: Iterable<Value>) {
set(this, iterable.asValue()) set(this, iterable.asValue())
} }
@JvmName("putNumbers") @JvmName("putNumbers")
infix fun String.put(iterable: Iterable<Number>) { public infix fun String.put(iterable: Iterable<Number>) {
set(this, iterable.map { it.asValue() }.asValue()) set(this, iterable.map { it.asValue() }.asValue())
} }
@JvmName("putStrings") @JvmName("putStrings")
infix fun String.put(iterable: Iterable<String>) { public infix fun String.put(iterable: Iterable<String>) {
set(this, iterable.map { it.asValue() }.asValue()) set(this, iterable.map { it.asValue() }.asValue())
} }
infix fun String.put(array: DoubleArray) { public infix fun String.put(array: DoubleArray) {
set(this, array.asValue()) set(this, array.asValue())
} }
infix fun String.putValue(any: Any?) { public infix fun String.put(meta: Meta?) {
set(this, Value.of(any))
}
infix fun String.put(meta: Meta?) {
this@MetaBuilder[this] = meta this@MetaBuilder[this] = meta
} }
infix fun String.put(repr: MetaRepr?) { public infix fun String.put(repr: MetaRepr?) {
set(this, repr?.toMeta()) set(this, repr?.toMeta())
} }
@JvmName("putMetas") @JvmName("putMetas")
infix fun String.put(value: Iterable<Meta>) { public infix fun String.put(value: Iterable<Meta>) {
this@MetaBuilder[this] = value.toList() 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) this@MetaBuilder[this] = MetaBuilder().apply(metaBuilder)
} }
infix fun Name.put(value: Value?) { public infix fun Name.put(value: Value?) {
set(this, value) set(this, value)
} }
infix fun Name.put(string: String?) { public infix fun Name.put(string: String?) {
set(this, string?.asValue()) set(this, string?.asValue())
} }
infix fun Name.put(number: Number?) { public infix fun Name.put(number: Number?) {
set(this, number?.asValue()) set(this, number?.asValue())
} }
infix fun Name.put(boolean: Boolean?) { public infix fun Name.put(boolean: Boolean?) {
set(this, boolean?.asValue()) set(this, boolean?.asValue())
} }
infix fun Name.put(enum: Enum<*>) { public infix fun Name.put(enum: Enum<*>) {
set(this, EnumValue(enum)) set(this, EnumValue(enum))
} }
@JvmName("putValues") @JvmName("putValues")
infix fun Name.put(iterable: Iterable<Value>) { public infix fun Name.put(iterable: Iterable<Value>) {
set(this, iterable.asValue()) set(this, iterable.asValue())
} }
infix fun Name.put(meta: Meta?) { public infix fun Name.put(meta: Meta?) {
this@MetaBuilder[this] = meta this@MetaBuilder[this] = meta
} }
infix fun Name.put(repr: MetaRepr?) { public infix fun Name.put(repr: MetaRepr?) {
set(this, repr?.toMeta()) set(this, repr?.toMeta())
} }
@JvmName("putMetas") @JvmName("putMetas")
infix fun Name.put(value: Iterable<Meta>) { public infix fun Name.put(value: Iterable<Meta>) {
this@MetaBuilder[this] = value.toList() 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) 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 * 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 -> return MetaBuilder().also { builder ->
items.mapValues { entry -> items.mapValues { entry ->
val item = entry.value val item = entry.value
@ -140,16 +136,10 @@ fun Meta.builder(): MetaBuilder {
/** /**
* Create a deep copy of this meta and apply builder to it * Create a deep copy of this meta and apply builder to it
*/ */
fun Meta.edit(builder: MetaBuilder.() -> Unit): MetaBuilder = builder().apply(builder) public 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)
/** /**
* Build a [MetaBuilder] using given transformation * Build a [MetaBuilder] using given transformation
*/ */
@Suppress("FunctionName") @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 package hep.dataforge.meta
import hep.dataforge.names.NameToken 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.builtins.MapSerializer
import kotlinx.serialization.json.JsonInput import kotlinx.serialization.descriptors.*
import kotlinx.serialization.json.JsonObjectSerializer import kotlinx.serialization.encoding.*
import kotlinx.serialization.json.JsonOutput 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 * Serialized for meta
*/ */
@Serializer(Meta::class) public object MetaSerializer : KSerializer<Meta> {
object MetaSerializer : KSerializer<Meta> {
private val mapSerializer = MapSerializer( private val mapSerializer: KSerializer<Map<NameToken, MetaItem<Meta>>> = MapSerializer(
NameToken.serializer(), NameToken,
MetaItem.serializer(MetaSerializer) MetaItemSerializer//MetaItem.serializer(MetaSerializer)
) )
override val descriptor: SerialDescriptor get() = mapSerializer.descriptor override val descriptor: SerialDescriptor get() = mapSerializer.descriptor
override fun deserialize(decoder: Decoder): Meta { override fun deserialize(decoder: Decoder): Meta {
return if (decoder is JsonInput) { return if (decoder is JsonDecoder) {
JsonObjectSerializer.deserialize(decoder).toMeta() JsonObject.serializer().deserialize(decoder).toMeta()
} else { } else {
object : MetaBase() { object : MetaBase() {
override val items: Map<NameToken, MetaItem<*>> = mapSerializer.deserialize(decoder) override val items: Map<NameToken, MetaItem<*>> = mapSerializer.deserialize(decoder)
@ -31,8 +71,8 @@ object MetaSerializer : KSerializer<Meta> {
} }
override fun serialize(encoder: Encoder, value: Meta) { override fun serialize(encoder: Encoder, value: Meta) {
if (encoder is JsonOutput) { if (encoder is JsonEncoder) {
JsonObjectSerializer.serialize(encoder, value.toJson()) JsonObject.serializer().serialize(encoder, value.toJson())
} else { } else {
mapSerializer.serialize(encoder, value.items) 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.names.asName
import hep.dataforge.values.DoubleArrayValue import hep.dataforge.values.DoubleArrayValue
import hep.dataforge.values.Value 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.properties.ReadWriteProperty
import kotlin.reflect.KProperty import kotlin.reflect.KProperty
/* Read-write delegates */ /* Read-write delegates */
open class MutableItemDelegate( public typealias MutableItemDelegate = ReadWriteProperty<Any?, MetaItem<*>?>
override val owner: MutableItemProvider,
key: Name? = null, public fun MutableItemProvider.item(key: Name? = null): MutableItemDelegate = object : MutableItemDelegate {
default: MetaItem<*>? = null override fun getValue(thisRef: Any?, property: KProperty<*>): MetaItem<*>? {
) : ItemDelegate(owner, key, default), ReadWriteProperty<Any?, MetaItem<*>?> { return getItem(key ?: property.name.asName())
}
override fun setValue(thisRef: Any?, property: KProperty<*>, value: MetaItem<*>?) { override fun setValue(thisRef: Any?, property: KProperty<*>, value: MetaItem<*>?) {
val name = key ?: property.name.asName() val name = key ?: property.name.asName()
owner.setItem(name, value) setItem(name, value)
} }
} }
fun MutableItemProvider.item(key: Name? = null): MutableItemDelegate = /* Mutable converters */
MutableItemDelegate(this, key)
//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 * 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) 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) 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) 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) 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 } 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 } 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 } 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) 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) 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) 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) 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 } 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) } }) item(key).convert(reader = { it?.let { it.node as M } }, writer = { it?.let { MetaItem.NodeItem(it) } })
/* Number delegates */
fun <T> MutableItemProvider.item( public fun MutableItemProvider.int(key: Name? = null): ReadWriteProperty<Any?, Int?> =
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?> =
item(key).convert(MetaConverter.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) 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) 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) item(key).convert(MetaConverter.float)
/* Safe number delegates*/ /* 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 } 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 } 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 } 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 } item(key).convert(MetaConverter.float) { default }
/* /* Extra delegates for special cases */
* 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()
}
fun MutableItemProvider.stringListOrNull( public fun MutableItemProvider.stringList(
vararg strings: String, vararg default: String,
key: Name? = null key: Name? = null,
): ReadWriteProperty<Any?, List<String>?> = ): ReadWriteProperty<Any?, List<String>> = item(key).convert(
item(listOf(*strings), key) { reader = { it?.stringList ?: listOf(*default) },
it?.value?.stringList writer = { it.map { str -> str.asValue() }.asValue().asMetaItem() }
} )
fun MutableItemProvider.numberList(vararg numbers: Number, key: Name? = null): ReadWriteProperty<Any?, List<Number>> = public fun MutableItemProvider.stringList(
item(listOf(*numbers), key) { item -> key: Name? = null,
item?.value?.list?.map { it.number } ?: emptyList() ): ReadWriteProperty<Any?, List<String>?> = item(key).convert(
} reader = { it?.stringList },
writer = { it?.map { str -> str.asValue() }?.asValue()?.asMetaItem() }
)
/** public fun MutableItemProvider.numberList(
* A special delegate for double arrays vararg default: Number,
*/ key: Name? = null,
fun MutableItemProvider.doubleArray(vararg doubles: Double, key: Name? = null): ReadWriteProperty<Any?, DoubleArray> = ): ReadWriteProperty<Any?, List<Number>> = item(key).convert(
item(doubleArrayOf(*doubles), key) { reader = { it?.value?.list?.map { value -> value.number } ?: listOf(*default) },
(it.value as? DoubleArrayValue)?.value writer = { it.map { num -> num.asValue() }.asValue().asMetaItem() }
?: it?.value?.list?.map { value -> value.number.toDouble() }?.toDoubleArray() )
?: doubleArrayOf()
}
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, key: Name? = null,
writer: (T) -> Value = { Value.of(it) }, writer: (T) -> Value = { Value.of(it) },
reader: (Value) -> T reader: (Value) -> T,
): ReadWriteProperty<Any?, List<T>?> = item(key).convert(MetaConverter.valueList(writer, reader)) ): 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 package hep.dataforge.meta
import hep.dataforge.names.* import hep.dataforge.names.*
import hep.dataforge.values.Value
interface MutableItemProvider : ItemProvider { public interface MutableMeta<out M : MutableMeta<M>> : MetaNode<M>, MutableItemProvider {
fun setItem(name: Name, item: MetaItem<*>?)
}
interface MutableMeta<out M : MutableMeta<M>> : MetaNode<M>, MutableItemProvider {
override val items: Map<NameToken, MetaItem<M>> override val items: Map<NameToken, MetaItem<M>>
// fun onChange(owner: Any? = null, action: (Name, MetaItem<*>?, MetaItem<*>?) -> Unit) // fun onChange(owner: Any? = null, action: (Name, MetaItem<*>?, MetaItem<*>?) -> Unit)
// fun removeListener(owner: Any? = null) // 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. * Changes in Meta are not thread safe.
*/ */
abstract class AbstractMutableMeta<M : MutableMeta<M>> : AbstractMetaNode<M>(), MutableMeta<M> { public abstract class AbstractMutableMeta<M : MutableMeta<M>> : AbstractMetaNode<M>(), MutableMeta<M> {
protected val _items: MutableMap<NameToken, MetaItem<M>> = LinkedHashMap() protected val children: MutableMap<NameToken, MetaItem<M>> = LinkedHashMap()
override val items: Map<NameToken, MetaItem<M>> override val items: Map<NameToken, MetaItem<M>>
get() = _items get() = children
//protected abstract fun itemChanged(name: Name, oldItem: MetaItem<*>?, newItem: MetaItem<*>?) //protected abstract fun itemChanged(name: Name, oldItem: MetaItem<*>?, newItem: MetaItem<*>?)
protected open fun replaceItem(key: NameToken, oldItem: MetaItem<M>?, newItem: MetaItem<M>?) { protected open fun replaceItem(key: NameToken, oldItem: MetaItem<M>?, newItem: MetaItem<M>?) {
if (newItem == null) { if (newItem == null) {
_items.remove(key) children.remove(key)
} else { } else {
_items[key] = newItem children[key] = newItem
} }
//itemChanged(key.asName(), oldItem, newItem) //itemChanged(key.asName(), oldItem, newItem)
} }
@ -56,12 +51,12 @@ abstract class AbstractMutableMeta<M : MutableMeta<M>> : AbstractMetaNode<M>(),
when (name.length) { when (name.length) {
0 -> error("Can't setValue meta item for empty name") 0 -> error("Can't setValue meta item for empty name")
1 -> { 1 -> {
val token = name.first()!! val token = name.firstOrNull()!!
@Suppress("UNCHECKED_CAST") val oldItem: MetaItem<M>? = get(name) as? MetaItem<M> @Suppress("UNCHECKED_CAST") val oldItem: MetaItem<M>? = get(name) as? MetaItem<M>
replaceItem(token, oldItem, wrapItem(item)) replaceItem(token, oldItem, wrapItem(item))
} }
else -> { else -> {
val token = name.first()!! val token = name.firstOrNull()!!
//get existing or create new node. Query is ignored for new node //get existing or create new node. Query is ignored for new node
if (items[token] == null) { if (items[token] == null) {
replaceItem(token, null, MetaItem.NodeItem(empty())) 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: * Update existing mutable node with another node. The rules are following:
* * value replaces anything * * value replaces anything
* * node updates node and replaces anything but node * * node updates node and replaces anything but node
* * node list updates node list if number of nodes in the list is the same and replaces anything otherwise * * node list updates node list if number of nodes in the list is the same and replaces anything otherwise
*/ */
fun <M : MutableMeta<M>> M.update(meta: Meta) { public fun <M : MutableMeta<M>> M.update(meta: Meta) {
meta.items.forEach { entry -> meta.items.forEach { entry ->
when (val value = entry.value) { when (val value = entry.value) {
is MetaItem.ValueItem -> setValue(entry.key.asName(), value.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 * 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" } require(!name.isEmpty()) { "Name could not be empty for append operation" }
val newIndex = name.last()!!.index val newIndex = name.lastOrNull()!!.index
if (newIndex.isNotEmpty()) { if (newIndex != null) {
set(name, value) set(name, value)
} else { } else {
val index = (getIndexed(name).keys.mapNotNull { it.toIntOrNull() }.max() ?: -1) + 1 val index = (getIndexed(name).keys.mapNotNull { it.toIntOrNull() }.maxOrNull() ?: -1) + 1
set(name.withIndex(index.toString()), value) 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. * Apply existing node with given [builder] or create a new element with it.
*/ */
@DFExperimental @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)) { val item = when (val existingItem = get(name)) {
null -> empty().also { set(name, it) } null -> empty().also { set(name, it) }
is MetaItem.NodeItem<M> -> existingItem.node is MetaItem.NodeItem<M> -> existingItem.node

@ -3,28 +3,54 @@ package hep.dataforge.meta
import hep.dataforge.meta.descriptors.* import hep.dataforge.meta.descriptors.*
import hep.dataforge.names.Name import hep.dataforge.names.Name
import hep.dataforge.names.NameToken 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]. * 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 { public open class Scheme(
constructor(config: Config, defaultProvider: (Name) -> MetaItem<*>?) : this() { config: Config = Config(),
this.config = config internal var default: ItemProvider? = null,
this.defaultProvider = defaultProvider 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 * Set a configurable property
*/
override fun getDefaultItem(name: Name): MetaItem<*>? { override fun setItem(name: Name, item: MetaItem<*>?) {
return defaultProvider(name) ?: descriptor?.get(name)?.defaultItem() 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 if default value is unavailable.
* Values from [defaultProvider] completely replace * 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) override fun toMeta(): Laminate = Laminate(config, defaultLayer)
private inner class DefaultLayer(val path: Name) : MetaBase() { public fun isEmpty(): Boolean = config.isEmpty()
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()
}
} }
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 * A specification for simplified generation of wrappers
*/ */
open class SchemeSpec<T : Scheme>(val builder: () -> T) : public open class SchemeSpec<T : Scheme>(
Specification<T> { private val builder: (config: Config, defaultProvider: ItemProvider, descriptor: NodeDescriptor?) -> T,
override fun empty(): T = builder() ) : Specification<T>, Described {
override fun wrap(config: Config, defaultProvider: (Name) -> MetaItem<*>?): T { public constructor(emptyBuilder: () -> T) : this({ config: Config, defaultProvider: ItemProvider, descriptor: NodeDescriptor? ->
return empty().apply { emptyBuilder().apply {
this.config = config 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") @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 // * A scheme that uses [Meta] as a default layer
*/ // */
open class MetaScheme( //public open class MetaScheme(
val meta: Meta, // private val meta: Meta,
override val descriptor: NodeDescriptor? = null, // override val descriptor: NodeDescriptor? = null,
config: Config = Config() // config: Config = Config(),
) : Scheme(config, meta::get) { //) : Scheme(config, meta) {
override val defaultLayer: Meta get() = Laminate(meta, descriptor?.defaultItem().node) // override val defaultLayer: Meta get() = Laminate(meta, descriptor?.defaultItem().node)
//}
public fun Meta.asScheme(): Scheme = Scheme().apply {
config = this@asScheme.asConfig()
} }
fun Meta.asScheme() = public fun <T : MutableItemProvider> Meta.toScheme(spec: Specification<T>, block: T.() -> Unit = {}): T =
MetaScheme(this) spec.read(this).apply(block)
fun <T : Configurable> Meta.toScheme(spec: Specification<T>, block: T.() -> Unit) = spec.wrap(this).apply(block)

@ -1,67 +1,102 @@
package hep.dataforge.meta package hep.dataforge.meta
import hep.dataforge.names.Name import hep.dataforge.names.Name
import hep.dataforge.names.asName
import kotlin.jvm.JvmName 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. * Allows to apply custom configuration in a type safe way to simple untyped configuration.
* By convention [Scheme] companion should inherit this class * By convention [Scheme] companion should inherit this class
* *
*/ */
interface Specification<T : Configurable> { public interface Specification<T : MutableItemProvider> {
fun empty() = wrap() /**
* 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 * Update given configuration using given type as a builder
*/ */
fun <T : Configurable> Specification<T>.update(config: Config, action: T.() -> Unit): T = wrap(config).apply(action) public fun <T : MutableItemProvider> Specification<T>.update(meta: Meta, action: T.() -> Unit): T =
read(meta).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)
}
/** /**
* Apply specified configuration to configurable * Apply specified configuration to configurable
*/ */
fun <T : Configurable, C : Configurable, S : Specification<C>> T.configure(spec: S, action: C.() -> Unit) = public fun <T : MetaRepr, C : MutableItemProvider, S : Specification<C>> T.configure(spec: S, action: C.() -> Unit): T =
apply { spec.update(config, action) } apply { spec.update(toMeta(), action) }
/** /**
* Update configuration using given specification * 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) } apply { spec.update(config, action) }
/** /**
* Create a style based on given specification * 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) } 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( spec.wrap(
Config(), it Config(), it
) )
} }
@JvmName("configSpec") @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 * General marker for dataforge builders
*/ */
@DslMarker @DslMarker
annotation class DFBuilder public annotation class DFBuilder
@RequiresOptIn(level = RequiresOptIn.Level.WARNING) @RequiresOptIn(level = RequiresOptIn.Level.WARNING)
@Retention(AnnotationRetention.BINARY) @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 * An object which provides its descriptor
*/ */
interface Described { public interface Described {
val descriptor: ItemDescriptor? public val descriptor: ItemDescriptor?
companion object { public companion object {
const val DESCRIPTOR_NODE = "@descriptor" //public const val DESCRIPTOR_NODE: String = "@descriptor"
} }
} }

@ -1,32 +1,48 @@
package hep.dataforge.meta.descriptors package hep.dataforge.meta.descriptors
import hep.dataforge.meta.Laminate import hep.dataforge.meta.Laminate
import hep.dataforge.meta.Meta
import hep.dataforge.meta.MetaBase import hep.dataforge.meta.MetaBase
import hep.dataforge.meta.MetaItem import hep.dataforge.meta.MetaItem
import hep.dataforge.names.NameToken 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<*>> override val items: Map<NameToken, MetaItem<*>>
get() = descriptor.items.entries.associate { entry -> get() = buildMap {
NameToken(entry.key) to entry.value.defaultItem() 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. * Build a default [MetaItem] from descriptor.
*/ */
fun ItemDescriptor.defaultItem(): MetaItem<*> { public fun ItemDescriptor.defaultItem(): MetaItem<*>? {
return when (this) { return when (this) {
is ValueDescriptor -> defaultItem() is ValueDescriptor -> defaultItem()
is NodeDescriptor -> defaultItem() is NodeDescriptor -> defaultItem()

@ -4,29 +4,26 @@ import hep.dataforge.meta.*
import hep.dataforge.names.* import hep.dataforge.names.*
import hep.dataforge.values.* import hep.dataforge.values.*
/**
* A common parent for [ValueDescriptor] and [NodeDescriptor]. Describes a single [MetaItem] or a group of same-name-siblings.
*/
@DFBuilder @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 * 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 * The item description text
*
* @return
*/ */
var info: String? by config.string() public var info: String? by config.string()
/** /**
* True if the item is required * 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 * @return
*/ */
var attributes by config.node() public var attributes: Config? by config.node()
}
/** /**
* Configure attributes of the descriptor * An index field by which this node is identified in case of same name siblings construct
*/ */
fun ItemDescriptor.attributes(block: Config.() -> Unit) { public var indexKey: String by config.string(DEFAULT_INDEX_KEY)
(attributes ?: Config().also { this.attributes = it }).apply(block)
}
/** public companion object{
* Set specific attribute in the descriptor public const val DEFAULT_INDEX_KEY: String = "@index"
*/
fun ItemDescriptor.setAttribute(name: Name, value: Any?) {
attributes {
set(name, value)
} }
} }
/**
* 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 * 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 if (item == null) return !required
return when (this) { return when (this) {
is ValueDescriptor -> isAllowedValue(item.value ?: return false) is ValueDescriptor -> isAllowedValue(item.value ?: return false)
@ -73,7 +70,7 @@ fun ItemDescriptor.validateItem(item: MetaItem<*>?): Boolean {
* @author Alexander Nozik * @author Alexander Nozik
*/ */
@DFBuilder @DFBuilder
class NodeDescriptor(config: Config = Config()) : ItemDescriptor(config) { public class NodeDescriptor(config: Config = Config()) : ItemDescriptor(config) {
init { init {
config[IS_NODE_KEY] = true config[IS_NODE_KEY] = true
} }
@ -90,14 +87,12 @@ class NodeDescriptor(config: Config = Config()) : ItemDescriptor(config) {
* *
* @return * @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) public val items: Map<String, ItemDescriptor>
val items: Map<String, ItemDescriptor>
get() = config.getIndexed(ITEM_KEY).mapValues { (_, item) -> get() = config.getIndexed(ITEM_KEY).mapValues { (_, item) ->
val node = item.node ?: error("Node descriptor must be a node") val node = item.node ?: error("Node descriptor must be a node")
if (node[IS_NODE_KEY].boolean == true) { if (node[IS_NODE_KEY].boolean == true) {
@ -111,7 +106,7 @@ class NodeDescriptor(config: Config = Config()) : ItemDescriptor(config) {
* The map of children node descriptors * The map of children node descriptors
*/ */
@Suppress("UNCHECKED_CAST") @Suppress("UNCHECKED_CAST")
val nodes: Map<String, NodeDescriptor> public val nodes: Map<String, NodeDescriptor>
get() = config.getIndexed(ITEM_KEY).entries.filter { get() = config.getIndexed(ITEM_KEY).entries.filter {
it.value.node[IS_NODE_KEY].boolean == true it.value.node[IS_NODE_KEY].boolean == true
}.associate { (name, item) -> }.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 { get() = config.getIndexed(ITEM_KEY).entries.filter {
it.value.node[IS_NODE_KEY].boolean != true it.value.node[IS_NODE_KEY].boolean != true
}.associate { (name, item) -> }.associate { (name, item) ->
@ -141,7 +136,7 @@ class NodeDescriptor(config: Config = Config()) : ItemDescriptor(config) {
} }
NodeDescriptor(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 config[token] = descriptor.config
} }
fun item(name: Name, descriptor: ItemDescriptor) { public fun item(name: Name, descriptor: ItemDescriptor) {
buildNode(name.cutLast()).newItem(name.last().toString(), descriptor) 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) 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)) item(name, NodeDescriptor().apply(block))
} }
fun node(name: String, block: NodeDescriptor.() -> Unit) { public fun node(name: String, block: NodeDescriptor.() -> Unit) {
node(name.toName(), block) 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" } require(name.length >= 1) { "Name length for value descriptor must be non-empty" }
item(name, ValueDescriptor().apply(block)) item(name, ValueDescriptor().apply(block))
} }
fun value(name: String, block: ValueDescriptor.() -> Unit) { public fun value(name: String, block: ValueDescriptor.() -> Unit) {
value(name.toName(), block) value(name.toName(), block)
} }
companion object { public companion object {
val ITEM_KEY = "item".asName() internal val ITEM_KEY: Name = "item".asName()
val IS_NODE_KEY = "@isNode".asName() internal val IS_NODE_KEY: Name = "@isNode".asName()
const val DEFAULT_INDEX_KEY = "@index" public inline operator fun invoke(block: NodeDescriptor.() -> Unit): NodeDescriptor = NodeDescriptor().apply(block)
inline operator fun invoke(block: NodeDescriptor.() -> Unit) = NodeDescriptor().apply(block)
//TODO infer descriptor from spec //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 * 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 if (name.isEmpty()) return this
return when (this) { return when (this) {
is ValueDescriptor -> null // empty name already checked 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 * A descriptor for meta value
@ -213,7 +212,7 @@ operator fun ItemDescriptor.get(name: String): ItemDescriptor? = get(name.toName
* @author Alexander Nozik * @author Alexander Nozik
*/ */
@DFBuilder @DFBuilder
class ValueDescriptor(config: Config = Config()) : ItemDescriptor(config) { public class ValueDescriptor(config: Config = Config()) : ItemDescriptor(config) {
/** /**
* True if the value is required * True if the value is required
@ -227,9 +226,9 @@ class ValueDescriptor(config: Config = Config()) : ItemDescriptor(config) {
* *
* @return * @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) this.default = Value.of(v)
} }
@ -238,9 +237,9 @@ class ValueDescriptor(config: Config = Config()) : ItemDescriptor(config) {
* *
* @return * @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) this.type = listOf(*t)
} }
@ -251,7 +250,7 @@ class ValueDescriptor(config: Config = Config()) : ItemDescriptor(config) {
* @param value * @param value
* @return * @return
*/ */
fun isAllowedValue(value: Value): Boolean { public fun isAllowedValue(value: Value): Boolean {
return (type?.let { it.contains(ValueType.STRING) || it.contains(value.type) } ?: true) return (type?.let { it.contains(ValueType.STRING) || it.contains(value.type) } ?: true)
&& (allowedValues.isEmpty() || allowedValues.contains(value)) && (allowedValues.isEmpty() || allowedValues.contains(value))
} }
@ -262,7 +261,7 @@ class ValueDescriptor(config: Config = Config()) : ItemDescriptor(config) {
* *
* @return * @return
*/ */
var allowedValues: List<Value> by config.item().convert( public var allowedValues: List<Value> by config.item().convert(
reader = { reader = {
val value = it.value val value = it.value
when { when {
@ -279,7 +278,7 @@ class ValueDescriptor(config: Config = Config()) : ItemDescriptor(config) {
/** /**
* Allow given list of value and forbid others * 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) } 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 * 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 { return NodeDescriptor().apply {
config.update(other.config) config.update(other.config)
config.update(this@plus.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 * 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) -> return items.entries.associate { (token, item) ->
token.toString() to when (item) { token.toString() to when (item) {
is MetaItem.NodeItem -> item.node.toMap() 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. * All other values will be converted to values.
*/ */
@DFExperimental @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") @Suppress("UNCHECKED_CAST")
fun toItem(value: Any?): MetaItem<*> = when (value) { fun toItem(value: Any?): MetaItem<*> = when (value) {
is MetaItem<*> -> value is MetaItem<*> -> value

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