Return-Path: Delivered-To: apmail-jackrabbit-commits-archive@www.apache.org Received: (qmail 41904 invoked from network); 21 Aug 2006 14:10:51 -0000 Received: from hermes.apache.org (HELO mail.apache.org) (209.237.227.199) by minotaur.apache.org with SMTP; 21 Aug 2006 14:10:51 -0000 Received: (qmail 93995 invoked by uid 500); 21 Aug 2006 14:10:50 -0000 Delivered-To: apmail-jackrabbit-commits-archive@jackrabbit.apache.org Received: (qmail 93947 invoked by uid 500); 21 Aug 2006 14:10:50 -0000 Mailing-List: contact commits-help@jackrabbit.apache.org; run by ezmlm Precedence: bulk List-Help: List-Unsubscribe: List-Post: List-Id: Reply-To: dev@jackrabbit.apache.org Delivered-To: mailing list commits@jackrabbit.apache.org Received: (qmail 93875 invoked by uid 99); 21 Aug 2006 14:10:44 -0000 Received: from asf.osuosl.org (HELO asf.osuosl.org) (140.211.166.49) by apache.org (qpsmtpd/0.29) with ESMTP; Mon, 21 Aug 2006 07:10:44 -0700 X-ASF-Spam-Status: No, hits=-9.4 required=10.0 tests=ALL_TRUSTED,NO_REAL_NAME X-Spam-Check-By: apache.org Received-SPF: pass (asf.osuosl.org: local policy) Received: from [140.211.166.113] (HELO eris.apache.org) (140.211.166.113) by apache.org (qpsmtpd/0.29) with ESMTP; Mon, 21 Aug 2006 07:10:42 -0700 Received: by eris.apache.org (Postfix, from userid 65534) id 362651A981A; Mon, 21 Aug 2006 07:10:22 -0700 (PDT) Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit Subject: svn commit: r433248 - in /jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/state: ChildPropertyEntry.java ItemState.java NodeState.java PropertyReference.java PropertyState.java SessionItemStateManager.java Date: Mon, 21 Aug 2006 14:10:20 -0000 To: commits@jackrabbit.apache.org From: mreutegg@apache.org X-Mailer: svnmailer-1.0.8 Message-Id: <20060821141022.362651A981A@eris.apache.org> X-Virus-Checked: Checked by ClamAV on apache.org X-Spam-Rating: minotaur.apache.org 1.6.2 0/1000/N Author: mreutegg Date: Mon Aug 21 07:10:18 2006 New Revision: 433248 URL: http://svn.apache.org/viewvc?rev=433248&view=rev Log: - Introduce ChildPropertyEntry.isAvailable() - Add ItemState.collectTransientStates() which traverses the ItemState hierarchy and collects the transiently modified ItemStates. - Change various methods of NodeState to return only valid Property- and Node-States. - Change SessionItemStateManager.getChangeLog() to use new method ItemState.collectTransientStates() and remove unused methods. - Prepare removal of ZombieHierarchyManager :) Modified: jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/state/ChildPropertyEntry.java jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/state/ItemState.java jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/state/NodeState.java jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/state/PropertyReference.java jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/state/PropertyState.java jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/state/SessionItemStateManager.java Modified: jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/state/ChildPropertyEntry.java URL: http://svn.apache.org/viewvc/jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/state/ChildPropertyEntry.java?rev=433248&r1=433247&r2=433248&view=diff ============================================================================== --- jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/state/ChildPropertyEntry.java (original) +++ jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/state/ChildPropertyEntry.java Mon Aug 21 07:10:18 2006 @@ -42,4 +42,14 @@ * PropertyState. */ public PropertyState getPropertyState() throws NoSuchItemStateException, ItemStateException; + + /** + * Returns true if the referenced PropertyState is + * available. That is, the referenced PropertyState is already + * cached and ready to be returned by {@link #getPropertyState()}. + * + * @return true if the PropertyState is available; + * otherwise false. + */ + public boolean isAvailable(); } Modified: jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/state/ItemState.java URL: http://svn.apache.org/viewvc/jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/state/ItemState.java?rev=433248&r1=433247&r2=433248&view=diff ============================================================================== --- jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/state/ItemState.java (original) +++ jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/state/ItemState.java Mon Aug 21 07:10:18 2006 @@ -485,6 +485,19 @@ public abstract void revert(Set affectedItemStates); /** + * Checks if this ItemState is transiently modified or new and + * adds itself to the Set of transientStates if + * that is the case. It this ItemState has children it will + * call the method {@link #collectTransientStates(java.util.Set)} on those + * ItemStates. + * + * @param transientStates the Set of transient ItemState, + * collected while the ItemState + * hierarchy is traversed. + */ + public abstract void collectTransientStates(Set transientStates); + + /** * Add an ItemStateListener * * @param listener the new listener to be informed on modifications Modified: jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/state/NodeState.java URL: http://svn.apache.org/viewvc/jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/state/NodeState.java?rev=433248&r1=433247&r2=433248&view=diff ============================================================================== --- jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/state/NodeState.java (original) +++ jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/state/NodeState.java Mon Aug 21 07:10:18 2006 @@ -325,7 +325,7 @@ * false otherwise. */ public boolean hasChildNodeEntries() { - return !childNodeEntries.isEmpty(); + return containsValidChildNodeEntry(childNodeEntries); } /** @@ -337,7 +337,7 @@ * the specified name. */ public synchronized boolean hasChildNodeEntry(QName name) { - return !childNodeEntries.get(name).isEmpty(); + return containsValidChildNodeEntry(childNodeEntries.get(name)); } /** @@ -620,6 +620,45 @@ } /** + * @inheritDoc + * @see ItemState#collectTransientStates(Set) + */ + public void collectTransientStates(Set transientStates) { + switch (status) { + case STATUS_EXISTING_MODIFIED: + case STATUS_EXISTING_REMOVED: + case STATUS_NEW: + case STATUS_STALE_DESTROYED: + case STATUS_STALE_MODIFIED: + transientStates.add(this); + } + // call available property states + for (Iterator it = properties.values().iterator(); it.hasNext(); ) { + PropertyReference ref = (PropertyReference) it.next(); + if (ref.isAvailable()) { + try { + ref.getPropertyState().collectTransientStates(transientStates); + } 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 + } + } + } + } + + /** * Determines if there is a property entry with the specified * QName. * @@ -628,7 +667,18 @@ * QName. */ public synchronized boolean hasPropertyName(QName propName) { - return properties.containsKey(propName); + PropertyReference ref = (PropertyReference) properties.get(propName); + if (ref.isResolved()) { + try { + return ref.getPropertyState().isValid(); + } catch (ItemStateException e) { + // probably deleted in the meantime + return false; + } + } else { + // then it must be valid + return true; + } } /** @@ -639,7 +689,17 @@ * @see #addPropertyName */ public synchronized Collection getPropertyNames() { - return Collections.unmodifiableSet(properties.keySet()); + Collection names; + if (status == STATUS_EXISTING_MODIFIED) { + names = new ArrayList(); + for (Iterator it = getPropertyEntries().iterator(); it.hasNext(); ) { + names.add(((ChildPropertyEntry) it.next()).getName()); + } + } else { + // this node state is unmodified, return all + names = properties.keySet(); + } + return Collections.unmodifiableCollection(names); } /** @@ -649,7 +709,30 @@ * @see #addPropertyName */ public synchronized Collection getPropertyEntries() { - return Collections.unmodifiableCollection(properties.values()); + Collection props; + if (status == STATUS_EXISTING_MODIFIED) { + // filter out removed properties + props = new ArrayList(); + for (Iterator it = properties.values().iterator(); it.hasNext(); ) { + ChildPropertyEntry propEntry = (ChildPropertyEntry) it.next(); + if (propEntry.isAvailable()) { + try { + if (propEntry.getPropertyState().isValid()) { + props.add(propEntry); + } + } catch (ItemStateException e) { + // removed in the meantime -> ignore + } + } else { + // never been accessed before, assume valid + props.add(propEntry); + } + } + } else { + // no need to filter out properties, there are no removed properties + props = properties.values(); + } + return Collections.unmodifiableCollection(props); } /** @@ -731,6 +814,26 @@ */ public synchronized PropertyState getPropertyState(QName propertyName) throws NoSuchItemStateException, ItemStateException { + PropertyState propState = getAnyPropertyState(propertyName); + if (propState.isValid()) { + return propState; + } else { + throw new NoSuchItemStateException(idFactory.createPropertyId(getNodeId(), propertyName).toString()); + } + } + + /** + * Returns the property state with the given name and also takes removed + * property states into account. + * + * @param propertyName the name of the property state to return. + * @throws NoSuchItemStateException if there is no property state with the + * given name. + * @throws ItemStateException if an error occurs while retrieving the + * property state. + */ + public synchronized PropertyState getAnyPropertyState(QName propertyName) + throws NoSuchItemStateException, ItemStateException { PropertyReference propRef = (PropertyReference) properties.get(propertyName); if (propRef == null) { throw new NoSuchItemStateException(idFactory.createPropertyId(getNodeId(), propertyName).toString()); @@ -873,6 +976,9 @@ } else { throw new RepositoryException("Unexpected error: Child state to be renamed does not exist."); } + // mark both this and newParent modified + markModified(); + newParent.markModified(); } /** @@ -913,8 +1019,57 @@ * found in this NodeState. */ int getChildNodeIndex(QName name, ChildNodeEntry cne) { + if (!this.getDefinition().allowsSameNameSiblings()) { + return Path.INDEX_DEFAULT; + } List sns = childNodeEntries.get(name); - return sns.indexOf(cne) + 1; + // index is one based + int index = 1; + for (Iterator it = sns.iterator(); it.hasNext(); ) { + ChildNodeEntry e = (ChildNodeEntry) it.next(); + if (e == cne) { + return index; + } + // skip removed entries + try { + if (e.isAvailable() && e.getNodeState().isValid()) { + index++; + } + } catch (ItemStateException ex) { + // probably removed or stale + } + } + // not found + return 0; + } + + //-------------------------------< internal >------------------------------- + + /** + * Returns true if the collection of child node + * entries contains at least one valid ChildNodeEntry. + * + * @param entries the collection to check. + * @return true if one of the entries is valid; otherwise + * false. + */ + private static boolean containsValidChildNodeEntry(Collection entries) { + for (Iterator it = entries.iterator(); it.hasNext(); ) { + ChildNodeEntry cne = (ChildNodeEntry) it.next(); + if (cne.isAvailable()) { + try { + if (cne.getNodeState().isValid()) { + return true; + } + } catch (ItemStateException e) { + // probably removed in the meantime, check next + } + } else { + // then it has never been accessed and must exist + return true; + } + } + return false; } //------------------------------------------------------< inner classes >--- @@ -986,7 +1141,8 @@ /** * Returns a List of ChildNodeEntrys for the - * given nodeName. + * given nodeName. This method does not filter out + * removed ChildNodeEntrys! * * @param nodeName the child node name. * @return same name sibling nodes with the given nodeName. @@ -1037,7 +1193,8 @@ /** * Returns the ChildNodeEntry with the given - * nodeName and index. + * nodeName and index. This method ignores + * ChildNodeEntrys which are marked removed! * * @param nodeName name of the child node entry. * @param index the index of the child node entry. @@ -1056,8 +1213,27 @@ if (obj instanceof List) { // map entry is a list of siblings List siblings = (List) obj; - if (index <= siblings.size()) { - return ((LinkedEntries.LinkNode) siblings.get(index - 1)).getChildNodeEntry(); + // filter out removed states + for (Iterator it = siblings.iterator(); it.hasNext(); ) { + ChildNodeEntry cne = ((LinkedEntries.LinkNode) it.next()).getChildNodeEntry(); + if (cne.isAvailable()) { + try { + if (cne.getNodeState().isValid()) { + index--; + } else { + // child node removed + } + } catch (ItemStateException e) { + // should never happen, cne.isAvailable() returned true + } + } else { + // then this child node entry has never been accessed + // before and is assumed valid + index--; + } + if (index == 0) { + return cne; + } } } else { // map entry is a single child node entry Modified: jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/state/PropertyReference.java URL: http://svn.apache.org/viewvc/jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/state/PropertyReference.java?rev=433248&r1=433247&r2=433248&view=diff ============================================================================== --- jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/state/PropertyReference.java (original) +++ jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/state/PropertyReference.java Mon Aug 21 07:10:18 2006 @@ -89,4 +89,12 @@ public PropertyState getPropertyState() throws NoSuchItemStateException, ItemStateException { return (PropertyState) resolve(); } + + /** + * @inheritDoc + * @see ChildPropertyEntry#isAvailable() + */ + public boolean isAvailable() { + return isResolved(); + } } 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?rev=433248&r1=433247&r2=433248&view=diff ============================================================================== --- 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 Aug 21 07:10:18 2006 @@ -147,6 +147,21 @@ } /** + * @inheritDoc + * @see ItemState#collectTransientStates(Set) + */ + public void collectTransientStates(Set transientStates) { + switch (status) { + case STATUS_EXISTING_MODIFIED: + case STATUS_EXISTING_REMOVED: + case STATUS_NEW: + case STATUS_STALE_DESTROYED: + case STATUS_STALE_MODIFIED: + transientStates.add(this); + } + } + + /** * {@inheritDoc} */ protected synchronized void copy(ItemState state) { Modified: jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/state/SessionItemStateManager.java URL: http://svn.apache.org/viewvc/jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/state/SessionItemStateManager.java?rev=433248&r1=433247&r2=433248&view=diff ============================================================================== --- jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/state/SessionItemStateManager.java (original) +++ jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/state/SessionItemStateManager.java Mon Aug 21 07:10:18 2006 @@ -18,7 +18,6 @@ import org.apache.commons.collections.iterators.IteratorChain; import org.apache.jackrabbit.jcr2spi.HierarchyManager; -import org.apache.jackrabbit.jcr2spi.ZombieHierarchyManager; import org.apache.jackrabbit.jcr2spi.HierarchyManagerImpl; import org.apache.jackrabbit.jcr2spi.util.ReferenceChangeTracker; import org.apache.jackrabbit.jcr2spi.util.LogUtil; @@ -75,7 +74,6 @@ import javax.jcr.nodetype.NoSuchNodeTypeException; import javax.jcr.lock.LockException; import java.util.ArrayList; -import java.util.Collections; import java.util.Iterator; import java.util.List; import java.util.Set; @@ -346,172 +344,8 @@ // make sure all entries are removed refTracker.clear(); } - //-------------------------------------------< Transient state handling >--- - /** - * Returns an iterator over those transient item state instances that are - * direct or indirect descendents of the item state with the given - * parentId. The transient item state instance with the given - * parentId itself (if there is such) will not be included. - *

- * The instances are returned in depth-first tree traversal order. - * - * @param parent the common parent state of the transient item state - * instances to be returned. - * @return an iterator over descendant transient item state instances - */ - private Iterator getDescendantTransientItemStates(NodeState parent) { - if (!transientStateMgr.hasPendingChanges()) { - return Collections.EMPTY_LIST.iterator(); - } - - // build ordered collection of descendant transient states - // sorted by decreasing relative depth - - // use an array of lists to group the descendants by relative depth; - // the depth is used as array index - List[] la = new List[10]; - try { - Iterator iter = transientStateMgr.getModifiedOrAddedItemStates(); - while (iter.hasNext()) { - ItemState state = (ItemState) iter.next(); - // determine relative depth: > 0 means it's a descendant - int depth; - try { - depth = hierMgr.getRelativeDepth(parent, state); - } catch (ItemNotFoundException infe) { - /** - * one of the parents of the specified item has been - * removed externally; as we don't know its path, - * we can't determine if it is a descendant; - * InvalidItemStateException should only be thrown if - * a descendant is affected; - * => throw InvalidItemStateException for now - * todo FIXME - */ - // unable to determine relative depth, assume that the item - // (or any of its ancestors) has been removed externally - String msg = state.getId() - + ": the item seems to have been removed externally."; - log.debug(msg); - throw new InvalidItemStateException(msg); - } - - if (depth < 1) { - // not a descendant - continue; - } - - // ensure capacity - if (depth > la.length) { - List old[] = la; - la = new List[depth + 10]; - System.arraycopy(old, 0, la, 0, old.length); - } - - List list = la[depth - 1]; - if (list == null) { - list = new ArrayList(); - la[depth - 1] = list; - } - list.add(state); - } - } catch (RepositoryException re) { - log.warn("inconsistent hierarchy state", re); - } - // create an iterator over the collected descendants - // in decreasing depth order - IteratorChain resultIter = new IteratorChain(); - for (int i = la.length - 1; i >= 0; i--) { - List list = la[i]; - if (list != null) { - resultIter.addIterator(list.iterator()); - } - } - /** - * if the resulting iterator chain is empty return - * EMPTY_LIST.iterator() instead because older versions - * of IteratorChain (pre Commons Collections 3.1) - * would throw UnsupportedOperationException in this - * situation - */ - if (resultIter.getIterators().isEmpty()) { - return Collections.EMPTY_LIST.iterator(); - } - return resultIter; - } - - /** - * Same as {@link #getDescendantTransientItemStates(NodeState)} - * except that item state instances in the attic are returned. - * - * @param parent the common parent of the transient item state - * instances to be returned. - * @return an iterator over descendant transient item state instances in the attic - */ - private Iterator getDescendantTransientItemStatesInAttic(NodeState parent) { - if (!transientStateMgr.hasDeletedItemStates()) { - return Collections.EMPTY_LIST.iterator(); - } - // build ordered collection of descendant transient states in attic - // sorted by decreasing relative depth - - // use a special attic-aware hierarchy manager - ZombieHierarchyManager zombieHierMgr = - new ZombieHierarchyManager(this, transientStateMgr.getAttic(), nsResolver); - - // use an array of lists to group the descendants by relative depth; - // the depth is used as array index - List[] la = new List[10]; - try { - Iterator iter = transientStateMgr.getDeletedItemStates(); - while (iter.hasNext()) { - ItemState state = (ItemState) iter.next(); - // determine relative depth: > 0 means it's a descendant - int depth = zombieHierMgr.getRelativeDepth(parent, state); - if (depth < 1) { - // not a descendant - continue; - } - - // ensure capacity - if (depth > la.length) { - List old[] = la; - la = new List[depth + 10]; - System.arraycopy(old, 0, la, 0, old.length); - } - - List list = la[depth - 1]; - if (list == null) { - list = new ArrayList(); - la[depth - 1] = list; - } - list.add(state); - } - } catch (RepositoryException re) { - log.warn("inconsistent hierarchy state", re); - } - // create an iterator over the collected descendants - // in decreasing depth order - IteratorChain resultIter = new IteratorChain(); - for (int i = la.length - 1; i >= 0; i--) { - List list = la[i]; - if (list != null) { - resultIter.addIterator(list.iterator()); - } - } - /** - * if the resulting iterator chain is empty return - * EMPTY_LIST.iterator() instead because older versions - * of IteratorChain (pre Commons Collections 3.1) - * would throw UnsupportedOperationException in this - * situation - */ - if (resultIter.getIterators().isEmpty()) { - return Collections.EMPTY_LIST.iterator(); - } - return resultIter; - } + //-------------------------------------------< Transient state handling >--- /** * @@ -521,173 +355,113 @@ * @throws ItemStateException */ private ChangeLog getChangeLog(ItemState itemState) throws StaleItemStateException, ItemStateException { - ChangeLog changeLog = new ChangeLog(); - if (itemState.getParent() == null) { - // root state -> get all item states - for (Iterator it = transientStateMgr.addedStates(); it.hasNext(); ) { - changeLog.added((ItemState) it.next()); - } - for (Iterator it = transientStateMgr.modifiedStates(); it.hasNext(); ) { - changeLog.modified((ItemState) it.next()); - } - for (Iterator it = transientStateMgr.deletedStates(); it.hasNext(); ) { - changeLog.deleted((ItemState) it.next()); - } - for (Iterator it = transientStateMgr.getOperations(); it.hasNext(); ) { - changeLog.addOperation((Operation) it.next()); - } - } else { - // build changelog for affected and decendant states only - collectTransientStates(itemState, changeLog); - collectRemovedStates(itemState, changeLog); - - /** - * build set of item id's which are within the scope of - * (i.e. affected by) this save operation - */ - Iterator it = new IteratorChain(changeLog.modifiedStates(), changeLog.deletedStates()); - Set affectedStates = new HashSet(); - while (it.hasNext()) { - affectedStates.add(it.next()); - } - checkIsSelfContained(affectedStates, changeLog); - collectOperations(affectedStates, changeLog); - } - return changeLog; - } + // build changelog for affected and decendant states only + collectTransientStates(itemState, changeLog); - /** - * DIFF JACKRABBIT: copied and adapted from ItemImpl.getRemovedStates() - *

- * Builds a list of transient descendant item states in the attic - * (i.e. those marked as 'removed') that are within the scope of - * root. - * - * @throws StaleItemStateException - */ - private void collectRemovedStates(ItemState root, ChangeLog changeLog) - throws StaleItemStateException { - ItemState transientState; - if (root.isNode()) { - Iterator iter = getDescendantTransientItemStatesInAttic((NodeState)root); - while (iter.hasNext()) { - transientState = (ItemState) iter.next(); - // check if stale - if (transientState.getStatus() == ItemState.STATUS_STALE_MODIFIED) { - String msg = transientState.getId() - + ": the item cannot be removed because it has been modified externally."; - log.debug(msg); - throw new StaleItemStateException(msg); - } - if (transientState.getStatus() == ItemState.STATUS_STALE_DESTROYED) { - String msg = transientState.getId() - + ": the item cannot be removed because it has already been deleted externally."; - log.debug(msg); - throw new StaleItemStateException(msg); - } - changeLog.deleted(transientState); - } + /** + * build set of item id's which are within the scope of + * (i.e. affected by) this save operation + */ + Iterator it = new IteratorChain(changeLog.modifiedStates(), changeLog.deletedStates()); + Set affectedStates = new HashSet(); + while (it.hasNext()) { + affectedStates.add(it.next()); } + + checkIsSelfContained(affectedStates, changeLog); + collectOperations(affectedStates, changeLog); + + return changeLog; } /** * DIFF JACKRABBIT: copied and adapted from ItemImpl.getTransientStates() *

- * Builds a list of transient (i.e. new or modified) item states that are - * within the scope of state. + * Builds a ChangeLog of transient (i.e. new, modified or + * deleted) item states that are within the scope of state. * - * @throws StaleItemStateException - * @throws ItemStateException + * @throws StaleItemStateException if a stale ItemState is + * encountered while traversing the state + * hierarchy. The changeLog + * might have been populated with some + * transient item states. A client should + * therefore not reuse the changeLog + * if such an exception is thrown. + * @throws ItemStateException if state is a new item state. */ private void collectTransientStates(ItemState state, ChangeLog changeLog) throws StaleItemStateException, ItemStateException { - // list of transient states that should be persisted - ItemState transientState; - // fail-fast test: check status of this item's state if (state.isTransient()) { switch (state.getStatus()) { - case ItemState.STATUS_EXISTING_MODIFIED: - // add this item's state to the list - changeLog.modified(state); - break; - case ItemState.STATUS_NEW: { String msg = LogUtil.safeGetJCRPath(state, nsResolver, hierMgr) + ": cannot save a new item."; log.debug(msg); throw new ItemStateException(msg); } - case ItemState.STATUS_STALE_MODIFIED: { String msg = LogUtil.safeGetJCRPath(state, nsResolver, hierMgr) + ": the item cannot be saved because it has been modified externally."; log.debug(msg); throw new StaleItemStateException(msg); } - case ItemState.STATUS_STALE_DESTROYED: { String msg = LogUtil.safeGetJCRPath(state, nsResolver, hierMgr) + ": the item cannot be saved because it has been deleted externally."; log.debug(msg); throw new StaleItemStateException(msg); } - case ItemState.STATUS_UNDEFINED: { String msg = LogUtil.safeGetJCRPath(state, nsResolver, hierMgr) + ": the item cannot be saved; it seems to have been removed externally."; log.debug(msg); throw new StaleItemStateException(msg); } + } + } + + // Set of transient states that should be persisted + Set transientStates = new HashSet(); + 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 ItemState.STATUS_NEW: + changeLog.added(transientState); + break; + case ItemState.STATUS_EXISTING_MODIFIED: + changeLog.modified(transientState); + break; + case ItemState.STATUS_EXISTING_REMOVED: + changeLog.deleted(transientState); + break; + case ItemState.STATUS_STALE_MODIFIED: + { + String msg = transientState.getId() + ": the item cannot be saved because it has been modified externally."; + log.debug(msg); + throw new StaleItemStateException(msg); + } + case ItemState.STATUS_STALE_DESTROYED: + { + String msg = transientState.getId() + ": the item cannot be saved because it has been deleted externally."; + log.debug(msg); + throw new StaleItemStateException(msg); + } + case ItemState.STATUS_UNDEFINED: + { + String msg = transientState.getId() + ": the item cannot be saved; it seems to have been removed externally."; + log.debug(msg); + throw new StaleItemStateException(msg); + } default: - log.debug("unexpected state status (" + state.getStatus() + ")"); + log.debug("unexpected state status (" + transientState.getStatus() + ")"); // ignore break; - } - } - - if (state.isNode()) { - // build list of 'new' or 'modified' descendants - Iterator iter = getDescendantTransientItemStates((NodeState) state); - while (iter.hasNext()) { - transientState = (ItemState) iter.next(); - // fail-fast test: check status of transient state - switch (transientState.getStatus()) { - case ItemState.STATUS_NEW: - case ItemState.STATUS_EXISTING_MODIFIED: - // add modified state to the list - changeLog.modified(transientState); - break; - - case ItemState.STATUS_STALE_MODIFIED: - { - String msg = transientState.getId() + ": the item cannot be saved because it has been modified externally."; - log.debug(msg); - throw new StaleItemStateException(msg); - } - - case ItemState.STATUS_STALE_DESTROYED: - { - String msg = transientState.getId() + ": the item cannot be saved because it has been deleted externally."; - log.debug(msg); - throw new StaleItemStateException(msg); - } - - case ItemState.STATUS_UNDEFINED: - { - String msg = transientState.getId() + ": the item cannot be saved; it seems to have been removed externally."; - log.debug(msg); - throw new StaleItemStateException(msg); - } - - default: - log.debug("unexpected state status (" + transientState.getStatus() + ")"); - // ignore - break; - } } } }