jackrabbit-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From ang...@apache.org
Subject svn commit: r701388 - in /jackrabbit/trunk/jackrabbit-jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi: hierarchy/ state/
Date Fri, 03 Oct 2008 14:17:25 GMT
Author: angela
Date: Fri Oct  3 07:17:24 2008
New Revision: 701388

URL: http://svn.apache.org/viewvc?rev=701388&view=rev
Log:
JCR-1638: Redundant calls to RepositoryService.getChildInfos

Modified:
    jackrabbit/trunk/jackrabbit-jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/hierarchy/ChildNodeAttic.java
    jackrabbit/trunk/jackrabbit-jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/hierarchy/ChildNodeEntries.java
    jackrabbit/trunk/jackrabbit-jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/hierarchy/ChildNodeEntriesImpl.java
    jackrabbit/trunk/jackrabbit-jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/hierarchy/NodeEntry.java
    jackrabbit/trunk/jackrabbit-jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/hierarchy/NodeEntryImpl.java
    jackrabbit/trunk/jackrabbit-jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/state/ItemState.java
    jackrabbit/trunk/jackrabbit-jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/state/TransientItemStateManager.java
    jackrabbit/trunk/jackrabbit-jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/state/WorkspaceItemStateFactory.java

Modified: jackrabbit/trunk/jackrabbit-jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/hierarchy/ChildNodeAttic.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/hierarchy/ChildNodeAttic.java?rev=701388&r1=701387&r2=701388&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit-jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/hierarchy/ChildNodeAttic.java (original)
+++ jackrabbit/trunk/jackrabbit-jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/hierarchy/ChildNodeAttic.java Fri Oct  3 07:17:24 2008
@@ -38,6 +38,10 @@
     ChildNodeAttic() {
     }
 
+    boolean isEmpty() {
+        return attic.isEmpty();
+    }
+
     boolean contains(Name name, int index) {
         for (Iterator it = attic.iterator(); it.hasNext();) {
             NodeEntryImpl ne = (NodeEntryImpl) it.next();
@@ -48,6 +52,19 @@
         return false;
     }
 
+    boolean contains(Name name, int index, String uniqueId) {
+        for (Iterator it = attic.iterator(); it.hasNext();) {
+            NodeEntryImpl ne = (NodeEntryImpl) it.next();
+            if (uniqueId != null && uniqueId.equals(ne.getUniqueID())) {
+                return true;
+            } else if (ne.matches(name, index)) {
+                return true;
+            }
+        }
+        // not found
+        return false;
+    }
+
     List get(Name name) {
         List l = new ArrayList();
         for (Iterator it = attic.iterator(); it.hasNext();) {

Modified: jackrabbit/trunk/jackrabbit-jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/hierarchy/ChildNodeEntries.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/hierarchy/ChildNodeEntries.java?rev=701388&r1=701387&r2=701388&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit-jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/hierarchy/ChildNodeEntries.java (original)
+++ jackrabbit/trunk/jackrabbit-jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/hierarchy/ChildNodeEntries.java Fri Oct  3 07:17:24 2008
@@ -32,23 +32,11 @@
  */
 public interface ChildNodeEntries {
 
-    static final int STATUS_OK = 0;
-    static final int STATUS_INVALIDATED = 1;
-
-    /**
-     * Returns the status of this ChildNodeEntries object.
-     *
-     * @return {@link #STATUS_OK} or {@link #STATUS_INVALIDATED}
-     */
-    int getStatus();
-
     /**
      * Mark <code>ChildNodeEntries</code> in order to force reloading the
      * entries.
-     *
-     * @param status
      */
-    void setStatus(int status);
+    void invalidate();
 
     /**
      * Reloads this <code>ChildNodeEntries</code> object.
@@ -105,7 +93,7 @@
      * for validity of the entries is made.
      *
      * @param childInfo
-     * @return
+     * @return matching entry or <code>null</code>.
      */
     NodeEntry get(ChildInfo childInfo);
 

Modified: jackrabbit/trunk/jackrabbit-jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/hierarchy/ChildNodeEntriesImpl.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/hierarchy/ChildNodeEntriesImpl.java?rev=701388&r1=701387&r2=701388&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit-jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/hierarchy/ChildNodeEntriesImpl.java (original)
+++ jackrabbit/trunk/jackrabbit-jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/hierarchy/ChildNodeEntriesImpl.java Fri Oct  3 07:17:24 2008
@@ -46,17 +46,21 @@
 
     private static Logger log = LoggerFactory.getLogger(ChildNodeEntriesImpl.class);
 
+    private static final int STATUS_OK = 0;
+    private static final int STATUS_INVALIDATED = 1;
+
     private int status = STATUS_OK;
+    private boolean complete = false;
 
     /**
      * Linked list of {@link NodeEntry} instances.
      */
-    private final LinkedEntries entries;
+    private final LinkedEntries entries = new LinkedEntries();
 
     /**
      * Map used for lookup by name.
      */
-    private final NameMap entriesByName;
+    private final NameMap entriesByName = new NameMap();
 
     private final NodeEntry parent;
     private final EntryFactory factory;
@@ -67,9 +71,6 @@
      * NEW nor in a terminal status.
      */
     ChildNodeEntriesImpl(NodeEntry parent, EntryFactory factory) throws ItemNotFoundException, RepositoryException {
-        entriesByName = new NameMap();
-        entries = new LinkedEntries();
-
         this.parent = parent;
         this.factory = factory;
 
@@ -85,30 +86,40 @@
         } /* else: cannot retrieve child-entries from persistent layer. the parent
            * is NEW (transient only) or already removed from the persistent layer.
            */
-    }
 
-    /**
-     * Create a new <code>ChildNodeEntries</code> collection from the given
-     * <code>childNodeInfos</code> instead of retrieving them from the
-     * persistent layer.
-     *
-     * @param parent
-     * @param factory
-     * @param childNodeInfos
-     */
-    ChildNodeEntriesImpl(NodeEntry parent, EntryFactory factory, Iterator childNodeInfos) {
-        entriesByName = new NameMap();
-        entries = new LinkedEntries();
-
-        this.parent = parent;
-        this.factory = factory;
-
-        while (childNodeInfos.hasNext()) {
-            ChildInfo ci = (ChildInfo) childNodeInfos.next();
-            NodeEntry entry = factory.createNodeEntry(parent, ci.getName(), ci.getUniqueID());
-            add(entry, ci.getIndex());
-        }
-    }
+        /* all child infos have been read from the persistent layer therefore
+           mark this child-node-entries as 'complete' */
+        complete = true;
+    }
+
+     /**
+      * Create a new <code>ChildNodeEntries</code> collection from the given
+      * <code>childNodeInfos</code> instead of retrieving them from the
+      * persistent layer.
+      *
+      * @param parent
+      * @param factory
+      * @param childNodeInfos The complete list of child infos or
+      * <code>null</code> if an 'empty' ChildNodeEntriesImpl should be created.
+      * In the latter case, individual child entries will be added on demand
+      * and the complete list will be retrieved only to answer {@link #iterator()}
+      * if the passed boolean is <code>true</code>.
+      */
+     ChildNodeEntriesImpl(NodeEntry parent, EntryFactory factory, Iterator childNodeInfos) {
+         this.parent = parent;
+         this.factory = factory;
+
+         if (childNodeInfos != null) {
+             while (childNodeInfos.hasNext()) {
+                 ChildInfo ci = (ChildInfo) childNodeInfos.next();
+                 NodeEntry entry = factory.createNodeEntry(parent, ci.getName(), ci.getUniqueID());
+                 add(entry, ci.getIndex());
+             }
+             complete = true;
+         } else {
+             complete = false;
+         }
+     }
 
     /**
      * @see ChildNodeEntries#getStatus()
@@ -118,24 +129,17 @@
     }
 
     /**
-     * Mark <code>ChildNodeEntries</code> in order to force reloading the
-     * entries.
-     *
-     * @see ChildNodeEntries#setStatus(int)
+     * @see ChildNodeEntries#invalidate()
      */
-    public void setStatus(int status) {
-        if (status == STATUS_INVALIDATED || status == STATUS_OK) {
-            this.status = status;
-        } else {
-            throw new IllegalArgumentException();
-        }
+    public void invalidate() {
+        this.status = STATUS_INVALIDATED;
     }
 
     /**
      * @see ChildNodeEntries#reload()
      */
     public synchronized void reload() throws ItemNotFoundException, RepositoryException {
-        if (status == STATUS_OK ||
+        if (status == STATUS_OK && complete ||
             parent.getStatus() == Status.NEW || Status.isTerminal(parent.getStatus())) {
             // nothing to do
             return;
@@ -143,10 +147,10 @@
 
         NodeId id = parent.getWorkspaceId();
         Iterator childNodeInfos = factory.getItemStateFactory().getChildNodeInfos(id);
-        reload(childNodeInfos);
+        update(childNodeInfos);
     }
 
-    void reload(Iterator childNodeInfos) {
+    synchronized void update(Iterator childNodeInfos) {
         // TODO: should existing (not-new) entries that are not present in the childInfos be removed?
         // create list from all ChildInfos (for multiple loop)
         List cInfos = new ArrayList();
@@ -187,7 +191,8 @@
             add((NodeEntry) newEntries.get(i));
         }
         // finally reset the status
-        setStatus(ChildNodeEntries.STATUS_OK);
+        status = STATUS_OK;
+        complete = true;
     }
 
     /**
@@ -200,7 +205,7 @@
         }
         return Collections.unmodifiableList(l).iterator();
     }
-
+    
     /**
      * @see ChildNodeEntries#get(Name)
      */
@@ -262,14 +267,14 @@
      * @param cne the <code>NodeEntry</code> to add.
      * @see ChildNodeEntries#add(NodeEntry)
      */
-     public void add(NodeEntry cne) {
+     public synchronized void add(NodeEntry cne) {
         internalAdd(cne, Path.INDEX_UNDEFINED);
     }
 
     /**
      * @see ChildNodeEntries#add(NodeEntry, int)
      */
-    public void add(NodeEntry cne, int index) {
+    public synchronized void add(NodeEntry cne, int index) {
         if (index < Path.INDEX_UNDEFINED) {
             throw new IllegalArgumentException("Invalid index" + index);
         }
@@ -280,7 +285,7 @@
      *
      * @param entry
      * @param index
-     * @return
+     * @return the <code>LinkNode</code> belonging to the added entry.
      */
     private LinkedEntries.LinkNode internalAdd(NodeEntry entry, int index) {
         Name nodeName = entry.getName();
@@ -317,7 +322,7 @@
     /**
      * @see ChildNodeEntries#add(NodeEntry, NodeEntry)
      */
-    public void add(NodeEntry entry, NodeEntry beforeEntry) {
+    public synchronized void add(NodeEntry entry, NodeEntry beforeEntry) {
         if (beforeEntry != null) {
             // the link node where the new entry is ordered before
             LinkedEntries.LinkNode beforeLN = entries.getLinkNode(beforeEntry);
@@ -364,7 +369,7 @@
      * in this <code>ChildNodeEntries</code>.
      * @see ChildNodeEntries#reorder(NodeEntry, NodeEntry)
      */
-    public NodeEntry reorder(NodeEntry insertEntry, NodeEntry beforeEntry) {
+    public synchronized NodeEntry reorder(NodeEntry insertEntry, NodeEntry beforeEntry) {
         // the link node to move
         LinkedEntries.LinkNode insertLN = entries.getLinkNode(insertEntry);
         if (insertLN == null) {
@@ -624,7 +629,7 @@
          * Return true if more than one NodeEnty with the given name exists.
          *
          * @param qName
-         * @return
+         * @return true if more than one NodeEnty with the given name exists.
          */
         public boolean containsSiblings(Name qName) {
             return snsMap.containsKey(qName);
@@ -662,7 +667,7 @@
          * exists for the given qualified name an empty list is returned.
          *
          * @param qName
-         * @return
+         * @return list of entries or an empty list.
          */
         public List getList(Name qName) {
             Object obj = get(qName);
@@ -762,7 +767,7 @@
          *
          * @param siblings
          * @param index
-         * @return
+         * @return matching entry or <code>null</code>.
          */
         private static NodeEntry findMatchingEntry(List siblings, int index) {
             // shortcut if index can never match

Modified: jackrabbit/trunk/jackrabbit-jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/hierarchy/NodeEntry.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/hierarchy/NodeEntry.java?rev=701388&r1=701387&r2=701388&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit-jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/hierarchy/NodeEntry.java (original)
+++ jackrabbit/trunk/jackrabbit-jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/hierarchy/NodeEntry.java Fri Oct  3 07:17:24 2008
@@ -185,14 +185,16 @@
     public void setNodeEntries(Iterator childInfos) throws RepositoryException;
 
     /**
-     * Adds a new child NodeEntry to this entry.
+     * Adds a child NodeEntry to this entry if it not yet present with this
+     * node entry.
      *
      * @param nodeName
+     * @param index
      * @param uniqueID
-     * @return the new <code>NodeEntry</code>
+     * @return the <code>NodeEntry</code>.
      * @throws RepositoryException If an unexpected error occurs.
      */
-    public NodeEntry addNodeEntry(Name nodeName, String uniqueID, int index) throws RepositoryException;
+    public NodeEntry getOrAddNodeEntry(Name nodeName, int index, String uniqueID) throws RepositoryException;
 
     /**
      * Adds a new, transient child <code>NodeEntry</code>
@@ -245,12 +247,14 @@
      * Returns an unmodifiable Iterator over those children that represent valid
      * PropertyEntries.
      *
-     * @return
+     * @return an unmodifiable Iterator over those children that represent valid
+     * PropertyEntries.
      */
     public Iterator getPropertyEntries();
 
     /**
-     * Add an existing <code>PropertyEntry</code> with the given name.
+     * Add an existing <code>PropertyEntry</code> with the given name if it is
+     * not yet contained in this <code>NodeEntry</code>.
      * Please note the difference to {@link #addNewPropertyEntry(Name, QPropertyDefinition)}
      * which adds a new, transient entry.
      *
@@ -259,7 +263,7 @@
      * @throws ItemExistsException if a child item exists with the given name
      * @throws RepositoryException if an unexpected error occurs.
      */
-    public PropertyEntry addPropertyEntry(Name propName) throws ItemExistsException, RepositoryException;
+    public PropertyEntry getOrAddPropertyEntry(Name propName) throws ItemExistsException, RepositoryException;
 
     /**
      * Adds property entries for the given <code>Name</code>s. It depends on

Modified: jackrabbit/trunk/jackrabbit-jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/hierarchy/NodeEntryImpl.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/hierarchy/NodeEntryImpl.java?rev=701388&r1=701387&r2=701388&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit-jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/hierarchy/NodeEntryImpl.java (original)
+++ jackrabbit/trunk/jackrabbit-jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/hierarchy/NodeEntryImpl.java Fri Oct  3 07:17:24 2008
@@ -26,6 +26,7 @@
 import org.apache.jackrabbit.spi.IdFactory;
 import org.apache.jackrabbit.spi.PropertyId;
 import org.apache.jackrabbit.spi.PathFactory;
+import org.apache.jackrabbit.spi.ChildInfo;
 import org.apache.jackrabbit.jcr2spi.state.NodeState;
 import org.apache.jackrabbit.jcr2spi.state.ItemState;
 import org.apache.jackrabbit.jcr2spi.state.ChangeLog;
@@ -74,13 +75,13 @@
     /**
      * Insertion-ordered collection of NodeEntry objects.
      */
-    private ChildNodeEntries childNodeEntries;
+    private final ChildNodeEntries childNodeEntries;
 
     /**
      * Map used to remember transiently removed or moved childNodeEntries, that
      * must not be retrieved from the persistent storage.
      */
-    private ChildNodeAttic childNodeAttic;
+    private final ChildNodeAttic childNodeAttic;
 
     /**
      * Map of properties.<br>
@@ -115,11 +116,13 @@
      * @param name      the name of the child node.
      * @param factory   the entry factory.
      */
-    private NodeEntryImpl(NodeEntryImpl parent, Name name, String uniqueID, EntryFactory factory) {
+    private NodeEntryImpl(NodeEntryImpl parent, Name name, String uniqueID,
+                          EntryFactory factory) {
         super(parent, name, factory);
         this.uniqueID = uniqueID; // NOTE: don't use setUniqueID (for mod only)
 
         properties = new ChildPropertyEntriesImpl(this, factory);
+        childNodeEntries = new ChildNodeEntriesImpl(this, factory, null);
 
         propertiesInAttic = new HashMap();
         childNodeAttic = new ChildNodeAttic();
@@ -128,20 +131,18 @@
     }
 
     /**
-     *
-     * @return
+     * @return the entry corresponding to the root node.
      */
     static NodeEntry createRootEntry(EntryFactory factory) {
         return new NodeEntryImpl(null, NameConstants.ROOT, null, factory);
     }
 
     /**
-     *
      * @param parent
      * @param name
      * @param uniqueId
      * @param factory
-     * @return
+     * @return the created entry.
      */
     static NodeEntry createNodeEntry(NodeEntryImpl parent, Name name, String uniqueId, EntryFactory factory) {
         return new NodeEntryImpl(parent, name, uniqueId, factory);
@@ -173,7 +174,7 @@
         }
         // invalidate 'childNodeEntries'
         if (getStatus() != Status.NEW && childNodeEntries != null) {
-            childNodeEntries.setStatus(ChildNodeEntries.STATUS_INVALIDATED);
+            childNodeEntries.invalidate();
         }
         // ... and invalidate the resolved state (if available)
         super.invalidate(recursive);
@@ -218,7 +219,6 @@
         }
 
         revertTransientChanges();
-
         // now make sure the attached state is reverted to the original state
         super.revert();
     }
@@ -247,7 +247,7 @@
      */
     public void remove() {
         removeEntry(this);
-        if (getStatus() != Status.STALE_DESTROYED && parent.childNodeEntries != null) {
+        if (getStatus() != Status.STALE_DESTROYED) {
             NodeEntry removed = parent.childNodeEntries.remove(this);
             if (removed == null) {
                 // try attic
@@ -393,7 +393,7 @@
             Name name = elem.getName();
 
             // first try to resolve to known node or property entry
-            NodeEntry cne = (entry.childNodeEntries == null) ? null : entry.getNodeEntry(name, index, false);
+            NodeEntry cne = entry.getNodeEntry(name, index, false);
             if (cne != null) {
                 entry = (NodeEntryImpl) cne;
             } else if (index == Path.INDEX_DEFAULT && i == path.getLength() - 1 && entry.properties.contains(name)) {
@@ -403,11 +403,9 @@
                 // no valid entry
                 // -> check for moved child entry in node-attic
                 // -> check if child points to a removed/moved sns
-                if (entry.childNodeEntries != null) {
-                    List siblings = entry.childNodeEntries.get(name);
-                    if (entry.containsAtticChild(siblings, name, index)) {
-                        throw new PathNotFoundException(path.toString());
-                    }
+                List siblings = entry.childNodeEntries.get(name);
+                if (entry.containsAtticChild(siblings, name, index)) {
+                    throw new PathNotFoundException(path.toString());
                 }
                /*
                 * Unknown entry (not-existing or not yet loaded):
@@ -426,7 +424,7 @@
                 }
                 Path remainingPath = pb.getPath();
 
-                NodeId parentId = entry.getId();
+                NodeId parentId = entry.getWorkspaceId();
                 IdFactory idFactory = factory.getIdFactory();
 
                 NodeId nodeId = idFactory.createNodeId(parentId, remainingPath);
@@ -490,16 +488,11 @@
      * @see NodeEntry#hasNodeEntry(Name)
      */
     public synchronized boolean hasNodeEntry(Name nodeName) {
-        try {
-            List namedEntries = childNodeEntries().get(nodeName);
-            if (namedEntries.isEmpty()) {
-                return false;
-            } else {
-                return EntryValidation.containsValidNodeEntry(namedEntries.iterator());
-            }
-        } catch (RepositoryException e) {
-            log.debug("Unable to determine if a child node with name " + nodeName + " exists.");
+        List namedEntries = childNodeEntries.get(nodeName);
+        if (namedEntries.isEmpty()) {
             return false;
+        } else {
+            return EntryValidation.containsValidNodeEntry(namedEntries.iterator());
         }
     }
 
@@ -529,8 +522,7 @@
      * @see NodeEntry#getNodeEntry(Name, int, boolean)
      */
     public NodeEntry getNodeEntry(Name nodeName, int index, boolean loadIfNotFound) throws RepositoryException {
-        // TODO: avoid loading the child-infos if childNodeEntries == null
-        List entries = childNodeEntries().get(nodeName);
+        List entries = childNodeEntries.get(nodeName);
         NodeEntry cne = null;
         if (entries.size() >= index) {
             // position of entry might differ from index-1 if a SNS with lower
@@ -541,10 +533,11 @@
                     cne = ne;
                 }
             }
-        } else if (loadIfNotFound
+        }
+
+        if (cne == null && loadIfNotFound
                 && !containsAtticChild(entries, nodeName, index)
                 && Status.NEW != getStatus()) {
-
             PathFactory pf = factory.getPathFactory();
             NodeId cId = factory.getIdFactory().createNodeId(getId(), pf.create(nodeName, index));
             cne = loadNodeEntry(cId);
@@ -558,7 +551,7 @@
      */
     public synchronized Iterator getNodeEntries() throws RepositoryException {
         Collection entries = new ArrayList();
-        for (Iterator it = childNodeEntries().iterator(); it.hasNext();) {
+        for (Iterator it = getCompleteChildNodeEntries().iterator(); it.hasNext();) {
             NodeEntry entry = (NodeEntry) it.next();
             if (EntryValidation.isValidNodeEntry(entry)) {
                 entries.add(entry);
@@ -571,7 +564,7 @@
      * @see NodeEntry#getNodeEntries(Name)
      */
     public synchronized List getNodeEntries(Name nodeName) throws RepositoryException {
-        List namedEntries = childNodeEntries().get(nodeName);
+        List namedEntries = getCompleteChildNodeEntries().get(nodeName);
         if (namedEntries.isEmpty()) {
             return Collections.EMPTY_LIST;
         } else {
@@ -595,19 +588,33 @@
      * @throws RepositoryException
      */
     public void setNodeEntries(Iterator childInfos) throws RepositoryException {
-        if (childNodeEntries == null) {
-            childNodeEntries = new ChildNodeEntriesImpl(this, factory, childInfos);
+        if (childNodeAttic.isEmpty()) {
+            ((ChildNodeEntriesImpl) childNodeEntries).update(childInfos);
         } else {
-            ((ChildNodeEntriesImpl) childNodeEntries).reload(childInfos);
+            // filter those entries that have been moved to the attic.
+            List remaining = new ArrayList();
+            while (childInfos.hasNext()) {
+                ChildInfo ci = (ChildInfo) childInfos.next();
+                if (!childNodeAttic.contains(ci.getName(), ci.getIndex(), ci.getUniqueID())) {
+                    remaining.add(ci);
+                }
+            }
+            ((ChildNodeEntriesImpl) childNodeEntries).update(remaining.iterator());
         }
     }
 
     /**
      * @inheritDoc
-     * @see NodeEntry#addNodeEntry(Name, String, int)
+     * @see NodeEntry#getOrAddNodeEntry(Name,int,String)
      */
-    public NodeEntry addNodeEntry(Name nodeName, String uniqueID, int index) throws RepositoryException {
-        return internalAddNodeEntry(nodeName, uniqueID, index, childNodeEntries());
+    public NodeEntry getOrAddNodeEntry(Name nodeName, int index, String uniqueID) throws RepositoryException {
+        NodeEntry ne = lookupNodeEntry(uniqueID, nodeName, index);
+        if (ne == null) {
+            ne = internalAddNodeEntry(nodeName, uniqueID, index);
+        } else {
+            log.debug("Child NodeEntry already exists -> didn't add.");
+        }
+        return ne;
     }
 
     /**
@@ -616,7 +623,7 @@
      */
     public NodeState addNewNodeEntry(Name nodeName, String uniqueID,
                                      Name primaryNodeType, QNodeDefinition definition) throws RepositoryException {
-        NodeEntry entry = internalAddNodeEntry(nodeName, uniqueID, Path.INDEX_UNDEFINED, childNodeEntries());
+        NodeEntry entry = internalAddNodeEntry(nodeName, uniqueID, Path.INDEX_UNDEFINED);
         NodeState state = factory.getItemStateFactory().createNewNodeState(entry, primaryNodeType, definition);
         if (!entry.isAvailable()) {
             entry.setItemState(state);
@@ -625,17 +632,14 @@
     }
 
     /**
-     *
      * @param nodeName
      * @param uniqueID
      * @param index
-     * @param childEntries
-     * @return
+     * @return the added entry.
      */
-    private NodeEntry internalAddNodeEntry(Name nodeName, String uniqueID,
-                                           int index, ChildNodeEntries childEntries) {
+    private NodeEntry internalAddNodeEntry(Name nodeName, String uniqueID, int index) {
         NodeEntry entry = factory.createNodeEntry(this, nodeName, uniqueID);
-        childEntries.add(entry, index);
+        childNodeEntries.add(entry, index);
         return entry;
     }
 
@@ -697,11 +701,16 @@
 
     /**
      * @inheritDoc
-     * @see NodeEntry#addPropertyEntry(Name)
+     * @see NodeEntry#getOrAddPropertyEntry(Name)
      */
-    public PropertyEntry addPropertyEntry(Name propName) throws ItemExistsException {
-        // TODO: check for existing prop.
-        return internalAddPropertyEntry(propName, true);
+    public PropertyEntry getOrAddPropertyEntry(Name propName) throws ItemExistsException {
+        PropertyEntry pe = lookupPropertyEntry(propName);
+        if (pe == null) {
+            pe = internalAddPropertyEntry(propName, true);
+        }  else {
+            log.debug("Child PropertyEntry already exists -> didn't add.");
+        }
+        return pe;
     }
 
     /**
@@ -710,7 +719,7 @@
      *
      * @param propName
      * @param notifySpecial
-     * @return
+     * @return the added entry.
      */
     private PropertyEntry internalAddPropertyEntry(Name propName, boolean notifySpecial) {
         PropertyEntry entry = factory.createPropertyEntry(this, propName);
@@ -826,12 +835,12 @@
     public void orderBefore(NodeEntry beforeEntry) throws RepositoryException {
         if (Status.NEW == getStatus()) {
             // new states get remove upon revert
-            parent.childNodeEntries().reorder(this, beforeEntry);
+            parent.childNodeEntries.reorder(this, beforeEntry);
         } else {
             createSiblingRevertInfos();
             parent.createRevertInfo();
             // now reorder child entries on parent
-            NodeEntry previousBefore = parent.childNodeEntries().reorder(this, beforeEntry);
+            NodeEntry previousBefore = parent.childNodeEntries.reorder(this, beforeEntry);
             parent.revertInfo.reordered(this, previousBefore);
         }
     }
@@ -853,7 +862,7 @@
            parent.childNodeAttic.add(this);
        }
 
-       NodeEntryImpl entry = (NodeEntryImpl) parent.childNodeEntries().remove(this);
+       NodeEntryImpl entry = (NodeEntryImpl) parent.childNodeEntries.remove(this);
        if (entry != this) {
            // should never occur
            String msg = "Internal error. Attempt to move NodeEntry (" + getName() + ") which is not connected to its parent.";
@@ -864,7 +873,7 @@
        parent = (NodeEntryImpl) newParent;
        name = newName;
        // register entry with its new parent
-       parent.childNodeEntries().add(this);
+       parent.childNodeEntries.add(this);
        return this;
    }
 
@@ -883,11 +892,6 @@
         Name eventName = childEvent.getPath().getNameElement().getName();
         switch (childEvent.getType()) {
             case Event.NODE_ADDED:
-                if (childNodeEntries == null) {
-                    // childNodeEntries not yet loaded -> ignore
-                    return;
-                }
-
                 int index = childEvent.getPath().getNameElement().getNormalizedIndex();
                 String uniqueChildID = null;
                 if (childEvent.getItemId().getPath() == null) {
@@ -907,7 +911,7 @@
                     cne = childNodeEntries.get(eventName, index);
                 }
                 if (cne == null) {
-                    internalAddNodeEntry(eventName, uniqueChildID, index, childNodeEntries);
+                    internalAddNodeEntry(eventName, uniqueChildID, index);
                 } else {
                     // child already exists -> deal with NEW entries, that were
                     // added by some other session.
@@ -1024,23 +1028,23 @@
         Name propName = propertyEntry.getName();
         if (propertiesInAttic.containsKey(propName)) {
             properties.add((PropertyEntry) propertiesInAttic.remove(propName));
-        } // else: propEntry has never been moved to the attic (see 'addPropertyEntry')
+        } // else: propEntry has never been moved to the attic (see 'getOrAddPropertyEntry')
     }
 
     /**
-     *
      * @param oldName
      * @param oldIndex
-     * @return
+     * @return <code>true</code> if the given oldName and oldIndex match
+     * {@link #getWorkspaceName()} and {@link #getWorkspaceIndex()}, respectively.
      */
     boolean matches(Name oldName, int oldIndex) {
         return getWorkspaceName().equals(oldName) && getWorkspaceIndex() == oldIndex;
     }
 
     /**
-     *
      * @param oldName
-     * @return
+     * @return <code>true</code> if the given oldName matches
+     * {@link #getWorkspaceName()}.
      */
     boolean matches(Name oldName) {
         return getWorkspaceName().equals(oldName);
@@ -1066,7 +1070,8 @@
     /**
      *
      * @param childId
-     * @return
+     * @return the entry or <code>null</code> if building the corresponding
+     * <code>NodeState</code> failed with <code>ItemNotFoundException</code>.
      */
     private NodeEntry loadNodeEntry(NodeId childId) throws RepositoryException {
         try {
@@ -1080,7 +1085,8 @@
     /**
      *
      * @param childId
-     * @return
+     * @return the entry or <code>null</code> if building the corresponding
+     * <code>PropertyState</code> failed with <code>ItemNotFoundException</code>.
      * @throws ItemNotFoundException
      * @throws RepositoryException
      */
@@ -1101,7 +1107,8 @@
      *
      * @param eventId
      * @param eventPath
-     * @return
+     * @return the entry or <code>null</code> if the matching entry has a status
+     * <code>Status#NEW</code>.
      */
     private HierarchyEntry lookupEntry(ItemId eventId, Path eventPath) {
         Name childName = eventPath.getNameElement().getName();
@@ -1122,7 +1129,7 @@
         NodeEntry child = null;
         if (uniqueChildId != null) {
             child = childNodeAttic.get(uniqueChildId);
-            if (child == null && childNodeEntries != null) {
+            if (child == null) {
                 child = childNodeEntries.get(childName, uniqueChildId);
             }
         }
@@ -1194,13 +1201,9 @@
      * <code>null</code>, since the child node entries are loaded/reloaded
      * in case they have not been loaded yet.
      */
-    private ChildNodeEntries childNodeEntries() throws InvalidItemStateException, RepositoryException {
+    private ChildNodeEntries getCompleteChildNodeEntries() throws InvalidItemStateException, RepositoryException {
         try {
-            if (childNodeEntries == null) {
-                childNodeEntries = new ChildNodeEntriesImpl(this, factory);
-            } else if (childNodeEntries.getStatus() == ChildNodeEntries.STATUS_INVALIDATED) {
-                childNodeEntries.reload();
-            }
+            childNodeEntries.reload();
         } catch (ItemNotFoundException e) {
             log.debug("NodeEntry does not exist (anymore) -> remove.");
             remove();
@@ -1215,7 +1218,8 @@
      * loaded yet, no attempt is made to do so.
      *
      * @param includeAttic
-     * @return
+     * @return iterator over all children entries, that currently are loaded
+     * with this NodeEntry
      */
     private Iterator getAllChildEntries(boolean includeAttic) {
         IteratorChain chain = new IteratorChain();
@@ -1230,7 +1234,7 @@
             chain.addIterator(props.iterator());
         }
         // add childNodeEntries
-        if (childNodeEntries != null) {
+        synchronized (childNodeEntries) {
             chain.addIterator(childNodeEntries.iterator());
         }
         return chain;
@@ -1245,7 +1249,7 @@
      * this <code>NodeEntry</code>.
      */
     private int getChildIndex(NodeEntry cne) throws ItemNotFoundException, RepositoryException {
-        List sns = childNodeEntries().get(cne.getName());
+        List sns = childNodeEntries.get(cne.getName());
         // index is one based
         int index = Path.INDEX_DEFAULT;
         for (Iterator it = sns.iterator(); it.hasNext(); ) {
@@ -1264,14 +1268,17 @@
     }
 
     /**
-     * Returns true if the attic contains a matching child entry or if any of
-     * the remaining child entries present in the siblings list has been modified
-     * in a way that its original index is equal to the given child index.
+     * Returns <code>true</code> if the attic contains a matching child entry or
+     * if any of the remaining child entries present in the siblings list has
+     * been modified in a way that its original index is equal to the given
+     * child index.
      *
      * @param siblings
      * @param childName
      * @param childIndex
-     * @return
+     * @return <code>true</code> if there is a child entry in the attic that
+     * matches the given name/index or if the siblings list contain a reordered
+     * entry that matches.
      */
     private boolean containsAtticChild(List siblings, Name childName, int childIndex) {
         // check if a matching entry exists in the attic
@@ -1309,7 +1316,7 @@
         }
         // for SNSs without UniqueID remember original index in order to
         // be able to build the workspaceID TODO: improve
-        List sns = parent.childNodeEntries().get(name);
+        List sns = parent.getCompleteChildNodeEntries().get(name);
         if (sns.size() > 1) {
             for (Iterator it = sns.iterator(); it.hasNext();) {
                 NodeEntryImpl sibling = (NodeEntryImpl) it.next();
@@ -1331,7 +1338,7 @@
         if (isTransientlyMoved())  {
             // move NodeEntry back to its original parent
             // TODO improve for simple renaming
-            parent.childNodeEntries().remove(this);
+            parent.childNodeEntries.remove(this);
             revertInfo.oldParent.childNodeAttic.remove(this);
 
             // now restore moved entry with the old name and index and re-add
@@ -1340,7 +1347,7 @@
             name = revertInfo.oldName;
             ItemState state = internalGetItemState();
             if (state != null && !Status.isTerminal(state.getStatus())) {
-                parent.childNodeEntries().add(this, revertInfo.oldIndex);
+                parent.childNodeEntries.add(this, revertInfo.oldIndex);
             }
         }
         // revert reordering of child-node-entries
@@ -1373,9 +1380,9 @@
      */
     private class RevertInfo implements ItemStateLifeCycleListener {
 
-        private NodeEntryImpl oldParent;
-        private Name oldName;
-        private int oldIndex;
+        private final NodeEntryImpl oldParent;
+        private final Name oldName;
+        private final int oldIndex;
 
         private Map reorderedChildren;
 

Modified: jackrabbit/trunk/jackrabbit-jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/state/ItemState.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/state/ItemState.java?rev=701388&r1=701387&r2=701388&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit-jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/state/ItemState.java (original)
+++ jackrabbit/trunk/jackrabbit-jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/state/ItemState.java Fri Oct  3 07:17:24 2008
@@ -31,6 +31,7 @@
 
 import javax.jcr.RepositoryException;
 import javax.jcr.ItemNotFoundException;
+import javax.jcr.InvalidItemStateException;
 import java.util.Collection;
 import java.util.Iterator;
 import java.util.Collections;
@@ -369,7 +370,7 @@
     /**
      * Marks this item state as modified.
      */
-    void markModified() {
+    void markModified() throws InvalidItemStateException {
         switch (status) {
             case Status.EXISTING:
                 setStatus(Status.EXISTING_MODIFIED);
@@ -384,12 +385,12 @@
             case Status.STALE_MODIFIED:
                 // should actually not get here because item should check before
                 // it modifies an item state.
-                throw new IllegalStateException("Cannot mark stale state modified.");
+                throw new InvalidItemStateException("Cannot mark stale state modified.");
 
             case Status.EXISTING_REMOVED:
             default:
-                String msg = "Cannot mark item state with status " + status + " modified.";
-                throw new IllegalStateException(msg);
+                String msg = "Cannot mark item state with status '" + Status.getName(status) + "' modified.";
+                throw new InvalidItemStateException(msg);
         }
     }
 }

Modified: jackrabbit/trunk/jackrabbit-jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/state/TransientItemStateManager.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/state/TransientItemStateManager.java?rev=701388&r1=701387&r2=701388&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit-jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/state/TransientItemStateManager.java (original)
+++ jackrabbit/trunk/jackrabbit-jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/state/TransientItemStateManager.java Fri Oct  3 07:17:24 2008
@@ -100,7 +100,12 @@
                                  QNodeDefinition definition, NodeState parent)
             throws RepositoryException {
         NodeState nodeState = ((NodeEntry) parent.getHierarchyEntry()).addNewNodeEntry(nodeName, uniqueID, nodeTypeName, definition);
-        parent.markModified();
+        try {
+            parent.markModified();
+        } catch (RepositoryException e) {
+            nodeState.getHierarchyEntry().remove();
+            throw e;
+        }
 
         return nodeState;
     }
@@ -121,12 +126,17 @@
     PropertyState createNewPropertyState(Name propName, NodeState parent,
                                          QPropertyDefinition definition,
                                          QValue[] values, int propertyType)
-        throws ItemExistsException, ConstraintViolationException, RepositoryException {
+            throws ItemExistsException, ConstraintViolationException, RepositoryException {
         // NOTE: callers must make sure, the property type is not 'undefined'
-        PropertyState propState = ((NodeEntry) parent.getHierarchyEntry()).addNewPropertyEntry(propName, definition);
-        propState.setValues(values, propertyType);
-        parent.markModified();
-
+        NodeEntry nodeEntry = (NodeEntry) parent.getHierarchyEntry();
+        PropertyState propState = nodeEntry.addNewPropertyEntry(propName, definition);
+        try {
+            propState.setValues(values, propertyType);
+            parent.markModified();
+        } catch (RepositoryException e) {
+            propState.getHierarchyEntry().remove();
+            throw e;
+        }
         return propState;
     }
 

Modified: jackrabbit/trunk/jackrabbit-jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/state/WorkspaceItemStateFactory.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/state/WorkspaceItemStateFactory.java?rev=701388&r1=701387&r2=701388&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit-jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/state/WorkspaceItemStateFactory.java (original)
+++ jackrabbit/trunk/jackrabbit-jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/state/WorkspaceItemStateFactory.java Fri Oct  3 07:17:24 2008
@@ -18,8 +18,11 @@
 
 import java.util.ArrayList;
 import java.util.Collections;
+import java.util.HashMap;
 import java.util.Iterator;
 import java.util.List;
+import java.util.Map;
+import java.util.NoSuchElementException;
 
 import javax.jcr.ItemExistsException;
 import javax.jcr.ItemNotFoundException;
@@ -142,7 +145,7 @@
     public PropertyState createDeepPropertyState(PropertyId propertyId, NodeEntry anyParent) throws ItemNotFoundException, RepositoryException {
         try {
             PropertyInfo info = service.getPropertyInfo(sessionInfo, propertyId);
-            return createDeepPropertyState(info, anyParent);
+            return createDeepPropertyState(info, anyParent, null);
         } catch (PathNotFoundException e) {
             throw new ItemNotFoundException(e.getMessage());
         }
@@ -189,16 +192,16 @@
      * @param nodeId
      * @param itemInfos
      * @param entry
+     * @param isDeep
      * @return
      * @throws ItemNotFoundException
      * @throws RepositoryException
      */
-    private synchronized NodeState createItemStates(NodeId nodeId,
-                                                    Iterator itemInfos,
-                                                    NodeEntry entry,
-                                                    boolean isDeep)
+    private synchronized NodeState createItemStates(NodeId nodeId, Iterator itemInfos,
+                                                    NodeEntry entry, boolean isDeep)
             throws ItemNotFoundException, RepositoryException {
         NodeState nodeState;
+        ItemInfos infos = new ItemInfos(itemInfos);
         // first entry in the iterator is the originally requested Node.
         if (itemInfos.hasNext()) {
             NodeInfo first = (NodeInfo) itemInfos.next();
@@ -206,7 +209,7 @@
                 // for a deep state, the hierarchy entry does not correspond to
                 // the given NodeEntry -> retrieve NodeState before executing
                 // validation check.
-                nodeState = createDeepNodeState(first, entry);
+                nodeState = createDeepNodeState(first, entry, infos);
                 assertMatchingPath(first, nodeState.getNodeEntry());
             } else {
                 // 'isDeep' == false -> the given NodeEntry must match to the
@@ -225,9 +228,9 @@
             while (itemInfos.hasNext()) {
                 ItemInfo info = (ItemInfo) itemInfos.next();
                 if (info.denotesNode()) {
-                    createDeepNodeState((NodeInfo) info, parentEntry);
+                    createDeepNodeState((NodeInfo) info, parentEntry, infos);
                 } else {
-                    createDeepPropertyState((PropertyInfo) info, parentEntry);
+                    createDeepPropertyState((PropertyInfo) info, parentEntry, infos);
                 }
             }
         }
@@ -245,7 +248,7 @@
      */
     private NodeState createNodeState(NodeInfo info, NodeEntry entry) throws ItemNotFoundException, RepositoryException {
         // make sure the entry has the correct ItemId
-        // this make not be the case, if the hierachy has not been completely
+        // this make not be the case, if the hierarchy has not been completely
         // resolved yet -> if uniqueID is present, set it on this entry or on
         // the appropriate parent entry
         String uniqueID = info.getId().getUniqueID();
@@ -318,7 +321,7 @@
      * @return
      * @throws RepositoryException
      */
-    private NodeState createDeepNodeState(NodeInfo info, NodeEntry anyParent) throws RepositoryException {
+    private NodeState createDeepNodeState(NodeInfo info, NodeEntry anyParent, ItemInfos infos) throws RepositoryException {
         try {
             // node for nodeId exists -> build missing entries in hierarchy
             // Note, that the path contained in NodeId does not reveal which
@@ -331,7 +334,7 @@
             for (int i = 0; i < missingElems.length; i++) {
                 Name name = missingElems[i].getName();
                 int index = missingElems[i].getNormalizedIndex();
-                entry = createIntermediateNodeEntry(entry, name, index);
+                entry = createIntermediateNodeEntry(entry, name, index, infos);
             }
             if (entry == anyParent) {
                 throw new RepositoryException("Internal error while getting deep itemState");
@@ -349,29 +352,28 @@
      * @return
      * @throws RepositoryException
      */
-    private PropertyState createDeepPropertyState(PropertyInfo info, NodeEntry anyParent) throws RepositoryException {
+    private PropertyState createDeepPropertyState(PropertyInfo info, NodeEntry anyParent, ItemInfos infos) throws RepositoryException {
         try {
             // prop for propertyId exists -> build missing entries in hierarchy
             // Note, that the path contained in PropertyId 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();
             NodeEntry entry = anyParent;
+
             int i = 0;
             // NodeEntries except for the very last 'missingElem'
             while (i < missingElems.length - 1) {
                 Name name = missingElems[i].getName();
                 int index = missingElems[i].getNormalizedIndex();
-                entry = createIntermediateNodeEntry(entry, name, index);
+                entry = createIntermediateNodeEntry(entry, name, index, infos);
                 i++;
             }
             // create PropertyEntry for the last element if not existing yet
             Name propName = missingElems[i].getName();
-            PropertyEntry propEntry = entry.getPropertyEntry(propName);
-            if (propEntry == null) {
-                propEntry = entry.addPropertyEntry(propName);
-            }
+            PropertyEntry propEntry = entry.getOrAddPropertyEntry(propName);
+
             return createPropertyState(info, propEntry);
         } catch (PathNotFoundException e) {
             throw new ItemNotFoundException(e.getMessage());
@@ -386,13 +388,16 @@
      * @return
      * @throws RepositoryException
      */
-    private static NodeEntry createIntermediateNodeEntry(NodeEntry parentEntry, Name name, int index) throws RepositoryException {
-        NodeEntry entry;
-        if (parentEntry.hasNodeEntry(name, index)) {
-            entry = parentEntry.getNodeEntry(name, index);
-        } else {
-            entry = parentEntry.addNodeEntry(name, null, index);
+    private static NodeEntry createIntermediateNodeEntry(NodeEntry parentEntry,
+                                                         Name name, int index,
+                                                         ItemInfos infos) throws RepositoryException {
+        if (infos != null && !parentEntry.hasNodeEntry(name, index)) {
+            Iterator childInfos = infos.getChildInfos(parentEntry.getWorkspaceId());
+            if (childInfos != null) {
+                parentEntry.setNodeEntries(childInfos);
+            }
         }
+        NodeEntry entry = parentEntry.getOrAddNodeEntry(name, index, null);
         return entry;
     }
 
@@ -416,10 +421,9 @@
     }
 
     /**
-     *
      * @param entry
      * @param degree
-     * @return
+     * @return the ancestor entry at the specified degree.
      */
     private static NodeEntry getAncestor(HierarchyEntry entry, int degree) {
         NodeEntry parent = entry.getParent();
@@ -434,4 +438,81 @@
         return parent;
     }
 
+    //--------------------------------------------------------------------------
+    /**
+     * Iterator
+     */
+    private class ItemInfos implements Iterator {
+
+        private final List prefetchQueue = new ArrayList();
+        private final Map nodeInfos = new HashMap();
+        private final Iterator infos;
+
+        private ItemInfos(Iterator infos) {
+            super();
+            this.infos = infos;
+        }
+
+        // ------------------------------------------------------< Iterator >---
+        /**
+         * @see Iterator#hasNext()
+         */
+        public boolean hasNext() {
+            if (!prefetchQueue.isEmpty()) {
+                return true;
+            } else {
+                return prefetch();
+            }
+        }
+
+        /**
+         * @see Iterator#next()
+         */
+        public Object next() {
+            if (prefetchQueue.isEmpty()) {
+                throw new NoSuchElementException();
+            } else {
+                return prefetchQueue.remove(0);
+            }
+        }
+
+        /**
+         * @see Iterator#remove()
+         */
+        public void remove() {
+            throw new UnsupportedOperationException();
+        }
+
+        // -------------------------------------------------------< private >---
+        /**
+         * @param parentId
+         * @return The children <code>NodeInfo</code>s for the parent identified
+         * by the given <code>parentId</code> or <code>null</code> if the parent
+         * has not been read yet or does not provide child infos.
+         */
+        private Iterator getChildInfos(NodeId parentId) {
+            NodeInfo nodeInfo = (NodeInfo) nodeInfos.get(parentId);
+            while (nodeInfo == null && prefetch()) {
+                nodeInfo = (NodeInfo) nodeInfos.get(parentId);
+            }
+            return nodeInfo == null? null : nodeInfo.getChildInfos();
+        }
+
+        /**
+         * @return <code>true</code> if the next info could be retrieved.
+         */
+        private boolean prefetch() {
+            if (infos.hasNext()) {
+                ItemInfo info = (ItemInfo) infos.next();
+                prefetchQueue.add(info);
+                if (info.denotesNode()) {
+                    NodeInfo nodeInfo = (NodeInfo) info;
+                    nodeInfos.put(nodeInfo.getId(), nodeInfo);
+                }
+                return true;
+            } else {
+                return false;
+            }
+        }
+    }
 }



Mime
View raw message