View Javadoc

1   /**
2    * This file Copyright (c) 2011-2012 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.List;
51  
52  import javax.jcr.Node;
53  import javax.jcr.NodeIterator;
54  import javax.jcr.PathNotFoundException;
55  import javax.jcr.Property;
56  import javax.jcr.RepositoryException;
57  import javax.jcr.Session;
58  import javax.jcr.nodetype.NodeType;
59  import javax.jcr.nodetype.NodeTypeManager;
60  
61  import org.apache.commons.lang.StringUtils;
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   * @version $Id$
72   */
73  public class NodeUtil {
74  
75      private static final Logger log = LoggerFactory.getLogger(NodeUtil.class);
76  
77      /**
78       * Node filter accepting everything except nodes with namespace jcr (version and system store).
79       */
80      public static Predicate ALL_NODES_EXCEPT_JCR_FILTER = new AbstractPredicate<Node>() {
81          @Override
82          public boolean evaluateTyped(Node node) {
83              try {
84                  return !node.getName().startsWith(MgnlNodeType.JCR_PREFIX);
85              } catch (RepositoryException e) {
86                  return false;
87              }
88          }
89      };
90  
91      /**
92       * Node filter accepting everything except meta data and jcr types.
93       */
94      public static AbstractPredicate<Node> EXCLUDE_META_DATA_FILTER = new AbstractPredicate<Node>() {
95  
96          @Override
97          public boolean evaluateTyped(Node node) {
98              try {
99                  return !node.getName().startsWith(MgnlNodeType.JCR_PREFIX)
100                 && !NodeUtil.isNodeType(node, MgnlNodeType.NT_METADATA);
101             } catch (RepositoryException e) {
102                 return false;
103             }
104         }
105     };
106 
107     /**
108      * Node filter accepting all nodes of a type with namespace mgnl.
109      */
110     public static AbstractPredicate<Node> MAGNOLIA_FILTER = new AbstractPredicate<Node>() {
111 
112         @Override
113         public boolean evaluateTyped(Node node) {
114 
115             try {
116                 String nodeTypeName = node.getPrimaryNodeType().getName();
117                 // accept only "magnolia" nodes
118                 return nodeTypeName.startsWith(MgnlNodeType.MGNL_PREFIX);
119             } catch (RepositoryException e) {
120                 // TODO should we really mask this error? shouldn't it be thrown instead?
121                 log.error("Unable to read nodetype for node {}", getNodePathIfPossible(node));
122             }
123             return false;
124         }
125     };
126 
127     /**
128      * Get a Node by identifier.
129      */
130     public static Node getNodeByIdentifier(String workspace, String identifier) throws RepositoryException {
131         Node target = null;
132         Session jcrSession;
133         if (workspace == null || identifier == null) {
134             return target;
135         }
136 
137         jcrSession = MgnlContext.getJCRSession(workspace);
138         if (jcrSession != null) {
139             target = jcrSession.getNodeByIdentifier(identifier);
140         }
141         return target;
142     }
143 
144     /**
145      * from default content.
146      */
147     public static boolean hasMixin(Node node, String mixinName) throws RepositoryException {
148         if (StringUtils.isBlank(mixinName)) {
149             throw new IllegalArgumentException("Mixin name can't be empty.");
150         }
151         for (NodeType type : node.getMixinNodeTypes()) {
152             if (mixinName.equals(type.getName())) {
153                 return true;
154             }
155         }
156         return false;
157     }
158 
159     /**
160      * TODO dlipp: better name? Clear javadoc! Move to MetaDataUtil, do not assign method-param! TODO cringele :
161      * shouldn't @param nodeType be aligned to JCR API? There it is nodeTypeName, nodeType is used for NodeType object
162      */
163     public static boolean isNodeType(Node node, String type) throws RepositoryException {
164         node = NodeUtil.deepUnwrap(node, JCRPropertiesFilteringNodeWrapper.class);
165         final String actualType = node.getProperty(MgnlNodeType.JCR_PRIMARY_TYPE).getString();
166         // if the node is frozen, and we're not looking specifically for frozen nodes, then we compare with the original
167         // node type
168         if (MgnlNodeType.NT_FROZENNODE.equals(actualType) && !(MgnlNodeType.NT_FROZENNODE.equals(type))) {
169             final Property p = node.getProperty(MgnlNodeType.JCR_FROZEN_PRIMARY_TYPE);
170             final String s = p.getString();
171             NodeTypeManager ntManager = node.getSession().getWorkspace().getNodeTypeManager();
172             NodeType primaryNodeType = ntManager.getNodeType(s);
173             return primaryNodeType.isNodeType(type);
174         }
175         return node.isNodeType(type);
176     }
177 
178     public static Node unwrap(Node node) throws RepositoryException {
179         Node unwrappedNode = node;
180         while (unwrappedNode instanceof DelegateNodeWrapper) {
181             unwrappedNode = ((DelegateNodeWrapper) unwrappedNode).getWrappedNode();
182         }
183         return unwrappedNode;
184     }
185 
186     /**
187      * Removes a wrapper by type. The wrapper can be deep in a chain of wrappers in which case wrappers before it will
188      * be cloned creating a new chain that leads to the same real node.
189      */
190     public static Node deepUnwrap(Node node, Class<? extends DelegateNodeWrapper> wrapper) {
191         if (node instanceof DelegateNodeWrapper) {
192             return ((DelegateNodeWrapper) node).deepUnwrap(wrapper);
193         }
194         return node;
195     }
196 
197     /**
198      * Removes all wrappers of a given type. Other wrappers are cloned creating a new chain that leads to the same real
199      * node.
200      */
201     public static Node deepUnwrapAll(Node node, Class<? extends DelegateNodeWrapper> wrapperClass) {
202         while (node instanceof DelegateNodeWrapper) {
203             Node unwrapped = ((DelegateNodeWrapper) node).deepUnwrap(wrapperClass);
204             // If the unwrapping had no effect we're done
205             if (unwrapped == node) {
206                 break;
207             }
208             node = unwrapped;
209         }
210         return node;
211     }
212 
213     public static boolean isWrappedWith(Node node, Class<? extends DelegateNodeWrapper> wrapper) {
214         if (wrapper.isInstance(node)){
215             return true;
216         }
217 
218         if (node instanceof DelegateNodeWrapper) {
219             return isWrappedWith(((DelegateNodeWrapper)node).getWrappedNode(), wrapper);
220         }
221         return false;
222     }
223 
224 
225     /**
226      * Convenience - delegate to {@link Node#orderBefore(String, String)}.
227      */
228     public static void orderBefore(Node node, String siblingName) throws RepositoryException {
229         node.getParent().orderBefore(node.getName(), siblingName);
230     }
231 
232     /**
233      * Orders the node directly after a given sibling. If no sibling is specified the node is placed first.
234      */
235     public static void orderAfter(Node node, String siblingName) throws RepositoryException {
236 
237         if (siblingName == null) {
238             orderFirst(node);
239             return;
240         }
241 
242         Node parent = node.getParent();
243         Node sibling = parent.getNode(siblingName);
244         Node siblingAfter = getSiblingAfter(sibling);
245 
246         if (siblingAfter == null) {
247             orderLast(node);
248             return;
249         }
250 
251         // Move the node before the sibling directly after the target sibling
252         parent.orderBefore(node.getName(), siblingAfter.getName());
253     }
254 
255     /**
256      * Orders the node first among its siblings.
257      */
258     public static void orderFirst(Node node) throws RepositoryException {
259         Node parent = node.getParent();
260         NodeIterator siblings = parent.getNodes();
261         Node firstSibling = siblings.nextNode();
262         if (!firstSibling.isSame(node)) {
263             parent.orderBefore(node.getName(), firstSibling.getName());
264         }
265     }
266 
267     /**
268      * Orders the node last among its siblings.
269      */
270     public static void orderLast(Node node) throws RepositoryException {
271         node.getParent().orderBefore(node.getName(), null);
272     }
273 
274     /**
275      * Orders the node up one step among its siblings. If the node is the only sibling or the first sibling this method
276      * has no effect.
277      */
278     public static void orderNodeUp(Node node) throws RepositoryException {
279         Node siblingBefore = getSiblingBefore(node);
280         if (siblingBefore != null) {
281             node.getParent().orderBefore(node.getName(), siblingBefore.getName());
282         }
283     }
284 
285     /**
286      * Orders the node down one step among its siblings. If the node is the only sibling or the last sibling this method
287      * has no effect.
288      */
289     public static void orderNodeDown(Node node) throws RepositoryException {
290         Node siblingAfter = getSiblingAfter(node);
291         if (siblingAfter != null) {
292             node.getParent().orderBefore(siblingAfter.getName(), node.getName());
293         }
294     }
295 
296     public static Node getSiblingBefore(Node node) throws RepositoryException {
297         Node parent = node.getParent();
298         NodeIterator siblings = parent.getNodes();
299         Node previousSibling = null;
300         while (siblings.hasNext()) {
301             Node sibling = siblings.nextNode();
302             if (isSame(node, sibling)) {
303                 return previousSibling;
304             }
305             previousSibling = sibling;
306         }
307         return null;
308     }
309 
310     public static Node getSiblingAfter(Node node) throws RepositoryException {
311         Node parent = node.getParent();
312         NodeIterator siblings = parent.getNodes();
313         while (siblings.hasNext()) {
314             Node sibling = siblings.nextNode();
315             if (isSame(node, sibling)) {
316                 break;
317             }
318         }
319         return siblings.hasNext() ? siblings.nextNode() : null;
320     }
321 
322     /**
323      * Gets the siblings of this node.
324      * @param node node from which will be siblings retrieved
325      * @param nodeTypeName requested type of siblings nodes
326      * @return list of siblings of the given Node (only the given node is excluded)
327      */
328     public static Iterable<Node> getSiblings(Node node) throws RepositoryException {
329         Node parent = node.getParent();
330         Iterable<Node> allSiblings = NodeUtil.getNodes(parent);
331         List<Node> siblings = new ArrayList<Node>();
332 
333         for(Node sibling: allSiblings) {
334             if (!NodeUtil.isSame(node, sibling)) {
335                 siblings.add(sibling);
336             }
337         }
338         return siblings;
339     }
340 
341     /**
342      * Gets the siblings of this node with certain type.
343      * @param node node from which will be siblings retrieved
344      * @param nodeTypeName requested type of siblings nodes
345      * @return list of siblings of the given Node (the given node is excluded)
346      */
347     public static Iterable<Node> getSiblings(Node node, String nodeTypeName) throws RepositoryException {
348         Node parent = node.getParent();
349         Iterable<Node> allSiblings = NodeUtil.getNodes(parent, nodeTypeName);
350         List<Node> sameTypeSiblings = new ArrayList<Node>();
351 
352         for(Node sibling: allSiblings) {
353             if (!NodeUtil.isSame(node, sibling)) {
354                 sameTypeSiblings.add(sibling);
355             }
356         }
357         return sameTypeSiblings;
358     }
359 
360     /**
361      * Gets the siblings of this node according to predicate.
362      * @param node node from which will be siblings retrieved
363      * @param predicate predicate
364      * @return list of siblings of the given Node (the given node is excluded)
365      */
366     public static Iterable<Node> getSiblings(Node node, Predicate predicate) throws RepositoryException {
367         Node parent = node.getParent();
368         Iterable<Node> allSiblings = NodeUtil.getNodes(parent, predicate);
369         List<Node> sameTypeSiblings = new ArrayList<Node>();
370 
371         for(Node sibling: allSiblings) {
372             if (!NodeUtil.isSame(node, sibling)) {
373                 sameTypeSiblings.add(sibling);
374             }
375         }
376         return sameTypeSiblings;
377     }
378 
379     /**
380      * Gets the siblings before this node.
381      * @param node node from which will be siblings retrieved
382      * @return list of siblings before the given Node (the given node is excluded)
383      */
384     public static Iterable<Node> getSiblingsBefore(Node node) throws RepositoryException {
385         int toIndex = 0;
386         Node parent = node.getParent();
387         List<Node> allSiblings = NodeUtil.asList(NodeUtil.getNodes(parent));
388 
389         for(Node sibling: allSiblings) {
390             if (NodeUtil.isSame(node, sibling)) {
391                 break;
392             }
393             toIndex++;
394         }
395         return allSiblings.subList(0, toIndex);
396     }
397 
398     /**
399      * Gets the siblings after this node.
400      * @param node node from which will be siblings retrieved
401      * @return list of siblings after the given Node (the given node is excluded)
402      */
403     public static Iterable<Node> getSiblingsAfter(Node node) throws RepositoryException {
404         int fromIndex = 0;
405         Node parent = node.getParent();
406         List<Node> allSiblings = NodeUtil.asList(NodeUtil.getNodes(parent));
407 
408         for(Node sibling: allSiblings) {
409             if (NodeUtil.isSame(node, sibling)) {
410                 fromIndex++;
411                 break;
412             }
413             fromIndex++;
414         }
415         return allSiblings.subList(fromIndex, allSiblings.size());
416     }
417 
418     /**
419      * Gets the siblings before this node with certain type.
420      * @param node node from which will be siblings retrieved
421      * @param nodeTypeName requested type of siblings nodes
422      * @return list of siblings before the given Node (the given node is excluded)
423      */
424     public static Iterable<Node> getSiblingsBefore(Node node, String nodeTypeName) throws RepositoryException {
425         Node parent = node.getParent();
426         Iterable<Node> allSiblings = NodeUtil.getNodes(parent);
427         List<Node> sameTypeSiblings = new ArrayList<Node>();
428 
429         for(Node sibling: allSiblings) {
430             if (NodeUtil.isSame(node, sibling)) {
431                 break;
432             }
433             if (isNodeType(sibling, nodeTypeName)) {
434                 sameTypeSiblings.add(sibling);
435             }
436         }
437         return sameTypeSiblings;
438     }
439 
440     /**
441      * Gets the siblings after this node with certain type.
442      * @param node node from which will be siblings retrieved
443      * @param nodeTypeName requested type of siblings nodes
444      * @return list of siblings after the given Node (the given node is excluded)
445      */
446     public static Iterable<Node> getSiblingsAfter(Node node, String nodeTypeName) throws RepositoryException {
447         Node parent = node.getParent();
448         List<Node> allSiblings = NodeUtil.asList(NodeUtil.getNodes(parent));
449         int fromIndex = 0;
450 
451         for(Node sibling: allSiblings) {
452             fromIndex++;
453             if (NodeUtil.isSame(node, sibling)) {
454                 break;
455             }
456         }
457 
458         List<Node> sameTypeSiblings = new ArrayList<Node>();
459         for(Node sibling: allSiblings.subList(fromIndex, allSiblings.size())) {
460             if (isNodeType(sibling, nodeTypeName)) {
461                 sameTypeSiblings.add(sibling);
462             }
463         }
464         return sameTypeSiblings;
465     }
466 
467     public static void moveNode(Node nodeToMove, Node newParent) throws RepositoryException {
468         if (!isSame(newParent, nodeToMove.getParent())) {
469             String newPath = combinePathAndName(newParent.getPath(), nodeToMove.getName());
470             nodeToMove.getSession().move(nodeToMove.getPath(), newPath);
471         }
472     }
473 
474     public static void moveNodeBefore(Node nodeToMove, Node target) throws RepositoryException {
475         Node targetParent = target.getParent();
476         moveNode(nodeToMove, targetParent);
477         targetParent.orderBefore(nodeToMove.getName(), target.getName());
478     }
479 
480     public static void moveNodeAfter(Node nodeToMove, Node target) throws RepositoryException {
481         Node targetParent = target.getParent();
482         moveNode(nodeToMove, targetParent);
483         orderAfter(nodeToMove, target.getName());
484     }
485 
486     public static boolean isFirstSibling(Node node) throws RepositoryException {
487         Node parent = node.getParent();
488         NodeIterator nodes = parent.getNodes();
489         return isSame(nodes.nextNode(), node);
490     }
491 
492     /**
493      * Check if node1 and node2 are siblings.
494      */
495     public static boolean isSameNameSiblings(Node node1, Node node2) throws RepositoryException {
496         Node parent1 = node1.getParent();
497         Node parent2 = node2.getParent();
498         return isSame(parent1, parent2) && node1.getName().equals(node2.getName());
499     }
500 
501     public static boolean isLastSibling(Node node) throws RepositoryException {
502         Node parent = node.getParent();
503         NodeIterator nodes = parent.getNodes();
504         Node last = null;
505         while (nodes.hasNext()) {
506             last = nodes.nextNode();
507         }
508         return isSame(last, node);
509     }
510 
511     public static void renameNode(Node node, String newName) throws RepositoryException {
512         Node parent = node.getParent();
513         String newPath = combinePathAndName(parent.getPath(), newName);
514         node.getSession().move(node.getPath(), newPath);
515     }
516 
517 
518     /**
519      * @return Whether the provided node as the provided permission or not.
520      * @throws RuntimeException
521      *             in case of RepositoryException.
522      */
523     public static boolean isGranted(Node node, long permissions) {
524         try {
525             return PermissionUtil.isGranted(node, permissions);
526         } catch (RepositoryException e) {
527             // TODO dlipp - apply consistent ExceptionHandling
528             throw new RuntimeException(e);
529         }
530     }
531 
532     /**
533      * Returns true if both arguments represents the same node. In case the nodes are wrapped the comparison is done one
534      * the actual nodes behind the wrappers.
535      */
536     public static boolean isSame(Node lhs, Node rhs) throws RepositoryException {
537         return unwrap(lhs).isSame(unwrap(rhs));
538     }
539 
540     public static String combinePathAndName(String path, String name) {
541         if ("/".equals(path)) {
542             return "/" + name;
543         }
544         return path + "/" + name;
545     }
546 
547     /**
548      * Creates a node under the specified parent and relative path, then returns it. Should the node already exist, the
549      * method will simply return it.
550      */
551     public static Node createPath(Node parent, String relPath, String primaryNodeTypeName) throws RepositoryException, PathNotFoundException, AccessDeniedException {
552         return createPath(parent, relPath, primaryNodeTypeName, false);
553     }
554 
555     /**
556      * Creates a node under the specified parent and relative path, then returns it. Should the node already exist, the
557      * method will simply return it.
558      */
559     public static Node createPath(Node parent, String relPath, String primaryNodeTypeName, boolean save) throws RepositoryException, PathNotFoundException, AccessDeniedException {
560         // remove leading /
561         String currentPath = StringUtils.removeStart(relPath, "/");
562 
563         if (StringUtils.isEmpty(currentPath)) {
564             return parent;
565         }
566 
567         Node root = parent;
568         String[] names = currentPath.split("/");
569 
570         for (int i = 0; i < names.length; i++) {
571             String name = names[i];
572             if (root.hasNode(name)) {
573                 root = root.getNode(name);
574             } else {
575                 final Node newNode = root.addNode(name, primaryNodeTypeName);
576                 if (save) {
577                     root.getSession().save();
578                 }
579                 root = newNode;
580             }
581         }
582         return root;
583     }
584 
585     /**
586      * Visits the given node and then all of nodes beneath it except for metadata nodes and nodes of jcr type.
587      */
588     public static void visit(Node node, NodeVisitor visitor) throws RepositoryException {
589         visit(node, visitor, EXCLUDE_META_DATA_FILTER);
590     }
591 
592     public static void visit(Node node, NodeVisitor visitor, Predicate predicate) throws RepositoryException {
593         // TODO should it really visit the start node even if it doesn't match the filter?
594         visitor.visit(node);
595         for (Node child : getNodes(node, predicate)) {
596             visit(child, visitor, predicate);
597         }
598         if (visitor instanceof PostNodeVisitor) {
599             ((PostNodeVisitor) visitor).postVisit(node);
600         }
601     }
602 
603     public static Iterable<Node> getNodes(Node parent, Predicate predicate) throws RepositoryException {
604         return asIterable(new FilteringNodeIterator(parent.getNodes(), predicate));
605     }
606 
607     public static Iterable<Node> getNodes(Node parent) throws RepositoryException {
608         return getNodes(parent, EXCLUDE_META_DATA_FILTER);
609     }
610 
611     public static Iterable<Node> getNodes(Node parent, String nodeTypeName) throws RepositoryException {
612         return getNodes(parent, new NodeTypePredicate(nodeTypeName, false));
613     }
614 
615     public static Iterable<Node> asIterable(NodeIterator iterator) {
616         return new NodeIterableAdapter(iterator);
617     }
618 
619     public static List<Node> asList(Iterable<Node> nodes) {
620         List<Node> nodesList = new ArrayList<Node>();
621         for (Node node : nodes) {
622             nodesList.add(node);
623         }
624         return nodesList;
625     }
626 
627     /**
628      * This method return the node's name on success, otherwise it handles the {@link RepositoryException} by throwing a
629      * {@link RuntimeRepositoryException}.
630      */
631     public static String getName(Node content) {
632         try {
633             return content.getName();
634         } catch (RepositoryException e) {
635             throw new RuntimeRepositoryException(e);
636         }
637     }
638 
639     /**
640      * Get all children (by recursion) using MAGNOLIA_FILTER (filter accepting all nodes of a type with namespace mgnl).
641      */
642     public static Iterable<Node> collectAllChildren(Node node) throws RepositoryException {
643         List<Node> nodes = new ArrayList<Node>();
644         return collectAllChildren(nodes, node, MAGNOLIA_FILTER);
645     }
646 
647     /**
648      * Get all children (by recursion) using a Predicate.
649      */
650     public static Iterable<Node> collectAllChildren(Node node, Predicate predicate) throws RepositoryException {
651         List<Node> nodes = new ArrayList<Node>();
652         return collectAllChildren(nodes, node, predicate);
653     }
654 
655     /**
656      * Get all children (by recursion) using a Predicate.
657      * // TODO this method should really be private or renamed
658      */
659     public static Iterable<Node> collectAllChildren(List<Node> nodes, Node parent, Predicate predicate) throws RepositoryException {
660         // get filtered sub nodes first
661         nodes.addAll(asList(getNodes(parent, predicate)));
662 
663         // get all children to find recursively
664         Iterator<Node> allChildren = getNodes(parent, EXCLUDE_META_DATA_FILTER).iterator();
665 
666         // recursion
667         while (allChildren.hasNext()) {
668             collectAllChildren(nodes, allChildren.next(), predicate);
669         }
670 
671         return nodes;
672     }
673 
674     /**
675      * Get all Ancestors until level 1.
676      */
677     public static Collection<Node> getAncestors(Node node) throws RepositoryException {
678         List<Node> allAncestors = new ArrayList<Node>();
679         int level = node.getDepth();
680         while (level != 0) {
681             try {
682                 allAncestors.add((Node) node.getAncestor(--level));
683             } catch (AccessDeniedException e) {
684                 log.debug("Node " + node.getIdentifier() + " didn't allow access to Ancestor's ");
685             }
686         }
687         return allAncestors;
688     }
689 
690     /**
691      * Used for building exception messages where we want to avoid handling another exception inside a throws clause.
692      */
693     public static String getNodeIdentifierIfPossible(Node content) {
694         try {
695             return content.getIdentifier();
696         } catch (RepositoryException e) {
697             return "<not available>";
698         }
699     }
700 
701     public static String getNodePathIfPossible(Node node) {
702         try {
703             return node.getPath();
704         } catch (RepositoryException e) {
705             return "<not available>";
706         }
707     }
708 
709     /**
710      * Return the Path of the node.
711      *
712      * @return the path for the node or an empty String in case of exception
713      */
714     public static String getPathIfPossible(Node node) {
715         try {
716             return node.getPath();
717         } catch (RepositoryException e) {
718             log.error("Failed to get handle: " + e.getMessage(), e);
719             return StringUtils.EMPTY;
720         }
721     }
722 
723     public static NodeIterator filterNodeType(NodeIterator iterator, String nodeType){
724         return new FilteringNodeIterator(iterator, new info.magnolia.jcr.predicate.NodeTypePredicate(nodeType));
725     }
726 
727     public static NodeIterator filterDuplicates(NodeIterator iterator){
728         return new FilteringNodeIterator(iterator, new info.magnolia.jcr.predicate.DuplicateNodePredicate());
729     }
730 
731     public static NodeIterator filterParentNodeType(NodeIterator iterator, final String nodeType) throws RepositoryException{
732         return new FilteringNodeIterator(iterator, new info.magnolia.jcr.predicate.NodeTypeParentPredicate(nodeType)) {
733             @Override
734             public Node nextNode(){
735                 Node node = super.nextNode();
736                 try {
737                     while(node.getDepth() != 0 && !node.isNodeType(nodeType)){
738                         if(node.getDepth() != 0){
739                             node = node.getParent();
740                         }
741                     }
742                 } catch (RepositoryException e) {
743                     throw new RuntimeException(e.getMessage(), e);
744                 }
745                 return node;
746             }
747         };
748     }
749 
750     public static Collection<Node> getCollectionFromNodeIterator(NodeIterator iterator){
751         Collection<Node> nodeCollection = new HashSet<Node>(150);
752         while(iterator.hasNext()){
753             nodeCollection.add(iterator.nextNode());
754         }
755         return nodeCollection;
756     }
757 }