diff --git a/.github/workflows/pages.yml b/.github/workflows/pages.yml new file mode 100644 index 00000000..74c1bffd --- /dev/null +++ b/.github/workflows/pages.yml @@ -0,0 +1,33 @@ +name: Dokka publication + +on: + push: + branches: + - master + +jobs: + build: + runs-on: ubuntu-20.04 + steps: + - name: Checkout the repo + uses: actions/checkout@v2 + - name: Set up JDK 11 + uses: actions/setup-java@v1 + with: + java-version: 11 + - name: Cache gradle + uses: actions/cache@v2 + with: + path: ~/.gradle/caches + key: ubuntu-20.04-gradle-${{ hashFiles('*.gradle.kts') }} + restore-keys: | + ubuntu-20.04-gradle- + - name: Build + run: | + ./gradlew dokkaHtmlMultiModule --no-daemon --no-parallel --stacktrace + mv build/dokka/htmlMultiModule/-modules.html build/dokka/htmlMultiModule/index.html + - name: Deploy to GitHub Pages + uses: JamesIves/github-pages-deploy-action@4.1.0 + with: + branch: gh-pages + folder: build/dokka/htmlMultiModule diff --git a/build.gradle.kts b/build.gradle.kts index 27f40ae9..6fdbfd60 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -4,15 +4,11 @@ plugins { allprojects { group = "space.kscience" - version = "0.4.0-dev-2" + version = "0.4.0-dev-5" } subprojects { apply(plugin = "maven-publish") - repositories { - maven("https://dl.bintray.com/mipt-npm/kscience") - maven("https://dl.bintray.com/mipt-npm/dev") - } } readme { diff --git a/dataforge-data/src/commonMain/kotlin/space/kscience/dataforge/data/DataSet.kt b/dataforge-data/src/commonMain/kotlin/space/kscience/dataforge/data/DataSet.kt index 525d4c86..305f50f8 100644 --- a/dataforge-data/src/commonMain/kotlin/space/kscience/dataforge/data/DataSet.kt +++ b/dataforge-data/src/commonMain/kotlin/space/kscience/dataforge/data/DataSet.kt @@ -28,12 +28,11 @@ public interface DataSet { public suspend fun getData(name: Name): Data? /** - * Get a snapshot of names of children of given node. Empty if node does not exist or is a leaf. - * - * By default traverses the whole tree. Could be optimized in descendants + * Get a snapshot of names of top level children of given node. Empty if node does not exist or is a leaf. */ - public suspend fun listChildren(prefix: Name = Name.EMPTY): List = + public suspend fun listTop(prefix: Name = Name.EMPTY): List = flow().map { it.name }.filter { it.startsWith(prefix) && (it.length == prefix.length + 1) }.toList() + // By default traverses the whole tree. Could be optimized in descendants public companion object { public val META_KEY: Name = "@meta".asName() diff --git a/dataforge-data/src/commonMain/kotlin/space/kscience/dataforge/data/DataTree.kt b/dataforge-data/src/commonMain/kotlin/space/kscience/dataforge/data/DataTree.kt index 13c8afbe..cf852b17 100644 --- a/dataforge-data/src/commonMain/kotlin/space/kscience/dataforge/data/DataTree.kt +++ b/dataforge-data/src/commonMain/kotlin/space/kscience/dataforge/data/DataTree.kt @@ -43,7 +43,7 @@ public interface DataTree : DataSet { } } - override suspend fun listChildren(prefix: Name): List = + override suspend fun listTop(prefix: Name): List = getItem(prefix).tree?.items()?.keys?.map { prefix + it } ?: emptyList() override suspend fun getData(name: Name): Data? = when (name.length) { diff --git a/dataforge-meta/api/dataforge-meta.api b/dataforge-meta/api/dataforge-meta.api index b31a14c0..f5e908d2 100644 --- a/dataforge-meta/api/dataforge-meta.api +++ b/dataforge-meta/api/dataforge-meta.api @@ -433,12 +433,14 @@ public final class space/kscience/dataforge/meta/ReadOnlySpecification$DefaultIm public static fun invoke (Lspace/kscience/dataforge/meta/ReadOnlySpecification;Lkotlin/jvm/functions/Function1;)Lspace/kscience/dataforge/meta/ItemProvider; } -public class space/kscience/dataforge/meta/Scheme : space/kscience/dataforge/meta/MetaRepr, space/kscience/dataforge/meta/MutableItemProvider, space/kscience/dataforge/meta/descriptors/Described { +public class space/kscience/dataforge/meta/Scheme : space/kscience/dataforge/meta/MetaRepr, space/kscience/dataforge/meta/MutableItemProvider, space/kscience/dataforge/meta/ObservableItemProvider, space/kscience/dataforge/meta/descriptors/Described { public fun ()V public fun getDefaultLayer ()Lspace/kscience/dataforge/meta/Meta; public synthetic fun getDescriptor ()Lspace/kscience/dataforge/meta/descriptors/ItemDescriptor; public final fun getDescriptor ()Lspace/kscience/dataforge/meta/descriptors/NodeDescriptor; public fun getItem (Lspace/kscience/dataforge/names/Name;)Lspace/kscience/dataforge/meta/TypedMetaItem; + public fun onChange (Ljava/lang/Object;Lkotlin/jvm/functions/Function3;)V + public fun removeListener (Ljava/lang/Object;)V public final fun setDescriptor (Lspace/kscience/dataforge/meta/descriptors/NodeDescriptor;)V public fun setItem (Lspace/kscience/dataforge/names/Name;Lspace/kscience/dataforge/meta/TypedMetaItem;)V public fun toMeta ()Lspace/kscience/dataforge/meta/Laminate; diff --git a/dataforge-meta/src/commonMain/kotlin/space/kscience/dataforge/meta/Scheme.kt b/dataforge-meta/src/commonMain/kotlin/space/kscience/dataforge/meta/Scheme.kt index 319401ac..613161e1 100644 --- a/dataforge-meta/src/commonMain/kotlin/space/kscience/dataforge/meta/Scheme.kt +++ b/dataforge-meta/src/commonMain/kotlin/space/kscience/dataforge/meta/Scheme.kt @@ -4,19 +4,42 @@ import space.kscience.dataforge.meta.descriptors.* import space.kscience.dataforge.names.Name import space.kscience.dataforge.names.NameToken import space.kscience.dataforge.names.asName +import kotlin.jvm.Synchronized /** * 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 */ -public open class Scheme() : MutableItemProvider, Described, MetaRepr { +public open class Scheme() : MutableItemProvider, Described, MetaRepr, ObservableItemProvider { private var items: MutableItemProvider = Config() + private val listeners = HashSet() + private var default: ItemProvider? = null final override var descriptor: NodeDescriptor? = null + + /** + * Add a listener to this scheme changes. If the inner provider is observable, then listening will be delegated to it. + * Otherwise, local listeners will be created. + */ + @Synchronized + override fun onChange(owner: Any?, action: (Name, MetaItem?, MetaItem?) -> Unit) { + (items as? ObservableItemProvider)?.onChange(owner, action) + ?: run { listeners.add(ItemListener(owner, action)) } + } + + /** + * Remove all listeners belonging to given owner + */ + @Synchronized + override fun removeListener(owner: Any?) { + (items as? ObservableItemProvider)?.removeListener(owner) + ?: listeners.removeAll { it.owner === owner } + } + internal fun wrap( items: MutableItemProvider, default: ItemProvider? = null, @@ -50,13 +73,16 @@ public open class Scheme() : MutableItemProvider, Described, MetaRepr { * Set a configurable property */ override fun setItem(name: Name, item: MetaItem?) { + val oldItem = items[name] if (validateItem(name, item)) { items[name] = item + listeners.forEach { it.action(name, oldItem, item) } } else { error("Validation failed for property $name with value $item") } } + /** * Provide a default layer which returns items from [default] and falls back to descriptor * values if default value is unavailable. @@ -100,7 +126,7 @@ public fun > S.wrap( * Relocate scheme target onto given [MutableItemProvider]. Old provider does not get updates anymore. * Current state of the scheme used as a default. */ -public fun T.retarget(provider: MutableItemProvider) :T = apply { wrap(provider) } +public fun T.retarget(provider: MutableItemProvider): T = apply { wrap(provider) } /** * A shortcut to edit a [Scheme] object in-place diff --git a/settings.gradle.kts b/settings.gradle.kts index 95df91b7..c55ee461 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -7,8 +7,8 @@ pluginManagement { maven("https://dl.bintray.com/kotlin/kotlin-eap") } - val toolsVersion = "0.9.1" - val kotlinVersion = "1.4.31" + val toolsVersion = "0.9.3" + val kotlinVersion = "1.4.32" plugins { id("ru.mipt.npm.gradle.project") version toolsVersion