You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@jackrabbit.apache.org by ju...@apache.org on 2009/09/08 18:09:45 UTC

svn commit: r812570 [4/24] - in /jackrabbit/sandbox/JCR-1456: ./ jackrabbit-api/ jackrabbit-api/src/main/appended-resources/ jackrabbit-api/src/main/appended-resources/META-INF/ jackrabbit-api/src/main/java/org/apache/jackrabbit/api/security/ jackrabbi...

Modified: jackrabbit/sandbox/JCR-1456/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/NodeImpl.java
URL: http://svn.apache.org/viewvc/jackrabbit/sandbox/JCR-1456/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/NodeImpl.java?rev=812570&r1=812569&r2=812570&view=diff
==============================================================================
--- jackrabbit/sandbox/JCR-1456/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/NodeImpl.java (original)
+++ jackrabbit/sandbox/JCR-1456/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/NodeImpl.java Tue Sep  8 16:09:28 2009
@@ -32,11 +32,11 @@
 import javax.jcr.AccessDeniedException;
 import javax.jcr.Binary;
 import javax.jcr.InvalidItemStateException;
+import javax.jcr.InvalidLifecycleTransitionException;
 import javax.jcr.Item;
 import javax.jcr.ItemExistsException;
 import javax.jcr.ItemNotFoundException;
 import javax.jcr.ItemVisitor;
-import javax.jcr.MergeException;
 import javax.jcr.NamespaceException;
 import javax.jcr.NoSuchWorkspaceException;
 import javax.jcr.Node;
@@ -59,13 +59,12 @@
 import javax.jcr.nodetype.PropertyDefinition;
 import javax.jcr.query.Query;
 import javax.jcr.query.QueryResult;
-import javax.jcr.version.OnParentVersionAction;
 import javax.jcr.version.Version;
 import javax.jcr.version.VersionException;
 import javax.jcr.version.VersionHistory;
-import javax.jcr.version.VersionIterator;
 import javax.jcr.version.VersionManager;
 
+import org.apache.jackrabbit.commons.JcrUtils;
 import org.apache.jackrabbit.commons.iterator.NodeIteratorAdapter;
 import org.apache.jackrabbit.commons.iterator.PropertyIteratorAdapter;
 import org.apache.jackrabbit.core.id.ItemId;
@@ -91,15 +90,6 @@
 import org.apache.jackrabbit.core.state.NodeState;
 import org.apache.jackrabbit.core.state.PropertyState;
 import org.apache.jackrabbit.core.value.InternalValue;
-import org.apache.jackrabbit.core.version.DateVersionSelector;
-import org.apache.jackrabbit.core.version.InternalFreeze;
-import org.apache.jackrabbit.core.version.InternalFrozenNode;
-import org.apache.jackrabbit.core.version.InternalFrozenVersionHistory;
-import org.apache.jackrabbit.core.version.InternalVersion;
-import org.apache.jackrabbit.core.version.InternalVersionHistory;
-import org.apache.jackrabbit.core.version.LabelVersionSelector;
-import org.apache.jackrabbit.core.version.VersionImpl;
-import org.apache.jackrabbit.core.version.VersionSelector;
 import org.apache.jackrabbit.spi.Name;
 import org.apache.jackrabbit.spi.Path;
 import org.apache.jackrabbit.spi.commons.conversion.MalformedPathException;
@@ -109,11 +99,16 @@
 import org.apache.jackrabbit.spi.commons.name.PathFactoryImpl;
 import org.apache.jackrabbit.util.ChildrenCollectorFilter;
 import org.apache.jackrabbit.util.ISO9075;
-import org.apache.jackrabbit.uuid.UUID;
 import org.apache.jackrabbit.value.ValueHelper;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+import static org.apache.jackrabbit.spi.commons.name.NameConstants.JCR_ISCHECKEDOUT;
+import static org.apache.jackrabbit.spi.commons.name.NameConstants.JCR_LIFECYCLE_POLICY;
+import static org.apache.jackrabbit.spi.commons.name.NameConstants.JCR_CURRENT_LIFECYCLE_STATE;
+import static org.apache.jackrabbit.spi.commons.name.NameConstants.MIX_LIFECYCLE;
+import static org.apache.jackrabbit.spi.commons.name.NameConstants.MIX_REFERENCEABLE;
+
 /**
  * <code>NodeImpl</code> implements the <code>Node</code> interface.
  */
@@ -152,6 +147,15 @@
     }
 
     /**
+     * Returns the node-state associated with this node.
+     *
+     * @return state associated with this node
+     */
+    NodeState getNodeState() {
+        return data.getNodeState();
+    }
+
+    /**
      * Returns the id of the property at <code>relPath</code> or <code>null</code>
      * if no property exists at <code>relPath</code>.
      * <p/>
@@ -310,95 +314,6 @@
     }
 
     /**
-     * Computes the values of well-known system (i.e. protected) properties.
-     * todo: duplicate code in BatchedItemOperations: consolidate and delegate to NodeTypeInstanceHandler
-     *
-     * @param name
-     * @param def
-     * @return
-     * @throws RepositoryException
-     */
-    protected InternalValue[] computeSystemGeneratedPropertyValues(Name name,
-                                                                   PropertyDefinitionImpl def)
-            throws RepositoryException {
-        InternalValue[] genValues = null;
-
-        /**
-         * todo: need to come up with some callback mechanism for applying system generated values
-         * (e.g. using a NodeTypeInstanceHandler interface)
-         */
-
-        NodeState thisState = data.getNodeState();
-
-        // compute system generated values
-        NodeTypeImpl nt = (NodeTypeImpl) def.getDeclaringNodeType();
-        // TODO JCR-2116: Built-In Node Types; => adapt to JCR 2.0 built-in node types (mix:created, etc)
-        if (nt.getQName().equals(NameConstants.MIX_REFERENCEABLE)) {
-            // mix:referenceable node type
-            if (name.equals(NameConstants.JCR_UUID)) {
-                // jcr:uuid property
-                genValues = new InternalValue[]{
-                        InternalValue.create(thisState.getNodeId().toString())
-                };
-            }
-/*
-       todo consolidate version history creation code (currently in ItemImpl.initVersionHistories)
-       } else if (nt.getQName().equals(MIX_VERSIONABLE)) {
-           // mix:versionable node type
-           VersionHistory hist = session.getVersionManager().getOrCreateVersionHistory(this);
-           if (name.equals(JCR_VERSIONHISTORY)) {
-               // jcr:versionHistory property
-               genValues = new InternalValue[]{InternalValue.create(new UUID(hist.getUUID()))};
-           } else if (name.equals(JCR_BASEVERSION)) {
-               // jcr:baseVersion property
-               genValues = new InternalValue[]{InternalValue.create(new UUID(hist.getRootVersion().getUUID()))};
-           } else if (name.equals(JCR_ISCHECKEDOUT)) {
-               // jcr:isCheckedOut property
-               genValues = new InternalValue[]{InternalValue.create(true)};
-           } else if (name.equals(JCR_PREDECESSORS)) {
-               // jcr:predecessors property
-               genValues = new InternalValue[]{InternalValue.create(new UUID(hist.getRootVersion().getUUID()))};
-           }
-*/
-        } else if (nt.getQName().equals(NameConstants.NT_HIERARCHYNODE)
-                || nt.getQName().equals(NameConstants.MIX_CREATED)) {
-            // nt:hierarchyNode node type
-            if (name.equals(NameConstants.JCR_CREATED)) {
-                // jcr:created property
-                genValues = new InternalValue[]{InternalValue.create(Calendar.getInstance())};
-            }
-        } else if (nt.getQName().equals(NameConstants.NT_RESOURCE)) {
-            // nt:resource node type
-            if (name.equals(NameConstants.JCR_LASTMODIFIED)) {
-                // jcr:lastModified property
-                genValues = new InternalValue[]{InternalValue.create(Calendar.getInstance())};
-            }
-        } else if (nt.getQName().equals(NameConstants.NT_VERSION)) {
-            // nt:version node type
-            if (name.equals(NameConstants.JCR_CREATED)) {
-                // jcr:created property
-                genValues = new InternalValue[]{InternalValue.create(Calendar.getInstance())};
-            }
-        } else if (nt.getQName().equals(NameConstants.NT_BASE)) {
-            // nt:base node type
-            if (name.equals(NameConstants.JCR_PRIMARYTYPE)) {
-                // jcr:primaryType property
-                genValues = new InternalValue[]{InternalValue.create(thisState.getNodeTypeName())};
-            } else if (name.equals(NameConstants.JCR_MIXINTYPES)) {
-                // jcr:mixinTypes property
-                Set<Name> mixins = thisState.getMixinTypeNames();
-                ArrayList<InternalValue> values = new ArrayList<InternalValue>(mixins.size());
-                for (Name n : mixins) {
-                    values.add(InternalValue.create(n));
-                }
-                genValues = values.toArray(new InternalValue[values.size()]);
-            }
-        }
-
-        return genValues;
-    }
-
-    /**
      * @param name
      * @param type
      * @param multiValued
@@ -496,20 +411,21 @@
         // create a new property state
         PropertyState propState;
         try {
+            PropDef propDef = def.unwrap();
             propState =
                     stateMgr.createTransientPropertyState(getNodeId(), name,
                             ItemState.STATUS_NEW);
             propState.setType(type);
-            propState.setMultiValued(def.isMultiple());
-            propState.setDefinitionId(def.unwrap().getId());
+            propState.setMultiValued(propDef.isMultiple());
+            propState.setDefinitionId(propDef.getId());
             // compute system generated values if necessary
-            InternalValue[] genValues =
-                    computeSystemGeneratedPropertyValues(name, def);
-            InternalValue[] defValues = def.unwrap().getDefaultValues();
+            InternalValue[] genValues = session.getNodeTypeInstanceHandler()
+                    .computeSystemGeneratedPropertyValues(data.getNodeState(), propDef);
+            if (genValues == null) {
+                genValues = propDef.getDefaultValues();
+            }
             if (genValues != null) {
                 propState.setValues(genValues);
-            } else if (defValues != null) {
-                propState.setValues(defValues);
             }
         } catch (ItemStateException ise) {
             String msg = "failed to add property " + name + " to " + this;
@@ -717,131 +633,6 @@
         setRemoved();
     }
 
-    protected NodeImpl internalAddNode(String relPath, NodeTypeImpl nodeType)
-            throws ItemExistsException, PathNotFoundException, VersionException,
-            ConstraintViolationException, LockException, RepositoryException {
-        return internalAddNode(relPath, nodeType, null);
-    }
-
-    protected NodeImpl internalAddNode(String relPath, NodeTypeImpl nodeType,
-                                       NodeId id)
-            throws ItemExistsException, PathNotFoundException, VersionException,
-            ConstraintViolationException, LockException, RepositoryException {
-        Path nodePath;
-        Name nodeName;
-        Path parentPath;
-        try {
-            nodePath =
-                PathFactoryImpl.getInstance().create(getPrimaryPath(), session.getQPath(relPath), false)
-                .getCanonicalPath();
-            if (nodePath.getNameElement().getIndex() != 0) {
-                String msg = "illegal subscript specified: " + nodePath;
-                log.debug(msg);
-                throw new RepositoryException(msg);
-            }
-            nodeName = nodePath.getNameElement().getName();
-            parentPath = nodePath.getAncestor(1);
-        } catch (NameException e) {
-            String msg =
-                "failed to resolve path " + relPath + " relative to " + this;
-            log.debug(msg);
-            throw new RepositoryException(msg, e);
-        }
-
-        NodeImpl parentNode;
-        try {
-            Item parent = itemMgr.getItem(parentPath);
-            if (!parent.isNode()) {
-                String msg = "cannot add a node to property " + parentPath;
-                log.debug(msg);
-                throw new ConstraintViolationException(msg);
-            }
-            parentNode = (NodeImpl) parent;
-        } catch (AccessDeniedException ade) {
-            throw new PathNotFoundException(relPath);
-        }
-
-        // make sure that parent node is checked-out and not locked
-        int options = ItemValidator.CHECK_LOCK | ItemValidator.CHECK_VERSIONING;
-        session.getValidator().checkModify(parentNode, options, Permission.NONE);
-
-        // delegate the creation of the child node to the parent node
-        return parentNode.internalAddChildNode(nodeName, nodeType, id);
-    }
-
-    protected NodeImpl internalAddChildNode(Name nodeName,
-                                            NodeTypeImpl nodeType)
-            throws ItemExistsException, ConstraintViolationException,
-            RepositoryException {
-        return internalAddChildNode(nodeName, nodeType, null);
-    }
-
-    protected NodeImpl internalAddChildNode(Name nodeName,
-                                            NodeTypeImpl nodeType, NodeId id)
-            throws ItemExistsException, ConstraintViolationException,
-            RepositoryException {
-        Path nodePath;
-        try {
-            nodePath = PathFactoryImpl.getInstance().create(getPrimaryPath(), nodeName, true);
-        } catch (MalformedPathException e) {
-            // should never happen
-            String msg = "internal error: invalid path " + this;
-            log.debug(msg);
-            throw new RepositoryException(msg, e);
-        }
-
-        Name nodeTypeName = null;
-        if (nodeType != null) {
-            nodeTypeName = nodeType.getQName();
-            if (nodeType.isMixin()) {
-                throw new ConstraintViolationException(session.getJCRName(nodeTypeName) + ": not a primary node type.");
-            }
-            if (nodeType.isAbstract()) {
-                throw new ConstraintViolationException(session.getJCRName(nodeTypeName)  + ": is an abstract node type.");
-            }
-        }
-        NodeDefinitionImpl def;
-        try {
-            def = getApplicableChildNodeDefinition(nodeName, nodeTypeName);
-        } catch (RepositoryException re) {
-            String msg = "no definition found in parent node's node type for new node";
-            log.debug(msg);
-            throw new ConstraintViolationException(msg, re);
-        }
-        if (nodeType == null) {
-            // use default node type
-            nodeType = (NodeTypeImpl) def.getDefaultPrimaryType();
-        } else {
-            // adding a node with explicit specifying the node type name
-            // requires the editing session to have nt_management privilege.
-            session.getAccessManager().checkPermission(nodePath, Permission.NODE_TYPE_MNGMT);
-        }
-
-        // check for name collisions
-        NodeState thisState = data.getNodeState();
-        ChildNodeEntry cne = thisState.getChildNodeEntry(nodeName, 1);
-        if (cne != null) {
-            // there's already a child node entry with that name;
-            // check same-name sibling setting of new node
-            if (!def.allowsSameNameSiblings()) {
-                throw new ItemExistsException(itemMgr.safeGetJCRPath(nodePath));
-            }
-            // check same-name sibling setting of existing node
-            NodeId newId = cne.getId();
-            if (!((NodeImpl) itemMgr.getItem(newId)).getDefinition().allowsSameNameSiblings()) {
-                throw new ItemExistsException(itemMgr.safeGetJCRPath(nodePath));
-            }
-        }
-
-        // check protected flag of parent (i.e. this) node and retention/hold
-        int options = ItemValidator.CHECK_CONSTRAINTS | ItemValidator.CHECK_HOLD
-                | ItemValidator.CHECK_RETENTION;
-        session.getValidator().checkModify(this, options, Permission.NONE);
-
-        // now do create the child node
-        return createChildNode(nodeName, def, nodeType, id);
-    }
-
     private void setMixinTypesProperty(Set<Name> mixinNames) throws RepositoryException {
         NodeState thisState = data.getNodeState();
         // get or create jcr:mixinTypes property
@@ -1043,7 +834,7 @@
         // check state of this instance
         sanityCheck();
 
-        int options = ItemValidator.CHECK_LOCK | ItemValidator.CHECK_VERSIONING
+        int options = ItemValidator.CHECK_LOCK | ItemValidator.CHECK_CHECKED_OUT
                 | ItemValidator.CHECK_CONSTRAINTS | ItemValidator.CHECK_HOLD;
         int permissions = Permission.NODE_TYPE_MNGMT;
         // special handling of mix:(simple)versionable. since adding the mixin alters
@@ -1149,7 +940,7 @@
         // check state of this instance
         sanityCheck();
 
-        int options = ItemValidator.CHECK_LOCK | ItemValidator.CHECK_VERSIONING
+        int options = ItemValidator.CHECK_LOCK | ItemValidator.CHECK_CHECKED_OUT
                 | ItemValidator.CHECK_CONSTRAINTS | ItemValidator.CHECK_HOLD;
         int permissions = Permission.NODE_TYPE_MNGMT;
         session.getValidator().checkModify(this, options, permissions);
@@ -1384,7 +1175,7 @@
     protected void checkSetProperty()
             throws VersionException, LockException, RepositoryException {
         // make sure this node is checked-out and is not locked
-        int options = ItemValidator.CHECK_LOCK | ItemValidator.CHECK_VERSIONING;
+        int options = ItemValidator.CHECK_LOCK | ItemValidator.CHECK_CHECKED_OUT;
         session.getValidator().checkModify(this, options, Permission.NONE);
     }
 
@@ -1628,29 +1419,81 @@
      * @param id           id of the new node or <code>null</code> if a new
      *                     id should be assigned
      * @return the newly added node
-     * @throws ItemExistsException
-     * @throws NoSuchNodeTypeException
-     * @throws VersionException
-     * @throws ConstraintViolationException
-     * @throws LockException
-     * @throws RepositoryException
+     * @throws RepositoryException if the node can not added
      */
-    public synchronized NodeImpl addNode(Name nodeName, Name nodeTypeName,
-                                         NodeId id)
-            throws ItemExistsException, NoSuchNodeTypeException, VersionException,
-            ConstraintViolationException, LockException, RepositoryException {
+    public synchronized NodeImpl addNode(
+            Name nodeName, Name nodeTypeName, NodeId id)
+            throws RepositoryException {
         // check state of this instance
         sanityCheck();
 
-        // make sure this node is checked-out and not locked by another session.
-        int options = ItemValidator.CHECK_LOCK | ItemValidator.CHECK_VERSIONING;
-        session.getValidator().checkModify(this, options, Permission.NONE);
+        Path nodePath = PathFactoryImpl.getInstance().create(
+                getPrimaryPath(), nodeName, true);
 
+        // Check the explicitly specified node type (if any)
         NodeTypeImpl nt = null;
         if (nodeTypeName != null) {
             nt = session.getNodeTypeManager().getNodeType(nodeTypeName);
+            if (nt.isMixin()) {
+                throw new ConstraintViolationException(
+                        "Unable to add a node with a mixin node type: "
+                        + session.getJCRName(nodeTypeName));
+            } else if (nt.isAbstract()) {
+                throw new ConstraintViolationException(
+                        "Unable to add a node with an abstract node type: "
+                        + session.getJCRName(nodeTypeName));
+            } else {
+                // adding a node with explicit specifying the node type name
+                // requires the editing session to have nt_management privilege.
+                session.getAccessManager().checkPermission(
+                        nodePath, Permission.NODE_TYPE_MNGMT);
+            }
+        }
+
+        // Get the applicable child node definition for this node.
+        NodeDefinitionImpl def;
+        try {
+            def = getApplicableChildNodeDefinition(nodeName, nodeTypeName);
+        } catch (RepositoryException e) {
+            throw new ConstraintViolationException(
+                    "No child node definition for "
+                    + session.getJCRName(nodeName) + " found in " + this, e);
+        }
+
+        // Use default node type from child node definition if needed
+        if (nt == null) {
+            nt = (NodeTypeImpl) def.getDefaultPrimaryType();
+        }
+
+        // check for name collisions
+        NodeState thisState = data.getNodeState();
+        ChildNodeEntry cne = thisState.getChildNodeEntry(nodeName, 1);
+        if (cne != null) {
+            // there's already a child node entry with that name;
+            // check same-name sibling setting of new node
+            if (!def.allowsSameNameSiblings()) {
+                throw new ItemExistsException(
+                        "This node already exists: "
+                        + itemMgr.safeGetJCRPath(nodePath));
+            }
+            // check same-name sibling setting of existing node
+            NodeImpl existing = itemMgr.getNode(cne.getId(), getNodeId());
+            if (!existing.getDefinition().allowsSameNameSiblings()) {
+                throw new ItemExistsException(
+                        "Same-name siblings not allowed for " + existing);
+            }
         }
-        return internalAddChildNode(nodeName, nt, id);
+
+        // check protected flag of parent (i.e. this) node and retention/hold
+        // make sure this node is checked-out and not locked by another session.
+        int options =
+            ItemValidator.CHECK_LOCK | ItemValidator.CHECK_CHECKED_OUT
+            | ItemValidator.CHECK_CONSTRAINTS | ItemValidator.CHECK_HOLD
+            | ItemValidator.CHECK_RETENTION;
+        session.getValidator().checkModify(this, options, Permission.NONE);
+
+        // now do create the child node
+        return createChildNode(nodeName, def, nt, id);
     }
 
     /**
@@ -1874,7 +1717,7 @@
         }
 
         // make sure this node is checked-out and neither protected nor locked
-        int options = ItemValidator.CHECK_LOCK | ItemValidator.CHECK_VERSIONING
+        int options = ItemValidator.CHECK_LOCK | ItemValidator.CHECK_CHECKED_OUT
                 | ItemValidator.CHECK_CONSTRAINTS;
         session.getValidator().checkModify(this, options, Permission.NONE);
 
@@ -2043,7 +1886,7 @@
         // (1) make sure that parent node is checked-out
         // (2) check lock status
         // (3) check protected flag of parent (i.e. this) node
-        int options = ItemValidator.CHECK_LOCK | ItemValidator.CHECK_VERSIONING | ItemValidator.CHECK_CONSTRAINTS;
+        int options = ItemValidator.CHECK_LOCK | ItemValidator.CHECK_CHECKED_OUT | ItemValidator.CHECK_CONSTRAINTS;
         session.getValidator().checkModify(this, options, Permission.NONE);
 
         // (4) check for name collisions
@@ -2143,31 +1986,116 @@
         return (Node) itemMgr.getItem(parentId);
     }
 
-    //-----------------------------------------------------------------< Node >
+    //----------------------------------------------------------------< Node >
+
     /**
      * {@inheritDoc}
      */
-    public synchronized Node addNode(String relPath)
-            throws ItemExistsException, PathNotFoundException, VersionException,
-            ConstraintViolationException, LockException, RepositoryException {
-        // check state of this instance
-        sanityCheck();
-
-        return internalAddNode(relPath, null);
+    public Node addNode(String relPath) throws RepositoryException {
+        return addNodeWithUuid(relPath, null, null);
     }
 
     /**
      * {@inheritDoc}
      */
-    public synchronized Node addNode(String relPath, String nodeTypeName)
-            throws ItemExistsException, PathNotFoundException,
-            NoSuchNodeTypeException, VersionException,
-            ConstraintViolationException, LockException, RepositoryException {
+    public Node addNode(String relPath, String nodeTypeName)
+            throws RepositoryException {
+        return addNodeWithUuid(relPath, nodeTypeName, null);
+    }
+
+    /**
+     * Adds a node with the given UUID. You can only add a node with a UUID
+     * that is not already assigned to another node in this workspace.
+     *
+     * @since Apache Jackrabbit 1.6
+     * @see <a href="https://issues.apache.org/jira/browse/JCR-1972">JCR-1972</a>
+     * @see Node#addNode(String)
+     * @param relPath path of the new node
+     * @param uuid    UUID of the new node,
+     *                or <code>null</code> for a random new UUID
+     * @return the newly added node
+     * @throws RepositoryException if the node can not be added
+     */
+    public Node addNodeWithUuid(String relPath, String uuid)
+            throws RepositoryException {
+        return addNodeWithUuid(relPath, null, uuid);
+    }
+
+    /**
+     * Adds a node with the given node type and UUID. You can only add a node
+     * with a UUID that is not already assigned to another node in this
+     * workspace.
+     *
+     * @since Apache Jackrabbit 1.6
+     * @see <a href="https://issues.apache.org/jira/browse/JCR-1972">JCR-1972</a>
+     * @see Node#addNode(String, String)
+     * @param relPath      path of the new node
+     * @param nodeTypeName name of the new node's node type,
+     *                     or <code>null</code> for automatic type assignment
+     * @param uuid         UUID of the new node,
+     *                     or <code>null</code> for a random new UUID
+     * @return the newly added node
+     * @throws RepositoryException if the node can not be added
+     */
+    public synchronized Node addNodeWithUuid(
+            String relPath, String nodeTypeName, String uuid)
+            throws RepositoryException {
         // check state of this instance
         sanityCheck();
 
-        NodeTypeImpl nt = (NodeTypeImpl) session.getNodeTypeManager().getNodeType(nodeTypeName);
-        return internalAddNode(relPath, nt);
+        // Get the canonical path of the new node
+        Path path;
+        try {
+            path = PathFactoryImpl.getInstance().create(
+                    getPrimaryPath(), session.getQPath(relPath), true);
+        } catch (NameException e) {
+            throw new RepositoryException(
+                    "Failed to resolve path " + relPath
+                    + " relative to " + this, e);
+        }
+
+        // Get the last path element and check that it's a simple name
+        Path.Element last = path.getNameElement();
+        if (!last.denotesName() || last.getIndex() != 0) {
+            throw new RepositoryException(
+                    "Invalid last path element for adding node "
+                    + relPath + " relative to " + this);
+        }
+
+        // Get the parent node instance
+        NodeImpl parentNode;
+        Path parentPath = path.getAncestor(1);
+        try {
+            parentNode = itemMgr.getNode(parentPath);
+        } catch (PathNotFoundException e) {
+            if (itemMgr.propertyExists(parentPath)) {
+                throw new ConstraintViolationException(
+                        "Unable to add a child node to property "
+                        + session.getJCRPath(parentPath));
+            }
+            throw e;
+        } catch (AccessDeniedException ade) {
+            throw new PathNotFoundException(
+                    "Failed to resolve path " + relPath + " relative to " + this);
+        }
+
+        // Resolve node type name (if any)
+        Name typeName = null;
+        if (nodeTypeName != null) {
+            typeName = session.getQName(nodeTypeName);
+        }
+
+        // Check that the given UUID (if any) does not already exist
+        NodeId id = null;
+        if (uuid != null) {
+            id = new NodeId(uuid);
+            if (itemMgr.itemExists(id)) {
+                throw new ItemExistsException(
+                        "A node with this UUID already exists: " + uuid);
+            }
+        }
+
+        return parentNode.addNode(last.getName(), typeName, id);
     }
 
     /**
@@ -2883,7 +2811,7 @@
             return false;
         }
 
-        int options = ItemValidator.CHECK_LOCK | ItemValidator.CHECK_VERSIONING
+        int options = ItemValidator.CHECK_LOCK | ItemValidator.CHECK_CHECKED_OUT
                 | ItemValidator.CHECK_CONSTRAINTS | ItemValidator.CHECK_HOLD;
         int permissions = Permission.NODE_TYPE_MNGMT;
         // special handling of mix:(simple)versionable. since adding the mixin alters
@@ -2971,10 +2899,7 @@
         // check state of this instance
         sanityCheck();
 
-        ArrayList nodes = new ArrayList();
-        // traverse children using a special filtering 'collector'
-        accept(new ChildrenCollectorFilter(namePattern, nodes, true, false, 1));
-        return new NodeIteratorAdapter(nodes);
+        return ChildrenCollectorFilter.collectChildNodes(this, namePattern);
     }
 
     /**
@@ -2985,10 +2910,7 @@
         // check state of this instance
         sanityCheck();
 
-        ArrayList properties = new ArrayList();
-        // traverse children using a special filtering 'collector'
-        accept(new ChildrenCollectorFilter(namePattern, properties, false, true, 1));
-        return new PropertyIteratorAdapter(properties);
+        return ChildrenCollectorFilter.collectProperties(this, namePattern);
     }
 
     /**
@@ -3302,8 +3224,8 @@
         Path parentPath = parentNode.getPrimaryPath();
         PathBuilder builder = new PathBuilder(parentPath);
 
-        ChildNodeEntry entry = ((NodeState) parentNode.getItemState()).
-                getChildNodeEntry(getNodeId());
+        ChildNodeEntry entry =
+            parentNode.getNodeState().getChildNodeEntry(getNodeId());
         if (entry == null) {
             String msg = "failed to build path of " + id + ": "
                     + parentId + " has no child entry for "
@@ -3321,1252 +3243,169 @@
     }
 
     //------------------------------< versioning support: public Node methods >
+
     /**
      * {@inheritDoc}
      */
-    public Version checkin()
-            throws VersionException, UnsupportedRepositoryOperationException,
-            InvalidItemStateException, LockException, RepositoryException {
+    public boolean isCheckedOut() throws RepositoryException {
         // check state of this instance
         sanityCheck();
 
-        // check if versionable
-        boolean isFull = checkVersionable();
-
-        // check if checked out
-        if (!internalIsCheckedOut()) {
-            String msg = this + ": Node is already checked-in. ignoring.";
-            log.debug(msg);
-            return getBaseVersion();
+        // try shortcut first:
+        // if current node is 'new' we can safely consider it checked-out since
+        // otherwise it would had been impossible to add it in the first place
+        if (isNew()) {
+            return true;
         }
 
-        // check lock status, holds and permissions
-        int options = ItemValidator.CHECK_LOCK | ItemValidator.CHECK_HOLD | ItemValidator.CHECK_PENDING_CHANGES_ON_NODE;
-        session.getValidator().checkModify(this, options, Permission.VERSION_MNGMT);
-
-        Version v = session.getVersionManager().checkin(this);
-        boolean success = false;
+        // search nearest ancestor that is versionable
+        // FIXME should not only rely on existence of jcr:isCheckedOut property
+        // but also verify that node.isNodeType("mix:versionable")==true;
+        // this would have a negative impact on performance though...
         try {
-            internalSetProperty(NameConstants.JCR_ISCHECKEDOUT, InternalValue.create(false));
-            if (isFull) {
-                internalSetProperty(
-                        NameConstants.JCR_BASEVERSION,
-                        InternalValue.create(new NodeId(v.getUUID())));
-                internalSetProperty(NameConstants.JCR_PREDECESSORS, InternalValue.EMPTY_ARRAY, PropertyType.REFERENCE);
-                if (hasProperty(NameConstants.JCR_ACTIVITY)) {
-                    removeChildProperty(NameConstants.JCR_ACTIVITY);
-                }
-            }
-            save();
-            success = true;
-        } finally {
-            if (!success) {
-                try {
-                    // TODO: need to revert changes made within the version manager as well.
-                    refresh(false);
-                } catch (RepositoryException e) {
-                    // cleanup failed
-                    log.error("Error while cleaning up after failed Node.checkin", e);
+            NodeState state = getNodeState();
+            while (!state.hasPropertyName(JCR_ISCHECKEDOUT)) {
+                ItemId parentId = state.getParentId();
+                if (parentId == null) {
+                    // root reached or out of hierarchy
+                    return true;
                 }
+                state = (NodeState)
+                    session.getItemStateManager().getItemState(parentId);
             }
+            PropertyId id = new PropertyId(state.getNodeId(), JCR_ISCHECKEDOUT);
+            PropertyState ps =
+                (PropertyState) session.getItemStateManager().getItemState(id);
+            return ps.getValues()[0].getBoolean();
+        } catch (ItemStateException e) {
+            throw new RepositoryException(e);
         }
-        return v;
     }
 
     /**
-     * {@inheritDoc}
+     * Returns the version manager of this workspace.
      */
-    public void checkout()
-            throws UnsupportedRepositoryOperationException, LockException,
-            RepositoryException {
-        // check state of this instance
-        sanityCheck();
-
-        // check if versionable
-        boolean isFull = checkVersionable();
-
-        // check checked-out status
-        if (internalIsCheckedOut()) {
-            String msg = this + ": Node is already checked-out. ignoring.";
-            log.debug(msg);
-            return;
-        }
-
-        int options = ItemValidator.CHECK_LOCK | ItemValidator.CHECK_HOLD;
-        session.getValidator().checkModify(this, options, Permission.VERSION_MNGMT);
-
-        boolean hasPendingChanges = hasPendingChanges();
-        Property[] props = new Property[3];
-        boolean success = false;
-        try {
-            props[0] = internalSetProperty(NameConstants.JCR_ISCHECKEDOUT, InternalValue.create(true));
-            if (isFull) {
-                NodeImpl activity = (NodeImpl) session.getWorkspace().getVersionManager().getActivity();
-                Version baseVersion = session.getVersionManager().checkout(this);
-                props[1] = internalSetProperty(NameConstants.JCR_PREDECESSORS,
-                        new InternalValue[]{
-                                InternalValue.create(new NodeId(baseVersion.getUUID()))
-                        });
-                if (activity != null) {
-                    props[2] = internalSetProperty(NameConstants.JCR_ACTIVITY,
-                            InternalValue.create(activity.getNodeId()));
-                }
-            }
-            if (hasPendingChanges) {
-                for (Property prop : props) {
-                    if (prop != null) {
-                        prop.save();
-                    }
-                }
-            } else {
-                save();
-            }
-            success = true;
-        } finally {
-            if (!success) {
-                for (Property prop : props) {
-                    if (prop != null) {
-                        try {
-                            prop.refresh(false);
-                        } catch (RepositoryException e) {
-                            log.error("Error while cleaning up after failed Node.checkout", e);
-                        }
-                    }
-                }
-            }
-        }
+    private VersionManagerImpl getVersionManagerImpl() {
+        return session.getWorkspaceImpl().getVersionManagerImpl();
     }
 
     /**
      * {@inheritDoc}
      */
-    public void update(String srcWorkspaceName)
-            throws NoSuchWorkspaceException, AccessDeniedException,
-            LockException, InvalidItemStateException, RepositoryException {
-        internalMerge(srcWorkspaceName, null, false, false);
+    public void update(String srcWorkspaceName) throws RepositoryException {
+        getVersionManagerImpl().update(this, srcWorkspaceName);
     }
 
     /**
-     * {@inheritDoc}
+     * Use {@link VersionManager#checkin(String)} instead
      */
-    public NodeIterator merge(String srcWorkspace, boolean bestEffort)
-            throws NoSuchWorkspaceException, AccessDeniedException,
-            VersionException, LockException, InvalidItemStateException,
-            RepositoryException {
-        return merge(srcWorkspace, bestEffort, false);
+    @Deprecated
+    public Version checkin() throws RepositoryException {
+        return getVersionManagerImpl().checkin(getPath());
     }
 
     /**
-     * @see VersionManager#merge(String, String, boolean, boolean)
+     * Use {@link VersionManagerImpl#checkin(String, Calendar)} instead
+     *
+     * @since Apache Jackrabbit 1.6
+     * @see <a href="https://issues.apache.org/jira/browse/JCR-1972">JCR-1972</a>
      */
-    public NodeIterator merge(String srcWorkspace, boolean bestEffort, boolean isShallow)
-            throws NoSuchWorkspaceException, AccessDeniedException,
-            VersionException, LockException, InvalidItemStateException,
-            RepositoryException {
-
-        List failedIds = new ArrayList();
-        internalMerge(srcWorkspace, failedIds, bestEffort, isShallow);
-
-        return new LazyItemIterator(itemMgr, failedIds);
+    @Deprecated
+    public Version checkin(Calendar created) throws RepositoryException {
+        return getVersionManagerImpl().checkin(getPath(), created);
     }
 
     /**
-     * {@inheritDoc}
+     * Use {@link VersionManager#checkout(String)} instead
      */
-    public void cancelMerge(Version version)
-            throws VersionException, InvalidItemStateException,
-            UnsupportedRepositoryOperationException, RepositoryException {
-        internalFinishMerge(version, true);
+    @Deprecated
+    public void checkout() throws RepositoryException {
+        getVersionManagerImpl().checkout(getPath());
     }
 
     /**
-     * {@inheritDoc}
+     * Use {@link VersionManager#merge(String, String, boolean)} instead
      */
-    public void doneMerge(Version version) throws VersionException,
-            InvalidItemStateException, UnsupportedRepositoryOperationException,
-            RepositoryException {
-        internalFinishMerge(version, false);
+    @Deprecated
+    public NodeIterator merge(String srcWorkspace, boolean bestEffort)
+            throws RepositoryException {
+        return getVersionManagerImpl().merge(
+                getPath(), srcWorkspace, bestEffort);
     }
 
     /**
-     * {@inheritDoc}
+     * Use {@link VersionManager#cancelMerge(String, Version)} instead
      */
-    public boolean isCheckedOut() throws RepositoryException {
-        // check state of this instance
-        sanityCheck();
+    @Deprecated
+    public void cancelMerge(Version version) throws RepositoryException {
+        getVersionManagerImpl().cancelMerge(getPath(), version);
+    }
 
-        return internalIsCheckedOut();
+    /**
+     * Use {@link VersionManager#doneMerge(String, Version)} instead
+     */
+    @Deprecated
+    public void doneMerge(Version version) throws RepositoryException {
+        getVersionManagerImpl().doneMerge(getPath(), version);
     }
 
     /**
-     * {@inheritDoc}
+     * Use {@link VersionManager#restore(String, String, boolean)} instead
      */
+    @Deprecated
     public void restore(String versionName, boolean removeExisting)
-            throws VersionException, ItemExistsException,
-            UnsupportedRepositoryOperationException, LockException,
-            InvalidItemStateException, RepositoryException {
-
-        // checks
-        sanityCheck();
-        int options = ItemValidator.CHECK_PENDING_CHANGES | ItemValidator.CHECK_LOCK | ItemValidator.CHECK_HOLD;
-        session.getValidator().checkModify(this, options, Permission.NONE);
-
-        Version v = getVersionHistory().getVersion(versionName);
-        DateVersionSelector gvs = new DateVersionSelector(v.getCreated());
-        internalRestore(v, gvs, removeExisting);
-        // session.save/revert is done in internal restore
+            throws RepositoryException {
+        getVersionManagerImpl().restore(getPath(), versionName, removeExisting);
     }
 
     /**
-     * {@inheritDoc}
+     * Use {@link VersionManager#restore(String, Version, boolean)} instead
      */
+    @Deprecated
     public void restore(Version version, boolean removeExisting)
-            throws VersionException, ItemExistsException,
-            UnsupportedRepositoryOperationException, LockException,
-            RepositoryException {
-
-        // do checks
-        sanityCheck();
-        checkVersionable();
-        int options = ItemValidator.CHECK_PENDING_CHANGES | ItemValidator.CHECK_LOCK | ItemValidator.CHECK_HOLD;
-        session.getValidator().checkModify(this, options, Permission.NONE);
-
-        // check if 'own' version
-        if (!version.getContainingHistory().isSame(getVersionHistory())) {
-            throw new VersionException("Unable to restore version. Not same version history.");
-        }
-
-        internalRestore(version, new DateVersionSelector(version.getCreated()), removeExisting);
-        // session.save/revert is done in internal restore
+            throws RepositoryException {
+        getVersionManagerImpl().restore(this, version, removeExisting);
     }
 
     /**
-     * {@inheritDoc}
+     * Use {@link VersionManager#restore(String, Version, boolean)} instead
      */
+    @Deprecated
     public void restore(Version version, String relPath, boolean removeExisting)
-            throws PathNotFoundException, ItemExistsException, VersionException,
-            ConstraintViolationException, UnsupportedRepositoryOperationException,
-            LockException, InvalidItemStateException, RepositoryException {
-
-        // do checks
-        sanityCheck();
-        int options = ItemValidator.CHECK_PENDING_CHANGES | ItemValidator.CHECK_LOCK | ItemValidator.CHECK_HOLD;
-        session.getValidator().checkModify(this, options, Permission.NONE);
-
-        // if node exists, do a 'normal' restore
+            throws RepositoryException {
         if (hasNode(relPath)) {
-            getNode(relPath).restore(version, removeExisting);
+            getVersionManagerImpl().restore((NodeImpl) getNode(relPath), version, removeExisting);
         } else {
-            NodeImpl node;
-            try {
-                // check if versionable node exists
-                InternalFrozenNode fn = ((VersionImpl) version).getInternalFrozenNode();
-                node = (NodeImpl) session.getNodeById(fn.getFrozenId());
-                if (removeExisting) {
-                    try {
-                        Path relative = session.getQPath(relPath);
-                        Path dstPath =
-                            PathFactoryImpl.getInstance().create(getPrimaryPath(), relative, false)
-                            .getCanonicalPath();
-                        // move to respective location
-                        session.move(node.getPath(), session.getJCRPath(dstPath));
-                        // need to refetch ?
-                        node = (NodeImpl) session.getNodeById(fn.getFrozenId());
-                    } catch (NameException e) {
-                        throw new RepositoryException(e);
-                    }
-                } else {
-                    throw new ItemExistsException("Unable to restore version. Versionable node already exists.");
-                }
-            } catch (ItemNotFoundException e) {
-                // not found, create new one
-                node = addNode(relPath, ((VersionImpl) version).getInternalFrozenNode());
-            }
-
-            // recreate node from frozen state
-            node.internalRestore(version, new DateVersionSelector(version.getCreated()), removeExisting);
-            // session.save/revert is done in internal restore
+            getVersionManagerImpl().restore(
+                getPath() + "/" + relPath, version, removeExisting);
         }
     }
 
     /**
-     * {@inheritDoc}
+     * Use {@link VersionManager#restoreByLabel(String, String, boolean)}
+     * instead
      */
+    @Deprecated
     public void restoreByLabel(String versionLabel, boolean removeExisting)
-            throws VersionException, ItemExistsException,
-            UnsupportedRepositoryOperationException, LockException,
-            InvalidItemStateException, RepositoryException {
-
-        // do checks
-        sanityCheck();
-        int options = ItemValidator.CHECK_PENDING_CHANGES | ItemValidator.CHECK_LOCK | ItemValidator.CHECK_HOLD;
-        session.getValidator().checkModify(this, options, Permission.NONE);
-
-        Version v = getVersionHistory().getVersionByLabel(versionLabel);
-        if (v == null) {
-            throw new VersionException("No version for label " + versionLabel + " found.");
-        }
-        internalRestore(v, new LabelVersionSelector(versionLabel), removeExisting);
-        // session.save/revert is done in internal restore
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    public VersionHistory getVersionHistory()
-            throws UnsupportedRepositoryOperationException, RepositoryException {
-        // check state of this instance
-        sanityCheck();
-
-        boolean isFull = checkVersionable();
-
-        InternalVersionHistory vh;
-        if (isFull) {
-            NodeId id = NodeId.valueOf(getProperty(NameConstants.JCR_VERSIONHISTORY).getString());
-            vh = session.getVersionManager().getVersionHistory(id);
-        } else {
-            vh = session.getVersionManager().getVersionHistoryOfNode((NodeId) id);
-        }
-        return (VersionHistory) session.getNodeById(vh.getId());
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    public Version getBaseVersion()
-            throws UnsupportedRepositoryOperationException, RepositoryException {
-        // check state of this instance
-        sanityCheck();
-
-        boolean isFull = checkVersionable();
-
-        InternalVersion v;
-        if (isFull) {
-            NodeId id = NodeId.valueOf(getProperty(NameConstants.JCR_BASEVERSION).getString());
-            v = session.getVersionManager().getVersion(id);
-        } else {
-            // note, that the method currently only works for linear version
-            // graphs (i.e. simple versioning)
-            v = session.getVersionManager().getHeadVersionOfNode(((NodeId) id));
-        }
-
-        return (Version) session.getNodeById(v.getId());
-    }
-
-    //-----------------------------------< versioning support: implementation >
-    /**
-     * Checks if this node is versionable, i.e. has 'mix:versionable' or a
-     * 'mix:simpleVersionable'.
-     * @return <code>true</code> if this node is full versionable, i.e. is
-     *         of nodetype mix:versionable
-     * @throws UnsupportedRepositoryOperationException
-     *          if this node is not versionable at all
-     */
-    private boolean checkVersionable()
-            throws UnsupportedRepositoryOperationException, RepositoryException {
-        if (isNodeType(NameConstants.MIX_VERSIONABLE)) {
-            return true;
-        } else if (isNodeType(NameConstants.MIX_SIMPLE_VERSIONABLE)) {
-            return false;
-        } else {
-            String msg = "Unable to perform a versioning operation on a " +
-                         "non versionable node: " + this;
-            log.debug(msg);
-            throw new UnsupportedRepositoryOperationException(msg);
-        }
-    }
-
-    /**
-     * Returns the corresponding node in the workspace of the given session.
-     * <p/>
-     * Given a node N1 in workspace W1, its corresponding node N2 in workspace
-     * W2 is defined as follows:
-     * <ul>
-     * <li>If N1 is the root node of W1 then N2 is the root node of W2.
-     * <li>If N1 is referenceable (has a UUID) then N2 is the node in W2 with
-     * the same UUID.
-     * <li>If N1 is not referenceable (does not have a UUID) then there is some
-     * node M1 which is either the nearest ancestor of N1 that is
-     * referenceable, or is the root node of W1. If the corresponding node
-     * of M1 is M2 in W2, then N2 is the node with the same relative path
-     * from M2 as N1 has from M1.
-     * </ul>
-     *
-     * @param srcSession
-     * @return the corresponding node or <code>null</code> if no corresponding
-     *         node exists.
-     * @throws RepositoryException If another error occurs.
-     */
-    private NodeImpl getCorrespondingNode(SessionImpl srcSession) throws RepositoryException {
-
-        // search nearest ancestor that is referenceable
-        NodeImpl m1 = this;
-        while (!m1.isNodeType(NameConstants.MIX_REFERENCEABLE)) {
-            if (m1.getDepth() == 0) {
-                // root node
-                try {
-                    return (NodeImpl) srcSession.getItem(getPath());
-                } catch (PathNotFoundException e) {
-                    return null;
-                }
-            }
-            m1 = (NodeImpl) m1.getParent();
-        }
-
-        try {
-            // get corresponding ancestor (might throw ItemNotFoundException)
-            NodeImpl m2 = (NodeImpl) srcSession.getNodeByUUID(m1.getUUID());
-
-            // return m2, if m1 == n1
-            if (m1 == this) {
-                return m2;
-            }
-
-            String relPath;
-            try {
-                Path p = m1.getPrimaryPath().computeRelativePath(getPrimaryPath());
-                // use prefix mappings of srcSession
-                relPath = session.getJCRPath(p);
-            } catch (NameException be) {
-                // should never get here...
-                String msg = "internal error: failed to determine relative path";
-                log.error(msg, be);
-                throw new RepositoryException(msg, be);
-            }
-            if (!m2.hasNode(relPath)) {
-                return null;
-            } else {
-                return (NodeImpl) m2.getNode(relPath);
-            }
-        } catch (ItemNotFoundException e) {
-            return null;
-        }
-    }
-
-    /**
-     * Performs the merge test. If the result is 'update', then the corresponding
-     * source node is returned. if the result is 'leave' or 'besteffort-fail'
-     * then <code>null</code> is returned. If the result of the merge test is
-     * 'fail' with bestEffort set to <code>false</code> a MergeException is
-     * thrown.
-     * <p/>
-     * jsr170 - 8.2.10 Merge:
-     * [...]
-     * <ul>
-     * <li>If N is currently checked-in then:</li>
-     * <ul>
-     * <li>If V' is a successor (to any degree) of V, then the merge result
-     *     for N is update.
-     * </li>
-     * <li>If V' is a predecessor (to any degree) of V or if V and
-     *     V' are identical (i.e., are actually the same version),
-     *     then the merge result for N is leave.
-     * </li>
-     * <li>If V is neither a successor of, predecessor of, nor
-     *     identical with V', then the merge result for N is failed.
-     *     This is the case where N and N' represent divergent
-     *     branches of the version graph, thus determining the
-     *     result of a merge is non-trivial.
-     * </li>
-     * </ul>
-     * <li>If N is currently checked-out then:</li>
-     * <ul>
-     * <li>If V' is a predecessor (to any degree) of V or if V and
-     *     V' are identical (i.e., are actually the same version),
-     *     then the merge result for N is leave.
-     * </li>
-     * <li>If any other relationship holds between V and V',
-     *     then the merge result for N is fail.
-     * </li>
-     * </ul>
-     * </ul>
-     *
-     * @param srcSession the source session
-     * @param failedIds the list to store the failed node ids.
-     * @param bestEffort the best effort flag
-     * @return the corresponding source node or <code>null</code>
-     * @throws RepositoryException if an error occurs.
-     * @throws AccessDeniedException if access is denied
-     */
-    private NodeImpl doMergeTest(SessionImpl srcSession, List<ItemId> failedIds, boolean bestEffort)
-            throws RepositoryException, AccessDeniedException {
-
-        // If N does not have a corresponding node then the merge result for N is leave.
-        NodeImpl srcNode = getCorrespondingNode(srcSession);
-        if (srcNode == null) {
-            return null;
-        }
-
-        // if not versionable, update
-        if (!isNodeType(NameConstants.MIX_VERSIONABLE) || failedIds == null) {
-            return srcNode;
-        }
-        // if source node is not versionable, leave
-        if (!srcNode.isNodeType(NameConstants.MIX_VERSIONABLE)) {
-            return null;
-        }
-        // test versions
-        VersionImpl v = (VersionImpl) getBaseVersion();
-        VersionImpl vp = (VersionImpl) srcNode.getBaseVersion();
-        if (vp.isMoreRecent(v) && !isCheckedOut()) {
-            // I f V' is a successor (to any degree) of V, then the merge result for
-            // N is update. This case can be thought of as the case where N' is
-            // "newer" than N and therefore N should be updated to reflect N'.
-            return srcNode;
-        } else if (v.isSame(vp) || v.isMoreRecent(vp)) {
-            // If V' is a predecessor (to any degree) of V or if V and V' are
-            // identical (i.e., are actually the same version), then the merge
-            // result for N is leave. This case can be thought of as the case where
-            // N' is "older" or the "same age" as N and therefore N should be left alone.
-            return null;
-        } else {
-            // If V is neither a successor of, predecessor of, nor identical
-            // with V', then the merge result for N is failed. This is the case
-            // where N and N' represent divergent branches of the version graph,
-            // thus determining the result of a merge is non-trivial.
-            if (bestEffort) {
-                // add 'offending' version to jcr:mergeFailed property
-                Set<String> set = internalGetMergeFailed();
-                set.add(srcNode.getBaseVersion().getUUID());
-                internalSetMergeFailed(set);
-                failedIds.add(id);
-                return null;
-            } else {
-                String msg =
-                    "Unable to merge nodes. Violating versions. " + this;
-                log.debug(msg);
-                throw new MergeException(msg);
-            }
-        }
-    }
-
-    /**
-     * Perform {@link Node#cancelMerge(Version)} or {@link Node#doneMerge(Version)}
-     * depending on the value of <code>cancel</code>.
-     */
-    private void internalFinishMerge(Version version, boolean cancel)
-            throws VersionException, InvalidItemStateException,
-            UnsupportedRepositoryOperationException, RepositoryException {
-        // check state of this instance
-        sanityCheck();
-
-        // check versionable
-        checkVersionable();
-
-        // check lock, permissions and checkout-status
-        int options = ItemValidator.CHECK_LOCK | ItemValidator.CHECK_VERSIONING | ItemValidator.CHECK_PENDING_CHANGES_ON_NODE | ItemValidator.CHECK_HOLD;
-        session.getValidator().checkModify(this, options, Permission.VERSION_MNGMT);
-
-        // check if version is in mergeFailed list
-        Set<String> failed = internalGetMergeFailed();
-        if (!failed.remove(version.getUUID())) {
-            String msg =
-                "Unable to finish merge. Specified version is not in"
-                + " jcr:mergeFailed property: " + this;
-            log.error(msg);
-            throw new VersionException(msg);
-        }
-
-        boolean success = false;
-        try {
-            // remove version from mergeFailed list
-            internalSetMergeFailed(failed);
-
-            if (!cancel) {
-                // add version to jcr:predecessors list
-                Value[] vals = getProperty(NameConstants.JCR_PREDECESSORS).getValues();
-                InternalValue[] v = new InternalValue[vals.length + 1];
-                for (int i = 0; i < vals.length; i++) {
-                    v[i] = InternalValue.create(new NodeId(vals[i].getString()));
-                }
-                v[vals.length] = InternalValue.create(new NodeId(version.getUUID()));
-                internalSetProperty(NameConstants.JCR_PREDECESSORS, v);
-            }
-
-            save();
-            success = true;
-        } finally {
-            if (!success) {
-                try {
-                    refresh(false);
-                } catch (RepositoryException e) {
-                    log.error("Error while reverting changes upon failed Node.doneMerge or Node.cancelMerge, respectively.", e);
-                }
-            }
-        }
-    }
-
-    /**
-     * @return
-     * @throws RepositoryException
-     */
-    private Set<String> internalGetMergeFailed() throws RepositoryException {
-        HashSet<String> set = new HashSet<String>();
-        if (hasProperty(NameConstants.JCR_MERGEFAILED)) {
-            Value[] vals = getProperty(NameConstants.JCR_MERGEFAILED).getValues();
-            for (int i = 0; i < vals.length; i++) {
-                set.add(vals[i].getString());
-            }
-        }
-        return set;
-    }
-
-    /**
-     * @param set
-     * @throws RepositoryException
-     */
-    private void internalSetMergeFailed(Set<String> set) throws RepositoryException {
-        if (set.isEmpty()) {
-            internalSetProperty(NameConstants.JCR_MERGEFAILED, (InternalValue[]) null);
-        } else {
-            InternalValue[] vals = new InternalValue[set.size()];
-            Iterator<String> iter = set.iterator();
-            int i = 0;
-            while (iter.hasNext()) {
-                String uuid = iter.next();
-                vals[i++] = InternalValue.create(new NodeId(uuid));
-            }
-            internalSetProperty(NameConstants.JCR_MERGEFAILED, vals);
-        }
-    }
-
-    /**
-     * Determines the checked-out status of this node.
-     * <p/>
-     * A node is considered <i>checked-out</i> if it is versionable and
-     * checked-out, or is non-versionable but its nearest versionable ancestor
-     * is checked-out, or is non-versionable and there are no versionable
-     * ancestors.
-     *
-     * @return a boolean
-     * @see Node#isCheckedOut()
-     */
-    protected boolean internalIsCheckedOut() throws RepositoryException {
-        /**
-         * try shortcut first:
-         * if current node is 'new' we can safely consider it checked-out
-         * since otherwise it would had been impossible to add it in the first
-         * place
-         */
-        if (isNew()) {
-            return true;
-        }
-
-        // search nearest ancestor that is versionable
-        /**
-         * FIXME should not only rely on existence of jcr:isCheckedOut property
-         * but also verify that node.isNodeType("mix:versionable")==true;
-         * this would have a negative impact on performance though...
-         */
-        try {
-            NodeState state = (NodeState) getItemState();
-            while (!state.hasPropertyName(NameConstants.JCR_ISCHECKEDOUT)) {
-                ItemId parentId = state.getParentId();
-                if (parentId == null) {
-                    // root reached or out of hierarchy
-                    return true;
-                }
-                state = (NodeState) session.getItemStateManager().getItemState(parentId);
-            }
-            PropertyState ps = (PropertyState) session.getItemStateManager().getItemState(new PropertyId(state.getNodeId(), NameConstants.JCR_ISCHECKEDOUT));
-            return ps.getValues()[0].getBoolean();
-        } catch (ItemStateException e) {
-            throw new RepositoryException(e.getMessage());
-        }
-    }
-
-    /**
-     * Creates a new node at <code>relPath</code> of the node type, uuid and
-     * eventual mixin types stored in the frozen node. The same as
-     * <code>{@link #addNode(String relPath)}</code> except that the primary
-     * node type type, the uuid and evt. mixin type of the new node is
-     * explictly specified in the nt:frozen node.
-     * <p/>
-     *
-     * @param name   The name of the new <code>Node</code> that is to be created.
-     * @param frozen The frozen node that contains the creation information
-     * @return The node that was added.
-     * @throws ItemExistsException          If an item at the
-     *                                      specified path already exists(and same-name siblings are not allowed).
-     * @throws PathNotFoundException        If specified path implies intermediary
-     *                                      <code>Node</code>s that do not exist.
-     * @throws NoSuchNodeTypeException      If the specified <code>nodeTypeName</code>
-     *                                      is not recognized.
-     * @throws ConstraintViolationException If an attempt is made to add a node as the
-     *                                      child of a <code>Property</code>
-     * @throws RepositoryException          if another error occurs.
-     */
-    private NodeImpl addNode(Name name, InternalFrozenNode frozen)
-            throws ItemExistsException, PathNotFoundException,
-            ConstraintViolationException, NoSuchNodeTypeException,
-            RepositoryException {
-
-        // get frozen node type
-        NodeTypeManagerImpl ntMgr = session.getNodeTypeManager();
-        NodeTypeImpl nt = ntMgr.getNodeType(frozen.getFrozenPrimaryType());
-
-        // get frozen id
-        NodeId id = frozen.getFrozenId();
-
-        NodeImpl node = internalAddChildNode(name, nt, id);
-
-        // get frozen mixin
-        // todo: also respect mixing types on creation?
-        Name[] mxNames = frozen.getFrozenMixinTypes();
-        for (Name mxName : mxNames) {
-            node.addMixin(mxName);
-        }
-        return node;
-    }
-
-    /**
-     * Creates a new node at <code>relPath</code> of the node type, uuid and
-     * eventual mixin types stored in the frozen node. The same as
-     * <code>{@link #addNode(String relPath)}</code> except that the primary
-     * node type type, the uuid and evt. mixin type of the new node is
-     * explicitly specified in the nt:frozen node.
-     * <p/>
-     *
-     * @param relPath The path of the new <code>Node</code> that is to be created.
-     * @param frozen  The frozen node that contains the creation information
-     * @return The node that was added.
-     * @throws ItemExistsException          If an item at the
-     *                                      specified path already exists(and same-name siblings are not allowed).
-     * @throws PathNotFoundException        If specified path implies intermediary
-     *                                      <code>Node</code>s that do not exist.
-     * @throws NoSuchNodeTypeException      If the specified <code>nodeTypeName</code>
-     *                                      is not recognized.
-     * @throws ConstraintViolationException If an attempt is made to add a node as the
-     *                                      child of a <code>Property</code>
-     * @throws RepositoryException          if another error occurs.
-     */
-    private NodeImpl addNode(String relPath, InternalFrozenNode frozen)
-            throws ItemExistsException, PathNotFoundException,
-            ConstraintViolationException, NoSuchNodeTypeException,
-            RepositoryException {
-
-        // get frozen node type
-        NodeTypeManagerImpl ntMgr = session.getNodeTypeManager();
-        NodeTypeImpl nt = ntMgr.getNodeType(frozen.getFrozenPrimaryType());
-
-        // get frozen id
-        NodeId id = frozen.getFrozenId();
-
-        NodeImpl node = internalAddNode(relPath, nt, id);
-
-        // get frozen mixin
-        // todo: also respect mixing types on creation?
-        Name[] mxNames = frozen.getFrozenMixinTypes();
-        for (Name mxName : mxNames) {
-            node.addMixin(mxName);
-        }
-        return node;
-    }
-
-    /**
-     * Executes the Node#update or Node#merge call.
-     *
-     * @param srcWorkspaceName Name of the source workspace as passed to
-     * {@link Node#merge(String, boolean)} or {@link Node#update(String)}.
-     * @param failedIds List to place the failed ids or <code>null</code> if
-     * {@link Node#update(String)} should be executed.
-     * @param bestEffort Flag passed to {@link Node#merge(String, boolean)} or
-     * false if {@link Node#update(String)} should be executed.
-     * @throws NoSuchWorkspaceException
-     * @throws AccessDeniedException
-     * @throws LockException
-     * @throws InvalidItemStateException
-     * @throws RepositoryException
-     */
-    private void internalMerge(String srcWorkspaceName,
-                               List<ItemId> failedIds, boolean bestEffort,
-                               boolean shallow)
-            throws NoSuchWorkspaceException, AccessDeniedException,
-            LockException, InvalidItemStateException, RepositoryException {
-
-        // might be added in future releases
-        boolean removeExisting = true;
-        boolean replaceExisting = false;
-
-        // do checks
-        sanityCheck();
-        session.getValidator().checkModify(this, ItemValidator.CHECK_PENDING_CHANGES, Permission.VERSION_MNGMT);
-
-        // if same workspace, ignore
-        if (srcWorkspaceName.equals(session.getWorkspace().getName())) {
-            return;
-        }
-
-        SessionImpl srcSession = null;
-        boolean success = false;
-        try {
-            // create session on other workspace for current subject
-            // (may throw NoSuchWorkspaceException and AccessDeniedException)
-            srcSession = rep.createSession(session.getSubject(), srcWorkspaceName);
-            internalMerge(srcSession, failedIds, bestEffort, removeExisting, replaceExisting, shallow);
-            session.save();
-            success = true;
-        } finally {
-            if (!success) {
-                try {
-                    session.refresh(false);
-                } catch (RepositoryException e) {
-                    log.error("Error while cleaning up after failed merge/update", e);
-                }
-            }
-            if (srcSession != null) {
-                // we don't need the other session anymore, logout
-                srcSession.logout();
-            }
-        }
-    }
-
-    /**
-     * Merges/Updates this node with its corresponding ones
-     *
-     * @param srcSession
-     * @param failedIds
-     * @param bestEffort
-     * @param removeExisting
-     * @param replaceExisting
-     * @throws LockException
-     * @throws RepositoryException
-     */
-    private void internalMerge(SessionImpl srcSession, List<ItemId> failedIds,
-                               boolean bestEffort, boolean removeExisting,
-                               boolean replaceExisting, boolean shallow)
-            throws LockException, RepositoryException {
-
-        if (shallow) {
-            throw new UnsupportedRepositoryOperationException("Shallow merge not supported yet");
-        }
-
-        NodeImpl srcNode = doMergeTest(srcSession, failedIds, bestEffort);
-        if (srcNode == null) {
-            // leave, iterate over children, but ignore non-versionable child
-            // nodes (see JCR-1046)
-            NodeIterator iter = getNodes();
-            while (iter.hasNext()) {
-                NodeImpl n = (NodeImpl) iter.nextNode();
-                if (n.isNodeType(NameConstants.MIX_VERSIONABLE)) {
-                    n.internalMerge(srcSession, failedIds, bestEffort, removeExisting, replaceExisting, shallow);
-                }
-            }
-            return;
-        }
-
-        // check lock and hold status
-        int options = ItemValidator.CHECK_LOCK | ItemValidator.CHECK_HOLD;
-        session.getValidator().checkModify(this, options, Permission.NONE);
-
-        // update the properties
-        PropertyIterator iter = getProperties();
-        while (iter.hasNext()) {
-            PropertyImpl p = (PropertyImpl) iter.nextProperty();
-            if (!srcNode.hasProperty(p.getQName())) {
-                p.internalRemove(true);
-            }
-        }
-        iter = srcNode.getProperties();
-        while (iter.hasNext()) {
-            PropertyImpl p = (PropertyImpl) iter.nextProperty();
-            // ignore system types
-            if (p.getQName().equals(NameConstants.JCR_PRIMARYTYPE)
-                    || p.getQName().equals(NameConstants.JCR_MIXINTYPES)
-                    || p.getQName().equals(NameConstants.JCR_UUID)) {
-                continue;
-            }
-            if (p.getDefinition().isMultiple()) {
-                internalSetProperty(p.getQName(), p.internalGetValues());
-            } else {
-                internalSetProperty(p.getQName(), p.internalGetValue());
-            }
-        }
-        // todo: add/remove mixins ?
-
-        // update the nodes. remove non existing ones
-        NodeIterator niter = getNodes();
-        while (niter.hasNext()) {
-            NodeImpl n = (NodeImpl) niter.nextNode();
-            Path.Element name = n.getPrimaryPath().getNameElement();
-            int idx = name.getIndex();
-            if (idx == 0) {
-                idx = 1;
-            }
-            if (!srcNode.hasNode(name.getName(), idx)) {
-                n.internalRemove(true);
-            }
-        }
-
-        // add source ones
-        niter = srcNode.getNodes();
-        while (niter.hasNext()) {
-            NodeImpl child = (NodeImpl) niter.nextNode();
-            NodeImpl dstNode = null;
-            NodeId childId = child.getNodeId();
-            Path.Element name = child.getPrimaryPath().getNameElement();
-            int idx = name.getIndex();
-            if (idx == 0) {
-                idx = 1;
-            }
-
-            if (child.isNodeType(NameConstants.MIX_REFERENCEABLE)) {
-                // check if correspondance exist in
-                // this workspace
-                try {
-                    dstNode = session.getNodeById(childId);
-                    // check if same parent
-                    if (!isSame(dstNode.getParent())) {
-                        if (removeExisting) {
-                            dstNode.internalRemove(false);
-                            dstNode = null;
-                        } else if (replaceExisting) {
-                            // node exists outside of this update tree, so continue there
-                        } else {
-                            throw new ItemExistsException("Unable to update item: " + dstNode);
-                        }
-                    }
-
-                } catch (ItemNotFoundException e) {
-                    // does not exist
-                }
-            } else {
-                // if child is not referenceable, clear uuid
-                childId = null;
-            }
-            if (dstNode == null && hasNode(name.getName(), idx)) {
-                // the exact behaviour for SNS is not specified by the spec
-                // so we just try to find the corresponding one.
-                dstNode = getNode(name.getName(), idx);
-            }
-            if (dstNode == null) {
-                dstNode = internalAddChildNode(name.getName(), (NodeTypeImpl) child.getPrimaryNodeType(), childId);
-                // add mixins
-                NodeType[] mixins = child.getMixinNodeTypes();
-                for (int i = 0; i < mixins.length; i++) {
-                    dstNode.addMixin(mixins[i].getName());
-                }
-                dstNode.internalMerge(srcSession, null, bestEffort, removeExisting, replaceExisting, shallow);
-            } else {
-                dstNode.internalMerge(srcSession, failedIds, bestEffort, removeExisting, replaceExisting, shallow);
-            }
-        }
-    }
-
-    /**
-     * Internal method to restore a version.
-     *
-     * @param version version to restore
-     * @param vsel the version selector that will select the correct version for
-     * OPV=Version child nodes.
-     * @param removeExisting remove existing flag
-     * @throws RepositoryException if an error occurs
-     */
-    private void internalRestore(Version version, VersionSelector vsel, boolean removeExisting)
-            throws RepositoryException {
-
-        boolean success = false;
-        try {
-            internalRestore((VersionImpl) version, vsel, removeExisting);
-            session.save();
-            success = true;
-        } finally {
-            if (!success) {
-                // revert session
-                try {
-                    log.debug("reverting changes applied during restore...");
-                    session.refresh(false);
-                } catch (RepositoryException e) {
-                    log.error("Error while reverting changes applied during restore.", e);
-                }
-            }
-        }
-    }
-
-    /**
-     * Internal method to restore a version.
-     *
-     * @param version version to restore
-     * @param vsel the version selector that will select the correct version for
-     * OPV=Version child nodes.
-     * @param removeExisting remove existing flag
-     * @return array of restored versions
-     * @throws RepositoryException if an error occurs
-     */
-    public Version[] internalRestore(VersionImpl version, VersionSelector vsel,
-                                        boolean removeExisting)
             throws RepositoryException {
-
-        // fail if root version
-        if (version.isRootVersion()) {
-            throw new VersionException("Restore of root version not allowed.");
-        }
-
-        boolean isFull = checkVersionable();
-
-        // check permission
-        session.getAccessManager().checkPermission(getPrimaryPath(), Permission.VERSION_MNGMT);
-
-        // set jcr:isCheckedOut property to true, in order to avoid any conflicts
-        internalSetProperty(NameConstants.JCR_ISCHECKEDOUT, InternalValue.create(true));
-
-        // 1. The child node and properties of N will be changed, removed or
-        //    added to, depending on their corresponding copies in V and their
-        //    own OnParentVersion attributes (see 7.2.8, below, for details).
-        HashSet<Version> restored = new HashSet<Version>();
-        restoreFrozenState(version.getInternalFrozenNode(), vsel, restored, removeExisting);
-        restored.add(version);
-
-        if (isFull) {
-            // 2. N's jcr:baseVersion property will be changed to point to V.
-            internalSetProperty(
-                    NameConstants.JCR_BASEVERSION,
-                    InternalValue.create((NodeId) version.getId()));
-
-            // 4. N's jcr:predecessor property is set to null
-            internalSetProperty(NameConstants.JCR_PREDECESSORS, InternalValue.EMPTY_ARRAY, PropertyType.REFERENCE);
-
-            // also clear mergeFailed
-            internalSetProperty(NameConstants.JCR_MERGEFAILED, (InternalValue[]) null);
-
-        } else {
-            // with simple versioning, the node is checked in automatically,
-            // thus not allowing any branches
-            session.getVersionManager().checkin(this);
-        }
-        // 3. N's jcr:isCheckedOut property is set to false.
-        internalSetProperty(NameConstants.JCR_ISCHECKEDOUT, InternalValue.create(false));
-
-        return restored.toArray(new Version[restored.size()]);
+        getVersionManagerImpl().restoreByLabel(
+                getPath(), versionLabel, removeExisting);
     }
 
     /**
-     * Restores the properties and child nodes from the frozen state.
-     *
-     * @param freeze the frozen node
-     * @param vsel version selector
-     * @param restored set of restored versions
-     * @param removeExisting remove existing flag
-     * @throws RepositoryException if an error occurs
+     * Use {@link VersionManager#getVersionHistory(String)} instead
      */
-    public void restoreFrozenState(InternalFrozenNode freeze, VersionSelector vsel,
-                            Set<Version> restored, boolean removeExisting)
-            throws RepositoryException {
-
-        // check uuid
-        if (isNodeType(NameConstants.MIX_REFERENCEABLE)) {
-            if (!getNodeId().equals(freeze.getFrozenId())) {
-                throw new ItemExistsException("Unable to restore version of " + this + ". UUID changed.");
-            }
-        }
-
-        // check primary type
-        if (!freeze.getFrozenPrimaryType().equals(data.getNodeState().getNodeTypeName())) {
-            // todo: check with spec what should happen here
-            throw new ItemExistsException("Unable to restore version of " + this + ". PrimaryType changed.");
-        }
-
-        // adjust mixins
-        Name[] mixinNames = freeze.getFrozenMixinTypes();
-        setMixinTypesProperty(new HashSet<Name>(Arrays.asList(mixinNames)));
-
-        // copy frozen properties
-        PropertyState[] props = freeze.getFrozenProperties();
-        HashSet<Name> propNames = new HashSet<Name>();
-        for (PropertyState prop : props) {
-            // skip properties that should not to be reverted back
-            if (prop.getName().equals(NameConstants.JCR_ACTIVITY)) {
-                continue;
-            }
-            propNames.add(prop.getName());
-            if (prop.isMultiValued()) {
-                internalSetProperty(
-                        prop.getName(), prop.getValues(), prop.getType());
-            } else {
-                internalSetProperty(prop.getName(), prop.getValues()[0]);
-            }
-        }
-        // remove properties that do not exist in the frozen representation
-        PropertyIterator piter = getProperties();
-        while (piter.hasNext()) {
-            PropertyImpl prop = (PropertyImpl) piter.nextProperty();
-            // ignore some props that are not well guarded by the OPV
-            if (prop.getQName().equals(NameConstants.JCR_VERSIONHISTORY)) {
-                continue;
-            } else if (prop.getQName().equals(NameConstants.JCR_PREDECESSORS)) {
-                continue;
-            }
-            if (prop.getDefinition().getOnParentVersion() == OnParentVersionAction.COPY
-                    || prop.getDefinition().getOnParentVersion() == OnParentVersionAction.VERSION) {
-                if (!propNames.contains(prop.getQName())) {
-                    removeChildProperty(prop.getQName());
-                }
-            }
-        }
-
-        // add 'auto-create' properties that do not exist yet
-        NodeTypeManagerImpl ntMgr = session.getNodeTypeManager();
-        for (Name mixinName : mixinNames) {
-            NodeTypeImpl mixin = ntMgr.getNodeType(mixinName);
-            PropertyDefinition[] pda = mixin.getAutoCreatedPropertyDefinitions();
-            for (PropertyDefinition aPda : pda) {
-                PropertyDefinitionImpl pd = (PropertyDefinitionImpl) aPda;
-                if (!hasProperty(pd.getQName())) {
-                    createChildProperty(pd.getQName(), pd.getRequiredType(), pd);
-                }
-            }
-        }
-
-        // first delete some of the version histories
-        NodeIterator iter = getNodes();
-        while (iter.hasNext()) {
-            NodeImpl n = (NodeImpl) iter.nextNode();
-            if (n.getDefinition().getOnParentVersion() == OnParentVersionAction.COPY) {
-                // only remove OPV=Copy nodes
-                n.internalRemove(true);
-            } else if (n.getDefinition().getOnParentVersion() == OnParentVersionAction.VERSION) {
-                // only remove, if node to be restored does not contain child,
-                // or if restored child is not versionable
-                NodeId vhId = n.hasProperty(NameConstants.JCR_VERSIONHISTORY)
-                        ? new NodeId(n.getProperty(NameConstants.JCR_VERSIONHISTORY).getString())
-                        : null;
-                if (vhId == null || !freeze.hasFrozenHistory(vhId)) {
-                    n.internalRemove(true);
-                }
-            }
-        }
-
-        // restore the frozen nodes
-        InternalFreeze[] frozenNodes = freeze.getFrozenChildNodes();
-        for (InternalFreeze child : frozenNodes) {
-            NodeImpl restoredChild = null;
-            if (child instanceof InternalFrozenNode) {
-                InternalFrozenNode f = (InternalFrozenNode) child;
-                // check for existing
-                if (f.getFrozenId() != null) {
-                    try {
-                        NodeImpl existing = (NodeImpl) session.getNodeById(f.getFrozenId());
-                        // check if one of this restore trees node
-                        if (removeExisting) {
-                            existing.remove();
-                        } else if (existing.isShareable()) {
-                            // if existing node is shareable, then clone it
-                            restoredChild = clone(existing, f.getName());
-                        } else {
-                            // since we delete the OPV=Copy children beforehand, all
-                            // found nodes must be outside of this tree
-                            throw new ItemExistsException(
-                                    "Unable to restore node, item already"
-                                            + " exists outside of restored tree: "
-                                            + existing);
-                        }
-                    } catch (ItemNotFoundException e) {
-                        // ignore, item with uuid does not exist
-                    }
-                }
-                if (restoredChild == null) {
-                    restoredChild = addNode(f.getName(), f);
-                    restoredChild.restoreFrozenState(f, vsel, restored, removeExisting);
-                }
-
-            } else if (child instanceof InternalFrozenVersionHistory) {
-                InternalFrozenVersionHistory f = (InternalFrozenVersionHistory) child;
-                VersionHistory history = (VersionHistory) session.getNodeById(f.getVersionHistoryId());
-                NodeId nodeId = NodeId.valueOf(history.getVersionableUUID());
-                String oldVersion = "jcr:dummy";
-
-                // check if representing versionable already exists somewhere
-                if (itemMgr.itemExists(nodeId)) {
-                    NodeImpl n = session.getNodeById(nodeId);
-                    if (removeExisting) {
-                        String dstPath = getPath() + "/" + n.getName();
-                        if (!n.getPath().equals(dstPath)) {
-                            session.move(n.getPath(), dstPath);
-                        }
-                        oldVersion = n.getBaseVersion().getName();
-                    } else if (n.getParent().isSame(this)) {
-                        n.internalRemove(true);
-                    } else {
-                        // since we delete the OPV=Copy children beforehand, all
-                        // found nodes must be outside of this tree
-                        throw new ItemExistsException(
-                                "Unable to restore node, item already exists"
-                                        + " outside of restored tree: " + n);
-                    }
-                }
-                // get desired version from version selector
-                VersionImpl v = (VersionImpl) vsel.select(history);
-
-                // check existing version of item exists
-                if (!itemMgr.itemExists(nodeId)) {
-                    if (v == null) {
-                        // if version selector was unable to select version,
-                        // choose the initial one
-                        Version[] vs = history.getRootVersion().getSuccessors();
-                        if (vs.length == 0) {
-                            String msg = "Unable to select appropariate version for "
-                                    + child.getName() + " using " + vsel;
-                            log.error(msg);
-                            throw new VersionException(msg);
-                        }
-                        v = (VersionImpl) vs[0];
-                    }
-                    restoredChild = addNode(child.getName(), v.getInternalFrozenNode());
-                } else {
-                    restoredChild = session.getNodeById(nodeId);
-                    if (v == null || oldVersion == null || v.getName().equals(oldVersion)) {
-                        v = null;
-                    }
-                }
-                if (v != null) {
-                    try {
-                        restoredChild.internalRestore(v, vsel, removeExisting);
-                    } catch (RepositoryException e) {
-                        log.error("Error while restoring node: " + e);
-                        log.error("  child path: " + restoredChild);
-                        log.error("  selected version: " + v.getName());
-                        StringBuffer avail = new StringBuffer();
-                        VersionIterator vi = history.getAllVersions();
-                        while (vi.hasNext()) {
-                            avail.append(vi.nextVersion().getName());
-                            if (vi.hasNext()) {
-                                avail.append(", ");
-                            }
-                        }
-                        log.error("  available versions: " + avail);
-                        log.error("  versionselector: " + vsel);
-                        throw e;
-                    }
-                    // add this version to set
-                    restored.add(v);
-                }
-            }
-            // ensure proper ordering (issue JCR-469)
-            if (restoredChild != null
-                    && getPrimaryNodeType().hasOrderableChildNodes()) {
-                orderBefore(restoredChild.getPrimaryPath().getNameElement(), null);
-            }
-        }
+    @Deprecated
+    public VersionHistory getVersionHistory() throws RepositoryException {
+        return getVersionManagerImpl().getVersionHistory(getPath());
     }
 
     /**
-     * Copies a property to this node
-     *
-     * @param prop property to copy from
-     * @throws RepositoryException if an error occurs
+     * Use {@link VersionManager#getBaseVersion(String)} instead
      */
-    protected void internalCopyPropertyFrom(PropertyImpl prop) throws RepositoryException {
-        if (prop.getDefinition().isMultiple()) {
-            Value[] values = prop.getValues();
-            InternalValue[] ivalues = new InternalValue[values.length];
-            for (int i = 0; i < values.length; i++) {
-                ivalues[i] = InternalValue.create(values[i], session, rep.getDataStore());
-            }
-            internalSetProperty(prop.getQName(), ivalues);
-        } else {
-            InternalValue value = InternalValue.create(prop.getValue(), session, rep.getDataStore());
-            internalSetProperty(prop.getQName(), value);
-        }
+    @Deprecated
+    public Version getBaseVersion() throws RepositoryException {
+        return getVersionManagerImpl().getBaseVersion(getPath());
     }
 
     //------------------------------------------------------< locking support >
@@ -4773,10 +3612,7 @@
         // check state of this instance
         sanityCheck();
 
-        ArrayList nodes = new ArrayList();
-        // traverse children using a special filtering 'collector'
-        accept(new ChildrenCollectorFilter(nameGlobs, nodes, true, false, 1));
-        return new NodeIteratorAdapter(nodes);
+        return ChildrenCollectorFilter.collectChildNodes(this, nameGlobs);
     }
 
     /**
@@ -4787,10 +3623,7 @@
         // check state of this instance
         sanityCheck();
 
-        ArrayList props = new ArrayList();
-        // traverse children using a special filtering 'collector'
-        accept(new ChildrenCollectorFilter(nameGlobs, props, false, true, 1));
-        return new PropertyIteratorAdapter(props);
+        return ChildrenCollectorFilter.collectProperties(this, nameGlobs);
     }
 
     /**
@@ -4804,7 +3637,7 @@
 
         // make sure this node is checked-out, neither protected nor locked and
         // the editing session has sufficient permission to change the primary type.
-        int options = ItemValidator.CHECK_VERSIONING | ItemValidator.CHECK_LOCK
+        int options = ItemValidator.CHECK_CHECKED_OUT | ItemValidator.CHECK_LOCK
                 | ItemValidator.CHECK_CONSTRAINTS | ItemValidator.CHECK_HOLD;
         session.getValidator().checkModify(this, options, Permission.NODE_TYPE_MNGMT);
 
@@ -5069,28 +3902,116 @@
         return prop;
     }
 
-    // TODO: JCR-1565 JSR 283 lifecycle management
+    /**
+     * Returns all allowed transitions from the current lifecycle state of
+     * this node.
+     * <p>
+     * The lifecycle policy node referenced by the "jcr:lifecyclePolicy"
+     * property is expected to contain a "transitions" node with a list of
+     * child nodes, one for each transition. These transition nodes must
+     * have single-valued string "from" and "to" properties that identify
+     * the allowed source and target states of each transition.
+     * <p>

[... 114 lines stripped ...]