jackrabbit-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From mreut...@apache.org
Subject svn commit: r791737 - in /jackrabbit/trunk: jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/lucene/ jackrabbit-core/src/test/java/org/apache/jackrabbit/core/query/ jackrabbit-spi-commons/src/main/java/org/apache/jackrabbit/spi/commons/qu...
Date Tue, 07 Jul 2009 07:46:51 GMT
Author: mreutegg
Date: Tue Jul  7 07:46:51 2009
New Revision: 791737

URL: http://svn.apache.org/viewvc?rev=791737&view=rev
Log:
JCR-977: jcr:deref in xpath predicates

Added:
    jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/lucene/PredicateDerefQuery.java   (with props)
    jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/query/ParentNodeTest.java   (with props)
Modified:
    jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/lucene/DerefQuery.java
    jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/lucene/LuceneQueryBuilder.java
    jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/lucene/ParentAxisQuery.java
    jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/query/DerefTest.java
    jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/query/TestAll.java
    jackrabbit/trunk/jackrabbit-spi-commons/src/main/java/org/apache/jackrabbit/spi/commons/query/AndQueryNode.java
    jackrabbit/trunk/jackrabbit-spi-commons/src/main/java/org/apache/jackrabbit/spi/commons/query/DefaultQueryNodeFactory.java
    jackrabbit/trunk/jackrabbit-spi-commons/src/main/java/org/apache/jackrabbit/spi/commons/query/LocationStepQueryNode.java
    jackrabbit/trunk/jackrabbit-spi-commons/src/main/java/org/apache/jackrabbit/spi/commons/query/NAryQueryNode.java
    jackrabbit/trunk/jackrabbit-spi-commons/src/main/java/org/apache/jackrabbit/spi/commons/query/NotQueryNode.java
    jackrabbit/trunk/jackrabbit-spi-commons/src/main/java/org/apache/jackrabbit/spi/commons/query/OrQueryNode.java
    jackrabbit/trunk/jackrabbit-spi-commons/src/main/java/org/apache/jackrabbit/spi/commons/query/PathQueryNode.java
    jackrabbit/trunk/jackrabbit-spi-commons/src/main/java/org/apache/jackrabbit/spi/commons/query/QueryTreeDump.java
    jackrabbit/trunk/jackrabbit-spi-commons/src/main/java/org/apache/jackrabbit/spi/commons/query/RelationQueryNode.java
    jackrabbit/trunk/jackrabbit-spi-commons/src/main/java/org/apache/jackrabbit/spi/commons/query/sql/QueryFormat.java
    jackrabbit/trunk/jackrabbit-spi-commons/src/main/java/org/apache/jackrabbit/spi/commons/query/xpath/QueryFormat.java
    jackrabbit/trunk/jackrabbit-spi-commons/src/main/java/org/apache/jackrabbit/spi/commons/query/xpath/XPathQueryBuilder.java

Modified: jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/lucene/DerefQuery.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/lucene/DerefQuery.java?rev=791737&r1=791736&r2=791737&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/lucene/DerefQuery.java (original)
+++ jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/lucene/DerefQuery.java Tue Jul  7 07:46:51 2009
@@ -114,7 +114,15 @@
      * @return 'DerefQuery'.
      */
     public String toString(String field) {
-        return "DerefQuery";
+        StringBuffer sb = new StringBuffer();
+        sb.append("DerefQuery(");
+        sb.append(refProperty);
+        sb.append(", ");
+        sb.append(contextQuery);
+        sb.append(", ");
+        sb.append(nameTest);
+        sb.append(")");
+        return sb.toString();
     }
 
     /**
@@ -291,6 +299,14 @@
             throw new UnsupportedOperationException();
         }
 
+        /**
+         * 1. do context query
+         * 2. go through each document from the query
+         * 3. find reference property UUIDs
+         * 4. Use UUIDs to find document number
+         * 5. Use the name test to filter the documents
+         * @throws IOException
+         */
         private void calculateChildren() throws IOException {
             if (uuids == null) {
                 uuids = new ArrayList();

Modified: jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/lucene/LuceneQueryBuilder.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/lucene/LuceneQueryBuilder.java?rev=791737&r1=791736&r2=791737&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/lucene/LuceneQueryBuilder.java (original)
+++ jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/lucene/LuceneQueryBuilder.java Tue Jul  7 07:46:51 2009
@@ -41,11 +41,11 @@
 import org.apache.jackrabbit.core.state.ItemStateManager;
 import org.apache.jackrabbit.spi.Name;
 import org.apache.jackrabbit.spi.Path;
-import org.apache.jackrabbit.spi.commons.conversion.MalformedPathException;
+import org.apache.jackrabbit.spi.PathFactory;
 import org.apache.jackrabbit.spi.commons.conversion.NameException;
 import org.apache.jackrabbit.spi.commons.conversion.NamePathResolver;
 import org.apache.jackrabbit.spi.commons.name.NameConstants;
-import org.apache.jackrabbit.spi.commons.name.PathBuilder;
+import org.apache.jackrabbit.spi.commons.name.PathFactoryImpl;
 import org.apache.jackrabbit.spi.commons.query.AndQueryNode;
 import org.apache.jackrabbit.spi.commons.query.DefaultQueryNodeVisitor;
 import org.apache.jackrabbit.spi.commons.query.DerefQueryNode;
@@ -68,11 +68,11 @@
 import org.apache.jackrabbit.util.XMLChar;
 import org.apache.lucene.analysis.Analyzer;
 import org.apache.lucene.index.Term;
+import org.apache.lucene.queryParser.ParseException;
+import org.apache.lucene.queryParser.QueryParser;
 import org.apache.lucene.search.BooleanQuery;
 import org.apache.lucene.search.Query;
 import org.apache.lucene.search.BooleanClause.Occur;
-import org.apache.lucene.queryParser.QueryParser;
-import org.apache.lucene.queryParser.ParseException;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -90,6 +90,16 @@
     private static final Logger log = LoggerFactory.getLogger(LuceneQueryBuilder.class);
 
     /**
+     * The path factory instance.
+     */
+    private static final PathFactory PATH_FACTORY = PathFactoryImpl.getInstance();
+
+    /**
+     * The name of a parent path element.
+     */
+    private static final Name PARENT_ELEMENT_NAME = PATH_FACTORY.getParentElement().getName();
+
+    /**
      * Root node of the abstract query tree
      */
     private final QueryRootNode root;
@@ -494,6 +504,10 @@
 
         NameQuery nameTest = null;
         if (node.getNameTest() != null) {
+            if (node.getNameTest().equals(PARENT_ELEMENT_NAME)) {
+                andQuery.add(new ParentAxisQuery(context, null, indexFormatVersion, nsMappings), Occur.MUST);
+                return andQuery;
+            }
             nameTest = new NameQuery(node.getNameTest(), indexFormatVersion, nsMappings);
         }
 
@@ -587,6 +601,16 @@
     }
 
     public Object visit(RelationQueryNode node, Object data) throws RepositoryException {
+        PathQueryNode relPath = node.getRelativePath();
+        if (relPath == null
+                && node.getOperation() != QueryConstants.OPERATION_SIMILAR
+                && node.getOperation() != QueryConstants.OPERATION_SPELLCHECK) {
+            exceptions.add(new InvalidQueryException("@* not supported in predicate"));
+            return data;
+        }
+        LocationStepQueryNode[] steps = relPath.getPathSteps();
+        Name propertyName = steps[steps.length - 1].getNameTest();
+        
         Query query;
         String[] stringValues = new String[1];
         switch (node.getValueType()) {
@@ -608,7 +632,6 @@
                         || node.getOperation() == QueryConstants.OPERATION_NE_GENERAL
                         || node.getOperation() == QueryConstants.OPERATION_NE_VALUE) {
                     // only use coercing on non-range operations
-                    Name propertyName = node.getRelativePath().getNameElement().getName();
                     stringValues = getStringValues(propertyName, node.getStringValue());
                 } else {
                     stringValues[0] = node.getStringValue();
@@ -622,13 +645,6 @@
                         + node.getValueType());
         }
 
-        if (node.getRelativePath() == null
-                && node.getOperation() != QueryConstants.OPERATION_SIMILAR
-                && node.getOperation() != QueryConstants.OPERATION_SPELLCHECK) {
-            exceptions.add(new InvalidQueryException("@* not supported in predicate"));
-            return data;
-        }
-
         // get property transformation
         final int[] transform = new int[]{TransformConstants.TRANSFORM_NONE};
         node.acceptOperands(new DefaultQueryNodeVisitor() {
@@ -642,37 +658,24 @@
             }
         }, null);
 
-        Path relPath = node.getRelativePath();
         if (node.getOperation() == QueryConstants.OPERATION_SIMILAR) {
             // this is a bit ugly:
-            // add the name of a dummy property because relPath actually
+            // use the name of a dummy property because relPath actually
             // references a property. whereas the relPath of the similar
             // operation references a node
-            PathBuilder builder;
-            if (relPath == null) {
-                builder = new PathBuilder();
-            } else {
-                builder = new PathBuilder(relPath);
-            }
-            builder.addLast(NameConstants.JCR_PRIMARYTYPE);
-            try {
-                relPath = builder.getPath();
-            } catch (MalformedPathException e) {
-                // will never happen
-            }
+            propertyName = NameConstants.JCR_PRIMARYTYPE;
         }
         String field = "";
         try {
-            field = resolver.getJCRName(relPath.getNameElement().getName());
+            field = resolver.getJCRName(propertyName);
         } catch (NamespaceException e) {
             // should never happen
             exceptions.add(e);
         }
 
         // support for fn:name()
-        Name propName = relPath.getNameElement().getName();
-        if (propName.getNamespaceURI().equals(SearchManager.NS_FN_URI)
-                && propName.getLocalName().equals("name()")) {
+        if (propertyName.getNamespaceURI().equals(SearchManager.NS_FN_URI)
+                && propertyName.getLocalName().equals("name()")) {
             if (node.getValueType() != QueryConstants.TYPE_STRING) {
                 exceptions.add(new InvalidQueryException("Name function can "
                         + "only be used in conjunction with a string literal"));
@@ -864,40 +867,114 @@
             }
         }
 
-        if (relPath.getLength() > 1) {
+        if (steps.length > 1) {
             // child axis in relation
-            Path.Element[] elements = relPath.getElements();
             // elements.length - 1 = property name
             // elements.length - 2 = last child axis name test
-            for (int i = elements.length - 2; i >= 0; i--) {
+            boolean selectParent = true; 
+            for (int i = steps.length - 2; i >= 0; i--) {
+                LocationStepQueryNode step = steps[i];
                 Name name = null;
-                if (!elements[i].getName().equals(RelationQueryNode.STAR_NAME_TEST)) {
-                    name = elements[i].getName();
+                if (!RelationQueryNode.STAR_NAME_TEST.equals(steps[i].getNameTest())) {
+                    name = steps[i].getNameTest();
                 }
-                if (i == elements.length - 2) {
-                    // join name test with property query if there is one
-                    if (name != null) {
-                        Query nameTest = new NameQuery(name,
-                                indexFormatVersion, nsMappings);
-                        BooleanQuery and = new BooleanQuery();
-                        and.add(query, Occur.MUST);
-                        and.add(nameTest, Occur.MUST);
-                        query = and;
-                    } else {
-                        // otherwise the query can be used as is
+                if (i == steps.length - 2) {
+                    if (step instanceof DerefQueryNode) {
+                        query = createPredicateDeref(query, (DerefQueryNode) step, data);
+                        if (steps.length == 2) {
+                            selectParent = false;
+                        }
+                    } else if (step instanceof LocationStepQueryNode) {
+                        // join name test with property query if there is one
+                        if (name != null) {
+                            if (!name.equals(PARENT_ELEMENT_NAME)) {
+                                Query nameTest = new NameQuery(name,
+                                        indexFormatVersion, nsMappings);
+                                BooleanQuery and = new BooleanQuery();
+                                and.add(query, Occur.MUST);
+                                and.add(nameTest, Occur.MUST);
+    
+                                query = and;
+                            } else {
+                                // If we're searching the parent, we want to return the child axis,
+                                // not the parent because this is part of the predicate. For instance,
+                                // if the query is //child[../base], this part of the code is operating
+                                // on the "../base" portion. So we want to return all the child nodes
+                                // of "base", which will then be matched against the non predicate part.
+                                query = new ChildAxisQuery(sharedItemMgr,
+                                                           query,
+                                                           null,
+                                                           indexFormatVersion,
+                                                           nsMappings);
+                                selectParent = false;
+                            }
+                        } else {
+                            // otherwise the query can be used as is
+                        }
+                    }
+                } else if (name != null && name.equals(PARENT_ELEMENT_NAME)) {
+                    // We need to select one of the properties if we haven't already.
+                    if (selectParent) { 
+                        query = new ParentAxisQuery(query, null,
+                                                    indexFormatVersion, nsMappings);
+
+                        selectParent = false;
                     }
+
+                    // See the note above on searching parents
+                    query = new ChildAxisQuery(sharedItemMgr,
+                                               query,
+                                               null,
+                                               indexFormatVersion,
+                                               nsMappings);
                 } else {
-                    query = new ParentAxisQuery(query, name,
-                            indexFormatVersion, nsMappings);
+                    if (step instanceof LocationStepQueryNode) {
+                        query = new ParentAxisQuery(query, name, indexFormatVersion, nsMappings);
+                    } else {
+                        throw new UnsupportedOperationException();
+                    }
                 }
             }
             // finally select the parent of the selected nodes
-            query = new ParentAxisQuery(query, null,
-                    indexFormatVersion, nsMappings);
+            if (selectParent) {
+                query = new ParentAxisQuery(query, null, indexFormatVersion, nsMappings);
+            }
         }
 
         return query;
     }
+    
+    public Query createPredicateDeref(Query subQuery, DerefQueryNode node, Object data) throws RepositoryException {
+        Query context = (Query) data;
+        
+        if (context == null) {
+            exceptions.add(new IllegalArgumentException("Unsupported query"));
+        }
+
+        try {
+            String refProperty = resolver.getJCRName(node.getRefProperty());
+
+            context = new PredicateDerefQuery(subQuery, refProperty, node.getNameTest(),
+                    indexFormatVersion, nsMappings);
+
+            // attach predicates
+            Object[] predicates = node.acceptOperands(this, data);
+            if (predicates.length > 0) {
+                BooleanQuery andQuery = new BooleanQuery();
+                for (int i = 0; i < predicates.length; i++) {
+                    andQuery.add((Query) predicates[i], Occur.MUST);
+                }
+                andQuery.add(context, Occur.MUST);
+                context = andQuery;
+            }
+            
+        } catch (NamespaceException e) {
+            // should never happen
+            exceptions.add(e);
+        }
+
+        return context;
+    }
 
     public Object visit(OrderQueryNode node, Object data) {
         return data;

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=791737&r1=791736&r2=791737&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 Tue Jul  7 07:46:51 2009
@@ -124,7 +124,13 @@
      * @return 'ParentAxisQuery'.
      */
     public String toString(String field) {
-        return "ParentAxisQuery";
+        StringBuffer sb = new StringBuffer();
+        sb.append("ParentAxisQuery(");
+        sb.append(contextQuery);
+        sb.append(", ");
+        sb.append(nameTest);
+        sb.append(")");
+        return sb.toString();
     }
 
     //-----------------------< ParentAxisWeight >-------------------------------

Added: jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/lucene/PredicateDerefQuery.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/lucene/PredicateDerefQuery.java?rev=791737&view=auto
==============================================================================
--- jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/lucene/PredicateDerefQuery.java (added)
+++ jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/lucene/PredicateDerefQuery.java Tue Jul  7 07:46:51 2009
@@ -0,0 +1,375 @@
+/*
+ * 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.lucene;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.BitSet;
+import java.util.List;
+import java.util.Set;
+
+import org.apache.jackrabbit.spi.Name;
+import org.apache.lucene.index.IndexReader;
+import org.apache.lucene.index.Term;
+import org.apache.lucene.index.TermDocs;
+import org.apache.lucene.search.Explanation;
+import org.apache.lucene.search.HitCollector;
+import org.apache.lucene.search.Query;
+import org.apache.lucene.search.Scorer;
+import org.apache.lucene.search.Searcher;
+import org.apache.lucene.search.Similarity;
+import org.apache.lucene.search.Weight;
+
+/**
+ * Implements a Lucene <code>Query</code> which returns the nodes which have a 
+ * reference property which matches the nodes of the subquery.
+ */
+public class PredicateDerefQuery extends Query {
+
+    /**
+     * The context query
+     */
+    private final Query subQuery;
+
+    /**
+     * The name of the reference property.
+     */
+    private final String refProperty;
+
+    /**
+     * The nameTest to apply on target node, or <code>null</code> if all
+     * target nodes should be selected.
+     */
+    private final Name nameTest;
+
+    /**
+     * The index format version.
+     */
+    private final IndexFormatVersion version;
+
+    /**
+     * The internal namespace mappings.
+     */
+    private final NamespaceMappings nsMappings;
+
+    /**
+     * The scorer of the context query
+     */
+    private Scorer subQueryScorer;
+
+    /**
+     * The scorer of the name test query
+     */
+    private Scorer nameTestScorer;
+    /**
+     * Creates a new <code>DerefQuery</code> based on a <code>context</code>
+     * query.
+     *
+     * @param context the context for this query.
+     * @param subQuery TODO
+     * @param refProperty the name of the reference property.
+     * @param nameTest a name test or <code>null</code> if any node is
+     *  selected.
+     * @param version the index format version.
+     * @param nsMappings the namespace mappings.
+     */
+    PredicateDerefQuery(Query subQuery, String refProperty,
+               Name nameTest, IndexFormatVersion version, NamespaceMappings nsMappings) {
+        this.subQuery = subQuery;
+        this.refProperty = refProperty;
+        this.nameTest = nameTest;
+        this.version = version;
+        this.nsMappings = nsMappings;
+    }
+
+    /**
+     * Creates a <code>Weight</code> instance for this query.
+     *
+     * @param searcher the <code>Searcher</code> instance to use.
+     * @return a <code>DerefWeight</code>.
+     */
+    protected Weight createWeight(Searcher searcher) {
+        return new DerefWeight(searcher);
+    }
+
+    /**
+     * Returns PredicateDerefQuery(subQuery, referenceNodeProperty, nameTest)
+     *
+     * @param field the name of a field.
+     * @return 'DerefQuery'.
+     */
+    public String toString(String field) {
+        StringBuffer sb = new StringBuffer();
+        sb.append("PredicateDerefQuery(");
+        sb.append(subQuery);
+        sb.append(", ");
+        sb.append(nameTest);
+        sb.append(", ");
+        sb.append(refProperty);
+        sb.append(")");
+        return sb.toString();
+    }
+
+
+    /**
+     * {@inheritDoc}
+     */
+    public void extractTerms(Set terms) {
+        // no terms to extract
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public Query rewrite(IndexReader reader) throws IOException {
+        Query cQuery = subQuery.rewrite(reader);
+        if (cQuery == subQuery) {
+            return this;
+        } else {
+            return new PredicateDerefQuery(subQuery, refProperty, nameTest, version, nsMappings);
+        }
+    }
+
+    //-------------------< DerefWeight >------------------------------------
+
+    /**
+     * The <code>Weight</code> implementation for this <code>DerefQuery</code>.
+     */
+    private class DerefWeight implements Weight {
+
+        /**
+         * The searcher in use
+         */
+        private final Searcher searcher;
+
+        /**
+         * Creates a new <code>DerefWeight</code> instance using
+         * <code>searcher</code>.
+         *
+         * @param searcher a <code>Searcher</code> instance.
+         */
+        private DerefWeight(Searcher searcher) {
+            this.searcher = searcher;
+        }
+
+        /**
+         * Returns this <code>DerefQuery</code>.
+         *
+         * @return this <code>DerefQuery</code>.
+         */
+        public Query getQuery() {
+            return PredicateDerefQuery.this;
+        }
+
+        /**
+         * {@inheritDoc}
+         */
+        public float getValue() {
+            return 1.0f;
+        }
+
+        /**
+         * {@inheritDoc}
+         */
+        public float sumOfSquaredWeights() throws IOException {
+            return 1.0f;
+        }
+
+        /**
+         * {@inheritDoc}
+         */
+        public void normalize(float norm) {
+        }
+
+        /**
+         * Creates a scorer for this <code>DerefQuery</code>.
+         *
+         * @param reader a reader for accessing the index.
+         * @return a <code>DerefScorer</code>.
+         * @throws IOException if an error occurs while reading from the index.
+         */
+        public Scorer scorer(IndexReader reader) throws IOException {
+            subQueryScorer = subQuery.weight(searcher).scorer(reader);
+            if (nameTest != null) {
+                nameTestScorer = new NameQuery(nameTest, version, nsMappings).weight(searcher).scorer(reader);
+            }
+            return new DerefScorer(searcher.getSimilarity(), reader);
+        }
+
+        /**
+         * {@inheritDoc}
+         */
+        public Explanation explain(IndexReader reader, int doc) throws IOException {
+            return new Explanation();
+        }
+    }
+
+    //----------------------< DerefScorer >---------------------------------
+
+    /**
+     * Implements a <code>Scorer</code> for this <code>DerefQuery</code>.
+     */
+    private class DerefScorer extends Scorer {
+
+        /**
+         * An <code>IndexReader</code> to access the index.
+         */
+        private final IndexReader reader;
+
+        /**
+         * BitSet storing the id's of selected documents
+         */
+        private final BitSet subQueryHits;
+        
+        /**
+         * BitSet storing the id's of selected documents
+         */
+        private final BitSet hits;
+
+        /**
+         * List of UUIDs of selected nodes
+         */
+        private List uuids = null;
+
+        
+        /**
+         * The next document id to return
+         */
+        private int nextDoc = -1;
+
+        /**
+         * Creates a new <code>DerefScorer</code>.
+         *
+         * @param similarity the <code>Similarity</code> instance to use.
+         * @param reader     for index access.
+         */
+        protected DerefScorer(Similarity similarity, IndexReader reader) {
+            super(similarity);
+            this.reader = reader;
+            this.hits = new BitSet(reader.maxDoc());
+            this.subQueryHits = new BitSet(reader.maxDoc());
+        }
+
+        /**
+         * {@inheritDoc}
+         */
+        public boolean next() throws IOException {
+            calculateChildren();
+            nextDoc = hits.nextSetBit(nextDoc + 1);
+            return nextDoc > -1;
+        }
+
+        /**
+         * {@inheritDoc}
+         */
+        public int doc() {
+            return nextDoc;
+        }
+
+        /**
+         * {@inheritDoc}
+         */
+        public float score() throws IOException {
+            return 1.0f;
+        }
+
+        /**
+         * {@inheritDoc}
+         */
+        public boolean skipTo(int target) throws IOException {
+            calculateChildren();
+            nextDoc = hits.nextSetBit(target);
+            return nextDoc > -1;
+        }
+
+        /**
+         * {@inheritDoc}
+         *
+         * @throws UnsupportedOperationException this implementation always
+         *                                       throws an <code>UnsupportedOperationException</code>.
+         */
+        public Explanation explain(int doc) throws IOException {
+            throw new UnsupportedOperationException();
+        }
+
+
+        /**
+         * Perform the sub query
+         * For each reference property UUID
+         *      - find document number
+         *      - if document # is in subquery bitset add to bit set
+         * Use the name test to filter the documents
+         * @throws IOException
+         */
+        private void calculateChildren() throws IOException {
+            if (uuids == null) {
+                uuids = new ArrayList();
+//                subQueryHits.clear();
+//                hits.clear();
+                subQueryScorer.score(new HitCollector() {
+                    public void collect(int doc, float score) {
+                        subQueryHits.set(doc);
+                    }
+                });
+
+                TermDocs termDocs = reader.termDocs(new Term(FieldNames.PROPERTIES_SET, refProperty));
+                String prefix = FieldNames.createNamedValue(refProperty, "");
+                while (termDocs.next()) {
+                    int doc = termDocs.doc();
+                     
+                    String[] values = reader.document(doc).getValues(FieldNames.PROPERTIES);
+                    if (values == null) {
+                        // no reference properties at all on this node
+                        continue;
+                    }
+                    for (int v = 0; v < values.length; v++) {
+                        if (values[v].startsWith(prefix)) {
+                            String uuid = values[v].substring(prefix.length());
+                            
+                            TermDocs node = reader.termDocs(new Term(FieldNames.UUID, uuid));
+                            try {
+                                while (node.next()) {
+                                    if (subQueryHits.get(node.doc())) {
+                                        hits.set(doc);
+                                    }
+                                }
+                            } finally {
+                                node.close();
+                            }
+                        }
+                    }
+                }
+                
+                // collect nameTest hits
+                final BitSet nameTestHits = new BitSet();
+                if (nameTestScorer != null) {
+                    nameTestScorer.score(new HitCollector() {
+                        public void collect(int doc, float score) {
+                            nameTestHits.set(doc);
+                        }
+                    });
+                }
+
+                // filter out the target nodes that do not match the name test
+                // if there is any name test at all.
+                if (nameTestScorer != null) {
+                    hits.and(nameTestHits);
+                }
+            }
+        }
+    }
+}

Propchange: jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/lucene/PredicateDerefQuery.java
------------------------------------------------------------------------------
    svn:eol-style = native

Modified: jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/query/DerefTest.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/query/DerefTest.java?rev=791737&r1=791736&r2=791737&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/query/DerefTest.java (original)
+++ jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/query/DerefTest.java Tue Jul  7 07:46:51 2009
@@ -137,6 +137,17 @@
                 new Node[]{microsoft});
     }
 
+    public void testDerefInPredicate() throws RepositoryException {
+        executeXPathQuery(testPath + "/people//*[jcr:deref(@worksfor, '*')/@ceo='McNealy']",
+                new Node[]{andrew, eric});
+
+        executeXPathQuery("//*[people/jcr:deref(@worksfor, '*')/@ceo='McNealy']",
+                new Node[]{testRootNode});
+
+//        executeXPathQuery("//*[jcr:contains(people/jcr:deref(@worksfor, '*'),'ballmer')]",
+//                new Node[]{testRootNode});
+    }
+
     /**
      * Checks if jcr:deref works when dereferencing into the version storage.
      */

Added: jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/query/ParentNodeTest.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/query/ParentNodeTest.java?rev=791737&view=auto
==============================================================================
--- jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/query/ParentNodeTest.java (added)
+++ jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/query/ParentNodeTest.java Tue Jul  7 07:46:51 2009
@@ -0,0 +1,102 @@
+/*
+ * 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.Node;
+import javax.jcr.RepositoryException;
+import javax.jcr.query.Query;
+import javax.jcr.query.QueryResult;
+
+/**
+ * <code>QueryResultTest</code> tests various methods on the
+ * <code>NodeIterator</code> returned by a <code>QueryResult</code>.
+ */
+public class ParentNodeTest extends AbstractQueryTest {
+
+    protected void setUp() throws Exception {
+        super.setUp();
+        
+        // creates the following test structure:
+        // + base (foo1=bar1)
+        //    + child (foo2=bar2)
+        //    + child2 (foo2=bar2)
+        //    + child3 (foo2=bar2)
+        Node base = testRootNode.addNode("base");
+        base.setProperty("foo1", "bar1");
+
+        base.addNode("child").setProperty("foo2", "bar2");
+        base.addNode("child2").setProperty("foo2", "bar2");
+        base.addNode("child3").setProperty("foo2", "bar2");
+        
+        superuser.save();
+    }
+
+    public void testParentInPath() throws RepositoryException {
+        String stmt = testPath + "//child/..[@foo1]";
+        QueryResult result = qm.createQuery(stmt, Query.XPATH).execute();
+        assertEquals("Wrong size of NodeIterator in result",
+                1, result.getNodes().getSize());
+
+        assertEquals("base", result.getNodes().nextNode().getName());
+    }
+
+    public void testParentInAttribute1() throws RepositoryException {
+        String stmt = testPath + "//child[../@foo1]";
+        QueryResult result = qm.createQuery(stmt, Query.XPATH).execute();
+        assertTrue("Wrong size of NodeIterator in result", result.getNodes().getSize() > 0);
+
+        assertEquals("child", result.getNodes().nextNode().getName());
+    }
+    
+    public void testParentInAttribute2() throws RepositoryException {
+        String stmt = testPath + "//child[../child/@foo2]";
+        QueryResult result = qm.createQuery(stmt, Query.XPATH).execute();
+        assertTrue("Wrong size of NodeIterator in result", result.getNodes().getSize() > 0);
+
+        assertEquals("child", result.getNodes().nextNode().getName());
+    }
+
+    public void testParentInAttribute3() throws RepositoryException {
+        String stmt = testPath + "//child[../../base/@foo1]";
+        QueryResult result = qm.createQuery(stmt, Query.XPATH).execute();
+        assertTrue("Wrong size of NodeIterator in result", result.getNodes().getSize() > 0);
+        
+        assertEquals("child", result.getNodes().nextNode().getName());
+    }
+
+    public void testParentInAttribute4() throws RepositoryException {
+        String stmt = testPath + "//child[../@foo1 = 'bar1']";
+        QueryResult result = qm.createQuery(stmt, Query.XPATH).execute();
+        assertTrue("Wrong size of NodeIterator in result", result.getNodes().getSize() > 0);
+
+        assertEquals("child", result.getNodes().nextNode().getName());
+    }
+
+    public void testParentAttributeWithDescendant() throws RepositoryException {
+        String stmt = testPath + "//base[../base/@foo1 = 'bar1']/child";
+        QueryResult result = qm.createQuery(stmt, Query.XPATH).execute();
+        assertTrue("Wrong size of NodeIterator in result", result.getNodes().getSize() > 0);
+
+        assertEquals("child", result.getNodes().nextNode().getName());
+    }
+    
+    public void testParentWithAnd() throws RepositoryException {
+        String stmt = testPath + "//child[../@foo1 = 'bar1 and @foo2']";
+        QueryResult result = qm.createQuery(stmt, Query.XPATH).execute();
+        assertEquals("Wrong size of NodeIterator in result", 0, result.getNodes().getSize());
+    }
+}

Propchange: jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/query/ParentNodeTest.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=791737&r1=791736&r2=791737&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 Tue Jul  7 07:46:51 2009
@@ -60,6 +60,7 @@
         suite.addTestSuite(IndexFormatVersionTest.class);
         suite.addTestSuite(IndexingRuleTest.class);
         suite.addTestSuite(ShareableNodeTest.class);
+        suite.addTestSuite(ParentNodeTest.class);
 
         return suite;
     }

Modified: jackrabbit/trunk/jackrabbit-spi-commons/src/main/java/org/apache/jackrabbit/spi/commons/query/AndQueryNode.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-spi-commons/src/main/java/org/apache/jackrabbit/spi/commons/query/AndQueryNode.java?rev=791737&r1=791736&r2=791737&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit-spi-commons/src/main/java/org/apache/jackrabbit/spi/commons/query/AndQueryNode.java (original)
+++ jackrabbit/trunk/jackrabbit-spi-commons/src/main/java/org/apache/jackrabbit/spi/commons/query/AndQueryNode.java Tue Jul  7 07:46:51 2009
@@ -22,7 +22,7 @@
  * Implements a query node that defines an AND operation between arbitrary
  * other {@link QueryNode}s.
  */
-public class AndQueryNode extends NAryQueryNode {
+public class AndQueryNode extends NAryQueryNode<QueryNode> {
 
     /**
      * Creates a new <code>AndQueryNode</code> with a <code>parent</code>

Modified: jackrabbit/trunk/jackrabbit-spi-commons/src/main/java/org/apache/jackrabbit/spi/commons/query/DefaultQueryNodeFactory.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-spi-commons/src/main/java/org/apache/jackrabbit/spi/commons/query/DefaultQueryNodeFactory.java?rev=791737&r1=791736&r2=791737&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit-spi-commons/src/main/java/org/apache/jackrabbit/spi/commons/query/DefaultQueryNodeFactory.java (original)
+++ jackrabbit/trunk/jackrabbit-spi-commons/src/main/java/org/apache/jackrabbit/spi/commons/query/DefaultQueryNodeFactory.java Tue Jul  7 07:46:51 2009
@@ -89,7 +89,7 @@
      */
     public RelationQueryNode createRelationQueryNode(QueryNode parent,
                                                      int operation) {
-        return new RelationQueryNode(parent, operation);
+        return new RelationQueryNode(parent, operation, this);
     }
 
     /**

Modified: jackrabbit/trunk/jackrabbit-spi-commons/src/main/java/org/apache/jackrabbit/spi/commons/query/LocationStepQueryNode.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-spi-commons/src/main/java/org/apache/jackrabbit/spi/commons/query/LocationStepQueryNode.java?rev=791737&r1=791736&r2=791737&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit-spi-commons/src/main/java/org/apache/jackrabbit/spi/commons/query/LocationStepQueryNode.java (original)
+++ jackrabbit/trunk/jackrabbit-spi-commons/src/main/java/org/apache/jackrabbit/spi/commons/query/LocationStepQueryNode.java Tue Jul  7 07:46:51 2009
@@ -32,7 +32,7 @@
  * /     -> descendants = false, nameTest = ""
  * </code>
  */
-public class LocationStepQueryNode extends NAryQueryNode {
+public class LocationStepQueryNode extends NAryQueryNode<QueryNode> {
 
     /** Constant value for position index = last() */
     public static final int LAST = Integer.MIN_VALUE;
@@ -46,7 +46,7 @@
      * TODO: The root location step should be refactored somehow
      */
     public static final Name EMPTY_NAME = NameFactoryImpl.getInstance().create("", "");
-
+    
     /** Empty <code>QueryNode</code> array for us as return value */
     private static final QueryNode[] EMPTY = new QueryNode[0];
 
@@ -68,9 +68,11 @@
     private int index = NONE;
 
     /**
-     * Creates a new <code>LocationStepQueryNode</code> that matches only
-     * the empty name (the repository root). The created location step
-     * uses only the child axis.
+     * Creates a new <code>LocationStepQueryNode</code> that matches only the
+     * empty name (the repository root). The created location step uses only the
+     * child axis.
+     *
+     * @param parent the parent of this query node.
      */
     protected LocationStepQueryNode(QueryNode parent) {
         super(parent);
@@ -132,7 +134,7 @@
         if (operands == null) {
             return EMPTY;
         } else {
-            return (QueryNode[]) operands.toArray(new QueryNode[operands.size()]);
+            return operands.toArray(new QueryNode[operands.size()]);
         }
     }
 

Modified: jackrabbit/trunk/jackrabbit-spi-commons/src/main/java/org/apache/jackrabbit/spi/commons/query/NAryQueryNode.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-spi-commons/src/main/java/org/apache/jackrabbit/spi/commons/query/NAryQueryNode.java?rev=791737&r1=791736&r2=791737&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit-spi-commons/src/main/java/org/apache/jackrabbit/spi/commons/query/NAryQueryNode.java (original)
+++ jackrabbit/trunk/jackrabbit-spi-commons/src/main/java/org/apache/jackrabbit/spi/commons/query/NAryQueryNode.java Tue Jul  7 07:46:51 2009
@@ -26,7 +26,7 @@
 /**
  * Defines an abstract query node for nodes that have child nodes.
  */
-public abstract class NAryQueryNode extends QueryNode {
+public abstract class NAryQueryNode<T extends QueryNode> extends QueryNode {
 
     /**
      * Empty result.
@@ -36,7 +36,7 @@
     /**
      * The list of operands / children
      */
-    protected List operands = null;
+    protected List<T> operands = null;
 
     /**
      * Creates a new <code>NAryQueryNode</code> with a reference to a parent
@@ -55,10 +55,10 @@
      * @param parent   the parent node.
      * @param operands child nodes of this <code>NAryQueryNode</code>.
      */
-    public NAryQueryNode(QueryNode parent, QueryNode[] operands) {
+    public NAryQueryNode(QueryNode parent, T[] operands) {
         super(parent);
         if (operands.length > 0) {
-            this.operands = new ArrayList();
+            this.operands = new ArrayList<T>();
             this.operands.addAll(Arrays.asList(operands));
         }
     }
@@ -68,9 +68,9 @@
      *
      * @param operand the child {@link QueryNode} to add.
      */
-    public void addOperand(QueryNode operand) {
+    public void addOperand(T operand) {
         if (operands == null) {
-            operands = new ArrayList();
+            operands = new ArrayList<T>();
         }
         operands.add(operand);
     }
@@ -83,7 +83,7 @@
      *  and has been removed; <code>false</code> if this node does not contain
      *  <code>operand</code> as a child node.
      */
-    public boolean removeOperand(QueryNode operand) {
+    public boolean removeOperand(T operand) {
         if (operands == null) {
             return false;
         }
@@ -108,7 +108,7 @@
         if (operands == null) {
             return new QueryNode[0];
         } else {
-            return (QueryNode[]) operands.toArray(new QueryNode[operands.size()]);
+            return operands.toArray(new QueryNode[operands.size()]);
         }
     }
 
@@ -131,16 +131,16 @@
      * @param visitor the visitor to call back.
      * @param data    arbitrary data for the visitor.
      * @return the return values of the <code>visitor.visit()</code> calls.
-     * @throws RepositoryException
+     * @throws RepositoryException if an error occurs.
      */
     public Object[] acceptOperands(QueryNodeVisitor visitor, Object data) throws RepositoryException {
         if (operands == null) {
             return EMPTY;
         }
 
-        List result = new ArrayList(operands.size());
-        for (int i = 0; i < operands.size(); i++) {
-            Object r = ((QueryNode) operands.get(i)).accept(visitor, data);
+        List<Object> result = new ArrayList<Object>(operands.size());
+        for (T operand : operands) {
+            Object r = operand.accept(visitor, data);
             if (r != null) {
                 result.add(r);
             }
@@ -166,9 +166,8 @@
         if (operands == null) {
             return false;
         }
-        for (Iterator iter = operands.iterator(); iter.hasNext();) {
-            QueryNode queryNode = (QueryNode) iter.next();
-            if (queryNode.needsSystemTree()) {
+        for (T operand : operands) {
+            if (operand.needsSystemTree()) {
                 return true;
             }
         }

Modified: jackrabbit/trunk/jackrabbit-spi-commons/src/main/java/org/apache/jackrabbit/spi/commons/query/NotQueryNode.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-spi-commons/src/main/java/org/apache/jackrabbit/spi/commons/query/NotQueryNode.java?rev=791737&r1=791736&r2=791737&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit-spi-commons/src/main/java/org/apache/jackrabbit/spi/commons/query/NotQueryNode.java (original)
+++ jackrabbit/trunk/jackrabbit-spi-commons/src/main/java/org/apache/jackrabbit/spi/commons/query/NotQueryNode.java Tue Jul  7 07:46:51 2009
@@ -21,7 +21,7 @@
 /**
  * Implements a query node that defines a not operation on the child query.
  */
-public class NotQueryNode extends NAryQueryNode {
+public class NotQueryNode extends NAryQueryNode<QueryNode> {
 
     /**
      * Creates a new <code>NotQueryNode</code> instance.

Modified: jackrabbit/trunk/jackrabbit-spi-commons/src/main/java/org/apache/jackrabbit/spi/commons/query/OrQueryNode.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-spi-commons/src/main/java/org/apache/jackrabbit/spi/commons/query/OrQueryNode.java?rev=791737&r1=791736&r2=791737&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit-spi-commons/src/main/java/org/apache/jackrabbit/spi/commons/query/OrQueryNode.java (original)
+++ jackrabbit/trunk/jackrabbit-spi-commons/src/main/java/org/apache/jackrabbit/spi/commons/query/OrQueryNode.java Tue Jul  7 07:46:51 2009
@@ -22,7 +22,7 @@
  * Implements a query node that defines an OR operation between arbitrary
  * other {@link QueryNode}s.
  */
-public class OrQueryNode extends NAryQueryNode {
+public class OrQueryNode extends NAryQueryNode<QueryNode> {
 
     /**
      * Creates a new <code>OrQueryNode</code> with a <code>parent</code>

Modified: jackrabbit/trunk/jackrabbit-spi-commons/src/main/java/org/apache/jackrabbit/spi/commons/query/PathQueryNode.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-spi-commons/src/main/java/org/apache/jackrabbit/spi/commons/query/PathQueryNode.java?rev=791737&r1=791736&r2=791737&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit-spi-commons/src/main/java/org/apache/jackrabbit/spi/commons/query/PathQueryNode.java (original)
+++ jackrabbit/trunk/jackrabbit-spi-commons/src/main/java/org/apache/jackrabbit/spi/commons/query/PathQueryNode.java Tue Jul  7 07:46:51 2009
@@ -26,7 +26,7 @@
 /**
  * Implements a query node that defines a path restriction.
  */
-public class PathQueryNode extends NAryQueryNode {
+public class PathQueryNode extends NAryQueryNode<LocationStepQueryNode> {
 
     /**
      * Flag indicating whether this path is absolute.
@@ -101,7 +101,7 @@
         if (operands == null) {
             return EMPTY;
         } else {
-            return (LocationStepQueryNode[]) operands.toArray(new LocationStepQueryNode[operands.size()]);
+            return operands.toArray(new LocationStepQueryNode[operands.size()]);
         }
     }
 

Modified: jackrabbit/trunk/jackrabbit-spi-commons/src/main/java/org/apache/jackrabbit/spi/commons/query/QueryTreeDump.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-spi-commons/src/main/java/org/apache/jackrabbit/spi/commons/query/QueryTreeDump.java?rev=791737&r1=791736&r2=791737&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit-spi-commons/src/main/java/org/apache/jackrabbit/spi/commons/query/QueryTreeDump.java (original)
+++ jackrabbit/trunk/jackrabbit-spi-commons/src/main/java/org/apache/jackrabbit/spi/commons/query/QueryTreeDump.java Tue Jul  7 07:46:51 2009
@@ -205,14 +205,14 @@
         buffer.append(PADDING, 0, indent);
         buffer.append("+ RelationQueryNode: Op: ");
         buffer.append(QueryConstants.OPERATION_NAMES.getName(node.getOperation()));
-        buffer.append(" Prop=");
-        Path relPath = node.getRelativePath();
+        buffer.append(" Prop=[");
+        PathQueryNode relPath = node.getRelativePath();
         if (relPath == null) {
             buffer.append(relPath);
         } else {
-            appendPath(relPath, buffer);
+            visit(relPath, buffer);
         }
-        buffer.append(" Type=").append(QueryConstants.TYPE_NAMES.getName(node.getValueType()));
+        buffer.append("] Type=").append(QueryConstants.TYPE_NAMES.getName(node.getValueType()));
         if (node.getValueType() == QueryConstants.TYPE_DATE) {
             buffer.append(" Value=").append(node.getDateValue());
         } else if (node.getValueType() == QueryConstants.TYPE_DOUBLE) {

Modified: jackrabbit/trunk/jackrabbit-spi-commons/src/main/java/org/apache/jackrabbit/spi/commons/query/RelationQueryNode.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-spi-commons/src/main/java/org/apache/jackrabbit/spi/commons/query/RelationQueryNode.java?rev=791737&r1=791736&r2=791737&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit-spi-commons/src/main/java/org/apache/jackrabbit/spi/commons/query/RelationQueryNode.java (original)
+++ jackrabbit/trunk/jackrabbit-spi-commons/src/main/java/org/apache/jackrabbit/spi/commons/query/RelationQueryNode.java Tue Jul  7 07:46:51 2009
@@ -22,14 +22,12 @@
 
 import org.apache.jackrabbit.spi.Name;
 import org.apache.jackrabbit.spi.Path;
-import org.apache.jackrabbit.spi.commons.conversion.MalformedPathException;
+import org.apache.jackrabbit.spi.Path.Element;
 import org.apache.jackrabbit.spi.commons.name.NameFactoryImpl;
-import org.apache.jackrabbit.spi.commons.name.PathBuilder;
-
 /**
  * Implements a query node that defines property value relation.
  */
-public class RelationQueryNode extends NAryQueryNode implements QueryConstants {
+public class RelationQueryNode extends NAryQueryNode<QueryNode> implements QueryConstants {
 
     /**
      * Acts as an syntetic placeholder for a location step that matches any
@@ -41,7 +39,7 @@
     /**
      * The relative path to the property.
      */
-    private Path relPath;
+    private PathQueryNode relPath;
 
     /**
      * If <code>true</code> this relation query node contains a value preceded
@@ -90,15 +88,23 @@
      */
     private int type;
 
+    private final QueryNodeFactory factory;
+
     /**
      * Creates a new <code>RelationQueryNode</code> without a type nor value
      * assigned.
      *
-     * @param parent the parent node for this query node.
-     */
-    protected RelationQueryNode(QueryNode parent, int operation) {
+     * @param parent    the parent node for this query node.
+     * @param operation the operation.
+     * @param factory   the query node factory.
+     */
+    protected RelationQueryNode(QueryNode parent,
+                                int operation,
+                                QueryNodeFactory factory) {
         super(parent);
         this.operation = operation;
+        this.factory = factory;
+        this.relPath = factory.createPathQueryNode(this);
     }
 
     /**
@@ -138,38 +144,9 @@
     }
 
     /**
-     * Returns the name of the property in this relation query node. Please
-     * note that this method does not return the full relative path that
-     * reference the property to match, but only the name of the final name
-     * element of the path returned by {@link #getRelativePath()}.
-     *
-     * @return the name of the property in this relation query node.
-     * @deprecated Use {@link #getRelativePath()} instead.
-     */
-    public Name getProperty() {
-        return relPath == null ? null : relPath.getNameElement().getName();
-    }
-
-    /**
-     * Sets a new property name for this relation query node.
-     *
-     * @param name the new property name.
-     * @deprecated Use {@link #setRelativePath(Path)} instead.
-     */
-    public void setProperty(Name name) {
-        PathBuilder builder = new PathBuilder();
-        builder.addLast(name);
-        try {
-            this.relPath = builder.getPath();
-        } catch (MalformedPathException e) {
-            // path is always valid
-        }
-    }
-
-    /**
      * @return the relative path that references the property in this relation.
      */
-    public Path getRelativePath() {
+    public PathQueryNode getRelativePath() {
         return relPath;
     }
 
@@ -180,10 +157,14 @@
      * @throws IllegalArgumentException if <code>relPath</code> is absolute.
      */
     public void setRelativePath(Path relPath) {
-        if (relPath != null && relPath.isAbsolute()) {
+        if (relPath.isAbsolute()) {
             throw new IllegalArgumentException("relPath must be relative");
         }
-        this.relPath = relPath;
+        
+        Element[] elements = relPath.getElements();
+        for (Element element : elements) {
+            addPathElement(element);
+        }
     }
 
     /**
@@ -193,23 +174,9 @@
      * @param element the path element to append.
      */
     public void addPathElement(Path.Element element) {
-        PathBuilder builder = new PathBuilder();
-        if (relPath != null) {
-            builder.addAll(relPath.getElements());
-        }
-        builder.addLast(element);
-        try {
-            relPath = builder.getPath();
-        }
-        catch (MalformedPathException e) {
-            // path is always valid
-        }
-        // try to normalize the path
-        try {
-          relPath = relPath.getNormalizedPath();
-        } catch (RepositoryException e) {
-            // just keep the original in that case
-        }
+        LocationStepQueryNode step = factory.createLocationStepQueryNode(relPath);
+        step.setNameTest(element.getName());
+        relPath.addPathStep(step);
     }
 
     /**

Modified: jackrabbit/trunk/jackrabbit-spi-commons/src/main/java/org/apache/jackrabbit/spi/commons/query/sql/QueryFormat.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-spi-commons/src/main/java/org/apache/jackrabbit/spi/commons/query/sql/QueryFormat.java?rev=791737&r1=791736&r2=791737&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit-spi-commons/src/main/java/org/apache/jackrabbit/spi/commons/query/sql/QueryFormat.java (original)
+++ jackrabbit/trunk/jackrabbit-spi-commons/src/main/java/org/apache/jackrabbit/spi/commons/query/sql/QueryFormat.java Tue Jul  7 07:46:51 2009
@@ -406,14 +406,14 @@
         StringBuffer sb = (StringBuffer) data;
         try {
             StringBuffer propName = new StringBuffer();
-            Path relPath = node.getRelativePath();
+            PathQueryNode relPath = node.getRelativePath();
             if (relPath == null) {
                 propName.append(".");
-            } else if (relPath.getLength() > 1) {
+            } else if (relPath.getPathSteps().length > 1) {
                 exceptions.add(new InvalidQueryException("Child axis not supported in SQL"));
                 return data;
             } else {
-                appendName(relPath.getNameElement().getName(), resolver, propName);
+                visit(relPath, data);
             }
             // surround name with property function
             node.acceptOperands(this, propName);

Modified: jackrabbit/trunk/jackrabbit-spi-commons/src/main/java/org/apache/jackrabbit/spi/commons/query/xpath/QueryFormat.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-spi-commons/src/main/java/org/apache/jackrabbit/spi/commons/query/xpath/QueryFormat.java?rev=791737&r1=791736&r2=791737&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit-spi-commons/src/main/java/org/apache/jackrabbit/spi/commons/query/xpath/QueryFormat.java (original)
+++ jackrabbit/trunk/jackrabbit-spi-commons/src/main/java/org/apache/jackrabbit/spi/commons/query/xpath/QueryFormat.java Tue Jul  7 07:46:51 2009
@@ -345,29 +345,13 @@
 
             StringBuffer propPath = new StringBuffer();
             // only encode if not position function
-            Path relPath = node.getRelativePath();
+            PathQueryNode relPath = node.getRelativePath();
             if (relPath == null) {
                 propPath.append(".");
-            } else if (relPath.getNameElement().getName().equals(XPathQueryBuilder.FN_POSITION_FULL)) {
+            } else if (relPath.getNumOperands() > 0 && relPath.getPathSteps()[0].getNameTest().equals(XPathQueryBuilder.FN_POSITION_FULL)) {
                 propPath.append(resolver.getJCRName(XPathQueryBuilder.FN_POSITION_FULL));
             } else {
-                Path.Element[] elements = relPath.getElements();
-                String slash = "";
-                for (int i = 0; i < elements.length; i++) {
-                    propPath.append(slash);
-                    slash = "/";
-                    if (i == elements.length - 1 && node.getOperation() != OPERATION_SIMILAR) {
-                        propPath.append("@");
-                    }
-                    if (elements[i].getName().equals(RelationQueryNode.STAR_NAME_TEST)) {
-                        propPath.append("*");
-                    } else {
-                        propPath.append(resolver.getJCRName(encode(elements[i].getName())));
-                    }
-                    if (elements[i].getIndex() != 0) {
-                        propPath.append("[").append(elements[i].getIndex()).append("]");
-                    }
-                }
+                visit(relPath, data);
             }
 
             // surround name with property function

Modified: jackrabbit/trunk/jackrabbit-spi-commons/src/main/java/org/apache/jackrabbit/spi/commons/query/xpath/XPathQueryBuilder.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-spi-commons/src/main/java/org/apache/jackrabbit/spi/commons/query/xpath/XPathQueryBuilder.java?rev=791737&r1=791736&r2=791737&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit-spi-commons/src/main/java/org/apache/jackrabbit/spi/commons/query/xpath/XPathQueryBuilder.java (original)
+++ jackrabbit/trunk/jackrabbit-spi-commons/src/main/java/org/apache/jackrabbit/spi/commons/query/xpath/XPathQueryBuilder.java Tue Jul  7 07:46:51 2009
@@ -424,7 +424,10 @@
                         if (tmpRelPath == null) {
                             tmpRelPath = new PathBuilder();
                         }
-                        tmpRelPath.addLast(tmp.getRelativePath().getNameElement());
+                        PathQueryNode relPath = tmp.getRelativePath();
+                        LocationStepQueryNode[] steps = relPath.getPathSteps();
+                        
+                        tmpRelPath.addLast(steps[steps.length-1].getNameTest());
                     }
                 }
                 break;
@@ -545,7 +548,11 @@
                 }
                 break;
             case JJTDOTDOT:
-                exceptions.add(new InvalidQueryException("Parent axis is not supported"));
+                if (queryNode instanceof LocationStepQueryNode) {
+                    ((LocationStepQueryNode) queryNode).setNameTest(PATH_FACTORY.getParentElement().getName());
+                } else {
+                    ((RelationQueryNode) queryNode).addPathElement(PATH_FACTORY.getParentElement());
+                }
                 break;
             default:
                 // per default traverse
@@ -943,47 +950,12 @@
                     }
                     if (queryNode.getType() == QueryNode.TYPE_PATH) {
                         PathQueryNode pathNode = (PathQueryNode) queryNode;
-                        DerefQueryNode derefNode = factory.createDerefQueryNode(pathNode, null, false);
-
-                        // assign property name
-                        node.jjtGetChild(1).jjtAccept(this, derefNode);
-                        // check property name
-                        if (derefNode.getRefProperty() == null) {
-                            exceptions.add(new InvalidQueryException("Wrong first argument type for jcr:deref"));
-                        }
-
-                        SimpleNode literal = (SimpleNode) node.jjtGetChild(2).jjtGetChild(0);
-                        if (literal.getId() == JJTSTRINGLITERAL) {
-                            String value = literal.getValue();
-                            // strip quotes
-                            value = value.substring(1, value.length() - 1);
-                            if (!value.equals("*")) {
-                                Name name = null;
-                                try {
-                                    name = decode(resolver.getQName(value));
-                                } catch (NameException e) {
-                                    exceptions.add(new InvalidQueryException("Illegal name: " + value));
-                                }
-                                derefNode.setNameTest(name);
-                            }
-                        } else {
-                            exceptions.add(new InvalidQueryException("Second argument for jcr:deref must be a String"));
-                        }
-
-                        // check if descendant
-                        if (!descendant) {
-                            Node p = node.jjtGetParent();
-                            for (int i = 0; i < p.jjtGetNumChildren(); i++) {
-                                SimpleNode c = (SimpleNode) p.jjtGetChild(i);
-                                if (c == node) {
-                                    break;
-                                }
-                                descendant = (c.getId() == JJTSLASHSLASH
-                                        || c.getId() == JJTROOTDESCENDANTS);
-                            }
-                        }
-                        derefNode.setIncludeDescendants(descendant);
-                        pathNode.addPathStep(derefNode);
+                        
+                        pathNode.addPathStep(createDerefQueryNode(node, descendant, pathNode));
+                    } else if (queryNode.getType() == QueryNode.TYPE_RELATION) {
+                        RelationQueryNode relNode = (RelationQueryNode) queryNode;
+                        DerefQueryNode deref = createDerefQueryNode(node, descendant, relNode.getRelativePath());
+                        relNode.getRelativePath().addPathStep(deref);
                     } else {
                         exceptions.add(new InvalidQueryException("Unsupported location for jcr:deref()"));
                     }
@@ -1103,6 +1075,51 @@
         return queryNode;
     }
 
+    private DerefQueryNode createDerefQueryNode(SimpleNode node, boolean descendant, QueryNode pathNode)
+        throws NamespaceException {
+        DerefQueryNode derefNode = factory.createDerefQueryNode(pathNode, null, false);
+
+        // assign property name
+        node.jjtGetChild(1).jjtAccept(this, derefNode);
+        // check property name
+        if (derefNode.getRefProperty() == null) {
+            exceptions.add(new InvalidQueryException("Wrong first argument type for jcr:deref"));
+        }
+
+        SimpleNode literal = (SimpleNode) node.jjtGetChild(2).jjtGetChild(0);
+        if (literal.getId() == JJTSTRINGLITERAL) {
+            String value = literal.getValue();
+            // strip quotes
+            value = value.substring(1, value.length() - 1);
+            if (!value.equals("*")) {
+                Name name = null;
+                try {
+                    name = decode(resolver.getQName(value));
+                } catch (NameException e) {
+                    exceptions.add(new InvalidQueryException("Illegal name: " + value));
+                }
+                derefNode.setNameTest(name);
+            }
+        } else {
+            exceptions.add(new InvalidQueryException("Second argument for jcr:deref must be a String"));
+        }
+
+        // check if descendant
+        if (!descendant) {
+            Node p = node.jjtGetParent();
+            for (int i = 0; i < p.jjtGetNumChildren(); i++) {
+                SimpleNode c = (SimpleNode) p.jjtGetChild(i);
+                if (c == node) {
+                    break;
+                }
+                descendant = (c.getId() == JJTSLASHSLASH
+                        || c.getId() == JJTROOTDESCENDANTS);
+            }
+        }
+        derefNode.setIncludeDescendants(descendant);
+        return derefNode;
+    }
+
     private OrderQueryNode.OrderSpec createOrderSpec(SimpleNode node,
                                                      OrderQueryNode queryNode) {
         SimpleNode child = (SimpleNode) node.jjtGetChild(0);



Mime
View raw message