1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.apache.commons.configuration2.tree;
18
19 import java.util.ArrayList;
20 import java.util.Collection;
21 import java.util.Collections;
22 import java.util.HashMap;
23 import java.util.HashSet;
24 import java.util.LinkedList;
25 import java.util.List;
26 import java.util.Map;
27 import java.util.Set;
28 import java.util.SortedMap;
29 import java.util.TreeMap;
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57 final class ModelTransaction {
58
59
60
61
62
63
64
65 private static final int MAX_REPLACEMENTS = 200;
66
67
68 private static final int LEVEL_UNKNOWN = -1;
69
70
71 private final TreeData currentData;
72
73
74 private final ImmutableNode queryRoot;
75
76
77 private final NodeSelector rootNodeSelector;
78
79
80 private final NodeKeyResolver<ImmutableNode> resolver;
81
82
83 private final Map<ImmutableNode, ImmutableNode> replacementMapping;
84
85
86 private final Map<ImmutableNode, ImmutableNode> replacedNodes;
87
88
89 private final Map<ImmutableNode, ImmutableNode> parentMapping;
90
91
92 private final Collection<ImmutableNode> addedNodes;
93
94
95 private final Collection<ImmutableNode> removedNodes;
96
97
98
99
100 private final Collection<ImmutableNode> allRemovedNodes;
101
102
103
104
105
106
107 private final SortedMap<Integer, Map<ImmutableNode, Operations>> operations;
108
109
110 private Map<ImmutableNode, Object> newReferences;
111
112
113 private ImmutableNode newRoot;
114
115
116
117
118
119
120
121
122
123 public ModelTransaction(final TreeData treeData, final NodeSelector selector, final NodeKeyResolver<ImmutableNode> resolver) {
124 currentData = treeData;
125 this.resolver = resolver;
126 replacementMapping = getCurrentData().copyReplacementMapping();
127 replacedNodes = new HashMap<>();
128 parentMapping = getCurrentData().copyParentMapping();
129 operations = new TreeMap<>();
130 addedNodes = new LinkedList<>();
131 removedNodes = new LinkedList<>();
132 allRemovedNodes = new LinkedList<>();
133 queryRoot = initQueryRoot(treeData, selector);
134 rootNodeSelector = selector;
135 }
136
137
138
139
140
141
142 public NodeKeyResolver<ImmutableNode> getResolver() {
143 return resolver;
144 }
145
146
147
148
149
150
151
152 public ImmutableNode getQueryRoot() {
153 return queryRoot;
154 }
155
156
157
158
159
160
161
162 public void addAddNodesOperation(final ImmutableNode parent, final Collection<? extends ImmutableNode> newNodes) {
163 final ChildrenUpdateOperation op = new ChildrenUpdateOperation();
164 op.addNewNodes(newNodes);
165 fetchOperations(parent, LEVEL_UNKNOWN).addChildrenOperation(op);
166 }
167
168
169
170
171
172
173
174 public void addAddNodeOperation(final ImmutableNode parent, final ImmutableNode newChild) {
175 final ChildrenUpdateOperation op = new ChildrenUpdateOperation();
176 op.addNewNode(newChild);
177 fetchOperations(parent, LEVEL_UNKNOWN).addChildrenOperation(op);
178 }
179
180
181
182
183
184
185
186
187 public void addAttributeOperation(final ImmutableNode target, final String name, final Object value) {
188 fetchOperations(target, LEVEL_UNKNOWN).addOperation(new AddAttributeOperation(name, value));
189 }
190
191
192
193
194
195
196
197 public void addAttributesOperation(final ImmutableNode target, final Map<String, Object> attributes) {
198 fetchOperations(target, LEVEL_UNKNOWN).addOperation(new AddAttributesOperation(attributes));
199 }
200
201
202
203
204
205
206
207 public void addRemoveNodeOperation(final ImmutableNode parent, final ImmutableNode node) {
208 final ChildrenUpdateOperation op = new ChildrenUpdateOperation();
209 op.addNodeToRemove(node);
210 fetchOperations(parent, LEVEL_UNKNOWN).addChildrenOperation(op);
211 }
212
213
214
215
216
217
218
219 public void addRemoveAttributeOperation(final ImmutableNode target, final String name) {
220 fetchOperations(target, LEVEL_UNKNOWN).addOperation(new RemoveAttributeOperation(name));
221 }
222
223
224
225
226
227
228 public void addClearNodeValueOperation(final ImmutableNode target) {
229 addChangeNodeValueOperation(target, null);
230 }
231
232
233
234
235
236
237
238 public void addChangeNodeValueOperation(final ImmutableNode target, final Object newValue) {
239 fetchOperations(target, LEVEL_UNKNOWN).addOperation(new ChangeNodeValueOperation(newValue));
240 }
241
242
243
244
245
246
247
248 public void addChangeNodeNameOperation(final ImmutableNode target, final String newName) {
249 fetchOperations(target, LEVEL_UNKNOWN).addOperation(new ChangeNodeNameOperation(newName));
250 }
251
252
253
254
255
256
257
258 public void addNewReferences(final Map<ImmutableNode, ?> refs) {
259 fetchReferenceMap().putAll(refs);
260 }
261
262
263
264
265
266
267
268 public void addNewReference(final ImmutableNode node, final Object ref) {
269 fetchReferenceMap().put(node, ref);
270 }
271
272
273
274
275
276
277
278 public TreeData execute() {
279 executeOperations();
280 updateParentMapping();
281 return new TreeData(newRoot, parentMapping, replacementMapping,
282 currentData.getNodeTracker().update(newRoot, rootNodeSelector, getResolver(), getCurrentData()), updateReferenceTracker());
283 }
284
285
286
287
288
289
290 public TreeData getCurrentData() {
291 return currentData;
292 }
293
294
295
296
297
298
299
300 ImmutableNode getParent(final ImmutableNode node) {
301 return getCurrentData().getParent(node);
302 }
303
304
305
306
307
308
309
310
311
312 Operations fetchOperations(final ImmutableNode target, final int level) {
313 final Integer nodeLevel = Integer.valueOf(level == LEVEL_UNKNOWN ? level(target) : level);
314 final Map<ImmutableNode, Operations> levelOperations = operations.computeIfAbsent(nodeLevel, k -> new HashMap<>());
315 return levelOperations.computeIfAbsent(target, k -> new Operations());
316 }
317
318
319
320
321
322
323
324
325
326 private ImmutableNode initQueryRoot(final TreeData treeData, final NodeSelector selector) {
327 return selector == null ? treeData.getRootNode() : treeData.getNodeTracker().getTrackedNode(selector);
328 }
329
330
331
332
333
334
335
336
337 private int level(final ImmutableNode node) {
338 ImmutableNode current = getCurrentData().getParent(node);
339 int level = 0;
340 while (current != null) {
341 level++;
342 current = getCurrentData().getParent(current);
343 }
344 return level;
345 }
346
347
348
349
350 private void executeOperations() {
351 while (!operations.isEmpty()) {
352 final Integer level = operations.lastKey();
353 operations.remove(level).forEach((k, v) -> v.apply(k, level));
354 }
355 }
356
357
358
359
360
361 private void updateParentMapping() {
362 replacementMapping.putAll(replacedNodes);
363 if (replacementMapping.size() > MAX_REPLACEMENTS) {
364 rebuildParentMapping();
365 } else {
366 updateParentMappingForAddedNodes();
367 updateParentMappingForRemovedNodes();
368 }
369 }
370
371
372
373
374
375 private void rebuildParentMapping() {
376 replacementMapping.clear();
377 parentMapping.clear();
378 InMemoryNodeModel.updateParentMapping(parentMapping, newRoot);
379 }
380
381
382
383
384 private void updateParentMappingForAddedNodes() {
385 addedNodes.forEach(node -> InMemoryNodeModel.updateParentMapping(parentMapping, node));
386 }
387
388
389
390
391 private void updateParentMappingForRemovedNodes() {
392 removedNodes.forEach(this::removeNodesFromParentAndReplacementMapping);
393 }
394
395
396
397
398
399
400 private void removeNodesFromParentAndReplacementMapping(final ImmutableNode root) {
401 NodeTreeWalker.INSTANCE.walkBFS(root, new ConfigurationNodeVisitorAdapter<ImmutableNode>() {
402 @Override
403 public void visitBeforeChildren(final ImmutableNode node, final NodeHandler<ImmutableNode> handler) {
404 allRemovedNodes.add(node);
405 parentMapping.remove(node);
406 removeNodeFromReplacementMapping(node);
407 }
408 }, getCurrentData());
409 }
410
411
412
413
414
415
416
417 private void removeNodeFromReplacementMapping(final ImmutableNode node) {
418 ImmutableNode replacement = node;
419 do {
420 replacement = replacementMapping.remove(replacement);
421 } while (replacement != null);
422 }
423
424
425
426
427
428
429
430 private ReferenceTracker updateReferenceTracker() {
431 ReferenceTracker tracker = currentData.getReferenceTracker();
432 if (newReferences != null) {
433 tracker = tracker.addReferences(newReferences);
434 }
435 return tracker.updateReferences(replacedNodes, allRemovedNodes);
436 }
437
438
439
440
441
442
443 private Map<ImmutableNode, Object> fetchReferenceMap() {
444 if (newReferences == null) {
445 newReferences = new HashMap<>();
446 }
447 return newReferences;
448 }
449
450
451
452
453
454
455
456
457
458 private static <E> Collection<E> concatenate(final Collection<E> col1, final Collection<? extends E> col2) {
459 if (col2 == null) {
460 return col1;
461 }
462
463 final Collection<E> result = col1 != null ? col1 : new ArrayList<>(col2.size());
464 result.addAll(col2);
465 return result;
466 }
467
468
469
470
471
472
473
474
475
476 private static <E> Set<E> concatenate(final Set<E> set1, final Set<? extends E> set2) {
477 if (set2 == null) {
478 return set1;
479 }
480
481 final Set<E> result = set1 != null ? set1 : new HashSet<>();
482 result.addAll(set2);
483 return result;
484 }
485
486
487
488
489
490
491
492
493
494
495 private static <K, V> Map<K, V> concatenate(final Map<K, V> map1, final Map<? extends K, ? extends V> map2) {
496 if (map2 == null) {
497 return map1;
498 }
499
500 final Map<K, V> result = map1 != null ? map1 : new HashMap<>();
501 result.putAll(map2);
502 return result;
503 }
504
505
506
507
508
509
510
511
512
513 private static <E> Collection<E> append(final Collection<E> col, final E node) {
514 final Collection<E> result = col != null ? col : new LinkedList<>();
515 result.add(node);
516 return result;
517 }
518
519
520
521
522
523
524
525
526
527 private static <E> Set<E> append(final Set<E> col, final E elem) {
528 final Set<E> result = col != null ? col : new HashSet<>();
529 result.add(elem);
530 return result;
531 }
532
533
534
535
536
537
538
539
540
541
542
543 private static <K, V> Map<K, V> append(final Map<K, V> map, final K key, final V value) {
544 final Map<K, V> result = map != null ? map : new HashMap<>();
545 result.put(key, value);
546 return result;
547 }
548
549
550
551
552
553 private abstract static class Operation {
554
555
556
557
558
559
560
561 protected abstract ImmutableNode apply(ImmutableNode target, Operations operations);
562 }
563
564
565
566
567
568
569
570 private final class ChildrenUpdateOperation extends Operation {
571
572 private Collection<ImmutableNode> newNodes;
573
574
575 private Set<ImmutableNode> nodesToRemove;
576
577
578
579
580 private Map<ImmutableNode, ImmutableNode> nodesToReplace;
581
582
583
584
585
586
587 public void combine(final ChildrenUpdateOperation op) {
588 newNodes = concatenate(newNodes, op.newNodes);
589 nodesToReplace = concatenate(nodesToReplace, op.nodesToReplace);
590 nodesToRemove = concatenate(nodesToRemove, op.nodesToRemove);
591 }
592
593
594
595
596
597
598 public void addNewNode(final ImmutableNode node) {
599 newNodes = append(newNodes, node);
600 }
601
602
603
604
605
606
607 public void addNewNodes(final Collection<? extends ImmutableNode> nodes) {
608 newNodes = concatenate(newNodes, nodes);
609 }
610
611
612
613
614
615
616
617 public void addNodeToReplace(final ImmutableNode org, final ImmutableNode replacement) {
618 nodesToReplace = append(nodesToReplace, org, replacement);
619 }
620
621
622
623
624
625
626 public void addNodeToRemove(final ImmutableNode node) {
627 nodesToRemove = append(nodesToRemove, node);
628 }
629
630
631
632
633
634 @Override
635 protected ImmutableNode apply(final ImmutableNode target, final Operations operations) {
636 final Map<ImmutableNode, ImmutableNode> replacements = fetchReplacementMap();
637 final Set<ImmutableNode> removals = fetchRemovalSet();
638 final List<ImmutableNode> resultNodes = new LinkedList<>();
639
640 for (final ImmutableNode nd : target) {
641 final ImmutableNode repl = replacements.get(nd);
642 if (repl != null) {
643 resultNodes.add(repl);
644 replacedNodes.put(nd, repl);
645 } else if (removals.contains(nd)) {
646 removedNodes.add(nd);
647 } else {
648 resultNodes.add(nd);
649 }
650 }
651
652 concatenate(resultNodes, newNodes);
653 operations.newNodesAdded(newNodes);
654 return target.replaceChildren(resultNodes);
655 }
656
657
658
659
660
661
662 private Map<ImmutableNode, ImmutableNode> fetchReplacementMap() {
663 return nodesToReplace != null ? nodesToReplace : Collections.<ImmutableNode, ImmutableNode>emptyMap();
664 }
665
666
667
668
669
670
671 private Set<ImmutableNode> fetchRemovalSet() {
672 return nodesToRemove != null ? nodesToRemove : Collections.<ImmutableNode>emptySet();
673 }
674 }
675
676
677
678
679 private static final class AddAttributeOperation extends Operation {
680
681 private final String attributeName;
682
683
684 private final Object attributeValue;
685
686
687
688
689
690
691
692 public AddAttributeOperation(final String name, final Object value) {
693 attributeName = name;
694 attributeValue = value;
695 }
696
697 @Override
698 protected ImmutableNode apply(final ImmutableNode target, final Operations operations) {
699 return target.setAttribute(attributeName, attributeValue);
700 }
701 }
702
703
704
705
706 private static final class AddAttributesOperation extends Operation {
707
708 private final Map<String, Object> attributes;
709
710
711
712
713
714
715 public AddAttributesOperation(final Map<String, Object> attrs) {
716 attributes = attrs;
717 }
718
719 @Override
720 protected ImmutableNode apply(final ImmutableNode target, final Operations operations) {
721 return target.setAttributes(attributes);
722 }
723 }
724
725
726
727
728 private static final class RemoveAttributeOperation extends Operation {
729
730 private final String attributeName;
731
732
733
734
735
736
737 public RemoveAttributeOperation(final String name) {
738 attributeName = name;
739 }
740
741 @Override
742 protected ImmutableNode apply(final ImmutableNode target, final Operations operations) {
743 return target.removeAttribute(attributeName);
744 }
745 }
746
747
748
749
750 private static final class ChangeNodeValueOperation extends Operation {
751
752 private final Object newValue;
753
754
755
756
757
758
759 public ChangeNodeValueOperation(final Object value) {
760 newValue = value;
761 }
762
763 @Override
764 protected ImmutableNode apply(final ImmutableNode target, final Operations operations) {
765 return target.setValue(newValue);
766 }
767 }
768
769
770
771
772 private static final class ChangeNodeNameOperation extends Operation {
773
774 private final String newName;
775
776
777
778
779
780
781 public ChangeNodeNameOperation(final String name) {
782 newName = name;
783 }
784
785 @Override
786 protected ImmutableNode apply(final ImmutableNode target, final Operations operations) {
787 return target.setName(newName);
788 }
789 }
790
791
792
793
794 private final class Operations {
795
796 private ChildrenUpdateOperation childrenOperation;
797
798
799
800
801 private Collection<Operation> operations;
802
803
804 private Collection<ImmutableNode> addedNodesInOperation;
805
806
807
808
809
810
811 public void addChildrenOperation(final ChildrenUpdateOperation co) {
812 if (childrenOperation == null) {
813 childrenOperation = co;
814 } else {
815 childrenOperation.combine(co);
816 }
817 }
818
819
820
821
822
823
824 public void addOperation(final Operation op) {
825 operations = append(operations, op);
826 }
827
828
829
830
831
832
833
834 public void newNodesAdded(final Collection<ImmutableNode> newNodes) {
835 addedNodesInOperation = concatenate(addedNodesInOperation, newNodes);
836 }
837
838
839
840
841
842
843
844
845
846 public void apply(final ImmutableNode target, final int level) {
847 ImmutableNode node = target;
848 if (childrenOperation != null) {
849 node = childrenOperation.apply(node, this);
850 }
851
852 if (operations != null) {
853 for (final Operation op : operations) {
854 node = op.apply(node, this);
855 }
856 }
857
858 handleAddedNodes(node);
859 if (level == 0) {
860
861 newRoot = node;
862 replacedNodes.put(target, node);
863 } else {
864
865 propagateChange(target, node, level);
866 }
867 }
868
869
870
871
872
873
874
875
876
877 private void propagateChange(final ImmutableNode target, final ImmutableNode node, final int level) {
878 final ImmutableNode parent = getParent(target);
879 final ChildrenUpdateOperation co = new ChildrenUpdateOperation();
880 if (InMemoryNodeModel.checkIfNodeDefined(node)) {
881 co.addNodeToReplace(target, node);
882 } else {
883 co.addNodeToRemove(target);
884 }
885 fetchOperations(parent, level - 1).addChildrenOperation(co);
886 }
887
888
889
890
891
892
893 private void handleAddedNodes(final ImmutableNode node) {
894 if (addedNodesInOperation != null) {
895 addedNodesInOperation.forEach(child -> {
896 parentMapping.put(child, node);
897 addedNodes.add(child);
898 });
899 }
900 }
901 }
902 }