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 2006/10/09 18:46:18 UTC

svn commit: r454423 [2/3] - in /jackrabbit/trunk/contrib/spi: jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/ jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/lock/ jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/state/ jcr2spi/src/main/jav...

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?view=diff&rev=454423&r1=454422&r2=454423
==============================================================================
--- 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 Mon Oct  9 09:46:16 2006
@@ -27,6 +27,11 @@
 import org.apache.jackrabbit.spi.ItemId;
 import org.apache.jackrabbit.spi.Event;
 import org.apache.jackrabbit.spi.PropertyId;
+import org.apache.jackrabbit.jcr2spi.state.entry.ChildNodeEntry;
+import org.apache.jackrabbit.jcr2spi.state.entry.ChildPropertyEntry;
+import org.apache.jackrabbit.jcr2spi.state.entry.PropertyReference;
+import org.apache.jackrabbit.jcr2spi.state.entry.ChildNodeReference;
+import org.apache.jackrabbit.value.QValue;
 import org.slf4j.LoggerFactory;
 import org.slf4j.Logger;
 
@@ -140,8 +145,8 @@
     protected NodeState(QName name, String uuid, NodeState parent,
                         QName nodeTypeName, QNodeDefinition definition,
                         int initialStatus, ItemStateFactory isf,
-                        IdFactory idFactory) {
-        super(parent, initialStatus, idFactory);
+                        IdFactory idFactory, boolean isWorkspaceState) {
+        super(parent, initialStatus, idFactory, isWorkspaceState);
         this.name = name;
         this.uuid = uuid;
         this.nodeTypeName = nodeTypeName;
@@ -164,7 +169,7 @@
                         IdFactory idFactory) {
         super(overlayedState, parent, initialStatus, idFactory);
         this.isf = isf;
-        pull();
+        reset();
     }
 
     void init(QName[] mixinTypeNames, Collection childEntries, Collection propertyNames, NodeReferences references) {
@@ -173,14 +178,14 @@
         }
         // re-create property references
         propertiesInAttic.clear();
-        properties.clear(); // TODO: any more cleanup work to do? try some kind of merging?
+        properties.clear();
         Iterator it = propertyNames.iterator();
         while (it.hasNext()) {
             QName propName = (QName) it.next();
-            properties.put(propName, new PropertyReference(this, propName, isf, idFactory));
+            properties.put(propName, PropertyReference.create(this, propName, isf, idFactory));
         }
         // re-create child node entries
-        childNodeEntries.removeAll(); // TODO: any mre cleanup work to do? try some kind of merging?
+        childNodeEntries.removeAll();
         it = childEntries.iterator();
         while (it.hasNext()) {
             ChildNodeEntry cne = (ChildNodeEntry) it.next();
@@ -190,119 +195,7 @@
         this.references = references;
     }
 
-    /**
-     * {@inheritDoc}
-     */
-    protected synchronized void pull() {
-        if (overlayedState != null) {
-            synchronized (overlayedState) {
-                NodeState nodeState = (NodeState) overlayedState;
-                name = nodeState.name;
-                uuid = nodeState.uuid;
-                nodeTypeName = nodeState.nodeTypeName;
-                definition = nodeState.definition;
-
-                init(nodeState.getMixinTypeNames(), nodeState.getChildNodeEntries(), nodeState.getPropertyNames(), nodeState.getNodeReferences());
-            }
-        }
-    }
-
-    protected synchronized void refresh(Event event, ChangeLog changeLog) {
-        NodeId id = getNodeId();
-        switch (event.getType()) {
-            case Event.NODE_ADDED:
-            case Event.PROPERTY_ADDED:
-                if (id.equals(event.getParentId())) {
-                    ItemId evId = event.getItemId();
-                    ItemState newState = null;
-
-                    if (evId.denotesNode()) {
-                        QName name = event.getQPath().getNameElement().getName();
-                        String uuid = (((NodeId)evId).getRelativePath() != null) ? null : ((NodeId)evId).getUUID();
-                        ChildNodeEntry cne = childNodeEntries.add(name, uuid);
-                        try {
-                            newState = cne.getNodeState();
-                        } catch (ItemStateException e) {
-                            log.error("Internal error", e);
-                        }
-                    } else {
-                        PropertyId pId = (PropertyId) event.getItemId();
-                        PropertyReference re = new PropertyReference(this, pId.getQName(), isf, idFactory);
-                        properties.put(pId.getQName(), re);
-                        try {
-                            newState = re.getPropertyState();
-                        } catch (ItemStateException e) {
-                            log.error("Internal error", e);
-                        }
-                    }
-
-                    // connect the transient state to this state and make
-                    // sure its data are updated
-                    if (newState != null && changeLog != null) {
-                        for (Iterator it = changeLog.addedStates(); it.hasNext();) {
-                            ItemState added = (ItemState) it.next();
-                            if (added.getId().equals(evId)) {
-                                added.connect(newState);
-                                added.pull();
-                                break;
-                            }
-                        }
-                    }
-                } else {
-                    // ILLEGAL
-                    throw new IllegalArgumentException("Illegal event type " + event.getType() + " for NodeState.");
-                }
-                break;
-
-            case Event.NODE_REMOVED:
-                if (id.equals(event.getParentId())) {
-                    QName qName = event.getQPath().getNameElement().getName();
-                    int index = event.getQPath().getNameElement().getNormalizedIndex();
-                    childNodeEntries.remove(qName, index);
-                    setStatus(STATUS_MODIFIED);
-                } else if (id.equals(event.getItemId())) {
-                    setStatus(STATUS_REMOVED);
-                } else {
-                    // ILLEGAL
-                    throw new IllegalArgumentException("Illegal event type " + event.getType() + " for NodeState.");
-                }
-                break;
-
-            case Event.PROPERTY_REMOVED:
-                if (id.equals(event.getParentId())) {
-                    PropertyId pId = (PropertyId) event.getItemId();
-                    properties.remove(pId.getQName());
-                    setStatus(STATUS_MODIFIED);
-                } else {
-                    // ILLEGAL
-                    throw new IllegalArgumentException("Illegal event type " + event.getType() + " for NodeState.");
-                }
-                break;
-
-            case Event.PROPERTY_CHANGED:
-            default:
-                // ILLEGAL
-                throw new IllegalArgumentException("Illegal event type " + event.getType() + " for NodeState.");
-        }
-    }
-
-    //--------------------< 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;
-    }
-
+    //----------------------------------------------------------< ItemState >---
     /**
      * Determines if this item state represents a node.
      *
@@ -314,12 +207,21 @@
     }
 
     /**
+     * @see ItemState#getQName()
+     */
+    public final QName getQName() {
+        return name;
+    }
+
+    /**
      * {@inheritDoc}
+     * @see ItemState#getId()
      */
     public ItemId getId() {
         return getNodeId();
     }
 
+    //----------------------------------------------------------< NodeState >---
     /**
      * Returns the id of this node state.
      *
@@ -350,6 +252,14 @@
     }
 
     /**
+     * @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;
+    }
+
+    /**
      * Returns the name of this node's node type.
      *
      * @return the name of this node's node type.
@@ -368,20 +278,6 @@
     }
 
     /**
-     * Sets the names of this node's mixin types.
-     *
-     * @param mixinTypeNames set of names of mixin types
-     */
-    synchronized void setMixinTypeNames(QName[] mixinTypeNames) {
-        if (mixinTypeNames != null) {
-            this.mixinTypeNames = mixinTypeNames;
-        } else {
-            this.mixinTypeNames = new QName[0];
-        }
-        markModified();
-    }
-
-    /**
      * Return all nodetype names that apply to this <code>NodeState</code>
      * including the primary nodetype and the mixins.
      *
@@ -389,8 +285,9 @@
      */
     public synchronized QName[] getNodeTypeNames() {
         // mixin types
-        QName[] types = new QName[mixinTypeNames.length + 1];
-        System.arraycopy(mixinTypeNames, 0, types, 0, mixinTypeNames.length);
+        QName[] mixinNames = getMixinTypeNames();
+        QName[] types = new QName[mixinNames.length + 1];
+        System.arraycopy(mixinNames, 0, types, 0, mixinNames.length);
         // primary type
         types[types.length - 1] = getNodeTypeName();
         return types;
@@ -407,6 +304,21 @@
         return definition;
     }
 
+
+    /**
+     * Return the <code>NodeReferences</code> present on this state or
+     * <code>null</code>.
+     *
+     * @return references
+     */
+    NodeReferences getNodeReferences() {
+        if (getStatus() == Status.NEW) {
+            return null;
+        } else {
+            return references;
+        }
+    }
+
     /**
      * Determines if there are any child node entries.
      *
@@ -493,73 +405,401 @@
     }
 
     /**
-     * TODO: move this method to a node state implementation which contains all transient related methods?
+     * Determines if there is a property entry with the specified
+     * <code>QName</code>.
      *
-     * Adds a child node state to this node state.
+     * @param propName <code>QName</code> object specifying a property name
+     * @return <code>true</code> if there is a property entry with the specified
+     *         <code>QName</code>.
+     */
+    public synchronized boolean hasPropertyName(QName propName) {
+        ChildPropertyEntry entry = (ChildPropertyEntry) properties.get(propName);
+        if (entry == null) {
+            return false;
+        }
+        if (entry.isAvailable()) {
+            try {
+                return entry.getPropertyState().isValid();
+            } catch (ItemStateException e) {
+                // probably deleted in the meantime
+                return false;
+            }
+        } else {
+            // then it must be valid
+            return true;
+        }
+    }
+
+    /**
+     * Returns the names of this node's properties as a set of
+     * <code>QNames</code> objects.
      *
-     * @param child the node state to add.
-     * @param uuid  the uuid of the child node state or <code>null</code> if
-     *              <code>child</code> cannot be identified with a uuid.
-     * @throws IllegalArgumentException if <code>this</code> is not the parent
-     *                                  of <code>child</code>.
+     * @return set of <code>QNames</code> objects
      */
-    synchronized void addChildNodeState(NodeState child, String uuid) {
-        if (child.getParent() != this) {
-            throw new IllegalArgumentException("This NodeState is not the parent of child");
+    public synchronized Collection getPropertyNames() {
+        Collection names;
+        if (getStatus() == Status.EXISTING_MODIFIED) {
+            names = new ArrayList();
+            for (Iterator it = getPropertyEntries().iterator(); it.hasNext(); ) {
+                names.add(((ChildPropertyEntry) it.next()).getName());
+            }
+        } else {
+            // this node state is unmodified, return all
+            names = properties.keySet();
         }
-        ChildNodeEntry cne = ChildNodeReference.create(child, isf, idFactory);
-        childNodeEntries.add(cne);
-        markModified();
+        return Collections.unmodifiableCollection(names);
     }
 
     /**
-     * Renames this node to <code>newName</code>.
+     * Returns the complete collection of {@link ChildPropertyEntry}s.
      *
-     * @param newName the new name for this node state.
-     * @throws IllegalStateException if this is the root node.
+     * @return unmodifiable collection of <code>ChildPropertyEntry</code> objects
      */
-    private synchronized void rename(QName newName) {
-        if (parent == null) {
-            throw new IllegalStateException("root node cannot be renamed");
+    public synchronized Collection getPropertyEntries() {
+        Collection props;
+        if (getStatus() == Status.EXISTING_MODIFIED) {
+            // filter out removed properties
+            props = new ArrayList();
+            for (Iterator it = properties.values().iterator(); it.hasNext(); ) {
+                ChildPropertyEntry propEntry = (ChildPropertyEntry) it.next();
+                if (propEntry.isAvailable()) {
+                    try {
+                        if (propEntry.getPropertyState().isValid()) {
+                            props.add(propEntry);
+                        }
+                    } catch (ItemStateException e) {
+                        // removed in the meantime -> ignore
+                    }
+                } else {
+                    // never been accessed before, assume valid
+                    props.add(propEntry);
+                }
+            }
+        } else {
+            // no need to filter out properties, there are no removed properties
+            props = properties.values();
+        }
+        return Collections.unmodifiableCollection(props);
+    }
+
+    /*
+     * Returns the property state with the given name.
+     *
+     * @param propertyName the name of the property state to return.
+     * @throws NoSuchItemStateException if there is no property state with the
+     *                                  given name.
+     * @throws ItemStateException       if an error occurs while retrieving the
+     *                                  property state.
+     */
+    public synchronized PropertyState getPropertyState(QName propertyName)
+            throws NoSuchItemStateException, ItemStateException {
+        PropertyState propState = getAnyPropertyState(propertyName);
+        if (propState.isValid()) {
+            return propState;
+        } else {
+            throw new NoSuchItemStateException(idFactory.createPropertyId(getNodeId(), propertyName).toString());
         }
-        name = newName;
     }
 
     /**
-     * Notifies this node state that a child node state has been removed.
+     * Returns the property state with the given name and also takes removed
+     * property states into account.
      *
-     * @param nodeState the node state that has been removed.
-     * @throws IllegalArgumentException if <code>this</code> is not the parent
-     *                                  of <code>nodeState</code>.
+     * @param propertyName the name of the property state to return.
+     * @throws NoSuchItemStateException if there is no property state with the
+     *                                  given name.
+     * @throws ItemStateException       if an error occurs while retrieving the
+     *                                  property state.
      */
-    private synchronized void childNodeStateRemoved(NodeState nodeState) {
-        if (nodeState.getParent() != this) {
-            throw new IllegalArgumentException("This NodeState is not the parent of nodeState");
+    public synchronized PropertyState getAnyPropertyState(QName propertyName)
+            throws NoSuchItemStateException, ItemStateException {
+        ChildPropertyEntry propEntry = (ChildPropertyEntry) properties.get(propertyName);
+        if (propEntry == null) {
+            throw new NoSuchItemStateException(idFactory.createPropertyId(getNodeId(), propertyName).toString());
         }
-        // if nodeState does not exist anymore remove its child node entry
-        if (nodeState.getStatus() == STATUS_REMOVED) {
-            List entries = getChildNodeEntries(nodeState.getName());
-            for (Iterator it = entries.iterator(); it.hasNext(); ) {
-                ChildNodeEntry cne = (ChildNodeEntry) it.next();
+        return propEntry.getPropertyState();
+    }
+
+    /**
+     * TODO: find a better way to provide the index of a child node entry
+     * Returns the index of the given <code>ChildNodeEntry</code> and with
+     * <code>name</code>.
+     *
+     * @param name the name of the child node.
+     * @param cne  the <code>ChildNodeEntry</code> instance.
+     * @return the index of the child node entry or <code>0</code> if it is not
+     *         found in this <code>NodeState</code>.
+     */
+    public int getChildNodeIndex(QName name, ChildNodeEntry cne) {
+        List sns = childNodeEntries.get(name);
+        // index is one based
+        int index = 1;
+        for (Iterator it = sns.iterator(); it.hasNext(); ) {
+            ChildNodeEntry e = (ChildNodeEntry) it.next();
+            if (e == cne) {
+                return index;
+            }
+            // skip removed entries
+            try {
+                if (e.isAvailable() && e.getNodeState().isValid()) {
+                    index++;
+                }
+            } catch (ItemStateException ex) {
+                // probably removed or stale
+            }
+        }
+        // not found
+        return Path.INDEX_UNDEFINED;
+    }
+    //--------------------------------------------------< Workspace - State >---
+    /**
+     *
+     * @param event
+     * @param changeLog
+     * @see ItemState#refresh(Event, ChangeLog)
+     */
+    synchronized void refresh(Event event, ChangeLog changeLog) {
+        checkIsWorkspaceState();
+
+        NodeId id = getNodeId();
+        switch (event.getType()) {
+            case Event.NODE_ADDED:
+            case Event.PROPERTY_ADDED:
+                if (id.equals(event.getParentId())) {
+                    ItemId evId = event.getItemId();
+                    ItemState newState = null;
+
+                    if (evId.denotesNode()) {
+                        QName name = event.getQPath().getNameElement().getName();
+                        String uuid = (((NodeId)evId).getPath() != null) ? null : ((NodeId)evId).getUUID();
+                        ChildNodeEntry cne = childNodeEntries.add(name, uuid);
+                        try {
+                            newState = cne.getNodeState();
+                        } catch (ItemStateException e) {
+                            log.error("Internal error", e);
+                        }
+                    } else {
+                        QName pName = ((PropertyId) event.getItemId()).getQName();
+                        ChildPropertyEntry re = PropertyReference.create(this, pName, isf, idFactory);
+                        properties.put(pName, re);
+                        try {
+                            newState = re.getPropertyState();
+                        } catch (ItemStateException e) {
+                            log.error("Internal error", e);
+                        }
+                        // make sure this state is up to date (uuid/mixins)
+                        refresh(pName, event.getType());
+                    }
+
+                    // connect the added state from the transient layer to the
+                    // new workspaceState and make sure its data are updated.
+                    if (newState != null && changeLog != null) {
+                        for (Iterator it = changeLog.addedStates(); it.hasNext();) {
+                            ItemState added = (ItemState) it.next();
+                            if (added.getId().equals(evId)) {
+                                added.connect(newState);
+                                added.merge();
+                                break;
+                            }
+                        }
+                    }
+                    // and let the transiently modified session state now, that
+                    // its workspace state has been touched.
+                    setStatus(Status.MODIFIED);
+                } else {
+                    // ILLEGAL
+                    throw new IllegalArgumentException("Illegal event type " + event.getType() + " for NodeState.");
+                }
+                break;
+
+            case Event.NODE_REMOVED:
+                if (id.equals(event.getParentId())) {
+                    QName qName = event.getQPath().getNameElement().getName();
+                    int index = event.getQPath().getNameElement().getNormalizedIndex();
+                    childNodeEntries.remove(qName, index);
+                    setStatus(Status.MODIFIED);
+                } else if (id.equals(event.getItemId())) {
+                    setStatus(Status.REMOVED);
+                } else {
+                    // ILLEGAL
+                    throw new IllegalArgumentException("Illegal event type " + event.getType() + " for NodeState.");
+                }
+                break;
+
+            case Event.PROPERTY_REMOVED:
+                if (id.equals(event.getParentId())) {
+                    QName pName = ((PropertyId) event.getItemId()).getQName();
+                    properties.remove(pName);
+                    // make sure this state is up to date (uuid/mixins)
+                    refresh(pName, event.getType());
+                    setStatus(Status.MODIFIED);
+                } else {
+                    // ILLEGAL
+                    throw new IllegalArgumentException("Illegal event type " + event.getType() + " for NodeState.");
+                }
+                break;
+
+            case Event.PROPERTY_CHANGED:
+                if (id.equals(event.getParentId())) {
+                    QName pName = ((PropertyId) event.getItemId()).getQName();
+                    if (refresh(pName, event.getType())) {
+                        setStatus(Status.MODIFIED);
+                    }
+                } else {
+                    // ILLEGAL
+                    throw new IllegalArgumentException("Illegal event type " + event.getType() + " for NodeState.");
+                }
+                break;
+            default:
+                // ILLEGAL
+                throw new IllegalArgumentException("Illegal event type " + event.getType() + " for NodeState.");
+        }
+    }
+
+    /**
+     * Returns true, if the uuid or the mixin types of this state have been
+     * modified.
+     *
+     * @param propertyName
+     * @param eventType
+     * @return
+     */
+    private boolean refresh(QName propertyName, int eventType) {
+        if (QName.JCR_UUID.equals(propertyName)) {
+            // TODO: to be fixed.
+        } else if (QName.JCR_MIXINTYPES.equals(propertyName)) {
+            if (eventType == Event.PROPERTY_REMOVED) {
+                mixinTypeNames = QName.EMPTY_ARRAY;
+            } else { // added or changed
                 try {
-                    if (cne.getNodeState() == nodeState) {
-                        childNodeEntries.remove(cne);
-                        break;
+                    PropertyState ps = getPropertyState(propertyName);
+                    QValue[] values = ps.getValues();
+                    QName[] newMixins = new QName[values.length];
+                    for (int i = 0; i < values.length; i++) {
+                        newMixins[i] = QName.valueOf(values[i].getString());
                     }
+                    mixinTypeNames = newMixins;
                 } catch (ItemStateException e) {
-                    // does not exist anymore? TODO: better error handling
-                    log.warn("child node entry does not exist anymore", e);
+                    // should never occur.
+                    log.error("Internal error while updating mixin types.", e);
+                } catch (RepositoryException e) {
+                    // should never occur.
+                    log.error("Internal error while updating mixin types.", e);
+                }
+            }
+            return true;
+        }
+        return false;
+    }
+
+    //----------------------------------------------------< Session - State >---
+    /**
+     * {@inheritDoc}
+     * @see ItemState#reset()
+     */
+    synchronized void reset() {
+        checkIsSessionState();
+
+        if (overlayedState != null) {
+            synchronized (overlayedState) {
+                NodeState wspState = (NodeState) overlayedState;
+                name = wspState.name;
+                uuid = wspState.uuid;
+                nodeTypeName = wspState.nodeTypeName;
+                definition = wspState.definition;
+
+                init(wspState.getMixinTypeNames(), wspState.getChildNodeEntries(), wspState.getPropertyNames(), wspState.getNodeReferences());
+            }
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     * @see ItemState#merge()
+     */
+    synchronized void merge() {
+        checkIsSessionState();
+
+        if (overlayedState != null) {
+            synchronized (overlayedState) {
+                NodeState wspState = (NodeState) overlayedState;
+                name = wspState.name;
+                uuid = wspState.uuid;
+                nodeTypeName = wspState.nodeTypeName;
+                definition = wspState.definition;
+
+                mixinTypeNames = wspState.mixinTypeNames;
+                references = wspState.getNodeReferences();
+
+                // search for removed properties
+                Collection wspProps = wspState.getPropertyNames();
+                for (Iterator it = properties.keySet().iterator(); it.hasNext();) {
+                    ChildPropertyEntry pe = (ChildPropertyEntry) properties.get((QName) it.next());
+                    if (pe.isAvailable()) {
+                        try {
+                            PropertyState ps = getPropertyState(pe.getName());
+                            if (ps.getStatus() == Status.REMOVED || ps.getStatus() == Status.STALE_DESTROYED) {
+                                it.remove();
+                            }
+                        } catch (ItemStateException e) {
+                            log.error("Internal error while merging item node states.", e);
+                        }
+                    } else if (!wspProps.contains(pe.getName())) {
+                        // not available and not present in wsp-layer any more.
+                        it.remove();
+                    }
+                }
+                // add missing property entries
+                for (Iterator it = wspProps.iterator(); it.hasNext();) {
+                    QName propName = (QName) it.next();
+                    if (!hasPropertyName(propName)) {
+                        properties.put(propName, PropertyReference.create(this, propName, isf, idFactory));
+                    } // else property is already listed
+                }
+
+                Collection wspEntries = wspState.getChildNodeEntries();
+                // remove child entries, that are 'REMOVED' in the wsp layer
+                for (Iterator it = getChildNodeEntries().iterator(); it.hasNext();) {
+                    ChildNodeEntry cne = (ChildNodeEntry) it.next();
+                    if (cne.isAvailable()) {
+                        try {
+                            NodeState ns = cne.getNodeState();
+                            if (ns.getStatus() == Status.REMOVED) {
+                                childNodeEntries.remove(cne.getName(), cne.getIndex());
+                            }
+                        } catch (ItemStateException e) {
+                            // should not occur
+                            log.error("Internal error while merging item node states.", e);
+                        }
+                    } else if (wspState.getChildNodeEntries(cne.getName()).isEmpty()) {
+                        childNodeEntries.remove(cne.getName(), cne.getIndex());
+                    } // TODO: clean up same-named siblings
+                }
+
+                // add missing child entries
+
+                for (Iterator it = wspEntries.iterator(); it.hasNext();) {
+                    ChildNodeEntry wspEntry = (ChildNodeEntry) it.next();
+                    List namedEntries = getChildNodeEntries(wspEntry.getName());
+                    if (namedEntries.isEmpty()) {
+                        // simple case: no cne with the given name
+                        childNodeEntries.add(wspEntry.getName(), wspEntry.getUUID());
+                    } else {
+                        List wspCnes = wspState.getChildNodeEntries(wspEntry.getName());
+                        // TODO: compare sn-siblings an add missing ones
+                    }
                 }
             }
         }
-        markModified();
     }
 
     /**
      * @inheritDoc
      * @see ItemState#remove()
      */
-    public void remove() throws ItemStateException {
+    void remove() throws ItemStateException {
+        checkIsSessionState();
+
         if (!isValid()) {
             throw new ItemStateException("cannot remove an invalid NodeState");
         }
@@ -586,10 +826,10 @@
                 // already removed
             }
         }
-        if (getStatus() == STATUS_EXISTING || getStatus() == STATUS_EXISTING_MODIFIED) {
-            setStatus(STATUS_EXISTING_REMOVED);
-        } else if (getStatus() == STATUS_NEW) {
-            setStatus(STATUS_REMOVED);
+        if (getStatus() == Status.EXISTING || getStatus() == Status.EXISTING_MODIFIED) {
+            setStatus(Status.EXISTING_REMOVED);
+        } else if (getStatus() == Status.NEW) {
+            setStatus(Status.REMOVED);
         }
         // now inform parent
         parent.childNodeStateRemoved(this);
@@ -602,20 +842,17 @@
      * @inheritDoc
      * @see ItemState#revert(Set)
      */
-    public void revert(Set affectedItemStates) {
-        // all states except for 'new' ones must have an overlayed state in order
-        // to be 'reverted'.
-        if (getStatus() != STATUS_NEW && overlayedState == null) {
-            throw new IllegalStateException("revert cannot be called on workspace state");
-        }
+    void revert(Set affectedItemStates) {
+        checkIsSessionState();
+
         // copy to new list, when a property is reverted it may call this node
         // state to remove itself from properties.
         List props = new ArrayList(properties.values());
         for (Iterator it = props.iterator(); it.hasNext(); ) {
-            PropertyReference ref = (PropertyReference) it.next();
-            if (ref.isResolved()) {
+            ChildPropertyEntry entry = (ChildPropertyEntry) it.next();
+            if (entry.isAvailable()) {
                 try {
-                    PropertyState propState = ref.getPropertyState();
+                    PropertyState propState = entry.getPropertyState();
                     propState.revert(affectedItemStates);
                 } catch (ItemStateException e) {
                     // should not happen because PropertyReference is resolved
@@ -644,10 +881,10 @@
         // now revert child node states
         List children = new ArrayList(childNodeEntries);
         for (Iterator it = children.iterator(); it.hasNext(); ) {
-            ChildNodeReference ref = (ChildNodeReference) it.next();
-            if (ref.isResolved()) {
+            ChildNodeEntry entry = (ChildNodeEntry) it.next();
+            if (entry.isAvailable()) {
                 try {
-                    NodeState nodeState = ref.getNodeState();
+                    NodeState nodeState = entry.getNodeState();
                     nodeState.revert(affectedItemStates);
                 } catch (ItemStateException e) {
                     // should not happen because ChildNodeReference is resolved
@@ -660,31 +897,31 @@
 
         // now revert this node state
         switch (getStatus()) {
-            case STATUS_EXISTING:
+            case Status.EXISTING:
                 // nothing to do
                 break;
-            case STATUS_EXISTING_MODIFIED:
-            case STATUS_EXISTING_REMOVED:
-            case STATUS_STALE_MODIFIED:
+            case Status.EXISTING_MODIFIED:
+            case Status.EXISTING_REMOVED:
+            case Status.STALE_MODIFIED:
                 // revert state from overlayed
-                pull();
-                setStatus(STATUS_EXISTING);
+                reset();
+                setStatus(Status.EXISTING);
                 affectedItemStates.add(this);
                 break;
-            case STATUS_NEW:
+            case Status.NEW:
                 // set removed
-                setStatus(STATUS_REMOVED);
+                setStatus(Status.REMOVED);
                 // remove from parent
                 parent.childNodeStateRemoved(this);
                 affectedItemStates.add(this);
                 break;
-            case STATUS_REMOVED:
+            case Status.REMOVED:
                 // shouldn't happen actually, because a 'removed' state is not
                 // accessible anymore
                 log.warn("trying to revert an already removed node state");
                 parent.childNodeStateRemoved(this);
                 break;
-            case STATUS_STALE_DESTROYED:
+            case Status.STALE_DESTROYED:
                 // overlayed state does not exist anymore
                 parent.childNodeStateRemoved(this);
                 affectedItemStates.add(this);
@@ -696,21 +933,23 @@
      * @inheritDoc
      * @see ItemState#collectTransientStates(Set)
      */
-    public void collectTransientStates(Set transientStates) {
+    void collectTransientStates(Set transientStates) {
+        checkIsSessionState();
+
         switch (getStatus()) {
-            case STATUS_EXISTING_MODIFIED:
-            case STATUS_EXISTING_REMOVED:
-            case STATUS_NEW:
-            case STATUS_STALE_DESTROYED:
-            case STATUS_STALE_MODIFIED:
+            case Status.EXISTING_MODIFIED:
+            case Status.EXISTING_REMOVED:
+            case Status.NEW:
+            case Status.STALE_DESTROYED:
+            case Status.STALE_MODIFIED:
                 transientStates.add(this);
         }
         // call available property states
         for (Iterator it = properties.values().iterator(); it.hasNext(); ) {
-            PropertyReference ref = (PropertyReference) it.next();
-            if (ref.isAvailable()) {
+            ChildPropertyEntry entry = (ChildPropertyEntry) it.next();
+            if (entry.isAvailable()) {
                 try {
-                    ref.getPropertyState().collectTransientStates(transientStates);
+                    entry.getPropertyState().collectTransientStates(transientStates);
                 } catch (ItemStateException e) {
                     // should not happen because ref is available
                 }
@@ -732,86 +971,74 @@
     }
 
     /**
-     * Determines if there is a property entry with the specified
-     * <code>QName</code>.
+     * Sets the names of this node's mixin types.
      *
-     * @param propName <code>QName</code> object specifying a property name
-     * @return <code>true</code> if there is a property entry with the specified
-     *         <code>QName</code>.
+     * @param mixinTypeNames set of names of mixin types
      */
-    public synchronized boolean hasPropertyName(QName propName) {
-        PropertyReference ref = (PropertyReference) properties.get(propName);
-        if (ref == null) {
-            return false;
-        }
-        if (ref.isResolved()) {
-            try {
-                return ref.getPropertyState().isValid();
-            } catch (ItemStateException e) {
-                // probably deleted in the meantime
-                return false;
-            }
+    synchronized void setMixinTypeNames(QName[] mixinTypeNames) {
+        checkIsSessionState();
+
+        if (mixinTypeNames != null) {
+            this.mixinTypeNames = mixinTypeNames;
         } else {
-            // then it must be valid
-            return true;
+            this.mixinTypeNames = new QName[0];
         }
+        markModified();
     }
 
     /**
-     * Returns the names of this node's properties as a set of
-     * <code>QNames</code> objects.
+     * Adds a child node state to this node state.
      *
-     * @return set of <code>QNames</code> objects
+     * @param child the node state to add.
+     * @param uuid  the uuid of the child node state or <code>null</code> if
+     *              <code>child</code> cannot be identified with a uuid.
+     * @throws IllegalArgumentException if <code>this</code> is not the parent
+     *                                  of <code>child</code>.
      */
-    public synchronized Collection getPropertyNames() {
-        Collection names;
-        if (getStatus() == STATUS_EXISTING_MODIFIED) {
-            names = new ArrayList();
-            for (Iterator it = getPropertyEntries().iterator(); it.hasNext(); ) {
-                names.add(((ChildPropertyEntry) it.next()).getName());
-            }
-        } else {
-            // this node state is unmodified, return all
-            names = properties.keySet();
+    synchronized void addChildNodeState(NodeState child, String uuid) {
+        checkIsSessionState();
+
+        if (child.getParent() != this) {
+            throw new IllegalArgumentException("This NodeState is not the parent of child");
         }
-        return Collections.unmodifiableCollection(names);
+        ChildNodeEntry cne = ChildNodeReference.create(child, isf, idFactory);
+        childNodeEntries.add(cne);
+        markModified();
     }
 
     /**
-     * Returns the complete collection of {@link ChildPropertyEntry}s.
+     * Notifies this node state that a child node state has been removed.
      *
-     * @return unmodifiable collection of <code>ChildPropertyEntry</code> objects
+     * @param nodeState the node state that has been removed.
+     * @throws IllegalArgumentException if <code>this</code> is not the parent
+     *                                  of <code>nodeState</code>.
      */
-    public synchronized Collection getPropertyEntries() {
-        Collection props;
-        if (getStatus() == STATUS_EXISTING_MODIFIED) {
-            // filter out removed properties
-            props = new ArrayList();
-            for (Iterator it = properties.values().iterator(); it.hasNext(); ) {
-                ChildPropertyEntry propEntry = (ChildPropertyEntry) it.next();
-                if (propEntry.isAvailable()) {
-                    try {
-                        if (propEntry.getPropertyState().isValid()) {
-                            props.add(propEntry);
-                        }
-                    } catch (ItemStateException e) {
-                        // removed in the meantime -> ignore
+    private synchronized void childNodeStateRemoved(NodeState nodeState) {
+        checkIsSessionState();
+
+        if (nodeState.getParent() != this) {
+            throw new IllegalArgumentException("This NodeState is not the parent of nodeState");
+        }
+        // if nodeState does not exist anymore remove its child node entry
+        if (nodeState.getStatus() == Status.REMOVED) {
+            List entries = getChildNodeEntries(nodeState.getQName());
+            for (Iterator it = entries.iterator(); it.hasNext(); ) {
+                ChildNodeEntry cne = (ChildNodeEntry) it.next();
+                try {
+                    if (cne.getNodeState() == nodeState) {
+                        childNodeEntries.remove(cne.getName(), cne.getIndex());
+                        break;
                     }
-                } else {
-                    // never been accessed before, assume valid
-                    props.add(propEntry);
+                } catch (ItemStateException e) {
+                    // does not exist anymore? TODO: better error handling
+                    log.warn("child node entry does not exist anymore", e);
                 }
             }
-        } else {
-            // no need to filter out properties, there are no removed properties
-            props = properties.values();
         }
-        return Collections.unmodifiableCollection(props);
+        markModified();
     }
 
     /**
-     * TODO: move this method to a node state implementation which contains all transient related methods?
-     *
      * Adds a property state to this node state.
      *
      * @param propState the property state to add.
@@ -822,6 +1049,7 @@
      *                                  of <code>propState</code>.
      */
     synchronized void addPropertyState(PropertyState propState) throws ItemExistsException {
+        checkIsSessionState();
         if (propState.getParent() != this) {
             throw new IllegalArgumentException("This NodeState is not the parent of propState");
         }
@@ -837,7 +1065,7 @@
                 properties.remove(propertyName);
             }
             if (existingState != null) {
-                if (existingState.getStatus() == STATUS_EXISTING_REMOVED) {
+                if (existingState.getStatus() == Status.EXISTING_REMOVED) {
                     // move to attic
                     propertiesInAttic.put(propertyName, ref);
                 } else {
@@ -845,7 +1073,7 @@
                 }
             }
         }
-        properties.put(propertyName, new PropertyReference(propState, isf, idFactory));
+        properties.put(propertyName, PropertyReference.create(propState, isf, idFactory));
         markModified();
     }
 
@@ -857,53 +1085,85 @@
      *                                  of <code>propState</code>.
      */
     synchronized void propertyStateRemoved(PropertyState propState) {
+        checkIsSessionState();
         if (propState.getParent() != this) {
             throw new IllegalArgumentException("This NodeState is not the parent of propState");
         }
         // remove property state from map of properties if it does not exist
         // anymore, otherwise leave the property state in the map
-        if (propState.getStatus() == STATUS_REMOVED) {
+        if (propState.getStatus() == Status.REMOVED) {
             properties.remove(propState.getQName());
         }
         markModified();
     }
 
-    /*
-     * Returns the property state with the given name.
+    /**
+     * Reorders the child node <code>insertNode</code> before the child node
+     * <code>beforeNode</code>.
      *
-     * @param propertyName the name of the property state to return.
-     * @throws NoSuchItemStateException if there is no property state with the
-     *                                  given name.
-     * @throws ItemStateException       if an error occurs while retrieving the
-     *                                  property state.
+     * @param insertNode the child node to reorder.
+     * @param beforeNode the child node where to insert the node before. If
+     *                   <code>null</code> the child node <code>insertNode</code>
+     *                   is moved to the end of the child node entries.
+     * @throws NoSuchItemStateException if <code>insertNode</code> or
+     *                                  <code>beforeNode</code> is not a child
+     *                                  node of this <code>NodeState</code>.
      */
-    public synchronized PropertyState getPropertyState(QName propertyName)
-            throws NoSuchItemStateException, ItemStateException {
-        PropertyState propState = getAnyPropertyState(propertyName);
-        if (propState.isValid()) {
-            return propState;
+    synchronized void reorderChildNodeEntries(NodeState insertNode, NodeState beforeNode)
+        throws NoSuchItemStateException {
+        checkIsSessionState();
+
+        childNodeEntries.reorder(insertNode, beforeNode);
+        // mark this state as modified
+        markModified();
+    }
+
+    /**
+     * Moves a <code>ChildNodeEntry</code> to a new parent. If the new parent
+     * is this <code>NodeState</code>, the child state is renamed and moved
+     * to the end of the child entries collection.
+     *
+     * @param newParent
+     * @param childState
+     * @param newName
+     * @param newName <code>QName</code> object specifying the entry's new name
+     * @throws RepositoryException if the given child state is not a child
+     * of this node state.
+     */
+    synchronized void moveChildNodeEntry(NodeState newParent, NodeState childState, QName newName, QNodeDefinition newDefinition)
+        throws RepositoryException {
+        checkIsSessionState();
+
+        ChildNodeEntry oldEntry = childNodeEntries.remove(childState);
+        if (oldEntry != null) {
+            childState.rename(newName);
+            // re-parent target node
+            childState.parent = newParent;
+            // set definition according to new definition required by the new parent
+            childState.definition = newDefinition;
+            // add child node entry to new parent
+            newParent.childNodeEntries.add(childState);
         } else {
-            throw new NoSuchItemStateException(idFactory.createPropertyId(getNodeId(), propertyName).toString());
+            throw new RepositoryException("Unexpected error: Child state to be renamed does not exist.");
         }
+        // mark both this and newParent modified
+        markModified();
+        newParent.markModified();
     }
 
     /**
-     * Returns the property state with the given name and also takes removed
-     * property states into account.
+     * Renames this node to <code>newName</code>.
      *
-     * @param propertyName the name of the property state to return.
-     * @throws NoSuchItemStateException if there is no property state with the
-     *                                  given name.
-     * @throws ItemStateException       if an error occurs while retrieving the
-     *                                  property state.
+     * @param newName the new name for this node state.
+     * @throws IllegalStateException if this is the root node.
      */
-    public synchronized PropertyState getAnyPropertyState(QName propertyName)
-            throws NoSuchItemStateException, ItemStateException {
-        PropertyReference propRef = (PropertyReference) properties.get(propertyName);
-        if (propRef == null) {
-            throw new NoSuchItemStateException(idFactory.createPropertyId(getNodeId(), propertyName).toString());
+    private synchronized void rename(QName newName) {
+        checkIsSessionState();
+
+        if (parent == null) {
+            throw new IllegalStateException("root node cannot be renamed");
         }
-        return propRef.getPropertyState();
+        name = newName;
     }
 
     //---------------------------------------------------------< diff methods >
@@ -917,11 +1177,14 @@
      *         been added.
      */
     public synchronized Set getAddedPropertyNames() {
-        if (!hasOverlayedState()) {
+        checkIsSessionState();
+
+        if (getStatus() == Status.NEW) {
+            // state is new -> all
             return Collections.unmodifiableSet(properties.keySet());
         }
 
-        NodeState other = (NodeState) getOverlayedState();
+        NodeState other = (NodeState) getWorkspaceState();
         HashSet set = new HashSet(properties.keySet());
         set.removeAll(other.properties.keySet());
         return set;
@@ -934,7 +1197,10 @@
      * @return collection of added child node entries
      */
     public synchronized Collection getAddedChildNodeEntries() {
-        if (!hasOverlayedState()) {
+        checkIsSessionState();
+
+        if (getStatus() == Status.NEW) {
+            // state is new -> all child nodes are new too
             return childNodeEntries;
         }
 
@@ -942,7 +1208,7 @@
         for (Iterator it = childNodeEntries.iterator(); it.hasNext(); ) {
             ChildNodeEntry cne = (ChildNodeEntry) it.next();
             try {
-                if (cne.getNodeState().getStatus() == STATUS_NEW) {
+                if (cne.getNodeState().getStatus() == Status.NEW) {
                     added.add(cne);
                 }
             } catch (ItemStateException e) {
@@ -961,11 +1227,13 @@
      *         been removed.
      */
     public synchronized Set getRemovedPropertyNames() {
-        if (!hasOverlayedState()) {
+        checkIsSessionState();
+
+        if (getStatus() == Status.NEW) {
             return Collections.EMPTY_SET;
         }
 
-        NodeState other = (NodeState) getOverlayedState();
+        NodeState other = (NodeState) getWorkspaceState();
         HashSet set = new HashSet(other.properties.keySet());
         set.removeAll(properties.keySet());
         return set;
@@ -978,7 +1246,9 @@
      * @return collection of removed child node entries
      */
     public synchronized Collection getRemovedChildNodeEntries() {
-        if (!hasOverlayedState()) {
+        checkIsSessionState();
+
+        if (getStatus() == Status.NEW) {
             return Collections.EMPTY_LIST;
         }
 
@@ -986,7 +1256,7 @@
         for (Iterator it = childNodeEntries.iterator(); it.hasNext(); ) {
             ChildNodeEntry cne = (ChildNodeEntry) it.next();
             try {
-                if (cne.getNodeState().getStatus() == STATUS_EXISTING_REMOVED) {
+                if (cne.getNodeState().getStatus() == Status.EXISTING_REMOVED) {
                     removed.add(cne);
                 }
             } catch (ItemStateException e) {
@@ -996,116 +1266,7 @@
         return removed;
     }
 
-    /**
-     * Reorders the child node <code>insertNode</code> before the child node
-     * <code>beforeNode</code>.
-     *
-     * @param insertNode the child node to reorder.
-     * @param beforeNode the child node where to insert the node before. If
-     *                   <code>null</code> the child node <code>insertNode</code>
-     *                   is moved to the end of the child node entries.
-     * @throws NoSuchItemStateException if <code>insertNode</code> or
-     *                                  <code>beforeNode</code> is not a child
-     *                                  node of this <code>NodeState</code>.
-     */
-    synchronized void reorderChildNodeEntries(NodeState insertNode, NodeState beforeNode)
-        throws NoSuchItemStateException {
-        childNodeEntries.reorder(insertNode, beforeNode);
-        // mark this state as modified
-        markModified();
-    }
-
-    /**
-     * Moves a <code>ChildNodeEntry</code> to a new parent. If the new parent
-     * is this <code>NodeState</code>, the child state is renamed and moved
-     * to the end of the child entries collection.
-     *
-     * @param newParent
-     * @param childState
-     * @param newName
-     * @param newName <code>QName</code> object specifying the entry's new name
-     * @throws RepositoryException if the given child state is not a child
-     * of this node state.
-     */
-    synchronized void moveChildNodeEntry(NodeState newParent, NodeState childState, QName newName, QNodeDefinition newDefinition)
-        throws RepositoryException {
-        ChildNodeEntry oldEntry = childNodeEntries.remove(childState);
-        if (oldEntry != null) {
-            childState.rename(newName);
-            // re-parent target node
-            childState.parent = newParent;
-            // set definition according to new definition required by the new parent
-            childState.definition = newDefinition;
-            // add child node entry to new parent
-            newParent.childNodeEntries.add(childState);
-        } else {
-            throw new RepositoryException("Unexpected error: Child state to be renamed does not exist.");
-        }
-        // mark both this and newParent modified
-        markModified();
-        newParent.markModified();
-    }
-
-    /**
-     * Return the <code>NodeReferences</code> present on this state or
-     * <code>null</code>.
-     *
-     * @return references
-     */
-    NodeReferences getNodeReferences() {
-        if (hasOverlayedState()) {
-            return ((NodeState)getOverlayedState()).references;
-        } else {
-            return references;
-        }
-    }
-
-    /**
-     * Set the <code>NodeReferences</code> for this state.
-     *
-     * @param references
-     */
-    void setNodeReferences(NodeReferences references) {
-        if (getOverlayedState() != null) {
-            throw new UnsupportedOperationException("Cannot set references to a transient node state.");
-        }
-        this.references = references;
-    }
-
-    /**
-     * TODO: find a better way to provide the index of a child node entry
-     * Returns the index of the given <code>ChildNodeEntry</code> and with
-     * <code>name</code>.
-     *
-     * @param name the name of the child node.
-     * @param cne  the <code>ChildNodeEntry</code> instance.
-     * @return the index of the child node entry or <code>0</code> if it is not
-     *         found in this <code>NodeState</code>.
-     */
-    int getChildNodeIndex(QName name, ChildNodeEntry cne) {
-        List sns = childNodeEntries.get(name);
-        // index is one based
-        int index = 1;
-        for (Iterator it = sns.iterator(); it.hasNext(); ) {
-            ChildNodeEntry e = (ChildNodeEntry) it.next();
-            if (e == cne) {
-                return index;
-            }
-            // skip removed entries
-            try {
-                if (e.isAvailable() && e.getNodeState().isValid()) {
-                    index++;
-                }
-            } catch (ItemStateException ex) {
-                // probably removed or stale
-            }
-        }
-        // not found
-        return Path.INDEX_UNDEFINED;
-    }
-
     //-------------------------------< internal >-------------------------------
-
     /**
      * Returns <code>true</code> if the collection of child node
      * <code>entries</code> contains at least one valid <code>ChildNodeEntry</code>.
@@ -1134,7 +1295,6 @@
     }
 
     //------------------------------------------------------< inner classes >---
-
     /**
      * <code>ChildNodeEntries</code> represents an insertion-ordered
      * collection of <code>ChildNodeEntry</code>s that also maintains
@@ -1166,7 +1326,7 @@
          *         is no <code>ChildNodeEntry</code> for <code>nodeState</code>.
          */
         ChildNodeEntry get(NodeState nodeState) {
-            Object o = nameMap.get(nodeState.getName());
+            Object o = nameMap.get(nodeState.getQName());
             if (o == null) {
                 // no matching child node entry
                 return null;
@@ -1330,7 +1490,7 @@
                 }
             }
 
-            ChildNodeEntry entry = createChildNodeEntry(nodeName, uuid);
+            ChildNodeEntry entry = ChildNodeReference.create(NodeState.this, nodeName, uuid, isf, idFactory);
             LinkedEntries.LinkNode ln = entries.add(entry);
 
             if (siblings != null) {
@@ -1466,7 +1626,7 @@
          */
         ChildNodeEntry remove(NodeState nodeState) {
             ChildNodeEntry entry = null;
-            for (Iterator it = get(nodeState.getName()).iterator(); it.hasNext(); ) {
+            for (Iterator it = get(nodeState.getQName()).iterator(); it.hasNext(); ) {
                 ChildNodeEntry tmp = (ChildNodeEntry) it.next();
                 try {
                     if (tmp.isAvailable() && tmp.getNodeState() == nodeState) {
@@ -1484,16 +1644,6 @@
         }
 
         /**
-         * Removes the given child node entry.
-         *
-         * @param entry entry to be removed.
-         * @return the removed entry or <code>null</code> if there is no such entry.
-         */
-        public ChildNodeEntry remove(ChildNodeEntry entry) {
-            return remove(entry.getName(), entry.getIndex());
-        }
-
-        /**
          * Removes all child node entries
          */
         public void removeAll() {
@@ -1522,18 +1672,18 @@
             // the link node where insertLN is ordered before
             LinkedEntries.LinkNode beforeLN = null;
 
-            Object insertObj = nameMap.get(insertNode.getName());
+            Object insertObj = nameMap.get(insertNode.getQName());
             if (insertObj == null) {
                 // no matching child node entry
-                throw new NoSuchItemStateException(insertNode.getName().toString());
+                throw new NoSuchItemStateException(insertNode.getQName().toString());
             }
             insertLN = getLinkNode(insertObj, insertNode);
 
             // now retrieve LinkNode for beforeNode
             if (beforeNode != null) {
-                Object beforeObj = nameMap.get(beforeNode.getName());
+                Object beforeObj = nameMap.get(beforeNode.getQName());
                 if (beforeObj == null) {
-                    throw new NoSuchItemStateException(beforeNode.getName().toString());
+                    throw new NoSuchItemStateException(beforeNode.getQName().toString());
                 }
                 beforeLN = getLinkNode(beforeObj, beforeNode);
             }
@@ -1550,7 +1700,7 @@
 
                     // count our same name siblings until we reach beforeLN
                     int snsCount = 0;
-                    QName insertName = insertNode.getName();
+                    QName insertName = insertNode.getQName();
                     for (Iterator it = entries.linkNodeIterator(); it.hasNext(); ) {
                         LinkedEntries.LinkNode ln = (LinkedEntries.LinkNode) it.next();
                         if (ln == beforeLN) {
@@ -1573,25 +1723,6 @@
         }
 
         /**
-         * Creates a <code>ChildNodeEntry</code> instance based on
-         * <code>nodeName</code> and an optional <code>uuid</code>.
-         *
-         * @param nodeName the name of the child node.
-         * @param uuid     the UUID of the child node. If <code>null</code> the
-         *                 child node cannot be identified with a UUID.
-         * @return the created child node entry.
-         */
-        private ChildNodeEntry createChildNodeEntry(QName nodeName, String uuid) {
-            if (uuid == null) {
-                return new PathElementReference(NodeState.this, nodeName,
-                        isf, idFactory);
-            } else {
-                return new UUIDReference(NodeState.this,
-                        idFactory.createNodeId(uuid), isf, nodeName);
-            }
-        }
-
-        /**
          * Returns the matching <code>LinkNode</code> from a list or a single
          * <code>LinkNode</code>.
          *
@@ -1631,7 +1762,7 @@
                     log.warn("error retrieving a child node state", e);
                 }
             }
-            throw new NoSuchItemStateException(nodeState.getName().toString());
+            throw new NoSuchItemStateException(nodeState.getQName().toString());
         }
 
         //--------------------------------------< unmodifiable Collection view >

Modified: jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/state/PathResolver.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/state/PathResolver.java?view=diff&rev=454423&r1=454422&r2=454423
==============================================================================
--- jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/state/PathResolver.java (original)
+++ jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/state/PathResolver.java Mon Oct  9 09:46:16 2006
@@ -17,6 +17,7 @@
 package org.apache.jackrabbit.jcr2spi.state;
 
 import org.apache.jackrabbit.name.Path;
+import org.apache.jackrabbit.jcr2spi.state.entry.ChildNodeEntry;
 
 /**
  * <code>PathResolver</code> resolves a relative Path starting at a given
@@ -32,7 +33,7 @@
     /**
      * The path to resolve.
      */
-    private final Path relPath;
+    private final Path path;
 
     /**
      * Internal constructor.
@@ -48,47 +49,47 @@
                     "not contain parent path elements");
         }
         this.start = start;
-        this.relPath = relPath;
+        this.path = relPath;
     }
 
     /**
      * Resolves the path starting at <code>start</code>.
      *
      * @param start   the starting point.
-     * @param relPath the path to resolve.
+     * @param path the path to resolve.
      * @return the resolved item state.
      * @throws NoSuchItemStateException the the referenced item state does not
      *                                  exist.
      * @throws ItemStateException       if an error occurs while retrieving the
      *                                  item state.
-     * @throws IllegalArgumentException if relPath is absolute or not normalized
+     * @throws IllegalArgumentException if path is absolute or not normalized
      *                                  or starts with a parent ('..') path
      *                                  element.
      */
-    public static ItemState resolve(ItemState start, Path relPath)
+    public static ItemState resolve(ItemState start, Path path)
             throws NoSuchItemStateException, ItemStateException {
-        return new PathResolver(start, relPath).resolve();
+        return new PathResolver(start, path).resolve();
     }
 
     /**
-     * Looks up the <code>ItemState</code> at <code>relPath</code> starting at
+     * Looks up the <code>ItemState</code> at <code>path</code> starting at
      * <code>start</code>.
      *
      * @param start   the starting point.
-     * @param relPath the path to resolve.
+     * @param path the path to resolve.
      * @return the resolved item state or <code>null</code> if the item is not
      *         available.
      * @throws NoSuchItemStateException the the referenced item state does not
      *                                  exist.
      * @throws ItemStateException       if an error occurs while retrieving the
      *                                  item state.
-     * @throws IllegalArgumentException if relPath is absolute or not normalized
+     * @throws IllegalArgumentException if path is absolute or not normalized
      *                                  or starts with a parent ('..') path
      *                                  element.
      */
-    public static ItemState lookup(ItemState start, Path relPath)
+    public static ItemState lookup(ItemState start, Path path)
             throws NoSuchItemStateException, ItemStateException {
-        return new PathResolver(start, relPath).lookup();
+        return new PathResolver(start, path).lookup();
     }
 
     /**
@@ -102,15 +103,15 @@
     private ItemState resolve()
             throws NoSuchItemStateException, ItemStateException {
         if (!start.isNode()) {
-            throw new NoSuchItemStateException(relPath.toString());
+            throw new NoSuchItemStateException(path.toString());
         }
         NodeState state = (NodeState) start;
-        for (int i = 0; i < relPath.getLength(); i++) {
-            Path.PathElement elem = relPath.getElement(i);
+        for (int i = 0; i < path.getLength(); i++) {
+            Path.PathElement elem = path.getElement(i);
             // check for root element
             if (elem.denotesRoot()) {
                 if (start.getParent() != null) {
-                    throw new NoSuchItemStateException(relPath.toString());
+                    throw new NoSuchItemStateException(path.toString());
                 } else {
                     continue;
                 }
@@ -122,10 +123,10 @@
                         elem.getNormalizedIndex()).getNodeState();
             } else if (elem.getIndex() == 0 // property must not have index
                     && state.hasPropertyName(elem.getName())
-                    && i == relPath.getLength() - 1) { // property must be final path element
+                    && i == path.getLength() - 1) { // property must be final path element
                 return state.getPropertyState(elem.getName());
             } else {
-                throw new NoSuchItemStateException(relPath.toString());
+                throw new NoSuchItemStateException(path.toString());
             }
         }
         return state;
@@ -144,11 +145,11 @@
     private ItemState lookup()
             throws NoSuchItemStateException, ItemStateException {
         if (!start.isNode()) {
-            throw new NoSuchItemStateException(relPath.toString());
+            throw new NoSuchItemStateException(path.toString());
         }
         NodeState state = (NodeState) start;
-        for (int i = 0; i < relPath.getLength(); i++) {
-            Path.PathElement elem = relPath.getElement(i);
+        for (int i = 0; i < path.getLength(); i++) {
+            Path.PathElement elem = path.getElement(i);
             // first try to resolve node
             if (state.hasChildNodeEntry(elem.getName(), elem.getNormalizedIndex())) {
                 ChildNodeEntry cne = state.getChildNodeEntry(elem.getName(), elem.getNormalizedIndex());
@@ -159,11 +160,11 @@
                 }
             } else if (elem.getIndex() == 0 // property must not have index
                     && state.hasPropertyName(elem.getName())
-                    && i == relPath.getLength() - 1) { // property must be final path element
+                    && i == path.getLength() - 1) { // property must be final path element
                 // TODO: check if available
                 return state.getPropertyState(elem.getName());
             } else {
-                throw new NoSuchItemStateException(relPath.toString());
+                throw new NoSuchItemStateException(path.toString());
             }
         }
         return state;

Modified: jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/state/PropertyState.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/state/PropertyState.java?view=diff&rev=454423&r1=454422&r2=454423
==============================================================================
--- jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/state/PropertyState.java (original)
+++ jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/state/PropertyState.java Mon Oct  9 09:46:16 2006
@@ -73,7 +73,7 @@
     protected PropertyState(PropertyState overlayedState, NodeState parent,
                             int initialStatus, IdFactory idFactory) {
         super(overlayedState, parent, initialStatus, idFactory);
-        pull();
+        reset();
     }
 
     /**
@@ -86,8 +86,8 @@
      * @param idFactory
      */
     protected PropertyState(QName name, NodeState parent, QPropertyDefinition definition,
-                            int initialStatus, IdFactory idFactory) {
-        super(parent, initialStatus, idFactory);
+                            int initialStatus, IdFactory idFactory, boolean isWorkspaceState) {
+        super(parent, initialStatus, idFactory, isWorkspaceState);
         this.name = name;
         this.def = definition;
 
@@ -105,128 +105,7 @@
         this.values = (values == null) ? QValue.EMPTY_ARRAY : values;
     }
 
-    /**
-     * @inheritDoc
-     */
-    public void remove() {
-        if (getStatus() == STATUS_NEW) {
-            setStatus(STATUS_REMOVED);
-        } else {
-            setStatus(STATUS_EXISTING_REMOVED);
-        }
-        parent.propertyStateRemoved(this);
-    }
-
-    /**
-     * @inheritDoc
-     * @see ItemState#revert(Set)
-     */
-    public void revert(Set affectedItemStates) {
-        // all states except for 'new' ones must have an overlayed state in order
-        // to be 'reverted'.
-        if (getStatus() != STATUS_NEW && overlayedState == null) {
-            throw new IllegalStateException("revert cannot be called on workspace state");
-        }
-        switch (getStatus()) {
-            case STATUS_EXISTING:
-                // nothing to do
-                break;
-            case STATUS_EXISTING_MODIFIED:
-            case STATUS_EXISTING_REMOVED:
-            case STATUS_STALE_MODIFIED:
-                // revert state from overlayed
-                pull();
-                setStatus(STATUS_EXISTING);
-                affectedItemStates.add(this);
-                break;
-            case STATUS_NEW:
-                // set removed
-                setStatus(STATUS_REMOVED);
-                // and remove from parent
-                parent.propertyStateRemoved(this);
-                affectedItemStates.add(this);
-                break;
-            case STATUS_REMOVED:
-                // shouldn't happen actually, because a 'removed' state is not
-                // accessible anymore
-                log.warn("trying to revert an already removed property state");
-                parent.propertyStateRemoved(this);
-                break;
-            case STATUS_STALE_DESTROYED:
-                // overlayed does not exist anymore
-                parent.propertyStateRemoved(this);
-                affectedItemStates.add(this);
-                break;
-        }
-    }
-
-    /**
-     * @inheritDoc
-     * @see ItemState#collectTransientStates(Set)
-     */
-    public void collectTransientStates(Set transientStates) {
-        switch (getStatus()) {
-            case STATUS_EXISTING_MODIFIED:
-            case STATUS_EXISTING_REMOVED:
-            case STATUS_NEW:
-            case STATUS_STALE_DESTROYED:
-            case STATUS_STALE_MODIFIED:
-                transientStates.add(this);
-                break;
-            case STATUS_EXISTING:
-            case STATUS_REMOVED:
-                log.debug("Collecting transient states: Ignored PropertyState with status " + getStatus());
-                break;
-            default:
-                // should never occur. status is validated upon setStatus(int)
-                log.error("Internal error: Invalid state " + getStatus());
-        }
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    protected synchronized void pull() {
-        if (overlayedState != null) {
-            synchronized (overlayedState) {
-                PropertyState propState = (PropertyState) overlayedState;
-                name = propState.name;
-                type = propState.type;
-                def = propState.def;
-                values = propState.values;
-            }
-        }
-    }
-
-    protected synchronized void refresh(Event event, ChangeLog changeLog) {
-        switch (event.getType()) {
-            case Event.PROPERTY_REMOVED:
-                if (event.getItemId().equals(getId())) {
-                    setStatus(STATUS_REMOVED);
-                } else {
-                    // ILLEGAL
-                    throw new IllegalArgumentException("EventId " + event.getItemId() + " does not match id of this property state.");
-                }
-                break;
-
-            case Event.PROPERTY_CHANGED:
-                if (event.getItemId().equals(getId())) {
-                    setStatus(STATUS_MODIFIED);
-                } else {
-                    // ILLEGAL
-                    throw new IllegalArgumentException("EventId " + event.getItemId() + " does not match id of this property state.");
-                }
-                break;
-
-            case Event.PROPERTY_ADDED:
-            case Event.NODE_ADDED:
-            case Event.NODE_REMOVED:
-            default:
-                throw new IllegalArgumentException("Event type " + event.getType() + " cannot be applied to a PropertyState");
-        }
-    }
-
-    //--------------------< public READ methods and package private Setters >---
+    //----------------------------------------------------------< ItemState >---
     /**
      * Always returns false.
      *
@@ -238,12 +117,24 @@
     }
 
     /**
+     * Returns the name of this property.
+     *
+     * @return the name of this property.
+     * @see ItemState#getQName()
+     */
+    public QName getQName() {
+        return name;
+    }
+
+    /**
      * {@inheritDoc}
+     * @see ItemState#getId()
      */
     public ItemId getId() {
         return getPropertyId();
     }
 
+    //------------------------------------------------------< PropertyState >---
     /**
      * Returns the identifier of this property.
      *
@@ -254,15 +145,6 @@
     }
 
     /**
-     * Returns the name of this property.
-     *
-     * @return the name of this property.
-     */
-    public QName getQName() {
-        return name;
-    }
-
-    /**
      * Returns the type of the property value(s).
      *
      * @return the type of the property value(s).
@@ -305,20 +187,6 @@
     }
 
     /**
-     * Sets the value(s) of this property.
-     *
-     * @param values the new values
-     */
-    void setValues(QValue[] values, int type) throws RepositoryException {
-        // make sure the arguements are consistent and do not violate the
-        // given property definition.
-        validate(values, type, this.def);
-        this.values = values;
-        this.type = type;
-        markModified();
-    }
-
-    /**
      * Convenience method for single valued property states.
      *
      * @return
@@ -333,6 +201,158 @@
         } else {
             return values[0];
         }
+    }
+
+    //----------------------------------------------------< Workspace State >---
+    /**
+     * @see ItemState#refresh(Event, ChangeLog)
+     */
+    synchronized void refresh(Event event, ChangeLog changeLog) {
+        checkIsWorkspaceState();
+
+        switch (event.getType()) {
+            case Event.PROPERTY_REMOVED:
+                if (event.getItemId().equals(getId())) {
+                    setStatus(Status.REMOVED);
+                } else {
+                    // ILLEGAL
+                    throw new IllegalArgumentException("EventId " + event.getItemId() + " does not match id of this property state.");
+                }
+                break;
+
+            case Event.PROPERTY_CHANGED:
+                if (event.getItemId().equals(getId())) {
+                    setStatus(Status.MODIFIED);
+                } else {
+                    // ILLEGAL
+                    throw new IllegalArgumentException("EventId " + event.getItemId() + " does not match id of this property state.");
+                }
+                break;
+
+            case Event.PROPERTY_ADDED:
+            case Event.NODE_ADDED:
+            case Event.NODE_REMOVED:
+            default:
+                throw new IllegalArgumentException("Event type " + event.getType() + " cannot be applied to a PropertyState");
+        }
+    }
+
+    //----------------------------------------------------< Session - State >---
+    /**
+     * {@inheritDoc}
+     * @see ItemState#reset()
+     */
+    synchronized void reset() {
+        checkIsSessionState();
+        if (overlayedState != null) {
+            synchronized (overlayedState) {
+                PropertyState wspState = (PropertyState) overlayedState;
+                name = wspState.name;
+                type = wspState.type;
+                def = wspState.def;
+                values = wspState.values;
+            }
+        }
+    }
+
+    synchronized void merge() {
+        reset();
+    }
+
+    /**
+     * @inheritDoc
+     * @see ItemState#remove()
+     */
+    void remove() {
+        checkIsSessionState();
+
+        if (getStatus() == Status.NEW) {
+            setStatus(Status.REMOVED);
+        } else {
+            setStatus(Status.EXISTING_REMOVED);
+        }
+        parent.propertyStateRemoved(this);
+    }
+
+    /**
+     * @inheritDoc
+     * @see ItemState#revert(Set)
+     */
+    void revert(Set affectedItemStates) {
+        checkIsSessionState();
+
+        switch (getStatus()) {
+            case Status.EXISTING:
+                // nothing to do
+                break;
+            case Status.EXISTING_MODIFIED:
+            case Status.EXISTING_REMOVED:
+            case Status.STALE_MODIFIED:
+                // revert state from overlayed
+                reset();
+                setStatus(Status.EXISTING);
+                affectedItemStates.add(this);
+                break;
+            case Status.NEW:
+                // set removed
+                setStatus(Status.REMOVED);
+                // and remove from parent
+                parent.propertyStateRemoved(this);
+                affectedItemStates.add(this);
+                break;
+            case Status.REMOVED:
+                // shouldn't happen actually, because a 'removed' state is not
+                // accessible anymore
+                log.warn("trying to revert an already removed property state");
+                parent.propertyStateRemoved(this);
+                break;
+            case Status.STALE_DESTROYED:
+                // overlayed does not exist anymore
+                parent.propertyStateRemoved(this);
+                affectedItemStates.add(this);
+                break;
+        }
+    }
+
+    /**
+     * @inheritDoc
+     * @see ItemState#collectTransientStates(Set)
+     */
+    void collectTransientStates(Set transientStates) {
+        checkIsSessionState();
+
+        switch (getStatus()) {
+            case Status.EXISTING_MODIFIED:
+            case Status.EXISTING_REMOVED:
+            case Status.NEW:
+            case Status.STALE_DESTROYED:
+            case Status.STALE_MODIFIED:
+                transientStates.add(this);
+                break;
+            case Status.EXISTING:
+            case Status.REMOVED:
+                log.debug("Collecting transient states: Ignored PropertyState with status " + getStatus());
+                break;
+            default:
+                // should never occur. status is validated upon setStatus(int)
+                log.error("Internal error: Invalid state " + getStatus());
+        }
+    }
+
+    /**
+     * Sets the value(s) of this property.
+     *
+     * @param values the new values
+     */
+    void setValues(QValue[] values, int type) throws RepositoryException {
+        checkIsSessionState();
+
+        // make sure the arguements are consistent and do not violate the
+        // given property definition.
+        validate(values, type, this.def);
+        this.values = values;
+        this.type = type;
+        markModified();
     }
 
     /**

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?view=diff&rev=454423&r1=454422&r2=454423
==============================================================================
--- 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 Mon Oct  9 09:46:16 2006
@@ -18,6 +18,8 @@
 
 import org.apache.jackrabbit.jcr2spi.HierarchyManager;
 import org.apache.jackrabbit.jcr2spi.HierarchyManagerImpl;
+import org.apache.jackrabbit.jcr2spi.state.entry.ChildPropertyEntry;
+import org.apache.jackrabbit.jcr2spi.state.entry.ChildNodeEntry;
 import org.apache.jackrabbit.jcr2spi.util.ReferenceChangeTracker;
 import org.apache.jackrabbit.jcr2spi.util.LogUtil;
 import org.apache.jackrabbit.jcr2spi.nodetype.EffectiveNodeType;
@@ -366,7 +368,7 @@
             throws StaleItemStateException, ItemStateException {
         // fail-fast test: check status of this item's state
         switch (state.getStatus()) {
-            case ItemState.STATUS_NEW:
+            case Status.NEW:
                 {
                     String msg = LogUtil.safeGetJCRPath(state, nsResolver) + ": cannot save a new item.";
                     log.debug(msg);
@@ -375,13 +377,13 @@
         }
         if (throwOnStale) {
             switch (state.getStatus()) {
-                case ItemState.STATUS_STALE_MODIFIED:
+                case Status.STALE_MODIFIED:
                     {
                         String msg = LogUtil.safeGetJCRPath(state, nsResolver) + ": the item cannot be saved because it has been modified externally.";
                         log.debug(msg);
                         throw new StaleItemStateException(msg);
                     }
-                case ItemState.STATUS_STALE_DESTROYED:
+                case Status.STALE_DESTROYED:
                     {
                         String msg = LogUtil.safeGetJCRPath(state, nsResolver) + ": the item cannot be saved because it has been deleted externally.";
                         log.debug(msg);
@@ -398,16 +400,16 @@
             ItemState transientState = (ItemState) it.next();
             // fail-fast test: check status of transient state
             switch (transientState.getStatus()) {
-                case ItemState.STATUS_NEW:
+                case Status.NEW:
                     changeLog.added(transientState);
                     break;
-                case ItemState.STATUS_EXISTING_MODIFIED:
+                case Status.EXISTING_MODIFIED:
                     changeLog.modified(transientState);
                     break;
-                case ItemState.STATUS_EXISTING_REMOVED:
+                case Status.EXISTING_REMOVED:
                     changeLog.deleted(transientState);
                     break;
-                case ItemState.STATUS_STALE_MODIFIED:
+                case Status.STALE_MODIFIED:
                     if (throwOnStale) {
                         String msg = transientState.getId() + ": the item cannot be saved because it has been modified externally.";
                         log.debug(msg);
@@ -415,7 +417,7 @@
                     } else {
                         changeLog.modified(transientState);
                     }
-                case ItemState.STATUS_STALE_DESTROYED:
+                case Status.STALE_DESTROYED:
                     if (throwOnStale) {
                         String msg = transientState.getId() + ": the item cannot be saved because it has been deleted externally.";
                         log.debug(msg);

Added: jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/state/Status.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/state/Status.java?view=auto&rev=454423
==============================================================================
--- jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/state/Status.java (added)
+++ jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/state/Status.java Mon Oct  9 09:46:16 2006
@@ -0,0 +1,70 @@
+/*
+ * $Id$
+ *
+ * Copyright 1997-2005 Day Management AG
+ * Barfuesserplatz 6, 4001 Basel, Switzerland
+ * All Rights Reserved.
+ *
+ * This software is the confidential and proprietary information of
+ * Day Management AG, ("Confidential Information"). You shall not
+ * disclose such Confidential Information and shall use it only in
+ * accordance with the terms of the license agreement you entered into
+ * with Day.
+ */
+package org.apache.jackrabbit.jcr2spi.state;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * <code>Status</code>...
+ */
+public class Status {
+
+    private static Logger log = LoggerFactory.getLogger(Status.class);
+
+    /**
+     * 'existing', i.e. persistent state
+     */
+    public static final int EXISTING = 1;
+    /**
+     * 'existing', i.e. persistent state that has been transiently modified (copy-on-write)
+     */
+    public static final int EXISTING_MODIFIED = 2;
+    /**
+     * 'existing', i.e. persistent state that has been transiently removed (copy-on-write)
+     */
+    public static final int EXISTING_REMOVED = 3;
+    /**
+     * 'new' state
+     */
+    public static final int NEW = 4;
+    /**
+     * 'existing', i.e. persistent state that has been persistently modified by somebody else
+     */
+    public static final int STALE_MODIFIED = 5;
+    /**
+     * 'existing', i.e. persistent state that has been destroyed by somebody else
+     */
+    public static final int STALE_DESTROYED = 6;
+
+    /**
+     * a state is permanently modified either by saving transient changes or
+     * by wsp operations or be external modification
+     * TODO: improve. status only temporarily used to indicate to a SessionISM-state to copy changes
+     */
+    public static final int MODIFIED = 7;
+
+    /**
+     * a new state was deleted and is now 'removed'
+     * or an existing item has been removed by a workspace operation or
+     * by an external modification.
+     */
+    public static final int REMOVED = 8;
+
+    
+    public static boolean isTerminalStatus(int status) {
+        return status == REMOVED || status == STALE_DESTROYED;
+    }
+
+}

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

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