@@ -53,14 +53,13 @@ class ModelImporter(
53
53
// Therefore, choose a map with is optimized for memory usage.
54
54
// For the same reason store `INodeReference`s instead of `INode`s.
55
55
// In a few cases, where we need the `INode` we can resolve it.
56
- private var originalIdToExisting = MemoryEfficientMap < String , INodeReference >( )
56
+ private val originalIdToExisting by lazy(::buildExistingIndex )
57
57
58
58
// Use`INode` instead of `INodeReference` in `postponedReferences` and `nodesToRemove`
59
59
// because we know that we will always need the `INode`s in those cases.
60
60
// Those cases are deleting nodes and adding references to nodes.
61
61
private val postponedReferences = mutableListOf<PostponedReference >()
62
62
private val nodesToRemove = HashSet <INode >()
63
- private var numExpectedNodes = 0
64
63
private var currentNodeProgress = 0
65
64
private val logger = KotlinLogging .logger {}
66
65
@@ -104,34 +103,51 @@ class ModelImporter(
104
103
*/
105
104
@JvmName(" importData" )
106
105
fun import (data : ModelData ) {
107
- INodeResolutionScope .runWithAdditionalScope(root.getArea()) {
108
- logImportSize(data.root, logger)
109
- logger.info { " Building indices for import..." }
110
- originalIdToExisting = MemoryEfficientMap ()
111
- postponedReferences.clear()
112
- nodesToRemove.clear()
113
- numExpectedNodes = countExpectedNodes(data.root)
114
- val progressReporter = ProgressReporter (numExpectedNodes.toULong(), logger)
115
- currentNodeProgress = 0
116
- buildExistingIndex(root)
106
+ importIntoNodes(sequenceOf(ExistingAndExpectedNode (root, data)))
107
+ }
117
108
118
- logger.info { " Importing nodes..." }
119
- data.root.originalId()?.let { originalIdToExisting[it] = root.reference }
120
- syncNode(root, data.root, progressReporter)
109
+ /* *
110
+ * Incrementally updates existing children of the given with specified data.
111
+ *
112
+ * @param nodeCombinationsToImport Combinations of an old existing child and the new expected data.
113
+ * The combinations are consumed lazily.
114
+ * Callers can use this to load expected data on demand.
115
+ */
116
+ fun importIntoNodes (nodeCombinationsToImport : Sequence <ExistingAndExpectedNode >) {
117
+ logger.info { " Building indices for import..." }
118
+ postponedReferences.clear()
119
+ nodesToRemove.clear()
121
120
122
- logger.info { " Synchronizing references..." }
123
- postponedReferences.forEach { it.setPostponedReference() }
121
+ nodeCombinationsToImport.forEach { nodeCombination ->
122
+ importIntoNode(nodeCombination.expectedNodeData, nodeCombination.existingNode)
123
+ }
124
124
125
- logger.info { " Removing extra nodes..." }
126
- nodesToRemove.forEach {
127
- doAndPotentiallyContinueOnErrors {
128
- if (it.isValid) { // if it's invalid then it's already removed
129
- it.remove()
130
- }
125
+ logger.info { " Synchronizing references..." }
126
+ postponedReferences.forEach { it.setPostponedReference() }
127
+
128
+ logger.info { " Removing extra nodes..." }
129
+ nodesToRemove.forEach {
130
+ doAndPotentiallyContinueOnErrors {
131
+ if (it.isValid) { // if it's invalid then it's already removed
132
+ it.remove()
131
133
}
132
134
}
135
+ }
133
136
134
- logger.info { " Synchronization finished." }
137
+ logger.info { " Synchronization finished." }
138
+ }
139
+
140
+ private fun importIntoNode (expectedNodeData : ModelData , existingNode : INode = root) {
141
+ INodeResolutionScope .runWithAdditionalScope(existingNode.getArea()) {
142
+ logImportSize(expectedNodeData.root, logger)
143
+ logger.info { " Building indices for nodes import..." }
144
+ currentNodeProgress = 0
145
+ val numExpectedNodes = countExpectedNodes(expectedNodeData.root)
146
+ val progressReporter = ProgressReporter (numExpectedNodes.toULong(), logger)
147
+
148
+ logger.info { " Importing nodes..." }
149
+ expectedNodeData.root.originalId()?.let { originalIdToExisting[it] = existingNode.reference }
150
+ syncNode(existingNode, expectedNodeData.root, progressReporter)
135
151
}
136
152
}
137
153
@@ -240,10 +256,12 @@ class ModelImporter(
240
256
}
241
257
}
242
258
243
- private fun buildExistingIndex (root : INode ) {
259
+ private fun buildExistingIndex (): MemoryEfficientMap <String , INodeReference > {
260
+ val localOriginalIdToExisting = MemoryEfficientMap <String , INodeReference >()
244
261
root.getDescendants(true ).forEach { node ->
245
- node.originalId()?.let { originalIdToExisting [it] = node.reference }
262
+ node.originalId()?.let { localOriginalIdToExisting [it] = node.reference }
246
263
}
264
+ return localOriginalIdToExisting
247
265
}
248
266
249
267
private fun syncProperties (node : INode , nodeData : NodeData ) {
@@ -291,3 +309,8 @@ internal fun INode.originalId(): String? {
291
309
internal fun NodeData.originalId (): String? {
292
310
return properties[NodeData .idPropertyKey] ? : id
293
311
}
312
+
313
+ data class ExistingAndExpectedNode (
314
+ val existingNode : INode ,
315
+ val expectedNodeData : ModelData ,
316
+ )
0 commit comments