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