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