Skip to content

Commit 759b3ef

Browse files
authored
Merge pull request #1482 from modelix/lionweb2
LionWeb REST API in the Model Server
2 parents 8572a71 + e07eef3 commit 759b3ef

File tree

220 files changed

+8503
-3192
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

220 files changed

+8503
-3192
lines changed
Lines changed: 19 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,12 @@
11
package org.modelix.model.sync.bulk
22

3+
import org.modelix.kotlin.utils.DelicateModelixApi
34
import org.modelix.model.api.INode
5+
import org.modelix.model.api.IReadableNode
6+
import org.modelix.model.api.getOriginalOrCurrentReference
7+
import org.modelix.model.api.getOriginalReference
8+
import org.modelix.model.api.meta.NullConcept
49
import org.modelix.model.data.NodeData
5-
import org.modelix.model.data.associateWithNotNull
610

711
/**
812
* A ModelExporter exports a node and its subtree in bulk.
@@ -14,15 +18,21 @@ expect class ModelExporter(root: INode)
1418
* This function is recursively called on the node's children.
1519
*/
1620
fun INode.asExported(): NodeData {
17-
val idKey = NodeData.idPropertyKey
21+
return asReadableNode().asExported()
22+
}
23+
24+
fun IReadableNode.asExported(): NodeData {
25+
@OptIn(DelicateModelixApi::class) // json file should contain role IDs
1826
return NodeData(
19-
id = getPropertyValue(idKey) ?: reference.serialize(),
20-
concept = getConceptReference()?.getUID(),
21-
role = roleInParent,
22-
properties = getPropertyRoles().associateWithNotNull { getPropertyValue(it) }.filterKeys { it != idKey },
23-
references = getReferenceRoles().associateWithNotNull {
24-
getReferenceTarget(it)?.getPropertyValue(idKey) ?: getReferenceTargetRef(it)?.serialize()
27+
id = getOriginalOrCurrentReference(),
28+
concept = getConceptReference().takeIf { it != NullConcept.getReference() }?.getUID(),
29+
role = getContainmentLink().getIdOrNameOrNull(),
30+
properties = getAllProperties()
31+
.filterNot { it.first.matches(NodeData.ID_PROPERTY_REF) }
32+
.associate { it.first.getIdOrName() to it.second },
33+
references = getAllReferenceTargetRefs().associate {
34+
it.first.getIdOrName() to (getReferenceTarget(it.first)?.getOriginalReference() ?: it.second.serialize())
2535
},
26-
children = allChildren.map { it.asExported() },
36+
children = getAllChildren().map { it.asExported() },
2737
)
2838
}

bulk-model-sync-mps/src/main/kotlin/org/modelix/mps/model/sync/bulk/MPSBulkSynchronizer.kt

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,6 @@ import org.modelix.model.api.INode
2525
import org.modelix.model.api.TreePointer
2626
import org.modelix.model.api.getRootNode
2727
import org.modelix.model.client2.ModelClientV2
28-
import org.modelix.model.client2.lazyLoadVersion
2928
import org.modelix.model.data.ModelData
3029
import org.modelix.model.lazy.RepositoryId
3130
import org.modelix.model.mpsadapters.MPSArea

commitlint.config.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ module.exports = {
88
"authorization",
99
"bulk-model-sync",
1010
"bulk-model-sync-gradle",
11+
"datastructures",
1112
"deps",
1213
"metamodel-export",
1314
"model-api-gen",

datastructures/build.gradle.kts

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
plugins {
2+
`maven-publish`
3+
`modelix-kotlin-multiplatform`
4+
alias(libs.plugins.kotlin.serialization)
5+
}
6+
7+
kotlin {
8+
sourceSets {
9+
commonMain {
10+
dependencies {
11+
api(project(":kotlin-utils"))
12+
api(project(":streams"))
13+
implementation(libs.kotlin.serialization.core)
14+
implementation(libs.kotlin.coroutines.core)
15+
implementation(libs.kotlincrypto.sha2)
16+
}
17+
}
18+
commonTest {
19+
dependencies {
20+
implementation(kotlin("test"))
21+
}
22+
}
23+
jvmMain {
24+
dependencies {
25+
implementation(libs.trove4j)
26+
implementation(libs.apache.commons.collections)
27+
implementation(libs.kotlin.coroutines.core)
28+
}
29+
}
30+
jsMain {
31+
dependencies {
32+
}
33+
}
34+
}
35+
}
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
package org.modelix.datastructures
2+
3+
import org.modelix.datastructures.objects.IDataTypeConfiguration
4+
import org.modelix.datastructures.objects.IObjectData
5+
import org.modelix.datastructures.objects.Object
6+
import org.modelix.streams.IStream
7+
import org.modelix.streams.IStreamExecutorProvider
8+
9+
/**
10+
* Also works as a multimap. A multimap can store multiple entries with the same key. It depends on the configuration of
11+
* the implementation whether it behaves as a multimap or not.
12+
*/
13+
interface IPersistentMap<K, V> : IStreamExecutorProvider {
14+
fun asObject(): Object<*>
15+
fun getKeyTypeConfig(): IDataTypeConfiguration<K>
16+
17+
fun putAll(entries: Iterable<Pair<K, V>>): IStream.One<IPersistentMap<K, V>>
18+
fun removeAll(keys: Iterable<K>): IStream.One<IPersistentMap<K, V>>
19+
fun removeAllEntries(entries: Iterable<Pair<K, V>>): IStream.One<IPersistentMap<K, V>>
20+
21+
fun getAllValues(keys: Iterable<K>): IStream.Many<V>
22+
fun getAllValues(): IStream.Many<V>
23+
fun getAll(keys: Iterable<K>): IStream.Many<Pair<K, V>>
24+
fun getAll(): IStream.Many<Pair<K, V>>
25+
26+
fun get(key: K): IStream.ZeroOrOne<V> = getAll(listOf(key)).filter { it.first == key }.map { it.second }.firstOrEmpty()
27+
fun put(key: K, value: V): IStream.One<IPersistentMap<K, V>> = putAll(listOf(key to value))
28+
fun remove(key: K): IStream.One<IPersistentMap<K, V>> = removeAll(listOf(key))
29+
30+
fun getChanges(oldMap: IPersistentMap<K, V>, changesOnly: Boolean): IStream.Many<MapChangeEvent<K, V>>
31+
}
32+
33+
interface IPersistentMapRootData<K, V> : IObjectData {
34+
fun createMapInstance(self: Object<IPersistentMapRootData<K, V>>): IPersistentMap<K, V>
35+
}
36+
37+
fun <K, V> Object<IPersistentMapRootData<K, V>>.createMapInstance(): IPersistentMap<K, V> {
38+
return data.createMapInstance(this)
39+
}
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
package org.modelix.datastructures
2+
3+
sealed class MapChangeEvent<K, V>
4+
data class EntryAddedEvent<K, V>(val key: K, val value: V) : MapChangeEvent<K, V>()
5+
data class EntryRemovedEvent<K, V>(val key: K, val value: V) : MapChangeEvent<K, V>()
6+
data class EntryChangedEvent<K, V>(val key: K, val oldValue: V, val newValue: V) : MapChangeEvent<K, V>()
Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
package org.modelix.datastructures
2+
3+
import org.modelix.datastructures.objects.IDataTypeConfiguration
4+
import org.modelix.datastructures.objects.IObjectData
5+
import org.modelix.datastructures.objects.IObjectGraph
6+
import org.modelix.datastructures.objects.Object
7+
import org.modelix.datastructures.objects.ObjectReference
8+
import org.modelix.streams.IStream
9+
import org.modelix.streams.IStreamExecutorProvider
10+
11+
class MapWithObjectReferenceValues<K, V : IObjectData>(
12+
val graph: IObjectGraph,
13+
val map: IPersistentMap<K, ObjectReference<V>>,
14+
) : IPersistentMap<K, V>, IStreamExecutorProvider by graph {
15+
16+
private fun IPersistentMap<K, ObjectReference<V>>.wrap() = MapWithObjectReferenceValues(graph, this)
17+
private fun IStream.One<IPersistentMap<K, ObjectReference<V>>>.wrap() = map { it.wrap() }
18+
private fun V.toRef() = graph.fromCreated(this)
19+
20+
override fun asObject(): Object<*> {
21+
return map.asObject()
22+
}
23+
24+
override fun getKeyTypeConfig(): IDataTypeConfiguration<K> {
25+
return map.getKeyTypeConfig()
26+
}
27+
28+
override fun putAll(entries: Iterable<Pair<K, V>>): IStream.One<MapWithObjectReferenceValues<K, V>> {
29+
return map.putAll(entries.map { it.first to it.second.toRef() }).wrap()
30+
}
31+
32+
override fun removeAll(keys: Iterable<K>): IStream.One<MapWithObjectReferenceValues<K, V>> {
33+
return map.removeAll(keys).wrap()
34+
}
35+
36+
override fun removeAllEntries(entries: Iterable<Pair<K, V>>): IStream.One<MapWithObjectReferenceValues<K, V>> {
37+
return map.removeAllEntries(entries.map { it.first to it.second.toRef() }).wrap()
38+
}
39+
40+
override fun getAllValues(keys: Iterable<K>): IStream.Many<V> {
41+
return map.getAllValues(keys).flatMap { it.resolve() }.map { it.data }
42+
}
43+
44+
override fun getAllValues(): IStream.Many<V> {
45+
return map.getAllValues().flatMap { it.resolve() }.map { it.data }
46+
}
47+
48+
override fun getAll(keys: Iterable<K>): IStream.Many<Pair<K, V>> {
49+
return map.getAll(keys).flatMap { entry -> entry.second.resolve().map { value -> entry.first to value.data } }
50+
}
51+
52+
override fun getAll(): IStream.Many<Pair<K, V>> {
53+
return map.getAll().flatMap { entry -> entry.second.resolve().map { value -> entry.first to value.data } }
54+
}
55+
56+
override fun put(key: K, value: V) = putAll(listOf(key to value))
57+
58+
override fun remove(key: K) = removeAll(listOf(key))
59+
60+
override fun getChanges(
61+
oldMap: IPersistentMap<K, V>,
62+
changesOnly: Boolean,
63+
): IStream.Many<MapChangeEvent<K, V>> {
64+
oldMap as MapWithObjectReferenceValues<K, V>
65+
return map.getChanges(oldMap.map, changesOnly).flatMap { event ->
66+
when (event) {
67+
is EntryAddedEvent<K, ObjectReference<V>> -> {
68+
event.value.resolveData().map { value ->
69+
EntryAddedEvent(event.key, value)
70+
}
71+
}
72+
is EntryChangedEvent<K, ObjectReference<V>> -> {
73+
event.oldValue.resolveData().zipWith(event.newValue.resolveData()) { oldValue, newValue ->
74+
EntryChangedEvent(event.key, oldValue, newValue)
75+
}
76+
}
77+
is EntryRemovedEvent<K, ObjectReference<V>> -> {
78+
event.value.resolveData().map { value ->
79+
EntryRemovedEvent(event.key, value)
80+
}
81+
}
82+
}
83+
}
84+
}
85+
}
86+
87+
fun <K, V : IObjectData> IPersistentMap<K, ObjectReference<V>>.autoResolveValues(): IPersistentMap<K, V> {
88+
return MapWithObjectReferenceValues(asObject().graph, this)
89+
}
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
package org.modelix.datastructures.btree
2+
3+
import org.modelix.datastructures.objects.IObjectGraph
4+
import org.modelix.kotlin.utils.DelicateModelixApi
5+
import org.modelix.streams.IStream
6+
7+
data class BTree<K, V>(val root: BTreeNode<K, V>) {
8+
constructor(config: BTreeConfig<K, V>) : this(BTreeNodeLeaf(config, emptyList()))
9+
10+
val graph: IObjectGraph get() = root.config.graph
11+
12+
fun validate() {
13+
graph.getStreamExecutor().query {
14+
root.validate(true)
15+
@OptIn(DelicateModelixApi::class)
16+
check(root.getEntries().toList().getSynchronous().map { it.key }.toSet().size == root.getEntries().map { it.key }.count().getSynchronous()) {
17+
"duplicate entries: $root"
18+
}
19+
check(root.getEntries().map { it.key }.toList().getSynchronous().sortedWith(root.config.keyConfiguration) == root.getEntries().map { it.key }.toList().getSynchronous()) {
20+
"not sorted: $this"
21+
}
22+
IStream.of(Unit)
23+
}
24+
}
25+
fun put(key: K, value: V): BTree<K, V> = copy(root = root.put(key, value).createRoot())
26+
fun get(key: K): V? = root.get(key)
27+
fun getAll(keys: Iterable<K>): IStream.Many<Pair<K, V>> = root.getAll(keys)
28+
fun remove(key: K): BTree<K, V> = copy(root = root.remove(key).createRoot())
29+
fun remove(key: K, value: V): BTree<K, V> = copy(root = root.remove(key).createRoot())
30+
fun getEntries(): IStream.Many<BTreeEntry<K, V>> = root.getEntries()
31+
}
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
package org.modelix.datastructures.btree
2+
3+
import org.modelix.datastructures.objects.Base64DataTypeConfiguration
4+
import org.modelix.datastructures.objects.IDataTypeConfiguration
5+
import org.modelix.datastructures.objects.IObjectGraph
6+
import org.modelix.datastructures.objects.LongDataTypeConfiguration
7+
import org.modelix.datastructures.objects.StringDataTypeConfiguration
8+
9+
data class BTreeConfig<K, V>(
10+
val minEntries: Int,
11+
val minChildren: Int,
12+
val multimap: Boolean,
13+
val keyConfiguration: IDataTypeConfiguration<K>,
14+
val valueConfiguration: IDataTypeConfiguration<V>,
15+
val graph: IObjectGraph,
16+
) {
17+
val maxEntries = 2 * minEntries
18+
val maxChildren = 2 * minChildren
19+
val nodeDeserializer = BTreeNode.Deserializer(this)
20+
val entryComparatorForInsertion: Comparator<BTreeEntry<K, V>> = if (multimap) {
21+
compareBy<BTreeEntry<K, V>, K>(keyConfiguration) { it.key }
22+
.thenBy<BTreeEntry<K, V>, V>(valueConfiguration) { it.value }
23+
} else {
24+
compareBy<BTreeEntry<K, V>, K>(keyConfiguration) { it.key }
25+
}
26+
27+
companion object {
28+
fun builder() = BTreeConfigBuilder<Nothing, Nothing>()
29+
}
30+
}
31+
32+
class BTreeConfigBuilder<K, V> {
33+
private var graph: IObjectGraph? = null
34+
private var minEntries: Int = 8
35+
private var minChildren: Int = 8
36+
private var keyConfiguration: IDataTypeConfiguration<K>? = null
37+
private var valueConfiguration: IDataTypeConfiguration<V>? = null
38+
private var multimap: Boolean = false
39+
40+
fun minEntries(value: Int) = also { minEntries = value }
41+
fun minChildren(value: Int) = also { minChildren = value }
42+
43+
fun maxEntries(value: Int) = also {
44+
require(value % 2 == 0) { "Has to be a multiple of 2: $value" }
45+
minEntries(value / 2)
46+
}
47+
48+
fun maxChildren(value: Int) = also {
49+
require(value % 2 == 0) { "Has to be a multiple of 2: $value" }
50+
minChildren(value / 2)
51+
}
52+
53+
fun graph(value: IObjectGraph) = also { graph = value }
54+
55+
fun <T> keyConfiguration(config: IDataTypeConfiguration<T>): BTreeConfigBuilder<T, V> {
56+
return (this as BTreeConfigBuilder<T, V>).also { it.keyConfiguration = config }
57+
}
58+
59+
fun <T> valueConfiguration(config: IDataTypeConfiguration<T>): BTreeConfigBuilder<K, T> {
60+
return (this as BTreeConfigBuilder<K, T>).also { it.valueConfiguration = config }
61+
}
62+
63+
fun multipleValuesPerKey() = also { multimap = true }
64+
fun singleValuePerKey() = also { multimap = false }
65+
66+
fun stringKeys() = keyConfiguration(Base64DataTypeConfiguration(StringDataTypeConfiguration()))
67+
fun stringValues() = valueConfiguration(Base64DataTypeConfiguration(StringDataTypeConfiguration()))
68+
fun longKeys() = keyConfiguration(LongDataTypeConfiguration())
69+
fun longValues() = valueConfiguration(LongDataTypeConfiguration())
70+
71+
fun build() = BTreeConfig(
72+
graph = checkNotNull(graph) { "graph not specified" },
73+
minEntries = minEntries,
74+
minChildren = minChildren,
75+
multimap = multimap,
76+
keyConfiguration = checkNotNull(keyConfiguration) { "keyConfiguration not specified" },
77+
valueConfiguration = checkNotNull(valueConfiguration) { "valueConfiguration not specified" },
78+
)
79+
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
package org.modelix.datastructures.btree
2+
3+
class BTreeEntry<K, V>(val key: K, val value: V) {
4+
override fun toString(): String {
5+
return "$key -> $value"
6+
}
7+
}

0 commit comments

Comments
 (0)