View Javadoc

1   /**
2    * This file Copyright (c) 2011-2013 Magnolia International
3    * Ltd.  (http://www.magnolia-cms.com). All rights reserved.
4    *
5    *
6    * This file is dual-licensed under both the Magnolia
7    * Network Agreement and the GNU General Public License.
8    * You may elect to use one or the other of these licenses.
9    *
10   * This file is distributed in the hope that it will be
11   * useful, but AS-IS and WITHOUT ANY WARRANTY; without even the
12   * implied warranty of MERCHANTABILITY or FITNESS FOR A
13   * PARTICULAR PURPOSE, TITLE, or NONINFRINGEMENT.
14   * Redistribution, except as permitted by whichever of the GPL
15   * or MNA you select, is prohibited.
16   *
17   * 1. For the GPL license (GPL), you can redistribute and/or
18   * modify this file under the terms of the GNU General
19   * Public License, Version 3, as published by the Free Software
20   * Foundation.  You should have received a copy of the GNU
21   * General Public License, Version 3 along with this program;
22   * if not, write to the Free Software Foundation, Inc., 51
23   * Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
24   *
25   * 2. For the Magnolia Network Agreement (MNA), this file
26   * and the accompanying materials are made available under the
27   * terms of the MNA which accompanies this distribution, and
28   * is available at http://www.magnolia-cms.com/mna.html
29   *
30   * Any modifications to this file must keep this entire header
31   * intact.
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   * Various utility methods to collect data from JCR repository.
75   *
76   * @version $Id$
77   */
78  public class NodeUtil {
79  
80      private static final Logger log = LoggerFactory.getLogger(NodeUtil.class);
81  
82      /**
83       * Node filter accepting everything except nodes with namespace jcr (version and system store).
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       * Node filter accepting everything except meta data and jcr types.
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      * Node filter accepting all nodes of a type with namespace mgnl.
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                 // accept only "magnolia" nodes
123                 return nodeTypeName.startsWith(MgnlNodeType.MGNL_PREFIX);
124             } catch (RepositoryException e) {
125                 // TODO should we really mask this error? shouldn't it be thrown instead?
126                 log.error("Unable to read nodetype for node {}", getNodePathIfPossible(node));
127             }
128             return false;
129         }
130     };
131 
132     /**
133      * Get a Node by identifier.
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      * from default content.
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      * TODO dlipp: better name? Clear javadoc! Move to MetaDataUtil, do not assign method-param! TODO cringele :
166      * shouldn't @param nodeType be aligned to JCR API? There it is nodeTypeName, nodeType is used for NodeType object
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         // if the node is frozen, and we're not looking specifically for frozen nodes, then we compare with the original
172         // node type
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      * Removes a wrapper by type. The wrapper can be deep in a chain of wrappers in which case wrappers before it will
193      * be cloned creating a new chain that leads to the same real node.
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      * Removes all wrappers of a given type. Other wrappers are cloned creating a new chain that leads to the same real
204      * node.
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             // If the unwrapping had no effect we're done
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      * Convenience - delegate to {@link Node#orderBefore(String, String)}.
232      */
233     public static void orderBefore(Node node, String siblingName) throws RepositoryException {
234         node.getParent().orderBefore(node.getName(), siblingName);
235     }
236 
237     /**
238      * Orders the node directly after a given sibling. If no sibling is specified the node is placed first.
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         // Move the node before the sibling directly after the target sibling
257         parent.orderBefore(node.getName(), siblingAfter.getName());
258     }
259 
260     /**
261      * Orders the node first among its siblings.
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      * Orders the node last among its siblings.
274      */
275     public static void orderLast(Node node) throws RepositoryException {
276         node.getParent().orderBefore(node.getName(), null);
277     }
278 
279     /**
280      * Orders the node up one step among its siblings. If the node is the only sibling or the first sibling this method
281      * has no effect.
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      * Orders the node down one step among its siblings. If the node is the only sibling or the last sibling this method
292      * has no effect.
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      * Gets the siblings of this node.
329      * @param node node from which will be siblings retrieved
330      * @param nodeTypeName requested type of siblings nodes
331      * @return list of siblings of the given Node (only the given node is excluded)
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      * Gets the siblings of this node with certain type.
348      * @param node node from which will be siblings retrieved
349      * @param nodeTypeName requested type of siblings nodes
350      * @return list of siblings of the given Node (the given node is excluded)
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      * Gets the siblings of this node according to predicate.
367      * @param node node from which will be siblings retrieved
368      * @param predicate predicate
369      * @return list of siblings of the given Node (the given node is excluded)
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      * Gets the siblings before this node.
386      * @param node node from which will be siblings retrieved
387      * @return list of siblings before the given Node (the given node is excluded)
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      * Gets the siblings after this node.
405      * @param node node from which will be siblings retrieved
406      * @return list of siblings after the given Node (the given node is excluded)
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      * Gets the siblings before this node with certain type.
425      * @param node node from which will be siblings retrieved
426      * @param nodeTypeName requested type of siblings nodes
427      * @return list of siblings before the given Node (the given node is excluded)
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      * Gets the siblings after this node with certain type.
447      * @param node node from which will be siblings retrieved
448      * @param nodeTypeName requested type of siblings nodes
449      * @return list of siblings after the given Node (the given node is excluded)
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      * Check if node1 and node2 are siblings.
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      * @return Whether the provided node as the provided permission or not.
534      * @throws RuntimeException
535      *             in case of RepositoryException.
536      */
537     public static boolean isGranted(Node node, long permissions) {
538         try {
539             return PermissionUtil.isGranted(node, permissions);
540         } catch (RepositoryException e) {
541             // TODO dlipp - apply consistent ExceptionHandling
542             throw new RuntimeException(e);
543         }
544     }
545 
546     /**
547      * Returns true if both arguments represents the same node. In case the nodes are wrapped the comparison is done one
548      * the actual nodes behind the wrappers.
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      * Creates a node under the specified parent and relative path, then returns it. Should the node already exist, the
563      * method will simply return it.
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      * Creates a node under the specified parent and relative path, then returns it. Should the node already exist, the
571      * method will simply return it.
572      */
573     public static Node createPath(Node parent, String relPath, String primaryNodeTypeName, boolean save) throws RepositoryException, PathNotFoundException, AccessDeniedException {
574         // remove leading /
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      * Visits the given node and then all of nodes beneath it except for metadata nodes and nodes of jcr type.
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         // TODO should it really visit the start node even if it doesn't match the filter?
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      * This method return the node's name on success, otherwise it handles the {@link RepositoryException} by throwing a
646      * {@link RuntimeRepositoryException}.
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      * Get all children (by recursion) using MAGNOLIA_FILTER (filter accepting all nodes of a type with namespace mgnl).
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      * Get all children (by recursion) using a Predicate.
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      * Get all children (by recursion) using a Predicate.
674      * // TODO this method should really be private or renamed
675      */
676     public static Iterable<Node> collectAllChildren(List<Node> nodes, Node parent, Predicate predicate) throws RepositoryException {
677         // get filtered sub nodes first
678         nodes.addAll(asList(getNodes(parent, predicate)));
679 
680         // get all children to find recursively
681         Iterator<Node> allChildren = getNodes(parent, EXCLUDE_META_DATA_FILTER).iterator();
682 
683         // recursion
684         while (allChildren.hasNext()) {
685             collectAllChildren(nodes, allChildren.next(), predicate);
686         }
687 
688         return nodes;
689     }
690 
691     /**
692      * Get all Ancestors until level 1.
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      * Used for building exception messages where we want to avoid handling another exception inside a throws clause.
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      * Return the Path of the node.
728      *
729      * @return the path for the node or an empty String in case of exception
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 }