@@ -29,13 +29,14 @@ class ModelImporter(private val root: INode, val stats: ImportStats? = null) {
29
29
val addedNodes = addAllMissingChildren(root, originalIdToExisting, originalIdToSpec)
30
30
syncAllProperties(addedNodes, originalIdToSpec)
31
31
32
-
33
32
val allNodes = allExistingNodes + addedNodes
33
+
34
34
handleAllMovesAcrossParents(allNodes, originalIdToExisting, originalIdToSpec)
35
+
35
36
val originalIdToRef: MutableMap <String , INodeReference > = buildRefIndex(allNodes)
36
37
syncAllReferences(allNodes, originalIdToSpec, originalIdToRef)
37
- deleteAllExtraChildren(allNodes, originalIdToSpec)
38
38
39
+ deleteAllExtraChildren(allNodes, originalIdToSpec)
39
40
}
40
41
41
42
private fun buildExistingIndex (allNodes : List <INode >): MutableMap <String , INode > {
@@ -167,22 +168,23 @@ class ModelImporter(private val root: INode, val stats: ImportStats? = null) {
167
168
168
169
val targetIndices = HashMap <String ?, Int >(nodeData.children.size)
169
170
for (child in toBeSortedSpec) {
170
-
171
171
val childrenInRole = existingChildren.filter { it.roleInParent == child.role }
172
- val baseIndex = child.getIndexWithinRole(nodeData).coerceAtMost(childrenInRole.lastIndex)
172
+ val baseIndex = child.getIndexWithinRole(nodeData)
173
173
var offset = 0
174
- offset + = childrenInRole.slice(0 .. baseIndex).count {
174
+ offset + = childrenInRole.slice(0 .. baseIndex.coerceAtMost(childrenInRole.lastIndex) ).count {
175
175
! originalIdToSpec.containsKey(it.originalId()) // node will be deleted
176
176
}
177
- offset - = childrenInRole.slice(0 .. baseIndex).count {
178
- ! existingIds.contains(it.originalId()) // node will be moved here
179
- }
177
+ offset - = specifiedChildren
178
+ .filter { it.role == child.role }
179
+ .slice(0 .. baseIndex.coerceIn(0 .. specifiedChildren.lastIndex))
180
+ .count {
181
+ ! existingIds.contains(it.originalId()) // node will be moved here
182
+ }
180
183
val index = (baseIndex + offset).coerceIn(0 .. childrenInRole.size)
181
184
targetIndices[child.originalId()] = index
182
185
}
183
186
184
187
existingChildren.forEach { child ->
185
-
186
188
val currentIndex = child.index()
187
189
val targetRole = originalIdToSpec[child.originalId()]?.role
188
190
val targetIndex = targetIndices[child.originalId()]
@@ -197,36 +199,55 @@ class ModelImporter(private val root: INode, val stats: ImportStats? = null) {
197
199
originalIdToExisting : MutableMap <String , INode >,
198
200
originalIdToSpec : MutableMap <String , NodeData >
199
201
) {
200
- allNodes.forEach {
201
- originalIdToSpec[it.originalId()]?.let { spec -> handleMoveAcrossParents(it, spec, originalIdToExisting, originalIdToSpec) }
202
+ val moves = collectMovesAcrossParents(allNodes, originalIdToExisting, originalIdToSpec)
203
+ while (moves.isNotEmpty()) {
204
+ val nextMove = moves.first { ! it.nodeToBeMoved.getDescendants(false ).contains(it.targetParent) }
205
+ performMoveAcrossParents(nextMove.targetParent, nextMove.nodeToBeMoved, originalIdToSpec)
206
+ moves.remove(nextMove)
202
207
}
203
208
}
204
209
205
- private fun handleMoveAcrossParents (
206
- node : INode ,
207
- nodeData : NodeData ,
210
+ private fun collectMovesAcrossParents (
211
+ allNodes : List <INode >,
208
212
originalIdToExisting : MutableMap <String , INode >,
209
213
originalIdToSpec : MutableMap <String , NodeData >
210
- ) {
214
+ ): MutableList <MoveAcrossParents > {
215
+ val movesAcrossParents = mutableListOf<MoveAcrossParents >()
216
+ allNodes.forEach {node ->
217
+ val nodeData = originalIdToSpec[node.originalId()] ? : return @forEach
211
218
212
- val existingChildren = node.allChildren.toList()
213
- val missingIds = nodeData.children.map { it.originalId() }.toSet()
214
- .subtract(node.allChildren.map { it.originalId() }.toSet())
215
- val toBeMovedHere = missingIds
216
- .filter { originalIdToSpec.containsKey(it) }
217
- .mapNotNull { originalIdToExisting[it] }
218
-
219
- toBeMovedHere.forEach {nodeToBeMoved ->
220
- val spec = originalIdToSpec[nodeToBeMoved.originalId()]!!
221
- val childrenInRole = existingChildren.filter { it.roleInParent == spec.role }
222
- val baseTargetIndex = spec.getIndexWithinRole(nodeData).coerceAtMost(childrenInRole.size)
223
- val offset = existingChildren.slice(0 until baseTargetIndex).count {
224
- ! originalIdToSpec.containsKey(it.originalId()) // node will be deleted
219
+ val missingIds = nodeData.children.map { it.originalId() }.toSet()
220
+ .subtract(node.allChildren.map { it.originalId() }.toSet())
221
+ val toBeMovedHere = missingIds
222
+ .filter { originalIdToSpec.containsKey(it) }
223
+ .mapNotNull { originalIdToExisting[it] }
224
+
225
+ toBeMovedHere.forEach {
226
+ movesAcrossParents.add(MoveAcrossParents (node, it))
225
227
}
226
- val targetIndex = (baseTargetIndex + offset).coerceIn(0 .. childrenInRole.size)
228
+ }
229
+ return movesAcrossParents
230
+ }
231
+
232
+ private data class MoveAcrossParents (val targetParent : INode , val nodeToBeMoved : INode )
227
233
228
- node.moveChildWithStats(spec.role, targetIndex, nodeToBeMoved)
234
+ private fun performMoveAcrossParents (
235
+ node : INode ,
236
+ toBeMovedHere : INode ,
237
+ originalIdToSpec : MutableMap <String , NodeData >
238
+ ) {
239
+ val nodeData = originalIdToSpec[node.originalId()] ? : return
240
+ val existingChildren = node.allChildren.toList()
241
+ val spec = originalIdToSpec[toBeMovedHere.originalId()]!!
242
+ val childrenInRole = existingChildren.filter { it.roleInParent == spec.role }
243
+ val baseTargetIndex = spec.getIndexWithinRole(nodeData).coerceAtMost(childrenInRole.size)
244
+ val offset = childrenInRole.slice(0 until baseTargetIndex).count {
245
+ ! originalIdToSpec.containsKey(it.originalId()) // node will be deleted
229
246
}
247
+ val targetIndex = (baseTargetIndex + offset).coerceIn(0 .. childrenInRole.size)
248
+
249
+ node.moveChildWithStats(spec.role, targetIndex, toBeMovedHere)
250
+
230
251
}
231
252
232
253
private fun deleteAllExtraChildren (allNodes : List <INode >, originalIdToSpec : MutableMap <String , NodeData >) {
0 commit comments