Skip to content

Commit e07eef3

Browse files
committed
feat(model-datastructure): introduced IModelTree.builder as a public API for creating new instances
1 parent 4915ddd commit e07eef3

File tree

12 files changed

+207
-74
lines changed

12 files changed

+207
-74
lines changed

datastructures/src/commonMain/kotlin/org/modelix/datastructures/IPersistentMap.kt

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package org.modelix.datastructures
22

33
import org.modelix.datastructures.objects.IDataTypeConfiguration
4+
import org.modelix.datastructures.objects.IObjectData
45
import org.modelix.datastructures.objects.Object
56
import org.modelix.streams.IStream
67
import org.modelix.streams.IStreamExecutorProvider
@@ -28,3 +29,11 @@ interface IPersistentMap<K, V> : IStreamExecutorProvider {
2829

2930
fun getChanges(oldMap: IPersistentMap<K, V>, changesOnly: Boolean): IStream.Many<MapChangeEvent<K, V>>
3031
}
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+
}

datastructures/src/commonMain/kotlin/org/modelix/datastructures/MapWithObjectReferenceValues.kt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,3 +83,7 @@ class MapWithObjectReferenceValues<K, V : IObjectData>(
8383
}
8484
}
8585
}
86+
87+
fun <K, V : IObjectData> IPersistentMap<K, ObjectReference<V>>.autoResolveValues(): IPersistentMap<K, V> {
88+
return MapWithObjectReferenceValues(asObject().graph, this)
89+
}

datastructures/src/commonMain/kotlin/org/modelix/datastructures/hamt/HamtNode.kt

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,31 @@
11
package org.modelix.datastructures.hamt
22

3+
import org.modelix.datastructures.IPersistentMap
4+
import org.modelix.datastructures.IPersistentMapRootData
35
import org.modelix.datastructures.MapChangeEvent
46
import org.modelix.datastructures.btree.BTree
57
import org.modelix.datastructures.btree.BTreeConfig
68
import org.modelix.datastructures.btree.BTreeNode
79
import org.modelix.datastructures.objects.IDataTypeConfiguration
8-
import org.modelix.datastructures.objects.IObjectData
910
import org.modelix.datastructures.objects.IObjectDeserializer
1011
import org.modelix.datastructures.objects.IObjectGraph
1112
import org.modelix.datastructures.objects.IObjectReferenceFactory
1213
import org.modelix.datastructures.objects.Object
1314
import org.modelix.datastructures.objects.hash
15+
import org.modelix.datastructures.objects.upcast
1416
import org.modelix.streams.IStream
1517
import kotlin.jvm.JvmName
1618

1719
/**
1820
* Implementation of a hash array mapped trie.
1921
*/
20-
sealed class HamtNode<K, V : Any> : IObjectData {
22+
sealed class HamtNode<K, V : Any> : IPersistentMapRootData<K, V> {
2123
abstract val config: Config<K, V>
2224

25+
override fun createMapInstance(self: Object<IPersistentMapRootData<K, V>>): IPersistentMap<K, V> {
26+
return HamtTree(self.upcast<HamtNode<K, V>>())
27+
}
28+
2329
protected operator fun V.compareTo(other: V): Int = config.valueConfig.compare(this, other)
2430

2531
override fun getDeserializer() = config.deserializer

datastructures/src/commonMain/kotlin/org/modelix/datastructures/patricia/PatriciaNode.kt

Lines changed: 31 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,59 +1,67 @@
11
package org.modelix.datastructures.patricia
22

3+
import org.modelix.datastructures.IPersistentMap
4+
import org.modelix.datastructures.IPersistentMapRootData
35
import org.modelix.datastructures.objects.IObjectData
46
import org.modelix.datastructures.objects.IObjectDeserializer
57
import org.modelix.datastructures.objects.IObjectGraph
68
import org.modelix.datastructures.objects.IObjectReferenceFactory
9+
import org.modelix.datastructures.objects.Object
710
import org.modelix.datastructures.objects.ObjectReference
811
import org.modelix.datastructures.objects.getHashString
12+
import org.modelix.datastructures.objects.upcast
913
import org.modelix.datastructures.serialization.SplitJoinSerializer
1014
import org.modelix.kotlin.utils.urlDecode
1115
import org.modelix.kotlin.utils.urlEncode
1216
import org.modelix.streams.IStream
1317
import org.modelix.streams.plus
1418

15-
data class PatriciaNode<V : Any>(
16-
val config: PatriciaTrieConfig<*, V>,
19+
data class PatriciaNode<K, V : Any>(
20+
val config: PatriciaTrieConfig<K, V>,
1721
val ownPrefix: String,
1822
val firstChars: String,
19-
val children: List<ObjectReference<PatriciaNode<V>>>,
23+
val children: List<ObjectReference<PatriciaNode<K, V>>>,
2024

2125
/**
2226
* [ownPrefix] is part of the key for entry that s stored in this node
2327
*/
2428
val value: V?,
25-
) : IObjectData {
26-
constructor(config: PatriciaTrieConfig<*, V>) : this(config, "", "", emptyList(), null)
29+
) : IPersistentMapRootData<K, V> {
30+
constructor(config: PatriciaTrieConfig<K, V>) : this(config, "", "", emptyList(), null)
2731

28-
constructor(config: PatriciaTrieConfig<*, V>, key: String, value: V) :
32+
constructor(config: PatriciaTrieConfig<K, V>, key: String, value: V) :
2933
this(config = config, ownPrefix = key, value = value, firstChars = "", children = emptyList())
3034

35+
override fun createMapInstance(self: Object<IPersistentMapRootData<K, V>>): IPersistentMap<K, V> {
36+
return PatriciaTrie(self.upcast<PatriciaNode<K, V>>())
37+
}
38+
3139
fun calculateDepth(): Int = (children.maxOfOrNull { it.resolveNow().data.calculateDepth() } ?: 0) + 1
3240

33-
fun withChildInserted(index: Int, firstChar: Char, child: PatriciaNode<V>): PatriciaNode<V> {
41+
fun withChildInserted(index: Int, firstChar: Char, child: PatriciaNode<K, V>): PatriciaNode<K, V> {
3442
return copy(
3543
firstChars = firstChars.take(index) + firstChar + firstChars.drop(index),
3644
children = children.take(index) + config.graph.fromCreated(child) + children.drop(index),
3745
)
3846
}
3947

40-
private fun withChildReplacedNullable(index: Int, child: PatriciaNode<V>?): PatriciaNode<V>? {
48+
private fun withChildReplacedNullable(index: Int, child: PatriciaNode<K, V>?): PatriciaNode<K, V>? {
4149
return if (child == null) withoutChild(index) else withChildReplaced(index, config.graph.fromCreated(child))
4250
}
4351

44-
private fun withChildReplaced(index: Int, child: ObjectReference<PatriciaNode<V>>): PatriciaNode<V> {
52+
private fun withChildReplaced(index: Int, child: ObjectReference<PatriciaNode<K, V>>): PatriciaNode<K, V> {
4553
if (children[index] === child) return this
4654
return copy(children = children.take(index) + child + children.drop(index + 1))
4755
}
4856

49-
fun withoutChild(index: Int): PatriciaNode<V>? {
57+
fun withoutChild(index: Int): PatriciaNode<K, V>? {
5058
return copy(
5159
firstChars = firstChars.take(index) + firstChars.drop(index + 1),
5260
children = children.take(index) + children.drop(index + 1),
5361
)
5462
}
5563

56-
fun tryMerge(): IStream.ZeroOrOne<PatriciaNode<V>> {
64+
fun tryMerge(): IStream.ZeroOrOne<PatriciaNode<K, V>> {
5765
if (value != null) return IStream.of(this)
5866
return when (children.size) {
5967
0 -> IStream.empty()
@@ -89,14 +97,14 @@ data class PatriciaNode<V : Any>(
8997
/**
9098
* Returns a new root that contains only those entries that start with the given prefix.
9199
*/
92-
fun slice(prefix: CharSequence): IStream.ZeroOrOne<PatriciaNode<V>> {
100+
fun slice(prefix: CharSequence): IStream.ZeroOrOne<PatriciaNode<K, V>> {
93101
return getSubtree(prefix).map { it.copy(ownPrefix = prefix.toString() + it.ownPrefix) }
94102
}
95103

96104
/**
97105
* Returns a subtree with all known suffixes of the provided prefix.
98106
*/
99-
fun getSubtree(prefix: CharSequence): IStream.ZeroOrOne<PatriciaNode<V>> {
107+
fun getSubtree(prefix: CharSequence): IStream.ZeroOrOne<PatriciaNode<K, V>> {
100108
if (ownPrefix.startsWith(prefix)) {
101109
return if (ownPrefix.length == prefix.length) {
102110
IStream.of(this.copy(ownPrefix = ""))
@@ -127,7 +135,7 @@ data class PatriciaNode<V : Any>(
127135
}
128136
}
129137

130-
fun put(newKey: CharSequence, newValue: V?): IStream.ZeroOrOne<PatriciaNode<V>> {
138+
fun put(newKey: CharSequence, newValue: V?): IStream.ZeroOrOne<PatriciaNode<K, V>> {
131139
val commonPrefix = newKey.commonPrefixWith(this.ownPrefix)
132140
val remainingNewKey = newKey.drop(commonPrefix.length)
133141
val remainingOwnPrefix = this.ownPrefix.drop(commonPrefix.length)
@@ -144,7 +152,7 @@ data class PatriciaNode<V : Any>(
144152
.mapNotNull { withChildReplacedNullable(index, it) }
145153
} else {
146154
val insertionIndex = (-index) - 1
147-
val newChild = PatriciaNode<V>(
155+
val newChild = PatriciaNode<K, V>(
148156
config = config,
149157
ownPrefix = remainingNewKey.toString(),
150158
value = newValue,
@@ -160,16 +168,16 @@ data class PatriciaNode<V : Any>(
160168
).flatMapZeroOrOne { it.tryMerge() }
161169
}
162170

163-
fun split(commonPrefix: CharSequence): PatriciaNode<V> {
171+
fun split(commonPrefix: CharSequence): PatriciaNode<K, V> {
164172
val remainingPrefix = this.ownPrefix.drop(commonPrefix.length)
165-
return PatriciaNode<V>(
173+
return PatriciaNode<K, V>(
166174
config = config,
167175
ownPrefix = commonPrefix.toString(),
168176
value = null,
169177
firstChars = remainingPrefix.take(1),
170178
children = listOf(
171179
config.graph.fromCreated(
172-
PatriciaNode<V>(
180+
PatriciaNode<K, V>(
173181
config = config,
174182
ownPrefix = remainingPrefix,
175183
firstChars = this.firstChars,
@@ -191,24 +199,24 @@ data class PatriciaNode<V : Any>(
191199
}
192200

193201
override fun getDeserializer(): IObjectDeserializer<*> {
194-
return Deserializer(config)
202+
return Deserializer<K, V>(config)
195203
}
196204

197205
override fun getContainmentReferences(): List<ObjectReference<IObjectData>> {
198206
return children + (value?.let { config.valueConfig.getContainmentReferences(it) } ?: emptyList())
199207
}
200208

201-
class Deserializer<V : Any>(val config: (IObjectGraph) -> PatriciaTrieConfig<*, V>) : IObjectDeserializer<PatriciaNode<V>> {
202-
constructor(config: PatriciaTrieConfig<*, V>) : this({ config })
209+
class Deserializer<K, V : Any>(val config: (IObjectGraph) -> PatriciaTrieConfig<K, V>) : IObjectDeserializer<PatriciaNode<K, V>> {
210+
constructor(config: PatriciaTrieConfig<K, V>) : this({ config })
203211
override fun deserialize(
204212
serialized: String,
205213
referenceFactory: IObjectReferenceFactory,
206-
): PatriciaNode<V> {
214+
): PatriciaNode<K, V> {
207215
val S1 = SplitJoinSerializer.SEPARATORS[0]
208216
val S2 = SplitJoinSerializer.SEPARATORS[1]
209217
val parts = serialized.split(S1, limit = 4)
210218
val config = config(referenceFactory as IObjectGraph)
211-
return PatriciaNode<V>(
219+
return PatriciaNode<K, V>(
212220
config,
213221
parts[0].urlDecode()!!,
214222
parts[1].urlDecode()!!,

datastructures/src/commonMain/kotlin/org/modelix/datastructures/patricia/PatriciaTrie.kt

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,10 +15,10 @@ import org.modelix.streams.IStreamExecutorProvider
1515
*/
1616
class PatriciaTrie<K, V : Any>(
1717
val config: PatriciaTrieConfig<K, V>,
18-
val root: Object<PatriciaNode<V>>,
18+
val root: Object<PatriciaNode<K, V>>,
1919
) : IPersistentMap<K, V>, IStreamExecutorProvider by root.graph {
2020
constructor(config: PatriciaTrieConfig<K, V>) : this(config, PatriciaNode(config).asObject(config.graph))
21-
constructor(root: Object<PatriciaNode<V>>) : this(root.data.config as PatriciaTrieConfig<K, V>, root)
21+
constructor(root: Object<PatriciaNode<K, V>>) : this(root.data.config as PatriciaTrieConfig<K, V>, root)
2222

2323
override fun asObject(): Object<*> {
2424
return root
@@ -32,7 +32,7 @@ class PatriciaTrie<K, V : Any>(
3232
return config.keyConfig.serialize(key)
3333
}
3434

35-
private fun withNewRoot(newRoot: PatriciaNode<V>?): PatriciaTrie<K, V> {
35+
private fun withNewRoot(newRoot: PatriciaNode<K, V>?): PatriciaTrie<K, V> {
3636
return PatriciaTrie(config, (newRoot ?: PatriciaNode(config)).asObject(config.graph))
3737
}
3838

model-datastructure/src/commonMain/kotlin/org/modelix/datastructures/model/DefaultModelTree.kt

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,15 @@
11
package org.modelix.datastructures.model
22

33
import org.modelix.datastructures.IPersistentMap
4-
import org.modelix.datastructures.hamt.HamtNode
54
import org.modelix.datastructures.objects.Object
6-
import org.modelix.datastructures.objects.ObjectReference
75
import org.modelix.datastructures.objects.asObject
8-
import org.modelix.datastructures.patricia.PatriciaNode
6+
import org.modelix.datastructures.objects.upcast
97
import org.modelix.model.TreeId
108
import org.modelix.model.api.INodeReference
119
import org.modelix.model.api.ITree
1210
import org.modelix.model.api.PNodeReference
1311
import org.modelix.model.persistent.CPTree
12+
import kotlin.jvm.JvmName
1413

1514
/**
1615
* Legacy storage with new API that hides details about the type of IDs that's used internally.
@@ -23,7 +22,7 @@ class Int64ModelTree(nodesMap: IPersistentMap<Long, NodeObjectData<Long>>, treeI
2322
override fun asObject(): Object<CPTree> {
2423
return CPTree(
2524
id = getId(),
26-
int64Hamt = nodesMap.asObject().ref as ObjectReference<HamtNode<Long, ObjectReference<NodeObjectData<Long>>>>,
25+
int64Hamt = nodesMap.asObject().ref.upcast(),
2726
trieWithNodeRefIds = null,
2827
usesRoleIds = true,
2928
).asObject(graph)
@@ -41,7 +40,7 @@ class DefaultModelTree(nodesMap: IPersistentMap<INodeReference, NodeObjectData<I
4140
return CPTree(
4241
id = getId(),
4342
int64Hamt = null,
44-
trieWithNodeRefIds = nodesMap.asObject() as ObjectReference<PatriciaNode<ObjectReference<NodeObjectData<INodeReference>>>>,
43+
trieWithNodeRefIds = nodesMap.asObject().ref.upcast(),
4544
usesRoleIds = true,
4645
).asObject(graph)
4746
}
@@ -54,3 +53,13 @@ class DefaultModelTree(nodesMap: IPersistentMap<INodeReference, NodeObjectData<I
5453
}
5554

5655
class NodeNotFoundException(nodeId: Any?) : RuntimeException("Node doesn't exist: $nodeId")
56+
57+
@JvmName("asModelTreeWithNodeReferences")
58+
fun IPersistentMap<INodeReference, NodeObjectData<INodeReference>>.asModelTree(treeId: TreeId): IModelTree<INodeReference> {
59+
return DefaultModelTree(this, treeId)
60+
}
61+
62+
@JvmName("asModelTreeWithInt64")
63+
fun IPersistentMap<Long, NodeObjectData<Long>>.asModelTree(treeId: TreeId): IModelTree<Long> {
64+
return Int64ModelTree(this, treeId)
65+
}

model-datastructure/src/commonMain/kotlin/org/modelix/datastructures/model/IModelTree.kt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,10 @@ interface IModelTree<NodeId> : IStreamExecutorProvider {
5959
fun mutate(operations: Iterable<MutationParameters<NodeId>>): IStream.One<IModelTree<NodeId>>
6060

6161
fun mutate(operation: MutationParameters<NodeId>): IStream.One<IModelTree<NodeId>> = mutate(listOf(operation))
62+
63+
companion object {
64+
fun builder(): ModelTreeBuilder<Long> = ModelTreeBuilder.newWithInt64Ids()
65+
}
6266
}
6367

6468
sealed class MutationParameters<NodeId> {

model-datastructure/src/commonMain/kotlin/org/modelix/datastructures/model/IdTranslatingModelTree.kt

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import org.modelix.streams.IStream
1313
import org.modelix.streams.IStreamExecutor
1414
import org.modelix.streams.mapFirst
1515
import org.modelix.streams.mapSecond
16+
import kotlin.jvm.JvmName
1617

1718
class NodeReferenceAsLongModelTree(tree: IModelTree<INodeReference>) : IdTranslatingModelTree<Long, INodeReference>(tree) {
1819
override fun Long.toInternal(): INodeReference = PNodeReference(this, getId().id)
@@ -186,3 +187,13 @@ abstract class IdTranslatingModelTree<ExternalId, InternalId>(val tree: IModelTr
186187
}
187188
}
188189
}
190+
191+
@JvmName("withIdTranslationToInt64")
192+
fun IModelTree<INodeReference>.withIdTranslation(): IModelTree<Long> {
193+
return NodeReferenceAsLongModelTree(this)
194+
}
195+
196+
@JvmName("withIdTranslationToNodeReferences")
197+
fun IModelTree<Long>.withIdTranslation(): IModelTree<INodeReference> {
198+
return LongAsNodeReferenceModelTree(this)
199+
}

0 commit comments

Comments
 (0)