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.DATE, 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 throw new RepositoryException(JcrI18n.propertyNotFoundOnNode.text(name, getPath(), cache.workspaceName()));
1543 }
1544
1545
1546
1547
1548
1549
1550 public final Property setProperty( String name,
1551 boolean value )
1552 throws ValueFormatException, VersionException, LockException, ConstraintViolationException, RepositoryException {
1553 checkSession();
1554 return editor().setProperty(nameFrom(name), valueFrom(PropertyType.BOOLEAN, value));
1555 }
1556
1557
1558
1559
1560
1561
1562 @Override
1563 public Property setProperty( String name,
1564 Binary value )
1565 throws ValueFormatException, VersionException, LockException, ConstraintViolationException, RepositoryException {
1566 return editor().setProperty(nameFrom(name), valueFrom(PropertyType.BINARY, value));
1567 }
1568
1569
1570
1571
1572
1573
1574 @Override
1575 public Property setProperty( String name,
1576 BigDecimal value )
1577 throws ValueFormatException, VersionException, LockException, ConstraintViolationException, RepositoryException {
1578 return editor().setProperty(nameFrom(name), valueFrom(PropertyType.DECIMAL, value));
1579 }
1580
1581
1582
1583
1584
1585
1586 public final Property setProperty( String name,
1587 Calendar value )
1588 throws ValueFormatException, VersionException, LockException, ConstraintViolationException, RepositoryException {
1589 checkSession();
1590
1591 if (value == null) {
1592 return removeExistingValuedProperty(name);
1593 }
1594
1595 return editor().setProperty(nameFrom(name), valueFrom(value));
1596 }
1597
1598
1599
1600
1601
1602
1603 public final Property setProperty( String name,
1604 double value )
1605 throws ValueFormatException, VersionException, LockException, ConstraintViolationException, RepositoryException {
1606 checkSession();
1607 return editor().setProperty(nameFrom(name), valueFrom(PropertyType.DOUBLE, value));
1608 }
1609
1610
1611
1612
1613
1614
1615 public final Property setProperty( String name,
1616 InputStream value )
1617 throws ValueFormatException, VersionException, LockException, ConstraintViolationException, RepositoryException {
1618 checkSession();
1619 if (value == null) {
1620 return removeExistingValuedProperty(name);
1621 }
1622
1623 return editor().setProperty(nameFrom(name), valueFrom(value));
1624 }
1625
1626
1627
1628
1629
1630
1631 public final Property setProperty( String name,
1632 long value )
1633 throws ValueFormatException, VersionException, LockException, ConstraintViolationException, RepositoryException {
1634 checkSession();
1635 return editor().setProperty(nameFrom(name), valueFrom(PropertyType.LONG, value));
1636 }
1637
1638
1639
1640
1641
1642
1643 public final Property setProperty( String name,
1644 javax.jcr.Node value )
1645 throws ValueFormatException, VersionException, LockException, ConstraintViolationException, RepositoryException {
1646 checkSession();
1647 if (value == null) {
1648 return removeExistingValuedProperty(name);
1649 }
1650
1651 return editor().setProperty(nameFrom(name), valueFrom(value));
1652 }
1653
1654
1655
1656
1657
1658
1659 public final Property setProperty( String name,
1660 String value )
1661 throws ValueFormatException, VersionException, LockException, ConstraintViolationException, RepositoryException {
1662 checkSession();
1663 if (value == null) {
1664 return removeExistingValuedProperty(name);
1665 }
1666
1667 return editor().setProperty(nameFrom(name), valueFrom(PropertyType.STRING, value));
1668 }
1669
1670
1671
1672
1673
1674
1675 public final Property setProperty( String name,
1676 String value,
1677 int type )
1678 throws ValueFormatException, VersionException, LockException, ConstraintViolationException, RepositoryException {
1679 checkSession();
1680 if (value == null) {
1681 return removeExistingValuedProperty(name);
1682 }
1683
1684 return editor().setProperty(nameFrom(name), valueFrom(type, value));
1685 }
1686
1687
1688
1689
1690
1691
1692 public final Property setProperty( String name,
1693 String[] values )
1694 throws ValueFormatException, VersionException, LockException, ConstraintViolationException, RepositoryException {
1695 checkSession();
1696 if (values == null) {
1697 return removeExistingValuedProperty(name);
1698 }
1699
1700 return editor().setProperty(nameFrom(name), valuesFrom(PropertyType.STRING, values), PropertyType.UNDEFINED);
1701 }
1702
1703
1704
1705
1706
1707
1708 public final Property setProperty( String name,
1709 String[] values,
1710 int type )
1711 throws ValueFormatException, VersionException, LockException, ConstraintViolationException, RepositoryException {
1712 checkSession();
1713 if (values == null) {
1714 return removeExistingValuedProperty(name);
1715 }
1716
1717 return editor().setProperty(nameFrom(name), valuesFrom(type, values), PropertyType.UNDEFINED);
1718 }
1719
1720
1721
1722
1723
1724
1725 public final Property setProperty( String name,
1726 Value value )
1727 throws ValueFormatException, VersionException, LockException, ConstraintViolationException, RepositoryException {
1728 checkSession();
1729 if (value == null) {
1730 return removeExistingValuedProperty(name);
1731 }
1732
1733 return editor().setProperty(nameFrom(name), (JcrValue)value);
1734 }
1735
1736
1737
1738
1739
1740
1741 public final Property setProperty( String name,
1742 Value value,
1743 int type )
1744 throws ValueFormatException, VersionException, LockException, ConstraintViolationException, RepositoryException {
1745 checkSession();
1746 if (value == null) {
1747 return removeExistingValuedProperty(name);
1748 }
1749
1750 return editor().setProperty(nameFrom(name), ((JcrValue)value).asType(type));
1751 }
1752
1753
1754
1755
1756
1757
1758 public final Property setProperty( String name,
1759 Value[] values )
1760 throws ValueFormatException, VersionException, LockException, ConstraintViolationException, RepositoryException {
1761 checkSession();
1762 if (values == null) {
1763
1764 return removeExistingValuedProperty(name);
1765 }
1766
1767 return setProperty(name, values, PropertyType.UNDEFINED);
1768 }
1769
1770
1771
1772
1773
1774
1775 public final Property setProperty( String name,
1776 Value[] values,
1777 int type )
1778 throws ValueFormatException, VersionException, LockException, ConstraintViolationException, RepositoryException {
1779 checkSession();
1780 if (values == null) {
1781
1782 return removeExistingValuedProperty(name);
1783 }
1784
1785
1786 return editor().setProperty(nameFrom(name), values, type);
1787 }
1788
1789
1790
1791
1792
1793
1794
1795 private void checkNotProtected() throws ConstraintViolationException, RepositoryException {
1796 JcrNodeDefinition nodeDefn = cache.nodeTypes().getNodeDefinition(nodeInfo().getPayload().getDefinitionId());
1797 if (nodeDefn.isProtected()) {
1798 throw new ConstraintViolationException(JcrI18n.cannotRemoveItemWithProtectedDefinition.text(getPath()));
1799 }
1800 }
1801
1802 final JcrVersionManager versionManager() {
1803 return session().workspace().versionManager();
1804 }
1805
1806
1807
1808
1809
1810
1811 public final boolean isCheckedOut() throws RepositoryException {
1812 checkSession();
1813 return editor().isCheckedOut();
1814 }
1815
1816
1817
1818
1819
1820
1821 public final Version checkin() throws RepositoryException {
1822 return versionManager().checkin(this);
1823 }
1824
1825
1826
1827
1828
1829
1830 public final void checkout() throws UnsupportedRepositoryOperationException, LockException, RepositoryException {
1831 versionManager().checkout(this);
1832 }
1833
1834
1835
1836
1837
1838
1839 public final NodeIterator merge( String srcWorkspace,
1840 boolean bestEffort ) throws ConstraintViolationException, RepositoryException {
1841 CheckArg.isNotNull(srcWorkspace, "source workspace name");
1842
1843 checkNotProtected();
1844
1845 return versionManager().merge(this, srcWorkspace, bestEffort, false);
1846 }
1847
1848
1849
1850
1851
1852
1853 public final void cancelMerge( Version version ) throws RepositoryException {
1854 versionManager().cancelMerge(this, version);
1855 }
1856
1857
1858
1859
1860
1861
1862 public final void doneMerge( Version version ) throws RepositoryException {
1863 versionManager().doneMerge(this, version);
1864 }
1865
1866
1867
1868
1869
1870
1871 public final JcrVersionHistoryNode getVersionHistory() throws RepositoryException {
1872 return versionManager().getVersionHistory(this);
1873 }
1874
1875
1876
1877
1878
1879
1880 public final JcrVersionNode getBaseVersion() throws RepositoryException {
1881 checkSession();
1882
1883
1884 if (!hasProperty(JcrLexicon.BASE_VERSION)) {
1885 throw new UnsupportedRepositoryOperationException(JcrI18n.requiresVersionable.text());
1886 }
1887
1888 return (JcrVersionNode)session().getNodeByUUID(getProperty(JcrLexicon.BASE_VERSION).getString());
1889 }
1890
1891
1892
1893
1894
1895
1896 public final void restore( String versionName,
1897 boolean removeExisting ) throws RepositoryException {
1898 restore(getVersionHistory().getVersion(versionName), removeExisting);
1899 }
1900
1901
1902
1903
1904
1905
1906 public final void restore( Version version,
1907 boolean removeExisting ) throws RepositoryException {
1908 try {
1909 checkNotProtected();
1910 } catch (ConstraintViolationException cve) {
1911 throw new UnsupportedRepositoryOperationException(cve);
1912 }
1913 versionManager().restore(path(), version, null, removeExisting);
1914 }
1915
1916
1917
1918
1919
1920
1921 public final void restore( Version version,
1922 String relPath,
1923 boolean removeExisting ) throws RepositoryException {
1924 checkNotProtected();
1925
1926 PathFactory pathFactory = context().getValueFactories().getPathFactory();
1927 Path relPathAsPath = pathFactory.create(relPath);
1928 if (relPathAsPath.isAbsolute()) throw new RepositoryException(JcrI18n.invalidRelativePath.text(relPath));
1929 Path actualPath = pathFactory.create(path(), relPathAsPath).getCanonicalPath();
1930
1931 versionManager().restore(actualPath, version, null, removeExisting);
1932 }
1933
1934
1935
1936
1937
1938
1939 public final void restoreByLabel( String versionLabel,
1940 boolean removeExisting ) throws RepositoryException {
1941 restore(getVersionHistory().getVersionByLabel(versionLabel), removeExisting);
1942 }
1943
1944
1945
1946
1947
1948
1949
1950 public final boolean holdsLock() throws RepositoryException {
1951 checkSession();
1952 return lockManager().holdsLock(this);
1953 }
1954
1955
1956
1957
1958
1959
1960
1961 public final boolean isLocked() throws LockException, RepositoryException {
1962 return lockManager().isLocked(this);
1963 }
1964
1965
1966
1967
1968
1969
1970 public final Lock lock( boolean isDeep,
1971 boolean isSessionScoped ) throws LockException, RepositoryException {
1972 checkSession();
1973 return lockManager().lock(this, isDeep, isSessionScoped, -1L, null);
1974 }
1975
1976
1977
1978
1979
1980
1981 public final void unlock() throws LockException, RepositoryException {
1982 checkSession();
1983 lockManager().unlock(this);
1984 }
1985
1986
1987
1988
1989
1990
1991 public final Lock getLock() throws LockException, RepositoryException {
1992 checkSession();
1993 return lockManager().getLock(this);
1994 }
1995
1996
1997
1998
1999
2000
2001 public final boolean isModified() {
2002 try {
2003 checkSession();
2004 Node<JcrNodePayload, JcrPropertyPayload> node = nodeInfo();
2005
2006 return !node.isNew() && node.isChanged(true);
2007 } catch (RepositoryException re) {
2008 throw new IllegalStateException(re);
2009 }
2010 }
2011
2012
2013
2014
2015
2016
2017 public final boolean isNew() {
2018 try {
2019 checkSession();
2020 return nodeInfo().isNew();
2021 } catch (RepositoryException re) {
2022 throw new IllegalStateException(re);
2023 }
2024 }
2025
2026
2027
2028
2029
2030
2031 public final String getCorrespondingNodePath( String workspaceName )
2032 throws NoSuchWorkspaceException, ItemNotFoundException, RepositoryException {
2033 CheckArg.isNotNull(workspaceName, "workspace name");
2034 checkSession();
2035 NamespaceRegistry namespaces = this.context().getNamespaceRegistry();
2036 return correspondingNodePath(workspaceName).getString(namespaces);
2037 }
2038
2039 protected final Path correspondingNodePath( String workspaceName )
2040 throws NoSuchWorkspaceException, ItemNotFoundException, RepositoryException {
2041 assert workspaceName != null;
2042 NamespaceRegistry namespaces = this.context().getNamespaceRegistry();
2043
2044
2045 AbstractJcrNode referenceableRoot = this;
2046 while (!referenceableRoot.isNodeType(JcrMixLexicon.REFERENCEABLE.getString(namespaces))) {
2047 referenceableRoot = referenceableRoot.getParent();
2048 }
2049
2050
2051 Path relativePath = path().equals(referenceableRoot.path()) ? null : path().relativeTo(referenceableRoot.path());
2052 UUID uuid = UUID.fromString(referenceableRoot.getUUID());
2053 return this.cache.getPathForCorrespondingNode(workspaceName, uuid, relativePath);
2054 }
2055
2056
2057
2058
2059
2060
2061 public final void update( String srcWorkspaceName ) throws NoSuchWorkspaceException, RepositoryException {
2062 CheckArg.isNotNull(srcWorkspaceName, "workspace name");
2063 checkSession();
2064
2065 if (session().hasPendingChanges()) {
2066 throw new InvalidItemStateException(JcrI18n.noPendingChangesAllowed.text());
2067 }
2068
2069 checkNotProtected();
2070
2071 Path correspondingPath = null;
2072 try {
2073 correspondingPath = correspondingNodePath(srcWorkspaceName);
2074 } catch (ItemNotFoundException infe) {
2075 return;
2076 }
2077
2078
2079 cache.graphSession().immediateClone(correspondingPath, srcWorkspaceName, path(), true, true);
2080
2081 session().refresh(false);
2082 }
2083
2084
2085
2086
2087
2088
2089
2090 public final void orderBefore( String srcChildRelPath,
2091 String destChildRelPath ) throws UnsupportedRepositoryOperationException, RepositoryException {
2092 checkSession();
2093
2094 if (!getPrimaryNodeType().hasOrderableChildNodes()) {
2095 throw new UnsupportedRepositoryOperationException(
2096 JcrI18n.notOrderable.text(getPrimaryNodeType().getName(), getPath()));
2097 }
2098
2099 PathFactory pathFactory = this.cache.pathFactory();
2100 Path srcPath = pathFactory.create(srcChildRelPath);
2101 if (srcPath.isAbsolute()) {
2102
2103 throw new IllegalArgumentException(JcrI18n.invalidPathParameter.text(srcChildRelPath, "relativePath"));
2104 }
2105 if (srcPath.isAbsolute() || srcPath.size() != 1) {
2106 throw new ItemNotFoundException(JcrI18n.pathNotFound.text(srcPath.getString(namespaces()), cache.workspaceName()));
2107 }
2108
2109 Path.Segment sourceSegment = srcPath.getLastSegment();
2110 try {
2111 nodeInfo().getChild(sourceSegment);
2112 } catch (org.modeshape.graph.property.PathNotFoundException e) {
2113 String workspaceName = this.cache.workspaceName();
2114 throw new ItemNotFoundException(JcrI18n.pathNotFound.text(srcPath, workspaceName));
2115 }
2116
2117 Path.Segment destSegment = null;
2118
2119 if (destChildRelPath != null) {
2120 Path destPath = pathFactory.create(destChildRelPath);
2121 if (destPath.isAbsolute()) {
2122
2123 throw new IllegalArgumentException(JcrI18n.invalidPathParameter.text(destChildRelPath, "relativePath"));
2124 }
2125 if (destPath.size() != 1) {
2126 throw new ItemNotFoundException(
2127 JcrI18n.pathNotFound.text(destPath.getString(namespaces()), cache.workspaceName()));
2128 }
2129
2130 destSegment = destPath.getLastSegment();
2131
2132
2133 try {
2134 nodeInfo().getChild(destSegment);
2135 } catch (org.modeshape.graph.property.PathNotFoundException e) {
2136 String workspaceName = this.cache.session().getWorkspace().getName();
2137 throw new ItemNotFoundException(JcrI18n.pathNotFound.text(destPath, workspaceName));
2138 }
2139 }
2140
2141 this.editor().orderChildBefore(sourceSegment, destSegment);
2142 }
2143
2144 protected static List<Object> createPatternsFor( String[] namePatterns ) throws RepositoryException {
2145 List<Object> patterns = new LinkedList<Object>();
2146 for (String stringPattern : namePatterns) {
2147 stringPattern = stringPattern.trim();
2148 int length = stringPattern.length();
2149 if (length == 0) continue;
2150 if (stringPattern.indexOf("*") == -1) {
2151
2152 patterns.add(stringPattern);
2153 } else {
2154
2155 StringBuilder sb = new StringBuilder(length);
2156 for (int i = 0; i != length; i++) {
2157 char c = stringPattern.charAt(i);
2158 switch (c) {
2159
2160 case '/':
2161 case '[':
2162 case ']':
2163 case '\'':
2164 case '"':
2165 case '|':
2166 case '\t':
2167 case '\n':
2168 case '\r':
2169 String msg = JcrI18n.invalidNamePattern.text(c, stringPattern);
2170 throw new RepositoryException(msg);
2171
2172 case '?':
2173 case '(':
2174 case ')':
2175 case '$':
2176 case '^':
2177 case '.':
2178 case '{':
2179 case '}':
2180 case '\\':
2181 sb.append("\\");
2182 sb.append(c);
2183 break;
2184 case '*':
2185
2186 sb.append(".*");
2187 break;
2188 default:
2189 sb.append(c);
2190 break;
2191 }
2192 }
2193 String escapedString = sb.toString();
2194 Pattern pattern = Pattern.compile(escapedString);
2195 patterns.add(pattern);
2196 }
2197 }
2198 return patterns;
2199 }
2200
2201
2202
2203
2204
2205
2206 public void refresh( boolean keepChanges ) throws RepositoryException {
2207 checkSession();
2208 this.cache.refresh(this.nodeId, location.getPath(), keepChanges);
2209 }
2210
2211
2212
2213
2214
2215
2216 public void save() throws RepositoryException {
2217 checkSession();
2218 session().checkReferentialIntegrityOfChanges(this);
2219 cache.save(nodeId, location.getPath());
2220 }
2221
2222 @Override
2223 public String toString() {
2224
2225 try {
2226 PropertyIterator iter = this.getProperties();
2227 StringBuffer propertyBuff = new StringBuffer();
2228 while (iter.hasNext()) {
2229 AbstractJcrProperty prop = (AbstractJcrProperty)iter.nextProperty();
2230 propertyBuff.append(prop).append(", ");
2231 }
2232 return this.getPath() + " {" + propertyBuff.toString() + "}";
2233 } catch (RepositoryException re) {
2234 return re.getMessage();
2235 }
2236 }
2237
2238
2239
2240
2241
2242
2243 @Override
2244 public boolean equals( Object obj ) {
2245 if (obj == this) return true;
2246 if (obj instanceof AbstractJcrNode) {
2247 AbstractJcrNode that = (AbstractJcrNode)obj;
2248 if (this.cache != that.cache) return false;
2249 return this.location.equals(that.location);
2250 }
2251 return false;
2252 }
2253
2254
2255
2256
2257
2258
2259 @Override
2260 public int hashCode() {
2261 return HashCode.compute(cache, location.getUuid());
2262 }
2263
2264
2265
2266
2267
2268
2269 @SuppressWarnings( "unused" )
2270 @Override
2271 public void followLifecycleTransition( String transition )
2272 throws UnsupportedRepositoryOperationException, InvalidLifecycleTransitionException, RepositoryException {
2273 throw new UnsupportedRepositoryOperationException();
2274 }
2275
2276
2277
2278
2279
2280
2281 @Override
2282 public String[] getAllowedLifecycleTransistions() throws UnsupportedRepositoryOperationException, RepositoryException {
2283 throw new UnsupportedRepositoryOperationException();
2284 }
2285
2286
2287
2288
2289
2290
2291 @Override
2292 public NodeIterator getSharedSet() throws RepositoryException {
2293 if (isShareable()) {
2294
2295 return sharedSet();
2296 }
2297
2298 return new JcrSingleNodeIterator(this);
2299 }
2300
2301
2302
2303
2304
2305
2306
2307
2308
2309 NodeIterator sharedSet() throws RepositoryException {
2310 AbstractJcrNode original = this;
2311 String identifierOfSharedNode = getIdentifier();
2312 if (this instanceof JcrSharedNode) {
2313 original = ((JcrSharedNode)this).originalNode();
2314 }
2315
2316 QueryBuilder builder = new QueryBuilder(context().getValueFactories().getTypeSystem());
2317 QueryCommand query = builder.select("jcr:primaryType")
2318 .from("mode:share")
2319 .where()
2320 .referenceValue("mode:share", "mode:sharedUuid")
2321 .isEqualTo(identifierOfSharedNode)
2322 .end()
2323 .query();
2324 Query jcrQuery = session().workspace().queryManager().createQuery(query);
2325 QueryResult result = jcrQuery.execute();
2326
2327 return new JcrNodeIterator(original, result.getNodes());
2328 }
2329
2330
2331
2332
2333
2334
2335 @Override
2336 public void removeShare() throws VersionException, LockException, ConstraintViolationException, RepositoryException {
2337 if (isShareable()) {
2338
2339 NodeIterator sharedSetNodes = sharedSet();
2340 long sharedSetSize = sharedSetNodes.getSize();
2341 if (sharedSetSize <= 1) {
2342
2343 doRemove();
2344 return;
2345 }
2346
2347 AbstractJcrNode originalNode = (AbstractJcrNode)sharedSetNodes.nextNode();
2348 if (originalNode == this) {
2349
2350 JcrSharedNode firstProxy = (JcrSharedNode)sharedSetNodes.nextNode();
2351 assert !this.isRoot();
2352 assert !firstProxy.isRoot();
2353 boolean sameParent = firstProxy.getParent().equals(this.getParent());
2354 NodeEditor parentEditor = firstProxy.editorForParent();
2355 if (sameParent) {
2356
2357 parentEditor.orderChildBefore(this.segment(), firstProxy.segment());
2358
2359 firstProxy.doRemove();
2360 } else {
2361
2362 Node<JcrNodePayload, JcrPropertyPayload> proxyNode = firstProxy.proxyInfo();
2363 Node<JcrNodePayload, JcrPropertyPayload> nextChild = parentEditor.node().getChildAfter(proxyNode);
2364 Name newName = proxyNode.getName();
2365
2366 firstProxy.doRemove();
2367
2368 Node<JcrNodePayload, JcrPropertyPayload> newNode = parentEditor.moveToBeChild(this, newName);
2369 if (nextChild != null) {
2370
2371 parentEditor.orderChildBefore(newNode.getSegment(), nextChild.getSegment());
2372 }
2373 }
2374 } else {
2375
2376 doRemove();
2377 }
2378 return;
2379 }
2380
2381
2382 doRemove();
2383 }
2384
2385
2386
2387
2388
2389
2390 @Override
2391 public void removeSharedSet() throws VersionException, LockException, ConstraintViolationException, RepositoryException {
2392 if (isShareable()) {
2393
2394 NodeIterator sharedSetNodes = sharedSet();
2395 while (sharedSetNodes.hasNext()) {
2396 AbstractJcrNode nodeInSharedSet = (AbstractJcrNode)sharedSetNodes.nextNode();
2397 nodeInSharedSet.doRemove();
2398 }
2399 } else {
2400
2401
2402
2403 doRemove();
2404 }
2405 }
2406
2407
2408
2409
2410
2411
2412
2413
2414
2415
2416
2417
2418
2419
2420
2421
2422 @Override
2423 public void remove()
2424 throws VersionException, LockException, ConstraintViolationException, AccessDeniedException, RepositoryException {
2425
2426
2427 removeShare();
2428 }
2429
2430
2431
2432
2433
2434
2435
2436
2437
2438
2439 protected abstract void doRemove()
2440 throws VersionException, LockException, ConstraintViolationException, AccessDeniedException, RepositoryException;
2441
2442 }