/**
* 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());
}
}