/** * This file Copyright (c) 2008-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.ItemType; import info.magnolia.cms.core.NodeData; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.Comparator; import java.util.List; import javax.jcr.PathNotFoundException; import javax.jcr.RepositoryException; import org.apache.commons.lang.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * This wrapper inherits content from the parent hierarchy. The method {@link #isAnchor()} defines * the anchor to which the inheritance is performed relative to. By default the anchor is a page * (mgnl:content). * <p> * The inheritance is then performed as follows: * <ul> * <li>try to get the content directly</li> * <li>find next anchor</li> * <li>try to get the content from the anchor</li> * <li>repeat until no anchor can be found anymore (root)</li> * </ul> * <p> * The {@link #getChildren()} methods merge the direct and inherited children by first adding the * inherited children to the collection and then the direct children. * @author pbracher * @version $Id$ */ public class InheritanceContentWrapper extends ContentWrapper { private static Logger log = LoggerFactory.getLogger(InheritanceContentWrapper.class); /** * From where the inheritance started. */ private final Content start; /** * Used if in the {@link #wrap(Content)} method. */ public InheritanceContentWrapper(Content wrappedContent, Content start) { super(wrappedContent); this.start = start; } /** * Starts the inheritance. */ public InheritanceContentWrapper(Content node) { this(node, node); } @Override public boolean hasContent(String name) throws RepositoryException { return getContentSafely(name) != null; } @Override public Content getContent(String name) throws RepositoryException { Content inherited = getContentSafely(name); if(inherited == null){ throw new PathNotFoundException("Can't inherit a node [" + name + "] on node [" + getWrappedContent().getHandle() + "]"); } return inherited; } @Override public Collection<Content> getChildren(ContentFilter filter, String namePattern, Comparator<Content> orderCriteria){ List<Content> children = new ArrayList<Content>(); // add inherited children try { Content inherited = getContentSafely(findNextAnchor(), resolveInnerPath()); if(inherited != null){ children.addAll(((AbstractContent)inherited).getChildren(filter, namePattern, orderCriteria)); } } catch (RepositoryException e) { throw new RuntimeException("Can't inherit children from " + getWrappedContent(), e); } // add direct children children.addAll(((AbstractContent)getWrappedContent()).getChildren(filter, namePattern, orderCriteria)); if(orderCriteria != null){ Collections.sort(children, orderCriteria); } return wrapContentNodes(children); } /** * Returns the inner path of the this node up to the anchor. */ protected String resolveInnerPath() throws RepositoryException { final String path; InheritanceContentWrapper anchor = findAnchor(); // if no anchor left we are relative to the root if(anchor == null){ path = this.getHandle(); } else{ path = StringUtils.substringAfter(this.getHandle(), anchor.getHandle()); } return StringUtils.removeStart(path,"/"); } /** * This method returns null if no content has been found. */ protected Content getContentSafely(String name) throws RepositoryException { if(getWrappedContent().hasContent(name)){ return super.getContent(name); } String innerPath = resolveInnerPath() + "/" + name; innerPath = StringUtils.removeStart(innerPath,"/"); Content inherited = getContentSafely(findNextAnchor(), innerPath); return inherited; } /** * This method returns null if no content has been found. */ protected Content getContentSafely(InheritanceContentWrapper anchor, String path) throws RepositoryException{ if(anchor == null){ return null; } if(StringUtils.isEmpty(path)){ return anchor; } return anchor.getContentSafely(path); } /** * Find the anchor for this node. */ protected InheritanceContentWrapper findAnchor() throws RepositoryException{ if(getLevel() ==0){ return null; } if(isAnchor()){ return this; } // until the current node is the anchor return ((InheritanceContentWrapper)getParent()).findAnchor(); } /** * Find next anchor. */ protected InheritanceContentWrapper findNextAnchor() throws RepositoryException{ final InheritanceContentWrapper currentAnchor = findAnchor(); if(currentAnchor != null && getLevel() >0){ return ((InheritanceContentWrapper)currentAnchor.getParent()).findAnchor(); } return null; } /** * True if this node is an anchor. By default true if this node is of type mgnl:content (page) */ protected boolean isAnchor() { return isNodeType(ItemType.CONTENT.getSystemName()); } @Override public NodeData getNodeData(String name) { try { if (getWrappedContent().hasNodeData(name)) { return getWrappedContent().getNodeData(name); } Content inherited = getContentSafely(findNextAnchor(), resolveInnerPath()); if(inherited != null){ return inherited.getNodeData(name); } } catch (RepositoryException e) { throw new RuntimeException("Can't inherit nodedata " + name + " for " + getWrappedContent(), e); } // creates a none existing node data in the standard manner return super.getNodeData(name); } @Override public boolean hasNodeData(String name) throws RepositoryException { try { if (getWrappedContent().hasNodeData(name)) { return getWrappedContent().hasNodeData(name); } Content inherited = getContentSafely(findNextAnchor(), resolveInnerPath()); if (inherited != null) { return inherited.hasNodeData(name); } } catch (RepositoryException e) { throw new RuntimeException("Can't inherit nodedata " + name + " for " + getWrappedContent(), e); } // creates a none existing node data in the standard manner return super.hasNodeData(name); } /** * Wrap returned nodes. Sets the inherited flag */ @Override protected Content wrap(Content node) { // only wrap once if(node instanceof InheritanceContentWrapper){ return node; } return new InheritanceContentWrapper(node, start); } /** * True if this is not a sub node of the starting point. */ public boolean isInherited() { return !getWrappedContent().getHandle().startsWith(start.getHandle()); } }