info.magnolia.cms.util.ExtendingContentWrapper.java Source code

Java tutorial

Introduction

Here is the source code for info.magnolia.cms.util.ExtendingContentWrapper.java

Source

/**
 * This file Copyright (c) 2010-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.cms.util;

import info.magnolia.cms.core.AbstractContent;
import info.magnolia.cms.core.Content;
import info.magnolia.cms.core.DefaultNodeData;
import info.magnolia.cms.core.NodeData;

import java.util.Collection;
import java.util.Comparator;
import java.util.LinkedHashMap;
import java.util.SortedMap;
import java.util.TreeMap;

import javax.jcr.RepositoryException;

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

/**
 * This wrapper allows extending other nodes (mainly useful to extend configurations). A content node can define
 * a nodeData with the name 'extends'. Its value is either an absolute or relative path. The merge is then performed as follows:
 *
 * <ul>
 * <li>nodeDatas are merged and values are overwritten
 * <li>sub content nodes are merged, the original order is guaranteed, new nodes are added at the
 * end of the list
 * </ul>
 *
 * The mechanism supports multiple inheritances as such:
 * <ul>
 * <li>the node the current node inherits from can again extend a node
 * <li>nodes laying deeper in the hierarchy can extend an other node
 * </ul>
 *
 * @deprecated since Magnolia 5.0 - use {@link info.magnolia.jcr.wrapper.ExtendingNodeWrapper} instead.
 * @version $Id$
 * @see InheritanceContentWrapper a class supporting content inheritance.
 */
public class ExtendingContentWrapper extends ContentWrapper {

    protected static final String EXTENDING_NODE_DATA = "extends";
    protected static final String EXTENDING_NODE_DATA_OVERRIDE = "override";

    private boolean extending;

    private Content extendedContent;

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

    //This is just for test purposes. Use ExtendingContentWrapper(Content wrappedContent) as was here before.
    ExtendingContentWrapper(Content wrappedContent, boolean failOnError) {

        super(wrappedContent);
        try {
            extending = getWrappedContent().hasNodeData(EXTENDING_NODE_DATA);
            if (extending) {
                NodeData extendingNodeData = getWrappedContent().getNodeData(EXTENDING_NODE_DATA);

                // check if override is not forced
                String extendedNode = extendingNodeData.getString();

                Content parent = extendingNodeData.getParent();

                if (StringUtils.isBlank(extendedNode)) {
                    // there is nothing to do, extending node is not defined ... probably caught in middle of config
                    extending = false;
                } else if (EXTENDING_NODE_DATA_OVERRIDE.equals(extendedNode)) {
                    extending = false;
                }
                if (extending) {
                    if (isExists(extendedNode, parent)) {
                        // support multiple inheritance
                        extendedContent = wrapIfNeeded(extendingNodeData.getReferencedContent());
                    } else {
                        String message = "Can't find referenced node for value: " + wrappedContent;
                        log.error(message);
                        extending = false;
                        if (failOnError) {
                            throw new RuntimeException(message);
                        }
                    }

                }

            }
        } catch (RepositoryException e) {
            throw new RuntimeException("Can't wrap node [" + wrappedContent + "]", e);
        }
    }

    public ExtendingContentWrapper(Content wrappedContent) {
        this(wrappedContent, false);
    }

    /**
     * Does not support the extends nodedata but chains the two nodes directly. Each node is
     * wrapped internally to ensure that each of them support the extends nodedata for themselves.
     */
    protected ExtendingContentWrapper(Content wrappedContent, Content extendedContent) {
        super(wrapIfNeeded(wrappedContent));
        extending = true;
        try {
            if (getWrappedContent().hasNodeData(EXTENDING_NODE_DATA)) {
                NodeData extendingNodeData = getWrappedContent().getNodeData(EXTENDING_NODE_DATA);

                // check if override is not forced
                extending = !EXTENDING_NODE_DATA_OVERRIDE.equals(extendingNodeData.getString());
            }
        } catch (RepositoryException e) {
            throw new RuntimeException("Can't determine extends point for node [" + wrappedContent + "]", e);
        }
        // might extend further more
        this.extendedContent = wrapIfNeeded(extendedContent);
    }

    private boolean isExists(String extendedNode, Content parent) throws RepositoryException {
        if (extendedNode.startsWith("/")) {
            return getWrappedContent().getHierarchyManager().isExist(extendedNode);
        }
        return parent.hasContent(extendedNode);
    }

    private static Content wrapIfNeeded(Content content) {
        if (content instanceof ExtendingContentWrapper) {
            return content;
        }
        return new ExtendingContentWrapper(content);
    }

    public boolean isExtending() {
        return this.extending;
    }

    @Override
    public Content getWrappedContent() {
        Content wrapped = super.getWrappedContent();
        if (wrapped instanceof ExtendingContentWrapper) {
            ExtendingContentWrapper wrappedECW = (ExtendingContentWrapper) wrapped;
            if (!wrappedECW.extending) {
                // wrapped but not extending ==> should not be wrapped in the first place but decision is made too late - in init<> of the ECW
                return ((ExtendingContentWrapper) wrapped).getWrappedContent();
            }
        }
        return super.getWrappedContent();
    }

    @Override
    public boolean hasContent(String name) throws RepositoryException {
        if (getWrappedContent().hasContent(name)) {
            return true;
        } else if (extending && extendedContent.hasContent(name)) {
            return true;
        }
        return false;
    }

    @Override
    public Content getContent(String name) throws RepositoryException {
        Content content;
        if (getWrappedContent().hasContent(name)) {
            content = getWrappedContent().getContent(name);
        } else if (extending && extendedContent.hasContent(name)) {
            content = extendedContent.getContent(name);
        } else {
            // this will throw a PathNotFoundException
            content = getWrappedContent().getContent(name);
        }
        return wrap(content);
    }

    @Override
    public Collection<Content> getChildren(ContentFilter filter, String namePattern,
            Comparator<Content> orderCriteria) {
        Collection<Content> directChildren = ((AbstractContent) getWrappedContent()).getChildren(filter,
                namePattern, orderCriteria);
        if (extending) {
            Collection<Content> inheritedChildren = ((AbstractContent) extendedContent).getChildren(filter,
                    namePattern, orderCriteria);
            // keep order, add new elements at the end of the collection
            LinkedHashMap<String, Content> merged = new LinkedHashMap<String, Content>();
            for (Content content : inheritedChildren) {
                merged.put(content.getName(), content);
            }
            for (Content content : directChildren) {
                merged.put(content.getName(), content);
            }
            return wrapContentNodes(merged.values());
        }
        return wrapContentNodes(directChildren);
    }

    @Override
    public NodeData getNodeData(String name) {
        if (EXTENDING_NODE_DATA.equals(name)) {
            return new DefaultNodeData(extendedContent, name) {
                @Override
                public boolean isExist() {
                    return false;
                }

                @Override
                public void save() throws RepositoryException {
                    // do nothing
                }
            };
        }
        try {
            if (getWrappedContent().hasNodeData(name)) {
                return wrap(getWrappedContent().getNodeData(name));
            } else if (extending && extendedContent.hasNodeData(name)) {
                return wrap(extendedContent.getNodeData(name));
            } else {
                return wrap(getWrappedContent().getNodeData(name));
            }
        } catch (RepositoryException e) {
            throw new RuntimeException("Can't read nodedata from extended node [" + extendedContent + "]", e);
        }
    }

    @Override
    public boolean hasNodeData(String name) throws RepositoryException {
        if (EXTENDING_NODE_DATA.equals(name)) {
            return false;
        }
        return super.hasNodeData(name);
    }

    @Override
    public Collection<NodeData> getNodeDataCollection() {
        final Content wrapped = getWrappedContent();
        Collection<NodeData> directChildren = wrapped.getNodeDataCollection();
        try {
            if (wrapped.hasNodeData(EXTENDING_NODE_DATA)) {
                for (NodeData child : directChildren) {
                    if (EXTENDING_NODE_DATA.equals(child.getName())) {
                        directChildren.remove(child);
                        break;
                    }
                }
            }
        } catch (RepositoryException e) {
            throw new RuntimeException("Can't read nodedata collection from node [" + wrapped.getHandle() + "]", e);
        }
        if (extending) {
            Collection<NodeData> inheritedChildren = extendedContent.getNodeDataCollection();
            // sort by name
            SortedMap<String, NodeData> merged = new TreeMap<String, NodeData>();
            for (NodeData nodeData : inheritedChildren) {
                merged.put(nodeData.getName(), nodeData);
            }
            for (NodeData nodeData : directChildren) {
                merged.put(nodeData.getName(), nodeData);
            }
            return wrapNodeDatas(merged.values());
        }
        return wrapNodeDatas(directChildren);
    }

    @Override
    protected Content wrap(Content node) {
        // get the same subnode of the extended content
        try {
            if (extending && extendedContent.hasContent(node.getName())) {
                // FIXME we have to calculate the relative path
                Content extendedSubContent = extendedContent.getContent(node.getName());
                return new ExtendingContentWrapper(node, extendedSubContent);
            }
        } catch (RepositoryException e) {
            throw new RuntimeException("Can't wrap " + node, e);
        }
        return wrapIfNeeded(node);
    }

}