info.magnolia.jcr.util.NodeUtil.java Source code

Java tutorial

Introduction

Here is the source code for info.magnolia.jcr.util.NodeUtil.java

Source

/**
 * This file Copyright (c) 2011-2012 Magnolia International
 * Ltd.  (http://www.magnolia-cms.com). All rights reserved.
 *
 *
 * This file is dual-licensed under both the Magnolia
 * Network Agreement and the GNU General Public License.
 * You may elect to use one or the other of these licenses.
 *
 * This file is distributed in the hope that it will be
 * useful, but AS-IS and WITHOUT ANY WARRANTY; without even the
 * implied warranty of MERCHANTABILITY or FITNESS FOR A
 * PARTICULAR PURPOSE, TITLE, or NONINFRINGEMENT.
 * Redistribution, except as permitted by whichever of the GPL
 * or MNA you select, is prohibited.
 *
 * 1. For the GPL license (GPL), you can redistribute and/or
 * modify this file under the terms of the GNU General
 * Public License, Version 3, as published by the Free Software
 * Foundation.  You should have received a copy of the GNU
 * General Public License, Version 3 along with this program;
 * if not, write to the Free Software Foundation, Inc., 51
 * Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 * 2. For the Magnolia Network Agreement (MNA), this file
 * and the accompanying materials are made available under the
 * terms of the MNA which accompanies this distribution, and
 * is available at http://www.magnolia-cms.com/mna.html
 *
 * Any modifications to this file must keep this entire header
 * intact.
 *
 */
package info.magnolia.jcr.util;

import info.magnolia.cms.security.AccessDeniedException;
import info.magnolia.cms.security.PermissionUtil;
import info.magnolia.context.MgnlContext;
import info.magnolia.jcr.RuntimeRepositoryException;
import info.magnolia.jcr.iterator.NodeIterableAdapter;
import info.magnolia.jcr.predicate.AbstractPredicate;
import info.magnolia.jcr.wrapper.DelegateNodeWrapper;
import info.magnolia.jcr.wrapper.JCRPropertiesFilteringNodeWrapper;

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;

import javax.jcr.Node;
import javax.jcr.NodeIterator;
import javax.jcr.PathNotFoundException;
import javax.jcr.Property;
import javax.jcr.PropertyIterator;
import javax.jcr.RepositoryException;
import javax.jcr.Session;
import javax.jcr.Value;
import javax.jcr.nodetype.NodeType;
import javax.jcr.nodetype.NodeTypeManager;

import org.apache.commons.lang.StringUtils;
import org.apache.jackrabbit.JcrConstants;
import org.apache.jackrabbit.commons.iterator.FilteringNodeIterator;
import org.apache.jackrabbit.commons.predicate.NodeTypePredicate;
import org.apache.jackrabbit.commons.predicate.Predicate;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * Various utility methods to collect data from JCR repository.
 */
public class NodeUtil {

    private static final Logger log = LoggerFactory.getLogger(NodeUtil.class);

    /**
     * Predicate hiding properties prefixed with jcr or mgnl.
     * @deprecated since 5.0 - obsolete as there's no nodetypes with namespace jcr. In addition you could use {@link info.magnolia.jcr.predicate.JCRMgnlPropertyHidingPredicate}
     */
    public static AbstractPredicate<Property> ALL_PROPERTIES_EXCEPT_JCR_AND_MGNL_FILTER = new AbstractPredicate<Property>() {

        @Override
        public boolean evaluateTyped(Property property) {
            try {
                String name = property.getName();
                return !name.startsWith(NodeTypes.JCR_PREFIX) && !name.startsWith(NodeTypes.MGNL_PREFIX);
            } catch (RepositoryException e) {
                String path;
                try {
                    path = property.getPath();
                } catch (RepositoryException e1) {
                    path = "<path not available>";
                }
                log.error("Unable to read name of property {}", path);
                // either invalid or not accessible to the current user
                return false;
            }
        }
    };

    /**
     * Node filter accepting everything except nodes with namespace jcr (version and system store).
     * @deprecated since 5.0 - obsolete as there's no nodetypes with namespace jcr
     */
    public static Predicate ALL_NODES_EXCEPT_JCR_FILTER = new AbstractPredicate<Node>() {
        @Override
        public boolean evaluateTyped(Node node) {
            try {
                return !node.getName().startsWith(NodeTypes.JCR_PREFIX);
            } catch (RepositoryException e) {
                log.error("Unable to read name for node {}", getNodePathIfPossible(node));
                return false;
            }
        }
    };

    /**
     * Node filter accepting everything except meta data and jcr types.
     * @deprecated since 5.0 - obsolete as there's no nodetypes with namespace jcr and because of MAGNOLIA-4640
     */
    public static AbstractPredicate<Node> EXCLUDE_META_DATA_FILTER = new AbstractPredicate<Node>() {

        @Override
        public boolean evaluateTyped(Node node) {
            try {
                return !node.getName().startsWith(NodeTypes.JCR_PREFIX)
                        && !NodeUtil.isNodeType(node, NodeTypes.MetaData.NAME);
            } catch (RepositoryException e) {
                log.error("Unable to read name or nodeType for node {}", getNodePathIfPossible(node));
                return false;
            }
        }
    };

    /**
     * Node filter accepting all nodes of a type with namespace mgnl.
     */
    public static AbstractPredicate<Node> MAGNOLIA_FILTER = new AbstractPredicate<Node>() {

        @Override
        public boolean evaluateTyped(Node node) {

            try {
                String nodeTypeName = node.getPrimaryNodeType().getName();
                // accept only "magnolia" nodes
                return nodeTypeName.startsWith(NodeTypes.MGNL_PREFIX);
            } catch (RepositoryException e) {
                log.error("Unable to read nodeType for node {}", getNodePathIfPossible(node));
            }
            return false;
        }
    };

    /**
     * Get a Node by identifier.
     */
    public static Node getNodeByIdentifier(String workspace, String identifier) throws RepositoryException {
        Node target = null;
        Session jcrSession;
        if (workspace == null || identifier == null) {
            return target;
        }

        jcrSession = MgnlContext.getJCRSession(workspace);
        if (jcrSession != null) {
            target = jcrSession.getNodeByIdentifier(identifier);
        }
        return target;
    }

    /**
     * from default content.
     */
    public static boolean hasMixin(Node node, String mixinName) throws RepositoryException {
        if (StringUtils.isBlank(mixinName)) {
            throw new IllegalArgumentException("Mixin name can't be empty.");
        }
        for (NodeType type : node.getMixinNodeTypes()) {
            if (mixinName.equals(type.getName())) {
                return true;
            }
        }
        return false;
    }

    /**
     * TODO dlipp: better name? Clear javadoc! Do not assign method-param!
     * TODO cringele : shouldn't @param nodeType be aligned to JCR API? There it is nodeTypeName, nodeType is used for NodeType object
     */
    public static boolean isNodeType(Node node, String type) throws RepositoryException {
        node = NodeUtil.deepUnwrap(node, JCRPropertiesFilteringNodeWrapper.class);
        final String actualType = node.getProperty(JcrConstants.JCR_PRIMARYTYPE).getString();
        // if the node is frozen, and we're not looking specifically for frozen nodes, then we compare with the original
        // node type
        if (JcrConstants.NT_FROZENNODE.equals(actualType) && !(JcrConstants.NT_FROZENNODE.equals(type))) {
            final Property p = node.getProperty(JcrConstants.JCR_FROZENPRIMARYTYPE);
            final String s = p.getString();
            NodeTypeManager ntManager = node.getSession().getWorkspace().getNodeTypeManager();
            NodeType primaryNodeType = ntManager.getNodeType(s);
            return primaryNodeType.isNodeType(type);
        }
        return node.isNodeType(type);
    }

    public static Node unwrap(Node node) throws RepositoryException {
        Node unwrappedNode = node;
        while (unwrappedNode instanceof DelegateNodeWrapper) {
            unwrappedNode = ((DelegateNodeWrapper) unwrappedNode).getWrappedNode();
        }
        return unwrappedNode;
    }

    /**
     * Removes a wrapper by type. The wrapper can be deep in a chain of wrappers in which case wrappers before it will
     * be cloned creating a new chain that leads to the same real node.
     */
    public static Node deepUnwrap(Node node, Class<? extends DelegateNodeWrapper> wrapper) {
        if (node instanceof DelegateNodeWrapper) {
            return ((DelegateNodeWrapper) node).deepUnwrap(wrapper);
        }
        return node;
    }

    /**
     * Removes all wrappers of a given type. Other wrappers are cloned creating a new chain that leads to the same real
     * node.
     */
    public static Node deepUnwrapAll(Node node, Class<? extends DelegateNodeWrapper> wrapperClass) {
        while (node instanceof DelegateNodeWrapper) {
            Node unwrapped = ((DelegateNodeWrapper) node).deepUnwrap(wrapperClass);
            // If the unwrapping had no effect we're done
            if (unwrapped == node) {
                break;
            }
            node = unwrapped;
        }
        return node;
    }

    public static boolean isWrappedWith(Node node, Class<? extends DelegateNodeWrapper> wrapper) {
        if (wrapper.isInstance(node)) {
            return true;
        }

        if (node instanceof DelegateNodeWrapper) {
            return isWrappedWith(((DelegateNodeWrapper) node).getWrappedNode(), wrapper);
        }
        return false;
    }

    /**
     * Convenience - delegate to {@link Node#orderBefore(String, String)}.
     */
    public static void orderBefore(Node node, String siblingName) throws RepositoryException {
        node.getParent().orderBefore(node.getName(), siblingName);
    }

    /**
     * Orders the node directly after a given sibling. If no sibling is specified the node is placed first.
     */
    public static void orderAfter(Node node, String siblingName) throws RepositoryException {

        if (siblingName == null) {
            orderFirst(node);
            return;
        }

        Node parent = node.getParent();
        Node sibling = parent.getNode(siblingName);
        Node siblingAfter = getSiblingAfter(sibling);

        if (siblingAfter == null) {
            orderLast(node);
            return;
        }

        // Move the node before the sibling directly after the target sibling
        parent.orderBefore(node.getName(), siblingAfter.getName());
    }

    /**
     * Orders the node first among its siblings.
     */
    public static void orderFirst(Node node) throws RepositoryException {
        Node parent = node.getParent();
        NodeIterator siblings = parent.getNodes();
        Node firstSibling = siblings.nextNode();
        if (!firstSibling.isSame(node)) {
            parent.orderBefore(node.getName(), firstSibling.getName());
        }
    }

    /**
     * Orders the node last among its siblings.
     */
    public static void orderLast(Node node) throws RepositoryException {
        node.getParent().orderBefore(node.getName(), null);
    }

    /**
     * Orders the node up one step among its siblings. If the node is the only sibling or the first sibling this method
     * has no effect.
     */
    public static void orderNodeUp(Node node) throws RepositoryException {
        Node siblingBefore = getSiblingBefore(node);
        if (siblingBefore != null) {
            node.getParent().orderBefore(node.getName(), siblingBefore.getName());
        }
    }

    /**
     * Orders the node down one step among its siblings. If the node is the only sibling or the last sibling this method
     * has no effect.
     */
    public static void orderNodeDown(Node node) throws RepositoryException {
        Node siblingAfter = getSiblingAfter(node);
        if (siblingAfter != null) {
            node.getParent().orderBefore(siblingAfter.getName(), node.getName());
        }
    }

    public static Node getSiblingBefore(Node node) throws RepositoryException {
        Node parent = node.getParent();
        NodeIterator siblings = parent.getNodes();
        Node previousSibling = null;
        while (siblings.hasNext()) {
            Node sibling = siblings.nextNode();
            if (isSame(node, sibling)) {
                return previousSibling;
            }
            previousSibling = sibling;
        }
        return null;
    }

    public static Node getSiblingAfter(Node node) throws RepositoryException {
        Node parent = node.getParent();
        NodeIterator siblings = parent.getNodes();
        while (siblings.hasNext()) {
            Node sibling = siblings.nextNode();
            if (isSame(node, sibling)) {
                break;
            }
        }
        return siblings.hasNext() ? siblings.nextNode() : null;
    }

    /**
     * Gets the siblings of this node.
     * @param node node from which will be siblings retrieved
     * @param nodeTypeName requested type of siblings nodes
     * @return list of siblings of the given Node (only the given node is excluded)
     */
    public static Iterable<Node> getSiblings(Node node) throws RepositoryException {
        Node parent = node.getParent();
        Iterable<Node> allSiblings = NodeUtil.getNodes(parent);
        List<Node> siblings = new ArrayList<Node>();

        for (Node sibling : allSiblings) {
            if (!NodeUtil.isSame(node, sibling)) {
                siblings.add(sibling);
            }
        }
        return siblings;
    }

    /**
     * Gets the siblings of this node with certain type.
     * @param node node from which will be siblings retrieved
     * @param nodeTypeName requested type of siblings nodes
     * @return list of siblings of the given Node (the given node is excluded)
     */
    public static Iterable<Node> getSiblings(Node node, String nodeTypeName) throws RepositoryException {
        Node parent = node.getParent();
        Iterable<Node> allSiblings = NodeUtil.getNodes(parent, nodeTypeName);
        List<Node> sameTypeSiblings = new ArrayList<Node>();

        for (Node sibling : allSiblings) {
            if (!NodeUtil.isSame(node, sibling)) {
                sameTypeSiblings.add(sibling);
            }
        }
        return sameTypeSiblings;
    }

    /**
     * Gets the siblings of this node according to predicate.
     * @param node node from which will be siblings retrieved
     * @param predicate predicate
     * @return list of siblings of the given Node (the given node is excluded)
     */
    public static Iterable<Node> getSiblings(Node node, Predicate predicate) throws RepositoryException {
        Node parent = node.getParent();
        Iterable<Node> allSiblings = NodeUtil.getNodes(parent, predicate);
        List<Node> sameTypeSiblings = new ArrayList<Node>();

        for (Node sibling : allSiblings) {
            if (!NodeUtil.isSame(node, sibling)) {
                sameTypeSiblings.add(sibling);
            }
        }
        return sameTypeSiblings;
    }

    /**
     * Gets the siblings before this node.
     * @param node node from which will be siblings retrieved
     * @return list of siblings before the given Node (the given node is excluded)
     */
    public static Iterable<Node> getSiblingsBefore(Node node) throws RepositoryException {
        int toIndex = 0;
        Node parent = node.getParent();
        List<Node> allSiblings = NodeUtil.asList(NodeUtil.getNodes(parent));

        for (Node sibling : allSiblings) {
            if (NodeUtil.isSame(node, sibling)) {
                break;
            }
            toIndex++;
        }
        return allSiblings.subList(0, toIndex);
    }

    /**
     * Gets the siblings after this node.
     * @param node node from which will be siblings retrieved
     * @return list of siblings after the given Node (the given node is excluded)
     */
    public static Iterable<Node> getSiblingsAfter(Node node) throws RepositoryException {
        int fromIndex = 0;
        Node parent = node.getParent();
        List<Node> allSiblings = NodeUtil.asList(NodeUtil.getNodes(parent));

        for (Node sibling : allSiblings) {
            if (NodeUtil.isSame(node, sibling)) {
                fromIndex++;
                break;
            }
            fromIndex++;
        }
        return allSiblings.subList(fromIndex, allSiblings.size());
    }

    /**
     * Gets the siblings before this node with certain type.
     * @param node node from which will be siblings retrieved
     * @param nodeTypeName requested type of siblings nodes
     * @return list of siblings before the given Node (the given node is excluded)
     */
    public static Iterable<Node> getSiblingsBefore(Node node, String nodeTypeName) throws RepositoryException {
        Node parent = node.getParent();
        Iterable<Node> allSiblings = NodeUtil.getNodes(parent);
        List<Node> sameTypeSiblings = new ArrayList<Node>();

        for (Node sibling : allSiblings) {
            if (NodeUtil.isSame(node, sibling)) {
                break;
            }
            if (isNodeType(sibling, nodeTypeName)) {
                sameTypeSiblings.add(sibling);
            }
        }
        return sameTypeSiblings;
    }

    /**
     * Gets the siblings after this node with certain type.
     * @param node node from which will be siblings retrieved
     * @param nodeTypeName requested type of siblings nodes
     * @return list of siblings after the given Node (the given node is excluded)
     */
    public static Iterable<Node> getSiblingsAfter(Node node, String nodeTypeName) throws RepositoryException {
        Node parent = node.getParent();
        List<Node> allSiblings = NodeUtil.asList(NodeUtil.getNodes(parent));
        int fromIndex = 0;

        for (Node sibling : allSiblings) {
            fromIndex++;
            if (NodeUtil.isSame(node, sibling)) {
                break;
            }
        }

        List<Node> sameTypeSiblings = new ArrayList<Node>();
        for (Node sibling : allSiblings.subList(fromIndex, allSiblings.size())) {
            if (isNodeType(sibling, nodeTypeName)) {
                sameTypeSiblings.add(sibling);
            }
        }
        return sameTypeSiblings;
    }

    public static void moveNode(Node nodeToMove, Node newParent) throws RepositoryException {
        if (!isSame(newParent, nodeToMove.getParent())) {
            String newPath = combinePathAndName(newParent.getPath(), nodeToMove.getName());
            nodeToMove.getSession().move(nodeToMove.getPath(), newPath);
        }
    }

    public static void moveNodeBefore(Node nodeToMove, Node target) throws RepositoryException {
        Node targetParent = target.getParent();
        moveNode(nodeToMove, targetParent);
        targetParent.orderBefore(nodeToMove.getName(), target.getName());
    }

    public static void moveNodeAfter(Node nodeToMove, Node target) throws RepositoryException {
        Node targetParent = target.getParent();
        moveNode(nodeToMove, targetParent);
        orderAfter(nodeToMove, target.getName());
    }

    public static boolean isFirstSibling(Node node) throws RepositoryException {
        Node parent = node.getParent();
        NodeIterator nodes = parent.getNodes();
        return isSame(nodes.nextNode(), node);
    }

    /**
     * Check if node1 and node2 are siblings.
     */
    public static boolean isSameNameSiblings(Node node1, Node node2) throws RepositoryException {
        Node parent1 = node1.getParent();
        Node parent2 = node2.getParent();
        return isSame(parent1, parent2) && node1.getName().equals(node2.getName());
    }

    public static boolean isLastSibling(Node node) throws RepositoryException {
        Node parent = node.getParent();
        NodeIterator nodes = parent.getNodes();
        Node last = null;
        while (nodes.hasNext()) {
            last = nodes.nextNode();
        }
        return isSame(last, node);
    }

    public static void renameNode(Node node, String newName) throws RepositoryException {
        Node parent = node.getParent();
        String newPath = combinePathAndName(parent.getPath(), newName);
        node.getSession().move(node.getPath(), newPath);
    }

    /**
     * @return Whether the provided node as the provided permission or not.
     * @throws RuntimeRepositoryException in case of RepositoryException.
     */
    public static boolean isGranted(Node node, long permissions) {
        try {
            return PermissionUtil.isGranted(node, permissions);
        } catch (RepositoryException e) {
            throw new RuntimeRepositoryException(e);
        }
    }

    /**
     * Returns true if both arguments represents the same node. In case the nodes are wrapped the comparison is done one
     * the actual nodes behind the wrappers.
     */
    public static boolean isSame(Node lhs, Node rhs) throws RepositoryException {
        return unwrap(lhs).isSame(unwrap(rhs));
    }

    /**
     * @return a valid jcr path combined from the provided path and name.
     */
    public static String combinePathAndName(String path, String name) {
        if ("/".equals(path)) {
            return "/" + name;
        }
        return path + "/" + name;
    }

    /**
     * Creates a node under the specified parent and relative path, then returns it. Should the node already exist, the
     * method will simply return it.
     */
    public static Node createPath(Node parent, String relPath, String primaryNodeTypeName)
            throws RepositoryException, PathNotFoundException, AccessDeniedException {
        return createPath(parent, relPath, primaryNodeTypeName, false);
    }

    /**
     * Creates a node under the specified parent and relative path, then returns it. Should the node already exist, the
     * method will simply return it.
     */
    public static Node createPath(Node parent, String relPath, String primaryNodeTypeName, boolean save)
            throws RepositoryException, PathNotFoundException, AccessDeniedException {
        // remove leading /
        String currentPath = StringUtils.removeStart(relPath, "/");

        if (StringUtils.isEmpty(currentPath)) {
            return parent;
        }

        Node root = parent;
        String[] names = currentPath.split("/");

        for (int i = 0; i < names.length; i++) {
            String name = names[i];
            if (root.hasNode(name)) {
                root = root.getNode(name);
            } else {
                final Node newNode = root.addNode(name, primaryNodeTypeName);
                if (save) {
                    root.getSession().save();
                }
                root = newNode;
            }
        }
        return root;
    }

    /**
     * Visits the given node and then all of nodes beneath it except for metadata nodes and nodes of jcr type.
     */
    public static void visit(Node node, NodeVisitor visitor) throws RepositoryException {
        visit(node, visitor, EXCLUDE_META_DATA_FILTER);
    }

    public static void visit(Node node, NodeVisitor visitor, Predicate predicate) throws RepositoryException {
        // TODO should it really visit the start node even if it doesn't match the filter?
        visitor.visit(node);
        for (Node child : getNodes(node, predicate)) {
            visit(child, visitor, predicate);
        }
        if (visitor instanceof PostNodeVisitor) {
            ((PostNodeVisitor) visitor).postVisit(node);
        }
    }

    public static Iterable<Node> getNodes(Node parent, Predicate predicate) throws RepositoryException {
        return asIterable(new FilteringNodeIterator(parent.getNodes(), predicate));
    }

    public static Iterable<Node> getNodes(Node parent) throws RepositoryException {
        return getNodes(parent, EXCLUDE_META_DATA_FILTER);
    }

    public static Iterable<Node> getNodes(Node parent, String nodeTypeName) throws RepositoryException {
        return getNodes(parent, new NodeTypePredicate(nodeTypeName, false));
    }

    public static Iterable<Node> asIterable(NodeIterator iterator) {
        return new NodeIterableAdapter(iterator);
    }

    public static List<Node> asList(Iterable<Node> nodes) {
        List<Node> nodesList = new ArrayList<Node>();
        for (Node node : nodes) {
            nodesList.add(node);
        }
        return nodesList;
    }

    /**
     * This method return the node's name on success, otherwise it handles the {@link RepositoryException} by throwing a
     * {@link RuntimeRepositoryException}.
     */
    public static String getName(Node content) {
        try {
            return content.getName();
        } catch (RepositoryException e) {
            throw new RuntimeRepositoryException(e);
        }
    }

    /**
     * Get all children (by recursion) using MAGNOLIA_FILTER (filter accepting all nodes of a type with namespace mgnl).
     */
    public static Iterable<Node> collectAllChildren(Node node) throws RepositoryException {
        List<Node> nodes = new ArrayList<Node>();
        return collectAllChildren(nodes, node, MAGNOLIA_FILTER);
    }

    /**
     * Get all children (by recursion) using a Predicate.
     */
    public static Iterable<Node> collectAllChildren(Node node, Predicate predicate) throws RepositoryException {
        List<Node> nodes = new ArrayList<Node>();
        return collectAllChildren(nodes, node, predicate);
    }

    /**
     * Get all children (by recursion) using a Predicate.
     * // TODO this method should really be private or renamed
     */
    public static Iterable<Node> collectAllChildren(List<Node> nodes, Node parent, Predicate predicate)
            throws RepositoryException {
        // get filtered sub nodes first
        nodes.addAll(asList(getNodes(parent, predicate)));

        // get all children to find recursively
        Iterator<Node> allChildren = getNodes(parent, EXCLUDE_META_DATA_FILTER).iterator();

        // recursion
        while (allChildren.hasNext()) {
            collectAllChildren(nodes, allChildren.next(), predicate);
        }

        return nodes;
    }

    /**
     * Get all Ancestors until level 1.
     */
    public static Collection<Node> getAncestors(Node node) throws RepositoryException {
        List<Node> allAncestors = new ArrayList<Node>();
        int level = node.getDepth();
        while (level != 0) {
            try {
                allAncestors.add((Node) node.getAncestor(--level));
            } catch (AccessDeniedException e) {
                log.debug("Node " + node.getIdentifier() + " didn't allow access to Ancestor's ");
            }
        }
        return allAncestors;
    }

    /**
     * Used for building exception messages where we want to avoid handling another exception inside a throws clause.
     */
    public static String getNodeIdentifierIfPossible(Node content) {
        try {
            return content.getIdentifier();
        } catch (RepositoryException e) {
            return "<not available>";
        }
    }

    public static String getNodePathIfPossible(Node node) {
        try {
            return node.getPath();
        } catch (RepositoryException e) {
            return "<not available>";
        }
    }

    /**
     * Return the Path of the node.
     *
     * @return the path for the node or an empty String in case of exception
     */
    public static String getPathIfPossible(Node node) {
        try {
            return node.getPath();
        } catch (RepositoryException e) {
            log.error("Failed to get handle: " + e.getMessage(), e);
            return StringUtils.EMPTY;
        }
    }

    public static NodeIterator filterNodeType(NodeIterator iterator, String nodeType) {
        return new FilteringNodeIterator(iterator, new info.magnolia.jcr.predicate.NodeTypePredicate(nodeType));
    }

    public static NodeIterator filterDuplicates(NodeIterator iterator) {
        return new FilteringNodeIterator(iterator, new info.magnolia.jcr.predicate.DuplicateNodePredicate());
    }

    public static NodeIterator filterParentNodeType(NodeIterator iterator, final String nodeType)
            throws RepositoryException {
        return new FilteringNodeIterator(iterator,
                new info.magnolia.jcr.predicate.NodeTypeParentPredicate(nodeType)) {
            @Override
            public Node nextNode() {
                Node node = super.nextNode();
                try {
                    while (node.getDepth() != 0 && !node.isNodeType(nodeType)) {
                        if (node.getDepth() != 0) {
                            node = node.getParent();
                        }
                    }
                } catch (RepositoryException e) {
                    throw new RuntimeException(e.getMessage(), e);
                }
                return node;
            }
        };
    }

    public static Collection<Node> getCollectionFromNodeIterator(NodeIterator iterator) {
        Collection<Node> nodeCollection = new HashSet<Node>(150);
        while (iterator.hasNext()) {
            nodeCollection.add(iterator.nextNode());
        }
        return nodeCollection;
    }

    //for n2b
    public static Collection<Node> getSortedCollectionFromNodeIterator(NodeIterator iterator) {
        Collection<Node> nodeCollection = new LinkedList<Node>();
        while (iterator.hasNext()) {
            nodeCollection.add(iterator.nextNode());
        }
        return nodeCollection;
    }

    /**
     * Write the properties of the node to the log.
     */
    public static void traceNodeProperties(Node nodeOp) throws RepositoryException {
        // debug by logging properties.
        PropertyIterator propIter;
        propIter = nodeOp.getProperties();
        log.info("Trace Node Properties:");
        while (propIter.hasNext()) {
            Property prop = propIter.nextProperty();
            if (prop.isMultiple()) {
                Value[] values = prop.getValues();
                for (int i = 0; i < values.length; i++) {
                    log.info(prop.toString() + "[" + i + "] = " + upToNCharacters(values[i].getString(), 30));
                }
            } else {
                log.info(prop.toString() + " = " + upToNCharacters(prop.getString(), 30));
            }

        }
    }

    /**
     * Write the children of the node to the log.
     */
    public static void traceNodeChildren(Node nodeOp) throws RepositoryException {
        // debug by logging properties.
        NodeIterator nodeIter;
        nodeIter = nodeOp.getNodes();
        log.info("Trace Node Children:");
        while (nodeIter.hasNext()) {
            Node n = nodeIter.nextNode();
            log.info(n.toString());
        }
    }

    private static String upToNCharacters(String s, int n) {
        return s.substring(0, Math.min(s.length(), n));
    }

}