1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34 package info.magnolia.jcr.util;
35
36 import info.magnolia.cms.security.AccessDeniedException;
37 import info.magnolia.cms.security.PermissionUtil;
38 import info.magnolia.context.MgnlContext;
39 import info.magnolia.jcr.RuntimeRepositoryException;
40 import info.magnolia.jcr.iterator.NodeIterableAdapter;
41 import info.magnolia.jcr.predicate.AbstractPredicate;
42 import info.magnolia.jcr.wrapper.DelegateNodeWrapper;
43 import info.magnolia.jcr.wrapper.JCRPropertiesFilteringNodeWrapper;
44
45 import java.util.ArrayList;
46 import java.util.Collection;
47 import java.util.Iterator;
48 import java.util.LinkedHashSet;
49 import java.util.List;
50
51 import javax.jcr.Node;
52 import javax.jcr.NodeIterator;
53 import javax.jcr.PathNotFoundException;
54 import javax.jcr.Property;
55 import javax.jcr.RepositoryException;
56 import javax.jcr.Session;
57 import javax.jcr.nodetype.NodeType;
58 import javax.jcr.nodetype.NodeTypeManager;
59
60 import org.apache.commons.lang.StringUtils;
61 import org.apache.jackrabbit.JcrConstants;
62 import org.apache.jackrabbit.commons.iterator.FilteringNodeIterator;
63 import org.apache.jackrabbit.commons.predicate.NodeTypePredicate;
64 import org.apache.jackrabbit.commons.predicate.Predicate;
65 import org.slf4j.Logger;
66 import org.slf4j.LoggerFactory;
67
68
69
70
71 public class NodeUtil {
72
73 private static final Logger log = LoggerFactory.getLogger(NodeUtil.class);
74
75
76
77
78
79 public static AbstractPredicate<Property> ALL_PROPERTIES_EXCEPT_JCR_AND_MGNL_FILTER = new AbstractPredicate<Property>() {
80
81 @Override
82 public boolean evaluateTyped(Property property) {
83 try {
84 String name = property.getName();
85 return !name.startsWith(NodeTypes.JCR_PREFIX) && !name.startsWith(NodeTypes.MGNL_PREFIX);
86 } catch (RepositoryException e) {
87 String path;
88 try {
89 path = property.getPath();
90 } catch (RepositoryException e1) {
91 path = "<path not available>";
92 }
93 log.error("Unable to read name of property {}", path);
94
95 return false;
96 }
97 }
98 };
99
100
101
102
103
104 public static Predicate ALL_NODES_EXCEPT_JCR_FILTER = new AbstractPredicate<Node>() {
105 @Override
106 public boolean evaluateTyped(Node node) {
107 try {
108 return !node.getName().startsWith(NodeTypes.JCR_PREFIX);
109 } catch (RepositoryException e) {
110 log.error("Unable to read name for node {}", getNodePathIfPossible(node));
111 return false;
112 }
113 }
114 };
115
116
117
118
119
120 public static AbstractPredicate<Node> EXCLUDE_META_DATA_FILTER = new AbstractPredicate<Node>() {
121
122 @Override
123 public boolean evaluateTyped(Node node) {
124 try {
125 return !node.getName().startsWith(NodeTypes.JCR_PREFIX)
126 && !NodeUtil.isNodeType(node, NodeTypes.MetaData.NAME);
127 } catch (RepositoryException e) {
128 log.error("Unable to read name or nodeType for node {}", getNodePathIfPossible(node));
129 return false;
130 }
131 }
132 };
133
134
135
136
137 public static AbstractPredicate<Node> MAGNOLIA_FILTER = new AbstractPredicate<Node>() {
138
139 @Override
140 public boolean evaluateTyped(Node node) {
141
142 try {
143 String nodeTypeName = node.getPrimaryNodeType().getName();
144
145 return nodeTypeName.startsWith(NodeTypes.MGNL_PREFIX);
146 } catch (RepositoryException e) {
147 log.error("Unable to read nodeType for node {}", getNodePathIfPossible(node));
148 }
149 return false;
150 }
151 };
152
153
154
155
156 public static Node getNodeByIdentifier(String workspace, String identifier) throws RepositoryException {
157 Node target = null;
158 Session jcrSession;
159 if (workspace == null || identifier == null) {
160 return target;
161 }
162
163 jcrSession = MgnlContext.getJCRSession(workspace);
164 if (jcrSession != null) {
165 target = jcrSession.getNodeByIdentifier(identifier);
166 }
167 return target;
168 }
169
170
171
172
173
174 public static boolean hasMixin(Node node, String mixinName) throws RepositoryException {
175 if (StringUtils.isBlank(mixinName)) {
176 throw new IllegalArgumentException("Mixin name can't be empty.");
177 }
178 for (NodeType type : node.getMixinNodeTypes()) {
179 if (mixinName.equals(type.getName())) {
180 return true;
181 }
182 }
183 return false;
184 }
185
186
187
188
189
190 public static boolean isNodeType(Node node, String nodeTypeName) throws RepositoryException {
191 node = NodeUtil.deepUnwrap(node, JCRPropertiesFilteringNodeWrapper.class);
192 final String actualType = node.getProperty(JcrConstants.JCR_PRIMARYTYPE).getString();
193
194
195 if (JcrConstants.NT_FROZENNODE.equals(actualType) && !(JcrConstants.NT_FROZENNODE.equals(nodeTypeName))) {
196 final Property p = node.getProperty(JcrConstants.JCR_FROZENPRIMARYTYPE);
197 final String s = p.getString();
198 NodeTypeManager ntManager = node.getSession().getWorkspace().getNodeTypeManager();
199 NodeType primaryNodeType = ntManager.getNodeType(s);
200 return primaryNodeType.isNodeType(nodeTypeName);
201 }
202 return node.isNodeType(nodeTypeName);
203 }
204
205 public static Node unwrap(Node node) throws RepositoryException {
206 Node unwrappedNode = node;
207 while (unwrappedNode instanceof DelegateNodeWrapper) {
208 unwrappedNode = ((DelegateNodeWrapper) unwrappedNode).getWrappedNode();
209 }
210 return unwrappedNode;
211 }
212
213
214
215
216
217 public static Node deepUnwrap(Node node, Class<? extends DelegateNodeWrapper> wrapper) {
218 if (node instanceof DelegateNodeWrapper) {
219 return ((DelegateNodeWrapper) node).deepUnwrap(wrapper);
220 }
221 return node;
222 }
223
224
225
226
227
228 public static Node deepUnwrapAll(Node node, Class<? extends DelegateNodeWrapper> wrapperClass) {
229 while (node instanceof DelegateNodeWrapper) {
230 Node unwrapped = ((DelegateNodeWrapper) node).deepUnwrap(wrapperClass);
231
232 if (unwrapped == node) {
233 break;
234 }
235 node = unwrapped;
236 }
237 return node;
238 }
239
240 public static boolean isWrappedWith(Node node, Class<? extends DelegateNodeWrapper> wrapper) {
241 if (wrapper.isInstance(node)){
242 return true;
243 }
244
245 if (node instanceof DelegateNodeWrapper) {
246 return isWrappedWith(((DelegateNodeWrapper)node).getWrappedNode(), wrapper);
247 }
248 return false;
249 }
250
251
252
253
254
255 public static void orderBefore(Node node, String siblingName) throws RepositoryException {
256 node.getParent().orderBefore(node.getName(), siblingName);
257 }
258
259
260
261
262 public static void orderAfter(Node node, String siblingName) throws RepositoryException {
263
264 if (siblingName == null) {
265 orderFirst(node);
266 return;
267 }
268
269 Node parent = node.getParent();
270 Node sibling = parent.getNode(siblingName);
271 Node siblingAfter = getSiblingAfter(sibling);
272
273 if (siblingAfter == null) {
274 orderLast(node);
275 return;
276 }
277
278
279 parent.orderBefore(node.getName(), siblingAfter.getName());
280 }
281
282
283
284
285 public static void orderFirst(Node node) throws RepositoryException {
286 Node parent = node.getParent();
287 NodeIterator siblings = parent.getNodes();
288 Node firstSibling = siblings.nextNode();
289 if (!firstSibling.isSame(node)) {
290 parent.orderBefore(node.getName(), firstSibling.getName());
291 }
292 }
293
294
295
296
297 public static void orderLast(Node node) throws RepositoryException {
298 node.getParent().orderBefore(node.getName(), null);
299 }
300
301
302
303
304
305 public static void orderNodeUp(Node node) throws RepositoryException {
306 Node siblingBefore = getSiblingBefore(node);
307 if (siblingBefore != null) {
308 node.getParent().orderBefore(node.getName(), siblingBefore.getName());
309 }
310 }
311
312
313
314
315
316 public static void orderNodeDown(Node node) throws RepositoryException {
317 Node siblingAfter = getSiblingAfter(node);
318 if (siblingAfter != null) {
319 node.getParent().orderBefore(siblingAfter.getName(), node.getName());
320 }
321 }
322
323 public static Node getSiblingBefore(Node node) throws RepositoryException {
324 Node parent = node.getParent();
325 NodeIterator siblings = parent.getNodes();
326 Node previousSibling = null;
327 while (siblings.hasNext()) {
328 Node sibling = siblings.nextNode();
329 if (isSame(node, sibling)) {
330 return previousSibling;
331 }
332 previousSibling = sibling;
333 }
334 return null;
335 }
336
337 public static Node getSiblingAfter(Node node) throws RepositoryException {
338 Node parent = node.getParent();
339 NodeIterator siblings = parent.getNodes();
340 while (siblings.hasNext()) {
341 Node sibling = siblings.nextNode();
342 if (isSame(node, sibling)) {
343 break;
344 }
345 }
346 return siblings.hasNext() ? siblings.nextNode() : null;
347 }
348
349
350
351
352
353
354 public static Iterable<Node> getSiblings(Node node) throws RepositoryException {
355 Node parent = node.getParent();
356 Iterable<Node> allSiblings = NodeUtil.getNodes(parent);
357 List<Node> siblings = new ArrayList<Node>();
358
359 for(Node sibling: allSiblings) {
360 if (!NodeUtil.isSame(node, sibling)) {
361 siblings.add(sibling);
362 }
363 }
364 return siblings;
365 }
366
367
368
369
370
371
372
373 public static Iterable<Node> getSiblings(Node node, String nodeTypeName) throws RepositoryException {
374 Node parent = node.getParent();
375 Iterable<Node> allSiblings = NodeUtil.getNodes(parent, nodeTypeName);
376 List<Node> sameTypeSiblings = new ArrayList<Node>();
377
378 for(Node sibling: allSiblings) {
379 if (!NodeUtil.isSame(node, sibling)) {
380 sameTypeSiblings.add(sibling);
381 }
382 }
383 return sameTypeSiblings;
384 }
385
386
387
388
389
390
391
392 public static Iterable<Node> getSiblings(Node node, Predicate predicate) throws RepositoryException {
393 Node parent = node.getParent();
394 Iterable<Node> allSiblings = NodeUtil.getNodes(parent, predicate);
395 List<Node> sameTypeSiblings = new ArrayList<Node>();
396
397 for(Node sibling: allSiblings) {
398 if (!NodeUtil.isSame(node, sibling)) {
399 sameTypeSiblings.add(sibling);
400 }
401 }
402 return sameTypeSiblings;
403 }
404
405
406
407
408
409
410 public static Iterable<Node> getSiblingsBefore(Node node) throws RepositoryException {
411 int toIndex = 0;
412 Node parent = node.getParent();
413 List<Node> allSiblings = NodeUtil.asList(NodeUtil.getNodes(parent));
414
415 for(Node sibling: allSiblings) {
416 if (NodeUtil.isSame(node, sibling)) {
417 break;
418 }
419 toIndex++;
420 }
421 return allSiblings.subList(0, toIndex);
422 }
423
424
425
426
427
428
429 public static Iterable<Node> getSiblingsAfter(Node node) throws RepositoryException {
430 int fromIndex = 0;
431 Node parent = node.getParent();
432 List<Node> allSiblings = NodeUtil.asList(NodeUtil.getNodes(parent));
433
434 for(Node sibling: allSiblings) {
435 if (NodeUtil.isSame(node, sibling)) {
436 fromIndex++;
437 break;
438 }
439 fromIndex++;
440 }
441 return allSiblings.subList(fromIndex, allSiblings.size());
442 }
443
444
445
446
447
448
449
450 public static Iterable<Node> getSiblingsBefore(Node node, String nodeTypeName) throws RepositoryException {
451 Node parent = node.getParent();
452 Iterable<Node> allSiblings = NodeUtil.getNodes(parent);
453 List<Node> sameTypeSiblings = new ArrayList<Node>();
454
455 for(Node sibling: allSiblings) {
456 if (NodeUtil.isSame(node, sibling)) {
457 break;
458 }
459 if (isNodeType(sibling, nodeTypeName)) {
460 sameTypeSiblings.add(sibling);
461 }
462 }
463 return sameTypeSiblings;
464 }
465
466
467
468
469
470
471
472 public static Iterable<Node> getSiblingsAfter(Node node, String nodeTypeName) throws RepositoryException {
473 Node parent = node.getParent();
474 List<Node> allSiblings = NodeUtil.asList(NodeUtil.getNodes(parent));
475 int fromIndex = 0;
476
477 for(Node sibling: allSiblings) {
478 fromIndex++;
479 if (NodeUtil.isSame(node, sibling)) {
480 break;
481 }
482 }
483
484 List<Node> sameTypeSiblings = new ArrayList<Node>();
485 for(Node sibling: allSiblings.subList(fromIndex, allSiblings.size())) {
486 if (isNodeType(sibling, nodeTypeName)) {
487 sameTypeSiblings.add(sibling);
488 }
489 }
490 return sameTypeSiblings;
491 }
492
493 public static void moveNode(Node nodeToMove, Node newParent) throws RepositoryException {
494 if (!isSame(newParent, nodeToMove.getParent())) {
495 String newPath = combinePathAndName(newParent.getPath(), nodeToMove.getName());
496 nodeToMove.getSession().move(nodeToMove.getPath(), newPath);
497 }
498 }
499
500 public static void moveNodeBefore(Node nodeToMove, Node target) throws RepositoryException {
501 Node targetParent = target.getParent();
502 moveNode(nodeToMove, targetParent);
503 targetParent.orderBefore(nodeToMove.getName(), target.getName());
504 }
505
506 public static void moveNodeAfter(Node nodeToMove, Node target) throws RepositoryException {
507 Node targetParent = target.getParent();
508 moveNode(nodeToMove, targetParent);
509 orderAfter(nodeToMove, target.getName());
510 }
511
512 public static boolean isFirstSibling(Node node) throws RepositoryException {
513 Node parent = node.getParent();
514 NodeIterator nodes = parent.getNodes();
515 return isSame(nodes.nextNode(), node);
516 }
517
518
519
520
521 public static boolean isSameNameSiblings(Node node1, Node node2) throws RepositoryException {
522 Node parent1 = node1.getParent();
523 Node parent2 = node2.getParent();
524 return isSame(parent1, parent2) && node1.getName().equals(node2.getName());
525 }
526
527 public static boolean isLastSibling(Node node) throws RepositoryException {
528 Node parent = node.getParent();
529 NodeIterator nodes = parent.getNodes();
530 Node last = null;
531 while (nodes.hasNext()) {
532 last = nodes.nextNode();
533 }
534 return isSame(last, node);
535 }
536
537 public static void renameNode(Node node, String newName) throws RepositoryException {
538 if (node.getName().equals(newName)) {
539 return;
540 }
541 final Node parent = node.getParent();
542 final String newPath = combinePathAndName(parent.getPath(), newName);
543 final Node siblingAfter = NodeUtil.getSiblingAfter(node);
544
545 node.getSession().move(node.getPath(), newPath);
546
547 if(siblingAfter != null) {
548 parent.orderBefore(newName, siblingAfter.getName());
549 }
550 }
551
552
553
554
555
556 public static boolean isGranted(Node node, long permissions) {
557 try {
558 return PermissionUtil.isGranted(node, permissions);
559 } catch (RepositoryException e) {
560 throw new RuntimeRepositoryException(e);
561 }
562 }
563
564
565
566
567
568 public static boolean isSame(Node lhs, Node rhs) throws RepositoryException {
569 return unwrap(lhs).isSame(unwrap(rhs));
570 }
571
572
573
574
575 public static String combinePathAndName(String path, String name) {
576 if ("/".equals(path)) {
577 return "/" + name;
578 }
579 return path + "/" + name;
580 }
581
582
583
584
585
586 public static Node createPath(Node parent, String relPath, String primaryNodeTypeName) throws RepositoryException, PathNotFoundException, AccessDeniedException {
587 return createPath(parent, relPath, primaryNodeTypeName, false);
588 }
589
590
591
592
593
594 public static Node createPath(Node parent, String relPath, String primaryNodeTypeName, boolean save) throws RepositoryException, PathNotFoundException, AccessDeniedException {
595
596 String currentPath = StringUtils.removeStart(relPath, "/");
597
598 if (StringUtils.isEmpty(currentPath)) {
599 return parent;
600 }
601
602 Node root = parent;
603 String[] names = currentPath.split("/");
604
605 for (int i = 0; i < names.length; i++) {
606 String name = names[i];
607 if (root.hasNode(name)) {
608 root = root.getNode(name);
609 } else {
610 final Node newNode = root.addNode(name, primaryNodeTypeName);
611 if (newNode.canAddMixin(JcrConstants.MIX_LOCKABLE)) {
612 newNode.addMixin(JcrConstants.MIX_LOCKABLE);
613 }
614 if (save) {
615 root.getSession().save();
616 }
617 root = newNode;
618 }
619 }
620 return root;
621 }
622
623
624
625
626 public static void visit(Node node, NodeVisitor visitor) throws RepositoryException {
627 visit(node, visitor, EXCLUDE_META_DATA_FILTER);
628 }
629
630 public static void visit(Node node, NodeVisitor visitor, Predicate predicate) throws RepositoryException {
631
632 visitor.visit(node);
633 for (Node child : getNodes(node, predicate)) {
634 visit(child, visitor, predicate);
635 }
636 if (visitor instanceof PostNodeVisitor) {
637 ((PostNodeVisitor) visitor).postVisit(node);
638 }
639 }
640
641 public static Iterable<Node> getNodes(Node parent, Predicate predicate) throws RepositoryException {
642 return asIterable(new FilteringNodeIterator(parent.getNodes(), predicate));
643 }
644
645 public static Iterable<Node> getNodes(Node parent) throws RepositoryException {
646 return getNodes(parent, EXCLUDE_META_DATA_FILTER);
647 }
648
649 public static Iterable<Node> getNodes(Node parent, String nodeTypeName) throws RepositoryException {
650 return getNodes(parent, new NodeTypePredicate(nodeTypeName, false));
651 }
652
653 public static Iterable<Node> asIterable(NodeIterator iterator) {
654 return new NodeIterableAdapter(iterator);
655 }
656
657 public static List<Node> asList(Iterable<Node> nodes) {
658 List<Node> nodesList = new ArrayList<Node>();
659 for (Node node : nodes) {
660 nodesList.add(node);
661 }
662 return nodesList;
663 }
664
665
666
667
668
669 public static String getName(Node content) {
670 try {
671 return content.getName();
672 } catch (RepositoryException e) {
673 throw new RuntimeRepositoryException(e);
674 }
675 }
676
677
678
679
680 public static Iterable<Node> collectAllChildren(Node node) throws RepositoryException {
681 List<Node> nodes = new ArrayList<Node>();
682 return collectAllChildren(nodes, node, MAGNOLIA_FILTER);
683 }
684
685
686
687
688 public static Iterable<Node> collectAllChildren(Node node, Predicate predicate) throws RepositoryException {
689 List<Node> nodes = new ArrayList<Node>();
690 return collectAllChildren(nodes, node, predicate);
691 }
692
693
694
695
696
697 public static Iterable<Node> collectAllChildren(List<Node> nodes, Node parent, Predicate predicate) throws RepositoryException {
698
699 nodes.addAll(asList(getNodes(parent, predicate)));
700
701
702 Iterator<Node> allChildren = getNodes(parent, EXCLUDE_META_DATA_FILTER).iterator();
703
704
705 while (allChildren.hasNext()) {
706 collectAllChildren(nodes, allChildren.next(), predicate);
707 }
708
709 return nodes;
710 }
711
712
713
714
715 public static Collection<Node> getAncestors(Node node) throws RepositoryException {
716 List<Node> allAncestors = new ArrayList<Node>();
717 int level = node.getDepth();
718 while (level != 0) {
719 try {
720 allAncestors.add((Node) node.getAncestor(--level));
721 } catch (AccessDeniedException e) {
722 log.debug("Node " + node.getIdentifier() + " didn't allow access to Ancestor's ");
723 }
724 }
725 return allAncestors;
726 }
727
728
729
730
731
732
733
734
735
736
737 public static Node getNearestAncestorOfType(Node node, String nodeTypeName) throws RepositoryException {
738 if (node.getDepth() == 0) {
739 return null;
740 }
741
742 node = node.getParent();
743
744 while (true) {
745 if (NodeUtil.isNodeType(node, nodeTypeName)) {
746 return node;
747 }
748 if (node.getDepth() == 0) {
749 break;
750 }
751 node = node.getParent();
752 }
753 return null;
754 }
755
756
757
758
759 public static String getNodeIdentifierIfPossible(Node content) {
760 try {
761 return content.getIdentifier();
762 } catch (RepositoryException e) {
763 return "<not available>";
764 }
765 }
766
767 public static String getNodePathIfPossible(Node node) {
768 try {
769 return node.getPath();
770 } catch (RepositoryException e) {
771 return "<not available>";
772 }
773 }
774
775
776
777
778
779
780 public static String getPathIfPossible(Node node) {
781 try {
782 return node.getPath();
783 } catch (RepositoryException e) {
784 log.error("Failed to get handle: " + e.getMessage(), e);
785 return "";
786 }
787 }
788
789 public static NodeIterator filterNodeType(NodeIterator iterator, String nodeType){
790 return new FilteringNodeIterator(iterator, new info.magnolia.jcr.predicate.NodeTypePredicate(nodeType));
791 }
792
793 public static NodeIterator filterDuplicates(NodeIterator iterator){
794 return new FilteringNodeIterator(iterator, new info.magnolia.jcr.predicate.DuplicateNodePredicate());
795 }
796
797 public static NodeIterator filterParentNodeType(NodeIterator iterator, final String nodeType) throws RepositoryException{
798 return new FilteringNodeIterator(iterator, new info.magnolia.jcr.predicate.NodeTypeParentPredicate(nodeType)) {
799 @Override
800 public Node nextNode(){
801 Node node = super.nextNode();
802 try {
803 while(node.getDepth() != 0 && !node.isNodeType(nodeType)){
804 if(node.getDepth() != 0){
805 node = node.getParent();
806 }
807 }
808 } catch (RepositoryException e) {
809 throw new RuntimeException(e.getMessage(), e);
810 }
811 return node;
812 }
813 };
814 }
815
816 public static Collection<Node> getCollectionFromNodeIterator(NodeIterator iterator){
817 Collection<Node> nodeCollection = new LinkedHashSet<Node>(150);
818 while(iterator.hasNext()){
819 nodeCollection.add(iterator.nextNode());
820 }
821 return nodeCollection;
822 }
823
824
825
826
827 @Deprecated
828 public static Collection<Node> getSortedCollectionFromNodeIterator(NodeIterator iterator){
829 return getCollectionFromNodeIterator(iterator);
830 }
831 }