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