You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@jackrabbit.apache.org by an...@apache.org on 2008/10/14 09:48:24 UTC

svn commit: r704361 [2/5] - in /jackrabbit/trunk: jackrabbit-jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/ jackrabbit-jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/hierarchy/ jackrabbit-jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/o...

Modified: jackrabbit/trunk/jackrabbit-jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/hierarchy/NodeEntryImpl.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/hierarchy/NodeEntryImpl.java?rev=704361&r1=704360&r2=704361&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit-jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/hierarchy/NodeEntryImpl.java (original)
+++ jackrabbit/trunk/jackrabbit-jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/hierarchy/NodeEntryImpl.java Tue Oct 14 00:48:22 2008
@@ -16,46 +16,51 @@
  */
 package org.apache.jackrabbit.jcr2spi.hierarchy;
 
-import org.apache.jackrabbit.spi.Name;
-import org.apache.jackrabbit.spi.Path;
-import org.apache.jackrabbit.spi.NodeId;
+import org.apache.commons.collections.iterators.IteratorChain;
+import org.apache.jackrabbit.commons.iterator.RangeIteratorAdapter;
+import org.apache.jackrabbit.jcr2spi.operation.AddNode;
+import org.apache.jackrabbit.jcr2spi.operation.AddProperty;
+import org.apache.jackrabbit.jcr2spi.operation.Move;
+import org.apache.jackrabbit.jcr2spi.operation.Operation;
+import org.apache.jackrabbit.jcr2spi.operation.Remove;
+import org.apache.jackrabbit.jcr2spi.operation.ReorderNodes;
+import org.apache.jackrabbit.jcr2spi.operation.SetMixin;
+import org.apache.jackrabbit.jcr2spi.state.ItemState;
+import org.apache.jackrabbit.jcr2spi.state.NodeState;
+import org.apache.jackrabbit.jcr2spi.state.PropertyState;
+import org.apache.jackrabbit.jcr2spi.state.Status;
+import org.apache.jackrabbit.jcr2spi.util.StateUtility;
+import org.apache.jackrabbit.spi.ChildInfo;
 import org.apache.jackrabbit.spi.Event;
+import org.apache.jackrabbit.spi.IdFactory;
 import org.apache.jackrabbit.spi.ItemId;
+import org.apache.jackrabbit.spi.Name;
+import org.apache.jackrabbit.spi.NodeId;
+import org.apache.jackrabbit.spi.Path;
+import org.apache.jackrabbit.spi.PathFactory;
+import org.apache.jackrabbit.spi.PropertyId;
 import org.apache.jackrabbit.spi.QNodeDefinition;
 import org.apache.jackrabbit.spi.QPropertyDefinition;
-import org.apache.jackrabbit.spi.IdFactory;
-import org.apache.jackrabbit.spi.PropertyId;
-import org.apache.jackrabbit.spi.PathFactory;
-import org.apache.jackrabbit.spi.ChildInfo;
-import org.apache.jackrabbit.jcr2spi.state.NodeState;
-import org.apache.jackrabbit.jcr2spi.state.ItemState;
-import org.apache.jackrabbit.jcr2spi.state.ChangeLog;
-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.jackrabbit.spi.QValue;
 import org.apache.jackrabbit.spi.commons.name.NameConstants;
 import org.apache.jackrabbit.spi.commons.name.PathBuilder;
-import org.apache.jackrabbit.commons.iterator.RangeIteratorAdapter;
-import org.apache.commons.collections.iterators.IteratorChain;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+import javax.jcr.InvalidItemStateException;
 import javax.jcr.ItemExistsException;
-import javax.jcr.RepositoryException;
-import javax.jcr.PathNotFoundException;
 import javax.jcr.ItemNotFoundException;
-import javax.jcr.InvalidItemStateException;
-import java.util.Iterator;
-import java.util.List;
+import javax.jcr.PathNotFoundException;
+import javax.jcr.RepositoryException;
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Collections;
 import java.util.HashMap;
-import java.util.Set;
 import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
 import java.util.Map;
-import java.util.LinkedHashMap;
+import java.util.Set;
 
 /**
  * <code>NodeEntryImpl</code> implements common functionality for child
@@ -173,7 +178,7 @@
             }
         }
         // invalidate 'childNodeEntries'
-        if (getStatus() != Status.NEW && childNodeEntries != null) {
+        if (getStatus() != Status.NEW) {
             childNodeEntries.invalidate();
         }
         // ... and invalidate the resolved state (if available)
@@ -217,8 +222,9 @@
             properties.addAll(propertiesInAttic.values());
             propertiesInAttic.clear();
         }
+        // NOTE: childNodeAttic must not be cleared for the move of child entries
+        // will be separately reverted.
 
-        revertTransientChanges();
         // now make sure the attached state is reverted to the original state
         super.revert();
     }
@@ -246,50 +252,52 @@
      * @see HierarchyEntry#remove()
      */
     public void remove() {
-        removeEntry(this);
-        if (getStatus() != Status.STALE_DESTROYED) {
-            NodeEntry removed = parent.childNodeEntries.remove(this);
-            if (removed == null) {
-                // try attic
-                parent.childNodeAttic.remove(this);
+        ItemState state = internalGetItemState();
+        if (state != null) {
+            if (getStatus() == Status.EXISTING_MODIFIED) {
+                state.setStatus(Status.STALE_DESTROYED);
+            } else {
+                state.setStatus(Status.REMOVED);
+                parent.internalRemoveChildEntry(this);
             }
+        } else {
+            // unresolved: ignore.
+            parent.internalRemoveChildEntry(this);
         }
 
-        // TODO: deal with childNodeAttic
-        // now traverse all child-entries and mark the attached states removed
-        // without removing the child-entries themselves. this is not required
-        // since this (i.e. the parent is removed as well).
+        // now remove all child-entries.
         for (Iterator it = getAllChildEntries(true); it.hasNext();) {
             HierarchyEntryImpl ce = (HierarchyEntryImpl) it.next();
-            removeEntry(ce);
+            ce.remove();
         }
     }
 
     /**
-     * If the underlying state is available and transiently modified, new or
-     * stale, it gets added to the changeLog. Subsequently this call is repeated
-     * recursively to collect all child states that meet the condition,
-     * including those property states that have been moved to the attic.
-     *
-     * @inheritDoc
-     * @see HierarchyEntry#collectStates(ChangeLog, boolean)
+     * @see HierarchyEntry#complete(Operation)
      */
-    public synchronized void collectStates(ChangeLog changeLog, boolean throwOnStale) throws InvalidItemStateException {
-        super.collectStates(changeLog, throwOnStale);
-
-        // collect transient child states including properties in attic.
-        for (Iterator it = getAllChildEntries(true); it.hasNext();) {
-            HierarchyEntry ce = (HierarchyEntry) it.next();
-            ce.collectStates(changeLog, throwOnStale);
+    public void complete(Operation operation) throws RepositoryException {
+        if (operation instanceof AddNode) {
+            complete((AddNode) operation);
+        } else if (operation instanceof AddProperty) {
+            complete((AddProperty) operation);
+        } else if (operation instanceof SetMixin) {
+            complete((SetMixin) operation);
+        } else if (operation instanceof Remove) {
+            complete((Remove) operation);
+        } else if (operation instanceof ReorderNodes) {
+            complete((ReorderNodes) operation);
+        } else if (operation instanceof Move) {
+            complete((Move) operation);
+        } else {
+            throw new IllegalArgumentException();
         }
     }
-
     //----------------------------------------------------------< NodeEntry >---
     /**
      * @inheritDoc
      * @see NodeEntry#getId()
      */
-    public NodeId getId() {
+    public NodeId getId() throws InvalidItemStateException, RepositoryException {
         IdFactory idFactory = factory.getIdFactory();
         if (uniqueID != null) {
             return idFactory.createNodeId(uniqueID);
@@ -308,7 +316,7 @@
     /**
      * @see NodeEntry#getWorkspaceId()
      */
-    public NodeId getWorkspaceId() {
+    public NodeId getWorkspaceId() throws InvalidItemStateException, RepositoryException {
         IdFactory idFactory = factory.getIdFactory();
         if (uniqueID != null || parent == null) {
             // uniqueID and root-node -> internal id is always the same as getId().
@@ -316,7 +324,7 @@
         } else {
             PathFactory pf = factory.getPathFactory();
             NodeId parentId = (revertInfo != null) ? revertInfo.oldParent.getWorkspaceId() : parent.getWorkspaceId();
-            return idFactory.createNodeId(parentId, pf.create(getWorkspaceName(), getWorkspaceIndex()));
+            return idFactory.createNodeId(parentId, pf.create(getName(true), getIndex(true)));
         }
     }
 
@@ -345,23 +353,8 @@
      * @inheritDoc
      * @see NodeEntry#getIndex()
      */
-    public int getIndex() {
-        if (parent == null) {
-            // the root state may never have siblings
-            return Path.INDEX_DEFAULT;
-        }
-
-        NodeState state = (NodeState) internalGetItemState();
-        try {
-            if (state == null || !state.hasDefinition() || state.getDefinition().allowsSameNameSiblings()) {
-                return parent.getChildIndex(this);
-            } else {
-                return Path.INDEX_DEFAULT;
-            }
-        } catch (RepositoryException e) {
-            log.error("Error while building Index. ", e.getMessage());
-            return Path.INDEX_UNDEFINED;
-        }
+    public int getIndex() throws InvalidItemStateException, RepositoryException {
+        return getIndex(false);
     }
 
     /**
@@ -424,6 +417,12 @@
                 }
                 Path remainingPath = pb.getPath();
 
+                // shortcut: entry is NEW and still unresolved remaining path
+                // elements -> hierarchy doesn't exist anyway.
+                if (entry.getStatus() == Status.NEW) {
+                    throw new PathNotFoundException(path.toString());
+                }
+
                 NodeId parentId = entry.getWorkspaceId();
                 IdFactory idFactory = factory.getIdFactory();
 
@@ -484,7 +483,6 @@
     }
 
     /**
-     * @inheritDoc
      * @see NodeEntry#hasNodeEntry(Name)
      */
     public synchronized boolean hasNodeEntry(Name nodeName) {
@@ -497,7 +495,6 @@
     }
 
     /**
-     * @inheritDoc
      * @see NodeEntry#hasNodeEntry(Name, int)
      */
     public synchronized boolean hasNodeEntry(Name nodeName, int index) {
@@ -510,7 +507,6 @@
     }
 
     /**
-     * @inheritDoc
      * @see NodeEntry#getNodeEntry(Name, int)
      */
     public synchronized NodeEntry getNodeEntry(Name nodeName, int index) throws RepositoryException {
@@ -518,7 +514,6 @@
     }
 
     /**
-     * @inheritDoc
      * @see NodeEntry#getNodeEntry(Name, int, boolean)
      */
     public NodeEntry getNodeEntry(Name nodeName, int index, boolean loadIfNotFound) throws RepositoryException {
@@ -539,14 +534,13 @@
                 && !containsAtticChild(entries, nodeName, index)
                 && Status.NEW != getStatus()) {
             PathFactory pf = factory.getPathFactory();
-            NodeId cId = factory.getIdFactory().createNodeId(getId(), pf.create(nodeName, index));
+            NodeId cId = factory.getIdFactory().createNodeId(getWorkspaceId(), pf.create(nodeName, index));
             cne = loadNodeEntry(cId);
         }
         return cne;
     }
 
     /**
-     * @inheritDoc
      * @see NodeEntry#getNodeEntries()
      */
     public synchronized Iterator getNodeEntries() throws RepositoryException {
@@ -583,9 +577,7 @@
     }
 
     /**
-     *
-     * @param childInfos
-     * @throws RepositoryException
+     * @see NodeEntry#setNodeEntries(Iterator)
      */
     public void setNodeEntries(Iterator childInfos) throws RepositoryException {
         if (childNodeAttic.isEmpty()) {
@@ -604,8 +596,7 @@
     }
 
     /**
-     * @inheritDoc
-     * @see NodeEntry#getOrAddNodeEntry(Name,int,String)
+     * @see NodeEntry#addNodeEntry(Name, String, int)
      */
     public NodeEntry getOrAddNodeEntry(Name nodeName, int index, String uniqueID) throws RepositoryException {
         NodeEntry ne = lookupNodeEntry(uniqueID, nodeName, index);
@@ -618,33 +609,17 @@
     }
 
     /**
-     * @inheritDoc
      * @see NodeEntry#addNewNodeEntry(Name, String, Name, QNodeDefinition)
      */
-    public NodeState addNewNodeEntry(Name nodeName, String uniqueID,
+    public NodeEntry addNewNodeEntry(Name nodeName, String uniqueID,
                                      Name primaryNodeType, QNodeDefinition definition) throws RepositoryException {
         NodeEntry entry = internalAddNodeEntry(nodeName, uniqueID, Path.INDEX_UNDEFINED);
         NodeState state = factory.getItemStateFactory().createNewNodeState(entry, primaryNodeType, definition);
-        if (!entry.isAvailable()) {
-            entry.setItemState(state);
-        }
-        return state;
-    }
-
-    /**
-     * @param nodeName
-     * @param uniqueID
-     * @param index
-     * @return the added entry.
-     */
-    private NodeEntry internalAddNodeEntry(Name nodeName, String uniqueID, int index) {
-        NodeEntry entry = factory.createNodeEntry(this, nodeName, uniqueID);
-        childNodeEntries.add(entry, index);
+        entry.setItemState(state);
         return entry;
     }
 
     /**
-     * @inheritDoc
      * @see NodeEntry#hasPropertyEntry(Name)
      */
     public synchronized boolean hasPropertyEntry(Name propName) {
@@ -653,7 +628,6 @@
     }
 
     /**
-     * @inheritDoc
      * @see NodeEntry#getPropertyEntry(Name)
      */
     public synchronized PropertyEntry getPropertyEntry(Name propName) {
@@ -700,8 +674,7 @@
     }
 
     /**
-     * @inheritDoc
-     * @see NodeEntry#getOrAddPropertyEntry(Name)
+     * @see NodeEntry#addPropertyEntry(Name)
      */
     public PropertyEntry getOrAddPropertyEntry(Name propName) throws ItemExistsException {
         PropertyEntry pe = lookupPropertyEntry(propName);
@@ -714,30 +687,9 @@
     }
 
     /**
-     * Internal method that adds a PropertyEntry without checking of that entry
-     * exists.
-     *
-     * @param propName
-     * @param notifySpecial
-     * @return the added entry.
+     * @see NodeEntry#setPropertyEntries(Collection)
      */
-    private PropertyEntry internalAddPropertyEntry(Name propName, boolean notifySpecial) {
-        PropertyEntry entry = factory.createPropertyEntry(this, propName);
-        properties.add(entry);
-
-        // if property-name is jcr:uuid or jcr:mixin this affects this entry
-        // and the attached nodeState.
-        if (notifySpecial && StateUtility.isUuidOrMixin(propName)) {
-            notifyUUIDorMIXINModified(entry);
-        }
-        return entry;
-    }
-
-    /**
-     * @inheritDoc
-     * @see NodeEntry#addPropertyEntries(Collection)
-     */
-    public void addPropertyEntries(Collection propNames) throws ItemExistsException, RepositoryException {
+    public void setPropertyEntries(Collection propNames) throws ItemExistsException, RepositoryException {
         Set diff = new HashSet();
         diff.addAll(properties.getPropertyNames());
         boolean containsExtra = diff.removeAll(propNames);
@@ -747,7 +699,7 @@
             Name propName = (Name) it.next();
             if (!properties.contains(propName)) {
                 // TODO: check again.
-                // addPropertyEntries is used by WorkspaceItemStateFactory upon
+                // setPropertyEntries is used by WorkspaceItemStateFactory upon
                 // creating a NodeState, in which case the uuid/mixins are set
                 // anyway and not need exists to explicitely load the corresponding
                 // property state in order to retrieve the values.
@@ -771,10 +723,9 @@
     }
 
     /**
-     * @inheritDoc
      * @see NodeEntry#addNewPropertyEntry(Name, QPropertyDefinition)
      */
-    public PropertyState addNewPropertyEntry(Name propName, QPropertyDefinition definition)
+    public PropertyEntry addNewPropertyEntry(Name propName, QPropertyDefinition definition, QValue[] values, int propertyType)
             throws ItemExistsException, RepositoryException {
         // check for an existing property
         PropertyEntry existing = properties.get(propName);
@@ -782,10 +733,9 @@
             try {
                 PropertyState existingState = existing.getPropertyState();
                 int status = existingState.getStatus();
-
                 if (Status.isTerminal(status)) {
                     // an old property-entry that is not valid any more
-                    properties.remove(propName);
+                    properties.remove(existing);
                 } else if (status == Status.EXISTING_REMOVED) {
                     // transiently removed -> move it to the attic
                     propertiesInAttic.put(propName, existing);
@@ -796,36 +746,22 @@
             } catch (ItemNotFoundException e) {
                 // entry does not exist on the persistent layer
                 // -> therefore remove from properties map
-                properties.remove(propName);
+                properties.remove(existing);
             } catch (RepositoryException e) {
                 // some other error -> remove from properties map
-                properties.remove(propName);
+                properties.remove(existing);
             }
         }
 
         // add the property entry
         PropertyEntry entry = factory.createPropertyEntry(this, propName);
-        properties.add(entry);
+        PropertyState state = factory.getItemStateFactory().createNewPropertyState(entry, definition, values, propertyType);
+        entry.setItemState(state);
 
-        PropertyState state = factory.getItemStateFactory().createNewPropertyState(entry, definition);
-        if (!entry.isAvailable()) {
-            entry.setItemState(state);
-        }
-
-        return state;
-    }
+        // add the property entry if creating the new state was successful
+        properties.add(entry);
 
-    /**
-     * @param propName
-     */
-    void internalRemovePropertyEntry(Name propName) {
-        if (!properties.remove(propName)) {
-            propertiesInAttic.remove(propName);
-        }
-        // special properties
-        if (StateUtility.isUuidOrMixin(propName)) {
-            notifyUUIDorMIXINRemoved(propName);
-        }
+        return entry;
     }
 
     /**
@@ -837,11 +773,9 @@
             // new states get remove upon revert
             parent.childNodeEntries.reorder(this, beforeEntry);
         } else {
-            createSiblingRevertInfos();
-            parent.createRevertInfo();
+            createRevertInfo();
             // now reorder child entries on parent
-            NodeEntry previousBefore = parent.childNodeEntries.reorder(this, beforeEntry);
-            parent.revertInfo.reordered(this, previousBefore);
+            parent.childNodeEntries.reorder(this, beforeEntry);
         }
     }
 
@@ -856,10 +790,18 @@
 
        // 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();
+       if (transientMove) {
            createRevertInfo();
-           parent.childNodeAttic.add(this);
+           if (Status.NEW != getStatus()) {
+               if (newParent != revertInfo.oldParent) {
+                   revertInfo.oldParent.childNodeAttic.add(this);
+               } else {
+                   // entry is either rename OR moved back to it's original
+                   // parent. for the latter case make sure, there is no attic
+                   // entry remaing refering to the entry that is being added.
+                   revertInfo.oldParent.childNodeAttic.remove(this);
+               }
+           }
        }
 
        NodeEntryImpl entry = (NodeEntryImpl) parent.childNodeEntries.remove(this);
@@ -885,73 +827,83 @@
     }
 
     /**
-     * @param childEvent
      * @see NodeEntry#refresh(Event)
      */
     public void refresh(Event childEvent) {
-        Name eventName = childEvent.getPath().getNameElement().getName();
+        ItemId eventId = childEvent.getItemId();
+        Path eventPath = childEvent.getPath();
+        Name eventName = eventPath.getNameElement().getName();
+        HierarchyEntry child = lookupEntry(eventId, eventPath);
+
         switch (childEvent.getType()) {
             case Event.NODE_ADDED:
-                int index = childEvent.getPath().getNameElement().getNormalizedIndex();
-                String uniqueChildID = null;
-                if (childEvent.getItemId().getPath() == null) {
-                    uniqueChildID = childEvent.getItemId().getUniqueID();
-                }
-
-                // TODO: TOBEFIXED for SNSs
-                // first check if no matching child entry exists.
-                NodeEntry cne;
-                if (uniqueChildID != null) {
-                    cne = childNodeEntries.get(eventName, uniqueChildID);
-                    if (cne == null) {
-                        // entry may exist but without having uniqueID resolved
-                        cne = childNodeEntries.get(eventName, index);
-                    }
-                } else {
-                    cne = childNodeEntries.get(eventName, index);
-                }
-                if (cne == null) {
-                    internalAddNodeEntry(eventName, uniqueChildID, index);
-                } else {
-                    // child already exists -> deal with NEW entries, that were
-                    // added by some other session.
-                    // TODO: TOBEFIXED
-                }
-                break;
-
             case Event.PROPERTY_ADDED:
-                // create a new property reference if it has not been
-                // added by some earlier 'add' event
-                HierarchyEntry child = lookupEntry(childEvent.getItemId(), childEvent.getPath());
-                if (child == null) {
-                    internalAddPropertyEntry(eventName, true);
+                if (child == null || child.getStatus() == Status.REMOVED) {
+                    // no such child or a colliding new child existed but got
+                    // removed already -> add the new entry.
+                    if (childEvent.getType() ==  Event.NODE_ADDED) {
+                        String uniqueChildID = (eventId.getPath() == null) ? eventId.getUniqueID() : null;
+                        int index = eventPath.getNameElement().getNormalizedIndex();
+                        internalAddNodeEntry(eventName, uniqueChildID, index);
+                    } else {
+                        internalAddPropertyEntry(eventName, true);
+                    }
                 } else {
-                    child.reload(false, true);
+                    // item already present
+                    int status = child.getStatus();
+                    if (Status.NEW == status) {
+                        // event conflicts with a transiently added item on this
+                        // node entry -> mark the parent node (this) stale.
+                        internalGetItemState().setStatus(Status.MODIFIED);
+                    } // else: child already added -> ignore
                 }
                 break;
 
             case Event.NODE_REMOVED:
             case Event.PROPERTY_REMOVED:
-                child = lookupEntry(childEvent.getItemId(), childEvent.getPath());
                 if (child != null) {
-                    child.remove();
+                    int status = child.getStatus();
+                    if (Status.isTransient(status) || Status.isStale(status)) {
+                        if (Status.EXISTING_REMOVED == status) {
+                            // colliding item removal -> mark parent stale
+                            internalGetItemState().setStatus(Status.MODIFIED);
+                        }
+                        // pending changes -> don't remove entry in the hierarchy
+                        // but rather change status to 'STALE_DESTROYED'
+                        ItemState childState = ((HierarchyEntryImpl) child).internalGetItemState();
+                        childState.setStatus(Status.STALE_DESTROYED);
+                    } else {
+                        // no pending changes -> save to remove the entry.
+                        child.remove();
+                    }
                 } // else: child-Entry has not been loaded yet -> ignore
                 break;
 
             case Event.PROPERTY_CHANGED:
-                child = lookupEntry(childEvent.getItemId(), childEvent.getPath());
                 if (child == null) {
                     // prop-Entry has not been loaded yet -> add propEntry
                     internalAddPropertyEntry(eventName, true);
                 } else if (child.isAvailable()) {
+                    int status = child.getStatus();
+                    // if the child has pending changes -> stale.
                     // Reload data from server and try to merge them with the
                     // current session-state. if the latter is transiently
                     // modified and merge fails it must be marked STALE afterwards.
-                    child.reload(false, false);
-                    // special cases: jcr:uuid and jcr:mixinTypes affect the parent
-                    // (i.e. this NodeEntry) since both props are protected
-                    if (StateUtility.isUuidOrMixin(eventName)) {
-                        notifyUUIDorMIXINModified((PropertyEntry) child);
+                    if (Status.isStale(status))  {
+                        // ignore. nothing to do.
+                    } else if (Status.isTransient(child.getStatus())) {
+                        // pending changes -> don't reload entry but rather
+                        // mark it stale
+                        ((HierarchyEntryImpl) child).internalGetItemState().setStatus(Status.MODIFIED);
+                    } else {
+                        // no pending changes -> invalidate and force reload
+                        // upon next access.
+                        child.invalidate(false);
+                        // special cases: jcr:uuid and jcr:mixinTypes affect the
+                        // parent (i.e. this NodeEntry)
+                        if (StateUtility.isUuidOrMixin(eventName)) {
+                            notifyUUIDorMIXINModified((PropertyEntry) child);
+                        }
                     }
                 } // else: existing entry but state not yet built -> ignore event
                 break;
@@ -962,7 +914,6 @@
     }
     //-------------------------------------------------< HierarchyEntryImpl >---
     /**
-     * @inheritDoc
      * @see HierarchyEntryImpl#doResolve()
      * <p/>
      * Returns a <code>NodeState</code>.
@@ -1005,30 +956,67 @@
         // recursively build path of parent
         buildPath(builder, parentEntry, wspPath);
 
-        int index = (wspPath) ? nEntry.getWorkspaceIndex() : nEntry.getIndex();
-        Name name = (wspPath) ? nEntry.getWorkspaceName() : nEntry.getName();
-        // add to path
-        if (index == Path.INDEX_UNDEFINED) {
-            throw new RepositoryException("Invalid index " + index + " with nodeEntry " + nEntry);
-        }
-
-        // TODO: check again. special treatment for default index for consistency with PathFormat.parse
+        int index = nEntry.getIndex(wspPath);
+        Name name = nEntry.getName(wspPath);
         builder.addLast(name, index);
     }
 
     //-----------------------------------------------< private || protected >---
     /**
-     * @throws IllegalArgumentException if <code>this</code> is not the parent
-     * of the given <code>ItemState</code>.
+     * @param nodeName
+     * @param uniqueID
+     * @param index
+     * @return the added entry.
+     */
+    private NodeEntry internalAddNodeEntry(Name nodeName, String uniqueID, int index) {
+        NodeEntry entry = factory.createNodeEntry(this, nodeName, uniqueID);
+        childNodeEntries.add(entry, index);
+        return entry;
+    }
+
+    /**
+     * Internal method that adds a PropertyEntry without checking of that entry
+     * exists.
+     *
+     * @param propName
+     * @param notifySpecial
+     * @return the added entry.
      */
-    synchronized void revertPropertyRemoval(PropertyEntry propertyEntry) {
-        if (propertyEntry.getParent() != this) {
-            throw new IllegalArgumentException("Internal error: Parent mismatch.");
-        }
-        Name propName = propertyEntry.getName();
-        if (propertiesInAttic.containsKey(propName)) {
-            properties.add((PropertyEntry) propertiesInAttic.remove(propName));
-        } // else: propEntry has never been moved to the attic (see 'getOrAddPropertyEntry')
+    private PropertyEntry internalAddPropertyEntry(Name propName, boolean notifySpecial) {
+        PropertyEntry entry = factory.createPropertyEntry(this, propName);
+        properties.add(entry);
+
+        // if property-name is jcr:uuid or jcr:mixin this affects this entry
+        // and the attached nodeState.
+        if (notifySpecial && StateUtility.isUuidOrMixin(propName)) {
+            notifyUUIDorMIXINModified(entry);
+        }
+        return entry;
+    }
+
+    /**
+     *
+     * @param childEntry
+     */
+    void internalRemoveChildEntry(HierarchyEntry childEntry) {
+        if (childEntry.denotesNode()) {
+            if (childNodeEntries.remove((NodeEntry) childEntry) == null) {
+                childNodeAttic.remove((NodeEntryImpl) childEntry);
+            }
+        } else {
+            Name propName = childEntry.getName();
+            PropertyEntry atticEntry = (PropertyEntry) propertiesInAttic.get(propName);
+            if (atticEntry == null) {
+                properties.remove((PropertyEntry) childEntry);
+            } else if (atticEntry == childEntry) {
+                propertiesInAttic.remove(propName);
+            } // else: no such prop-entry. should not get here
+
+            // special properties
+            if (StateUtility.isUuidOrMixin(propName)) {
+                notifyUUIDorMIXINRemoved(propName);
+            }
+        }
     }
 
     /**
@@ -1038,7 +1026,12 @@
      * {@link #getWorkspaceName()} and {@link #getWorkspaceIndex()}, respectively.
      */
     boolean matches(Name oldName, int oldIndex) {
-        return getWorkspaceName().equals(oldName) && getWorkspaceIndex() == oldIndex;
+        try {
+            return getName(true).equals(oldName) && getIndex(true) == oldIndex;
+        } catch (RepositoryException e) {
+            // should not get here
+            return false;
+        }
     }
 
     /**
@@ -1047,23 +1040,33 @@
      * {@link #getWorkspaceName()}.
      */
     boolean matches(Name oldName) {
-        return getWorkspaceName().equals(oldName);
+        return getName(true).equals(oldName);
     }
 
 
-    private Name getWorkspaceName() {
-        if (revertInfo != null) {
+    private Name getName(boolean wspName) {
+        if (wspName && revertInfo != null) {
             return revertInfo.oldName;
         } else {
-            return getName();
+            return name;
         }
     }
 
-    private int getWorkspaceIndex() {
-        if (revertInfo != null) {
+    private int getIndex(boolean wspIndex) throws InvalidItemStateException, RepositoryException {
+        if (parent == null) {
+            // the root state may never have siblings
+            return Path.INDEX_DEFAULT;
+        }
+
+        if (wspIndex && revertInfo != null) {
             return revertInfo.oldIndex;
         } else {
-            return getIndex();
+            NodeState state = (NodeState) internalGetItemState();
+            if (state == null || !state.hasDefinition() || state.getDefinition().allowsSameNameSiblings()) {
+                return parent.getChildIndex(this, wspIndex);
+            } else {
+                return Path.INDEX_DEFAULT;
+            }
         }
     }
 
@@ -1083,7 +1086,6 @@
     }
 
     /**
-     *
      * @param childId
      * @return the entry or <code>null</code> if building the corresponding
      * <code>PropertyState</code> failed with <code>ItemNotFoundException</code>.
@@ -1120,9 +1122,7 @@
         } else {
             child = lookupPropertyEntry(childName);
         }
-        // a NEW hierarchyEntry may never be affected by an external modification
-        // -> return null.
-        return (child == null || child.getStatus() == Status.NEW) ? null : child;
+        return child;
     }
 
     private NodeEntry lookupNodeEntry(String uniqueChildId, Name childName, int index) {
@@ -1244,27 +1244,60 @@
      * Returns the index of the given <code>NodeEntry</code>.
      *
      * @param cne  the <code>NodeEntry</code> instance.
+     * @param wspIndex if <code>true</code> transiently removed siblings are respected.
      * @return the index of the child node entry.
      * @throws ItemNotFoundException if the given entry isn't a valid child of
      * this <code>NodeEntry</code>.
      */
-    private int getChildIndex(NodeEntry cne) throws ItemNotFoundException, RepositoryException {
-        List sns = childNodeEntries.get(cne.getName());
-        // index is one based
-        int index = Path.INDEX_DEFAULT;
-        for (Iterator it = sns.iterator(); it.hasNext(); ) {
-            NodeEntry entry = (NodeEntry) it.next();
-            if (entry == cne) {
-                return index;
+    private int getChildIndex(NodeEntry cne, boolean wspIndex) throws ItemNotFoundException, RepositoryException {
+        List sns = new ArrayList(childNodeEntries.get(cne.getName()));
+
+        if (wspIndex) {
+            List atticSiblings = childNodeAttic.get(cne.getName());
+            for (Iterator it = atticSiblings.iterator(); it.hasNext();) {
+                NodeEntryImpl sibl = (NodeEntryImpl) it.next();
+                if (sibl.revertInfo != null) {
+                    sns.add(sibl.revertInfo.oldIndex - 1, sibl);
+                } else {
+                    log.error("Sibling in attic doesn't have revertInfo....");
+                }
             }
-            // skip entries that belong to removed or invalid states.
-            // NOTE, that in this case the nodestate must be available from the cne.
-            if (EntryValidation.isValidNodeEntry(entry)) {
-                index++;
+        }
+
+        if (sns.isEmpty()) {
+            // the given node entry is not connected with his parent any more
+            // -> throw
+            String msg = "NodeEntry " + cne.getName() + " is disconnected from its parent -> remove.";
+            cne.remove();
+            throw new InvalidItemStateException(msg);
+
+        } else if (sns.size() == 1) {
+            // no siblings -> simply return the default index.
+            return Path.INDEX_DEFAULT;
+
+        } else {
+            // siblings exist.
+            int index = Path.INDEX_DEFAULT;
+            for (Iterator it = sns.iterator(); it.hasNext(); ) {
+                NodeEntry entry = (NodeEntry) it.next();
+                if (entry == cne) {
+                    return index;
+                }
+                // for wsp index ignore all transiently added items.
+                // otherwise: skip entries that belong to removed or invalid states.
+                // NOTE, that in this case the nodestate must be available from the cne.
+                boolean isValid = (wspIndex) ?
+                        EntryValidation.isValidWorkspaceNodeEntry(entry) :
+                        EntryValidation.isValidNodeEntry(entry);
+                if (isValid) {
+                    index++;
+                }
             }
+            // not found, since child entries are only weakly connected to the
+            // LinkNode in ChildNodeEntries, equality may not determine the
+            // correct matching entry -> return default index.
+            return Path.INDEX_DEFAULT;
         }
-        // not found (should not occur)
-        throw new ItemNotFoundException("No valid child entry for NodeEntry " + cne);
     }
 
     /**
@@ -1285,8 +1318,14 @@
         if (childNodeAttic.contains(childName, childIndex)) {
             return true;
         }
-        // in case of reordered/moved SNSs we also have to look for a child
-        // entry, which hold the given index before
+        // special treatment for potentially moved/reordered/removed sns
+        // TODO: check again
+        if (childIndex > Path.INDEX_DEFAULT) {
+            List siblingsInAttic = childNodeAttic.get(childName);
+            if (siblings.size() < childIndex && childIndex <= siblings.size() + siblingsInAttic.size()) {
+                return true;
+            }
+        }
         if (getStatus() == Status.EXISTING_MODIFIED) {
             for (Iterator it = siblings.iterator(); it.hasNext();) {
                 NodeEntryImpl child = (NodeEntryImpl) it.next();
@@ -1301,198 +1340,217 @@
      * 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());
+    private void createRevertInfo() throws RepositoryException {
+        if (revertInfo == null && getStatus() != Status.NEW) {
+            revertInfo = new RevertInfo();
         }
     }
 
-    /**
-     * Special handling for MOVE and REORDER with same-name-siblings
-     */
-    private void createSiblingRevertInfos() throws RepositoryException {
-        if (revertInfo != null) {
-            return; // nothing to do
+    private void complete(AddNode operation) throws RepositoryException {
+        if (operation.getParentState().getHierarchyEntry() != this) {
+            throw new IllegalArgumentException();
+        }
+
+        for (Iterator it = operation.getAddedStates().iterator(); it.hasNext();) {
+            HierarchyEntry he = ((ItemState) it.next()).getHierarchyEntry();
+            if (he.getStatus() == Status.NEW) {
+                switch (operation.getStatus()) {
+                    case Operation.STATUS_PERSISTED:
+                        ((HierarchyEntryImpl) he).internalGetItemState().setStatus(Status.EXISTING);
+                        he.invalidate(false);
+                        break;
+                    case Operation.STATUS_UNDO:
+                        he.revert();
+                        break;
+                    default: // ignore
+                }
+            } // entry isn't NEW any more -> ignore
+        }
+    }
+
+    private void complete(AddProperty operation) throws RepositoryException {
+        if (operation.getParentState().getHierarchyEntry() != this) {
+            throw new IllegalArgumentException();
+        }
+        PropertyEntry pe = getPropertyEntry(operation.getPropertyName());
+        if (pe != null && pe.getStatus() == Status.NEW) {
+            switch (operation.getStatus()) {
+                case Operation.STATUS_PERSISTED:
+                    // for autocreated/protected props, mark to be reloaded
+                    // upon next access.
+                    PropertyState addedState = (PropertyState) ((PropertyEntryImpl) pe).internalGetItemState();
+                    addedState.setStatus(Status.EXISTING);
+                    QPropertyDefinition pd = addedState.getDefinition();
+                    if (pd.isAutoCreated() || pd.isProtected()) {
+                        pe.invalidate(true);
+                    } // else: assume added property is up to date.
+                    break;
+                case Operation.STATUS_UNDO:
+                    pe.revert();
+                    break;
+                default: // ignore
+            }
+        } // else: no such prop entry or entry has already been persisted
+          //       e.g due to external modifications merged into this NodeEntry.
+    }
+
+    private void complete(Remove operation) throws RepositoryException {
+        HierarchyEntry rmEntry = operation.getRemoveState().getHierarchyEntry();
+        if (rmEntry.getParent() != this) {
+            throw new IllegalArgumentException();
         }
-        // for SNSs without UniqueID remember original index in order to
-        // be able to build the workspaceID TODO: improve
-        List sns = parent.getCompleteChildNodeEntries().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();
+        switch (operation.getStatus()) {
+            case Operation.STATUS_PERSISTED:
+                if (Status.isTerminal(rmEntry.getStatus())) {
+                    log.debug("Removal of State " + rmEntry + " has already been completed.");
                 }
+                rmEntry.remove();
+                break;
+            case Operation.STATUS_UNDO:
+                if (!rmEntry.denotesNode()) {
+                    Name propName = ((PropertyEntry) rmEntry).getName();
+                    if (propertiesInAttic.containsKey(propName)) {
+                        properties.add((PropertyEntry) propertiesInAttic.remove(propName));
+                    } // else: propEntry has never been moved to the attic (see 'addPropertyEntry')
+                }
+                rmEntry.revert();
+            default: // ignore
+        }
+
+    }
+
+    private void complete(SetMixin operation) throws RepositoryException {
+        if (operation.getNodeState().getHierarchyEntry() != this) {
+            throw new IllegalArgumentException();
+        }
+        PropertyEntry pe = getPropertyEntry(NameConstants.JCR_MIXINTYPES);
+        if (pe != null) {
+            PropertyState pState = pe.getPropertyState();
+            switch (operation.getStatus()) {
+                case Operation.STATUS_PERSISTED:
+                    Name[] mixins = StateUtility.getMixinNames(pState);
+                    getNodeState().setMixinTypeNames(mixins);
+                    if (pState.getStatus() == Status.NEW || pState.getStatus() == Status.EXISTING_MODIFIED) {
+                        pState.setStatus(Status.EXISTING);
+                    }
+                    break;
+                case Operation.STATUS_UNDO:
+                    pe.revert();
+                    break;
+                default: // ignore
             }
+        } // else: no such prop-Entry (should not occur)
+    }
+
+    private void complete(ReorderNodes operation) throws RepositoryException {
+        HierarchyEntry he = operation.getInsertNode().getHierarchyEntry();
+        if (he != this) {
+            throw new IllegalArgumentException();
+        }
+        // NOTE: if reorder occured in combination with a 'move' the clean-up
+        // of the revertInfo is postponed until {@link #complete(Move)}.
+        switch (operation.getStatus()) {
+            case Operation.STATUS_PERSISTED:
+                if (revertInfo != null && !revertInfo.isMoved()) {
+                    revertInfo.dispose(true);
+                }
+                break;
+            case Operation.STATUS_UNDO:
+                if (he.getStatus() == Status.NEW) {
+                    he.revert();
+                } else if (revertInfo != null && !revertInfo.isMoved()) {
+                    revertInfo.dispose(false);
+                }
+                break;
+            default: // ignore
         }
     }
 
-    /**
-     * Revert a transient move and reordering of child entries
-     */
-    private void revertTransientChanges() throws RepositoryException {
-        if (revertInfo == null) {
-            return; // nothing to do
+    private void complete(Move operation) throws RepositoryException {
+        HierarchyEntry he = operation.getSourceState().getHierarchyEntry();
+        if (he != this) {
+            throw new IllegalArgumentException();
         }
+        switch (operation.getStatus()) {
+            case Operation.STATUS_PERSISTED:
+                if (getStatus() != Status.NEW && revertInfo != null) {
+                    revertInfo.oldParent.childNodeAttic.remove(this);
+                    revertInfo.dispose(true);
+                }
+                // and mark the moved state existing
+                // internalGetItemState().setStatus(Status.EXISTING);
+                break;
+            case Operation.STATUS_UNDO:
+                if (getStatus() == Status.NEW) {
+                    revert();
+                } else if (revertInfo != null) {
+                    revertMove();
+                    revertInfo.dispose(false);
+                }
+                break;
+            default: // ignore
+        }
+    }
 
-        if (isTransientlyMoved())  {
+    private void revertMove() {
+        NodeEntryImpl oldParent = revertInfo.oldParent;
+        if (oldParent == parent) {
+            // simple renaming
+            parent.childNodeEntries.remove(this);
+        } else {
             // move NodeEntry back to its original parent
-            // TODO improve for simple renaming
             parent.childNodeEntries.remove(this);
-            revertInfo.oldParent.childNodeAttic.remove(this);
+            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);
-            }
+            parent = oldParent;
         }
-        // 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
-        // root entry does not have oldParent
-        if (revertInfo.oldParent != null) {
-            revertInfo.oldParent.childNodeAttic.remove(this);
-        }
-        revertInfo.dispose();
-        revertInfo = null;
+        // now restore moved entry with the old name and index and re-add
+        // it to its original parent
+        name = revertInfo.oldName;
+        parent.childNodeEntries.add(this, revertInfo.oldIndex, revertInfo.oldSuccessor);
     }
 
     //--------------------------------------------------------< 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).
+     * Upon move or reorder of this entry the original hierarchy information is
+     * store in the RevertInfo for later operation undo and in order to be able
+     * to build the workspace id / path.
      */
-    private class RevertInfo implements ItemStateLifeCycleListener {
+    private class RevertInfo {
 
         private final NodeEntryImpl oldParent;
         private final Name oldName;
         private final int oldIndex;
+        private final NodeEntryImpl oldSuccessor;
 
-        private Map reorderedChildren;
-
-        private RevertInfo(NodeEntryImpl oldParent, Name 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.getName());
-                    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 RevertInfo() throws InvalidItemStateException, RepositoryException {
+            this.oldParent = parent;
+            this.oldName = name;
+            this.oldIndex = getIndex();
+            this.oldSuccessor = (NodeEntryImpl) ((ChildNodeEntriesImpl) parent.childNodeEntries).getNext(NodeEntryImpl.this);
         }
 
         private boolean isMoved() {
             return oldParent != getParent() || !getName().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
+        private void dispose(boolean persisted) {
+            if (!persisted) {
+                NodeEntryImpl ne = NodeEntryImpl.this;
+                ChildNodeEntriesImpl parentCNEs = (ChildNodeEntriesImpl) parent.childNodeEntries;
+                parentCNEs.reorder(ne, revertInfo.oldSuccessor);
                 try {
-                    child.revert();
+                    if (oldIndex != ne.getIndex()) {
+                        // TODO: TOBEFIXED
+                        log.warn("Reverting didn't restore the correct index.");
+                    }
                 } catch (RepositoryException e) {
-                    log.error("Internal error", e);
-                    return false;
+                    log.warn("Unable to calculate index.", e.getMessage());
                 }
             }
-            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
-                    try {
-                        revertTransientChanges();
-                    } catch (RepositoryException e) {
-                        log.warn("Internal error", e);
-                    }
-                    break;
-            }
+            revertInfo = null;
         }
     }
 }

Modified: jackrabbit/trunk/jackrabbit-jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/hierarchy/PropertyEntry.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/hierarchy/PropertyEntry.java?rev=704361&r1=704360&r2=704361&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit-jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/hierarchy/PropertyEntry.java (original)
+++ jackrabbit/trunk/jackrabbit-jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/hierarchy/PropertyEntry.java Tue Oct 14 00:48:22 2008
@@ -21,6 +21,7 @@
 
 import javax.jcr.RepositoryException;
 import javax.jcr.ItemNotFoundException;
+import javax.jcr.InvalidItemStateException;
 
 /**
  * <code>PropertyEntry</code>...
@@ -30,7 +31,7 @@
     /**
      * @return the <code>NodeId</code> of this child node entry.
      */
-    public PropertyId getId();
+    public PropertyId getId() throws InvalidItemStateException, RepositoryException;
 
     /**
      * Returns the ID that must be used for resolving this entry OR loading its
@@ -41,7 +42,7 @@
      * @return
      * @see #getId()
      */
-    public PropertyId getWorkspaceId();
+    public PropertyId getWorkspaceId() throws InvalidItemStateException, RepositoryException;
 
     /**
      * @return the referenced <code>PropertyState</code>.

Modified: jackrabbit/trunk/jackrabbit-jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/hierarchy/PropertyEntryImpl.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/hierarchy/PropertyEntryImpl.java?rev=704361&r1=704360&r2=704361&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit-jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/hierarchy/PropertyEntryImpl.java (original)
+++ jackrabbit/trunk/jackrabbit-jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/hierarchy/PropertyEntryImpl.java Tue Oct 14 00:48:22 2008
@@ -22,9 +22,12 @@
 import org.apache.jackrabbit.jcr2spi.state.PropertyState;
 import org.apache.jackrabbit.jcr2spi.state.ItemState;
 import org.apache.jackrabbit.jcr2spi.state.Status;
+import org.apache.jackrabbit.jcr2spi.operation.Operation;
+import org.apache.jackrabbit.jcr2spi.operation.SetPropertyValue;
 
 import javax.jcr.RepositoryException;
 import javax.jcr.ItemNotFoundException;
+import javax.jcr.InvalidItemStateException;
 
 /**
  * <code>PropertyEntryImpl</code> implements a reference to a property state.
@@ -78,14 +81,14 @@
     /**
      * @see PropertyEntry#getId()
      */
-    public PropertyId getId() {
+    public PropertyId getId() throws InvalidItemStateException, RepositoryException {
         return factory.getIdFactory().createPropertyId(parent.getId(), getName());
     }
 
     /**
      * @see PropertyEntry#getWorkspaceId()
      */
-    public PropertyId getWorkspaceId() {
+    public PropertyId getWorkspaceId() throws InvalidItemStateException, RepositoryException {
         return factory.getIdFactory().createPropertyId(parent.getWorkspaceId(), getName());
     }
 
@@ -100,7 +103,6 @@
     /**
      * Returns false.
      *
-     * @inheritDoc
      * @see HierarchyEntry#denotesNode()
      */
     public boolean denotesNode() {
@@ -108,13 +110,49 @@
     }
 
     /**
-     * @inheritDoc
      * @see HierarchyEntry#remove()
      */
     public void remove() {
-        removeEntry(this);
-        if (getStatus() != Status.STALE_DESTROYED) {
-            parent.internalRemovePropertyEntry(getName());
+        ItemState state = internalGetItemState();
+        int status = getStatus();
+        if (state != null) {
+            if (status == Status.EXISTING_MODIFIED) {
+                state.setStatus(Status.STALE_DESTROYED);
+            } else {
+                state.setStatus(Status.REMOVED);
+                parent.internalRemoveChildEntry(this);
+            }
+        } else {
+            // unresolved
+            parent.internalRemoveChildEntry(this);
+        }
+    }
+
+    /**
+     * @see HierarchyEntry#complete(Operation)
+     */
+    public void complete(Operation operation) throws RepositoryException {
+        if (!(operation instanceof SetPropertyValue)) {
+            throw new IllegalArgumentException();
+        }
+        SetPropertyValue op = (SetPropertyValue) operation;
+        if (op.getPropertyState().getHierarchyEntry() != this) {
+            throw new IllegalArgumentException();
+        }
+        switch (operation.getStatus()) {
+            case Operation.STATUS_PERSISTED:
+                /*
+                NOTE: Property can only be the changelog target, if it was
+                      existing and has been modified. removal, add and implicit modification
+                      of protected properties must be persisted by save on parent.
+                */
+                op.getPropertyState().setStatus(Status.EXISTING);
+                break;
+            case Operation.STATUS_UNDO:
+                revert();
+                break;
+            default:
+                // ignore
         }
     }
 }

Modified: jackrabbit/trunk/jackrabbit-jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/hierarchy/UniqueIdResolver.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/hierarchy/UniqueIdResolver.java?rev=704361&r1=704360&r2=704361&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit-jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/hierarchy/UniqueIdResolver.java (original)
+++ jackrabbit/trunk/jackrabbit-jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/hierarchy/UniqueIdResolver.java Tue Oct 14 00:48:22 2008
@@ -121,11 +121,18 @@
      * @see EntryFactory.NodeEntryListener#entryCreated(NodeEntry)
      */
     public void entryCreated(NodeEntry entry) {
-        String uniqueID = entry.getUniqueID();
-        if (uniqueID != null) {
-            Object previous = lookUp.put(uniqueID, entry);
-            if (previous != null) {
-                ((NodeEntry) previous).remove();
+        synchronized (lookUp) {
+            String uniqueID = entry.getUniqueID();
+            if (uniqueID != null) {
+                // get an previous entry first and remove it before adding the
+                // new one. otherwise the newly added entry will be removed
+                // again upon removal of the original entry.
+                Object previous = lookUp.get(uniqueID);
+                if (previous != null) {
+                    lookUp.remove(uniqueID);
+                    ((NodeEntry) previous).remove();
+                }
+                lookUp.put(uniqueID, entry);
             }
         }
     }
@@ -136,7 +143,13 @@
     public void uniqueIdChanged(NodeEntry entry, String previousUniqueID) {
         synchronized (lookUp) {
             if (previousUniqueID != null) {
-                lookUp.remove(previousUniqueID);
+                Object previous = lookUp.get(previousUniqueID);
+                if (previous != entry) {
+                    // changed entry was not in this cache -> return.
+                    return;
+                } else {
+                    lookUp.remove(previousUniqueID);
+                }
             }
             String uniqueID = entry.getUniqueID();
             if (uniqueID != null) {

Modified: jackrabbit/trunk/jackrabbit-jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/operation/AbstractCopy.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/operation/AbstractCopy.java?rev=704361&r1=704360&r2=704361&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit-jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/operation/AbstractCopy.java (original)
+++ jackrabbit/trunk/jackrabbit-jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/operation/AbstractCopy.java Tue Oct 14 00:48:22 2008
@@ -80,6 +80,8 @@
      * @see Operation#persisted()
      */
     public void persisted() {
+        assert status == STATUS_PENDING;
+        status = STATUS_PERSISTED;
         destParentState.getHierarchyEntry().invalidate(false);
     }
 
@@ -88,11 +90,11 @@
         return srcWorkspaceName;
     }
 
-    public NodeId getNodeId() {
+    public NodeId getNodeId() throws RepositoryException {
         return srcState.getNodeId();
     }
 
-    public NodeId getDestinationParentId() {
+    public NodeId getDestinationParentId() throws RepositoryException {
         return destParentState.getNodeId();
     }
 

Modified: jackrabbit/trunk/jackrabbit-jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/operation/AbstractOperation.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/operation/AbstractOperation.java?rev=704361&r1=704360&r2=704361&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit-jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/operation/AbstractOperation.java (original)
+++ jackrabbit/trunk/jackrabbit-jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/operation/AbstractOperation.java Tue Oct 14 00:48:22 2008
@@ -38,6 +38,7 @@
      * The collection of affected ItemIds.
      */
     private final Collection affectedStates = new ArrayList();
+    protected int status;
 
     /**
      * Returns the name of the class
@@ -57,6 +58,18 @@
     }
 
     /**
+     * @inheritDoc
+     */
+    public void undo() throws RepositoryException {
+        assert status == STATUS_PENDING;
+        throw new UnsupportedOperationException("Undo not supported.");
+    }
+
+    public int getStatus() {
+        return status;
+    }
+
+    /**
      * Adds an affected <code>ItemState</code>.
      *
      * @param affectedState the <code>ItemState</code>s of the affected item.
@@ -81,4 +94,15 @@
         }
         return (NodeState) itemState;
     }
+
+    /**
+     * Asserts that the NodeEntry of the given parent state has it's child node
+     * entries loaded.
+     *
+     * @param parentState
+     * @throws RepositoryException
+     */
+    protected static void assertChildNodeEntries(NodeState parentState) throws RepositoryException {
+        parentState.getNodeEntry().getNodeEntries();
+    }
 }
\ No newline at end of file

Modified: jackrabbit/trunk/jackrabbit-jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/operation/AddLabel.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/operation/AddLabel.java?rev=704361&r1=704360&r2=704361&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit-jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/operation/AddLabel.java (original)
+++ jackrabbit/trunk/jackrabbit-jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/operation/AddLabel.java Tue Oct 14 00:48:22 2008
@@ -16,22 +16,22 @@
  */
 package org.apache.jackrabbit.jcr2spi.operation;
 
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.apache.jackrabbit.spi.Name;
-import org.apache.jackrabbit.spi.Path;
-import org.apache.jackrabbit.jcr2spi.state.NodeState;
 import org.apache.jackrabbit.jcr2spi.hierarchy.NodeEntry;
+import org.apache.jackrabbit.jcr2spi.state.NodeState;
+import org.apache.jackrabbit.spi.Name;
 import org.apache.jackrabbit.spi.NodeId;
+import org.apache.jackrabbit.spi.Path;
 import org.apache.jackrabbit.spi.commons.name.NameConstants;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 
-import javax.jcr.RepositoryException;
 import javax.jcr.AccessDeniedException;
 import javax.jcr.ItemExistsException;
+import javax.jcr.RepositoryException;
 import javax.jcr.UnsupportedRepositoryOperationException;
-import javax.jcr.version.VersionException;
 import javax.jcr.nodetype.ConstraintViolationException;
 import javax.jcr.nodetype.NoSuchNodeTypeException;
+import javax.jcr.version.VersionException;
 
 /**
  * <code>AddLabel</code>...
@@ -66,6 +66,7 @@
      * @throws VersionException
      */
     public void accept(OperationVisitor visitor) throws RepositoryException, ConstraintViolationException, AccessDeniedException, ItemExistsException, NoSuchNodeTypeException, UnsupportedRepositoryOperationException, VersionException {
+        assert status == STATUS_PENDING;
         visitor.visit(this);
     }
 
@@ -77,6 +78,8 @@
      * @see Operation#persisted()
      */
     public void persisted() {
+        assert status == STATUS_PENDING;
+        status = STATUS_PERSISTED;
         try {
             NodeEntry vhEntry = (NodeEntry) versionHistoryState.getHierarchyEntry();
             NodeEntry lnEntry = vhEntry.getNodeEntry(NameConstants.JCR_VERSIONLABELS, Path.INDEX_DEFAULT);
@@ -88,11 +91,11 @@
         }
     }
     //----------------------------------------< Access Operation Parameters >---
-    public NodeId getVersionHistoryId() {
+    public NodeId getVersionHistoryId() throws RepositoryException {
         return versionHistoryState.getNodeEntry().getWorkspaceId();
     }
 
-    public NodeId getVersionId() {
+    public NodeId getVersionId() throws RepositoryException {
         return versionState.getNodeEntry().getWorkspaceId();
     }
 

Modified: jackrabbit/trunk/jackrabbit-jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/operation/AddNode.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/operation/AddNode.java?rev=704361&r1=704360&r2=704361&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit-jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/operation/AddNode.java (original)
+++ jackrabbit/trunk/jackrabbit-jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/operation/AddNode.java Tue Oct 14 00:48:22 2008
@@ -19,17 +19,19 @@
 import org.apache.jackrabbit.jcr2spi.state.NodeState;
 import org.apache.jackrabbit.spi.Name;
 import org.apache.jackrabbit.spi.NodeId;
-import org.slf4j.LoggerFactory;
 import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 
-import javax.jcr.nodetype.ConstraintViolationException;
-import javax.jcr.nodetype.NoSuchNodeTypeException;
+import javax.jcr.AccessDeniedException;
 import javax.jcr.ItemExistsException;
 import javax.jcr.RepositoryException;
-import javax.jcr.AccessDeniedException;
 import javax.jcr.UnsupportedRepositoryOperationException;
-import javax.jcr.version.VersionException;
 import javax.jcr.lock.LockException;
+import javax.jcr.nodetype.ConstraintViolationException;
+import javax.jcr.nodetype.NoSuchNodeTypeException;
+import javax.jcr.version.VersionException;
+import java.util.ArrayList;
+import java.util.List;
 
 /**
  * <code>AddNode</code>...
@@ -44,7 +46,10 @@
     private final Name nodeTypeName;
     private final String uuid;
 
-    private AddNode(NodeState parentState, Name nodeName, Name nodeTypeName, String uuid) {
+    private List addedStates = new ArrayList();
+
+    private AddNode(NodeState parentState, Name nodeName, Name nodeTypeName, String uuid)
+            throws RepositoryException {
         this.parentId = parentState.getNodeId();
         this.parentState = parentState;
         this.nodeName = nodeName;
@@ -60,6 +65,7 @@
      * @param visitor
      */
     public void accept(OperationVisitor visitor) throws LockException, ConstraintViolationException, AccessDeniedException, ItemExistsException, NoSuchNodeTypeException, UnsupportedRepositoryOperationException, VersionException, RepositoryException {
+        assert status == STATUS_PENDING;
         visitor.visit(this);
     }
 
@@ -68,9 +74,21 @@
      *
      * @see Operation#persisted()
      */
-    public void persisted() {
-        throw new UnsupportedOperationException("persisted() not implemented for transient modification.");
+    public void persisted() throws RepositoryException {
+        assert status == STATUS_PENDING;
+        status = STATUS_PERSISTED;
+        parentState.getHierarchyEntry().complete(this);
+    }
+
+    /**
+     * @see Operation#undo()
+     */
+    public void undo() throws RepositoryException {
+        assert status == STATUS_PENDING;
+        status = STATUS_UNDO;
+        parentState.getHierarchyEntry().complete(this);
     }
+
     //----------------------------------------< Access Operation Parameters >---
     public NodeId getParentId() {
         return parentId;
@@ -92,10 +110,29 @@
         return uuid;
     }
 
-    //------------------------------------------------------------< Factory >---
+    public void addedState(List newStates) {
+        addedStates.addAll(newStates);
+    }
 
+    public List getAddedStates() {
+        return addedStates;
+    }
+
+    //------------------------------------------------------------< Factory >---
+    /**
+     *
+     * @param parentState
+     * @param nodeName
+     * @param nodeTypeName
+     * @param uuid
+     * @return
+     */
     public static Operation create(NodeState parentState, Name nodeName,
-                                   Name nodeTypeName, String uuid) {
+                                   Name nodeTypeName, String uuid) throws RepositoryException {
+        // make sure the parent hierarchy entry has its child entries loaded
+        // in order to be able to detect conflicts.
+        assertChildNodeEntries(parentState);
+        
         AddNode an = new AddNode(parentState, nodeName, nodeTypeName, uuid);
         return an;
     }

Modified: jackrabbit/trunk/jackrabbit-jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/operation/AddProperty.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/operation/AddProperty.java?rev=704361&r1=704360&r2=704361&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit-jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/operation/AddProperty.java (original)
+++ jackrabbit/trunk/jackrabbit-jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/operation/AddProperty.java Tue Oct 14 00:48:22 2008
@@ -44,7 +44,9 @@
 
     private final QPropertyDefinition definition;
 
-    private AddProperty(NodeState parentState, Name propName, int propertyType, QValue[] values, QPropertyDefinition definition) {
+    private AddProperty(NodeState parentState, Name propName,
+                        int propertyType, QValue[] values,
+                        QPropertyDefinition definition) throws RepositoryException {
         this.parentId = parentState.getNodeId();
         this.parentState = parentState;
         this.propertyName = propName;
@@ -61,17 +63,27 @@
      * @param visitor
      */
     public void accept(OperationVisitor visitor) throws ValueFormatException, LockException, ConstraintViolationException, AccessDeniedException, ItemExistsException, UnsupportedRepositoryOperationException, VersionException, RepositoryException {
+        assert status == STATUS_PENDING;
         visitor.visit(this);
     }
 
     /**
-     * Throws UnsupportedOperationException
-     *
      * @see Operation#persisted()
      */
-    public void persisted() {
-        throw new UnsupportedOperationException("persisted() not implemented for transient modification.");
+    public void persisted() throws RepositoryException {
+        assert status == STATUS_PENDING;
+        status = STATUS_PERSISTED;
+        parentState.getHierarchyEntry().complete(this);
+    }
+
+    /**
+     * @see Operation#undo()
+     */
+    public void undo() throws RepositoryException {
+        status = STATUS_UNDO;
+        parentState.getHierarchyEntry().complete(this);
     }
+    
     //----------------------------------------< Access Operation Parameters >---
     public NodeId getParentId() {
         return parentId;
@@ -112,7 +124,7 @@
      * @return
      */
     public static Operation create(NodeState parentState, Name propName, int propertyType,
-                                   QPropertyDefinition def, QValue[] values) {
+                                   QPropertyDefinition def, QValue[] values) throws RepositoryException {
         AddProperty ap = new AddProperty(parentState, propName, propertyType, values, def);
         return ap;
     }

Modified: jackrabbit/trunk/jackrabbit-jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/operation/Checkin.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/operation/Checkin.java?rev=704361&r1=704360&r2=704361&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit-jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/operation/Checkin.java (original)
+++ jackrabbit/trunk/jackrabbit-jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/operation/Checkin.java Tue Oct 14 00:48:22 2008
@@ -16,21 +16,21 @@
  */
 package org.apache.jackrabbit.jcr2spi.operation;
 
-import org.apache.jackrabbit.jcr2spi.state.NodeState;
-import org.apache.jackrabbit.jcr2spi.version.VersionManager;
 import org.apache.jackrabbit.jcr2spi.hierarchy.NodeEntry;
 import org.apache.jackrabbit.jcr2spi.hierarchy.PropertyEntry;
+import org.apache.jackrabbit.jcr2spi.state.NodeState;
+import org.apache.jackrabbit.jcr2spi.version.VersionManager;
 import org.apache.jackrabbit.spi.NodeId;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-import javax.jcr.RepositoryException;
 import javax.jcr.AccessDeniedException;
 import javax.jcr.ItemExistsException;
+import javax.jcr.RepositoryException;
 import javax.jcr.UnsupportedRepositoryOperationException;
-import javax.jcr.version.VersionException;
 import javax.jcr.nodetype.ConstraintViolationException;
 import javax.jcr.nodetype.NoSuchNodeTypeException;
+import javax.jcr.version.VersionException;
 import java.util.Iterator;
 
 /**
@@ -57,6 +57,7 @@
      * @see Operation#accept(OperationVisitor)
      */
     public void accept(OperationVisitor visitor) throws RepositoryException, ConstraintViolationException, AccessDeniedException, ItemExistsException, NoSuchNodeTypeException, UnsupportedRepositoryOperationException, VersionException {
+        assert status == STATUS_PENDING;
         visitor.visit(this);
     }
 
@@ -66,10 +67,15 @@
      * @see Operation#persisted()
      */
     public void persisted() {
+        assert status == STATUS_PENDING;
+        status = STATUS_PERSISTED;
         try {
-            mgr.getVersionHistoryEntry(nodeState).invalidate(true);
+            NodeEntry vhe = mgr.getVersionHistoryEntry(nodeState);
+            if (vhe != null) {
+                vhe.invalidate(true);
+            }
         } catch (RepositoryException e) {
-            log.warn("Internal error", e);
+            log.debug("Failed to access Version history entry -> skip invalidation.", e);
         }
         Iterator entries = ((NodeEntry) nodeState.getHierarchyEntry()).getPropertyEntries();
         while (entries.hasNext()) {
@@ -84,7 +90,7 @@
      *
      * @return The nodeState to be checked in.
      */
-    public NodeId getNodeId() {
+    public NodeId getNodeId() throws RepositoryException {
         return nodeState.getNodeEntry().getWorkspaceId();
     }
 

Modified: jackrabbit/trunk/jackrabbit-jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/operation/Checkout.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/operation/Checkout.java?rev=704361&r1=704360&r2=704361&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit-jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/operation/Checkout.java (original)
+++ jackrabbit/trunk/jackrabbit-jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/operation/Checkout.java Tue Oct 14 00:48:22 2008
@@ -16,21 +16,21 @@
  */
 package org.apache.jackrabbit.jcr2spi.operation;
 
-import org.apache.jackrabbit.jcr2spi.state.NodeState;
-import org.apache.jackrabbit.jcr2spi.version.VersionManager;
 import org.apache.jackrabbit.jcr2spi.hierarchy.NodeEntry;
 import org.apache.jackrabbit.jcr2spi.hierarchy.PropertyEntry;
+import org.apache.jackrabbit.jcr2spi.state.NodeState;
+import org.apache.jackrabbit.jcr2spi.version.VersionManager;
 import org.apache.jackrabbit.spi.NodeId;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-import javax.jcr.RepositoryException;
 import javax.jcr.AccessDeniedException;
 import javax.jcr.ItemExistsException;
+import javax.jcr.RepositoryException;
 import javax.jcr.UnsupportedRepositoryOperationException;
-import javax.jcr.version.VersionException;
 import javax.jcr.nodetype.ConstraintViolationException;
 import javax.jcr.nodetype.NoSuchNodeTypeException;
+import javax.jcr.version.VersionException;
 import java.util.Iterator;
 
 /**
@@ -51,6 +51,7 @@
 
     //----------------------------------------------------------< Operation >---
     public void accept(OperationVisitor visitor) throws RepositoryException, ConstraintViolationException, AccessDeniedException, ItemExistsException, NoSuchNodeTypeException, UnsupportedRepositoryOperationException, VersionException {
+        assert status == STATUS_PENDING;
         visitor.visit(this);
     }
 
@@ -60,10 +61,15 @@
      * @see Operation#persisted()
      */
     public void persisted() {
+        assert status == STATUS_PENDING;
+        status = STATUS_PERSISTED;
         try {
-            mgr.getVersionHistoryEntry(nodeState).invalidate(true);
+            NodeEntry vhe = mgr.getVersionHistoryEntry(nodeState);
+            if (vhe != null) {
+                vhe.invalidate(true);
+            }
         } catch (RepositoryException e) {
-            log.warn("Internal error", e);
+            log.warn("Failed to access Version history entry -> skip invalidation.", e);
         }
         // non-recursive invalidation (but including all properties)
         NodeEntry nodeEntry = (NodeEntry) nodeState.getHierarchyEntry();
@@ -80,7 +86,7 @@
      *
      * @return
      */
-    public NodeId getNodeId() {
+    public NodeId getNodeId() throws RepositoryException {
         return nodeState.getNodeEntry().getWorkspaceId();
     }
 

Modified: jackrabbit/trunk/jackrabbit-jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/operation/Clone.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/operation/Clone.java?rev=704361&r1=704360&r2=704361&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit-jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/operation/Clone.java (original)
+++ jackrabbit/trunk/jackrabbit-jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/operation/Clone.java Tue Oct 14 00:48:22 2008
@@ -55,6 +55,7 @@
      * @param visitor
      */
     public void accept(OperationVisitor visitor) throws NoSuchWorkspaceException, LockException, ConstraintViolationException, AccessDeniedException, ItemExistsException, UnsupportedRepositoryOperationException, VersionException, RepositoryException {
+        assert status == STATUS_PENDING;
         visitor.visit(this);
     }
 
@@ -62,7 +63,9 @@
      * @see Operation#persisted()
      */
     public void persisted() {
+        assert status == STATUS_PENDING;
         if (removeExisting) {
+            status = STATUS_PERSISTED;
             // invalidate the complete tree -> find root-hierarchy-entry
             HierarchyEntry he = destParentState.getHierarchyEntry();
             while (he.getParent() != null) {

Modified: jackrabbit/trunk/jackrabbit-jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/operation/Copy.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/operation/Copy.java?rev=704361&r1=704360&r2=704361&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit-jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/operation/Copy.java (original)
+++ jackrabbit/trunk/jackrabbit-jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/operation/Copy.java Tue Oct 14 00:48:22 2008
@@ -44,6 +44,7 @@
      * @param visitor
      */
     public void accept(OperationVisitor visitor) throws NoSuchWorkspaceException, LockException, ConstraintViolationException, AccessDeniedException, ItemExistsException, UnsupportedRepositoryOperationException, VersionException, RepositoryException {
+        assert status == STATUS_PENDING;
         visitor.visit(this);
     }
 

Modified: jackrabbit/trunk/jackrabbit-jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/operation/LockOperation.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/operation/LockOperation.java?rev=704361&r1=704360&r2=704361&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit-jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/operation/LockOperation.java (original)
+++ jackrabbit/trunk/jackrabbit-jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/operation/LockOperation.java Tue Oct 14 00:48:22 2008
@@ -52,6 +52,7 @@
      * @see Operation#accept(OperationVisitor)
      */
     public void accept(OperationVisitor visitor) throws RepositoryException, ConstraintViolationException, AccessDeniedException, ItemExistsException, NoSuchNodeTypeException, UnsupportedRepositoryOperationException, VersionException {
+        assert status == STATUS_PENDING;
         visitor.visit(this);
     }
 
@@ -61,12 +62,14 @@
      * @see Operation#persisted()
      */
     public void persisted() {
+        assert status == STATUS_PENDING;
+        status = STATUS_PERSISTED;
         // non-recursive invalidation
         nodeState.getHierarchyEntry().invalidate(false);
     }
 
     //----------------------------------------< Access Operation Parameters >---
-    public NodeId getNodeId() {
+    public NodeId getNodeId() throws RepositoryException {
         return nodeState.getNodeId();
     }
 

Modified: jackrabbit/trunk/jackrabbit-jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/operation/LockRefresh.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/operation/LockRefresh.java?rev=704361&r1=704360&r2=704361&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit-jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/operation/LockRefresh.java (original)
+++ jackrabbit/trunk/jackrabbit-jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/operation/LockRefresh.java Tue Oct 14 00:48:22 2008
@@ -45,6 +45,7 @@
      * @see Operation#accept(OperationVisitor)
      */
     public void accept(OperationVisitor visitor) throws RepositoryException, ConstraintViolationException, AccessDeniedException, ItemExistsException, NoSuchNodeTypeException, UnsupportedRepositoryOperationException, VersionException {
+        assert status == STATUS_PENDING;
         visitor.visit(this);
     }
 
@@ -52,12 +53,14 @@
      * @see Operation#persisted()
      */
     public void persisted() {
+        assert status == STATUS_PENDING;
+        status = STATUS_PERSISTED;
         // nothing to do.
     }
 
     //----------------------------------------< Access Operation Parameters >---
-    public NodeId getNodeId() {
-        return nodeState.getNodeId();
+    public NodeId getNodeId() throws RepositoryException {
+        return (NodeId) nodeState.getNodeId();
     }
 
     //------------------------------------------------------------< Factory >---

Modified: jackrabbit/trunk/jackrabbit-jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/operation/LockRelease.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/operation/LockRelease.java?rev=704361&r1=704360&r2=704361&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit-jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/operation/LockRelease.java (original)
+++ jackrabbit/trunk/jackrabbit-jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/operation/LockRelease.java Tue Oct 14 00:48:22 2008
@@ -48,6 +48,7 @@
      * @see Operation#accept(OperationVisitor)
      */
     public void accept(OperationVisitor visitor) throws RepositoryException, ConstraintViolationException, AccessDeniedException, ItemExistsException, NoSuchNodeTypeException, UnsupportedRepositoryOperationException, VersionException {
+        assert status == STATUS_PENDING;
         visitor.visit(this);
     }
 
@@ -58,6 +59,8 @@
      * @see Operation#persisted()
      */
     public void persisted() {
+        assert status == STATUS_PENDING;
+        status = STATUS_PERSISTED;
         // non-recursive invalidation but including all properties
         NodeEntry nodeEntry = nodeState.getNodeEntry();
         Iterator entries = nodeEntry.getPropertyEntries();
@@ -69,7 +72,7 @@
     }
 
     //----------------------------------------< Access Operation Parameters >---
-    public NodeId getNodeId() {
+    public NodeId getNodeId() throws RepositoryException {
         return nodeState.getNodeId();
     }
 

Modified: jackrabbit/trunk/jackrabbit-jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/operation/Merge.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/operation/Merge.java?rev=704361&r1=704360&r2=704361&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit-jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/operation/Merge.java (original)
+++ jackrabbit/trunk/jackrabbit-jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/operation/Merge.java Tue Oct 14 00:48:22 2008
@@ -18,6 +18,7 @@
 
 import org.apache.jackrabbit.jcr2spi.state.NodeState;
 import org.apache.jackrabbit.jcr2spi.version.VersionManager;
+import org.apache.jackrabbit.jcr2spi.hierarchy.NodeEntry;
 import org.apache.jackrabbit.spi.NodeId;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -59,6 +60,7 @@
      * @see Operation#accept(OperationVisitor)
      */
     public void accept(OperationVisitor visitor) throws RepositoryException, ConstraintViolationException, AccessDeniedException, ItemExistsException, NoSuchNodeTypeException, UnsupportedRepositoryOperationException, VersionException {
+        assert status == STATUS_PENDING;
         visitor.visit(this);
     }
 
@@ -68,16 +70,21 @@
      * @see Operation#persisted()
      */
     public void persisted() {
+        assert status == STATUS_PENDING;
+        status = STATUS_PERSISTED;
         try {
-            mgr.getVersionHistoryEntry(nodeState).invalidate(true);
+            NodeEntry vhe = mgr.getVersionHistoryEntry(nodeState);
+            if (vhe != null) {
+                vhe.invalidate(true);
+            }
         } catch (RepositoryException e) {
-            log.warn("Error while retrieving VersionHistory state:", e.getMessage());
+            log.warn("Error while retrieving VersionHistory entry:", e.getMessage());
         }
         nodeState.getHierarchyEntry().invalidate(true);
     }
 
     //----------------------------------------< Access Operation Parameters >---
-    public NodeId getNodeId() {
+    public NodeId getNodeId() throws RepositoryException {
         return nodeState.getNodeEntry().getWorkspaceId();
     }