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.jcr;
25
26 import java.util.Collection;
27 import java.util.Iterator;
28 import java.util.LinkedList;
29 import java.util.List;
30 import java.util.NoSuchElementException;
31 import javax.jcr.AccessDeniedException;
32 import javax.jcr.Node;
33 import javax.jcr.NodeIterator;
34 import javax.jcr.PathNotFoundException;
35 import javax.jcr.Property;
36 import javax.jcr.PropertyIterator;
37 import javax.jcr.PropertyType;
38 import javax.jcr.ReferentialIntegrityException;
39 import javax.jcr.RepositoryException;
40 import javax.jcr.UnsupportedRepositoryOperationException;
41 import javax.jcr.Value;
42 import javax.jcr.version.Version;
43 import javax.jcr.version.VersionException;
44 import javax.jcr.version.VersionHistory;
45 import javax.jcr.version.VersionIterator;
46 import org.modeshape.graph.Graph;
47 import org.modeshape.graph.connector.RepositorySourceException;
48 import org.modeshape.graph.property.Name;
49 import org.modeshape.graph.property.Path;
50 import org.modeshape.graph.property.Reference;
51 import org.modeshape.graph.property.Path.Segment;
52
53
54
55
56 class JcrVersionHistoryNode extends JcrNode implements VersionHistory {
57
58 private static final String[] EMPTY_STRING_ARRAY = new String[0];
59
60 public JcrVersionHistoryNode( AbstractJcrNode node ) {
61 super(node.cache, node.nodeId, node.location);
62
63 assert !node.isRoot() : "Version histories should always be located in the /jcr:system/jcr:versionStorage subgraph";
64 }
65
66
67
68
69
70 private AbstractJcrNode versionLabels() throws RepositoryException {
71 Segment segment = segmentFrom(JcrLexicon.VERSION_LABELS);
72 return nodeInfo().getChild(segment).getPayload().getJcrNode();
73 }
74
75
76
77
78 @Override
79 public VersionIterator getAllVersions() throws RepositoryException {
80 return new JcrVersionIterator(getNodes());
81 }
82
83
84
85
86 @Override
87 public Version getRootVersion() throws RepositoryException {
88
89 Segment segment = context().getValueFactories().getPathFactory().createSegment(JcrLexicon.ROOT_VERSION);
90 try {
91 return (JcrVersionNode)nodeInfo().getChild(segment).getPayload().getJcrNode();
92 } catch (org.modeshape.graph.property.PathNotFoundException e) {
93 String msg = JcrI18n.childNotFoundUnderNode.text(segment, getPath(), cache.workspaceName());
94 throw new PathNotFoundException(msg);
95 } catch (RepositorySourceException e) {
96 throw new RepositoryException(e.getLocalizedMessage(), e);
97 }
98 }
99
100
101
102
103 @Override
104 public JcrVersionNode getVersion( String versionName ) throws VersionException, RepositoryException {
105 try {
106 AbstractJcrNode version = getNode(versionName);
107 return (JcrVersionNode)version;
108 } catch (PathNotFoundException pnfe) {
109 throw new VersionException(JcrI18n.invalidVersionName.text(versionName, getPath()));
110 }
111 }
112
113
114
115
116 @Override
117 public JcrVersionNode getVersionByLabel( String label ) throws VersionException, RepositoryException {
118 Property prop = versionLabels().getProperty(label);
119 if (prop == null) throw new VersionException(JcrI18n.invalidVersionLabel.text(label, getPath()));
120
121 AbstractJcrNode version = session().getNodeByUUID(prop.getString());
122
123 assert version != null;
124
125 return (JcrVersionNode)version;
126 }
127
128
129
130
131 @Override
132 public String[] getVersionLabels() throws RepositoryException {
133 PropertyIterator iter = versionLabels().getProperties();
134
135 String[] labels = new String[(int)iter.getSize()];
136 for (int i = 0; iter.hasNext(); i++) {
137 labels[i] = iter.nextProperty().getName();
138 }
139
140 return labels;
141 }
142
143
144
145
146
147
148
149
150 @SuppressWarnings( "deprecation" )
151 private Collection<String> versionLabelsFor( Version version ) throws RepositoryException {
152 if (!version.getParent().equals(this)) {
153 throw new VersionException(JcrI18n.invalidVersion.text(version.getPath(), getPath()));
154 }
155
156 String versionUuid = version.getUUID();
157
158 PropertyIterator iter = versionLabels().getProperties();
159
160 List<String> labels = new LinkedList<String>();
161 for (int i = 0; iter.hasNext(); i++) {
162 Property prop = iter.nextProperty();
163
164 if (versionUuid.equals(prop.getString())) {
165 labels.add(prop.getName());
166 }
167 }
168
169 return labels;
170 }
171
172
173
174
175 @Override
176 public String[] getVersionLabels( Version version ) throws RepositoryException {
177 return versionLabelsFor(version).toArray(EMPTY_STRING_ARRAY);
178 }
179
180
181
182
183 @Override
184 public String getVersionableUUID() throws RepositoryException {
185 return getProperty(JcrLexicon.VERSIONABLE_UUID).getString();
186 }
187
188
189
190
191 @Override
192 public boolean hasVersionLabel( String label ) throws RepositoryException {
193 return versionLabels().hasProperty(label);
194 }
195
196
197
198
199 @Override
200 public boolean hasVersionLabel( Version version,
201 String label ) throws RepositoryException {
202 Collection<String> labels = versionLabelsFor(version);
203
204 return labels.contains(label);
205 }
206
207
208
209
210 @SuppressWarnings( "deprecation" )
211 @Override
212 public void removeVersion( String versionName )
213 throws ReferentialIntegrityException, AccessDeniedException, UnsupportedRepositoryOperationException, VersionException,
214 RepositoryException {
215
216 JcrVersionNode version = getVersion(versionName);
217
218
219
220
221 Path versionHistoryPath = version.path().getParent();
222 for (PropertyIterator iter = version.getReferences(); iter.hasNext();) {
223 AbstractJcrProperty prop = (AbstractJcrProperty)iter.next();
224
225 Path nodePath = prop.path().getParent();
226
227
228 if (nodePath.isRoot()) {
229 throw new ReferentialIntegrityException(JcrI18n.cannotRemoveVersion.text(prop.getPath()));
230 }
231
232 if (!versionHistoryPath.equals(nodePath.getParent())) {
233 throw new ReferentialIntegrityException(JcrI18n.cannotRemoveVersion.text(prop.getPath()));
234 }
235
236 }
237
238 String versionUuid = version.getUUID();
239 Value[] values;
240
241
242 Property predecessors = version.getProperty(JcrLexicon.PREDECESSORS);
243 values = predecessors.getValues();
244 for (int i = 0; i < values.length; i++) {
245 AbstractJcrNode predecessor = session().getNodeByUUID(values[i].getString());
246 Value[] nodeSuccessors = predecessor.getProperty(JcrLexicon.SUCCESSORS).getValues();
247 Value[] newNodeSuccessors = new Value[nodeSuccessors.length - 1];
248
249 int idx = 0;
250 for (int j = 0; j < nodeSuccessors.length; j++) {
251 if (!versionUuid.equals(nodeSuccessors[j].getString())) {
252 newNodeSuccessors[idx++] = nodeSuccessors[j];
253 }
254 }
255
256 predecessor.editor().setProperty(JcrLexicon.SUCCESSORS, newNodeSuccessors, PropertyType.REFERENCE, false);
257 }
258
259
260 Property successors = version.getProperty(JcrLexicon.SUCCESSORS);
261 values = successors.getValues();
262 for (int i = 0; i < values.length; i++) {
263 AbstractJcrNode successor = session().getNodeByUUID(values[i].getString());
264 Value[] nodePredecessors = successor.getProperty(JcrLexicon.PREDECESSORS).getValues();
265 Value[] newNodePredecessors = new Value[nodePredecessors.length - 1];
266
267 int idx = 0;
268 for (int j = 0; j < nodePredecessors.length; j++) {
269 if (!versionUuid.equals(nodePredecessors[j].getString())) {
270 newNodePredecessors[idx++] = nodePredecessors[j];
271 }
272 }
273
274 successor.editor().setProperty(JcrLexicon.PREDECESSORS, newNodePredecessors, PropertyType.REFERENCE, false);
275 }
276
277 session().recordRemoval(version.location);
278 version.editor().destroy();
279 }
280
281
282
283
284 @SuppressWarnings( "deprecation" )
285 @Override
286 public void addVersionLabel( String versionName,
287 String label,
288 boolean moveLabel ) throws VersionException, RepositoryException {
289 AbstractJcrNode versionLabels = versionLabels();
290 Version version = getVersion(versionName);
291
292 try {
293
294 versionLabels.getProperty(label);
295 if (!moveLabel) throw new VersionException(JcrI18n.versionLabelAlreadyExists.text(label));
296
297 } catch (PathNotFoundException pnfe) {
298
299 }
300
301 Graph graph = cache.session().repository().createSystemGraph(context());
302 Reference ref = context().getValueFactories().getReferenceFactory().create(version.getUUID());
303 graph.set(label).on(versionLabels.location).to(ref);
304
305 versionLabels.refresh(false);
306
307 }
308
309
310
311
312 @Override
313 public void removeVersionLabel( String label ) throws VersionException, RepositoryException {
314 AbstractJcrNode versionLabels = versionLabels();
315
316 try {
317
318 versionLabels.getProperty(label);
319 } catch (PathNotFoundException pnfe) {
320
321 throw new VersionException(JcrI18n.invalidVersionLabel.text(label, getPath()));
322 }
323
324 Graph graph = cache.session().repository().createSystemGraph(context());
325 graph.remove(label).on(versionLabels.location).and();
326
327 versionLabels.refresh(false);
328 }
329
330 @Override
331 public NodeIterator getAllFrozenNodes() throws RepositoryException {
332 return new FrozenNodeIterator(getAllVersions());
333 }
334
335 @Override
336 public NodeIterator getAllLinearFrozenNodes() throws RepositoryException {
337 return new FrozenNodeIterator(getAllLinearVersions());
338 }
339
340 @Override
341 public VersionIterator getAllLinearVersions() throws RepositoryException {
342 AbstractJcrNode existingNode = session().getNodeByIdentifier(getVersionableIdentifier());
343 if (existingNode == null) return getAllVersions();
344
345 assert existingNode.isNodeType(JcrMixLexicon.VERSIONABLE);
346
347 LinkedList<JcrVersionNode> versions = new LinkedList<JcrVersionNode>();
348 JcrVersionNode baseVersion = existingNode.getBaseVersion();
349
350 while (baseVersion != null) {
351 versions.addFirst(baseVersion);
352 baseVersion = baseVersion.getLinearPredecessor();
353 }
354
355 return new LinearVersionIterator(versions, versions.size());
356 }
357
358 @Override
359 public String getVersionableIdentifier() throws RepositoryException {
360
361 return getVersionableUUID();
362 }
363
364
365
366
367
368
369 class JcrVersionIterator implements VersionIterator {
370
371 private final NodeIterator nodeIterator;
372 private Version next;
373 private int position = 0;
374
375 public JcrVersionIterator( NodeIterator nodeIterator ) {
376 super();
377 this.nodeIterator = nodeIterator;
378 }
379
380
381
382
383 @Override
384 public Version nextVersion() {
385 Version next = this.next;
386
387 if (next != null) {
388 this.next = null;
389 return next;
390 }
391
392 next = nextVersionIfPossible();
393
394 if (next == null) {
395 throw new NoSuchElementException();
396 }
397
398 position++;
399 return next;
400 }
401
402 private JcrVersionNode nextVersionIfPossible() {
403 while (nodeIterator.hasNext()) {
404 AbstractJcrNode node = (AbstractJcrNode)nodeIterator.nextNode();
405
406 Name nodeName;
407 try {
408 nodeName = node.segment().getName();
409 } catch (RepositoryException re) {
410 throw new IllegalStateException(re);
411 }
412
413 if (!JcrLexicon.VERSION_LABELS.equals(nodeName)) {
414 return (JcrVersionNode)node;
415 }
416 }
417
418 return null;
419 }
420
421
422
423
424 @Override
425 public long getPosition() {
426 return position;
427 }
428
429
430
431
432 @Override
433 public long getSize() {
434
435
436 return nodeIterator.getSize() - 1;
437 }
438
439
440
441
442 @Override
443 public void skip( long count ) {
444
445
446 while (count-- > 0) {
447 nextVersion();
448 }
449 }
450
451
452
453
454 @Override
455 public boolean hasNext() {
456 if (this.next != null) return true;
457
458 this.next = nextVersionIfPossible();
459
460 return this.next != null;
461 }
462
463
464
465
466 @Override
467 public Object next() {
468 return nextVersion();
469 }
470
471
472
473
474 @Override
475 public void remove() {
476 throw new UnsupportedOperationException();
477 }
478 }
479
480
481
482
483
484
485 class LinearVersionIterator implements VersionIterator {
486
487 private final Iterator<? extends Version> versions;
488 private final int size;
489 private int pos;
490
491 protected LinearVersionIterator( Iterable<? extends Version> versions,
492 int size ) {
493 this.versions = versions.iterator();
494 this.size = size;
495 this.pos = 0;
496 }
497
498 @Override
499 public long getPosition() {
500 return pos;
501 }
502
503 @Override
504 public long getSize() {
505 return this.size;
506 }
507
508 @Override
509 public void skip( long skipNum ) {
510 while (skipNum-- > 0 && versions.hasNext()) {
511 versions.next();
512 pos++;
513 }
514
515 }
516
517 @Override
518 public Version nextVersion() {
519 return versions.next();
520 }
521
522 @Override
523 public boolean hasNext() {
524 return versions.hasNext();
525 }
526
527 @Override
528 public Object next() {
529 return nextVersion();
530 }
531
532 @Override
533 public void remove() {
534 throw new UnsupportedOperationException();
535 }
536 }
537
538 class FrozenNodeIterator implements NodeIterator {
539 private final VersionIterator versions;
540
541 FrozenNodeIterator( VersionIterator versionIter ) {
542 this.versions = versionIter;
543 }
544
545 @Override
546 public boolean hasNext() {
547 return versions.hasNext();
548 }
549
550 @Override
551 public Object next() {
552 return nextNode();
553 }
554
555 @Override
556 public void remove() {
557 throw new UnsupportedOperationException();
558 }
559
560 @Override
561 public Node nextNode() {
562 try {
563 return versions.nextVersion().getFrozenNode();
564 } catch (RepositoryException re) {
565
566 throw new IllegalStateException(re);
567 }
568 }
569
570 @Override
571 public long getPosition() {
572 return versions.getPosition();
573 }
574
575 @Override
576 public long getSize() {
577 return versions.getSize();
578 }
579
580 @Override
581 public void skip( long skipNum ) {
582 versions.skip(skipNum);
583 }
584 }
585 }