Index: jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/lucene/DerefQuery.java
===================================================================
--- jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/lucene/DerefQuery.java (revision 790821)
+++ jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/lucene/DerefQuery.java (working copy)
@@ -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();
Index: jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/lucene/LuceneQueryBuilder.java
===================================================================
--- jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/lucene/LuceneQueryBuilder.java (revision 790821)
+++ jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/lucene/LuceneQueryBuilder.java (working copy)
@@ -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,41 +867,115 @@
}
}
- 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;
+ 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 {
+ if (step instanceof LocationStepQueryNode) {
+ query = new ParentAxisQuery(query, name, indexFormatVersion, nsMappings);
} else {
- // otherwise the query can be used as is
+ throw new UnsupportedOperationException();
}
- } else {
- query = new ParentAxisQuery(query, name,
- indexFormatVersion, nsMappings);
}
}
// 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;
}
Index: jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/lucene/ParentAxisQuery.java
===================================================================
--- jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/lucene/ParentAxisQuery.java (revision 790821)
+++ jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/lucene/ParentAxisQuery.java (working copy)
@@ -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 >-------------------------------
Index: jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/lucene/PredicateDerefQuery.java
===================================================================
--- jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/lucene/PredicateDerefQuery.java (revision 0)
+++ jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/lucene/PredicateDerefQuery.java (revision 0)
@@ -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 Query 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 null 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 DerefQuery based on a context
+ * 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 null 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 Weight instance for this query.
+ *
+ * @param searcher the Searcher instance to use.
+ * @return a DerefWeight.
+ */
+ 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 Weight implementation for this DerefQuery.
+ */
+ private class DerefWeight implements Weight {
+
+ /**
+ * The searcher in use
+ */
+ private final Searcher searcher;
+
+ /**
+ * Creates a new DerefWeight instance using
+ * searcher.
+ *
+ * @param searcher a Searcher instance.
+ */
+ private DerefWeight(Searcher searcher) {
+ this.searcher = searcher;
+ }
+
+ /**
+ * Returns this DerefQuery.
+ *
+ * @return this DerefQuery.
+ */
+ 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 DerefQuery.
+ *
+ * @param reader a reader for accessing the index.
+ * @return a DerefScorer.
+ * @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 Scorer for this DerefQuery.
+ */
+ private class DerefScorer extends Scorer {
+
+ /**
+ * An IndexReader 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 DerefScorer.
+ *
+ * @param similarity the Similarity 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 UnsupportedOperationException.
+ */
+ 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);
+ }
+ }
+ }
+ }
+}
Property changes on: jackrabbit-core\src\main\java\org\apache\jackrabbit\core\query\lucene\PredicateDerefQuery.java
___________________________________________________________________
Added: svn:eol-style
+ native
Index: jackrabbit-core/src/test/java/org/apache/jackrabbit/core/query/DerefTest.java
===================================================================
--- jackrabbit-core/src/test/java/org/apache/jackrabbit/core/query/DerefTest.java (revision 790821)
+++ jackrabbit-core/src/test/java/org/apache/jackrabbit/core/query/DerefTest.java (working copy)
@@ -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.
*/
Index: jackrabbit-core/src/test/java/org/apache/jackrabbit/core/query/ParentNodeTest.java
===================================================================
--- jackrabbit-core/src/test/java/org/apache/jackrabbit/core/query/ParentNodeTest.java (revision 0)
+++ jackrabbit-core/src/test/java/org/apache/jackrabbit/core/query/ParentNodeTest.java (revision 0)
@@ -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;
+
+/**
+ * QueryResultTest tests various methods on the
+ * NodeIterator returned by a QueryResult.
+ */
+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());
+ }
+}
Property changes on: jackrabbit-core\src\test\java\org\apache\jackrabbit\core\query\ParentNodeTest.java
___________________________________________________________________
Added: svn:eol-style
+ native
Index: jackrabbit-core/src/test/java/org/apache/jackrabbit/core/query/TestAll.java
===================================================================
--- jackrabbit-core/src/test/java/org/apache/jackrabbit/core/query/TestAll.java (revision 790821)
+++ jackrabbit-core/src/test/java/org/apache/jackrabbit/core/query/TestAll.java (working copy)
@@ -60,6 +60,7 @@
suite.addTestSuite(IndexFormatVersionTest.class);
suite.addTestSuite(IndexingRuleTest.class);
suite.addTestSuite(ShareableNodeTest.class);
+ suite.addTestSuite(ParentNodeTest.class);
return suite;
}
Index: jackrabbit-spi-commons/src/main/java/org/apache/jackrabbit/spi/commons/query/AndQueryNode.java
===================================================================
--- jackrabbit-spi-commons/src/main/java/org/apache/jackrabbit/spi/commons/query/AndQueryNode.java (revision 790821)
+++ jackrabbit-spi-commons/src/main/java/org/apache/jackrabbit/spi/commons/query/AndQueryNode.java (working copy)
@@ -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 {
/**
* Creates a new AndQueryNode with a parent
Index: jackrabbit-spi-commons/src/main/java/org/apache/jackrabbit/spi/commons/query/DefaultQueryNodeFactory.java
===================================================================
--- jackrabbit-spi-commons/src/main/java/org/apache/jackrabbit/spi/commons/query/DefaultQueryNodeFactory.java (revision 790821)
+++ jackrabbit-spi-commons/src/main/java/org/apache/jackrabbit/spi/commons/query/DefaultQueryNodeFactory.java (working copy)
@@ -89,7 +89,7 @@
*/
public RelationQueryNode createRelationQueryNode(QueryNode parent,
int operation) {
- return new RelationQueryNode(parent, operation);
+ return new RelationQueryNode(parent, operation, this);
}
/**
Index: jackrabbit-spi-commons/src/main/java/org/apache/jackrabbit/spi/commons/query/LocationStepQueryNode.java
===================================================================
--- jackrabbit-spi-commons/src/main/java/org/apache/jackrabbit/spi/commons/query/LocationStepQueryNode.java (revision 790821)
+++ jackrabbit-spi-commons/src/main/java/org/apache/jackrabbit/spi/commons/query/LocationStepQueryNode.java (working copy)
@@ -32,7 +32,7 @@
* / -> descendants = false, nameTest = ""
*
*/
-public class LocationStepQueryNode extends NAryQueryNode {
+public class LocationStepQueryNode extends NAryQueryNode {
/** 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 QueryNode 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 LocationStepQueryNode that matches only
- * the empty name (the repository root). The created location step
- * uses only the child axis.
+ * Creates a new LocationStepQueryNode 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()]);
}
}
Index: jackrabbit-spi-commons/src/main/java/org/apache/jackrabbit/spi/commons/query/NAryQueryNode.java
===================================================================
--- jackrabbit-spi-commons/src/main/java/org/apache/jackrabbit/spi/commons/query/NAryQueryNode.java (revision 790821)
+++ jackrabbit-spi-commons/src/main/java/org/apache/jackrabbit/spi/commons/query/NAryQueryNode.java (working copy)
@@ -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 extends QueryNode {
/**
* Empty result.
@@ -36,7 +36,7 @@
/**
* The list of operands / children
*/
- protected List operands = null;
+ protected List operands = null;
/**
* Creates a new NAryQueryNode with a reference to a parent
@@ -55,10 +55,10 @@
* @param parent the parent node.
* @param operands child nodes of this NAryQueryNode.
*/
- 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();
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();
}
operands.add(operand);
}
@@ -83,7 +83,7 @@
* and has been removed; false if this node does not contain
* operand 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 visitor.visit() 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