You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@jackrabbit.apache.org by mr...@apache.org on 2006/08/15 12:59:04 UTC

svn commit: r431572 - in /jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi: ./ state/

Author: mreutegg
Date: Tue Aug 15 03:59:04 2006
New Revision: 431572

URL: http://svn.apache.org/viewvc?rev=431572&view=rev
Log:
- Rename TransientChangeLog to TransientItemStateManager.
- ChangeLog does not disconnect ItemStates anymore
- Added WorkspaceItemStateManager which extends CachingItemStateManager. CachingItemStateManager is now more generic.
- TransientItemStateManager implements TransientItemStateFactory

Added:
    jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/state/WorkspaceItemStateManager.java   (with props)
Removed:
    jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/state/TransientChangeLog.java
Modified:
    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/state/CachingItemStateManager.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/NodeState.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/state/TransientItemStateManager.java

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?rev=431572&r1=431571&r2=431572&view=diff
==============================================================================
--- 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 Tue Aug 15 03:59:04 2006
@@ -28,11 +28,11 @@
 import org.apache.jackrabbit.jcr2spi.state.ChangeLog;
 import org.apache.jackrabbit.jcr2spi.state.UpdatableItemStateManager;
 import org.apache.jackrabbit.jcr2spi.state.NodeReferences;
-import org.apache.jackrabbit.jcr2spi.state.CachingItemStateManager;
 import org.apache.jackrabbit.jcr2spi.state.ItemStateFactory;
 import org.apache.jackrabbit.jcr2spi.state.WorkspaceItemStateFactory;
 import org.apache.jackrabbit.jcr2spi.state.NodeState;
 import org.apache.jackrabbit.jcr2spi.state.ItemStateManager;
+import org.apache.jackrabbit.jcr2spi.state.WorkspaceItemStateManager;
 import org.apache.jackrabbit.jcr2spi.operation.OperationVisitor;
 import org.apache.jackrabbit.jcr2spi.operation.AddNode;
 import org.apache.jackrabbit.jcr2spi.operation.AddProperty;
@@ -116,7 +116,7 @@
     private final SessionInfo sessionInfo;
 
     // TODO: TO-BE-FIXED. Major refactoring of caching mechanism with change to SPI ids
-    private final CachingItemStateManager cache;
+    private final WorkspaceItemStateManager cache;
 
     private final NamespaceRegistryImpl nsRegistry;
     private final NodeTypeRegistry ntRegistry;
@@ -140,7 +140,7 @@
             this.sessionInfo = sessionInfo;
 
             ItemStateFactory isf = createItemStateFactory();
-            cache = new CachingItemStateManager(isf, service.getIdFactory());
+            cache = new WorkspaceItemStateManager(isf, service.getIdFactory());
             addEventListener(cache);
 
             nsRegistry = createNamespaceRegistry();

Modified: jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/state/CachingItemStateManager.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/state/CachingItemStateManager.java?rev=431572&r1=431571&r2=431572&view=diff
==============================================================================
--- jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/state/CachingItemStateManager.java (original)
+++ jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/state/CachingItemStateManager.java Tue Aug 15 03:59:04 2006
@@ -19,27 +19,19 @@
 import org.apache.jackrabbit.name.Path;
 import org.apache.jackrabbit.spi.ItemId;
 import org.apache.jackrabbit.spi.NodeId;
-import org.apache.jackrabbit.spi.EventIterator;
-import org.apache.jackrabbit.spi.Event;
 import org.apache.jackrabbit.spi.IdFactory;
-import org.apache.jackrabbit.jcr2spi.observation.InternalEventListener;
 import org.apache.commons.collections.map.ReferenceMap;
 import org.apache.commons.collections.map.LRUMap;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 import java.util.Map;
-import java.util.List;
-import java.util.ArrayList;
-import java.util.HashSet;
-import java.util.Set;
-import java.util.Iterator;
 
 /**
  * <code>CachingItemStateManager</code> implements an {@link ItemStateManager}
  * and decorates it with a caching facility.
  */
-public class CachingItemStateManager implements ItemStateManager, InternalEventListener {
+public class CachingItemStateManager implements ItemStateManager, ItemStateLifeCycleListener {
 
     /**
      * The logger instance for this class.
@@ -89,6 +81,7 @@
         this.recentlyUsed = new LRUMap(1000); // TODO: make configurable
         // initialize root
         root = isf.createNodeState(idFactory.createNodeId((String) null, Path.ROOT), this);
+        root.addListener(this);
     }
 
     //---------------------------------------------------< ItemStateManager >---
@@ -137,87 +130,31 @@
         return false;
     }
 
-    //-------------------------------< InternalEventListener >------------------
+    //------------------------< ItemStateListener >-----------------------------
 
-    /**
-     * Processes <code>events</code> and invalidates cached <code>ItemState</code>s
-     * accordingly.
-     * @param events 
-     * @param isLocal
-     */
-    public void onEvent(EventIterator events, boolean isLocal) {
-        // if events origin from local changes then
-        // cache does not need invalidation
-        if (isLocal) {
-            return;
+    public void statusChanged(ItemState state, int previousStatus) {
+        if (state.getStatus() == ItemState.STATUS_REMOVED ||
+                state.getStatus() == ItemState.STATUS_STALE_DESTROYED) {
+            recentlyUsed.remove(state);
+            if (state.isNode()) {
+                NodeState nodeState = (NodeState) state;
+                if (nodeState.getUUID() != null) {
+                    uuid2NodeState.remove(nodeState.getUUID());
+                }
+            }
         }
+    }
 
-        // collect set of removed node ids
-        Set removedNodeIds = new HashSet();
-        List eventList = new ArrayList();
-        while (events.hasNext()) {
-            Event e = events.nextEvent();
-            eventList.add(e);
-        }
+    public void stateCreated(ItemState created) {
+    }
 
-        for (Iterator it = eventList.iterator(); it.hasNext(); ) {
-            Event e = (Event) it.next();
-            ItemId itemId = e.getItemId();
-            NodeId parentId = e.getParentId();
-            ItemState state;
-            NodeState parent;
-            switch (e.getType()) {
-                case Event.NODE_ADDED:
-                case Event.PROPERTY_ADDED:
-                    state = lookup(itemId);
-                    if (state != null) {
-                        // TODO: item already exists ???
-                        // remove from cache and invalidate
-                        recentlyUsed.remove(state);
-                        state.discard();
-                    }
-                    parent = (NodeState) lookup(parentId);
-                    if (parent != null) {
-                        // discard and let wsp manager reload state when accessed next time
-                        recentlyUsed.remove(parent);
-                        parent.discard();
-                    }
-                    break;
-                case Event.NODE_REMOVED:
-                case Event.PROPERTY_REMOVED:
-                    state = lookup(itemId);
-                    if (state != null) {
-                        if (itemId.denotesNode()) {
-                            if (itemId.getRelativePath() == null) {
-                                // also remove mapping from uuid
-                                uuid2NodeState.remove(itemId.getUUID());
-                            }
-                        }
-                        recentlyUsed.remove(state);
-                        state.notifyStateDestroyed();
-                    }
-                    state = lookup(parentId);
-                    if (state != null) {
-                        parent = (NodeState) state;
-                        // check if removed as well
-                        if (removedNodeIds.contains(parent.getId())) {
-                            // do not invalidate here
-                        } else {
-                            // discard and let wsp manager reload state when accessed next time
-                            recentlyUsed.remove(parent);
-                            parent.discard();
-                        }
-                    }
-                    break;
-                case Event.PROPERTY_CHANGED:
-                    state = lookup(itemId);
-                    // discard and let wsp manager reload state when accessed next time
-                    if (state != null) {
-                        recentlyUsed.remove(state);
-                        state.discard();
-                    }
-            }
-        }
+    public void stateModified(ItemState modified) {
+    }
+
+    public void stateDestroyed(ItemState destroyed) {
+    }
+
+    public void stateDiscarded(ItemState discarded) {
     }
 
     //------------------------------< internal >--------------------------------
@@ -228,7 +165,7 @@
      *
      * @param state the touched state.
      */
-    private void touch(ItemState state) {
+    protected void touch(ItemState state) {
         recentlyUsed.put(state, state);
     }
 
@@ -245,7 +182,8 @@
         String uuid = id.getUUID();
         Path relPath = id.getRelativePath();
 
-        NodeState nodeState = null;
+        // start with root node if no uuid part in id
+        NodeState nodeState = root;
         // resolve uuid part
         if (uuid != null) {
             nodeState = (NodeState) uuid2NodeState.get(uuid);
@@ -253,6 +191,7 @@
                 // state identified by the uuid is not yet cached -> get from ISM
                 NodeId refId = (relPath == null) ? (NodeId) id : idFactory.createNodeId(uuid);
                 nodeState = isf.createNodeState(refId, this);
+                nodeState.addListener(this);
                 uuid2NodeState.put(uuid, nodeState);
             }
         }
@@ -273,7 +212,7 @@
      * @return the cached <code>ItemState</code> or <code>null</code> if it is not
      *         present in the cache.
      */
-    private ItemState lookup(ItemId id) {
+    protected ItemState lookup(ItemId id) {
         ItemState state;
         // resolve UUID
         if (id.getUUID() != null) {

Modified: jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/state/ChangeLog.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/state/ChangeLog.java?rev=431572&r1=431571&r2=431572&view=diff
==============================================================================
--- jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/state/ChangeLog.java (original)
+++ jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/state/ChangeLog.java Tue Aug 15 03:59:04 2006
@@ -19,8 +19,6 @@
 import org.apache.jackrabbit.jcr2spi.operation.Operation;
 
 import java.util.Iterator;
-import java.util.List;
-import java.util.ArrayList;
 import java.util.Set;
 import java.util.LinkedHashSet;
 
@@ -53,7 +51,7 @@
     /**
      * Type of operation this changelog is collection state modifications for.
      */  
-    private List operations = new ArrayList();
+    private Set operations = new LinkedHashSet();
 
     //-----------------------------------------------< Inform the ChangeLog >---
     // DIFF JR: method added
@@ -78,23 +76,20 @@
 
     /**
      * A state has been modified. If the state is not a new state
-     * (not in the collection of added ones), then disconnect
-     * the local state from its underlying shared state and add
+     * (not in the collection of added ones), then add
      * it to the modified states collection.
      *
      * @param state state that has been modified
      */
     public void modified(ItemState state) {
         if (!addedStates.contains(state)) {
-            state.disconnect();
             modifiedStates.add(state);
         }
     }
 
     /**
      * A state has been deleted. If the state is not a new state
-     * (not in the collection of added ones), then disconnect
-     * the local state from its underlying shared state, remove
+     * (not in the collection of added ones), then remove
      * it from the modified states collection and add it to the
      * deleted states collection.
      *
@@ -102,7 +97,6 @@
      */
     public void deleted(ItemState state) {
         if (addedStates.remove(state)) {
-            state.disconnect();
             modifiedStates.remove(state);
             deletedStates.add(state);
         }
@@ -307,7 +301,6 @@
      * @return <code>true</code> if the operation was removed.
      */
     protected boolean removeOperation(Operation operation) {
-        // @todo optimize
         return operations.remove(operation);
     }
 }

Modified: jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/state/NodeState.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/state/NodeState.java?rev=431572&r1=431571&r2=431572&view=diff
==============================================================================
--- jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/state/NodeState.java (original)
+++ jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/state/NodeState.java Tue Aug 15 03:59:04 2006
@@ -192,6 +192,22 @@
     }
 
     //--------------------< public READ methods and package private Setters >---
+
+    /**
+     * @return the name of this node state.
+     */
+    public final QName getName() {
+        return name;
+    }
+
+    /**
+     * @return the UUID of this node state or <code>null</code> if this
+     * node cannot be identified with a UUID.
+     */
+    public final String getUUID() {
+        return uuid;
+    }
+
     /**
      * Determines if this item state represents a node.
      *

Modified: jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/state/SessionItemStateManager.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/state/SessionItemStateManager.java?rev=431572&r1=431571&r2=431572&view=diff
==============================================================================
--- jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/state/SessionItemStateManager.java (original)
+++ jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/state/SessionItemStateManager.java Tue Aug 15 03:59:04 2006
@@ -103,7 +103,7 @@
      * State manager for the transient items
      */
     // DIFF JACKRABBIT: private final TransientItemStateManager transientStateMgr;
-    private final TransientChangeLog transientStateMgr;
+    private final TransientItemStateManager transientStateMgr;
 
     /**
      * Hierarchy manager
@@ -115,6 +115,11 @@
     private final ItemStateValidator validator;
 
     /**
+     * The root node state or <code>null</code> if it hasn't been retrieved yet.
+     */
+    private NodeState rootNodeState;
+
+    /**
      * Creates a new <code>SessionItemStateManager</code> instance.
      *
      * @param workspaceItemStateMgr
@@ -126,7 +131,7 @@
                                    NamespaceResolver nsResolver) {
         this.workspaceItemStateMgr = workspaceItemStateMgr;
         // DIFF JACKRABBIT: this.transientStateMgr = new TransientItemStateManager();
-        this.transientStateMgr = new TransientChangeLog(idFactory, workspaceItemStateMgr);
+        this.transientStateMgr = new TransientItemStateManager(idFactory, workspaceItemStateMgr);
         // DIFF JR: validator added
         this.validator = validator;
         // DIFF JR: idFactory added
@@ -328,7 +333,7 @@
         }
 
         // list of transient items that should be discarded
-        ChangeLog changeLog = new TransientChangeLog(idFactory, workspaceItemStateMgr);
+        ChangeLog changeLog = new ChangeLog();
 
         // check status of current item's state
         if (itemState.isTransient()) {
@@ -622,7 +627,7 @@
      * @throws ItemStateException
      */
     private ChangeLog getChangeLog(ItemState itemState) throws StaleItemStateException, ItemStateException {
-        ChangeLog changeLog = new TransientChangeLog(idFactory, workspaceItemStateMgr);
+        ChangeLog changeLog = new ChangeLog();
         if (itemState.getParent() == null) {
             // root state -> get all item states
             for (Iterator it = transientStateMgr.addedStates(); it.hasNext(); ) {
@@ -1385,7 +1390,9 @@
         // remove property entry
         parent.removePropertyName(target.getQName());
         // destroy property state
-        destroy(target);
+        // DIFF JR: notification of transient item state manager is done using ItemStateLifeCycleListener
+        //destroy(target);
+        target.notifyStateDiscarded();
     }
 
     /**
@@ -1436,10 +1443,6 @@
                 // TODO: check if correct
             }
         }
-
-        // destroy target state
-        // DIFF JR: destroy targetState (not overlayed state)
-        destroy(targetState);
     }
 
     /**
@@ -1482,19 +1485,6 @@
         propState.setValues(iva);
         propState.setType(valueType);
     }
-
-    /**
-     * Destroy an item state.
-     *
-     * @param state item state that should be destroyed
-     */
-    private void destroy(ItemState state) {
-        // DIFF JACKRABBIT: persistentStateMgr.destroy(state);
-        transientStateMgr.deleted(state);
-        // todo correct?
-        state.notifyStateDiscarded();
-    }
-
 
     /**
      * Computes the values of well-known system (i.e. protected) properties

Modified: jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/state/TransientItemStateManager.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/state/TransientItemStateManager.java?rev=431572&r1=431571&r2=431572&view=diff
==============================================================================
--- jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/state/TransientItemStateManager.java (original)
+++ jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/state/TransientItemStateManager.java Tue Aug 15 03:59:04 2006
@@ -18,52 +18,232 @@
 
 import org.apache.jackrabbit.jcr2spi.operation.Operation;
 import org.apache.jackrabbit.name.QName;
+import org.apache.jackrabbit.spi.NodeId;
+import org.apache.jackrabbit.spi.ItemId;
+import org.apache.jackrabbit.spi.PropertyId;
+import org.apache.jackrabbit.spi.IdFactory;
+import org.apache.commons.collections.iterators.IteratorChain;
+import org.slf4j.LoggerFactory;
+import org.slf4j.Logger;
 
 import javax.jcr.ItemExistsException;
 import java.util.Iterator;
 
 /**
- * <code>TransientItemStateManager</code> ...
+ * <code>TransientItemStateManager</code> implements a {@link ItemStateManager}
+ * and adds more methods that support transient changes (e.g. resurrect deleted
+ * state).
  */
-interface TransientItemStateManager extends ItemStateManager {
+public class TransientItemStateManager
+        implements ItemStateManager, TransientItemStateFactory, ItemStateLifeCycleListener {
 
     /**
-     * @return the number of entries
+     * Logger instance for this class.
      */
-    public int getEntriesCount();
+    private static final Logger log = LoggerFactory.getLogger(TransientItemStateManager.class);
 
     /**
-     * @return <code>true</code> if there are any entries in attic.
+     * The change log which keeps track of changes and maintains hard references
+     * to changed item states.
      */
-    public boolean hasEntriesInAttic();
+    private final ChangeLog changeLog;
 
     /**
-     * @return an iterator over all entries
+     *
      */
-    public Iterator getEntries();
+    private final IdFactory idFactory;
 
     /**
-     * @return an iterator over all entries in attic
+     * The parent item state manager, which return item states that are then
+     * overlayed by transient item states created by this TransientItemStateManager.
      */
-    public Iterator getEntriesInAttic();
+    private final ItemStateManager parent;
 
     /**
-     * Adds an operation to this transient item state manager.
+     * ItemStateManager view of the states in the attic; lazily instantiated
+     * in {@link #getAttic()}
+     */
+    private AtticItemStateManager attic;
+
+    TransientItemStateManager(IdFactory idFactory, ItemStateManager parent) {
+        this.changeLog = new ChangeLog();
+        this.idFactory = idFactory;
+        this.parent = parent;
+    }
+
+    //-----------------------< ItemStateManager >-------------------------------
+
+    /**
+     * Return the root node state or <code>null</code> if the root state has
+     * not been modified yet.
+     *
+     * @return
+     * @throws ItemStateException
+     * @see ItemStateManager#getRootState()
+     */
+    public NodeState getRootState() throws ItemStateException {
+        // TODO
+        return null;
+    }
+
+    /**
+     * Return an item state given its id. Returns <code>null</code>
+     * if the item state is neither in the added nor in the modified
+     * section. Throws a <code>NoSuchItemStateException</code> if
+     * the item state is in the deleted section.
+     *
+     * @return item state or <code>null</code>
+     * @throws NoSuchItemStateException if the item has been deleted
+     * @see ItemStateManager#getItemState(ItemId)
+     */
+    public ItemState getItemState(ItemId id) throws NoSuchItemStateException, ItemStateException {
+        // TODO: this is expensive. Improvement: Lookup item, then check its state
+        ItemState state = null;
+        for (Iterator it = changeLog.addedStates(); it.hasNext(); ) {
+            ItemState s = (ItemState) it.next();
+            if (s.getId().equals(id)) {
+                state = s;
+                break;
+            }
+        }
+        if (state == null) {
+            for (Iterator it = changeLog.modifiedStates(); it.hasNext(); ) {
+                ItemState s = (ItemState) it.next();
+                if (s.getId().equals(id)) {
+                    state = s;
+                    break;
+                }
+            }
+            if (state == null) {
+                for (Iterator it = changeLog.deletedStates(); it.hasNext(); ) {
+                    ItemState s = (ItemState) it.next();
+                    if (s.getId().equals(id)) {
+                        throw new NoSuchItemStateException("State has been marked destroyed: " + id);
+                    }
+                }
+            }
+        }
+        return state;
+    }
+
+    /**
+     * Return a flag indicating whether a given item state exists.
+     *
+     * @return <code>true</code> if item state exists within this
+     *         log; <code>false</code> otherwise
+     * @see ItemStateManager#hasItemState(ItemId)
+     */
+    public boolean hasItemState(ItemId id) {
+        // TODO: too expensive. lookup item and check status
+        for (Iterator it = changeLog.addedStates(); it.hasNext(); ) {
+            ItemState s = (ItemState) it.next();
+            if (s.getId().equals(id)) {
+                return true;
+            }
+        }
+        for (Iterator it = changeLog.modifiedStates(); it.hasNext(); ) {
+            ItemState s = (ItemState) it.next();
+            if (s.getId().equals(id)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /**
+     * Return a node references object given its id. Returns
+     * <code>null</code> if the node reference is not in the modified
+     * section.
      *
-     * @param operationType the operation.
-     * @throws IllegalStateException if <code>operationType</code> is not
-     * compatible with the previously executed operation (invalid sequence of
-     * operations).
+     * @return node references or <code>null</code>
+     * @see ItemStateManager#getNodeReferences(NodeId)
+     */
+    public NodeReferences getNodeReferences(NodeId id) {
+        // TODO: improve
+        for (Iterator it = changeLog.modifiedRefs(); it.hasNext(); ) {
+            NodeReferences refs = (NodeReferences) it.next();
+            if (refs.getId().equals(id)) {
+                return refs;
+            }
+        }
+        return null;
+    }
+
+    /**
+     * Returns <code>false</code> if the node reference is not in the modified
+     * section.
+     *
+     * @return false if no references are present in this changelog for the
+     * given id.
+     * @see ItemStateManager#hasNodeReferences(NodeId)
+     */
+    public boolean hasNodeReferences(NodeId id) {
+        return getNodeReferences(id) != null;
+    }
+
+    /**
+     * @return the operations that have been recorded until now.
+     */
+    public Iterator getOperations() {
+        return changeLog.getOperations();
+    }
+
+    /**
+     * Add the given operation to the list of operations to be recorded within
+     * this TransientItemStateManager.
+     *
+     * @param operation
+     */
+    void addOperation(Operation operation) {
+        changeLog.addOperation(operation);
+    }
+
+    /**
+     * Removes the <code>operation</code> from the list of operations.
+     * @param operation the Operation to remove.
+     * @return <code>true</code> if the operation was removed.
+     */
+    boolean removeOperation(Operation operation) {
+        return changeLog.removeOperation(operation);
+    }
+
+    /**
+     * @return the number of entries
      */
-    public void addOperation(Operation operationType) throws IllegalStateException;
+    public int getEntriesCount() {
+        return changeLog.addedStates.size() + changeLog.modifiedStates.size();
+    }
 
-    //----------------< methods for creating & discarding ItemState instances >
+    /**
+     * @return <code>true</code> if there are any entries in attic.
+     */
+    public boolean hasEntriesInAttic() {
+        return changeLog.deletedStates.size() > 0;
+    }
+
+    /**
+     * @return an iterator over all entries
+     */
+    public Iterator getEntries() {
+        IteratorChain it = new IteratorChain();
+        it.addIterator(changeLog.modifiedStates());
+        it.addIterator(changeLog.addedStates());
+        return it;
+    }
 
     /**
+     * @return an iterator over all entries in attic
+     */
+    public Iterator getEntriesInAttic() {
+        return changeLog.deletedStates();
+    }
+
+    /**
+     * TODO: throw ItemExistsException? how to check?
      * Creates a new transient {@link NodeState} that does not overlay any other
      * {@link NodeState}.
      *
-     * @param name         the name of the <code>NodeState</code> to create.
+     * @param nodeName     the name of the <code>NodeState</code> to create.
      * @param uuid         the uuid of the <code>NodeState</code> to create or
      *                     <code>null</code> if the created <code>NodeState</code>
      *                     cannot be identified by a UUID.
@@ -71,10 +251,17 @@
      * @param parent       the parent of the new node state.
      * @return a new transient {@link NodeState}.
      */
-    NodeState createNodeState(QName name,
-                              String uuid,
-                              QName nodeTypeName,
-                              NodeState parent);
+    public NodeState createNodeState(QName nodeName,
+                                     String uuid,
+                                     QName nodeTypeName,
+                                     NodeState parent) {
+        NodeState nodeState = createNewNodeState(nodeName, uuid, parent);
+        nodeState.setNodeTypeName(nodeTypeName);
+        parent.addChildNodeState(nodeState, uuid);
+        changeLog.added(nodeState);
+        nodeState.addListener(this);
+        return nodeState;
+    }
 
     /**
      * Creates a new transient property state for a given <code>parent</code>
@@ -86,39 +273,74 @@
      * @throws ItemExistsException if <code>parent</code> already has a property
      *                             with the given name.
      */
-    PropertyState createPropertyState(NodeState parent, QName propName)
-            throws ItemExistsException;
+    public PropertyState createPropertyState(NodeState parent, QName propName)
+            throws ItemExistsException {
+        PropertyState propState = createNewPropertyState(propName, parent);
+        parent.addPropertyState(propState);
+        changeLog.added(propState);
+        propState.addListener(this);
+        return propState;
+    }
 
     /**
-     * Disposes the specified instance, i.e. discards it and removes it from
-     * the map.
+     * Disposes a single item <code>state</code>. The state is discarded removed
+     * from the map of added or modified states and disconnected from the
+     * underlying state. This method does not take states into account that are
+     * marked as deleted.
      *
-     * @param state the <code>ItemState</code> instance that should be disposed
-     * @see ItemState#discard()
+     * @param state the item state to dispose.
      */
-    public void disposeItemState(ItemState state);
+    public void disposeItemState(ItemState state) {
+        state.discard();
+        if (changeLog.addedStates.remove(state)) {
+            changeLog.modifiedStates.remove(state);
+        }
+        state.onDisposed();
+    }
 
     /**
-     * Transfers the specified instance from the 'active' map to the attic.
+     * A state has been deleted. If the state is not a new state
+     * (not in the collection of added ones), then remove
+     * it from the modified states collection.
+     * The state is added to the deleted states collection in any case.
      *
-     * @param state the <code>ItemState</code> instance that should be moved to
-     *              the attic
+     * @param state state that has been deleted
      */
-    public void moveItemStateToAttic(ItemState state);
+    public void moveItemStateToAttic(ItemState state) {
+        if (changeLog.addedStates.remove(state)) {
+            changeLog.modifiedStates.remove(state);
+        }
+        changeLog.deleted(state);
+    }
 
     /**
-     * Disposes the specified instance in the attic, i.e. discards it and
-     * removes it from the attic.
+     * Disposes a single item <code>state</code> that is marked as deleted. The
+     * state is discarded removed from the map of removed states and
+     * disconnected from the underlying state.
      *
-     * @param state the <code>ItemState</code> instance that should be disposed
-     * @see ItemState#discard()
+     * @param state the item state to dispose.
      */
-    public void disposeItemStateInAttic(ItemState state);
+    public void disposeItemStateInAttic(ItemState state) {
+        state.discard();
+        changeLog.deletedStates.remove(state);
+        state.onDisposed();
+    }
 
     /**
      * Disposes all transient item states in the cache and in the attic.
      */
-    public void disposeAllItemStates();
+    public void disposeAllItemStates() {
+        IteratorChain it = new IteratorChain();
+        it.addIterator(changeLog.modifiedStates());
+        it.addIterator(changeLog.addedStates());
+        it.addIterator(changeLog.deletedStates());
+        while (it.hasNext()) {
+            ItemState state = (ItemState) it.next();
+            state.discard();
+            state.onDisposed();
+        }
+        changeLog.reset();
+    }
 
     /**
      * Return the attic item state provider that holds all items
@@ -126,14 +348,315 @@
      *
      * @return attic
      */
-    public ItemStateManager getAttic();
-
-    //-----------------------------------< methods for controlling operations >
+    public ItemStateManager getAttic() {
+        if (attic == null) {
+            attic = new AtticItemStateManager();
+        }
+        return attic;
+    }
 
     /**
      * Disposes a collection of {@link org.apache.jackrabbit.jcr2spi.operation.Operation}s.
      *
      * @param operations the operations.
      */
-    public void disposeOperations(Iterator operations);
+    public void disposeOperations(Iterator operations) {
+        while (operations.hasNext()) {
+            changeLog.removeOperation((Operation) operations.next());
+        }
+    }
+
+    /**
+     * TODO: remove this method when not used anymore
+     * Return an iterator over all added states.
+     *
+     * @return iterator over all added states.
+     */
+    public Iterator addedStates() {
+        return changeLog.addedStates();
+    }
+
+    /**
+     * TODO: remove this method when not used anymore
+     * Return an iterator over all modified states.
+     *
+     * @return iterator over all modified states.
+     */
+    public Iterator modifiedStates() {
+        return changeLog.modifiedStates();
+    }
+
+    /**
+     * TODO: remove this method when not used anymore
+     * Return an iterator over all deleted states.
+     *
+     * @return iterator over all deleted states.
+     */
+    public Iterator deletedStates() {
+        return changeLog.deletedStates();
+    }
+
+    //----------------------< TransientItemStateFactory >-----------------------
+
+    /**
+     * @inheritDoc
+     * @see TransientItemStateFactory#createNewNodeState(QName, String, NodeState)
+     */
+    public NodeState createNewNodeState(QName name, String uuid, NodeState parent) {
+        NodeState nodeState = new NodeState(name, uuid, parent, null,
+                ItemState.STATUS_NEW, true, this, idFactory);
+        // get a notification when this item state is saved or invalidated
+        nodeState.addListener(this);
+        changeLog.added(nodeState);
+        return nodeState;
+    }
+
+    /**
+     * @inheritDoc
+     * @see TransientItemStateFactory#createNewPropertyState(QName, NodeState)
+     */
+    public PropertyState createNewPropertyState(QName name, NodeState parent) {
+        PropertyState propState = new PropertyState(name, parent,
+                ItemState.STATUS_NEW, true, idFactory);
+        // get a notification when this item state is saved or invalidated
+        propState.addListener(this);
+        changeLog.added(propState);
+        return propState;
+    }
+
+    /**
+     * @inheritDoc
+     * @see TransientItemStateFactory#createNodeState(NodeId, ItemStateManager)
+     */
+    public NodeState createNodeState(NodeId nodeId, ItemStateManager ism)
+            throws NoSuchItemStateException, ItemStateException {
+        // retrieve state to overlay
+        NodeState overlayedState = (NodeState) parent.getItemState(nodeId);
+        NodeId parentId = overlayedState.getParent().getNodeId();
+        NodeState parentState = (NodeState) ism.getItemState(parentId);
+        NodeState nodeState = new NodeState(overlayedState, parentState,
+                ItemState.STATUS_EXISTING, true, this, idFactory);
+        nodeState.addListener(this);
+        return nodeState;
+    }
+
+    /**
+     * @inheritDoc
+     * @see TransientItemStateFactory#createNodeState(NodeId, NodeState)
+     */
+    public NodeState createNodeState(NodeId nodeId, NodeState parentState)
+            throws NoSuchItemStateException, ItemStateException {
+        // retrieve state to overlay
+        NodeState overlayedState = (NodeState) parent.getItemState(nodeId);
+        NodeState nodeState = new NodeState(overlayedState, parentState,
+                ItemState.STATUS_EXISTING, true, this, idFactory);
+        nodeState.addListener(this);
+        return nodeState;
+    }
+
+    /**
+     * @inheritDoc
+     * @see TransientItemStateFactory#createPropertyState(PropertyId, ItemStateManager)
+     */
+    public PropertyState createPropertyState(PropertyId propertyId,
+                                             ItemStateManager ism)
+            throws NoSuchItemStateException, ItemStateException {
+        // retrieve state to overlay
+        PropertyState overlayedState = (PropertyState) parent.getItemState(propertyId);
+        NodeId parentId = overlayedState.getParent().getNodeId();
+        NodeState parentState = (NodeState) ism.getItemState(parentId);
+        PropertyState propState = new PropertyState(overlayedState, parentState,
+                ItemState.STATUS_EXISTING, true, idFactory);
+        propState.addListener(this);
+        return propState;
+    }
+
+    /**
+     * @inheritDoc
+     * @see TransientItemStateFactory#createPropertyState(PropertyId, NodeState)
+     */
+    public PropertyState createPropertyState(PropertyId propertyId,
+                                             NodeState parentState)
+            throws NoSuchItemStateException, ItemStateException {
+        // retrieve state to overlay
+        PropertyState overlayedState = (PropertyState) parent.getItemState(propertyId);
+        PropertyState propState = new PropertyState(overlayedState, parentState,
+                ItemState.STATUS_EXISTING, true, idFactory);
+        propState.addListener(this);
+        return propState;
+    }
+
+    //-----------------------< ItemStateLifeCycleListener >---------------------
+
+    /**
+     * @inheritDoc
+     * @see ItemStateListener#stateCreated(ItemState)
+     */
+    public void stateCreated(ItemState created) {
+    }
+
+    /**
+     * @inheritDoc
+     * @see ItemStateListener#stateModified(ItemState)
+     */
+    public void stateModified(ItemState modified) {
+    }
+
+    /**
+     * @inheritDoc
+     * @see ItemStateListener#stateDestroyed(ItemState)
+     */
+    public void stateDestroyed(ItemState destroyed) {
+        changeLog.deletedStates.remove(destroyed);
+    }
+
+    /**
+     * @inheritDoc
+     * @see ItemStateListener#stateDiscarded(ItemState)
+     */
+    public void stateDiscarded(ItemState discarded) {
+        // TODO: remove from modified (and deleted?) set of change log
+    }
+
+    /**
+     * @inheritDoc
+     * @see ItemStateLifeCycleListener#statusChanged(ItemState, int)
+     */
+    public void statusChanged(ItemState state, int previousStatus) {
+        // TODO: depending on status of state adapt change log
+        // e.g. a revert on states will reset the status from
+        // 'existing modified' to 'existing'.
+        // a state which changes from 'existing' to 'existing modified' will
+        // go into the modified set of the change log, etc.
+        switch (state.getStatus()) {
+            case ItemState.STATUS_EXISTING:
+                if (previousStatus == ItemState.STATUS_EXISTING_MODIFIED) {
+                    // was modified and is now refreshed
+                    changeLog.modifiedStates.remove(state);
+                } else if (previousStatus == ItemState.STATUS_EXISTING_REMOVED) {
+                    // was removed and is now refreshed
+                    changeLog.deletedStates.remove(state);
+                } else if (previousStatus == ItemState.STATUS_STALE_MODIFIED) {
+                    // was modified and state and is now refreshed
+                    changeLog.modifiedStates.remove(state);
+                } else if (previousStatus == ItemState.STATUS_NEW) {
+                    // was new and has been saved now
+                    changeLog.addedStates.remove(state);
+                }
+                break;
+            case ItemState.STATUS_EXISTING_MODIFIED:
+                changeLog.modified(state);
+                break;
+            case ItemState.STATUS_EXISTING_REMOVED:
+                // check if modified earlier
+                if (previousStatus == ItemState.STATUS_EXISTING_MODIFIED) {
+                    changeLog.modifiedStates.remove(state);
+                }
+                changeLog.deleted(state);
+                break;
+            case ItemState.STATUS_REMOVED:
+                if (previousStatus == ItemState.STATUS_NEW) {
+                    // was new and now removed again
+                    changeLog.addedStates.remove(state);
+                } else if (previousStatus == ItemState.STATUS_EXISTING_REMOVED) {
+                    // was removed and is now saved
+                    changeLog.deletedStates.remove(state);
+                }
+                break;
+            case ItemState.STATUS_STALE_DESTROYED:
+                // state is now stale. remove from modified
+                changeLog.modifiedStates.remove(state);
+                break;
+            case ItemState.STATUS_STALE_MODIFIED:
+                // state is now stale. keep in modified. wait until refreshed
+                break;
+            case ItemState.STATUS_NEW:
+                // should never happen
+                log.warn("ItemState changed status to 'new'");
+                break;
+            case ItemState.STATUS_UNDEFINED:
+                // should never happen
+                log.warn("ItemState changed status to 'undefined'");
+                break;
+            default:
+                log.warn("ItemState has invalid status: " + state.getStatus());
+        }
+    }
+
+    //--------------------------------------------------------< inner classes >
+
+    /**
+     * ItemStateManager view of the states in the attic
+     *
+     * @see TransientItemStateManager#getAttic()
+     */
+    private class AtticItemStateManager implements ItemStateManager {
+
+        AtticItemStateManager() {
+        }
+
+        /**
+         * Since the root node may never be removed, this method always returns
+         * <code>null</code>.
+         *
+         * @return <code>null</code> since the root node cannot be removed.
+         * @throws ItemStateException
+         * @see ItemStateManager#getRootState()
+         */
+        public NodeState getRootState() throws ItemStateException {
+            return null;
+        }
+
+        /**
+         * {@inheritDoc}
+         */
+        public ItemState getItemState(ItemId id)
+                throws NoSuchItemStateException, ItemStateException {
+
+            // TODO: too expensive. rather lookup item and check state
+            ItemState state = null;
+            for (Iterator it = changeLog.deletedStates(); it.hasNext(); ) {
+                ItemState s = (ItemState) it.next();
+                if (s.getId().equals(id)) {
+                    state = s;
+                }
+            }
+            if (state != null) {
+                return state;
+            } else {
+                throw new NoSuchItemStateException(id.toString());
+            }
+        }
+
+        /**
+         * {@inheritDoc}
+         */
+        public boolean hasItemState(ItemId id) {
+            // TODO: too expensive. rather lookup item and check state
+            for (Iterator it = changeLog.deletedStates(); it.hasNext(); ) {
+                ItemState s = (ItemState) it.next();
+                if (s.getId().equals(id)) {
+                    return true;
+                }
+            }
+            return false;
+        }
+
+        /**
+         * {@inheritDoc}
+         */
+        public NodeReferences getNodeReferences(NodeId id)
+                throws NoSuchItemStateException, ItemStateException {
+            // n/a
+            throw new ItemStateException("getNodeReferences() not implemented");
+        }
+
+        /**
+         * {@inheritDoc}
+         */
+        public boolean hasNodeReferences(NodeId id) {
+            // n/a
+            return false;
+        }
+    }
 }

Added: jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/state/WorkspaceItemStateManager.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/state/WorkspaceItemStateManager.java?rev=431572&view=auto
==============================================================================
--- jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/state/WorkspaceItemStateManager.java (added)
+++ jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/state/WorkspaceItemStateManager.java Tue Aug 15 03:59:04 2006
@@ -0,0 +1,115 @@
+/*
+ * 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.state;
+
+import org.apache.jackrabbit.jcr2spi.observation.InternalEventListener;
+import org.apache.jackrabbit.spi.EventIterator;
+import org.apache.jackrabbit.spi.Event;
+import org.apache.jackrabbit.spi.ItemId;
+import org.apache.jackrabbit.spi.NodeId;
+import org.apache.jackrabbit.spi.IdFactory;
+
+import java.util.Set;
+import java.util.HashSet;
+import java.util.List;
+import java.util.ArrayList;
+import java.util.Iterator;
+
+/**
+ * <code>WorkspaceItemStateManager</code>
+ */
+public class WorkspaceItemStateManager
+        extends CachingItemStateManager
+        implements InternalEventListener {
+
+    public WorkspaceItemStateManager(ItemStateFactory isf, IdFactory idFactory)
+            throws ItemStateException, NoSuchItemStateException {
+        super(isf, idFactory);
+    }
+
+    //-------------------------------< InternalEventListener >------------------
+
+    /**
+     * Processes <code>events</code> and invalidates cached <code>ItemState</code>s
+     * accordingly.
+     * @param events
+     * @param isLocal
+     */
+    public void onEvent(EventIterator events, boolean isLocal) {
+        // if events origin from local changes then
+        // cache does not need invalidation
+        if (isLocal) {
+            return;
+        }
+
+        // collect set of removed node ids
+        Set removedNodeIds = new HashSet();
+        List eventList = new ArrayList();
+        while (events.hasNext()) {
+            Event e = events.nextEvent();
+            eventList.add(e);
+        }
+
+        for (Iterator it = eventList.iterator(); it.hasNext(); ) {
+            Event e = (Event) it.next();
+            ItemId itemId = e.getItemId();
+            NodeId parentId = e.getParentId();
+            ItemState state;
+            NodeState parent;
+            switch (e.getType()) {
+                case Event.NODE_ADDED:
+                case Event.PROPERTY_ADDED:
+                    state = lookup(itemId);
+                    if (state != null) {
+                        // TODO: item already exists ???
+                        // invalidate
+                        state.discard();
+                    }
+                    parent = (NodeState) lookup(parentId);
+                    if (parent != null) {
+                        // discard and let wsp manager reload state when accessed next time
+                        parent.discard();
+                    }
+                    break;
+                case Event.NODE_REMOVED:
+                case Event.PROPERTY_REMOVED:
+                    state = lookup(itemId);
+                    if (state != null) {
+                        state.notifyStateDestroyed();
+                    }
+                    state = lookup(parentId);
+                    if (state != null) {
+                        parent = (NodeState) state;
+                        // check if removed as well
+                        if (removedNodeIds.contains(parent.getId())) {
+                            // do not invalidate here
+                        } else {
+                            // discard and let wsp manager reload state when accessed next time
+                            parent.discard();
+                        }
+                    }
+                    break;
+                case Event.PROPERTY_CHANGED:
+                    state = lookup(itemId);
+                    // discard and let wsp manager reload state when accessed next time
+                    if (state != null) {
+                        state.discard();
+                    }
+            }
+        }
+    }
+}

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