jackrabbit-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From mreut...@apache.org
Subject svn commit: r763188 - in /jackrabbit/trunk: jackrabbit-core/src/main/java/org/apache/jackrabbit/core/ jackrabbit-core/src/main/java/org/apache/jackrabbit/core/observation/ jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/lucene/ jackrabbi...
Date Wed, 08 Apr 2009 11:19:32 GMT
Author: mreutegg
Date: Wed Apr  8 11:19:31 2009
New Revision: 763188

URL: http://svn.apache.org/viewvc?rev=763188&view=rev
Log:
JSR 283: Shareable nodes support in query

Added:
    jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/observation/ShareableNodesTest.java   (with props)
    jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/query/ShareableNodeTest.java   (with props)
Modified:
    jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/SearchManager.java
    jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/observation/ChangeLogBasedHierarchyMgr.java
    jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/observation/EventImpl.java
    jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/observation/EventState.java
    jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/observation/EventStateCollection.java
    jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/lucene/CachingIndexReader.java
    jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/lucene/CachingMultiIndexReader.java
    jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/lucene/ChildAxisQuery.java
    jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/lucene/DescendantSelfAxisQuery.java
    jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/lucene/DocId.java
    jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/lucene/FieldNames.java
    jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/lucene/ForeignSegmentDocId.java
    jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/lucene/HierarchyResolver.java
    jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/lucene/JackrabbitIndexReader.java
    jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/lucene/NodeIndexer.java
    jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/lucene/ParentAxisQuery.java
    jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/lucene/SearchIndex.java
    jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/state/NodeState.java
    jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/observation/TestAll.java
    jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/query/TestAll.java
    jackrabbit/trunk/jackrabbit-jcr-tests/src/main/java/org/apache/jackrabbit/test/AbstractJCRTest.java

Modified: jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/SearchManager.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/SearchManager.java?rev=763188&r1=763187&r2=763188&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/SearchManager.java (original)
+++ jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/SearchManager.java Wed Apr  8 11:19:31 2009
@@ -387,8 +387,18 @@
                     if (e.isExternal()) {
                         removedNodes.add(e.getChildId());
                     }
+                    if (e.isShareableChildNode()) {
+                        // simply re-index shareable nodes
+                        removedNodes.add(e.getChildId());
+                    }
                 } else if (type == Event.NODE_REMOVED) {
                     removedNodes.add(e.getChildId());
+                    if (e.isShareableChildNode()) {
+                        // check if there is a node remaining in the shared set
+                        if (itemMgr.hasItemState(e.getChildId())) {
+                            addedNodes.put(e.getChildId(), e);
+                        }
+                    }
                 } else {
                     propEvents.add(e);
                 }

Modified: jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/observation/ChangeLogBasedHierarchyMgr.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/observation/ChangeLogBasedHierarchyMgr.java?rev=763188&r1=763187&r2=763188&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/observation/ChangeLogBasedHierarchyMgr.java (original)
+++ jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/observation/ChangeLogBasedHierarchyMgr.java Wed Apr  8 11:19:31 2009
@@ -29,6 +29,7 @@
 import org.apache.jackrabbit.core.state.NodeReferences;
 import org.apache.jackrabbit.core.state.NodeReferencesId;
 import org.apache.jackrabbit.spi.Path;
+import org.apache.jackrabbit.spi.Name;
 
 import javax.jcr.ItemNotFoundException;
 import javax.jcr.RepositoryException;
@@ -68,10 +69,10 @@
      * Same as {@link #getPath(ItemId)}} except that the <i>old</i> path is
      * returned in case of a moved/removed item.
      *
-     * @param id
-     * @return
-     * @throws ItemNotFoundException
-     * @throws RepositoryException
+     * @param id the id of the node for which to retrieve the path.
+     * @return the path of the item.
+     * @throws ItemNotFoundException if an item state cannot be found.
+     * @throws RepositoryException if another error occurs.
      */
     public Path getZombiePath(ItemId id)
             throws ItemNotFoundException, RepositoryException {
@@ -79,6 +80,21 @@
     }
 
     /**
+     * Same as {@link #getName(NodeId, NodeId)} except that the <i>old</i> path
+     * is returned in case of moved/removed item.
+     *
+     * @param id the id of the node for which to retrieve the name.
+     * @param parentId the id of the parent node.
+     * @return the name of the node.
+     * @throws ItemNotFoundException if an item state cannot be found.
+     * @throws RepositoryException if another error occurs.
+     */
+    public Name getZombieName(NodeId id, NodeId parentId)
+            throws ItemNotFoundException, RepositoryException {
+        return zombieHierMgr.getName(id, parentId);
+    }
+
+    /**
      * Implements an ItemStateManager that is overlayed by a ChangeLog.
      */
     private static class ChangeLogItemStateManager implements ItemStateManager {

Modified: jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/observation/EventImpl.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/observation/EventImpl.java?rev=763188&r1=763187&r2=763188&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/observation/EventImpl.java (original)
+++ jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/observation/EventImpl.java Wed Apr  8 11:19:31 2009
@@ -165,6 +165,17 @@
     }
 
     /**
+     * Returns a flag indicating whether the child node of this event is a
+     * shareable node. Only applies to node added/removed events.
+     *
+     * @return <code>true</code> for a shareable child node, <code>false</code>
+     *         otherwise.
+     */
+    public boolean isShareableChildNode() {
+        return eventState.isShareableNode();
+    }
+
+    /**
      * Return a flag indicating whether this is an externally generated event.
      *
      * @return <code>true</code> if this is an external event;

Modified: jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/observation/EventState.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/observation/EventState.java?rev=763188&r1=763187&r2=763188&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/observation/EventState.java (original)
+++ jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/observation/EventState.java Wed Apr  8 11:19:31 2009
@@ -113,6 +113,12 @@
     private final boolean external;
 
     /**
+     * If set to <code>true</code>, indicates that the child node of a node
+     * added or removed event is a shareable node.
+     */
+    private boolean shareableNode = false;
+
+    /**
      * Creates a new <code>EventState</code> instance.
      *
      * @param type       the type of this event.
@@ -563,6 +569,27 @@
     }
 
     /**
+     * Returns a flag indicating whether the child node of this event is a
+     * shareable node. Only applies to node added/removed events.
+     *
+     * @return <code>true</code> for a shareable child node, <code>false</code>
+     *         otherwise.
+     */
+    boolean isShareableNode() {
+        return shareableNode;
+    }
+
+    /**
+     * Sets a new value for the {@link #shareableNode} flag.
+     *
+     * @param shareableNode whether the child node is shareable.
+     * @see #isShareableNode()
+     */
+    void setShareableNode(boolean shareableNode) {
+        this.shareableNode = shareableNode;
+    }
+
+    /**
      * Returns a String representation of this <code>EventState</code>.
      *
      * @return a String representation of this <code>EventState</code>.

Modified: jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/observation/EventStateCollection.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/observation/EventStateCollection.java?rev=763188&r1=763187&r2=763188&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/observation/EventStateCollection.java (original)
+++ jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/observation/EventStateCollection.java Wed Apr  8 11:19:31 2009
@@ -173,6 +173,8 @@
                 // 4) child node removed
                 // 5) node moved
                 // 6) node reordered
+                // 7) shareable node added
+                // 8) shareable node removed
                 // cases 1) and 2) are detected with added and deleted states
                 // on the PropertyState itself.
                 // cases 3) and 4) are detected with added and deleted states
@@ -183,6 +185,10 @@
                 // the node that actually got moved.
                 // in case 6) only one node state changes. the state of the
                 // parent node.
+                // in case 7) parent of added shareable node has new child node
+                // entry.
+                // in case 8) parent of removed shareable node has removed child
+                // node entry.
                 NodeState n = (NodeState) state;
 
                 if (n.hasOverlayedState()) {
@@ -326,6 +332,9 @@
                                 session));
                     }
                 }
+
+                // create events if n is shareable
+                createShareableNodeEvents(n, changes, hmgr, stateMgr);
             } else {
                 // property changed
                 Path path = getPath(state.getId(), hmgr);
@@ -359,6 +368,9 @@
                         nodeType.getQName(),
                         mixins,
                         session));
+
+                // create events if n is shareable
+                createShareableNodeEvents(n, changes, hmgr, stateMgr);
             } else {
                 // property removed
                 // only create an event if node still exists
@@ -400,6 +412,9 @@
                         nodeType.getQName(),
                         mixins,
                         session));
+
+                // create events if n is shareable
+                createShareableNodeEvents(n, changes, hmgr, stateMgr);
             } else {
                 // property created / set
                 NodeState n = (NodeState) changes.get(state.getParentId());
@@ -518,6 +533,67 @@
 
     //----------------------------< internal >----------------------------------
 
+    private void createShareableNodeEvents(NodeState n,
+                                           ChangeLog changes,
+                                           ChangeLogBasedHierarchyMgr hmgr,
+                                           ItemStateManager stateMgr)
+            throws ItemStateException {
+        if (n.isShareable()) {
+            // check if a share was added or removed
+            for (Iterator added = n.getAddedShares().iterator(); added.hasNext(); ) {
+                NodeId parentId = (NodeId) added.next();
+                // ignore primary parent id
+                if (n.getParentId().equals(parentId)) {
+                    continue;
+                }
+                NodeState parent = (NodeState) changes.get(parentId);
+                if (parent == null) {
+                    // happens when mix:shareable is added to an existing node
+                    // usually the parent node state is in the change log
+                    // when a node is added to a shared set -> new child node
+                    // entry on parent node state.
+                    parent = (NodeState) stateMgr.getItemState(parentId);
+                }
+                Name ntName = getNodeType(parent, session).getQName();
+                EventState es = EventState.childNodeAdded(parentId,
+                        getPath(parentId, hmgr),
+                        n.getNodeId(),
+                        getNameElement(n.getNodeId(), parentId, hmgr),
+                        ntName,
+                        parent.getMixinTypeNames(),
+                        session);
+                es.setShareableNode(true);
+                events.add(es);
+            }
+            for (Iterator removed = n.getRemovedShares().iterator(); removed.hasNext(); ) {
+                NodeId parentId = (NodeId) removed.next();
+                // if this shareable node is removed, only create events for
+                // parent ids that are not primary
+                if (n.getParentId().equals(parentId)) {
+                    continue;
+                }
+                NodeState parent = (NodeState) changes.get(parentId);
+                if (parent == null) {
+                    // happens when mix:shareable is removed from an existing
+                    // node. Usually the parent node state is in the change log
+                    // when a node is removed to a shared set -> removed child
+                    // node entry on parent node state.
+                    parent = (NodeState) stateMgr.getItemState(parentId);
+                }
+                Name ntName = getNodeType(parent, session).getQName();
+                EventState es = EventState.childNodeRemoved(parentId,
+                        getZombiePath(parentId, hmgr),
+                        n.getNodeId(),
+                        getZombieNameElement(n.getNodeId(), parentId, hmgr),
+                        ntName,
+                        parent.getMixinTypeNames(),
+                        session);
+                es.setShareableNode(true);
+                events.add(es);
+            }
+        }
+    }
+
     /**
      * Resolves the node type name in <code>node</code> into a {@link javax.jcr.nodetype.NodeType}
      * object using the {@link javax.jcr.nodetype.NodeTypeManager} of <code>session</code>.
@@ -579,6 +655,61 @@
     }
 
     /**
+     * Returns the name element for the node with the given <code>nodeId</code>
+     * and its parent with <code>parentId</code>. This method is only useful
+     * if <code>nodeId</code> denotes a shareable node.
+     *
+     * @param nodeId the node id of a shareable node.
+     * @param parentId the id of the parent node.
+     * @param hmgr the hierarchy manager.
+     * @return the name element for the node.
+     * @throws ItemStateException if an error occurs while resolving the name.
+     */
+    private Path.Element getNameElement(NodeId nodeId,
+                                        NodeId parentId,
+                                        HierarchyManager hmgr)
+            throws ItemStateException {
+        try {
+            Name name = hmgr.getName(nodeId, parentId);
+            PathBuilder builder = new PathBuilder();
+            builder.addFirst(name);
+            return builder.getPath().getNameElement();
+        } catch (RepositoryException e) {
+            String msg = "Unable to get name for node with id: " + nodeId;
+            throw new ItemStateException(msg, e);
+        }
+    }
+
+    /**
+     * Returns the <i>zombie</i> (i.e. the old) name element for the node with
+     * the given <code>nodeId</code> and its parent with <code>parentId</code>.
+     * This method is only useful if <code>nodeId</code> denotes a shareable
+     * node.
+     *
+     * @param nodeId   the node id of a shareable node.
+     * @param parentId the id of the parent node.
+     * @param hmgr     the hierarchy manager.
+     * @return the name element for the node.
+     * @throws ItemStateException if an error occurs while resolving the name.
+     */
+    private Path.Element getZombieNameElement(NodeId nodeId,
+                                              NodeId parentId,
+                                              ChangeLogBasedHierarchyMgr hmgr)
+            throws ItemStateException {
+        try {
+            Name name = hmgr.getZombieName(nodeId, parentId);
+            PathBuilder builder = new PathBuilder();
+            builder.addFirst(name);
+            return builder.getPath().getNameElement();
+        } catch (RepositoryException e) {
+            // should never happen actually
+            String msg = "Unable to resolve zombie name for item: " + nodeId;
+            log.error(msg);
+            throw new ItemStateException(msg, e);
+        }
+    }
+
+    /**
      * Resolves the <i>zombie</i> (i.e. the old) path of the Item with id
      * <code>itemId</code>.
      *

Modified: jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/lucene/CachingIndexReader.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/lucene/CachingIndexReader.java?rev=763188&r1=763187&r2=763188&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/lucene/CachingIndexReader.java (original)
+++ jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/lucene/CachingIndexReader.java Wed Apr  8 11:19:31 2009
@@ -56,6 +56,12 @@
     private static long currentTick;
 
     /**
+     * BitSet where bits that correspond to document numbers are set for
+     * shareable nodes.
+     */
+    private final BitSet shareableNodes;
+
+    /**
      * Cache of nodes parent relation. If an entry in the array is not null,
      * that means the node with the document number = array-index has the node
      * with <code>DocId</code> as parent.
@@ -105,6 +111,16 @@
         super(delegatee);
         this.cache = cache;
         this.parents = new DocId[delegatee.maxDoc()];
+        this.shareableNodes = new BitSet();
+        TermDocs tDocs = delegatee.termDocs(
+                new Term(FieldNames.SHAREABLE_NODE, ""));
+        try {
+            while (tDocs.next()) {
+                shareableNodes.set(tDocs.doc());
+            }
+        } finally {
+            tDocs.close();
+        }
         this.cacheInitializer = new CacheInitializer(delegatee);
         if (initCache) {
             cacheInitializer.run();
@@ -144,31 +160,33 @@
 
         if (parent == null) {
             Document doc = document(n, FieldSelectors.UUID_AND_PARENT);
-            String parentUUID = doc.get(FieldNames.PARENT);
-            if (parentUUID == null || parentUUID.length() == 0) {
+            String[] parentUUIDs = doc.getValues(FieldNames.PARENT);
+            if (parentUUIDs.length == 0 || parentUUIDs[0].length() == 0) {
+                // root node
                 parent = DocId.NULL;
             } else {
-                // only create a DocId from document number if there is no
-                // existing DocId
-                if (!existing) {
-                    Term id = new Term(FieldNames.UUID, parentUUID);
-                    TermDocs docs = termDocs(id);
-                    try {
-                        while (docs.next()) {
-                            if (!deleted.get(docs.doc())) {
-                                parent = DocId.create(docs.doc());
-                                break;
+                if (shareableNodes.get(n)) {
+                    parent = DocId.create(parentUUIDs);
+                } else {
+                    if (!existing) {
+                        Term id = new Term(FieldNames.UUID, parentUUIDs[0]);
+                        TermDocs docs = termDocs(id);
+                        try {
+                            while (docs.next()) {
+                                if (!deleted.get(docs.doc())) {
+                                    parent = DocId.create(docs.doc());
+                                    break;
+                                }
                             }
+                        } finally {
+                            docs.close();
                         }
-                    } finally {
-                        docs.close();
                     }
-                }
-
-                // if still null, then parent is not in this index, or existing
-                // DocId was invalid. thus, only allowed to create DocId from uuid
-                if (parent == null) {
-                    parent = DocId.create(parentUUID);
+                    // if still null, then parent is not in this index, or existing
+                    // DocId was invalid. thus, only allowed to create DocId from uuid
+                    if (parent == null) {
+                        parent = DocId.create(parentUUIDs[0]);
+                    }
                 }
             }
 
@@ -379,8 +397,12 @@
                 public void collect(Term term, TermDocs tDocs) throws IOException {
                     UUID uuid = UUID.fromString(term.text());
                     while (tDocs.next()) {
-                        NodeInfo info = new NodeInfo(tDocs.doc(), uuid);
-                        docs.put(new Integer(info.docId), info);
+                        int doc = tDocs.doc();
+                        // skip shareable nodes
+                        if (!shareableNodes.get(doc)) {
+                            NodeInfo info = new NodeInfo(doc, uuid);
+                            docs.put(new Integer(doc), info);
+                        }
                     }
                 }
             });
@@ -392,9 +414,13 @@
                     while (tDocs.next()) {
                         Integer docId = new Integer(tDocs.doc());
                         NodeInfo info = (NodeInfo) docs.get(docId);
-                        info.parent = uuid;
-                        docs.remove(docId);
-                        docs.put(info.uuid, info);
+                        if (info == null) {
+                            // shareable node, see above
+                        } else {
+                            info.parent = uuid;
+                            docs.remove(docId);
+                            docs.put(info.uuid, info);
+                        }
                     }
                 }
             });
@@ -413,6 +439,9 @@
                 } else if (info.parent != null) {
                     foreignParents++;
                     parents[info.docId] = DocId.create(info.parent);
+                } else if (shareableNodes.get(info.docId)) {
+                    Document doc = reader.document(info.docId, FieldSelectors.UUID_AND_PARENT);
+                    parents[info.docId] = DocId.create(doc.getValues(FieldNames.PARENT));
                 } else {
                     // no parent -> root node
                     parents[info.docId] = DocId.NULL;
@@ -491,7 +520,7 @@
         void collect(Term term, TermDocs tDocs) throws IOException;
     }
 
-    private static class NodeInfo {
+    private final static class NodeInfo {
 
         final int docId;
 

Modified: jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/lucene/CachingMultiIndexReader.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/lucene/CachingMultiIndexReader.java?rev=763188&r1=763187&r2=763188&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/lucene/CachingMultiIndexReader.java (original)
+++ jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/lucene/CachingMultiIndexReader.java Wed Apr  8 11:19:31 2009
@@ -83,17 +83,11 @@
     }
 
     /**
-     * Returns the document number of the parent of <code>n</code> or
-     * <code>-1</code> if <code>n</code> does not have a parent (<code>n</code>
-     * is the root node).
-     *
-     * @param n the document number.
-     * @return the document number of <code>n</code>'s parent.
-     * @throws IOException if an error occurs while reading from the index.
+     * {@inheritDoc}
      */
-    public int getParent(int n) throws IOException {
+    public int[] getParents(int n, int[] docNumbers) throws IOException {
         DocId id = getParentDocId(n);
-        return id.getDocumentNumber(this);
+        return id.getDocumentNumbers(this, docNumbers);
     }
 
     /**

Modified: jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/lucene/ChildAxisQuery.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/lucene/ChildAxisQuery.java?rev=763188&r1=763187&r2=763188&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/lucene/ChildAxisQuery.java (original)
+++ jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/lucene/ChildAxisQuery.java Wed Apr  8 11:19:31 2009
@@ -701,9 +701,20 @@
             long time = System.currentTimeMillis();
             Hits childrenHits = new AdaptingHits();
             Hits nameHits = new ScorerHits(nameTestScorer);
+            int[] docs = new int[1];
             for (int h = nameHits.next(); h > -1; h = nameHits.next()) {
-                if (docIds.contains(new Integer(hResolver.getParent(h)))) {
-                    childrenHits.set(h);
+                docs = hResolver.getParents(h, docs);
+                if (docs.length == 1) {
+                    // optimize single value
+                    if (docIds.contains(new Integer(docs[0]))) {
+                        childrenHits.set(h);
+                    }
+                } else {
+                    for (int i = 0; i < docs.length; i++) {
+                        if (docIds.contains(new Integer(docs[i]))) {
+                            childrenHits.set(h);
+                        }
+                    }
                 }
             }
             time = System.currentTimeMillis() - time;

Modified: jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/lucene/DescendantSelfAxisQuery.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/lucene/DescendantSelfAxisQuery.java?rev=763188&r1=763187&r2=763188&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/lucene/DescendantSelfAxisQuery.java (original)
+++ jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/lucene/DescendantSelfAxisQuery.java Wed Apr  8 11:19:31 2009
@@ -26,6 +26,7 @@
 import org.apache.lucene.search.Weight;
 import org.apache.lucene.search.Sort;
 import org.apache.jackrabbit.core.SessionImpl;
+import org.apache.jackrabbit.core.ItemManager;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -269,6 +270,8 @@
 
                 private NodeTraversingQueryHits currentTraversal;
 
+                private ItemManager itemMgr = session.getItemManager();
+
                 {
                     fetchNextTraversal();
                 }
@@ -419,6 +422,16 @@
         private int[] ancestorDocs = new int[2];
 
         /**
+         * Reusable array that holds document numbers of parents.
+         */
+        private int[] pDocs = new int[1];
+
+        /**
+         * Reusable array that holds a single document number.
+         */
+        private final int[] singleDoc = new int[1];
+
+        /**
          * Creates a new <code>DescendantSelfAxisScorer</code>.
          *
          * @param similarity the <code>Similarity</code> instance to use.
@@ -476,12 +489,7 @@
             boolean match = subScorer.skipTo(target);
             if (match) {
                 collectContextHits();
-                if (isValid(subScorer.doc())) {
-                    return true;
-                } else {
-                    // find next valid
-                    return next();
-                }
+                return isValid(subScorer.doc()) || next();
             } else {
                 return false;
             }
@@ -532,25 +540,46 @@
             }
 
             // check if doc is a descendant of one of the context nodes
-            int parentDoc = hResolver.getParent(doc);
+            pDocs = hResolver.getParents(doc, pDocs);
+
+            if (pDocs.length == 0) {
+                return false;
+            }
 
             int ancestorCount = 0;
-            ancestorDocs[ancestorCount++] = parentDoc;
+            // can only remember one parent doc per level
+            ancestorDocs[ancestorCount++] = pDocs[0];
 
             // traverse
-            while (parentDoc != -1 && (!contextHits.get(parentDoc) || ancestorCount < minLevels)) {
-                parentDoc = hResolver.getParent(parentDoc);
-                // resize array if needed
-                if (ancestorCount == ancestorDocs.length) {
-                    // double the size of the new array
-                    int[] copy = new int[ancestorDocs.length * 2];
-                    System.arraycopy(ancestorDocs, 0, copy, 0, ancestorDocs.length);
-                    ancestorDocs = copy;
+            while (pDocs.length != 0) {
+                boolean valid = false;
+                for (int i = 0; i < pDocs.length; i++) {
+                    if (ancestorCount >= minLevels && contextHits.get(pDocs[i])) {
+                        valid = true;
+                        break;
+                    }
+                }
+                if (valid) {
+                    break;
+                } else {
+                    // load next level
+                    pDocs = getParents(pDocs, singleDoc);
+                    // resize array if needed
+                    if (ancestorCount == ancestorDocs.length) {
+                        // double the size of the new array
+                        int[] copy = new int[ancestorDocs.length * 2];
+                        System.arraycopy(ancestorDocs, 0, copy, 0, ancestorDocs.length);
+                        ancestorDocs = copy;
+                    }
+                    if (pDocs.length != 0) {
+                        // can only remember one parent doc per level
+                        ancestorDocs[ancestorCount++] = pDocs[0];
+                    }
                 }
-                ancestorDocs[ancestorCount++] = parentDoc;
             }
-            if (parentDoc != -1) {
-                // since current parentDoc is a descendant of one of the context
+
+            if (pDocs.length > 0) {
+                // since current parentDocs are descendants of one of the context
                 // docs we can promote all ancestorDocs to the context hits
                 for (int i = 0; i < ancestorCount; i++) {
                     contextHits.set(ancestorDocs[i]);
@@ -559,5 +588,31 @@
             }
             return false;
         }
+
+        /**
+         * Returns the parent document numbers for the given <code>docs</code>.
+         *
+         * @param docs  the current document numbers, for which to get the
+         *              parents.
+         * @param pDocs an array of document numbers for reuse as return value.
+         * @return the parent document number for the given <code>docs</code>.
+         * @throws IOException if an error occurs while reading from the index.
+         */
+        private int[] getParents(int[] docs, int[] pDocs) throws IOException {
+            // optimize single doc
+            if (docs.length == 1) {
+                return hResolver.getParents(docs[0], pDocs);
+            } else {
+                pDocs = new int[0];
+                for (int i = 0; i < docs.length; i++) {
+                    int[] p = hResolver.getParents(docs[i], new int[0]);
+                    int[] tmp = new int[p.length + pDocs.length];
+                    System.arraycopy(pDocs, 0, tmp, 0, pDocs.length);
+                    System.arraycopy(p, 0, tmp, pDocs.length, p.length);
+                    pDocs = tmp;
+                }
+                return pDocs;
+            }
+        }
     }
 }

Modified: jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/lucene/DocId.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/lucene/DocId.java?rev=763188&r1=763187&r2=763188&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/lucene/DocId.java (original)
+++ jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/lucene/DocId.java Wed Apr  8 11:19:31 2009
@@ -27,6 +27,8 @@
  */
 abstract class DocId {
 
+    static final int[] EMPTY = new int[0];
+
     /**
      * Indicates a null DocId. Will be returned if the root node is asked for
      * its parent.
@@ -34,12 +36,14 @@
     static final DocId NULL = new DocId() {
 
         /**
-         * Always returns <code>-1</code>.
+         * Always returns an empty array.
          * @param reader the index reader.
-         * @return always <code>-1</code>.
+         * @param docNumbers a int array for reuse as return value.
+         * @return always an empty array.
          */
-        final int getDocumentNumber(MultiIndexReader reader) {
-            return -1;
+        final int[] getDocumentNumbers(MultiIndexReader reader,
+                                       int[] docNumbers) {
+            return EMPTY;
         }
 
         /**
@@ -62,15 +66,22 @@
     };
 
     /**
-     * Returns the document number of this <code>DocId</code>. If this id is
-     * invalid <code>-1</code> is returned.
+     * Returns the document numbers of this <code>DocId</code>. An empty array
+     * is returned if this id is invalid.
      *
-     * @param reader the IndexReader to resolve this <code>DocId</code>.
-     * @return the document number of this <code>DocId</code> or <code>-1</code>
-     *         if it is invalid (e.g. does not exist).
+     * @param reader     the IndexReader to resolve this <code>DocId</code>.
+     * @param docNumbers an array for reuse. An implementation should use the
+     *                   passed array as a container for the return value,
+     *                   unless the length of the returned array is different
+     *                   from <code>docNumbers</code>. In which case an
+     *                   implementation will create a new array with an
+     *                   appropriate size.
+     * @return the document numbers of this <code>DocId</code> or
+     *         empty if it is invalid (e.g. does not exist).
      * @throws IOException if an error occurs while reading from the index.
      */
-    abstract int getDocumentNumber(MultiIndexReader reader) throws IOException;
+    abstract int[] getDocumentNumbers(MultiIndexReader reader, int[] docNumbers)
+            throws IOException;
 
     /**
      * Applies an offset to this <code>DocId</code>. The returned <code>DocId</code>
@@ -123,6 +134,16 @@
         return new UUIDDocId(uuid);
     }
 
+    /**
+     * Creates a <code>DocId</code> that references multiple UUIDs.
+     *
+     * @param uuids the UUIDs of the referenced nodes.
+     * @return a <code>DocId</code> based on multiple node UUIDs.
+     */
+    static DocId create(String[] uuids)  {
+        return new MultiUUIDDocId(uuids);
+    }
+
     //--------------------------< internal >------------------------------------
 
     /**
@@ -147,8 +168,13 @@
         /**
          * @inheritDoc
          */
-        int getDocumentNumber(MultiIndexReader reader) {
-            return docNumber;
+        int[] getDocumentNumbers(MultiIndexReader reader, int[] docNumbers) {
+            if (docNumbers.length == 1) {
+                docNumbers[0] = docNumber;
+                return docNumbers;
+            } else {
+                return new int[]{docNumber};
+            }
         }
 
         /**
@@ -208,7 +234,8 @@
         /**
          * @inheritDoc
          */
-        int getDocumentNumber(MultiIndexReader reader) throws IOException {
+        int[] getDocumentNumbers(MultiIndexReader reader, int[] docNumbers)
+                throws IOException {
             int realDoc = -1;
             ForeignSegmentDocId segDocId = doc;
             if (segDocId != null) {
@@ -222,12 +249,18 @@
                     doc = segDocId;
                 }
             }
-            return realDoc;
+
+            if (docNumbers.length == 1) {
+                docNumbers[0] = realDoc;
+                return docNumbers;
+            } else {
+                return new int[]{realDoc};
+            }
         }
 
         /**
          * This implementation will return <code>this</code>. Document number is
-         * not known until resolved in {@link #getDocumentNumber(MultiIndexReader)}.
+         * not known until resolved in {@link #getDocumentNumbers(MultiIndexReader,int[])}.
          *
          * @inheritDoc
          */
@@ -254,4 +287,76 @@
             return "UUIDDocId(" + new UUID(msb, lsb) + ")";
         }
     }
+
+    /**
+     * A DocId based on multiple UUIDDocIds.
+     */
+    private static final class MultiUUIDDocId extends DocId {
+
+        /**
+         * The internal uuid based doc ids.
+         */
+        private final UUIDDocId[] docIds;
+
+        /**
+         * @param uuids the uuids of the referenced nodes.
+         * @throws IllegalArgumentException if one of the uuids is malformed.
+         */
+        MultiUUIDDocId(String[] uuids) {
+            this.docIds = new UUIDDocId[uuids.length];
+            for (int i = 0; i < uuids.length; i++) {
+                docIds[i] = new UUIDDocId(UUID.fromString(uuids[i]));
+            }
+        }
+
+        /**
+         * @inheritDoc
+         */
+        int[] getDocumentNumbers(MultiIndexReader reader, int[] docNumbers)
+                throws IOException {
+            int[] tmp = new int[1];
+            docNumbers = new int[docIds.length];
+            for (int i = 0; i < docNumbers.length; i++) {
+                docNumbers[i] = docIds[i].getDocumentNumbers(reader, tmp)[0];
+            }
+            return docNumbers;
+        }
+
+        /**
+         * This implementation will return <code>this</code>. Document number is
+         * not known until resolved in {@link #getDocumentNumbers(MultiIndexReader,int[])}.
+         *
+         * @inheritDoc
+         */
+        DocId applyOffset(int offset) {
+            return this;
+        }
+
+        /**
+         * Always returns <code>true</code>.
+         *
+         * @param deleted the deleted documents.
+         * @return always <code>true</code>.
+         */
+        boolean isValid(BitSet deleted) {
+            return true;
+        }
+
+        /**
+         * Returns a String representation for this <code>DocId</code>.
+         *
+         * @return a String representation for this <code>DocId</code>.
+         */
+        public String toString() {
+            StringBuffer sb = new StringBuffer("MultiUUIDDocId(");
+            String separator = "";
+            for (int i = 0; i < docIds.length; i++) {
+                sb.append(separator);
+                separator = ", ";
+                sb.append(new UUID(docIds[i].msb, docIds[i].lsb));
+            }
+            sb.append(")");
+            return sb.toString();
+        }
+    }
 }

Modified: jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/lucene/FieldNames.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/lucene/FieldNames.java?rev=763188&r1=763187&r2=763188&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/lucene/FieldNames.java (original)
+++ jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/lucene/FieldNames.java Wed Apr  8 11:19:31 2009
@@ -107,6 +107,11 @@
     public static final String REINDEXING_REQUIRED = "_:REINDEXING_REQUIRED".intern();
 
     /**
+     * Name of the field that marks shareable nodes.
+     */
+    public static final String SHAREABLE_NODE = "_:SHAREABLE_NODE".intern();
+
+    /**
      * Returns a named length for use as a term in the index. The named length
      * is of the form: <code>propertyName</code> + '[' +
      * {@link LongField#longToString(long)}.

Modified: jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/lucene/ForeignSegmentDocId.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/lucene/ForeignSegmentDocId.java?rev=763188&r1=763187&r2=763188&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/lucene/ForeignSegmentDocId.java (original)
+++ jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/lucene/ForeignSegmentDocId.java Wed Apr  8 11:19:31 2009
@@ -26,6 +26,11 @@
 final class ForeignSegmentDocId extends DocId {
 
     /**
+     * Empty array of {@link ForeignSegmentDocId}s.
+     */
+    static final ForeignSegmentDocId[] EMPTY_ARRAY = new ForeignSegmentDocId[0];
+
+    /**
      * The document number.
      */
     private final int docNumber;
@@ -64,13 +69,23 @@
     /**
      * @inheritDoc
      */
-    int getDocumentNumber(MultiIndexReader reader) throws IOException {
-        return reader.getDocumentNumber(this);
+    int[] getDocumentNumbers(MultiIndexReader reader, int[] docNumbers) throws IOException {
+        int doc = reader.getDocumentNumber(this);
+        if (doc == -1) {
+            return EMPTY;
+        } else {
+            if (docNumbers.length == 1) {
+                docNumbers[0] = doc;
+                return docNumbers;
+            } else {
+                return new int[]{doc};
+            }
+        }
     }
 
     /**
      * This implementation will return <code>this</code>. Document number is
-     * not known until resolved in {@link #getDocumentNumber(MultiIndexReader)}.
+     * not known until resolved in {@link DocId#getDocumentNumbers(MultiIndexReader,int[])}.
      *
      * {@inheritDoc}
      */
@@ -82,7 +97,7 @@
      * Always returns <code>true</code> because this calls is in context of the
      * index segment where this DocId lives. Within this segment this DocId is
      * always valid. Whether the target of this DocId is valid can only be
-     * checked in the method {@link #getDocumentNumber(MultiIndexReader)}.
+     * checked in the method {@link DocId#getDocumentNumbers(MultiIndexReader,int[])}.
      *
      * @param deleted the deleted documents in the segment where this DocId
      *                lives.

Modified: jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/lucene/HierarchyResolver.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/lucene/HierarchyResolver.java?rev=763188&r1=763187&r2=763188&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/lucene/HierarchyResolver.java (original)
+++ jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/lucene/HierarchyResolver.java Wed Apr  8 11:19:31 2009
@@ -25,14 +25,20 @@
 public interface HierarchyResolver {
 
     /**
-     * Returns the document number of the parent of <code>n</code> or
-     * <code>-1</code> if <code>n</code> does not have a parent (<code>n</code>
-     * is the root node).
+     * Returns the document number of the parent of <code>n</code> or an empty
+     * array if <code>n</code> does not have a parent (<code>n</code> is the
+     * root node).
      *
-     * @param n the document number.
+     * @param n          the document number.
+     * @param docNumbers an array for reuse. An implementation should use the
+     *                   passed array as a container for the return value,
+     *                   unless the length of the returned array is different
+     *                   from <code>docNumbers</code>. In which case an
+     *                   implementation will create a new array with an
+     *                   appropriate size.
      * @return the document number of <code>n</code>'s parent.
-     * @throws java.io.IOException if an error occurs while reading from the index.
+     * @throws java.io.IOException if an error occurs while reading from the
+     *                             index.
      */
-    int getParent(int n) throws IOException;
-
+    int[] getParents(int n, int[] docNumbers) throws IOException;
 }

Modified: jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/lucene/JackrabbitIndexReader.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/lucene/JackrabbitIndexReader.java?rev=763188&r1=763187&r2=763188&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/lucene/JackrabbitIndexReader.java (original)
+++ jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/lucene/JackrabbitIndexReader.java Wed Apr  8 11:19:31 2009
@@ -82,8 +82,8 @@
     /**
      * {@inheritDoc}
      */
-    public int getParent(int n) throws IOException {
-        return resolver.getParent(n);
+    public int[] getParents(int n, int[] docNumbers) throws IOException {
+        return resolver.getParents(n, docNumbers);
     }
 
     //-------------------------< MultiIndexReader >-----------------------------

Modified: jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/lucene/NodeIndexer.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/lucene/NodeIndexer.java?rev=763188&r1=763187&r2=763188&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/lucene/NodeIndexer.java (original)
+++ jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/lucene/NodeIndexer.java Wed Apr  8 11:19:31 2009
@@ -186,28 +186,24 @@
         // UUID
         doc.add(new Field(
                 FieldNames.UUID, node.getNodeId().getUUID().toString(),
-                Field.Store.YES, Field.Index.NOT_ANALYZED_NO_NORMS, Field.TermVector.NO));
+                Field.Store.YES, Field.Index.NOT_ANALYZED_NO_NORMS));
         try {
             // parent UUID
             if (node.getParentId() == null) {
                 // root node
-                doc.add(new Field(FieldNames.PARENT, "", Field.Store.YES, Field.Index.NOT_ANALYZED_NO_NORMS, Field.TermVector.NO));
+                doc.add(new Field(FieldNames.PARENT, "", Field.Store.YES,
+                        Field.Index.NOT_ANALYZED_NO_NORMS));
                 addNodeName(doc, "", "");
+            } else if (node.getSharedSet().isEmpty()) {
+                addParentChildRelation(doc, node.getParentId());
             } else {
-                doc.add(new Field(
-                        FieldNames.PARENT, node.getParentId().toString(),
-                        Field.Store.YES, Field.Index.NOT_ANALYZED_NO_NORMS, Field.TermVector.NO));
-                NodeState parent = (NodeState) stateProvider.getItemState(node.getParentId());
-                ChildNodeEntry child = parent.getChildNodeEntry(node.getNodeId());
-                if (child == null) {
-                    // this can only happen when jackrabbit
-                    // is running in a cluster.
-                    throw new RepositoryException(
-                            "Missing child node entry for node with id: "
-                            + node.getNodeId());
-                }
-                Name name = child.getName();
-                addNodeName(doc, name.getNamespaceURI(), name.getLocalName());
+                // shareable node
+                for (Iterator it = node.getSharedSet().iterator(); it.hasNext(); ) {
+                    addParentChildRelation(doc, (NodeId) it.next());
+                }
+                // mark shareable nodes
+                doc.add(new Field(FieldNames.SHAREABLE_NODE, "",
+                        Field.Store.NO, Field.Index.NOT_ANALYZED_NO_NORMS));
             }
         } catch (NoSuchItemStateException e) {
             throwRepositoryException(e);
@@ -890,4 +886,32 @@
             doc.add(new Field(FieldNames.LOCAL_NAME, localName, Field.Store.NO, Field.Index.NOT_ANALYZED_NO_NORMS));
         }
     }
+
+    /**
+     * Adds a parent child relation to the given <code>doc</code>.
+     *
+     * @param doc      the document.
+     * @param parentId the id of the parent node.
+     * @throws ItemStateException  if the parent node cannot be read.
+     * @throws RepositoryException if the parent node does not have a child node
+     *                             entry for the current node.
+     */
+    protected void addParentChildRelation(Document doc,
+                                          NodeId parentId)
+            throws ItemStateException, RepositoryException {
+        doc.add(new Field(
+                FieldNames.PARENT, parentId.toString(),
+                Field.Store.YES, Field.Index.NOT_ANALYZED_NO_NORMS, Field.TermVector.NO));
+        NodeState parent = (NodeState) stateProvider.getItemState(parentId);
+        ChildNodeEntry child = parent.getChildNodeEntry(node.getNodeId());
+        if (child == null) {
+            // this can only happen when jackrabbit
+            // is running in a cluster.
+            throw new RepositoryException(
+                    "Missing child node entry for node with id: "
+                    + node.getNodeId());
+        }
+        Name name = child.getName();
+        addNodeName(doc, name.getNamespaceURI(), name.getLocalName());
+    }
 }

Modified: jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/lucene/ParentAxisQuery.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/lucene/ParentAxisQuery.java?rev=763188&r1=763187&r2=763188&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/lucene/ParentAxisQuery.java (original)
+++ jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/lucene/ParentAxisQuery.java Wed Apr  8 11:19:31 2009
@@ -312,13 +312,24 @@
 
                 final IOException[] ex = new IOException[1];
                 contextScorer.score(new HitCollector() {
+
+                    private int[] docs = new int[1];
+
                     public void collect(int doc, float score) {
                         try {
-                            doc = hResolver.getParent(doc);
-                            if (doc != -1) {
-                                hits.set(doc);
+                            docs = hResolver.getParents(doc, docs);
+                            if (docs.length == 1) {
+                                // optimize single value
+                                hits.set(docs[0]);
                                 if (score != DEFAULT_SCORE.floatValue()) {
-                                    scores.put(new Integer(doc), new Float(score));
+                                    scores.put(new Integer(docs[0]), new Float(score));
+                                }
+                            } else {
+                                for (int i = 0; i < docs.length; i++) {
+                                    hits.set(docs[i]);
+                                    if (score != DEFAULT_SCORE.floatValue()) {
+                                        scores.put(new Integer(docs[i]), new Float(score));
+                                    }
                                 }
                             }
                         } catch (IOException e) {

Modified: jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/lucene/SearchIndex.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/lucene/SearchIndex.java?rev=763188&r1=763187&r2=763188&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/lucene/SearchIndex.java (original)
+++ jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/lucene/SearchIndex.java Wed Apr  8 11:19:31 2009
@@ -1410,11 +1410,11 @@
         /**
          * @inheritDoc
          */
-        public int getParent(int n) throws IOException {
+        public int[] getParents(int n, int[] docNumbers) throws IOException {
             int i = readerIndex(n);
             DocId id = subReaders[i].getParentDocId(n - starts[i]);
             id = id.applyOffset(starts[i]);
-            return id.getDocumentNumber(this);
+            return id.getDocumentNumbers(this, docNumbers);
         }
 
         //-------------------------< MultiIndexReader >-------------------------

Modified: jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/state/NodeState.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/state/NodeState.java?rev=763188&r1=763187&r2=763188&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/state/NodeState.java (original)
+++ jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/state/NodeState.java Wed Apr  8 11:19:31 2009
@@ -797,6 +797,36 @@
         }
     }
 
+    /**
+     * Returns a set of shares that were added.
+     *
+     * @return the set of shares that were added. Set of {@link NodeId}s.
+     */
+    public synchronized Set getAddedShares() {
+        if (!hasOverlayedState() || !isShareable()) {
+            return Collections.EMPTY_SET;
+        }
+        NodeState other = (NodeState) getOverlayedState();
+        HashSet set = new HashSet(sharedSet);
+        set.removeAll(other.sharedSet);
+        return set;
+    }
+
+    /**
+     * Returns a set of shares that were removed.
+     *
+     * @return the set of shares that were removed. Set of {@link NodeId}s.
+     */
+    public synchronized Set getRemovedShares() {
+        if (!hasOverlayedState() || !isShareable()) {
+            return Collections.EMPTY_SET;
+        }
+        NodeState other = (NodeState) getOverlayedState();
+        HashSet set = new HashSet(other.sharedSet);
+        set.removeAll(sharedSet);
+        return set;
+    }
+
     //--------------------------------------------------< ItemState overrides >
 
     /**

Added: jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/observation/ShareableNodesTest.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/observation/ShareableNodesTest.java?rev=763188&view=auto
==============================================================================
--- jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/observation/ShareableNodesTest.java (added)
+++ jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/observation/ShareableNodesTest.java Wed Apr  8 11:19:31 2009
@@ -0,0 +1,91 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.jackrabbit.core.observation;
+
+import javax.jcr.RepositoryException;
+import javax.jcr.Node;
+import javax.jcr.Workspace;
+import javax.jcr.observation.Event;
+
+import org.apache.jackrabbit.test.api.observation.EventResult;
+import org.apache.jackrabbit.core.NodeImpl;
+
+/**
+ * <code>ShareableNodesTest</code>...
+ */
+public class ShareableNodesTest extends AbstractObservationTest {
+
+    public void testAddShareableMixin() throws RepositoryException {
+        Node n1 = testRootNode.addNode(nodeName1);
+        testRootNode.save();
+
+        EventResult result = new EventResult(log);
+        addEventListener(result);
+
+        n1.addMixin(mixShareable);
+        testRootNode.save();
+
+        Event[] events = result.getEvents(DEFAULT_WAIT_TIMEOUT);
+        for (int i = 0; i < events.length; i++) {
+            assertFalse("must not contain node added event", events[i].getType() == Event.NODE_ADDED);
+            assertFalse("must not contain node removed event", events[i].getType() == Event.NODE_REMOVED);
+        }
+    }
+
+    public void testAddShare() throws RepositoryException {
+        Node n1 = testRootNode.addNode(nodeName1);
+        Node n2 = testRootNode.addNode(nodeName2);
+        Node s = n1.addNode(nodeName3);
+        s.addMixin(mixShareable);
+        testRootNode.save();
+
+        EventResult result = new EventResult(log);
+        addEventListener(result);
+
+        Workspace wsp = superuser.getWorkspace();
+        wsp.clone(wsp.getName(), s.getPath(), n2.getPath() + "/" + s.getName(), false);
+
+        checkNodeAdded(result.getEvents(DEFAULT_WAIT_TIMEOUT),
+                new String[]{nodeName2 + "/" + nodeName3},
+                new String[0]);
+    }
+
+    public void testRemoveShare() throws RepositoryException {
+        Node n1 = testRootNode.addNode(nodeName1);
+        Node n2 = testRootNode.addNode(nodeName2);
+        Node s = n1.addNode(nodeName3);
+        s.addMixin(mixShareable);
+        testRootNode.save();
+
+        Workspace wsp = superuser.getWorkspace();
+        wsp.clone(wsp.getName(), s.getPath(), n2.getPath() + "/" + s.getName(), false);
+
+        EventResult result = new EventResult(log);
+        addEventListener(result);
+
+        removeFromSharedSet(n2.getNode(nodeName3));
+        testRootNode.save();
+
+        checkNodeRemoved(result.getEvents(DEFAULT_WAIT_TIMEOUT),
+                new String[]{nodeName2 + "/" + nodeName3},
+                new String[0]);
+    }
+
+    protected void removeFromSharedSet(Node node) throws RepositoryException {
+        ((NodeImpl) node).removeShare();
+    }
+}

Propchange: jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/observation/ShareableNodesTest.java
------------------------------------------------------------------------------
    svn:eol-style = native

Modified: jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/observation/TestAll.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/observation/TestAll.java?rev=763188&r1=763187&r2=763188&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/observation/TestAll.java (original)
+++ jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/observation/TestAll.java Wed Apr  8 11:19:31 2009
@@ -42,6 +42,7 @@
         suite.addTestSuite(MoveInPlaceTest.class);
         suite.addTestSuite(EventJournalTest.class);
         suite.addTestSuite(UserDataTest.class);
+        suite.addTestSuite(ShareableNodesTest.class);
 
         return suite;
     }

Added: jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/query/ShareableNodeTest.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/query/ShareableNodeTest.java?rev=763188&view=auto
==============================================================================
--- jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/query/ShareableNodeTest.java (added)
+++ jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/query/ShareableNodeTest.java Wed Apr  8 11:19:31 2009
@@ -0,0 +1,157 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.jackrabbit.core.query;
+
+import javax.jcr.RepositoryException;
+import javax.jcr.Node;
+import javax.jcr.Workspace;
+import javax.jcr.NodeIterator;
+
+import org.apache.jackrabbit.core.NodeImpl;
+
+/**
+ * <code>ShareableNodeTest</code> performs query tests with shareable nodes.
+ */
+public class ShareableNodeTest extends AbstractQueryTest {
+
+    public void testPathConstraint() throws RepositoryException {
+        Node n1 = testRootNode.addNode(nodeName1);
+        Node n2 = testRootNode.addNode(nodeName2);
+        Node s = n1.addNode(nodeName3);
+        s.setProperty(propertyName1, "value");
+        s.addMixin(mixShareable);
+        Node n4 = s.addNode(nodeName4);
+        n4.setProperty(propertyName2, "value");
+        testRootNode.save();
+
+        Workspace wsp = superuser.getWorkspace();
+        wsp.clone(wsp.getName(), s.getPath(), n2.getPath() + "/" + s.getName(), false);
+
+        String stmt = testPath + "/" + nodeName1 + "/*[@" + propertyName1 + "='value']";
+        NodeIterator nodes = executeQuery(stmt).getNodes();
+        assertEquals("wrong result size", 1, nodes.getSize());
+        // spec does not say which path must be returned -> use isSame()
+        assertTrue("wrong node", s.isSame(nodes.nextNode()));
+
+        stmt = testPath + "/" + nodeName2 + "/*[@" + propertyName1 + "='value']";
+        nodes = executeQuery(stmt).getNodes();
+        assertEquals("wrong result size", 1, nodes.getSize());
+        // spec does not say which path must be returned -> use isSame()
+        assertTrue("wrong node", s.isSame(nodes.nextNode()));
+
+        stmt = testPath + "//*[@" + propertyName1 + "='value']";
+        nodes = executeQuery(stmt).getNodes();
+        assertEquals("wrong result size", 1, nodes.getSize());
+        // spec does not say which path must be returned -> use isSame()
+        assertTrue("wrong node", s.isSame(nodes.nextNode()));
+
+        stmt = testPath + "//*[@" + propertyName2 + "='value']";
+        nodes = executeQuery(stmt).getNodes();
+        assertEquals("wrong result size", 1, nodes.getSize());
+        // spec does not say which path must be returned -> use isSame()
+        assertTrue("wrong node", n4.isSame(nodes.nextNode()));
+
+        // remove a node from the shared set
+        ((NodeImpl) s).removeShare();
+        testRootNode.save();
+
+        s = n2.getNode(nodeName3);
+
+        stmt = testPath + "/" + nodeName1 + "/*[@" + propertyName1 + "='value']";
+        nodes = executeQuery(stmt).getNodes();
+        assertEquals("wrong result size", 0, nodes.getSize());
+
+        stmt = testPath + "/" + nodeName2 + "/*[@" + propertyName1 + "='value']";
+        nodes = executeQuery(stmt).getNodes();
+        assertEquals("wrong result size", 1, nodes.getSize());
+        // spec does not say which path must be returned -> use isSame()
+        assertTrue("wrong node", s.isSame(nodes.nextNode()));
+
+        stmt = testPath + "//*[@" + propertyName1 + "='value']";
+        nodes = executeQuery(stmt).getNodes();
+        assertEquals("wrong result size", 1, nodes.getSize());
+        // spec does not say which path must be returned -> use isSame()
+        assertTrue("wrong node", s.isSame(nodes.nextNode()));
+
+        // remove remaining node from the shared set
+        ((NodeImpl) s).removeShare();
+        testRootNode.save();
+
+        stmt = testPath + "/" + nodeName1 + "/*[@" + propertyName1 + "='value']";
+        nodes = executeQuery(stmt).getNodes();
+        assertEquals("wrong result size", 0, nodes.getSize());
+
+        stmt = testPath + "/" + nodeName2 + "/*[@" + propertyName1 + "='value']";
+        nodes = executeQuery(stmt).getNodes();
+        assertEquals("wrong result size", 0, nodes.getSize());
+
+        stmt = testPath + "//*[@" + propertyName1 + "='value']";
+        nodes = executeQuery(stmt).getNodes();
+        assertEquals("wrong result size", 0, nodes.getSize());
+    }
+
+    public void testName() throws RepositoryException {
+        Node n1 = testRootNode.addNode(nodeName1);
+        Node n2 = testRootNode.addNode(nodeName2);
+        Node s = n1.addNode(nodeName3);
+        s.addMixin(mixShareable);
+        testRootNode.save();
+
+        Workspace wsp = superuser.getWorkspace();
+        wsp.clone(wsp.getName(), s.getPath(), n2.getPath() + "/" + nodeName4, false);
+
+        String stmt = testPath + "//" + nodeName3;
+        NodeIterator nodes = executeQuery(stmt).getNodes();
+        assertEquals("wrong result size", 1, nodes.getSize());
+        // spec does not say which path must be returned -> use isSame()
+        assertTrue("wrong node", s.isSame(nodes.nextNode()));
+
+        stmt = testPath + "//" + nodeName4;
+        nodes = executeQuery(stmt).getNodes();
+        assertEquals("wrong result size", 1, nodes.getSize());
+        // spec does not say which path must be returned -> use isSame()
+        assertTrue("wrong node", s.isSame(nodes.nextNode()));
+
+        // remove a node from the shared set
+        ((NodeImpl) s).removeShare();
+        testRootNode.save();
+
+        s = n2.getNode(nodeName4);
+
+        stmt = testPath + "//" + nodeName3;
+        nodes = executeQuery(stmt).getNodes();
+        assertEquals("wrong result size", 0, nodes.getSize());
+
+        stmt = testPath + "//" + nodeName4;
+        nodes = executeQuery(stmt).getNodes();
+        assertEquals("wrong result size", 1, nodes.getSize());
+        // spec does not say which path must be returned -> use isSame()
+        assertTrue("wrong node", s.isSame(nodes.nextNode()));
+
+        // remove remaining node from the shared set
+        ((NodeImpl) s).removeShare();
+        testRootNode.save();
+
+        stmt = testPath + "//" + nodeName3;
+        nodes = executeQuery(stmt).getNodes();
+        assertEquals("wrong result size", 0, nodes.getSize());
+
+        stmt = testPath + "//" + nodeName4;
+        nodes = executeQuery(stmt).getNodes();
+        assertEquals("wrong result size", 0, nodes.getSize());
+    }
+}

Propchange: jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/query/ShareableNodeTest.java
------------------------------------------------------------------------------
    svn:eol-style = native

Modified: jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/query/TestAll.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/query/TestAll.java?rev=763188&r1=763187&r2=763188&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/query/TestAll.java (original)
+++ jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/query/TestAll.java Wed Apr  8 11:19:31 2009
@@ -59,6 +59,7 @@
         suite.addTestSuite(IndexingAggregateTest.class);
         suite.addTestSuite(IndexFormatVersionTest.class);
         suite.addTestSuite(IndexingRuleTest.class);
+        suite.addTestSuite(ShareableNodeTest.class);
 
         return suite;
     }

Modified: jackrabbit/trunk/jackrabbit-jcr-tests/src/main/java/org/apache/jackrabbit/test/AbstractJCRTest.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-jcr-tests/src/main/java/org/apache/jackrabbit/test/AbstractJCRTest.java?rev=763188&r1=763187&r2=763188&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit-jcr-tests/src/main/java/org/apache/jackrabbit/test/AbstractJCRTest.java (original)
+++ jackrabbit/trunk/jackrabbit-jcr-tests/src/main/java/org/apache/jackrabbit/test/AbstractJCRTest.java Wed Apr  8 11:19:31 2009
@@ -185,6 +185,11 @@
     protected String mixLockable;
 
     /**
+     * JCR Name mix:shareable using the namespace resolver of the current session.
+     */
+    protected String mixShareable;
+
+    /**
      * JCR Name nt:query using the namespace resolver of the current session.
      */
     protected String ntQuery;
@@ -327,6 +332,7 @@
         mixReferenceable = superuser.getNamespacePrefix(NS_MIX_URI) + ":referenceable";
         mixVersionable = superuser.getNamespacePrefix(NS_MIX_URI) + ":versionable";
         mixLockable = superuser.getNamespacePrefix(NS_MIX_URI) + ":lockable";
+        mixShareable = superuser.getNamespacePrefix(NS_MIX_URI) + ":shareable";
         ntQuery = superuser.getNamespacePrefix(NS_NT_URI) + ":query";
 
         // setup custom namespaces



Mime
View raw message