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.io.InputStream;
27 import java.security.AccessControlException;
28 import java.util.ArrayList;
29 import java.util.Calendar;
30 import java.util.Collection;
31 import java.util.Collections;
32 import java.util.HashSet;
33 import java.util.Iterator;
34 import java.util.LinkedList;
35 import java.util.List;
36 import java.util.Set;
37 import java.util.UUID;
38 import java.util.regex.Pattern;
39 import javax.jcr.AccessDeniedException;
40 import javax.jcr.InvalidItemStateException;
41 import javax.jcr.Item;
42 import javax.jcr.ItemExistsException;
43 import javax.jcr.ItemNotFoundException;
44 import javax.jcr.ItemVisitor;
45 import javax.jcr.NoSuchWorkspaceException;
46 import javax.jcr.NodeIterator;
47 import javax.jcr.PathNotFoundException;
48 import javax.jcr.Property;
49 import javax.jcr.PropertyIterator;
50 import javax.jcr.PropertyType;
51 import javax.jcr.RepositoryException;
52 import javax.jcr.UnsupportedRepositoryOperationException;
53 import javax.jcr.Value;
54 import javax.jcr.ValueFormatException;
55 import javax.jcr.lock.Lock;
56 import javax.jcr.lock.LockException;
57 import javax.jcr.nodetype.ConstraintViolationException;
58 import javax.jcr.nodetype.NoSuchNodeTypeException;
59 import javax.jcr.nodetype.NodeDefinition;
60 import javax.jcr.nodetype.NodeType;
61 import javax.jcr.nodetype.NodeTypeManager;
62 import javax.jcr.query.Query;
63 import javax.jcr.query.QueryResult;
64 import javax.jcr.version.Version;
65 import javax.jcr.version.VersionException;
66 import net.jcip.annotations.Immutable;
67 import org.modeshape.common.i18n.I18n;
68 import org.modeshape.common.util.CheckArg;
69 import org.modeshape.common.util.HashCode;
70 import org.modeshape.graph.Location;
71 import org.modeshape.graph.connector.RepositorySourceException;
72 import org.modeshape.graph.property.Binary;
73 import org.modeshape.graph.property.DateTime;
74 import org.modeshape.graph.property.Name;
75 import org.modeshape.graph.property.NamespaceRegistry;
76 import org.modeshape.graph.property.Path;
77 import org.modeshape.graph.property.PathFactory;
78 import org.modeshape.graph.property.ValueFactories;
79 import org.modeshape.graph.query.QueryBuilder;
80 import org.modeshape.graph.query.model.QueryCommand;
81 import org.modeshape.graph.session.GraphSession.Node;
82 import org.modeshape.graph.session.GraphSession.NodeId;
83 import org.modeshape.graph.session.GraphSession.PropertyInfo;
84 import org.modeshape.jcr.SessionCache.JcrNodePayload;
85 import org.modeshape.jcr.SessionCache.JcrPropertyPayload;
86 import org.modeshape.jcr.SessionCache.NodeEditor;
87
88
89
90
91
92
93 @Immutable
94 abstract class AbstractJcrNode extends AbstractJcrItem implements javax.jcr.Node {
95
96 private static final NodeType[] EMPTY_NODE_TYPES = new NodeType[] {};
97
98 protected final NodeId nodeId;
99 protected final Location location;
100
101 AbstractJcrNode( SessionCache cache,
102 NodeId nodeId,
103 Location location ) {
104 super(cache);
105 this.nodeId = nodeId;
106 this.location = location;
107 }
108
109 abstract boolean isRoot();
110
111 public abstract AbstractJcrNode getParent() throws ItemNotFoundException, RepositoryException;
112
113 final NodeId internalId() {
114 return nodeId;
115 }
116
117 final Name name() throws RepositoryException {
118 return nodeInfo().getName();
119 }
120
121 final Path.Segment segment() throws RepositoryException {
122 return nodeInfo().getSegment();
123 }
124
125 final Node<JcrNodePayload, JcrPropertyPayload> nodeInfo()
126 throws InvalidItemStateException, AccessDeniedException, RepositoryException {
127 try {
128 return cache.findNode(nodeId, location.getPath());
129 } catch (ItemNotFoundException infe) {
130 throw new InvalidItemStateException(infe.getMessage());
131 }
132 }
133
134 final NodeEditor editorForParent() throws RepositoryException {
135 try {
136 Node<JcrNodePayload, JcrPropertyPayload> parent = nodeInfo().getParent();
137 return cache.getEditorFor(parent);
138 } catch (ItemNotFoundException err) {
139 String msg = JcrI18n.nodeHasAlreadyBeenRemovedFromThisSession.text(nodeId, cache.workspaceName());
140 throw new RepositoryException(msg);
141 } catch (InvalidItemStateException err) {
142 String msg = JcrI18n.nodeHasAlreadyBeenRemovedFromThisSession.text(nodeId, cache.workspaceName());
143 throw new RepositoryException(msg);
144 }
145 }
146
147 final NodeEditor editor() throws RepositoryException {
148 try {
149 return cache.getEditorFor(nodeId, location.getPath());
150 } catch (ItemNotFoundException err) {
151 String msg = JcrI18n.nodeHasAlreadyBeenRemovedFromThisSession.text(nodeId, cache.workspaceName());
152 throw new RepositoryException(msg);
153 } catch (InvalidItemStateException err) {
154 String msg = JcrI18n.nodeHasAlreadyBeenRemovedFromThisSession.text(nodeId, cache.workspaceName());
155 throw new RepositoryException(msg);
156 }
157 }
158
159 final JcrValue valueFrom( int propertyType,
160 Object value ) {
161 return new JcrValue(cache.factories(), cache, propertyType, value);
162 }
163
164 final JcrValue valueFrom( Calendar value ) {
165 ValueFactories factories = cache.factories();
166 DateTime dateTime = factories.getDateFactory().create(value);
167 return new JcrValue(factories, cache, PropertyType.DATE, dateTime);
168 }
169
170 final JcrValue valueFrom( InputStream value ) {
171 ValueFactories factories = cache.factories();
172 Binary binary = factories.getBinaryFactory().create(value);
173 return new JcrValue(factories, cache, PropertyType.DATE, binary);
174 }
175
176 final JcrValue valueFrom( javax.jcr.Node value ) throws UnsupportedRepositoryOperationException, RepositoryException {
177 ValueFactories factories = cache.factories();
178 String uuid = factories.getStringFactory().create(value.getUUID());
179 return new JcrValue(factories, cache, PropertyType.REFERENCE, uuid);
180 }
181
182 final JcrValue[] valuesFrom( int propertyType,
183 Object[] values ) {
184
185
186
187 int len = values.length;
188 ValueFactories factories = cache.factories();
189 List<JcrValue> results = new ArrayList<JcrValue>(len);
190 for (int i = 0; i != len; ++i) {
191 if (values[i] != null) results.add(new JcrValue(factories, cache, propertyType, values[i]));
192 }
193 return results.toArray(new JcrValue[results.size()]);
194 }
195
196 @Override
197 Path path() throws RepositoryException {
198
199 return nodeInfo().getPath();
200 }
201
202 boolean isReferenceable() throws RepositoryException {
203 return isNodeType(JcrMixLexicon.REFERENCEABLE);
204 }
205
206 boolean isLockable() throws RepositoryException {
207 return isNodeType(JcrMixLexicon.LOCKABLE);
208 }
209
210 UUID uuid() throws RepositoryException {
211 PropertyInfo<JcrPropertyPayload> uuidProp = nodeInfo().getProperty(JcrLexicon.UUID);
212 if (uuidProp == null) {
213 uuidProp = nodeInfo().getProperty(ModeShapeLexicon.UUID);
214 }
215 assert uuidProp != null;
216 assert !uuidProp.getProperty().isEmpty();
217 return context().getValueFactories().getUuidFactory().create(uuidProp.getProperty().getFirstValue());
218 }
219
220
221
222
223
224
225 public String getUUID() throws RepositoryException {
226
227 if (!isReferenceable()) {
228 throw new UnsupportedRepositoryOperationException(JcrI18n.nodeNotReferenceable.text());
229 }
230
231 return uuid().toString();
232 }
233
234
235
236
237
238
239
240 public final boolean isNode() {
241 return true;
242 }
243
244
245
246
247
248
249
250 public boolean isNodeType( String nodeTypeName ) throws RepositoryException {
251 return isNodeType(nameFrom(nodeTypeName));
252 }
253
254
255
256
257
258
259
260
261
262
263 public final boolean isNodeType( Name nodeTypeName ) throws RepositoryException {
264 return cache.isNodeType(nodeInfo(), nodeTypeName);
265 }
266
267
268
269
270
271
272 public NodeDefinition getDefinition() throws RepositoryException {
273 NodeDefinitionId definitionId = nodeInfo().getPayload().getDefinitionId();
274 return session().nodeTypeManager().getNodeDefinition(definitionId);
275 }
276
277
278
279
280
281
282 public JcrNodeType getPrimaryNodeType() throws RepositoryException {
283 return session().nodeTypeManager().getNodeType(getPrimaryTypeName());
284 }
285
286 Name getPrimaryTypeName() throws RepositoryException {
287 return nodeInfo().getPayload().getPrimaryTypeName();
288 }
289
290
291
292
293
294
295 public NodeType[] getMixinNodeTypes() throws RepositoryException {
296 NodeTypeManager nodeTypeManager = session().nodeTypeManager();
297 Property mixinTypesProperty = getProperty(JcrLexicon.MIXIN_TYPES);
298 if (mixinTypesProperty == null) return EMPTY_NODE_TYPES;
299 List<NodeType> mixinNodeTypes = new LinkedList<NodeType>();
300 for (Value value : mixinTypesProperty.getValues()) {
301 String nodeTypeName = value.getString();
302 NodeType nodeType = nodeTypeManager.getNodeType(nodeTypeName);
303 if (nodeType != null) mixinNodeTypes.add(nodeType);
304 }
305 return mixinNodeTypes.toArray(new NodeType[mixinNodeTypes.size()]);
306 }
307
308 List<Name> getMixinTypeNames() throws RepositoryException {
309 return nodeInfo().getPayload().getMixinTypeNames();
310 }
311
312
313
314
315
316
317 public final Item getPrimaryItem() throws RepositoryException {
318
319 NodeType primaryType = getPrimaryNodeType();
320 String primaryItemNameString = primaryType.getPrimaryItemName();
321 if (primaryItemNameString == null) {
322 I18n msg = JcrI18n.noPrimaryItemNameDefinedOnPrimaryType;
323 throw new ItemNotFoundException(msg.text(primaryType.getName(), getPath(), cache.workspaceName()));
324 }
325 try {
326 Path primaryItemPath = context().getValueFactories().getPathFactory().create(primaryItemNameString);
327 if (primaryItemPath.size() != 1 || primaryItemPath.isAbsolute()) {
328 I18n msg = JcrI18n.primaryItemNameForPrimaryTypeIsNotValid;
329 throw new ItemNotFoundException(msg.text(primaryType.getName(),
330 primaryItemNameString,
331 getPath(),
332 cache.workspaceName()));
333 }
334 return cache.findJcrItem(nodeId, location.getPath(), primaryItemPath);
335 } catch (ValueFormatException error) {
336 I18n msg = JcrI18n.primaryItemNameForPrimaryTypeIsNotValid;
337 throw new ItemNotFoundException(msg.text(primaryType.getName(),
338 primaryItemNameString,
339 getPath(),
340 cache.workspaceName()));
341 } catch (PathNotFoundException error) {
342 I18n msg = JcrI18n.primaryItemDoesNotExist;
343 throw new ItemNotFoundException(msg.text(primaryType.getName(),
344 primaryItemNameString,
345 getPath(),
346 cache.workspaceName()));
347 }
348 }
349
350
351
352
353
354
355
356 @Override
357 public boolean isSame( Item otherItem ) throws RepositoryException {
358 CheckArg.isNotNull(otherItem, "otherItem");
359 if (super.isSame(otherItem) && otherItem instanceof javax.jcr.Node) {
360 if (otherItem instanceof AbstractJcrNode) {
361 AbstractJcrNode that = (AbstractJcrNode)otherItem;
362 if (this.isReferenceable() && that.isReferenceable()) {
363
364 return getUUID().equals(((AbstractJcrNode)otherItem).getUUID());
365 }
366
367
368
369
370
371
372 CorrespondenceId thisId = this.getCorrespondenceId();
373 CorrespondenceId thatId = that.getCorrespondenceId();
374 return thisId.equals(thatId);
375 }
376
377 return otherItem.isSame(this);
378 }
379 return false;
380 }
381
382 protected CorrespondenceId getCorrespondenceId() throws RepositoryException {
383 if (this.isReferenceable()) return new CorrespondenceId(getUUID());
384 assert !this.isRoot();
385
386
387 Path currentPath = path();
388 AbstractJcrNode node = this.getParent();
389 int beginIndex = currentPath.size() - 1;
390 while (!node.isRoot() && !node.isReferenceable()) {
391 node = node.getParent();
392 --beginIndex;
393 }
394
395 Path relativePath = currentPath.relativeTo(node.path());
396 assert !relativePath.isAbsolute();
397 return new CorrespondenceId(node.getUUID(), relativePath);
398 }
399
400
401
402
403
404
405 public final boolean hasProperties() throws RepositoryException {
406 return nodeInfo().getPropertyCount() > 0;
407 }
408
409
410
411
412
413
414
415 public final boolean hasProperty( String relativePath ) throws RepositoryException {
416 CheckArg.isNotEmpty(relativePath, "relativePath");
417 if (relativePath.indexOf('/') >= 0) {
418 try {
419 getProperty(relativePath);
420 return true;
421 } catch (PathNotFoundException e) {
422 return false;
423 }
424 }
425 if (relativePath.equals(".")) return false;
426 if (relativePath.equals("..")) return false;
427
428 return nodeInfo().getProperty(nameFrom(relativePath)) != null;
429 }
430
431 public final boolean hasProperty( Name name ) throws RepositoryException {
432 return nodeInfo().getProperty(name) != null;
433 }
434
435
436
437
438
439
440 public final PropertyIterator getProperties() throws RepositoryException {
441 return new JcrPropertyIterator(cache.findJcrPropertiesFor(nodeId, location.getPath()));
442 }
443
444
445
446
447
448
449 public PropertyIterator getProperties( String namePattern ) throws RepositoryException {
450 CheckArg.isNotNull(namePattern, "namePattern");
451 namePattern = namePattern.trim();
452 if (namePattern.length() == 0) return new JcrEmptyPropertyIterator();
453 if ("*".equals(namePattern)) {
454 Collection<AbstractJcrProperty> properties = cache.findJcrPropertiesFor(nodeId, location.getPath());
455 return new JcrPropertyIterator(properties);
456 }
457
458 return getProperties(namePattern.split("[|]"));
459 }
460
461 public PropertyIterator getProperties( String[] nameGlobs ) throws RepositoryException {
462 CheckArg.isNotNull(nameGlobs, "nameGlobs");
463 if (nameGlobs.length == 0) return new JcrEmptyPropertyIterator();
464 Collection<AbstractJcrProperty> properties = cache.findJcrPropertiesFor(nodeId, location.getPath());
465
466
467 List<Object> patterns = createPatternsFor(nameGlobs);
468
469 boolean foundMatch = false;
470 Collection<AbstractJcrProperty> matchingProperties = new LinkedList<AbstractJcrProperty>();
471 Iterator<AbstractJcrProperty> iter = properties.iterator();
472 while (iter.hasNext()) {
473 AbstractJcrProperty property = iter.next();
474 String propName = property.getName();
475 assert foundMatch == false;
476 for (Object patternOrMatch : patterns) {
477 if (patternOrMatch instanceof Pattern) {
478 Pattern pattern = (Pattern)patternOrMatch;
479 if (pattern.matcher(propName).matches()) {
480 foundMatch = true;
481 break;
482 }
483 } else {
484 String match = (String)patternOrMatch;
485 if (propName.equals(match)) {
486 foundMatch = true;
487 break;
488 }
489 }
490 }
491 if (foundMatch) {
492 matchingProperties.add(property);
493 foundMatch = false;
494 }
495 }
496 return new JcrPropertyIterator(matchingProperties);
497 }
498
499
500
501
502
503
504
505
506
507 protected final NodeIterator referencingNodes( int maxNumberOfNodes ) throws RepositoryException {
508 if (!this.isReferenceable()) {
509 return new JcrEmptyNodeIterator();
510 }
511 if (maxNumberOfNodes < 0) maxNumberOfNodes = Integer.MAX_VALUE;
512
513
514 String uuid = getUUID();
515 QueryBuilder builder = new QueryBuilder(context().getValueFactories().getTypeSystem());
516 QueryCommand query = builder.select("jcr:primaryType")
517 .fromAllNodesAs("allNodes")
518 .where()
519 .referenceValue("allNodes")
520 .isEqualTo(uuid)
521 .end()
522 .limit(maxNumberOfNodes)
523 .query();
524 Query jcrQuery = session().workspace().queryManager().createQuery(query);
525 QueryResult result = jcrQuery.execute();
526 return result.getNodes();
527 }
528
529
530
531
532
533
534
535 protected final boolean hasIncomingReferences() throws RepositoryException {
536 return referencingNodes(1).hasNext();
537 }
538
539
540
541
542
543
544 public final PropertyIterator getReferences() throws RepositoryException {
545 return getReferences(null);
546 }
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572 public final PropertyIterator getReferences( String propertyName ) throws RepositoryException {
573 if (!this.isReferenceable()) {
574
575 return new JcrEmptyPropertyIterator();
576 }
577 NodeIterator iter = referencingNodes(Integer.MAX_VALUE);
578 if (!iter.hasNext()) {
579 return new JcrEmptyPropertyIterator();
580 }
581 String uuid = getUUID();
582 List<Property> references = new LinkedList<Property>();
583 while (iter.hasNext()) {
584 javax.jcr.Node node = iter.nextNode();
585
586
587 PropertyIterator propIter = node.getProperties();
588 while (propIter.hasNext()) {
589 Property prop = propIter.nextProperty();
590
591 int propType = prop.getDefinition().getRequiredType();
592 if (propType == PropertyType.REFERENCE || propType == PropertyType.UNDEFINED || propType == PropertyType.STRING) {
593 if (propertyName != null && !propertyName.equals(prop.getName())) continue;
594 if (prop.getDefinition().isMultiple()) {
595 for (Value value : prop.getValues()) {
596 if (uuid.equals(value.getString())) {
597 references.add(prop);
598 break;
599 }
600 }
601 } else {
602 Value value = prop.getValue();
603 if (uuid.equals(value.getString())) {
604 references.add(prop);
605 }
606 }
607 }
608 }
609 }
610
611 if (references.isEmpty()) return new JcrEmptyPropertyIterator();
612 return new JcrPropertyIterator(references);
613 }
614
615
616
617
618
619
620
621
622 public final AbstractJcrProperty getProperty( Name propertyName ) throws RepositoryException {
623 AbstractJcrProperty property = cache.findJcrProperty(nodeId, location.getPath(), propertyName);
624
625 if (property != null && JcrLexicon.UUID.equals(propertyName) && !isReferenceable()) return null;
626 return property;
627 }
628
629
630
631
632
633
634
635 public final Property getProperty( String relativePath ) throws RepositoryException {
636 CheckArg.isNotEmpty(relativePath, "relativePath");
637 int indexOfFirstSlash = relativePath.indexOf('/');
638 if (indexOfFirstSlash == 0) {
639
640 throw new IllegalArgumentException(JcrI18n.invalidPathParameter.text(relativePath, "relativePath"));
641 }
642 Name propertyName = null;
643 if (indexOfFirstSlash != -1) {
644
645 Path path = pathFrom(relativePath).getNormalizedPath();
646 if (path.size() > 1) {
647 try {
648 AbstractJcrItem item = cache.findJcrItem(nodeId, location.getPath(), path);
649 if (item instanceof Property) {
650 return (Property)item;
651 }
652 } catch (ItemNotFoundException e) {
653 I18n msg = JcrI18n.propertyNotFoundAtPathRelativeToReferenceNode;
654 throw new PathNotFoundException(msg.text(relativePath, getPath(), cache.workspaceName()));
655 }
656 I18n msg = JcrI18n.propertyNotFoundAtPathRelativeToReferenceNode;
657 throw new PathNotFoundException(msg.text(relativePath, getPath(), cache.workspaceName()));
658 }
659 propertyName = path.getLastSegment().getName();
660 } else {
661 propertyName = nameFrom(relativePath);
662 }
663
664 Property result = getProperty(propertyName);
665 if (result != null) return result;
666 I18n msg = JcrI18n.pathNotFoundRelativeTo;
667 throw new PathNotFoundException(msg.text(relativePath, getPath(), cache.workspaceName()));
668 }
669
670
671
672
673
674
675
676 public final boolean hasNode( String relativePath ) throws RepositoryException {
677 CheckArg.isNotEmpty(relativePath, "relativePath");
678 if (relativePath.equals(".")) return true;
679 if (relativePath.equals("..")) return isRoot() ? false : true;
680 int indexOfFirstSlash = relativePath.indexOf('/');
681 if (indexOfFirstSlash == 0) {
682
683 throw new IllegalArgumentException(JcrI18n.invalidPathParameter.text(relativePath, "relativePath"));
684 }
685 if (indexOfFirstSlash != -1) {
686 Path path = pathFrom(relativePath).getNormalizedPath();
687 try {
688 AbstractJcrNode item = cache.findJcrNode(nodeId, location.getPath(), path);
689 return item != null;
690 } catch (PathNotFoundException e) {
691 return false;
692 }
693 }
694
695 try {
696 Path.Segment segment = segmentFrom(relativePath);
697 return nodeInfo().getChild(segment) != null;
698 } catch (org.modeshape.graph.property.PathNotFoundException e) {
699 return false;
700 }
701 }
702
703
704
705
706
707
708 public final boolean hasNodes() throws RepositoryException {
709 return nodeInfo().getChildrenCount() > 0;
710 }
711
712
713
714
715
716
717
718
719 public final AbstractJcrNode getNode( Name childNodeName ) throws RepositoryException {
720 try {
721 Path childPath = context().getValueFactories().getPathFactory().createRelativePath(childNodeName);
722 return cache.findJcrNode(nodeId, location.getPath(), childPath);
723 } catch (PathNotFoundException infe) {
724 return null;
725 } catch (ItemNotFoundException infe) {
726 return null;
727 }
728 }
729
730
731
732
733
734
735
736 public final AbstractJcrNode getNode( String relativePath ) throws RepositoryException {
737 CheckArg.isNotEmpty(relativePath, "relativePath");
738 if (relativePath.equals(".")) return this;
739 if (relativePath.equals("..")) return this.getParent();
740 int indexOfFirstSlash = relativePath.indexOf('/');
741 if (indexOfFirstSlash == 0) {
742
743 throw new IllegalArgumentException(JcrI18n.invalidPathParameter.text(relativePath, "relativePath"));
744 }
745 Path.Segment segment = null;
746 if (indexOfFirstSlash != -1) {
747
748 Path path = pathFrom(relativePath).getNormalizedPath();
749 if (path.size() == 1) {
750 if (path.getLastSegment().isSelfReference()) return this;
751 if (path.getLastSegment().isParentReference()) return this.getParent();
752 }
753
754 if (path.size() > 1) {
755 AbstractJcrItem item = cache.findJcrNode(nodeId, location.getPath(), path);
756 if (item instanceof javax.jcr.Node) {
757 return (AbstractJcrNode)item;
758 }
759 I18n msg = JcrI18n.nodeNotFoundAtPathRelativeToReferenceNode;
760 throw new PathNotFoundException(msg.text(relativePath, getPath(), cache.workspaceName()));
761 }
762 segment = path.getLastSegment();
763 } else {
764 segment = segmentFrom(relativePath);
765 }
766
767 try {
768 return nodeInfo().getChild(segment).getPayload().getJcrNode();
769 } catch (org.modeshape.graph.property.PathNotFoundException e) {
770 String msg = JcrI18n.childNotFoundUnderNode.text(segment, getPath(), cache.workspaceName());
771 throw new PathNotFoundException(msg);
772 } catch (RepositorySourceException e) {
773 throw new RepositoryException(e.getLocalizedMessage(), e);
774 }
775 }
776
777
778
779
780
781
782 public final NodeIterator getNodes() throws RepositoryException {
783 int childCount = nodeInfo().getChildrenCount();
784 if (childCount == 0) {
785 return new JcrEmptyNodeIterator();
786 }
787 List<AbstractJcrNode> matchingChildren = new LinkedList<AbstractJcrNode>();
788 for (Node<JcrNodePayload, JcrPropertyPayload> child : nodeInfo().getChildren()) {
789 matchingChildren.add(child.getPayload().getJcrNode());
790 }
791 return new JcrChildNodeIterator(matchingChildren, childCount);
792 }
793
794
795
796
797
798
799 public NodeIterator getNodes( String namePattern ) throws RepositoryException {
800 CheckArg.isNotNull(namePattern, "namePattern");
801 namePattern = namePattern.trim();
802 if (namePattern.length() == 0) return new JcrEmptyNodeIterator();
803 if ("*".equals(namePattern)) return getNodes();
804
805 return getNodes(namePattern.split("[|]"));
806 }
807
808 public NodeIterator getNodes( String[] nameGlobs ) throws RepositoryException {
809 CheckArg.isNotNull(nameGlobs, "nameGlobs");
810 if (nameGlobs.length == 0) return new JcrEmptyNodeIterator();
811
812 List<Object> patterns = createPatternsFor(nameGlobs);
813
814 List<AbstractJcrNode> matchingChildren = new LinkedList<AbstractJcrNode>();
815 NamespaceRegistry registry = namespaces();
816 boolean foundMatch = false;
817 for (Node<JcrNodePayload, JcrPropertyPayload> child : nodeInfo().getChildren()) {
818 String childName = child.getName().getString(registry);
819 for (Object patternOrMatch : patterns) {
820 if (patternOrMatch instanceof Pattern) {
821 Pattern pattern = (Pattern)patternOrMatch;
822 if (pattern.matcher(childName).matches()) foundMatch = true;
823 } else {
824 String match = (String)patternOrMatch;
825 if (childName.equals(match)) foundMatch = true;
826 }
827 if (foundMatch) {
828 foundMatch = false;
829 matchingChildren.add(child.getPayload().getJcrNode());
830 break;
831 }
832 }
833 }
834 return new JcrChildNodeIterator(matchingChildren, matchingChildren.size());
835 }
836
837
838
839
840
841
842
843 public final void accept( ItemVisitor visitor ) throws RepositoryException {
844 CheckArg.isNotNull(visitor, "visitor");
845 visitor.visit(this);
846 }
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869 public final boolean canAddMixin( String mixinName ) throws NoSuchNodeTypeException, RepositoryException {
870 CheckArg.isNotNull(mixinName, "mixinName");
871 CheckArg.isNotZeroLength(mixinName, "mixinName");
872
873 session().checkPermission(path(), ModeShapePermissions.SET_PROPERTY);
874
875 JcrNodeType mixinCandidateType = cache.nodeTypes().getNodeType(mixinName);
876
877 if (this.isLocked()) {
878 return false;
879 }
880
881 if (!isCheckedOut()) {
882 return false;
883 }
884
885 if (this.getDefinition().isProtected()) {
886 return false;
887 }
888
889 NodeType primaryType = this.getPrimaryNodeType();
890 NodeType[] mixinTypes = this.getMixinNodeTypes();
891
892 if (mixinCandidateType.isAbstract()) {
893 return false;
894 }
895
896 if (!mixinCandidateType.isMixin()) {
897 return false;
898 }
899
900
901
902 if (mixinCandidateType.conflictsWith(primaryType, mixinTypes)) {
903 return false;
904 }
905
906
907
908
909 for (JcrPropertyDefinition propertyDefinition : mixinCandidateType.propertyDefinitions()) {
910 if (!hasProperty(propertyDefinition.getInternalName())) continue;
911 AbstractJcrProperty existingProp = cache.findJcrProperty(nodeId,
912 location.getPath(),
913 propertyDefinition.getInternalName());
914 if (existingProp != null) {
915 if (propertyDefinition.isMultiple()) {
916 if (!propertyDefinition.canCastToTypeAndSatisfyConstraints(existingProp.getValues())) {
917 return false;
918 }
919 } else {
920 if (!propertyDefinition.canCastToTypeAndSatisfyConstraints(existingProp.getValue())) {
921 return false;
922 }
923 }
924 }
925 }
926
927
928
929
930 Set<Name> mixinChildNodeNames = new HashSet<Name>();
931 for (JcrNodeDefinition nodeDefinition : mixinCandidateType.childNodeDefinitions()) {
932 mixinChildNodeNames.add(nodeDefinition.getInternalName());
933 }
934
935 for (Name nodeName : mixinChildNodeNames) {
936
937 int snsCount = nodeInfo().getChildrenCount(nodeName);
938 for (Node<JcrNodePayload, JcrPropertyPayload> child : nodeInfo().getChildren(nodeName)) {
939 JcrNodeDefinition match = this.cache.nodeTypes().findChildNodeDefinition(mixinCandidateType.getInternalName(),
940 Collections.<Name>emptyList(),
941 nodeName,
942 child.getPayload().getPrimaryTypeName(),
943 snsCount,
944 false);
945
946 if (match == null) {
947 return false;
948 }
949 }
950
951 }
952
953 return true;
954 }
955
956
957
958
959
960
961
962
963
964
965
966
967
968 public final void addMixin( String mixinName ) throws RepositoryException {
969 CheckArg.isNotNull(mixinName, "mixinName");
970 CheckArg.isNotZeroLength(mixinName, "mixinName");
971
972 JcrNodeType mixinCandidateType = cache.nodeTypes().getNodeType(mixinName);
973
974
975 if (this.isLocked() && !holdsLock()) {
976 throw new LockException(JcrI18n.lockTokenNotHeld.text(this.location));
977 }
978
979 if (!isCheckedOut()) {
980 throw new VersionException(JcrI18n.nodeIsCheckedIn.text(getPath()));
981 }
982
983 if (!canAddMixin(mixinName)) {
984 throw new ConstraintViolationException(JcrI18n.cannotAddMixin.text(mixinName));
985 }
986
987 if (isNodeType(mixinName)) return;
988
989 this.editor().addMixin(mixinCandidateType);
990 }
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008 public final void removeMixin( String mixinName ) throws RepositoryException {
1009
1010 if (this.isLocked() && !holdsLock()) {
1011 throw new LockException(JcrI18n.lockTokenNotHeld.text(this.location));
1012 }
1013
1014 if (!isCheckedOut()) {
1015 throw new VersionException(JcrI18n.nodeIsCheckedIn.text(getPath()));
1016 }
1017
1018 if (getDefinition().isProtected()) {
1019 throw new ConstraintViolationException(JcrI18n.cannotRemoveFromProtectedNode.text(getPath()));
1020 }
1021
1022 Property existingMixinProperty = getProperty(JcrLexicon.MIXIN_TYPES);
1023
1024 if (existingMixinProperty == null) {
1025 throw new NoSuchNodeTypeException(JcrI18n.invalidMixinTypeForNode.text(mixinName, getPath()));
1026 }
1027
1028 Value[] existingMixinValues = existingMixinProperty.getValues();
1029
1030 if (existingMixinValues.length == 0) {
1031 throw new NoSuchNodeTypeException(JcrI18n.invalidMixinTypeForNode.text(mixinName, getPath()));
1032 }
1033
1034
1035
1036
1037
1038 int newMixinValuesCount = existingMixinValues.length - 1;
1039 Value[] newMixinValues = new Value[newMixinValuesCount];
1040 List<Name> newMixinNames = new ArrayList<Name>(newMixinValuesCount);
1041 Name primaryTypeName = getPrimaryNodeType().getInternalName();
1042
1043 int j = 0;
1044 for (int i = 0; i < existingMixinValues.length; i++) {
1045 if (!existingMixinValues[i].getString().equals(mixinName)) {
1046 if (j < newMixinValuesCount) {
1047 newMixinValues[j++] = existingMixinValues[i];
1048 newMixinNames.add(cache.nameFactory.create(existingMixinValues[i].getString()));
1049 } else {
1050 throw new NoSuchNodeTypeException(JcrI18n.invalidMixinTypeForNode.text(mixinName, getPath()));
1051 }
1052 }
1053 }
1054
1055
1056
1057
1058
1059
1060 for (PropertyIterator iter = getProperties(); iter.hasNext();) {
1061 Property property = iter.nextProperty();
1062 if (mixinName.equals(property.getDefinition().getDeclaringNodeType().getName())) {
1063 JcrPropertyDefinition match;
1064
1065
1066
1067 if (property.getDefinition().isMultiple()) {
1068 match = cache.nodeTypes().findPropertyDefinition(primaryTypeName,
1069 newMixinNames,
1070 JcrNodeType.RESIDUAL_NAME,
1071 property.getValues(),
1072 true);
1073 } else {
1074 match = cache.nodeTypes().findPropertyDefinition(primaryTypeName,
1075 newMixinNames,
1076 JcrNodeType.RESIDUAL_NAME,
1077 property.getValue(),
1078 true,
1079 true);
1080 }
1081
1082 if (match == null) {
1083 throw new ConstraintViolationException(JcrI18n.noDefinition.text("property",
1084 property.getName(),
1085 getPath(),
1086 primaryTypeName,
1087 newMixinNames));
1088 }
1089 }
1090 }
1091
1092
1093
1094
1095
1096 for (NodeIterator iter = getNodes(); iter.hasNext();) {
1097 AbstractJcrNode node = (AbstractJcrNode)iter.nextNode();
1098 Name childNodeName = cache.nameFactory.create(node.getName());
1099 int snsCount = node.nodeInfo().getChildrenCount(childNodeName);
1100 if (mixinName.equals(node.getDefinition().getDeclaringNodeType().getName())) {
1101
1102
1103 JcrNodeDefinition match = cache.nodeTypes().findChildNodeDefinition(primaryTypeName,
1104 newMixinNames,
1105 JcrNodeType.RESIDUAL_NAME,
1106 node.getPrimaryNodeType().getInternalName(),
1107 snsCount,
1108 true);
1109
1110 if (match == null) {
1111 throw new ConstraintViolationException(JcrI18n.noDefinition.text("child node",
1112 node.getName(),
1113 getPath(),
1114 primaryTypeName,
1115 newMixinNames));
1116 }
1117 }
1118 }
1119
1120 editor().setProperty(JcrLexicon.MIXIN_TYPES, newMixinValues, PropertyType.NAME, false);
1121 }
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135 public void setPrimaryType( String nodeTypeName )
1136 throws NoSuchNodeTypeException, VersionException, ConstraintViolationException, LockException, RepositoryException {
1137
1138 if (this.isLocked() && !holdsLock()) {
1139 throw new LockException(JcrI18n.lockTokenNotHeld.text(this.location));
1140 }
1141
1142 if (!isCheckedOut()) {
1143 throw new VersionException(JcrI18n.nodeIsCheckedIn.text(getPath()));
1144 }
1145
1146 JcrNodeType nodeType = session().nodeTypeManager().getNodeType(nodeTypeName);
1147
1148 if (nodeType.equals(getPrimaryNodeType())) return;
1149
1150 if (nodeType.isMixin()) {
1151 throw new ConstraintViolationException(JcrI18n.cannotUseMixinTypeAsPrimaryType.text(nodeTypeName));
1152 }
1153
1154 throw new ConstraintViolationException(JcrI18n.setPrimaryTypeNotSupported.text());
1155 }
1156
1157
1158
1159
1160
1161
1162 public final javax.jcr.Node addNode( String relPath )
1163 throws ItemExistsException, PathNotFoundException, VersionException, ConstraintViolationException, LockException,
1164 RepositoryException {
1165 return addNode(relPath, null, null);
1166 }
1167
1168
1169
1170
1171
1172
1173 public final javax.jcr.Node addNode( String relPath,
1174 String primaryNodeTypeName )
1175 throws ItemExistsException, PathNotFoundException, VersionException, ConstraintViolationException, LockException,
1176 RepositoryException {
1177 return this.addNode(relPath, primaryNodeTypeName, null);
1178 }
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197 final AbstractJcrNode addNode( String relPath,
1198 String primaryNodeTypeName,
1199 UUID desiredUuid )
1200 throws ItemExistsException, PathNotFoundException, VersionException, ConstraintViolationException, LockException,
1201 RepositoryException {
1202
1203 if (isLocked() && !holdsLock()) {
1204 throw new LockException(JcrI18n.lockTokenNotHeld.text(this.location));
1205 }
1206
1207
1208 NodeEditor editor = null;
1209 Path path = null;
1210 try {
1211 path = cache.pathFactory().create(relPath);
1212 } catch (org.modeshape.graph.property.ValueFormatException e) {
1213 throw new RepositoryException(JcrI18n.invalidPathParameter.text(relPath, "relPath"));
1214 }
1215 if (path.size() == 0) {
1216 throw new RepositoryException(JcrI18n.invalidPathParameter.text(relPath, "relPath"));
1217 }
1218 if (path.getLastSegment().getIndex() > 1 || relPath.endsWith("]")) {
1219 throw new RepositoryException(JcrI18n.invalidPathParameter.text(relPath, "relPath"));
1220 }
1221 if (path.size() > 1) {
1222
1223 Path parentPath = path.getParent();
1224 try {
1225
1226 Node<JcrNodePayload, JcrPropertyPayload> parentOfNewNode = cache.findNode(nodeId, location.getPath(), parentPath);
1227 editor = cache.getEditorFor(parentOfNewNode);
1228 } catch (RepositoryException e) {
1229
1230 try {
1231 Node<JcrNodePayload, JcrPropertyPayload> grandparent;
1232 if (parentPath.size() > 1) {
1233
1234
1235
1236
1237 Path grandparentPath = parentPath.getParent();
1238 assert grandparentPath != null;
1239
1240 grandparent = cache.findNode(nodeId, location.getPath(), grandparentPath);
1241
1242 } else {
1243 grandparent = this.nodeInfo();
1244 }
1245
1246 if (grandparent.getProperty(parentPath.getLastSegment().getName()) != null) {
1247
1248
1249 throw new ConstraintViolationException(JcrI18n.invalidPathParameter.text(relPath, "relPath"));
1250 }
1251 } catch (PathNotFoundException e2) {
1252
1253 }
1254
1255
1256 throw e;
1257 }
1258 } else {
1259 assert path.size() == 1;
1260 editor = editor();
1261 }
1262 Name childName = path.getLastSegment().getName();
1263
1264
1265 Name childPrimaryTypeName = null;
1266 if (primaryNodeTypeName != null) {
1267 try {
1268 childPrimaryTypeName = cache.nameFactory().create(primaryNodeTypeName);
1269 } catch (org.modeshape.graph.property.ValueFormatException e) {
1270 throw new RepositoryException(JcrI18n.invalidNodeTypeNameParameter.text(primaryNodeTypeName,
1271 "primaryNodeTypeName"));
1272 }
1273 }
1274
1275
1276 return editor.createChild(childName, desiredUuid, childPrimaryTypeName);
1277 }
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294 final boolean canAddNode( String relPath,
1295 String primaryNodeTypeName ) throws RepositoryException {
1296 CheckArg.isNotEmpty(relPath, relPath);
1297
1298 if (isLocked() && !holdsLock()) {
1299 return false;
1300 }
1301
1302
1303 Path path = null;
1304 try {
1305 path = cache.pathFactory().create(relPath);
1306 } catch (org.modeshape.graph.property.ValueFormatException e) {
1307 return false;
1308 }
1309 if (path.size() == 0) {
1310 return false;
1311 }
1312 if (path.getLastSegment().getIndex() > 1 || relPath.endsWith("]")) {
1313 return false;
1314 }
1315 if (path.size() > 1) {
1316
1317 Path parentPath = path.getParent();
1318 try {
1319
1320 cache.findNode(nodeId, location.getPath(), parentPath);
1321 } catch (RepositoryException e) {
1322 return false;
1323 }
1324 }
1325
1326
1327 if (primaryNodeTypeName != null) {
1328 if (!session().nodeTypeManager().hasNodeType(primaryNodeTypeName)) return false;
1329
1330 JcrNodeType nodeType = session().nodeTypeManager().getNodeType(primaryNodeTypeName);
1331 if (nodeType.isAbstract()) return false;
1332 if (nodeType.isMixin()) return false;
1333 }
1334
1335 return true;
1336 }
1337
1338 protected final Property removeExistingValuedProperty( String name ) throws ConstraintViolationException, RepositoryException {
1339 AbstractJcrProperty property = cache.findJcrProperty(nodeId, location.getPath(), nameFrom(name));
1340 if (property != null) {
1341 property.remove();
1342 return property;
1343 }
1344
1345 throw new RepositoryException(JcrI18n.propertyNotFoundOnNode.text(name, getPath(), cache.workspaceName()));
1346 }
1347
1348
1349
1350
1351
1352
1353 public final Property setProperty( String name,
1354 boolean value )
1355 throws ValueFormatException, VersionException, LockException, ConstraintViolationException, RepositoryException {
1356 return editor().setProperty(nameFrom(name), valueFrom(PropertyType.BOOLEAN, value));
1357 }
1358
1359
1360
1361
1362
1363
1364 public final Property setProperty( String name,
1365 Calendar value )
1366 throws ValueFormatException, VersionException, LockException, ConstraintViolationException, RepositoryException {
1367
1368 if (value == null) {
1369 return removeExistingValuedProperty(name);
1370 }
1371
1372 return editor().setProperty(nameFrom(name), valueFrom(value));
1373 }
1374
1375
1376
1377
1378
1379
1380 public final Property setProperty( String name,
1381 double value )
1382 throws ValueFormatException, VersionException, LockException, ConstraintViolationException, RepositoryException {
1383 return editor().setProperty(nameFrom(name), valueFrom(PropertyType.DOUBLE, value));
1384 }
1385
1386
1387
1388
1389
1390
1391 public final Property setProperty( String name,
1392 InputStream value )
1393 throws ValueFormatException, VersionException, LockException, ConstraintViolationException, RepositoryException {
1394 if (value == null) {
1395 return removeExistingValuedProperty(name);
1396 }
1397
1398 return editor().setProperty(nameFrom(name), valueFrom(value));
1399 }
1400
1401
1402
1403
1404
1405
1406 public final Property setProperty( String name,
1407 long value )
1408 throws ValueFormatException, VersionException, LockException, ConstraintViolationException, RepositoryException {
1409 return editor().setProperty(nameFrom(name), valueFrom(PropertyType.LONG, value));
1410 }
1411
1412
1413
1414
1415
1416
1417 public final Property setProperty( String name,
1418 javax.jcr.Node value )
1419 throws ValueFormatException, VersionException, LockException, ConstraintViolationException, RepositoryException {
1420 if (value == null) {
1421 return removeExistingValuedProperty(name);
1422 }
1423
1424 return editor().setProperty(nameFrom(name), valueFrom(value));
1425 }
1426
1427
1428
1429
1430
1431
1432 public final Property setProperty( String name,
1433 String value )
1434 throws ValueFormatException, VersionException, LockException, ConstraintViolationException, RepositoryException {
1435 if (value == null) {
1436 return removeExistingValuedProperty(name);
1437 }
1438
1439 return editor().setProperty(nameFrom(name), valueFrom(PropertyType.STRING, value));
1440 }
1441
1442
1443
1444
1445
1446
1447 public final Property setProperty( String name,
1448 String value,
1449 int type )
1450 throws ValueFormatException, VersionException, LockException, ConstraintViolationException, RepositoryException {
1451 if (value == null) {
1452 return removeExistingValuedProperty(name);
1453 }
1454
1455 return editor().setProperty(nameFrom(name), valueFrom(type, value));
1456 }
1457
1458
1459
1460
1461
1462
1463 public final Property setProperty( String name,
1464 String[] values )
1465 throws ValueFormatException, VersionException, LockException, ConstraintViolationException, RepositoryException {
1466 if (values == null) {
1467 return removeExistingValuedProperty(name);
1468 }
1469
1470 return editor().setProperty(nameFrom(name), valuesFrom(PropertyType.STRING, values), PropertyType.UNDEFINED);
1471 }
1472
1473
1474
1475
1476
1477
1478 public final Property setProperty( String name,
1479 String[] values,
1480 int type )
1481 throws ValueFormatException, VersionException, LockException, ConstraintViolationException, RepositoryException {
1482 if (values == null) {
1483 return removeExistingValuedProperty(name);
1484 }
1485
1486 return editor().setProperty(nameFrom(name), valuesFrom(type, values), PropertyType.UNDEFINED);
1487 }
1488
1489
1490
1491
1492
1493
1494 public final Property setProperty( String name,
1495 Value value )
1496 throws ValueFormatException, VersionException, LockException, ConstraintViolationException, RepositoryException {
1497 if (value == null) {
1498 return removeExistingValuedProperty(name);
1499 }
1500
1501 return editor().setProperty(nameFrom(name), (JcrValue)value);
1502 }
1503
1504
1505
1506
1507
1508
1509 public final Property setProperty( String name,
1510 Value value,
1511 int type )
1512 throws ValueFormatException, VersionException, LockException, ConstraintViolationException, RepositoryException {
1513 if (value == null) {
1514 return removeExistingValuedProperty(name);
1515 }
1516
1517 return editor().setProperty(nameFrom(name), ((JcrValue)value).asType(type));
1518 }
1519
1520
1521
1522
1523
1524
1525 public final Property setProperty( String name,
1526 Value[] values )
1527 throws ValueFormatException, VersionException, LockException, ConstraintViolationException, RepositoryException {
1528 if (values == null) {
1529
1530 return removeExistingValuedProperty(name);
1531 }
1532
1533 return setProperty(name, values, PropertyType.UNDEFINED);
1534 }
1535
1536
1537
1538
1539
1540
1541 public final Property setProperty( String name,
1542 Value[] values,
1543 int type )
1544 throws ValueFormatException, VersionException, LockException, ConstraintViolationException, RepositoryException {
1545 if (values == null) {
1546
1547 return removeExistingValuedProperty(name);
1548 }
1549
1550
1551 return editor().setProperty(nameFrom(name), values, type);
1552 }
1553
1554
1555
1556
1557
1558
1559
1560
1561 private void checkVersionable() throws UnsupportedRepositoryOperationException, RepositoryException {
1562 if (!isNodeType(JcrMixLexicon.VERSIONABLE)) {
1563 throw new UnsupportedRepositoryOperationException(JcrI18n.requiresVersionable.text());
1564 }
1565 }
1566
1567
1568
1569
1570
1571
1572
1573 private void checkNotProtected() throws ConstraintViolationException, RepositoryException {
1574 JcrNodeDefinition nodeDefn = cache.nodeTypes().getNodeDefinition(nodeInfo().getPayload().getDefinitionId());
1575 if (nodeDefn.isProtected()) {
1576 throw new ConstraintViolationException(JcrI18n.cannotRemoveItemWithProtectedDefinition.text(getPath()));
1577 }
1578 }
1579
1580 final JcrVersionManager versionManager() {
1581 return session().workspace().versionManager();
1582 }
1583
1584
1585
1586
1587
1588
1589 public final boolean isCheckedOut() throws RepositoryException {
1590 return editor().isCheckedOut();
1591 }
1592
1593
1594
1595
1596
1597
1598 public final Version checkin() throws RepositoryException {
1599 checkVersionable();
1600
1601 return versionManager().checkin(this);
1602 }
1603
1604
1605
1606
1607
1608
1609 public final void checkout() throws UnsupportedRepositoryOperationException, LockException, RepositoryException {
1610 checkVersionable();
1611
1612 versionManager().checkout(this);
1613 }
1614
1615
1616
1617
1618
1619
1620 public final NodeIterator merge( String srcWorkspace,
1621 boolean bestEffort ) throws ConstraintViolationException, RepositoryException {
1622 CheckArg.isNotNull(srcWorkspace, "source workspace name");
1623
1624 checkNotProtected();
1625
1626 return versionManager().merge(this, srcWorkspace, bestEffort);
1627 }
1628
1629
1630
1631
1632
1633
1634 public final void cancelMerge( Version version ) throws RepositoryException {
1635 checkVersionable();
1636
1637 versionManager().cancelMerge(this, version);
1638 }
1639
1640
1641
1642
1643
1644
1645 public final void doneMerge( Version version ) throws RepositoryException {
1646 checkVersionable();
1647
1648 versionManager().doneMerge(this, version);
1649 }
1650
1651
1652
1653
1654
1655
1656 public final JcrVersionHistoryNode getVersionHistory() throws RepositoryException {
1657 checkVersionable();
1658
1659 return versionManager().getVersionHistory(this);
1660 }
1661
1662
1663
1664
1665
1666
1667 public final JcrVersionNode getBaseVersion() throws RepositoryException {
1668 checkVersionable();
1669
1670
1671 if (!hasProperty(JcrLexicon.BASE_VERSION)) {
1672 throw new UnsupportedRepositoryOperationException(JcrI18n.requiresVersionable.text());
1673 }
1674
1675 return (JcrVersionNode)session().getNodeByUUID(getProperty(JcrLexicon.BASE_VERSION).getString());
1676 }
1677
1678
1679
1680
1681
1682
1683 public final void restore( String versionName,
1684 boolean removeExisting ) throws RepositoryException {
1685 restore(getVersionHistory().getVersion(versionName), removeExisting);
1686 }
1687
1688
1689
1690
1691
1692
1693 public final void restore( Version version,
1694 boolean removeExisting ) throws RepositoryException {
1695 try {
1696 checkNotProtected();
1697 } catch (ConstraintViolationException cve) {
1698 throw new UnsupportedRepositoryOperationException(cve);
1699 }
1700 versionManager().restore(path(), version, null, removeExisting);
1701 }
1702
1703
1704
1705
1706
1707
1708 public final void restore( Version version,
1709 String relPath,
1710 boolean removeExisting ) throws RepositoryException {
1711 checkNotProtected();
1712
1713 PathFactory pathFactory = context().getValueFactories().getPathFactory();
1714 Path relPathAsPath = pathFactory.create(relPath);
1715 if (relPathAsPath.isAbsolute()) throw new RepositoryException(JcrI18n.invalidRelativePath.text(relPath));
1716 Path actualPath = pathFactory.create(path(), relPathAsPath).getCanonicalPath();
1717
1718 versionManager().restore(actualPath, version, null, removeExisting);
1719 }
1720
1721
1722
1723
1724
1725
1726 public final void restoreByLabel( String versionLabel,
1727 boolean removeExisting ) throws RepositoryException {
1728 restore(getVersionHistory().getVersionByLabel(versionLabel), removeExisting);
1729 }
1730
1731
1732
1733
1734
1735
1736
1737 public final boolean holdsLock() {
1738 WorkspaceLockManager.ModeShapeLock lock = session().workspace().lockManager().lockFor(session(), this.location);
1739
1740 return lock != null && cache.session().lockTokens().contains(lock.getLockToken());
1741 }
1742
1743
1744
1745
1746
1747
1748
1749 public final boolean isLocked() throws LockException, RepositoryException {
1750 return lock() != null;
1751 }
1752
1753
1754
1755
1756
1757
1758 public final Lock lock( boolean isDeep,
1759 boolean isSessionScoped ) throws LockException, RepositoryException {
1760 if (!isLockable()) {
1761 throw new LockException(JcrI18n.nodeNotLockable.text(getPath()));
1762 }
1763
1764 if (isLocked()) {
1765 throw new LockException(JcrI18n.alreadyLocked.text(this.location));
1766 }
1767
1768 if (isDeep) {
1769 LinkedList<Node<JcrNodePayload, JcrPropertyPayload>> nodesToVisit = new LinkedList<Node<JcrNodePayload, JcrPropertyPayload>>();
1770 nodesToVisit.add(nodeInfo());
1771
1772 while (!nodesToVisit.isEmpty()) {
1773 Node<JcrNodePayload, JcrPropertyPayload> node = nodesToVisit.remove(nodesToVisit.size() - 1);
1774 if (session().workspace().lockManager().lockFor(session(), node.getLocation()) != null) throw new LockException(
1775 JcrI18n.parentAlreadyLocked.text(this.location,
1776 node.getLocation()));
1777
1778 for (Node<JcrNodePayload, JcrPropertyPayload> child : node.getChildren()) {
1779 nodesToVisit.add(child);
1780 }
1781 }
1782 }
1783
1784 WorkspaceLockManager.ModeShapeLock lock = session().workspace().lockManager().lock(session(),
1785 this.location,
1786 isDeep,
1787 isSessionScoped);
1788
1789 cache.session().addLockToken(lock.getLockToken());
1790 return lock.lockFor(cache);
1791 }
1792
1793
1794
1795
1796
1797
1798 public final void unlock() throws LockException, RepositoryException {
1799 WorkspaceLockManager.ModeShapeLock lock = session().workspace().lockManager().lockFor(session(), this.location);
1800
1801 if (lock == null) {
1802 throw new LockException(JcrI18n.notLocked.text(this.location));
1803 }
1804
1805 if (!session().lockTokens().contains(lock.getLockToken())) {
1806 try {
1807
1808 session().checkPermission(cache.workspaceName(), null, ModeShapePermissions.UNLOCK_ANY);
1809 } catch (AccessControlException iae) {
1810 throw new LockException(JcrI18n.lockTokenNotHeld.text(this.location));
1811 }
1812 }
1813
1814 session().workspace().lockManager().unlock(session().getExecutionContext(), lock);
1815 session().removeLockToken(lock.getLockToken());
1816 }
1817
1818 private final WorkspaceLockManager.ModeShapeLock lock() throws RepositoryException {
1819
1820 if (session() == null || session().workspace() == null) return null;
1821
1822 WorkspaceLockManager lockManager = session().workspace().lockManager();
1823 WorkspaceLockManager.ModeShapeLock lock = lockManager.lockFor(session(), this.location);
1824 if (lock != null) return lock;
1825
1826 AbstractJcrNode parent = this;
1827 while (!parent.isRoot()) {
1828 parent = parent.getParent();
1829
1830 WorkspaceLockManager.ModeShapeLock parentLock = lockManager.lockFor(session(), parent.location);
1831 if (parentLock != null && parentLock.isLive()) {
1832 return parentLock.isDeep() ? parentLock : null;
1833 }
1834 }
1835 return null;
1836 }
1837
1838
1839
1840
1841
1842
1843 public final Lock getLock() throws LockException, RepositoryException {
1844 WorkspaceLockManager.ModeShapeLock lock = lock();
1845
1846 if (lock == null) throw new LockException(JcrI18n.notLocked.text(this.location));
1847 return lock.lockFor(cache);
1848 }
1849
1850
1851
1852
1853
1854
1855 public final boolean isModified() {
1856 try {
1857 Node<JcrNodePayload, JcrPropertyPayload> node = nodeInfo();
1858
1859 return !node.isNew() && node.isChanged(true);
1860 } catch (RepositoryException re) {
1861 throw new IllegalStateException(re);
1862 }
1863 }
1864
1865
1866
1867
1868
1869
1870 public final boolean isNew() {
1871 try {
1872 return nodeInfo().isNew();
1873 } catch (RepositoryException re) {
1874 throw new IllegalStateException(re);
1875 }
1876 }
1877
1878
1879
1880
1881
1882
1883 public final String getCorrespondingNodePath( String workspaceName )
1884 throws NoSuchWorkspaceException, ItemNotFoundException, RepositoryException {
1885 CheckArg.isNotNull(workspaceName, "workspace name");
1886 NamespaceRegistry namespaces = this.context().getNamespaceRegistry();
1887 return correspondingNodePath(workspaceName).getString(namespaces);
1888 }
1889
1890 protected final Path correspondingNodePath( String workspaceName )
1891 throws NoSuchWorkspaceException, ItemNotFoundException, RepositoryException {
1892 assert workspaceName != null;
1893 NamespaceRegistry namespaces = this.context().getNamespaceRegistry();
1894
1895
1896 AbstractJcrNode referenceableRoot = this;
1897 while (!referenceableRoot.isNodeType(JcrMixLexicon.REFERENCEABLE.getString(namespaces))) {
1898 referenceableRoot = referenceableRoot.getParent();
1899 }
1900
1901
1902 Path relativePath = path().equals(referenceableRoot.path()) ? null : path().relativeTo(referenceableRoot.path());
1903 UUID uuid = UUID.fromString(referenceableRoot.getUUID());
1904 return this.cache.getPathForCorrespondingNode(workspaceName, uuid, relativePath);
1905 }
1906
1907
1908
1909
1910
1911
1912 public final void update( String srcWorkspaceName ) throws NoSuchWorkspaceException, RepositoryException {
1913 CheckArg.isNotNull(srcWorkspaceName, "workspace name");
1914
1915 if (session().hasPendingChanges()) {
1916 throw new InvalidItemStateException(JcrI18n.noPendingChangesAllowed.text());
1917 }
1918
1919 checkNotProtected();
1920
1921 Path correspondingPath = null;
1922 try {
1923 correspondingPath = correspondingNodePath(srcWorkspaceName);
1924 } catch (ItemNotFoundException infe) {
1925 return;
1926 }
1927
1928
1929 cache.graphSession().immediateClone(correspondingPath, srcWorkspaceName, path(), true, true);
1930
1931 session().refresh(false);
1932 }
1933
1934
1935
1936
1937
1938
1939
1940 public final void orderBefore( String srcChildRelPath,
1941 String destChildRelPath ) throws UnsupportedRepositoryOperationException, RepositoryException {
1942
1943 if (!getPrimaryNodeType().hasOrderableChildNodes()) {
1944 throw new UnsupportedRepositoryOperationException(
1945 JcrI18n.notOrderable.text(getPrimaryNodeType().getName(), getPath()));
1946 }
1947
1948 PathFactory pathFactory = this.cache.pathFactory();
1949 Path srcPath = pathFactory.create(srcChildRelPath);
1950 if (srcPath.isAbsolute() || srcPath.size() != 1) {
1951 throw new ItemNotFoundException(JcrI18n.pathNotFound.text(srcPath.getString(cache.context().getNamespaceRegistry()),
1952 cache.session().workspace().getName()));
1953 }
1954
1955 Path.Segment sourceSegment = srcPath.getLastSegment();
1956 try {
1957 nodeInfo().getChild(sourceSegment);
1958 } catch (org.modeshape.graph.property.PathNotFoundException e) {
1959 String workspaceName = this.cache.session().getWorkspace().getName();
1960 throw new ItemNotFoundException(JcrI18n.pathNotFound.text(srcPath, workspaceName));
1961 }
1962
1963 Path.Segment destSegment = null;
1964
1965 if (destChildRelPath != null) {
1966 Path destPath = pathFactory.create(destChildRelPath);
1967 if (destPath.isAbsolute() || destPath.size() != 1) {
1968 throw new ItemNotFoundException(JcrI18n.pathNotFound.text(destPath.getString(cache.context()
1969 .getNamespaceRegistry()),
1970 cache.session().workspace().getName()));
1971 }
1972
1973 destSegment = destPath.getLastSegment();
1974
1975
1976 try {
1977 nodeInfo().getChild(destSegment);
1978 } catch (org.modeshape.graph.property.PathNotFoundException e) {
1979 String workspaceName = this.cache.session().getWorkspace().getName();
1980 throw new ItemNotFoundException(JcrI18n.pathNotFound.text(destPath, workspaceName));
1981 }
1982 }
1983
1984 this.editor().orderChildBefore(sourceSegment, destSegment);
1985 }
1986
1987 protected static List<Object> createPatternsFor( String[] namePatterns ) throws RepositoryException {
1988 List<Object> patterns = new LinkedList<Object>();
1989 for (String stringPattern : namePatterns) {
1990 stringPattern = stringPattern.trim();
1991 int length = stringPattern.length();
1992 if (length == 0) continue;
1993 if (stringPattern.indexOf("*") == -1) {
1994
1995 patterns.add(stringPattern);
1996 } else {
1997
1998 StringBuilder sb = new StringBuilder(length);
1999 for (int i = 0; i != length; i++) {
2000 char c = stringPattern.charAt(i);
2001 switch (c) {
2002
2003 case '/':
2004 case '[':
2005 case ']':
2006 case '\'':
2007 case '"':
2008 case '|':
2009 case '\t':
2010 case '\n':
2011 case '\r':
2012 String msg = JcrI18n.invalidNamePattern.text(c, stringPattern);
2013 throw new RepositoryException(msg);
2014
2015 case '?':
2016 case '(':
2017 case ')':
2018 case '$':
2019 case '^':
2020 case '.':
2021 case '{':
2022 case '}':
2023 case '\\':
2024 sb.append("\\");
2025 sb.append(c);
2026 break;
2027 case '*':
2028
2029 sb.append(".*");
2030 break;
2031 default:
2032 sb.append(c);
2033 break;
2034 }
2035 }
2036 String escapedString = sb.toString();
2037 Pattern pattern = Pattern.compile(escapedString);
2038 patterns.add(pattern);
2039 }
2040 }
2041 return patterns;
2042 }
2043
2044
2045
2046
2047
2048
2049 public void refresh( boolean keepChanges ) throws RepositoryException {
2050 this.cache.refresh(this.nodeId, location.getPath(), keepChanges);
2051 }
2052
2053
2054
2055
2056
2057
2058 public void save() throws RepositoryException {
2059 session().checkReferentialIntegrityOfChanges(this);
2060 cache.save(nodeId, location.getPath());
2061 }
2062
2063 @Override
2064 public String toString() {
2065
2066 try {
2067 PropertyIterator iter = this.getProperties();
2068 StringBuffer propertyBuff = new StringBuffer();
2069 while (iter.hasNext()) {
2070 AbstractJcrProperty prop = (AbstractJcrProperty)iter.nextProperty();
2071 propertyBuff.append(prop).append(", ");
2072 }
2073 return this.getPath() + " {" + propertyBuff.toString() + "}";
2074 } catch (RepositoryException re) {
2075 return re.getMessage();
2076 }
2077 }
2078
2079
2080
2081
2082
2083
2084 @Override
2085 public boolean equals( Object obj ) {
2086 if (obj == this) return true;
2087 if (obj instanceof AbstractJcrNode) {
2088 AbstractJcrNode that = (AbstractJcrNode)obj;
2089 if (this.cache != that.cache) return false;
2090 return this.location.equals(that.location);
2091 }
2092 return false;
2093 }
2094
2095
2096
2097
2098
2099
2100 @Override
2101 public int hashCode() {
2102 return HashCode.compute(cache, location);
2103 }
2104 }