info.magnolia.ui.workbench.tree.HierarchicalJcrContainer.java Source code

Java tutorial

Introduction

Here is the source code for info.magnolia.ui.workbench.tree.HierarchicalJcrContainer.java

Source

/**
 * This file Copyright (c) 2011-2015 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.ui.workbench.tree;

import info.magnolia.cms.util.QueryUtil;
import info.magnolia.context.MgnlContext;
import info.magnolia.jcr.util.NodeTypes;
import info.magnolia.jcr.util.NodeUtil;
import info.magnolia.ui.vaadin.integration.contentconnector.JcrContentConnectorDefinition;
import info.magnolia.ui.vaadin.integration.contentconnector.NodeTypeDefinition;
import info.magnolia.ui.vaadin.integration.jcr.JcrItemAdapter;
import info.magnolia.ui.vaadin.integration.jcr.JcrItemId;
import info.magnolia.ui.vaadin.integration.jcr.JcrItemUtil;
import info.magnolia.ui.workbench.container.AbstractJcrContainer;
import info.magnolia.ui.workbench.container.OrderBy;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;

import javax.jcr.Item;
import javax.jcr.Node;
import javax.jcr.NodeIterator;
import javax.jcr.Property;
import javax.jcr.PropertyIterator;
import javax.jcr.RepositoryException;
import javax.jcr.Session;
import javax.jcr.nodetype.NodeType;
import javax.jcr.query.Query;

import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.vaadin.data.Container;

/**
 * Hierarchical implementation of {@link info.magnolia.ui.workbench.container.AbstractJcrContainer}.
 */
public class HierarchicalJcrContainer extends AbstractJcrContainer implements Container.Hierarchical {

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

    private static final String WHERE_CLAUSE_FOR_PATH = " ISCHILDNODE('%s')";

    private String nodePath;

    private boolean sortable;

    private static class ItemNameComparator implements Comparator<Item> {
        @Override
        public int compare(Item lhs, Item rhs) {
            try {
                return lhs.getName().compareTo(rhs.getName());
            } catch (RepositoryException e) {
                log.warn("Cannot compare item names: " + e);
                return 0;
            }
        }
    }

    public HierarchicalJcrContainer(JcrContentConnectorDefinition definition) {
        super(definition);
    }

    @Override
    public Collection<JcrItemId> getChildren(Object itemId) {
        long start = System.currentTimeMillis();
        Collection<Item> children = getChildren(getJcrItem(itemId));
        log.debug("Fetched {} children in {}ms", children.size(), System.currentTimeMillis() - start);
        return createContainerIds(children);
    }

    @Override
    public JcrItemId getParent(Object itemId) {
        try {
            Item item = getJcrItem(itemId);
            if (item.isNode() && item.getDepth() == 0) {
                return null;
            }
            return JcrItemUtil.getItemId(item.getParent());
        } catch (RepositoryException e) {
            handleRepositoryException(log, "Cannot determine parent for itemId: " + itemId, e);
            return null;
        }
    }

    @Override
    public Collection<JcrItemId> rootItemIds() {
        try {
            return createContainerIds(getRootItemIds());
        } catch (RepositoryException e) {
            handleRepositoryException(log, "Cannot retrieve root item id's", e);
            return Collections.emptySet();
        }
    }

    @Override
    public void refresh() {
        resetOffset();
        clearItemIndexes();
        fireItemSetChange();
    }

    @Override
    public boolean setParent(Object itemId, Object newParentId) throws UnsupportedOperationException {
        fireItemSetChange();
        return true;
    }

    @Override
    public boolean areChildrenAllowed(Object itemId) {
        final JcrItemAdapter item = ((JcrItemAdapter) getItem(itemId));
        if (item == null) {
            return false;
        }
        return item.isNode() && hasChildren(itemId);
    }

    @Override
    public boolean setChildrenAllowed(Object itemId, boolean areChildrenAllowed)
            throws UnsupportedOperationException {
        throw new UnsupportedOperationException();
    }

    @Override
    public boolean isRoot(Object itemId) {
        try {
            return isRoot(getJcrItem(itemId));
        } catch (RepositoryException e) {
            handleRepositoryException(log, "Cannot determine whether item is root - itemId: " + itemId, e);
            return true;
        }
    }

    @Override
    public boolean hasChildren(Object itemId) {
        final Item item = getJcrItem(itemId);
        if (item.isNode()) {
            final Node node = (Node) item;
            try {
                final NodeIterator it = node.getNodes();
                while (it.hasNext()) {
                    if (isNodeVisible(it.nextNode())) {
                        return true;
                    }
                }
            } catch (RepositoryException e) {
                log.warn("Failed to get child nodes of {}", NodeUtil.getPathIfPossible((node)));
            }

            if (getConfiguration().isIncludeProperties()) {
                try {
                    final PropertyIterator propertyIterator = node.getProperties();
                    while (propertyIterator.hasNext()) {
                        final Property property = propertyIterator.nextProperty();
                        if (!isJcrOrMgnlProperty(property)) {
                            return true;
                        }
                    }
                } catch (RepositoryException e) {
                    log.warn("Failed to get child nodes of {}", NodeUtil.getPathIfPossible((node)));
                }
            }
        }
        return false;
    }

    @Override
    public void sort(final Object[] propertyId, final boolean[] ascending) {
        sorters.clear();
        for (int i = 0; i < propertyId.length; i++) {
            if (getSortableContainerPropertyIds().contains(String.valueOf(propertyId[i]))) {
                OrderBy orderBy = new OrderBy((String) propertyId[i], ascending[i]);
                sorters.add(orderBy);
            }
        }
        fireItemSetChange();
    }

    private boolean isJcrOrMgnlProperty(Property property) throws RepositoryException {
        final String propertyName = property.getName();
        return propertyName.startsWith(NodeTypes.JCR_PREFIX) || propertyName.startsWith(NodeTypes.MGNL_PREFIX);
    }

    protected Collection<JcrItemId> createContainerIds(Collection<Item> children) {
        ArrayList<JcrItemId> ids = new ArrayList<JcrItemId>();
        for (Item child : children) {
            try {
                JcrItemId itemId = JcrItemUtil.getItemId(child);
                ids.add(itemId);
            } catch (RepositoryException e) {
                handleRepositoryException(log, "Cannot retrieve currentId", e);
            }
        }
        return ids;
    }

    public Collection<Item> getChildren(Item item) {
        if (!item.isNode()) {
            return Collections.emptySet();
        }

        Node node = (Node) item;
        ArrayList<Item> items = new ArrayList<Item>();
        try {
            NodeIterator iterator;
            // we cannot use query just for getting children of a node as SQL2 is not returning them in natural order
            // so if any sorter is defined use query, otherwise get all children from the node
            if (isSortable()) {
                nodePath = node.getPath();
                String query = constructJCRQuery(true);
                iterator = QueryUtil.search(getWorkspace(), query, Query.JCR_SQL2);
            } else {
                iterator = node.getNodes();
            }

            while (iterator.hasNext()) {
                Node next = iterator.nextNode();
                if (isNodeVisible(next)) {
                    items.add(next);
                }
            }

            if (getConfiguration().isIncludeProperties()) {
                ArrayList<Property> properties = new ArrayList<Property>();
                PropertyIterator propertyIterator = node.getProperties();
                while (propertyIterator.hasNext()) {
                    final Property property = propertyIterator.nextProperty();
                    if (!isJcrOrMgnlProperty(property)) {
                        properties.add(property);
                    }
                }
                ItemNameComparator itemNameComparator = new ItemNameComparator();
                Collections.sort(properties, itemNameComparator);
                items.addAll(properties);
            }
        } catch (RepositoryException e) {
            handleRepositoryException(log, "Could not retrieve children", e);
        }

        return Collections.unmodifiableCollection(items);
    }

    protected boolean isNodeVisible(Node node) throws RepositoryException {

        if (!getConfiguration().isIncludeSystemNodes() && node.getName().startsWith("jcr:")
                || node.getName().startsWith("rep:")) {
            return false;
        }

        String primaryNodeTypeName = node.getPrimaryNodeType().getName();
        for (NodeTypeDefinition nodeTypeDefinition : getConfiguration().getNodeTypes()) {
            if (nodeTypeDefinition.isStrict()) {
                if (primaryNodeTypeName.equals(nodeTypeDefinition.getName())) {
                    return true;
                }
            } else if (NodeUtil.isNodeType(node, nodeTypeDefinition.getName())) {
                return true;
            }
        }
        return false;
    }

    public Collection<Item> getRootItemIds() throws RepositoryException {
        return getChildren(getRootNode());
    }

    /**
     * Checks if an item is a root. Since root node is never shown, we consider its child nodes and properties as roots
     * to remove unnecessary offset in trees.
     */
    public boolean isRoot(Item item) throws RepositoryException {
        if (item != null) {
            try {
                int rootDepth = getRootNode().getDepth();
                return item.getDepth() <= rootDepth + 1;
            } catch (RepositoryException e) {
                handleRepositoryException(log, "Cannot determine depth of jcr item", e);
            }
        }
        return true;
    }

    @Override
    protected String getQueryWhereClause() {
        String whereClause;
        String clauseWorkspacePath = getQueryWhereClauseForNodePath(nodePath);
        String clauseNodeTypes = getQueryWhereClauseNodeTypes();
        if (StringUtils.isNotBlank(clauseNodeTypes)) {
            whereClause = " where ((" + clauseNodeTypes + ") ";
            if (StringUtils.isNotBlank(clauseWorkspacePath)) {
                whereClause += "and " + clauseWorkspacePath;
            }
            whereClause += ") ";
        } else {
            whereClause = " where ";
        }

        log.debug("JCR query WHERE clause is {}", whereClause);
        return whereClause;
    }

    private String getQueryWhereClauseForNodePath(final String nodePath) {
        return String.format(WHERE_CLAUSE_FOR_PATH, nodePath);
    }

    @Override
    protected String getQueryWhereClauseNodeTypes() {
        List<String> defs = new ArrayList<String>();
        for (NodeType nt : getSearchableNodeTypes()) {
            if (nt.isMixin()) {
                // Mixin type information is found in jcr:mixinTypes property see http://www.day.com/specs/jcr/2.0/10_Writing.html#10.10.3%20Assigning%20Mixin%20Node%20Types
                defs.add("[jcr:mixinTypes] = '" + nt.getName() + "'");
            } else {
                defs.add("[jcr:primaryType] = '" + nt.getName() + "'");
            }
        }
        return StringUtils.join(defs, " or ");
    }

    /**
     * Only used in tests.
     */
    String getPathInTree(Item item) throws RepositoryException {
        String base = getConfiguration().getRootPath();
        return "/".equals(base) ? item.getPath() : StringUtils.substringAfter(item.getPath(), base);
    }

    private Session getSession() throws RepositoryException {
        return MgnlContext.getJCRSession(getWorkspace());
    }

    protected Node getRootNode() throws RepositoryException {
        return getSession().getNode(getConfiguration().getRootPath());
    }

    public boolean isSortable() {
        return sortable;
    }

    public void setSortable(boolean sortable) {
        this.sortable = sortable;
    }
}