You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@jackrabbit.apache.org by md...@apache.org on 2010/12/22 15:32:01 UTC

svn commit: r1051916 - 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/q...

Author: mduerig
Date: Wed Dec 22 14:32:01 2010
New Revision: 1051916

URL: http://svn.apache.org/viewvc?rev=1051916&view=rev
Log:
JCR-952: Support lower and upper case functions in "order by" clause

Modified:
    jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/lucene/QueryImpl.java
    jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/lucene/SearchIndex.java
    jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/lucene/SingleColumnQueryResult.java
    jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/query/OrderByTest.java
    jackrabbit/trunk/jackrabbit-spi-commons/src/main/java/org/apache/jackrabbit/spi/commons/query/OrderQueryNode.java
    jackrabbit/trunk/jackrabbit-spi-commons/src/main/java/org/apache/jackrabbit/spi/commons/query/xpath/XPathQueryBuilder.java
    jackrabbit/trunk/jackrabbit-spi-commons/src/main/javacc/xpath/XPath.jjt

Modified: jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/lucene/QueryImpl.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/lucene/QueryImpl.java?rev=1051916&r1=1051915&r2=1051916&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/lucene/QueryImpl.java (original)
+++ jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/lucene/QueryImpl.java Wed Dec 22 14:32:01 2010
@@ -16,18 +16,6 @@
  */
 package org.apache.jackrabbit.core.query.lucene;
 
-import java.util.Collections;
-import java.util.LinkedHashMap;
-import java.util.Map;
-
-import javax.jcr.RepositoryException;
-import javax.jcr.Value;
-import javax.jcr.Workspace;
-import javax.jcr.nodetype.PropertyDefinition;
-import javax.jcr.query.InvalidQueryException;
-import javax.jcr.query.QueryResult;
-import javax.jcr.query.qom.QueryObjectModelFactory;
-
 import org.apache.jackrabbit.core.SessionImpl;
 import org.apache.jackrabbit.core.nodetype.NodeTypeImpl;
 import org.apache.jackrabbit.core.query.PropertyTypeRegistry;
@@ -51,6 +39,15 @@ import org.apache.lucene.search.Query;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+import javax.jcr.RepositoryException;
+import javax.jcr.Workspace;
+import javax.jcr.nodetype.PropertyDefinition;
+import javax.jcr.query.InvalidQueryException;
+import javax.jcr.query.QueryResult;
+import javax.jcr.query.qom.QueryObjectModelFactory;
+import java.util.LinkedHashMap;
+import java.util.Map;
+
 /**
  * Implements the {@link org.apache.jackrabbit.core.query.ExecutableQuery}
  * interface.
@@ -126,15 +123,17 @@ public class QueryImpl extends AbstractQ
         }
         Path[] orderProperties = new Path[orderSpecs.length];
         boolean[] ascSpecs = new boolean[orderSpecs.length];
+        String[] orderFuncs = new String[orderSpecs.length];
         for (int i = 0; i < orderSpecs.length; i++) {
             orderProperties[i] = orderSpecs[i].getPropertyPath();
             ascSpecs[i] = orderSpecs[i].isAscending();
+            orderFuncs[i] = orderSpecs[i].getFunction();
         }
 
         return new SingleColumnQueryResult(
                 index, sessionContext, this, query,
                 new SpellSuggestion(index.getSpellChecker(), root),
-                getColumns(), orderProperties, ascSpecs,
+                getColumns(), orderProperties, ascSpecs, orderFuncs,
                 orderProperties.length == 0 && getRespectDocumentOrder(),
                 offset, limit);
     }

Modified: jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/lucene/SearchIndex.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/lucene/SearchIndex.java?rev=1051916&r1=1051915&r2=1051916&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/lucene/SearchIndex.java (original)
+++ jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/lucene/SearchIndex.java Wed Dec 22 14:32:01 2010
@@ -769,8 +769,8 @@ public class SearchIndex extends Abstrac
      * @param orderSpecs      the order specs for the sort order properties.
      *                        <code>true</code> indicates ascending order,
      *                        <code>false</code> indicates descending.
-     * @param resultFetchHint a hint on how many results should be fetched.
-     * @return the query hits.
+     * @param orderFuncs      functions for the properties for sort order. 
+     * @param resultFetchHint a hint on how many results should be fetched.  @return the query hits.
      * @throws IOException if an error occurs while searching the index.
      */
     public MultiColumnQueryHits executeQuery(SessionImpl session,
@@ -778,11 +778,11 @@ public class SearchIndex extends Abstrac
                                              Query query,
                                              Path[] orderProps,
                                              boolean[] orderSpecs,
-                                             long resultFetchHint)
+                                             String[] orderFuncs, long resultFetchHint)
             throws IOException {
         checkOpen();
 
-        Sort sort = new Sort(createSortFields(orderProps, orderSpecs));
+        Sort sort = new Sort(createSortFields(orderProps, orderSpecs, orderFuncs));
 
         final IndexReader reader = getIndexReader(queryImpl.needsSystemTree());
         JackrabbitIndexSearcher searcher = new JackrabbitIndexSearcher(
@@ -1016,10 +1016,11 @@ public class SearchIndex extends Abstrac
      *
      * @param orderProps the order properties.
      * @param orderSpecs the order specs for the properties.
+     * @param orderFuncs the functions for the properties. 
      * @return an array of sort fields
      */
     protected SortField[] createSortFields(Path[] orderProps,
-                                           boolean[] orderSpecs) {
+                                           boolean[] orderSpecs, String[] orderFuncs) {
         List<SortField> sortFields = new ArrayList<SortField>();
         for (int i = 0; i < orderProps.length; i++) {
             if (orderProps[i].getLength() == 1
@@ -1030,7 +1031,13 @@ public class SearchIndex extends Abstrac
                 // are first.
                 sortFields.add(new SortField(null, SortField.SCORE, orderSpecs[i]));
             } else {
-                sortFields.add(new SortField(orderProps[i].getString(), scs, !orderSpecs[i]));
+                if ("upper-case".equals(orderFuncs[i])) {
+                    sortFields.add(new SortField(orderProps[i].getString(), new UpperCaseSortComparator(scs), !orderSpecs[i]));
+                } else if ("lower-case".equals(orderFuncs[i])) {
+                    sortFields.add(new SortField(orderProps[i].getString(), new LowerCaseSortComparator(scs), !orderSpecs[i]));
+                } else {
+                    sortFields.add(new SortField(orderProps[i].getString(), scs, !orderSpecs[i]));
+                }
             }
         }
         return sortFields.toArray(new SortField[sortFields.size()]);

Modified: jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/lucene/SingleColumnQueryResult.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/lucene/SingleColumnQueryResult.java?rev=1051916&r1=1051915&r2=1051916&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/lucene/SingleColumnQueryResult.java (original)
+++ jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/lucene/SingleColumnQueryResult.java Wed Dec 22 14:32:01 2010
@@ -16,15 +16,14 @@
  */
 package org.apache.jackrabbit.core.query.lucene;
 
-import java.io.IOException;
-
-import javax.jcr.RepositoryException;
-
 import org.apache.jackrabbit.core.session.SessionContext;
 import org.apache.jackrabbit.spi.Path;
 import org.apache.jackrabbit.spi.commons.query.qom.ColumnImpl;
 import org.apache.lucene.search.Query;
 
+import javax.jcr.RepositoryException;
+import java.io.IOException;
+
 /**
  * <code>SingleColumnQueryResult</code> implements a query result that returns
  * a single column. That is, executes a lucene query.
@@ -46,17 +45,23 @@ public class SingleColumnQueryResult ext
      */
     protected final boolean[] orderSpecs;
 
+    /**
+     * Function for each of the order properties (or <code>null</code> if none).
+     */
+    private String[] orderFuncs;
+
     public SingleColumnQueryResult(
             SearchIndex index, SessionContext sessionContext,
             AbstractQueryImpl queryImpl, Query query,
             SpellSuggestion spellSuggestion, ColumnImpl[] columns,
-            Path[] orderProps, boolean[] orderSpecs, boolean documentOrder,
+            Path[] orderProps, boolean[] orderSpecs, String[] orderFuncs, boolean documentOrder,
             long offset, long limit) throws RepositoryException {
         super(index, sessionContext, queryImpl, spellSuggestion,
                 columns, documentOrder, offset, limit);
         this.query = query;
         this.orderProps = orderProps;
         this.orderSpecs = orderSpecs;
+        this.orderFuncs = orderFuncs;
         // if document order is requested get all results right away
         getResults(docOrder ? Integer.MAX_VALUE : index.getResultFetchSize());
     }
@@ -68,7 +73,7 @@ public class SingleColumnQueryResult ext
             throws IOException {
         return index.executeQuery(
                 sessionContext.getSessionImpl(), queryImpl, query,
-                orderProps, orderSpecs, resultFetchHint);
+                orderProps, orderSpecs, orderFuncs, resultFetchHint);
     }
 
     /**

Modified: jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/query/OrderByTest.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/query/OrderByTest.java?rev=1051916&r1=1051915&r2=1051916&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/query/OrderByTest.java (original)
+++ jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/query/OrderByTest.java Wed Dec 22 14:32:01 2010
@@ -16,19 +16,18 @@
  */
 package org.apache.jackrabbit.core.query;
 
-import java.util.List;
-import java.util.Arrays;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.Calendar;
-
 import javax.jcr.Node;
-import javax.jcr.RepositoryException;
 import javax.jcr.NodeIterator;
-import javax.jcr.Value;
 import javax.jcr.PropertyType;
+import javax.jcr.RepositoryException;
+import javax.jcr.Value;
 import javax.jcr.query.Query;
 import javax.jcr.query.QueryResult;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Calendar;
+import java.util.Collections;
+import java.util.List;
 
 /**
  * Tests queries with order by.
@@ -61,6 +60,40 @@ public class OrderByTest extends Abstrac
         checkResult(result, 3);
     }
 
+    public void testOrderByUpperCase() throws RepositoryException {
+        Node n1 = testRootNode.addNode("node1");
+        Node n2 = testRootNode.addNode("node2");
+        Node n3 = testRootNode.addNode("node3");
+
+        n1.setProperty("text", "Amundsen");
+        n2.setProperty("text", "barents");
+        n3.setProperty("text", "Wegener");
+
+        testRootNode.save();
+
+        String xpath = "/" + testRoot + "/*[@jcr:primaryType='nt:unstructured'] order by fn:upper-case(@text)";
+        Query q = session.getWorkspace().getQueryManager().createQuery(xpath, Query.XPATH);
+        QueryResult result = q.execute();
+        checkResult(result, new Node[]{n1, n2, n3});
+    }
+
+    public void testOrderByLowerCase() throws RepositoryException {
+        Node n1 = testRootNode.addNode("node1");
+        Node n2 = testRootNode.addNode("node2");
+        Node n3 = testRootNode.addNode("node3");
+
+        n1.setProperty("text", "Amundsen");
+        n2.setProperty("text", "barents");
+        n3.setProperty("text", "Wegener");
+
+        testRootNode.save();
+
+        String xpath = "/" + testRoot + "/*[@jcr:primaryType='nt:unstructured'] order by fn:lower-case(@text)";
+        Query q = session.getWorkspace().getQueryManager().createQuery(xpath, Query.XPATH);
+        QueryResult result = q.execute();
+        checkResult(result, new Node[]{n1, n2, n3});
+    }
+
     public void testChildAxisString() throws RepositoryException {
         checkChildAxis(new Value[]{getValue("a"), getValue("b"), getValue("c")});
     }

Modified: jackrabbit/trunk/jackrabbit-spi-commons/src/main/java/org/apache/jackrabbit/spi/commons/query/OrderQueryNode.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-spi-commons/src/main/java/org/apache/jackrabbit/spi/commons/query/OrderQueryNode.java?rev=1051916&r1=1051915&r2=1051916&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit-spi-commons/src/main/java/org/apache/jackrabbit/spi/commons/query/OrderQueryNode.java (original)
+++ jackrabbit/trunk/jackrabbit-spi-commons/src/main/java/org/apache/jackrabbit/spi/commons/query/OrderQueryNode.java Wed Dec 22 14:32:01 2010
@@ -16,15 +16,14 @@
  */
 package org.apache.jackrabbit.spi.commons.query;
 
-import java.util.ArrayList;
-import java.util.List;
-
-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;
+import org.apache.jackrabbit.spi.commons.name.PathBuilder;
+
+import javax.jcr.RepositoryException;
+import java.util.ArrayList;
+import java.util.List;
 
 /**
  * Implements a query node that defines the order of nodes according to the
@@ -35,7 +34,7 @@ public class OrderQueryNode extends Quer
     /**
      * The order spects
      */
-    private final List specs = new ArrayList();
+    private final List<OrderSpec> specs = new ArrayList<OrderSpec>();
 
     /**
      * Creates a new <code>OrderQueryNode</code> with a reference to a parent
@@ -57,6 +56,78 @@ public class OrderQueryNode extends Quer
     }
 
     /**
+     * Create and add a new (empty) order specification to this query node.
+     */
+    public void newOrderSpec() {
+        specs.add(new OrderSpec((Path) null, true));
+    }
+
+    /**
+     * Set the last order specification of this query node to ascending/descending
+     * @see OrderSpec#setAscending(boolean)
+     *
+     * @param value  <code>true</code>true> for ascending and <code>false</code> for
+     * descending.
+     * @throws  IllegalStateException  if no order specification is set
+     */
+    public void setAscending(boolean value) {
+        if (specs.size() == 0) {
+            throw new IllegalStateException("No order specification set");
+        }
+
+        OrderSpec orderSpec = specs.get(specs.size() - 1);
+        orderSpec.setAscending(value);
+    }
+
+    /**
+     * Set the path of the last order specification of this query node.
+     * @see OrderSpec#setPath(org.apache.jackrabbit.spi.Path)
+     *
+     * @param path  a path
+     * @throws  IllegalStateException  if no order specification is set
+     */
+    public void setPath(Path path) {
+        if (specs.size() == 0) {
+            throw new IllegalStateException("No order specification set");
+        }
+
+        OrderSpec orderSpec = specs.get(specs.size() - 1);
+        orderSpec.setPath(path);
+    }
+
+    /**
+     * Set the function of the last order specification of this query node.
+     * @see OrderSpec#setFunction(String)
+     *
+     * @param name  a function name
+     * @throws  IllegalStateException  if no order specification is set
+     */
+    public void setFunction(String name) {
+        if (specs.size() == 0) {
+            throw new IllegalStateException("No order specification set");
+        }
+        
+        OrderSpec orderSpec = specs.get(specs.size() - 1);
+        orderSpec.setFunction(name);
+    }
+
+    /**
+     * Checks whether all order specifications of this query node have at least
+     * its path specified (i.e. non <code>null</code>.)
+     *
+     * @return  <code>true</code> iff all order specification of this query node are valid.
+     */
+    public boolean isValid() {
+        for (OrderSpec spec : specs) {
+            if (spec.getPropertyPath() == null) {
+                return false;
+            }
+        }
+
+        return true;
+    }
+
+    /**
      * Adds an order specification to this query node.
      *
      * @param property  the name of the property.
@@ -106,7 +177,7 @@ public class OrderQueryNode extends Quer
      *                                   index <code>i</code>.
      */
     public boolean isAscending(int i) throws IndexOutOfBoundsException {
-        return ((OrderSpec) specs.get(i)).ascending;
+        return specs.get(i).ascending;
     }
 
     /**
@@ -116,7 +187,7 @@ public class OrderQueryNode extends Quer
      * @return order by specs.
      */
     public OrderSpec[] getOrderSpecs() {
-        return (OrderSpec[]) specs.toArray(new OrderSpec[specs.size()]);
+        return specs.toArray(new OrderSpec[specs.size()]);
     }
 
     /**
@@ -141,14 +212,19 @@ public class OrderQueryNode extends Quer
         /**
          * The relative path to of the property
          */
-        private final Path property;
+        private Path property;
 
         /**
-         * If <code>true</code> this property is orderd ascending
+         * If <code>true</code> this property is ordered ascending
          */
         private boolean ascending;
 
         /**
+         * The function applied to the property
+         */
+        private String function;
+
+        /**
          * Creates a new <code>OrderSpec</code> for <code>property</code>.
          *
          * @param property  the name of the property.
@@ -213,6 +289,31 @@ public class OrderQueryNode extends Quer
         }
 
         /**
+         * Set a new value for the path
+         *
+         * @param path  a path
+         */
+        public void setPath(Path path) {
+            this.property = path;
+        }
+
+        /**
+         * Set a new value for a function
+         *
+         * @param name a function name
+         */
+        public void setFunction(String name) {
+            this.function = name;
+        }
+
+        /**
+         * @return  name of the function 
+         */
+        public String getFunction() {
+            return function;
+        }
+        
+        /**
          * Returns <code>true</code> if <code>this</code> order spec is equal
          * to <code>obj</code>
          * @param obj the reference object with which to compare.
@@ -227,6 +328,7 @@ public class OrderQueryNode extends Quer
             }
             return false;
         }
+
     }
 
     /**

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=1051916&r1=1051915&r2=1051916&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 Wed Dec 22 14:32:01 2010
@@ -16,16 +16,6 @@
  */
 package org.apache.jackrabbit.spi.commons.query.xpath;
 
-import java.io.StringReader;
-import java.util.ArrayList;
-import java.util.Calendar;
-import java.util.List;
-import java.util.Map;
-
-import javax.jcr.NamespaceException;
-import javax.jcr.RepositoryException;
-import javax.jcr.query.InvalidQueryException;
-
 import org.apache.commons.collections.map.ReferenceMap;
 import org.apache.jackrabbit.spi.Name;
 import org.apache.jackrabbit.spi.NameFactory;
@@ -57,6 +47,15 @@ import org.apache.jackrabbit.spi.commons
 import org.apache.jackrabbit.util.ISO8601;
 import org.apache.jackrabbit.util.ISO9075;
 
+import javax.jcr.NamespaceException;
+import javax.jcr.RepositoryException;
+import javax.jcr.query.InvalidQueryException;
+import java.io.StringReader;
+import java.util.ArrayList;
+import java.util.Calendar;
+import java.util.List;
+import java.util.Map;
+
 /**
  * Query builder that translates a XPath statement into a query tree structure.
  */
@@ -439,7 +438,7 @@ public class XPathQueryBuilder implement
                         || queryNode.getType() == QueryNode.TYPE_PATH) {
                     createNodeTest(node, queryNode);
                 } else if (queryNode.getType() == QueryNode.TYPE_ORDER) {
-                    createOrderSpec(node, (OrderQueryNode) queryNode);
+                    setOrderSpecPath(node, (OrderQueryNode) queryNode);
                 } else {
                     // traverse
                     node.childrenAccept(this, queryNode);
@@ -523,11 +522,18 @@ public class XPathQueryBuilder implement
                 queryNode = root.getOrderNode();
                 node.childrenAccept(this, queryNode);
                 break;
+            case JJTORDERSPEC:
+                OrderQueryNode orderQueryNode = (OrderQueryNode) queryNode;
+                orderQueryNode.newOrderSpec();
+                node.childrenAccept(this, queryNode);
+                if (!orderQueryNode.isValid()) {
+                    exceptions.add(new InvalidQueryException("Invalid order specification. (Missing @?)"));
+                }
+                break;
             case JJTORDERMODIFIER:
                 if (node.jjtGetNumChildren() > 0
                         && ((SimpleNode) node.jjtGetChild(0)).getId() == JJTDESCENDING) {
-                    OrderQueryNode.OrderSpec[] specs = ((OrderQueryNode) queryNode).getOrderSpecs();
-                    specs[specs.length - 1].setAscending(false);
+                    ((OrderQueryNode) queryNode).setAscending(false);
                 }
                 break;
             case JJTPREDICATELIST:
@@ -962,7 +968,7 @@ public class XPathQueryBuilder implement
                 }
             } else if (JCR_SCORE.equals(funName)) {
                 if (queryNode.getType() == QueryNode.TYPE_ORDER) {
-                    createOrderSpec(node, (OrderQueryNode) queryNode);
+                    setOrderSpecPath(node, (OrderQueryNode) queryNode);
                 } else {
                     exceptions.add(new InvalidQueryException("Unsupported location for jcr:score()"));
                 }
@@ -974,6 +980,9 @@ public class XPathQueryBuilder implement
                                 relNode, PropertyFunctionQueryNode.LOWER_CASE));
                         // get property name
                         node.jjtGetChild(1).jjtAccept(this, relNode);
+                    } else if (queryNode.getType() == QueryNode.TYPE_ORDER) {
+                        ((OrderQueryNode) queryNode).setFunction(FN_LOWER_CASE.getLocalName());
+                        node.childrenAccept(this, queryNode);
                     } else {
                         exceptions.add(new InvalidQueryException("Unsupported location for fn:lower-case()"));
                     }
@@ -988,6 +997,9 @@ public class XPathQueryBuilder implement
                                 relNode, PropertyFunctionQueryNode.UPPER_CASE));
                         // get property name
                         node.jjtGetChild(1).jjtAccept(this, relNode);
+                    } else if (queryNode.getType() == QueryNode.TYPE_ORDER) {
+                        ((OrderQueryNode) queryNode).setFunction(FN_UPPER_CASE.getLocalName());
+                        node.childrenAccept(this, queryNode);
                     } else {
                         exceptions.add(new InvalidQueryException("Unsupported location for fn:upper-case()"));
                     }
@@ -1120,10 +1132,8 @@ public class XPathQueryBuilder implement
         return derefNode;
     }
 
-    private OrderQueryNode.OrderSpec createOrderSpec(SimpleNode node,
-                                                     OrderQueryNode queryNode) {
+    private void setOrderSpecPath(SimpleNode node, OrderQueryNode queryNode) {
         SimpleNode child = (SimpleNode) node.jjtGetChild(0);
-        OrderQueryNode.OrderSpec spec = null;
         try {
             String propName = child.getValue();
             if (child.getId() == JJTQNAMELPAR) {
@@ -1139,14 +1149,12 @@ public class XPathQueryBuilder implement
             } else {
                 path = PathFactoryImpl.getInstance().create(element);
             }
-            spec = new OrderQueryNode.OrderSpec(path, true);
-            queryNode.addOrderSpec(spec);
+            queryNode.setPath(path);
         } catch (NameException e) {
             exceptions.add(new InvalidQueryException("Illegal name: " + child.getValue()));
         } catch (NamespaceException e) {
             exceptions.add(new InvalidQueryException("Illegal name: " + child.getValue()));
         }
-        return spec;
     }
 
     /**

Modified: jackrabbit/trunk/jackrabbit-spi-commons/src/main/javacc/xpath/XPath.jjt
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-spi-commons/src/main/javacc/xpath/XPath.jjt?rev=1051916&r1=1051915&r2=1051916&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit-spi-commons/src/main/javacc/xpath/XPath.jjt (original)
+++ jackrabbit/trunk/jackrabbit-spi-commons/src/main/javacc/xpath/XPath.jjt Wed Dec 22 14:32:01 2010
@@ -686,7 +686,7 @@ TOKEN :
  < Except : "except" > : DEFAULT
 }
 
-<OPERATOR, ITEMTYPE>
+<DEFAULT, OPERATOR, ITEMTYPE>
 TOKEN :
 {
  < As : "as" > : ITEMTYPE
@@ -716,12 +716,6 @@ TOKEN :
  < Castable : "castable" (<skip_>)+ "as" > : SINGLETYPE
 }
 
-<DEFAULT, OPERATOR, ITEMTYPE>
-TOKEN :
-{
- < RparAs : ")" (<skip_>)* "as" > : ITEMTYPE
-}
-
 <ITEMTYPE>
 TOKEN :
 {
@@ -1888,7 +1882,7 @@ void ConstructionDecl()  :
 void FunctionDecl()  :
 {}
 {
-  <DefineFunction>{jjtThis.processToken(token);} #DefineFunction(true) <QNameLpar>{jjtThis.processToken(token);} #QNameLpar(true) [ParamList()] (<Rpar> | (<RparAs>{jjtThis.processToken(token);} #RparAs(true) SequenceType())) (EnclosedExpr() | <External>{jjtThis.processToken(token);} #External(true))
+  <DefineFunction>{jjtThis.processToken(token);} #DefineFunction(true) <QNameLpar>{jjtThis.processToken(token);} #QNameLpar(true) [ParamList()] (<Rpar> (<As> {jjtThis.processToken(token);} #As(true) SequenceType())?) (EnclosedExpr() | <External>{jjtThis.processToken(token);} #External(true))
 }
 
 void ParamList()  :