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.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   * Various utility methods to collect data from JCR repository.
70   */
71  public class NodeUtil {
72  
73      private static final Logger log = LoggerFactory.getLogger(NodeUtil.class);
74  
75      /**
76       * Predicate hiding properties prefixed with jcr or mgnl.
77       * @deprecated since 5.0 - obsolete as there's no nodetypes with namespace jcr. In addition you could use {@link info.magnolia.jcr.predicate.JCRMgnlPropertyHidingPredicate}
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                  // either invalid or not accessible to the current user
95                  return false;
96              }
97          }
98      };
99  
100     /**
101      * Node filter accepting everything except nodes with namespace jcr (version and system store).
102      * @deprecated since 5.0 - obsolete as there's no nodetypes with namespace jcr
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      * Node filter accepting everything except meta data and jcr types.
118      * @deprecated since 5.0 - obsolete as there's no nodetypes with namespace jcr and because of MAGNOLIA-4640
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      * Node filter accepting all nodes of a type with namespace mgnl.
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                 // accept only "magnolia" nodes
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      * Get a Node by identifier.
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      * Checks if the node has a mixin assigned. This includes only those mixin types explicitly assigned to this node.
172      * @see javax.jcr.Node#getMixinNodeTypes()
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      * Checks if the node is of the supplied node type or if the node type is a mixin checks if the node has the mixin.
188      * Also, if the node is frozen checks the node's type prior to being frozen.
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         // if the node is frozen, and we're not looking specifically for frozen nodes, then we compare with the original
194         // node type
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      * Removes a wrapper by type. The wrapper can be deep in a chain of wrappers in which case wrappers before it will
215      * be cloned creating a new chain that leads to the same real node.
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      * Removes all wrappers of a given type. Other wrappers are cloned creating a new chain that leads to the same real
226      * node.
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             // If the unwrapping had no effect we're done
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      * Convenience - delegate to {@link Node#orderBefore(String, String)}.
254      */
255     public static void orderBefore(Node node, String siblingName) throws RepositoryException {
256         node.getParent().orderBefore(node.getName(), siblingName);
257     }
258 
259     /**
260      * Orders the node directly after a given sibling. If no sibling is specified the node is placed first.
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         // Move the node before the sibling directly after the target sibling
279         parent.orderBefore(node.getName(), siblingAfter.getName());
280     }
281 
282     /**
283      * Orders the node first among its siblings.
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      * Orders the node last among its siblings.
296      */
297     public static void orderLast(Node node) throws RepositoryException {
298         node.getParent().orderBefore(node.getName(), null);
299     }
300 
301     /**
302      * Orders the node up one step among its siblings. If the node is the only sibling or the first sibling this method
303      * has no effect.
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      * Orders the node down one step among its siblings. If the node is the only sibling or the last sibling this method
314      * has no effect.
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      * Gets the siblings of this node.
351      * @param node node from which will be siblings retrieved
352      * @return list of siblings of the given Node (only the given node is excluded)
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      * Gets the siblings of this node with certain type.
369      * @param node node from which will be siblings retrieved
370      * @param nodeTypeName requested type of siblings nodes
371      * @return list of siblings of the given Node (the given node is excluded)
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      * Gets the siblings of this node according to predicate.
388      * @param node node from which will be siblings retrieved
389      * @param predicate predicate
390      * @return list of siblings of the given Node (the given node is excluded)
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      * Gets the siblings before this node.
407      * @param node node from which will be siblings retrieved
408      * @return list of siblings before the given Node (the given node is excluded)
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      * Gets the siblings after this node.
426      * @param node node from which will be siblings retrieved
427      * @return list of siblings after the given Node (the given node is excluded)
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      * Gets the siblings before this node with certain type.
446      * @param node node from which will be siblings retrieved
447      * @param nodeTypeName requested type of siblings nodes
448      * @return list of siblings before the given Node (the given node is excluded)
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      * Gets the siblings after this node with certain type.
468      * @param node node from which will be siblings retrieved
469      * @param nodeTypeName requested type of siblings nodes
470      * @return list of siblings after the given Node (the given node is excluded)
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      * Check if node1 and node2 are siblings.
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      * @return Whether the provided node as the provided permission or not.
554      * @throws RuntimeRepositoryException in case of RepositoryException.
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      * Returns true if both arguments represents the same node. In case the nodes are wrapped the comparison is done one
566      * the actual nodes behind the wrappers.
567      */
568     public static boolean isSame(Node lhs, Node rhs) throws RepositoryException {
569         return unwrap(lhs).isSame(unwrap(rhs));
570     }
571 
572     /**
573      * @return a valid jcr path combined from the provided path and name.
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      * Creates a node under the specified parent and relative path, then returns it. Should the node already exist, the
584      * method will simply return it.
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      * Creates a node under the specified parent and relative path, then returns it. Should the node already exist, the
592      * method will simply return it.
593      */
594     public static Node createPath(Node parent, String relPath, String primaryNodeTypeName, boolean save) throws RepositoryException, PathNotFoundException, AccessDeniedException {
595         // remove leading /
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      * Visits the given node and then all of nodes beneath it except for metadata nodes and nodes of jcr type.
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         // TODO should it really visit the start node even if it doesn't match the filter?
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      * This method return the node's name on success, otherwise it handles the {@link RepositoryException} by throwing a
667      * {@link RuntimeRepositoryException}.
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      * Get all children (by recursion) using MAGNOLIA_FILTER (filter accepting all nodes of a type with namespace mgnl).
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      * Get all children (by recursion) using a Predicate.
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      * Get all children (by recursion) using a Predicate.
695      * // TODO this method should really be private or renamed
696      */
697     public static Iterable<Node> collectAllChildren(List<Node> nodes, Node parent, Predicate predicate) throws RepositoryException {
698         // get filtered sub nodes first
699         nodes.addAll(asList(getNodes(parent, predicate)));
700 
701         // get all children to find recursively
702         Iterator<Node> allChildren = getNodes(parent, EXCLUDE_META_DATA_FILTER).iterator();
703 
704         // recursion
705         while (allChildren.hasNext()) {
706             collectAllChildren(nodes, allChildren.next(), predicate);
707         }
708 
709         return nodes;
710     }
711 
712     /**
713      * Get all Ancestors until level 1.
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      * Returns the nearest ancestor of a certain node type or null if none of the ancestors have the node type. The
730      * nearest node is the first parent walking from the node and upwards. Works also for mixins and subtypes.
731      *
732      * @param node the node whose ancestors to consider
733      * @param nodeTypeName the node type to query for
734      * @return the nearest ancestor having the node type or null if none exists
735      * @throws RepositoryException
736      */
737     public static Node getNearestAncestorOfType(Node node, String nodeTypeName) throws RepositoryException {
738         if (node.getDepth() == 0) {
739             return null;
740         }
741         // skip the node itself
742         node = node.getParent();
743         // iterate ancestors upwards including the root node
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      * Used for building exception messages where we want to avoid handling another exception inside a throws clause.
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      * Return the Path of the node.
777      *
778      * @return the path for the node or an empty String in case of exception
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      * @Deprecated since 5.1. Use {@link #getCollectionFromNodeIterator(NodeIterator)} instead.
826      */
827     @Deprecated
828     public static Collection<Node> getSortedCollectionFromNodeIterator(NodeIterator iterator){
829         return getCollectionFromNodeIterator(iterator);
830     }
831 }