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

svn commit: r704898 [4/4] - in /jackrabbit/branches/1.5: ./ jackrabbit-jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/ jackrabbit-jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/hierarchy/ jackrabbit-jcr2spi/src/main/java/org/apache/jackrabbit...

Modified: jackrabbit/branches/1.5/jackrabbit-jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/state/SessionItemStateManager.java
URL: http://svn.apache.org/viewvc/jackrabbit/branches/1.5/jackrabbit-jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/state/SessionItemStateManager.java?rev=704898&r1=704897&r2=704898&view=diff
==============================================================================
--- jackrabbit/branches/1.5/jackrabbit-jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/state/SessionItemStateManager.java (original)
+++ jackrabbit/branches/1.5/jackrabbit-jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/state/SessionItemStateManager.java Wed Oct 15 05:59:19 2008
@@ -73,6 +73,8 @@
 import javax.jcr.nodetype.NoSuchNodeTypeException;
 import javax.jcr.lock.LockException;
 import java.util.Iterator;
+import java.util.List;
+import java.util.ArrayList;
 import java.io.InputStream;
 
 /**
@@ -139,17 +141,15 @@
     public void save(ItemState state) throws ReferentialIntegrityException,
             InvalidItemStateException, RepositoryException {
         // shortcut, if no modifications are present
-        if (!hasPendingChanges()) {
+        if (!transientStateMgr.hasPendingChanges()) {
             return;
         }
-
         // collect the changes to be saved
-        ChangeLog changeLog = getChangeLog(state, true);
+        ChangeLog changeLog = transientStateMgr.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 states and operations just processed from the transient ISM
             transientStateMgr.dispose(changeLog);
             // now its save to clear the changeLog
@@ -168,20 +168,17 @@
      * another item needs to be canceled as well in another sub-tree.
      */
     public void undo(ItemState itemState) throws ConstraintViolationException, RepositoryException {
-        try {
-            ChangeLog changeLog = getChangeLog(itemState, false);
-            if (!changeLog.isEmpty()) {
-                // let changelog revert all changes
-                changeLog.undo();
-                // remove transient states and related operations from the t-statemanager
-                transientStateMgr.dispose(changeLog);
-                changeLog.reset();
-            }
-        } catch (InvalidItemStateException e) {
-            // should never get here
-            String msg = "Unable to undo item.";
-            log.debug(msg);
-            throw new RepositoryException(e);
+        // short cut
+        if (!transientStateMgr.hasPendingChanges()) {
+            return;
+        }
+        ChangeLog changeLog = transientStateMgr.getChangeLog(itemState, false);
+        if (!changeLog.isEmpty()) {
+            // let changelog revert all changes
+            changeLog.undo();
+            // remove transient states and related operations from the t-statemanager
+            transientStateMgr.dispose(changeLog);
+            changeLog.reset();
         }
     }
 
@@ -262,7 +259,8 @@
         NodeState parent = operation.getParentState();
         ItemDefinitionProvider defProvider = mgrProvider.getItemDefinitionProvider();
         QNodeDefinition def = defProvider.getQNodeDefinition(parent.getAllNodeTypeNames(), operation.getNodeName(), operation.getNodeTypeName());
-        addNodeState(parent, operation.getNodeName(), operation.getNodeTypeName(), operation.getUuid(), def, options);
+        List newStates = addNodeState(parent, operation.getNodeName(), operation.getNodeTypeName(), operation.getUuid(), def, options);
+        operation.addedState(newStates);
 
         transientStateMgr.addOperation(operation);
     }
@@ -336,11 +334,9 @@
             | ItemStateValidator.CHECK_VERSIONING
             | ItemStateValidator.CHECK_CONSTRAINTS;
         removeItemState(state, options);
-        // unless new state got removed remember operation and mark parent modified.
-        if (!Status.isTerminal(state.getStatus())) {
-            transientStateMgr.addOperation(operation);
-            operation.getParentState().markModified();
-        }
+        
+        transientStateMgr.addOperation(operation);
+        operation.getParentState().markModified();
     }
 
     /**
@@ -351,11 +347,12 @@
         // NOTE: nodestate is only modified upon save of the changes!
         Name[] mixinNames = operation.getMixinNames();
         NodeState nState = operation.getNodeState();
-        NodeEntry nEntry = (NodeEntry) nState.getHierarchyEntry();
+        NodeEntry nEntry = nState.getNodeEntry();
 
-        // new array of mixinNames to be set on the nodestate (and corresponding property state)
+        // assert the existence of the property entry and set the array of
+        // mixinNames to be set on the corresponding property state
         PropertyEntry mixinEntry = nEntry.getPropertyEntry(NameConstants.JCR_MIXINTYPES);
-        if (mixinNames != null && mixinNames.length > 0) {
+        if (mixinNames.length > 0) {
             // update/create corresponding property state
             if (mixinEntry != null) {
                 // execute value of existing property
@@ -372,22 +369,16 @@
             }
             nState.markModified();
             transientStateMgr.addOperation(operation);
-        } else {
+        } else if (mixinEntry != null) {
             // remove the jcr:mixinTypes property state if already present
-            if (mixinEntry != null) {
-                PropertyState pState = mixinEntry.getPropertyState();
-                boolean newMixinState = pState.getStatus() == Status.NEW;
-                int options = ItemStateValidator.CHECK_LOCK | ItemStateValidator.CHECK_VERSIONING;
-                removeItemState(pState, options);
-                // only added the remove-mixin operation if it doesn't revert
-                // a previous 'add-mixin' (which has been removed automatically
-                // upon notification of removing the prop-state).
-                if (!newMixinState) {
-                    nState.markModified();
-                    transientStateMgr.addOperation(operation);
-                }
-            }
-        }
+            PropertyState pState = mixinEntry.getPropertyState();
+            boolean newMixinState = pState.getStatus() == Status.NEW;
+            int options = ItemStateValidator.CHECK_LOCK | ItemStateValidator.CHECK_VERSIONING;
+            removeItemState(pState, options);
+
+            nState.markModified();
+            transientStateMgr.addOperation(operation);
+        } // else: empty Name array and no mixin-prop-entry (should not occur)
     }
 
     /**
@@ -538,40 +529,6 @@
     //--------------------------------------------< Internal State Handling >---
     /**
      *
-     * @param itemState
-     * @param throwOnStale Throws InvalidItemStateException if either the given
-     * <code>ItemState</code> or any of its decendants is stale and the flag is true.
-     * @return
-     * @throws InvalidItemStateException 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.
-     * @throws RepositoryException if <code>state</code> is a new item state.
-     */
-    private ChangeLog getChangeLog(ItemState itemState, boolean throwOnStale) throws InvalidItemStateException, ConstraintViolationException, RepositoryException {
-        // build changelog for affected and decendant states only
-        ChangeLog changeLog = new ChangeLog(itemState);
-        // fail-fast test: check status of this item's state
-        if (itemState.getStatus() == Status.NEW) {
-            String msg = "Cannot save/revert an item with status NEW (" +itemState+ ").";
-            log.debug(msg);
-            throw new RepositoryException(msg);
-        }
-        if (throwOnStale && Status.isStale(itemState.getStatus())) {
-            String msg =  "Attempt to save/revert an item, that has been externally modified (" +itemState+ ").";
-            log.debug(msg);
-            throw new InvalidItemStateException(msg);
-        }
-        // collect transient/stale states that should be persisted or reverted
-        itemState.getHierarchyEntry().collectStates(changeLog, throwOnStale);
-
-        changeLog.collectOperations(transientStateMgr.getOperations());
-        changeLog.checkIsSelfContained();
-        return changeLog;
-    }
-
-    /**
-     *
      * @param parent
      * @param propertyName
      * @param propertyType
@@ -589,17 +546,17 @@
      * @throws VersionException
      * @throws RepositoryException
      */
-    private void addPropertyState(NodeState parent, Name propertyName,
+    private PropertyState addPropertyState(NodeState parent, Name propertyName,
                                   int propertyType, QValue[] values,
                                   QPropertyDefinition pDef, int options)
             throws LockException, ConstraintViolationException, AccessDeniedException, ItemExistsException, NoSuchNodeTypeException, UnsupportedRepositoryOperationException, VersionException, RepositoryException {
 
         validator.checkAddProperty(parent, propertyName, pDef, options);
         // create property state
-        transientStateMgr.createNewPropertyState(propertyName, parent, pDef, values, propertyType);
+        return transientStateMgr.createNewPropertyState(propertyName, parent, pDef, values, propertyType);
     }
 
-    private void addNodeState(NodeState parent, Name nodeName, Name nodeTypeName,
+    private List addNodeState(NodeState parent, Name nodeName, Name nodeTypeName,
                               String uuid, QNodeDefinition definition, int options)
             throws RepositoryException, ConstraintViolationException, AccessDeniedException,
             UnsupportedRepositoryOperationException, NoSuchNodeTypeException,
@@ -623,14 +580,17 @@
             }
         }
 
+        List addedStates = new ArrayList();
+
         // create new nodeState. NOTE, that the uniqueID is not added to the
-        // state for consistency between 'addNode' and importXML // TODO review
+        // state for consistency between 'addNode' and importXML
         NodeState nodeState = transientStateMgr.createNewNodeState(nodeName, null, nodeTypeName, definition, parent);
+        addedStates.add(nodeState);
         if (uuid != null) {
             QValue[] value = getQValues(uuid, qValueFactory);
             ItemDefinitionProvider defProvider = mgrProvider.getItemDefinitionProvider();
             QPropertyDefinition pDef = defProvider.getQPropertyDefinition(NameConstants.MIX_REFERENCEABLE, NameConstants.JCR_UUID, PropertyType.STRING, false);
-            addPropertyState(nodeState, NameConstants.JCR_UUID, PropertyType.STRING, value, pDef, 0);
+            addedStates.add(addPropertyState(nodeState, NameConstants.JCR_UUID, PropertyType.STRING, value, pDef, 0));
         }
 
         // add 'auto-create' properties defined in node type
@@ -642,7 +602,7 @@
                 if (autoValue != null) {
                     int propOptions = ItemStateValidator.CHECK_NONE;
                     // execute 'addProperty' without adding operation.
-                    addPropertyState(nodeState, pd.getName(), pd.getRequiredType(), autoValue, pd, propOptions);
+                    addedStates.add(addPropertyState(nodeState, pd.getName(), pd.getRequiredType(), autoValue, pd, propOptions));
                 }
             }
         }
@@ -653,8 +613,9 @@
             QNodeDefinition nd = nda[i];
             // execute 'addNode' without adding the operation.
             int opt = ItemStateValidator.CHECK_LOCK | ItemStateValidator.CHECK_COLLISION;
-            addNodeState(nodeState, nd.getName(), nd.getDefaultPrimaryType(), null, nd, opt);
+            addedStates.addAll(addNodeState(nodeState, nd.getName(), nd.getDefaultPrimaryType(), null, nd, opt));
         }
+        return addedStates;
     }
 
     private void removeItemState(ItemState itemState, int options) throws RepositoryException {

Modified: jackrabbit/branches/1.5/jackrabbit-jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/state/Status.java
URL: http://svn.apache.org/viewvc/jackrabbit/branches/1.5/jackrabbit-jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/state/Status.java?rev=704898&r1=704897&r2=704898&view=diff
==============================================================================
--- jackrabbit/branches/1.5/jackrabbit-jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/state/Status.java (original)
+++ jackrabbit/branches/1.5/jackrabbit-jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/state/Status.java Wed Oct 15 05:59:19 2008
@@ -63,7 +63,7 @@
     public static final int MODIFIED = 7;
 
     /**
-     * a new state was deleted and is now 'removed'
+     * a new state was removed and is now 'removed'
      * or an existing item has been removed by a workspace operation or
      * by an external modification.
      */
@@ -137,7 +137,7 @@
      *
      * @param status the status to check.
      * @return <code>true</code> if <code>status</code> indicates that an item
-     *         state is stale.
+     *         state is transiently modified.
      */
     public static boolean isTransient(int status) {
         return status == EXISTING_MODIFIED || status == EXISTING_REMOVED || status == NEW;
@@ -184,10 +184,10 @@
                 break;
             case STALE_MODIFIED:
             case STALE_DESTROYED:
-                isValid = (oldStatus == EXISTING_MODIFIED);
+                isValid = (oldStatus == EXISTING_MODIFIED || oldStatus == EXISTING_REMOVED || oldStatus == STALE_MODIFIED);
                 break;
             case REMOVED:
-                // external removal always possible -> getNewStatus(int, int)
+                // removal always possible -> getNewStatus(int, int)
                 isValid = true;
                 break;
             case MODIFIED:
@@ -220,7 +220,6 @@
                     // temporarily set the state to MODIFIED in order to inform listeners.
                     newStatus = Status.MODIFIED;
                 } else if (oldStatus == Status.EXISTING_MODIFIED) {
-                    // TODO: try to merge changes
                     newStatus = Status.STALE_MODIFIED;
                 } else {
                     // old status is EXISTING_REMOVED (or any other) => ignore.
@@ -229,7 +228,7 @@
                 }
                 break;
             case Status.REMOVED:
-                if (oldStatus == Status.EXISTING_MODIFIED) {
+                if (oldStatus == Status.EXISTING_MODIFIED || oldStatus == Status.STALE_MODIFIED) {
                     newStatus = Status.STALE_DESTROYED;
                 } else {
                     // applies both to NEW or to any other status

Modified: jackrabbit/branches/1.5/jackrabbit-jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/state/TransientISFactory.java
URL: http://svn.apache.org/viewvc/jackrabbit/branches/1.5/jackrabbit-jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/state/TransientISFactory.java?rev=704898&r1=704897&r2=704898&view=diff
==============================================================================
--- jackrabbit/branches/1.5/jackrabbit-jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/state/TransientISFactory.java (original)
+++ jackrabbit/branches/1.5/jackrabbit-jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/state/TransientISFactory.java Wed Oct 15 05:59:19 2008
@@ -23,6 +23,7 @@
 import org.apache.jackrabbit.spi.PropertyId;
 import org.apache.jackrabbit.spi.QPropertyDefinition;
 import org.apache.jackrabbit.spi.Name;
+import org.apache.jackrabbit.spi.QValue;
 import org.apache.jackrabbit.jcr2spi.hierarchy.NodeEntry;
 import org.apache.jackrabbit.jcr2spi.hierarchy.PropertyEntry;
 import org.apache.jackrabbit.jcr2spi.nodetype.ItemDefinitionProvider;
@@ -71,12 +72,10 @@
      * @inheritDoc
      * @see TransientItemStateFactory#createNewPropertyState(PropertyEntry, QPropertyDefinition)
      */
-    public PropertyState createNewPropertyState(PropertyEntry entry, QPropertyDefinition definition) {
-        PropertyState propState = new PropertyState(entry, this, definition, defProvider);
-
+    public PropertyState createNewPropertyState(PropertyEntry entry, QPropertyDefinition definition, QValue[] values, int propertyType) throws RepositoryException {
+        PropertyState propState = new PropertyState(entry, this, definition, defProvider, values, propertyType);
         // notify listeners that a property state has been created
         notifyCreated(propState);
-
         return propState;
     }
 

Modified: jackrabbit/branches/1.5/jackrabbit-jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/state/TransientItemStateFactory.java
URL: http://svn.apache.org/viewvc/jackrabbit/branches/1.5/jackrabbit-jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/state/TransientItemStateFactory.java?rev=704898&r1=704897&r2=704898&view=diff
==============================================================================
--- jackrabbit/branches/1.5/jackrabbit-jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/state/TransientItemStateFactory.java (original)
+++ jackrabbit/branches/1.5/jackrabbit-jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/state/TransientItemStateFactory.java Wed Oct 15 05:59:19 2008
@@ -21,6 +21,9 @@
 import org.apache.jackrabbit.jcr2spi.hierarchy.PropertyEntry;
 import org.apache.jackrabbit.spi.QNodeDefinition;
 import org.apache.jackrabbit.spi.QPropertyDefinition;
+import org.apache.jackrabbit.spi.QValue;
+
+import javax.jcr.RepositoryException;
 
 /**
  * <code>TransientItemStateFactory</code> extends the item state factory and
@@ -46,8 +49,12 @@
      *
      * @param entry
      * @param definition
+     * @param values
+     * @param propertyType
      * @return the created <code>PropertyState</code>.
      */
     public PropertyState createNewPropertyState(PropertyEntry entry,
-                                                QPropertyDefinition definition);
+                                                QPropertyDefinition definition,
+                                                QValue[] values, int propertyType)
+            throws RepositoryException;
 }

Modified: jackrabbit/branches/1.5/jackrabbit-jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/state/TransientItemStateManager.java
URL: http://svn.apache.org/viewvc/jackrabbit/branches/1.5/jackrabbit-jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/state/TransientItemStateManager.java?rev=704898&r1=704897&r2=704898&view=diff
==============================================================================
--- jackrabbit/branches/1.5/jackrabbit-jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/state/TransientItemStateManager.java (original)
+++ jackrabbit/branches/1.5/jackrabbit-jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/state/TransientItemStateManager.java Wed Oct 15 05:59:19 2008
@@ -16,19 +16,26 @@
  */
 package org.apache.jackrabbit.jcr2spi.state;
 
-import org.apache.jackrabbit.jcr2spi.operation.Operation;
+import org.apache.commons.collections.iterators.IteratorChain;
+import org.apache.jackrabbit.jcr2spi.hierarchy.HierarchyEntry;
 import org.apache.jackrabbit.jcr2spi.hierarchy.NodeEntry;
+import org.apache.jackrabbit.jcr2spi.hierarchy.PropertyEntry;
+import org.apache.jackrabbit.jcr2spi.operation.Operation;
 import org.apache.jackrabbit.spi.Name;
 import org.apache.jackrabbit.spi.QNodeDefinition;
 import org.apache.jackrabbit.spi.QPropertyDefinition;
 import org.apache.jackrabbit.spi.QValue;
-import org.slf4j.LoggerFactory;
 import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 
+import javax.jcr.InvalidItemStateException;
 import javax.jcr.ItemExistsException;
 import javax.jcr.RepositoryException;
 import javax.jcr.nodetype.ConstraintViolationException;
+import java.util.Collection;
 import java.util.Iterator;
+import java.util.LinkedHashSet;
+import java.util.Set;
 
 /**
  * <code>TransientItemStateManager</code> adds support for transient changes on
@@ -47,23 +54,40 @@
     private static final Logger log = LoggerFactory.getLogger(TransientItemStateManager.class);
 
     /**
-     * The change log which keeps track of changes and maintains hard references
-     * to changed item states.
+     * Added states
+     */
+    private final Set addedStates = new LinkedHashSet();
+
+    /**
+     * Modified states
+     */
+    private final Set modifiedStates = new LinkedHashSet();
+
+    /**
+     * Removed states
      */
-    private final ChangeLog changeLog;
+    private final Set removedStates = new LinkedHashSet();
+    /**
+     * Stale states
+     */
+    private final Set staleStates = new LinkedHashSet();
+
+    /**
+     * Set of operations
+     */
+    private Set operations = new LinkedHashSet();
 
     /**
      *
      */
     TransientItemStateManager() {
-        this.changeLog = new ChangeLog(null);
     }
 
     /**
      * @return the operations that have been recorded until now.
      */
     Iterator getOperations() {
-        return changeLog.getOperations();
+        return operations.iterator();
     }
 
     /**
@@ -73,14 +97,121 @@
      * @param operation
      */
     void addOperation(Operation operation) {
-        changeLog.addOperation(operation);
+        operations.add(operation);
     }
 
     /**
      * @return <code>true</code> if this transient ISM has pending changes.
      */
     boolean hasPendingChanges() {
-        return !changeLog.isEmpty();
+        return !operations.isEmpty();
+    }
+
+    /**
+     * Create the change log for the tree starting at <code>target</code>. This
+     * includes a  check if the ChangeLog to be created is totally 'self-contained'
+     * and independant; items within the scope of this update operation (i.e.
+     * below the target) must not have dependencies outside of this tree (e.g.
+     * moving a node requires that the target node including both old and new
+     * parents are saved).
+     *
+     * @param target
+     * @param throwOnStale Throws InvalidItemStateException if either the given
+     * <code>ItemState</code> or any of its decendants is stale and the flag is true.
+     * @return
+     * @throws InvalidItemStateException 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.
+     * @throws RepositoryException if <code>state</code> is a new item state.
+     */
+    ChangeLog getChangeLog(ItemState target, boolean throwOnStale) throws InvalidItemStateException, ConstraintViolationException, RepositoryException {
+        // fail-fast test: check status of this item's state
+        if (target.getStatus() == Status.NEW) {
+            String msg = "Cannot save/revert an item with status NEW (" +target+ ").";
+            log.debug(msg);
+            throw new RepositoryException(msg);
+        }
+        if (throwOnStale && Status.isStale(target.getStatus())) {
+            String msg =  "Attempt to save/revert an item, that has been externally modified (" +target+ ").";
+            log.debug(msg);
+            throw new InvalidItemStateException(msg);
+        }
+
+        Set ops = new LinkedHashSet();
+        Set affectedStates = new LinkedHashSet();
+
+        HierarchyEntry he = target.getHierarchyEntry();
+        if (he.getParent() == null) {
+            // the root entry -> the complete change log can be used for
+            // simplicity. collecting ops, states can be omitted.
+            if (throwOnStale && !staleStates.isEmpty()) {
+                String msg = "Cannot save changes: States has been modified externally.";
+                log.debug(msg);
+                throw new InvalidItemStateException(msg);
+            } else {
+                affectedStates.addAll(staleStates);
+            }
+            ops.addAll(operations);
+            affectedStates.addAll(addedStates);
+            affectedStates.addAll(modifiedStates);
+            affectedStates.addAll(removedStates);
+        } else {
+            // not root entry:
+            // - check if there is a stale state in the scope (save only)
+            if (throwOnStale) {
+                for (Iterator it = staleStates.iterator(); it.hasNext();) {
+                    ItemState state = (ItemState) it.next();
+                    if (containedInTree(target, state)) {
+                        String msg = "Cannot save changes: States has been modified externally.";
+                        log.debug(msg);
+                        throw new InvalidItemStateException(msg);
+                    }
+                }
+            }
+            // - collect all affected states within the scope of save/undo
+            Iterator[] its = new Iterator[] {
+                    addedStates.iterator(),
+                    removedStates.iterator(),
+                    modifiedStates.iterator()
+            };
+            IteratorChain chain = new IteratorChain(its);
+            if (!throwOnStale) {
+                chain.addIterator(staleStates.iterator());
+            }
+            while (chain.hasNext()) {
+                ItemState state = (ItemState) chain.next();
+                if (containedInTree(target, state)) {
+                    affectedStates.add(state);
+                }
+            }
+            // - collect the set of operations and
+            //   check if the affected states listed by the operations are all
+            //   listed in the modified,removed or added states collected by this
+            //   changelog.
+            for (Iterator it = operations.iterator(); it.hasNext();) {
+                Operation op = (Operation) it.next();
+                Collection opStates = op.getAffectedItemStates();
+                for (Iterator osIt = opStates.iterator(); osIt.hasNext();) {
+                    ItemState state = (ItemState) osIt.next();
+                    if (affectedStates.contains(state)) {
+                        // operation needs to be included
+                        if (!affectedStates.containsAll(opStates)) {
+                            // incomplete changelog: need to save a parent as well
+                            String msg = "ChangeLog is not self contained.";
+                            throw new ConstraintViolationException(msg);
+                        }
+                        // no violation: add operation an stop iteration over
+                        // all affected states present in the operation.
+                        ops.add(op);
+                        break;
+                    }
+                }
+            }
+        }
+
+        ChangeLog cl = new ChangeLog(target, ops, affectedStates);
+        return cl;
     }
 
     /**
@@ -99,15 +230,14 @@
     NodeState createNewNodeState(Name nodeName, String uniqueID, Name nodeTypeName,
                                  QNodeDefinition definition, NodeState parent)
             throws RepositoryException {
-        NodeState nodeState = ((NodeEntry) parent.getHierarchyEntry()).addNewNodeEntry(nodeName, uniqueID, nodeTypeName, definition);
+        NodeEntry ne = ((NodeEntry) parent.getHierarchyEntry()).addNewNodeEntry(nodeName, uniqueID, nodeTypeName, definition);
         try {
             parent.markModified();
         } catch (RepositoryException e) {
-            nodeState.getHierarchyEntry().remove();
+            ne.remove();
             throw e;
         }
-
-        return nodeState;
+        return ne.getNodeState();
     }
 
     /**
@@ -129,15 +259,14 @@
             throws ItemExistsException, ConstraintViolationException, RepositoryException {
         // NOTE: callers must make sure, the property type is not 'undefined'
         NodeEntry nodeEntry = (NodeEntry) parent.getHierarchyEntry();
-        PropertyState propState = nodeEntry.addNewPropertyEntry(propName, definition);
+        PropertyEntry pe = nodeEntry.addNewPropertyEntry(propName, definition, values, propertyType);
         try {
-            propState.setValues(values, propertyType);
             parent.markModified();
         } catch (RepositoryException e) {
-            propState.getHierarchyEntry().remove();
+            pe.remove();
             throw e;
         }
-        return propState;
+        return pe.getPropertyState();
     }
 
     /**
@@ -145,19 +274,74 @@
      * transiently modified item states.
      */
     void dispose() {
-        changeLog.reset();
+        addedStates.clear();
+        modifiedStates.clear();
+        removedStates.clear();
+        staleStates.clear();
+        // also clear all operations
+        operations.clear();
     }
 
     /**
-     * Remove the states and operations listed in the changeLog from the
-     * internal changeLog.
+     * Remove the states and operations listed in the changeLog from internal
+     * list of modifications.
      *
      * @param subChangeLog
      */
     void dispose(ChangeLog subChangeLog) {
-        changeLog.removeAll(subChangeLog);
+        Set affectedStates = subChangeLog.getAffectedStates();
+        addedStates.removeAll(affectedStates);
+        modifiedStates.removeAll(affectedStates);
+        removedStates.removeAll(affectedStates);
+        staleStates.removeAll(affectedStates);
+
+        operations.removeAll(subChangeLog.getOperations());
+    }
+
+    /**
+     * A state has been removed. If the state is not a new state
+     * (not in the collection of added ones), then remove
+     * it from the modified states collection and add it to the
+     * removed states collection.
+     *
+     * @param state state that has been removed
+     */
+    private void removed(ItemState state) {
+        if (!addedStates.remove(state)) {
+            modifiedStates.remove(state);
+        }
+        removedStates.add(state);
     }
 
+   /**
+     *
+     * @param parent
+     * @param state
+     * @return
+     */
+    private static boolean containedInTree(ItemState parent, ItemState state) {
+        HierarchyEntry he = state.getHierarchyEntry();
+       HierarchyEntry pHe = parent.getHierarchyEntry();
+       // short cuts first
+       if (he == pHe || he.getParent() == pHe) {
+           return true;
+       }
+       if (!parent.isNode() || he == pHe.getParent()) {
+           return false;
+       }
+       // none of the simple cases: walk up hierarchy
+       HierarchyEntry pe = he.getParent();
+       while (pe != null) {
+           if (pe == pHe) {
+               return true;
+           }
+           pe = pe.getParent();
+       }
+
+       // state isn't descendant of 'parent'
+       return false;
+   }
+
     //-----------------------------------------< ItemStateLifeCycleListener >---
     /**
      * Depending on status of the given state adapt change log.
@@ -168,28 +352,77 @@
      * @see ItemStateLifeCycleListener#statusChanged(ItemState, int)
      */
     public void statusChanged(ItemState state, int previousStatus) {
-        if (changeLog.isEmpty()) {
-            return;
-        }
+        /*
+        Update the collections of states that were transiently modified.
+        NOTE: cleanup of operations is omitted here. this is expected to
+        occur upon {@link ChangeLog#save()} and {@link ChangeLog#undo()}.
+        External modifications in contrast that clash with transient modifications
+        render the corresponding states stale.
+        */
         switch (state.getStatus()) {
-            case Status.EXISTING:
+            case (Status.EXISTING):
+                switch (previousStatus) {
+                    case Status.EXISTING_MODIFIED:
+                        // was modified and got persisted or reverted
+                        modifiedStates.remove(state);
+                        break;
+                    case Status.EXISTING_REMOVED:
+                        // was transiently removed and is now reverted
+                        removedStates.remove(state);
+                        break;
+                    case Status.STALE_MODIFIED:
+                        // was modified and stale and is now reverted
+                        staleStates.remove(state);
+                        break;
+                    case Status.NEW:
+                        // was new and has been saved now
+                        addedStates.remove(state);
+                        break;
+                    //default:
+                        // INVALIDATED, MODIFIED ignore. no effect to transient modifications.
+                        // any other status change is invalid -> see Status#isValidStatusChange(int, int
+                }
+                break;
             case Status.EXISTING_MODIFIED:
-            case Status.EXISTING_REMOVED:
-            case Status.REMOVED:
-                changeLog.statusChanged(state, previousStatus);
+                // transition from EXISTING to EXISTING_MODIFIED
+                modifiedStates.add(state);
+                break;
+            case (Status.EXISTING_REMOVED):
+                // transition from EXISTING or EXISTING_MODIFIED to EXISTING_REMOVED
+                removed(state);
+                break;
+            case (Status.REMOVED):
+                switch (previousStatus) {
+                    case Status.EXISTING_REMOVED:
+                        // was transiently removed and removal was persisted.
+                        // -> ignore
+                        break;
+                    case Status.NEW:
+                        // a new entry was removed again: remember as removed
+                        // in order to keep the operations and the affected
+                        // states in sync
+                        removed(state);
+                        break;
+                }
                 break;
             case Status.STALE_DESTROYED:
             case Status.STALE_MODIFIED:
-                // state is now stale. keep in modified. wait until refreshed
+                /**
+                state is stale due to external modification -> move it to
+                the collection of stale item states.
+                validation omitted for only 'existing_modified' states can
+                become stale see {@link Status#isValidStatusChange(int, int)}
+                 */
+                modifiedStates.remove(state);
+                staleStates.add(state);
+                break;
             case Status.MODIFIED:
-                // MODIFIED is only possible on EXISTING states -> thus, there
-                // must not be any transient modifications for that state.
-                // we ignore it.
             case Status.INVALIDATED:
-                // -> nothing to do here.
+                // MODIFIED, INVALIDATED: ignore.
+                log.debug("Item " + state.getName() + " changed status from " + Status.getName(previousStatus) + " to " + Status.getName(state.getStatus()) + ".");
                 break;
             default:
-                log.error("ItemState has invalid status: " + state.getStatus());
+                log.error("ItemState "+ state.getName() + " has invalid status: " + state.getStatus());
         }
     }
 
@@ -200,7 +433,7 @@
     public void created(ItemState state) {
         // new state has been created
         if (state.getStatus() == Status.NEW) {
-            changeLog.added(state);
+            addedStates.add(state);
         }
     }
 }

Modified: jackrabbit/branches/1.5/jackrabbit-jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/state/WorkspaceItemStateFactory.java
URL: http://svn.apache.org/viewvc/jackrabbit/branches/1.5/jackrabbit-jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/state/WorkspaceItemStateFactory.java?rev=704898&r1=704897&r2=704898&view=diff
==============================================================================
--- jackrabbit/branches/1.5/jackrabbit-jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/state/WorkspaceItemStateFactory.java (original)
+++ jackrabbit/branches/1.5/jackrabbit-jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/state/WorkspaceItemStateFactory.java Wed Oct 15 05:59:19 2008
@@ -145,7 +145,9 @@
     public PropertyState createDeepPropertyState(PropertyId propertyId, NodeEntry anyParent) throws ItemNotFoundException, RepositoryException {
         try {
             PropertyInfo info = service.getPropertyInfo(sessionInfo, propertyId);
-            return createDeepPropertyState(info, anyParent, null);
+            PropertyState propState = createDeepPropertyState(info, anyParent, null);
+            assertValidState(propState, info);
+            return propState;
         } catch (PathNotFoundException e) {
             throw new ItemNotFoundException(e.getMessage());
         }
@@ -210,7 +212,7 @@
                 // the given NodeEntry -> retrieve NodeState before executing
                 // validation check.
                 nodeState = createDeepNodeState(first, entry, infos);
-                assertMatchingPath(first, nodeState.getNodeEntry());
+                assertValidState(nodeState, first);
             } else {
                 // 'isDeep' == false -> the given NodeEntry must match to the
                 // first ItemInfo retrieved from the iterator.
@@ -261,9 +263,10 @@
             parent.setUniqueID(uniqueID);
         }
 
-        // now build the nodestate itself
-        NodeState state = new NodeState(entry, info, this, definitionProvider);
-        state.setMixinTypeNames(info.getMixins());
+        if (Status.isTransient(entry.getStatus()) || Status.isStale(entry.getStatus())) {
+            log.debug("Node has pending changes; omit resetting the state.");
+            return entry.getNodeState();
+        }
 
         // update NodeEntry from the information present in the NodeInfo (prop entries)
         List propNames = new ArrayList();
@@ -273,7 +276,7 @@
             propNames.add(propertyName);
         }
         try {
-            entry.addPropertyEntries(propNames);
+            entry.setPropertyEntries(propNames);
         } catch (ItemExistsException e) {
             // should not get here
             log.warn("Internal error", e);
@@ -286,8 +289,18 @@
             entry.setNodeEntries(childInfos);
         }
 
-        notifyCreated(state);
-        return state;
+        // now build or update the nodestate itself
+        NodeState tmp = new NodeState(entry, info, this, definitionProvider);
+        entry.setItemState(tmp);
+
+        NodeState nState = entry.getNodeState();
+        if (nState == tmp) {
+            // tmp state was used as resolution for the given entry i.e. the
+            // entry was not available before. otherwise the 2 states were
+            // merged. see HierarchyEntryImpl#setItemState
+            notifyCreated(nState);
+        }
+        return nState;
     }
 
     /**
@@ -298,7 +311,8 @@
      * @param entry
      * @return the new <code>PropertyState</code>.
      */
-    private PropertyState createPropertyState(PropertyInfo info, PropertyEntry entry) {
+    private PropertyState createPropertyState(PropertyInfo info, PropertyEntry entry)
+            throws RepositoryException {
         // make sure uuid part of id is correct
         String uniqueID = info.getId().getUniqueID();
         if (uniqueID != null) {
@@ -307,11 +321,23 @@
             parent.setUniqueID(uniqueID);
         }
 
-        // build the PropertyState
-        PropertyState state = new PropertyState(entry, info, this, definitionProvider);
-
-        notifyCreated(state);
-        return state;
+        if (Status.isTransient(entry.getStatus()) || Status.isStale(entry.getStatus())) {
+            log.debug("Property has pending changes; omit resetting the state.");
+            return entry.getPropertyState();
+        }
+
+        // now build or update the nodestate itself
+        PropertyState tmp = new PropertyState(entry, info, this, definitionProvider);
+        entry.setItemState(tmp);
+
+        PropertyState pState = entry.getPropertyState();
+        if (pState == tmp) {
+            // tmp state was used as resolution for the given entry i.e. the
+            // entry was not available before. otherwise the 2 states were
+            // merged. see HierarchyEntryImpl#setItemState
+            notifyCreated(pState);
+        }
+        return pState;
     }
 
     /**
@@ -326,10 +352,15 @@
             // node for nodeId exists -> build missing entries in hierarchy
             // Note, that the path contained in NodeId does not reveal which
             // entries are missing -> calculate relative path.
-            Path anyParentPath = anyParent.getPath();
+            Path anyParentPath = anyParent.getWorkspacePath();
             Path relPath = anyParentPath.computeRelativePath(info.getPath());
             Path.Element[] missingElems = relPath.getElements();
 
+            if (startsWithIllegalElement(missingElems)) {
+                log.error("Relative path to NodeEntry starts with illegal element -> ignore NodeInfo with path " + info.getPath());
+                return null;
+            }
+
             NodeEntry entry = anyParent;
             for (int i = 0; i < missingElems.length; i++) {
                 Name name = missingElems[i].getName();
@@ -360,8 +391,16 @@
             Path anyParentPath = anyParent.getWorkspacePath();
             Path relPath = anyParentPath.computeRelativePath(info.getPath());
             Path.Element[] missingElems = relPath.getElements();
-            NodeEntry entry = anyParent;
 
+            // make sure the missing elements don't start with . or .. in which
+            // case the info is not within the tree as it is expected
+            // (see also JCR-1797)
+            if (startsWithIllegalElement(missingElems)) {
+                log.error("Relative path to PropertyEntry starts with illegal element -> ignore PropertyInfo with path " + info.getPath());
+                return null;
+            }
+
+            NodeEntry entry = anyParent;
             int i = 0;
             // NodeEntries except for the very last 'missingElem'
             while (i < missingElems.length - 1) {
@@ -402,6 +441,23 @@
     }
 
     /**
+     * Validation check: make sure the state is not null (was really created)
+     * and matches with the specified ItemInfo (path).
+     *
+     * @param state
+     * @param info
+     * @throws ItemNotFoundException
+     * @throws RepositoryException
+     */
+    private static void assertValidState(ItemState state, ItemInfo info)
+            throws ItemNotFoundException, RepositoryException {
+        if (state == null) {
+            throw new ItemNotFoundException("HierarchyEntry does not belong to any existing ItemInfo. No ItemState was created.");
+        }
+        assertMatchingPath(info, state.getHierarchyEntry());
+    }
+
+    /**
      * Validation check: Path of the given ItemInfo must match to the Path of
      * the HierarchyEntry. This is required for Items that are identified by
      * a uniqueID that may move within the hierarchy upon restore or clone.
@@ -421,6 +477,22 @@
     }
 
     /**
+     * Returns true if the given <code>missingElems</code> start with a parent (..),
+     * a current (.) or the root element, in which case the info is not within
+     * the tree as it is expected.
+     * See also #JCR-1797 for the corresponding enhancement request.
+     *
+     * @param missingElems
+     * @return
+     */
+    private static boolean startsWithIllegalElement(Path.Element[] missingElems) {
+        if (missingElems.length > 0) {
+            return !missingElems[0].denotesName();
+        }
+        return false;
+    }
+
+    /**
      * @param entry
      * @param degree
      * @return the ancestor entry at the specified degree.

Modified: jackrabbit/branches/1.5/jackrabbit-jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/util/LogUtil.java
URL: http://svn.apache.org/viewvc/jackrabbit/branches/1.5/jackrabbit-jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/util/LogUtil.java?rev=704898&r1=704897&r2=704898&view=diff
==============================================================================
--- jackrabbit/branches/1.5/jackrabbit-jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/util/LogUtil.java (original)
+++ jackrabbit/branches/1.5/jackrabbit-jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/util/LogUtil.java Wed Oct 15 05:59:19 2008
@@ -65,9 +65,8 @@
         try {
             return safeGetJCRPath(itemState.getHierarchyEntry().getPath(), pathResolver);
         } catch (RepositoryException e) {
-            ItemId id = itemState.getId();
-            log.error("failed to convert " + id + " to JCR path.");
-            return id.toString();
+            log.error("failed to convert " + itemState.toString() + " to JCR path.");
+            return itemState.toString();
         }
     }
 

Modified: jackrabbit/branches/1.5/jackrabbit-jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/util/StateUtility.java
URL: http://svn.apache.org/viewvc/jackrabbit/branches/1.5/jackrabbit-jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/util/StateUtility.java?rev=704898&r1=704897&r2=704898&view=diff
==============================================================================
--- jackrabbit/branches/1.5/jackrabbit-jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/util/StateUtility.java (original)
+++ jackrabbit/branches/1.5/jackrabbit-jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/util/StateUtility.java Wed Oct 15 05:59:19 2008
@@ -16,13 +16,11 @@
  */
 package org.apache.jackrabbit.jcr2spi.util;
 
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.apache.jackrabbit.spi.Name;
+import org.apache.jackrabbit.jcr2spi.hierarchy.NodeEntry;
+import org.apache.jackrabbit.jcr2spi.state.NodeState;
 import org.apache.jackrabbit.jcr2spi.state.PropertyState;
 import org.apache.jackrabbit.jcr2spi.state.Status;
-import org.apache.jackrabbit.jcr2spi.state.NodeState;
-import org.apache.jackrabbit.jcr2spi.hierarchy.NodeEntry;
+import org.apache.jackrabbit.spi.Name;
 import org.apache.jackrabbit.spi.QValue;
 import org.apache.jackrabbit.spi.commons.name.NameConstants;
 
@@ -33,8 +31,6 @@
  */
 public class StateUtility {
 
-    private static Logger log = LoggerFactory.getLogger(StateUtility.class);
-
     /**
      *
      * @param ps

Modified: jackrabbit/branches/1.5/jackrabbit-jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/xml/SessionImporter.java
URL: http://svn.apache.org/viewvc/jackrabbit/branches/1.5/jackrabbit-jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/xml/SessionImporter.java?rev=704898&r1=704897&r2=704898&view=diff
==============================================================================
--- jackrabbit/branches/1.5/jackrabbit-jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/xml/SessionImporter.java (original)
+++ jackrabbit/branches/1.5/jackrabbit-jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/xml/SessionImporter.java Wed Oct 15 05:59:19 2008
@@ -453,19 +453,14 @@
             Operation an = AddNode.create(parent, nodeInfo.getName(), ntName, nodeInfo.getUUID());
             stateMgr.execute(an);
             // retrieve id of state that has been created during execution of AddNode
-            NodeState childState;
-            List cne = parent.getNodeEntry().getNodeEntries(nodeInfo.getName());
-            if (def.allowsSameNameSiblings()) {
-                // TODO TOBEFIXED find proper solution. problem with same-name-siblings
-                childState = ((NodeEntry)cne.get(cne.size()-1)).getNodeState();
-            } else {
-                childState = ((NodeEntry)cne.get(0)).getNodeState();
-            }
+            NodeState childState = (NodeState) ((AddNode) an).getAddedStates().get(0);
 
             // and set mixin types
-            // TODO: missing validation
-            Operation sm = SetMixin.create(childState, nodeInfo.getMixinNames());
-            stateMgr.execute(sm);
+            Name[] mixinNames = nodeInfo.getMixinNames();
+            if (mixinNames != null && mixinNames.length > 0) {
+                Operation sm = SetMixin.create(childState, nodeInfo.getMixinNames());
+                stateMgr.execute(sm);
+            }
             return childState;
         }
     }

Modified: jackrabbit/branches/1.5/jackrabbit-jcr2spi/src/test/java/org/apache/jackrabbit/jcr2spi/HierarchyNodeTest.java
URL: http://svn.apache.org/viewvc/jackrabbit/branches/1.5/jackrabbit-jcr2spi/src/test/java/org/apache/jackrabbit/jcr2spi/HierarchyNodeTest.java?rev=704898&r1=704897&r2=704898&view=diff
==============================================================================
--- jackrabbit/branches/1.5/jackrabbit-jcr2spi/src/test/java/org/apache/jackrabbit/jcr2spi/HierarchyNodeTest.java (original)
+++ jackrabbit/branches/1.5/jackrabbit-jcr2spi/src/test/java/org/apache/jackrabbit/jcr2spi/HierarchyNodeTest.java Wed Oct 15 05:59:19 2008
@@ -88,7 +88,11 @@
 
     public void testGetProperties() throws RepositoryException {
         Session readSession = helper.getReadOnlySession();
-        dump((Node) readSession.getItem(fileNode.getPath()));
+        try {
+            dump((Node) readSession.getItem(fileNode.getPath()));
+        } finally {
+            readSession.logout();
+        }
     }
 
     /** Recursively outputs the contents of the given node. */

Modified: jackrabbit/branches/1.5/jackrabbit-jcr2spi/src/test/java/org/apache/jackrabbit/jcr2spi/MoveNewTreeTest.java
URL: http://svn.apache.org/viewvc/jackrabbit/branches/1.5/jackrabbit-jcr2spi/src/test/java/org/apache/jackrabbit/jcr2spi/MoveNewTreeTest.java?rev=704898&r1=704897&r2=704898&view=diff
==============================================================================
--- jackrabbit/branches/1.5/jackrabbit-jcr2spi/src/test/java/org/apache/jackrabbit/jcr2spi/MoveNewTreeTest.java (original)
+++ jackrabbit/branches/1.5/jackrabbit-jcr2spi/src/test/java/org/apache/jackrabbit/jcr2spi/MoveNewTreeTest.java Wed Oct 15 05:59:19 2008
@@ -22,6 +22,9 @@
 import javax.jcr.RepositoryException;
 import javax.jcr.Item;
 import javax.jcr.PathNotFoundException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Iterator;
 
 /**
  * <code>MoveTreeTest</code>...
@@ -46,7 +49,6 @@
         assertTrue("Moving a node must move all child items as well.", ancestor.isSame(destParentNode));
         ancestor = grandChildNode.getAncestor(degree);
         assertTrue("Moving a node must move all child items as well.", ancestor.isSame(destParentNode));
-
     }
 
     public void testTreeEntries() throws RepositoryException {
@@ -109,4 +111,23 @@
             // OK
         }
     }
+
+    public void testRefreshMovedTree() throws RepositoryException {
+        testRootNode.refresh(true);
+        String msg = "Refresh must not revert a moved tree.";
+
+        assertFalse(msg, superuser.itemExists(srcPath + "/" + nodeName2 + "/" + nodeName3));
+        int degree = destParentNode.getDepth();
+
+        List l = new ArrayList();
+        l.add(childNode);
+        l.add(childProperty);
+        l.add(grandChildNode);
+
+        for (Iterator it = l.iterator(); it.hasNext();) {
+            Item item = (Item) it.next();
+            assertTrue(msg, item.isNew());
+            assertTrue(msg, childNode.getAncestor(degree).isSame(destParentNode));
+        }
+    }
 }

Modified: jackrabbit/branches/1.5/jackrabbit-jcr2spi/src/test/java/org/apache/jackrabbit/jcr2spi/NodeOrderTest.java
URL: http://svn.apache.org/viewvc/jackrabbit/branches/1.5/jackrabbit-jcr2spi/src/test/java/org/apache/jackrabbit/jcr2spi/NodeOrderTest.java?rev=704898&r1=704897&r2=704898&view=diff
==============================================================================
--- jackrabbit/branches/1.5/jackrabbit-jcr2spi/src/test/java/org/apache/jackrabbit/jcr2spi/NodeOrderTest.java (original)
+++ jackrabbit/branches/1.5/jackrabbit-jcr2spi/src/test/java/org/apache/jackrabbit/jcr2spi/NodeOrderTest.java Wed Oct 15 05:59:19 2008
@@ -109,4 +109,20 @@
             another.logout();
         }
     }
+
+    /**
+     * Test if the order of Nodes is the same when accessed through another
+     * <code>Session</code> after having accessed some of the nodes individually.
+     */
+    public void testOrderAfterIndividualAccess2() throws RepositoryException {
+        Session another = helper.getReadOnlySession();
+        try {
+            Node n2 = (Node) another.getItem(children[3].getPath());
+            Node n3 = (Node) another.getItem(children[1].getPath());
+            NodeIterator it = ((Node) another.getItem(testRootNode.getPath())).getNodes();
+            checkOrder(it, children);
+        } finally {
+            another.logout();
+        }
+    }
 }
\ No newline at end of file

Modified: jackrabbit/branches/1.5/jackrabbit-jcr2spi/src/test/java/org/apache/jackrabbit/jcr2spi/RefreshMovedTest.java
URL: http://svn.apache.org/viewvc/jackrabbit/branches/1.5/jackrabbit-jcr2spi/src/test/java/org/apache/jackrabbit/jcr2spi/RefreshMovedTest.java?rev=704898&r1=704897&r2=704898&view=diff
==============================================================================
--- jackrabbit/branches/1.5/jackrabbit-jcr2spi/src/test/java/org/apache/jackrabbit/jcr2spi/RefreshMovedTest.java (original)
+++ jackrabbit/branches/1.5/jackrabbit-jcr2spi/src/test/java/org/apache/jackrabbit/jcr2spi/RefreshMovedTest.java Wed Oct 15 05:59:19 2008
@@ -80,19 +80,22 @@
      */
     public void testRefreshOtherSession() throws RepositoryException {
         Session readSession = helper.getReadOnlySession();
-        Node anotherNode = (Node) readSession.getItem(srcPath);
-
-        // workspace move
-        testRootNode.getSession().getWorkspace().move(srcPath, destinationPath);
-
-        readSession.refresh(false);
         try {
-            String p = anotherNode.getPath();
-            // unless InvalidItemStateException is thrown the node must have
-            // been 'moved' to its new position.
-            assertTrue("Upon refresh of a node moved by another session it must be moved to the new destination (or removed).", p.equals(destinationPath));
-        } catch (InvalidItemStateException e) {
-            // ok as well.
+            Node anotherNode = (Node) readSession.getItem(srcPath);
+            // workspace move
+            testRootNode.getSession().getWorkspace().move(srcPath, destinationPath);
+
+            readSession.refresh(false);
+            try {
+                String p = anotherNode.getPath();
+                // unless InvalidItemStateException is thrown the node must have
+                // been 'moved' to its new position.
+                assertTrue("Upon refresh of a node moved by another session it must be moved to the new destination (or removed).", p.equals(destinationPath));
+            } catch (InvalidItemStateException e) {
+                // ok as well.
+            }
+        } finally {
+            readSession.logout();
         }
     }
 }
\ No newline at end of file

Modified: jackrabbit/branches/1.5/jackrabbit-jcr2spi/src/test/java/org/apache/jackrabbit/jcr2spi/RefreshTrueTest.java
URL: http://svn.apache.org/viewvc/jackrabbit/branches/1.5/jackrabbit-jcr2spi/src/test/java/org/apache/jackrabbit/jcr2spi/RefreshTrueTest.java?rev=704898&r1=704897&r2=704898&view=diff
==============================================================================
--- jackrabbit/branches/1.5/jackrabbit-jcr2spi/src/test/java/org/apache/jackrabbit/jcr2spi/RefreshTrueTest.java (original)
+++ jackrabbit/branches/1.5/jackrabbit-jcr2spi/src/test/java/org/apache/jackrabbit/jcr2spi/RefreshTrueTest.java Wed Oct 15 05:59:19 2008
@@ -16,18 +16,16 @@
  */
 package org.apache.jackrabbit.jcr2spi;
 
+import org.apache.jackrabbit.test.AbstractJCRTest;
+import org.apache.jackrabbit.test.NotExecutableException;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
-import org.apache.jackrabbit.test.NotExecutableException;
-import org.apache.jackrabbit.test.AbstractJCRTest;
 
-import javax.jcr.Value;
+import javax.jcr.InvalidItemStateException;
+import javax.jcr.Node;
 import javax.jcr.Property;
 import javax.jcr.RepositoryException;
-import javax.jcr.InvalidItemStateException;
-import javax.jcr.version.VersionException;
-import javax.jcr.nodetype.ConstraintViolationException;
-import javax.jcr.lock.LockException;
+import javax.jcr.Value;
 
 /**
  * <code>RefreshTrue</code>...
@@ -52,6 +50,22 @@
         super.tearDown();
     }
 
+    public void testNewNode() throws RepositoryException {
+        Node n = testRootNode.addNode(nodeName2);
+        Property p = n.setProperty(propertyName1, testValue);
+        testRootNode.refresh(true);
+
+        // n must still be new and accessible
+        String msg = "Refresh 'true' must not affect the new Node/Property.";
+        assertTrue(msg, testRootNode.hasNode(nodeName2));
+        assertTrue(msg, n.isNew());
+        assertTrue(msg, n.hasProperty(propertyName1));
+
+        // p must still be accessible
+        p.getString();
+        assertTrue(msg, p.isSame(n.getProperty(propertyName1)));
+    }
+
     public void testNewProperty() throws RepositoryException {
         Property p = testRootNode.setProperty(propertyName1, testValue);
         testRootNode.refresh(true);
@@ -63,7 +77,7 @@
         assertTrue("Refresh 'true' must not affect a new Property.", p.isSame(pAgain));
     }
 
-    public void testRemovedProperty() throws RepositoryException, LockException, ConstraintViolationException, VersionException {
+    public void testRemovedProperty() throws RepositoryException {
         Property p = testRootNode.setProperty(propertyName1, testValue);
         testRootNode.save();
 
@@ -79,4 +93,30 @@
         }
         assertFalse("Refresh 'true' must not revert removal of an item.", testRootNode.hasProperty(propertyName1));
     }
+
+    public void testRemovedNewItem() throws RepositoryException {
+        Node n = testRootNode.addNode(nodeName2);
+        Property p = n.setProperty(propertyName1, testValue);
+        n.remove();
+
+        testRootNode.refresh(true);
+
+        // n must still be new and accessible
+        String msg = "Refresh 'true' must revert the removal of new a Node/Property.";
+        assertFalse(msg, testRootNode.hasNode(nodeName2));
+        assertFalse(msg, n.isNew() && n.isModified());
+        assertFalse(msg, p.isNew() && p.isModified());
+        try {
+            n.hasProperty(propertyName1);
+            fail(msg);
+        } catch (InvalidItemStateException e) {
+            // success
+        }
+        try {
+            p.getString();
+            fail(msg);
+        } catch (InvalidItemStateException e) {
+            // success
+        }
+    }
 }
\ No newline at end of file

Modified: jackrabbit/branches/1.5/jackrabbit-jcr2spi/src/test/java/org/apache/jackrabbit/jcr2spi/RemoveNodeTest.java
URL: http://svn.apache.org/viewvc/jackrabbit/branches/1.5/jackrabbit-jcr2spi/src/test/java/org/apache/jackrabbit/jcr2spi/RemoveNodeTest.java?rev=704898&r1=704897&r2=704898&view=diff
==============================================================================
--- jackrabbit/branches/1.5/jackrabbit-jcr2spi/src/test/java/org/apache/jackrabbit/jcr2spi/RemoveNodeTest.java (original)
+++ jackrabbit/branches/1.5/jackrabbit-jcr2spi/src/test/java/org/apache/jackrabbit/jcr2spi/RemoveNodeTest.java Wed Oct 15 05:59:19 2008
@@ -134,12 +134,12 @@
             childNode.remove();
             superuser.save();
 
-            // try to remove already deleted node with session 2
+            // try to remove already removed node with session 2
             try {
                 childNode2.refresh(false);
                 childNode2.remove();
                 otherSession.save();
-                fail("Removing a node already deleted by other session should throw an InvalidItemStateException!");
+                fail("Removing a node already removed by other session should throw an InvalidItemStateException!");
             } catch (InvalidItemStateException e) {
                 //ok, works as expected
             }

Modified: jackrabbit/branches/1.5/jackrabbit-jcr2spi/src/test/java/org/apache/jackrabbit/jcr2spi/ReorderMoveTest.java
URL: http://svn.apache.org/viewvc/jackrabbit/branches/1.5/jackrabbit-jcr2spi/src/test/java/org/apache/jackrabbit/jcr2spi/ReorderMoveTest.java?rev=704898&r1=704897&r2=704898&view=diff
==============================================================================
--- jackrabbit/branches/1.5/jackrabbit-jcr2spi/src/test/java/org/apache/jackrabbit/jcr2spi/ReorderMoveTest.java (original)
+++ jackrabbit/branches/1.5/jackrabbit-jcr2spi/src/test/java/org/apache/jackrabbit/jcr2spi/ReorderMoveTest.java Wed Oct 15 05:59:19 2008
@@ -129,11 +129,19 @@
         // reorder
         srcParent.orderBefore(getRelPath(children[0]), null);
         testOrder(srcParent, new Node[] {children[3], children[0]});
+        assertTrue(srcParent.hasNode(snsName+"[1]"));
+        assertTrue(srcParent.hasNode(snsName+"[2]"));
+        assertFalse(srcParent.hasNode(snsName+"[3]"));
         assertFalse(srcParent.hasNode(snsName+"[4]"));
+        assertFalse(srcParent.hasNode(snsName+"[5]"));
 
         testRootNode.save();
         testOrder(srcParent, new Node[] {children[3], children[0]});
+        assertTrue(srcParent.hasNode(snsName+"[1]"));
+        assertTrue(srcParent.hasNode(snsName+"[2]"));
+        assertFalse(srcParent.hasNode(snsName+"[3]"));
         assertFalse(srcParent.hasNode(snsName+"[4]"));
+        assertFalse(srcParent.hasNode(snsName+"[5]"));
 
         // check if move have been successfull
         assertEquals(children[2].getPath(), destPath);

Modified: jackrabbit/branches/1.5/jackrabbit-jcr2spi/src/test/java/org/apache/jackrabbit/jcr2spi/RevertMoveTest.java
URL: http://svn.apache.org/viewvc/jackrabbit/branches/1.5/jackrabbit-jcr2spi/src/test/java/org/apache/jackrabbit/jcr2spi/RevertMoveTest.java?rev=704898&r1=704897&r2=704898&view=diff
==============================================================================
--- jackrabbit/branches/1.5/jackrabbit-jcr2spi/src/test/java/org/apache/jackrabbit/jcr2spi/RevertMoveTest.java (original)
+++ jackrabbit/branches/1.5/jackrabbit-jcr2spi/src/test/java/org/apache/jackrabbit/jcr2spi/RevertMoveTest.java Wed Oct 15 05:59:19 2008
@@ -67,5 +67,9 @@
         assertFalse("Reverting the move operation must remove the node at destination path.", testRootNode.getSession().itemExists(destinationPath));
         assertTrue("Reverting the move operation must re-add the node at its original position.", testRootNode.getSession().itemExists(srcPath));
         assertTrue("Reverting the move operation must re-add the node at its original position.", srcPath.equals(moveNode.getPath()));
+
+        assertFalse("The former destination must not be modified.", destParentNode.isModified());
+        assertFalse("The parent must not be modified.", srcParentNode.isModified());
+        assertFalse("The move-node must not be modified.", moveNode.isModified());
     }
 }
\ No newline at end of file

Modified: jackrabbit/branches/1.5/jackrabbit-jcr2spi/src/test/java/org/apache/jackrabbit/jcr2spi/TestAll.java
URL: http://svn.apache.org/viewvc/jackrabbit/branches/1.5/jackrabbit-jcr2spi/src/test/java/org/apache/jackrabbit/jcr2spi/TestAll.java?rev=704898&r1=704897&r2=704898&view=diff
==============================================================================
--- jackrabbit/branches/1.5/jackrabbit-jcr2spi/src/test/java/org/apache/jackrabbit/jcr2spi/TestAll.java (original)
+++ jackrabbit/branches/1.5/jackrabbit-jcr2spi/src/test/java/org/apache/jackrabbit/jcr2spi/TestAll.java Wed Oct 15 05:59:19 2008
@@ -34,14 +34,21 @@
         TestSuite suite = new TestSuite("jcr2spi tests");
 
         suite.addTestSuite(AccessByRelativePathTest.class);
+
+        // get node(s)
         suite.addTestSuite(SNSIndexTest.class);
         suite.addTestSuite(NodeOrderTest.class);
 
         // set/add property
+        suite.addTestSuite(GetPropertyTest.class);
+        suite.addTestSuite(AddPropertyTest.class);
         suite.addTestSuite(AddNewPropertyTest.class);
         suite.addTestSuite(SingleValuedPropertyTest.class);
         suite.addTestSuite(MultiValuedPropertyTest.class);
 
+        // change mixin types
+        suite.addTestSuite(MixinModificationTest.class);
+
         // move
         suite.addTestSuite(MoveTest.class);
         suite.addTestSuite(MoveReferenceableTest.class);
@@ -51,6 +58,8 @@
         suite.addTestSuite(MoveMultipleTest.class);
         //suite.addTestSuite(WorkspaceMoveTest.class);  // see JCR-1276
         suite.addTestSuite(RevertMoveTest.class);
+        suite.addTestSuite(MoveToNewTest.class);
+        suite.addTestSuite(MoveCombinedTest.class);
 
         // refresh
         suite.addTestSuite(RefreshFalseTest.class);
@@ -62,6 +71,7 @@
         suite.addTestSuite(RemovePropertyTest.class);
         suite.addTestSuite(RemoveReferenceableNodeTest.class);
         suite.addTestSuite(RemoveSNSTest.class);
+        suite.addTestSuite(RemoveMovedNodeTest.class);
 
         // rename
         suite.addTestSuite(RenameTest.class);
@@ -75,7 +85,7 @@
         suite.addTestSuite(ReorderNewAndSavedTest.class);
         suite.addTestSuite(ReorderMixedTest.class);
         suite.addTestSuite(ReorderMoveTest.class);
-
+        
         // update
         suite.addTestSuite(UpdateTest.class);
 
@@ -83,6 +93,7 @@
         suite.addTestSuite(ReplaceNodeTest.class);
         suite.addTestSuite(HierarchyNodeTest.class);
         suite.addTestSuite(LazyItemIteratorTest.class);
+        suite.addTestSuite(ExternalModificationTest.class);
 
         return suite;
     }

Modified: jackrabbit/branches/1.5/jackrabbit-jcr2spi/src/test/java/org/apache/jackrabbit/jcr2spi/nodetype/AddMixinTest.java
URL: http://svn.apache.org/viewvc/jackrabbit/branches/1.5/jackrabbit-jcr2spi/src/test/java/org/apache/jackrabbit/jcr2spi/nodetype/AddMixinTest.java?rev=704898&r1=704897&r2=704898&view=diff
==============================================================================
--- jackrabbit/branches/1.5/jackrabbit-jcr2spi/src/test/java/org/apache/jackrabbit/jcr2spi/nodetype/AddMixinTest.java (original)
+++ jackrabbit/branches/1.5/jackrabbit-jcr2spi/src/test/java/org/apache/jackrabbit/jcr2spi/nodetype/AddMixinTest.java Wed Oct 15 05:59:19 2008
@@ -118,6 +118,26 @@
         }
 
         assertTrue("Adding 2 mixins at once -> both must be present.", node.isNodeType(mixReferenceable) && node.isNodeType(mixLockable));
+    }
+
+    /**
+     * Implementation specific test adding a new Node with a nodeType, that has
+     * a mixin-supertype. The mixin must only take effect upon save.
+     *
+     * @throws NotExecutableException
+     * @throws RepositoryException
+     */
+    public void testAddMultipleAtOnce2() throws NotExecutableException, RepositoryException {
+        Node node;
+        try {
+            node = testRootNode.addNode(nodeName1, testNodeType);
+            node.addMixin(mixReferenceable);
+            node.addMixin(mixLockable);
+            testRootNode.save();
+        } catch (RepositoryException e) {
+            throw new NotExecutableException();
+        }
+
         List mixins = Arrays.asList(node.getMixinNodeTypes());
         assertTrue("Adding 2 mixins at once -> both must be present.", mixins.contains(ntMgr.getNodeType(mixReferenceable)) && mixins.contains(ntMgr.getNodeType(mixLockable)));
     }

Modified: jackrabbit/branches/1.5/jackrabbit-spi2jcr/pom.xml
URL: http://svn.apache.org/viewvc/jackrabbit/branches/1.5/jackrabbit-spi2jcr/pom.xml?rev=704898&r1=704897&r2=704898&view=diff
==============================================================================
--- jackrabbit/branches/1.5/jackrabbit-spi2jcr/pom.xml (original)
+++ jackrabbit/branches/1.5/jackrabbit-spi2jcr/pom.xml Wed Oct 15 05:59:19 2008
@@ -68,7 +68,6 @@
               <value>
                 org.apache.jackrabbit.jcr2spi.name.NamespaceRegistryTest#testReRegisteredNamespaceVisibility
                 org.apache.jackrabbit.jcr2spi.name.NamespaceRegistryTest#testRegisteredNamespaceVisibility
-                org.apache.jackrabbit.jcr2spi.ReorderMoveTest#testRevertMoveAndReorderSNS
                 org.apache.jackrabbit.jcr2spi.ReorderMoveTest#testRevertMoveReorderedSNS
                 org.apache.jackrabbit.value.BinaryValueTest#testBinaryValueEquals
               </value>

Modified: jackrabbit/branches/1.5/jackrabbit-spi2jcr/src/test/java/org/apache/jackrabbit/spi2jcr/RepositoryStubImpl.java
URL: http://svn.apache.org/viewvc/jackrabbit/branches/1.5/jackrabbit-spi2jcr/src/test/java/org/apache/jackrabbit/spi2jcr/RepositoryStubImpl.java?rev=704898&r1=704897&r2=704898&view=diff
==============================================================================
--- jackrabbit/branches/1.5/jackrabbit-spi2jcr/src/test/java/org/apache/jackrabbit/spi2jcr/RepositoryStubImpl.java (original)
+++ jackrabbit/branches/1.5/jackrabbit-spi2jcr/src/test/java/org/apache/jackrabbit/spi2jcr/RepositoryStubImpl.java Wed Oct 15 05:59:19 2008
@@ -80,8 +80,7 @@
 
         // TODO: make configurable
         BatchReadConfig brconfig = new BatchReadConfig();
-        brconfig.setDepth(NameConstants.NT_FILE, BatchReadConfig.DEPTH_INFINITE);
-        brconfig.setDepth(NameConstants.NT_RESOURCE, BatchReadConfig.DEPTH_INFINITE);
+        brconfig.setDepth(NameConstants.NT_UNSTRUCTURED, BatchReadConfig.DEPTH_INFINITE);
 
         return new RepositoryServiceImpl(jackrabbitRepo, brconfig);
     }