jackrabbit-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From ang...@apache.org
Subject svn commit: r477095 [2/2] - in /jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi: ./ lock/ operation/ state/ state/entry/ version/
Date Mon, 20 Nov 2006 08:06:20 GMT
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=477095&r1=477094&r2=477095
==============================================================================
--- 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 Nov 20 00:06:18 2006
@@ -27,6 +27,7 @@
 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.ChildItemEntry;
 import org.apache.jackrabbit.value.QValue;
 import org.slf4j.LoggerFactory;
 import org.slf4j.Logger;
@@ -40,8 +41,8 @@
 import java.util.HashMap;
 import java.util.Iterator;
 import java.util.List;
-import java.util.Set;
 import java.util.Map;
+import java.util.Arrays;
 
 /**
  * <code>NodeState</code> represents the state of a <code>Node</code>.
@@ -161,7 +162,9 @@
         if (mixinTypeNames != null) {
             this.mixinTypeNames = mixinTypeNames;
         }
-        // re-create property references
+        // set the node references
+        this.references = references;
+        // add property references
         propertiesInAttic.clear();
         properties.clear();
         Iterator it = propertyNames.iterator();
@@ -170,15 +173,13 @@
             ChildPropertyEntry pe = PropertyReference.create(this, propName, isf, idFactory);
             properties.put(propName, pe);
         }
-        // re-create child node entries
+        // add child node entries
         childNodeEntries.removeAll();
         it = childEntries.iterator();
         while (it.hasNext()) {
             ChildNodeEntry cne = (ChildNodeEntry) it.next();
             childNodeEntries.add(cne.getName(), cne.getUUID(), cne.getIndex());
         }
-        // set the node references
-        this.references = references;
     }
 
     //----------------------------------------------------------< ItemState >---
@@ -207,14 +208,175 @@
         return getNodeId();
     }
 
-    public void refresh() {
-        // TODO
+    /**
+     * {@inheritDoc}
+     * @see ItemState#reload(boolean)
+     */
+    public void reload(boolean keepChanges) {
+        // recursivly reload states from peristent storage including props
+        // that are in the attic.
+        for (Iterator it = getAllChildEntries(true, true); it.hasNext();) {
+            ChildItemEntry ce = (ChildItemEntry) it.next();
+            if (ce.isAvailable()) {
+                try {
+                    ce.getItemState().reload(keepChanges);
+                } catch (ItemStateException e) {
+                    // should not occur
+                }
+            }
+        }
+
+        if (isWorkspaceState()) {
+            // reload from persistent storage ('keepChanges' not relevant).
+            try {
+                NodeState tmp = isf.createNodeState(getNodeId(), parent);
+                if (merge(tmp, false) || getStatus() == Status.INVALIDATED) {
+                    setStatus(Status.MODIFIED);
+                }
+            } catch (NoSuchItemStateException e) {
+                // remove entry from parent
+                parent.childNodeEntries.remove(this);
+                // inform overlaying state and listeners
+                setStatus(Status.REMOVED);
+            } catch (ItemStateException e) {
+                // todo rather throw? remove from parent?
+                log.warn("Exception while refreshing property state: " + e);
+                log.debug("Stacktrace: ", e);
+            }
+        } else {
+            /* session-state: if keepChanges is true only existing or invalidated
+               states must be updated. otherwise the state gets updated and might
+               be marked 'Stale' if transient changes are present and the
+               workspace-state is modified. */
+            if (!keepChanges || getStatus() == Status.EXISTING || getStatus() == Status.INVALIDATED) {
+                // calling refresh on the workspace state will in turn reset this state
+                overlayedState.reload(keepChanges);
+            }
+        }
     }
 
-    public void invalidate() {
-        // TODO
+    boolean merge(ItemState another, boolean keepChanges) {
+        if (another == null) {
+            return false;
+        }
+        if (another == null || !another.isNode()) {
+            throw new IllegalArgumentException("Attempt to merge node state with property state.");
+        }
+        boolean modified = false;
+        synchronized (another) {
+            NodeState nState = (NodeState) another;
+            name = nState.name;
+            setUUID(nState.uuid);
+            nodeTypeName = nState.nodeTypeName;
+            definition = nState.definition;
+
+            // refs, mixin-types can be copied. they are never transiently changed.
+            references = nState.references;
+            List mixN = Arrays.asList(nState.mixinTypeNames);
+            modified = (mixN.size() != mixinTypeNames.length || !mixN.containsAll(Arrays.asList(mixinTypeNames)));
+            mixinTypeNames = nState.mixinTypeNames;
+
+            if (!keepChanges && !propertiesInAttic.isEmpty()) {
+                // remove all entries in the attic
+                modified = true;
+                propertiesInAttic.clear();
+            }
+
+            /* merge child entries without loosing valid entries and connected states. */
+            // add all entry from wspState that are missing in this state
+            for (Iterator it = nState.getAllChildEntries(false, false); it.hasNext();) {
+                ChildItemEntry ce = (ChildItemEntry) it.next();
+                QName childName = ce.getName();
+                if (ce.denotesNode()) {
+                    ChildNodeEntry cne = (ChildNodeEntry) ce;
+                    int index = cne.getIndex();
+                    if (!childNodeEntries.contains(childName, index, cne.getUUID())) {
+                        modified = true;
+                        childNodeEntries.add(childName, cne.getUUID(), index);
+                    }
+                } else {
+                    if (!hasPropertyName(childName)) {
+                        modified = true;
+                        addPropertyEntry(PropertyReference.create(this, childName, isf, idFactory));
+                    }
+                }
+            }
+
+            // if keepChanges is false, remove all entries from this state,
+            // that are missing in the given other state.
+            if (!keepChanges) {
+                for (Iterator it = getAllChildEntries(true, false); it.hasNext();) {
+                    ChildItemEntry ce = (ChildItemEntry) it.next();
+                    QName childName = ce.getName();
+                    boolean toRemove = false;
+                    if (ce.denotesNode()) {
+                        ChildNodeEntry cne = (ChildNodeEntry) ce;
+                        int index = cne.getIndex();
+                        toRemove = !nState.childNodeEntries.contains(childName, index, cne.getUUID());
+                    } else {
+                        toRemove = !nState.properties.containsKey(childName);
+                    }
+
+                    if (toRemove) {
+                        modified = true;
+                        if (ce.isAvailable()) {
+                            // TODO: check if correct.
+                            try {
+                                ItemState st = ce.getItemState();
+                                if (st.getStatus() == Status.EXISTING_MODIFIED) {
+                                    st.setStatus(Status.STALE_DESTROYED);
+                                } else {
+                                    st.setStatus(Status.REMOVED);
+                                }
+                            } catch (ItemStateException e) {
+                                log.error("Internal error", e);
+                            }
+                        }
+                        // and remove the corresponding entry
+                        if (ce.denotesNode()) {
+                            childNodeEntries.remove(childName, ((ChildNodeEntry)ce).getIndex());
+                        } else {
+                            properties.remove(childName);
+                        }
+                    }
+                }
+            }
+        }
+
+        return modified;
     }
 
+    /**
+     * {@inheritDoc}
+     * @see ItemState#invalidate(boolean)
+     */
+    public void invalidate(boolean recursive) {
+        if (recursive) {
+            for (Iterator it = getAllChildEntries(false, false); it.hasNext();) {
+                ChildItemEntry ce = (ChildItemEntry) it.next();
+                if (ce.isAvailable()) {
+                    try {
+                        ce.getItemState().invalidate(true);
+                    } catch (ItemStateException e) {
+                        // should not occur
+                    }
+                }
+            }
+            // TODO: props-in-attic?
+        }
+        // ... and invalidate this state
+        if (isWorkspaceState()) {
+            // workspace state
+            setStatus(Status.INVALIDATED);
+        } else {
+            // todo only invalidate if existing?
+            if (getStatus() == Status.EXISTING) {
+                // set workspace state invalidated, this will in turn invalidate
+                // this (session) state as well
+                overlayedState.setStatus(Status.INVALIDATED);
+            }
+        }
+    }
     //----------------------------------------------------------< NodeState >---
     /**
      * Returns the id of this node state.
@@ -578,8 +740,9 @@
      *
      * @param propName
      */
-    private void removePropertyEntry(QName propName) {
-        if (properties.remove(propName) != null) {
+    private ChildPropertyEntry removePropertyEntry(QName propName) {
+        ChildPropertyEntry cpe = (ChildPropertyEntry) properties.remove(propName);
+        if (cpe != null) {
             if (isWorkspaceState()) {
                 if (QName.JCR_UUID.equals(propName)) {
                     setUUID(null);
@@ -588,6 +751,7 @@
                 }
             }
         }
+        return cpe;
     }
 
     /**
@@ -699,7 +863,7 @@
      * @see ItemState#persisted(ChangeLog)
      */
     void persisted(ChangeLog changeLog) throws IllegalStateException {
-
+        // TODO: review...in case of CacheBehaviour.MANUAL and .INVALIDATION, some states must be invalidate (e.g. autocreated)
         // remember parent states that have need to adjust their uuid/mixintypes
         // or that got a new child entry added or existing entries removed.
         Map modParents = new HashMap();
@@ -760,7 +924,7 @@
                 }
 
                 // make sure the new state gets updated (e.g. uuid created by server)
-                addedState.reset();
+                addedState.merge(addedState.overlayedState, true);
                 // and mark the added-state existing
                 addedState.setStatus(Status.EXISTING);
                 // if parent is modified -> remember for final status reset
@@ -829,7 +993,9 @@
         IteratorChain chain = new IteratorChain(its);
         while (chain.hasNext()) {
             ItemState state = (ItemState) chain.next();
-            if (!(state.getStatus() == Status.EXISTING || state.getStatus() == Status.REMOVED)) {
+            if (!(state.getStatus() == Status.EXISTING ||
+                  state.getStatus() == Status.REMOVED ||
+                  state.getStatus() == Status.INVALIDATED)) {
                 // error: state has not been processed
                 // TODO: discard state and force reload of all data
             }
@@ -837,248 +1003,82 @@
     }
 
     /**
-     * {@inheritDoc}
-     * @see ItemState#reset()
-     */
-    synchronized void reset() {
-        checkIsSessionState();
-
-        if (overlayedState != null) {
-            synchronized (overlayedState) {
-                NodeState wspState = (NodeState) overlayedState;
-                name = wspState.name;
-                setUUID(wspState.uuid);
-                nodeTypeName = wspState.nodeTypeName;
-                definition = wspState.definition;
-
-                mixinTypeNames = wspState.mixinTypeNames;
-
-                // remove all entries in the attic
-                propertiesInAttic.clear();
-
-                // merge prop-names
-                Collection wspPropNames = wspState.getPropertyNames();
-                for (Iterator it = wspPropNames.iterator(); it.hasNext();) {
-                    QName propName = (QName) it.next();
-                    if (!hasPropertyName(propName)) {
-                        addPropertyEntry(PropertyReference.create(this, propName, isf, idFactory));
-                    }
-                }
-                for (Iterator it = properties.keySet().iterator(); it.hasNext();) {
-                    // remove all prop-entries in the session state that are
-                    // not present in the wsp-state.
-                    if (!wspPropNames.contains(it.next())) {
-                        it.remove();
-                    }
-                }
-
-                // merge child node entries
-                for (Iterator it = wspState.getChildNodeEntries().iterator(); it.hasNext();) {
-                    ChildNodeEntry cne = (ChildNodeEntry) it.next();
-                    int index = cne.getIndex();
-                    if (!childNodeEntries.contains(cne.getName(), index, cne.getUUID())) {
-                        childNodeEntries.add(cne.getName(), cne.getUUID(), index);
-                    }
-                }
-                List toRemove = new ArrayList();
-                for (Iterator it = getChildNodeEntries().iterator(); it.hasNext();) {
-                    ChildNodeEntry cne = (ChildNodeEntry) it.next();
-                    if (!wspState.childNodeEntries.contains(cne.getName(), cne.getIndex(), cne.getUUID())) {
-                        toRemove.add(cne);
-                    }
-                }
-                for (Iterator it = toRemove.iterator(); it.hasNext();) {
-                    ChildNodeEntry cne = (ChildNodeEntry) it.next();
-                    childNodeEntries.remove(cne.getName(), cne.getIndex());
-                }
-                // set the node references
-                references = wspState.references;
-            }
-        }
-    }
-
-    /**
+     * Recursively removes all child states and then calls {@link ItemState#remove()}.
+     *
      * @inheritDoc
      * @see ItemState#remove()
      */
     void remove() throws ItemStateException {
         checkIsSessionState();
-
         if (!isValid()) {
             throw new ItemStateException("cannot remove an invalid NodeState");
         }
-        // first remove all properties
-        for (Iterator it = properties.values().iterator(); it.hasNext(); ) {
-            ChildPropertyEntry cpe = ((ChildPropertyEntry) it.next());
-            if (cpe.isAvailable()) {
-                PropertyState pState = cpe.getPropertyState();
-                if (pState.isValid()) {
-                    pState.remove();
-                } else {
+        for (Iterator it = getAllChildEntries(true, false); it.hasNext();) {
+            ChildItemEntry ce = (ChildItemEntry) it.next();
+            if (ce.isAvailable()) {
+                ItemState childState = ce.getItemState();
+                if (childState.isValid()) {
+                    childState.remove();
+                } else if (!ce.denotesNode()) {
                     // remove invalid property state from properties map
                     it.remove();
+                    // TODO: check if for node-entries no action is required
                 }
-            } else {
+            } else if (!ce.denotesNode()) {
                 // remove unresolved entry from properties map
                 it.remove();
+                // TODO check if for node entries no action required
             }
         }
-        // move all properties from attic back to properties map
-        properties.putAll(propertiesInAttic);
-        propertiesInAttic.clear();
 
-        // then remove child node entries
-        for (Iterator it = childNodeEntries.iterator(); it.hasNext(); ) {
-            NodeState nodeState = ((ChildNodeEntry) it.next()).getNodeState();
-            if (nodeState.isValid()) {
-                nodeState.remove();
-            } else {
-                // already removed
-            }
-        }
-        if (getStatus() == Status.EXISTING || getStatus() == Status.EXISTING_MODIFIED) {
-            setStatus(Status.EXISTING_REMOVED);
-        } else if (getStatus() == Status.NEW) {
-            setStatus(Status.REMOVED);
+        if (!propertiesInAttic.isEmpty()) {
+            // move all properties from attic back to properties map
+            properties.putAll(propertiesInAttic);
+            propertiesInAttic.clear();
         }
-        // now inform parent
-        getParent().childNodeStateRemoved(this);
+
+        // process this state as well.
+        super.remove();
     }
 
     /**
-     * Calls {@link #revert(Set) on all child states and add itself to the
-     * set of affected states, if the current status indicates, that this
-     * <code>NodeState</code> has been transiently modified.
+     * Calls {@link ItemState#revert()} and moves all properties from the attic
+     * back into th properties map.
      *
      * @inheritDoc
-     * @see ItemState#revert(Set)
+     * @see ItemState#revert()
      */
-    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(); ) {
-            ChildPropertyEntry entry = (ChildPropertyEntry) it.next();
-            if (entry.isAvailable()) {
-                try {
-                    PropertyState propState = entry.getPropertyState();
-                    propState.revert(affectedItemStates);
-                } catch (ItemStateException e) {
-                    // should not happen because PropertyReference is resolved
-                    log.warn("Unable to get PropertyState from resolved PropertyReference");
-                }
-            } else {
-                // not touched or accessed before
-            }
-        }
-
-        // revert property states in attic
-        props.clear();
-        props.addAll(propertiesInAttic.values());
-        for (Iterator it = props.iterator(); it.hasNext(); ) {
-            PropertyReference ref = (PropertyReference) it.next();
-            try {
-                PropertyState propState = ref.getPropertyState();
-                propState.revert(affectedItemStates);
-            } catch (ItemStateException e) {
-                // probably stale destroyed property
-                // cleaned up when propertiesInAttic is cleared
-            }
-        }
-        propertiesInAttic.clear();
-
-        // now revert child node states
-        List children = new ArrayList(childNodeEntries);
-        for (Iterator it = children.iterator(); it.hasNext(); ) {
-            ChildNodeEntry entry = (ChildNodeEntry) it.next();
-            if (entry.isAvailable()) {
-                try {
-                    NodeState nodeState = entry.getNodeState();
-                    nodeState.revert(affectedItemStates);
-                } catch (ItemStateException e) {
-                    // should not happen because ChildNodeReference is resolved
-                    log.warn("Unable to get NodeState from resolved ChildNodeReference");
-                }
-            } else {
-                // not touched or accessed before
-            }
-        }
-
-        // now revert this node 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
-                reset();
-                setStatus(Status.EXISTING);
-                affectedItemStates.add(this);
-                break;
-            case Status.NEW:
-                // set removed
-                setStatus(Status.REMOVED);
-                // remove from parent
-                getParent().childNodeStateRemoved(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 node state");
-                getParent().childNodeStateRemoved(this);
-                break;
-            case Status.STALE_DESTROYED:
-                // overlayed state does not exist anymore
-                getParent().childNodeStateRemoved(this);
-                affectedItemStates.add(this);
-                break;
+    void revert() throws ItemStateException {
+        super.revert();
+        if (!propertiesInAttic.isEmpty()) {
+            // move all properties from attic back to properties map
+            properties.putAll(propertiesInAttic);
+            propertiesInAttic.clear();
         }
     }
 
     /**
+     * Adds this state to the changeLog if it is transiently modified, new or stale
+     * and subsequently calls this method on all child states including those
+     * property states that have been moved to the attic.
+     *
      * @inheritDoc
-     * @see ItemState#collectTransientStates(Collection)
+     * @see ItemState#collectStates(ChangeLog, boolean)
      */
-    void collectTransientStates(Collection transientStates) {
-        checkIsSessionState();
+    void collectStates(ChangeLog changeLog, boolean throwOnStale) throws StaleItemStateException {
+        super.collectStates(changeLog, throwOnStale);
 
-        switch (getStatus()) {
-            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(); ) {
-            ChildPropertyEntry entry = (ChildPropertyEntry) it.next();
-            if (entry.isAvailable()) {
+        // collect transient child states including properties in attic.
+        for (Iterator it = getAllChildEntries(false, true); it.hasNext();) {
+            ChildItemEntry ce = (ChildItemEntry) it.next();
+            if (ce.isAvailable()) {
                 try {
-                    entry.getPropertyState().collectTransientStates(transientStates);
+                    ce.getItemState().collectStates(changeLog, throwOnStale);
                 } catch (ItemStateException e) {
                     // should not happen because ref is available
                 }
             }
         }
-        // add all properties in attic
-        transientStates.addAll(propertiesInAttic.values());
-        // call available child node states
-        for (Iterator it = childNodeEntries.iterator(); it.hasNext(); ) {
-            ChildNodeEntry cne = (ChildNodeEntry) it.next();
-            if (cne.isAvailable()) {
-                try {
-                    cne.getNodeState().collectTransientStates(transientStates);
-                } catch (ItemStateException e) {
-                    // should not happen because cne is available
-                }
-            }
-        }
     }
 
     /**
@@ -1098,32 +1098,11 @@
     }
 
     /**
-     * Notifies this node state that a child node state has been removed.
-     *
-     * @param childState the node state that has been removed.
-     * @throws IllegalArgumentException if <code>this</code> is not the parent
-     *                                  of <code>nodeState</code>.
-     */
-    private synchronized void childNodeStateRemoved(NodeState childState) {
-        checkIsSessionState();
-
-        if (childState.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 (childState.getStatus() == Status.REMOVED) {
-            childNodeEntries.remove(childState);
-        }
-        markModified();
-    }
-
-    /**
      * Adds a property state to this node state.
      *
      * @param propState the property state to add.
-     * @throws ItemExistsException      if <code>this</code> node state already
-     *                                  contains a property state with the same
-     *                                  name as <code>propState</code>.
+     * @throws ItemExistsException If <code>this</code> node state already
+     * contains a valid property state with the same name as <code>propState</code>.
      * @throws IllegalArgumentException if <code>this</code> is not the parent
      *                                  of <code>propState</code>.
      */
@@ -1157,24 +1136,45 @@
     }
 
     /**
-     * Notifies this node state that a property state has been removed.
+     * Notifies this node state that a child item state has been removed or
+     * otherwise modified.
      *
-     * @param propState the property state that has been removed.
+     * @param childState the child item state that has been removed or modified.
      * @throws IllegalArgumentException if <code>this</code> is not the parent
-     *                                  of <code>propState</code>.
+     * of the given <code>ItemState</code>.
      */
-    synchronized void propertyStateRemoved(PropertyState propState) {
+    synchronized void childStatusChanged(ItemState childState, int previousStatus) {
         checkIsSessionState();
-        if (propState.getParent() != this) {
+        if (childState.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) {
-            removePropertyEntry(propState.getQName());
+
+        switch (childState.getStatus()) {
+            case Status.EXISTING_REMOVED:
+                markModified();
+                break;
+            case Status.REMOVED:
+                if (childState.isNode()) {
+                    childNodeEntries.remove((NodeState) childState);
+                } else {
+                    removePropertyEntry(childState.getQName());
+                }
+                // TODO: not correct. removing a NEW state may even remove the 'modified'
+                // flag from the parent, if this NEW state was the only modification.
+                if (previousStatus != Status.NEW) {
+                    markModified();
+                }
+                break;
+            case Status.EXISTING:
+                if (previousStatus == Status.EXISTING_REMOVED && !childState.isNode()) {
+                    QName propName = childState.getQName();
+                    if (propertiesInAttic.containsKey(propName)) {
+                        properties.put(childState.getQName(), propertiesInAttic.remove(propName));
+                    }
+                }
         }
-        markModified();
     }
+
     /**
      * Reorders the child node <code>insertNode</code> before the child node
      * <code>beforeNode</code>.
@@ -1219,6 +1219,14 @@
         newParent.markModified();
     }
 
+    /**
+     *
+     * @param newParent
+     * @param childState
+     * @param newName
+     * @param newDefinition
+     * @throws RepositoryException
+     */
     private void moveEntry(NodeState newParent, NodeState childState, QName newName, QNodeDefinition newDefinition) throws RepositoryException {
         ChildNodeEntry oldEntry = childNodeEntries.remove(childState);
         if (oldEntry != null) {
@@ -1234,6 +1242,36 @@
         }
     }
 
+    /**
+     *
+     * @param createNewList if true, both properties and childNodeEntries are
+     * copied to new list, since recursive calls may call this node state to
+     * inform the removal of a child entry.
+     *
+     * @return
+     */
+    private Iterator getAllChildEntries(boolean createNewList, boolean includeAttic) {
+        Iterator[] its;
+        if (createNewList) {
+            List props = new ArrayList(properties.values());
+            List children = new ArrayList(childNodeEntries);
+            if (includeAttic) {
+                List attic = new ArrayList(propertiesInAttic.values());
+                its = new Iterator[] {attic.iterator(), props.iterator(), children.iterator()};
+            } else {
+                its = new Iterator[] {props.iterator(), children.iterator()};
+            }
+        } else {
+            if (includeAttic) {
+                its = new Iterator[] {propertiesInAttic.values().iterator(), properties.values().iterator(), childNodeEntries.iterator()};
+            } else {
+                its = new Iterator[] {properties.values().iterator(),
+                    childNodeEntries.iterator()};
+            }
+        }
+        IteratorChain chain = new IteratorChain(its);
+        return chain;
+    }
     //-------------------------------< internal >-------------------------------
     /**
      * Returns <code>true</code> if the collection of child node
@@ -1373,7 +1411,7 @@
 
             // make sure all other modifications on the overlayed state are
             // reflected on the session-state.
-            sState.reset();
+            sState.merge(overlayed, false);
             // make sure, the session-state gets its status reset to Existing.
             if (sState.getStatus() == Status.EXISTING_MODIFIED) {
                 sState.setStatus(Status.EXISTING);

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=477095&r1=477094&r2=477095
==============================================================================
--- 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 Nov 20 00:06:18 2006
@@ -32,7 +32,6 @@
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-import java.util.Set;
 import java.util.Collection;
 import java.util.Iterator;
 
@@ -78,7 +77,7 @@
         this.name = overlayedState.name;
         this.def = overlayedState.def;
 
-        reset();
+        init(overlayedState.getType(), overlayedState.getValues());
     }
 
     /**
@@ -152,41 +151,69 @@
 
     /**
      * {@inheritDoc}
-     * @see ItemState#refresh()
+     * @see ItemState#reload(boolean)
      */
-    public void refresh() {
+    public void reload(boolean keepChanges) {
         if (isWorkspaceState()) {
-            // refresh from persistent storage
+            // refresh from persistent storage ('keepChanges' not relevant).
             try {
                 PropertyState tmp = isf.createPropertyState(getPropertyId(), parent);
-                init(tmp.getType(), tmp.getValues());
-                setStatus(Status.MODIFIED);
+                if (merge(tmp, false) || getStatus() == Status.INVALIDATED) {
+                    setStatus(Status.MODIFIED);
+                }
             } catch (NoSuchItemStateException e) {
-                // does not exist anymore
+                // TODO: make sure the property-entry is removed from the parent state
+                // inform overlaying state and listeners
                 setStatus(Status.REMOVED);
-                // todo also remove from parent? how do we make sure the parent
-                // todo does not get modified by this removal?
-                // parent.propertyStateRemoved(this);
             } catch (ItemStateException e) {
                 // todo rather throw? remove from parent?
                 log.warn("Exception while refreshing property state: " + e);
                 log.debug("Stacktrace: ", e);
             }
         } else {
-            // session state
-            if (getStatus() == Status.EXISTING || getStatus() == Status.INVALIDATED) {
-                // calling refresh on the workspace state will in turn
-                // also refresh / reset the session state
-                overlayedState.refresh();
+            /* session-state: if keepChanges is true only existing or invalidated
+               states must be updated. otherwise the state gets updated and might
+               be marked 'Stale' if transient changes are present and the
+               workspace-state is modified. */
+            if (!keepChanges || getStatus() == Status.EXISTING || getStatus() == Status.INVALIDATED) {
+                // calling refresh on the workspace state will in turn reset this state
+                overlayedState.reload(keepChanges);
             }
         }
     }
 
     /**
+     * If <code>keepChanges</code> is true, this method does nothing and returns
+     * false. Otherwise type and values of the other property state are compared
+     * to this state. If they differ, they will be copied to this state and
+     * this method returns true.
+     *
+     * @see ItemState#merge(ItemState, boolean)
+     */
+    boolean merge(ItemState another, boolean keepChanges) {
+        if (another == null) {
+            return false;
+        }
+        if (another.isNode()) {
+            throw new IllegalArgumentException("Attempt to merge property state with node state.");
+        }
+        if (keepChanges || !diff(this, (PropertyState) another)) {
+            // nothing to do.
+            return false;
+        }
+
+        synchronized (another) {
+            PropertyState pState = (PropertyState) another;
+            init(pState.type, pState.values);
+        }
+        return true;
+    }
+
+    /**
      * {@inheritDoc}
-     * @see ItemState#invalidate()
+     * @see ItemState#invalidate(boolean)
      */
-    public void invalidate() {
+    public void invalidate(boolean recursive) {
         if (isWorkspaceState()) {
             // workspace state
             setStatus(Status.INVALIDATED);
@@ -195,7 +222,7 @@
             if (getStatus() == Status.EXISTING) {
                 // set workspace state invalidated, this will in turn invalidate
                 // this (session) state as well
-                overlayedState.invalidate();
+                overlayedState.invalidate(recursive);
             }
         }
     }
@@ -282,20 +309,8 @@
                 break;
 
             case Event.PROPERTY_CHANGED:
-                // TODO: improve.
-                /* retrieve property value and type from server even if
-                   changes were issued from this session (changelog).
-                   this is currently the only way to update the workspace
-                   state, which is not connected to its overlaying session-state.
-                */
-                try {
-                    PropertyState tmp = isf.createPropertyState(getPropertyId(), parent);
-                    init(tmp.getType(), tmp.getValues());
-                    setStatus(Status.MODIFIED);
-                } catch (ItemStateException e) {
-                    // TODO: rather throw?
-                    log.error("Internal Error", e);
-                }
+                // retrieve modified property value and type from server.
+                reload(false);
                 break;
 
             case Event.PROPERTY_ADDED:
@@ -318,7 +333,8 @@
                 /*
                 NOTE: overlayedState must be existing, otherwise save was not
                 possible on prop. Similarly a property can only be the changelog
-                target, if it was modified. removal, add must be persisted on parent.
+                target, if it was modified. removal, add and implicit modification
+                of protected properties must be persisted by save on parent.
                 */
                 // push changes to overlayed state and reset status
                 ((PropertyState) overlayedState).init(getType(), getValues());
@@ -326,100 +342,7 @@
             }
         }
     }
-
-    /**
-     * {@inheritDoc}
-     * @see ItemState#reset()
-     */
-    synchronized void reset() {
-        checkIsSessionState();
-        if (overlayedState != null) {
-            synchronized (overlayedState) {
-                PropertyState wspState = (PropertyState) overlayedState;
-                init(wspState.type, wspState.values);
-            }
-        }
-    }
-
-    /**
-     * @inheritDoc
-     * @see ItemState#remove()
-     */
-    void remove() {
-        checkIsSessionState();
-        if (getStatus() == Status.NEW) {
-            setStatus(Status.REMOVED);
-        } else {
-            setStatus(Status.EXISTING_REMOVED);
-        }
-        getParent().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
-                getParent().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");
-                getParent().propertyStateRemoved(this);
-                break;
-            case Status.STALE_DESTROYED:
-                // overlayed does not exist anymore
-                getParent().propertyStateRemoved(this);
-                affectedItemStates.add(this);
-                break;
-        }
-    }
-
-    /**
-     * @inheritDoc
-     * @see ItemState#collectTransientStates(Collection)
-     */
-    void collectTransientStates(Collection 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.
      *
@@ -469,5 +392,35 @@
             }
         }
         ValueConstraint.checkValueConstraints(definition, values);
+    }
+
+    /**
+     * Returns true, if type and/or values of the given property states differ.
+     *
+     * @param p1
+     * @param p2
+     * @return if the 2 <code>PropertyState</code>s are different in terms of
+     * type and/or values.
+     */
+    private static boolean diff(PropertyState p1, PropertyState p2) {
+        // compare type
+        if (p1.getType() != p2.getType()) {
+            return true;
+        }
+
+        QValue[] vs1 = p1.getValues();
+        QValue[] vs2 = p2.getValues();
+        if (vs1.length != vs2.length) {
+            return true;
+        } else {
+            for (int i = 0; i < vs1.length; i++) {
+                boolean eq = (vs1[i] == null) ? vs2[i] == null : vs1[i].equals(vs2[i]);
+                if (!eq) {
+                    return true;
+                }
+            }
+        }
+        // no difference
+        return false;
     }
 }

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=477095&r1=477094&r2=477095
==============================================================================
--- 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 Nov 20 00:06:18 2006
@@ -44,6 +44,7 @@
 import org.apache.jackrabbit.jcr2spi.operation.LockRelease;
 import org.apache.jackrabbit.jcr2spi.operation.AddLabel;
 import org.apache.jackrabbit.jcr2spi.operation.RemoveLabel;
+import org.apache.jackrabbit.jcr2spi.operation.RemoveVersion;
 import org.apache.jackrabbit.name.NamespaceResolver;
 import org.slf4j.LoggerFactory;
 import org.slf4j.Logger;
@@ -55,6 +56,7 @@
 import org.apache.jackrabbit.spi.IdFactory;
 import org.apache.jackrabbit.value.QValue;
 import org.apache.jackrabbit.uuid.UUID;
+import org.apache.commons.collections.iterators.IteratorChain;
 
 import javax.jcr.InvalidItemStateException;
 import javax.jcr.ItemNotFoundException;
@@ -79,7 +81,6 @@
 import java.util.Calendar;
 import java.util.Collection;
 import java.util.Collections;
-import java.util.LinkedHashSet;
 import java.io.InputStream;
 import java.io.IOException;
 
@@ -285,17 +286,17 @@
         }
 
         // collect the changes to be saved
-        ChangeLog changeLog = getChangeLog(state);
+        ChangeLog changeLog = getChangeLog(state, true);
         if (!changeLog.isEmpty()) {
             // only pass changelog if there are transient modifications available
             // for the specified item and its decendants.
             workspaceItemStateMgr.execute(changeLog);
-        }
 
-        // remove operations just processed
-        transientStateMgr.disposeOperations(changeLog.getOperations());
-        // now its save to clear the changeLog
-        changeLog.reset();
+            // remove states and operations just processed from the transient ISM
+            transientStateMgr.dispose(changeLog);
+            // now its save to clear the changeLog
+            changeLog.reset();
+        }
     }
 
     /**
@@ -309,18 +310,21 @@
      *                            to be canceled as well in another sub-tree.
      */
     public void undo(ItemState itemState) throws ItemStateException, ConstraintViolationException {
-        // check if self contained
-        ChangeLog changeLog = new ChangeLog(itemState);
-        collectTransientStates(itemState, changeLog, false);
-        changeLog.checkIsSelfContained();
-        changeLog.collectOperations(transientStateMgr.getOperations());
-
-        // now do it for real
-        Set affectedItemStates = new HashSet();
-        itemState.revert(affectedItemStates);
+        ChangeLog changeLog = getChangeLog(itemState, false);
+        if (!changeLog.isEmpty()) {
+            // now do it for real
+            // TODO: check if states are reverted in correct order
+            Iterator[] its = new Iterator[] {changeLog.addedStates(), changeLog.deletedStates(), changeLog.modifiedStates()};
+            IteratorChain chain = new IteratorChain(its);
+            while (chain.hasNext()) {
+                ItemState state = (ItemState) chain.next();
+                state.revert();
+            }
 
-        // remove all canceled operations
-        transientStateMgr.disposeOperations(changeLog.getOperations());
+            // remove transient states and related operations from the t-statemanager
+            transientStateMgr.dispose(changeLog);
+            changeLog.reset();
+        }
     }
 
     /**
@@ -364,85 +368,35 @@
     /**
      *
      * @param itemState
+     * @param throwOnStale Throws StaleItemStateException if either the given
+     * <code>ItemState</code> or any of its decendants is stale and the flag is true.
      * @return
-     * @throws StaleItemStateException
-     * @throws ItemStateException
-     */
-    private ChangeLog getChangeLog(ItemState itemState) throws StaleItemStateException, ItemStateException, ConstraintViolationException {
-        // build changelog for affected and decendant states only
-        ChangeLog changeLog = new ChangeLog(itemState);
-        collectTransientStates(itemState, changeLog, true);
-        changeLog.collectOperations(transientStateMgr.getOperations());
-
-        changeLog.checkIsSelfContained();
-        return changeLog;
-    }
-
-    /**
-     * Builds a <code>ChangeLog</code> of transient (i.e. new, modified or
-     * deleted) item states that are within the scope of <code>state</code>.
-     *
      * @throws StaleItemStateException if a stale <code>ItemState</code> is
-     *                                 encountered while traversing the state
-     *                                 hierarchy. The <code>changeLog</code>
-     *                                 might have been populated with some
-     *                                 transient item states. A client should
-     *                                 therefore not reuse the <code>changeLog</code>
-     *                                 if such an exception is thrown.
+     * encountered while traversing the state hierarchy. The <code>changeLog</code>
+     * might have been populated with some transient item states. A client should
+     * therefore not reuse the <code>changeLog</code> if such an exception is thrown.
      * @throws ItemStateException if <code>state</code> is a new item state.
      */
-    private void collectTransientStates(ItemState state, ChangeLog changeLog, boolean throwOnStale)
-            throws StaleItemStateException, ItemStateException {
+    private ChangeLog getChangeLog(ItemState itemState, boolean throwOnStale) throws StaleItemStateException, ItemStateException, ConstraintViolationException {
+        // build changelog for affected and decendant states only
+        ChangeLog changeLog = new ChangeLog(itemState);
         // fail-fast test: check status of this item's state
-        if (state.getStatus() == Status.NEW) {
-            String msg = LogUtil.safeGetJCRPath(state, nsResolver) + ": cannot save a new item.";
+        if (itemState.getStatus() == Status.NEW) {
+            String msg = "Cannot save an item with status NEW (" +LogUtil.safeGetJCRPath(itemState, nsResolver)+ ").";
             log.debug(msg);
             throw new ItemStateException(msg);
         }
-
-        if (throwOnStale && Status.isStale(state.getStatus())) {
-            String msg = LogUtil.safeGetJCRPath(state, nsResolver) + ": the item cannot be saved because it has been modified/removed externally.";
+        if (throwOnStale && Status.isStale(itemState.getStatus())) {
+            String msg =  "Attempt to save an item, that has been externally modified (" +LogUtil.safeGetJCRPath(itemState, nsResolver)+ ").";
             log.debug(msg);
             throw new StaleItemStateException(msg);
         }
+        // collect transient/stale states that should be persisted or reverted
+        itemState.collectStates(changeLog, throwOnStale);
 
-        // Set of transient states that should be persisted
-        Set transientStates = new LinkedHashSet();
-        state.collectTransientStates(transientStates);
-
-        for (Iterator it = transientStates.iterator(); it.hasNext();) {
-            ItemState transientState = (ItemState) it.next();
-            // fail-fast test: check status of transient state
-            switch (transientState.getStatus()) {
-                case Status.NEW:
-                    changeLog.added(transientState);
-                    break;
-                case Status.EXISTING_MODIFIED:
-                    changeLog.modified(transientState);
-                    break;
-                case Status.EXISTING_REMOVED:
-                    changeLog.deleted(transientState);
-                    break;
-                case Status.STALE_MODIFIED:
-                    if (throwOnStale) {
-                        String msg = transientState.getId() + ": the item cannot be saved because it has been modified externally.";
-                        log.debug(msg);
-                        throw new StaleItemStateException(msg);
-                    } else {
-                        changeLog.modified(transientState);
-                    }
-                case Status.STALE_DESTROYED:
-                    if (throwOnStale) {
-                        String msg = transientState.getId() + ": the item cannot be saved because it has been deleted externally.";
-                        log.debug(msg);
-                        throw new StaleItemStateException(msg);
-                    }
-                default:
-                    log.debug("unexpected state status (" + transientState.getStatus() + ")");
-                    // ignore
-                    break;
-            }
-        }
+        changeLog.collectOperations(transientStateMgr.getOperations());
+        changeLog.checkIsSelfContained();
+        return changeLog;
     }
 
     //--------------------------------------------------------------------------
@@ -659,6 +613,10 @@
         throw new UnsupportedOperationException("Internal error: RemoveLabel cannot be handled by session ItemStateManager.");
     }
 
+    public void visit(RemoveVersion operation) throws VersionException, AccessDeniedException, ReferentialIntegrityException, RepositoryException {
+        throw new UnsupportedOperationException("Internal error: RemoveVersion cannot be handled by session ItemStateManager.");
+    }
+
     //--------------------------------------------< Internal State Handling >---
     /**
      *
@@ -745,7 +703,7 @@
 
     private void removeItemState(ItemState itemState, int options) throws RepositoryException {
         validator.checkRemoveItem(itemState, options);
-        // recursively remove the complete tree including the given node state.
+        // recursively remove the given state and all child states.
         boolean success = false;
         try {
             itemState.remove();
@@ -843,6 +801,7 @@
                 // nt:version node type defines jcr:created property
                 genValues = new QValue[]{QValue.create(Calendar.getInstance())};
             }
+            // TODO: defaults???
         }
         return genValues;
     }

Modified: jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/state/TransientItemStateManager.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/state/TransientItemStateManager.java?view=diff&rev=477095&r1=477094&r2=477095
==============================================================================
--- jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/state/TransientItemStateManager.java (original)
+++ jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/state/TransientItemStateManager.java Mon Nov 20 00:06:18 2006
@@ -96,15 +96,6 @@
     }
 
     /**
-     * Removes the <code>operation</code> from the list of operations.
-     * @param operation the Operation to remove.
-     * @return <code>true</code> if the operation was removed.
-     */
-    boolean removeOperation(Operation operation) {
-        return changeLog.removeOperation(operation);
-    }
-
-    /**
      * @return <code>true</code> if this transient ISM has pending changes.
      */
     boolean hasPendingChanges() {
@@ -169,14 +160,13 @@
     }
 
     /**
-     * Disposes a collection of {@link org.apache.jackrabbit.jcr2spi.operation.Operation}s.
+     * Remove the states and operations listed in the changeLog from the
+     * internal changeLog.
      *
-     * @param operations the operations.
+     * @param subChangeLog
      */
-    void disposeOperations(Iterator operations) {
-        while (operations.hasNext()) {
-            changeLog.removeOperation((Operation) operations.next());
-        }
+    void dispose(ChangeLog subChangeLog) {
+        changeLog.removeAll(subChangeLog);
     }
 
     //---------------------------------------------------< ItemStateManager >---
@@ -250,11 +240,6 @@
      * @see ItemStateLifeCycleListener#statusChanged(ItemState, int)
      */
     public void statusChanged(ItemState state, int previousStatus) {
-        if (!Status.isValidStatusChange(previousStatus, state.getStatus(), false)) {
-            log.error("ItemState has invalid status: " + state.getStatus());
-            return;
-        }
-
         // TODO: depending on status of state adapt change log
         // e.g. a revert on states will reset the status from
         // 'existing modified' to 'existing'.

Modified: jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/state/WorkspaceItemStateFactory.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/state/WorkspaceItemStateFactory.java?view=diff&rev=477095&r1=477094&r2=477095
==============================================================================
--- jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/state/WorkspaceItemStateFactory.java (original)
+++ jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/state/WorkspaceItemStateFactory.java Mon Nov 20 00:06:18 2006
@@ -285,7 +285,7 @@
         this.cache = cache;
     }
 
-    //------------------------------------------------------< ChildNodeEntry >---
+    //-----------------------------------------------------< ChildNodeEntry >---
     private class CNE implements ChildNodeEntry {
 
         private final QName name;
@@ -302,6 +302,10 @@
             throw new UnsupportedOperationException();
         }
 
+        public boolean denotesNode() {
+            return true;
+        }
+
         public QName getName() {
             return name;
         }
@@ -319,6 +323,10 @@
         }
 
         public boolean isAvailable() {
+            throw new UnsupportedOperationException();
+        }
+
+        public ItemState getItemState() throws NoSuchItemStateException, ItemStateException {
             throw new UnsupportedOperationException();
         }
     }

Modified: jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/state/WorkspaceItemStateManager.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/state/WorkspaceItemStateManager.java?view=diff&rev=477095&r1=477094&r2=477095
==============================================================================
--- jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/state/WorkspaceItemStateManager.java (original)
+++ jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/state/WorkspaceItemStateManager.java Mon Nov 20 00:06:18 2006
@@ -55,7 +55,7 @@
         } catch (UnsupportedRepositoryOperationException e) {
             // repository does not support observation
         }
-        this.eventFilter = filter == null ? Collections.EMPTY_LIST : Collections.singletonList(filter);
+        this.eventFilter = (filter == null) ? Collections.EMPTY_LIST : Collections.singletonList(filter);
     }
 
     //-------------------------------< InternalEventListener >------------------
@@ -83,7 +83,7 @@
     /**
      * Retrieve the workspace state(s) affected by the given event and refresh
      * them accordingly.
-     * 
+     *
      * @param events
      */
     private void pushEvents(Collection events) {

Added: jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/state/entry/ChildItemEntry.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/state/entry/ChildItemEntry.java?view=auto&rev=477095
==============================================================================
--- jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/state/entry/ChildItemEntry.java (added)
+++ jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/state/entry/ChildItemEntry.java Mon Nov 20 00:06:18 2006
@@ -0,0 +1,59 @@
+/*
+ * 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.entry;
+
+import org.apache.jackrabbit.jcr2spi.state.NoSuchItemStateException;
+import org.apache.jackrabbit.jcr2spi.state.ItemStateException;
+import org.apache.jackrabbit.jcr2spi.state.ItemState;
+import org.apache.jackrabbit.name.QName;
+
+/**
+ * <code>ChildItemEntry</code>...
+ */
+public interface ChildItemEntry {
+
+    /**
+     * True if this ChildItemEntry would resolve to a <code>NodeState</code>.
+     * 
+     * @return
+     */
+    public boolean denotesNode();
+
+    /**
+     * @return the name of this child entry.
+     */
+    public QName getName();
+
+    /**
+     * Returns <code>true</code> if the referenced <code>NodeState</code> is
+     * available. That is, the referenced <code>NodeState</code> is already
+     * cached and ready to be returned by {@link #getItemState()}.
+     *
+     * @return <code>true</code> if the <code>NodeState</code> is available;
+     * otherwise <code>false</code>.
+     */
+    public boolean isAvailable();
+
+    /**
+     * @return the referenced <code>ItemState</code>.
+     * @throws NoSuchItemStateException if the <code>ItemState</code> does not
+     * exist anymore.
+     * @throws ItemStateException If an error occurs while retrieving the
+     * <code>ItemState</code>.
+     */
+    public ItemState getItemState() throws NoSuchItemStateException, ItemStateException;
+}

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

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

Modified: jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/state/entry/ChildItemReference.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/state/entry/ChildItemReference.java?view=diff&rev=477095&r1=477094&r2=477095
==============================================================================
--- jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/state/entry/ChildItemReference.java (original)
+++ jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/state/entry/ChildItemReference.java Mon Nov 20 00:06:18 2006
@@ -31,7 +31,7 @@
  * @see ChildNodeReference
  * @see PropertyReference
  */
-abstract class ChildItemReference {
+abstract class ChildItemReference implements ChildItemEntry {
 
     /**
      * Cached weak reference to the target NodeState.
@@ -39,14 +39,14 @@
     private WeakReference target;
 
     /**
-     * The parent that owns this <code>ChildItemReference</code>.
+     * The name of the target item state.
      */
-    protected final NodeState parent;
+    private final QName name;
 
     /**
-     * The name of the target item state.
+     * The parent that owns this <code>ChildItemReference</code>.
      */
-    protected final QName name;
+    protected final NodeState parent;
 
     /**
      * The item state factory to create the the item state.
@@ -135,4 +135,20 @@
      */
     protected abstract ItemState doResolve()
             throws NoSuchItemStateException, ItemStateException;
+
+    /**
+     * @inheritDoc
+     * @see ChildItemEntry#getName()
+     */
+    public QName getName() {
+        return name;
+    }
+
+    /**
+     * @inheritDoc
+     * @see ChildItemEntry#isAvailable()
+     */
+    public boolean isAvailable() {
+        return isResolved();
+    }
 }

Modified: jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/state/entry/ChildNodeEntry.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/state/entry/ChildNodeEntry.java?view=diff&rev=477095&r1=477094&r2=477095
==============================================================================
--- jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/state/entry/ChildNodeEntry.java (original)
+++ jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/state/entry/ChildNodeEntry.java Mon Nov 20 00:06:18 2006
@@ -26,7 +26,7 @@
  * <code>ChildNodeEntry</code> specifies the name, index (in the case of
  * same-name siblings) and the UUID of a child node entry.
  */
-public interface ChildNodeEntry {
+public interface ChildNodeEntry extends ChildItemEntry {
 
     /**
      * @return the <code>NodeId</code> of this child node entry.
@@ -59,14 +59,4 @@
      */
     public NodeState getNodeState()
             throws NoSuchItemStateException, ItemStateException;
-
-    /**
-     * Returns <code>true</code> if the referenced <code>NodeState</code> is
-     * available. That is, the referenced <code>NodeState</code> is already
-     * cached and ready to be returned by {@link #getNodeState()}.
-     *
-     * @return <code>true</code> if the <code>NodeState</code> is available;
-     * otherwise <code>false</code>.
-     */
-    public boolean isAvailable();
 }

Modified: jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/state/entry/ChildNodeReference.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/state/entry/ChildNodeReference.java?view=diff&rev=477095&r1=477094&r2=477095
==============================================================================
--- jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/state/entry/ChildNodeReference.java (original)
+++ jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/state/entry/ChildNodeReference.java Mon Nov 20 00:06:18 2006
@@ -22,6 +22,7 @@
 import org.apache.jackrabbit.jcr2spi.state.ItemStateFactory;
 import org.apache.jackrabbit.jcr2spi.state.NoSuchItemStateException;
 import org.apache.jackrabbit.jcr2spi.state.ItemStateException;
+import org.apache.jackrabbit.jcr2spi.state.ItemState;
 
 /**
  * <code>ChildNodeReference</code> implements common functionality for child
@@ -105,26 +106,28 @@
 
     /**
      * @inheritDoc
-     * @see ChildNodeEntry#getName()
+     * @see ChildNodeEntry#getNodeState()
      */
-    public QName getName() {
-        return name;
+    public NodeState getNodeState()
+            throws NoSuchItemStateException, ItemStateException {
+        return (NodeState) resolve();
     }
 
     /**
+     * Returns true.
+     * 
      * @inheritDoc
-     * @see ChildNodeEntry#getNodeState()
+     * @see ChildItemEntry#denotesNode()
      */
-    public NodeState getNodeState()
-            throws NoSuchItemStateException, ItemStateException {
-        return (NodeState) resolve();
+    public boolean denotesNode() {
+        return true;
     }
 
     /**
      * @inheritDoc
-     * @see ChildNodeEntry#isAvailable()
+     * @see ChildItemEntry#getItemState()
      */
-    public boolean isAvailable() {
-        return isResolved();
+    public ItemState getItemState() throws NoSuchItemStateException, ItemStateException {
+        return getNodeState();
     }
 }

Modified: jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/state/entry/ChildPropertyEntry.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/state/entry/ChildPropertyEntry.java?view=diff&rev=477095&r1=477094&r2=477095
==============================================================================
--- jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/state/entry/ChildPropertyEntry.java (original)
+++ jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/state/entry/ChildPropertyEntry.java Mon Nov 20 00:06:18 2006
@@ -17,7 +17,6 @@
 package org.apache.jackrabbit.jcr2spi.state.entry;
 
 import org.apache.jackrabbit.spi.PropertyId;
-import org.apache.jackrabbit.name.QName;
 import org.apache.jackrabbit.jcr2spi.state.ItemStateException;
 import org.apache.jackrabbit.jcr2spi.state.NoSuchItemStateException;
 import org.apache.jackrabbit.jcr2spi.state.PropertyState;
@@ -25,7 +24,7 @@
 /**
  * <code>ChildPropertyEntry</code>...
  */
-public interface ChildPropertyEntry {
+public interface ChildPropertyEntry extends ChildItemEntry {
 
     /**
      * @return the <code>NodeId</code> of this child node entry.
@@ -33,11 +32,6 @@
     public PropertyId getId();
 
     /**
-     * @return the name of this child node entry.
-     */
-    public QName getName();
-
-    /**
      * @return the referenced <code>PropertyState</code>.
      * @throws NoSuchItemStateException if the <code>PropertyState</code> does not
      * exist anymore.
@@ -45,14 +39,4 @@
      * <code>PropertyState</code>.
      */
     public PropertyState getPropertyState() throws NoSuchItemStateException, ItemStateException;
-
-    /**
-     * Returns <code>true</code> if the referenced <code>PropertyState</code> is
-     * available. That is, the referenced <code>PropertyState</code> is already
-     * cached and ready to be returned by {@link #getPropertyState()}.
-     *
-     * @return <code>true</code> if the <code>PropertyState</code> is available;
-     *         otherwise <code>false</code>.
-     */
-    public boolean isAvailable();
 }

Modified: jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/state/entry/PropertyReference.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/state/entry/PropertyReference.java?view=diff&rev=477095&r1=477094&r2=477095
==============================================================================
--- jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/state/entry/PropertyReference.java (original)
+++ jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/state/entry/PropertyReference.java Mon Nov 20 00:06:18 2006
@@ -105,28 +105,31 @@
      * @inheritDoc
      */
     public PropertyId getId() {
-        return idFactory.createPropertyId(parent.getNodeId(), name);
+        return idFactory.createPropertyId(parent.getNodeId(), getName());
     }
 
     /**
      * @inheritDoc
      */
-    public QName getName() {
-        return name;
+    public PropertyState getPropertyState() throws NoSuchItemStateException, ItemStateException {
+        return (PropertyState) resolve();
     }
 
     /**
+     * Returns false.
+     *
      * @inheritDoc
+     * @see ChildItemEntry#denotesNode()
      */
-    public PropertyState getPropertyState() throws NoSuchItemStateException, ItemStateException {
-        return (PropertyState) resolve();
+    public boolean denotesNode() {
+        return false;
     }
 
     /**
      * @inheritDoc
-     * @see ChildPropertyEntry#isAvailable()
+     * @see ChildItemEntry#getItemState()
      */
-    public boolean isAvailable() {
-        return isResolved();
+    public ItemState getItemState() throws NoSuchItemStateException, ItemStateException {
+        return getPropertyState();
     }
 }

Modified: jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/version/VersionManagerImpl.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/version/VersionManagerImpl.java?view=diff&rev=477095&r1=477094&r2=477095
==============================================================================
--- jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/version/VersionManagerImpl.java (original)
+++ jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/version/VersionManagerImpl.java Mon Nov 20 00:06:18 2006
@@ -27,14 +27,15 @@
 import org.apache.jackrabbit.jcr2spi.operation.Restore;
 import org.apache.jackrabbit.jcr2spi.operation.ResolveMergeConflict;
 import org.apache.jackrabbit.jcr2spi.operation.Merge;
-import org.apache.jackrabbit.jcr2spi.operation.Remove;
 import org.apache.jackrabbit.jcr2spi.operation.AddLabel;
 import org.apache.jackrabbit.jcr2spi.operation.RemoveLabel;
+import org.apache.jackrabbit.jcr2spi.operation.RemoveVersion;
 import org.apache.jackrabbit.jcr2spi.WorkspaceManager;
 import org.slf4j.LoggerFactory;
 import org.slf4j.Logger;
 
 import javax.jcr.RepositoryException;
+import javax.jcr.UnsupportedRepositoryOperationException;
 import javax.jcr.version.VersionException;
 
 import org.apache.jackrabbit.name.QName;
@@ -120,7 +121,7 @@
 
     public void removeVersion(NodeState versionHistoryState, NodeState versionState) throws RepositoryException {
         NodeState wspVersionState = getWorkspaceState(versionState);
-        Operation op = Remove.create(wspVersionState);
+        Operation op = RemoveVersion.create(wspVersionState);
         workspaceManager.execute(op);
     }
 
@@ -157,16 +158,21 @@
 
     public Collection merge(NodeState nodeState, String workspaceName, boolean bestEffort) throws RepositoryException {
         NodeState wspState = getWorkspaceState(nodeState);
+        // TODO : needs to be fixed... repository may not support observation
         // TODO find better solution to build the mergeFailed-collection
+        EventFilter eventFilter;
+        try {
+            eventFilter = workspaceManager.createEventFilter(Event.ALL_TYPES, nodeState.getQPath(), true, null, null, false);
+        } catch (UnsupportedRepositoryOperationException e) {
+            eventFilter = null;
+        }
         final List failedIds = new ArrayList();
-        final EventFilter eventFilter = workspaceManager.createEventFilter(
-                Event.ALL_TYPES, nodeState.getQPath(), true, null, null, false);
-        InternalEventListener mergeFailedCollector = new InternalEventListener() {
+        final Collection fts = (eventFilter == null) ? Collections.EMPTY_LIST : Collections.singletonList(eventFilter);
 
+        InternalEventListener mergeFailedCollector = new InternalEventListener() {
             public Collection getEventFilters() {
-                return Collections.singletonList(eventFilter);
+                return fts;
             }
-
             public void onEvent(EventBundle eventBundle) {
                 if (eventBundle.isLocal()) {
                     EventIterator it = eventBundle.getEvents();



Mime
View raw message