Index: src/main/java/org/apache/jackrabbit/spi/commons/query/OrderQueryNode.java
===================================================================
--- src/main/java/org/apache/jackrabbit/spi/commons/query/OrderQueryNode.java (revision 744052)
+++ src/main/java/org/apache/jackrabbit/spi/commons/query/OrderQueryNode.java (working copy)
@@ -22,6 +22,9 @@
import javax.jcr.RepositoryException;
import org.apache.jackrabbit.spi.Name;
+import org.apache.jackrabbit.spi.Path;
+import org.apache.jackrabbit.spi.commons.name.PathBuilder;
+import org.apache.jackrabbit.spi.commons.conversion.MalformedPathException;
/**
* Implements a query node that defines the order of nodes according to the
@@ -59,8 +62,20 @@
* @param property the name of the property.
* @param ascending if true values of this properties are
* ordered ascending; descending if false.
+ * @deprecated use {@link #addOrderSpec(Path , boolean)} instead.
*/
public void addOrderSpec(Name property, boolean ascending) {
+ addOrderSpec(createPath(property), ascending);
+ }
+
+ /**
+ * Adds an order specification to this query node.
+ *
+ * @param property the relative path of the property.
+ * @param ascending if true values of this properties are
+ * ordered ascending; descending if false.
+ */
+ public void addOrderSpec(Path property, boolean ascending) {
specs.add(new OrderSpec(property, ascending));
}
@@ -124,9 +139,9 @@
public static final class OrderSpec {
/**
- * The name of the property
+ * The relative path to of the property
*/
- private final Name property;
+ private final Path property;
/**
* If true this property is orderd ascending
@@ -139,8 +154,20 @@
* @param property the name of the property.
* @param ascending if true the property is ordered
* ascending, otherwise descending.
+ * @deprecated use {@link OrderSpec#OrderSpec(Path, boolean)} instead.
*/
public OrderSpec(Name property, boolean ascending) {
+ this(createPath(property), ascending);
+ }
+
+ /**
+ * Creates a new OrderSpec for property.
+ *
+ * @param property the relative path of the property.
+ * @param ascending if true the property is ordered
+ * ascending, otherwise descending.
+ */
+ public OrderSpec(Path property, boolean ascending) {
this.property = property;
this.ascending = ascending;
}
@@ -149,8 +176,18 @@
* Returns the name of the property.
*
* @return the name of the property.
+ * @deprecated use {@link #getPropertyPath()} instead.
*/
public Name getProperty() {
+ return property.getNameElement().getName();
+ }
+
+ /**
+ * Returns the relative path of the property.
+ *
+ * @return the relative path of the property.
+ */
+ public Path getPropertyPath() {
return property;
}
@@ -199,4 +236,22 @@
return false;
}
+ //--------------------------------< internal >------------------------------
+
+ /**
+ * Creates a path with a single element out of the given name.
+ *
+ * @param name the name to create the path from.
+ * @return a path with a single element.
+ */
+ private static Path createPath(Name name) {
+ try {
+ PathBuilder builder = new PathBuilder();
+ builder.addLast(name);
+ return builder.getPath();
+ } catch (MalformedPathException e) {
+ // never happens, we just added an element
+ throw new InternalError();
+ }
+ }
}
Index: src/main/java/org/apache/jackrabbit/spi/commons/query/QueryTreeDump.java
===================================================================
--- src/main/java/org/apache/jackrabbit/spi/commons/query/QueryTreeDump.java (revision 744052)
+++ src/main/java/org/apache/jackrabbit/spi/commons/query/QueryTreeDump.java (working copy)
@@ -210,16 +210,7 @@
if (relPath == null) {
buffer.append(relPath);
} else {
- Path.Element[] elements = relPath.getElements();
- String slash = "";
- for (int i = 0; i < elements.length; i++) {
- buffer.append(slash);
- slash = "/";
- if (i == elements.length - 1) {
- buffer.append("@");
- }
- buffer.append(elements[i]);
- }
+ appendPath(relPath, buffer);
}
buffer.append(" Type=").append(QueryConstants.TYPE_NAMES.getName(node.getValueType()));
if (node.getValueType() == QueryConstants.TYPE_DATE) {
@@ -249,7 +240,8 @@
OrderQueryNode.OrderSpec[] specs = node.getOrderSpecs();
for (int i = 0; i < specs.length; i++) {
buffer.append(PADDING, 0, indent);
- buffer.append(" ").append(specs[i].getProperty());
+ buffer.append(" ");
+ appendPath(specs[i].getPropertyPath(), buffer);
buffer.append(" asc=").append(specs[i].isAscending());
buffer.append("\n");
}
@@ -301,4 +293,21 @@
}
indent -= 2;
}
+
+ /**
+ * Appends the relative path to the buffer using '/' as the
+ * delimiter for path elements.
+ *
+ * @param relPath a relative path.
+ * @param buffer the buffer where to append the path.
+ */
+ private static void appendPath(Path relPath, StringBuffer buffer) {
+ Path.Element[] elements = relPath.getElements();
+ String slash = "";
+ for (int i = 0; i < elements.length; i++) {
+ buffer.append(slash);
+ slash = "/";
+ buffer.append(elements[i]);
+ }
+ }
}
Index: src/main/java/org/apache/jackrabbit/spi/commons/query/sql/QueryFormat.java
===================================================================
--- src/main/java/org/apache/jackrabbit/spi/commons/query/sql/QueryFormat.java (revision 744052)
+++ src/main/java/org/apache/jackrabbit/spi/commons/query/sql/QueryFormat.java (working copy)
@@ -484,7 +484,12 @@
String comma = "";
for (int i = 0; i < specs.length; i++) {
sb.append(comma).append(" ");
- appendName(specs[i].getProperty(), resolver, sb);
+ Path propPath = specs[i].getPropertyPath();
+ if (propPath.getLength() > 1) {
+ exceptions.add(new InvalidQueryException("SQL does not support relative paths in order by clause"));
+ return sb;
+ }
+ appendName(propPath.getNameElement().getName(), resolver, sb);
if (!specs[i].isAscending()) {
sb.append(" DESC");
}
Index: src/main/java/org/apache/jackrabbit/spi/commons/query/xpath/QueryFormat.java
===================================================================
--- src/main/java/org/apache/jackrabbit/spi/commons/query/xpath/QueryFormat.java (revision 744052)
+++ src/main/java/org/apache/jackrabbit/spi/commons/query/xpath/QueryFormat.java (working copy)
@@ -445,9 +445,21 @@
try {
for (int i = 0; i < specs.length; i++) {
sb.append(comma);
- Name prop = encode(specs[i].getProperty());
- sb.append(" @");
- sb.append(resolver.getJCRName(prop));
+ Path propPath = specs[i].getPropertyPath();
+ Path.Element[] elements = propPath.getElements();
+ sb.append(" ");
+ String slash = "";
+ for (int j = 0; j < elements.length; j++) {
+ sb.append(slash);
+ slash = "/";
+ Path.Element element = elements[j];
+ Name name = encode(element.getName());
+ if (j == elements.length - 1) {
+ // last
+ sb.append("@");
+ }
+ sb.append(resolver.getJCRName(name));
+ }
if (!specs[i].isAscending()) {
sb.append(" descending");
}
Index: src/main/java/org/apache/jackrabbit/spi/commons/query/xpath/XPathQueryBuilder.java
===================================================================
--- src/main/java/org/apache/jackrabbit/spi/commons/query/xpath/XPathQueryBuilder.java (revision 744052)
+++ src/main/java/org/apache/jackrabbit/spi/commons/query/xpath/XPathQueryBuilder.java (working copy)
@@ -563,17 +563,32 @@
* @param node a relation query node.
*/
private void applyRelativePath(RelationQueryNode node) {
- if (tmpRelPath != null) {
- try {
- Path relPath = tmpRelPath.getPath();
- for (int i = 0; i < relPath.getLength(); i++) {
- node.addPathElement(relPath.getElements()[i]);
- }
- } catch (MalformedPathException e) {
- // should never happen
+ Path relPath = getRelativePath();
+ if (relPath != null) {
+ for (int i = 0; i < relPath.getLength(); i++) {
+ node.addPathElement(relPath.getElements()[i]);
}
+ }
+ }
+
+ /**
+ * Returns {@link #tmpRelPath} or null if there is none set.
+ * When this method returns {@link #tmpRelPath} will have been set
+ * null.
+ *
+ * @return {@link #tmpRelPath}.
+ */
+ private Path getRelativePath() {
+ try {
+ if (tmpRelPath != null) {
+ return tmpRelPath.getPath();
+ }
+ } catch (MalformedPathException e) {
+ // should never happen
+ } finally {
tmpRelPath = null;
}
+ return null;
}
/**
@@ -1099,8 +1114,14 @@
// cut off left parenthesis at end
propName = propName.substring(0, propName.length() - 1);
}
+ PathBuilder builder = new PathBuilder();
Name name = decode(resolver.getQName(propName));
- spec = new OrderQueryNode.OrderSpec(name, true);
+ Path relPath = getRelativePath();
+ if (relPath != null) {
+ builder.addAll(relPath.getElements());
+ }
+ builder.addLast(name);
+ spec = new OrderQueryNode.OrderSpec(builder.getPath(), true);
queryNode.addOrderSpec(spec);
} catch (NameException e) {
exceptions.add(new InvalidQueryException("Illegal name: " + child.getValue()));
Index: src/test/java/org/apache/jackrabbit/spi/commons/conversion/DummyNamespaceResolver.java
===================================================================
--- src/test/java/org/apache/jackrabbit/spi/commons/conversion/DummyNamespaceResolver.java (revision 744052)
+++ src/test/java/org/apache/jackrabbit/spi/commons/conversion/DummyNamespaceResolver.java (working copy)
@@ -25,7 +25,7 @@
* maps each valid XML prefix string to the same string as the namespace URI
* and vice versa.
*/
-class DummyNamespaceResolver implements NamespaceResolver {
+public class DummyNamespaceResolver implements NamespaceResolver {
/**
* Returns the given prefix.
Index: src/test/java/org/apache/jackrabbit/spi/commons/query/xpath/XPathOrderByTest.java
===================================================================
--- src/test/java/org/apache/jackrabbit/spi/commons/query/xpath/XPathOrderByTest.java (revision 0)
+++ src/test/java/org/apache/jackrabbit/spi/commons/query/xpath/XPathOrderByTest.java (revision 0)
@@ -0,0 +1,100 @@
+/*
+ * 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.spi.commons.query.xpath;
+
+import java.util.Collections;
+
+import javax.jcr.query.Query;
+
+import org.apache.jackrabbit.spi.commons.conversion.NameResolver;
+import org.apache.jackrabbit.spi.commons.conversion.DefaultNamePathResolver;
+import org.apache.jackrabbit.spi.commons.conversion.DummyNamespaceResolver;
+import org.apache.jackrabbit.spi.commons.query.QueryNodeFactory;
+import org.apache.jackrabbit.spi.commons.query.DefaultQueryNodeFactory;
+import org.apache.jackrabbit.spi.commons.query.QueryRootNode;
+import org.apache.jackrabbit.spi.commons.query.QueryParser;
+import org.apache.jackrabbit.spi.commons.query.OrderQueryNode;
+import org.apache.jackrabbit.spi.Name;
+import org.apache.jackrabbit.spi.Path;
+
+import junit.framework.TestCase;
+
+/**
+ * XPathOrderByTest performs various tests related to parsing an
+ * XPath statement with order by.
+ */
+public class XPathOrderByTest extends TestCase {
+
+ private static final NameResolver RESOLVER = new DefaultNamePathResolver(
+ new DummyNamespaceResolver());
+
+ private static final QueryNodeFactory FACTORY = new DefaultQueryNodeFactory(Collections.EMPTY_LIST);
+
+ public void testSimpleOrderBy() throws Exception {
+ String stmt = "//* order by @bar";
+ QueryRootNode root = QueryParser.parse(stmt, Query.XPATH, RESOLVER, FACTORY);
+ OrderQueryNode.OrderSpec[] specs = root.getOrderNode().getOrderSpecs();
+ assertEquals(1, specs.length);
+ assertTrue(specs[0].isAscending());
+ checkName(Name.NS_DEFAULT_URI, "bar", specs[0].getProperty());
+ Path propPath = specs[0].getPropertyPath();
+ assertEquals(1, propPath.getLength());
+ checkName(Name.NS_DEFAULT_URI, "bar", propPath.getNameElement().getName());
+ }
+
+ public void testAscending() throws Exception {
+ String stmt = "//* order by @bar ascending";
+ QueryRootNode root = QueryParser.parse(stmt, Query.XPATH, RESOLVER, FACTORY);
+ OrderQueryNode.OrderSpec[] specs = root.getOrderNode().getOrderSpecs();
+ assertEquals(1, specs.length);
+ assertTrue(specs[0].isAscending());
+ }
+
+ public void testDescending() throws Exception {
+ String stmt = "//* order by @bar descending";
+ QueryRootNode root = QueryParser.parse(stmt, Query.XPATH, RESOLVER, FACTORY);
+ OrderQueryNode.OrderSpec[] specs = root.getOrderNode().getOrderSpecs();
+ assertEquals(1, specs.length);
+ assertFalse(specs[0].isAscending());
+ }
+
+ public void testChildAxis() throws Exception {
+ String stmt = "//* order by foo_x0020_bar/@bar";
+ QueryRootNode root = QueryParser.parse(stmt, Query.XPATH, RESOLVER, FACTORY);
+ assertEquals(1, root.getOrderNode().getOrderSpecs().length);
+ OrderQueryNode.OrderSpec[] specs = root.getOrderNode().getOrderSpecs();
+ assertEquals(1, specs.length);
+ assertTrue(specs[0].isAscending());
+ checkName(Name.NS_DEFAULT_URI, "bar", specs[0].getProperty());
+ Path propPath = specs[0].getPropertyPath();
+ Path.Element[] elements = propPath.getElements();
+ assertEquals(2, elements.length);
+ checkName(Name.NS_DEFAULT_URI, "foo bar", elements[0].getName());
+ checkName(Name.NS_DEFAULT_URI, "bar", elements[1].getName());
+ }
+
+ public void testRoundTrip() throws Exception {
+ String stmt = "//* order by foo_x0020_bar/@bar";
+ QueryRootNode root = QueryParser.parse(stmt, Query.XPATH, RESOLVER, FACTORY);
+ assertEquals(stmt, QueryFormat.toString(root, RESOLVER));
+ }
+
+ private void checkName(String uri, String localName, Name name) {
+ assertEquals(uri, name.getNamespaceURI());
+ assertEquals(localName, name.getLocalName());
+ }
+}
Property changes on: src\test\java\org\apache\jackrabbit\spi\commons\query\xpath\XPathOrderByTest.java
___________________________________________________________________
Added: svn:eol-style
+ native