Compare commits

...

2 Commits

Author SHA1 Message Date
aa4c745819 remove unnecessary type limitations in actions 2024-02-18 18:43:33 +03:00
e850ca4145 minor refactoring 2024-02-18 18:34:26 +03:00
10 changed files with 83 additions and 31 deletions

View File

@ -21,7 +21,7 @@ internal fun MutableMap<Name, *>.removeWhatStartsWith(name: Name) {
/** /**
* An action that caches results on-demand and recalculates them on source push * An action that caches results on-demand and recalculates them on source push
*/ */
public abstract class AbstractAction<T : Any, R : Any>( public abstract class AbstractAction<T, R>(
public val outputType: KType, public val outputType: KType,
) : Action<T, R> { ) : Action<T, R> {
@ -37,21 +37,23 @@ public abstract class AbstractAction<T : Any, R : Any>(
* Update part of the data set using provided data * Update part of the data set using provided data
* *
* @param source the source data tree in case we need several data items to update * @param source the source data tree in case we need several data items to update
* @param meta the metadata used for the whole data tree
* @param updatedData an updated item
*/ */
protected open fun DataSink<R>.update( protected open fun DataSink<R>.update(
source: DataTree<T>, source: DataTree<T>,
meta: Meta, meta: Meta,
namedData: NamedData<T>, updatedData: NamedData<T>,
){ ) {
//by default regenerate the whole data set //by default regenerate the whole data set
generate(source,meta) generate(source, meta)
} }
@OptIn(DFInternal::class) @OptIn(DFInternal::class)
override fun execute( override fun execute(
dataSet: DataTree<T>, dataSet: DataTree<T>,
meta: Meta, meta: Meta,
): DataTree<R> = if(dataSet.isObservable()) { ): DataTree<R> = if (dataSet.isObservable()) {
MutableDataTree<R>(outputType, dataSet.updatesScope).apply { MutableDataTree<R>(outputType, dataSet.updatesScope).apply {
generate(dataSet, meta) generate(dataSet, meta)
dataSet.updates().onEach { dataSet.updates().onEach {
@ -64,8 +66,8 @@ public abstract class AbstractAction<T : Any, R : Any>(
close() close()
} }
} }
} else{ } else {
DataTree(outputType){ DataTree(outputType) {
generate(dataSet, meta) generate(dataSet, meta)
} }
} }

View File

@ -50,7 +50,7 @@ public class MapActionBuilder<T, R>(
} }
@PublishedApi @PublishedApi
internal class MapAction<T : Any, R : Any>( internal class MapAction<T, R>(
outputType: KType, outputType: KType,
private val block: MapActionBuilder<T, R>.() -> Unit, private val block: MapActionBuilder<T, R>.() -> Unit,
) : AbstractAction<T, R>(outputType) { ) : AbstractAction<T, R>(outputType) {
@ -86,8 +86,8 @@ internal class MapAction<T : Any, R : Any>(
data.forEach { mapOne(it.name, it.data, meta) } data.forEach { mapOne(it.name, it.data, meta) }
} }
override fun DataSink<R>.update(source: DataTree<T>, meta: Meta, namedData: NamedData<T>) { override fun DataSink<R>.update(source: DataTree<T>, meta: Meta, updatedData: NamedData<T>) {
mapOne(namedData.name, namedData.data, namedData.meta) mapOne(updatedData.name, updatedData.data, updatedData.meta)
} }
} }
@ -96,7 +96,7 @@ internal class MapAction<T : Any, R : Any>(
* A one-to-one mapping action * A one-to-one mapping action
*/ */
@DFExperimental @DFExperimental
public inline fun <T : Any, reified R : Any> Action.Companion.mapping( public inline fun <T, reified R> Action.Companion.mapping(
noinline builder: MapActionBuilder<T, R>.() -> Unit, noinline builder: MapActionBuilder<T, R>.() -> Unit,
): Action<T, R> = MapAction(typeOf<R>(), builder) ): Action<T, R> = MapAction(typeOf<R>(), builder)

View File

@ -12,7 +12,7 @@ import kotlin.reflect.KType
import kotlin.reflect.typeOf import kotlin.reflect.typeOf
public class JoinGroup<T : Any, R : Any>( public class JoinGroup<T, R>(
public var name: String, public var name: String,
internal val set: DataTree<T>, internal val set: DataTree<T>,
@PublishedApi internal var outputType: KType, @PublishedApi internal var outputType: KType,
@ -35,7 +35,7 @@ public class JoinGroup<T : Any, R : Any>(
} }
@DFBuilder @DFBuilder
public class ReduceGroupBuilder<T : Any, R : Any>( public class ReduceGroupBuilder<T, R>(
public val actionMeta: Meta, public val actionMeta: Meta,
private val outputType: KType, private val outputType: KType,
) { ) {
@ -79,7 +79,7 @@ public class ReduceGroupBuilder<T : Any, R : Any>(
} }
@PublishedApi @PublishedApi
internal class ReduceAction<T : Any, R : Any>( internal class ReduceAction<T, R>(
outputType: KType, outputType: KType,
private val action: ReduceGroupBuilder<T, R>.() -> Unit, private val action: ReduceGroupBuilder<T, R>.() -> Unit,
) : AbstractAction<T, R>(outputType) { ) : AbstractAction<T, R>(outputType) {

View File

@ -13,9 +13,9 @@ import kotlin.reflect.KType
import kotlin.reflect.typeOf import kotlin.reflect.typeOf
public class SplitBuilder<T : Any, R : Any>(public val name: Name, public val meta: Meta) { public class SplitBuilder<T, R>(public val name: Name, public val meta: Meta) {
public class FragmentRule<T : Any, R : Any>( public class FragmentRule<T, R>(
public val name: Name, public val name: Name,
public var meta: MutableMeta, public var meta: MutableMeta,
@PublishedApi internal var outputType: KType, @PublishedApi internal var outputType: KType,
@ -44,7 +44,7 @@ public class SplitBuilder<T : Any, R : Any>(public val name: Name, public val me
* Action that splits each incoming element into a number of fragments defined in builder * Action that splits each incoming element into a number of fragments defined in builder
*/ */
@PublishedApi @PublishedApi
internal class SplitAction<T : Any, R : Any>( internal class SplitAction<T, R>(
outputType: KType, outputType: KType,
private val action: SplitBuilder<T, R>.() -> Unit, private val action: SplitBuilder<T, R>.() -> Unit,
) : AbstractAction<T, R>(outputType) { ) : AbstractAction<T, R>(outputType) {
@ -77,8 +77,8 @@ internal class SplitAction<T : Any, R : Any>(
data.forEach { splitOne(it.name, it.data, meta) } data.forEach { splitOne(it.name, it.data, meta) }
} }
override fun DataSink<R>.update(source: DataTree<T>, meta: Meta, namedData: NamedData<T>) { override fun DataSink<R>.update(source: DataTree<T>, meta: Meta, updatedData: NamedData<T>) {
splitOne(namedData.name, namedData.data, namedData.meta) splitOne(updatedData.name, updatedData.data, updatedData.meta)
} }
} }

View File

@ -20,7 +20,7 @@ import space.kscience.dataforge.meta.string
import space.kscience.dataforge.misc.DFInternal import space.kscience.dataforge.misc.DFInternal
public interface GroupRule { public interface GroupRule {
public fun <T : Any> gather(set: DataTree<T>): Map<String, DataTree<T>> public fun <T> gather(set: DataTree<T>): Map<String, DataTree<T>>
public companion object { public companion object {
/** /**
@ -37,7 +37,7 @@ public interface GroupRule {
defaultTagValue: String, defaultTagValue: String,
): GroupRule = object : GroupRule { ): GroupRule = object : GroupRule {
override fun <T : Any> gather( override fun <T> gather(
set: DataTree<T>, set: DataTree<T>,
): Map<String, DataTree<T>> { ): Map<String, DataTree<T>> {
val map = HashMap<String, DataTreeBuilder<T>>() val map = HashMap<String, DataTreeBuilder<T>>()

View File

@ -10,7 +10,7 @@ public interface NamedData<out T> : Named, Data<T> {
} }
public operator fun NamedData<*>.component1(): Name = name public operator fun NamedData<*>.component1(): Name = name
public operator fun <T: Any> NamedData<T>.component2(): Data<T> = data public operator fun <T> NamedData<T>.component2(): Data<T> = data
private class NamedDataImpl<T>( private class NamedDataImpl<T>(
override val name: Name, override val name: Name,

View File

@ -19,6 +19,10 @@ public annotation class MetaBuilderMarker
public interface MutableMetaProvider : MetaProvider, MutableValueProvider { public interface MutableMetaProvider : MetaProvider, MutableValueProvider {
override fun get(name: Name): MutableMeta? override fun get(name: Name): MutableMeta?
public operator fun set(name: Name, node: Meta?) public operator fun set(name: Name, node: Meta?)
/**
* Set value with the given name. Does nothing if value is not changed.
*/
override fun setValue(name: Name, value: Value?) override fun setValue(name: Name, value: Value?)
} }
@ -48,11 +52,13 @@ public interface MutableMeta : Meta, MutableMetaProvider {
} }
override fun setValue(name: Name, value: Value?) { override fun setValue(name: Name, value: Value?) {
getOrCreate(name).value = value if (value != getValue(name)) {
getOrCreate(name).value = value
}
} }
/** /**
* Get existing node or create a new one * Get an existing node or create a new one
*/ */
public fun getOrCreate(name: Name): MutableMeta public fun getOrCreate(name: Name): MutableMeta
@ -198,10 +204,8 @@ public operator fun MutableMetaProvider.set(key: String, metas: Iterable<Meta>):
/** /**
* Update existing mutable node with another node. The rules are following: * Update the existing mutable node with another node.
* * value replaces anything * Values that are present in the current provider and are missing in [meta] are kept.
* * 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
*/ */
public fun MutableMetaProvider.update(meta: Meta) { public fun MutableMetaProvider.update(meta: Meta) {
meta.valueSequence().forEach { (name, value) -> meta.valueSequence().forEach { (name, value) ->
@ -222,7 +226,7 @@ public fun <M : MutableTypedMeta<M>> MutableTypedMeta<M>.edit(name: Name, builde
getOrCreate(name).apply(builder) getOrCreate(name).apply(builder)
/** /**
* Set a value at a given [name]. If node does not exist, create it. * Set a value at a given [name]. If a node does not exist, create it.
*/ */
public operator fun <M : MutableTypedMeta<M>> MutableTypedMeta<M>.set(name: Name, value: Value?) { public operator fun <M : MutableTypedMeta<M>> MutableTypedMeta<M>.set(name: Name, value: Value?) {
edit(name) { edit(name) {
@ -367,6 +371,21 @@ public fun MutableMeta.append(name: Name, value: Value): Unit = append(name, Met
public fun MutableMeta.append(key: String, value: Value): Unit = append(Name.parse(key), value) public fun MutableMeta.append(key: String, value: Value): Unit = append(Name.parse(key), value)
/**
* Update all items that exist in the [newMeta] and remove existing items that are missing in [newMeta].
* This produces the same result as clearing all items and updating blank meta with a [newMeta], but does not
* produce unnecessary invalidation events (if they are supported).
*/
public fun MutableMeta.reset(newMeta: Meta) {
//remove old items
(items.keys - newMeta.items.keys).forEach {
remove(it.asName())
}
newMeta.items.forEach { (token, item)->
set(token, item)
}
}
/** /**
* Create a mutable copy of this meta. The copy is created even if the Meta is already mutable * Create a mutable copy of this meta. The copy is created even if the Meta is already mutable
*/ */

View File

@ -76,6 +76,8 @@ internal class MetaBuilder(
override fun set(name: Name, node: Meta?) { override fun set(name: Name, node: Meta?) {
//skip setting if value has not changed
if(node == get(name)) return
when (name.length) { when (name.length) {
0 -> error("Can't set a meta with empty name") 0 -> error("Can't set a meta with empty name")
1 -> { 1 -> {
@ -89,7 +91,7 @@ internal class MetaBuilder(
} }
else -> { else -> {
getOrCreate(name.first().asName()).set(name.cutFirst(), node) getOrCreate(name.first().asName())[name.cutFirst()] = node
} }
} }
} }

View File

@ -61,4 +61,33 @@ class MetaTest {
assertEquals(null, indexed["8"]) assertEquals(null, indexed["8"])
assertEquals(12, indexed["12"].int) assertEquals(12, indexed["12"].int)
} }
@Test
fun reset() {
val oldMeta = MutableMeta {
"a" put {
"value" put "aValue"
}
"b" put {
"value" put "bValue"
}
"c" put {
"value" put "cValue"
}
}
val newMeta = Meta {
"a" put {
"value" put "aValue"
}
"b" put {
"value" put "bValue"
}
"d" put {
"value" put "dValue"
}
}
oldMeta.reset(newMeta)
println(oldMeta)
assertEquals(setOf("a", "b", "d"), oldMeta.items.keys.map { it.toString() }.toSet())
}
} }

View File

@ -1,5 +1,5 @@
distributionBase=GRADLE_USER_HOME distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.4-bin.zip distributionUrl=https\://services.gradle.org/distributions/gradle-8.6-bin.zip
zipStoreBase=GRADLE_USER_HOME zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists zipStorePath=wrapper/dists