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 2007/02/13 10:31:53 UTC

svn commit: r506927 [5/8] - in /jackrabbit/trunk/contrib/spi: jcr2spi/ jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/ jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/hierarchy/ jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/lock/ jcr2spi...

Added: jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/state/AbstractItemStateFactory.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/state/AbstractItemStateFactory.java?view=auto&rev=506927
==============================================================================
--- jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/state/AbstractItemStateFactory.java (added)
+++ jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/state/AbstractItemStateFactory.java Tue Feb 13 01:31:36 2007
@@ -0,0 +1,79 @@
+/*
+ * 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.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.Set;
+import java.util.HashSet;
+
+/**
+ * <code>AbstractItemStateFactory</code>...
+ */
+public abstract class AbstractItemStateFactory implements ItemStateFactory {
+
+    private static Logger log = LoggerFactory.getLogger(AbstractItemStateFactory.class);
+
+    private final Set creationListeners = new HashSet();
+
+    //---------------------------------------------------< ItemStateFactory >---
+    /**
+     * @inheritDoc
+     * @see ItemStateFactory#addCreationListener(ItemStateCreationListener)
+     */
+    public void addCreationListener(ItemStateCreationListener listener) {
+        synchronized (creationListeners) {
+            creationListeners.add(listener);
+        }
+    }
+
+    /**
+     * @inheritDoc
+     * @see ItemStateFactory#removeCreationListener(ItemStateCreationListener)
+     */
+    public void removeCreationListener(ItemStateCreationListener listener) {
+        synchronized (creationListeners) {
+            creationListeners.remove(listener);
+        }
+    }
+
+    //------------------------------------------------< private | protected >---
+    /**
+     *
+     * @return
+     */
+    private ItemStateCreationListener[] getListeners() {
+        synchronized (creationListeners) {
+            return (ItemStateCreationListener[]) creationListeners.toArray(new ItemStateCreationListener[creationListeners.size()]);
+        }
+    }
+
+    /**
+     * 
+     * @param createdState
+     */
+    void notifyCreated(ItemState createdState) {
+        ItemStateCreationListener[] listeners = getListeners();
+        for (int i = 0; i < listeners.length; i++) {
+            // notify listeners when this item state is saved or invalidated
+            createdState.addListener(listeners[i]);
+            // now inform about creation
+            listeners[i].created(createdState);
+        }
+    }
+}
\ No newline at end of file

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

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

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?view=diff&rev=506927&r1=506926&r2=506927
==============================================================================
--- 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 Feb 13 01:31:36 2007
@@ -19,7 +19,10 @@
 import org.apache.jackrabbit.jcr2spi.operation.Operation;
 import org.apache.jackrabbit.jcr2spi.operation.AddNode;
 import org.apache.jackrabbit.jcr2spi.operation.AddProperty;
+import org.apache.jackrabbit.jcr2spi.operation.SetMixin;
 import org.apache.jackrabbit.jcr2spi.config.CacheBehaviour;
+import org.apache.jackrabbit.jcr2spi.hierarchy.NodeEntry;
+import org.apache.jackrabbit.name.QName;
 
 import javax.jcr.nodetype.ConstraintViolationException;
 import java.util.Iterator;
@@ -237,15 +240,35 @@
                     deletedStates.remove(state);
                     // remove operations performed on the removed state
                     removeAffectedOperations(state);
-                    // remove add-operation (parent affected) as well
-                    NodeState parent = state.getParent();
-                    if (parent != null && parent.getStatus() != Status.REMOVED) {
-                        for (Iterator it = operations.iterator(); it.hasNext();) {
-                            Operation op = (Operation) it.next();
-                            if ((op instanceof AddNode || op instanceof AddProperty)
-                                && op.getAffectedItemStates().contains(parent)) {
-                                it.remove();
+                    /* remove the add-operation as well:
+                       since the affected state of an 'ADD' operation is the parent
+                       instead of the added-state, the set of operations
+                       need to be searched for the parent state && the proper
+                       operation type.
+                       SET_MIXIN can be is a special case of adding a property */
+                    NodeEntry parentEntry = state.getHierarchyEntry().getParent();
+                    if (parentEntry != null && parentEntry.isAvailable()) {
+                        try {
+                            NodeState parent = parentEntry.getNodeState();
+                            if (parent.getStatus() != Status.REMOVED) {
+                                for (Iterator it = operations.iterator(); it.hasNext();) {
+                                    Operation op = (Operation) it.next();
+                                    if (op instanceof AddNode && ((AddNode)op).getParentState() == parent) {
+                                        it.remove();
+                                        break;
+                                    } else if (op instanceof AddProperty && ((AddProperty)op).getParentState() == parent) {
+                                        it.remove();
+                                        break;
+                                    } else if (op instanceof SetMixin &&
+                                        QName.JCR_MIXINTYPES.equals(state.getQName()) &&
+                                        ((SetMixin)op).getNodeState() == parent) {
+                                        it.remove();
+                                        break;
+                                    }
+                                }
                             }
+                        } catch (ItemStateException e) {
+                            // should never occur -> ignore
                         }
                     }
                 } else if (previousStatus == Status.EXISTING_REMOVED) {

Added: jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/state/EmptyNodeReferences.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/state/EmptyNodeReferences.java?view=auto&rev=506927
==============================================================================
--- jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/state/EmptyNodeReferences.java (added)
+++ jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/state/EmptyNodeReferences.java Tue Feb 13 01:31:36 2007
@@ -0,0 +1,51 @@
+/*
+ * 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.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.apache.jackrabbit.util.IteratorHelper;
+
+import java.util.Iterator;
+
+/**
+ * <code>EmptyNodeReferences</code>...
+ */
+class EmptyNodeReferences implements NodeReferences {
+
+    private static Logger log = LoggerFactory.getLogger(EmptyNodeReferences.class);
+    private static NodeReferences INSTANCE;
+
+    private EmptyNodeReferences() {
+
+    }
+
+    static NodeReferences getInstance() {
+        if (INSTANCE == null) {
+            INSTANCE = new EmptyNodeReferences();
+        }
+        return INSTANCE;
+    }
+
+    public boolean isEmpty() {
+        return true;
+    }
+
+    public Iterator iterator() {
+        return IteratorHelper.EMPTY;
+    }
+}
\ No newline at end of file

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

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

Modified: jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/state/ItemState.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/state/ItemState.java?view=diff&rev=506927&r1=506926&r2=506927
==============================================================================
--- jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/state/ItemState.java (original)
+++ jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/state/ItemState.java Tue Feb 13 01:31:36 2007
@@ -18,16 +18,20 @@
 
 import org.apache.jackrabbit.util.WeakIdentityCollection;
 import org.apache.jackrabbit.spi.ItemId;
-import org.apache.jackrabbit.spi.IdFactory;
-import org.apache.jackrabbit.spi.Event;
-import org.apache.jackrabbit.name.Path;
-import org.apache.jackrabbit.name.MalformedPathException;
+import org.apache.jackrabbit.spi.NodeId;
+import org.apache.jackrabbit.spi.PropertyId;
 import org.apache.jackrabbit.name.QName;
+import org.apache.jackrabbit.name.Path;
 import org.apache.jackrabbit.jcr2spi.config.CacheBehaviour;
+import org.apache.jackrabbit.jcr2spi.hierarchy.HierarchyEntry;
+import org.apache.jackrabbit.jcr2spi.hierarchy.NodeEntry;
+import org.apache.jackrabbit.jcr2spi.hierarchy.PropertyEntry;
+import org.apache.jackrabbit.jcr2spi.nodetype.EffectiveNodeType;
+import org.apache.jackrabbit.jcr2spi.nodetype.NodeTypeRegistry;
+import org.apache.jackrabbit.jcr2spi.nodetype.NodeTypeConflictException;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-import javax.jcr.ItemNotFoundException;
 import javax.jcr.RepositoryException;
 import java.util.Collection;
 import java.util.Iterator;
@@ -44,8 +48,7 @@
     private static Logger log = LoggerFactory.getLogger(ItemState.class);
 
     /**
-     * Flag used to distinguish workspace states from session states. The first
-     * accepts call to {@link #refresh(Event)}, while the latter
+     * Flag used to distinguish workspace states from session states. The latter
      * will be able to handle the various methods related to transient
      * modifications.
      */
@@ -62,21 +65,13 @@
     private final transient Collection listeners = new WeakIdentityCollection(5);
 
     /**
-     *  IdFactory used to build id of the states
-     */
-    final IdFactory idFactory;
-
-    /**
      * The <code>ItemStateFactory</code> which is used to create new
      * <code>ItemState</code> instances.
      */
     final ItemStateFactory isf;
 
-    /**
-     * The parent <code>NodeState</code> or <code>null</code> if this
-     * instance represents the root node.
-     */
-    NodeState parent;
+    // TODO: find better solution..... needed to retrieve definition.
+    final NodeTypeRegistry ntReg;
 
     /**
      * the backing persistent item state (may be null)
@@ -86,11 +81,11 @@
     /**
      * Constructs a new unconnected item state
      *
-     * @param parent
-     * @param initialStatus the initial status of the item state object
+     * @param initialStatus
+     * @param isWorkspaceState
      */
-    protected ItemState(NodeState parent, int initialStatus, ItemStateFactory isf,
-                        IdFactory idFactory, boolean isWorkspaceState) {
+    protected ItemState(int initialStatus, boolean isWorkspaceState,
+                        ItemStateFactory isf, NodeTypeRegistry ntReg) {
         switch (initialStatus) {
             case Status.EXISTING:
             case Status.NEW:
@@ -101,11 +96,10 @@
                 log.debug(msg);
                 throw new IllegalArgumentException(msg);
         }
-        this.parent = parent;
         overlayedState = null;
 
-        this.idFactory = idFactory;
         this.isf = isf;
+        this.ntReg = ntReg;
         this.isWorkspaceState = isWorkspaceState;
     }
 
@@ -113,11 +107,10 @@
      * Constructs a new item state that is initially connected to an overlayed
      * state.
      *
-     * @param overlayedState the backing item state being overlayed
-     * @param initialStatus the initial status of the new <code>ItemState</code> instance
+     * @param overlayedState
+     * @param initialStatus
      */
-    protected ItemState(ItemState overlayedState, NodeState parent,
-                        int initialStatus, ItemStateFactory isf, IdFactory idFactory) {
+    protected ItemState(ItemState overlayedState, int initialStatus, ItemStateFactory isf) {
         switch (initialStatus) {
             case Status.EXISTING:
             case Status.EXISTING_MODIFIED:
@@ -129,16 +122,21 @@
                 log.debug(msg);
                 throw new IllegalArgumentException(msg);
         }
-        this.parent = parent;
-        this.idFactory = idFactory;
         this.isf = isf;
         this.isWorkspaceState = false;
-
+        this.ntReg = overlayedState.ntReg;
         connect(overlayedState);
     }
 
     //----------------------------------------------------------< ItemState >---
     /**
+     * The <code>HierarchyEntry</code> corresponding to this <code>ItemState</code>.
+     *
+     * @return The <code>HierarchyEntry</code> corresponding to this <code>ItemState</code>.
+     */
+    public abstract HierarchyEntry getHierarchyEntry();
+
+    /**
      * Returns <code>true</code> if this item state is valid, that is its status
      * is one of:
      * <ul>
@@ -153,6 +151,7 @@
     }
 
     /**
+     * Utility method:
      * Determines if this item state represents a node.
      *
      * @return true if this item state represents a node, otherwise false.
@@ -160,86 +159,48 @@
     public abstract boolean isNode();
 
     /**
-     * Returns the name of this state.
+     * Utility method:
+     * Returns the name of this state. Shortcut for calling 'getQName' on the
+     * {@link ItemState#getHierarchyEntry() hierarchy entry}.
      *
      * @return name of this state
      */
-    public abstract QName getQName();
+    public QName getQName() {
+        return getHierarchyEntry().getQName();
+    }
 
     /**
-     * Returns the identifier of this item state.
+     * Utility method:
+     * Returns the identifier of this item state. Shortcut for calling 'getId'
+     * on the {@link ItemState#getHierarchyEntry() hierarchy entry}.
      *
      * @return the identifier of this item state..
      */
     public abstract ItemId getId();
 
     /**
-     * Returns the qualified path of this item state.
-     *
-     * @return qualified path
-     * @throws ItemNotFoundException
-     * @throws RepositoryException
-     */
-    public Path getQPath() throws ItemNotFoundException, RepositoryException {
-        // shortcut for root state
-        if (parent == null) {
-            return Path.ROOT;
-        }
-
-        // build path otherwise
-        try {
-            Path.PathBuilder builder = new Path.PathBuilder();
-            buildPath(builder, this);
-            return builder.getPath();
-        } catch (MalformedPathException e) {
-            String msg = "Failed to build path of " + this;
-            throw new RepositoryException(msg, e);
-        }
-    }
-
-    /**
-     * Adds the path element of an item id to the path currently being built.
-     * On exit, <code>builder</code> contains the path of <code>state</code>.
+     * Utility method:
+     * Returns the qualified path of this item state. Shortcut for calling
+     * 'getPath' on the {@link ItemState#getHierarchyEntry() hierarchy entry}.
      *
-     * @param builder builder currently being used
-     * @param state   item to find path of
+     * @return
+     * @throws RepositoryException if an error occurs
      */
-    private void buildPath(Path.PathBuilder builder, ItemState state)
-        throws ItemNotFoundException {
-        NodeState parentState = state.getParent();
-        // shortcut for root state
-        if (parentState == null) {
-            builder.addRoot();
-            return;
-        }
-
-        // recursively build path of parent
-        buildPath(builder, parentState);
-
-        QName name = state.getQName();
-        if (state.isNode()) {
-            int index = ((NodeState)state).getIndex();
-            // add to path
-            if (index == Path.INDEX_DEFAULT) {
-                builder.addLast(name);
-            } else {
-                builder.addLast(name, index);
-            }
-        } else {
-            // property-state: add to path
-            builder.addLast(name);
-        }
+    public Path getQPath() throws RepositoryException {
+        return getHierarchyEntry().getPath();
     }
 
     /**
-     * Returns the parent <code>NodeState</code> or <code>null</code>
-     * if either this item state represents the root node or this item state is
-     * 'free floating', i.e. not attached to the repository's hierarchy.
+     * Utility method: Shortcut for calling
+     * 'getParent().getNodeState()' on the {@link ItemState#getHierarchyEntry()
+     * hierarchy entry}.
      *
-     * @return the parent <code>NodeState</code>
+     * @return
+     * @throws NoSuchItemStateException
+     * @throws ItemStateException
      */
-    public NodeState getParent() {
-        return parent;
+    public NodeState getParent() throws NoSuchItemStateException, ItemStateException {
+        return getHierarchyEntry().getParent().getNodeState();
     }
 
     /**
@@ -256,7 +217,7 @@
      *
      * @param newStatus the new status
      */
-    void setStatus(int newStatus) {
+    public void setStatus(int newStatus) {
         int oldStatus = status;
         if (oldStatus == newStatus) {
             return;
@@ -292,14 +253,6 @@
     }
 
     /**
-     * Reloads this item state recursively. If '<code>keepChanges</code>' is
-     * true, states with transient changes are left untouched. Otherwise this
-     * state gets its data reloaded from the persistent state.
-     * todo throw exception in case of error?
-     */
-    public abstract void reload(boolean keepChanges);
-
-    /**
      * Merge all data from the given state into this state. If
      * '<code>keepChanges</code>' is true, transient modifications present on
      * this state are not touched. Otherwise this state is completely reset
@@ -309,15 +262,7 @@
      * @param keepChanges
      * @return true if this state has been modified
      */
-    abstract boolean merge(ItemState another, boolean keepChanges);
-
-    /**
-     * Invalidates this item state recursively. In contrast to {@link #refresh}
-     * this method only sets the status of this item state to {@link
-     * Status#INVALIDATED} and does not acutally update it with the persistent
-     * state in the repository.
-     */
-    public abstract void invalidate(boolean recursive);
+     public abstract boolean merge(ItemState another, boolean keepChanges);
 
     /**
      * Add an <code>ItemStateLifeCycleListener</code>
@@ -353,26 +298,21 @@
     //-----------------------------------------< ItemStateLifeCycleListener >---
     /**
      *
-     * @param state
+     * @param overlayed
      * @param previousStatus
      */
-    public void statusChanged(ItemState state, int previousStatus) {
+    public void statusChanged(ItemState overlayed, int previousStatus) {
         checkIsSessionState();
-        state.checkIsWorkspaceState();
+        overlayed.checkIsWorkspaceState();
 
         // the given state is the overlayed state this state (session) is listening to.
-        if (state == overlayedState) {
-            switch (state.getStatus()) {
+        if (overlayed == overlayedState) {
+            switch (overlayed.getStatus()) {
                 case Status.MODIFIED:
                     // underlying state has been modified by external changes
                     if (status == Status.EXISTING || status == Status.INVALIDATED) {
-                        synchronized (this) {
-                            if (merge(state, false) || status == Status.INVALIDATED) {
-                                // temporarily set the state to MODIFIED in order
-                                // to inform listeners.
-                                setStatus(Status.MODIFIED);
-                            }
-                        }
+                        // temporarily set the state to MODIFIED in order to inform listeners.
+                        setStatus(Status.MODIFIED);
                     } else if (status == Status.EXISTING_MODIFIED) {
                         // TODO: try to merge changes
                         setStatus(Status.STALE_MODIFIED);
@@ -395,7 +335,7 @@
                     break;
                 default:
                     // Should never occur, since 'setStatus(int)' already validates
-                    log.error("Workspace state cannot have its state changed to " + state.getStatus());
+                    log.error("Workspace state cannot have its state changed to " + overlayed.getStatus());
                     break;
             }
         }
@@ -449,36 +389,6 @@
         return overlayedState != null;
     }
 
-    //--------------------------------------------------< Workspace - State >---
-    /**
-     * Used on 'workspace' states in order to update the state according to
-     * an external modification indicated by the given event.
-     *
-     * @param event
-     * @throws IllegalStateException if this state is a 'session' state.
-     */
-    abstract void refresh(Event event);
-
-    /**
-     * Returns the overlaying item state or <code>null</code> if that state
-     * has not been created yet or has been disconnected.
-     *
-     * @return
-     */
-    ItemState getSessionState() {
-        checkIsWorkspaceState();
-        ItemStateLifeCycleListener[] la;
-        synchronized (listeners) {
-            la = (ItemStateLifeCycleListener[]) listeners.toArray(new ItemStateLifeCycleListener[listeners.size()]);
-        }
-        for (int i = 0; i < la.length; i++) {
-            if (la[i] instanceof ItemState) {
-                return (ItemState) la[i];
-            }
-        }
-        return null;
-    }
-
     //----------------------------------------------------< Session - State >---
 
     /**
@@ -493,111 +403,65 @@
     /**
      * Connect this state to some underlying overlayed state.
      */
-    void connect(ItemState overlayedState) {
+    private void connect(ItemState overlayedState) {
         checkIsSessionState();
         overlayedState.checkIsWorkspaceState();
 
-        if (this.overlayedState != null && this.overlayedState != overlayedState) {
+        if (this.overlayedState == null) {
+            setOverLayedState(overlayedState);
+        } else if (this.overlayedState != overlayedState) {
             throw new IllegalStateException("Item state already connected to another underlying state: " + this);
-        }
-        this.overlayedState = overlayedState;
-        this.overlayedState.addListener(this);
+        } // attempt to connect state to its ol-state again -> nothing to do.
     }
 
     /**
-     * Removes this item state. This will change the status of this property
-     * state to either {@link Status#EXISTING_REMOVED} or {@link
-     * Status#REMOVED} depending on the current status.
+     * Replaces the overlayedState with a new instance retrieved from the
+     * persistent layer thus forcing a reload of this ItemState or in case
+     * of a NEW state, retrieves the overlayed state after the state has been
+     * persisted and connects the NEW state. Note, that in the latter case,
+     * the parent must already be connected to its overlayed state.
      *
-     * @throws ItemStateException if an error occurs while removing this item
-     * state. e.g. this item state is not valid anymore.
+     * @param keepChanges
+     * @throws NoSuchItemStateException
+     * @throws ItemStateException
      */
-    void remove() throws ItemStateException {
+    public void reconnect(boolean keepChanges) throws NoSuchItemStateException, ItemStateException {
         checkIsSessionState();
-        if (!isValid()) {
-            throw new ItemStateException("Cannot remove an invalid ItemState");
-        }
-        int oldStatus = getStatus();
-        if (oldStatus == Status.NEW) {
-            setStatus(Status.REMOVED);
+        // Need to use the workspace-ISF in order not to create yet another
+        // session-state.
+        ItemStateFactory wspIsf;
+        if (overlayedState != null) {
+            wspIsf = overlayedState.isf;
         } else {
-            setStatus(Status.EXISTING_REMOVED);
+            wspIsf = getParent().overlayedState.isf;
         }
-        // now inform parent
-        getParent().childStatusChanged(this, oldStatus);
-    }
-
-    /**
-     * Reverts this item state to its initial status (i.e. removing any transient
-     * modifications.
-     */
-    void revert() throws ItemStateException {
-        checkIsSessionState();
 
-        switch (getStatus()) {
-            case Status.EXISTING_MODIFIED:
-            case Status.STALE_MODIFIED:
-                // revert state from overlayed
-                merge(overlayedState, false);
-                setStatus(Status.EXISTING);
-                break;
-            case Status.EXISTING_REMOVED:
-                // revert state from overlayed
-                merge(overlayedState, false);
-                setStatus(Status.EXISTING);
-                parent.childStatusChanged(this, Status.EXISTING_REMOVED);
-                break;
-            case Status.NEW:
-                remove();
-                break;
-            case Status.STALE_DESTROYED:
-                // overlayed does not exist any more
-                // cannot call 'remove' on invalid state -> manuall remove
-                setStatus(Status.REMOVED);
-                parent.childStatusChanged(this, Status.STALE_DESTROYED);
-                break;
-            default:
-                // Cannot revert EXISTING, REMOVED, INVALIDATED, MODIFIED states.
-                // State was implicitely reverted
-                log.debug("State with status " + getStatus() + " cannot be reverted.");
+        ItemState overlayed;
+        if (isNode()) {
+            overlayed = wspIsf.createNodeState((NodeId) getId(), (NodeEntry) getHierarchyEntry());
+        } else {
+            overlayed = wspIsf.createPropertyState((PropertyId) getId(), (PropertyEntry) getHierarchyEntry());
+        }
+        setOverLayedState(overlayed);
+        boolean modified = merge(overlayed, keepChanges);
+        if (status == Status.NEW || status == Status.INVALIDATED) {
+            setStatus(Status.EXISTING);
+        } else if (modified) {
+            // start notification by marking ol-state modified.
+            overlayed.setStatus(Status.MODIFIED);
         }
     }
 
     /**
-     * Checks if this <code>ItemState</code> is transiently modified, new or stale
-     * modified. and adds itself to the <code>ChangeLog</code>.
-     * If this <code>ItemState</code> has children it will call
-     * {@link #collectStates(ChangeLog, boolean)} recursively.
      *
-     * @param changeLog the <code>ChangeLog</code> collecting the transient
-     * item states present in a given tree.
-     * @param throwOnStale If the given flag is true, this methods throws
-     * StaleItemStateException if this state is stale.
-     * @throws StaleItemStateException if <code>throwOnStale</code> is true and
-     * this state is stale.
+     * @param overlayedState
      */
-    void collectStates(ChangeLog changeLog, boolean throwOnStale) throws StaleItemStateException {
-        checkIsSessionState();
-        if (throwOnStale && Status.isStale(getStatus())) {
-            String msg = "Cannot save changes: " + getId() + " has been modified externally.";
-            log.debug(msg);
-            throw new StaleItemStateException(msg);
-        }
-        // only interested in transient modifications or stale-modified states
-        switch (getStatus()) {
-            case Status.NEW:
-                changeLog.added(this);
-                break;
-            case Status.EXISTING_MODIFIED:
-            case Status.STALE_MODIFIED:
-                changeLog.modified(this);
-                break;
-            case Status.EXISTING_REMOVED:
-                changeLog.deleted(this);
-                break;
-            default:
-                log.debug("Collecting states: Ignored ItemState with status " + getStatus());
+    private void setOverLayedState(ItemState overlayedState) {
+        if (this.overlayedState != null) {
+           this.overlayedState.removeListener(this);
         }
+        this.overlayedState = overlayedState;
+        this.overlayedState.addListener(this);
     }
 
     /**
@@ -627,5 +491,20 @@
                 String msg = "Cannot mark item state with status " + status + " modified.";
                 throw new IllegalStateException(msg);
         }
+    }
+
+    EffectiveNodeType getEffectiveNodeType() throws RepositoryException {
+        try {
+            EffectiveNodeType ent = getNodeTypeRegistry().getEffectiveNodeType(getParent().getNodeTypeNames());
+            return ent;
+        } catch (ItemStateException e) {
+            throw new RepositoryException("Error while accessing Definition ", e);
+        } catch (NodeTypeConflictException e) {
+            throw new RepositoryException("Error while accessing Definition ", e);
+        }
+    }
+
+    NodeTypeRegistry getNodeTypeRegistry() {
+        return ntReg;
     }
 }

Modified: jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/state/ItemStateFactory.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/state/ItemStateFactory.java?view=diff&rev=506927&r1=506926&r2=506927
==============================================================================
--- jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/state/ItemStateFactory.java (original)
+++ jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/state/ItemStateFactory.java Tue Feb 13 01:31:36 2007
@@ -18,6 +18,10 @@
 
 import org.apache.jackrabbit.spi.NodeId;
 import org.apache.jackrabbit.spi.PropertyId;
+import org.apache.jackrabbit.jcr2spi.hierarchy.NodeEntry;
+import org.apache.jackrabbit.jcr2spi.hierarchy.PropertyEntry;
+
+import java.util.Iterator;
 
 /**
  * <code>ItemStateFactory</code> provides methods to create child
@@ -28,68 +32,96 @@
 
     /**
      *
-     * @param ism
+     * @param entry
      * @return
      * @throws ItemStateException
      */
-    public NodeState createRootState(ItemStateManager ism) throws ItemStateException;
+    public NodeState createRootState(NodeEntry entry) throws ItemStateException;
 
     /**
      * Creates the child <code>NodeState</code> with the given
      * <code>nodeId</code>.
      *
      * @param nodeId the id of the <code>NodeState</code> to create.
-     * @param ism    the item state manager to retrievev the parent of the
-     *               <code>NodeState</code> to create.
+     * @param entry the <code>HierarchyEntry</code> the new state should
+     * be attached to.
      * @return the created <code>NodeState</code>.
      * @throws NoSuchItemStateException if there is no such <code>NodeState</code>.
-     * @throws ItemStateException       if an error occurs while retrieving the
-     *                                  <code>NodeState</code>.
+     * @throws ItemStateException if an error occurs while retrieving the <code>NodeState</code>.
      */
-    public NodeState createNodeState(NodeId nodeId, ItemStateManager ism)
-            throws NoSuchItemStateException, ItemStateException;
+    public NodeState createNodeState(NodeId nodeId, NodeEntry entry)
+        throws NoSuchItemStateException, ItemStateException;
+
 
     /**
-     * Creates the child <code>NodeState</code> with the given
-     * <code>nodeId</code>.
+     * Tries to retrieve the <code>NodeState</code> with the given <code>NodeId</code>
+     * and if the state exists, fills in the NodeEntries missing between the
+     * last known NodeEntry marked by <code>anyParent</code>.
      *
-     * @param nodeId the id of the <code>NodeState</code> to create.
-     * @param parent the parent of the <code>NodeState</code> to create.
+     * @param nodeId
+     * @param anyParent
      * @return the created <code>NodeState</code>.
      * @throws NoSuchItemStateException if there is no such <code>NodeState</code>.
-     * @throws ItemStateException       if an error occurs while retrieving the
-     *                                  <code>NodeState</code>.
+     * @throws ItemStateException if an error occurs while retrieving the <code>NodeState</code>.
      */
-    public NodeState createNodeState(NodeId nodeId, NodeState parent)
-            throws NoSuchItemStateException, ItemStateException;
+    public NodeState createDeepNodeState(NodeId nodeId, NodeEntry anyParent) throws NoSuchItemStateException, ItemStateException;
+
 
     /**
      * Creates the <code>PropertyState</code> with the given
      * <code>propertyId</code>.
      *
      * @param propertyId the id of the <code>PropertyState</code> to create.
-     * @param parent the parent of the <code>PropertyState</code> to create.
+     * @param entry the <code>HierarchyEntry</code> the new state should
+     * be attached to.
      * @return the created <code>PropertyState</code>.
      * @throws NoSuchItemStateException if there is no such <code>PropertyState</code>.
      * @throws ItemStateException       if an error occurs while retrieving the
      *                                  <code>PropertyState</code>.
      */
-    public PropertyState createPropertyState(PropertyId propertyId,
-                                             NodeState parent)
-            throws NoSuchItemStateException, ItemStateException;
+    public PropertyState createPropertyState(PropertyId propertyId, PropertyEntry entry)
+        throws NoSuchItemStateException, ItemStateException;
+
+
+    /**
+     * Tries to retrieve the <code>PropertyState</code> with the given <code>PropertyId</code>
+     * and if the state exists, fills in the HierarchyEntries missing between the
+     * last known NodeEntry marked by <code>anyParent</code>.
+     *
+     * @param propertyId
+     * @param anyParent
+     * @return
+     * @throws NoSuchItemStateException if there is no such <code>NodeState</code>.
+     * @throws ItemStateException if an error occurs while retrieving the <code>NodeState</code>.
+     */
+    public PropertyState createDeepPropertyState(PropertyId propertyId, NodeEntry anyParent) throws NoSuchItemStateException, ItemStateException;
 
+    /**
+     * Returns an Iterator over <code>ChildInfo</code>s for the given <code>NodeState</code>.
+     *
+     * @param nodeId
+     */
+    public Iterator getChildNodeInfos(NodeId nodeId) throws NoSuchItemStateException, ItemStateException;
 
     /**
+     * Returns the NodeReferences for the NodeState with the given ID.
      *
      * @param nodeState
+     * @return NodeReferences
+     */
+    public NodeReferences getNodeReferences(NodeState nodeState);
+
+    /**
+     * Adds the given <code>ItemStateCreationListener</code>.
+     *
+     * @param listener
      */
-    public ChildNodeEntries getChildNodeEntries(NodeState nodeState) throws NoSuchItemStateException, ItemStateException;
+    public void addCreationListener(ItemStateCreationListener listener);
 
     /**
-     * Set the cache used to retrieve item states that have already been
-     * built before.
+     * Removes the given <code>ItemStateCreationListener</code>.
      *
-     * @param cache
+     * @param listener
      */
-    public void setCache(ItemStateCache cache);
+    public void removeCreationListener(ItemStateCreationListener listener);
 }

Modified: jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/state/ItemStateValidator.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/state/ItemStateValidator.java?view=diff&rev=506927&r1=506926&r2=506927
==============================================================================
--- jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/state/ItemStateValidator.java (original)
+++ jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/state/ItemStateValidator.java Tue Feb 13 01:31:36 2007
@@ -20,7 +20,8 @@
 import org.apache.jackrabbit.jcr2spi.nodetype.NodeTypeRegistry;
 import org.apache.jackrabbit.jcr2spi.nodetype.EffectiveNodeType;
 import org.apache.jackrabbit.jcr2spi.ManagerProvider;
-import org.apache.jackrabbit.jcr2spi.state.entry.ChildNodeEntry;
+import org.apache.jackrabbit.jcr2spi.hierarchy.NodeEntry;
+import org.apache.jackrabbit.jcr2spi.hierarchy.PropertyEntry;
 import org.apache.jackrabbit.jcr2spi.util.LogUtil;
 import org.apache.jackrabbit.jcr2spi.security.AccessManager;
 import org.slf4j.LoggerFactory;
@@ -84,16 +85,10 @@
      * option for <code>{@link #checkRemoveItem}</code> method:<p/>
      * check that target node is not being referenced
      */
-    public static final int CHECK_REFERENCES = 16;
-
-    /**
-     * option for <code>{@link #checkRemoveItem}</code> method:<p/>
-     * check that target node is not being referenced
-     */
     public static final int CHECK_COLLISION = 32;
 
     public static final int CHECK_NONE = 0;
-    public static final int CHECK_ALL = CHECK_ACCESS | CHECK_LOCK | CHECK_VERSIONING | CHECK_CONSTRAINTS | CHECK_COLLISION | CHECK_REFERENCES;
+    public static final int CHECK_ALL = CHECK_ACCESS | CHECK_LOCK | CHECK_VERSIONING | CHECK_CONSTRAINTS | CHECK_COLLISION;
 
     /**
      * node type registry
@@ -132,8 +127,8 @@
      * @throws ConstraintViolationException if any of the validations fail
      * @throws RepositoryException          if another error occurs
      */
-    public void validate(NodeState nodeState)
-            throws ConstraintViolationException, RepositoryException {
+    public void validate(NodeState nodeState) throws ConstraintViolationException,
+        RepositoryException {
         // effective primary node type
         EffectiveNodeType entPrimary = ntReg.getEffectiveNodeType(nodeState.getNodeTypeName());
         // effective node type (primary type incl. mixins)
@@ -167,7 +162,7 @@
         QNodeDefinition[] cnda = entPrimaryAndMixins.getMandatoryNodeDefs();
         for (int i = 0; i < cnda.length; i++) {
             QNodeDefinition cnd = cnda[i];
-            if (!nodeState.hasChildNodeEntry(cnd.getQName())) {
+            if (!nodeState.getNodeEntry().hasNodeEntry(cnd.getQName())) {
                 String msg = safeGetJCRPath(nodeState)
                         + ": mandatory child node " + cnd.getQName()
                         + " does not exist";
@@ -376,8 +371,15 @@
         VersionException, LockException, ItemNotFoundException,
         ItemExistsException, PathNotFoundException, RepositoryException {
 
-        QPropertyDefinition def = propState.getDefinition();
-        checkWriteProperty(propState.getParent(), propState.getQName(), def, options);
+        try {
+            NodeState parent = propState.getParent();
+            QPropertyDefinition def = propState.getDefinition();
+            checkWriteProperty(parent, propState.getQName(), def, options);
+        } catch (NoSuchItemStateException e) {
+            throw new ItemNotFoundException(e);
+        } catch (ItemStateException e) {
+            throw new RepositoryException(e);
+        }
     }
 
     /**
@@ -432,7 +434,7 @@
      * @throws PathNotFoundException
      * @throws RepositoryException
      */
-    public void checkWriteProperty(NodeState parentState, QName propertyName, QPropertyDefinition definition, int options)
+    private void checkWriteProperty(NodeState parentState, QName propertyName, QPropertyDefinition definition, int options)
         throws ConstraintViolationException, AccessDeniedException,
         VersionException, LockException, ItemNotFoundException,
         ItemExistsException, PathNotFoundException, RepositoryException {
@@ -534,8 +536,6 @@
      *                    parent node is checked-out</li>
      *                    <li><code>{@link #CHECK_CONSTRAINTS}</code>:
      *                    make sure no node type constraints would be violated</li>
-     *                    <li><code>{@link #CHECK_REFERENCES}</code>:
-     *                    make sure no references exist on target node</li>
      *                    </ul>
      * @throws ConstraintViolationException
      * @throws AccessDeniedException
@@ -551,26 +551,28 @@
             ReferentialIntegrityException, RepositoryException {
 
         // TODO: missing check if all affected child-states can be removed as well
-        // NOTE: referencial integrity should be asserted for all child-nodes.
-        NodeState parentState = targetState.getParent();
-        if (parentState == null) {
+        if (targetState.isNode() && ((NodeState)targetState).isRoot()) {
             // root node
             throw new ConstraintViolationException("Cannot remove root node.");
         }
         // check parent
-        checkIsWritable(parentState, options);
+        try {
+            checkIsWritable(targetState.getParent(), options);
+        } catch (NoSuchItemStateException e) {
+            throw new ItemNotFoundException(e);
+        } catch (ItemStateException e) {
+            throw new RepositoryException(e);
+        }
 
         // access rights
         if ((options & CHECK_ACCESS) == CHECK_ACCESS) {
             try {
                 // make sure current session is allowed to remove target node
                 if (!mgrProvider.getAccessManager().canRemove(targetState)) {
-                    throw new AccessDeniedException(safeGetJCRPath(targetState)
-                            + ": not allowed to remove node");
+                    throw new AccessDeniedException(safeGetJCRPath(targetState) + ": not allowed to remove node");
                 }
             } catch (ItemNotFoundException infe) {
-                String msg = "internal error: failed to check access rights for "
-                        + safeGetJCRPath(targetState);
+                String msg = "internal error: failed to check access rights for " + safeGetJCRPath(targetState);
                 log.debug(msg);
                 throw new RepositoryException(msg, infe);
             }
@@ -581,10 +583,6 @@
             // check if target not protected and not mandatory
             checkRemoveConstraints(targetState);
         }
-        // check referential integrity of state to be deleted
-        if ((options & CHECK_REFERENCES) == CHECK_REFERENCES) {
-            checkReferences(targetState);
-        }
     }
 
     /**
@@ -603,8 +601,14 @@
      */
     private void checkIsCheckedOut(ItemState itemState)
             throws PathNotFoundException, VersionException, RepositoryException {
-        NodeState nodeState = (itemState.isNode()) ? (NodeState)itemState : itemState.getParent();
-        mgrProvider.getVersionManager().checkIsCheckedOut(nodeState);
+        try {
+            NodeState nodeState = (itemState.isNode()) ? (NodeState)itemState : itemState.getParent();
+            mgrProvider.getVersionManager().checkIsCheckedOut(nodeState);
+        } catch (NoSuchItemStateException e) {
+            throw new ItemNotFoundException(e);
+        } catch (ItemStateException e) {
+            throw new RepositoryException(e);
+        }
     }
 
     /**
@@ -616,12 +620,17 @@
      * @throws LockException         if write access to the specified path is not allowed
      * @throws RepositoryException   if another error occurs
      */
-    private void checkLock(ItemState itemState)
-            throws LockException, RepositoryException {
-        // make sure there's no foreign lock present the node (or the parent node
-        // in case the state represents a PropertyState).
-        NodeState nodeState = (itemState.isNode()) ? ((NodeState)itemState) : itemState.getParent();
-        mgrProvider.getLockManager().checkLock(nodeState);
+    private void checkLock(ItemState itemState) throws LockException, RepositoryException {
+        try {
+            // make sure there's no foreign lock present the node (or the parent node
+            // in case the state represents a PropertyState).
+            NodeState nodeState = (itemState.isNode()) ? ((NodeState)itemState) : itemState.getParent();
+            mgrProvider.getLockManager().checkLock(nodeState);
+        } catch (NoSuchItemStateException e) {
+            throw new ItemNotFoundException(e);
+        } catch (ItemStateException e) {
+            throw new RepositoryException(e);
+        }
     }
 
     /**
@@ -686,22 +695,23 @@
      * @throws RepositoryException
      */
     private void checkCollision(NodeState parentState, QName propertyName) throws ItemExistsException, RepositoryException {
+        NodeEntry parentEntry = (NodeEntry) parentState.getHierarchyEntry();
         // check for name collisions with existing child nodes
-        if (parentState.hasChildNodeEntry(propertyName)) {
-            String msg = "there's already a child node with name " + propertyName;
+        if (parentEntry.hasNodeEntry(propertyName)) {
+            String msg = "Child node with name '" + propertyName + "' already exists.";
             log.debug(msg);
             throw new RepositoryException(msg);
         }
         // check for name collisions with existing properties
-        if (parentState.hasPropertyName(propertyName)) {
-            PropertyState errorState = null;
+        PropertyEntry pe = parentEntry.getPropertyEntry(propertyName);
+        if (pe != null) {
             try {
-                errorState = parentState.getPropertyState(propertyName);
+                pe.getPropertyState();
             } catch (ItemStateException e) {
                 // should not occur. existance has been asserted before
                 throw new RepositoryException(e);
             }
-            throw new ItemExistsException(safeGetJCRPath(errorState));
+            throw new ItemExistsException("Property '" + pe.getQName() + "' already exists.");
         }
     }
 
@@ -721,12 +731,10 @@
                 + nodeName.getLocalName() + "' to " + safeGetJCRPath(parentState)
                 + ": colliding with same-named existing property");
 
-        } else if (parentState.hasChildNodeEntry(nodeName)) {
+        } else if (parentState.hasChildNodeEntry(nodeName, Path.INDEX_DEFAULT)) {
             // retrieve the existing node state that ev. conflicts with the new one.
             try {
-                ChildNodeEntry cne = parentState.getChildNodeEntry(nodeName, Path.INDEX_DEFAULT);
-                // cne must not be null, since existence has been checked before
-                NodeState conflictingState = cne.getNodeState();
+                NodeState conflictingState = parentState.getChildNodeState(nodeName, Path.INDEX_DEFAULT);
                 QNodeDefinition conflictDef = conflictingState.getDefinition();
                 QNodeDefinition newDef = getApplicableNodeDefinition(nodeName, nodeTypeName, parentState);
 
@@ -740,28 +748,6 @@
             } catch (ItemStateException e) {
                 // should not occur, since existence has been asserted before
                 throw new RepositoryException(e);
-            }
-        }
-    }
-
-    /**
-     *
-     * @param toDelete
-     * @throws ReferentialIntegrityException
-     * @throws RepositoryException
-     */
-    private void checkReferences(ItemState toDelete) throws ReferentialIntegrityException, RepositoryException {
-        if (!toDelete.isNode()) {
-            // PropertyState: nothing to do.
-            return;
-        }
-
-        NodeState targetState = (NodeState)toDelete;
-        EffectiveNodeType ent = getEffectiveNodeType(targetState);
-        if (ent.includesNodeType(QName.MIX_REFERENCEABLE)) {
-            ItemStateManager stateMgr = mgrProvider.getItemStateManager();
-            if (stateMgr.hasReferingStates(targetState)) {
-                throw new ReferentialIntegrityException(safeGetJCRPath(targetState) + ": cannot remove node with references");
             }
         }
     }

Modified: jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/state/NodeReferences.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/state/NodeReferences.java?view=diff&rev=506927&r1=506926&r2=506927
==============================================================================
--- jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/state/NodeReferences.java (original)
+++ jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/state/NodeReferences.java Tue Feb 13 01:31:36 2007
@@ -21,7 +21,7 @@
 /**
  * <code>NodeReferences</code>...
  */
-interface NodeReferences {
+public interface NodeReferences {
 
     /**
      * Returns a flag indicating whether the <code>Node</code> identified by this