/**
* 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.core;
import info.magnolia.cms.i18n.I18nContentSupportFactory;
import info.magnolia.cms.security.AccessDeniedException;
import info.magnolia.cms.security.PermissionUtil;
import info.magnolia.cms.util.NodeDataUtil;
import info.magnolia.cms.util.NodeTypeFilter;
import info.magnolia.context.MgnlContext;
import info.magnolia.logging.AuditLoggingUtil;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collection;
import java.util.Comparator;
import javax.jcr.PathNotFoundException;
import javax.jcr.PropertyType;
import javax.jcr.RepositoryException;
import javax.jcr.Value;
import javax.jcr.Workspace;
import org.apache.commons.lang.StringUtils;
/**
* A base class by implementing some default behavior.
* A subclass must carefully implement {@link #newNodeDataInstance(String, int, boolean)},
* {@link #getChildren(info.magnolia.cms.core.Content.ContentFilter, String, java.util.Comparator)} and
* {@link #getNodeDataCollection(String)}.
*
* @author pbaerfuss
* @version $Id$
*/
public abstract class AbstractContent extends ContentHandler implements Content {
private final static org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(AbstractContent.class);
@Override
public Content createContent(String name) throws PathNotFoundException, RepositoryException, AccessDeniedException {
return createContent(name, ItemType.CONTENT);
}
@Override
public Content createContent(String name, ItemType contentType) throws PathNotFoundException, RepositoryException, AccessDeniedException {
return createContent(name, contentType.getSystemName());
}
@Override
public NodeData createNodeData(String name) throws PathNotFoundException, RepositoryException, AccessDeniedException {
return setNodeData(name, "");
}
@Override
public NodeData createNodeData(String name, Value value) throws PathNotFoundException, RepositoryException, AccessDeniedException {
return setNodeData(name, value);
}
/**
* @deprecated
*/
@Override
@Deprecated
public NodeData createNodeData(String name, Value[] value) throws PathNotFoundException, RepositoryException, AccessDeniedException {
return setNodeData(name, value);
}
/**
* @deprecated
*/
@Override
@Deprecated
public NodeData createNodeData(String name, int type) throws PathNotFoundException, RepositoryException, AccessDeniedException {
// set some default values to create the property
switch (type) {
case PropertyType.STRING:
return setNodeData(name, StringUtils.EMPTY);
case PropertyType.BOOLEAN:
return setNodeData(name, Boolean.FALSE);
case PropertyType.DATE:
return setNodeData(name, Calendar.getInstance());
case PropertyType.LONG:
return setNodeData(name, Long.valueOf(0));
case PropertyType.DOUBLE:
return setNodeData(name, Double.valueOf(0.0));
default:
return newNodeDataInstance(name, type, true);
}
}
/**
* @deprecated
*/
@Override
@Deprecated
public NodeData createNodeData(String name, Object valueObj) throws RepositoryException {
return setNodeData(name, valueObj);
}
/**
* {@inheritDoc}
* Delegates to {@link #newNodeDataInstance(String, int, boolean)} by setting the type to PropertyType.UNDEFINED. A subclass has to handle this by trying to determine the type if the node data exists. The reason for this is that implementations want to instantiate different node data classes per type
*/
@Override
public NodeData getNodeData(String name) {
try {
// will try to determine the type if the node data exists, otherwise an non-mutable node data will be returned
return newNodeDataInstance(name, PropertyType.UNDEFINED, false);
}
catch(RepositoryException e){
throw new IllegalStateException("Can't instantiate node data " + name + " on node " + toString(), e);
}
}
/**
* As defined in {@link Content#getNodeData(String)} this method always returns a node data object. If the type is {@link PropertyType#UNDEFINED} the implementation should check if the node data exists and determine the type to use.
*
* @param createIfNotExisting if false an empty non-mutable node data will be returned if the node data doesn't exist otherwise a mutable nodedata object is returned (depending on the type)
*/
abstract public NodeData newNodeDataInstance(String name, int type, boolean createIfNotExisting) throws AccessDeniedException, RepositoryException;
/**
* Delegates to {@link NodeData#isExist()}.
*/
@Override
public boolean hasNodeData(String name) throws RepositoryException {
return getNodeData(name).isExist();
}
@Override
public NodeData setNodeData(String name, Value value) throws PathNotFoundException, RepositoryException, AccessDeniedException {
NodeData nodeData = newNodeDataInstance(name, value.getType(), true);
nodeData.setValue(value);
return nodeData;
}
@Override
public NodeData setNodeData(String name, Value[] value) throws PathNotFoundException, RepositoryException, AccessDeniedException {
if(value.length == 0){
throw new IllegalArgumentException("Value array can't be empty");
}
NodeData nodeData = newNodeDataInstance(name, value[0].getType(), true);
nodeData.setValue(value);
return nodeData;
}
@Override
public NodeData setNodeData(String name, boolean value) throws PathNotFoundException, RepositoryException, AccessDeniedException {
NodeData nodeData = newNodeDataInstance(name, PropertyType.BOOLEAN, true);
nodeData.setValue(value);
return nodeData;
}
@Override
public NodeData setNodeData(String name, long value) throws PathNotFoundException, RepositoryException, AccessDeniedException {
NodeData nodeData = newNodeDataInstance(name, PropertyType.LONG, true);
nodeData.setValue(value);
return nodeData;
}
@Override
public NodeData setNodeData(String name, double value) throws PathNotFoundException, RepositoryException, AccessDeniedException {
NodeData nodeData = newNodeDataInstance(name, PropertyType.DOUBLE, true);
nodeData.setValue(value);
return nodeData;
}
@Override
public NodeData setNodeData(String name, String value) throws PathNotFoundException, RepositoryException, AccessDeniedException {
NodeData nodeData = newNodeDataInstance(name, PropertyType.STRING, true);
nodeData.setValue(value);
return nodeData;
}
@Override
public NodeData setNodeData(String name, InputStream value) throws PathNotFoundException, RepositoryException, AccessDeniedException {
NodeData nodeData = newNodeDataInstance(name, PropertyType.BINARY, true);
nodeData.setValue(value);
return nodeData;
}
@Override
public NodeData setNodeData(String name, Calendar value) throws PathNotFoundException, RepositoryException, AccessDeniedException {
NodeData nodeData = newNodeDataInstance(name, PropertyType.DATE, true);
nodeData.setValue(value);
return nodeData;
}
@Override
public NodeData setNodeData(String name, Content value) throws PathNotFoundException, RepositoryException, AccessDeniedException {
NodeData nodeData = newNodeDataInstance(name, PropertyType.REFERENCE, true);
nodeData.setValue(value);
return nodeData;
}
/**
* Uses the {@link NodeDataUtil} to create and set the node data based on the object type.
*/
@Override
public NodeData setNodeData(String name, Object value) throws PathNotFoundException, RepositoryException, AccessDeniedException {
NodeData nodeData = newNodeDataInstance(name, NodeDataUtil.getJCRPropertyType(value), true);
NodeDataUtil.setValue(nodeData, value);
return nodeData;
}
@Override
public void deleteNodeData(String name) throws PathNotFoundException, RepositoryException {
getNodeData(name).delete();
}
/**
* {@inheritDoc}
* Delegates to {@link #getChildren(ItemType)} passing the current node's type.
*/
@Override
public Collection<Content> getChildren() {
String type = null;
try {
type = this.getNodeTypeName();
}
catch (RepositoryException re) {
throw new RuntimeException("Can't read type of node [" + toString() + "]", re);
}
// fix all getChildren calls from the root node
if ("rep:root".equalsIgnoreCase(type)) {
type = ItemType.CONTENT.getSystemName();
}
// --------------------------------------------------
return this.getChildren(type);
}
/**
* {@inheritDoc}
* Delegates to {@link #getChildren(info.magnolia.cms.core.Content.ContentFilter, java.util.Comparator)}.
*/
@Override
public Collection<Content> getChildren(ContentFilter filter) {
return getChildren(filter, null);
}
/**
* {@inheritDoc}
* Delegates to {@link #getChildren(info.magnolia.cms.core.Content.ContentFilter, java.util.Comparator)}.
*/
@Override
public Collection<Content> getChildren(ItemType itemType) {
return getChildren(new NodeTypeFilter(itemType), null);
}
/**
* {@inheritDoc}
* Delegates to {@link #getChildren(info.magnolia.cms.core.Content.ContentFilter, java.util.Comparator)}.
*/
@Override
public Collection<Content> getChildren(String contentType) {
return getChildren(new NodeTypeFilter(contentType), null);
}
/**
* {@inheritDoc}
* Delegates to {@link #getChildren(info.magnolia.cms.core.Content.ContentFilter, String, java.util.Comparator)}.
*/
@Override
public Collection<Content> getChildren(final String contentType, final String namePattern) {
return getChildren(new NodeTypeFilter(contentType), namePattern, null);
}
/**
* {@inheritDoc}
* Delegates to {@link #getChildren(info.magnolia.cms.core.Content.ContentFilter, String, java.util.Comparator)}.
*/
@Override
public Collection<Content> getChildren(ContentFilter filter, Comparator<Content> orderCriteria) {
return getChildren(filter, null, orderCriteria);
}
/**
* @param namePattern ignored if null.
*/
abstract public Collection<Content> getChildren(ContentFilter filter, String namePattern, Comparator<Content> orderCriteria);
/**
* @deprecated
*/
@Override
@Deprecated
public Content getChildByName(String namePattern) {
Collection<Content> children = getChildren("nt:base", namePattern);
if (!children.isEmpty()) {
return children.iterator().next();
}
return null;
}
@Override
public Collection<NodeData> getNodeDataCollection() {
return getNodeDataCollection(null);
}
protected Collection<NodeData> getBinaryNodeDatas(String namePattern) throws RepositoryException {
Collection<NodeData> nodeDatas = new ArrayList<NodeData>();
Collection<Content> binaryNodes = getChildren(ItemType.NT_RESOURCE, namePattern);
for (Content binaryNode : binaryNodes) {
nodeDatas.add(newNodeDataInstance(binaryNode.getName(), PropertyType.BINARY, false));
}
return nodeDatas;
}
@Override
public boolean hasChildren() {
return (this.getChildren().size() > 0);
}
@Override
public boolean hasChildren(String contentType) {
return (this.getChildren(contentType).size() > 0);
}
@Override
public void delete(String path) throws RepositoryException {
if(isNodeData(path)){
deleteNodeData(path);
}
else{
getContent(path).delete();
}
}
@Override
public boolean isNodeData(String path) throws AccessDeniedException, RepositoryException {
return hasNodeData(path);
}
@Override
public String getTemplate() {
return this.getMetaData().getTemplate();
}
@Override
public String getTitle() {
return I18nContentSupportFactory.getI18nSupport().getNodeData(this, "title").getString();
}
@Override
public void updateMetaData() throws RepositoryException, AccessDeniedException {
MetaData md = this.getMetaData();
md.setModificationDate();
md.setAuthorId(MgnlContext.getUser().getName());
AuditLoggingUtil.log(AuditLoggingUtil.ACTION_MODIFY, getWorkspace().getName(), this.getItemType(), getHandle());
}
@Override
public boolean isGranted(long permissions) {
// In case getHandle() was manipulated by the subclass (e.g. ContentVersion we need to make sure permissions are checked on the said path)
final String handle = getHandle();
try {
return PermissionUtil.isGranted(this.getJCRNode().getSession(), handle, permissions);
} catch (RepositoryException e) {
log.error("An error occurred while trying to access path " + handle + " with permissions " + permissions, e);
return false;
}
}
@Override
public Workspace getWorkspace() throws RepositoryException {
return getJCRNode().getSession().getWorkspace();
}
@Override
public String toString() {
String type = "";
String workspaceName = "";
try {
workspaceName = getWorkspace() == null ? "null" : getWorkspace().getName();
type = getItemType().getSystemName();
}
catch (RepositoryException e) {
// ignore
}
StringBuilder builder = new StringBuilder();
builder.append(workspaceName);
builder.append(':').append(getHandle());
builder.append('[');
builder.append(type);
builder.append(']');
return builder.toString();
}
}