jackrabbit-oak-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From thom...@apache.org
Subject svn commit: r1302927 [2/4] - in /jackrabbit/oak/trunk/oak-core/src: main/java/org/apache/jackrabbit/oak/query/ main/java/org/apache/jackrabbit/oak/query/ast/ main/java/org/apache/jackrabbit/oak/query/index/ test/java/org/apache/jackrabbit/oak/ test/jav...
Date Tue, 20 Mar 2012 15:05:06 GMT
Added: 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/XPathToSQL2Converter.java?rev=1302927&view=auto
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/XPathToSQL2Converter.java (added)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/XPathToSQL2Converter.java Tue Mar 20 15:05:04 2012
@@ -0,0 +1,732 @@
+/*
+ * 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;
+
+import org.apache.jackrabbit.mk.util.PathUtils;
+
+import java.math.BigDecimal;
+import java.text.ParseException;
+import java.util.ArrayList;
+
+/**
+ * This class can can convert a XPATH query to a SQL2 query.
+ */
+public class XPathToSQL2Converter {
+
+    // Character types, used during the tokenizer phase
+    private static final int CHAR_END = -1, CHAR_VALUE = 2;
+    private static final int CHAR_NAME = 4, CHAR_SPECIAL_1 = 5, CHAR_SPECIAL_2 = 6;
+    private static final int CHAR_STRING = 7, CHAR_DECIMAL = 8;
+
+    // Token types
+    private static final int KEYWORD = 1, IDENTIFIER = 2, END = 4, VALUE_STRING = 5, VALUE_NUMBER = 6;
+    private static final int MINUS = 12, PLUS = 13, OPEN = 14, CLOSE = 15;
+
+    // The query as an array of characters and character types
+    private String statement;
+    private char[] statementChars;
+    private int[] characterTypes;
+
+    // The current state of the parser
+    private int parseIndex;
+    private int currentTokenType;
+    private String currentToken;
+    private boolean currentTokenQuoted;
+    private ArrayList<String> expected;
+
+    /**
+     * Convert the query to SQL2.
+     *
+     * @param query the query string
+     * @return the SQL2 query
+     * @throws ParseException if parsing fails
+     */
+    public String convert(String query) throws ParseException {
+        initialize(query);
+        expected = new ArrayList<String>();
+        read();
+        String path = "";
+        Expression condition = null;
+        String from = "nt:base";
+        ArrayList<Expression> columnList = new ArrayList<Expression>();
+        boolean includeChildren = true;
+        while (true) {
+            String nodeType;
+            if (readIf("/")) {
+                if (readIf("/")) {
+                    includeChildren = true;
+                } else if (readIf("jcr:root")) {
+                    path = "/";
+                    read("/");
+                } else {
+                    includeChildren = false;
+                }
+                if (readIf("*")) {
+                    nodeType = "nt:base";
+                    from = nodeType;
+                } else if (readIf("element")) {
+                    read("(");
+                    if (readIf("*")) {
+                        // any
+                        if (!includeChildren) {
+                            path = PathUtils.concat(path, "%");
+                        }
+                    } else {
+                        String name = readIdentifier();
+                        path = PathUtils.concat(path, name);
+                    }
+                    read(",");
+                    nodeType = readIdentifier();
+                    from = nodeType;
+                    read(")");
+                } else if (readIf("@")) {
+                    Property p = new Property(readIdentifier());
+                    columnList.add(p);
+                } else if (readIf("(")) {
+                    do {
+                        read("@");
+                        Property p = new Property(readIdentifier());
+                        columnList.add(p);
+                    } while (readIf("|"));
+                    read(")");
+                } else {
+                    String name = readIdentifier();
+                    path = PathUtils.concat(path, name);
+                    continue;
+                }
+                if (readIf("[")) {
+                    Expression c = parseConstraint();
+                    condition = add(condition, c);
+                    read("]");
+                }
+            } else {
+                break;
+            }
+        }
+        if (path.isEmpty()) {
+            // no condition
+        } else if (path.equals("%")) {
+            // ignore
+        } else {
+            Condition c = new Condition(new Property("jcr:path"), "like", Literal.newString(path));
+            if (!includeChildren && path.endsWith("%")) {
+                Condition c2 = new Condition(new Property("jcr:path"), "like", Literal.newString(path + '/'));
+                c = new Condition(c, "and", new Condition(null, "not", c2));
+            } else if (includeChildren && !path.endsWith("%")) {
+                Condition c2 = new Condition(new Property("jcr:path"), "like", Literal.newString(path + "/%"));
+                c = new Condition(c, "or", c2);
+            }
+            condition = add(condition, c);
+        }
+        ArrayList<Order> orderList = new ArrayList<Order>();
+        if (readIf("order")) {
+            read("by");
+            do {
+                Order order = new Order();
+                order.expr = parseExpression();
+                if (readIf("descending")) {
+                    order.descending = true;
+                } else {
+                    readIf("ascending");
+                }
+                orderList.add(order);
+            } while (readIf(","));
+        }
+        if (!currentToken.isEmpty()) {
+            throw getSyntaxError("<end>");
+        }
+        StringBuilder buff = new StringBuilder("select ");
+        if (columnList.isEmpty()) {
+            buff.append('*');
+        } else {
+            for (int i = 0; i < columnList.size(); i++) {
+                if (i > 0) {
+                    buff.append(", ");
+                }
+                buff.append(columnList.get(i).toString());
+            }
+        }
+        buff.append(" from ");
+        buff.append('[' + from + ']');
+        if (condition != null) {
+            buff.append(" where ").append(condition);
+        }
+        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 buff.toString();
+    }
+
+    private static Expression add(Expression old, Expression add) {
+        if (old == null) {
+            return add;
+        }
+        return new Condition(old, "and", add);
+    }
+
+    private Expression parseConstraint() throws ParseException {
+        Expression a = parseAnd();
+        while (readIf("or")) {
+            a = new Condition(a, "or", parseAnd());
+        }
+        return a;
+    }
+
+    private Expression parseAnd() throws ParseException {
+        Expression a = parseCondition();
+        while (readIf("and")) {
+            a = new Condition(a, "and", parseCondition());
+        }
+        return a;
+    }
+
+    private Expression parseCondition() throws ParseException {
+        Expression a;
+        if (readIf("not")) {
+            read("(");
+            a = parseConstraint();
+            if (a instanceof Condition && ((Condition) a).operator.equals("is not null")) {
+                // not(@property) -> @property is null
+                Condition c = (Condition) a;
+                c = new Condition(c.left, "is null", null);
+                a = c;
+            } else {
+                Function f = new Function();
+                f.name = "not";
+                f.params.add(a);
+                a = f;
+            }
+            read(")");
+        } else if (readIf("(")) {
+            a = new Parenthesis(parseConstraint());
+            read(")");
+        } else {
+            Expression e = parseExpression();
+            if (e.isCondition()) {
+                return e;
+            }
+            a = parseCondition(e);
+        }
+        return a;
+    }
+
+    private Condition parseCondition(Expression left) throws ParseException {
+        Condition c;
+        if (readIf("=")) {
+            c = new Condition(left, "=", parseExpression());
+        } else if (readIf("<>")) {
+            c = new Condition(left, "<>", parseExpression());
+        } else if (readIf("<")) {
+            c = new Condition(left, "<", parseExpression());
+        } else if (readIf(">")) {
+            c = new Condition(left, ">", parseExpression());
+        } else if (readIf("<=")) {
+            c = new Condition(left, "<=", parseExpression());
+        } else if (readIf(">=")) {
+            c = new Condition(left, ">=", parseExpression());
+        } else {
+            c = new Condition(left, "is not null", null);
+        }
+        return c;
+    }
+
+    private Expression parseExpression() throws ParseException {
+        if (readIf("@")) {
+            return new Property(readIdentifier());
+        } else if (currentTokenType == VALUE_NUMBER) {
+            Literal l = Literal.newNumber(currentToken);
+            read();
+            return l;
+        } else if (currentTokenType == VALUE_STRING) {
+            Literal l = Literal.newString(currentToken);
+            read();
+            return l;
+        } else if (currentTokenType == IDENTIFIER) {
+            String name = readIdentifier();
+            read("(");
+            return parseFunction(name);
+        } else if (readIf("-")) {
+            if (currentTokenType != VALUE_NUMBER) {
+                throw getSyntaxError();
+            }
+            Literal l = Literal.newNumber('-' + currentToken);
+            read();
+            return l;
+        } else if (readIf("+")) {
+            if (currentTokenType != VALUE_NUMBER) {
+                throw getSyntaxError();
+            }
+            return parseExpression();
+        } else {
+            throw getSyntaxError();
+        }
+    }
+
+    private Expression parseFunction(String functionName) throws ParseException {
+        if ("jcr:like".equals(functionName)) {
+            Condition c = new Condition(parseExpression(), "like", null);
+            read(",");
+            c.right = parseExpression();
+            read(")");
+            return c;
+        } else if ("jcr:contains".equals(functionName)) {
+            Function f = new Function();
+            f.name = "contains";
+            if (readIf(".")) {
+                // special case: jcr:contains(., expr)
+                f.params.add(new Literal("*"));
+            } else {
+                f.params.add(parseExpression());
+            }
+            read(",");
+            f.params.add(parseExpression());
+            read(")");
+            return f;
+        } else if ("jcr:score".equals(functionName)) {
+            Function f = new Function();
+            f.name = "score";
+            // TODO score: support parameters?
+            read(")");
+            return f;
+        // } else if ("jcr:deref".equals(functionName)) {
+            // TODO support jcr:deref?
+        } else {
+            throw getSyntaxError("jcr:like | jcr:contains | jcr:score | jcr:deref");
+        }
+    }
+
+    private boolean readIf(String token) throws ParseException {
+        if (isToken(token)) {
+            read();
+            return true;
+        }
+        return false;
+    }
+
+    private boolean isToken(String token) {
+        boolean result = token.equals(currentToken) && !currentTokenQuoted;
+        if (result) {
+            return true;
+        }
+        addExpected(token);
+        return false;
+    }
+
+    private void read(String expected) throws ParseException {
+        if (!expected.equals(currentToken) || currentTokenQuoted) {
+            throw getSyntaxError(expected);
+        }
+        read();
+    }
+
+    private String readIdentifier() throws ParseException {
+        if (currentTokenType != IDENTIFIER) {
+            throw getSyntaxError("identifier");
+        }
+        String s = currentToken;
+        read();
+        return s;
+    }
+
+    private void addExpected(String token) {
+        if (expected != null) {
+            expected.add(token);
+        }
+    }
+
+    private void initialize(String query) throws ParseException {
+        if (query == null) {
+            query = "";
+        }
+        statement = query;
+        int len = query.length() + 1;
+        char[] command = new char[len];
+        int[] types = new int[len];
+        len--;
+        query.getChars(0, len, command, 0);
+        command[len] = ' ';
+        int startLoop = 0;
+        for (int i = 0; i < len; i++) {
+            char c = command[i];
+            int type = 0;
+            switch (c) {
+            case '@':
+            case '|':
+            case '/':
+            case '-':
+            case '(':
+            case ')':
+            case '{':
+            case '}':
+            case '*':
+            case ',':
+            case ';':
+            case '+':
+            case '%':
+            case '?':
+            case '$':
+            case '[':
+            case ']':
+                type = CHAR_SPECIAL_1;
+                break;
+            case '!':
+            case '<':
+            case '>':
+            case '=':
+                type = CHAR_SPECIAL_2;
+                break;
+            case '.':
+                type = CHAR_DECIMAL;
+                break;
+            case '\'':
+                type = CHAR_STRING;
+                types[i] = CHAR_STRING;
+                startLoop = i;
+                while (command[++i] != '\'') {
+                    checkRunOver(i, len, startLoop);
+                }
+                break;
+            case ':':
+            case '_':
+                type = CHAR_NAME;
+                break;
+            default:
+                if (c >= 'a' && c <= 'z') {
+                    type = CHAR_NAME;
+                } else if (c >= 'A' && c <= 'Z') {
+                    type = CHAR_NAME;
+                } else if (c >= '0' && c <= '9') {
+                    type = CHAR_VALUE;
+                } else {
+                    if (Character.isJavaIdentifierPart(c)) {
+                        type = CHAR_NAME;
+                    }
+                }
+            }
+            types[i] = (byte) type;
+        }
+        statementChars = command;
+        types[len] = CHAR_END;
+        characterTypes = types;
+        parseIndex = 0;
+    }
+
+    private void checkRunOver(int i, int len, int startLoop) throws ParseException {
+        if (i >= len) {
+            parseIndex = startLoop;
+            throw getSyntaxError();
+        }
+    }
+
+    private void read() throws ParseException {
+        currentTokenQuoted = false;
+        if (expected != null) {
+            expected.clear();
+        }
+        int[] types = characterTypes;
+        int i = parseIndex;
+        int type = types[i];
+        while (type == 0) {
+            type = types[++i];
+        }
+        int start = i;
+        char[] chars = statementChars;
+        char c = chars[i++];
+        currentToken = "";
+        switch (type) {
+        case CHAR_NAME:
+            while (true) {
+                type = types[i];
+                if (type != CHAR_NAME && type != CHAR_VALUE) {
+                    c = chars[i];
+                    break;
+                }
+                i++;
+            }
+            currentToken = statement.substring(start, i);
+            if (currentToken.isEmpty()) {
+                throw getSyntaxError();
+            }
+            currentTokenType = IDENTIFIER;
+            parseIndex = i;
+            return;
+        case CHAR_SPECIAL_2:
+            if (types[i] == CHAR_SPECIAL_2) {
+                i++;
+            }
+            // fall through
+        case CHAR_SPECIAL_1:
+            currentToken = statement.substring(start, i);
+            switch (c) {
+            case '+':
+                currentTokenType = PLUS;
+                break;
+            case '-':
+                currentTokenType = MINUS;
+                break;
+            case '(':
+                currentTokenType = OPEN;
+                break;
+            case ')':
+                currentTokenType = CLOSE;
+                break;
+            default:
+                currentTokenType = KEYWORD;
+            }
+            parseIndex = i;
+            return;
+        case CHAR_VALUE:
+            long number = c - '0';
+            while (true) {
+                c = chars[i];
+                if (c < '0' || c > '9') {
+                    if (c == '.') {
+                        readDecimal(start, i);
+                        break;
+                    }
+                    if (c == 'E' || c == 'e') {
+                        readDecimal(start, i);
+                        break;
+                    }
+                    currentTokenType = VALUE_NUMBER;
+                    currentToken = String.valueOf(number);
+                    parseIndex = i;
+                    break;
+                }
+                number = number * 10 + (c - '0');
+                if (number > Integer.MAX_VALUE) {
+                    readDecimal(start, i);
+                    break;
+                }
+                i++;
+            }
+            return;
+        case CHAR_DECIMAL:
+            if (types[i] != CHAR_VALUE) {
+                currentTokenType = KEYWORD;
+                currentToken = ".";
+                parseIndex = i;
+                return;
+            }
+            readDecimal(i - 1, i);
+            return;
+        case CHAR_STRING:
+            readString(i, '\'');
+            return;
+        case CHAR_END:
+            currentToken = "";
+            currentTokenType = END;
+            parseIndex = i;
+            return;
+        default:
+            throw getSyntaxError();
+        }
+    }
+
+    private void readString(int i, char end) {
+        char[] chars = statementChars;
+        String result = null;
+        while (true) {
+            for (int begin = i;; i++) {
+                if (chars[i] == end) {
+                    if (result == null) {
+                        result = statement.substring(begin, i);
+                    } else {
+                        result += statement.substring(begin - 1, i);
+                    }
+                    break;
+                }
+            }
+            if (chars[++i] != end) {
+                break;
+            }
+            i++;
+        }
+        currentToken = result;
+        parseIndex = i;
+        currentTokenType = VALUE_STRING;
+    }
+
+    private void readDecimal(int start, int i) throws ParseException {
+        char[] chars = statementChars;
+        int[] types = characterTypes;
+        while (true) {
+            int t = types[i];
+            if (t != CHAR_DECIMAL && t != CHAR_VALUE) {
+                break;
+            }
+            i++;
+        }
+        if (chars[i] == 'E' || chars[i] == 'e') {
+            i++;
+            if (chars[i] == '+' || chars[i] == '-') {
+                i++;
+            }
+            if (types[i] != CHAR_VALUE) {
+                throw getSyntaxError();
+            }
+            while (types[++i] == CHAR_VALUE) {
+                // go until the first non-number
+            }
+        }
+        parseIndex = i;
+        String sub = statement.substring(start, i);
+        try {
+            new BigDecimal(sub);
+        } catch (NumberFormatException e) {
+            throw new ParseException("Data conversion error converting " + sub + " to BigDecimal: " + e, i);
+        }
+        currentToken = sub;
+        currentTokenType = VALUE_NUMBER;
+    }
+
+    private ParseException getSyntaxError() {
+        if (expected == null || expected.isEmpty()) {
+            return getSyntaxError(null);
+        } else {
+            StringBuilder buff = new StringBuilder();
+            for (String exp : expected) {
+                if (buff.length() > 0) {
+                    buff.append(", ");
+                }
+                buff.append(exp);
+            }
+            return getSyntaxError(buff.toString());
+        }
+    }
+
+    private ParseException getSyntaxError(String expected) {
+        int index = Math.min(parseIndex, statement.length() - 1);
+        String query = statement.substring(0, index) + "(*)" + statement.substring(index).trim();
+        if (expected != null) {
+            query += "; expected: " + expected;
+        }
+        return new ParseException("Query:\n" + query, index);
+    }
+
+    abstract static class Expression {
+        boolean isCondition() {
+            return false;
+        }
+    }
+
+    static class Literal extends Expression {
+        final String value;
+        Literal(String value) {
+            this.value = value;
+        }
+        static Literal newNumber(String s) {
+            return new Literal(s);
+        }
+        static Literal newString(String s) {
+            return new Literal(SQL2Parser.escapeStringLiteral(s));
+        }
+
+        @Override
+        public String toString() {
+            return value;
+        }
+    }
+
+    static class Property extends Expression {
+        final String name;
+        Property(String name) {
+            this.name = name;
+        }
+
+        @Override
+        public String toString() {
+            return '[' + name + ']';
+        }
+    }
+
+    static class Parenthesis extends Expression {
+        final Expression expr;
+        public Parenthesis(Expression expr) {
+            this.expr = expr;
+        }
+
+        @Override
+        public String toString() {
+            return "(" + expr + ')';
+        }
+    }
+
+    static class Condition extends Expression {
+        final Expression left;
+        final String operator;
+        Expression right;
+        Condition(Expression left, String operator, Expression right) {
+            this.left = left;
+            this.operator = operator;
+            this.right = right;
+        }
+
+        @Override
+        public String toString() {
+            return
+                (left == null ? "" : left + " ") +
+                operator +
+                (right == null ? "" : " " + right);
+        }
+        @Override
+        boolean isCondition() {
+            return true;
+        }
+    }
+
+    static class Function extends Expression {
+        String name;
+        final ArrayList<Expression> params = new ArrayList<Expression>();
+
+        @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));
+            }
+            buff.append(')');
+            return buff.toString();
+        }
+        @Override
+        boolean isCondition() {
+            return name.equals("contains") || name.equals("not");
+        }
+    }
+
+    static class Order {
+        boolean descending;
+        Expression expr;
+
+        @Override
+        public String toString() {
+            return expr + (descending ? " desc" : "");
+        }
+    }
+
+}
+

Added: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/AndImpl.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/AndImpl.java?rev=1302927&view=auto
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/AndImpl.java (added)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/AndImpl.java Tue Mar 20 15:05:04 2012
@@ -0,0 +1,62 @@
+/*
+ * 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.ast;
+
+import org.apache.jackrabbit.oak.query.index.Filter;
+
+public class AndImpl extends ConstraintImpl {
+
+    private final ConstraintImpl constraint1, constraint2;
+
+    public AndImpl(ConstraintImpl constraint1, ConstraintImpl constraint2) {
+        this.constraint1 = constraint1;
+        this.constraint2 = constraint2;
+    }
+
+    public ConstraintImpl getConstraint1() {
+        return constraint1;
+    }
+
+    public ConstraintImpl getConstraint2() {
+        return constraint2;
+    }
+
+    @Override
+    public boolean evaluate() {
+        return constraint1.evaluate() && constraint2.evaluate();
+    }
+
+    @Override
+    boolean accept(AstVisitor v) {
+        return v.visit(this);
+    }
+
+    @Override
+    public String toString() {
+        return protect(constraint1) + " AND " + protect(constraint2);
+    }
+
+    @Override
+    public void apply(Filter f) {
+        constraint1.apply(f);
+        constraint2.apply(f);
+    }
+
+}
+

Added: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/AstElement.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/AstElement.java?rev=1302927&view=auto
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/AstElement.java (added)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/AstElement.java Tue Mar 20 15:05:04 2012
@@ -0,0 +1,47 @@
+/*
+ * 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.ast;
+
+import org.apache.jackrabbit.oak.query.Query;
+
+abstract class AstElement {
+
+    protected Query query;
+
+    abstract boolean accept(AstVisitor v);
+
+    protected String protect(Object expression) {
+        String str = expression.toString();
+        if (str.indexOf(' ') >= 0) {
+            return '(' + str + ')';
+        } else {
+            return str;
+        }
+    }
+
+    protected String quotePath(String path) {
+        return '[' + path + ']';
+    }
+
+    public void setQuery(Query query) {
+        this.query = query;
+    }
+
+}
+

Added: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/AstElementFactory.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/AstElementFactory.java?rev=1302927&view=auto
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/AstElementFactory.java (added)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/AstElementFactory.java Tue Mar 20 15:05:04 2012
@@ -0,0 +1,135 @@
+/*
+ * 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.ast;
+
+import org.apache.jackrabbit.oak.query.Value;
+
+/**
+ * A factory for syntax tree elements.
+ */
+public class AstElementFactory {
+
+    public AndImpl and(ConstraintImpl constraint1, ConstraintImpl constraint2) {
+        return new AndImpl((ConstraintImpl) constraint1, (ConstraintImpl) constraint2);
+    }
+
+    public OrderingImpl ascending(DynamicOperandImpl operand) {
+        return new OrderingImpl((DynamicOperandImpl) operand, Order.ASCENDING);
+    }
+
+    public BindVariableValueImpl bindVariable(String bindVariableName) {
+        return new BindVariableValueImpl(bindVariableName);
+    }
+
+    public ChildNodeImpl childNode(String selectorName, String path) {
+        return new ChildNodeImpl(selectorName, path);
+    }
+
+    public ChildNodeJoinConditionImpl childNodeJoinCondition(String childSelectorName, String parentSelectorName)
+            {
+        return new ChildNodeJoinConditionImpl(childSelectorName, parentSelectorName);
+    }
+
+    public ColumnImpl column(String selectorName, String propertyName, String columnName) {
+        return new ColumnImpl(selectorName, propertyName, columnName);
+    }
+
+    public ComparisonImpl comparison(DynamicOperandImpl operand1, Operator operator, StaticOperandImpl operand2) {
+        return new ComparisonImpl((DynamicOperandImpl) operand1, operator, (StaticOperandImpl) operand2);
+    }
+
+    public DescendantNodeImpl descendantNode(String selectorName, String path) {
+        return new DescendantNodeImpl(selectorName, path);
+    }
+
+    public DescendantNodeJoinConditionImpl descendantNodeJoinCondition(String descendantSelectorName,
+            String ancestorSelectorName) {
+        return new DescendantNodeJoinConditionImpl(descendantSelectorName, ancestorSelectorName);
+    }
+
+    public OrderingImpl descending(DynamicOperandImpl operand) {
+        return new OrderingImpl((DynamicOperandImpl) operand, Order.DESCENDING);
+    }
+
+    public EquiJoinConditionImpl equiJoinCondition(String selector1Name, String property1Name, String selector2Name,
+            String property2Name) {
+        return new EquiJoinConditionImpl(selector1Name, property1Name, selector2Name, property2Name);
+    }
+
+    public FullTextSearchImpl fullTextSearch(String selectorName, String propertyName,
+            StaticOperandImpl fullTextSearchExpression) {
+        return new FullTextSearchImpl(selectorName, propertyName, fullTextSearchExpression);
+    }
+
+    public FullTextSearchScoreImpl fullTextSearchScore(String selectorName) {
+        return new FullTextSearchScoreImpl(selectorName);
+    }
+
+    public JoinImpl join(SourceImpl left, SourceImpl right, JoinType joinType, JoinConditionImpl joinCondition) {
+        return new JoinImpl(left, right, joinType, joinCondition);
+    }
+
+    public LengthImpl length(PropertyValueImpl propertyValue) {
+        return new LengthImpl(propertyValue);
+    }
+
+    public LiteralImpl literal(Value literalValue) {
+        return new LiteralImpl(literalValue);
+    }
+
+    public LowerCaseImpl lowerCase(DynamicOperandImpl operand) {
+        return new LowerCaseImpl((DynamicOperandImpl) operand);
+    }
+
+    public NodeLocalNameImpl nodeLocalName(String selectorName) {
+        return new NodeLocalNameImpl(selectorName);
+    }
+
+    public NodeNameImpl nodeName(String selectorName) {
+        return new NodeNameImpl(selectorName);
+    }
+
+    public NotImpl not(ConstraintImpl constraint) {
+        return new NotImpl((ConstraintImpl) constraint);
+    }
+
+    public OrImpl or(ConstraintImpl constraint1, ConstraintImpl constraint2) {
+        return new OrImpl((ConstraintImpl) constraint1, (ConstraintImpl) constraint2);
+    }
+
+    public PropertyExistenceImpl propertyExistence(String selectorName, String propertyName) {
+        return new PropertyExistenceImpl(selectorName, propertyName);
+    }
+
+    public PropertyValueImpl propertyValue(String selectorName, String propertyName) {
+        return new PropertyValueImpl(selectorName, propertyName);
+    }
+
+    public SameNodeImpl sameNode(String selectorName, String path) {
+        return new SameNodeImpl(selectorName, path);
+    }
+
+    public SameNodeJoinConditionImpl sameNodeJoinCondition(String selector1Name, String selector2Name, String selector2Path) {
+        return new SameNodeJoinConditionImpl(selector1Name, selector2Name, selector2Path);
+    }
+
+    public SelectorImpl selector(String nodeTypeName, String selectorName) {
+        return new SelectorImpl(nodeTypeName, selectorName);
+    }
+
+    public UpperCaseImpl upperCase(DynamicOperandImpl operand) {
+        return new UpperCaseImpl((DynamicOperandImpl) operand);
+    }
+
+}

Added: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/AstVisitor.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/AstVisitor.java?rev=1302927&view=auto
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/AstVisitor.java (added)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/AstVisitor.java Tue Mar 20 15:05:04 2012
@@ -0,0 +1,75 @@
+/*
+ * 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.ast;
+
+public interface AstVisitor {
+
+    boolean visit(AndImpl node);
+
+    boolean visit(BindVariableValueImpl node);
+
+    boolean visit(ChildNodeImpl node);
+
+    boolean visit(ChildNodeJoinConditionImpl node);
+
+    boolean visit(ColumnImpl node);
+
+    boolean visit(ComparisonImpl node);
+
+    boolean visit(DescendantNodeImpl node);
+
+    boolean visit(DescendantNodeJoinConditionImpl node);
+
+    boolean visit(EquiJoinConditionImpl node);
+
+    boolean visit(FullTextSearchImpl node);
+
+    boolean visit(FullTextSearchScoreImpl node);
+
+    boolean visit(JoinImpl node);
+
+    boolean visit(LengthImpl node);
+
+    boolean visit(LiteralImpl node);
+
+    boolean visit(LowerCaseImpl node);
+
+    boolean visit(NodeLocalNameImpl node);
+
+    boolean visit(NodeNameImpl node);
+
+    boolean visit(NotImpl node);
+
+    boolean visit(OrderingImpl node);
+
+    boolean visit(OrImpl node);
+
+    boolean visit(PropertyExistenceImpl node);
+
+    boolean visit(PropertyValueImpl node);
+
+    boolean visit(SameNodeImpl node);
+
+    boolean visit(SameNodeJoinConditionImpl node);
+
+    boolean visit(SelectorImpl node);
+
+    boolean visit(UpperCaseImpl node);
+
+}
\ No newline at end of file

Added: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/AstVisitorBase.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/AstVisitorBase.java?rev=1302927&view=auto
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/AstVisitorBase.java (added)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/AstVisitorBase.java Tue Mar 20 15:05:04 2012
@@ -0,0 +1,141 @@
+/*
+ * 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.ast;
+
+import org.apache.jackrabbit.oak.query.Query;
+
+public abstract class AstVisitorBase implements AstVisitor {
+
+    /**
+     * Calls accept on each of the attached constraints of the AND node.
+     */
+    @Override
+    public boolean visit(AndImpl node) {
+        node.getConstraint1().accept(this);
+        node.getConstraint2().accept(this);
+        return true;
+    }
+
+    /**
+     * Calls accept on the two operands in the comparison node.
+     */
+    @Override
+    public boolean visit(ComparisonImpl node) {
+        node.getOperand1().accept(this);
+        node.getOperand2().accept(this);
+        return true;
+    }
+
+    /**
+     * Calls accept on the static operand in the fulltext search constraint.
+     */
+    @Override
+    public boolean visit(FullTextSearchImpl node) {
+        node.getFullTextSearchExpression().accept(this);
+        return true;
+    }
+
+    /**
+     * Calls accept on the two sources and the join condition in the join node.
+     */
+    @Override
+    public boolean visit(JoinImpl node) {
+        node.getRight().accept(this);
+        node.getLeft().accept(this);
+        node.getJoinCondition().accept(this);
+        return true;
+    }
+
+    /**
+     * Calls accept on the property value in the length node.
+     */
+    @Override
+    public boolean visit(LengthImpl node) {
+        return node.getPropertyValue().accept(this);
+    }
+
+    /**
+     * Calls accept on the dynamic operand in the lower-case node.
+     */
+    @Override
+    public boolean visit(LowerCaseImpl node) {
+        return node.getOperand().accept(this);
+    }
+
+    /**
+     * Calls accept on the constraint in the NOT node.
+     */
+    @Override
+    public boolean visit(NotImpl node) {
+        return node.getConstraint().accept(this);
+    }
+
+    /**
+     * Calls accept on the dynamic operand in the ordering node.
+     */
+    @Override
+    public boolean visit(OrderingImpl node) {
+        return node.getOperand().accept(this);
+    }
+
+    /**
+     * Calls accept on each of the attached constraints of the OR node.
+     */
+    @Override
+    public boolean visit(OrImpl node) {
+        node.getConstraint1().accept(this);
+        node.getConstraint2().accept(this);
+        return true;
+    }
+
+    /**
+     * Calls accept on the following contained QOM nodes:
+     * <ul>
+     * <li>Source</li>
+     * <li>Constraints</li>
+     * <li>Orderings</li>
+     * <li>Columns</li>
+     * </ul>
+     */
+    public boolean visit(Query node) {
+        node.getSource().accept(this);
+        ConstraintImpl constraint = node.getConstraint();
+        if (constraint != null) {
+            constraint.accept(this);
+        }
+        OrderingImpl[] orderings = node.getOrderings();
+        if (orderings != null) {
+            for (OrderingImpl ordering : orderings) {
+                ordering.accept(this);
+            }
+        }
+        ColumnImpl[] columns = node.getColumns();
+        for (ColumnImpl column : columns) {
+            column.accept(this);
+        }
+        return true;
+    }
+
+    /**
+     * Calls accept on the dynamic operand in the lower-case node.
+     */
+    @Override
+    public boolean visit(UpperCaseImpl node) {
+        return node.getOperand().accept(this);
+    }
+
+}

Added: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/BindVariableValueImpl.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/BindVariableValueImpl.java?rev=1302927&view=auto
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/BindVariableValueImpl.java (added)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/BindVariableValueImpl.java Tue Mar 20 15:05:04 2012
@@ -0,0 +1,51 @@
+/*
+ * 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.ast;
+
+import org.apache.jackrabbit.oak.query.Value;
+
+public class BindVariableValueImpl extends StaticOperandImpl {
+
+    private final String bindVariableName;
+    private Value value;
+
+    public BindVariableValueImpl(String bindVariableName) {
+        this.bindVariableName = bindVariableName;
+    }
+
+    public String getBindVariableName() {
+        return bindVariableName;
+    }
+
+    @Override
+    boolean accept(AstVisitor v) {
+        return v.visit(this);
+    }
+
+    @Override
+    public String toString() {
+        return '$' + bindVariableName;
+    }
+
+    @Override
+    Value currentValue() {
+        return value;
+    }
+
+}

Added: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/ChildNodeImpl.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/ChildNodeImpl.java?rev=1302927&view=auto
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/ChildNodeImpl.java (added)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/ChildNodeImpl.java Tue Mar 20 15:05:04 2012
@@ -0,0 +1,76 @@
+/*
+ * 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.ast;
+
+import org.apache.jackrabbit.mk.util.PathUtils;
+import org.apache.jackrabbit.oak.query.index.Filter;
+import org.apache.jackrabbit.oak.query.index.Filter.PathRestriction;
+
+public class ChildNodeImpl extends ConstraintImpl {
+
+    private final String selectorName;
+    private final String parentPath;
+    private SelectorImpl selector;
+
+    public ChildNodeImpl(String selectorName, String parentPath) {
+        this.selectorName = selectorName;
+        this.parentPath = parentPath;
+    }
+
+    public String getSelectorName() {
+        return selectorName;
+    }
+
+    public String getParentPath() {
+        return parentPath;
+    }
+
+    @Override
+    boolean accept(AstVisitor v) {
+        return v.visit(this);
+    }
+
+    @Override
+    public String toString() {
+        return "ISCHILDNODE(" + selectorName + ", " + quotePath(parentPath) + ')';
+    }
+
+    public void bindSelector(SourceImpl source) {
+        selector = source.getSelector(selectorName);
+        if (selector == null) {
+            throw new RuntimeException("Unknown selector: " + selectorName);
+        }
+    }
+
+    @Override
+    public boolean evaluate() {
+        String p = selector.currentPath();
+        // the parent of the root is the root,
+        // so we need to special case this
+        return !PathUtils.denotesRoot(p) && PathUtils.getParentPath(p).equals(parentPath);
+    }
+
+    @Override
+    public void apply(Filter f) {
+        if (selector == f.getSelector()) {
+            f.restrictPath(parentPath, PathRestriction.DIRECT_CHILDREN);
+        }
+    }
+
+}
\ No newline at end of file

Added: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/ChildNodeJoinConditionImpl.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/ChildNodeJoinConditionImpl.java?rev=1302927&view=auto
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/ChildNodeJoinConditionImpl.java (added)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/ChildNodeJoinConditionImpl.java Tue Mar 20 15:05:04 2012
@@ -0,0 +1,89 @@
+/*
+ * 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.ast;
+
+import org.apache.jackrabbit.mk.util.PathUtils;
+import org.apache.jackrabbit.oak.query.index.Filter;
+import org.apache.jackrabbit.oak.query.index.Filter.PathRestriction;
+
+public class ChildNodeJoinConditionImpl extends JoinConditionImpl {
+
+    private final String childSelectorName;
+    private final String parentSelectorName;
+    private SelectorImpl childSelector;
+    private SelectorImpl parentSelector;
+
+    public ChildNodeJoinConditionImpl(String childSelectorName, String parentSelectorName) {
+        this.childSelectorName = childSelectorName;
+        this.parentSelectorName = parentSelectorName;
+    }
+
+    public String getChildSelectorName() {
+        return childSelectorName;
+    }
+
+    public String getParentSelectorName() {
+        return parentSelectorName;
+    }
+
+    @Override
+    boolean accept(AstVisitor v) {
+        return v.visit(this);
+    }
+
+    @Override
+    public String toString() {
+        String child = getChildSelectorName();
+        String parent = getParentSelectorName();
+        return "ISCHILDNODE(" + child + ", " + parent + ')';
+    }
+
+    public void bindSelector(SourceImpl source) {
+        parentSelector = source.getSelector(parentSelectorName);
+        if (parentSelector == null) {
+            throw new RuntimeException("Unknown selector: " + parentSelector);
+        }
+        childSelector = source.getSelector(childSelectorName);
+        if (childSelector == null) {
+            throw new RuntimeException("Unknown selector: " + childSelectorName);
+        }
+    }
+
+    @Override
+    public boolean evaluate() {
+        String p = parentSelector.currentPath();
+        String c = childSelector.currentPath();
+        // the parent of the root is the root,
+        // so we need to special case this
+        return !PathUtils.denotesRoot(c) && PathUtils.getParentPath(c).equals(p);
+    }
+
+    @Override
+    public void apply(Filter f) {
+        String p = parentSelector.currentPath();
+        String c = childSelector.currentPath();
+        if (f.getSelector() == parentSelector && c != null) {
+            f.restrictPath(PathUtils.getParentPath(c), PathRestriction.EXACT);
+        }
+        if (f.getSelector() == childSelector && p != null) {
+            f.restrictPath(p, PathRestriction.DIRECT_CHILDREN);
+        }
+    }
+
+}
\ No newline at end of file

Added: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/ColumnImpl.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/ColumnImpl.java?rev=1302927&view=auto
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/ColumnImpl.java (added)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/ColumnImpl.java Tue Mar 20 15:05:04 2012
@@ -0,0 +1,74 @@
+/*
+ * 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.ast;
+
+import org.apache.jackrabbit.oak.query.Value;
+
+public class ColumnImpl extends AstElement {
+
+    private final String selectorName, propertyName, columnName;
+    private SelectorImpl selector;
+
+    public ColumnImpl(String selectorName, String propertyName, String columnName) {
+        this.selectorName = selectorName;
+        this.propertyName = propertyName;
+        this.columnName = columnName;
+    }
+
+    public String getColumnName() {
+        return columnName;
+    }
+
+    public String getPropertyName() {
+        return propertyName;
+    }
+
+    public String getSelectorName() {
+        return selectorName;
+    }
+
+    @Override
+    boolean accept(AstVisitor v) {
+        return v.visit(this);
+    }
+
+    @Override
+    public String toString() {
+        if (propertyName != null) {
+            return getSelectorName() + '.' + getPropertyName()
+                    + " AS " + getColumnName();
+        } else {
+            return getSelectorName() + ".*";
+        }
+    }
+
+    public Value currentValue() {
+        if (propertyName == null) {
+            // TODO for SELECT * FROM queries, currently return the path (for testing only)
+            String p = selector.currentPath();
+            return p == null ? null : query.getValueFactory().createValue(p);
+        }
+        return selector.currentProperty(propertyName);
+    }
+
+    public void bindSelector(SourceImpl source) {
+        selector = source.getSelector(selectorName);
+    }
+
+}

Added: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/ComparisonImpl.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/ComparisonImpl.java?rev=1302927&view=auto
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/ComparisonImpl.java (added)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/ComparisonImpl.java Tue Mar 20 15:05:04 2012
@@ -0,0 +1,283 @@
+/*
+ * 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.ast;
+
+import org.apache.jackrabbit.oak.query.Value;
+import org.apache.jackrabbit.oak.query.ValueFactory;
+import org.apache.jackrabbit.oak.query.index.Filter;
+
+public class ComparisonImpl extends ConstraintImpl {
+
+    private final DynamicOperandImpl operand1;
+    private final Operator operator;
+    private final StaticOperandImpl operand2;
+
+    public ComparisonImpl(DynamicOperandImpl operand1, Operator operator, StaticOperandImpl operand2) {
+        this.operand1 = operand1;
+        this.operator = operator;
+        this.operand2 = operand2;
+    }
+
+    public DynamicOperandImpl getOperand1() {
+        return operand1;
+    }
+
+    public String getOperator() {
+        return operator.toString();
+    }
+
+    public StaticOperandImpl getOperand2() {
+        return operand2;
+    }
+
+    @Override
+    public boolean evaluate() {
+        Value v1 = operand1.currentValue();
+        Value v2 = operand2.currentValue();
+        if (v1 == null || v2 == null) {
+            // TODO comparison: what about (null <> x) ?
+            return false;
+        }
+        switch (operator) {
+        case EQ:
+            return v1.equals(v2);
+        case GE:
+        case GT:
+        case LE:
+        case LT:
+            return operand1.currentValue() .equals(operand2.currentValue());
+        case NE:
+            return !operand1.currentValue().equals(operand2.currentValue());
+        case LIKE:
+            return evaluateLike(v1, v2);
+        }
+        // TODO Auto-generated method stub
+        return false;
+    }
+
+    private static boolean evaluateLike(Value v1, Value v2) {
+        LikePattern like = new LikePattern(v2.getString());
+        return like.matches(v1.getString());
+    }
+
+    @Override
+    boolean accept(AstVisitor v) {
+        return v.visit(this);
+    }
+
+    @Override
+    public String toString() {
+        return operand1 + " " + operator + " " + operand2;
+    }
+
+    /**
+     * A pattern matcher.
+     */
+    public static class LikePattern {
+
+        // TODO LIKE: optimize condition to '=' when no patterns are used, or 'between x and x+1'
+        // TODO LIKE: what to do for invalid patterns (patterns ending with a backslash)
+
+        private static final int MATCH = 0, ONE = 1, ANY = 2;
+
+        private String patternString;
+        private boolean invalidPattern;
+        private char[] patternChars;
+        private int[] patternTypes;
+        private int patternLength;
+        private String lowerBounds, upperBound;
+
+        public LikePattern(String pattern) {
+            initPattern(pattern);
+            initBounds();
+        }
+
+        public boolean matches(String value) {
+            return !invalidPattern && compareAt(value, 0, 0, value.length(), patternChars, patternTypes);
+        }
+
+        private static boolean compare(char[] pattern, String s, int pi, int si) {
+            return pattern[pi] == s.charAt(si);
+        }
+
+        private boolean compareAt(String s, int pi, int si, int sLen, char[] pattern, int[] types) {
+            for (; pi < patternLength; pi++) {
+                int type = types[pi];
+                switch (type) {
+                case MATCH:
+                    if (si >= sLen || !compare(pattern, s, pi, si++)) {
+                        return false;
+                    }
+                    break;
+                case ONE:
+                    if (si++ >= sLen) {
+                        return false;
+                    }
+                    break;
+                case ANY:
+                    if (++pi >= patternLength) {
+                        return true;
+                    }
+                    while (si < sLen) {
+                        if (compare(pattern, s, pi, si) && compareAt(s, pi, si, sLen, pattern, types)) {
+                            return true;
+                        }
+                        si++;
+                    }
+                    return false;
+                default:
+                    throw new RuntimeException("Internal error: " + type);
+                }
+            }
+            return si == sLen;
+        }
+
+        private void initPattern(String p) {
+            patternLength = 0;
+            if (p == null) {
+                patternTypes = null;
+                patternChars = null;
+                return;
+            }
+            int len = p.length();
+            patternChars = new char[len];
+            patternTypes = new int[len];
+            boolean lastAny = false;
+            for (int i = 0; i < len; i++) {
+                char c = p.charAt(i);
+                int type;
+                if (c == '\\') {
+                    if (i >= len - 1) {
+                        invalidPattern = true;
+                        return;
+                    }
+                    c = p.charAt(++i);
+                    type = MATCH;
+                    lastAny = false;
+                } else if (c == '%') {
+                    if (lastAny) {
+                        continue;
+                    }
+                    type = ANY;
+                    lastAny = true;
+                } else if (c == '_') {
+                    type = ONE;
+                } else {
+                    type = MATCH;
+                    lastAny = false;
+                }
+                patternTypes[patternLength] = type;
+                patternChars[patternLength++] = c;
+            }
+            for (int i = 0; i < patternLength - 1; i++) {
+                if (patternTypes[i] == ANY && patternTypes[i + 1] == ONE) {
+                    patternTypes[i] = ONE;
+                    patternTypes[i + 1] = ANY;
+                }
+            }
+            patternString = new String(patternChars, 0, patternLength);
+        }
+
+        @Override
+        public String toString() {
+            return patternString;
+        }
+
+        /**
+         * Get the lower bound if any.
+         *
+         * @return return the lower bound, or null if unbound
+         */
+        public String getLowerBound() {
+            return lowerBounds;
+        }
+
+        /**
+         * Get the upper bound if any.
+         *
+         * @return return the upper bound, or null if unbound
+         */
+        public String getUpperBound() {
+            return upperBound;
+        }
+
+        private void initBounds() {
+            if (invalidPattern) {
+                return;
+            }
+            if (patternLength <= 0 || patternTypes[0] != MATCH) {
+                // can't use an index
+                return;
+            }
+            int maxMatch = 0;
+            StringBuilder buff = new StringBuilder();
+            while (maxMatch < patternLength && patternTypes[maxMatch] == MATCH) {
+                buff.append(patternChars[maxMatch++]);
+            }
+            String lower = buff.toString();
+            if (lower.length() == 0) {
+                return;
+            }
+            if (maxMatch == patternLength) {
+                lowerBounds = upperBound = lower;
+                return;
+            }
+            lowerBounds = lower;
+            char next = lower.charAt(lower.length() - 1);
+            // search the 'next' unicode character (or at least a character
+            // that is higher)
+            for (int i = 1; i < 2000; i++) {
+                String upper = lower.substring(0, lower.length() - 1) + (char) (next + i);
+                if (upper.compareTo(lower) > 0) {
+                    upperBound = upper;
+                    return;
+                }
+            }
+        }
+
+    }
+
+    @Override
+    public void apply(Filter f) {
+        Value v = operand2.currentValue();
+        if (v != null) {
+            if (operator == Operator.LIKE) {
+                String pattern;
+                pattern = v.getString();
+                LikePattern p = new LikePattern(pattern);
+                String lowerBound = p.getLowerBound();
+                String upperBound = p.getUpperBound();
+                if (lowerBound == null && upperBound == null) {
+                    // ignore
+                } else {
+                    ValueFactory vf = query.getValueFactory();
+                    if (lowerBound != null) {
+                        operand1.apply(f, Operator.GE, vf.createValue(lowerBound));
+                    }
+                    if (upperBound != null) {
+                        operand1.apply(f, Operator.LE, vf.createValue(upperBound));
+                    }
+                }
+            } else {
+                operand1.apply(f, operator, v);
+            }
+        }
+    }
+
+}

Added: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/ConstraintImpl.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/ConstraintImpl.java?rev=1302927&view=auto
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/ConstraintImpl.java (added)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/ConstraintImpl.java Tue Mar 20 15:05:04 2012
@@ -0,0 +1,37 @@
+/*
+ * 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.ast;
+
+import org.apache.jackrabbit.oak.query.index.Filter;
+
+public abstract class ConstraintImpl extends AstElement {
+
+    /**
+     * Evaluate the result using the currently set values.
+     *
+     * @return true if the constraint matches
+     */
+    public abstract boolean evaluate();
+
+    /**
+     * Apply the condition to the filter, further restricting the filter if possible.
+     *
+     * @param f the filter
+     */
+    public abstract void apply(Filter f);
+
+}

Added: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/DescendantNodeImpl.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/DescendantNodeImpl.java?rev=1302927&view=auto
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/DescendantNodeImpl.java (added)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/DescendantNodeImpl.java Tue Mar 20 15:05:04 2012
@@ -0,0 +1,74 @@
+/*
+ * 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.ast;
+
+import org.apache.jackrabbit.mk.util.PathUtils;
+import org.apache.jackrabbit.oak.query.index.Filter;
+import org.apache.jackrabbit.oak.query.index.Filter.PathRestriction;
+
+public class DescendantNodeImpl extends ConstraintImpl {
+
+    private final String selectorName;
+    private final String ancestorPath;
+    private SelectorImpl selector;
+
+    public DescendantNodeImpl(String selectorName, String ancestorPath) {
+        this.selectorName = selectorName;
+        this.ancestorPath = ancestorPath;
+    }
+
+    public String getSelectorName() {
+        return selectorName;
+    }
+
+    public String getAncestorPath() {
+        return ancestorPath;
+    }
+
+    @Override
+    public boolean evaluate() {
+        String p = selector.currentPath();
+        return PathUtils.isAncestor(ancestorPath, p);
+    }
+
+    @Override
+    boolean accept(AstVisitor v) {
+        return v.visit(this);
+    }
+
+    @Override
+    public String toString() {
+        return "ISDESCENDANTNODE(" + getSelectorName() + ", " + quotePath(ancestorPath) + ')';
+    }
+
+    public void bindSelector(SourceImpl source) {
+        selector = source.getSelector(selectorName);
+        if (selector == null) {
+            throw new RuntimeException("Unknown selector: " + selectorName);
+        }
+    }
+
+    @Override
+    public void apply(Filter f) {
+        if (f.getSelector() == selector) {
+            f.restrictPath(ancestorPath, PathRestriction.ALL_CHILDREN);
+        }
+    }
+
+}

Added: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/DescendantNodeJoinConditionImpl.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/DescendantNodeJoinConditionImpl.java?rev=1302927&view=auto
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/DescendantNodeJoinConditionImpl.java (added)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/DescendantNodeJoinConditionImpl.java Tue Mar 20 15:05:04 2012
@@ -0,0 +1,88 @@
+/*
+ * 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.ast;
+
+import org.apache.jackrabbit.mk.util.PathUtils;
+import org.apache.jackrabbit.oak.query.index.Filter;
+import org.apache.jackrabbit.oak.query.index.Filter.PathRestriction;
+
+public class DescendantNodeJoinConditionImpl extends JoinConditionImpl {
+
+    private final String descendantSelectorName;
+    private final String ancestorSelectorName;
+    private SelectorImpl descendantSelector;
+    private SelectorImpl ancestorSelector;
+
+    public DescendantNodeJoinConditionImpl(String descendantSelectorName,
+            String ancestorSelectorName) {
+        this.descendantSelectorName = descendantSelectorName;
+        this.ancestorSelectorName = ancestorSelectorName;
+    }
+
+    public String getDescendantSelectorName() {
+        return descendantSelectorName;
+    }
+
+    public String getAncestorSelectorName() {
+        return ancestorSelectorName;
+    }
+
+    @Override
+    boolean accept(AstVisitor v) {
+        return v.visit(this);
+    }
+
+    @Override
+    public String toString() {
+        String descendant = getDescendantSelectorName();
+        String ancestor = getAncestorSelectorName();
+        return "ISDESCENDANTNODE(" + descendant + ", " + ancestor + ')';
+    }
+
+    public void bindSelector(SourceImpl source) {
+        descendantSelector = source.getSelector(descendantSelectorName);
+        if (descendantSelector == null) {
+            throw new RuntimeException("Unknown selector: " + descendantSelectorName);
+        }
+        ancestorSelector = source.getSelector(ancestorSelectorName);
+        if (ancestorSelector == null) {
+            throw new RuntimeException("Unknown selector: " + ancestorSelectorName);
+        }
+    }
+
+    @Override
+    public boolean evaluate() {
+        String a = ancestorSelector.currentPath();
+        String d = descendantSelector.currentPath();
+        return PathUtils.isAncestor(a, d);
+    }
+
+    @Override
+    public void apply(Filter f) {
+        String d = descendantSelector.currentPath();
+        String a = ancestorSelector.currentPath();
+        if (d != null && f.getSelector() == ancestorSelector) {
+            f.restrictPath(PathUtils.getParentPath(d), PathRestriction.PARENT);
+        }
+        if (a != null && f.getSelector() == descendantSelector) {
+            f.restrictPath(a, PathRestriction.DIRECT_CHILDREN);
+        }
+    }
+
+}

Added: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/DynamicOperandImpl.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/DynamicOperandImpl.java?rev=1302927&view=auto
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/DynamicOperandImpl.java (added)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/DynamicOperandImpl.java Tue Mar 20 15:05:04 2012
@@ -0,0 +1,28 @@
+/*
+ * 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.ast;
+
+import org.apache.jackrabbit.oak.query.Value;
+import org.apache.jackrabbit.oak.query.index.Filter;
+
+public abstract class DynamicOperandImpl extends AstElement {
+
+    public abstract Value currentValue();
+
+    public abstract void apply(Filter f, Operator operator, Value v);
+
+}

Added: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/EquiJoinConditionImpl.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/EquiJoinConditionImpl.java?rev=1302927&view=auto
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/EquiJoinConditionImpl.java (added)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/EquiJoinConditionImpl.java Tue Mar 20 15:05:04 2012
@@ -0,0 +1,103 @@
+/*
+ * 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.ast;
+
+import org.apache.jackrabbit.oak.query.Value;
+import org.apache.jackrabbit.oak.query.index.Filter;
+
+public class EquiJoinConditionImpl extends JoinConditionImpl {
+
+    private final String property1Name;
+    private final String property2Name;
+    private final String selector1Name;
+    private final String selector2Name;
+    private SelectorImpl selector1;
+    private SelectorImpl selector2;
+
+    public EquiJoinConditionImpl(String selector1Name, String property1Name, String selector2Name,
+            String property2Name) {
+        this.selector1Name = selector1Name;
+        this.property1Name = property1Name;
+        this.selector2Name = selector2Name;
+        this.property2Name = property2Name;
+    }
+
+    public String getSelector1Name() {
+        return selector1Name;
+    }
+
+    public String getProperty1Name() {
+        return property1Name;
+    }
+
+    public String getSelector2Name() {
+        return selector2Name;
+    }
+
+    public String getProperty2Name() {
+        return property2Name;
+    }
+
+    @Override
+    boolean accept(AstVisitor v) {
+        return v.visit(this);
+    }
+
+    @Override
+    public String toString() {
+        // TODO quote property names?
+        return getSelector1Name() + '.' + getProperty1Name()
+                + " = " + getSelector2Name() + '.' + getProperty2Name();
+    }
+
+    public void bindSelector(SourceImpl source) {
+        selector1 = source.getSelector(selector1Name);
+        if (selector1 == null) {
+            throw new RuntimeException("Unknown selector: " + selector1Name);
+        }
+        selector2 = source.getSelector(selector2Name);
+        if (selector2 == null) {
+            throw new RuntimeException("Unknown selector: " + selector2Name);
+        }
+    }
+
+    @Override
+    public boolean evaluate() {
+        Value v1 = selector1.currentProperty(property1Name);
+        if (v1 == null) {
+            return false;
+        }
+        // TODO data type mapping
+        Value v2 = selector2.currentProperty(property2Name);
+        return v2 != null && v1.equals(v2);
+    }
+
+    @Override
+    public void apply(Filter f) {
+        Value v1 = selector1.currentProperty(property1Name);
+        Value v2 = selector2.currentProperty(property2Name);
+        if (f.getSelector() == selector1 && v2 != null) {
+            f.restrictProperty(property1Name, Operator.EQ, v2);
+        }
+        if (f.getSelector() == selector2 && v1 != null) {
+            f.restrictProperty(property2Name, Operator.EQ, v1);
+        }
+    }
+
+}

Added: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/FullTextSearchImpl.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/FullTextSearchImpl.java?rev=1302927&view=auto
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/FullTextSearchImpl.java (added)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/FullTextSearchImpl.java Tue Mar 20 15:05:04 2012
@@ -0,0 +1,82 @@
+/*
+ * 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.ast;
+
+import org.apache.jackrabbit.oak.query.index.Filter;
+
+public class FullTextSearchImpl extends ConstraintImpl {
+
+    private final String selectorName;
+    private final String propertyName;
+    private final StaticOperandImpl fullTextSearchExpression;
+
+    public FullTextSearchImpl(String selectorName, String propertyName,
+            StaticOperandImpl fullTextSearchExpression) {
+        this.selectorName = selectorName;
+        this.propertyName = propertyName;
+        this.fullTextSearchExpression = fullTextSearchExpression;
+    }
+
+    public StaticOperandImpl getFullTextSearchExpression() {
+        return fullTextSearchExpression;
+    }
+
+    public String getPropertyName() {
+        return propertyName;
+    }
+
+    public String getSelectorName() {
+        return selectorName;
+    }
+
+    @Override
+    boolean accept(AstVisitor v) {
+        return v.visit(this);
+    }
+
+    @Override
+    public String toString() {
+        // TODO quote property names?
+        StringBuilder builder = new StringBuilder();
+        builder.append("CONTAINS(");
+        builder.append(getSelectorName());
+        if (propertyName != null) {
+            builder.append('.');
+            builder.append(propertyName);
+            builder.append(", ");
+        } else {
+            builder.append(".*, ");
+        }
+        builder.append(getFullTextSearchExpression());
+        builder.append(')');
+        return builder.toString();
+    }
+
+    @Override
+    public boolean evaluate() {
+        // TODO support evaluating fulltext conditions
+        return false;
+    }
+
+    @Override
+    public void apply(Filter f) {
+        // TODO support fulltext index conditions
+    }
+
+}

Added: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/FullTextSearchScoreImpl.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/FullTextSearchScoreImpl.java?rev=1302927&view=auto
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/FullTextSearchScoreImpl.java (added)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/FullTextSearchScoreImpl.java Tue Mar 20 15:05:04 2012
@@ -0,0 +1,57 @@
+/*
+ * 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.ast;
+
+import org.apache.jackrabbit.oak.query.Value;
+import org.apache.jackrabbit.oak.query.index.Filter;
+
+public class FullTextSearchScoreImpl extends DynamicOperandImpl {
+
+    private final String selectorName;
+
+    public FullTextSearchScoreImpl(String selectorName) {
+        this.selectorName = selectorName;
+    }
+
+    public String getSelectorName() {
+        return selectorName;
+    }
+
+    @Override
+    boolean accept(AstVisitor v) {
+        return v.visit(this);
+    }
+
+    @Override
+    public String toString() {
+        return "SCORE(" + getSelectorName() + ')';
+    }
+
+    @Override
+    public Value currentValue() {
+        // TODO support evaluating fulltext conditions (score)
+        return null;
+    }
+
+    @Override
+    public void apply(Filter f, Operator operator, Value v) {
+        // TODO support fulltext index conditions (score)
+    }
+
+}

Added: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/JoinConditionImpl.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/JoinConditionImpl.java?rev=1302927&view=auto
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/JoinConditionImpl.java (added)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/JoinConditionImpl.java Tue Mar 20 15:05:04 2012
@@ -0,0 +1,25 @@
+/*
+ * 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.ast;
+
+import org.apache.jackrabbit.oak.query.index.Filter;
+
+
+public abstract class JoinConditionImpl extends AstElement {
+
+    public abstract boolean evaluate();
+
+    public abstract void apply(Filter f);
+
+}



Mime
View raw message