jackrabbit-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From tri...@apache.org
Subject svn commit: rev 53784 - in incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core: . nodetype util version
Date Tue, 05 Oct 2004 08:57:46 GMT
Author: tripod
Date: Tue Oct  5 01:57:45 2004
New Revision: 53784

Added:
   incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/util/Text.java   (contents, props changed)
   incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/version/InternalFreeze.java   (contents, props changed)
   incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/version/InternalFrozenNode.java   (contents, props changed)
   incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/version/InternalFrozenVersionHistory.java   (contents, props changed)
   incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/version/InternalVersion.java   (contents, props changed)
   incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/version/InternalVersionHistory.java   (contents, props changed)
   incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/version/NodeWrapper.java   (contents, props changed)
   incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/version/PersistentNode.java   (contents, props changed)
   incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/version/PersistentProperty.java   (contents, props changed)
Removed:
   incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/version/FrozenNode.java
Modified:
   incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/ItemImpl.java
   incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/ItemManager.java
   incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/NodeImpl.java
   incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/PropertyImpl.java
   incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/RepositoryImpl.java
   incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/WorkspaceImpl.java
   incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/nodetype/builtin_nodetypes.xml
   incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/version/GenericVersionSelector.java
   incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/version/VersionHistoryImpl.java
   incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/version/VersionImpl.java
   incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/version/VersionIteratorImpl.java
   incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/version/VersionManager.java
   incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/version/package.html
Log:
versioning implementation is now stores directly in persistence state.

Modified: incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/ItemImpl.java
==============================================================================
--- incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/ItemImpl.java	(original)
+++ incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/ItemImpl.java	Tue Oct  5 01:57:45 2004
@@ -24,7 +24,7 @@
 import org.apache.jackrabbit.core.observation.ObservationManagerFactory;
 import org.apache.jackrabbit.core.state.*;
 import org.apache.jackrabbit.core.util.uuid.UUID;
-import org.apache.jackrabbit.core.version.VersionImpl;
+import org.apache.jackrabbit.core.version.VersionManager;
 import org.apache.log4j.Logger;
 
 import javax.jcr.*;
@@ -655,10 +655,10 @@
                 NodeImpl node = (NodeImpl) itemMgr.getItem(itemState.getId());
                 if (node.isNodeType(NodeTypeRegistry.MIX_VERSIONABLE)) {
                     VersionHistory hist = rep.getVersionManager().createVersionHistory(node);
-                    node.internalSetProperty(VersionImpl.PROPNAME_VERSION_HISTORY, InternalValue.create(new UUID(hist.getUUID())));
-                    node.internalSetProperty(VersionImpl.PROPNAME_BASE_VERSION, InternalValue.create(new UUID(hist.getRootVersion().getUUID())));
-                    node.internalSetProperty(VersionImpl.PROPNAME_IS_CHECKED_OUT, InternalValue.create(true));
-                    node.internalSetProperty(VersionImpl.PROPNAME_PREDECESSORS, new InternalValue[]{InternalValue.create(new UUID(hist.getRootVersion().getUUID()))});
+                    node.internalSetProperty(VersionManager.PROPNAME_VERSION_HISTORY, InternalValue.create(new UUID(hist.getUUID())));
+                    node.internalSetProperty(VersionManager.PROPNAME_BASE_VERSION, InternalValue.create(new UUID(hist.getRootVersion().getUUID())));
+                    node.internalSetProperty(VersionManager.PROPNAME_IS_CHECKED_OUT, InternalValue.create(true));
+                    node.internalSetProperty(VersionManager.PROPNAME_PREDECESSORS, new InternalValue[]{InternalValue.create(new UUID(hist.getRootVersion().getUUID()))});
                 }
             }
         }

Modified: incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/ItemManager.java
==============================================================================
--- incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/ItemManager.java	(original)
+++ incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/ItemManager.java	Tue Oct  5 01:57:45 2004
@@ -16,12 +16,8 @@
 package org.apache.jackrabbit.core;
 
 import org.apache.commons.collections.ReferenceMap;
-import org.apache.jackrabbit.core.nodetype.NodeTypeRegistry;
 import org.apache.jackrabbit.core.state.*;
 import org.apache.jackrabbit.core.util.IteratorHelper;
-import org.apache.jackrabbit.core.version.FrozenNode;
-import org.apache.jackrabbit.core.version.VersionHistoryImpl;
-import org.apache.jackrabbit.core.version.VersionImpl;
 import org.apache.log4j.Logger;
 
 import javax.jcr.*;
@@ -371,17 +367,8 @@
         // in order to maintain item cache consistency
         ItemLifeCycleListener[] listeners = new ItemLifeCycleListener[]{this};
 
-        // create node object; create specialized nodes for nodes of specific
-        // primary types (i.e. nt:version & nt:versionHistory)
-        if (state.getNodeTypeName().equals(NodeTypeRegistry.NT_VERSION_HISTORY)) {
-            return new VersionHistoryImpl(this, session, id, state, def, listeners);
-        } else if (state.getNodeTypeName().equals(NodeTypeRegistry.NT_FROZEN)) {
-            return new FrozenNode(this, session, id, state, def, listeners);
-        } else if (state.getNodeTypeName().equals(NodeTypeRegistry.NT_VERSION)) {
-            return new VersionImpl(this, session, id, state, def, listeners);
-        } else {
-            return new NodeImpl(this, session, id, state, def, listeners);
-        }
+        // create node object
+        return new NodeImpl(this, session, id, state, def, listeners);
     }
 
     NodeImpl createNodeInstance(NodeState state) throws RepositoryException {

Modified: incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/NodeImpl.java
==============================================================================
--- incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/NodeImpl.java	(original)
+++ incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/NodeImpl.java	Tue Oct  5 01:57:45 2004
@@ -17,13 +17,10 @@
 
 import org.apache.jackrabbit.core.nodetype.*;
 import org.apache.jackrabbit.core.state.*;
+import org.apache.jackrabbit.core.version.*;
+import org.apache.jackrabbit.core.util.uuid.UUID;
 import org.apache.jackrabbit.core.util.ChildrenCollector;
 import org.apache.jackrabbit.core.util.IteratorHelper;
-import org.apache.jackrabbit.core.util.uuid.UUID;
-import org.apache.jackrabbit.core.version.FrozenNode;
-import org.apache.jackrabbit.core.version.GenericVersionSelector;
-import org.apache.jackrabbit.core.version.VersionImpl;
-import org.apache.jackrabbit.core.version.VersionSelector;
 import org.apache.log4j.Logger;
 
 import javax.jcr.*;
@@ -32,7 +29,6 @@
 import javax.jcr.lock.LockException;
 import javax.jcr.nodetype.*;
 import javax.jcr.util.TraversingItemVisitor;
-import javax.jcr.version.OnParentVersionAction;
 import javax.jcr.version.Version;
 import javax.jcr.version.VersionException;
 import javax.jcr.version.VersionHistory;
@@ -117,16 +113,16 @@
 	} else if (nt.getQName().equals(NodeTypeRegistry.MIX_VERSIONABLE)) {
 	    // mix:versionable node type
 	    VersionHistory hist = rep.getVersionManager().getOrCreateVersionHistory(this);
-	    if (name.equals(VersionImpl.PROPNAME_VERSION_HISTORY)) {
+	    if (name.equals(InternalVersion.PROPNAME_VERSION_HISTORY)) {
 		// jcr:versionHistory property
 		genValues = new InternalValue[]{InternalValue.create(new UUID(hist.getUUID()))};
-	    } else if (name.equals(VersionImpl.PROPNAME_BASE_VERSION)) {
+	    } else if (name.equals(InternalVersion.PROPNAME_BASE_VERSION)) {
 		// jcr:baseVersion property
 		genValues = new InternalValue[]{InternalValue.create(new UUID(hist.getRootVersion().getUUID()))};
-	    } else if (name.equals(VersionImpl.PROPNAME_IS_CHECKED_OUT)) {
+	    } else if (name.equals(InternalVersion.PROPNAME_IS_CHECKED_OUT)) {
 		// jcr:isCheckedOut property
 		genValues = new InternalValue[]{InternalValue.create(true)};
-	    } else if (name.equals(VersionImpl.PROPNAME_PREDECESSORS)) {
+	    } else if (name.equals(InternalVersion.PROPNAME_PREDECESSORS)) {
 		// jcr:predecessors property
 		genValues = new InternalValue[]{InternalValue.create(new UUID(hist.getRootVersion().getUUID()))};
 	    }
@@ -1745,10 +1741,10 @@
             // todo centralize version history creation code (currently in NodeImpl.addMixin & ItemImpl.initVersionHistories
             if (ntName.equals(NodeTypeRegistry.MIX_VERSIONABLE)) {
                 VersionHistory hist = rep.getVersionManager().createVersionHistory(this);
-                internalSetProperty(VersionImpl.PROPNAME_VERSION_HISTORY, InternalValue.create(new UUID(hist.getUUID())));
-                internalSetProperty(VersionImpl.PROPNAME_BASE_VERSION, InternalValue.create(new UUID(hist.getRootVersion().getUUID())));
-                internalSetProperty(VersionImpl.PROPNAME_IS_CHECKED_OUT, InternalValue.create(true));
-                internalSetProperty(VersionImpl.PROPNAME_PREDECESSORS, new InternalValue[]{InternalValue.create(new UUID(hist.getRootVersion().getUUID()))});
+                internalSetProperty(VersionManager.PROPNAME_VERSION_HISTORY, InternalValue.create(new UUID(hist.getUUID())));
+                internalSetProperty(VersionManager.PROPNAME_BASE_VERSION, InternalValue.create(new UUID(hist.getRootVersion().getUUID())));
+                internalSetProperty(VersionManager.PROPNAME_IS_CHECKED_OUT, InternalValue.create(true));
+                internalSetProperty(VersionManager.PROPNAME_PREDECESSORS, new InternalValue[]{InternalValue.create(new UUID(hist.getRootVersion().getUUID()))});
             }
         } catch (RepositoryException re) {
             // try to undo the modifications by removing the mixin
@@ -2056,6 +2052,15 @@
         return thisState.getUUID();
     }
 
+    /**
+     * @see Node#getUUID()
+     */
+    public String internalGetUUID() throws RepositoryException {
+        // check state of this instance
+        checkItemState();
+        return ((NodeState) state).getUUID();
+    }
+
     //-------------------------------------------------< versioning support >---
     /**
      * Checks if this node is versionable, i.e. has 'mix:versionable'.
@@ -2090,11 +2095,11 @@
             throw new IllegalStateException(msg);
         }
         Version v = rep.getVersionManager().checkin(this);
-        Property prop = internalSetProperty(VersionImpl.PROPNAME_IS_CHECKED_OUT, InternalValue.create(false));
+        Property prop = internalSetProperty(VersionManager.PROPNAME_IS_CHECKED_OUT, InternalValue.create(false));
         prop.save();
-        prop = internalSetProperty(VersionImpl.PROPNAME_BASE_VERSION, InternalValue.create(new UUID(v.getUUID())));
+        prop = internalSetProperty(VersionManager.PROPNAME_BASE_VERSION, InternalValue.create(new UUID(v.getUUID())));
         prop.save();
-        prop = internalSetProperty(VersionImpl.PROPNAME_PREDECESSORS, new InternalValue[0]);
+        prop = internalSetProperty(VersionManager.PROPNAME_PREDECESSORS, new InternalValue[0]);
         prop.save();
         return v;
     }
@@ -2109,14 +2114,13 @@
             log.debug(msg);
             throw new VersionException(msg);
         }
-        Property prop = internalSetProperty(VersionImpl.PROPNAME_IS_CHECKED_OUT, InternalValue.create(true));
+        Property prop = internalSetProperty(VersionManager.PROPNAME_IS_CHECKED_OUT, InternalValue.create(true));
         prop.save();
-        prop = internalSetProperty(VersionImpl.PROPNAME_PREDECESSORS,
+        prop = internalSetProperty(VersionManager.PROPNAME_PREDECESSORS,
                 new InternalValue[]{
                     InternalValue.create(new UUID(getBaseVersion().getUUID()))
                 });
         prop.save();
-
     }
 
     /**
@@ -2135,7 +2139,7 @@
             log.debug(msg);
             throw new VersionException(msg);
         }
-        Value[] values = getProperty(VersionImpl.PROPNAME_PREDECESSORS).getValues();
+        Value[] values = getProperty(VersionManager.PROPNAME_PREDECESSORS).getValues();
         InternalValue[] preds = new InternalValue[values.length + 1];
         for (int i = 0; i < values.length; i++) {
             if (values[i].getString().equals(v.getUUID())) {
@@ -2145,7 +2149,7 @@
             preds[i + 1] = InternalValue.create(new UUID(values[i].getString()));
         }
         preds[0] = InternalValue.create(new UUID(v.getUUID()));
-        Property prop = internalSetProperty(VersionImpl.PROPNAME_PREDECESSORS, preds);
+        Property prop = internalSetProperty(VersionManager.PROPNAME_PREDECESSORS, preds);
         prop.save();
     }
 
@@ -2159,7 +2163,7 @@
             throw new VersionException("Unable to remove predecessor. Node not checked-out.");
         }
 
-        Value[] values = getProperty(VersionImpl.PROPNAME_PREDECESSORS).getValues();
+        Value[] values = getProperty(VersionManager.PROPNAME_PREDECESSORS).getValues();
         if (values.length > 0) {
             boolean found = false;
             InternalValue[] preds = new InternalValue[values.length - 1];
@@ -2173,7 +2177,7 @@
                 }
             }
             if (found) {
-                Property prop = internalSetProperty(VersionImpl.PROPNAME_PREDECESSORS, preds);
+                Property prop = internalSetProperty(VersionManager.PROPNAME_PREDECESSORS, preds);
                 prop.save();
                 return;
             }
@@ -2188,8 +2192,8 @@
      * @see Version#getPredecessors()
      */
     public Version[] getPredecessors() throws RepositoryException {
-        if (hasProperty(VersionImpl.PROPNAME_PREDECESSORS)) {
-            Value[] values = getProperty(VersionImpl.PROPNAME_PREDECESSORS).getValues();
+        if (hasProperty(VersionManager.PROPNAME_PREDECESSORS)) {
+            Value[] values = getProperty(VersionManager.PROPNAME_PREDECESSORS).getValues();
             Version[] preds = new Version[values.length];
             for (int i = 0; i < values.length; i++) {
                 preds[i] = (Version) session.getNodeByUUID(values[i].getString());
@@ -2373,14 +2377,14 @@
             return null;
         }
         // test versions
-        VersionImpl v = (VersionImpl) getBaseVersion();
-        VersionImpl vp = (VersionImpl) srcNode.getBaseVersion();
+        InternalVersion v = ((VersionImpl) getBaseVersion()).getInternalVersion();
+        InternalVersion vp = ((VersionImpl) srcNode.getBaseVersion()).getInternalVersion();
         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)) {
+        } else if (v.equals(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
@@ -2396,7 +2400,7 @@
                 List values = hasProperty(ItemImpl.PROPNAME_MERGE_FAILED)
                         ? Arrays.asList(getProperty(ItemImpl.PROPNAME_MERGE_FAILED).getValues())
                         : new ArrayList();
-                values.add(new ReferenceValue(vp));
+                values.add(new ReferenceValue(srcNode.getBaseVersion()));
                 setProperty(ItemImpl.PROPNAME_MERGE_FAILED, (Value[]) values.toArray(new Value[values.size()]));
                 return null;
             } else {
@@ -2413,7 +2417,7 @@
     public boolean isCheckedOut()
             throws UnsupportedRepositoryOperationException, RepositoryException {
         checkVersionable();
-        return getProperty(VersionImpl.PROPNAME_IS_CHECKED_OUT).getBoolean();
+        return getProperty(VersionManager.PROPNAME_IS_CHECKED_OUT).getBoolean();
     }
 
     /**
@@ -2422,8 +2426,8 @@
     public boolean safeIsCheckedOut() throws RepositoryException {
         // what if this node is not versionable but has OPV==Copy?
         // do we need to search ancestors for a verionable node?
-        return hasProperty(VersionImpl.PROPNAME_IS_CHECKED_OUT)
-                ? getProperty(VersionImpl.PROPNAME_IS_CHECKED_OUT).getBoolean()
+        return hasProperty(VersionManager.PROPNAME_IS_CHECKED_OUT)
+                ? getProperty(VersionManager.PROPNAME_IS_CHECKED_OUT).getBoolean()
                 : true;
     }
 
@@ -2442,7 +2446,7 @@
 
         GenericVersionSelector gvs = new GenericVersionSelector();
         gvs.setName(versionName);
-        internalRestore((VersionImpl) getVersionHistory().getVersion(versionName), gvs);
+        internalRestore(getVersionHistory().getVersion(versionName), gvs);
         save();
     }
 
@@ -2462,7 +2466,7 @@
         if (!version.getParent().getUUID().equals(getVersionHistory().getUUID())) {
             throw new VersionException("Unable to restore version. Not same version history.");
         }
-        internalRestore((VersionImpl) version, new GenericVersionSelector(version.getCreated()));
+        internalRestore(version, new GenericVersionSelector(version.getCreated()));
         save();
     }
 
@@ -2481,16 +2485,15 @@
         }
 
         // recreate node from frozen state
-        NodeImpl node = addNode(relPath, (VersionImpl) version);
+        NodeImpl node = addNode(relPath, ((VersionImpl) version).getFrozenNode());
         if (!node.isCheckedOut()) {
             String msg = "Unable to restore version. Node is not checked-out " + node.safeGetJCRPath();
             log.debug(msg);
             throw new VersionException(msg);
         }
 
-        node.internalRestore((VersionImpl) version, new GenericVersionSelector(version.getCreated()));
+        node.internalRestore(version, new GenericVersionSelector(version.getCreated()));
         node.getParent().save();
-
     }
 
     /**
@@ -2505,7 +2508,7 @@
             throw new VersionException(msg);
         }
 
-        VersionImpl v = (VersionImpl) getVersionHistory().getVersionByLabel(versionLabel);
+        Version v = getVersionHistory().getVersionByLabel(versionLabel);
         if (v == null) {
             throw new VersionException("No version for label " + versionLabel + " found.");
         }
@@ -2521,9 +2524,8 @@
      * explictly specified in the nt:frozen node.
      * <p/>
      *
-     * @param relPath The path of the new <code>Node</code> that is to be created,
-     *                the last item of this pathwill be the name of the new <code>Node</code>.
-     * @param frozen  The frozen node that contains the creation information
+     * @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).
@@ -2535,34 +2537,97 @@
      *                                      child of a <code>Property</code>
      * @throws RepositoryException          if another error occurs.
      */
-    private NodeImpl addNode(String relPath, FrozenNode frozen)
+    private NodeImpl addNode(QName name, InternalFrozenNode frozen)
             throws ItemExistsException, PathNotFoundException,
             ConstraintViolationException, NoSuchNodeTypeException,
             RepositoryException {
 
         // get frozen node type
         NodeTypeManagerImpl ntMgr = session.getNodeTypeManager();
-        String ntName = frozen.getProperty(VersionImpl.PROPNAME_FROZEN_PRIMARY_TYPE).getString();
-        NodeTypeImpl nt = (NodeTypeImpl) ntMgr.getNodeType(ntName);
+        NodeTypeImpl nt = (NodeTypeImpl) ntMgr.getNodeType(frozen.getFrozenPrimaryType());
 
         // get frozen uuid
-        String uuid = frozen.hasProperty(VersionImpl.PROPNAME_FROZEN_UUID)
-                ? frozen.getProperty(VersionImpl.PROPNAME_FROZEN_UUID).getString()
-                : null;
+        String uuid = frozen.getFrozenUUID();
+
+        NodeImpl node = internalAddChildNode(name, nt, uuid);
 
         // get frozen mixin
+        // todo: also respect mixing types on creation?
+        QName[] mxNames = frozen.getFrozenMixinTypes();
+        for (int i = 0; i < mxNames.length; i++) {
+            try {
+                node.addMixin(mxNames[i].toJCRName(session.getNamespaceResolver()));
+            } catch (NoPrefixDeclaredException e) {
+                throw new NoSuchNodeTypeException("Unable to resolve mixin: " + e.toString());
+            }
+        }
+        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
+     * explictly 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 = (NodeTypeImpl) ntMgr.getNodeType(frozen.getFrozenPrimaryType());
+
+        // get frozen uuid
+        String uuid = frozen.getFrozenUUID();
+
         NodeImpl node = internalAddNode(relPath, nt, uuid);
 
+        // get frozen mixin
         // todo: also respect mixing types on creation?
-        if (frozen.hasProperty(VersionImpl.PROPNAME_FROZEN_MIXIN_TYPES)) {
-            Value[] mxNames = frozen.getProperty(VersionImpl.PROPNAME_FROZEN_MIXIN_TYPES).getValues();
-            for (int i = 0; i < mxNames.length; i++) {
-                node.addMixin(mxNames[i].getString());
+        QName[] mxNames = frozen.getFrozenMixinTypes();
+        for (int i = 0; i < mxNames.length; i++) {
+            try {
+                node.addMixin(mxNames[i].toJCRName(session.getNamespaceResolver()));
+            } catch (NoPrefixDeclaredException e) {
+                throw new NoSuchNodeTypeException("Unable to resolve mixin: " + e.toString());
             }
         }
         return node;
     }
 
+
+    /**
+     * Internal method to restore a version.
+     *
+     * @param version
+     * @param vsel    the version selector that will select the correct version for
+     *                OPV=Version childnodes.
+     * @throws UnsupportedRepositoryOperationException
+     *
+     * @throws RepositoryException
+     */
+    private void internalRestore(Version version, VersionSelector vsel)
+            throws UnsupportedRepositoryOperationException, RepositoryException {
+        internalRestore(((VersionImpl) version).getInternalVersion(), vsel);
+    }
+
     /**
      * Internal method to restore a version.
      *
@@ -2573,38 +2638,24 @@
      *
      * @throws RepositoryException
      */
-    private void internalRestore(VersionImpl version, VersionSelector vsel)
+    private void internalRestore(InternalVersion version, VersionSelector vsel)
             throws UnsupportedRepositoryOperationException, RepositoryException {
 
         // 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).
-        restoreFrozenState(version, vsel);
+        restoreFrozenState(version.getFrozenNode(), vsel);
 
         // 2. Ns jcr:baseVersion property will be changed to point to V.
-        internalSetProperty(VersionImpl.PROPNAME_BASE_VERSION, InternalValue.create(new UUID(version.getUUID())));
+        internalSetProperty(VersionManager.PROPNAME_BASE_VERSION, InternalValue.create(new UUID(version.getUUID())));
 
         // 3. Ns jcr:isCheckedOut property is set to false.
-        internalSetProperty(VersionImpl.PROPNAME_IS_CHECKED_OUT, InternalValue.create(false));
+        internalSetProperty(VersionManager.PROPNAME_IS_CHECKED_OUT, InternalValue.create(false));
 
         // 4. N's jcr:predecessor property is set to null
-        internalSetProperty(VersionImpl.PROPNAME_PREDECESSORS, new InternalValue[0]);
+        internalSetProperty(VersionManager.PROPNAME_PREDECESSORS, new InternalValue[0]);
     }
 
-    /**
-     * Little helper to retrieve the opv value for a property. depends on the
-     * implementaion. if nt:frozen is used, need to lookup prop def.
-     *
-     * @param name
-     * @param type
-     * @param multiValued
-     * @return
-     * @throws RepositoryException
-     */
-    private int guessOPV(QName name, int type, boolean multiValued) throws RepositoryException {
-        PropertyDefImpl def = getApplicablePropertyDef(name, type, multiValued);
-        return def.getOnParentVersion();
-    }
 
     /**
      * Creates the frozen state from a node
@@ -2612,114 +2663,88 @@
      * @param freeze
      * @throws RepositoryException
      */
-    void restoreFrozenState(FrozenNode freeze, VersionSelector vsel)
+    void restoreFrozenState(InternalFrozenNode freeze, VersionSelector vsel)
             throws RepositoryException {
-        PropertyIterator piter = freeze.getProperties();
-        while (piter.hasNext()) {
-            PropertyImpl prop = (PropertyImpl) piter.nextProperty();
-            // check for special property
-            if (prop.getQName().equals(VersionImpl.PROPNAME_FROZEN_UUID)) {
-                // check if uuid is the same as 'this' one.
-                if (!isNodeType(NodeTypeRegistry.MIX_REFERENCEABLE)) {
-                    throw new ItemExistsException("Unable to restore version of " + safeGetJCRPath() + ". Not referenceable.");
-                }
-                if (!prop.getString().equals(this.getUUID())) {
-                    throw new ItemExistsException("Unable to restore version of " + safeGetJCRPath() + ". UUID changed.");
-                }
-            } else if (prop.getQName().equals(VersionImpl.PROPNAME_FROZEN_PRIMARY_TYPE)) {
-                // check if primaryType is the same as 'this' one.
-                if (!prop.getString().equals(this.getPrimaryNodeType().getName())) {
-                    // todo: check with spec what should happen here
-                    throw new ItemExistsException("Unable to restore version of " + safeGetJCRPath() + ". PrimaryType changed.");
-                }
-            } else if (prop.getQName().equals(VersionImpl.PROPNAME_FROZEN_MIXIN_TYPES)) {
-                // add mixins
-                Value[] values = prop.getValues();
-                NodeType[] mixins = getMixinNodeTypes();
-                for (int i = 0; i < values.length; i++) {
-                    String name = values[i].getString();
-                    boolean found = false;
-                    for (int j = 0; j < mixins.length; j++) {
-                        if (name.equals(mixins[j].getName())) {
-                            // clear
-                            mixins[j] = null;
-                            found = true;
-                            break;
-                        }
-                    }
-                    if (!found) {
-                        addMixin(name);
-                    }
-                }
-                // remove additional
-                for (int i = 0; i < mixins.length; i++) {
-                    if (mixins[i] != null) {
-                        removeMixin(mixins[i].getName());
-                    }
+        // check uuid
+        if (!isNodeType(NodeTypeRegistry.MIX_REFERENCEABLE)) {
+            throw new ItemExistsException("Unable to restore version of " + safeGetJCRPath() + ". Not referenceable.");
+        }
+        if (!freeze.getFrozenUUID().equals(getUUID())) {
+            throw new ItemExistsException("Unable to restore version of " + safeGetJCRPath() + ". UUID changed.");
+        }
+        // check primarty type
+        if (!freeze.getFrozenPrimaryType().equals(nodeType.getQName())) {
+            // todo: check with spec what should happen here
+            throw new ItemExistsException("Unable to restore version of " + safeGetJCRPath() + ". PrimaryType changed.");
+        }
+        // adjust mixins
+        QName[] values = freeze.getFrozenMixinTypes();
+        NodeType[] mixins = getMixinNodeTypes();
+        for (int i = 0; i < values.length; i++) {
+            boolean found = false;
+            for (int j = 0; j < mixins.length; j++) {
+                if (values[i].equals(((NodeTypeImpl) mixins[j]).getQName())) {
+                    // clear
+                    mixins[j] = null;
+                    found = true;
+                    break;
                 }
-            } else if (prop.getQName().equals(VersionImpl.PROPNAME_SUCCESSORS)) {
-                // ignore
-            } else if (prop.getQName().equals(VersionImpl.PROPNAME_CREATED)) {
-                // ignore
-            } else if (prop.getQName().equals(VersionImpl.PROPNAME_VERSION_LABELS)) {
-                // ignore
-            } else if (prop.getQName().equals(VersionImpl.PROPNAME_VERSION_HISTORY)) {
-                // ignore
-            } else if (prop.getQName().equals(VersionImpl.PROPNAME_PRIMARYTYPE)) {
-                // ignore
-            } else if (prop.getQName().equals(VersionImpl.PROPNAME_MIXINTYPES)) {
-                // ignore
-            } else if (prop.getQName().equals(VersionImpl.PROPNAME_UUID)) {
-                // ignore
-            } else {
-                // normal property
-                int type = PropertyType.UNDEFINED;
-                if (prop.getDefinition().isMultiple()) {
-                    Value[] values = prop.getValues();
-                    if (values.length != 0) {
-                        type = values[0].getType();
-                    }
-                } else {
-                    type = prop.getValue().getType();
+            }
+            if (!found) {
+                try {
+                    addMixin(values[i].toJCRName(session.getNamespaceResolver()));
+                } catch (NoPrefixDeclaredException e) {
+                    String msg = "Unable to add mixin for restored node: " + e.getMessage();
+                    log.error(msg);
+                    throw new RepositoryException(msg);
                 }
-                int opv = guessOPV(prop.getQName(), type, prop.getDefinition().isMultiple());
-                switch (opv) {
-                    case OnParentVersionAction.ABORT:
-                        throw new RepositoryException("Checkin aborted due to OPV in " + prop.safeGetJCRPath());
-                    case OnParentVersionAction.COMPUTE:
-                    case OnParentVersionAction.IGNORE:
-                    case OnParentVersionAction.INITIALIZE:
-                        break;
-                    case OnParentVersionAction.VERSION:
-                    case OnParentVersionAction.COPY:
-                        internalCopyPropertyFrom(prop);
-                        break;
+            }
+        }
+        // remove additional
+        for (int i = 0; i < mixins.length; i++) {
+            if (mixins[i] != null) {
+                removeMixin(mixins[i].getName());
+            }
+        }
+
+        // copy frozen properties
+        PersistentProperty[] props = freeze.getFrozenProperties();
+        for (int i = 0; i < props.length; i++) {
+            PersistentProperty prop = props[i];
+            if (prop.getValues().length == 1) {
+                try {
+                    internalSetProperty(props[i].getName(), prop.getValues()[0]);
+                    continue;
+                } catch (RepositoryException e) {
+// ignore and try multiple below
                 }
             }
+            internalSetProperty(props[i].getName(), prop.getValues());
+
         }
 
-        // iterate over the nodes
-        NodeIterator niter = freeze.getNodes();
-        while (niter.hasNext()) {
-            NodeImpl child = (NodeImpl) niter.nextNode();
-            if (child.isNodeType(NodeTypeRegistry.NT_FROZEN)) {
-                FrozenNode f = (FrozenNode) child;
+        // restore the frozen nodes
+        InternalFreeze[] frozenNodes = freeze.getFrozenChildNodes();
+        for (int i = 0; i < frozenNodes.length; i++) {
+            InternalFreeze child = frozenNodes[i];
+            if (child instanceof InternalFrozenNode) {
+                InternalFrozenNode f = (InternalFrozenNode) child;
                 // if frozen node exist, replace
                 // todo: make work for same name siblings
-                if (hasNode(child.getName())) {
-                    remove(child.getName());
+                if (hasNode(f.getName())) {
+                    getNode(f.getName()).remove(".");
                 }
-                NodeImpl n = addNode(child.getName(), f);
+                NodeImpl n = addNode(f.getName(), f);
                 n.restoreFrozenState(f, vsel);
-            } else if (child.isNodeType(NodeTypeRegistry.NT_FROZEN_VERSIONABLE_CHILD)) {
+            } else if (child instanceof InternalFrozenVersionHistory) {
                 // check if child already exists
                 if (hasNode(child.getName())) {
                     // do nothing
                 } else {
                     // get desired version from version selector
-                    VersionHistory vh = (VersionHistory) child.getProperty(VersionImpl.PROPNAME_VERSION_HISTORY).getNode();
-                    VersionImpl v = (VersionImpl) vsel.select(vh);
-                    NodeImpl node = addNode(child.getName(), v);
+                    VersionHistory history = ((InternalFrozenVersionHistory) child).getVersionHistory(session);
+                    InternalVersion v = ((VersionImpl) vsel.select(history)).getInternalVersion();
+                    NodeImpl node = addNode(child.getName(), v.getFrozenNode());
                     node.internalRestore(v, vsel);
                 }
             }

Modified: incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/PropertyImpl.java
==============================================================================
--- incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/PropertyImpl.java	(original)
+++ incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/PropertyImpl.java	Tue Oct  5 01:57:45 2004
@@ -295,6 +295,45 @@
         return propId.getName();
     }
 
+    /**
+     * Returns the internal values of this property
+     *
+     * @return
+     * @throws RepositoryException
+     */
+    public InternalValue[] internalGetValues() throws RepositoryException {
+
+        // check state of this instance
+        checkItemState();
+
+        // check multi-value flag
+        if (!definition.isMultiple()) {
+            throw new ValueFormatException(safeGetJCRPath() + " is not multi-valued");
+        }
+
+        PropertyState state = (PropertyState) getItemState();
+        return state.getValues();
+    }
+
+    /**
+     * Returns the internal values of this property
+     *
+     * @return
+     * @throws RepositoryException
+     */
+    public InternalValue internalGetValue() throws RepositoryException {
+        // check state of this instance
+        checkItemState();
+
+        // check multi-value flag
+        if (definition.isMultiple()) {
+            throw new ValueFormatException(safeGetJCRPath() + " is multi-valued and can therefore only be retrieved as an array of values");
+        }
+
+        PropertyState state = (PropertyState) getItemState();
+        return state.getValues()[0];
+    }
+
     //-------------------------------------------------------------< Property >
     /**
      * @see Property#getValues()

Modified: incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/RepositoryImpl.java
==============================================================================
--- incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/RepositoryImpl.java	(original)
+++ incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/RepositoryImpl.java	Tue Oct  5 01:57:45 2004
@@ -46,6 +46,8 @@
 
     private static final String DEFAULT_WORKSPACE_NAME = "default";
 
+    private static final String VERSION_WORKSPACE_NAME = "default";
+
     private static final String ANONYMOUS_USER = "anonymous";
 
     private static final Credentials ANONYMOUS_CREDENTIALS =
@@ -262,7 +264,6 @@
          *   this 'read-only' system workspace
          */
 
-        // FIXME version manager should not operate on default workspace
         // check system root node of system workspace
         SessionImpl sysSession = getSystemSession(DEFAULT_WORKSPACE_NAME);
         NodeImpl rootNode = (NodeImpl) sysSession.getRootNode();
@@ -270,13 +271,25 @@
             rootNode.addNode(SYSTEM_ROOT_NAME, NodeTypeRegistry.NT_UNSTRUCTURED);
             rootNode.save();
         }
+
         // init version manager
-        vMgr = new VersionManager(sysSession);
+        // todo: as soon as dynamic workspaces are available, base on system ws
+        SessionImpl verSession = getSystemSession(VERSION_WORKSPACE_NAME);
+        NodeImpl vRootNode = (NodeImpl) verSession.getRootNode();
+        try {
+            if (!vRootNode.hasNode(SYSTEM_ROOT_NAME)) {
+                verSession.getWorkspace().clone(DEFAULT_WORKSPACE_NAME,
+                        SYSTEM_ROOT_NAME.toJCRName(verSession.getNamespaceResolver()),
+                        SYSTEM_ROOT_NAME.toJCRName(verSession.getNamespaceResolver()));
+            }
+        } catch (NoPrefixDeclaredException e) {
+            throw new RepositoryException("Error: " + e.toString());
+        }
+        vMgr = new VersionManager(verSession);
 
         // load repository properties
         repProps = new Properties();
         loadRepProps();
-
         nodesCount = Long.parseLong(repProps.getProperty(STATS_NODE_COUNT_PROPERTY, "0"));
         propsCount = Long.parseLong(repProps.getProperty(STATS_PROP_COUNT_PROPERTY, "0"));
 

Modified: incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/WorkspaceImpl.java
==============================================================================
--- incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/WorkspaceImpl.java	(original)
+++ incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/WorkspaceImpl.java	Tue Oct  5 01:57:45 2004
@@ -48,7 +48,7 @@
 /**
  * A <code>WorkspaceImpl</code> ...
  */
-class WorkspaceImpl implements Workspace {
+public class WorkspaceImpl implements Workspace {
 
     private static Logger log = Logger.getLogger(WorkspaceImpl.class);
 
@@ -118,7 +118,7 @@
         return rep;
     }
 
-    PersistentItemStateManager getPersistentStateManager() {
+    public PersistentItemStateManager getPersistentStateManager() {
         return persistentStateMgr;
     }
 

Modified: incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/nodetype/builtin_nodetypes.xml
==============================================================================
--- incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/nodetype/builtin_nodetypes.xml	(original)
+++ incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/nodetype/builtin_nodetypes.xml	Tue Oct  5 01:57:45 2004
@@ -104,7 +104,7 @@
         <propertyDef name="jcr:encoding" type="String" autoCreate="false" mandatory="false" onParentVersion="COPY" protected="false" primaryItem="false" multiple="false"/>
         <propertyDef name="jcr:mimeType" type="String" autoCreate="false" mandatory="true" onParentVersion="COPY" protected="false" primaryItem="false" multiple="false"/>
         <propertyDef name="jcr:data" type="Binary" autoCreate="false" mandatory="true" onParentVersion="COPY" protected="false" primaryItem="true" multiple="false"/>
-        <propertyDef name="jcr:lastModified" type="Date" autoCreate="false" mandatory="true" onParentVersion="COMPUTE" protected="false" primaryItem="false" multiple="false"/>
+        <propertyDef name="jcr:lastModified" type="Date" autoCreate="true" mandatory="true" onParentVersion="COMPUTE" protected="false" primaryItem="false" multiple="false"/>
     </nodeType>
     <nodeType name="nt:folder" mixin="false" orderableChildNodes="false">
         <supertypes>

Added: incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/util/Text.java
==============================================================================
--- (empty file)
+++ incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/util/Text.java	Tue Oct  5 01:57:45 2004
@@ -0,0 +1,104 @@
+/*
+ * Copyright 2004 The Apache Software Foundation.
+ *
+ * Licensed 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.core.util;
+
+import java.io.UnsupportedEncodingException;
+import java.security.NoSuchAlgorithmException;
+import java.security.MessageDigest;
+
+/**
+ * This Class provides some text related utilities
+ */
+public class Text {
+
+    /**
+     * used for the md5
+     */
+    public final static char[] hexTable = "0123456789abcdef".toCharArray();
+
+    /**
+     * Calculate an MD5 hash of the string given.
+     *
+     * @param data the data to encode
+     * @param enc  the character encoding to use
+     * @return a hex encoded string of the md5 digested input
+     */
+    public static String md5(String data, String enc)
+            throws UnsupportedEncodingException {
+        try {
+            return digest("MD5", data.getBytes(enc));
+        } catch (NoSuchAlgorithmException e) {
+            throw new InternalError("MD5 digest not available???");
+        }
+    }
+
+    /**
+     * Calculate an MD5 hash of the string given using 'utf-8' encoding.
+     *
+     * @param data the data to encode
+     * @return a hex encoded string of the md5 digested input
+     */
+    public static String md5(String data) {
+        try {
+            return md5(data, "utf-8");
+        } catch (UnsupportedEncodingException e) {
+            throw new InternalError("UTF8 digest not available???");
+        }
+    }
+
+    /**
+     * Digest the plain string using the given algorithm.
+     *
+     * @param algorithm The alogrithm for the digest. This algorithm must be
+     *                  supported by the MessageDigest class.
+     * @param data      The plain text String to be digested.
+     * @param enc       The character encoding to use
+     * @return The digested plain text String represented as Hex digits.
+     * @throws NoSuchAlgorithmException     if the desired algorithm is not supported by
+     *                                      the MessageDigest class.
+     * @throws UnsupportedEncodingException if the encoding is not supported
+     */
+    public static String digest(String algorithm, String data, String enc)
+            throws NoSuchAlgorithmException, UnsupportedEncodingException {
+
+        return digest(algorithm, data.getBytes(enc));
+    }
+
+    /**
+     * Digest the plain string using the given algorithm.
+     *
+     * @param algorithm The alogrithm for the digest. This algorithm must be
+     *                  supported by the MessageDigest class.
+     * @param data      the data to digest with the given algorithm
+     * @return The digested plain text String represented as Hex digits.
+     * @throws NoSuchAlgorithmException if the desired algorithm is not supported by
+     *                                  the MessageDigest class.
+     */
+    public static String digest(String algorithm, byte[] data)
+            throws NoSuchAlgorithmException {
+
+        MessageDigest md = MessageDigest.getInstance(algorithm);
+        byte[] digest = md.digest(data);
+        StringBuffer res = new StringBuffer(digest.length * 2);
+        for (int i = 0; i < digest.length; i++) {
+            byte b = digest[i];
+            res.append(hexTable[(b >> 4) & 15]);
+            res.append(hexTable[b & 15]);
+        }
+        return res.toString();
+    }
+
+}

Modified: incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/version/GenericVersionSelector.java
==============================================================================
--- incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/version/GenericVersionSelector.java	(original)
+++ incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/version/GenericVersionSelector.java	Tue Oct  5 01:57:45 2004
@@ -29,57 +29,135 @@
  */
 public class GenericVersionSelector implements VersionSelector {
 
+    /**
+     * a versionname hint
+     */
     private String name = null;
 
+    /**
+     * a versionlabel hint
+     */
     private String label = null;
 
+    /**
+     * a version date hint
+     */
     private Calendar date = null;
 
+    /**
+     * flag indicating that it should return the latest version, if no other found
+     */
     private boolean returnLatest = true;
 
+    /**
+     * Creates a default <code>GenericVersionSelector</code> that always selects
+     * the latest version.
+     */
     public GenericVersionSelector() {
     }
 
+    /**
+     * Creates a <code>GenericVersionSelector</code> that will try to select a
+     * version with the given label.
+     *
+     * @param label
+     */
     public GenericVersionSelector(String label) {
         this.label = label;
     }
 
+    /**
+     * Creates a <code>GenericVersionSelector</code> that will select the oldest
+     * version of all those that are more recent than the given date.
+     *
+     * @param date
+     */
     public GenericVersionSelector(Calendar date) {
         this.date = date;
     }
 
+    /**
+     * Returns the name hint.
+     *
+     * @return
+     */
     public String getName() {
         return name;
     }
 
+    /**
+     * Sets the name hint
+     *
+     * @param name
+     */
     public void setName(String name) {
         this.name = name;
     }
 
+    /**
+     * Returns the label hint
+     *
+     * @return
+     */
     public String getLabel() {
         return label;
     }
 
+    /**
+     * Sets the label hint
+     *
+     * @param label
+     */
     public void setLabel(String label) {
         this.label = label;
     }
 
+    /**
+     * Returns the date hint
+     *
+     * @return
+     */
     public Calendar getDate() {
         return date;
     }
 
+    /**
+     * Sets the date hint
+     *
+     * @param date
+     */
     public void setDate(Calendar date) {
         this.date = date;
     }
 
+    /**
+     * Returns the flag, if the latest version should be selected, if no
+     * version can be found using the given hint.
+     *
+     * @return
+     */
     public boolean isReturnLatest() {
         return returnLatest;
     }
 
+    /**
+     * Sets the flag, if the latest version should be selected, if no
+     * version can be found using the given hint.
+     *
+     * @param returnLatest
+     */
     public void setReturnLatest(boolean returnLatest) {
         this.returnLatest = returnLatest;
     }
 
+    /**
+     * Selects a version from the given version history using the previously
+     * assigned hint in the following order: name, label, date, latest.
+     *
+     * @param versionHistory
+     * @return
+     * @throws RepositoryException
+     */
     public Version select(VersionHistory versionHistory) throws RepositoryException {
         Version selected = null;
         if (name != null) {
@@ -97,16 +175,40 @@
         return selected;
     }
 
+    /**
+     * Selects a version by version name.
+     *
+     * @param history
+     * @param name
+     * @return the version with the given name or <code>null</code>
+     * @throws RepositoryException
+     */
     public static Version selectByName(VersionHistory history, String name)
             throws RepositoryException {
         return history.hasNode(name) ? history.getVersion(name) : null;
     }
 
+    /**
+     * Selects a version by label
+     *
+     * @param history
+     * @param label
+     * @return the version with the given label or <code>null</code>
+     * @throws RepositoryException
+     */
     public static Version selectByLabel(VersionHistory history, String label)
             throws RepositoryException {
         return history.getVersionByLabel(label);
     }
 
+    /**
+     * Selects a version by date.
+     *
+     * @param history
+     * @param date
+     * @return the latest version newer than the given date date or <code>null</code>
+     * @throws RepositoryException
+     */
     public static Version selectByDate(VersionHistory history, Calendar date)
             throws RepositoryException {
         long time = date == null ? Long.MAX_VALUE : date.getTimeInMillis();

Added: incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/version/InternalFreeze.java
==============================================================================
--- (empty file)
+++ incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/version/InternalFreeze.java	Tue Oct  5 01:57:45 2004
@@ -0,0 +1,32 @@
+/*
+ * Copyright 2004 The Apache Software Foundation.
+ *
+ * Licensed 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.core.version;
+
+import org.apache.jackrabbit.core.QName;
+
+/**
+ * Abstract class that represents either a frozen child or a frozen versionable
+ * node.
+ */
+public abstract class InternalFreeze {
+
+    /**
+     * Returns the name of the frozen node
+     *
+     * @return
+     */
+    public abstract QName getName();
+}

Added: incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/version/InternalFrozenNode.java
==============================================================================
--- (empty file)
+++ incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/version/InternalFrozenNode.java	Tue Oct  5 01:57:45 2004
@@ -0,0 +1,283 @@
+/*
+ * Copyright 2004 The Apache Software Foundation.
+ *
+ * Licensed 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.core.version;
+
+import org.apache.jackrabbit.core.*;
+import org.apache.jackrabbit.core.nodetype.NodeTypeImpl;
+import org.apache.jackrabbit.core.nodetype.NodeTypeRegistry;
+import org.apache.jackrabbit.core.util.uuid.UUID;
+
+import javax.jcr.NodeIterator;
+import javax.jcr.PropertyIterator;
+import javax.jcr.PropertyType;
+import javax.jcr.RepositoryException;
+import javax.jcr.nodetype.NodeType;
+import javax.jcr.version.OnParentVersionAction;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * The InternalFrozenNode class presents the frozen node that was generated
+ * during a {@link javax.jcr.Node#checkin()}. It holds the set of frozen
+ * properties, the frozen child nodes and the frozen version history
+ * references of the original node.
+ */
+public class InternalFrozenNode extends InternalFreeze {
+
+    /**
+     * the underlaying persistance node
+     */
+    private PersistentNode node;
+
+    /**
+     * the list of frozen child nodes
+     */
+    private InternalFreeze[] frozenChildNodes;
+
+    /**
+     * the list of frozen properties
+     */
+    private PersistentProperty[] frozenProperties;
+
+    /**
+     * the frozen uuid of the original node
+     */
+    private String frozenUUID;
+
+    /**
+     * the frozen primary type of the orginal node
+     */
+    private QName frozenPrimaryType;
+
+    /**
+     * the frozen list of mixin types of the original node
+     */
+    private QName[] frozenMixinTypes;
+
+    /**
+     * Creates a new frozen node based on the given persistance node.
+     *
+     * @param node
+     * @throws RepositoryException
+     */
+    protected InternalFrozenNode(PersistentNode node) throws RepositoryException {
+        this.node = node;
+
+        // init the internal properties
+        frozenUUID = node.getPropertyValue(VersionManager.PROPNAME_FROZEN_UUID).internalValue().toString();
+        frozenPrimaryType = (QName) node.getPropertyValue(VersionManager.PROPNAME_FROZEN_PRIMARY_TYPE).internalValue();
+        InternalValue[] values = node.getPropertyValues(VersionManager.PROPNAME_FROZEN_MIXIN_TYPES);
+        if (values == null) {
+            frozenMixinTypes = new QName[0];
+        } else {
+            frozenMixinTypes = new QName[values.length];
+            for (int i = 0; i < values.length; i++) {
+                frozenMixinTypes[i] = (QName) values[i].internalValue();
+            }
+        }
+        // init the frozen properties
+        PersistentProperty[] props = node.getProperties();
+        List propList = new ArrayList();
+        for (int i = 0; i < props.length; i++) {
+            PersistentProperty prop = props[i];
+            if (prop.getName().equals(VersionManager.PROPNAME_FROZEN_UUID)) {
+                // already set
+            } else if (prop.getName().equals(VersionManager.PROPNAME_FROZEN_PRIMARY_TYPE)) {
+                // already set
+            } else if (prop.getName().equals(VersionManager.PROPNAME_FROZEN_MIXIN_TYPES)) {
+                // already set
+            } else if (prop.getName().equals(ItemImpl.PROPNAME_PRIMARYTYPE)) {
+                // ignore
+            } else if (prop.getName().equals(ItemImpl.PROPNAME_UUID)) {
+                // ignore
+            } else {
+                propList.add(prop);
+            }
+        }
+        frozenProperties = (PersistentProperty[]) propList.toArray(new PersistentProperty[propList.size()]);
+
+        // init the frozen child nodes
+        PersistentNode[] childNodes = node.getChildNodes();
+        frozenChildNodes = new InternalFreeze[childNodes.length];
+        for (int i = 0; i < childNodes.length; i++) {
+            if (childNodes[i].hasProperty(VersionManager.PROPNAME_FROZEN_PRIMARY_TYPE)) {
+                frozenChildNodes[i] = new InternalFrozenNode(childNodes[i]);
+            } else if (childNodes[i].hasProperty(VersionManager.PROPNAME_VERSION_HISTORY)) {
+                frozenChildNodes[i] = new InternalFrozenVersionHistory(childNodes[i]);
+            } else {
+                // unkown ?
+            }
+        }
+
+    }
+
+    /**
+     * Returns the name of this frozen node
+     *
+     * @return
+     */
+    public QName getName() {
+        return node.getName();
+    }
+
+    /**
+     * Returns the UUID of this frozen node
+     *
+     * @return
+     */
+    public String getUUID() {
+        return node.getUUID();
+    }
+
+    /**
+     * Returns the list of frozen child nodes
+     *
+     * @return
+     */
+    public InternalFreeze[] getFrozenChildNodes() {
+        return frozenChildNodes;
+    }
+
+    /**
+     * Returns the list of frozen properties
+     *
+     * @return
+     */
+    public PersistentProperty[] getFrozenProperties() {
+        return frozenProperties;
+    }
+
+    /**
+     * Returns the frozen UUID
+     *
+     * @return
+     */
+    public String getFrozenUUID() {
+        return frozenUUID;
+    }
+
+    /**
+     * Returns the frozen primary type
+     *
+     * @return
+     */
+    public QName getFrozenPrimaryType() {
+        return frozenPrimaryType;
+    }
+
+    /**
+     * Returns the list of the frozen mixin types
+     *
+     * @return
+     */
+    public QName[] getFrozenMixinTypes() {
+        return frozenMixinTypes;
+    }
+
+    /**
+     * Checks-in a <code>src</code> node. It creates a new child node of
+     * <code>parent</code> with the given <code>name</code> and adds the
+     * source nodes properties according to their OPV value to the
+     * list of frozen properties. It creates frozen child nodes for each child
+     * node of <code>src</code> according to its OPV value.
+     *
+     * @param parent
+     * @param name
+     * @param src
+     * @return
+     * @throws RepositoryException
+     */
+    protected static InternalFrozenNode checkin(PersistentNode parent, QName name, NodeImpl src)
+            throws RepositoryException {
+
+        // create new node
+        PersistentNode node = parent.addNode(name, NodeTypeRegistry.NT_UNSTRUCTURED);
+
+        // initialize the internal properties
+        if (src.isNodeType(NodeTypeRegistry.MIX_REFERENCEABLE)) {
+            node.setPropertyValue(VersionManager.PROPNAME_FROZEN_UUID, InternalValue.create(src.getUUID()));
+        }
+
+        node.setPropertyValue(VersionManager.PROPNAME_FROZEN_PRIMARY_TYPE,
+                InternalValue.create(((NodeTypeImpl) src.getPrimaryNodeType()).getQName()));
+
+        if (src.hasProperty(NodeImpl.PROPNAME_MIXINTYPES)) {
+            NodeType[] mixins = src.getMixinNodeTypes();
+            InternalValue[] ivalues = new InternalValue[mixins.length];
+            for (int i = 0; i < mixins.length; i++) {
+                ivalues[i] = InternalValue.create(((NodeTypeImpl) mixins[i]).getQName());
+            }
+            node.setPropertyValues(VersionManager.PROPNAME_FROZEN_MIXIN_TYPES, PropertyType.NAME, ivalues);
+        }
+
+        // add the properties
+        PropertyIterator piter = src.getProperties();
+        while (piter.hasNext()) {
+            PropertyImpl prop = (PropertyImpl) piter.nextProperty();
+// ignore some properties that not have a OPV=Ignore yet
+            if (prop.getQName().equals(VersionManager.PROPNAME_VERSION_HISTORY)) {
+                continue;
+            }
+            if (prop.getQName().equals(VersionManager.PROPNAME_PREDECESSORS)) {
+                continue;
+            }
+            switch (prop.getDefinition().getOnParentVersion()) {
+                case OnParentVersionAction.ABORT:
+                    parent.reload();
+                    throw new RepositoryException("Checkin aborted due to OPV in " + prop.safeGetJCRPath());
+                case OnParentVersionAction.COMPUTE:
+                case OnParentVersionAction.IGNORE:
+                case OnParentVersionAction.INITIALIZE:
+                    break;
+                case OnParentVersionAction.VERSION:
+                case OnParentVersionAction.COPY:
+                    node.copyFrom(prop);
+                    break;
+            }
+        }
+
+        // add the frozen children and vistories
+        NodeIterator niter = src.getNodes();
+        while (niter.hasNext()) {
+            NodeImpl child = (NodeImpl) niter.nextNode();
+            switch (child.getDefinition().getOnParentVersion()) {
+                case OnParentVersionAction.ABORT:
+                    throw new RepositoryException("Checkin aborted due to OPV in " + child.safeGetJCRPath());
+                case OnParentVersionAction.COMPUTE:
+                case OnParentVersionAction.IGNORE:
+                case OnParentVersionAction.INITIALIZE:
+                    break;
+                case OnParentVersionAction.VERSION:
+                    if (child.isNodeType(NodeTypeRegistry.MIX_VERSIONABLE)) {
+// create frozen versionable child
+                        PersistentNode newChild = node.addNode(child.getQName(), NodeTypeRegistry.NT_UNSTRUCTURED);
+                        newChild.setPropertyValue(VersionManager.PROPNAME_VERSION_HISTORY,
+                                InternalValue.create(new UUID(child.getVersionHistory().getUUID())));
+                        newChild.setPropertyValue(VersionManager.PROPNAME_BASE_VERSION,
+                                InternalValue.create(new UUID(child.getBaseVersion().getUUID())));
+                    }
+                    // else ignore
+                    break;
+                case OnParentVersionAction.COPY:
+                    checkin(node, child.getQName(), child);
+                    break;
+            }
+        }
+        parent.store();
+        return new InternalFrozenNode(node);
+    }
+
+}
\ No newline at end of file

Added: incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/version/InternalFrozenVersionHistory.java
==============================================================================
--- (empty file)
+++ incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/version/InternalFrozenVersionHistory.java	Tue Oct  5 01:57:45 2004
@@ -0,0 +1,69 @@
+/*
+ * Copyright 2004 The Apache Software Foundation.
+ *
+ * Licensed 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.core.version;
+
+import org.apache.jackrabbit.core.QName;
+import org.apache.jackrabbit.core.util.uuid.UUID;
+
+import javax.jcr.RepositoryException;
+import javax.jcr.Session;
+import javax.jcr.version.VersionHistory;
+
+/**
+ * This Class represents a frozen versionable child node, that was created
+ * during a {@link javax.jcr.Node#checkin()} with a OPV==Version node.
+ */
+public class InternalFrozenVersionHistory extends InternalFreeze {
+
+    /**
+     * the underlaying persistence node
+     */
+    private PersistentNode node;
+
+    /**
+     * Creates a new frozen version history.
+     *
+     * @param node
+     */
+    protected InternalFrozenVersionHistory(PersistentNode node) {
+        this.node = node;
+    }
+
+    /**
+     * Returns the name of this frozen version history
+     *
+     * @return
+     */
+    public QName getName() {
+        return node.getName();
+    }
+
+    /**
+     * Returns the version history that was versioned with this node.
+     *
+     * @param session
+     * @return
+     * @throws RepositoryException
+     */
+    public VersionHistory getVersionHistory(Session session)
+            throws RepositoryException {
+        String historyId = ((UUID) node.getPropertyValue(VersionManager.PROPNAME_VERSION_HISTORY).internalValue()).toString();
+        PersistentNode hNode = node.getNodeByUUID(historyId);
+        return new VersionHistoryImpl(session, new InternalVersionHistory(hNode));
+    }
+
+
+}

Added: incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/version/InternalVersion.java
==============================================================================
--- (empty file)
+++ incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/version/InternalVersion.java	Tue Oct  5 01:57:45 2004
@@ -0,0 +1,292 @@
+/*
+ * Copyright 2004 The Apache Software Foundation.
+ *
+ * Licensed 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.core.version;
+
+import org.apache.jackrabbit.core.InternalValue;
+import org.apache.jackrabbit.core.QName;
+import org.apache.jackrabbit.core.util.uuid.UUID;
+
+import javax.jcr.PropertyType;
+import javax.jcr.RepositoryException;
+import javax.jcr.version.Version;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Calendar;
+import java.util.HashSet;
+
+/**
+ * This Class implements the Version representation of the node.
+ */
+public final class InternalVersion {
+
+    /**
+     * the list/cache of predecessors (values == InternalVersion)
+     */
+    private ArrayList predecessors = new ArrayList();
+
+    /**
+     * the list of successors (values == InternalVersion)
+     */
+    private ArrayList successors = new ArrayList();
+
+    /**
+     * the internal version history that this version is contained in
+     */
+    private InternalVersionHistory versionHistory;
+
+    /**
+     * the underlaying persistance node of this history
+     */
+    private PersistentNode node;
+
+    /**
+     * the date when this version was created
+     */
+    private Calendar created;
+
+    /**
+     * the set of version labes of this history (values == String)
+     */
+    private HashSet labelCache = null;
+
+    /**
+     * the internal frozen node that was versioned with this version
+     */
+    private InternalFrozenNode frozen;
+
+    /**
+     * Creates a new internal version with the given version history and
+     * persistance node
+     *
+     * @param vh
+     * @param node
+     * @throws RepositoryException
+     */
+    InternalVersion(InternalVersionHistory vh, PersistentNode node) throws RepositoryException {
+        this.versionHistory = vh;
+        this.node = node;
+        // get frozen node
+        PersistentNode pNode = node.getNode(VersionManager.NODENAME_FROZEN, 1);
+        frozen = pNode == null ? null : new InternalFrozenNode(pNode);
+
+        // init internal values
+        InternalValue[] values = node.getPropertyValues(VersionManager.PROPNAME_CREATED);
+        if (values != null) {
+            created = (Calendar) values[0].internalValue();
+        }
+    }
+
+    /**
+     * Returns the uuid of this version
+     *
+     * @return
+     */
+    public String getUUID() {
+        return node.getUUID();
+    }
+
+    /**
+     * Returns the name of this version
+     *
+     * @return
+     */
+    public QName getName() {
+        return node.getName();
+    }
+
+    /**
+     * Returns the frozen node
+     *
+     * @return
+     */
+    public InternalFrozenNode getFrozenNode() {
+        return frozen;
+    }
+
+    /**
+     * adds a successor version to the internal cache
+     *
+     * @param successor
+     */
+    private void addSuccessor(InternalVersion successor) {
+        successors.add(successor);
+    }
+
+    /**
+     * resolves the predecessors property and indirectly adds it self to their
+     * successor list.
+     */
+    void resolvePredecessors() {
+        InternalValue[] values = node.getPropertyValues(VersionManager.PROPNAME_PREDECESSORS);
+        if (values != null) {
+            for (int i = 0; i < values.length; i++) {
+                InternalVersion v = versionHistory.getVersion(values[i].internalValue().toString());
+                predecessors.add(v);
+                v.addSuccessor(this);
+            }
+        }
+    }
+
+    /**
+     * @see Version#getCreated()
+     */
+    public Calendar getCreated() {
+        return created;
+    }
+
+    /**
+     * @see Version#getSuccessors()
+     */
+    public InternalVersion[] getSuccessors() {
+        return (InternalVersion[]) successors.toArray(new InternalVersion[successors.size()]);
+    }
+
+    /**
+     * @see Version#getSuccessors()
+     */
+    public InternalVersion[] getPredecessors() {
+        return (InternalVersion[]) predecessors.toArray(new InternalVersion[predecessors.size()]);
+    }
+
+    /**
+     * stores the internal predecessor cache to the persistance node
+     *
+     * @throws RepositoryException
+     */
+    private void storePredecessors() throws RepositoryException {
+        InternalValue[] values = new InternalValue[predecessors.size()];
+        for (int i = 0; i < values.length; i++) {
+            values[i] = InternalValue.create(new UUID(((InternalVersion) predecessors.get(i)).getUUID()));
+        }
+        node.setPropertyValues(VersionManager.PROPNAME_PREDECESSORS, PropertyType.REFERENCE, values);
+    }
+
+    /**
+     * Detaches itself from the version graph.
+     *
+     * @throws RepositoryException
+     */
+    void internalDetach() throws RepositoryException {
+        // detach this from all successors
+        InternalVersion[] succ = (InternalVersion[]) getSuccessors();
+        for (int i = 0; i < succ.length; i++) {
+            succ[i].internalDetachPredecessor(this);
+        }
+
+        // clear properties
+        successors.clear();
+        predecessors.clear();
+        labelCache = null;
+        storePredecessors();
+    }
+
+    /**
+     * Removes the predecessor V of this predecessor list and adds all of Vs
+     * predecessors to it.
+     * <p/>
+     * please note, that this operation might corrupt the version graph
+     *
+     * @param v the successor to detach
+     */
+    private void internalDetachPredecessor(InternalVersion v) throws RepositoryException {
+        // remove 'v' from predecessor list
+        for (int i = 0; i < predecessors.size(); i++) {
+            if (predecessors.get(i).equals(v)) {
+                predecessors.remove(i);
+                break;
+            }
+        }
+        // attach v's successors
+        predecessors.clear();
+        predecessors.addAll(Arrays.asList(v.getPredecessors()));
+        storePredecessors();
+    }
+
+    /**
+     * Checks if this version is more recent than the given version <code>v</code>.
+     * A version is more recent if and only if it is a successor (or a successor
+     * of a successor, etc., to any degree of separation) of the compared one.
+     *
+     * @param v the version to check
+     * @return <code>true</code> if the version is more recent;
+     *         <code>false</code> otherwise.
+     */
+    public boolean isMoreRecent(InternalVersion v) {
+        for (int i = 0; i < predecessors.size(); i++) {
+            InternalVersion pred = (InternalVersion) predecessors.get(i);
+            if (pred.equals(this) || pred.isMoreRecent(v)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /**
+     * returns the internal version history of this version
+     *
+     * @return
+     */
+    protected InternalVersionHistory getVersionHistory() {
+        return versionHistory;
+    }
+
+    /**
+     * adds a label to the label cache. does not affect storage
+     *
+     * @param label
+     * @return
+     */
+    protected boolean internalAddLabel(String label) {
+        if (labelCache == null) {
+            labelCache = new HashSet();
+        }
+        return labelCache.add(label);
+    }
+
+    /**
+     * removes a label from the label cache. does not affect storage
+     *
+     * @param label
+     * @return
+     */
+    protected boolean internalRemoveLabel(String label) {
+        if (labelCache == null) {
+            return false;
+        } else {
+            return labelCache.remove(label);
+        }
+    }
+
+    /**
+     * checks, if a label is in the label cache
+     *
+     * @param label
+     * @return
+     */
+    protected boolean internalHasLabel(String label) {
+        return labelCache == null ? false : labelCache.contains(label);
+    }
+
+    /**
+     * returns the array of the cached labels
+     *
+     * @return
+     */
+    protected String[] internalGetLabels() {
+        return labelCache == null ? new String[0] : (String[]) labelCache.toArray(new String[labelCache.size()]);
+    }
+
+}

Added: incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/version/InternalVersionHistory.java
==============================================================================
--- (empty file)
+++ incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/version/InternalVersionHistory.java	Tue Oct  5 01:57:45 2004
@@ -0,0 +1,379 @@
+/*
+ * Copyright 2004 The Apache Software Foundation.
+ *
+ * Licensed 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.core.version;
+
+import org.apache.jackrabbit.core.InternalValue;
+import org.apache.jackrabbit.core.ItemImpl;
+import org.apache.jackrabbit.core.NodeImpl;
+import org.apache.jackrabbit.core.QName;
+import org.apache.jackrabbit.core.nodetype.NodeTypeRegistry;
+import org.apache.jackrabbit.core.util.Text;
+import org.apache.jackrabbit.core.util.uuid.UUID;
+import org.apache.log4j.Logger;
+
+import javax.jcr.PropertyType;
+import javax.jcr.RepositoryException;
+import javax.jcr.Value;
+import javax.jcr.version.VersionException;
+import javax.jcr.version.VersionHistory;
+import java.util.Calendar;
+import java.util.HashMap;
+import java.util.Iterator;
+
+/**
+ * This Class implements a version history.
+ */
+public class InternalVersionHistory {
+
+    /**
+     * default logger
+     */
+    private static Logger log = Logger.getLogger(InternalVersionHistory.class);
+
+    /**
+     * the cache of the version labels
+     * key = version label (String)
+     * value = version
+     */
+    private HashMap labelCache = new HashMap();
+
+    /**
+     * the root version of this history
+     */
+    private InternalVersion rootVersion;
+
+    /**
+     * the hashmap of all versions
+     * key = UUID (String)
+     * value = version
+     */
+    private HashMap versionCache = new HashMap();
+
+    /**
+     * The nodes state of this version history
+     */
+    private PersistentNode node;
+
+    /**
+     * the node that holds the label nodes
+     */
+    private PersistentNode labelNode;
+
+    /**
+     * Creates a new VersionHistory object for the given node state.
+     */
+    InternalVersionHistory(PersistentNode node) throws RepositoryException {
+        this.node = node;
+        init();
+    }
+
+    /**
+     * Initialies the history and loads all internal caches
+     *
+     * @throws RepositoryException
+     */
+    private void init() throws RepositoryException {
+        versionCache.clear();
+        labelCache.clear();
+
+        // get entries
+        PersistentNode[] children = node.getChildNodes();
+        for (int i = 0; i < children.length; i++) {
+            PersistentNode child = children[i];
+            if (child.getName().equals(VersionManager.NODENAME_VERSION_LABELS)) {
+                labelNode = child;
+                continue;
+            }
+            InternalVersion v = new InternalVersion(this, child);
+            versionCache.put(child.getUUID(), v);
+            if (child.getName().equals(VersionManager.NODENAME_ROOTVERSION)) {
+                rootVersion = v;
+            }
+        }
+
+        // resolve successors and predecessors
+        Iterator iter = versionCache.values().iterator();
+        while (iter.hasNext()) {
+            InternalVersion v = (InternalVersion) iter.next();
+            v.resolvePredecessors();
+        }
+
+        // init label cache
+        PersistentNode labels[] = labelNode.getChildNodes();
+        for (int i = 0; i < labels.length; i++) {
+            PersistentNode lNode = labels[i];
+            String name = (String) lNode.getPropertyValue(VersionManager.PROPNAME_NAME).internalValue();
+            String ref = ((UUID) lNode.getPropertyValue(VersionManager.PROPNAME_VERSION).internalValue()).toString();
+            InternalVersion v = getVersion(ref);
+            labelCache.put(name, v);
+            v.internalAddLabel(name);
+        }
+    }
+
+    /**
+     * Returns the uuid of this version history
+     *
+     * @return
+     */
+    public String getUUID() {
+        return node.getUUID();
+    }
+
+    /**
+     * @see VersionHistory#getRootVersion()
+     */
+    public InternalVersion getRootVersion() throws RepositoryException {
+        return rootVersion;
+    }
+
+    /**
+     * @see VersionHistory#getVersion(java.lang.String)
+     */
+    public InternalVersion getVersion(QName versionName) throws RepositoryException {
+        // maybe add cache by name?
+        Iterator iter = versionCache.values().iterator();
+        while (iter.hasNext()) {
+            InternalVersion v = (InternalVersion) iter.next();
+            if (v.getName().equals(versionName)) {
+                return v;
+            }
+        }
+        throw new VersionException("Version " + versionName + " does not exist.");
+    }
+
+    /**
+     * @see VersionHistory#hasVersion(String)
+     */
+    public boolean hasVersion(QName versionName) {
+        // maybe add cache?
+        Iterator iter = versionCache.values().iterator();
+        while (iter.hasNext()) {
+            InternalVersion v = (InternalVersion) iter.next();
+            if (v.getName().equals(versionName)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /**
+     * Returns the version with the given uuid or <code>null</code> if the
+     * respective version does not exist.
+     *
+     * @param uuid
+     * @return
+     */
+    public InternalVersion getVersion(String uuid) {
+        return (InternalVersion) versionCache.get(uuid);
+    }
+
+    /**
+     * @see VersionHistory#getVersionByLabel(java.lang.String)
+     */
+    public InternalVersion getVersionByLabel(String label) throws RepositoryException {
+        return (InternalVersion) labelCache.get(label);
+    }
+
+    /**
+     * Removes the indicated version from this VersionHistory. If the specified
+     * vesion does not exist, if it specifies the root version or if it is
+     * referenced by any node e.g. as base version, a VersionException is thrown.
+     * <p/>
+     * all successors of the removed version become successors of the
+     * predecessors of the removed version and vice versa. then, the entire
+     * version node and all its subnodes are removed.
+     *
+     * @param versionName
+     * @throws RepositoryException
+     */
+    public void removeVersion(QName versionName) throws RepositoryException {
+        InternalVersion v = (InternalVersion) getVersion(versionName);
+        if (v.equals(rootVersion)) {
+            String msg = "Removal of " + versionName + " not allowed.";
+            log.error(msg);
+            throw new VersionException(msg);
+        }
+
+        // remove from persistance state
+        node.removeNode(versionName);
+
+        // unregister from labels
+        String[] labels = v.internalGetLabels();
+        for (int i = 0; i < labels.length; i++) {
+            v.internalRemoveLabel(labels[i]);
+            QName name = new QName("", Text.md5(labels[i]));
+            labelNode.removeNode(name);
+        }
+
+        // detach from the version graph
+        v.internalDetach();
+
+        // and remove from history
+        versionCache.remove(v.getUUID());
+        store();
+
+    }
+
+    /**
+     * Adds a label to a version
+     *
+     * @param version
+     * @param label
+     * @throws RepositoryException
+     */
+    public void addVersionLabel(InternalVersion version, String label, boolean move) throws RepositoryException {
+        InternalVersion prev = (InternalVersion) labelCache.get(label);
+        if (version.equals(prev)) {
+            // ignore
+            return;
+        } else if (prev != null && !move) {
+            // already defined elsewhere, throw
+            throw new RepositoryException("Version label " + label + " already defined for version " + prev);
+        } else if (prev != null) {
+            // if already defined, but move, remove old label first
+            removeVersionLabel(label);
+        }
+        labelCache.put(label, version);
+        version.internalAddLabel(label);
+        QName name = new QName("", Text.md5(label));
+        PersistentNode lNode = labelNode.addNode(name, NodeTypeRegistry.NT_UNSTRUCTURED);
+        lNode.setPropertyValue(VersionManager.PROPNAME_NAME, InternalValue.create(label));
+        lNode.setPropertyValue(VersionManager.PROPNAME_VERSION, InternalValue.create(new UUID(version.getUUID())));
+        lNode.store();
+
+    }
+
+    /**
+     * Removes the label from the respective version
+     *
+     * @param label
+     * @throws RepositoryException if the label does not exist
+     */
+    public void removeVersionLabel(String label) throws RepositoryException {
+        InternalVersion v = (InternalVersion) labelCache.remove(label);
+        if (v == null) {
+            throw new RepositoryException("Version label " + label + " is not in version history.");
+        }
+        v.internalRemoveLabel(label);
+        QName name = new QName("", Text.md5(label));
+        labelNode.removeNode(name);
+        labelNode.store();
+    }
+
+    /**
+     * Checks in a node. It creates a new version with the given name and freezes
+     * the state of the given node.
+     *
+     * @param name
+     * @param src
+     * @return
+     * @throws RepositoryException
+     */
+    protected InternalVersion checkin(QName name, NodeImpl src)
+            throws RepositoryException {
+
+        // copy predecessors from src node
+        Value[] preds = src.getProperty(VersionManager.PROPNAME_PREDECESSORS).getValues();
+        InternalValue[] predecessors = new InternalValue[preds.length];
+        for (int i = 0; i < preds.length; i++) {
+            String uuid = preds[i].getString();
+            // check if version exist
+            if (!versionCache.containsKey(uuid)) {
+                throw new RepositoryException("invalid predecessor in source node");
+            }
+            predecessors[i] = InternalValue.create(new UUID(uuid));
+        }
+
+        PersistentNode vNode = node.addNode(name, NodeTypeRegistry.NT_UNSTRUCTURED);
+        vNode.setPropertyValue(ItemImpl.PROPNAME_UUID, InternalValue.create(vNode.getUUID()));
+        vNode.setMixinNodeTypes(new QName[]{NodeTypeRegistry.MIX_REFERENCEABLE});
+
+        // initialize 'created' and 'predecessors'
+        vNode.setPropertyValue(VersionManager.PROPNAME_CREATED, InternalValue.create(Calendar.getInstance()));
+        vNode.setPropertyValues(VersionManager.PROPNAME_PREDECESSORS, PropertyType.REFERENCE, predecessors);
+
+
+        // checkin source node
+        InternalFrozenNode.checkin(vNode, VersionManager.NODENAME_FROZEN, src);
+
+        // and store
+        store();
+
+        // update version graph
+        InternalVersion version = new InternalVersion(this, vNode);
+        version.resolvePredecessors();
+
+        // update cache
+        versionCache.put(version.getUUID(), version);
+
+        return version;
+    }
+
+
+    /**
+     * Stores the changes made to this version history
+     *
+     * @throws RepositoryException
+     */
+    protected void store() throws RepositoryException {
+        node.store();
+    }
+
+    /**
+     * discards the changes made to this version history
+     *
+     * @throws RepositoryException
+     */
+    protected void reload() throws RepositoryException {
+        node.reload();
+        init();
+    }
+
+    /**
+     * Creates a new <code>InternalVersionHistory</code> below the given parent
+     * node and with the given name.
+     *
+     * @param parent
+     * @param name
+     * @return
+     * @throws RepositoryException
+     */
+    protected static InternalVersionHistory create(PersistentNode parent, QName name)
+            throws RepositoryException {
+
+        // create history node
+        PersistentNode pNode = parent.addNode(name, NodeTypeRegistry.NT_UNSTRUCTURED);
+        pNode.setPropertyValue(ItemImpl.PROPNAME_UUID, InternalValue.create(pNode.getUUID()));
+        pNode.setMixinNodeTypes(new QName[]{NodeTypeRegistry.MIX_REFERENCEABLE});
+
+        // create label node
+        pNode.addNode(VersionManager.NODENAME_VERSION_LABELS, NodeTypeRegistry.NT_UNSTRUCTURED);
+
+        // create root version
+        PersistentNode vNode = pNode.addNode(VersionManager.NODENAME_ROOTVERSION, NodeTypeRegistry.NT_UNSTRUCTURED);
+        vNode.setPropertyValue(ItemImpl.PROPNAME_UUID, InternalValue.create(vNode.getUUID()));
+        vNode.setMixinNodeTypes(new QName[]{NodeTypeRegistry.MIX_REFERENCEABLE});
+
+        // initialize 'created' and 'predecessors'
+        vNode.setPropertyValue(VersionManager.PROPNAME_CREATED, InternalValue.create(Calendar.getInstance()));
+        vNode.setPropertyValues(VersionManager.PROPNAME_PREDECESSORS, PropertyType.REFERENCE, new InternalValue[0]);
+
+        parent.store();
+        return new InternalVersionHistory(pNode);
+    }
+}
+
+

Added: incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/version/NodeWrapper.java
==============================================================================
--- (empty file)
+++ incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/version/NodeWrapper.java	Tue Oct  5 01:57:45 2004
@@ -0,0 +1,532 @@
+/*
+ * Copyright 2004 The Apache Software Foundation.
+ *
+ * Licensed 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.core.version;
+
+import org.apache.jackrabbit.core.NodeImpl;
+import org.apache.jackrabbit.core.QName;
+
+import javax.jcr.*;
+import javax.jcr.access.AccessDeniedException;
+import javax.jcr.lock.Lock;
+import javax.jcr.lock.LockException;
+import javax.jcr.nodetype.ConstraintViolationException;
+import javax.jcr.nodetype.NoSuchNodeTypeException;
+import javax.jcr.nodetype.NodeDef;
+import javax.jcr.nodetype.NodeType;
+import javax.jcr.version.Version;
+import javax.jcr.version.VersionException;
+import javax.jcr.version.VersionHistory;
+import java.io.InputStream;
+import java.util.Calendar;
+
+/**
+ * This Class implements a generic wrapper around a node object.
+ */
+public class NodeWrapper implements Node {
+
+    /**
+     * the internal node
+     */
+    private final NodeImpl delegatee;
+
+    /**
+     * Creates a new wrapper for the given node.
+     *
+     * @param delegatee
+     */
+    public NodeWrapper(NodeImpl delegatee) {
+        this.delegatee = delegatee;
+    }
+
+    /**
+     * Returns the delegatee of this wrapper.
+     *
+     * @return the node that is wrapped.
+     */
+    public Node unwrap() {
+        return delegatee;
+    }
+
+    /**
+     * @see Node#addNode(String)
+     */
+    public Node addNode(String relPath) throws ItemExistsException, PathNotFoundException, ConstraintViolationException, RepositoryException {
+        return delegatee.addNode(relPath);
+    }
+
+    /**
+     * @see Node#addNode(String, String)
+     */
+    public Node addNode(String relPath, String primaryNodeTypeName) throws ItemExistsException, PathNotFoundException, NoSuchNodeTypeException, ConstraintViolationException, RepositoryException {
+        return delegatee.addNode(relPath, primaryNodeTypeName);
+    }
+
+    /**
+     * @see Node#orderBefore(String, String)
+     */
+    public void orderBefore(String srcName, String destName) throws UnsupportedRepositoryOperationException, ConstraintViolationException, ItemNotFoundException, RepositoryException {
+        delegatee.orderBefore(srcName, destName);
+    }
+
+    /**
+     * @see Node#setProperty(String, javax.jcr.Value)
+     */
+    public Property setProperty(String name, Value value) throws ValueFormatException, RepositoryException {
+        return delegatee.setProperty(name, value);
+    }
+
+    /**
+     * @see Node#setProperty(String, javax.jcr.Value[])
+     */
+    public Property setProperty(String name, Value[] values) throws ValueFormatException, RepositoryException {
+        return delegatee.setProperty(name, values);
+    }
+
+    /**
+     * @see Node#setProperty(String, String[])
+     */
+    public Property setProperty(String name, String[] values) throws ValueFormatException, RepositoryException {
+        return delegatee.setProperty(name, values);
+    }
+
+    /**
+     * @see Node#setProperty(String, String)
+     */
+    public Property setProperty(String name, String value) throws ValueFormatException, RepositoryException {
+        return delegatee.setProperty(name, value);
+    }
+
+    /**
+     * @see Node#setProperty(String, java.io.InputStream)
+     */
+    public Property setProperty(String name, InputStream value) throws ValueFormatException, RepositoryException {
+        return delegatee.setProperty(name, value);
+    }
+
+    /**
+     * @see Node#setProperty(String, boolean)
+     */
+    public Property setProperty(String name, boolean value) throws ValueFormatException, RepositoryException {
+        return delegatee.setProperty(name, value);
+    }
+
+    /**
+     * @see Node#setProperty(String, double)
+     */
+    public Property setProperty(String name, double value) throws ValueFormatException, RepositoryException {
+        return delegatee.setProperty(name, value);
+    }
+
+    /**
+     * @see Node#setProperty(String, long)
+     */
+    public Property setProperty(String name, long value) throws ValueFormatException, RepositoryException {
+        return delegatee.setProperty(name, value);
+    }
+
+    /**
+     * @see Node#setProperty(String, java.util.Calendar)
+     */
+    public Property setProperty(String name, Calendar value) throws ValueFormatException, RepositoryException {
+        return delegatee.setProperty(name, value);
+    }
+
+    /**
+     * @see Node#setProperty(String, javax.jcr.Node)
+     */
+    public Property setProperty(String name, Node value) throws ValueFormatException, RepositoryException {
+        return delegatee.setProperty(name, value);
+    }
+
+    /**
+     * @see Node#remove(String)
+     */
+    public void remove(String relPath) throws PathNotFoundException, RepositoryException {
+        delegatee.remove(relPath);
+    }
+
+    /**
+     * @see Node#getNode(String)
+     */
+    public Node getNode(String relPath) throws PathNotFoundException, RepositoryException {
+        return delegatee.getNode(relPath);
+    }
+
+    /**
+     * @see Node#getNodes()
+     */
+    public NodeIterator getNodes() throws RepositoryException {
+        return delegatee.getNodes();
+    }
+
+    /**
+     * @see Node#getNodes(String)
+     */
+    public NodeIterator getNodes(String namePattern) throws RepositoryException {
+        return delegatee.getNodes(namePattern);
+    }
+
+    /**
+     * @see Node#getProperty(String)
+     */
+    public Property getProperty(String relPath) throws PathNotFoundException, RepositoryException {
+        return delegatee.getProperty(relPath);
+    }
+
+    public Property getProperty(QName name) throws PathNotFoundException, RepositoryException {
+        return delegatee.getProperty(name);
+    }
+
+    /**
+     * @see javax.jcr.Node#getProperties()
+     */
+    public PropertyIterator getProperties() throws RepositoryException {
+        return delegatee.getProperties();
+    }
+
+    /**
+     * @see Node#getProperties(String)
+     */
+    public PropertyIterator getProperties(String namePattern) throws RepositoryException {
+        return delegatee.getProperties(namePattern);
+    }
+
+    /**
+     * @see javax.jcr.Node#getPrimaryItem()
+     */
+    public Item getPrimaryItem() throws ItemNotFoundException, RepositoryException {
+        return delegatee.getPrimaryItem();
+    }
+
+    /**
+     * @see javax.jcr.Node#getUUID()
+     */
+    public String getUUID() throws UnsupportedRepositoryOperationException, RepositoryException {
+        return delegatee.getUUID();
+    }
+
+    /**
+     * @see javax.jcr.Node#getReferences()
+     */
+    public PropertyIterator getReferences() throws RepositoryException {
+        return delegatee.getReferences();
+    }
+
+    /**
+     * @see Node#hasNode(String)
+     */
+    public boolean hasNode(String relPath) throws RepositoryException {
+        return delegatee.hasNode(relPath);
+    }
+
+    /**
+     * @see Node#hasProperty(String)
+     */
+    public boolean hasProperty(String relPath) throws RepositoryException {
+        return delegatee.hasProperty(relPath);
+    }
+
+    public boolean hasProperty(QName name) throws RepositoryException {
+        return delegatee.hasProperty(name);
+    }
+
+    /**
+     * @see javax.jcr.Node#hasNodes()
+     */
+    public boolean hasNodes() throws RepositoryException {
+        return delegatee.hasNodes();
+    }
+
+    /**
+     * @see javax.jcr.Node#hasProperties()
+     */
+    public boolean hasProperties() throws RepositoryException {
+        return delegatee.hasProperties();
+    }
+
+    /**
+     * @see javax.jcr.Node#getPrimaryNodeType()
+     */
+    public NodeType getPrimaryNodeType() throws RepositoryException {
+        return delegatee.getPrimaryNodeType();
+    }
+
+    /**
+     * @see javax.jcr.Node#getMixinNodeTypes()
+     */
+    public NodeType[] getMixinNodeTypes() throws RepositoryException {
+        return delegatee.getMixinNodeTypes();
+    }
+
+    /**
+     * @see Node#isNodeType(String)
+     */
+    public boolean isNodeType(String nodeTypeName) throws RepositoryException {
+        return delegatee.isNodeType(nodeTypeName);
+    }
+
+    /**
+     * @see Node#addMixin(String)
+     */
+    public void addMixin(String mixinName) throws NoSuchNodeTypeException, ConstraintViolationException, RepositoryException {
+        delegatee.addMixin(mixinName);
+    }
+
+    /**
+     * @see Node#removeMixin(String)
+     */
+    public void removeMixin(String mixinName) throws NoSuchNodeTypeException, ConstraintViolationException, RepositoryException {
+        delegatee.removeMixin(mixinName);
+    }
+
+    /**
+     * @see Node#canAddMixin(String)
+     */
+    public boolean canAddMixin(String mixinName) throws RepositoryException {
+        return delegatee.canAddMixin(mixinName);
+    }
+
+    /**
+     * @see javax.jcr.Node#getDefinition()
+     */
+    public NodeDef getDefinition() throws RepositoryException {
+        return delegatee.getDefinition();
+    }
+
+    /**
+     * @see javax.jcr.Node#checkin()
+     */
+    public Version checkin() throws UnsupportedRepositoryOperationException, RepositoryException {
+        return delegatee.checkin();
+    }
+
+    /**
+     * @see javax.jcr.Node#checkout()
+     */
+    public void checkout() throws UnsupportedRepositoryOperationException, RepositoryException {
+        delegatee.checkout();
+    }
+
+    /**
+     * @see Node#addPredecessor(javax.jcr.version.Version)
+     */
+    public void addPredecessor(Version v) throws VersionException, UnsupportedRepositoryOperationException, RepositoryException {
+        delegatee.addPredecessor(v);
+    }
+
+    /**
+     * @see Node#removePredecessor(javax.jcr.version.Version)
+     */
+    public void removePredecessor(Version v) throws VersionException, UnsupportedRepositoryOperationException, RepositoryException {
+        delegatee.removePredecessor(v);
+    }
+
+    /**
+     * @see Node#update(String)
+     */
+    public void update(String srcWorkspaceName) throws NoSuchWorkspaceException, AccessDeniedException, RepositoryException {
+        delegatee.update(srcWorkspaceName);
+    }
+
+    /**
+     * @see Node#merge(String, boolean)
+     */
+    public void merge(String srcWorkspace, boolean bestEffort) throws UnsupportedRepositoryOperationException, NoSuchWorkspaceException, AccessDeniedException, MergeException, RepositoryException {
+        delegatee.merge(srcWorkspace, bestEffort);
+    }
+
+    /**
+     * @see javax.jcr.Node#isCheckedOut()
+     */
+    public boolean isCheckedOut() throws UnsupportedRepositoryOperationException, RepositoryException {
+        return delegatee.isCheckedOut();
+    }
+
+    /**
+     * @see Node#restore(String)
+     */
+    public void restore(String versionName) throws VersionException, UnsupportedRepositoryOperationException, RepositoryException {
+        delegatee.restore(versionName);
+    }
+
+    /**
+     * @see Node#restore(javax.jcr.version.Version)
+     */
+    public void restore(Version version) throws UnsupportedRepositoryOperationException, RepositoryException {
+        delegatee.restore(version);
+    }
+
+    /**
+     * @see Node#restore(javax.jcr.version.Version, String)
+     */
+    public void restore(Version version, String relPath) throws PathNotFoundException, ItemExistsException, ConstraintViolationException, UnsupportedRepositoryOperationException, RepositoryException {
+        delegatee.restore(version, relPath);
+    }
+
+    /**
+     * @see Node#restoreByLabel(String)
+     */
+    public void restoreByLabel(String versionLabel) throws UnsupportedRepositoryOperationException, RepositoryException {
+        delegatee.restoreByLabel(versionLabel);
+    }
+
+    /**
+     * @see javax.jcr.Node#getVersionHistory()
+     */
+    public VersionHistory getVersionHistory() throws UnsupportedRepositoryOperationException, RepositoryException {
+        return delegatee.getVersionHistory();
+    }
+
+    /**
+     * @see javax.jcr.Node#getBaseVersion()
+     */
+    public Version getBaseVersion() throws UnsupportedRepositoryOperationException, RepositoryException {
+        return delegatee.getBaseVersion();
+    }
+
+    /**
+     * @see Node#lock(boolean, boolean)
+     */
+    public Lock lock(boolean isDeep, boolean isSessionScoped) throws UnsupportedRepositoryOperationException, LockException, AccessDeniedException, RepositoryException {
+        return delegatee.lock(isDeep, isSessionScoped);
+    }
+
+    /**
+     * @see javax.jcr.Node#getLock()
+     */
+    public Lock getLock() throws UnsupportedRepositoryOperationException, LockException, AccessDeniedException, RepositoryException {
+        return delegatee.getLock();
+    }
+
+    /**
+     * @see javax.jcr.Node#unlock()
+     */
+    public void unlock() throws UnsupportedRepositoryOperationException, LockException, AccessDeniedException, RepositoryException {
+        delegatee.unlock();
+    }
+
+    /**
+     * @see javax.jcr.Node#holdsLock()
+     */
+    public boolean holdsLock() throws RepositoryException {
+        return delegatee.holdsLock();
+    }
+
+    /**
+     * @see javax.jcr.Node#isLocked()
+     */
+    public boolean isLocked() throws RepositoryException {
+        return delegatee.isLocked();
+    }
+
+    /**
+     * @see javax.jcr.Node#getPath()
+     */
+    public String getPath() throws RepositoryException {
+        return delegatee.getPath();
+    }
+
+    /**
+     * @see javax.jcr.Node#getName()
+     */
+    public String getName() throws RepositoryException {
+        return delegatee.getName();
+    }
+
+    /**
+     * @see Node#getAncestor(int)
+     */
+    public Item getAncestor(int depth) throws ItemNotFoundException, AccessDeniedException, RepositoryException {
+        return delegatee.getAncestor(depth);
+    }
+
+    /**
+     * @see javax.jcr.Node#getParent()
+     */
+    public Node getParent() throws ItemNotFoundException, AccessDeniedException, RepositoryException {
+        return delegatee.getParent();
+    }
+
+    /**
+     * @see javax.jcr.Node#getDepth()
+     */
+    public int getDepth() throws RepositoryException {
+        return delegatee.getDepth();
+    }
+
+    /**
+     * @see javax.jcr.Node#getSession()
+     */
+    public Session getSession() throws RepositoryException {
+        return delegatee.getSession();
+    }
+
+    /**
+     * @see javax.jcr.Node#isNode()
+     */
+    public boolean isNode() {
+        return delegatee.isNode();
+    }
+
+    /**
+     * @see javax.jcr.Node#isNew()
+     */
+    public boolean isNew() {
+        return delegatee.isNew();
+    }
+
+    /**
+     * @see javax.jcr.Node#isModified()
+     */
+    public boolean isModified() {
+        return delegatee.isModified();
+    }
+
+    /**
+     * @see Node#isSame(javax.jcr.Item)
+     */
+    public boolean isSame(Item otherItem) {
+        return delegatee.isSame(otherItem);
+    }
+
+    /**
+     * @see Node#accept(javax.jcr.ItemVisitor)
+     */
+    public void accept(ItemVisitor visitor) throws RepositoryException {
+        delegatee.accept(visitor);
+    }
+
+    /**
+     * @see javax.jcr.Node#save()
+     */
+    public void save() throws AccessDeniedException, LockException, ConstraintViolationException, InvalidItemStateException, RepositoryException {
+        delegatee.save();
+    }
+
+    /**
+     * @see Node#refresh(boolean)
+     */
+    public void refresh(boolean keepChanges) throws InvalidItemStateException, RepositoryException {
+        delegatee.refresh(keepChanges);
+    }
+
+    /**
+     * @see Node#isGranted(long)
+     */
+    public boolean isGranted(long permissions) throws UnsupportedRepositoryOperationException, RepositoryException {
+        return delegatee.isGranted(permissions);
+    }
+}

Added: incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/version/PersistentNode.java
==============================================================================
--- (empty file)
+++ incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/version/PersistentNode.java	Tue Oct  5 01:57:45 2004
@@ -0,0 +1,568 @@
+/*
+ * Copyright 2004 The Apache Software Foundation.
+ *
+ * Licensed 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.core.version;
+
+import org.apache.jackrabbit.core.*;
+import org.apache.jackrabbit.core.nodetype.*;
+import org.apache.jackrabbit.core.state.*;
+import org.apache.jackrabbit.core.util.uuid.UUID;
+
+import javax.jcr.PropertyType;
+import javax.jcr.RepositoryException;
+import javax.jcr.nodetype.ConstraintViolationException;
+import javax.jcr.nodetype.NoSuchNodeTypeException;
+import javax.jcr.nodetype.NodeDef;
+import javax.jcr.nodetype.PropertyDef;
+import java.util.HashSet;
+import java.util.List;
+
+/**
+ * This Class provides some basic node operations directly on the persistent
+ * state.
+ */
+public class PersistentNode {
+
+    /**
+     * the underlaying persistent state
+     */
+    private PersistentNodeState nodeState;
+
+    /**
+     * the state manager
+     */
+    private final PersistentItemStateManager stateMgr;
+
+    /**
+     * the node type manager
+     */
+    private final NodeTypeManagerImpl ntMgr;
+
+    /**
+     * the cached name
+     */
+    private QName name = null;
+
+    /**
+     * Creates a new persistent node
+     *
+     * @param statemgr
+     * @param ntMgr
+     * @param nodeState
+     */
+    protected PersistentNode(PersistentItemStateManager statemgr,
+                             NodeTypeManagerImpl ntMgr,
+                             PersistentNodeState nodeState) {
+        this.nodeState = nodeState;
+        this.stateMgr = statemgr;
+        this.ntMgr = ntMgr;
+    }
+
+
+    /**
+     * returns the name of this node
+     *
+     * @return
+     */
+    protected QName getName() {
+        if (name == null) {
+            try {
+                String parentId = nodeState.getParentUUID();
+                NodeState parent = (NodeState) stateMgr.getItemState(new NodeId(parentId));
+                name = ((NodeState.ChildNodeEntry) parent.getChildNodeEntries(nodeState.getUUID()).get(0)).getName();
+            } catch (ItemStateException e) {
+                // should never occurr
+                throw new IllegalStateException(e.toString());
+            }
+        }
+        return name;
+    }
+
+    /**
+     * Returns the uuid of this node
+     *
+     * @return
+     */
+    protected String getUUID() {
+        return nodeState.getUUID();
+    }
+
+    /**
+     * Returns the properties of this node
+     *
+     * @return
+     */
+    protected PersistentProperty[] getProperties() {
+        try {
+            List list = nodeState.getPropertyEntries();
+            PersistentProperty[] props = new PersistentProperty[list.size()];
+            for (int i = 0; i < list.size(); i++) {
+                NodeState.PropertyEntry entry = (NodeState.PropertyEntry) list.get(i);
+                PropertyId propId = new PropertyId(nodeState.getUUID(), entry.getName());
+                props[i] = new PersistentProperty((PropertyState) stateMgr.getItemState(propId));
+            }
+            return props;
+        } catch (ItemStateException e) {
+            return null;
+        }
+    }
+
+    /**
+     * Checks if the given property exists
+     *
+     * @param name
+     * @return
+     */
+    protected boolean hasProperty(QName name) {
+        PropertyId propId = new PropertyId(nodeState.getUUID(), name);
+        return stateMgr.hasItemState(propId);
+    }
+
+    /**
+     * Returns the values of the given property of <code>null</code>
+     *
+     * @param name
+     * @return
+     */
+    protected InternalValue[] getPropertyValues(QName name) {
+        PropertyId propId = new PropertyId(nodeState.getUUID(), name);
+        try {
+            PropertyState ps = (PropertyState) stateMgr.getItemState(propId);
+            return ps.getValues();
+        } catch (ItemStateException e) {
+            return null;
+        }
+    }
+
+    /**
+     * Returns the value of the given property or <code>null</code>
+     *
+     * @param name
+     * @return
+     */
+    protected InternalValue getPropertyValue(QName name) {
+        PropertyId propId = new PropertyId(nodeState.getUUID(), name);
+        try {
+            PropertyState ps = (PropertyState) stateMgr.getItemState(propId);
+            return ps.getValues()[0];
+        } catch (ItemStateException e) {
+            return null;
+        }
+    }
+
+    /**
+     * Sets the property value
+     *
+     * @param name
+     * @param value
+     * @throws RepositoryException
+     */
+    protected void setPropertyValue(QName name, InternalValue value)
+            throws RepositoryException {
+        setPropertyValues(name, value.getType(), new InternalValue[]{value});
+    }
+
+    /**
+     * Sets the property values
+     *
+     * @param name
+     * @param type
+     * @param values
+     * @throws RepositoryException
+     */
+    protected void setPropertyValues(QName name, int type, InternalValue[] values)
+            throws RepositoryException {
+        PersistentPropertyState prop = getOrCreatePropertyState(name, type, true);
+        prop.setValues(values);
+    }
+
+    /**
+     * Retrieves or creates a new property state as child property of this node
+     *
+     * @param name
+     * @param type
+     * @param multiValued
+     * @return
+     * @throws RepositoryException
+     */
+    private PersistentPropertyState getOrCreatePropertyState(QName name, int type, boolean multiValued)
+            throws RepositoryException {
+
+        PropertyId propId = new PropertyId(nodeState.getUUID(), name);
+        if (stateMgr.hasItemState(propId)) {
+            try {
+                return (PersistentPropertyState) stateMgr.getItemState(propId);
+            } catch (ItemStateException e) {
+                throw new RepositoryException("Unable to create property: " + e.toString());
+            }
+        } else {
+            try {
+                PropertyDefImpl def = getApplicablePropertyDef(name, type, multiValued);
+                PersistentPropertyState propState = stateMgr.createPropertyState(nodeState.getUUID(), name);
+                propState.setType(type);
+                propState.setDefinitionId(new PropDefId(def.unwrap()));
+
+                // need to store nodestate
+                nodeState.addPropertyEntry(name);
+                return propState;
+            } catch (ItemStateException e) {
+                throw new RepositoryException("Unable to store property: " + e.toString());
+            }
+        }
+    }
+
+    /**
+     * retrieves the property definition for the given contraints
+     *
+     * @param propertyName
+     * @param type
+     * @param multiValued
+     * @return
+     * @throws RepositoryException
+     */
+    protected PropertyDefImpl getApplicablePropertyDef(QName propertyName,
+                                                       int type, boolean multiValued)
+            throws RepositoryException {
+        PropDef pd = getEffectiveNodeType().getApplicablePropertyDef(propertyName, type, multiValued);
+        return ntMgr.getPropDef(new PropDefId(pd));
+    }
+
+    /**
+     * Retrieves the node definition for the given contraints.
+     *
+     * @param nodeName
+     * @param nodeTypeName
+     * @return
+     * @throws RepositoryException
+     */
+    protected NodeDefImpl getApplicableChildNodeDef(QName nodeName, QName nodeTypeName)
+            throws RepositoryException {
+        ChildNodeDef cnd = getEffectiveNodeType().getApplicableChildNodeDef(nodeName, nodeTypeName);
+        return ntMgr.getNodeDef(new NodeDefId(cnd));
+    }
+
+    /**
+     * Returns the effective (i.e. merged and resolved) node type representation
+     * of this node's primary and mixin node types.
+     *
+     * @return the effective node type
+     * @throws RepositoryException
+     */
+    protected EffectiveNodeType getEffectiveNodeType() throws RepositoryException {
+        // build effective node type of mixins & primary type
+        NodeTypeRegistry ntReg = ntMgr.getNodeTypeRegistry();
+        // existing mixin's
+        HashSet set = new HashSet(nodeState.getMixinTypeNames());
+        // primary type
+        set.add(nodeState.getNodeTypeName());
+        try {
+            return ntReg.buildEffectiveNodeType((QName[]) set.toArray(new QName[set.size()]));
+        } catch (NodeTypeConflictException ntce) {
+            String msg = "internal error: failed to build effective node type for node " + nodeState.getUUID();
+            throw new RepositoryException(msg, ntce);
+        }
+    }
+
+    /**
+     * checks if the given child node exists.
+     *
+     * @param name
+     * @return
+     */
+    protected boolean hasNode(QName name) {
+        return nodeState.hasChildNodeEntry(name);
+    }
+
+    /**
+     * removes the (first) child node with the given name.
+     *
+     * @param name
+     * @return
+     * @throws RepositoryException
+     */
+    protected boolean removeNode(QName name) throws RepositoryException {
+        return removeNode(name, 1);
+    }
+
+    /**
+     * removes the child node with the given name and 1-based index
+     *
+     * @param name
+     * @param index
+     * @return
+     * @throws RepositoryException
+     */
+    protected boolean removeNode(QName name, int index) throws RepositoryException {
+        if (nodeState.removeChildNodeEntry(name, index)) {
+            return true;
+        } else {
+            return false;
+        }
+    }
+
+    /**
+     * retrieves the child node with the given name and 1-base index or
+     * <code>null</code> if the node does not exist.
+     *
+     * @param name
+     * @param index
+     * @return
+     * @throws RepositoryException
+     */
+    protected PersistentNode getNode(QName name, int index) throws RepositoryException {
+        NodeState.ChildNodeEntry entry = nodeState.getChildNodeEntry(name, index);
+        if (entry == null) {
+            return null;
+        }
+        try {
+            PersistentNodeState state = (PersistentNodeState) stateMgr.getItemState(new NodeId(entry.getUUID()));
+            return new PersistentNode(stateMgr, ntMgr, state);
+        } catch (ItemStateException e) {
+            throw new RepositoryException("Unable to getNode: " + e.toString());
+        }
+    }
+
+    /**
+     * returns the node with the given uuid.
+     *
+     * @param uuid
+     * @return
+     * @throws RepositoryException
+     */
+    protected PersistentNode getNodeByUUID(String uuid) throws RepositoryException {
+        try {
+            PersistentNodeState state = (PersistentNodeState) stateMgr.getItemState(new NodeId(uuid));
+            return new PersistentNode(stateMgr, ntMgr, state);
+        } catch (ItemStateException e) {
+            throw new RepositoryException("Unable to getNode: " + e.toString());
+        }
+    }
+
+    /**
+     * Adds a new child node with the given name
+     *
+     * @param nodeName
+     * @param nodeTypeName
+     * @return
+     * @throws NoSuchNodeTypeException
+     * @throws ConstraintViolationException
+     * @throws RepositoryException
+     */
+    protected PersistentNode addNode(QName nodeName, QName nodeTypeName)
+            throws NoSuchNodeTypeException, ConstraintViolationException, RepositoryException {
+        NodeTypeImpl nodeType = ntMgr.getNodeType(nodeTypeName);
+        NodeDefImpl def;
+        try {
+            def = getApplicableChildNodeDef(nodeName, nodeType == null ? null : nodeType.getQName());
+        } catch (RepositoryException re) {
+            String msg = "no definition found in parent node's node type for new node";
+            throw new ConstraintViolationException(msg, re);
+        }
+        if (nodeType == null) {
+            // use default node type
+            nodeType = (NodeTypeImpl) def.getDefaultPrimaryType();
+        }
+        return createChildNode(nodeName, def, nodeType, null);
+    }
+
+    /**
+     * creates a new child node
+     *
+     * @param name
+     * @param def
+     * @param nodeType
+     * @param uuid
+     * @return
+     * @throws RepositoryException
+     */
+    private PersistentNode createChildNode(QName name, NodeDefImpl def,
+                                           NodeTypeImpl nodeType, String uuid)
+            throws RepositoryException {
+
+        String parentUUID = nodeState.getUUID();
+        // create a new node state
+        PersistentNodeState state = null;
+        try {
+            if (uuid == null) {
+                uuid = UUID.randomUUID().toString();	// version 4 uuid
+            }
+            state = stateMgr.createNodeState(uuid, nodeType.getQName(), parentUUID);
+            state.setDefinitionId(new NodeDefId(def.unwrap()));
+        } catch (ItemStateException ise) {
+            String msg = "failed to add child node " + name + " to " + parentUUID;
+            throw new RepositoryException(msg, ise);
+        }
+
+        // create Node instance wrapping new node state
+        PersistentNode node = new PersistentNode(stateMgr, ntMgr, state);
+        // add new child node entry
+        nodeState.addChildNodeEntry(name, state.getUUID());
+
+        // add 'auto-create' properties defined in node type
+        PropertyDef[] pda = nodeType.getAutoCreatePropertyDefs();
+        for (int i = 0; i < pda.length; i++) {
+            PropertyDefImpl pd = (PropertyDefImpl) pda[i];
+            node.getOrCreatePropertyState(pd.getQName(), pd.getRequiredType(), pd.isMultiple());
+        }
+
+        // recursively add 'auto-create' child nodes defined in node type
+        NodeDef[] nda = nodeType.getAutoCreateNodeDefs();
+        for (int i = 0; i < nda.length; i++) {
+            NodeDefImpl nd = (NodeDefImpl) nda[i];
+            node.createChildNode(nd.getQName(), nd, (NodeTypeImpl) nd.getDefaultPrimaryType(), null);
+        }
+
+        // store primary type
+        node.setPropertyValue(ItemImpl.PROPNAME_PRIMARYTYPE, InternalValue.create(nodeType.getQName()));
+        return node;
+    }
+
+    /**
+     * returns all child nodes
+     *
+     * @return
+     * @throws RepositoryException
+     */
+    protected PersistentNode[] getChildNodes() throws RepositoryException {
+        try {
+            List entries = nodeState.getChildNodeEntries();
+            PersistentNode[] children = new PersistentNode[entries.size()];
+            for (int i = 0; i < entries.size(); i++) {
+                NodeState.ChildNodeEntry entry = (NodeState.ChildNodeEntry) entries.get(i);
+                PersistentNodeState state = (PersistentNodeState) stateMgr.getItemState(new NodeId(entry.getUUID()));
+                children[i] = new PersistentNode(stateMgr, ntMgr, state);
+            }
+            return children;
+        } catch (ItemStateException e) {
+            throw new RepositoryException(e);
+        }
+    }
+
+    /**
+     * stores the persistent state recursively
+     *
+     * @throws RepositoryException
+     */
+    protected void store() throws RepositoryException {
+        try {
+            store(nodeState);
+        } catch (ItemStateException e) {
+            throw new RepositoryException(e);
+        }
+    }
+
+    /**
+     * stores the given persistent state recursively
+     *
+     * @param state
+     * @throws ItemStateException
+     */
+    private void store(PersistentNodeState state) throws ItemStateException {
+        // first store all transient properties
+        List props = state.getPropertyEntries();
+        for (int i = 0; i < props.size(); i++) {
+            NodeState.PropertyEntry entry = (NodeState.PropertyEntry) props.get(i);
+            PersistentPropertyState pstate = (PersistentPropertyState) stateMgr.getItemState(new PropertyId(state.getUUID(), entry.getName()));
+            if (pstate.isTransient()) {
+                pstate.store();
+            }
+        }
+        // now store all child node entries
+        List nodes = state.getChildNodeEntries();
+        for (int i = 0; i < nodes.size(); i++) {
+            NodeState.ChildNodeEntry entry = (NodeState.ChildNodeEntry) nodes.get(i);
+            PersistentNodeState nstate = (PersistentNodeState) stateMgr.getItemState(new NodeId(entry.getUUID()));
+            store(nstate);
+        }
+        // and store itself
+        state.store();
+    }
+
+    /**
+     * reloads the persistent state recursively
+     *
+     * @throws RepositoryException
+     */
+    protected void reload() throws RepositoryException {
+        try {
+            reload(nodeState);
+            // refetch nodestate if discarded
+            nodeState = (PersistentNodeState) stateMgr.getItemState(nodeState.getId());
+        } catch (ItemStateException e) {
+            throw new RepositoryException(e);
+        }
+    }
+
+    /**
+     * reloads the given persistent state recursively
+     *
+     * @param state
+     * @throws ItemStateException
+     */
+    private void reload(PersistentNodeState state) throws ItemStateException {
+        // first discard all all transient properties
+        List props = state.getPropertyEntries();
+        for (int i = 0; i < props.size(); i++) {
+            NodeState.PropertyEntry entry = (NodeState.PropertyEntry) props.get(i);
+            PersistentPropertyState pstate = (PersistentPropertyState) stateMgr.getItemState(new PropertyId(state.getUUID(), entry.getName()));
+            if (pstate.isTransient()) {
+                pstate.discard();
+            }
+        }
+        // now reload all child node entries
+        List nodes = state.getChildNodeEntries();
+        for (int i = 0; i < nodes.size(); i++) {
+            NodeState.ChildNodeEntry entry = (NodeState.ChildNodeEntry) nodes.get(i);
+            PersistentNodeState nstate = (PersistentNodeState) stateMgr.getItemState(new NodeId(entry.getUUID()));
+            reload(nstate);
+        }
+        // and reload itself
+        if (state.isTransient()) {
+            state.discard();
+        }
+    }
+
+    /**
+     * copies a property
+     *
+     * @param prop
+     * @throws RepositoryException
+     */
+    protected void copyFrom(PropertyImpl prop) throws RepositoryException {
+        if (prop.getDefinition().isMultiple()) {
+            InternalValue[] values = prop.internalGetValues();
+            setPropertyValues(prop.getQName(), values[0].getType(), values);
+        } else {
+            setPropertyValue(prop.getQName(), prop.internalGetValue());
+        }
+    }
+
+    /**
+     * sets the mixing node type and adds the respective property
+     *
+     * @param mixins
+     * @throws RepositoryException
+     */
+    protected void setMixinNodeTypes(QName[] mixins) throws RepositoryException {
+        HashSet set = new HashSet();
+        InternalValue[] values = new InternalValue[mixins.length];
+        for (int i = 0; i < mixins.length; i++) {
+            set.add(mixins[i]);
+            values[i] = InternalValue.create(mixins[i]);
+        }
+        nodeState.setMixinTypeNames(set);
+        setPropertyValues(ItemImpl.PROPNAME_MIXINTYPES, PropertyType.NAME, values);
+    }
+}

Added: incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/version/PersistentProperty.java
==============================================================================
--- (empty file)
+++ incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/version/PersistentProperty.java	Tue Oct  5 01:57:45 2004
@@ -0,0 +1,69 @@
+/*
+ * Copyright 2004 The Apache Software Foundation.
+ *
+ * Licensed 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.core.version;
+
+import org.apache.jackrabbit.core.InternalValue;
+import org.apache.jackrabbit.core.QName;
+import org.apache.jackrabbit.core.state.PropertyState;
+
+/**
+ * This Class represents a property that was frozen during a
+ * {@link javax.jcr.Node#checkin()}
+ */
+public class PersistentProperty {
+
+    /**
+     * the underlaying persistent state
+     */
+    private PropertyState state;
+
+    /**
+     * Creates a new persistent property
+     *
+     * @param state
+     */
+    public PersistentProperty(PropertyState state) {
+        this.state = state;
+    }
+
+    /**
+     * returns the name of the property
+     *
+     * @return
+     */
+    public QName getName() {
+        return state.getName();
+    }
+
+    /**
+     * returns the values of the property
+     *
+     * @return
+     */
+    public InternalValue[] getValues() {
+        return state.getValues();
+    }
+
+    /**
+     * returns the type of the property
+     *
+     * @return
+     */
+    public int getType() {
+        return state.getType();
+    }
+
+}

Modified: incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/version/VersionHistoryImpl.java
==============================================================================
--- incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/version/VersionHistoryImpl.java	(original)
+++ incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/version/VersionHistoryImpl.java	Tue Oct  5 01:57:45 2004
@@ -16,189 +16,93 @@
 package org.apache.jackrabbit.core.version;
 
 import org.apache.jackrabbit.core.*;
-import org.apache.jackrabbit.core.state.NodeState;
-import org.apache.log4j.Logger;
 
-import javax.jcr.NodeIterator;
 import javax.jcr.RepositoryException;
-import javax.jcr.nodetype.NodeDef;
+import javax.jcr.Session;
+import javax.jcr.UnsupportedRepositoryOperationException;
 import javax.jcr.version.Version;
-import javax.jcr.version.VersionException;
 import javax.jcr.version.VersionHistory;
 import javax.jcr.version.VersionIterator;
-import java.util.HashMap;
 
 /**
- * This Class implements a version history.
+ * This Class implements a version history that extends a node.
  */
-public class VersionHistoryImpl extends NodeImpl implements VersionHistory {
+public class VersionHistoryImpl extends NodeWrapper implements VersionHistory {
 
     /**
-     * default logger
+     * the internal version history
      */
-    private static Logger log = Logger.getLogger(VersionHistoryImpl.class);
+    private final InternalVersionHistory history;
 
     /**
-     * the name of the 'jcr:rootVersion' node
-     */
-    public static final QName NODENAME_ROOTVERSION = new QName(NamespaceRegistryImpl.NS_JCR_URI, "rootVersion");
-
-    /**
-     * the cache of the version labels (initialized on first usage)
-     * key = version label (String)
-     * value = version name (String)
-     */
-    private HashMap labelCache = null;
-
-    /**
-     * Creates a new VersionHistory object for the given node.
-     * <p/>
-     * Currently, it just casts the given node into a VersionHistoryImpl, but
-     * this might change, if in the future spec the VersionHistory does not
-     * extend Node anymore.
+     * creates a new version history implementation for the given session and
+     * internal version history
      *
-     * @param node
-     * @return a VersionHistory
-     */
-    protected static VersionHistoryImpl create(NodeImpl node) {
-        return (VersionHistoryImpl) node;
-    }
-
-    /**
-     * Creates a new VersionHistoryImpl object. This is only called by the
-     * item manager, when creating new node instances.
-     *
-     * @see org.apache.jackrabbit.core.ItemManager#createNodeInstance(org.apache.jackrabbit.core.state.NodeState, javax.jcr.nodetype.NodeDef)
+     * @param session
+     * @param history
+     * @throws RepositoryException
      */
-    public VersionHistoryImpl(ItemManager itemMgr, SessionImpl session, NodeId id,
-                              NodeState state, NodeDef definition,
-                              ItemLifeCycleListener[] listeners)
+    protected VersionHistoryImpl(Session session, InternalVersionHistory history)
             throws RepositoryException {
-        super(itemMgr, session, id, state, definition, listeners);
+        super((NodeImpl) session.getNodeByUUID(history.getUUID()));
+        this.history = history;
     }
 
     /**
      * @see VersionHistory#getRootVersion()
      */
     public Version getRootVersion() throws RepositoryException {
-        return (Version) getNode(NODENAME_ROOTVERSION);
+        return new VersionImpl(unwrap().getSession(), history.getRootVersion());
     }
 
     /**
      * @see VersionHistory#getAllVersions()
      */
     public VersionIterator getAllVersions() throws RepositoryException {
-        return new VersionIteratorImpl(getRootVersion());
+        return new VersionIteratorImpl(unwrap().getSession(), history.getRootVersion());
     }
 
     /**
-     * @see VersionHistory#getVersion(java.lang.String)
+     * @see VersionHistory#getVersion(String)
      */
     public Version getVersion(String versionName) throws RepositoryException {
-        return (Version) getNode(versionName);
+        try {
+            QName name = QName.fromJCRName(versionName, ((SessionImpl) unwrap().getSession()).getNamespaceResolver());
+            InternalVersion v = history.getVersion(name);
+            return v == null ? null : new VersionImpl(unwrap().getSession(), v);
+        } catch (IllegalNameException e) {
+            throw new RepositoryException(e);
+        } catch (UnknownPrefixException e) {
+            throw new RepositoryException(e);
+        }
     }
 
     /**
-     * @see VersionHistory#getVersionByLabel(java.lang.String)
+     * @see VersionHistory#getVersionByLabel(String)
      */
     public Version getVersionByLabel(String label) throws RepositoryException {
-        initLabelCache();
-        return labelCache.containsKey(label)
-                ? (Version) getNode((String) labelCache.get(label))
-                : null;
-    }
-
-    /**
-     * Removes the indicated version from this VersionHistory. If the specified
-     * vesion does not exist, if it specifies the root version or if it is
-     * referenced by any node e.g. as base version, a VersionException is thrown.
-     * <p/>
-     * all successors of the removed version become successors of the
-     * predecessors of the removed version and vice versa. then, the entire
-     * version node and all its subnodes are removed.
-     *
-     * @param versionName
-     * @throws RepositoryException todo: add to spec
-     */
-    public void removeVersion(String versionName) throws RepositoryException {
-        VersionImpl v = (VersionImpl) getVersion(versionName);
-        if (v.isSame(getRootVersion())) {
-            String msg = "Removal of " + versionName + " not allowed.";
-            log.error(msg);
-            throw new VersionException(msg);
-        }
-        // check if any references to this node exist outside the version graph
-        // todo: check this
-
-        // detach from the version graph
-        v.internalDetach();
-
-        // and remove from history
-        remove(versionName);
-        save();
+        InternalVersion v = history.getVersionByLabel(label);
+        return v == null ? null : new VersionImpl(unwrap().getSession(), v);
     }
 
     /**
-     * Initializes the label cache
-     *
-     * @throws RepositoryException
+     * @see VersionHistory#addVersionLabel(Version, String, boolean)
      */
-    private void initLabelCache() throws RepositoryException {
-        if (labelCache != null) {
-            return;
-        }
-        labelCache = new HashMap();
-        NodeIterator iter = getNodes();
-        while (iter.hasNext()) {
-            // assuming all subnodes are 'versions'
-            Version v = (Version) iter.nextNode();
-            String[] labels = v.getVersionLabels();
-            for (int i = 0; i < labels.length; i++) {
-                if (labelCache.containsKey(labels[i])) {
-                    log.error("Label " + labels[i] + " duplicate: in " + v.getName() + " and in " + labelCache.get(labels[i]));
-                } else {
-                    labelCache.put(labels[i], v.getName());
-                }
-            }
-        }
+    public void addVersionLabel(Version version, String label, boolean move) throws RepositoryException {
+        history.addVersionLabel(((VersionImpl) version).version, label, move);
     }
 
     /**
-     * Adds a label to a version
-     *
-     * @param version
-     * @param label
-     * @throws RepositoryException
+     * @see VersionHistory#removeVersionLabel(String)
      */
-    public void addVersionLabel(Version version, String label) throws RepositoryException {
-        initLabelCache();
-        String vname = (String) labelCache.get(label);
-        if (vname == null) {
-            // if not exists, add
-            labelCache.put(label, version.getName());
-            ((VersionImpl) version).internalAddVersionLabel(label);
-        } else if (vname.equals(version.getName())) {
-            // if already defined to this version, ignore
-        } else {
-            // already defined eslwhere, throw
-            throw new RepositoryException("Version label " + label + " already defined for version " + vname);
-        }
+    public void removeVersionLabel(String label) throws RepositoryException {
+        history.removeVersionLabel(label);
     }
 
     /**
-     * Removes the label from the respective version
-     *
-     * @param label
-     * @throws RepositoryException if the label does not exist
+     * @see javax.jcr.Node#getUUID()
      */
-    public void removeVersionLabel(String label) throws RepositoryException {
-        initLabelCache();
-        String name = (String) labelCache.remove(label);
-        if (name == null) {
-            throw new RepositoryException("Version label " + label + " is not in version history.");
-        }
-        ((VersionImpl) getVersion(name)).internalRemoveVersionLabel(label);
+    public String getUUID() throws UnsupportedRepositoryOperationException, RepositoryException {
+        return history.getUUID();
     }
 }
-
-

Modified: incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/version/VersionImpl.java
==============================================================================
--- incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/version/VersionImpl.java	(original)
+++ incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/version/VersionImpl.java	Tue Oct  5 01:57:45 2004
@@ -15,320 +15,109 @@
  */
 package org.apache.jackrabbit.core.version;
 
-import org.apache.jackrabbit.core.*;
-import org.apache.jackrabbit.core.state.NodeState;
-import org.apache.jackrabbit.core.util.uuid.UUID;
+import org.apache.jackrabbit.core.NodeImpl;
 
-import javax.jcr.Property;
 import javax.jcr.RepositoryException;
-import javax.jcr.Value;
-import javax.jcr.ValueFormatException;
-import javax.jcr.nodetype.NodeDef;
+import javax.jcr.Session;
+import javax.jcr.UnsupportedRepositoryOperationException;
 import javax.jcr.version.Version;
 import java.util.Calendar;
-import java.util.HashSet;
-import java.util.Iterator;
-import java.util.Set;
 
 /**
- * This Class implements the Version representation of the node.
+ * This Class implements a Version that extends the node interface
  */
-public class VersionImpl extends FrozenNode implements Version {
+public class VersionImpl extends NodeWrapper implements Version {
 
     /**
-     * name of the 'jcr:versionLabels' property
+     * the internal version
      */
-    public static final QName PROPNAME_VERSION_LABELS = new QName(NamespaceRegistryImpl.NS_JCR_URI, "versionLabels");
+    protected final InternalVersion version;
 
     /**
-     * name of the 'jcr:predecessors' property
-     */
-    public static final QName PROPNAME_PREDECESSORS = new QName(NamespaceRegistryImpl.NS_JCR_URI, "predecessors");
-
-    /**
-     * name of the 'jcr:successors' property
-     */
-    public static final QName PROPNAME_SUCCESSORS = new QName(NamespaceRegistryImpl.NS_JCR_URI, "successors");
-
-    /**
-     * name of the 'jcr:isCheckedOut' property
-     */
-    public static final QName PROPNAME_IS_CHECKED_OUT = new QName(NamespaceRegistryImpl.NS_JCR_URI, "isCheckedOut");
-
-    /**
-     * name of the 'jcr:versionHistory' property
-     */
-    public static final QName PROPNAME_VERSION_HISTORY = new QName(NamespaceRegistryImpl.NS_JCR_URI, "versionHistory");
-
-    /**
-     * name of the 'jcr:baseVersion' property
-     */
-    public static final QName PROPNAME_BASE_VERSION = new QName(NamespaceRegistryImpl.NS_JCR_URI, "baseVersion");
-
-    /**
-     * cache for version labels
-     */
-    private Set cachedVersionLabels;
-
-    /**
-     * Creates a new Version node. This is only called by the ItemManager when
-     * creating new node instances.
+     * Creates a new version implementation
      *
-     * @see org.apache.jackrabbit.core.ItemManager#createNodeInstance(org.apache.jackrabbit.core.state.NodeState, javax.jcr.nodetype.NodeDef)
+     * @param session
+     * @param version
+     * @throws RepositoryException
      */
-    public VersionImpl(ItemManager itemMgr, SessionImpl session, NodeId id,
-                       NodeState state, NodeDef definition,
-                       ItemLifeCycleListener[] listeners)
+    protected VersionImpl(Session session, InternalVersion version)
             throws RepositoryException {
-        super(itemMgr, session, id, state, definition, listeners);
+        super((NodeImpl) session.getNodeByUUID(version.getUUID()));
+        this.version = version;
     }
 
-
     /**
      * @see Version#getCreated()
      */
     public Calendar getCreated() throws RepositoryException {
-        // no check for NULL needed since its mandatory
-        return getProperty(PROPNAME_CREATED).getDate();
+        return version.getCreated();
     }
 
     /**
      * @see Version#getVersionLabels()
      */
     public String[] getVersionLabels() throws RepositoryException {
-        initLabelCache();
-        return (String[]) cachedVersionLabels.toArray(new String[cachedVersionLabels.size()]);
+        return version.internalGetLabels();
     }
 
-    /**
-     * Checks if this version contains the given version label.
-     *
-     * @param label the param to check
-     * @throws RepositoryException if an error occurrs
-     *                             <p/>
-     *                             todo: add to spec
-     */
-    public boolean hasVersionLabel(String label) throws RepositoryException {
-        initLabelCache();
-        return cachedVersionLabels.contains(label);
-    }
-
-    /**
-     * @see Version#addVersionLabel(java.lang.String)
-     */
     public void addVersionLabel(String label) throws RepositoryException {
-        // delegate to version history (will probably change in spec)
-        getHistory().addVersionLabel(this, label);
+        version.getVersionHistory().addVersionLabel(version, label, false);
     }
 
-    /**
-     * @see Version#removeVersionLabel(java.lang.String)
-     */
     public void removeVersionLabel(String label) throws RepositoryException {
-        // delegate to version history (will probably change in spec)
-        getHistory().removeVersionLabel(label);
+        version.getVersionHistory().removeVersionLabel(label);
     }
 
     /**
      * @see Version#getSuccessors()
      */
     public Version[] getSuccessors() throws RepositoryException {
-        if (hasProperty(PROPNAME_SUCCESSORS)) {
-            Value[] values = getProperty(PROPNAME_SUCCESSORS).getValues();
-            Version[] preds = new Version[values.length];
-            for (int i = 0; i < values.length; i++) {
-                preds[i] = (Version) session.getNodeByUUID(values[i].getString());
-            }
-            return preds;
+        // need to wrap it around proper node
+        InternalVersion[] suc = version.getSuccessors();
+        Version[] ret = new Version[suc.length];
+        for (int i = 0; i < suc.length; i++) {
+            ret[i] = new VersionImpl(unwrap().getSession(), suc[i]);
         }
-        return new Version[0];
+        return ret;
     }
 
     /**
-     * Adds a successor to the jcr:successor list
-     * <p/>
-     * please note, that this operation might corrupt the version graph
-     *
-     * @param succ
+     * @see Version#getPredecessors()
      */
-    void internalAddSuccessor(VersionImpl succ) throws RepositoryException {
-        Version[] successors = getSuccessors();
-        InternalValue[] values = new InternalValue[successors.length + 1];
-        for (int i = 0; i < successors.length; i++) {
-            values[i] = InternalValue.create(new UUID(successors[i].getUUID()));
-        }
-        values[successors.length] = InternalValue.create(new UUID(succ.getUUID()));
-        internalSetProperty(PROPNAME_SUCCESSORS, values);
-    }
-
-    /**
-     * Detaches itself from the version graph.
-     *
-     * @throws RepositoryException
-     */
-    void internalDetach() throws RepositoryException {
-        // detach this from all successors
-        VersionImpl[] succ = (VersionImpl[]) getSuccessors();
-        for (int i = 0; i < succ.length; i++) {
-            succ[i].internalDetachPredecessor(this);
-        }
-        // detach this from all predecessors
-        VersionImpl[] pred = (VersionImpl[]) getPredecessors();
+    public Version[] getPredecessors() throws RepositoryException {
+        // need to wrap it around proper node
+        InternalVersion[] pred = version.getPredecessors();
+        Version[] ret = new Version[pred.length];
         for (int i = 0; i < pred.length; i++) {
-            pred[i].internalDetachSuccessor(this);
+            ret[i] = new VersionImpl(unwrap().getSession(), pred[i]);
         }
-        // clear properties
-        internalSetProperty(PROPNAME_PREDECESSORS, new InternalValue[0]);
-        internalSetProperty(PROPNAME_SUCCESSORS, new InternalValue[0]);
+        return ret;
     }
 
     /**
-     * Removes the successor V of this successors list and adds all of Vs
-     * successors to it.
-     * <p/>
-     * please note, that this operation might corrupt the version graph
-     *
-     * @param v the successor to detach
+     * @see javax.jcr.Node#getUUID()
      */
-    private void internalDetachSuccessor(VersionImpl v) throws RepositoryException {
-        Version[] vsucc = v.getSuccessors();
-        Version[] successors = getSuccessors();
-        InternalValue[] values = new InternalValue[successors.length - 1 + vsucc.length];
-        int idx = 0;
-        // copy successors but ignore 'v'
-        for (int i = 0; i < successors.length; i++) {
-            if (!successors[i].isSame(v)) {
-                values[idx++] = InternalValue.create(new UUID(successors[i].getUUID()));
-            }
-        }
-        // attach v's successors
-        for (int i = 0; i < vsucc.length; i++) {
-            values[idx++] = InternalValue.create(new UUID(vsucc[i].getUUID()));
-        }
-        internalSetProperty(PROPNAME_SUCCESSORS, values);
-    }
-
-    /**
-     * Removes the predecessor V of this predecessor list and adds all of Vs
-     * predecessors to it.
-     * <p/>
-     * please note, that this operation might corrupt the version graph
-     *
-     * @param v the successor to detach
-     */
-    private void internalDetachPredecessor(VersionImpl v) throws RepositoryException {
-        Version[] vpred = v.getPredecessors();
-        Version[] tpred = getPredecessors();
-        InternalValue[] values = new InternalValue[tpred.length - 1 + vpred.length];
-        int idx = 0;
-        // copy predecessors but ignore 'v'
-        for (int i = 0; i < tpred.length; i++) {
-            if (!tpred[i].isSame(v)) {
-                values[idx++] = InternalValue.create(new UUID(tpred[i].getUUID()));
-            }
-        }
-        // attach v's predecessors
-        for (int i = 0; i < vpred.length; i++) {
-            values[idx++] = InternalValue.create(new UUID(vpred[i].getUUID()));
-        }
-        internalSetProperty(PROPNAME_PREDECESSORS, values);
-    }
-
-    /**
-     * @see NodeImpl#internalSetProperty(org.apache.jackrabbit.core.QName, org.apache.jackrabbit.core.InternalValue)
-     */
-    public Property internalSetProperty(QName name,
-                                        InternalValue value)
-            throws ValueFormatException, RepositoryException {
-        return super.internalSetProperty(name, value);
-    }
-
-    /**
-     * @see NodeImpl#internalSetProperty(org.apache.jackrabbit.core.QName, org.apache.jackrabbit.core.InternalValue[])
-     */
-    protected Property internalSetProperty(QName name,
-                                           InternalValue[] value)
-            throws ValueFormatException, RepositoryException {
-        return super.internalSetProperty(name, value);
-    }
-
-    /**
-     * @see Version#addVersionLabel(java.lang.String)
-     */
-    protected void internalAddVersionLabel(String label) throws RepositoryException {
-        initLabelCache();
-        cachedVersionLabels.add(label);
-        saveLabelCache();
-    }
-
-    /**
-     * @see Version#addVersionLabel(java.lang.String)
-     */
-    protected void internalRemoveVersionLabel(String label) throws RepositoryException {
-        initLabelCache();
-        cachedVersionLabels.remove(label);
-        saveLabelCache();
+    public String getUUID() throws UnsupportedRepositoryOperationException, RepositoryException {
+        return version.getUUID();
     }
 
     /**
-     * Initializes / Loads the version label cache
-     */
-    private void initLabelCache() throws RepositoryException {
-        if (cachedVersionLabels == null) {
-            cachedVersionLabels = new HashSet();
-            if (hasProperty(PROPNAME_VERSION_LABELS)) {
-                Value[] values = getProperty(PROPNAME_VERSION_LABELS).getValues();
-                for (int i = 0; i < values.length; i++) {
-                    cachedVersionLabels.add(values[i].getString());
-                }
-            }
-        }
-    }
-
-    /**
-     * Saves the current labels in the cache to the 'VersionLabels' property
-     * of this node.
+     * Returns the internal version
      *
-     * @throws RepositoryException
+     * @return
      */
-    private void saveLabelCache() throws RepositoryException {
-        InternalValue[] newValues = new InternalValue[cachedVersionLabels.size()];
-        Iterator iter = cachedVersionLabels.iterator();
-        for (int i = 0; i < newValues.length; i++) {
-            newValues[i] = InternalValue.create((String) iter.next());
-        }
-        internalSetProperty(PROPNAME_VERSION_LABELS, newValues);
-        save();
+    public InternalVersion getInternalVersion() {
+        return version;
     }
 
     /**
-     * Returns the version history of this version and not the extended node.
+     * Returns the forzen node of this version
      *
-     * @return the version history of this version graph
+     * @return
      * @throws RepositoryException
      */
-    private VersionHistoryImpl getHistory() throws RepositoryException {
-        return (VersionHistoryImpl) getParent();
+    public InternalFrozenNode getFrozenNode() throws RepositoryException {
+        return version.getFrozenNode();
     }
 
-
-    /**
-     * Checks if this version is more recent than the given version <code>v</code>.
-     * A version is more recent if and only if it is a successor (or a successor
-     * of a successor, etc., to any degree of separation) of the compared one.
-     *
-     * @param v the version to check
-     * @return <code>true</code> if the version is more recent;
-     *         <code>false</code> otherwise.
-     * @throws RepositoryException if an error occurrs.
-     */
-    public boolean isMoreRecent(Version v) throws RepositoryException {
-        VersionIteratorImpl iter = new VersionIteratorImpl(v);
-        while (iter.hasNext()) {
-            if (iter.nextVersion().isSame(this)) {
-                return true;
-            }
-        }
-        return false;
-    }
 }

Modified: incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/version/VersionIteratorImpl.java
==============================================================================
--- incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/version/VersionIteratorImpl.java	(original)
+++ incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/version/VersionIteratorImpl.java	Tue Oct  5 01:57:45 2004
@@ -16,9 +16,12 @@
 package org.apache.jackrabbit.core.version;
 
 import javax.jcr.RepositoryException;
+import javax.jcr.Session;
 import javax.jcr.version.Version;
 import javax.jcr.version.VersionIterator;
+import java.util.HashSet;
 import java.util.NoSuchElementException;
+import java.util.Set;
 import java.util.Stack;
 
 /**
@@ -38,12 +41,24 @@
     private Stack successors = new Stack();
 
     /**
+     * the set of versions already returned. due to the topology of the version
+     * graph it is possible to reach a version via different paths.
+     */
+    private Set visited = new HashSet();
+
+    /**
+     * the session for wrapping the versions
+     */
+    private final Session session;
+
+    /**
      * Creates a new VersionIterator that iterates over the version tree,
      * starting the root node.
      *
      * @param rootVersion
      */
-    public VersionIteratorImpl(Version rootVersion) throws RepositoryException {
+    public VersionIteratorImpl(Session session, InternalVersion rootVersion) {
+        this.session = session;
         successors.push(rootVersion);
     }
 
@@ -54,14 +69,16 @@
         if (successors.isEmpty()) {
             throw new NoSuchElementException();
         }
-        Version ret = (Version) successors.peek();
+        InternalVersion ret = (InternalVersion) successors.peek();
+        visited.add(ret);
         pos++;
+        push(ret.getSuccessors());
+
         try {
-            push(ret.getSuccessors());
+            return new VersionImpl(session, ret);
         } catch (RepositoryException e) {
-            // ignore
+            throw new NoSuchElementException("Unable to provide element: " + e.toString());
         }
-        return ret;
     }
 
     /**
@@ -115,9 +132,11 @@
      *
      * @param versions
      */
-    private void push(Version[] versions) {
+    private void push(InternalVersion[] versions) {
         for (int i = 0; i < versions.length; i++) {
-            successors.push(versions[i]);
+            if (!visited.contains(versions[i])) {
+                successors.push(versions[i]);
+            }
         }
     }
 }

Modified: incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/version/VersionManager.java
==============================================================================
--- incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/version/VersionManager.java	(original)
+++ incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/version/VersionManager.java	Tue Oct  5 01:57:45 2004
@@ -16,8 +16,11 @@
 package org.apache.jackrabbit.core.version;
 
 import org.apache.jackrabbit.core.*;
+import org.apache.jackrabbit.core.nodetype.NodeTypeManagerImpl;
 import org.apache.jackrabbit.core.nodetype.NodeTypeRegistry;
-import org.apache.jackrabbit.core.util.uuid.UUID;
+import org.apache.jackrabbit.core.state.ItemStateException;
+import org.apache.jackrabbit.core.state.PersistentItemStateManager;
+import org.apache.jackrabbit.core.state.PersistentNodeState;
 import org.apache.log4j.Logger;
 
 import javax.jcr.Node;
@@ -27,23 +30,101 @@
 import javax.jcr.version.VersionHistory;
 
 /**
- * This Class implements...
+ * This Class provides general versioning functions.
  */
 public class VersionManager {
 
     private static Logger log = Logger.getLogger(VersionManager.class);
 
-    // root path for version storage
-    public static final QName VERSION_HISTORY_ROOT_NAME =
-            new QName(NamespaceRegistryImpl.NS_JCR_URI, "versionStorage");
+    /**
+     * root path for version storage
+     */
+    public static final QName VERSION_HISTORY_ROOT_NAME = new QName(NamespaceRegistryImpl.NS_JCR_URI, "versionStorage");
+
+    /**
+     * name of the 'jcr:frozenUUID' property
+     */
+    public static final QName PROPNAME_FROZEN_UUID = new QName(NamespaceRegistryImpl.NS_JCR_URI, "frozenUUID");
+
+    /**
+     * name of the 'jcr:frozenPrimaryType' property
+     */
+    public static final QName PROPNAME_FROZEN_PRIMARY_TYPE = new QName(NamespaceRegistryImpl.NS_JCR_URI, "frozenPrimaryType");
+
+    /**
+     * name of the 'jcr:frozenMixinTypes' property
+     */
+    public static final QName PROPNAME_FROZEN_MIXIN_TYPES = new QName(NamespaceRegistryImpl.NS_JCR_URI, "frozenMixinTypes");
+
+    /**
+     * name of the 'jcr:versionLabels' node
+     */
+    public static final QName NODENAME_VERSION_LABELS = new QName(NamespaceRegistryImpl.NS_JCR_URI, "versionLabels");
+
+    /**
+     * name of the 'jcr:frozen' property
+     */
+    public static final QName NODENAME_FROZEN = new QName(NamespaceRegistryImpl.NS_JCR_URI, "frozen");
+
+    /**
+     * name of the 'jcr:predecessors' property
+     */
+    public static final QName PROPNAME_PREDECESSORS = new QName(NamespaceRegistryImpl.NS_JCR_URI, "predecessors");
+
+    /**
+     * name of the 'jcr:successors' property
+     */
+    public static final QName PROPNAME_SUCCESSORS = new QName(NamespaceRegistryImpl.NS_JCR_URI, "successors");
+
+    /**
+     * name of the 'jcr:name' property
+     */
+    public static final QName PROPNAME_NAME = new QName(NamespaceRegistryImpl.NS_JCR_URI, "name");
+
+    /**
+     * name of the 'jcr:version' property
+     */
+    public static final QName PROPNAME_VERSION = new QName(NamespaceRegistryImpl.NS_JCR_URI, "version");
 
-    // the system session for the versioning
-    private final SessionImpl session;
+    /**
+     * name of the 'jcr:isCheckedOut' property
+     */
+    public static final QName PROPNAME_IS_CHECKED_OUT = new QName(NamespaceRegistryImpl.NS_JCR_URI, "isCheckedOut");
+
+    /**
+     * name of the 'jcr:versionHistory' property
+     */
+    public static final QName PROPNAME_VERSION_HISTORY = new QName(NamespaceRegistryImpl.NS_JCR_URI, "versionHistory");
+
+    /**
+     * name of the 'jcr:baseVersion' property
+     */
+    public static final QName PROPNAME_BASE_VERSION = new QName(NamespaceRegistryImpl.NS_JCR_URI, "baseVersion");
+
+    /**
+     * name of the 'jcr:created' property
+     */
+    public static final QName PROPNAME_CREATED = new QName(NamespaceRegistryImpl.NS_JCR_URI, "created");
+
+    /**
+     * the name of the 'jcr:rootVersion' node
+     */
+    public static final QName NODENAME_ROOTVERSION = new QName(NamespaceRegistryImpl.NS_JCR_URI, "rootVersion");
 
     /**
      * the root node of the version histories
      */
-    private final NodeImpl historyRoot;
+    private final PersistentNode historyRoot;
+
+    /**
+     * the state manager for the version storage
+     */
+    private PersistentItemStateManager stateMgr;
+
+    /**
+     * the nodetype manager for the version storage
+     */
+    private NodeTypeManagerImpl ntMgr;
 
     /**
      * Creates a new VersionManager.
@@ -52,7 +133,8 @@
      * @throws RepositoryException
      */
     public VersionManager(SessionImpl session) throws RepositoryException {
-        this.session = session;
+        this.stateMgr = ((WorkspaceImpl) session.getWorkspace()).getPersistentStateManager();
+        this.ntMgr = session.getNodeTypeManager();
 
         // check for versionhistory root
         NodeImpl systemRoot = ((RepositoryImpl) session.getRepository()).getSystemRootNode(session);
@@ -61,14 +143,20 @@
             systemRoot.addNode(VERSION_HISTORY_ROOT_NAME, NodeTypeRegistry.NT_UNSTRUCTURED);
             systemRoot.save();
         }
-        historyRoot = systemRoot.getNode(VERSION_HISTORY_ROOT_NAME);
+
+        try {
+            PersistentNodeState nodeState = (PersistentNodeState) stateMgr.getItemState(new NodeId(systemRoot.getNode(VERSION_HISTORY_ROOT_NAME).internalGetUUID()));
+            historyRoot = new PersistentNode(stateMgr, ntMgr, nodeState);
+        } catch (ItemStateException e) {
+            throw new RepositoryException("Unable to initialize VersionManager: " + e.toString());
+        }
     }
 
     /**
-     * Creates a new Version History and returns the UUID of it.
+     * Creates a new Version History..
      *
      * @param node the node for which the version history is to be initialized
-     * @return the UUID of the new version history node
+     * @return the newly created version history.
      * @throws RepositoryException
      */
     public VersionHistory createVersionHistory(NodeImpl node)
@@ -77,22 +165,12 @@
         // check if history already exists
         QName historyNodeName = new QName(NamespaceRegistryImpl.NS_DEFAULT_URI, node.getUUID());
         if (historyRoot.hasNode(historyNodeName)) {
-            //throw new RepositoryException("Unable to initialize version history. Already exists");
-            historyRoot.getNode(historyNodeName).remove(".");
+            historyRoot.removeNode(historyNodeName);
         }
 
-        // create new history node
-        VersionHistoryImpl vh = VersionHistoryImpl.create(historyRoot.addNode(historyNodeName,
-                NodeTypeRegistry.NT_VERSION_HISTORY));
-
-        // and initialize the root version
-        ((VersionImpl) vh.getRootVersion()).initFrozenState(node);
-
-        // save new history
-        historyRoot.save();
-
-        // must aquire version history with the node's session
-        return VersionHistoryImpl.create((NodeImpl) node.getSession().getNodeByUUID(vh.getUUID()));
+        // create new history node in the persistent state
+        InternalVersionHistory history = InternalVersionHistory.create(historyRoot, historyNodeName);
+        return new VersionHistoryImpl(node.getSession(), history);
     }
 
     /**
@@ -103,7 +181,9 @@
      * @throws RepositoryException
      */
     public Version getBaseVersion(NodeImpl node) throws RepositoryException {
-        return (Version) node.getSession().getNodeByUUID(node.getProperty(VersionImpl.PROPNAME_BASE_VERSION).getString());
+        InternalVersionHistory history = getInternalVersionHistory(node);
+        InternalVersion version = history.getVersion(node.getProperty(PROPNAME_BASE_VERSION).getString());
+        return version == null ? null : new VersionImpl(node.getSession(), version);
     }
 
     /**
@@ -114,8 +194,29 @@
      * @return
      * @throws RepositoryException
      */
-    public VersionHistoryImpl getVersionHistory(NodeImpl node) throws RepositoryException {
-        return VersionHistoryImpl.create((NodeImpl) node.getSession().getNodeByUUID(node.getProperty(VersionImpl.PROPNAME_VERSION_HISTORY).getString()));
+    public VersionHistory getVersionHistory(NodeImpl node)
+            throws RepositoryException {
+        InternalVersionHistory history = getInternalVersionHistory(node);
+        return new VersionHistoryImpl(node.getSession(), history);
+    }
+
+    /**
+     * returns the internal version history for the given node
+     *
+     * @param node
+     * @return
+     * @throws RepositoryException
+     */
+    private InternalVersionHistory getInternalVersionHistory(NodeImpl node)
+            throws RepositoryException {
+        String histUUID = node.getProperty(PROPNAME_VERSION_HISTORY).getString();
+        try {
+            PersistentNodeState state = (PersistentNodeState) stateMgr.getItemState(new NodeId(histUUID));
+            PersistentNode hNode = new PersistentNode(stateMgr, ntMgr, state);
+            return new InternalVersionHistory(hNode);
+        } catch (ItemStateException e) {
+            throw new RepositoryException(e);
+        }
     }
 
     /**
@@ -130,12 +231,13 @@
         // assuming node is versionable and checkout (check in nodeimpl)
         // To create a new version of a versionable node N, the client calls N.checkin.
         // This causes the following series of events:
+        InternalVersionHistory history = getInternalVersionHistory(node);
 
         // 0. resolve the predecessors
-        Value[] values = node.getProperty(VersionImpl.PROPNAME_PREDECESSORS).getValues();
-        VersionImpl[] preds = new VersionImpl[values.length];
+        Value[] values = node.getProperty(PROPNAME_PREDECESSORS).getValues();
+        InternalVersion[] preds = new InternalVersion[values.length];
         for (int i = 0; i < values.length; i++) {
-            preds[i] = (VersionImpl) node.getSession().getNodeByUUID(values[i].getString());
+            preds[i] = history.getVersion(values[i].getString());
         }
 
         // 0.1 search a predecessor, suitable for generating the new name
@@ -144,7 +246,7 @@
         for (int i = 0; i < preds.length; i++) {
             // take the first pred. without a successor
             if (preds[i].getSuccessors().length == 0) {
-                versionName = preds[i].getName();
+                versionName = preds[i].getName().getLocalName(); //assuming no namespaces in version names
                 // need to count the dots
                 int pos = -1;
                 int numDots = 0;
@@ -160,63 +262,15 @@
             }
         }
         // if no empty found, generate new name
-        VersionHistoryImpl vh = getVersionHistory(node);
         if (versionName == null) {
-            versionName = preds[0].getName();
+            versionName = preds[0].getName().getLocalName();
             do {
                 versionName += ".1";
-            } while (vh.hasNode(versionName));
+            } while (history.hasVersion(new QName("", versionName)));
         }
 
-        try {
-            // 1. A new nt:version node V is created and added as a child node to VH,
-            //    the nt:versionHistory pointed to by Ns jcr:versionHistory property.
-            VersionImpl v = (VersionImpl) vh.addNode(versionName,
-                    NodeTypeRegistry.NT_VERSION.toJCRName(session.getNamespaceResolver()));
-
-            // 3. Ns base version is changed to V by altering Ns jcr:baseVersion
-            //    property to point to V.
-            //   (will be done in the nodeimpl)
-
-            // 4. Ns checked-in/checked-out status is changed to checked-in by
-            //    changing its jcr:isCheckedOut property to false.
-            //    (will be done in NodeImpl)
-
-            // 5. The state of N is recorded in V by storing information about
-            //    Ns child items (properties or child nodes) to V, as prescribed by
-            //    the OnParentVersion attribute of each of Ns child items.
-            //    See 7.2.8, below, for the details. The jcr:primaryType,
-            //    jcr:mixinTypes and jcr:uuid properties of N are copied over to V
-            //    but renamed to jcr:frozenPrimaryType, jcr:frozenMixinTypes and
-            //    jcr:frozenUUID to avoid conflict with V's own properties with these names.
-            v.createFrozenState(node);
-
-            // 2. Ns current jcr:predecessors property is copied to V, and Ns
-            //    jcr:predecessors property is then set to null.  A reference to V
-            //    is then added to the jcr:successors property of each of the versions
-            //    identified in Vs jcr:predecessors property.
-            InternalValue[] ivPreds = new InternalValue[preds.length];
-            for (int i = 0; i < preds.length; i++) {
-                ivPreds[i] = InternalValue.create(new UUID(preds[i].getUUID()));
-                preds[i].internalAddSuccessor(v);
-            }
-            v.internalSetProperty(VersionImpl.PROPNAME_PREDECESSORS, ivPreds);
-
-            // 6. V is given a name, sometimes based upon the name of Vs predecessor.
-            //    For example, an increment from 1.5 to 1.6.
-            // (is done before)
-            vh.save();
-            return v;
-        } catch (RepositoryException e) {
-            log.error("Aborting checkin. Error while creating version: " + e.toString());
-            vh.refresh(false);
-            throw e;
-        } catch (NoPrefixDeclaredException npde) {
-            String msg = "Aborting checkin. Error while creating version: " + npde.toString();
-            log.error(msg, npde);
-            vh.refresh(false);
-            throw new RepositoryException(msg, npde);
-        }
+        InternalVersion v = history.checkin(new QName("", versionName), node);
+        return new VersionImpl(node.getSession(), v);
     }
 
 }

Modified: incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/version/package.html
==============================================================================
--- incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/version/package.html	(original)
+++ incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/version/package.html	Tue Oct  5 01:57:45 2004
@@ -5,4 +5,10 @@
 A lot of the versioning functionality is handled in the NodeImpl or VersionImpl
 itself, since it operates on internal properties and node of that nodes. This
 also might move to another class, eg. the VersionManager later.<br>
+<p>
+Open issues:
+<ul>
+<li>consider multi-threading behaviour
+</ul>
+
 </body>

Mime
View raw message