Author: thomasm
Date: Thu Oct 24 12:50:13 2013
New Revision: 1535363

URL: http://svn.apache.org/r1535363
Log:
OAK-155 Query: limited support for the deprecated JCR 1.0 query language 
Query.SQL (refactor to split parsing and SQL-2 generation)

Added:
    
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/xpath/
    
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/xpath/Expression.java
    
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/xpath/Order.java
    
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/xpath/Selector.java
    
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/xpath/Statement.java
    
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/xpath/XPathToSQL2Converter.java
      - copied, changed from r1535299, 
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/XPathToSQL2Converter.java
Removed:
    
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/XPathToSQL2Converter.java
Modified:
    
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/QueryEngineImpl.java
    
jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/query/AbstractQueryTest.java
    
jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/query/SQL2ParserTest.java

Modified: 
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/QueryEngineImpl.java
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/QueryEngineImpl.java?rev=1535363&r1=1535362&r2=1535363&view=diff
==============================================================================
--- 
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/QueryEngineImpl.java
 (original)
+++ 
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/QueryEngineImpl.java
 Thu Oct 24 12:50:13 2013
@@ -34,6 +34,7 @@ import org.apache.jackrabbit.oak.api.Que
 import org.apache.jackrabbit.oak.api.Result;
 import org.apache.jackrabbit.oak.namepath.NamePathMapper;
 import org.apache.jackrabbit.oak.query.index.TraversingIndex;
+import org.apache.jackrabbit.oak.query.xpath.XPathToSQL2Converter;
 import org.apache.jackrabbit.oak.spi.query.Filter;
 import org.apache.jackrabbit.oak.spi.query.QueryIndex;
 import org.apache.jackrabbit.oak.spi.query.QueryIndexProvider;

Added: 
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/xpath/Expression.java
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/xpath/Expression.java?rev=1535363&view=auto
==============================================================================
--- 
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/xpath/Expression.java
 (added)
+++ 
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/xpath/Expression.java
 Thu Oct 24 12:50:13 2013
@@ -0,0 +1,327 @@
+/*
+ * 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.oak.query.xpath;
+
+import java.util.ArrayList;
+
+import org.apache.jackrabbit.oak.query.SQL2Parser;
+import org.apache.jackrabbit.util.ISO9075;
+
+/**
+ * An expression.
+ */
+abstract class Expression {
+    
+    static final int PRECEDENCE_OR = 1, PRECEDENCE_AND = 2, 
+            PRECEDENCE_CONDITION = 3, PRECEDENCE_OPERAND = 4;
+    
+    /**
+     * The "and" combination of two conditions.
+     * 
+     * @param old the first expression (may be null)
+     * @param add the second expression (may be null)
+     * @return the combined expression (may be null)
+     */
+    public static Expression and(Expression old, Expression add) {
+        if (old == null) {
+            return add;
+        } else if (add == null) {
+            return old;
+        }
+        return new Expression.Condition(old, "and", add, 
Expression.PRECEDENCE_AND);
+    }
+    
+    /**
+     * Whether this is a condition.
+     * 
+     * @return true if it is 
+     */
+    boolean isCondition() {
+        return false;
+    }
+    
+    /**
+     * Get the operator / operation precedence. The JCR specification uses:
+     * 1=OR, 2=AND, 3=condition, 4=operand  
+     * 
+     * @return the precedence (as an example, multiplication needs to return
+     *         a higher number than addition)
+     */
+    int getPrecedence() {
+        return PRECEDENCE_OPERAND;
+    }
+    
+    /**
+     * Get the column alias name of an expression. For a property, this is the
+     * property name (no matter how many selectors the query contains); for
+     * other expressions it matches the toString() method.
+     * 
+     * @return the simple column name
+     */
+    String getColumnAliasName() {
+        return toString();
+    }
+    
+    /**
+     * Whether the result of this expression is a name. Names are subject to
+     * ISO9075 encoding.
+     * 
+     * @return whether this expression is a name.
+     */
+    boolean isName() {
+        return false;
+    }
+
+    /**
+     * A literal expression.
+     */
+    static class Literal extends Expression {
+    
+        final String value;
+        final String rawText;
+    
+        Literal(String value, String rawText) {
+            this.value = value;
+            this.rawText = rawText;
+        }
+    
+        public static Expression newBoolean(boolean value) {
+            return new Literal(String.valueOf(value), String.valueOf(value));
+        }
+    
+        static Literal newNumber(String s) {
+            return new Literal(s, s);
+        }
+    
+        static Literal newString(String s) {
+            return new Literal(SQL2Parser.escapeStringLiteral(s), s);
+        }
+    
+        @Override
+        public String toString() {
+            return value;
+        }
+    
+    }
+
+    /**
+     * A condition.
+     */
+    static class Condition extends Expression {
+    
+        final Expression left;
+        final String operator;
+        Expression right;
+        final int precedence;
+    
+        /**
+         * Create a new condition.
+         * 
+         * @param left the left hand side operator, or null
+         * @param operator the operator
+         * @param right the right hand side operator, or null
+         * @param precedence the operator precedence 
(Expression.PRECEDENCE_...)
+         */
+        Condition(Expression left, String operator, Expression right, int 
precedence) {
+            this.left = left;
+            this.operator = operator;
+            this.right = right;
+            this.precedence = precedence;
+        }
+        
+        @Override
+        int getPrecedence() {
+            return precedence;
+        }
+    
+        @Override
+        public String toString() {
+            String leftExpr;
+            boolean leftExprIsName;
+            if (left == null) {
+                leftExprIsName = false;
+                leftExpr = "";
+            } else {
+                leftExprIsName = left.isName();
+                leftExpr = left.toString();
+                if (left.getPrecedence() < precedence) {
+                    leftExpr = "(" + leftExpr + ")";
+                }
+            }
+            boolean impossible = false;
+            String rightExpr;
+            if (right == null) {
+                rightExpr = "";
+            } else {
+                if (leftExprIsName && !"like".equals(operator)) {
+                    // need to de-escape _x0020_ and so on
+                    if (!(right instanceof Literal)) {
+                        throw new IllegalArgumentException(
+                                "Can only compare a name against a string 
literal, not " + right);
+                    }
+                    Literal l = (Literal) right;
+                    String raw = l.rawText;
+                    String decoded = ISO9075.decode(raw);
+                    String encoded = ISO9075.encode(decoded);
+                    rightExpr = SQL2Parser.escapeStringLiteral(decoded);
+                    if (!encoded.toUpperCase().equals(raw.toUpperCase())) {
+                        // nothing can potentially match
+                        impossible = true;
+                    }
+                } else {
+                    rightExpr = right.toString();
+                }
+                if (right.getPrecedence() < precedence) {
+                    rightExpr = "(" + right + ")";
+                }
+            }
+            if (impossible) {
+                // a condition that can not possibly be true
+                return "upper(" + leftExpr + ") = 'never matches'";
+            }
+            return (leftExpr + " " + operator + " " + rightExpr).trim();
+        }
+    
+        @Override
+        boolean isCondition() {
+            return true;
+        }
+    
+    }
+
+    /**
+     * A function call.
+     */
+    static class Function extends Expression {
+    
+        final String name;
+        final ArrayList<Expression> params = new ArrayList<Expression>();
+    
+        Function(String name) {
+            this.name = name;
+        }
+    
+        @Override
+        public String toString() {
+            StringBuilder buff = new StringBuilder(name);
+            buff.append('(');
+            for (int i = 0; i < params.size(); i++) {
+                if (i > 0) {
+                    buff.append(", ");
+                }
+                buff.append(params.get(i).toString());
+            }
+            buff.append(')');
+            return buff.toString();
+        }
+    
+        @Override
+        boolean isCondition() {
+            return name.equals("contains") || name.equals("not");
+        }
+        
+        @Override
+        boolean isName() {
+            if ("upper".equals(name) || "lower".equals(name)) {
+                return params.get(0).isName();
+            }
+            return "name".equals(name);
+        }
+    
+    }
+
+    /**
+     * A cast operation.
+     */
+    static class Cast extends Expression {
+    
+        final Expression expr;
+        final String type;
+    
+        Cast(Expression expr, String type) {
+            this.expr = expr;
+            this.type = type;
+        }
+    
+        @Override
+        public String toString() {
+            StringBuilder buff = new StringBuilder("cast(");
+            buff.append(expr.toString());
+            buff.append(" as ").append(type).append(')');
+            return buff.toString();
+        }
+    
+        @Override
+        boolean isCondition() {
+            return false;
+        }
+    
+    }
+
+    /**
+     * A selector parameter.
+     */
+    static class SelectorExpr extends Expression {
+    
+        private final Selector selector;
+    
+        SelectorExpr(Selector selector) {
+            this.selector = selector;
+        }
+    
+        @Override
+        public String toString() {
+            return selector.name;
+        }
+    
+    }
+
+    /**
+     * A property expression.
+     */
+    static class Property extends Expression {
+    
+        final Selector selector;
+        final String name;
+    
+        Property(Selector selector, String name) {
+            this.selector = selector;
+            this.name = name;
+        }
+    
+        @Override
+        public String toString() {
+            StringBuilder buff = new StringBuilder();
+            if (!selector.onlySelector) {
+                buff.append(selector.name).append('.');
+            }
+            if (name.equals("*")) {
+                buff.append('*');
+            } else {
+                buff.append('[').append(name).append(']');
+            }
+            return buff.toString();
+        }
+        
+        @Override
+        public String getColumnAliasName() {
+            return name;
+        }
+    
+    }
+    
+}
\ No newline at end of file

Added: 
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/xpath/Order.java
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/xpath/Order.java?rev=1535363&view=auto
==============================================================================
--- 
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/xpath/Order.java
 (added)
+++ 
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/xpath/Order.java
 Thu Oct 24 12:50:13 2013
@@ -0,0 +1,32 @@
+/*
+ * 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.oak.query.xpath;
+
+/**
+ * An order by expression.
+ */
+class Order {
+
+    boolean descending;
+    Expression expr;
+
+    @Override
+    public String toString() {
+        return expr + (descending ? " desc" : "");
+    }
+
+}
\ No newline at end of file

Added: 
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/xpath/Selector.java
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/xpath/Selector.java?rev=1535363&view=auto
==============================================================================
--- 
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/xpath/Selector.java
 (added)
+++ 
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/xpath/Selector.java
 Thu Oct 24 12:50:13 2013
@@ -0,0 +1,90 @@
+/*
+ * 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.oak.query.xpath;
+
+/**
+ * A selector.
+ */
+class Selector {
+
+    /**
+     * The selector name.
+     */
+    String name;
+    
+    /**
+     * Whether this is the only selector in the query.
+     */
+    boolean onlySelector;
+    
+    /**
+     * The node type, if set, or null.
+     */
+    String nodeType;
+    
+    /**
+     * Whether this is a child node of the previous selector or a given path.
+     * Examples:
+     * <ul><li>/jcr:root/*
+     * </li><li>/jcr:root/test/*
+     * </li><li>/jcr:root/element()
+     * </li><li>/jcr:root/element(*)
+     * </li></ul>
+     */
+    boolean isChild;
+    
+    /**
+     * Whether this is a parent node of the previous selector or given path.
+     * Examples:
+     * <ul><li>testroot//child/..[@foo1]
+     * </li><li>/jcr:root/test/descendant/..[@test]
+     * </li></ul>
+     */
+    boolean isParent;
+    
+    /**
+     * Whether this is a descendant of the previous selector or a given path.
+     * Examples:
+     * <ul><li>/jcr:root//descendant
+     * </li><li>/jcr:root/test//descendant
+     * </li><li>/jcr:root[@x]
+     * </li><li>/jcr:root (just by itself)
+     * </li></ul>
+     */
+    boolean isDescendant;
+    
+    /**
+     * The path (only used for the first selector).
+     */
+    String path = "";
+    
+    /**
+     * The node name, if set.
+     */
+    String nodeName;
+    
+    /**
+     * The condition for this selector.
+     */
+    Expression condition;
+    
+    /**
+     * The join condition from the previous selector.
+     */
+    Expression joinCondition;
+    
+}
\ No newline at end of file

Added: 
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/xpath/Statement.java
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/xpath/Statement.java?rev=1535363&view=auto
==============================================================================
--- 
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/xpath/Statement.java
 (added)
+++ 
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/xpath/Statement.java
 Thu Oct 24 12:50:13 2013
@@ -0,0 +1,158 @@
+/*
+ * 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.oak.query.xpath;
+
+import java.util.ArrayList;
+
+import org.apache.jackrabbit.oak.query.QueryImpl;
+import org.apache.jackrabbit.oak.query.xpath.Expression.Property;
+
+/**
+ * An xpath statement.
+ */
+public class Statement {
+
+    private String xpathQuery;
+    
+    private boolean explain;
+    private boolean measure;
+    
+    /**
+     * The selector to get the columns from (the selector used in the select
+     * column list).
+     */
+    private Selector columnSelector;
+    
+    private ArrayList<Expression> columnList = new ArrayList<Expression>();
+    
+    /**
+     * All selectors.
+     */
+    private ArrayList<Selector> selectors;
+
+    private ArrayList<Order> orderList = new ArrayList<Order>();
+    
+    @Override
+    public String toString() {
+        StringBuilder buff = new StringBuilder();
+        
+        // explain | measure ...
+        if (explain) {
+            buff.append("explain ");
+        } else if (measure) {
+            buff.append("measure ");
+        }
+        
+        // select ...
+        buff.append("select ");
+        buff.append(new Expression.Property(columnSelector, 
QueryImpl.JCR_PATH).toString());
+        if (selectors.size() > 1) {
+            buff.append(" as 
").append('[').append(QueryImpl.JCR_PATH).append(']');
+        }
+        buff.append(", ");
+        buff.append(new Expression.Property(columnSelector, 
QueryImpl.JCR_SCORE).toString());
+        if (selectors.size() > 1) {
+            buff.append(" as 
").append('[').append(QueryImpl.JCR_SCORE).append(']');
+        }
+        if (columnList.isEmpty()) {
+            buff.append(", ");
+            buff.append(new Expression.Property(columnSelector, 
"*").toString());
+        } else {
+            for (int i = 0; i < columnList.size(); i++) {
+                buff.append(", ");
+                Expression e = columnList.get(i);
+                String columnName = e.toString();
+                buff.append(columnName);
+                if (selectors.size() > 1) {
+                    buff.append(" as 
[").append(e.getColumnAliasName()).append("]");
+                }
+            }
+        }
+        
+        // from ...
+        buff.append(" from ");
+        for (int i = 0; i < selectors.size(); i++) {
+            Selector s = selectors.get(i);
+            if (i > 0) {
+                buff.append(" inner join ");
+            }
+            String nodeType = s.nodeType;
+            if (nodeType == null) {
+                nodeType = "nt:base";
+            }
+            buff.append('[' + nodeType + ']').append(" as ").append(s.name);
+            if (s.joinCondition != null) {
+                buff.append(" on ").append(s.joinCondition);
+            }
+        }
+        
+        // where ...
+        Expression where = null;
+        for (Selector s : selectors) {
+            where = Expression.and(where, s.condition);
+        }
+        if (where != null) {
+            buff.append(" where ").append(where.toString());
+        }
+        
+        // order by ...
+        if (!orderList.isEmpty()) {
+            buff.append(" order by ");
+            for (int i = 0; i < orderList.size(); i++) {
+                if (i > 0) {
+                    buff.append(", ");
+                }
+                buff.append(orderList.get(i));
+            }
+        }
+
+        // leave original xpath string as a comment
+        buff.append(" /* xpath: ");
+        buff.append(xpathQuery);
+        buff.append(" */");
+        return buff.toString();        
+    }
+
+    public void setExplain(boolean explain) {
+        this.explain = explain;
+    }
+
+    public void setMeasure(boolean measure) {
+        this.measure = measure;
+    }
+
+    public void addSelectColumn(Property p) {
+        columnList.add(p);
+    }
+
+    public void setSelectors(ArrayList<Selector> selectors) {
+        this.selectors = selectors;
+    }
+
+    public void addOrderBy(Order order) {
+        this.orderList.add(order);
+    }
+
+    public void setColumnSelector(Selector columnSelector) {
+        this.columnSelector = columnSelector;
+    }
+    
+    public void setOriginalQuery(String xpathQuery) {
+        this.xpathQuery = xpathQuery;
+    }
+
+}

Copied: 
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/xpath/XPathToSQL2Converter.java
 (from r1535299, 
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/XPathToSQL2Converter.java)
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/xpath/XPathToSQL2Converter.java?p2=jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/xpath/XPathToSQL2Converter.java&p1=jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/XPathToSQL2Converter.java&r1=1535299&r2=1535363&rev=1535363&view=diff
==============================================================================
--- 
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/XPathToSQL2Converter.java
 (original)
+++ 
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/xpath/XPathToSQL2Converter.java
 Thu Oct 24 12:50:13 2013
@@ -14,7 +14,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.jackrabbit.oak.query;
+package org.apache.jackrabbit.oak.query.xpath;
 
 import org.apache.jackrabbit.oak.commons.PathUtils;
 import org.apache.jackrabbit.util.ISO9075;
@@ -63,14 +63,18 @@ public class XPathToSQL2Converter {
      * @throws ParseException if parsing fails
      */
     public String convert(String query) throws ParseException {
+        
         query = query.trim();
-        boolean explain = query.startsWith("explain ");
-        if (explain) {
+        
+        Statement statement = new Statement();
+
+        if (query.startsWith("explain ")) {
             query = query.substring("explain".length()).trim();
+            statement.setExplain(true);
         }
-        boolean measure = query.startsWith("measure");
-        if (measure) {
+        if (query.startsWith("measure")) {
             query = query.substring("measure".length()).trim();
+            statement.setMeasure(true);
         }
         
         if (query.isEmpty()) {
@@ -78,6 +82,8 @@ public class XPathToSQL2Converter {
             query = "//jcr:root";
         }
         
+        statement.setOriginalQuery(query);
+        
         initialize(query);
         
         expected = new ArrayList<String>();
@@ -89,8 +95,6 @@ public class XPathToSQL2Converter {
 
         currentSelector.name = "a";
 
-        ArrayList<Expression> columnList = new ArrayList<Expression>();
-        
         String pathPattern = "";
         boolean startOfQuery = true;
 
@@ -188,23 +192,23 @@ public class XPathToSQL2Converter {
                 }
             } else if (readIf("@")) {
                 rewindSelector();
-                Property p = readProperty();
-                columnList.add(p);
+                Expression.Property p = readProperty();
+                statement.addSelectColumn(p);
             } else if (readIf("rep:excerpt")) {
                 rewindSelector();
                 readExcerpt();
-                Property p = new Property(currentSelector, "rep:excerpt");
-                columnList.add(p);
+                Expression.Property p = new 
Expression.Property(currentSelector, "rep:excerpt");
+                statement.addSelectColumn(p);
             } else if (readIf("(")) {
                 rewindSelector();
                 do {
                     if (readIf("@")) {
-                        Property p = readProperty();
-                        columnList.add(p);
+                        Expression.Property p = readProperty();
+                        statement.addSelectColumn(p);
                     } else if (readIf("rep:excerpt")) {
                         readExcerpt();
-                        Property p = new Property(currentSelector, 
"rep:excerpt");
-                        columnList.add(p);
+                        Expression.Property p = new 
Expression.Property(currentSelector, "rep:excerpt");
+                        statement.addSelectColumn(p);
                     }
                 } while (readIf("|"));
                 read(")");
@@ -241,7 +245,7 @@ public class XPathToSQL2Converter {
             }
             if (readIf("[")) {
                 Expression c = parseConstraint();
-                currentSelector.condition = add(currentSelector.condition, c);
+                currentSelector.condition = 
Expression.and(currentSelector.condition, c);
                 read("]");
             }
             startOfQuery = false;
@@ -256,7 +260,6 @@ public class XPathToSQL2Converter {
         if (selectors.size() == 1) {
             currentSelector.onlySelector = true;
         }
-        ArrayList<Order> orderList = new ArrayList<Order>();
         if (readIf("order")) {
             read("by");
             do {
@@ -267,88 +270,17 @@ public class XPathToSQL2Converter {
                 } else {
                     readIf("ascending");
                 }
-                orderList.add(order);
+                statement.addOrderBy(order);
             } while (readIf(","));
         }
         if (!currentToken.isEmpty()) {
             throw getSyntaxError("<end>");
         }
-        StringBuilder buff = new StringBuilder();
-        
-        // explain | measure ...
-        if (explain) {
-            buff.append("explain ");
-        } else if (measure) {
-            buff.append("measure ");
-        }
-        
-        // select ...
-        buff.append("select ");
-        buff.append(new Property(currentSelector, 
QueryImpl.JCR_PATH).toString());
-        if (selectors.size() > 1) {
-            buff.append(" as 
").append('[').append(QueryImpl.JCR_PATH).append(']');
-        }
-        buff.append(", ");
-        buff.append(new Property(currentSelector, 
QueryImpl.JCR_SCORE).toString());
-        if (selectors.size() > 1) {
-            buff.append(" as 
").append('[').append(QueryImpl.JCR_SCORE).append(']');
-        }
-        if (columnList.isEmpty()) {
-            buff.append(", ");
-            buff.append(new Property(currentSelector, "*").toString());
-        } else {
-            for (int i = 0; i < columnList.size(); i++) {
-                buff.append(", ");
-                Expression e = columnList.get(i);
-                String columnName = e.toString();
-                buff.append(columnName);
-                if (selectors.size() > 1) {
-                    buff.append(" as 
[").append(e.getColumnAliasName()).append("]");
-                }
-            }
-        }
-        
-        // from ...
-        buff.append(" from ");
-        for (int i = 0; i < selectors.size(); i++) {
-            Selector s = selectors.get(i);
-            if (i > 0) {
-                buff.append(" inner join ");
-            }
-            String nodeType = s.nodeType;
-            if (nodeType == null) {
-                nodeType = "nt:base";
-            }
-            buff.append('[' + nodeType + ']').append(" as ").append(s.name);
-            if (s.joinCondition != null) {
-                buff.append(" on ").append(s.joinCondition);
-            }
-        }
+        statement.setColumnSelector(currentSelector);
+        statement.setSelectors(selectors);
         
-        // where ...
-        Expression where = null;
-        for (Selector s : selectors) {
-            where = add(where, s.condition);
-        }
-        if (where != null) {
-            buff.append(" where ").append(where.toString());
-        }
-        // order by ...
-        if (!orderList.isEmpty()) {
-            buff.append(" order by ");
-            for (int i = 0; i < orderList.size(); i++) {
-                if (i > 0) {
-                    buff.append(", ");
-                }
-                buff.append(orderList.get(i));
-            }
-        }
+        return statement.toString();
 
-        // leave original xpath string as a comment
-        buff.append(" /* xpath: ");
-        buff.append(query);
-        buff.append(" */");
-        return buff.toString();
     }
     
     private void appendNodeName(String name) {
@@ -391,15 +323,15 @@ public class XPathToSQL2Converter {
         Expression condition = currentSelector.condition;
         Expression joinCondition = null;
         if (currentSelector.nodeName != null) {
-            Function f = new Function("name");
-            f.params.add(new SelectorExpr(currentSelector));
+            Expression.Function f = new Expression.Function("name");
+            f.params.add(new Expression.SelectorExpr(currentSelector));
             String n = currentSelector.nodeName;
             // encode again, because it will be decoded again
             n = ISO9075.encode(n);
-            Condition c = new Condition(f, "=", 
-                    Literal.newString(n), 
+            Expression.Condition c = new Expression.Condition(f, "=", 
+                    Expression.Literal.newString(n), 
                     Expression.PRECEDENCE_CONDITION);
-            condition = add(condition, c);
+            condition = Expression.and(condition, c);
         }
         if (currentSelector.isDescendant) {
             if (isFirstSelector) {
@@ -407,24 +339,24 @@ public class XPathToSQL2Converter {
                     if (!PathUtils.isAbsolute(path)) {
                         path = PathUtils.concat("/", path);
                     }
-                    Function c = new Function("isdescendantnode");
-                    c.params.add(new SelectorExpr(currentSelector));
-                    c.params.add(Literal.newString(path));
-                    condition = add(condition, c);
+                    Expression.Function c = new 
Expression.Function("isdescendantnode");
+                    c.params.add(new Expression.SelectorExpr(currentSelector));
+                    c.params.add(Expression.Literal.newString(path));
+                    condition = Expression.and(condition, c);
                 }
             } else {
-                Function c = new Function("isdescendantnode");
-                c.params.add(new SelectorExpr(currentSelector));
-                c.params.add(new SelectorExpr(selectors.get(selectors.size() - 
1)));
+                Expression.Function c = new 
Expression.Function("isdescendantnode");
+                c.params.add(new Expression.SelectorExpr(currentSelector));
+                c.params.add(new 
Expression.SelectorExpr(selectors.get(selectors.size() - 1)));
                 joinCondition = c;
             } 
         } else if (currentSelector.isParent) {
             if (isFirstSelector) {
                 throw getSyntaxError();
             } else {
-                Function c = new Function("ischildnode");
-                c.params.add(new SelectorExpr(selectors.get(selectors.size() - 
1)));
-                c.params.add(new SelectorExpr(currentSelector));
+                Expression.Function c = new Expression.Function("ischildnode");
+                c.params.add(new 
Expression.SelectorExpr(selectors.get(selectors.size() - 1)));
+                c.params.add(new Expression.SelectorExpr(currentSelector));
                 joinCondition = c;
             }
         } else if (currentSelector.isChild) {
@@ -433,15 +365,15 @@ public class XPathToSQL2Converter {
                     if (!PathUtils.isAbsolute(path)) {
                         path = PathUtils.concat("/", path);
                     }
-                    Function c = new Function("ischildnode");
-                    c.params.add(new SelectorExpr(currentSelector));
-                    c.params.add(Literal.newString(path));
-                    condition = add(condition, c);
+                    Expression.Function c = new 
Expression.Function("ischildnode");
+                    c.params.add(new Expression.SelectorExpr(currentSelector));
+                    c.params.add(Expression.Literal.newString(path));
+                    condition = Expression.and(condition, c);
                 }
             } else {
-                Function c = new Function("ischildnode");
-                c.params.add(new SelectorExpr(currentSelector));
-                c.params.add(new SelectorExpr(selectors.get(selectors.size() - 
1)));
+                Expression.Function c = new Expression.Function("ischildnode");
+                c.params.add(new Expression.SelectorExpr(currentSelector));
+                c.params.add(new 
Expression.SelectorExpr(selectors.get(selectors.size() - 1)));
                 joinCondition = c;
             }
         } else {
@@ -449,10 +381,10 @@ public class XPathToSQL2Converter {
                 // a child node of a given path, such as "/test"
                 // use the same selector for now, and extend the path
             } else if (PathUtils.isAbsolute(path)) {
-                Function c = new Function("issamenode");
-                c.params.add(new SelectorExpr(currentSelector));
-                c.params.add(Literal.newString(path));
-                condition = add(condition, c);
+                Expression.Function c = new Expression.Function("issamenode");
+                c.params.add(new Expression.SelectorExpr(currentSelector));
+                c.params.add(Expression.Literal.newString(path));
+                condition = Expression.and(condition, c);
             }
         }
         if (force || condition != null || joinCondition != null) {
@@ -463,25 +395,16 @@ public class XPathToSQL2Converter {
             Selector nextSelector = new Selector();
             nextSelector.name = nextSelectorName;
             currentSelector.condition = condition;
-            currentSelector.joinCondition = add(currentSelector.joinCondition, 
joinCondition);
+            currentSelector.joinCondition = 
Expression.and(currentSelector.joinCondition, joinCondition);
             selectors.add(currentSelector);
             currentSelector = nextSelector;
         }
     }
 
-    private static Expression add(Expression old, Expression add) {
-        if (old == null) {
-            return add;
-        } else if (add == null) {
-            return old;
-        }
-        return new Condition(old, "and", add, Expression.PRECEDENCE_AND);
-    }
-
     private Expression parseConstraint() throws ParseException {
         Expression a = parseAnd();
         while (readIf("or")) {
-            a = new Condition(a, "or", parseAnd(), Expression.PRECEDENCE_OR);
+            a = new Expression.Condition(a, "or", parseAnd(), 
Expression.PRECEDENCE_OR);
         }
         return a;
     }
@@ -489,7 +412,7 @@ public class XPathToSQL2Converter {
     private Expression parseAnd() throws ParseException {
         Expression a = parseCondition();
         while (readIf("and")) {
-            a = new Condition(a, "and", parseCondition(), 
Expression.PRECEDENCE_AND);
+            a = new Expression.Condition(a, "and", parseCondition(), 
Expression.PRECEDENCE_AND);
         }
         return a;
     }
@@ -499,13 +422,13 @@ public class XPathToSQL2Converter {
         if (readIf("fn:not") || readIf("not")) {
             read("(");
             a = parseConstraint();
-            if (a instanceof Condition && ((Condition) a).operator.equals("is 
not null")) {
+            if (a instanceof Expression.Condition && ((Expression.Condition) 
a).operator.equals("is not null")) {
                 // not(@property) -> @property is null
-                Condition c = (Condition) a;
-                c = new Condition(c.left, "is null", null, 
Expression.PRECEDENCE_CONDITION);
+                Expression.Condition c = (Expression.Condition) a;
+                c = new Expression.Condition(c.left, "is null", null, 
Expression.PRECEDENCE_CONDITION);
                 a = c;
             } else {
-                Function f = new Function("not");
+                Expression.Function f = new Expression.Function("not");
                 f.params.add(a);
                 a = f;
             }
@@ -523,27 +446,27 @@ public class XPathToSQL2Converter {
         return a;
     }
 
-    private Condition parseCondition(Expression left) throws ParseException {
-        Condition c;
+    private Expression.Condition parseCondition(Expression left) throws 
ParseException {
+        Expression.Condition c;
         if (readIf("=")) {
-            c = new Condition(left, "=", parseExpression(), 
Expression.PRECEDENCE_CONDITION);
+            c = new Expression.Condition(left, "=", parseExpression(), 
Expression.PRECEDENCE_CONDITION);
         } else if (readIf("<>")) {
-            c = new Condition(left, "<>", parseExpression(), 
Expression.PRECEDENCE_CONDITION);
+            c = new Expression.Condition(left, "<>", parseExpression(), 
Expression.PRECEDENCE_CONDITION);
         } else if (readIf("!=")) {
-            c = new Condition(left, "<>", parseExpression(), 
Expression.PRECEDENCE_CONDITION);
+            c = new Expression.Condition(left, "<>", parseExpression(), 
Expression.PRECEDENCE_CONDITION);
         } else if (readIf("<")) {
-            c = new Condition(left, "<", parseExpression(), 
Expression.PRECEDENCE_CONDITION);
+            c = new Expression.Condition(left, "<", parseExpression(), 
Expression.PRECEDENCE_CONDITION);
         } else if (readIf(">")) {
-            c = new Condition(left, ">", parseExpression(), 
Expression.PRECEDENCE_CONDITION);
+            c = new Expression.Condition(left, ">", parseExpression(), 
Expression.PRECEDENCE_CONDITION);
         } else if (readIf("<=")) {
-            c = new Condition(left, "<=", parseExpression(), 
Expression.PRECEDENCE_CONDITION);
+            c = new Expression.Condition(left, "<=", parseExpression(), 
Expression.PRECEDENCE_CONDITION);
         } else if (readIf(">=")) {
-            c = new Condition(left, ">=", parseExpression(), 
Expression.PRECEDENCE_CONDITION);
+            c = new Expression.Condition(left, ">=", parseExpression(), 
Expression.PRECEDENCE_CONDITION);
         // TODO support "x eq y"? it seems this only matches for single value 
properties?  
         // } else if (readIf("eq")) {
         //    c = new Condition(left, "==", parseExpression(), 
Expression.PRECEDENCE_CONDITION);
         } else {
-            c = new Condition(left, "is not null", null, 
Expression.PRECEDENCE_CONDITION);
+            c = new Expression.Condition(left, "is not null", null, 
Expression.PRECEDENCE_CONDITION);
         }
         return c;
     }
@@ -552,22 +475,22 @@ public class XPathToSQL2Converter {
         if (readIf("@")) {
             return readProperty();
         } else if (readIf("true")) {
-            return Literal.newBoolean(true);
+            return Expression.Literal.newBoolean(true);
         } else if (readIf("false")) {
-            return Literal.newBoolean(false);
+            return Expression.Literal.newBoolean(false);
         } else if (currentTokenType == VALUE_NUMBER) {
-            Literal l = Literal.newNumber(currentToken);
+            Expression.Literal l = Expression.Literal.newNumber(currentToken);
             read();
             return l;
         } else if (currentTokenType == VALUE_STRING) {
-            Literal l = Literal.newString(currentToken);
+            Expression.Literal l = Expression.Literal.newString(currentToken);
             read();
             return l;
         } else if (readIf("-")) {
             if (currentTokenType != VALUE_NUMBER) {
                 throw getSyntaxError();
             }
-            Literal l = Literal.newNumber('-' + currentToken);
+            Expression.Literal l = Expression.Literal.newNumber('-' + 
currentToken);
             read();
             return l;
         } else if (readIf("+")) {
@@ -604,7 +527,7 @@ public class XPathToSQL2Converter {
                 } else {
                     buff.append(readIdentifier());
                 }
-                return new Property(currentSelector, buff.toString());
+                return new Expression.Property(currentSelector, 
buff.toString());
             } else {
                 break;
             }
@@ -625,54 +548,54 @@ public class XPathToSQL2Converter {
             } else {
                 buff.append("/*");
             }
-            return new Property(currentSelector, buff.toString());
+            return new Expression.Property(currentSelector, buff.toString());
         }
         throw getSyntaxError();
     }
 
     private Expression parseFunction(String functionName) throws 
ParseException {
         if ("jcr:like".equals(functionName)) {
-            Condition c = new Condition(parseExpression(), 
+            Expression.Condition c = new 
Expression.Condition(parseExpression(), 
                     "like", null, Expression.PRECEDENCE_CONDITION);
             read(",");
             c.right = parseExpression();
             read(")");
             return c;
         } else if ("jcr:contains".equals(functionName)) {
-            Function f = new Function("contains");
+            Expression.Function f = new Expression.Function("contains");
             f.params.add(parseExpression());
             read(",");
             f.params.add(parseExpression());
             read(")");
             return f;
         } else if ("jcr:score".equals(functionName)) {
-            Function f = new Function("score");
-            f.params.add(new SelectorExpr(currentSelector));
+            Expression.Function f = new Expression.Function("score");
+            f.params.add(new Expression.SelectorExpr(currentSelector));
             read(")");
             return f;
         } else if ("xs:dateTime".equals(functionName)) {
             Expression expr = parseExpression();
-            Cast c = new Cast(expr, "date");
+            Expression.Cast c = new Expression.Cast(expr, "date");
             read(")");
             return c;
         } else if ("fn:lower-case".equals(functionName)) {
-            Function f = new Function("lower");
+            Expression.Function f = new Expression.Function("lower");
             f.params.add(parseExpression());
             read(")");
             return f;
         } else if ("fn:upper-case".equals(functionName)) {
-            Function f = new Function("upper");
+            Expression.Function f = new Expression.Function("upper");
             f.params.add(parseExpression());
             read(")");
             return f;
         } else if ("fn:name".equals(functionName)) {
-            Function f = new Function("name");
+            Expression.Function f = new Expression.Function("name");
             if (!readIf(")")) {
                 // only name(.) and name() are currently supported
                 read(".");
                 read(")");
             }
-            f.params.add(new SelectorExpr(currentSelector));
+            f.params.add(new Expression.SelectorExpr(currentSelector));
             return f;
         } else if ("jcr:deref".equals(functionName)) {
              // TODO maybe support jcr:deref
@@ -714,11 +637,11 @@ public class XPathToSQL2Converter {
         read();
     }
 
-    private Property readProperty() throws ParseException {
+    private Expression.Property readProperty() throws ParseException {
         if (readIf("*")) {
-            return new Property(currentSelector, "*");
+            return new Expression.Property(currentSelector, "*");
         }
-        return new Property(currentSelector, readIdentifier());
+        return new Expression.Property(currentSelector, readIdentifier());
     }
     
     private void readExcerpt() throws ParseException {
@@ -1040,382 +963,5 @@ public class XPathToSQL2Converter {
         return new ParseException("Query:\n" + query, index);
     }
 
-    /**
-     * A selector.
-     */
-    static class Selector {
-
-        /**
-         * The selector name.
-         */
-        String name;
-        
-        /**
-         * Whether this is the only selector in the query.
-         */
-        boolean onlySelector;
-        
-        /**
-         * The node type, if set, or null.
-         */
-        String nodeType;
-        
-        /**
-         * Whether this is a child node of the previous selector or a given 
path.
-         * Examples:
-         * <ul><li>/jcr:root/*
-         * </li><li>/jcr:root/test/*
-         * </li><li>/jcr:root/element()
-         * </li><li>/jcr:root/element(*)
-         * </li></ul>
-         */
-        boolean isChild;
-        
-        /**
-         * Whether this is a parent node of the previous selector or given 
path.
-         * Examples:
-         * <ul><li>testroot//child/..[@foo1]
-         * </li><li>/jcr:root/test/descendant/..[@test]
-         * </li></ul>
-         */
-        boolean isParent;
-        
-        /**
-         * Whether this is a descendant of the previous selector or a given 
path.
-         * Examples:
-         * <ul><li>/jcr:root//descendant
-         * </li><li>/jcr:root/test//descendant
-         * </li><li>/jcr:root[@x]
-         * </li><li>/jcr:root (just by itself)
-         * </li></ul>
-         */
-        boolean isDescendant;
-        
-        /**
-         * The path (only used for the first selector).
-         */
-        String path = "";
-        
-        /**
-         * The node name, if set.
-         */
-        String nodeName;
-        
-        /**
-         * The condition for this selector.
-         */
-        Expression condition;
-        
-        /**
-         * The join condition from the previous selector.
-         */
-        Expression joinCondition;
-        
-    }
-
-    /**
-     * An expression.
-     */
-    abstract static class Expression {
-        
-        static final int PRECEDENCE_OR = 1, PRECEDENCE_AND = 2, 
-                PRECEDENCE_CONDITION = 3, PRECEDENCE_OPERAND = 4;
-        
-        /**
-         * Whether this is a condition.
-         * 
-         * @return true if it is 
-         */
-        boolean isCondition() {
-            return false;
-        }
-        
-        /**
-         * Get the operator / operation precedence. The JCR specification uses:
-         * 1=OR, 2=AND, 3=condition, 4=operand  
-         * 
-         * @return the precedence (as an example, multiplication needs to 
return
-         *         a higher number than addition)
-         */
-        int getPrecedence() {
-            return PRECEDENCE_OPERAND;
-        }
-        
-        /**
-         * Get the column alias name of an expression. For a property, this is 
the
-         * property name (no matter how many selectors the query contains); for
-         * other expressions it matches the toString() method.
-         * 
-         * @return the simple column name
-         */
-        String getColumnAliasName() {
-            return toString();
-        }
-        
-        /**
-         * Whether the result of this expression is a name. Names are subject 
to
-         * ISO9075 encoding.
-         * 
-         * @return whether this expression is a name.
-         */
-        boolean isName() {
-            return false;
-        }
-
-    }
-
-    /**
-     * A selector parameter.
-     */
-    static class SelectorExpr extends Expression {
-
-        private final Selector selector;
-
-        SelectorExpr(Selector selector) {
-            this.selector = selector;
-        }
-
-        @Override
-        public String toString() {
-            return selector.name;
-        }
-
-    }
-
-    /**
-     * A literal expression.
-     */
-    static class Literal extends Expression {
-
-        final String value;
-        final String rawText;
-
-        Literal(String value, String rawText) {
-            this.value = value;
-            this.rawText = rawText;
-        }
-
-        public static Expression newBoolean(boolean value) {
-            return new Literal(String.valueOf(value), String.valueOf(value));
-        }
-
-        static Literal newNumber(String s) {
-            return new Literal(s, s);
-        }
-
-        static Literal newString(String s) {
-            return new Literal(SQL2Parser.escapeStringLiteral(s), s);
-        }
-
-        @Override
-        public String toString() {
-            return value;
-        }
-
-    }
-
-    /**
-     * A property expression.
-     */
-    static class Property extends Expression {
-
-        final Selector selector;
-        final String name;
-
-        Property(Selector selector, String name) {
-            this.selector = selector;
-            this.name = name;
-        }
-
-        @Override
-        public String toString() {
-            StringBuilder buff = new StringBuilder();
-            if (!selector.onlySelector) {
-                buff.append(selector.name).append('.');
-            }
-            if (name.equals("*")) {
-                buff.append('*');
-            } else {
-                buff.append('[').append(name).append(']');
-            }
-            return buff.toString();
-        }
-        
-        @Override
-        public String getColumnAliasName() {
-            return name;
-        }
-
-    }
-
-    /**
-     * A condition.
-     */
-    static class Condition extends Expression {
-
-        final Expression left;
-        final String operator;
-        Expression right;
-        final int precedence;
-
-        /**
-         * Create a new condition.
-         * 
-         * @param left the left hand side operator, or null
-         * @param operator the operator
-         * @param right the right hand side operator, or null
-         * @param precedence the operator precedence 
(Expression.PRECEDENCE_...)
-         */
-        Condition(Expression left, String operator, Expression right, int 
precedence) {
-            this.left = left;
-            this.operator = operator;
-            this.right = right;
-            this.precedence = precedence;
-        }
-        
-        @Override
-        int getPrecedence() {
-            return precedence;
-        }
-
-        @Override
-        public String toString() {
-            String leftExpr;
-            boolean leftExprIsName;
-            if (left == null) {
-                leftExprIsName = false;
-                leftExpr = "";
-            } else {
-                leftExprIsName = left.isName();
-                leftExpr = left.toString();
-                if (left.getPrecedence() < precedence) {
-                    leftExpr = "(" + leftExpr + ")";
-                }
-            }
-            boolean impossible = false;
-            String rightExpr;
-            if (right == null) {
-                rightExpr = "";
-            } else {
-                if (leftExprIsName && !"like".equals(operator)) {
-                    // need to de-escape _x0020_ and so on
-                    if (!(right instanceof Literal)) {
-                        throw new IllegalArgumentException(
-                                "Can only compare a name against a string 
literal, not " + right);
-                    }
-                    Literal l = (Literal) right;
-                    String raw = l.rawText;
-                    String decoded = ISO9075.decode(raw);
-                    String encoded = ISO9075.encode(decoded);
-                    rightExpr = SQL2Parser.escapeStringLiteral(decoded);
-                    if (!encoded.toUpperCase().equals(raw.toUpperCase())) {
-                        // nothing can potentially match
-                        impossible = true;
-                    }
-                } else {
-                    rightExpr = right.toString();
-                }
-                if (right.getPrecedence() < precedence) {
-                    rightExpr = "(" + right + ")";
-                }
-            }
-            if (impossible) {
-                // a condition that can not possibly be true
-                return "upper(" + leftExpr + ") = 'never matches'";
-            }
-            return (leftExpr + " " + operator + " " + rightExpr).trim();
-        }
-
-        @Override
-        boolean isCondition() {
-            return true;
-        }
-
-    }
-
-    /**
-     * A function call.
-     */
-    static class Function extends Expression {
-
-        final String name;
-        final ArrayList<Expression> params = new ArrayList<Expression>();
-
-        Function(String name) {
-            this.name = name;
-        }
-
-        @Override
-        public String toString() {
-            StringBuilder buff = new StringBuilder(name);
-            buff.append('(');
-            for (int i = 0; i < params.size(); i++) {
-                if (i > 0) {
-                    buff.append(", ");
-                }
-                buff.append(params.get(i).toString());
-            }
-            buff.append(')');
-            return buff.toString();
-        }
-
-        @Override
-        boolean isCondition() {
-            return name.equals("contains") || name.equals("not");
-        }
-        
-        @Override
-        boolean isName() {
-            if ("upper".equals(name) || "lower".equals(name)) {
-                return params.get(0).isName();
-            }
-            return "name".equals(name);
-        }
-
-    }
-
-    /**
-     * A cast operation.
-     */
-    static class Cast extends Expression {
-
-        final Expression expr;
-        final String type;
-
-        Cast(Expression expr, String type) {
-            this.expr = expr;
-            this.type = type;
-        }
-
-        @Override
-        public String toString() {
-            StringBuilder buff = new StringBuilder("cast(");
-            buff.append(expr.toString());
-            buff.append(" as ").append(type).append(')');
-            return buff.toString();
-        }
-
-        @Override
-        boolean isCondition() {
-            return false;
-        }
-
-    }
-
-    /**
-     * An order by expression.
-     */
-    static class Order {
-
-        boolean descending;
-        Expression expr;
-
-        @Override
-        public String toString() {
-            return expr + (descending ? " desc" : "");
-        }
-
-    }
-
 }
 

Modified: 
jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/query/AbstractQueryTest.java
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/query/AbstractQueryTest.java?rev=1535363&r1=1535362&r2=1535363&view=diff
==============================================================================
--- 
jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/query/AbstractQueryTest.java
 (original)
+++ 
jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/query/AbstractQueryTest.java
 Thu Oct 24 12:50:13 2013
@@ -59,6 +59,7 @@ import org.apache.jackrabbit.oak.kernel.
 import org.apache.jackrabbit.oak.plugins.memory.BooleanPropertyState;
 import org.apache.jackrabbit.oak.plugins.memory.StringPropertyState;
 import org.apache.jackrabbit.oak.plugins.value.Conversions;
+import org.apache.jackrabbit.oak.query.xpath.XPathToSQL2Converter;
 import org.junit.Before;
 
 /**

Modified: 
jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/query/SQL2ParserTest.java
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/query/SQL2ParserTest.java?rev=1535363&r1=1535362&r2=1535363&view=diff
==============================================================================
--- 
jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/query/SQL2ParserTest.java
 (original)
+++ 
jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/query/SQL2ParserTest.java
 Thu Oct 24 12:50:13 2013
@@ -23,6 +23,7 @@ import static org.apache.jackrabbit.oak.
 
 import java.text.ParseException;
 
+import org.apache.jackrabbit.oak.query.xpath.XPathToSQL2Converter;
 import org.apache.jackrabbit.oak.spi.state.NodeState;
 import org.junit.Test;
 


Reply via email to