1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24 package org.modeshape.graph.connector.base;
25
26 import java.util.ArrayList;
27 import java.util.Collection;
28 import java.util.Collections;
29 import java.util.HashMap;
30 import java.util.HashSet;
31 import java.util.Iterator;
32 import java.util.List;
33 import java.util.ListIterator;
34 import java.util.Map;
35 import java.util.Set;
36 import java.util.UUID;
37 import net.jcip.annotations.NotThreadSafe;
38 import org.modeshape.common.util.StringUtil;
39 import org.modeshape.graph.ExecutionContext;
40 import org.modeshape.graph.GraphI18n;
41 import org.modeshape.graph.Location;
42 import org.modeshape.graph.connector.RepositorySourceException;
43 import org.modeshape.graph.connector.UuidAlreadyExistsException;
44 import org.modeshape.graph.property.Name;
45 import org.modeshape.graph.property.NamespaceRegistry;
46 import org.modeshape.graph.property.Path;
47 import org.modeshape.graph.property.PathNotFoundException;
48 import org.modeshape.graph.property.Property;
49 import org.modeshape.graph.property.PropertyType;
50 import org.modeshape.graph.property.Reference;
51 import org.modeshape.graph.property.UuidFactory;
52 import org.modeshape.graph.property.ValueFactory;
53 import org.modeshape.graph.property.Path.Segment;
54 import org.modeshape.graph.query.QueryResults;
55 import org.modeshape.graph.request.AccessQueryRequest;
56 import org.modeshape.graph.request.FullTextSearchRequest;
57
58
59
60
61
62
63
64 @NotThreadSafe
65 public abstract class MapTransaction<NodeType extends MapNode, WorkspaceType extends MapWorkspace<NodeType>>
66 extends BaseTransaction<NodeType, WorkspaceType> {
67
68
69 private Map<String, WorkspaceChanges> changesByWorkspaceName;
70
71
72
73
74
75
76
77
78 protected MapTransaction( ExecutionContext context,
79 Repository<NodeType, WorkspaceType> repository,
80 UUID rootNodeUuid ) {
81 super(context, repository, rootNodeUuid);
82 }
83
84
85
86
87
88
89
90
91
92
93
94 protected WorkspaceChanges getChangesFor( WorkspaceType workspace,
95 boolean createIfMissing ) {
96 if (changesByWorkspaceName == null) {
97 if (!createIfMissing) return null;
98 WorkspaceChanges changes = new WorkspaceChanges(workspace);
99 changesByWorkspaceName = new HashMap<String, WorkspaceChanges>();
100 changesByWorkspaceName.put(workspace.getName(), changes);
101 return changes;
102 }
103 WorkspaceChanges changes = changesByWorkspaceName.get(workspace.getName());
104 if (changes == null && createIfMissing) {
105 changes = new WorkspaceChanges(workspace);
106 changesByWorkspaceName.put(workspace.getName(), changes);
107 }
108 return changes;
109 }
110
111
112
113
114
115
116
117 public NodeType getNode( WorkspaceType workspace,
118 Location location ) {
119 assert location != null;
120
121 UUID uuid = location.getUuid();
122 if (uuid != null) {
123 WorkspaceChanges changes = getChangesFor(workspace, false);
124 NodeType node = null;
125 if (changes != null) {
126
127 if (changes.isRemoved(uuid)) {
128
129 Path lowestExisting = null;
130 if (location.hasPath()) {
131 getNode(workspace, location.getPath(), location);
132 assert false;
133 }
134 lowestExisting = pathFactory.createRootPath();
135 throw new PathNotFoundException(location, lowestExisting, GraphI18n.nodeDoesNotExist.text(readable(location)));
136 }
137
138 node = changes.getChangedOrAdded(uuid);
139 if (node != null) return node;
140 }
141
142 node = workspace.getNode(uuid);
143 if (node != null) return node;
144 }
145
146 if (location.hasPath()) {
147 return getNode(workspace, location.getPath(), location);
148 }
149
150 Path lowestExisting = pathFactory.createRootPath();
151 throw new PathNotFoundException(location, lowestExisting, GraphI18n.nodeDoesNotExist.text(readable(location)));
152 }
153
154
155
156
157
158
159
160
161 protected NodeType findNode( WorkspaceType workspace,
162 UUID uuid ) {
163 WorkspaceChanges changes = getChangesFor(workspace, false);
164 NodeType node = null;
165 if (changes != null) {
166
167 if (changes.isRemoved(uuid)) {
168
169 return null;
170 }
171
172 node = changes.getChangedOrAdded(uuid);
173 if (node != null) return node;
174 }
175
176 node = workspace.getNode(uuid);
177 return node;
178 }
179
180
181
182
183
184
185
186 protected void destroyNode( WorkspaceType workspace,
187 NodeType node ) {
188 WorkspaceChanges changes = getChangesFor(workspace, true);
189 destroyNode(changes, workspace, node);
190 }
191
192
193
194
195
196
197
198
199 private void destroyNode( WorkspaceChanges changes,
200 WorkspaceType workspace,
201 NodeType node ) {
202
203 for (UUID childUuid : node.getChildren()) {
204 NodeType child = changes.getChangedOrAdded(childUuid);
205 if (child == null) {
206
207 child = workspace.getNode(childUuid);
208 }
209 destroyNode(changes, workspace, child);
210 }
211
212 UUID uuid = node.getUuid();
213 changes.removed(uuid);
214 }
215
216
217
218
219
220
221
222 @SuppressWarnings( "unchecked" )
223 public NodeType addChild( WorkspaceType workspace,
224 NodeType parent,
225 Name name,
226 int index,
227 UUID uuid,
228 Iterable<Property> properties ) {
229 if (uuid == null) {
230 uuid = UUID.randomUUID();
231 }
232 WorkspaceChanges changes = getChangesFor(workspace, true);
233
234
235 if (!parent.hasChanges()) {
236 parent = findNode(workspace, parent.getUuid());
237 }
238
239 NodeType newNode = null;
240 if (index < 0) {
241
242 int snsIndex = 1;
243 for (NodeType child : getChildren(workspace, parent)) {
244 if (child.getName().getName().equals(name)) ++snsIndex;
245 }
246
247
248 newNode = createNode(uuid, pathFactory.createSegment(name, snsIndex), parent.getUuid(), properties);
249
250 parent = (NodeType)parent.withChild(uuid);
251 } else {
252 int snsIndex = 0;
253 ListIterator<NodeType> existingSiblings = getChildren(workspace, parent).listIterator(index);
254 while (existingSiblings.hasNext()) {
255 NodeType existingSibling = existingSiblings.next();
256 Segment existingSegment = existingSibling.getName();
257 if (existingSegment.getName().equals(name)) {
258 int existingIndex = existingSegment.getIndex();
259 if (snsIndex == 0) snsIndex = existingIndex;
260 existingSibling = (NodeType)existingSibling.withName(pathFactory.createSegment(name, existingIndex + 1));
261 changes.changed(existingSibling);
262 }
263 }
264
265 newNode = createNode(uuid, pathFactory.createSegment(name, snsIndex + 1), parent.getUuid(), properties);
266
267 parent = (NodeType)parent.withChild(index, uuid);
268 }
269 changes.created(newNode);
270 changes.changed(parent);
271 return newNode;
272 }
273
274
275
276
277
278
279
280
281 @SuppressWarnings( "unchecked" )
282 public Location addChild( WorkspaceType workspace,
283 NodeType parent,
284 NodeType newChild,
285 NodeType beforeOtherChild,
286 Name desiredName ) {
287
288 if (!parent.hasChanges()) {
289 parent = findNode(workspace, parent.getUuid());
290 }
291
292
293 Segment newChildSegment = newChild.getName();
294 Name newChildName = newChildSegment.getName();
295 int snsIndex = newChildSegment.getIndex();
296 UUID newChildUuid = newChild.getUuid();
297
298
299 NodeType oldParent = getParent(workspace, newChild);
300
301
302 WorkspaceChanges changes = getChangesFor(workspace, true);
303
304
305
306
307
308
309 int oldIndex = -1;
310 if (oldParent != null) {
311
312 oldIndex = oldParent.getChildren().indexOf(newChildUuid);
313 if (oldParent == parent) {
314 oldParent = (NodeType)oldParent.withoutChild(newChildUuid);
315 changes.changed(oldParent);
316 parent = oldParent;
317 } else {
318 oldParent = (NodeType)oldParent.withoutChild(newChildUuid);
319 changes.changed(oldParent);
320 }
321
322
323 List<NodeType> siblings = getChildren(workspace, oldParent);
324 for (ListIterator<NodeType> iter = siblings.listIterator(oldIndex); iter.hasNext();) {
325 NodeType sibling = iter.next();
326 if (sibling.getName().getName().equals(newChildName)) {
327 sibling = (NodeType)sibling.withName(pathFactory.createSegment(newChildName, snsIndex++));
328 changes.changed(sibling);
329 }
330 }
331 }
332
333
334 int index = parent.getChildren().size();
335 if (beforeOtherChild != null) {
336 if (!beforeOtherChild.getParent().equals(parent.getUuid())) {
337
338 throw new RepositorySourceException(null);
339 }
340 UUID otherChild = beforeOtherChild.getUuid();
341 index = parent.getChildren().indexOf(otherChild);
342 if (index == -1) index = parent.getChildren().size();
343 }
344 assert index >= 0;
345
346
347 newChildName = desiredName != null ? desiredName : newChildName;
348
349
350 ListIterator<NodeType> existingSiblings = getChildren(workspace, parent).listIterator();
351 int i = 0;
352 snsIndex = 1;
353 Segment childName = null;
354 while (existingSiblings.hasNext()) {
355 NodeType existingSibling = existingSiblings.next();
356 Segment existingSegment = existingSibling.getName();
357 if (i < index) {
358
359 if (existingSegment.getName().equals(newChildName)) {
360 ++snsIndex;
361 }
362 } else {
363 if (i == index) {
364
365 childName = pathFactory.createSegment(newChildName, snsIndex);
366 }
367 if (existingSegment.getName().equals(newChildName)) {
368 existingSibling = (NodeType)existingSibling.withName(pathFactory.createSegment(newChildName, ++snsIndex));
369 changes.changed(existingSibling);
370 }
371 }
372 ++i;
373 }
374 if (childName == null) {
375
376 childName = pathFactory.createSegment(newChildName, snsIndex);
377 }
378
379
380 newChild = (NodeType)newChild.withName(childName).withParent(parent.getUuid());
381 parent = (NodeType)parent.withChild(index, newChildUuid);
382 changes.changed(newChild);
383 changes.changed(parent);
384 return Location.create(pathFor(workspace, newChild), newChildUuid);
385 }
386
387
388
389
390
391
392
393
394
395
396
397 @SuppressWarnings( "unchecked" )
398 protected NodeType createNode( UUID uuid,
399 Segment name,
400 UUID parentUuid,
401 Iterable<Property> properties ) {
402 return (NodeType)new MapNode(uuid, name, parentUuid, properties, null);
403 }
404
405
406
407
408
409
410
411 public NodeType getChild( WorkspaceType workspace,
412 NodeType parent,
413 Segment childSegment ) {
414 List<NodeType> children = new Children(parent.getChildren(), workspace);
415 for (NodeType child : children) {
416 if (child.getName().equals(childSegment)) return child;
417 }
418 return null;
419 }
420
421
422
423
424
425
426
427 public List<NodeType> getChildren( WorkspaceType workspace,
428 NodeType node ) {
429 List<UUID> children = node.getChildren();
430 if (children.isEmpty()) return Collections.emptyList();
431 return new Children(children, workspace);
432 }
433
434
435
436
437
438
439
440 public NodeType getParent( WorkspaceType workspace,
441 NodeType node ) {
442 UUID parentUuid = node.getParent();
443 if (parentUuid == null) return null;
444 return getNode(workspace, Location.create(parentUuid));
445 }
446
447
448
449
450
451
452
453 @SuppressWarnings( "unchecked" )
454 public void removeAllChildren( WorkspaceType workspace,
455 NodeType node ) {
456 for (NodeType child : getChildren(workspace, node)) {
457 destroyNode(workspace, child);
458 }
459 node = (NodeType)node.withoutChildren();
460 getChangesFor(workspace, true).changed(node);
461 }
462
463
464
465
466
467
468
469 @SuppressWarnings( "unchecked" )
470 public Location removeNode( WorkspaceType workspace,
471 NodeType node ) {
472 NodeType parent = getParent(workspace, node);
473 if (parent == null) {
474
475 WorkspaceChanges changes = getChangesFor(workspace, true);
476 changes.removeAll(createNode(rootNodeUuid, null, null, null));
477 return Location.create(pathFactory.createRootPath(), rootNodeUuid);
478 }
479 Location result = Location.create(pathFor(workspace, node), node.getUuid());
480
481
482 int index = parent.getChildren().indexOf(node.getUuid());
483 assert index != -1;
484 Name name = node.getName().getName();
485 int snsIndex = node.getName().getIndex();
486 WorkspaceChanges changes = getChangesFor(workspace, true);
487
488 parent = (NodeType)parent.withoutChild(node.getUuid());
489 changes.changed(parent);
490
491
492 List<NodeType> siblings = getChildren(workspace, parent);
493 if (index < siblings.size()) {
494 for (ListIterator<NodeType> iter = siblings.listIterator(index); iter.hasNext();) {
495 NodeType sibling = iter.next();
496 if (sibling.getName().getName().equals(name)) {
497 sibling = (NodeType)sibling.withName(pathFactory.createSegment(name, snsIndex++));
498 changes.changed(sibling);
499 }
500 }
501 }
502
503 destroyNode(changes, workspace, node);
504 return result;
505 }
506
507
508
509
510
511
512
513 @SuppressWarnings( "unchecked" )
514 public NodeType removeProperty( WorkspaceType workspace,
515 NodeType node,
516 Name propertyName ) {
517 NodeType copy = (NodeType)node.withoutProperty(propertyName);
518 if (copy != node) {
519 WorkspaceChanges changes = getChangesFor(workspace, true);
520 changes.changed(copy);
521 }
522 return copy;
523 }
524
525
526
527
528
529
530
531 @SuppressWarnings( "unchecked" )
532 public NodeType setProperties( WorkspaceType workspace,
533 NodeType node,
534 Iterable<Property> propertiesToSet,
535 Iterable<Name> propertiesToRemove,
536 boolean removeAllExisting ) {
537 NodeType copy = (NodeType)node.withProperties(propertiesToSet, propertiesToRemove, removeAllExisting);
538 if (copy != node) {
539 WorkspaceChanges changes = getChangesFor(workspace, true);
540 changes.changed(copy);
541 }
542 return copy;
543 }
544
545
546
547
548
549
550
551
552 @SuppressWarnings( "unchecked" )
553 public NodeType copyNode( WorkspaceType originalWorkspace,
554 NodeType original,
555 WorkspaceType newWorkspace,
556 NodeType newParent,
557 Name desiredName,
558 boolean recursive ) {
559 if (desiredName == null) desiredName = original.getName().getName();
560
561 UUID uuid = UUID.randomUUID();
562 NodeType copy = addChild(newWorkspace, newParent, desiredName, -1, uuid, original.getProperties().values());
563
564 Map<UUID, UUID> oldToNewUuids = new HashMap<UUID, UUID>();
565 oldToNewUuids.put(original.getUuid(), uuid);
566
567 if (recursive) {
568 WorkspaceChanges changes = getChangesFor(newWorkspace, true);
569
570 for (NodeType originalChild : getChildren(originalWorkspace, original)) {
571 NodeType newChild = copyBranch(originalWorkspace,
572 originalChild,
573 changes,
574 newWorkspace,
575 copy,
576 false,
577 oldToNewUuids);
578 copy = (NodeType)copy.withChild(newChild.getUuid());
579 }
580 }
581
582
583 WorkspaceChanges changes = getChangesFor(newWorkspace, true);
584 changes.changed(copy);
585
586
587
588 UuidFactory uuidFactory = context.getValueFactories().getUuidFactory();
589 ValueFactory<Reference> referenceFactory = context.getValueFactories().getReferenceFactory();
590 for (Map.Entry<UUID, UUID> oldToNew : oldToNewUuids.entrySet()) {
591 NodeType newNode = findNode(newWorkspace, oldToNew.getValue());
592 assert newNode != null;
593
594 for (Map.Entry<Name, Property> entry : newNode.getProperties().entrySet()) {
595 Property property = entry.getValue();
596
597 List<Object> newValues = new ArrayList<Object>();
598 boolean foundReference = false;
599 for (Iterator<?> iter = property.getValues(); iter.hasNext();) {
600 Object value = iter.next();
601 PropertyType type = PropertyType.discoverType(value);
602 if (type == PropertyType.REFERENCE) {
603 UUID oldReferencedUuid = uuidFactory.create(value);
604 UUID newReferencedUuid = oldToNewUuids.get(oldReferencedUuid);
605 if (newReferencedUuid != null) {
606 newValues.add(referenceFactory.create(newReferencedUuid));
607 foundReference = true;
608 }
609 } else {
610 newValues.add(value);
611 }
612 }
613
614 if (foundReference) {
615 Property newProperty = propertyFactory.create(property.getName(), newValues);
616 newNode = (NodeType)newNode.withProperty(newProperty);
617 changes.changed(newNode);
618 }
619 }
620 }
621
622 return copy;
623 }
624
625 protected void print( WorkspaceType workspace,
626 NodeType node,
627 int level ) {
628 StringBuilder sb = new StringBuilder();
629 sb.append(StringUtil.createString(' ', level * 2));
630 sb.append(readable(node.getName())).append(" (").append(node.getUuid()).append(") {");
631 boolean first = true;
632 for (Property property : node.getProperties().values()) {
633 if (first) first = false;
634 else sb.append(',');
635 sb.append(readable(property.getName())).append('=');
636 if (property.isMultiple()) sb.append(property.getValuesAsArray());
637 else sb.append(readable(property.getFirstValue()));
638 }
639 sb.append('}');
640 System.out.println(sb);
641 for (NodeType child : getChildren(workspace, node)) {
642 print(workspace, child, level + 1);
643 }
644 }
645
646
647
648
649
650
651
652
653
654 @SuppressWarnings( "unchecked" )
655 public NodeType cloneNode( WorkspaceType originalWorkspace,
656 NodeType original,
657 WorkspaceType newWorkspace,
658 NodeType newParent,
659 Name desiredName,
660 Segment desiredSegment,
661 boolean removeExisting,
662 java.util.Set<Location> removedExistingNodes )
663 throws org.modeshape.graph.connector.UuidAlreadyExistsException {
664
665 WorkspaceChanges changes = getChangesFor(newWorkspace, true);
666 NodeType newNode = null;
667
668
669
670
671
672 Set<UUID> uuidsInFromBranch = getUuidsUnderNode(originalWorkspace, original);
673 NodeType existing = null;
674 if (removeExisting) {
675
676
677 for (UUID uuid : uuidsInFromBranch) {
678 if (null != (existing = findNode(newWorkspace, uuid))) {
679 if (removedExistingNodes != null) {
680 Path path = pathFor(newWorkspace, existing);
681 removedExistingNodes.add(Location.create(path, uuid));
682 }
683 removeNode(newWorkspace, existing);
684 }
685 }
686
687
688 } else {
689 uuidsInFromBranch.add(original.getUuid());
690 for (UUID uuid : uuidsInFromBranch) {
691 if (null != (existing = findNode(newWorkspace, uuid))) {
692 NamespaceRegistry namespaces = context.getNamespaceRegistry();
693 String path = pathFor(newWorkspace, existing).getString(namespaces);
694 throw new UuidAlreadyExistsException(getRepository().getSourceName(), uuid, path, newWorkspace.getName());
695 }
696 }
697 }
698
699 UUID uuid = original.getUuid();
700 if (desiredSegment != null) {
701
702 int index = 0;
703 List<NodeType> children = getChildren(newWorkspace, newParent);
704 NodeType existingWithSameName = null;
705 for (NodeType child : children) {
706 if (child.getName().equals(desiredSegment)) {
707 existingWithSameName = child;
708 break;
709 }
710 ++index;
711 }
712 if (index == children.size()) {
713
714 newNode = addChild(newWorkspace, newParent, original.getName().getName(), -1, uuid, original.getProperties()
715 .values());
716 } else {
717
718 newNode = createNode(uuid, desiredSegment, newParent.getUuid(), original.getProperties().values());
719
720
721 assert existingWithSameName != null;
722 destroyNode(newWorkspace, existingWithSameName);
723
724
725 newParent = (NodeType)newParent.withoutChild(existingWithSameName.getUuid()).withChild(index, uuid);
726 }
727 } else {
728
729 existing = findNode(newWorkspace, original.getUuid());
730 if (existing != null) {
731 if (removedExistingNodes != null) {
732 Path path = pathFor(newWorkspace, existing);
733 removedExistingNodes.add(Location.create(path, original.getUuid()));
734 }
735 removeNode(newWorkspace, existing);
736 }
737
738 if (desiredName != null) {
739
740 newNode = addChild(newWorkspace, newParent, desiredName, -1, uuid, original.getProperties().values());
741 } else {
742
743 newNode = addChild(newWorkspace, newParent, original.getName().getName(), -1, uuid, original.getProperties()
744 .values());
745 }
746 }
747
748
749 if (!newParent.hasChanges()) {
750 newParent = findNode(newWorkspace, newParent.getUuid());
751 }
752
753
754 for (NodeType originalChild : getChildren(originalWorkspace, original)) {
755 NodeType newChild = copyBranch(originalWorkspace, originalChild, changes, newWorkspace, newNode, true, null);
756 newNode = (NodeType)newNode.withChild(newChild.getUuid());
757 }
758 changes.created(newNode);
759 changes.changed(newParent);
760
761 return newNode;
762 }
763
764
765
766
767
768
769
770
771
772 protected Set<UUID> getUuidsUnderNode( WorkspaceType workspace,
773 NodeType node ) {
774 Set<UUID> uuids = new HashSet<UUID>();
775 uuidsUnderNode(workspace, node, uuids);
776 return uuids;
777 }
778
779 private void uuidsUnderNode( WorkspaceType workspace,
780 NodeType node,
781 Set<UUID> accumulator ) {
782 for (NodeType child : getChildren(workspace, node)) {
783 accumulator.add(child.getUuid());
784 uuidsUnderNode(workspace, child, accumulator);
785 }
786 }
787
788 @SuppressWarnings( "unchecked" )
789 protected NodeType copyBranch( WorkspaceType originalWorkspace,
790 NodeType original,
791 WorkspaceChanges newWorkspaceChanges,
792 WorkspaceType newWorkspace,
793 NodeType newParent,
794 boolean reuseUuid,
795 Map<UUID, UUID> oldToNewUuids ) {
796
797 UUID copyUuid = reuseUuid ? original.getUuid() : UUID.randomUUID();
798 NodeType copy = createNode(copyUuid, original.getName(), newParent.getUuid(), original.getProperties().values());
799 newWorkspaceChanges.created(copy);
800 if (!reuseUuid) {
801 assert oldToNewUuids != null;
802 oldToNewUuids.put(original.getUuid(), copy.getUuid());
803 }
804
805
806 for (NodeType originalChild : getChildren(originalWorkspace, original)) {
807 NodeType newChild = copyBranch(originalWorkspace,
808 originalChild,
809 newWorkspaceChanges,
810 newWorkspace,
811 copy,
812 reuseUuid,
813 oldToNewUuids);
814 copy = (NodeType)copy.withChild(newChild.getUuid());
815 }
816 newWorkspaceChanges.changed(copy);
817 return copy;
818 }
819
820
821
822
823
824
825
826
827
828
829
830 public QueryResults query( WorkspaceType workspace,
831 AccessQueryRequest accessQuery ) {
832 return null;
833 }
834
835
836
837
838
839
840
841
842
843
844
845 public QueryResults search( WorkspaceType workspace,
846 FullTextSearchRequest search ) {
847 return null;
848 }
849
850
851
852
853
854
855 @Override
856 public void commit() {
857 super.commit();
858
859 if (changesByWorkspaceName != null) {
860 for (WorkspaceChanges changes : changesByWorkspaceName.values()) {
861 changes.commit();
862 }
863 changesByWorkspaceName.clear();
864 }
865 }
866
867
868
869
870
871
872 @Override
873 public void rollback() {
874 super.rollback();
875 if (changesByWorkspaceName != null) {
876 changesByWorkspaceName.clear();
877 }
878 }
879
880
881
882
883 protected class WorkspaceChanges {
884 private final WorkspaceType workspace;
885 private final Map<UUID, NodeType> changedOrAddedNodes = new HashMap<UUID, NodeType>();
886 private final Set<UUID> removedNodes = new HashSet<UUID>();
887 private boolean removeAll = false;
888
889 protected WorkspaceChanges( WorkspaceType workspace ) {
890 this.workspace = workspace;
891 }
892
893 public WorkspaceType getWorkspace() {
894 return workspace;
895 }
896
897 public void removeAll( NodeType newRootNode ) {
898 changedOrAddedNodes.clear();
899 removedNodes.clear();
900 removeAll = true;
901 changedOrAddedNodes.put(newRootNode.getUuid(), newRootNode);
902 }
903
904 public boolean isRemoved( UUID uuid ) {
905 return removedNodes.contains(uuid);
906 }
907
908 public NodeType getChangedOrAdded( UUID uuid ) {
909 return changedOrAddedNodes.get(uuid);
910 }
911
912 public void removed( UUID uuid ) {
913 removedNodes.add(uuid);
914 changedOrAddedNodes.remove(uuid);
915 }
916
917 public void created( NodeType node ) {
918 UUID uuid = node.getUuid();
919 removedNodes.remove(uuid);
920 changedOrAddedNodes.put(uuid, node);
921 }
922
923 public void changed( NodeType node ) {
924 UUID uuid = node.getUuid();
925 assert !removedNodes.contains(uuid);
926 changedOrAddedNodes.put(uuid, node);
927 }
928
929 @SuppressWarnings( "unchecked" )
930 public void commit() {
931 if (removeAll) {
932 workspace.removeAll();
933 }
934 for (NodeType changed : changedOrAddedNodes.values()) {
935 workspace.putNode((NodeType)changed.freeze());
936 }
937 for (UUID uuid : removedNodes) {
938
939
940 workspace.removeNode(uuid);
941 }
942 }
943 }
944
945 protected class Children implements List<NodeType> {
946 private final List<UUID> uuids;
947 private final WorkspaceType workspace;
948 private final List<NodeType> cache;
949
950 protected Children( List<UUID> uuids,
951 WorkspaceType workspace ) {
952 this.uuids = uuids;
953 this.workspace = workspace;
954 this.cache = new ArrayList<NodeType>(uuids.size());
955 }
956
957
958
959
960
961
962 public int size() {
963 return uuids.size();
964 }
965
966
967
968
969
970
971 public boolean contains( Object o ) {
972 if (o instanceof MapNode) {
973 return uuids.contains(((MapNode)o).getUuid());
974 }
975 return false;
976 }
977
978
979
980
981
982
983 public boolean containsAll( Collection<?> c ) {
984 for (Object o : c) {
985 if (!contains(o)) return false;
986 }
987 return true;
988 }
989
990
991
992
993
994
995 public NodeType get( int index ) {
996 NodeType result = null;
997 if (cache.size() > index) {
998 result = cache.get(index);
999 }
1000 if (result == null) {
1001 UUID uuid = uuids.get(index);
1002
1003 result = findNode(workspace, uuid);
1004 if (result == null) {
1005
1006 result = getNode(workspace, Location.create(uuid));
1007 }
1008 while (cache.size() <= index) {
1009 cache.add(null);
1010 }
1011 cache.set(index, result);
1012 }
1013 return result;
1014 }
1015
1016
1017
1018
1019
1020
1021 public int indexOf( Object o ) {
1022 if (o instanceof MapNode) {
1023 return uuids.indexOf(((MapNode)o).getUuid());
1024 }
1025 return -1;
1026 }
1027
1028
1029
1030
1031
1032
1033 public int lastIndexOf( Object o ) {
1034
1035 return indexOf(0);
1036 }
1037
1038
1039
1040
1041
1042
1043 public boolean isEmpty() {
1044 return uuids.isEmpty();
1045 }
1046
1047
1048
1049
1050
1051
1052 public Iterator<NodeType> iterator() {
1053 return listIterator(0);
1054 }
1055
1056
1057
1058
1059
1060
1061 public ListIterator<NodeType> listIterator() {
1062 return listIterator(0);
1063 }
1064
1065
1066
1067
1068
1069
1070 public ListIterator<NodeType> listIterator( final int index ) {
1071 final int maxIndex = size();
1072 return new ListIterator<NodeType>() {
1073 private int current = index;
1074
1075 public boolean hasNext() {
1076 return current < maxIndex;
1077 }
1078
1079 public NodeType next() {
1080 return get(current++);
1081 }
1082
1083 public void remove() {
1084 throw new UnsupportedOperationException();
1085 }
1086
1087 public void add( NodeType e ) {
1088 throw new UnsupportedOperationException();
1089 }
1090
1091 public boolean hasPrevious() {
1092 return current > 0;
1093 }
1094
1095 public int nextIndex() {
1096 return current;
1097 }
1098
1099 public NodeType previous() {
1100 return get(--current);
1101 }
1102
1103 public int previousIndex() {
1104 return current - 1;
1105 }
1106
1107 public void set( NodeType e ) {
1108 throw new UnsupportedOperationException();
1109 }
1110 };
1111 }
1112
1113
1114
1115
1116
1117
1118 public List<NodeType> subList( int fromIndex,
1119 int toIndex ) {
1120 return new Children(uuids.subList(fromIndex, toIndex), workspace);
1121 }
1122
1123
1124
1125
1126
1127
1128 public Object[] toArray() {
1129 final int length = uuids.size();
1130 Object[] result = new Object[length];
1131 for (int i = 0; i != length; ++i) {
1132 result[i] = get(i);
1133 }
1134 return result;
1135 }
1136
1137
1138
1139
1140
1141
1142 @SuppressWarnings( "unchecked" )
1143 public <T> T[] toArray( T[] a ) {
1144 final int length = uuids.size();
1145 for (int i = 0; i != length; ++i) {
1146 a[i] = (T)get(i);
1147 }
1148 return a;
1149 }
1150
1151
1152
1153
1154
1155
1156 public boolean add( NodeType e ) {
1157 throw new UnsupportedOperationException();
1158 }
1159
1160
1161
1162
1163
1164
1165 public void add( int index,
1166 NodeType element ) {
1167 throw new UnsupportedOperationException();
1168 }
1169
1170
1171
1172
1173
1174
1175 public boolean addAll( Collection<? extends NodeType> c ) {
1176 throw new UnsupportedOperationException();
1177 }
1178
1179
1180
1181
1182
1183
1184 public boolean addAll( int index,
1185 Collection<? extends NodeType> c ) {
1186 throw new UnsupportedOperationException();
1187 }
1188
1189
1190
1191
1192
1193
1194 public void clear() {
1195 throw new UnsupportedOperationException();
1196 }
1197
1198
1199
1200
1201
1202
1203 public boolean remove( Object o ) {
1204 throw new UnsupportedOperationException();
1205 }
1206
1207
1208
1209
1210
1211
1212 public NodeType remove( int index ) {
1213 throw new UnsupportedOperationException();
1214 }
1215
1216
1217
1218
1219
1220
1221 public boolean removeAll( Collection<?> c ) {
1222 throw new UnsupportedOperationException();
1223 }
1224
1225
1226
1227
1228
1229
1230 public boolean retainAll( Collection<?> c ) {
1231 throw new UnsupportedOperationException();
1232 }
1233
1234
1235
1236
1237
1238
1239 public NodeType set( int index,
1240 NodeType element ) {
1241 throw new UnsupportedOperationException();
1242 }
1243 }
1244 }