jackrabbit-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From ang...@apache.org
Subject svn commit: r513279 [1/3] - in /jackrabbit/trunk/contrib/spi/jcr2spi: ./ src/main/java/org/apache/jackrabbit/jcr2spi/ src/main/java/org/apache/jackrabbit/jcr2spi/hierarchy/ src/main/java/org/apache/jackrabbit/jcr2spi/lock/ src/main/java/org/apache/jack...
Date Thu, 01 Mar 2007 11:17:33 GMT
Author: angela
Date: Thu Mar  1 03:17:22 2007
New Revision: 513279

URL: http://svn.apache.org/viewvc?view=rev&rev=513279
Log:
fixed: move, reorder (work in progress)
fixed: locktoken transfer
fixed: recursive transient removal of invalidated tree fails (restoreTests)
improve: simplify usage of workspace operations
fixed: store original itemId with transient operations in order to properly reflect the changelog upon batch creation
fixed: workspace import doesn't invalidate
fixed: session import uuid handling



Added:
    jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/hierarchy/ChildNodeAttic.java   (with props)
    jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/operation/WorkspaceImport.java
Modified:
    jackrabbit/trunk/contrib/spi/jcr2spi/TODO.txt
    jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/NodeImpl.java
    jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/WorkspaceImpl.java
    jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/WorkspaceManager.java
    jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/hierarchy/ChildNodeEntries.java
    jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/hierarchy/EntryFactory.java
    jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/hierarchy/HierarchyEntryImpl.java
    jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/hierarchy/HierarchyManager.java
    jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/hierarchy/HierarchyManagerImpl.java
    jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/hierarchy/NodeEntry.java
    jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/hierarchy/NodeEntryImpl.java
    jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/hierarchy/PropertyEntry.java
    jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/hierarchy/PropertyEntryImpl.java
    jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/lock/LockManagerImpl.java
    jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/operation/AbstractCopy.java
    jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/operation/AddLabel.java
    jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/operation/AddNode.java
    jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/operation/AddProperty.java
    jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/operation/Checkin.java
    jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/operation/Checkout.java
    jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/operation/Clone.java
    jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/operation/LockOperation.java
    jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/operation/LockRefresh.java
    jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/operation/LockRelease.java
    jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/operation/Merge.java
    jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/operation/Move.java
    jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/operation/OperationVisitor.java
    jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/operation/Remove.java
    jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/operation/RemoveLabel.java
    jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/operation/ReorderNodes.java
    jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/operation/ResolveMergeConflict.java
    jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/operation/Restore.java
    jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/operation/SetMixin.java
    jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/operation/SetPropertyValue.java
    jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/operation/Update.java
    jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/state/ChangeLog.java
    jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/state/ItemState.java
    jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/state/ItemStateValidator.java
    jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/state/NodeState.java
    jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/state/PropertyState.java
    jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/state/SessionItemStateManager.java
    jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/util/ReferenceChangeTracker.java
    jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/util/StateUtility.java
    jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/version/VersionManagerImpl.java
    jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/xml/Importer.java
    jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/xml/SessionImporter.java
    jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/xml/SysViewImportHandler.java
    jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/xml/WorkspaceContentHandler.java

Modified: jackrabbit/trunk/contrib/spi/jcr2spi/TODO.txt
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/contrib/spi/jcr2spi/TODO.txt?view=diff&rev=513279&r1=513278&r2=513279
==============================================================================
--- jackrabbit/trunk/contrib/spi/jcr2spi/TODO.txt (original)
+++ jackrabbit/trunk/contrib/spi/jcr2spi/TODO.txt Thu Mar  1 03:17:22 2007
@@ -9,23 +9,14 @@
         
 - TODO: test Observation
 
-- TODO: test Merge
-
-- BUG: NodeEntryImpl needs an attic for transiently moved and removed
-       child entries in order not to access those from the persistent
-       storage again.
-       In addition: having an attic allows to properly handle events
-       notifying external modifications for those moved/removed
-       NodeEntries, that are not identified by a uniqueID
+- TODO: test Merge, Workspace.restore, Workspace.copy, Workspace.clone
+        (missing clone impl with spi2dav)
        
 - BUG: Node.hasProperty currently returns false, if prop-entry has
        not been loaded yet -> should use same mechanism as Node.getProperty.
-       
-- BUG: Restore (testcase failed)
 
-- BUG: LockToken transfer with Session.addLockToken, Session.removeLockToken
-   
-- BUG: Workspace.importXML does not invalidate
+- BUG: Locks that have been accessed by another session are not informed
+       if the lock is released by the lock-holder.
 
 - IMPROVE: Replace ItemState-duality by copy on write behaviour
 

Modified: jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/NodeImpl.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/NodeImpl.java?view=diff&rev=513279&r1=513278&r2=513279
==============================================================================
--- jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/NodeImpl.java (original)
+++ jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/NodeImpl.java Thu Mar  1 03:17:22 2007
@@ -116,7 +116,7 @@
         }
     }
 
-    //-----------------------------------------------------< Item interface >---
+    //---------------------------------------------------------------< Item >---
     /**
      * @see Item#getName()
      */
@@ -155,7 +155,7 @@
         return true;
     }
 
-    //-----------------------------------------------------< Node interface >---
+    //---------------------------------------------------------------< Node >---
     /**
      * @see Node#addNode(String)
      */
@@ -730,7 +730,7 @@
      */
     private List getMixinTypes() {
         QName[] mixinValue = new QName[0];
-        if (getNodeEntry().hasPropertyEntry(QName.JCR_MIXINTYPES)) {
+        if (hasProperty(QName.JCR_MIXINTYPES)) {
             if (getNodeState().getStatus() == Status.EXISTING) {
                 mixinValue = getNodeState().getMixinTypeNames();
             } else {
@@ -1209,7 +1209,12 @@
         }
 
         // check effective node type
-        return getEffectiveNodeType().includesNodeType(qName);
+        try {
+            EffectiveNodeType effnt = session.getValidator().getEffectiveNodeType(getNodeState().getNodeTypeNames());
+            return effnt.includesNodeType(qName);
+        } catch (NodeTypeConflictException e) {
+            throw new RepositoryException(e);
+        }
     }
 
     //-----------------------------------------------------------< ItemImpl >---
@@ -1669,19 +1674,6 @@
             throw new RepositoryException(msg, e);
         }
         return targetEntry;
-    }
-
-    /**
-     * 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
-     */
-    private EffectiveNodeType getEffectiveNodeType() throws RepositoryException {
-        // build effective node type of mixins & primary type
-        ItemStateValidator validator = session.getValidator();
-        return validator.getEffectiveNodeType(getNodeState());
     }
 
     /**

Modified: jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/WorkspaceImpl.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/WorkspaceImpl.java?view=diff&rev=513279&r1=513278&r2=513279
==============================================================================
--- jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/WorkspaceImpl.java (original)
+++ jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/WorkspaceImpl.java Thu Mar  1 03:17:22 2007
@@ -29,6 +29,7 @@
 import org.apache.jackrabbit.jcr2spi.operation.Copy;
 import org.apache.jackrabbit.jcr2spi.operation.Clone;
 import org.apache.jackrabbit.jcr2spi.operation.Operation;
+import org.apache.jackrabbit.jcr2spi.operation.WorkspaceImport;
 import org.apache.jackrabbit.jcr2spi.security.AccessManager;
 import org.apache.jackrabbit.jcr2spi.lock.LockManager;
 import org.apache.jackrabbit.jcr2spi.lock.LockManagerImpl;
@@ -327,8 +328,7 @@
     /**
      * @see javax.jcr.Workspace#importXML(String, InputStream, int)
      */
-    public void importXML(String parentAbsPath, InputStream in,
-                          int uuidBehavior)
+    public void importXML(String parentAbsPath, InputStream in, int uuidBehavior)
         throws IOException, PathNotFoundException, ItemExistsException,
         ConstraintViolationException, InvalidSerializedDataException,
         LockException, RepositoryException {
@@ -345,7 +345,7 @@
             getValidator().checkIsWritable(parentState, options);
 
             // run the import
-            wspManager.importXml(parentState, in, uuidBehavior);
+            wspManager.execute(WorkspaceImport.create(parentState, in, uuidBehavior));
         } else {
             throw new PathNotFoundException("No node at path " + parentAbsPath);
         }

Modified: jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/WorkspaceManager.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/WorkspaceManager.java?view=diff&rev=513279&r1=513278&r2=513279
==============================================================================
--- jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/WorkspaceManager.java (original)
+++ jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/WorkspaceManager.java Thu Mar  1 03:17:22 2007
@@ -22,8 +22,6 @@
 import org.apache.jackrabbit.jcr2spi.name.NamespaceStorage;
 import org.apache.jackrabbit.jcr2spi.name.NamespaceRegistryImpl;
 import org.apache.jackrabbit.jcr2spi.state.ItemState;
-import org.apache.jackrabbit.jcr2spi.state.ItemStateException;
-import org.apache.jackrabbit.jcr2spi.state.PropertyState;
 import org.apache.jackrabbit.jcr2spi.state.ChangeLog;
 import org.apache.jackrabbit.jcr2spi.state.UpdatableItemStateManager;
 import org.apache.jackrabbit.jcr2spi.state.ItemStateFactory;
@@ -31,6 +29,7 @@
 import org.apache.jackrabbit.jcr2spi.state.NodeState;
 import org.apache.jackrabbit.jcr2spi.state.TransientItemStateFactory;
 import org.apache.jackrabbit.jcr2spi.state.TransientISFactory;
+import org.apache.jackrabbit.jcr2spi.state.Status;
 import org.apache.jackrabbit.jcr2spi.operation.OperationVisitor;
 import org.apache.jackrabbit.jcr2spi.operation.AddNode;
 import org.apache.jackrabbit.jcr2spi.operation.AddProperty;
@@ -54,6 +53,7 @@
 import org.apache.jackrabbit.jcr2spi.operation.AddLabel;
 import org.apache.jackrabbit.jcr2spi.operation.RemoveLabel;
 import org.apache.jackrabbit.jcr2spi.operation.RemoveVersion;
+import org.apache.jackrabbit.jcr2spi.operation.WorkspaceImport;
 import org.apache.jackrabbit.jcr2spi.security.AccessManager;
 import org.apache.jackrabbit.jcr2spi.observation.InternalEventListener;
 import org.apache.jackrabbit.jcr2spi.config.CacheBehaviour;
@@ -109,7 +109,6 @@
 import java.util.Set;
 import java.util.HashSet;
 import java.util.Map;
-import java.io.InputStream;
 
 import EDU.oswego.cs.dl.util.concurrent.Sync;
 import EDU.oswego.cs.dl.util.concurrent.Mutex;
@@ -209,8 +208,6 @@
      * determine, which lock this token belongs to. The latter would be
      * necessary in order to build the 'Lock' object properly.
      *
-     * TODO: check if throwing an exception would be more appropriate
-     *
      * @param lt
      * @throws LockException
      * @throws RepositoryException
@@ -236,7 +233,16 @@
      * @throws RepositoryException
      */
     public void removeLockToken(String lt) throws LockException, RepositoryException {
-        sessionInfo.removeLockToken(lt);
+        String[] tokems = sessionInfo.getLockTokens();
+        for (int i = 0; i < tokems.length; i++) {
+            if (tokems[i].equals(lt)) {
+                sessionInfo.removeLockToken(lt);
+                return;
+            }
+        }
+        // sessionInfo doesn't contain the given lock token and is therefore
+        // not the lock holder
+        throw new RepositoryException("Unable to remove locktoken '" + lt + "' from Session.");
     }
 
     /**
@@ -481,6 +487,10 @@
      * @see AccessManager#isGranted(NodeState, Path, String[])
      */
     public boolean isGranted(NodeState parentState, Path relPath, String[] actions) throws ItemNotFoundException, RepositoryException {
+        // TODO: correct?
+        if (parentState.getStatus() == Status.NEW) {
+            return true;
+        }
         // TODO: check again.
         // build itemId from the given state and the relative path without
         // making an attempt to retrieve the proper id of the item possibly
@@ -495,37 +505,34 @@
      * @see AccessManager#isGranted(ItemState, String[])
      */
     public boolean isGranted(ItemState itemState, String[] actions) throws ItemNotFoundException, RepositoryException {
-        ItemState wspState = itemState.getWorkspaceState();
         // a 'new' state can always be read, written and removed
         // TODO: correct?
-        if (wspState == null) {
+        if (itemState.getStatus() == Status.NEW) {
             return true;
         }
-        return service.isGranted(sessionInfo, wspState.getId(), actions);
+        return service.isGranted(sessionInfo, itemState.getId(), actions);
     }
 
     /**
      * @see AccessManager#canRead(ItemState)
      */
     public boolean canRead(ItemState itemState) throws ItemNotFoundException, RepositoryException {
-        ItemState wspState = itemState.getWorkspaceState();
         // a 'new' state can always be read
-        if (wspState == null) {
+        if (itemState.getStatus() == Status.NEW) {
             return true;
         }
-        return service.isGranted(sessionInfo, wspState.getId(), AccessManager.READ);
+        return service.isGranted(sessionInfo, itemState.getId(), AccessManager.READ);
     }
 
     /**
      * @see AccessManager#canRemove(ItemState)
      */
     public boolean canRemove(ItemState itemState) throws ItemNotFoundException, RepositoryException {
-        ItemState wspState = itemState.getWorkspaceState();
         // a 'new' state can always be removed again
-        if (wspState == null) {
+        if (itemState.getStatus() == Status.NEW) {
             return true;
         }
-        return service.isGranted(sessionInfo, wspState.getId(), AccessManager.REMOVE);
+        return service.isGranted(sessionInfo, itemState.getId(), AccessManager.REMOVE);
     }
 
     /**
@@ -541,12 +548,6 @@
         return false;
     }
 
-    //---------------------------------------------------------< XML import >---
-    public void importXml(NodeState parentState, InputStream xmlStream, int uuidBehaviour) throws RepositoryException, LockException, ConstraintViolationException, AccessDeniedException, UnsupportedRepositoryOperationException, ItemExistsException, VersionException {
-        NodeId parentId = parentState.getNodeId();
-        service.importXml(sessionInfo, parentId, xmlStream, uuidBehaviour);
-    }
-
     //---------------------------------------------------< NamespaceStorage >---
 
     public Map getRegisteredNamespaces() throws RepositoryException {
@@ -698,7 +699,7 @@
          * @see OperationVisitor#visit(AddNode)
          */
         public void visit(AddNode operation) throws RepositoryException {
-            NodeId parentId = operation.getParentState().getNodeId();
+            NodeId parentId = operation.getParentId();
             batch.addNode(parentId, operation.getNodeName(), operation.getNodeTypeName(), operation.getUuid());
         }
 
@@ -707,7 +708,7 @@
          * @see OperationVisitor#visit(AddProperty)
          */
         public void visit(AddProperty operation) throws RepositoryException {
-            NodeId parentId = operation.getParentState().getNodeId();
+            NodeId parentId = operation.getParentId();
             QName propertyName = operation.getPropertyName();
             if (operation.isMultiValued()) {
                 batch.addProperty(parentId, propertyName, operation.getValues());
@@ -722,8 +723,8 @@
          * @see OperationVisitor#visit(Clone)
          */
         public void visit(Clone operation) throws NoSuchWorkspaceException, LockException, ConstraintViolationException, AccessDeniedException, ItemExistsException, UnsupportedRepositoryOperationException, VersionException, RepositoryException {
-            NodeId nId = operation.getNodeState().getNodeId();
-            NodeId destParentId = operation.getDestinationParentState().getNodeId();
+            NodeId nId = operation.getNodeId();
+            NodeId destParentId = operation.getDestinationParentId();
             service.clone(sessionInfo, operation.getWorkspaceName(), nId, destParentId, operation.getDestinationName(), operation.isRemoveExisting());
         }
 
@@ -732,8 +733,8 @@
          * @see OperationVisitor#visit(Copy)
          */
         public void visit(Copy operation) throws NoSuchWorkspaceException, LockException, ConstraintViolationException, AccessDeniedException, ItemExistsException, UnsupportedRepositoryOperationException, VersionException, RepositoryException {
-            NodeId nId = operation.getNodeState().getNodeId();
-            NodeId destParentId = operation.getDestinationParentState().getNodeId();
+            NodeId nId = operation.getNodeId();
+            NodeId destParentId = operation.getDestinationParentId();
             service.copy(sessionInfo, operation.getWorkspaceName(), nId, destParentId, operation.getDestinationName());
         }
 
@@ -743,7 +744,8 @@
          */
         public void visit(Move operation) throws LockException, ConstraintViolationException, AccessDeniedException, ItemExistsException, UnsupportedRepositoryOperationException, VersionException, RepositoryException {
             NodeId moveId = operation.getSourceId();
-            NodeId destParentId = operation.getDestinationParentState().getNodeId();
+            NodeId destParentId = operation.getDestinationParentId();
+
             if (batch == null) {
                 service.move(sessionInfo, moveId, destParentId, operation.getDestinationName());
             } else {
@@ -756,7 +758,7 @@
          * @see OperationVisitor#visit(Update)
          */
         public void visit(Update operation) throws NoSuchWorkspaceException, AccessDeniedException, LockException, InvalidItemStateException, RepositoryException {
-            NodeId nId = operation.getNodeState().getNodeId();
+            NodeId nId = operation.getNodeId();
             service.update(sessionInfo, nId, operation.getSourceWorkspaceName());
         }
 
@@ -765,8 +767,7 @@
          * @see OperationVisitor#visit(Remove)
          */
         public void visit(Remove operation) throws RepositoryException {
-            ItemId id = operation.getRemoveState().getId();
-            batch.remove(id);
+            batch.remove(operation.getRemoveId());
         }
 
         /**
@@ -774,7 +775,7 @@
          * @see OperationVisitor#visit(SetMixin)
          */
         public void visit(SetMixin operation) throws RepositoryException {
-            batch.setMixins(operation.getNodeState().getNodeId(), operation.getMixinNames());
+            batch.setMixins(operation.getNodeId(), operation.getMixinNames());
         }
 
         /**
@@ -782,9 +783,8 @@
          * @see OperationVisitor#visit(SetPropertyValue)
          */
         public void visit(SetPropertyValue operation) throws RepositoryException {
-            PropertyState pState = operation.getPropertyState();
-            PropertyId id = pState.getPropertyId();
-            if (pState.isMultiValued()) {
+            PropertyId id = operation.getPropertyId();
+            if (operation.isMultiValued()) {
                 batch.setValue(id, operation.getValues());
             } else {
                 batch.setValue(id, operation.getValues()[0]);
@@ -796,12 +796,9 @@
          * @see OperationVisitor#visit(ReorderNodes)
          */
         public void visit(ReorderNodes operation) throws RepositoryException {
-            NodeId parentId = operation.getParentState().getNodeId();
-            NodeId insertId = operation.getInsertNode().getNodeId();
-            NodeId beforeId = null;
-            if (operation.getBeforeNode() != null) {
-                beforeId = operation.getBeforeNode().getNodeId() ;
-            }
+            NodeId parentId = operation.getParentId();
+            NodeId insertId = operation.getInsertId();
+            NodeId beforeId = operation.getBeforeId();
             batch.reorderNodes(parentId, insertId, beforeId);
         }
 
@@ -810,7 +807,7 @@
          * @see OperationVisitor#visit(Checkout)
          */
         public void visit(Checkout operation) throws UnsupportedRepositoryOperationException, LockException, RepositoryException {
-            service.checkout(sessionInfo, operation.getNodeState().getNodeId());
+            service.checkout(sessionInfo, operation.getNodeId());
         }
 
         /**
@@ -818,7 +815,7 @@
          * @see OperationVisitor#visit(Checkin)
          */
         public void visit(Checkin operation) throws UnsupportedRepositoryOperationException, LockException, InvalidItemStateException, RepositoryException {
-            service.checkin(sessionInfo, operation.getNodeState().getNodeId());
+            service.checkin(sessionInfo, operation.getNodeId());
         }
 
         /**
@@ -826,32 +823,19 @@
          * @see OperationVisitor#visit(Restore)
          */
         public void visit(Restore operation) throws VersionException, PathNotFoundException, ItemExistsException, UnsupportedRepositoryOperationException, LockException, InvalidItemStateException, RepositoryException {
-            NodeState nState = operation.getNodeState();
-            NodeState[] versionStates = operation.getVersionStates();
-            if (versionStates == null || versionStates.length == 0) {
-                throw new IllegalArgumentException("Restore must specify at least a singe version.");
-            }
-
-            NodeId[] vIds = new NodeId[versionStates.length];
-            for (int i = 0; i < vIds.length; i++) {
-                vIds[i] = versionStates[i].getNodeId();
-            }
-
-            if (nState == null) {
-                service.restore(sessionInfo, vIds, operation.removeExisting());
+            NodeId nId = operation.getNodeId();
+            if (nId == null) {
+                service.restore(sessionInfo, operation.getVersionIds(), operation.removeExisting());
             } else {
-                if (vIds.length > 1) {
-                    throw new IllegalArgumentException("Restore from a single node must specify but one single Version.");
-                }
-
                 NodeId targetId;
                 Path relPath = operation.getRelativePath();
                 if (relPath != null) {
-                    targetId = getIdFactory().createNodeId(nState.getNodeId(), relPath);
+                    targetId = getIdFactory().createNodeId(nId, relPath);
                 } else {
-                    targetId = nState.getNodeId();
+                    targetId = nId;
                 }
-                service.restore(sessionInfo, targetId, vIds[0], operation.removeExisting());
+                NodeId versionId = operation.getVersionIds()[0];
+                service.restore(sessionInfo, targetId, versionId, operation.removeExisting());
             }
         }
 
@@ -860,7 +844,7 @@
          * @see OperationVisitor#visit(Merge)
          */
         public void visit(Merge operation) throws NoSuchWorkspaceException, AccessDeniedException, MergeException, LockException, InvalidItemStateException, RepositoryException {
-            NodeId nId = operation.getNodeState().getNodeId();
+            NodeId nId = operation.getNodeId();
             IdIterator failed = service.merge(sessionInfo, nId, operation.getSourceWorkspaceName(), operation.bestEffort());
             operation.setFailedIds(failed);
         }
@@ -870,45 +854,10 @@
          * @see OperationVisitor#visit(ResolveMergeConflict)
          */
         public void visit(ResolveMergeConflict operation) throws VersionException, InvalidItemStateException, UnsupportedRepositoryOperationException, RepositoryException {
-            try {
-                NodeState nState = operation.getNodeState();
-                NodeId nId = nState.getNodeId();
-                NodeId vId = operation.getVersionState().getNodeId();
-
-                PropertyState mergeFailedState = nState.getPropertyState(QName.JCR_MERGEFAILED);
-                QValue[] vs = mergeFailedState.getValues();
-
-                NodeId[] mergeFailedIds = new NodeId[vs.length - 1];
-                for (int i = 0, j = 0; i < vs.length; i++) {
-                    NodeId id = getIdFactory().createNodeId(vs[i].getString());
-                    if (!id.equals(vId)) {
-                        mergeFailedIds[j] = id;
-                        j++;
-                    }
-                    // else: the version id is being solved by this call and not
-                    // part of 'jcr:mergefailed' any more
-                }
-
-                PropertyState predecessorState = nState.getPropertyState(QName.JCR_PREDECESSORS);
-                vs = predecessorState.getValues();
-
-                boolean resolveDone = operation.resolveDone();
-                int noOfPredecessors = (resolveDone) ? vs.length + 1 : vs.length;
-                NodeId[] predecessorIds = new NodeId[noOfPredecessors];
-
-                int i = 0;
-                while (i < vs.length) {
-                    predecessorIds[i] = getIdFactory().createNodeId(vs[i].getString());
-                    i++;
-                }
-                if (resolveDone) {
-                    predecessorIds[i] = vId;
-                }
-                service.resolveMergeConflict(sessionInfo, nId, mergeFailedIds, predecessorIds);
-            } catch (ItemStateException e) {
-                // should not occur.
-                throw new RepositoryException(e);
-            }
+            NodeId nId = operation.getNodeId();
+            NodeId[] mergedFailedIds = operation.getMergeFailedIds();
+            NodeId[] predecessorIds = operation.getPredecessorIds();
+            service.resolveMergeConflict(sessionInfo, nId, mergedFailedIds, predecessorIds);
         }
 
         /**
@@ -916,8 +865,7 @@
          * @see OperationVisitor#visit(LockOperation)
          */
         public void visit(LockOperation operation) throws AccessDeniedException, InvalidItemStateException, UnsupportedRepositoryOperationException, LockException, RepositoryException {
-            NodeId nId = operation.getNodeState().getNodeId();
-            LockInfo lInfo = service.lock(sessionInfo, nId, operation.isDeep(), operation.isSessionScoped());
+            LockInfo lInfo = service.lock(sessionInfo, operation.getNodeId(), operation.isDeep(), operation.isSessionScoped());
             operation.setLockInfo(lInfo);
         }
 
@@ -926,8 +874,7 @@
          * @see OperationVisitor#visit(LockRefresh)
          */
         public void visit(LockRefresh operation) throws AccessDeniedException, InvalidItemStateException, UnsupportedRepositoryOperationException, LockException, RepositoryException {
-            NodeId nId = operation.getNodeState().getNodeId();
-            service.refreshLock(sessionInfo, nId);
+            service.refreshLock(sessionInfo, operation.getNodeId());
         }
 
         /**
@@ -935,8 +882,7 @@
          * @see OperationVisitor#visit(LockRelease)
          */
         public void visit(LockRelease operation) throws AccessDeniedException, InvalidItemStateException, UnsupportedRepositoryOperationException, LockException, RepositoryException {
-            NodeId nId = operation.getNodeState().getNodeId();
-            service.unlock(sessionInfo, nId);
+            service.unlock(sessionInfo, operation.getNodeId());
         }
 
         /**
@@ -944,8 +890,8 @@
          * @see OperationVisitor#visit(AddLabel)
          */
         public void visit(AddLabel operation) throws VersionException, RepositoryException {
-            NodeId vhId = operation.getVersionHistoryState().getNodeId();
-            NodeId vId = operation.getVersionState().getNodeId();
+            NodeId vhId = operation.getVersionHistoryId();
+            NodeId vId = operation.getVersionId();
             service.addVersionLabel(sessionInfo, vhId, vId, operation.getLabel(), operation.moveLabel());
         }
 
@@ -954,8 +900,8 @@
          * @see OperationVisitor#visit(RemoveLabel)
          */
         public void visit(RemoveLabel operation) throws VersionException, RepositoryException {
-            NodeId vhId = operation.getVersionHistoryState().getNodeId();
-            NodeId vId = operation.getVersionState().getNodeId();
+            NodeId vhId = operation.getVersionHistoryId();
+            NodeId vId = operation.getVersionId();
             service.removeVersionLabel(sessionInfo, vhId, vId, operation.getLabel());
         }
 
@@ -964,9 +910,17 @@
          * @see OperationVisitor#visit(RemoveVersion)
          */
         public void visit(RemoveVersion operation) throws VersionException, AccessDeniedException, ReferentialIntegrityException, RepositoryException {
-            NodeState versionState = (NodeState) operation.getRemoveState();
+            NodeId versionId = (NodeId) operation.getRemoveId();
             NodeState vhState = operation.getParentState();
-            service.removeVersion(sessionInfo, vhState.getNodeId(), versionState.getNodeId());
+            service.removeVersion(sessionInfo, vhState.getNodeId(), versionId);
+        }
+
+        /**
+         * @inheritDoc
+         * @see OperationVisitor#visit(WorkspaceImport)
+         */
+        public void visit(WorkspaceImport operation) throws RepositoryException {
+            service.importXml(sessionInfo, operation.getNodeId(), operation.getXmlStream(), operation.getUuidBehaviour());
         }
     }
 

Added: jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/hierarchy/ChildNodeAttic.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/hierarchy/ChildNodeAttic.java?view=auto&rev=513279
==============================================================================
--- jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/hierarchy/ChildNodeAttic.java (added)
+++ jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/hierarchy/ChildNodeAttic.java Thu Mar  1 03:17:22 2007
@@ -0,0 +1,113 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.jackrabbit.jcr2spi.hierarchy;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.apache.jackrabbit.name.QName;
+
+import java.util.Set;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.ArrayList;
+
+/**
+ * <code>ChildNodeAttic</code>...
+ */
+class ChildNodeAttic {
+
+    private static Logger log = LoggerFactory.getLogger(ChildNodeAttic.class);
+
+    private Set attic = new HashSet();
+
+    ChildNodeAttic() {
+    }
+
+    boolean contains(QName name, int index) {
+        for (Iterator it = attic.iterator(); it.hasNext();) {
+            NodeEntryImpl ne = (NodeEntryImpl) it.next();
+            if (ne.matches(name, index)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    List get(QName name) {
+        List l = new ArrayList();
+        for (Iterator it = attic.iterator(); it.hasNext();) {
+            NodeEntryImpl ne = (NodeEntryImpl) it.next();
+            if (ne.matches(name)) {
+                l.add(ne);
+            }
+        }
+        return l;
+    }
+
+    /**
+     *
+     * @param name The original name of the NodeEntry before it has been moved.
+     * @param index The original index of the NodeEntry before it has been moved.
+     * @return
+     */
+    NodeEntry get(QName name, int index) {
+        for (Iterator it = attic.iterator(); it.hasNext();) {
+            NodeEntryImpl ne = (NodeEntryImpl) it.next();
+            if (ne.matches(name, index)) {
+                return ne;
+            }
+        }
+        // not found
+        return null;
+    }
+
+    /**
+     *
+     * @param uniqueId
+     * @return
+     */
+    NodeEntry get(String uniqueId) {
+        if (uniqueId == null) {
+            throw new IllegalArgumentException();
+        }
+        for (Iterator it = attic.iterator(); it.hasNext();) {
+            NodeEntryImpl ne = (NodeEntryImpl) it.next();
+            if (uniqueId.equals(ne.getUniqueID())) {
+                return ne;
+            }
+        }
+        // not found
+        return null;
+    }
+
+    void add(NodeEntryImpl movedEntry) {
+        attic.add(movedEntry);
+    }
+
+    void remove(NodeEntryImpl movedEntry) {
+        if (attic.contains(movedEntry)) {
+            attic.remove(movedEntry);
+        }
+    }
+
+    void clear() {
+        if (attic != null) {
+            attic.clear();
+        }
+    }
+}
\ No newline at end of file

Propchange: jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/hierarchy/ChildNodeAttic.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/hierarchy/ChildNodeAttic.java
------------------------------------------------------------------------------
    svn:keywords = author date id revision url

Modified: jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/hierarchy/ChildNodeEntries.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/hierarchy/ChildNodeEntries.java?view=diff&rev=513279&r1=513278&r2=513279
==============================================================================
--- jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/hierarchy/ChildNodeEntries.java (original)
+++ jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/hierarchy/ChildNodeEntries.java Thu Mar  1 03:17:22 2007
@@ -142,7 +142,7 @@
         // no matching entry found
         return false;
     }
-    
+
     /**
      * Returns a <code>List</code> of <code>NodeEntry</code>s for the
      * given <code>nodeName</code>. This method does <b>not</b> filter out
@@ -216,6 +216,10 @@
         if (obj instanceof List) {
             // map entry is a list of siblings
             List siblings = (List) obj;
+            // shortcut if index can never match
+            if (index > siblings.size()) {
+                return null;
+            }
             // filter out removed states
             for (Iterator it = siblings.iterator(); it.hasNext(); ) {
                 NodeEntry cne = ((LinkedEntries.LinkNode) it.next()).getNodeEntry();
@@ -269,49 +273,46 @@
     }
 
     /**
-     * Adds a <code>NodeEntry</code> to the end of the list.
+     * Adds a <code>NodeEntry</code> to the end of the list. Same as
+     * {@link #add(NodeEntry, int)}, where the index is {@link Path#INDEX_UNDEFINED}.
      *
      * @param cne the <code>NodeEntry</code> to add.
      */
      void add(NodeEntry cne) {
-        QName nodeName = cne.getQName();
-        List siblings = null;
-        Object obj = nameMap.get(nodeName);
-        if (obj != null) {
-            if (obj instanceof List) {
-                // map entry is a list of siblings
-                siblings = (ArrayList) obj;
-            } else {
-                // map entry is a single child node entry,
-                // convert to siblings list
-                siblings = new ArrayList();
-                siblings.add(obj);
-                nameMap.put(nodeName, siblings);
-            }
-        }
-
-        LinkedEntries.LinkNode ln = entries.add(cne);
-
-        if (siblings != null) {
-            siblings.add(ln);
-        } else {
-            nameMap.put(nodeName, ln);
-        }
+        add(cne, Path.INDEX_UNDEFINED);
     }
 
     /**
-     * Adds a <code>NodeEntry</code>. If an entry with the given index
-     * already exists, the the new sibling is inserted before.
+     * Adds a <code>NodeEntry</code>.<br>
+     * Note the following special cases:
+     * <ol>
+     * <li>If an entry with the given index already exists, the the new sibling
+     * is inserted before.</li>
+     * <li>If the given index is bigger that the last entry in the siblings list,
+     * intermediate entries will be created.</li>
+     * </ol>
      *
      * @param cne the <code>NodeEntry</code> to add.
      */
     void add(NodeEntry cne, int index) {
         QName nodeName = cne.getQName();
 
-        // retrieve ev. sibling node with same index
-        // if index is 'undefined' behave just as '#add(NodeEntry).
+        // retrieve ev. sibling node with same index if index is 'undefined'
+        // the existing entry is always null and no reordering occur.
         LinkedEntries.LinkNode existing = (index < Path.INDEX_DEFAULT) ? null : getLinkNode(nodeName, index);
 
+        // in case index greater than default -> make sure all intermediate
+        // entries exist.
+        if (index > Path.INDEX_DEFAULT) {
+            int previousIndex = index - 1;
+            LinkedEntries.LinkNode previous = getLinkNode(nodeName, previousIndex);
+            if (previous == null) {
+                // add missing entry (or entries)
+                parent.addNodeEntry(nodeName, null, previousIndex);
+
+            } // else: all intermediate entries exist
+        } // else: undefined or default index are not affected
+
         // add new entry (same as #add(NodeEntry)
         List siblings = null;
         Object obj = nameMap.get(nodeName);
@@ -335,8 +336,8 @@
             nameMap.put(nodeName, ln);
         }
 
-        // if new entry must be inserted instead of appended at the end
-        // reorder entries now
+        // reorder the child entries if, the new entry must be inserted rather
+        // than appended at the end of the list.
         if (existing != null) {
             reorder(obj, ln, existing);
         }
@@ -430,25 +431,27 @@
      * @param insertNode the NodeEntry to move.
      * @param beforeNode the NodeEntry where <code>insertNode</code> is
      * reordered to.
+     * @return the NodeEntry that followed the 'insertNode' before the reordering.
      * @throws NoSuchElementException if <code>insertNode</code> or
      * <code>beforeNode</code> does not have a <code>NodeEntry</code>
      * in this <code>ChildNodeEntries</code>.
      */
-    boolean reorder(NodeEntry insertNode, NodeEntry beforeNode) {
+    NodeEntry reorder(NodeEntry insertNode, NodeEntry beforeNode) {
         Object insertObj = nameMap.get(insertNode.getQName());
         // the link node to move
         LinkedEntries.LinkNode insertLN = getLinkNode(insertNode);
         if (insertLN == null) {
-            return false;
+            throw new NoSuchElementException();
         }
         // the link node where insertLN is ordered before
         LinkedEntries.LinkNode beforeLN = (beforeNode != null) ? getLinkNode(beforeNode) : null;
         if (beforeNode != null && beforeLN == null) {
-            return false;
+            throw new NoSuchElementException();
         }
 
+        NodeEntry previousBefore = insertLN.getNextLinkNode().getNodeEntry();
         reorder(insertObj, insertLN, beforeLN);
-        return true;
+        return previousBefore;
     }
 
     /**
@@ -483,9 +486,7 @@
                     }
                 }
             }
-        } else {
-            // no same name siblings -> nothing to do.
-        }
+        } // else: no same name siblings -> no special handling required
 
         // reorder in linked list
         entries.reorderNode(insertLN, beforeLN);

Modified: jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/hierarchy/EntryFactory.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/hierarchy/EntryFactory.java?view=diff&rev=513279&r1=513278&r2=513279
==============================================================================
--- jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/hierarchy/EntryFactory.java (original)
+++ jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/hierarchy/EntryFactory.java Thu Mar  1 03:17:22 2007
@@ -68,17 +68,10 @@
         return isf;
     }
 
+
     public void notifyIdChange(NodeEntry entry, String previousUniqueID) {
         listener.uniqueIdChanged(entry, previousUniqueID);
     }
-
-    public void notifyEntryMoved(NodeEntry old, NodeEntry newEntry) {
-        // TODO: check if correct
-        if (old.getUniqueID() != null) {
-            listener.uniqueIdChanged(newEntry, old.getUniqueID());
-        }
-    }
-
 
     //--------------------------------------------------------------------------
     public interface NodeEntryListener {

Modified: jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/hierarchy/HierarchyEntryImpl.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/hierarchy/HierarchyEntryImpl.java?view=diff&rev=513279&r1=513278&r2=513279
==============================================================================
--- jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/hierarchy/HierarchyEntryImpl.java (original)
+++ jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/hierarchy/HierarchyEntryImpl.java Thu Mar  1 03:17:22 2007
@@ -350,20 +350,32 @@
         if (state == null) {
             // nothing to do -> correct status must be set upon resolution.
             return;
-        } else {
-            state.checkIsSessionState();
-            if (!state.isValid()) {
-                throw new ItemStateException("Cannot remove an invalid ItemState");
+        }
+
+        state.checkIsSessionState();
+        // if during recursive removal an invalidated entry is found, reload
+        // it in order to determine the current status.
+        if (state.getStatus() == Status.INVALIDATED) {
+            reload(false, false);
+            // check if upon reload the item has been removed -> nothing to do
+            if (Status.isTerminal(state.getStatus())) {
+                return;
             }
-            int oldStatus = state.getStatus();
-            if (oldStatus == Status.NEW) {
+        }
+
+        switch (state.getStatus()) {
+            case Status.NEW:
                 remove();
-            } else {
+                break;
+            case Status.EXISTING:
+            case Status.EXISTING_MODIFIED:
                 state.setStatus(Status.EXISTING_REMOVED);
                 // NOTE: parent does not need to be informed. an transiently
                 // removed propertyEntry is automatically moved to the 'attic'
                 // if a conflict with a new entry occurs.
-            }
+                break;
+            default:
+                throw new ItemStateException("Cannot transiently remove an ItemState with status " + Status.getName(state.getStatus()));
         }
     }
 

Modified: jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/hierarchy/HierarchyManager.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/hierarchy/HierarchyManager.java?view=diff&rev=513279&r1=513278&r2=513279
==============================================================================
--- jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/hierarchy/HierarchyManager.java (original)
+++ jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/hierarchy/HierarchyManager.java Thu Mar  1 03:17:22 2007
@@ -41,15 +41,18 @@
     public NodeEntry getRootEntry();
 
     /**
-     * If the Hierarchy already lists the entry with the given itemId it is
+     * Lookup of <code>HierarchyEntry</code> workspace Id, that may be different
+     * if a entry (or any of its ancestors) has been transiently moved or
+     * reordered.<p/>
+     * If the Hierarchy already lists the entry with the given workspaceItemId it is
      * returned otherwise <code>null</code>. See {@link #getHierarchyEntry(ItemId)}
      * for a method that resolves the ItemId including lookup in the persistence
      * layer if the entry has not been loaded yet.
      *
-     * @param itemId
+     * @param workspaceItemId
      * @return
      */
-    public HierarchyEntry lookup(ItemId itemId);
+    public HierarchyEntry lookup(ItemId workspaceItemId);
 
     /**
      * Resolves a itemId into a <code>HierarchyEntry</code>.

Modified: jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/hierarchy/HierarchyManagerImpl.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/hierarchy/HierarchyManagerImpl.java?view=diff&rev=513279&r1=513278&r2=513279
==============================================================================
--- jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/hierarchy/HierarchyManagerImpl.java (original)
+++ jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/hierarchy/HierarchyManagerImpl.java Thu Mar  1 03:17:22 2007
@@ -64,16 +64,17 @@
     /**
      * @see HierarchyManager#lookup(ItemId)
      */
-    public HierarchyEntry lookup(ItemId itemId) {
-        String uniqueID = itemId.getUniqueID();
+    public HierarchyEntry lookup(ItemId workspaceItemId) {
+        String uniqueID = workspaceItemId.getUniqueID();
         if (uniqueID == null) {
-            return PathResolver.lookup(rootEntry, itemId.getPath());
+            return rootEntry.lookupDeepEntry(workspaceItemId.getPath());
         } else {
             NodeEntry nEntry = uniqueIdResolver.lookup(idFactory.createNodeId(uniqueID));
-            if (itemId.getPath() == null) {
+            Path path = workspaceItemId.getPath();
+            if (path == null) {
                 return nEntry;
             } else {
-                return PathResolver.lookup(nEntry, itemId.getPath());
+                return nEntry.lookupDeepEntry(path);
             }
         }
     }

Modified: jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/hierarchy/NodeEntry.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/hierarchy/NodeEntry.java?view=diff&rev=513279&r1=513278&r2=513279
==============================================================================
--- jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/hierarchy/NodeEntry.java (original)
+++ jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/hierarchy/NodeEntry.java Thu Mar  1 03:17:22 2007
@@ -45,6 +45,17 @@
     public NodeId getId();
 
     /**
+     * Returns the ID that must be used for resolving this entry OR loading its
+     * children entries from the persistent layer. This is the same as
+     * <code>getId()</code> unless this entry or any of its ancestors has been
+     * transiently moved.
+     *
+     * @return
+     * @see #getId()
+     */
+    public NodeId getWorkspaceId();
+
+    /**
      * @return the unique ID of the node state which is referenced by this
      * child node entry or <code>null</code> if the node state cannot be
      * identified with a unique ID.
@@ -86,6 +97,17 @@
     public HierarchyEntry getDeepEntry(Path path) throws PathNotFoundException, RepositoryException;
 
     /**
+     * Traverse the tree below this entry and return the child entry matching
+     * the given 'workspacePath', i.e. transient modifications and new entries
+     * are ignored.<p/>
+     * If no matching entry can be found, <code>null</code> is return.
+     *
+     * @param workspacePath
+     * @return matching entry or <code>null</code>.
+     */
+    public HierarchyEntry lookupDeepEntry(Path workspacePath);
+
+    /**
      * Determines if there is a valid <code>NodeEntry</code> with the
      * specified <code>nodeName</code>.
      *
@@ -157,18 +179,16 @@
     /**
      * Adds a new, transient child <code>NodeEntry</code>
      *
+     * @param nodeName
+     * @param uniqueID
+     * @param primaryNodeType
+     * @param definition
      * @return
+     * @throws ItemExistsException
      */
     public NodeState addNewNodeEntry(QName nodeName, String uniqueID, QName primaryNodeType, QNodeDefinition definition) throws ItemExistsException;
 
     /**
-     * @param newName
-     * @param newParent
-     * @return
-     */
-    public NodeEntry moveNodeEntry(NodeState childState, QName newName, NodeEntry newParent) throws RepositoryException;
-
-    /**
      * Determines if there is a property entry with the specified <code>QName</code>.
      *
      * @param propName <code>QName</code> object specifying a property name
@@ -226,10 +246,20 @@
      *
      * @param beforeEntry the child node where to insert the node before. If
      * <code>null</code> this entry is moved to the end of its parents child node entries.
-     * @return true if the reordering was successful. False if either of the
-     * given entry does not exist in the listed child entries..
      */
-    public boolean orderBefore(NodeEntry beforeEntry) ;
+    public void orderBefore(NodeEntry beforeEntry) ;
+
+    /**
+     * @param newName
+     * @param newParent
+     * @return
+     */
+    public NodeEntry move(QName newName, NodeEntry newParent, boolean transientMove) throws RepositoryException;
+
+    /**
+     * @return true if this <code>NodeEntry</code> is transiently moved.
+     */
+    public boolean isTransientlyMoved();
 
     /**
      * The parent entry of a external event gets informed about the modification.

Modified: jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/hierarchy/NodeEntryImpl.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/hierarchy/NodeEntryImpl.java?view=diff&rev=513279&r1=513278&r2=513279
==============================================================================
--- jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/hierarchy/NodeEntryImpl.java (original)
+++ jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/hierarchy/NodeEntryImpl.java Thu Mar  1 03:17:22 2007
@@ -35,6 +35,7 @@
 import org.apache.jackrabbit.jcr2spi.state.StaleItemStateException;
 import org.apache.jackrabbit.jcr2spi.state.Status;
 import org.apache.jackrabbit.jcr2spi.state.PropertyState;
+import org.apache.jackrabbit.jcr2spi.state.ItemStateLifeCycleListener;
 import org.apache.jackrabbit.jcr2spi.util.StateUtility;
 import org.apache.commons.collections.iterators.IteratorChain;
 import org.slf4j.Logger;
@@ -51,6 +52,8 @@
 import java.util.HashMap;
 import java.util.Set;
 import java.util.HashSet;
+import java.util.Map;
+import java.util.LinkedHashMap;
 
 /**
  * <code>NodeEntryImpl</code> implements common functionality for child
@@ -58,8 +61,6 @@
  */
 public class NodeEntryImpl extends HierarchyEntryImpl implements NodeEntry {
 
-    // TODO: TOBEFIXED attic for transiently removed or moved child-node-entries required, in order 'know' that they are remove instead of retrieving them from the server again.
-
     private static Logger log = LoggerFactory.getLogger(NodeEntryImpl.class);
 
     /**
@@ -75,6 +76,12 @@
     private ChildNodeEntries childNodeEntries;
 
     /**
+     * Map used to remember transiently removed or moved childNodeEntries, that
+     * must not be retrieved from the persistent storage.
+     */
+    private ChildNodeAttic childNodeAttic;
+
+    /**
      * Map of properties. Key = {@link QName} of property. Value = {@link
      * PropertyEntry}.
      */
@@ -87,6 +94,18 @@
     private final HashMap propertiesInAttic = new HashMap();
 
     /**
+     * Upon transient 'move' ('rename') or 'reorder' of SNSs this
+     * <code>NodeEntry</code> remembers the original parent, name and index
+     * for later revert as well as for the creation of the
+     * {@link #getWorkspaceId() workspace id}. Finally the revertInfo is
+     * used to find the target of an <code>Event</code> indicating external
+     * modification.
+     *
+     * @see #refresh(Event)
+     */
+    private RevertInfo revertInfo;
+
+    /**
      * Creates a new <code>NodeEntryImpl</code>
      *
      * @param parent    the <code>NodeEntry</code> that owns this child item
@@ -97,6 +116,7 @@
     NodeEntryImpl(NodeEntryImpl parent, QName name, String uniqueID, EntryFactory factory) {
         super(parent, name, factory);
         this.uniqueID = uniqueID; // NOTE: don't use setUniqueID (for mod only)
+        this.childNodeAttic = new ChildNodeAttic();
     }
 
     /**
@@ -161,6 +181,7 @@
      * Calls {@link HierarchyEntryImpl#revert()} and moves all properties from the
      * attic back into th properties map. If this HierarchyEntry has been
      * transiently moved, it is in addition moved back to its old parent.
+     * Similarly reordering of child node entries is reverted.
      *
      * @inheritDoc
      * @see HierarchyEntry#revert()
@@ -171,16 +192,9 @@
             properties.putAll(propertiesInAttic);
             propertiesInAttic.clear();
         }
-        // if this entry has been moved before -> move back
-        NodeState state = (NodeState) internalGetItemState();
-        if (state != null && StateUtility.isMovedState(state)) {
-            // move NodeEntry back to its original parent
-            parent.childNodeEntries().remove(this);
-            NodeEntryImpl oldEntry = (NodeEntryImpl) state.getWorkspaceState().getHierarchyEntry();
-            oldEntry.parent.childNodeEntries().add(oldEntry);
 
-            factory.notifyEntryMoved(this, oldEntry);
-        }
+        revertTransientChanges();
+
         // now make sure the underlying state is reverted to the original state
         super.revert();
     }
@@ -267,12 +281,30 @@
                 // root node
                 return idFactory.createNodeId((String) null, Path.ROOT);
             } else {
-                return factory.getIdFactory().createNodeId(parent.getId(), Path.create(getQName(), getIndex()));
+                return idFactory.createNodeId(parent.getId(), Path.create(getQName(), getIndex()));
             }
         }
     }
 
     /**
+     * @see NodeEntry#getWorkspaceId()
+     */
+    public NodeId getWorkspaceId() {
+        IdFactory idFactory = factory.getIdFactory();
+        if (uniqueID != null || parent == null) {
+            // uniqueID and root-node -> internal id is always the same as getId().
+            return getId();
+        } else if (revertInfo != null) {
+            NodeId parentId = revertInfo.oldParent.getWorkspaceId();
+            Path path = Path.create(revertInfo.oldName, revertInfo.oldIndex);
+            return idFactory.createNodeId(parentId, path);
+        } else {
+            NodeId parentId = parent.getWorkspaceId();
+            return idFactory.createNodeId(parentId, Path.create(getQName(), getIndex()));
+        }
+    }
+
+    /**
      * @inheritDoc
      * @see NodeEntry#getUniqueID()
      */
@@ -353,10 +385,20 @@
             } else if (index == Path.INDEX_DEFAULT && entry.properties.containsKey(name)
                 && i == path.getLength() - 1) {
                 // property must not have index && must be final path element
-                PropertyEntry pe = (PropertyEntry) entry.properties.get(name);
-                return pe;
+                return (PropertyEntry) entry.properties.get(name);
             } else {
-                /*
+                // no valid entry
+                // -> check for moved child entry in node-attic
+                // -> check if child points to a removed sns
+                if (entry.childNodeAttic.contains(name, index)) {
+                    throw new PathNotFoundException(path.toString());
+                } else if (entry.childNodeEntries != null) {
+                    int noSNS = entry.childNodeEntries.get(name).size() + entry.childNodeAttic.get(name).size();
+                    if (index <= noSNS) {
+                        throw new PathNotFoundException(path.toString());
+                    }
+                }
+               /*
                 * Unknown entry (not-existing or not yet loaded):
                 * Skip all intermediate entries and directly try to load the ItemState
                 * (including building the itermediate entries. If that fails
@@ -409,6 +451,40 @@
     }
 
     /**
+     * @see NodeEntry#lookupDeepEntry(Path)
+     */
+    public HierarchyEntry lookupDeepEntry(Path workspacePath) {
+        NodeEntryImpl entry = this;
+        for (int i = 0; i < workspacePath.getLength(); i++) {
+            Path.PathElement elem = workspacePath.getElement(i);
+            // check for root element
+            if (elem.denotesRoot()) {
+                if (getParent() != null) {
+                    log.warn("NodeEntry out of 'hierarchy'" + workspacePath.toString());
+                    return null;
+                } else {
+                    continue;
+                }
+            }
+
+            int index = elem.getNormalizedIndex();
+            QName childName = elem.getName();
+
+            // first try to resolve node
+            NodeEntry cne = entry.lookupNodeEntry(childName, index);
+            if (cne != null) {
+                entry = (NodeEntryImpl) cne;
+            } else if (index == Path.INDEX_DEFAULT && i == workspacePath.getLength() - 1) {
+                // property must not have index && must be final path element
+                return entry.lookupPropertyEntry(childName);
+            } else {
+                return null;
+            }
+        }
+        return entry;
+    }
+
+    /**
      * @inheritDoc
      * @see NodeEntry#hasNodeEntry(QName)
      */
@@ -525,7 +601,8 @@
      * @inheritDoc
      * @see NodeEntry#addNewNodeEntry(QName, String, QName, QNodeDefinition)
      */
-    public NodeState addNewNodeEntry(QName nodeName, String uniqueID, QName primaryNodeType, QNodeDefinition definition) throws ItemExistsException {
+    public NodeState addNewNodeEntry(QName nodeName, String uniqueID,
+                                     QName primaryNodeType, QNodeDefinition definition) throws ItemExistsException {
         NodeEntryImpl entry = internalAddNodeEntry(nodeName, uniqueID, Path.INDEX_UNDEFINED, childNodeEntries());
         NodeState state = factory.getItemStateFactory().createNewNodeState(entry, primaryNodeType, definition);
         entry.internalSetItemState(state);
@@ -540,32 +617,14 @@
      * @param childEntries
      * @return
      */
-    private NodeEntryImpl internalAddNodeEntry(QName nodeName, String uniqueID, int index, ChildNodeEntries childEntries) {
+    private NodeEntryImpl internalAddNodeEntry(QName nodeName, String uniqueID,
+                                               int index, ChildNodeEntries childEntries) {
         NodeEntryImpl entry = new NodeEntryImpl(this, nodeName, uniqueID, factory);
         childEntries.add(entry, index);
         return entry;
     }
 
     /**
-     * @see NodeEntry#moveNodeEntry(NodeState, QName, NodeEntry)
-     */
-    public NodeEntry moveNodeEntry(NodeState childState, QName newName, NodeEntry newParent) throws RepositoryException {
-        NodeEntry oldEntry = childNodeEntries().remove(childState.getNodeEntry());
-        if (oldEntry != null) {
-            NodeEntryImpl movedEntry = (NodeEntryImpl) newParent.addNodeEntry(newName, oldEntry.getUniqueID(), Path.INDEX_UNDEFINED);
-            movedEntry.internalSetItemState(childState);
-
-            factory.notifyEntryMoved(oldEntry, movedEntry);
-            return movedEntry;
-        } else {
-            // should never occur
-            String msg = "Internal error. Attempt to move NodeEntry (" + childState + ") which is not connected to its parent.";
-            log.error(msg);
-            throw new RepositoryException(msg);
-        }
-    }
-
-    /**
      * @inheritDoc
      * @see NodeEntry#hasPropertyEntry(QName)
      */
@@ -717,8 +776,56 @@
      * @inheritDoc
      * @see NodeEntry#orderBefore(NodeEntry)
      */
-    public boolean orderBefore(NodeEntry beforeEntry) {
-        return parent.childNodeEntries().reorder(this, beforeEntry);
+    public void orderBefore(NodeEntry beforeEntry) {
+        if (Status.NEW == getStatus()) {
+            // new states get remove upon revert
+            parent.childNodeEntries().reorder(this, beforeEntry);
+        } else {
+            createSiblingRevertInfos();
+            parent.createRevertInfo();
+            // now reorder child entries on parent
+            NodeEntry previousBefore = parent.childNodeEntries().reorder(this, beforeEntry);
+            parent.revertInfo.reordered(this, previousBefore);
+        }
+    }
+
+   /**
+    * @see NodeEntry#move(QName, NodeEntry, boolean)
+    */
+   public NodeEntry move(QName newName, NodeEntry newParent, boolean transientMove) throws RepositoryException {
+       if (parent == null) {
+           // the root may never be moved
+           throw new RepositoryException("Root cannot be moved.");
+       }
+
+       // for existing nodeEntry that are 'moved' for the first time, the
+       // original data must be stored and this entry is moved to the attic.
+       if (transientMove && !isTransientlyMoved() && Status.NEW != getStatus()) {
+           createSiblingRevertInfos();
+           createRevertInfo();
+           parent.childNodeAttic.add(this);
+       }
+
+       NodeEntryImpl entry = (NodeEntryImpl) parent.childNodeEntries().remove(this);
+       if (entry != this) {
+           // should never occur
+           String msg = "Internal error. Attempt to move NodeEntry (" + getQName() + ") which is not connected to its parent.";
+           log.error(msg);
+           throw new RepositoryException(msg);
+       }
+       // set name and parent to new values
+       parent = (NodeEntryImpl) newParent;
+       name = newName;
+       // register entry with its new parent
+       parent.childNodeEntries().add(this);
+       return this;
+   }
+
+    /**
+     * @see NodeEntry#isTransientlyMoved()
+     */
+    public boolean isTransientlyMoved() {
+        return revertInfo != null && revertInfo.isMoved();
     }
 
     /**
@@ -731,10 +838,18 @@
         switch (childEvent.getType()) {
             case Event.NODE_ADDED:
                 int index = childEvent.getQPath().getNameElement().getNormalizedIndex();
-                String uniqueChildID = (childEvent.getItemId().getPath() == null) ? childEvent.getItemId().getUniqueID() : null;
+                String uniqueChildID = null;
+                if (childEvent.getItemId().getPath() == null) {
+                    uniqueChildID = childEvent.getItemId().getUniqueID();
+                }
                 // first check if no matching child entry exists.
                 // TODO: TOBEFIXED for SNSs
-                NodeEntry cne = (uniqueChildID != null) ? childNodeEntries().get(eventName, uniqueChildID) : childNodeEntries().get(eventName, index);
+                NodeEntry cne;
+                if (uniqueChildID != null) {
+                    cne = childNodeEntries().get(eventName, uniqueChildID);
+                } else {
+                    cne = childNodeEntries().get(eventName, index);
+                }
                 if (cne == null) {
                     cne = internalAddNodeEntry(eventName, uniqueChildID, index, childNodeEntries());
                     modified = true;
@@ -764,7 +879,7 @@
 
             case Event.NODE_REMOVED:
             case Event.PROPERTY_REMOVED:
-                HierarchyEntry child = getEntryForExternalEvent(childEvent.getItemId(), childEvent.getQPath());
+                HierarchyEntry child = lookupEntry(childEvent.getItemId(), childEvent.getQPath());
                 if (child != null) {
                     child.remove();
                     modified = true;
@@ -772,7 +887,7 @@
                 break;
 
             case Event.PROPERTY_CHANGED:
-                child = getEntryForExternalEvent(childEvent.getItemId(), childEvent.getQPath());
+                child = lookupEntry(childEvent.getItemId(), childEvent.getQPath());
                 if (child != null) {
                     // Reload data from server and try to merge them with the
                     // current session-state. if the latter is transiently
@@ -802,8 +917,8 @@
 
         // TODO: check if status of THIS_state must be marked modified...
     }
-
-    //------------------------------------------------------< HierarchyEntryImpl >---
+    
+    //-------------------------------------------------< HierarchyEntryImpl >---
     /**
      * @inheritDoc
      * @see HierarchyEntryImpl#doResolve()
@@ -812,7 +927,7 @@
      */
     ItemState doResolve()
         throws NoSuchItemStateException, ItemStateException {
-        return factory.getItemStateFactory().createNodeState(getId(), this);
+        return factory.getItemStateFactory().createNodeState((NodeId) getWorkspaceId(), this);
     }
 
     //-----------------------------------------------< private || protected >---
@@ -831,6 +946,33 @@
     }
 
     /**
+     *
+     * @param oldName
+     * @param oldIndex
+     * @return
+     */
+    boolean matches(QName oldName, int oldIndex) {
+        if (revertInfo != null) {
+            return revertInfo.oldName.equals(oldName) && revertInfo.oldIndex == oldIndex;
+        } else {
+            return getQName().equals(oldName) && getIndex() == oldIndex;
+        }
+    }
+
+    /**
+     *
+     * @param oldName
+     * @return
+     */
+    boolean matches(QName oldName) {
+        if (revertInfo != null) {
+            return revertInfo.oldName.equals(oldName);
+        } else {
+            return getQName().equals(oldName);
+        }
+    }
+
+    /**
      * Searches the child-entries of this NodeEntry for a matching child.
      * Since {@link #refresh(Event)} must always be called on the parent
      * NodeEntry, there is no need to check if a given event id would point
@@ -840,25 +982,25 @@
      * @param eventPath
      * @return
      */
-    private HierarchyEntry getEntryForExternalEvent(ItemId eventId, Path eventPath) {
+    private HierarchyEntry lookupEntry(ItemId eventId, Path eventPath) {
         QName childName = eventPath.getNameElement().getName();
         HierarchyEntry child = null;
         if (eventId.denotesNode()) {
             String uniqueChildID = (eventId.getPath() == null) ? eventId.getUniqueID() : null;
+            // for external node-removal the attic must be consulted first
+            // in order to be able to apply the changes to the proper node-entry.
             if (uniqueChildID != null) {
-                child = childNodeEntries().get(childName, uniqueID);
+                child = childNodeAttic.get(uniqueChildID);
+                if (child == null && childNodeEntries != null) {
+                    child = childNodeEntries.get(childName, uniqueChildID);
+                }
             }
             if (child == null) {
-                child = childNodeEntries().get(childName, eventPath.getNameElement().getNormalizedIndex());
+                int index = eventPath.getNameElement().getNormalizedIndex();
+                child = lookupNodeEntry(childName, index);
             }
         } else {
-            // for external prop-removal the attic must be consulted first
-            // in order not access a NEW prop shadowing a transiently removed
-            // property with the same name.
-            child = (HierarchyEntry) propertiesInAttic.get(childName);
-            if (child == null) {
-                child = (HierarchyEntry) properties.get(childName);
-            }
+            child = lookupPropertyEntry(childName);
         }
         if (child != null) {
             // a NEW hierarchyEntry may never be affected by an external
@@ -871,6 +1013,32 @@
         return child;
     }
 
+    private NodeEntryImpl lookupNodeEntry(QName childName, int index) {
+        NodeEntryImpl child = (NodeEntryImpl) childNodeAttic.get(childName, index);
+        if (child == null && childNodeEntries != null) {
+            List namedChildren = childNodeEntries.get(childName);
+            for (Iterator it = namedChildren.iterator(); it.hasNext(); ) {
+                NodeEntryImpl c = (NodeEntryImpl) it.next();
+                if (c.matches(childName, index)) {
+                    child = c;
+                    break;
+                }
+            }
+        }
+        return child;
+    }
+
+    private PropertyEntry lookupPropertyEntry(QName childName) {
+        // for external prop-removal the attic must be consulted first
+        // in order not access a NEW prop shadowing a transiently removed
+        // property with the same name.
+        PropertyEntry child = (PropertyEntry) propertiesInAttic.get(childName);
+        if (child == null) {
+            child = (PropertyEntry) properties.get(childName);
+        }
+        return child;
+    }
+
     /**
      * Deals with modified jcr:uuid and jcr:mixinTypes property.
      * See {@link #notifyUUIDorMIXINRemoved(QName)}
@@ -919,43 +1087,28 @@
      */
     private ChildNodeEntries childNodeEntries() {
         if (childNodeEntries == null) {
+            childNodeEntries = new ChildNodeEntries(this);
             ItemState state = internalGetItemState();
-            if (state != null) {
-                if (state.getStatus() == Status.NEW) {
-                    childNodeEntries = new ChildNodeEntries(this);
-                } else if (StateUtility.isMovedState((NodeState) state)) {
-                    // TODO: TOBEFIXED need to retrieve the original id. currently this will fail in case of SNS
-                    // since, the index cannot be determined from the original parent any more
-                    NodeId originalID = ((NodeState) state.getWorkspaceState()).getNodeId();
-                    childNodeEntries = loadChildNodeEntries(originalID);
-                } else {
-                    childNodeEntries = loadChildNodeEntries(getId());
+            if (state == null || state.getStatus() != Status.NEW) {
+                try {
+                    NodeId id = (NodeId) getWorkspaceId();
+                    Iterator it = factory.getItemStateFactory().getChildNodeInfos(id);
+                    while (it.hasNext()) {
+                        ChildInfo ci = (ChildInfo) it.next();
+                        internalAddNodeEntry(ci.getName(), ci.getUniqueID(), ci.getIndex(), childNodeEntries);
+                    }
+                } catch (NoSuchItemStateException e) {
+                    log.error("Cannot retrieve child node entries.", e);
+                    // ignore (TODO correct?)
+                } catch (ItemStateException e) {
+                    log.error("Cannot retrieve child node entries.", e);
+                    // ignore (TODO correct?)
                 }
-            } else {
-                childNodeEntries = loadChildNodeEntries(getId());
             }
         }
         return childNodeEntries;
     }
 
-    private ChildNodeEntries loadChildNodeEntries(NodeId id) {
-        ChildNodeEntries cnes = new ChildNodeEntries(this);
-        try {
-            Iterator it = factory.getItemStateFactory().getChildNodeInfos(id);
-            while (it.hasNext()) {
-                ChildInfo ci = (ChildInfo) it.next();
-                internalAddNodeEntry(ci.getName(), ci.getUniqueID(), ci.getIndex(), cnes);
-            }
-        } catch (NoSuchItemStateException e) {
-            log.error("Cannot retrieve child node entries.", e);
-            // ignore (TODO correct?)
-        } catch (ItemStateException e) {
-            log.error("Cannot retrieve child node entries.", e);
-            // ignore (TODO correct?)
-        }
-        return cnes;
-    }
-
     /**
      * Returns an Iterator over all children entries, that currently are loaded
      * with this NodeEntry. NOTE, that if the childNodeEntries have not been
@@ -986,8 +1139,7 @@
                 its = new Iterator[] {properties.values().iterator(), children};
             }
         }
-        IteratorChain chain = new IteratorChain(its);
-        return chain;
+        return new IteratorChain(its);
     }
 
     /**
@@ -1014,5 +1166,197 @@
         }
         // not found (should not occur)
         return Path.INDEX_UNDEFINED;
+    }
+
+    /**
+     * If 'revertInfo' is null it gets created from the current information
+     * present on this entry.
+     */
+    private void createRevertInfo() {
+        if (revertInfo == null) {
+            revertInfo = new RevertInfo(parent, name, getIndex());
+        }
+    }
+
+    /**
+     * Special handling for MOVE and REORDER with same-name-siblings
+     */
+    private void createSiblingRevertInfos() {
+        if (revertInfo != null) {
+            return; // nothing to do
+        }
+        // for SNSs without UniqueID remember original index in order to
+        // be able to build the workspaceID TODO: improve
+        List sns = parent.childNodeEntries().get(name);
+        if (sns.size() > 1) {
+            for (Iterator it = sns.iterator(); it.hasNext();) {
+                NodeEntryImpl sibling = (NodeEntryImpl) it.next();
+                if (sibling.getUniqueID() == null && Status.NEW != sibling.getStatus()) {
+                    sibling.createRevertInfo();
+                }
+            }
+        }
+    }
+
+    /**
+     * Revert a transient move and reordering of child entries
+     */
+    private void revertTransientChanges() {
+        if (revertInfo == null) {
+            return; // nothing to do
+        }
+
+        if (isTransientlyMoved())  {
+            // move NodeEntry back to its original parent
+            // TODO improve for simple renaming
+            parent.childNodeEntries().remove(this);
+            revertInfo.oldParent.childNodeAttic.remove(this);
+
+            // now restore moved entry with the old name and index and re-add
+            // it to its original parent (unless it got destroyed)
+            parent = revertInfo.oldParent;
+            name = revertInfo.oldName;
+            ItemState state = internalGetItemState();
+            if (state != null && !Status.isTerminal(state.getStatus())) {
+                parent.childNodeEntries().add(this, revertInfo.oldIndex);
+            }
+        }
+        // revert reordering of child-node-entries
+        revertInfo.revertReordering();
+
+        revertInfo.dispose();
+        revertInfo = null;
+    }
+
+    /**
+     * This entry has be set to 'EXISTING' again -> move and/or reordering of
+     * child entries has been completed and the 'revertInfo' needs to be
+     * reset/removed.
+     */
+    private void completeTransientChanges() {
+        // old parent can forget this one
+        revertInfo.oldParent.childNodeAttic.remove(this);
+        revertInfo.dispose();
+        revertInfo = null;
+    }
+
+    //--------------------------------------------------------< inner class >---
+    /**
+     * Upon move of this entry or upon reorder of its child-entries store
+     * original hierarchy information for later revert and in order to be able
+     * to build the workspace id(s).
+     */
+    private class RevertInfo implements ItemStateLifeCycleListener {
+
+        private NodeEntryImpl oldParent;
+        private QName oldName;
+        private int oldIndex;
+
+        private Map reorderedChildren;
+
+        private RevertInfo(NodeEntryImpl oldParent, QName oldName, int oldIndex) {
+            this.oldParent = oldParent;
+            this.oldName = oldName;
+            this.oldIndex = oldIndex;
+
+            ItemState state = internalGetItemState();
+            if (state != null) {
+                state.addListener(this);
+            } // else: should never be null.
+        }
+
+        private void dispose() {
+            ItemState state = internalGetItemState();
+            if (state != null) {
+                state.removeListener(this);
+            }
+
+            if (reorderedChildren != null) {
+                // special handling of SNS-children  TODO: improve
+                // since reordered sns-children are not marked modified (unless they
+                // got modified by some other action, their revertInfo
+                // must be disposed manually
+                for (Iterator it = reorderedChildren.keySet().iterator(); it.hasNext();) {
+                    NodeEntry ne = (NodeEntry) it.next();
+                    List sns = childNodeEntries.get(ne.getQName());
+                    if (sns.size() > 1) {
+                        for (Iterator snsIt = sns.iterator(); snsIt.hasNext();) {
+                            NodeEntryImpl sibling = (NodeEntryImpl) snsIt.next();
+                            if (sibling.revertInfo != null && Status.EXISTING == sibling.getStatus()) {
+                                sibling.revertInfo.dispose();
+                                sibling.revertInfo = null;
+                            }
+                        }
+                    }
+                }
+                reorderedChildren.clear();
+            }
+        }
+
+        private boolean isMoved() {
+            return oldParent != getParent() || !getQName().equals(oldName);
+        }
+
+        private void reordered(NodeEntry insertEntry, NodeEntry previousBefore) {
+            if (reorderedChildren == null) {
+                reorderedChildren = new LinkedHashMap();
+            }
+            reorderedChildren.put(insertEntry, previousBefore);
+        }
+
+        private void revertReordering() {
+            if (reorderedChildren == null) {
+                return; // nothing to do
+            }
+            // revert all 'reorder' calls in in reverse other they were performed
+            NodeEntry[] reordered = (NodeEntry[]) reorderedChildren.keySet().toArray(new NodeEntry[reorderedChildren.size()]);
+            for (int i = reordered.length-1; i >= 0; i--) {
+                NodeEntry ordered = reordered[i];
+                if (isValidReorderedChild(ordered)) {
+                    NodeEntry previousBefore = (NodeEntry) reorderedChildren.get(ordered);
+                    if (previousBefore == null || isValidReorderedChild(previousBefore)) {
+                        childNodeEntries.reorder(ordered, previousBefore);
+                    }
+                }
+            }
+        }
+
+        private boolean isValidReorderedChild(NodeEntry child) {
+            if (Status.isTerminal(child.getStatus())) {
+                log.warn("Cannot revert reordering. 'previousBefore' does not exist any more.");
+                return false;
+            }
+            if (child.isTransientlyMoved()) {
+                // child has been moved away -> move back
+                try {
+                    child.revert();
+                } catch (ItemStateException e) {
+                    log.error("Internal error", e);
+                    return false;
+                }
+            }
+            return true;
+        }
+
+        /**
+         * @see ItemStateLifeCycleListener#statusChanged(ItemState, int)
+         */
+        public void statusChanged(ItemState state, int previousStatus) {
+            switch (state.getStatus()) {
+                case Status.EXISTING:
+                    // stop listening
+                    state.removeListener(this);
+                    completeTransientChanges();
+                    break;
+
+                case Status.REMOVED:
+                case Status.STALE_DESTROYED:
+                    // stop listening
+                    state.removeListener(this);
+                    // remove from the attic
+                    revertTransientChanges();
+                    break;
+            }
+        }
     }
 }

Modified: jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/hierarchy/PropertyEntry.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/hierarchy/PropertyEntry.java?view=diff&rev=513279&r1=513278&r2=513279
==============================================================================
--- jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/hierarchy/PropertyEntry.java (original)
+++ jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/hierarchy/PropertyEntry.java Thu Mar  1 03:17:22 2007
@@ -32,6 +32,17 @@
     public PropertyId getId();
 
     /**
+     * Returns the ID that must be used for resolving this entry OR loading its
+     * children entries from the persistent layer. This is the same as
+     * <code>getId()</code> unless any of its ancestors has been transiently
+     * moved.
+     *
+     * @return
+     * @see #getId()
+     */
+    public PropertyId getWorkspaceId();
+
+    /**
      * @return the referenced <code>PropertyState</code>.
      * @throws NoSuchItemStateException if the <code>PropertyState</code> does not
      * exist anymore.

Modified: jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/hierarchy/PropertyEntryImpl.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/hierarchy/PropertyEntryImpl.java?view=diff&rev=513279&r1=513278&r2=513279
==============================================================================
--- jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/hierarchy/PropertyEntryImpl.java (original)
+++ jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/hierarchy/PropertyEntryImpl.java Thu Mar  1 03:17:22 2007
@@ -23,16 +23,12 @@
 import org.apache.jackrabbit.jcr2spi.state.ItemState;
 import org.apache.jackrabbit.jcr2spi.state.ItemStateException;
 import org.apache.jackrabbit.jcr2spi.state.Status;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
 
 /**
  * <code>PropertyEntryImpl</code> implements a reference to a property state.
  */
 public class PropertyEntryImpl extends HierarchyEntryImpl implements PropertyEntry {
 
-    private static Logger log = LoggerFactory.getLogger(PropertyEntryImpl.class);
-
     /**
      * Creates a new <code>PropertyEntryImpl</code>.
      *
@@ -64,21 +60,27 @@
      * <p/>
      * Returns a <code>PropertyState</code>.
      */
-    ItemState doResolve()
-            throws NoSuchItemStateException, ItemStateException {
-        return factory.getItemStateFactory().createPropertyState(getId(), this);
+    ItemState doResolve() throws NoSuchItemStateException, ItemStateException {
+        return factory.getItemStateFactory().createPropertyState(getWorkspaceId(), this);
     }
 
     //------------------------------------------------------< PropertyEntry >---
     /**
-     * @inheritDoc
+     * @see PropertyEntry#getId()
      */
     public PropertyId getId() {
         return factory.getIdFactory().createPropertyId(parent.getId(), getQName());
     }
 
     /**
-     * @inheritDoc
+     * @see PropertyEntry#getWorkspaceId()
+     */
+    public PropertyId getWorkspaceId() {
+        return factory.getIdFactory().createPropertyId(parent.getWorkspaceId(), getQName());
+    }
+
+    /**
+     * @see PropertyEntry#getPropertyState()
      */
     public PropertyState getPropertyState() throws NoSuchItemStateException, ItemStateException {
         return (PropertyState) getItemState();



Mime
View raw message