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