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.Iterator;
24 import java.util.LinkedList;
25 import java.util.List;
26 import java.util.Map;
27 import java.util.concurrent.atomic.AtomicReference;
28
29 import org.apache.commons.configuration2.ex.ConfigurationRuntimeException;
30 import org.apache.commons.lang3.mutable.Mutable;
31 import org.apache.commons.lang3.mutable.MutableObject;
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48 public class InMemoryNodeModel implements NodeModel<ImmutableNode> {
49
50
51
52
53
54 private interface TransactionInitializer {
55
56
57
58
59
60
61
62
63 boolean initTransaction(ModelTransaction tx);
64 }
65
66
67
68
69 private static final NodeHandler<ImmutableNode> DUMMY_HANDLER = new TreeData(null, Collections.<ImmutableNode, ImmutableNode>emptyMap(),
70 Collections.<ImmutableNode, ImmutableNode>emptyMap(), null, new ReferenceTracker());
71
72
73
74
75
76
77
78
79 private static void addAttributeProperty(final ModelTransaction tx, final NodeAddData<ImmutableNode> addData, final Iterable<?> values) {
80 if (addData.getPathNodes().isEmpty()) {
81 tx.addAttributeOperation(addData.getParent(), addData.getNewNodeName(), values.iterator().next());
82 } else {
83 final int pathNodeCount = addData.getPathNodes().size();
84 final ImmutableNode childWithAttribute = new ImmutableNode.Builder().name(addData.getPathNodes().get(pathNodeCount - 1))
85 .addAttribute(addData.getNewNodeName(), values.iterator().next()).create();
86 final ImmutableNode newChild = pathNodeCount > 1
87 ? createNodeOnPath(addData.getPathNodes().subList(0, pathNodeCount - 1).iterator(), Collections.singleton(childWithAttribute))
88 : childWithAttribute;
89 tx.addAddNodeOperation(addData.getParent(), newChild);
90 }
91 }
92
93
94
95
96
97
98
99
100 private static void addNodeProperty(final ModelTransaction tx, final NodeAddData<ImmutableNode> addData, final Iterable<?> values) {
101 final Collection<ImmutableNode> newNodes = createNodesToAdd(addData.getNewNodeName(), values);
102 addNodesByAddData(tx, addData, newNodes);
103 }
104
105
106
107
108
109
110
111
112
113 private static void addNodesByAddData(final ModelTransaction tx, final NodeAddData<ImmutableNode> addData, final Collection<ImmutableNode> newNodes) {
114 if (addData.getPathNodes().isEmpty()) {
115 tx.addAddNodesOperation(addData.getParent(), newNodes);
116 } else {
117 final ImmutableNode newChild = createNodeToAddWithPath(addData, newNodes);
118 tx.addAddNodeOperation(addData.getParent(), newChild);
119 }
120 }
121
122
123
124
125
126
127
128
129 private static IllegalArgumentException attributeKeyException(final String key) {
130 return new IllegalArgumentException("New nodes cannot be added to an attribute key: " + key);
131 }
132
133
134
135
136
137
138
139 static boolean checkIfNodeDefined(final ImmutableNode node) {
140 return node.getValue() != null || !node.getChildren().isEmpty() || !node.getAttributes().isEmpty();
141 }
142
143
144
145
146
147
148
149
150
151
152
153
154 private static TreeData createDataWithTrackedChildNode(final TreeData current, final ImmutableNode parent, final String childName,
155 final NodeKeyResolver<ImmutableNode> resolver, final MutableObject<NodeSelector> refSelector) {
156 final TreeData newData;
157 final List<ImmutableNode> namedChildren = current.getChildren(parent, childName);
158 if (!namedChildren.isEmpty()) {
159 newData = updateDataWithNewTrackedNode(current, namedChildren.get(0), resolver, refSelector);
160 } else {
161 final ImmutableNode child = new ImmutableNode.Builder().name(childName).create();
162 final ModelTransaction tx = new ModelTransaction(current, null, resolver);
163 tx.addAddNodeOperation(parent, child);
164 newData = updateDataWithNewTrackedNode(tx.execute(), child, resolver, refSelector);
165 }
166 return newData;
167 }
168
169
170
171
172
173
174
175
176
177 private static ImmutableNode createNodeOnPath(final Iterator<String> it, final Collection<ImmutableNode> newNodes) {
178 final String nodeName = it.next();
179 final ImmutableNode.Builder builder;
180 if (it.hasNext()) {
181 builder = new ImmutableNode.Builder(1);
182 builder.addChild(createNodeOnPath(it, newNodes));
183 } else {
184 builder = new ImmutableNode.Builder(newNodes.size());
185 builder.addChildren(newNodes);
186 }
187 return builder.name(nodeName).create();
188 }
189
190
191
192
193
194
195
196
197 private static Collection<ImmutableNode> createNodesToAdd(final String newNodeName, final Iterable<?> values) {
198 final Collection<ImmutableNode> nodes = new LinkedList<>();
199 values.forEach(value -> nodes.add(new ImmutableNode.Builder().name(newNodeName).value(value).create()));
200 return nodes;
201 }
202
203
204
205
206
207
208
209
210
211 private static ImmutableNode createNodeToAddWithPath(final NodeAddData<ImmutableNode> addData, final Collection<ImmutableNode> newNodes) {
212 return createNodeOnPath(addData.getPathNodes().iterator(), newNodes);
213 }
214
215
216
217
218
219
220
221
222
223
224 private static TreeData createSelectorsForTrackedNodes(final Mutable<Collection<NodeSelector>> refSelectors, final List<ImmutableNode> nodes,
225 final TreeData current, final NodeKeyResolver<ImmutableNode> resolver) {
226 final List<NodeSelector> selectors = new ArrayList<>(nodes.size());
227 final Map<ImmutableNode, String> cache = new HashMap<>();
228 nodes.forEach(node -> selectors.add(new NodeSelector(resolver.nodeKey(node, cache, current))));
229 refSelectors.setValue(selectors);
230 final NodeTracker newTracker = current.getNodeTracker().trackNodes(selectors, nodes);
231 return current.updateNodeTracker(newTracker);
232 }
233
234
235
236
237
238
239
240
241
242
243
244 private static String determineRootName(final ImmutableNode rootNode, final ImmutableNode node, final String rootName) {
245 if (rootName != null) {
246 return rootName;
247 }
248 if (rootNode.getNodeName() == null) {
249 return node.getNodeName();
250 }
251 return null;
252 }
253
254
255
256
257
258
259
260
261 private static boolean initializeClearTransaction(final ModelTransaction tx, final Collection<QueryResult<ImmutableNode>> results) {
262 results.forEach(result -> {
263 if (result.isAttributeResult()) {
264 tx.addRemoveAttributeOperation(result.getNode(), result.getAttributeName());
265 } else {
266 tx.addClearNodeValueOperation(result.getNode());
267 }
268 });
269
270 return !results.isEmpty();
271 }
272
273
274
275
276
277
278
279
280 private static boolean initializeUpdateTransaction(final ModelTransaction tx, final Map<QueryResult<ImmutableNode>, Object> changedValues) {
281 changedValues.forEach((k, v) -> {
282 final ImmutableNode node = k.getNode();
283 if (k.isAttributeResult()) {
284 tx.addAttributeOperation(node, k.getAttributeName(), v);
285 } else {
286 tx.addChangeNodeValueOperation(node, v);
287 }
288 });
289
290 return !changedValues.isEmpty();
291 }
292
293
294
295
296
297
298
299
300 private static ImmutableNode initialRootNode(final ImmutableNode providedRoot) {
301 return providedRoot != null ? providedRoot : new ImmutableNode.Builder().create();
302 }
303
304
305
306
307
308
309
310
311
312
313 private static TreeData updateDataWithNewTrackedNode(final TreeData current, final ImmutableNode node, final NodeKeyResolver<ImmutableNode> resolver,
314 final MutableObject<NodeSelector> refSelector) {
315 final NodeSelector selector = new NodeSelector(resolver.nodeKey(node, new HashMap<>(), current));
316 refSelector.setValue(selector);
317 final NodeTracker newTracker = current.getNodeTracker().trackNodes(Collections.singleton(selector), Collections.singleton(node));
318 return current.updateNodeTracker(newTracker);
319 }
320
321
322
323
324
325
326
327
328
329 static void updateParentMapping(final Map<ImmutableNode, ImmutableNode> parents, final ImmutableNode root) {
330 NodeTreeWalker.INSTANCE.walkBFS(root, new ConfigurationNodeVisitorAdapter<ImmutableNode>() {
331 @Override
332 public void visitBeforeChildren(final ImmutableNode node, final NodeHandler<ImmutableNode> handler) {
333 node.forEach(c -> parents.put(c, node));
334 }
335 }, DUMMY_HANDLER);
336 }
337
338
339
340
341
342
343
344 private static boolean valuesNotEmpty(final Iterable<?> values) {
345 return values.iterator().hasNext();
346 }
347
348
349 private final AtomicReference<TreeData> structure;
350
351
352
353
354 public InMemoryNodeModel() {
355 this(null);
356 }
357
358
359
360
361
362
363
364 public InMemoryNodeModel(final ImmutableNode root) {
365 structure = new AtomicReference<>(createTreeData(initialRootNode(root), null));
366 }
367
368 @Override
369 public void addNodes(final String key, final Collection<? extends ImmutableNode> nodes, final NodeKeyResolver<ImmutableNode> resolver) {
370 addNodes(key, null, nodes, resolver);
371 }
372
373
374
375
376
377
378
379
380
381
382
383
384 public void addNodes(final String key, final NodeSelector selector, final Collection<? extends ImmutableNode> nodes,
385 final NodeKeyResolver<ImmutableNode> resolver) {
386 if (nodes != null && !nodes.isEmpty()) {
387 updateModel(tx -> {
388 final List<QueryResult<ImmutableNode>> results = resolver.resolveKey(tx.getQueryRoot(), key, tx.getCurrentData());
389 if (results.size() == 1) {
390 if (results.get(0).isAttributeResult()) {
391 throw attributeKeyException(key);
392 }
393 tx.addAddNodesOperation(results.get(0).getNode(), nodes);
394 } else {
395 final NodeAddData<ImmutableNode> addData = resolver.resolveAddKey(tx.getQueryRoot(), key, tx.getCurrentData());
396 if (addData.isAttribute()) {
397 throw attributeKeyException(key);
398 }
399 final ImmutableNode newNode = new ImmutableNode.Builder(nodes.size()).name(addData.getNewNodeName()).addChildren(nodes).create();
400 addNodesByAddData(tx, addData, Collections.singleton(newNode));
401 }
402 return true;
403 }, selector, resolver);
404 }
405 }
406
407 @Override
408 public void addProperty(final String key, final Iterable<?> values, final NodeKeyResolver<ImmutableNode> resolver) {
409 addProperty(key, null, values, resolver);
410 }
411
412
413
414
415
416
417
418
419
420
421
422
423 public void addProperty(final String key, final NodeSelector selector, final Iterable<?> values, final NodeKeyResolver<ImmutableNode> resolver) {
424 if (valuesNotEmpty(values)) {
425 updateModel(tx -> {
426 initializeAddTransaction(tx, key, values, resolver);
427 return true;
428 }, selector, resolver);
429 }
430 }
431
432
433
434
435
436
437
438 @Override
439 public void clear(final NodeKeyResolver<ImmutableNode> resolver) {
440 final ImmutableNode newRoot = new ImmutableNode.Builder().name(getRootNode().getNodeName()).create();
441 setRootNode(newRoot);
442 }
443
444
445
446
447 @Override
448 public void clearProperty(final String key, final NodeKeyResolver<ImmutableNode> resolver) {
449 clearProperty(key, null, resolver);
450 }
451
452
453
454
455
456
457
458
459
460
461
462 public void clearProperty(final String key, final NodeSelector selector, final NodeKeyResolver<ImmutableNode> resolver) {
463 updateModel(tx -> {
464 final List<QueryResult<ImmutableNode>> results = resolver.resolveKey(tx.getQueryRoot(), key, tx.getCurrentData());
465 return initializeClearTransaction(tx, results);
466 }, selector, resolver);
467 }
468
469
470
471
472
473
474 @Override
475 public List<QueryResult<ImmutableNode>> clearTree(final String key, final NodeKeyResolver<ImmutableNode> resolver) {
476 return clearTree(key, null, resolver);
477 }
478
479
480
481
482
483
484
485
486
487
488
489
490 public List<QueryResult<ImmutableNode>> clearTree(final String key, final NodeSelector selector, final NodeKeyResolver<ImmutableNode> resolver) {
491 final List<QueryResult<ImmutableNode>> removedElements = new LinkedList<>();
492 updateModel(tx -> {
493 boolean changes = false;
494 final TreeData currentStructure = tx.getCurrentData();
495 final List<QueryResult<ImmutableNode>> results = resolver.resolveKey(tx.getQueryRoot(), key, currentStructure);
496 removedElements.clear();
497 removedElements.addAll(results);
498 for (final QueryResult<ImmutableNode> result : results) {
499 if (result.isAttributeResult()) {
500 tx.addRemoveAttributeOperation(result.getNode(), result.getAttributeName());
501 } else {
502 if (result.getNode() == currentStructure.getRootNode()) {
503
504 clear(resolver);
505 return false;
506 }
507 tx.addRemoveNodeOperation(currentStructure.getParent(result.getNode()), result.getNode());
508 }
509 changes = true;
510 }
511 return changes;
512 }, selector, resolver);
513
514 return removedElements;
515 }
516
517
518
519
520
521
522
523
524
525 private Map<ImmutableNode, ImmutableNode> createParentMapping(final ImmutableNode root) {
526 final Map<ImmutableNode, ImmutableNode> parents = new HashMap<>();
527 updateParentMapping(parents, root);
528 return parents;
529 }
530
531
532
533
534
535
536
537
538 private TreeData createTreeData(final ImmutableNode root, final TreeData current) {
539 final NodeTracker newTracker = current != null ? current.getNodeTracker().detachAllTrackedNodes() : new NodeTracker();
540 return createTreeDataForRootAndTracker(root, newTracker);
541 }
542
543
544
545
546
547
548
549
550
551 private TreeData createTreeDataForRootAndTracker(final ImmutableNode root, final NodeTracker newTracker) {
552 return new TreeData(root, createParentMapping(root), Collections.<ImmutableNode, ImmutableNode>emptyMap(), newTracker, new ReferenceTracker());
553 }
554
555
556
557
558
559
560
561
562
563
564
565 private boolean executeTransactionOnCurrentStructure(final TransactionInitializer txInit, final NodeSelector selector, final TreeData currentData,
566 final NodeKeyResolver<ImmutableNode> resolver) {
567 final boolean done;
568 final ModelTransaction tx = new ModelTransaction(currentData, selector, resolver);
569 if (!txInit.initTransaction(tx)) {
570 done = true;
571 } else {
572 final TreeData newData = tx.execute();
573 done = structure.compareAndSet(tx.getCurrentData(), newData);
574 }
575 return done;
576 }
577
578
579
580
581
582
583
584
585
586
587
588
589 private boolean executeTransactionOnDetachedTrackedNode(final TransactionInitializer txInit, final NodeSelector selector, final TreeData currentData,
590 final NodeKeyResolver<ImmutableNode> resolver) {
591 if (selector != null) {
592 final InMemoryNodeModel detachedNodeModel = currentData.getNodeTracker().getDetachedNodeModel(selector);
593 if (detachedNodeModel != null) {
594 detachedNodeModel.updateModel(txInit, null, resolver);
595 return true;
596 }
597 }
598
599 return false;
600 }
601
602
603
604
605 @Override
606 public ImmutableNode getInMemoryRepresentation() {
607 return getTreeData().getRootNode();
608 }
609
610
611
612
613
614 @Override
615 public NodeHandler<ImmutableNode> getNodeHandler() {
616 return getReferenceNodeHandler();
617 }
618
619
620
621
622
623
624
625 public ReferenceNodeHandler getReferenceNodeHandler() {
626 return getTreeData();
627 }
628
629
630
631
632
633
634
635
636
637
638 public ImmutableNode getRootNode() {
639 return getTreeData().getRootNode();
640 }
641
642
643
644
645
646
647
648
649
650
651 public ImmutableNode getTrackedNode(final NodeSelector selector) {
652 return structure.get().getNodeTracker().getTrackedNode(selector);
653 }
654
655
656
657
658
659
660
661
662
663
664
665 public NodeHandler<ImmutableNode> getTrackedNodeHandler(final NodeSelector selector) {
666 final TreeData currentData = structure.get();
667 final InMemoryNodeModel detachedNodeModel = currentData.getNodeTracker().getDetachedNodeModel(selector);
668 return detachedNodeModel != null ? detachedNodeModel.getNodeHandler()
669 : new TrackedNodeHandler(currentData.getNodeTracker().getTrackedNode(selector), currentData);
670 }
671
672
673
674
675
676
677 TreeData getTreeData() {
678 return structure.get();
679 }
680
681
682
683
684
685
686
687
688
689 private void initializeAddTransaction(final ModelTransaction tx, final String key, final Iterable<?> values,
690 final NodeKeyResolver<ImmutableNode> resolver) {
691 final NodeAddData<ImmutableNode> addData = resolver.resolveAddKey(tx.getQueryRoot(), key, tx.getCurrentData());
692 if (addData.isAttribute()) {
693 addAttributeProperty(tx, addData, values);
694 } else {
695 addNodeProperty(tx, addData, values);
696 }
697 }
698
699
700
701
702
703
704
705
706
707
708
709
710
711 public boolean isTrackedNodeDetached(final NodeSelector selector) {
712 return structure.get().getNodeTracker().isTrackedNodeDetached(selector);
713 }
714
715
716
717
718
719
720
721
722
723
724
725
726
727 public void mergeRoot(final ImmutableNode node, final String rootName, final Map<ImmutableNode, ?> references, final Object rootRef,
728 final NodeKeyResolver<ImmutableNode> resolver) {
729 updateModel(tx -> {
730 final TreeData current = tx.getCurrentData();
731 final String newRootName = determineRootName(current.getRootNode(), node, rootName);
732 if (newRootName != null) {
733 tx.addChangeNodeNameOperation(current.getRootNode(), newRootName);
734 }
735 tx.addAddNodesOperation(current.getRootNode(), node.getChildren());
736 tx.addAttributesOperation(current.getRootNode(), node.getAttributes());
737 if (node.getValue() != null) {
738 tx.addChangeNodeValueOperation(current.getRootNode(), node.getValue());
739 }
740 if (references != null) {
741 tx.addNewReferences(references);
742 }
743 if (rootRef != null) {
744 tx.addNewReference(current.getRootNode(), rootRef);
745 }
746 return true;
747 }, null, resolver);
748 }
749
750
751
752
753
754
755
756
757
758 private boolean replaceActiveTrackedNode(final TreeData currentData, final NodeSelector selector, final ImmutableNode newNode) {
759 final NodeTracker newTracker = currentData.getNodeTracker().replaceAndDetachTrackedNode(selector, newNode);
760 return structure.compareAndSet(currentData, currentData.updateNodeTracker(newTracker));
761 }
762
763
764
765
766
767
768
769
770
771 private boolean replaceDetachedTrackedNode(final TreeData currentData, final NodeSelector selector, final ImmutableNode newNode) {
772 final InMemoryNodeModel detachedNodeModel = currentData.getNodeTracker().getDetachedNodeModel(selector);
773 if (detachedNodeModel != null) {
774 detachedNodeModel.setRootNode(newNode);
775 return true;
776 }
777
778 return false;
779 }
780
781
782
783
784
785
786
787
788
789
790
791
792 public void replaceRoot(final ImmutableNode newRoot, final NodeKeyResolver<ImmutableNode> resolver) {
793 if (newRoot == null) {
794 throw new IllegalArgumentException("Replaced root node must not be null!");
795 }
796
797 final TreeData current = structure.get();
798
799 final TreeData temp = createTreeDataForRootAndTracker(newRoot, current.getNodeTracker());
800 structure.set(temp.updateNodeTracker(temp.getNodeTracker().update(newRoot, null, resolver, temp)));
801 }
802
803
804
805
806
807
808
809
810
811
812
813 public void replaceTrackedNode(final NodeSelector selector, final ImmutableNode newNode) {
814 if (newNode == null) {
815 throw new IllegalArgumentException("Replacement node must not be null!");
816 }
817
818 boolean done;
819 do {
820 final TreeData currentData = structure.get();
821 done = replaceDetachedTrackedNode(currentData, selector, newNode) || replaceActiveTrackedNode(currentData, selector, newNode);
822 } while (!done);
823 }
824
825
826
827
828
829
830
831
832
833
834 public Collection<NodeSelector> selectAndTrackNodes(final String key, final NodeKeyResolver<ImmutableNode> resolver) {
835 final Mutable<Collection<NodeSelector>> refSelectors = new MutableObject<>();
836 boolean done;
837 do {
838 final TreeData current = structure.get();
839 final List<ImmutableNode> nodes = resolver.resolveNodeKey(current.getRootNode(), key, current);
840 if (nodes.isEmpty()) {
841 return Collections.emptyList();
842 }
843 done = structure.compareAndSet(current, createSelectorsForTrackedNodes(refSelectors, nodes, current, resolver));
844 } while (!done);
845 return refSelectors.getValue();
846 }
847
848
849
850
851
852
853
854
855
856
857
858
859
860 public void setProperty(final String key, final NodeSelector selector, final Object value, final NodeKeyResolver<ImmutableNode> resolver) {
861 updateModel(tx -> {
862 boolean added = false;
863 final NodeUpdateData<ImmutableNode> updateData = resolver.resolveUpdateKey(tx.getQueryRoot(), key, value, tx.getCurrentData());
864 if (!updateData.getNewValues().isEmpty()) {
865 initializeAddTransaction(tx, key, updateData.getNewValues(), resolver);
866 added = true;
867 }
868 final boolean cleared = initializeClearTransaction(tx, updateData.getRemovedNodes());
869 final boolean updated = initializeUpdateTransaction(tx, updateData.getChangedValues());
870 return added || cleared || updated;
871 }, selector, resolver);
872 }
873
874 @Override
875 public void setProperty(final String key, final Object value, final NodeKeyResolver<ImmutableNode> resolver) {
876 setProperty(key, null, value, resolver);
877 }
878
879
880
881
882
883
884
885
886 @Override
887 public void setRootNode(final ImmutableNode newRoot) {
888 structure.set(createTreeData(initialRootNode(newRoot), structure.get()));
889 }
890
891
892
893
894
895
896
897
898
899
900 public Collection<NodeSelector> trackChildNodes(final String key, final NodeKeyResolver<ImmutableNode> resolver) {
901 final Mutable<Collection<NodeSelector>> refSelectors = new MutableObject<>();
902 boolean done;
903 do {
904 refSelectors.setValue(Collections.<NodeSelector>emptyList());
905 final TreeData current = structure.get();
906 final List<ImmutableNode> nodes = resolver.resolveNodeKey(current.getRootNode(), key, current);
907 if (nodes.size() == 1) {
908 final ImmutableNode node = nodes.get(0);
909 done = node.getChildren().isEmpty()
910 || structure.compareAndSet(current, createSelectorsForTrackedNodes(refSelectors, node.getChildren(), current, resolver));
911 } else {
912 done = true;
913 }
914 } while (!done);
915 return refSelectors.getValue();
916 }
917
918
919
920
921
922
923
924
925
926
927
928
929 public NodeSelector trackChildNodeWithCreation(final String key, final String childName, final NodeKeyResolver<ImmutableNode> resolver) {
930 final MutableObject<NodeSelector> refSelector = new MutableObject<>();
931 boolean done;
932
933 do {
934 final TreeData current = structure.get();
935 final List<ImmutableNode> nodes = resolver.resolveNodeKey(current.getRootNode(), key, current);
936 if (nodes.size() != 1) {
937 throw new ConfigurationRuntimeException("Key does not select a single node: " + key);
938 }
939
940 final ImmutableNode parent = nodes.get(0);
941 final TreeData newData = createDataWithTrackedChildNode(current, parent, childName, resolver, refSelector);
942
943 done = structure.compareAndSet(current, newData);
944 } while (!done);
945
946 return refSelector.getValue();
947 }
948
949
950
951
952
953
954
955
956
957
958
959
960 public void trackNode(final NodeSelector selector, final NodeKeyResolver<ImmutableNode> resolver) {
961 boolean done;
962 do {
963 final TreeData current = structure.get();
964 final NodeTracker newTracker = current.getNodeTracker().trackNode(current.getRootNode(), selector, resolver, current);
965 done = structure.compareAndSet(current, current.updateNodeTracker(newTracker));
966 } while (!done);
967 }
968
969
970
971
972
973
974
975
976
977 public void untrackNode(final NodeSelector selector) {
978 boolean done;
979 do {
980 final TreeData current = structure.get();
981 final NodeTracker newTracker = current.getNodeTracker().untrackNode(selector);
982 done = structure.compareAndSet(current, current.updateNodeTracker(newTracker));
983 } while (!done);
984 }
985
986
987
988
989
990
991
992
993
994
995 private void updateModel(final TransactionInitializer txInit, final NodeSelector selector, final NodeKeyResolver<ImmutableNode> resolver) {
996 boolean done;
997
998 do {
999 final TreeData currentData = getTreeData();
1000 done = executeTransactionOnDetachedTrackedNode(txInit, selector, currentData, resolver)
1001 || executeTransactionOnCurrentStructure(txInit, selector, currentData, resolver);
1002 } while (!done);
1003 }
1004 }