org.apache.jackrabbit.jcr2spi.NodeImpl Maven / Gradle / Ivy
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.jackrabbit.jcr2spi;
import org.apache.jackrabbit.jcr2spi.hierarchy.NodeEntry;
import org.apache.jackrabbit.jcr2spi.hierarchy.PropertyEntry;
import org.apache.jackrabbit.jcr2spi.lock.LockStateManager;
import org.apache.jackrabbit.jcr2spi.nodetype.EffectiveNodeType;
import org.apache.jackrabbit.jcr2spi.nodetype.NodeTypeImpl;
import org.apache.jackrabbit.jcr2spi.nodetype.NodeTypeManagerImpl;
import org.apache.jackrabbit.jcr2spi.operation.AddNode;
import org.apache.jackrabbit.jcr2spi.operation.AddProperty;
import org.apache.jackrabbit.jcr2spi.operation.Operation;
import org.apache.jackrabbit.jcr2spi.operation.ReorderNodes;
import org.apache.jackrabbit.jcr2spi.operation.SetMixin;
import org.apache.jackrabbit.jcr2spi.operation.SetPrimaryType;
import org.apache.jackrabbit.jcr2spi.operation.Update;
import org.apache.jackrabbit.jcr2spi.state.ItemState;
import org.apache.jackrabbit.jcr2spi.state.ItemStateValidator;
import org.apache.jackrabbit.jcr2spi.state.NodeState;
import org.apache.jackrabbit.jcr2spi.state.Status;
import org.apache.jackrabbit.jcr2spi.util.LogUtil;
import org.apache.jackrabbit.jcr2spi.util.StateUtility;
import org.apache.jackrabbit.spi.Name;
import org.apache.jackrabbit.spi.NodeId;
import org.apache.jackrabbit.spi.Path;
import org.apache.jackrabbit.spi.PropertyId;
import org.apache.jackrabbit.spi.QNodeDefinition;
import org.apache.jackrabbit.spi.QPropertyDefinition;
import org.apache.jackrabbit.spi.QValue;
import org.apache.jackrabbit.spi.commons.conversion.NameException;
import org.apache.jackrabbit.spi.commons.name.NameConstants;
import org.apache.jackrabbit.spi.commons.value.ValueFormat;
import org.apache.jackrabbit.util.ChildrenCollectorFilter;
import org.apache.jackrabbit.value.ValueHelper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.jcr.AccessDeniedException;
import javax.jcr.Binary;
import javax.jcr.InvalidItemStateException;
import javax.jcr.Item;
import javax.jcr.ItemExistsException;
import javax.jcr.ItemNotFoundException;
import javax.jcr.ItemVisitor;
import javax.jcr.NoSuchWorkspaceException;
import javax.jcr.Node;
import javax.jcr.NodeIterator;
import javax.jcr.PathNotFoundException;
import javax.jcr.Property;
import javax.jcr.PropertyIterator;
import javax.jcr.PropertyType;
import javax.jcr.Repository;
import javax.jcr.RepositoryException;
import javax.jcr.UnsupportedRepositoryOperationException;
import javax.jcr.Value;
import javax.jcr.ValueFormatException;
import javax.jcr.lock.Lock;
import javax.jcr.lock.LockException;
import javax.jcr.nodetype.ConstraintViolationException;
import javax.jcr.nodetype.NoSuchNodeTypeException;
import javax.jcr.nodetype.NodeDefinition;
import javax.jcr.nodetype.NodeType;
import javax.jcr.version.Version;
import javax.jcr.version.VersionException;
import javax.jcr.version.VersionHistory;
import java.io.InputStream;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Calendar;
import java.util.Iterator;
import java.util.List;
/**
* NodeImpl
...
*/
public class NodeImpl extends ItemImpl implements Node {
private static Logger log = LoggerFactory.getLogger(NodeImpl.class);
protected NodeImpl(SessionImpl session, NodeState state, ItemLifeCycleListener[] listeners) {
super(session, state, listeners);
Name nodeTypeName = state.getNodeTypeName();
// make sure the nodetype name is valid
if (!session.getNodeTypeManager().hasNodeType(nodeTypeName)) {
// should not occur. Since nodetypes are defined by the 'server'
// its not possible to determine a fallback nodetype that is
// always available.
throw new IllegalArgumentException("Unknown nodetype " + LogUtil.saveGetJCRName(nodeTypeName, session.getNameResolver()));
}
}
//---------------------------------------------------------------< Item >---
/**
* @see Item#getName()
*/
@Override
public String getName() throws RepositoryException {
checkStatus();
return session.getNameResolver().getJCRName(getQName());
}
/**
* Implementation of {@link Item#accept(javax.jcr.ItemVisitor)} for nodes.
*
* @param visitor
* @throws RepositoryException
* @see Item#accept(javax.jcr.ItemVisitor)
*/
@Override
public void accept(ItemVisitor visitor) throws RepositoryException {
checkStatus();
visitor.visit(this);
}
/**
* Returns true
*
* @return true
* @see Item#isNode()
*/
@Override
public boolean isNode() {
return true;
}
//---------------------------------------------------------------< Node >---
/**
* @see Node#addNode(String)
*/
public Node addNode(String relPath) throws ItemExistsException, PathNotFoundException, VersionException, ConstraintViolationException, LockException, RepositoryException {
// validation performed in subsequent method
return addNode(relPath, null);
}
/**
* @see Node#addNode(String, String)
*/
public Node addNode(String relPath, String primaryNodeTypeName) throws ItemExistsException, PathNotFoundException, NoSuchNodeTypeException, LockException, VersionException, ConstraintViolationException, RepositoryException {
checkIsWritable();
// build path object and retrieve parent node
Path nodePath = getPath(relPath).getNormalizedPath();
if (nodePath.getIndex() != Path.INDEX_UNDEFINED) {
String msg = "Illegal subscript specified: " + relPath;
log.debug(msg);
throw new RepositoryException(msg);
}
NodeImpl parentNode;
if (nodePath.getLength() == 1) {
parentNode = this;
} else {
Path parentPath = nodePath.getAncestor(1);
ItemManager itemMgr = getItemManager();
if (itemMgr.nodeExists(parentPath)) {
parentNode = (NodeImpl) itemMgr.getNode(parentPath);
} else if (itemMgr.propertyExists(parentPath)) {
String msg = "Cannot add a node to property " + LogUtil.safeGetJCRPath(parentPath, session.getPathResolver());
log.debug(msg);
throw new ConstraintViolationException(msg);
} else {
throw new PathNotFoundException("Cannot add a new node to a non-existing parent at " + LogUtil.safeGetJCRPath(parentPath, session.getPathResolver()));
}
}
// get names objects for node and nt
Name nodeName = nodePath.getName();
Name ntName = (primaryNodeTypeName == null) ? null : getQName(primaryNodeTypeName);
// create new node (including validation checks)
return parentNode.createNode(nodeName, ntName);
}
/**
* @see Node#orderBefore(String, String)
*/
public synchronized void orderBefore(String srcChildRelPath,
String destChildRelPath)
throws UnsupportedRepositoryOperationException, VersionException, ConstraintViolationException, ItemNotFoundException, LockException, RepositoryException {
checkIsWritable();
if (!getPrimaryNodeType().hasOrderableChildNodes()) {
throw new UnsupportedRepositoryOperationException("Child node ordering not supported on node " + safeGetJCRPath());
}
// check arguments
if (srcChildRelPath.equals(destChildRelPath)) {
// there's nothing to do
return;
}
// check existence
if (!hasNode(srcChildRelPath)) {
throw new ItemNotFoundException("Node " + safeGetJCRPath() + " has no child node with name " + srcChildRelPath);
}
if (destChildRelPath != null && !hasNode(destChildRelPath)) {
throw new ItemNotFoundException("Node " + safeGetJCRPath() + " has no child node with name " + destChildRelPath);
}
Path srcPath = getReorderPath(srcChildRelPath);
Path beforePath = null;
if (destChildRelPath != null) {
beforePath = getReorderPath(destChildRelPath);
}
Operation op = ReorderNodes.create(getNodeState(), srcPath, beforePath);
session.getSessionItemStateManager().execute(op);
}
/**
* @see Node#setProperty(String, Value)
*/
public Property setProperty(String name, Value value) throws ValueFormatException, VersionException, LockException, ConstraintViolationException, RepositoryException {
// validation performed in subsequent method
int type = PropertyType.UNDEFINED;
if (value != null) {
type = value.getType();
}
return setProperty(name, value, type);
}
/**
* @see Node#setProperty(String, javax.jcr.Value, int)
*/
public Property setProperty(String name, Value value, int type) throws ValueFormatException, VersionException, LockException, ConstraintViolationException, RepositoryException {
checkIsWritable();
Name propName = getQName(name);
Property prop;
if (hasProperty(propName)) {
// property already exists: pass call to property
prop = getProperty(propName);
Value v = (type == PropertyType.UNDEFINED) ? value : ValueHelper.convert(value, type, session.getValueFactory());
prop.setValue(v);
} else {
if (value == null) {
return new StaleProperty();
} else {
// new property to be added
prop = createProperty(propName, value, type);
}
}
return prop;
}
/**
* @see Node#setProperty(String, Value[])
*/
public Property setProperty(String name, Value[] values) throws ValueFormatException, VersionException, LockException, ConstraintViolationException, RepositoryException {
// validation performed in subsequent method
int type;
if (values == null || values.length == 0 || values[0] == null) {
type = PropertyType.UNDEFINED;
} else {
type = values[0].getType();
}
return setProperty(name, values, type);
}
/**
* @see Node#setProperty(String, Value[], int)
*/
public Property setProperty(String name, Value[] values, int type) throws ValueFormatException, VersionException, LockException, ConstraintViolationException, RepositoryException {
checkIsWritable();
Name propName = getQName(name);
Property prop;
if (hasProperty(propName)) {
// property already exists: pass call to property
prop = getProperty(propName);
Value[] vs = (type == PropertyType.UNDEFINED) ? values : ValueHelper.convert(values, type, session.getValueFactory());
prop.setValue(vs);
} else {
if (values == null) {
// create and remove property is a nop.
throw new ItemNotFoundException("Cannot remove a non-existing property.");
} else {
// new property to be added
prop = createProperty(propName, values, type);
}
}
return prop;
}
/**
* @see Node#setProperty(String, String[])
*/
public Property setProperty(String name, String[] values) throws ValueFormatException, VersionException, LockException, ConstraintViolationException, RepositoryException {
// validation performed in subsequent method
return setProperty(name, values, PropertyType.UNDEFINED);
}
/**
* @see Node#setProperty(String, String[], int)
*/
public Property setProperty(String name, String[] values, int type) throws ValueFormatException, VersionException, LockException, ConstraintViolationException, RepositoryException {
// validation performed in subsequent method
Value[] vs;
if (type == PropertyType.UNDEFINED) {
vs = ValueHelper.convert(values, PropertyType.STRING, session.getValueFactory());
} else {
vs = ValueHelper.convert(values, type, session.getValueFactory());
}
return setProperty(name, vs, type);
}
/**
* @see Node#setProperty(String, String)
*/
public Property setProperty(String name, String value) throws ValueFormatException, VersionException, LockException, ConstraintViolationException, RepositoryException {
// validation performed in subsequent method
Value v = (value == null) ? null : session.getValueFactory().createValue(value, PropertyType.STRING);
return setProperty(name, v, PropertyType.UNDEFINED);
}
/**
* @see Node#setProperty(String, String, int)
*/
public Property setProperty(String name, String value, int type) throws ValueFormatException, VersionException, LockException, ConstraintViolationException, RepositoryException {
// validation performed in subsequent method
Value v = (value == null) ? null : session.getValueFactory().createValue(value, type);
return setProperty(name, v, type);
}
/**
* @see Node#setProperty(String, InputStream)
*/
public Property setProperty(String name, InputStream value) throws ValueFormatException, VersionException, LockException, ConstraintViolationException, RepositoryException {
// validation performed in subsequent method
Value v = (value == null ? null : session.getValueFactory().createValue(value));
return setProperty(name, v, PropertyType.BINARY);
}
/**
* @see javax.jcr.Node#setProperty(String, Binary)
*/
public Property setProperty(String name, Binary value) throws RepositoryException {
// validation performed in subsequent method
Value v = (value == null ? null : session.getValueFactory().createValue(value));
return setProperty(name, v, PropertyType.BINARY);
}
/**
* @see Node#setProperty(String, boolean)
*/
public Property setProperty(String name, boolean value) throws ValueFormatException, VersionException, LockException, ConstraintViolationException, RepositoryException {
// validation performed in subsequent method
return setProperty(name, session.getValueFactory().createValue(value), PropertyType.BOOLEAN);
}
/**
* @see Node#setProperty(String, double)
*/
public Property setProperty(String name, double value) throws ValueFormatException, VersionException, LockException, ConstraintViolationException, RepositoryException {
// validation performed in subsequent method
return setProperty(name, session.getValueFactory().createValue(value), PropertyType.DOUBLE);
}
/**
* @see javax.jcr.Node#setProperty(String, BigDecimal)
*/
public Property setProperty(String name, BigDecimal value) throws RepositoryException {
// validation performed in subsequent method
Value v = (value == null ? null : session.getValueFactory().createValue(value));
return setProperty(name, v, PropertyType.DECIMAL);
}
/**
* @see Node#setProperty(String, long)
*/
public Property setProperty(String name, long value) throws ValueFormatException, VersionException, LockException, ConstraintViolationException, RepositoryException {
// validation performed in subsequent method
return setProperty(name, session.getValueFactory().createValue(value), PropertyType.LONG);
}
/**
* @see Node#setProperty(String, Calendar)
*/
public Property setProperty(String name, Calendar value) throws ValueFormatException, VersionException, LockException, ConstraintViolationException, RepositoryException {
// validation performed in subsequent method
Value v = (value == null ? null : session.getValueFactory().createValue(value));
return setProperty(name, v, PropertyType.DATE);
}
/**
* @see Node#setProperty(String, Node)
*/
public Property setProperty(String name, Node value) throws ValueFormatException, VersionException, LockException, ConstraintViolationException, RepositoryException {
// duplicate check to make sure, property can be written before value
// validation below.
checkIsWritable();
Value v;
if (value == null) {
v = null;
} else {
PropertyImpl.checkValidReference(value, PropertyType.REFERENCE, session.getNameResolver());
v = session.getValueFactory().createValue(value);
}
return setProperty(name, v, PropertyType.REFERENCE);
}
/**
* @see Node#getNode(String)
*/
public Node getNode(String relPath) throws PathNotFoundException, RepositoryException {
checkStatus();
NodeEntry nodeEntry = resolveRelativeNodePath(relPath);
if (nodeEntry == null) {
throw new PathNotFoundException(relPath);
}
try {
return (Node) getItemManager().getItem(nodeEntry);
} catch (ItemNotFoundException e) {
throw new PathNotFoundException(relPath, e);
}
}
/**
* @see Node#getNodes()
*/
public NodeIterator getNodes() throws RepositoryException {
checkStatus();
// NOTE: Don't use a class derived from TraversingElementVisitor to traverse
// the child nodes because this would lead to an infinite recursion.
try {
return getItemManager().getChildNodes(getNodeEntry());
} catch (ItemNotFoundException infe) {
String msg = "Failed to list the child nodes of " + safeGetJCRPath();
log.debug(msg);
throw new RepositoryException(msg, infe);
} catch (AccessDeniedException ade) {
String msg = "Failed to list the child nodes of " + safeGetJCRPath();
log.debug(msg);
throw new RepositoryException(msg, ade);
}
}
/**
* @see Node#getNodes(String)
*/
public NodeIterator getNodes(String namePattern) throws RepositoryException {
checkStatus();
return ChildrenCollectorFilter.collectChildNodes(this, namePattern);
}
/**
* @see javax.jcr.Node#getNodes(String[])
*/
public NodeIterator getNodes(String[] nameGlobs) throws RepositoryException {
checkStatus();
return ChildrenCollectorFilter.collectChildNodes(this, nameGlobs);
}
/**
* @see Node#getProperty(String)
*/
public Property getProperty(String relPath) throws PathNotFoundException, RepositoryException {
checkStatus();
PropertyEntry entry = resolveRelativePropertyPath(relPath);
if (entry == null) {
throw new PathNotFoundException(relPath);
}
try {
return (Property) getItemManager().getItem(entry);
} catch (AccessDeniedException e) {
throw new PathNotFoundException(relPath);
} catch (ItemNotFoundException e) {
throw new PathNotFoundException(relPath);
}
}
/**
* @see Node#getProperties()
*/
public PropertyIterator getProperties() throws RepositoryException {
checkStatus();
try {
return getItemManager().getChildProperties(getNodeEntry());
} catch (ItemNotFoundException infe) {
String msg = "Failed to list the child properties of " + getPath();
log.debug(msg);
throw new RepositoryException(msg, infe);
} catch (AccessDeniedException ade) {
String msg = "Failed to list the child properties of " + getPath();
log.debug(msg);
throw new RepositoryException(msg, ade);
}
}
/**
* @see Node#getProperties(String)
*/
public PropertyIterator getProperties(String namePattern) throws RepositoryException {
checkStatus();
return ChildrenCollectorFilter.collectProperties(this, namePattern);
}
/**
* @see javax.jcr.Node#getProperties(String)
*/
public PropertyIterator getProperties(String[] nameGlobs)
throws RepositoryException {
checkStatus();
return ChildrenCollectorFilter.collectProperties(this, nameGlobs);
}
/**
* @see Node#getPrimaryItem()
*/
public Item getPrimaryItem() throws ItemNotFoundException, RepositoryException {
checkStatus();
String name = getPrimaryNodeType().getPrimaryItemName();
if (name == null) {
throw new ItemNotFoundException("No primary item present on Node " + safeGetJCRPath());
}
if (hasProperty(name)) {
return getProperty(name);
} else if (hasNode(name)) {
return getNode(name);
} else {
throw new ItemNotFoundException("Primary item " + name + " does not exist on Node " + safeGetJCRPath());
}
}
/**
* @see Node#getUUID()
*/
public String getUUID() throws UnsupportedRepositoryOperationException, RepositoryException {
checkStatus();
String uuid = getNodeState().getUniqueID();
if (uuid == null || !isNodeType(NameConstants.MIX_REFERENCEABLE)) {
throw new UnsupportedRepositoryOperationException();
}
// Node is referenceable -> NodeId must contain a UUID part
return uuid;
}
/**
* @see Node#getIdentifier()
*/
public String getIdentifier() throws RepositoryException {
checkStatus();
return session.getIdFactory().toJcrIdentifier(getNodeEntry().getId());
}
/**
* @see Node#getIndex()
*/
public int getIndex() throws RepositoryException {
checkStatus();
int index = getNodeEntry().getIndex();
if (index == Path.INDEX_UNDEFINED) {
throw new RepositoryException("Error while retrieving index.");
}
return index;
}
/**
* @see Node#getReferences()
*/
public PropertyIterator getReferences() throws RepositoryException {
return getReferences(null);
}
/**
* @see javax.jcr.Node#getReferences(String)
*/
public PropertyIterator getReferences(String name) throws RepositoryException {
return getReferences(name, false);
}
/**
* @see javax.jcr.Node#getWeakReferences()
*/
public PropertyIterator getWeakReferences() throws RepositoryException {
return getWeakReferences(null);
}
/**
* @see javax.jcr.Node#getWeakReferences()
*/
public PropertyIterator getWeakReferences(String name) throws RepositoryException {
return getReferences(name, true);
}
/**
* @see Node#hasNode(String)
*/
public boolean hasNode(String relPath) throws RepositoryException {
checkStatus();
NodeEntry nodeEntry = resolveRelativeNodePath(relPath);
return (nodeEntry != null) && getItemManager().itemExists(nodeEntry);
}
/**
* @see Node#hasProperty(String)
*/
public boolean hasProperty(String relPath) throws RepositoryException {
checkStatus();
PropertyEntry childEntry = resolveRelativePropertyPath(relPath);
return (childEntry != null) && getItemManager().itemExists(childEntry);
}
/**
* Returns true, if this Node
has a property with the given name.
*
* @param propertyName
* @return true
, if this Node
has a property with
* the given name.
*/
private boolean hasProperty(Name propertyName) {
return getNodeEntry().hasPropertyEntry(propertyName);
}
/**
* @see Node#hasNodes()
*/
public boolean hasNodes() throws RepositoryException {
checkStatus();
return getItemManager().hasChildNodes(getNodeEntry());
}
/**
* @see Node#hasProperties()
*/
public boolean hasProperties() throws RepositoryException {
checkStatus();
return getItemManager().hasChildProperties(getNodeEntry());
}
/**
* @see Node#getPrimaryNodeType()
*/
public NodeType getPrimaryNodeType() throws RepositoryException {
checkStatus();
return session.getNodeTypeManager().getNodeType(getPrimaryNodeTypeName());
}
/**
* @see javax.jcr.Node#setPrimaryType(String)
*/
public void setPrimaryType(String nodeTypeName) throws RepositoryException {
checkStatus();
if (getNodeState().isRoot()) {
String msg = "The primary type of the root node may not be changed.";
log.debug(msg);
throw new RepositoryException(msg);
}
Name ntName = getQName(nodeTypeName);
if (ntName.equals(getPrimaryNodeTypeName())) {
log.debug("Changing the primary type has no effect: '" + nodeTypeName + "' already is the primary node type.");
return;
}
NodeTypeManagerImpl ntMgr = session.getNodeTypeManager();
NodeType nt = ntMgr.getNodeType(ntName);
if (nt.isMixin() || nt.isAbstract()) {
throw new ConstraintViolationException("Cannot change the primary type: '" + nodeTypeName + "' is a mixin type or abstract.");
}
// perform the operation
Operation op = SetPrimaryType.create(getNodeState(), ntName);
session.getSessionItemStateManager().execute(op);
}
/**
* @see Node#getMixinNodeTypes()
*/
public NodeType[] getMixinNodeTypes() throws RepositoryException {
checkStatus();
Name[] mixinNames = getNodeState().getMixinTypeNames();
NodeType[] nta = new NodeType[mixinNames.length];
for (int i = 0; i < mixinNames.length; i++) {
nta[i] = session.getNodeTypeManager().getNodeType(mixinNames[i]);
}
return nta;
}
/**
* @see Node#isNodeType(String)
*/
public boolean isNodeType(String nodeTypeName) throws RepositoryException {
checkStatus();
// try shortcut first (avoids parsing of name)
if (session.getNameResolver().getJCRName(getPrimaryNodeTypeName()).equals(nodeTypeName)) {
return true;
}
// parse to Name and check against effective nodetype
return isNodeType(getQName(nodeTypeName));
}
/**
* @see Node#addMixin(String)
*/
public void addMixin(String mixinName) throws NoSuchNodeTypeException,
VersionException, ConstraintViolationException, LockException, RepositoryException {
checkIsWritable();
Name mixinQName = getQName(mixinName);
// get mixin types present in the jcr:mixinTypes property without
// modifying the NodeState.
List mixinValue = getMixinTypes();
if (!mixinValue.contains(mixinQName) && !isNodeType(mixinQName)) {
if (!canAddMixin(mixinQName)) {
throw new ConstraintViolationException("Cannot add '" + mixinName + "' mixin type.");
}
mixinValue.add(mixinQName);
// perform the operation
Operation op = SetMixin.create(getNodeState(), mixinValue.toArray(new Name[mixinValue.size()]));
session.getSessionItemStateManager().execute(op);
}
}
/**
* @see Node#removeMixin(String)
*/
public void removeMixin(String mixinName) throws NoSuchNodeTypeException,
VersionException, ConstraintViolationException, LockException, RepositoryException {
checkIsWritable();
Name ntName = getQName(mixinName);
List mixinValue = getMixinTypes();
// remove name of target mixin
if (!mixinValue.remove(ntName)) {
throw new NoSuchNodeTypeException("Cannot remove mixin '" + mixinName + "': Nodetype is not present on this node.");
}
// mix:referenceable needs additional assertion: the mixin cannot be
// removed, if any references are left to this node.
NodeTypeImpl mixin = session.getNodeTypeManager().getNodeType(ntName);
if (mixin.isNodeType(NameConstants.MIX_REFERENCEABLE)) {
EffectiveNodeType entRemaining = getRemainingENT(mixinValue);
if (!entRemaining.includesNodeType(NameConstants.MIX_REFERENCEABLE)) {
PropertyIterator iter = getReferences();
if (iter.hasNext()) {
throw new ConstraintViolationException("Mixin type " + mixinName + " can not be removed: the node is being referenced through at least one property of type REFERENCE");
}
}
}
/*
* mix:lockable: the mixin cannot be removed if the node is currently
* locked even if the editing session is the lock holder.
*/
if (mixin.isNodeType((NameConstants.MIX_LOCKABLE))) {
EffectiveNodeType entRemaining = getRemainingENT(mixinValue);
if (!entRemaining.includesNodeType(NameConstants.MIX_LOCKABLE) && isLocked()) {
throw new ConstraintViolationException(mixinName + " can not be removed: the node is locked.");
}
}
// delegate to operation
Name[] mixins = mixinValue.toArray(new Name[mixinValue.size()]);
Operation op = SetMixin.create(getNodeState(), mixins);
session.getSessionItemStateManager().execute(op);
}
/**
* Retrieves the value of the jcr:mixinTypes property present with this
* Node including those that have been transiently added and excluding
* those, that have been transiently removed.
* NOTE, that the result of this method, does NOT represent the list of
* mixin-types that currently affect this node.
*
* @return mixin names present with the jcr:mixinTypes property.
*/
private List getMixinTypes() {
Name[] mixinValue;
if (getNodeState().getStatus() == Status.EXISTING) {
// jcr:mixinTypes must correspond to the mixins present on the nodestate.
mixinValue = getNodeState().getMixinTypeNames();
} else {
try {
PropertyEntry pe = getNodeEntry().getPropertyEntry(NameConstants.JCR_MIXINTYPES);
if (pe != null) {
// prop entry exists (and ev. has been transiently mod.)
// -> retrieve mixin types from prop
mixinValue = StateUtility.getMixinNames(pe.getPropertyState());
} else {
// prop entry has not been loaded yet -> not modified
mixinValue = getNodeState().getMixinTypeNames();
}
} catch (RepositoryException e) {
// should never occur
log.warn("Internal error", e);
mixinValue = Name.EMPTY_ARRAY;
}
}
List l = new ArrayList();
l.addAll(Arrays.asList(mixinValue));
return l;
}
/**
* Build the effective node type of remaining mixin's & primary type
*
* @param remainingMixins
* @return effective node type
* @throws ConstraintViolationException
* @throws NoSuchNodeTypeException
*/
private EffectiveNodeType getRemainingENT(List remainingMixins)
throws ConstraintViolationException, NoSuchNodeTypeException {
Name[] allRemaining = remainingMixins.toArray(new Name[remainingMixins.size() + 1]);
allRemaining[remainingMixins.size()] = getPrimaryNodeTypeName();
return session.getEffectiveNodeTypeProvider().getEffectiveNodeType(allRemaining);
}
/**
* @see Node#canAddMixin(String)
*/
public boolean canAddMixin(String mixinName) throws RepositoryException {
if (!isWritable()) {
// shortcut: repository does not support writing anyway.
return false;
}
try {
// first check if node is writable regarding protection status,
// locks, versioning, access restriction.
session.getValidator().checkIsWritable(getNodeState(), ItemStateValidator.CHECK_ALL);
// then make sure the new mixin would not conflict.
return canAddMixin(getQName(mixinName));
} catch (LockException e) {
log.debug("Cannot add mixin '" + mixinName + "': " + e.getMessage());
return false;
} catch (VersionException e) {
log.debug("Cannot add mixin '" + mixinName + "': " + e.getMessage());
return false;
} catch (ConstraintViolationException e) {
log.debug("Cannot add mixin '" + mixinName + "': " + e.getMessage());
return false;
}
}
/**
* @see Node#getDefinition()
*/
public NodeDefinition getDefinition() throws RepositoryException {
checkStatus();
QNodeDefinition qnd = getNodeState().getDefinition();
return session.getNodeTypeManager().getNodeDefinition(qnd);
}
/**
* @see Node#checkin()
*/
public Version checkin() throws VersionException, UnsupportedRepositoryOperationException, InvalidItemStateException, LockException, RepositoryException {
checkIsVersionable();
checkHasPendingChanges();
checkIsLocked();
if (isCheckedOut()) {
NodeEntry newVersion = session.getVersionStateManager().checkin(getNodeState());
return (Version) getItemManager().getItem(newVersion);
} else {
// nothing to do
log.debug("Node " + safeGetJCRPath() + " is already checked in.");
return getBaseVersion();
}
}
/**
* @see Node#checkout()
*/
public void checkout() throws UnsupportedRepositoryOperationException, LockException, RepositoryException {
checkIsVersionable();
checkIsLocked();
if (!isCheckedOut()) {
if (session.isSupportedOption(Repository.OPTION_ACTIVITIES_SUPPORTED)) {
NodeImpl activity = (NodeImpl) session.getWorkspace().getVersionManager().getActivity();
NodeId activityId = (activity == null) ? null : activity.getNodeState().getNodeId();
session.getVersionStateManager().checkout(getNodeState(), activityId);
} else {
session.getVersionStateManager().checkout(getNodeState());
}
} else {
// nothing to do
log.debug("Node " + safeGetJCRPath() + " is already checked out.");
}
}
Version checkpoint() throws RepositoryException {
checkIsVersionable();
checkHasPendingChanges();
checkIsLocked();
if (!isCheckedOut()) {
checkout();
return getBaseVersion();
} else {
NodeEntry newVersion;
if (session.isSupportedOption(Repository.OPTION_ACTIVITIES_SUPPORTED)) {
NodeImpl activity = (NodeImpl) session.getWorkspace().getVersionManager().getActivity();
NodeId activityId = (activity == null) ? null : activity.getNodeState().getNodeId();
newVersion = session.getVersionStateManager().checkpoint(getNodeState(), activityId);
} else {
newVersion = session.getVersionStateManager().checkpoint(getNodeState());
}
return (Version) getItemManager().getItem(newVersion);
}
}
/**
* @see Node#doneMerge(Version)
*/
public void doneMerge(Version version) throws VersionException, InvalidItemStateException, UnsupportedRepositoryOperationException, RepositoryException {
resolveMergeConflict(version, true);
}
/**
* @see Node#cancelMerge(Version)
*/
public void cancelMerge(Version version) throws VersionException, InvalidItemStateException, UnsupportedRepositoryOperationException, RepositoryException {
resolveMergeConflict(version, false);
}
/**
* Internal method covering both {@link #doneMerge(Version)} and {@link #cancelMerge(Version)}.
*
* @param version
* @param done
* @throws VersionException
* @throws InvalidItemStateException
* @throws UnsupportedRepositoryOperationException
* @throws RepositoryException
*/
private void resolveMergeConflict(Version version, boolean done) throws VersionException, InvalidItemStateException, UnsupportedRepositoryOperationException, RepositoryException {
checkIsVersionable();
checkHasPendingChanges();
checkIsLocked();
// check if checked out
if (!isCheckedOut()) {
String msg = "Unable to resolve merge conflict. Node is checked-in: " + safeGetJCRPath();
log.error(msg);
throw new VersionException(msg);
}
// check if version is in mergeFailed list
boolean isConflicting = false;
if (hasProperty(NameConstants.JCR_MERGEFAILED)) {
Value[] vals = getProperty(NameConstants.JCR_MERGEFAILED).getValues();
for (int i = 0; i < vals.length && !isConflicting; i++) {
isConflicting = vals[i].getString().equals(version.getUUID());
}
}
if (!isConflicting) {
String msg = "Unable to resolve merge conflict. Specified version is not in jcr:mergeFailed property: " + safeGetJCRPath();
log.error(msg);
throw new VersionException(msg);
}
NodeState versionState = session.getVersionState(version);
session.getVersionStateManager().resolveMergeConflict(getNodeState(), versionState, done);
}
/**
* @see Node#update(String)
*/
public void update(String srcWorkspaceName) throws NoSuchWorkspaceException, AccessDeniedException, LockException, InvalidItemStateException, RepositoryException {
checkIsWritable();
checkSessionHasPendingChanges();
// if same workspace, ignore
if (session.getWorkspace().getName().equals(srcWorkspaceName)) {
return;
}
// test if the corresponding node exists in the src-workspace which includes
// a check if the specified source workspace is accessible
try {
getCorrespondingNodePath(srcWorkspaceName);
} catch (ItemNotFoundException e) {
// no corresponding node exists -> method has not effect
return;
}
Operation op = Update.create(getNodeState(), srcWorkspaceName);
((WorkspaceImpl)session.getWorkspace()).getUpdatableItemStateManager().execute(op);
}
/**
* @see Node#merge(String, boolean)
*/
public NodeIterator merge(String srcWorkspace, boolean bestEffort) throws NoSuchWorkspaceException, AccessDeniedException, VersionException, LockException, InvalidItemStateException, RepositoryException {
return session.getWorkspace().getVersionManager().merge(getPath(), srcWorkspace, bestEffort);
}
/**
* TODO: Issue 728 of the pfd... this method is a leftover and will be removed in the final version.
* -> change to package protected then
*/
public NodeIterator merge(String srcWorkspace, boolean bestEffort, boolean isShallow) throws RepositoryException {
return session.getWorkspace().getVersionManager().merge(getPath(), srcWorkspace, bestEffort, isShallow);
}
/**
* @see Node#getCorrespondingNodePath(String)
*/
public String getCorrespondingNodePath(String workspaceName) throws ItemNotFoundException, NoSuchWorkspaceException, AccessDeniedException, RepositoryException {
checkStatus();
SessionImpl srcSession = null;
try {
// create session on other workspace for current subject
// (may throw NoSuchWorkspaceException and AccessDeniedException)
srcSession = session.switchWorkspace(workspaceName);
// search nearest ancestor that is referenceable
NodeImpl referenceableNode = this;
while (referenceableNode.getDepth() != Path.ROOT_DEPTH
&& !referenceableNode.isNodeType(NameConstants.MIX_REFERENCEABLE)) {
referenceableNode = (NodeImpl) referenceableNode.getParent();
}
// if root is common ancestor, corresponding path is same as ours
// otherwise access referenceable ancestor and calculate correspond. path.
String correspondingPath;
if (referenceableNode.getDepth() == Path.ROOT_DEPTH) {
if (!srcSession.getItemManager().nodeExists(getQPath())) {
throw new ItemNotFoundException("No corresponding path found in workspace " + workspaceName + "(" + safeGetJCRPath() + ")");
} else {
correspondingPath = getPath();
}
} else {
// get corresponding ancestor
Node correspNode = srcSession.getNodeByUUID(referenceableNode.getUUID());
// path of m2 found, if m1 == n1
if (referenceableNode == this) {
correspondingPath = correspNode.getPath();
} else {
Path p = referenceableNode.getQPath().computeRelativePath(getQPath());
// use prefix mappings of srcSession
String relPath = session.getPathResolver().getJCRPath(p);
if (!correspNode.hasNode(relPath)) {
throw new ItemNotFoundException("No corresponding path found in workspace " + workspaceName + "(" + safeGetJCRPath() + ")");
} else {
correspondingPath = correspNode.getNode(relPath).getPath();
}
}
}
return correspondingPath;
} finally {
if (srcSession != null) {
// we don't need the other session anymore, logout
srcSession.logout();
}
}
}
/**
* @see Node#isCheckedOut()
*/
public boolean isCheckedOut() throws RepositoryException {
checkStatus();
// shortcut: if state is new, its ancestor must be checkout
if (isNew()) {
return true;
}
return session.getVersionStateManager().isCheckedOut(getNodeState());
}
/**
* @see Node#restore(String, boolean)
*/
public void restore(String versionName, boolean removeExisting) throws VersionException, ItemExistsException, UnsupportedRepositoryOperationException, LockException, InvalidItemStateException, RepositoryException {
checkSessionHasPendingChanges();
// check for version-enabled and lock are performed with subsequent calls.
Version v = getVersionHistory().getVersion(versionName);
restore(this, null, v, removeExisting);
}
/**
* @see Node#restore(Version, boolean)
*/
public void restore(Version version, boolean removeExisting) throws VersionException, ItemExistsException, UnsupportedRepositoryOperationException, LockException, RepositoryException {
checkSessionHasPendingChanges();
restore(this, null, version, removeExisting);
}
/**
* @see Node#restore(Version, String, boolean)
*/
public void restore(Version version, String relPath, boolean removeExisting) throws PathNotFoundException, ItemExistsException, VersionException, ConstraintViolationException, UnsupportedRepositoryOperationException, LockException, InvalidItemStateException, RepositoryException {
checkSessionHasPendingChanges();
// additional checks are performed with subsequent calls.
if (hasNode(relPath)) {
// node at 'relPath' exists -> call restore on the target Node
getNode(relPath).restore(version, removeExisting);
} else {
// node at 'relPath' does not yet exist -> build the NodeId
Path nPath = getPath(relPath);
Path parentPath = nPath.getAncestor(1);
ItemManager itemMgr = getItemManager();
if (itemMgr.nodeExists(parentPath)) {
Node parent = itemMgr.getNode(parentPath);
Path relQPath = parentPath.computeRelativePath(nPath);
NodeImpl parentNode = ((NodeImpl)parent);
// call the restore
restore(parentNode, relQPath, version, removeExisting);
} else if (itemMgr.propertyExists(parentPath)) {
// the item at parentParentPath is Property
throw new ConstraintViolationException("Cannot restore to a parent presenting a property (relative path = '" + relPath + "'");
} else {
// although the node itself must not exist, is direct ancestor must.
throw new PathNotFoundException("Cannot restore to relative path '" + relPath + ": Ancestor does not exist.");
}
}
}
/**
* @see Node#restoreByLabel(String, boolean)
*/
public void restoreByLabel(String versionLabel, boolean removeExisting) throws VersionException, ItemExistsException, UnsupportedRepositoryOperationException, LockException, InvalidItemStateException, RepositoryException {
checkSessionHasPendingChanges();
// check for version-enabled and lock are performed with subsequent calls.
Version v = getVersionHistory().getVersionByLabel(versionLabel);
if (v == null) {
throw new VersionException("No version for label " + versionLabel + " found.");
}
restore(this, null, v, removeExisting);
}
/**
* Common internal restore method for the various Node#restore calls.
*
* @param targetNode
* @param relQPath
* @param version
* @param removeExisting
* @throws PathNotFoundException
* @throws ItemExistsException
* @throws VersionException
* @throws ConstraintViolationException
* @throws UnsupportedRepositoryOperationException
* @throws LockException
* @throws InvalidItemStateException
* @throws RepositoryException
*/
private void restore(NodeImpl targetNode, Path relQPath, Version version, boolean removeExisting) throws PathNotFoundException, ItemExistsException, VersionException, ConstraintViolationException, UnsupportedRepositoryOperationException, LockException, InvalidItemStateException, RepositoryException {
if (relQPath == null) {
/* restore target already exists. */
// target must be versionable
targetNode.checkIsVersionable();
VersionHistory vH = targetNode.getVersionHistory();
// version must be a version of the target node
if (!vH.isSame(version.getContainingHistory())) {
throw new VersionException("Version " + version + " does not correspond to the restore target.");
}
// version must not be the root version
if (vH.getRootVersion().isSame(version)) {
throw new VersionException("Attempt to restore root version.");
}
targetNode.checkIsWritable();
targetNode.checkIsLocked();
} else {
/* If no node exists at relPath then a VersionException is thrown if
the parent node is not checked out. */
if (!targetNode.isCheckedOut()) {
throw new VersionException("Parent " + targetNode.safeGetJCRPath()
+ " for non-existing restore target '"
+ LogUtil.safeGetJCRPath(relQPath, session.getPathResolver())
+ "' must be checked out.");
}
targetNode.checkIsLocked();
// NOTE: check for nodetype constraint violation is left to the 'server'
}
NodeState versionState = session.getVersionState(version);
session.getVersionStateManager().restore(targetNode.getNodeState(), relQPath, versionState, removeExisting);
}
/**
* @see Node#getVersionHistory()
*/
public VersionHistory getVersionHistory() throws UnsupportedRepositoryOperationException, RepositoryException {
checkIsVersionable();
return (VersionHistory) getProperty(NameConstants.JCR_VERSIONHISTORY).getNode();
}
/**
* @see Node#getBaseVersion()
*/
public Version getBaseVersion() throws UnsupportedRepositoryOperationException, RepositoryException {
checkIsVersionable();
return (Version) getProperty(NameConstants.JCR_BASEVERSION).getNode();
}
/**
* @see Node#lock(boolean, boolean)
*/
public Lock lock(boolean isDeep, boolean isSessionScoped) throws UnsupportedRepositoryOperationException, LockException, AccessDeniedException, InvalidItemStateException, RepositoryException {
return lock(isDeep, isSessionScoped, Long.MAX_VALUE, null);
}
public Lock lock(boolean isDeep, boolean isSessionScoped, long timeoutHint, String ownerHint) throws UnsupportedRepositoryOperationException, LockException, AccessDeniedException, InvalidItemStateException, RepositoryException {
checkIsLockable();
checkHasPendingChanges();
return session.getLockStateManager().lock(getNodeState(), isDeep, isSessionScoped, timeoutHint, ownerHint);
}
/**
* @see Node#getLock()
*/
public Lock getLock() throws UnsupportedRepositoryOperationException, LockException, AccessDeniedException, RepositoryException {
// lock can be inherited from a parent > do not check for node being lockable.
checkStatus();
return session.getLockStateManager().getLock(getNodeState());
}
/**
* @see javax.jcr.Node#unlock()
*/
public void unlock() throws UnsupportedRepositoryOperationException, LockException, AccessDeniedException, InvalidItemStateException, RepositoryException {
checkIsLockable();
checkHasPendingChanges();
session.getLockStateManager().unlock(getNodeState());
}
/**
* @see javax.jcr.Node#holdsLock()
*/
public boolean holdsLock() throws RepositoryException {
// lock can be inherited from a parent > do not check for node being lockable.
checkStatus();
if (isNew() || !isNodeType(NameConstants.MIX_LOCKABLE)) {
// a node that is new or not lockable never holds a lock
return false;
} else {
LockStateManager lMgr = session.getLockStateManager();
return (lMgr.isLocked(getNodeState()) && lMgr.getLock(getNodeState()).getNode().isSame(this));
}
}
/**
* @see javax.jcr.Node#isLocked()
*/
public boolean isLocked() throws RepositoryException {
// lock can be inherited from a parent > do not check for node being lockable.
checkStatus();
return session.getLockStateManager().isLocked(getNodeState());
}
/**
* @see javax.jcr.Node#followLifecycleTransition(String)
*/
public void followLifecycleTransition(String transition) throws RepositoryException {
session.checkSupportedOption(Repository.OPTION_LIFECYCLE_SUPPORTED);
// TODO: implementation missing
throw new UnsupportedRepositoryOperationException("JCR-1104");
}
/**
* @see javax.jcr.Node#getAllowedLifecycleTransistions()
*/
public String[] getAllowedLifecycleTransistions() throws RepositoryException {
session.checkSupportedOption(Repository.OPTION_LIFECYCLE_SUPPORTED);
// TODO: implementation missing
throw new UnsupportedRepositoryOperationException("JCR-1104");
}
/**
* @see javax.jcr.Node#getSharedSet()
*/
public NodeIterator getSharedSet() throws RepositoryException {
// TODO: implementation missing
throw new UnsupportedRepositoryOperationException("JCR-1104");
}
/**
* @see javax.jcr.Node#removeShare()
*/
public void removeShare() throws RepositoryException {
// TODO: implementation missing
throw new UnsupportedRepositoryOperationException("JCR-1104");
}
/**
* @see javax.jcr.Node#removeSharedSet()
*/
public void removeSharedSet() throws RepositoryException {
throw new UnsupportedRepositoryOperationException("JCR-1104");
}
//--------------------------------------------------------< public impl >---
/**
*
* @param qName
* @return
* @throws RepositoryException
*/
boolean isNodeType(Name qName) throws RepositoryException {
// first do trivial checks without using type hierarchy
if (qName.equals(getPrimaryNodeTypeName())) {
return true;
}
// check if contained in mixin types
for (Name mixin : getNodeState().getMixinTypeNames()) {
if (mixin.equals(qName)) {
return true;
}
}
// NEW nodes with inherited-mixins -> mixin not yet active
if (getNodeState().getStatus() == Status.NEW &&
session.getNodeTypeManager().getNodeType(qName).isMixin()) {
return false;
}
// check effective node type
EffectiveNodeType effnt = session.getEffectiveNodeTypeProvider().getEffectiveNodeType(getNodeState().getNodeTypeNames());
return effnt.includesNodeType(qName);
}
//-----------------------------------------------------------< ItemImpl >---
/**
* @see ItemImpl#getName()
*/
@Override
Name getQName() throws RepositoryException {
if (getNodeState().isRoot()) {
// shortcut. the given state represents the root or an orphaned node
return NameConstants.ROOT;
}
return getNodeState().getName();
}
//------------------------------------------------------< check methods >---
/**
* Checks if this nodes session has pending changes.
*
* @throws InvalidItemStateException if this nodes session has pending changes
* @throws RepositoryException
*/
private void checkSessionHasPendingChanges() throws RepositoryException {
session.checkHasPendingChanges();
}
/**
*
* @throws InvalidItemStateException
* @throws RepositoryException
*/
private void checkHasPendingChanges() throws InvalidItemStateException, RepositoryException {
if (hasPendingChanges()) {
String msg = "Node has pending changes: " + getPath();
log.debug(msg);
throw new InvalidItemStateException(msg);
}
}
/**
* @return true if this Node
is modified or new.
*/
private boolean hasPendingChanges() {
return isModified() || isNew();
}
/**
* Checks if this node is lockable, i.e. has 'mix:lockable'.
*
* @throws UnsupportedRepositoryOperationException if this node is not lockable.
* @throws RepositoryException if another error occurs.
*/
private void checkIsLockable() throws UnsupportedRepositoryOperationException, RepositoryException {
checkStatus();
if (!isNodeType(NameConstants.MIX_LOCKABLE)) {
String msg = "Unable to perform locking operation on non-lockable node: " + getPath();
log.debug(msg);
throw new LockException(msg);
}
}
/**
* Check whether this node is locked by somebody else.
*
* @throws LockException if this node is locked by somebody else.
* @throws RepositoryException if some other error occurs.
*/
void checkIsLocked() throws LockException, RepositoryException {
if (isNew()) {
// if this node is new, no checks must be performed.
return;
}
// perform check
session.getLockStateManager().checkLock(getNodeState());
}
/**
* Check if this node is versionable.
*
* @throws UnsupportedRepositoryOperationException
* @throws RepositoryException
*/
private void checkIsVersionable() throws UnsupportedRepositoryOperationException, RepositoryException {
checkStatus();
if (!isNodeType(NameConstants.MIX_VERSIONABLE)) {
String msg = "Unable to perform versioning operation on non versionable node: " + getPath();
log.debug(msg);
throw new UnsupportedRepositoryOperationException(msg);
}
}
//---------------------------------------------< private implementation >---
/**
* Create a new NodeState
and subsequently retrieves the
* corresponding Node
object.
*
* @param nodeName name of the new node
* @param nodeTypeName name of the new node's node type or null
* if it should be determined automatically
* @return the newly added node
* @throws ItemExistsException
* @throws NoSuchNodeTypeException
* @throws VersionException
* @throws ConstraintViolationException
* @throws LockException
* @throws RepositoryException
*/
private synchronized Node createNode(Name nodeName, Name nodeTypeName)
throws ItemExistsException, NoSuchNodeTypeException, VersionException,
ConstraintViolationException, LockException, RepositoryException {
QNodeDefinition definition = session.getItemDefinitionProvider().getQNodeDefinition(getNodeState().getAllNodeTypeNames(), nodeName, nodeTypeName);
if (nodeTypeName == null) {
// use default node type
nodeTypeName = definition.getDefaultPrimaryType();
}
// validation check are performed by item state manager
// NOTE: uuid is generated while creating new state.
Operation an = AddNode.create(getNodeState(), nodeName, nodeTypeName, null);
session.getSessionItemStateManager().execute(an);
// finally retrieve the new node
List addedStates = ((AddNode) an).getAddedStates();
ItemState nState = addedStates.get(0);
return (Node) getItemManager().getItem(nState.getHierarchyEntry());
}
// TODO: protected due to usage within VersionImpl, VersionHistoryImpl (check for alternatives)
/**
*
* @param nodeName
* @param index
* @return
* @throws PathNotFoundException
* @throws RepositoryException
*/
protected Node getNode(Name nodeName, int index) throws PathNotFoundException, RepositoryException {
checkStatus();
try {
NodeEntry nEntry = getNodeEntry().getNodeEntry(nodeName, index);
if (nEntry == null) {
throw new PathNotFoundException(LogUtil.saveGetJCRName(nodeName, session.getNameResolver()));
}
return (Node) getItemManager().getItem(nEntry);
} catch (AccessDeniedException e) {
throw new PathNotFoundException(LogUtil.saveGetJCRName(nodeName, session.getNameResolver()));
}
}
/**
*
* @param qName
* @return
* @throws PathNotFoundException
* @throws RepositoryException
*/
// TODO: protected due to usage within VersionImpl, VersionHistoryImpl (check for alternatives)
protected Property getProperty(Name qName) throws PathNotFoundException, RepositoryException {
checkStatus();
try {
PropertyEntry pEntry = getNodeEntry().getPropertyEntry(qName, true);
if (pEntry == null) {
throw new PathNotFoundException(LogUtil.saveGetJCRName(qName, session.getNameResolver()));
}
return (Property) getItemManager().getItem(pEntry);
} catch (AccessDeniedException e) {
throw new PathNotFoundException(LogUtil.saveGetJCRName(qName, session.getNameResolver()));
}
}
/**
* Create a new single valued property
*
* @param qName
* @param type
* @param value
* @return
* @throws ConstraintViolationException if no applicable property definition
* could be found.
* @throws RepositoryException if another error occurs.
*/
private Property createProperty(Name qName, Value value, int type)
throws ConstraintViolationException, RepositoryException {
QPropertyDefinition def = getApplicablePropertyDefinition(qName, type, false);
int targetType = def.getRequiredType();
if (targetType == PropertyType.UNDEFINED) {
targetType = type;
}
QValue qvs;
if (targetType == PropertyType.UNDEFINED) {
qvs = ValueFormat.getQValue(value, session.getNamePathResolver(), session.getQValueFactory());
targetType = qvs.getType();
} else {
Value targetValue = ValueHelper.convert(value, targetType, session.getValueFactory());
qvs = ValueFormat.getQValue(targetValue, session.getNamePathResolver(), session.getQValueFactory());
}
return createProperty(qName, targetType, def, new QValue[] {qvs});
}
/**
* Create a new multi valued property
*
* @param qName
* @param type
* @param values
* @return
* @throws ConstraintViolationException
* @throws RepositoryException
*/
private Property createProperty(Name qName, Value[] values, int type)
throws ConstraintViolationException, RepositoryException {
QPropertyDefinition def = getApplicablePropertyDefinition(qName, type, true);
int targetType = def.getRequiredType();
// make sure, the final type is not set to undefined
if (targetType == PropertyType.UNDEFINED) {
if (type == PropertyType.UNDEFINED) {
// try to retrieve type from the values array
if (values.length > 0) {
for (Value value : values) {
if (value != null) {
targetType = value.getType();
break;
}
}
}
if (targetType == PropertyType.UNDEFINED) {
// fallback
targetType = PropertyType.STRING;
}
} else {
targetType = type;
}
}
Value[] targetValues = ValueHelper.convert(values, targetType, session.getValueFactory());
QValue[] qvs = ValueFormat.getQValues(targetValues, session.getNamePathResolver(), session.getQValueFactory());
return createProperty(qName, targetType, def, qvs);
}
/**
*
* @param qName
* @param type
* @param def
* @param qvs
* @return
* @throws PathNotFoundException
* @throws ConstraintViolationException
* @throws RepositoryException
*/
private Property createProperty(Name qName, int type, QPropertyDefinition def,
QValue[] qvs)
throws ConstraintViolationException, RepositoryException {
Operation op = AddProperty.create(getNodeState(), qName, type, def, qvs);
session.getSessionItemStateManager().execute(op);
return getProperty(qName);
}
/**
*
* @param jcrName
* @return
* @throws RepositoryException
*/
private Name getQName(String jcrName) throws RepositoryException {
Name qName;
try {
qName = session.getNameResolver().getQName(jcrName);
} catch (NameException upe) {
throw new RepositoryException("invalid name: "+ jcrName, upe);
}
return qName;
}
/**
* @return the primary node type name.
*/
private Name getPrimaryNodeTypeName() {
return getNodeState().getNodeTypeName();
}
/**
*
* @param name
* @param weak
* @return
* @throws RepositoryException
*/
private LazyItemIterator getReferences(String name, boolean weak) throws RepositoryException {
checkStatus();
Name propName = (name == null) ? null : getQName(name);
Iterator itr = getNodeState().getNodeReferences(propName, weak);
return new LazyItemIterator(getItemManager(), session.getHierarchyManager(), itr);
}
/**
*
* @param mixinName
* @return
* @throws NoSuchNodeTypeException
* @throws ConstraintViolationException
*/
private boolean canAddMixin(Name mixinName) throws NoSuchNodeTypeException,
ConstraintViolationException {
NodeTypeManagerImpl ntMgr = session.getNodeTypeManager();
// first check characteristics of each mixin
NodeType mixin = ntMgr.getNodeType(mixinName);
if (!mixin.isMixin()) {
log.error(mixin.getName() + ": not a mixin node type");
return false;
}
// get list of existing nodetypes
Name[] existingNts = getNodeState().getNodeTypeNames();
// build effective node type representing primary type including existing mixins
EffectiveNodeType entExisting = session.getEffectiveNodeTypeProvider().getEffectiveNodeType(existingNts);
// check if the base type supports adding this mixin
if (!entExisting.supportsMixin(mixinName)) {
log.debug(mixin.getName() + ": not supported on node type " + getPrimaryNodeTypeName());
return false;
}
// second, build new effective node type for nts including the new mixin
// types, detecting eventual incompatibilities
Name[] resultingNts = new Name[existingNts.length + 1];
System.arraycopy(existingNts, 0, resultingNts, 0, existingNts.length);
resultingNts[existingNts.length] = mixinName;
session.getEffectiveNodeTypeProvider().getEffectiveNodeType(resultingNts);
// all validations succeeded: return true
return true;
}
/**
* @return NodeState
of this Node
*/
private NodeState getNodeState() {
return (NodeState) getItemState();
}
/**
* @return NodeEntry
of this Node
*/
private NodeEntry getNodeEntry() {
return (NodeEntry) getItemState().getHierarchyEntry();
}
/**
*
* @param relativePath
* @return
* @throws RepositoryException
*/
private Path getReorderPath(String relativePath) throws RepositoryException {
try {
Path p = session.getPathResolver().getQPath(relativePath);
if (p.isAbsolute() || p.getLength() != 1 || p.getDepth() != 1) {
throw new RepositoryException("Invalid relative path: " + relativePath);
}
return p;
} catch (NameException e) {
String msg = "Invalid relative path: " + relativePath;
log.debug(msg);
throw new RepositoryException(msg, e);
}
}
/**
* @param relativeJcrPath
* @return Path object for the specified relative JCR path string.
* @throws RepositoryException
*/
private Path getPath(String relativeJcrPath) throws RepositoryException {
try {
Path p = session.getPathResolver().getQPath(relativeJcrPath);
return getPath(p);
} catch (NameException e) {
String msg = "Invalid relative path: " + relativeJcrPath;
log.debug(msg);
throw new RepositoryException(msg, e);
}
}
/**
* @param relativePath
* @return normalized absolute path calculated from the given relative
* path and the path of this node.
* @throws RepositoryException
*/
private Path getPath(Path relativePath) throws RepositoryException {
// shortcut
if (relativePath.getLength() == 1 && relativePath.denotesCurrent()) {
return getQPath();
}
return session.getPathFactory().create(getQPath(), relativePath, true);
}
/**
* Returns the NodeEntry
at relPath
or
* null
if no node exists at relPath
.
*
* Note that access rights are not checked.
*
* @param relPath relative path of a (possible) node.
* @return the HierarchyEntry of the node at relPath
or
* null
if no node exists at relPath
.
* @throws RepositoryException if relPath
is not a valid
* relative path.
*/
private NodeEntry resolveRelativeNodePath(String relPath) throws RepositoryException {
NodeEntry targetEntry = null;
try {
Path rp = session.getPathResolver().getQPath(relPath);
// shortcut
if (rp.getLength() == 1) {
if (rp.denotesCurrent()) {
targetEntry = getNodeEntry();
} else if (rp.denotesParent()) {
targetEntry = getNodeEntry().getParent();
} else {
// try to get child entry + force loading of not known yet
targetEntry = getNodeEntry().getNodeEntry(
rp.getName(), rp.getNormalizedIndex(), true);
}
} else {
// rp length > 1
Path p = getPath(rp);
targetEntry = session.getHierarchyManager().getNodeEntry(p.getCanonicalPath());
}
} catch (PathNotFoundException e) {
// item does not exist -> ignore and return null
} catch (NameException e) {
String msg = "Invalid relative path: " + relPath;
log.debug(msg);
throw new RepositoryException(msg, e);
}
return targetEntry;
}
/**
* Returns the id of the property at relPath
or null
* if no property exists at relPath
.
*
* Note that access rights are not checked.
*
* @param relPath relative path of a (possible) property
* @return the PropertyEntry of the property at relPath
or
* null
if no property exists at relPath
* @throws RepositoryException if relPath
is not a valid
* relative path
*/
private PropertyEntry resolveRelativePropertyPath(String relPath) throws RepositoryException {
PropertyEntry targetEntry = null;
try {
Path rp = session.getPathResolver().getQPath(relPath);
if (rp.getLength() == 1 && rp.denotesName()) {
// a single path element must always denote a name. '.' and '..'
// will never point to a property. If the NodeEntry does not
// contain such a property entry, the targetEntry is 'null;
Name propName = rp.getName();
// check if property entry exists
targetEntry = getNodeEntry().getPropertyEntry(propName, true);
} else {
// build and resolve absolute path
Path p = getPath(rp).getCanonicalPath();
try {
targetEntry = session.getHierarchyManager().getPropertyEntry(p);
} catch (PathNotFoundException e) {
// ignore -> return null;
}
}
} catch (NameException e) {
String msg = "failed to resolve property path " + relPath + " relative to " + safeGetJCRPath();
log.debug(msg);
throw new RepositoryException(msg, e);
}
return targetEntry;
}
/**
* Returns the applicable property definition for a property with the
* specified name and type.
*
* @param propertyName
* @param type
* @param multiValued
* @return
* @throws ConstraintViolationException if no applicable property definition
* could be found
* @throws RepositoryException if another error occurs
*/
private QPropertyDefinition getApplicablePropertyDefinition(Name propertyName,
int type,
boolean multiValued)
throws ConstraintViolationException, RepositoryException {
return session.getItemDefinitionProvider().getQPropertyDefinition(getNodeState().getAllNodeTypeNames(), propertyName, type, multiValued);
}
}