All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.apache.jackrabbit.commons.AbstractNode 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.commons;

import java.io.InputStream;
import java.util.Calendar;

import javax.jcr.Item;
import javax.jcr.ItemNotFoundException;
import javax.jcr.ItemVisitor;
import javax.jcr.Node;
import javax.jcr.PathNotFoundException;
import javax.jcr.Property;
import javax.jcr.PropertyIterator;
import javax.jcr.RepositoryException;
import javax.jcr.Session;
import javax.jcr.UnsupportedRepositoryOperationException;
import javax.jcr.Value;
import javax.jcr.ValueFactory;
import javax.jcr.lock.LockException;
import javax.jcr.nodetype.NodeType;
import javax.jcr.nodetype.NodeTypeManager;
import javax.jcr.version.Version;
import javax.jcr.version.VersionHistory;

/**
 * Abstract base class for implementing the JCR {@link Node} interface.
 * 

* {@link Item} methods without a default implementation: *

    *
  • {@link Item#accept(javax.jcr.ItemVisitor)}
  • *
  • {@link Item#getName()}
  • *
  • {@link Item#getParent()}
  • *
  • {@link Item#getSession()}
  • *
  • {@link Item#isModified()}
  • *
  • {@link Item#isNew()}
  • *
  • {@link Item#isSame(Item)}
  • *
  • {@link Item#refresh(boolean)}
  • *
  • {@link Item#remove()}
  • *
  • {@link Item#save()}
  • *
*

* {@link Node} methods without a default implementation: *

    *
  • {@link Node#addMixin(String)}
  • *
  • {@link Node#addNode(String)}
  • *
  • {@link Node#addNode(String, String)}
  • *
  • {@link Node#canAddMixin(String)}
  • *
  • {@link Node#cancelMerge(Version)}
  • *
  • {@link Node#checkin()}
  • *
  • {@link Node#checkout()}
  • *
  • {@link Node#doneMerge(Version)}
  • *
  • {@link Node#getBaseVersion()}
  • *
  • {@link Node#getCorrespondingNodePath(String)}
  • *
  • {@link Node#getDefinition()}
  • *
  • {@link Node#getIndex()}
  • *
  • {@link Node#getLock()}
  • *
  • {@link Node#getNode(String)}
  • *
  • {@link Node#getNodes()}
  • *
  • {@link Node#getNodes(String)}
  • *
  • {@link Node#getPrimaryItem()}
  • *
  • {@link Node#getProperties()}
  • *
  • {@link Node#getProperties(String)}
  • *
  • {@link Node#getReferences()}
  • *
  • {@link Node#lock(boolean, boolean)}
  • *
  • {@link Node#merge(String, boolean)}
  • *
  • {@link Node#orderBefore(String, String)}
  • *
  • {@link Node#removeMixin(String)}
  • *
  • {@link Node#restore(Version, String, boolean)}
  • *
  • {@link Node#setProperty(String, Value)}
  • *
  • {@link Node#setProperty(String, Value[])}
  • *
  • {@link Node#unlock()}
  • *
  • {@link Node#update(String)}
  • *
*/ public abstract class AbstractNode extends AbstractItem implements Node { //----------------------------------------------------------------< Item > /** * Accepts the given item visitor. *

* The default implementation calls {@link ItemVisitor#visit(Node)} on * the given visitor with this node as the argument. * * @param visitor item visitor * @throws RepositoryException if an error occurs */ public void accept(ItemVisitor visitor) throws RepositoryException { visitor.visit(this); } /** * Returns the path of this node. *

* The default implementation recursively calls this method on the * parent node and appends the name and optionally the index of this * node to construct the full path. Returns "/" if the parent node is * not available (i.e. this is the root node). * * @return node path * @throws RepositoryException if an error occurs */ public String getPath() throws RepositoryException { try { StringBuffer buffer = new StringBuffer(getParent().getPath()); if (buffer.length() > 1) { buffer.append('/'); } buffer.append(getName()); int index = getIndex(); if (index != 1) { buffer.append('['); buffer.append(index); buffer.append(']'); } return buffer.toString(); } catch (ItemNotFoundException e) { return "/"; } } /** * Returns true. * * @return true */ public boolean isNode() { return true; } //----------------------------------------------------------------< Node > /** * Returns the declared mixin node types of this node. *

* The default implementation uses the values of the * jcr:mixinTypes property to look up the mixin node types * from the {@link NodeTypeManager} of the current workspace. * * @return mixin node types * @throws RepositoryException if an error occurs */ public NodeType[] getMixinNodeTypes() throws RepositoryException { try { NodeTypeManager manager = getSession().getWorkspace().getNodeTypeManager(); Property property = getProperty(getName("jcr:mixinTypes")); Value[] values = property.getValues(); NodeType[] types = new NodeType[values.length]; for (int i = 0; i < values.length; i++) { types[i] = manager.getNodeType(values[i].getString()); } return types; } catch (PathNotFoundException e) { // jcr:mixinTypes does not exist, i.e. no mixin types on this node return new NodeType[0]; } } /** * Returns the primary node type of this node. *

* The default implementation uses the value of the * jcr:primaryType property to look up the primary * node type from the {@link NodeTypeManager} of the current workspace. * * @return primary node type * @throws RepositoryException if an error occurs */ public NodeType getPrimaryNodeType() throws RepositoryException { NodeTypeManager manager = getSession().getWorkspace().getNodeTypeManager(); Property property = getProperty(getName("jcr:primaryType")); return manager.getNodeType(property.getString()); } /** * Returns the property at the given relative path from this node. *

* The default implementation looks up the parent node of the given * relative path and iterates through the properties of that node to * find and return the identified property. * * @param relPath relative path of the property * @return property * @throws PathNotFoundException if the property is not found * @throws RepositoryException if an error occurs */ public Property getProperty(String relPath) throws PathNotFoundException, RepositoryException { // Corner case, remove any "/." self references at the end of the path while (relPath.endsWith("/.")) { relPath = relPath.substring(0, relPath.length() - 2); } // Find the parent node of the identified property Node node = this; int slash = relPath.lastIndexOf('/'); if (slash == 0) { node = getSession().getRootNode(); relPath = relPath.substring(1); } else if (slash > 0) { node = getNode(relPath.substring(0, slash)); relPath = relPath.substring(slash + 1); } // Look for the named property. Must iterate and re-check for the name // since the client could have used an invalid path like "./a|b". PropertyIterator properties = node.getProperties(relPath); while (properties.hasNext()) { Property property = (Property) properties.next(); if (relPath.equals(property.getName())) { return property; } } throw new PathNotFoundException("Property not found: " + relPath); } /** * Returns the UUID of this node. *

* The default implementation checks if this node is referenceable (i.e. of * type mix:referenceable) and returns the contents of the * jcr:uuid property if it is. * * @return node UUID * @throws UnsupportedRepositoryOperationException * if this node is not referenceable * @throws RepositoryException if an error occurs */ public String getUUID() throws UnsupportedRepositoryOperationException, RepositoryException { if (isNodeType(getName("mix:referenceable"))) { return getProperty(getName("jcr:uuid")).getString(); } else { throw new UnsupportedRepositoryOperationException( "This node is not referenceable: " + getPath()); } } /** * Returns the version history of this node. *

* The default implementation returns the containing version history of * the base version of this node. * * @return version history * @throws RepositoryException if an error occurs */ public VersionHistory getVersionHistory() throws RepositoryException { return getBaseVersion().getContainingHistory(); } /** * Checks whether a node at the given relative path exists. *

* The default implementation looks up the node using * {@link Node#getNode(String)} and returns true if * a {@link PathNotFoundException} is not thrown. * * @param relPath relative path * @return true if a node exists at the given path, * false otherwise * @throws RepositoryException if an error occurs */ public boolean hasNode(String relPath) throws RepositoryException { try { getNode(relPath); return true; } catch (PathNotFoundException e) { return false; } } /** * Checks if this node has one or more properties. *

* The default implementation calls {@link Node#getNodes()} and returns * true iff returned iterator has at least one element. * * @return true if this node has child nodes, * false otherwise * @throws RepositoryException if an error occurs */ public boolean hasNodes() throws RepositoryException { return getNodes().hasNext(); } /** * Checks if this node has one or more properties. *

* The default implementation calls {@link Node#getProperties()} and * returns true iff returned iterator has at least one element. *

* Note that in normal circumstances (i.e. no weird access controls) this * method will always return true since all nodes always have * at least the jcr:primaryType property. * * @return true if this node has properties, * false otherwise * @throws RepositoryException if an error occurs */ public boolean hasProperties() throws RepositoryException { return getProperties().hasNext(); } /** * Checks whether a property at the given relative path exists. *

* The default implementation looks up the property using * {@link Node#getProperty(String)} and returns true if * a {@link PathNotFoundException} is not thrown. * * @param relPath relative path * @return true if a property exists at the given path, * false otherwise * @throws RepositoryException if an error occurs */ public boolean hasProperty(String relPath) throws RepositoryException { try { getProperty(relPath); return true; } catch (PathNotFoundException e) { return false; } } /** * Checks if this node holds a lock. *

* The default implementation calls {@link Node#getLock()} and returns * true iff the holding node of the lock is the same as this * node. * * @return true if this node holds a lock, * false otherwise * @throws RepositoryException if an error occurs */ public boolean holdsLock() throws RepositoryException { try { return isSame(getLock().getNode()); } catch (LockException e) { return false; } } /** * Checks whether this node is checked out. *

* The default implementation checks the jcr:isCheckedOut * property if this node is versionable, and recursively calls this method * on the parent node if this node is not versionable. A non-versionable * root node always returns true from this method. * * @return true if this node is checked out, * false otherwise * @throws RepositoryException if an error occurs */ public boolean isCheckedOut() throws RepositoryException { if (isNodeType(getName("jcr:versionable"))) { // This node is versionable, check the jcr:isCheckedOut property return getProperty(getName("jcr:isCheckedOut")).getBoolean(); } else { try { // This node is not versionable, is the parent checked out? return getParent().isCheckedOut(); } catch (ItemNotFoundException e) { // This node is the root node, always checked out return true; } } } /** * Checks if this node is locked. *

* The default implementation calls {@link Node#getLock()} and returns * true iff a {@link LockException} is not thrown. * * @return true if this node is locked, * false otherwise * @throws RepositoryException if an error occurs */ public boolean isLocked() throws RepositoryException { try { getLock(); return true; } catch (LockException e) { return false; } } /** * Checks whether this node is of the given type. *

* The default implementation iterates through the primary and mixin * types and all the supertypes of this node, returning true * if a type with the given name is encountered. Returns false * if none of the types matches. * * @param name type name * @return true if this node is of the given type, * false otherwise * @throws RepositoryException if an error occurs */ public boolean isNodeType(String name) throws RepositoryException { NodeType type = getPrimaryNodeType(); if (name.equals(type.getName())) { return true; } NodeType[] supertypes = type.getSupertypes(); for (int i = 0; i < supertypes.length; i++) { if (name.equals(supertypes[i].getName())) { return true; } } NodeType[] mixins = getMixinNodeTypes(); for (int i = 0; i < mixins.length; i++) { if (name.equals(mixins[i].getName())) { return true; } supertypes = mixins[i].getSupertypes(); for (int j = 0; j < supertypes.length; j++) { if (name.equals(supertypes[j].getName())) { return true; } } } return false; } /** * Restores this node to the version with the given name. *

* The default implement retrieves the named {@link Version} from the * associated {@link VersionHistory} and forwards the call to the * {@link Node#restore(Version, boolean)} method. * * @param versionName version name * @param removeExisting passed through * @throws RepositoryException if an error occurs */ public void restore(String versionName, boolean removeExisting) throws RepositoryException { restore(getVersionHistory().getVersion(versionName), removeExisting); } /** * Restores this node to the given version. *

* The default implementation forwards the call to the * {@link Node#restore(Version, String, boolean)} method using the * relative path ".". * * @param version passed through * @param removeExisting passed through * @throws RepositoryException if an error occurs */ public void restore(Version version, boolean removeExisting) throws RepositoryException { restore(version, ".", removeExisting); } /** * Restores this node to the version with the given label. *

* The default implement retrieves the labeled {@link Version} from the * associated {@link VersionHistory} and forwards the call to the * {@link Node#restore(Version, boolean)} method. * * @param versionLabel version label * @param removeExisting passed through * @throws RepositoryException if an error occurs */ public void restoreByLabel(String versionLabel, boolean removeExisting) throws RepositoryException { restore(getVersionHistory().getVersionByLabel(versionLabel), removeExisting); } /** * Sets the value of the named property. *

* The default implementation uses the {@link ValueFactory} of the * current {@link Session} to create a {@link Value} instances from * the given string values and forwards the call to the * {@link Node#setProperty(String, Value[])} method. * * @param name property name * @param strings string values * @return modified property * @throws RepositoryException if an error occurs */ public Property setProperty(String name, String[] strings) throws RepositoryException { ValueFactory factory = getSession().getValueFactory(); Value[] values = new Value[strings.length]; for (int i = 0; i < strings.length; i++) { values[i] = factory.createValue(strings[i]); } return setProperty(name, values); } /** * Sets the value of the named property. *

* The default implementation uses the {@link ValueFactory} of the * current {@link Session} to create a {@link Value} instance from * the given string value and forwards the call to the * {@link Node#setProperty(String, Value)} method. * * @param name property name * @param value string value * @return modified property * @throws RepositoryException if an error occurs */ public Property setProperty(String name, String value) throws RepositoryException { ValueFactory factory = getSession().getValueFactory(); return setProperty(name, factory.createValue(value)); } /** * Sets the value of the named property. *

* The default implementation uses the {@link ValueFactory} of the * current {@link Session} to create a {@link Value} instance from * the given binary value and forwards the call to the * {@link Node#setProperty(String, Value)} method. * * @param name property name * @param value binary value * @return modified property * @throws RepositoryException if an error occurs */ public Property setProperty(String name, InputStream value) throws RepositoryException { ValueFactory factory = getSession().getValueFactory(); return setProperty(name, factory.createValue(value)); } /** * Sets the value of the named property. *

* The default implementation uses the {@link ValueFactory} of the * current {@link Session} to create a {@link Value} instance from * the given boolean value and forwards the call to the * {@link Node#setProperty(String, Value)} method. * * @param name property name * @param value boolean value * @return modified property * @throws RepositoryException if an error occurs */ public Property setProperty(String name, boolean value) throws RepositoryException { ValueFactory factory = getSession().getValueFactory(); return setProperty(name, factory.createValue(value)); } /** * Sets the value of the named property. *

* The default implementation uses the {@link ValueFactory} of the * current {@link Session} to create a {@link Value} instance from * the given double value and forwards the call to the * {@link Node#setProperty(String, Value)} method. * * @param name property name * @param value double value * @return modified property * @throws RepositoryException if an error occurs */ public Property setProperty(String name, double value) throws RepositoryException { ValueFactory factory = getSession().getValueFactory(); return setProperty(name, factory.createValue(value)); } /** * Sets the value of the named property. *

* The default implementation uses the {@link ValueFactory} of the * current {@link Session} to create a {@link Value} instance from * the given long value and forwards the call to the * {@link Node#setProperty(String, Value)} method. * * @param name property name * @param value long value * @return modified property * @throws RepositoryException if an error occurs */ public Property setProperty(String name, long value) throws RepositoryException { ValueFactory factory = getSession().getValueFactory(); return setProperty(name, factory.createValue(value)); } /** * Sets the value of the named property. *

* The default implementation uses the {@link ValueFactory} of the * current {@link Session} to create a {@link Value} instance from * the given date value and forwards the call to the * {@link Node#setProperty(String, Value)} method. * * @param name property name * @param value date value * @return modified property * @throws RepositoryException if an error occurs */ public Property setProperty(String name, Calendar value) throws RepositoryException { ValueFactory factory = getSession().getValueFactory(); return setProperty(name, factory.createValue(value)); } /** * Sets the value of the named property. *

* The default implementation uses the {@link ValueFactory} of the * current {@link Session} to create a {@link Value} instance from * the given reference value and forwards the call to the * {@link Node#setProperty(String, Value)} method. * * @param name property name * @param value reference value * @return modified property * @throws RepositoryException if an error occurs */ public Property setProperty(String name, Node value) throws RepositoryException { ValueFactory factory = getSession().getValueFactory(); return setProperty(name, factory.createValue(value)); } /** * Sets the value of the named property. *

* The default implementation uses the {@link ValueFactory} of the * current {@link Session} to convert the given value to the given * type and forwards the call to the * {@link Node#setProperty(String, Value)} method. * * @param name property name * @param value property value * @param type property type * @return modified property * @throws RepositoryException if an error occurs */ public Property setProperty(String name, Value value, int type) throws RepositoryException { if (value.getType() != type) { ValueFactory factory = getSession().getValueFactory(); value = factory.createValue(value.getString(), type); } return setProperty(name, value); } /** * Sets the value of the named property. *

* The default implementation uses the {@link ValueFactory} of the * current {@link Session} to convert the given values to the given * type and forwards the call to the * {@link Node#setProperty(String, Value[])} method. * * @param name property name * @param values property values * @param type property type * @return modified property * @throws RepositoryException if an error occurs */ public Property setProperty(String name, Value[] values, int type) throws RepositoryException { ValueFactory factory = getSession().getValueFactory(); Value[] converted = new Value[values.length]; for (int i = 0; i < values.length; i++) { if (values[i].getType() != type) { converted[i] = factory.createValue(values[i].getString(), type); } else { converted[i] = values[i]; } } return setProperty(name, converted); } /** * Sets the value of the named property. *

* The default implementation uses the {@link ValueFactory} of the * current {@link Session} to create {@link Value} instances of the * given type from the given string values and forwards the call to the * {@link Node#setProperty(String, Value[])} method. * * @param name property name * @param strings string values * @param type property type * @return modified property * @throws RepositoryException if an error occurs */ public Property setProperty(String name, String[] strings, int type) throws RepositoryException { ValueFactory factory = getSession().getValueFactory(); Value[] values = new Value[strings.length]; for (int i = 0; i < strings.length; i++) { values[i] = factory.createValue(strings[i], type); } return setProperty(name, values); } /** * Sets the value of the named property. *

* The default implementation uses the {@link ValueFactory} of the * current {@link Session} to create a {@link Value} instance of the * given type from the given string value and forwards the call to the * {@link Node#setProperty(String, Value)} method. * * @param name property name * @param value string value * @param type property type * @return modified property * @throws RepositoryException if an error occurs */ public Property setProperty(String name, String value, int type) throws RepositoryException { ValueFactory factory = getSession().getValueFactory(); return setProperty(name, factory.createValue(value, type)); } //-------------------------------------------------------------< private > /** * Returns the prefixed JCR name for the namespace URI and local name * using the current namespace mappings. * * @param uri namespace URI * @param name namespace-local name * @return prefixed JCR name * @throws RepositoryException if an error occurs */ private String getName(String name) throws RepositoryException { return new NamespaceHelper(getSession()).getJcrName(name); } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy