@@ -2,18 +2,26 @@ package org.modelix.model.lazy
2
2
3
3
import com.badoo.reaktive.maybe.Maybe
4
4
import com.badoo.reaktive.maybe.map
5
+ import com.badoo.reaktive.observable.Observable
6
+ import com.badoo.reaktive.observable.asObservable
7
+ import com.badoo.reaktive.observable.concatWith
8
+ import com.badoo.reaktive.observable.flatMap
5
9
import com.badoo.reaktive.observable.flatMapSingle
6
10
import com.badoo.reaktive.observable.toList
11
+ import com.badoo.reaktive.single.flatMapObservable
12
+ import com.badoo.reaktive.single.flatten
7
13
import com.badoo.reaktive.single.map
8
14
import com.badoo.reaktive.single.notNull
9
15
import com.badoo.reaktive.single.singleOf
16
+ import com.badoo.reaktive.single.zipWith
10
17
import kotlinx.datetime.Clock
11
18
import kotlinx.datetime.Instant
12
19
import kotlinx.datetime.TimeZone
13
20
import kotlinx.datetime.toInstant
14
21
import org.modelix.model.IKeyValueStore
15
22
import org.modelix.model.IVersion
16
23
import org.modelix.model.LinearHistory
24
+ import org.modelix.model.VersionMerger
17
25
import org.modelix.model.api.IIdGenerator
18
26
import org.modelix.model.api.INodeReference
19
27
import org.modelix.model.api.ITree
@@ -35,16 +43,17 @@ import org.modelix.model.persistent.CPVersion
35
43
import org.modelix.model.persistent.EntryAddedEvent
36
44
import org.modelix.model.persistent.EntryChangedEvent
37
45
import org.modelix.model.persistent.EntryRemovedEvent
46
+ import org.modelix.model.persistent.IKVValue
38
47
import org.modelix.model.persistent.OperationsList
48
+ import org.modelix.model.persistent.getAllObjects
39
49
import org.modelix.streams.getSynchronous
40
50
import org.modelix.streams.iterateSynchronous
41
51
import kotlin.jvm.JvmName
42
52
43
53
class CLVersion : IVersion {
44
54
val asyncStore: IAsyncObjectStore
45
55
var store: IDeserializingKeyValueStore
46
- var data: CPVersion ? = null
47
- private set
56
+ val data: CPVersion
48
57
val treeHash: KVEntryReference <CPTree >
49
58
50
59
private constructor (
@@ -70,11 +79,11 @@ class CLVersion : IVersion {
70
79
time = time,
71
80
author = author,
72
81
treeHash = this .treeHash,
73
- previousVersion = previousVersion?.let { KVEntryReference (it.data!! ) },
74
- originalVersion = originalVersion?.let { KVEntryReference (it.data!! ) },
75
- baseVersion = baseVersion?.let { KVEntryReference (it.data!! ) },
76
- mergedVersion1 = mergedVersion1?.let { KVEntryReference (it.data!! ) },
77
- mergedVersion2 = mergedVersion2?.let { KVEntryReference (it.data!! ) },
82
+ previousVersion = previousVersion?.let { KVEntryReference (it.data) },
83
+ originalVersion = originalVersion?.let { KVEntryReference (it.data) },
84
+ baseVersion = baseVersion?.let { KVEntryReference (it.data) },
85
+ mergedVersion1 = mergedVersion1?.let { KVEntryReference (it.data) },
86
+ mergedVersion2 = mergedVersion2?.let { KVEntryReference (it.data) },
78
87
operations = localizedOps.toTypedArray(),
79
88
operationsHash = null ,
80
89
numberOfOperations = localizedOps.size,
@@ -86,11 +95,11 @@ class CLVersion : IVersion {
86
95
time = time,
87
96
author = author,
88
97
treeHash = this .treeHash,
89
- previousVersion = previousVersion?.let { KVEntryReference (it.data!! ) },
90
- originalVersion = originalVersion?.let { KVEntryReference (it.data!! ) },
91
- baseVersion = baseVersion?.let { KVEntryReference (it.data!! ) },
92
- mergedVersion1 = mergedVersion1?.let { KVEntryReference (it.data!! ) },
93
- mergedVersion2 = mergedVersion2?.let { KVEntryReference (it.data!! ) },
98
+ previousVersion = previousVersion?.let { KVEntryReference (it.data) },
99
+ originalVersion = originalVersion?.let { KVEntryReference (it.data) },
100
+ baseVersion = baseVersion?.let { KVEntryReference (it.data) },
101
+ mergedVersion1 = mergedVersion1?.let { KVEntryReference (it.data) },
102
+ mergedVersion2 = mergedVersion2?.let { KVEntryReference (it.data) },
94
103
operations = null ,
95
104
operationsHash = KVEntryReference (opsList),
96
105
numberOfOperations = localizedOps.size,
@@ -116,17 +125,17 @@ class CLVersion : IVersion {
116
125
}
117
126
118
127
val author: String?
119
- get() = data!! .author
128
+ get() = data.author
120
129
121
130
val id: Long
122
- get() = data!! .id
131
+ get() = data.id
123
132
124
133
@Deprecated(" Use getTimestamp()" )
125
134
val time: String?
126
- get() = data!! .time
135
+ get() = data.time
127
136
128
137
fun getTimestamp (): Instant ? {
129
- val dateTimeStr = data!! .time ? : return null
138
+ val dateTimeStr = data.time ? : return null
130
139
try {
131
140
return Instant .fromEpochSeconds(dateTimeStr.toLong())
132
141
} catch (ex: Exception ) {}
@@ -138,47 +147,47 @@ class CLVersion : IVersion {
138
147
139
148
@Deprecated(" Use getContentHash()" , ReplaceWith (" getContentHash()" ))
140
149
val hash: String
141
- get() = data!! .hash
150
+ get() = data.hash
142
151
143
- override fun getContentHash (): String = data!! .hash
152
+ override fun getContentHash (): String = data.hash
144
153
145
154
@Deprecated(" Use getTree()" , ReplaceWith (" getTree()" ))
146
155
@get:JvmName(" getTree_()" )
147
156
val tree: CLTree
148
- get() = CLTree (treeHash!! .getValue(store), store)
157
+ get() = CLTree (treeHash.getValue(store), store)
149
158
150
159
override fun getTree (): CLTree = tree
151
160
152
161
val baseVersion: CLVersion ?
153
162
get() {
154
- val previousVersionHash = data!! .baseVersion ? : data!! .previousVersion ? : return null
163
+ val previousVersionHash = data.baseVersion ? : data.previousVersion ? : return null
155
164
val previousVersion = previousVersionHash.getValue(store)
156
165
return CLVersion (previousVersion, store)
157
166
}
158
167
159
168
val operations: Iterable <IOperation >
160
169
get() {
161
- val operationsHash = data!! .operationsHash
170
+ val operationsHash = data.operationsHash
162
171
val ops = operationsHash?.getValue(store)?.getOperations(asyncStore)?.toList()?.getSynchronous()
163
- ? : data!! .operations?.toList()
172
+ ? : data.operations?.toList()
164
173
? : emptyList()
165
174
return globalizeOps(ops)
166
175
}
167
176
168
177
val numberOfOperations: Int
169
- get() = data!! .numberOfOperations
178
+ get() = data.numberOfOperations
170
179
171
180
fun operationsInlined (): Boolean {
172
- return data!! .operations != null
181
+ return data.operations != null
173
182
}
174
183
175
- fun isMerge () = this .data!! .mergedVersion1 != null
184
+ fun isMerge () = this .data.mergedVersion1 != null
176
185
177
- fun getMergedVersion1 () = this .data!! .mergedVersion1?.let { CLVersion (it.getValue(store), store) }
178
- fun getMergedVersion2 () = this .data!! .mergedVersion2?.let { CLVersion (it.getValue(store), store) }
186
+ fun getMergedVersion1 () = this .data.mergedVersion1?.let { CLVersion (it.getValue(store), store) }
187
+ fun getMergedVersion2 () = this .data.mergedVersion2?.let { CLVersion (it.getValue(store), store) }
179
188
180
189
fun write (): String {
181
- KVEntryReference (data!! ).write(store)
190
+ KVEntryReference (data).write(store)
182
191
return hash
183
192
}
184
193
@@ -188,13 +197,13 @@ class CLVersion : IVersion {
188
197
189
198
other as CLVersion
190
199
191
- if (data? .id != other.data? .id) return false
200
+ if (data.id != other.data.id) return false
192
201
193
202
return true
194
203
}
195
204
196
205
override fun hashCode (): Int {
197
- return data? .id? .hashCode() ? : 0
206
+ return data.id.hashCode()
198
207
}
199
208
200
209
override fun toString (): String {
@@ -313,6 +322,54 @@ class CLVersion : IVersion {
313
322
}
314
323
}
315
324
}
325
+
326
+ fun getParents (stopAt : CLVersion ? ): List <CLVersion > {
327
+ if (stopAt != null && this .getContentHash() == stopAt.getContentHash()) {
328
+ return emptyList()
329
+ }
330
+ val ancestors = if (isMerge()) {
331
+ listOf (getMergedVersion1()!! , getMergedVersion2()!! )
332
+ } else {
333
+ listOfNotNull(baseVersion)
334
+ }
335
+ return ancestors.filter { stopAt == null || it.getContentHash() != stopAt.getContentHash() }
336
+ }
337
+
338
+ fun getAncestors (includeSelf : Boolean , stopAt : CLVersion ? ): List <CLVersion > {
339
+ if (stopAt != null && this .getContentHash() == stopAt.getContentHash()) {
340
+ return emptyList()
341
+ }
342
+ return if (includeSelf) {
343
+ listOf (this ) + getAncestors(false , stopAt)
344
+ } else {
345
+ getParents(stopAt).flatMap { it.getAncestors(true , stopAt) }
346
+ }
347
+ }
348
+ }
349
+
350
+ fun CLVersion.fullDiff (baseVersion : CLVersion ? ): Observable <IKVValue > {
351
+ val history = historyDiff(baseVersion)
352
+ return history.concatWith(
353
+ history.flatMap { version ->
354
+ val baseVersion = version.baseVersion?.getValue(asyncStore) ? : singleOf(null )
355
+ val currentVersion = version.treeHash.getValue(asyncStore)
356
+ val treeDiff = currentVersion.zipWith(baseVersion) { v, b ->
357
+ if (b == null ) v.getAllObjects(asyncStore) else v.objectDiff(b, asyncStore)
358
+ }.flatten()
359
+ if (version.operationsHash != null ) {
360
+ val operations = version.operationsHash.getValue(asyncStore).flatMapObservable { it.getAllObjects(asyncStore) }
361
+ treeDiff.concatWith(operations)
362
+ } else {
363
+ treeDiff
364
+ }
365
+ },
366
+ )
367
+ }
368
+
369
+ fun CLVersion.historyDiff (baseVersion : CLVersion ? ): Observable <CPVersion > {
370
+ val commonBase = VersionMerger .commonBaseVersion(this , baseVersion)
371
+ val history = getAncestors(true , commonBase).map { it.data }
372
+ return history.asObservable()
316
373
}
317
374
318
375
fun CLVersion.computeDelta (baseVersion : CLVersion ? ): Map <String , String > {
0 commit comments