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