You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@jackrabbit.apache.org by mr...@apache.org on 2005/01/27 12:13:35 UTC

svn commit: r126598 - in incubator/jackrabbit/trunk: applications/test src/grammar/sql src/grammar/xpath src/java/org/apache/jackrabbit/core/search src/java/org/apache/jackrabbit/core/search/lucene src/java/org/apache/jackrabbit/core/search/sql src/java/org/apache/jackrabbit/core/search/xpath

Author: mreutegg
Date: Thu Jan 27 03:13:33 2005
New Revision: 126598

URL: http://svn.apache.org/viewcvs?view=rev&rev=126598
Log:
Implement 'order by' syntax for XPath and SQL. Actual ordering on the query result is not yet done.
Modified:
   incubator/jackrabbit/trunk/applications/test/   (props changed)
   incubator/jackrabbit/trunk/src/grammar/sql/JCRSQL.jjt
   incubator/jackrabbit/trunk/src/grammar/xpath/strip.xsl
   incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/search/OrderQueryNode.java
   incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/search/lucene/QueryImpl.java
   incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/search/sql/   (props changed)
   incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/search/sql/DefaultParserVisitor.java
   incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/search/sql/JCRSQLQueryBuilder.java
   incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/search/sql/QueryFormat.java
   incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/search/xpath/QueryFormat.java
   incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/search/xpath/XPathQueryBuilder.java

Modified: incubator/jackrabbit/trunk/src/grammar/sql/JCRSQL.jjt
Url: http://svn.apache.org/viewcvs/incubator/jackrabbit/trunk/src/grammar/sql/JCRSQL.jjt?view=diff&rev=126598&p1=incubator/jackrabbit/trunk/src/grammar/sql/JCRSQL.jjt&r1=126597&p2=incubator/jackrabbit/trunk/src/grammar/sql/JCRSQL.jjt&r2=126598
==============================================================================
--- incubator/jackrabbit/trunk/src/grammar/sql/JCRSQL.jjt	(original)
+++ incubator/jackrabbit/trunk/src/grammar/sql/JCRSQL.jjt	Thu Jan 27 03:13:33 2005
@@ -93,7 +93,9 @@
 | < OR: "OR" >
 | < IS: "IS" >
 | < AND: "AND" >
+| < ASC: "ASC" >
 | < NOT: "NOT" >
+| < DESC: "DESC" >
 | < LIKE: "LIKE" >
 | < NULL: "NULL" >
 | < FROM: "FROM" >
@@ -551,5 +553,23 @@
 void OrderByClause() :
 {}
 {
-  <ORDER> <BY> Identifier() (<COMMA> Identifier())*
+  <ORDER> <BY> OrderSpec() (<COMMA> OrderSpec())*
 }
+
+void OrderSpec() :
+{}
+{
+  Identifier() (AscendingOrderSpec() | DescendingOrderSpec())?
+}
+
+void AscendingOrderSpec() :
+{}
+{
+  <ASC>
+}
+
+void DescendingOrderSpec() :
+{}
+{
+  <DESC>
+}
\ No newline at end of file

Modified: incubator/jackrabbit/trunk/src/grammar/xpath/strip.xsl
Url: http://svn.apache.org/viewcvs/incubator/jackrabbit/trunk/src/grammar/xpath/strip.xsl?view=diff&rev=126598&p1=incubator/jackrabbit/trunk/src/grammar/xpath/strip.xsl&r1=126597&p2=incubator/jackrabbit/trunk/src/grammar/xpath/strip.xsl&r2=126598
==============================================================================
--- incubator/jackrabbit/trunk/src/grammar/xpath/strip.xsl	(original)
+++ incubator/jackrabbit/trunk/src/grammar/xpath/strip.xsl	Thu Jan 27 03:13:33 2005
@@ -14,7 +14,7 @@
 
 <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
   version="1.0" xmlns:g="http://www.w3.org/2001/03/XPath/grammar">
-  <xsl:param name="spec1" select="'xpath'"/>
+  <xsl:param name="spec1" select="'xquery'"/>
   <xsl:param name="spec2" select="'dummy'"/>
   <xsl:param name="spec3" select="'dummy'"/>
 

Modified: incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/search/OrderQueryNode.java
Url: http://svn.apache.org/viewcvs/incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/search/OrderQueryNode.java?view=diff&rev=126598&p1=incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/search/OrderQueryNode.java&r1=126597&p2=incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/search/OrderQueryNode.java&r2=126598
==============================================================================
--- incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/search/OrderQueryNode.java	(original)
+++ incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/search/OrderQueryNode.java	Thu Jan 27 03:13:33 2005
@@ -18,6 +18,9 @@
 
 import org.apache.jackrabbit.core.QName;
 
+import java.util.List;
+import java.util.ArrayList;
+
 /**
  * Implements a query node that defines the order of nodes according to the
  * values of properties.
@@ -25,33 +28,37 @@
 public class OrderQueryNode extends QueryNode {
 
     /**
-     * The name of the properties to order
-     */
-    private QName[] properties;
-
-    /**
-     * Array of flag indicating whether a node is ordered ascending or descending
+     * The order spects
      */
-    private boolean[] orderSpecs;
+    private List specs = new ArrayList();
 
     /**
      * Creates a new <code>OrderQueryNode</code> with a reference to a parent
      * node and sort properties.
      *
      * @param parent     the parent node of this query node.
-     * @param properties the names of the properties to sort the result nodes.
-     * @param orderSpecs if <code>true</code> a result node is orderd ascending;
-     *                   otherwise descending.
      */
-    public OrderQueryNode(QueryNode parent, QName[] properties, boolean[] orderSpecs) {
+    public OrderQueryNode(QueryNode parent) {
         super(parent);
-        if (properties.length != orderSpecs.length) {
-            throw new IllegalArgumentException("Number of propertes and orderSpecs must be the same");
-        }
-        this.properties = properties;
-        this.orderSpecs = orderSpecs;
     }
 
+    /**
+     * Adds an order specification to this query node.
+     * @param property the name of the property.
+     * @param ascending if <code>true</code> values of this properties are
+     *   ordered ascending; descending if <code>false</code>.
+     */
+    public void addOrderSpec(QName property, boolean ascending) {
+        specs.add(new OrderSpec(property, ascending));
+    }
+
+    /**
+     * Adds an order specification to this query node.
+     * @param spec the order spec.
+     */
+    public void addOrderSpec(OrderSpec spec) {
+        specs.add(spec);
+    }
 
     /**
      * @see QueryNode#accept(org.apache.jackrabbit.core.search.QueryNodeVisitor, java.lang.Object)
@@ -67,30 +74,73 @@
      *
      * @return the order spec for the property <code>i</code>.
      *
-     * @exception ArrayIndexOutOfBoundsException if there is no property with
+     * @exception IndexOutOfBoundsException if there is no property with
      * index <code>i</code>.
      */
     public boolean isAscending(int i) {
-        return orderSpecs[i];
+        return ((OrderSpec) specs.get(i)).ascending;
     }
 
     /**
-     * Returns a <code>QName</code> array that contains the name of the properties
-     * to sort the result nodes.
+     * Returns a <code>OrderSpec</code> array that contains order by
+     * specifications.
      *
-     * @return names of order properties.
+     * @return order by specs.
      */
-    public QName[] getOrderByProperties() {
-        return properties;
+    public OrderSpec[] getOrderSpecs() {
+        return (OrderSpec[]) specs.toArray(new OrderSpec[specs.size()]);
     }
 
+    //------------------< OrderSpec class >-------------------------------------
+
     /**
-     * Returns a boolean array that contains the sort order specification
-     * for each property returned by {@link #getOrderByProperties()}.
-     * @return the sort specification.
+     * Implements a single order specification. Contains a property name
+     * and whether it is ordered ascending or descending.
      */
-    public boolean[] getOrderBySpecs() {
-        return orderSpecs;
-    }
+    public static final class OrderSpec {
+
+        /** The name of the property */
+        private QName property;
+
+        /** If <code>true</code> this property is orderd ascending */
+        private boolean ascending;
+
+        /**
+         * Creates a new <code>OrderSpec</code> for <code>property</code>.
+         * @param property the name of the property.
+         * @param ascending if <code>true</code> the property is ordered
+         * ascending, otherwise descending.
+         */
+        public OrderSpec(QName property, boolean ascending) {
+            this.property = property;
+            this.ascending = ascending;
+        }
 
+        /**
+         * Returns the name of the property.
+         * @return the name of the property.
+         */
+        public QName getProperty() {
+            return property;
+        }
+
+        /**
+         * If <code>true</code> the property is ordered ascending, otherwise
+         * descending.
+         * @return <code>true</code> for ascending; <code>false</code> for
+         * descending.
+         */
+        public boolean isAscending() {
+            return ascending;
+        }
+
+        /**
+         * Sets the new value for the ascending property.
+         * @param ascending <code>true</code> for ascending; <code>false</code>
+         * for descending.
+         */
+        public void setAscending(boolean ascending) {
+            this.ascending = ascending;
+        }
+    }
 }

Modified: incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/search/lucene/QueryImpl.java
Url: http://svn.apache.org/viewcvs/incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/search/lucene/QueryImpl.java?view=diff&rev=126598&p1=incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/search/lucene/QueryImpl.java&r1=126597&p2=incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/search/lucene/QueryImpl.java&r2=126598
==============================================================================
--- incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/search/lucene/QueryImpl.java	(original)
+++ incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/search/lucene/QueryImpl.java	Thu Jan 27 03:13:33 2005
@@ -142,17 +142,18 @@
                 session, index.getNamespaceMappings(), index.getAnalyzer());
 
         OrderQueryNode orderNode = root.getOrderNode();
-        // FIXME according to spec this should be descending
-        // by default. this contrasts to standard sql semantics
-        // where default is ascending.
-        boolean[] orderSpecs = null;
-        QName[] orderProperties = null;
+
+        OrderQueryNode.OrderSpec[] orderSpecs = null;
         if (orderNode != null) {
-            orderProperties = orderNode.getOrderByProperties();
-            orderSpecs = orderNode.getOrderBySpecs();
+            orderSpecs = orderNode.getOrderSpecs();
         } else {
-            orderProperties = new QName[0];
-            orderSpecs = new boolean[0];
+            orderSpecs = new OrderQueryNode.OrderSpec[0];
+        }
+        QName[] orderProperties = new QName[orderSpecs.length];
+        boolean[] ascSpecs = new boolean[orderSpecs.length];
+        for (int i = 0; i < orderSpecs.length; i++) {
+            orderProperties[i] = orderSpecs[i].getProperty();
+            ascSpecs[i] = orderSpecs[i].isAscending();
         }
 
 
@@ -161,7 +162,7 @@
 
         // execute it
         try {
-            Hits result = index.executeQuery(query, orderProperties, orderSpecs);
+            Hits result = index.executeQuery(query, orderProperties, ascSpecs);
             uuids = new ArrayList(result.length());
             for (int i = 0; i < result.length(); i++) {
                 String uuid = result.doc(i).get(FieldNames.UUID);

Modified: incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/search/sql/DefaultParserVisitor.java
Url: http://svn.apache.org/viewcvs/incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/search/sql/DefaultParserVisitor.java?view=diff&rev=126598&p1=incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/search/sql/DefaultParserVisitor.java&r1=126597&p2=incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/search/sql/DefaultParserVisitor.java&r2=126598
==============================================================================
--- incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/search/sql/DefaultParserVisitor.java	(original)
+++ incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/search/sql/DefaultParserVisitor.java	Thu Jan 27 03:13:33 2005
@@ -77,4 +77,16 @@
     public Object visit(ASTContainsExpression node, Object data) {
         return data;
     }
+
+    public Object visit(ASTOrderSpec node, Object data) {
+        return data;
+    }
+
+    public Object visit(ASTAscendingOrderSpec node, Object data) {
+        return data;
+    }
+
+    public Object visit(ASTDescendingOrderSpec node, Object data) {
+        return data;
+    }
 }

Modified: incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/search/sql/JCRSQLQueryBuilder.java
Url: http://svn.apache.org/viewcvs/incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/search/sql/JCRSQLQueryBuilder.java?view=diff&rev=126598&p1=incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/search/sql/JCRSQLQueryBuilder.java&r1=126597&p2=incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/search/sql/JCRSQLQueryBuilder.java&r2=126598
==============================================================================
--- incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/search/sql/JCRSQLQueryBuilder.java	(original)
+++ incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/search/sql/JCRSQLQueryBuilder.java	Thu Jan 27 03:13:33 2005
@@ -39,8 +39,6 @@
 
 import javax.jcr.query.InvalidQueryException;
 import javax.jcr.util.ISO8601;
-import java.util.List;
-import java.util.ArrayList;
 import java.util.Date;
 import java.util.Calendar;
 import java.text.SimpleDateFormat;
@@ -347,21 +345,42 @@
     public Object visit(ASTOrderByClause node, Object data) {
         QueryRootNode root = (QueryRootNode) data;
 
-        // list of QNames
-        final List identifiers = new ArrayList();
+        OrderQueryNode order = new OrderQueryNode(root);
+        root.setOrderNode(order);
+        node.childrenAccept(this, order);
+        return root;
+    }
+
+    public Object visit(ASTOrderSpec node, Object data) {
+        OrderQueryNode order = (OrderQueryNode) data;
+
+        final QName[] identifier = new QName[1];
 
-        // collect identifiers
+        // collect identifier
         node.childrenAccept(new DefaultParserVisitor() {
             public Object visit(ASTIdentifier node, Object data) {
-                identifiers.add(node.getName());
+                identifier[0] = node.getName();
                 return data;
             }
-        }, root);
+        }, data);
 
-        QName[] props = (QName[]) identifiers.toArray(new QName[identifiers.size()]);
-        boolean[] orders = new boolean[props.length];
-        root.setOrderNode(new OrderQueryNode(root, props, orders));
-        return root;
+        OrderQueryNode.OrderSpec spec = new OrderQueryNode.OrderSpec(identifier[0], true);
+        order.addOrderSpec(spec);
+
+        node.childrenAccept(this, spec);
+
+        return data;
+    }
+
+    public Object visit(ASTAscendingOrderSpec node, Object data) {
+        // do nothing ascending is default anyway
+        return data;
+    }
+
+    public Object visit(ASTDescendingOrderSpec node, Object data) {
+        OrderQueryNode.OrderSpec spec = (OrderQueryNode.OrderSpec) data;
+        spec.setAscending(false);
+        return data;
     }
 
     public Object visit(ASTContainsExpression node, Object data) {

Modified: incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/search/sql/QueryFormat.java
Url: http://svn.apache.org/viewcvs/incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/search/sql/QueryFormat.java?view=diff&rev=126598&p1=incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/search/sql/QueryFormat.java&r1=126597&p2=incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/search/sql/QueryFormat.java&r2=126598
==============================================================================
--- incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/search/sql/QueryFormat.java	(original)
+++ incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/search/sql/QueryFormat.java	Thu Jan 27 03:13:33 2005
@@ -368,14 +368,14 @@
     public Object visit(OrderQueryNode node, Object data) {
         StringBuffer sb = (StringBuffer) data;
         sb.append(" ORDER BY");
-        QName[] properties = node.getOrderByProperties();
-        if (properties.length > 0) {
+        OrderQueryNode.OrderSpec[] specs = node.getOrderSpecs();
+        if (specs.length > 0) {
             try {
                 String comma = "";
-                for (int i = 0; i < properties.length; i++) {
+                for (int i = 0; i < specs.length; i++) {
                     sb.append(comma).append(" ");
-                    appendName(properties[i], resolver, sb);
-                    if (!node.isAscending(i)) {
+                    appendName(specs[i].getProperty(), resolver, sb);
+                    if (!specs[i].isAscending()) {
                         sb.append(" DESC");
                     }
                     comma = ",";

Modified: incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/search/xpath/QueryFormat.java
Url: http://svn.apache.org/viewcvs/incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/search/xpath/QueryFormat.java?view=diff&rev=126598&p1=incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/search/xpath/QueryFormat.java&r1=126597&p2=incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/search/xpath/QueryFormat.java&r2=126598
==============================================================================
--- incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/search/xpath/QueryFormat.java	(original)
+++ incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/search/xpath/QueryFormat.java	Thu Jan 27 03:13:33 2005
@@ -258,7 +258,6 @@
         StringBuffer sb = (StringBuffer) data;
         try {
 
-            //int propIdx = sb.length();
             String propName = "@" + ISO9075.encode(node.getProperty()).toJCRName(resolver);
 
             if (node.getOperation() == OPERATION_EQ_VALUE) {
@@ -305,12 +304,36 @@
     }
 
     public Object visit(OrderQueryNode node, Object data) {
-        // @todo implement
+        StringBuffer sb = (StringBuffer) data;
+        sb.append(" order by");
+        OrderQueryNode.OrderSpec[] specs = node.getOrderSpecs();
+        String comma = "";
+        try {
+            for (int i = 0; i < specs.length; i++) {
+                sb.append(comma);
+                QName prop = ISO9075.encode(specs[i].getProperty());
+                sb.append(" @").append(prop.toJCRName(resolver));
+                if (!specs[i].isAscending()) {
+                    sb.append(" descending");
+                }
+                comma = ",";
+            }
+        } catch (NoPrefixDeclaredException e) {
+            exceptions.add(e);
+        }
         return data;
     }
 
     //----------------------------< internal >----------------------------------
 
+    /**
+     * Appends the value of a relation node to the <code>StringBuffer</code>
+     * <code>sb</code>.
+     * @param node the relation node.
+     * @param b where to append the value.
+     * @throws NoPrefixDeclaredException if a prefix declaration is missing for
+     *  a namespace URI. 
+     */
     private void appendValue(RelationQueryNode node, StringBuffer b)
             throws NoPrefixDeclaredException {
         if (node.getType() == TYPE_LONG) {
@@ -327,6 +350,5 @@
         } else {
             exceptions.add(new InvalidQueryException("Invalid type: " + node.getType()));
         }
-
     }
 }

Modified: incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/search/xpath/XPathQueryBuilder.java
Url: http://svn.apache.org/viewcvs/incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/search/xpath/XPathQueryBuilder.java?view=diff&rev=126598&p1=incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/search/xpath/XPathQueryBuilder.java&r1=126597&p2=incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/search/xpath/XPathQueryBuilder.java&r2=126598
==============================================================================
--- incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/search/xpath/XPathQueryBuilder.java	(original)
+++ incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/search/xpath/XPathQueryBuilder.java	Thu Jan 27 03:13:33 2005
@@ -27,6 +27,7 @@
 import org.apache.jackrabbit.core.search.NotQueryNode;
 import org.apache.jackrabbit.core.search.TextsearchQueryNode;
 import org.apache.jackrabbit.core.search.NodeTypeQueryNode;
+import org.apache.jackrabbit.core.search.OrderQueryNode;
 import org.apache.jackrabbit.core.NamespaceResolver;
 import org.apache.jackrabbit.core.QName;
 import org.apache.jackrabbit.core.SearchManager;
@@ -45,7 +46,7 @@
 /**
  * Query builder that translates a XPath statement into a query tree structure.
  */
-public class XPathQueryBuilder implements XPathVisitor {
+public class XPathQueryBuilder implements XPathVisitor, XPathTreeConstants {
 
     /** QName for 'fn:not' */
     static final QName FN_NOT = new QName(SearchManager.NS_FN_URI, "not");
@@ -117,6 +118,9 @@
             throws InvalidQueryException {
         this.resolver = resolver;
         try {
+            // create an XQuery statement because we're actually using an
+            // XQuery parser.
+            statement = "for $v in " + statement + " return $v";
             XPath query = new XPath(new StringReader(statement));
             query.XPath2().jjtAccept(this, root);
         } catch (ParseException e) {
@@ -192,18 +196,19 @@
      */
     public Object visit(SimpleNode node, Object data) {
         switch (node.getId()) {
-            case XPathTreeConstants.JJTXPATH:
+            case JJTXPATH2:
                 data = createPathQueryNode(node);
                 break;
-            case XPathTreeConstants.JJTROOT:
+            case JJTROOT:
                 ((PathQueryNode) data).addPathStep(new LocationStepQueryNode((QueryNode) data, new QName("", ""), false));
                 break;
-            case XPathTreeConstants.JJTROOTDESCENDANTS:
+            case JJTROOTDESCENDANTS:
                 ((PathQueryNode) data).addPathStep(new LocationStepQueryNode((QueryNode) data, new QName("", ""), false));
                 break;
-            case XPathTreeConstants.JJTSTEPEXPR:
+            case JJTSTEPEXPR:
                 if (isAttributeAxis(node)) {
-                    if (data instanceof RelationQueryNode) {
+                    if (data instanceof RelationQueryNode
+                            || data instanceof OrderQueryNode) {
                         // traverse
                         node.childrenAccept(this, data);
                     } else if (data instanceof NotQueryNode) {
@@ -232,46 +237,60 @@
                     }
                 }
                 break;
-            case XPathTreeConstants.JJTNAMETEST:
+            case JJTNAMETEST:
                 if (data instanceof LocationStepQueryNode
                         || data instanceof RelationQueryNode
                         || data instanceof PathQueryNode) {
                     createNameTest(node, (QueryNode) data);
+                } else if (data instanceof OrderQueryNode) {
+                    data = createOrderSpec(node, (OrderQueryNode) data);
                 } else {
                     // traverse
                     node.childrenAccept(this, data);
                 }
                 break;
-            case XPathTreeConstants.JJTOREXPR:
+            case JJTOREXPR:
                 NAryQueryNode parent = (NAryQueryNode) data;
                 data = new OrQueryNode(parent);
                 parent.addOperand((QueryNode) data);
                 // traverse
                 node.childrenAccept(this, data);
                 break;
-            case XPathTreeConstants.JJTANDEXPR:
+            case JJTANDEXPR:
                 parent = (NAryQueryNode) data;
                 data = new AndQueryNode(parent);
                 parent.addOperand((QueryNode) data);
                 // traverse
                 node.childrenAccept(this, data);
                 break;
-            case XPathTreeConstants.JJTCOMPARISONEXPR:
+            case JJTCOMPARISONEXPR:
                 createExpression(node, (NAryQueryNode) data);
                 break;
-            case XPathTreeConstants.JJTSTRINGLITERAL:
-            case XPathTreeConstants.JJTDECIMALLITERAL:
-            case XPathTreeConstants.JJTDOUBLELITERAL:
-            case XPathTreeConstants.JJTINTEGERLITERAL:
+            case JJTSTRINGLITERAL:
+            case JJTDECIMALLITERAL:
+            case JJTDOUBLELITERAL:
+            case JJTINTEGERLITERAL:
                 if (data instanceof RelationQueryNode) {
                     assignValue(node, (RelationQueryNode) data);
                 } else {
                     exceptions.add(new InvalidQueryException("Internal error: data is not a RelationQueryNode"));
                 }
                 break;
-            case XPathTreeConstants.JJTFUNCTIONCALL:
+            case JJTFUNCTIONCALL:
                 data = createFunction(node, (QueryNode) data);
                 break;
+            case JJTORDERBYCLAUSE:
+                root.setOrderNode(new OrderQueryNode(root));
+                data = root.getOrderNode();
+                node.childrenAccept(this, data);
+                break;
+            case JJTORDERMODIFIER:
+                if (node.jjtGetNumChildren() > 0
+                        && ((SimpleNode) node.jjtGetChild(0)) .getId() == JJTDESCENDING) {
+                    OrderQueryNode.OrderSpec[] specs = ((OrderQueryNode) data).getOrderSpecs();
+                    specs[specs.length - 1].setAscending(false);
+                }
+                break;
             default:
                 // per default traverse
                 node.childrenAccept(this, data);
@@ -299,8 +318,8 @@
                 parent.addPathStep(queryNode);
                 break;
             }
-            descenant = (c.getId() == XPathTreeConstants.JJTSLASHSLASH
-                    || c.getId() == XPathTreeConstants.JJTROOTDESCENDANTS);
+            descenant = (c.getId() == JJTSLASHSLASH
+                    || c.getId() == JJTROOTDESCENDANTS);
         }
 
         node.childrenAccept(this, queryNode);
@@ -318,7 +337,7 @@
     private void createNameTest(SimpleNode node, QueryNode queryNode) {
         if (node.jjtGetNumChildren() > 0) {
             SimpleNode child = (SimpleNode) node.jjtGetChild(0);
-            if (child.getId() == XPathTreeConstants.JJTQNAME) {
+            if (child.getId() == JJTQNAME) {
                 try {
                     if (queryNode instanceof LocationStepQueryNode) {
                         QName name = ISO9075.decode(QName.fromJCRName(child.getValue(), resolver));
@@ -329,13 +348,17 @@
                     } else if (queryNode instanceof PathQueryNode) {
                         QName name = ISO9075.decode(QName.fromJCRName(child.getValue(), resolver));
                         root.addSelectProperty(name);
+                    } else if (queryNode instanceof OrderQueryNode) {
+                        QName name = ISO9075.decode(QName.fromJCRName(child.getValue(), resolver));
+                        // todo implement properly
+                        root.getOrderNode().addOrderSpec(name, true);
                     }
                 } catch (IllegalNameException e) {
                     exceptions.add(new InvalidQueryException("Illegal name: " + child.getValue()));
                 } catch (UnknownPrefixException e) {
                     exceptions.add(new InvalidQueryException("Unknown prefix: " + child.getValue()));
                 }
-            } else if (child.getId() == XPathTreeConstants.JJTSTAR) {
+            } else if (child.getId() == JJTSTAR) {
                 if (queryNode instanceof LocationStepQueryNode) {
                     ((LocationStepQueryNode) queryNode).setNameTest(null);
                 }
@@ -352,7 +375,7 @@
      * @param queryNode the current <code>QueryNode</code>.
      */
     private void createExpression(SimpleNode node, NAryQueryNode queryNode) {
-        if (node.getId() != XPathTreeConstants.JJTCOMPARISONEXPR) {
+        if (node.getId() != JJTCOMPARISONEXPR) {
             throw new IllegalArgumentException("node must be of type ComparisonExpr");
         }
         // get operation type
@@ -424,7 +447,7 @@
      * @param queryNode current node in the query tree.
      */
     private void assignValue(SimpleNode node, RelationQueryNode queryNode) {
-        if (node.getId() == XPathTreeConstants.JJTSTRINGLITERAL) {
+        if (node.getId() == JJTSTRINGLITERAL) {
             String value = node.getValue().substring(1, node.getValue().length() - 1);
             if (node.getValue().charAt(0) == '"') {
                 value = value.replaceAll("\"\"", "\"");
@@ -432,11 +455,11 @@
                 value = value.replaceAll("''", "'");
             }
             queryNode.setStringValue(value);
-        } else if (node.getId() == XPathTreeConstants.JJTDECIMALLITERAL) {
+        } else if (node.getId() == JJTDECIMALLITERAL) {
             queryNode.setDoubleValue(Double.parseDouble(node.getValue()));
-        } else if (node.getId() == XPathTreeConstants.JJTDOUBLELITERAL) {
+        } else if (node.getId() == JJTDOUBLELITERAL) {
             queryNode.setDoubleValue(Double.parseDouble(node.getValue()));
-        } else if (node.getId() == XPathTreeConstants.JJTINTEGERLITERAL) {
+        } else if (node.getId() == JJTINTEGERLITERAL) {
             queryNode.setLongValue(Long.parseLong(node.getValue()));
         } else {
             exceptions.add(new InvalidQueryException("Unsupported literal type:" + node.toString()));
@@ -476,7 +499,7 @@
                     if (queryNode instanceof RelationQueryNode) {
                         RelationQueryNode rel = (RelationQueryNode) queryNode;
                         SimpleNode literal = (SimpleNode) node.jjtGetChild(1).jjtGetChild(0);
-                        if (literal.getId() == XPathTreeConstants.JJTSTRINGLITERAL) {
+                        if (literal.getId() == JJTSTRINGLITERAL) {
                             String value = literal.getValue();
                             // strip quotes
                             value = value.substring(1, value.length() - 1);
@@ -501,7 +524,7 @@
                 if (node.jjtGetNumChildren() == 2) {
                     SimpleNode literal = (SimpleNode) node.jjtGetChild(1).jjtGetChild(0);
                     if (queryNode instanceof NAryQueryNode) {
-                        if (literal.getId() == XPathTreeConstants.JJTSTRINGLITERAL) {
+                        if (literal.getId() == JJTSTRINGLITERAL) {
                             String value = literal.getValue();
                             // strip quotes
                             value = value.substring(1, value.length() - 1);
@@ -532,7 +555,7 @@
                         }
 
                         SimpleNode literal = (SimpleNode) node.jjtGetChild(2).jjtGetChild(0);
-                        if (literal.getId() == XPathTreeConstants.JJTSTRINGLITERAL) {
+                        if (literal.getId() == JJTSTRINGLITERAL) {
                             String value = literal.getValue();
                             // strip quotes
                             value = value.substring(1, value.length() - 1);
@@ -556,6 +579,22 @@
         return queryNode;
     }
 
+    private OrderQueryNode.OrderSpec createOrderSpec(SimpleNode node,
+                                                     OrderQueryNode queryNode) {
+        SimpleNode child = (SimpleNode) node.jjtGetChild(0);
+        OrderQueryNode.OrderSpec spec = null;
+        try {
+            QName name = ISO9075.decode(QName.fromJCRName(child.getValue(), resolver));
+            spec = new OrderQueryNode.OrderSpec(name, true);
+            queryNode.addOrderSpec(spec);
+        } catch (IllegalNameException e) {
+            exceptions.add(new InvalidQueryException("Illegal name: " + child.getValue()));
+        } catch (UnknownPrefixException e) {
+            exceptions.add(new InvalidQueryException("Unknown prefix: " + child.getValue()));
+        }
+        return spec;
+    }
+
     /**
      * Returns true if <code>node</code> has a child node which is the attribute
      * axis.
@@ -564,7 +603,7 @@
      */
     private boolean isAttributeAxis(SimpleNode node) {
         for (int i = 0; i < node.jjtGetNumChildren(); i++) {
-            if (((SimpleNode) node.jjtGetChild(i)).getId() == XPathTreeConstants.JJTAT) {
+            if (((SimpleNode) node.jjtGetChild(i)).getId() == JJTAT) {
                 return true;
             }
         }