1
1
package org.modelix.datastructures.patricia
2
2
3
+ import org.modelix.datastructures.IPersistentMap
4
+ import org.modelix.datastructures.IPersistentMapRootData
3
5
import org.modelix.datastructures.objects.IObjectData
4
6
import org.modelix.datastructures.objects.IObjectDeserializer
5
7
import org.modelix.datastructures.objects.IObjectGraph
6
8
import org.modelix.datastructures.objects.IObjectReferenceFactory
9
+ import org.modelix.datastructures.objects.Object
7
10
import org.modelix.datastructures.objects.ObjectReference
8
11
import org.modelix.datastructures.objects.getHashString
12
+ import org.modelix.datastructures.objects.upcast
9
13
import org.modelix.datastructures.serialization.SplitJoinSerializer
10
14
import org.modelix.kotlin.utils.urlDecode
11
15
import org.modelix.kotlin.utils.urlEncode
12
16
import org.modelix.streams.IStream
13
17
import org.modelix.streams.plus
14
18
15
- data class PatriciaNode <V : Any >(
16
- val config : PatriciaTrieConfig <* , V >,
19
+ data class PatriciaNode <K , V : Any >(
20
+ val config : PatriciaTrieConfig <K , V >,
17
21
val ownPrefix : String ,
18
22
val firstChars : String ,
19
- val children : List <ObjectReference <PatriciaNode <V >>>,
23
+ val children : List <ObjectReference <PatriciaNode <K , V >>>,
20
24
21
25
/* *
22
26
* [ownPrefix] is part of the key for entry that s stored in this node
23
27
*/
24
28
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 )
27
31
28
- constructor (config: PatriciaTrieConfig <* , V >, key: String , value: V ) :
32
+ constructor (config: PatriciaTrieConfig <K , V >, key: String , value: V ) :
29
33
this (config = config, ownPrefix = key, value = value, firstChars = " " , children = emptyList())
30
34
35
+ override fun createMapInstance (self : Object <IPersistentMapRootData <K , V >>): IPersistentMap <K , V > {
36
+ return PatriciaTrie (self.upcast<PatriciaNode <K , V >>())
37
+ }
38
+
31
39
fun calculateDepth (): Int = (children.maxOfOrNull { it.resolveNow().data.calculateDepth() } ? : 0 ) + 1
32
40
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 > {
34
42
return copy(
35
43
firstChars = firstChars.take(index) + firstChar + firstChars.drop(index),
36
44
children = children.take(index) + config.graph.fromCreated(child) + children.drop(index),
37
45
)
38
46
}
39
47
40
- private fun withChildReplacedNullable (index : Int , child : PatriciaNode <V >? ): PatriciaNode <V >? {
48
+ private fun withChildReplacedNullable (index : Int , child : PatriciaNode <K , V >? ): PatriciaNode <K , V >? {
41
49
return if (child == null ) withoutChild(index) else withChildReplaced(index, config.graph.fromCreated(child))
42
50
}
43
51
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 > {
45
53
if (children[index] == = child) return this
46
54
return copy(children = children.take(index) + child + children.drop(index + 1 ))
47
55
}
48
56
49
- fun withoutChild (index : Int ): PatriciaNode <V >? {
57
+ fun withoutChild (index : Int ): PatriciaNode <K , V >? {
50
58
return copy(
51
59
firstChars = firstChars.take(index) + firstChars.drop(index + 1 ),
52
60
children = children.take(index) + children.drop(index + 1 ),
53
61
)
54
62
}
55
63
56
- fun tryMerge (): IStream .ZeroOrOne <PatriciaNode <V >> {
64
+ fun tryMerge (): IStream .ZeroOrOne <PatriciaNode <K , V >> {
57
65
if (value != null ) return IStream .of(this )
58
66
return when (children.size) {
59
67
0 -> IStream .empty()
@@ -89,14 +97,14 @@ data class PatriciaNode<V : Any>(
89
97
/* *
90
98
* Returns a new root that contains only those entries that start with the given prefix.
91
99
*/
92
- fun slice (prefix : CharSequence ): IStream .ZeroOrOne <PatriciaNode <V >> {
100
+ fun slice (prefix : CharSequence ): IStream .ZeroOrOne <PatriciaNode <K , V >> {
93
101
return getSubtree(prefix).map { it.copy(ownPrefix = prefix.toString() + it.ownPrefix) }
94
102
}
95
103
96
104
/* *
97
105
* Returns a subtree with all known suffixes of the provided prefix.
98
106
*/
99
- fun getSubtree (prefix : CharSequence ): IStream .ZeroOrOne <PatriciaNode <V >> {
107
+ fun getSubtree (prefix : CharSequence ): IStream .ZeroOrOne <PatriciaNode <K , V >> {
100
108
if (ownPrefix.startsWith(prefix)) {
101
109
return if (ownPrefix.length == prefix.length) {
102
110
IStream .of(this .copy(ownPrefix = " " ))
@@ -127,7 +135,7 @@ data class PatriciaNode<V : Any>(
127
135
}
128
136
}
129
137
130
- fun put (newKey : CharSequence , newValue : V ? ): IStream .ZeroOrOne <PatriciaNode <V >> {
138
+ fun put (newKey : CharSequence , newValue : V ? ): IStream .ZeroOrOne <PatriciaNode <K , V >> {
131
139
val commonPrefix = newKey.commonPrefixWith(this .ownPrefix)
132
140
val remainingNewKey = newKey.drop(commonPrefix.length)
133
141
val remainingOwnPrefix = this .ownPrefix.drop(commonPrefix.length)
@@ -144,7 +152,7 @@ data class PatriciaNode<V : Any>(
144
152
.mapNotNull { withChildReplacedNullable(index, it) }
145
153
} else {
146
154
val insertionIndex = (- index) - 1
147
- val newChild = PatriciaNode <V >(
155
+ val newChild = PatriciaNode <K , V >(
148
156
config = config,
149
157
ownPrefix = remainingNewKey.toString(),
150
158
value = newValue,
@@ -160,16 +168,16 @@ data class PatriciaNode<V : Any>(
160
168
).flatMapZeroOrOne { it.tryMerge() }
161
169
}
162
170
163
- fun split (commonPrefix : CharSequence ): PatriciaNode <V > {
171
+ fun split (commonPrefix : CharSequence ): PatriciaNode <K , V > {
164
172
val remainingPrefix = this .ownPrefix.drop(commonPrefix.length)
165
- return PatriciaNode <V >(
173
+ return PatriciaNode <K , V >(
166
174
config = config,
167
175
ownPrefix = commonPrefix.toString(),
168
176
value = null ,
169
177
firstChars = remainingPrefix.take(1 ),
170
178
children = listOf (
171
179
config.graph.fromCreated(
172
- PatriciaNode <V >(
180
+ PatriciaNode <K , V >(
173
181
config = config,
174
182
ownPrefix = remainingPrefix,
175
183
firstChars = this .firstChars,
@@ -191,24 +199,24 @@ data class PatriciaNode<V : Any>(
191
199
}
192
200
193
201
override fun getDeserializer (): IObjectDeserializer <* > {
194
- return Deserializer (config)
202
+ return Deserializer < K , V > (config)
195
203
}
196
204
197
205
override fun getContainmentReferences (): List <ObjectReference <IObjectData >> {
198
206
return children + (value?.let { config.valueConfig.getContainmentReferences(it) } ? : emptyList())
199
207
}
200
208
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 })
203
211
override fun deserialize (
204
212
serialized : String ,
205
213
referenceFactory : IObjectReferenceFactory ,
206
- ): PatriciaNode <V > {
214
+ ): PatriciaNode <K , V > {
207
215
val S1 = SplitJoinSerializer .SEPARATORS [0 ]
208
216
val S2 = SplitJoinSerializer .SEPARATORS [1 ]
209
217
val parts = serialized.split(S1 , limit = 4 )
210
218
val config = config(referenceFactory as IObjectGraph )
211
- return PatriciaNode <V >(
219
+ return PatriciaNode <K , V >(
212
220
config,
213
221
parts[0 ].urlDecode()!! ,
214
222
parts[1 ].urlDecode()!! ,
0 commit comments