You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@jackrabbit.apache.org by th...@apache.org on 2012/02/23 16:34:29 UTC

svn commit: r1292828 [1/3] - in /jackrabbit/sandbox/jackrabbit-microkernel/src/main/java/org/apache/jackrabbit/query: ./ index/ qom/ qom/tree/

Author: thomasm
Date: Thu Feb 23 15:34:27 2012
New Revision: 1292828

URL: http://svn.apache.org/viewvc?rev=1292828&view=rev
Log:
Query implementation (WIP)

Added:
    jackrabbit/sandbox/jackrabbit-microkernel/src/main/java/org/apache/jackrabbit/query/
    jackrabbit/sandbox/jackrabbit-microkernel/src/main/java/org/apache/jackrabbit/query/NamespaceRegistryImpl.java
    jackrabbit/sandbox/jackrabbit-microkernel/src/main/java/org/apache/jackrabbit/query/ParserSQL2.java
    jackrabbit/sandbox/jackrabbit-microkernel/src/main/java/org/apache/jackrabbit/query/ParserXPath.java
    jackrabbit/sandbox/jackrabbit-microkernel/src/main/java/org/apache/jackrabbit/query/QueryImpl.java
    jackrabbit/sandbox/jackrabbit-microkernel/src/main/java/org/apache/jackrabbit/query/QueryManagerImpl.java
    jackrabbit/sandbox/jackrabbit-microkernel/src/main/java/org/apache/jackrabbit/query/QueryResultImpl.java
    jackrabbit/sandbox/jackrabbit-microkernel/src/main/java/org/apache/jackrabbit/query/RangeIteratorImpl.java
    jackrabbit/sandbox/jackrabbit-microkernel/src/main/java/org/apache/jackrabbit/query/RowImpl.java
    jackrabbit/sandbox/jackrabbit-microkernel/src/main/java/org/apache/jackrabbit/query/RowIteratorImpl.java
    jackrabbit/sandbox/jackrabbit-microkernel/src/main/java/org/apache/jackrabbit/query/index/
    jackrabbit/sandbox/jackrabbit-microkernel/src/main/java/org/apache/jackrabbit/query/index/Index.java
    jackrabbit/sandbox/jackrabbit-microkernel/src/main/java/org/apache/jackrabbit/query/qom/
    jackrabbit/sandbox/jackrabbit-microkernel/src/main/java/org/apache/jackrabbit/query/qom/QueryObjectModelFactoryImpl.java
    jackrabbit/sandbox/jackrabbit-microkernel/src/main/java/org/apache/jackrabbit/query/qom/QueryObjectModelImpl.java
    jackrabbit/sandbox/jackrabbit-microkernel/src/main/java/org/apache/jackrabbit/query/qom/tree/
    jackrabbit/sandbox/jackrabbit-microkernel/src/main/java/org/apache/jackrabbit/query/qom/tree/AndImpl.java
    jackrabbit/sandbox/jackrabbit-microkernel/src/main/java/org/apache/jackrabbit/query/qom/tree/BindVariableValueImpl.java
    jackrabbit/sandbox/jackrabbit-microkernel/src/main/java/org/apache/jackrabbit/query/qom/tree/ChildNodeImpl.java
    jackrabbit/sandbox/jackrabbit-microkernel/src/main/java/org/apache/jackrabbit/query/qom/tree/ChildNodeJoinConditionImpl.java
    jackrabbit/sandbox/jackrabbit-microkernel/src/main/java/org/apache/jackrabbit/query/qom/tree/ColumnImpl.java
    jackrabbit/sandbox/jackrabbit-microkernel/src/main/java/org/apache/jackrabbit/query/qom/tree/ComparisonImpl.java
    jackrabbit/sandbox/jackrabbit-microkernel/src/main/java/org/apache/jackrabbit/query/qom/tree/ConstraintImpl.java
    jackrabbit/sandbox/jackrabbit-microkernel/src/main/java/org/apache/jackrabbit/query/qom/tree/DescendantNodeImpl.java
    jackrabbit/sandbox/jackrabbit-microkernel/src/main/java/org/apache/jackrabbit/query/qom/tree/DescendantNodeJoinConditionImpl.java
    jackrabbit/sandbox/jackrabbit-microkernel/src/main/java/org/apache/jackrabbit/query/qom/tree/DynamicOperandImpl.java
    jackrabbit/sandbox/jackrabbit-microkernel/src/main/java/org/apache/jackrabbit/query/qom/tree/EquiJoinConditionImpl.java
    jackrabbit/sandbox/jackrabbit-microkernel/src/main/java/org/apache/jackrabbit/query/qom/tree/FullTextSearchImpl.java
    jackrabbit/sandbox/jackrabbit-microkernel/src/main/java/org/apache/jackrabbit/query/qom/tree/FullTextSearchScoreImpl.java
    jackrabbit/sandbox/jackrabbit-microkernel/src/main/java/org/apache/jackrabbit/query/qom/tree/JoinConditionImpl.java
    jackrabbit/sandbox/jackrabbit-microkernel/src/main/java/org/apache/jackrabbit/query/qom/tree/JoinImpl.java
    jackrabbit/sandbox/jackrabbit-microkernel/src/main/java/org/apache/jackrabbit/query/qom/tree/JoinType.java
    jackrabbit/sandbox/jackrabbit-microkernel/src/main/java/org/apache/jackrabbit/query/qom/tree/LengthImpl.java
    jackrabbit/sandbox/jackrabbit-microkernel/src/main/java/org/apache/jackrabbit/query/qom/tree/LiteralImpl.java
    jackrabbit/sandbox/jackrabbit-microkernel/src/main/java/org/apache/jackrabbit/query/qom/tree/LowerCaseImpl.java
    jackrabbit/sandbox/jackrabbit-microkernel/src/main/java/org/apache/jackrabbit/query/qom/tree/NodeLocalNameImpl.java
    jackrabbit/sandbox/jackrabbit-microkernel/src/main/java/org/apache/jackrabbit/query/qom/tree/NodeNameImpl.java
    jackrabbit/sandbox/jackrabbit-microkernel/src/main/java/org/apache/jackrabbit/query/qom/tree/NotImpl.java
    jackrabbit/sandbox/jackrabbit-microkernel/src/main/java/org/apache/jackrabbit/query/qom/tree/Operator.java
    jackrabbit/sandbox/jackrabbit-microkernel/src/main/java/org/apache/jackrabbit/query/qom/tree/OrImpl.java
    jackrabbit/sandbox/jackrabbit-microkernel/src/main/java/org/apache/jackrabbit/query/qom/tree/Order.java
    jackrabbit/sandbox/jackrabbit-microkernel/src/main/java/org/apache/jackrabbit/query/qom/tree/OrderingImpl.java
    jackrabbit/sandbox/jackrabbit-microkernel/src/main/java/org/apache/jackrabbit/query/qom/tree/PropertyExistenceImpl.java
    jackrabbit/sandbox/jackrabbit-microkernel/src/main/java/org/apache/jackrabbit/query/qom/tree/PropertyValueImpl.java
    jackrabbit/sandbox/jackrabbit-microkernel/src/main/java/org/apache/jackrabbit/query/qom/tree/QOMNode.java
    jackrabbit/sandbox/jackrabbit-microkernel/src/main/java/org/apache/jackrabbit/query/qom/tree/QOMTreeVisitor.java
    jackrabbit/sandbox/jackrabbit-microkernel/src/main/java/org/apache/jackrabbit/query/qom/tree/QOMVisitor.java
    jackrabbit/sandbox/jackrabbit-microkernel/src/main/java/org/apache/jackrabbit/query/qom/tree/SameNodeImpl.java
    jackrabbit/sandbox/jackrabbit-microkernel/src/main/java/org/apache/jackrabbit/query/qom/tree/SameNodeJoinConditionImpl.java
    jackrabbit/sandbox/jackrabbit-microkernel/src/main/java/org/apache/jackrabbit/query/qom/tree/SelectorImpl.java
    jackrabbit/sandbox/jackrabbit-microkernel/src/main/java/org/apache/jackrabbit/query/qom/tree/SourceImpl.java
    jackrabbit/sandbox/jackrabbit-microkernel/src/main/java/org/apache/jackrabbit/query/qom/tree/StaticOperandImpl.java
    jackrabbit/sandbox/jackrabbit-microkernel/src/main/java/org/apache/jackrabbit/query/qom/tree/UpperCaseImpl.java

Added: jackrabbit/sandbox/jackrabbit-microkernel/src/main/java/org/apache/jackrabbit/query/NamespaceRegistryImpl.java
URL: http://svn.apache.org/viewvc/jackrabbit/sandbox/jackrabbit-microkernel/src/main/java/org/apache/jackrabbit/query/NamespaceRegistryImpl.java?rev=1292828&view=auto
==============================================================================
--- jackrabbit/sandbox/jackrabbit-microkernel/src/main/java/org/apache/jackrabbit/query/NamespaceRegistryImpl.java (added)
+++ jackrabbit/sandbox/jackrabbit-microkernel/src/main/java/org/apache/jackrabbit/query/NamespaceRegistryImpl.java Thu Feb 23 15:34:27 2012
@@ -0,0 +1,160 @@
+/*
+ * 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.query;
+
+import java.util.HashMap;
+import java.util.LinkedHashMap;
+import java.util.Map.Entry;
+import javax.jcr.AccessDeniedException;
+import javax.jcr.NamespaceException;
+import javax.jcr.NamespaceRegistry;
+import javax.jcr.RepositoryException;
+import javax.jcr.UnsupportedRepositoryOperationException;
+import org.apache.jackrabbit.query.val.ExceptionFactory;
+import org.apache.jackrabbit.query.val.Val;
+
+/**
+ * The implementation of the corresponding JCR interface.
+ */
+public class NamespaceRegistryImpl implements NamespaceRegistry {
+
+    public static final NamespaceRegistryImpl BUILT_IN = new NamespaceRegistryImpl(null);
+
+    static {
+        NamespaceRegistryImpl registry = BUILT_IN;
+        registry.register(PREFIX_JCR, NAMESPACE_JCR);
+        registry.register(PREFIX_NT, NAMESPACE_NT);
+        registry.register(PREFIX_MIX, NAMESPACE_MIX);
+        registry.register(PREFIX_XML, NAMESPACE_XML);
+        registry.register(PREFIX_EMPTY, NAMESPACE_EMPTY);
+        registry.register("rep", "internal");
+    }
+
+    private final NamespaceRegistryImpl parent;
+    private LinkedHashMap<String, String> prefixToUri = new LinkedHashMap<String, String>();
+    private HashMap<String, String> uriToPrefix = new HashMap<String, String>();
+    private boolean shareMapsWithParent;
+
+    private NamespaceRegistryImpl(NamespaceRegistryImpl parent) {
+        this.parent = parent;
+        if (parent != null) {
+            prefixToUri = parent.prefixToUri;
+            uriToPrefix = parent.uriToPrefix;
+            shareMapsWithParent = true;
+        }
+    }
+
+    public static NamespaceRegistryImpl createLocalInstance() {
+        return new NamespaceRegistryImpl(BUILT_IN);
+    }
+
+    public String nameToString(Val name) {
+        if (name.getType() == Val.TYPE_MULTI_VALUE) {
+            Val[] namePair = name.getArray();
+            String uri = getPrefix(namePair[0].getString());
+            String localName = namePair[1].getString();
+            StringBuilder buff = new StringBuilder(uri.length() + 1 + localName.length());
+            buff.append(uri).append(':').append(localName);
+            return buff.toString();
+        }
+        return name.getString();
+    }
+
+    public Val parseName(String name) {
+        if (name.charAt(0) == '{') {
+            int index = name.indexOf('}');
+            Val namespace = Val.get(name.substring(1, index));
+            Val localName = Val.get(name.substring(index + 1));
+            return Val.get(namespace, localName);
+        }
+        int index = name.indexOf(':');
+        if (index < 0) {
+            return Val.get(name);
+        }
+        String prefix = name.substring(0, index);
+        String ns = getURI(prefix);
+        if (ns == null) {
+            throw ExceptionFactory.illegalArgument("Not URI found for prefix: {0}", prefix);
+        }
+        Val namespace = Val.get(ns);
+        Val localName = Val.get(name.substring(index + 1));
+        return Val.get(namespace, localName);
+    }
+
+    public String getPrefix(String uri) {
+        return uriToPrefix.get(uri);
+    }
+
+    public synchronized String[] getPrefixes() throws RepositoryException {
+        String[] prefixes = new String[prefixToUri.size()];
+        prefixToUri.keySet().toArray(prefixes);
+        return prefixes;
+    }
+
+    public String getURI(String prefix) {
+        return prefixToUri.get(prefix);
+    }
+
+    public synchronized String[] getURIs() throws RepositoryException {
+        String[] uris = new String[uriToPrefix.size()];
+        uriToPrefix.keySet().toArray(uris);
+        return uris;
+    }
+
+    public void registerNamespace(String prefix, String uri) throws NamespaceException,
+            UnsupportedRepositoryOperationException, AccessDeniedException, RepositoryException {
+        if (parent.prefixToUri.containsKey(prefix) || parent.uriToPrefix.containsKey(uri)) {
+            if (!parent.prefixToUri.get(prefix).equals(uri)) {
+                throw ExceptionFactory.namespace("Can not redefine built-in prefix: {0}", prefix);
+            }
+        } else if (prefix.equalsIgnoreCase("xml")) {
+            throw ExceptionFactory.namespace("Can not (re-)define an xml variant: {0}", prefix);
+        }
+        register(prefix, uri);
+    }
+
+    private synchronized void register(String prefix, String uri) {
+        if (shareMapsWithParent) {
+            prefixToUri = new LinkedHashMap<String, String>(prefixToUri);
+            uriToPrefix = new HashMap<String, String>(uriToPrefix);
+            shareMapsWithParent = false;
+        }
+        prefixToUri.put(prefix, uri);
+        uriToPrefix.put(uri, prefix);
+    }
+
+    public synchronized void unregisterNamespace(String prefix) throws NamespaceException, UnsupportedRepositoryOperationException,
+            AccessDeniedException, RepositoryException {
+        if (parent.prefixToUri.containsKey(prefix)) {
+            throw ExceptionFactory.namespace("Can not unregister a built-in prefix: {0}", prefix);
+        }
+        if (!prefixToUri.containsKey(prefix)) {
+            throw ExceptionFactory.namespace("Not registered: {0}", prefix);
+        }
+        String uri = prefixToUri.remove(prefix);
+        uriToPrefix.remove(uri);
+    }
+
+    public String toString() {
+        StringBuilder buff = new StringBuilder();
+        for (Entry<String, String> e : prefixToUri.entrySet()) {
+            buff.append('<').append(e.getKey()).append('=').append(e.getValue()).append('>');
+        }
+        return buff.toString();
+    }
+
+}

Added: jackrabbit/sandbox/jackrabbit-microkernel/src/main/java/org/apache/jackrabbit/query/ParserSQL2.java
URL: http://svn.apache.org/viewvc/jackrabbit/sandbox/jackrabbit-microkernel/src/main/java/org/apache/jackrabbit/query/ParserSQL2.java?rev=1292828&view=auto
==============================================================================
--- jackrabbit/sandbox/jackrabbit-microkernel/src/main/java/org/apache/jackrabbit/query/ParserSQL2.java (added)
+++ jackrabbit/sandbox/jackrabbit-microkernel/src/main/java/org/apache/jackrabbit/query/ParserSQL2.java Thu Feb 23 15:34:27 2012
@@ -0,0 +1,1002 @@
+/*
+ * 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.query;
+
+import java.math.BigDecimal;
+import java.util.ArrayList;
+import java.util.HashMap;
+
+import javax.jcr.PropertyType;
+import javax.jcr.RepositoryException;
+import javax.jcr.Value;
+import javax.jcr.ValueFactory;
+import javax.jcr.query.InvalidQueryException;
+import javax.jcr.query.qom.BindVariableValue;
+import javax.jcr.query.qom.Column;
+import javax.jcr.query.qom.Constraint;
+import javax.jcr.query.qom.DynamicOperand;
+import javax.jcr.query.qom.JoinCondition;
+import javax.jcr.query.qom.Literal;
+import javax.jcr.query.qom.Ordering;
+import javax.jcr.query.qom.PropertyExistence;
+import javax.jcr.query.qom.PropertyValue;
+import javax.jcr.query.qom.QueryObjectModel;
+import javax.jcr.query.qom.QueryObjectModelConstants;
+import javax.jcr.query.qom.QueryObjectModelFactory;
+import javax.jcr.query.qom.Selector;
+import javax.jcr.query.qom.Source;
+import javax.jcr.query.qom.StaticOperand;
+import org.apache.jackrabbit.query.val.ExceptionFactory;
+
+/**
+ * The SQL2 parser can convert a JCR-SQL2 query to a QueryObjectModel.
+ */
+public class ParserSQL2 {
+
+    // Character types, used during the tokenizer phase
+    private static final int CHAR_END = -1, CHAR_VALUE = 2, CHAR_QUOTED = 3;
+    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, PARAMETER = 3, END = 4, VALUE = 5;
+    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 Value currentValue;
+    private ArrayList<String> expected;
+
+    // The bind variables
+    private HashMap<String, BindVariableValue> bindVariables;
+
+    // The list of selectors of this query
+    private ArrayList<Selector> selectors;
+
+    // SQL injection protection: if disabled, literals are not allowed
+    private boolean allowTextLiterals = true, allowNumberLiterals = true;
+
+    private QueryObjectModelFactory factory;
+    private ValueFactory valueFactory;
+
+    /**
+     * Create a new parser. A parser can be re-used, but it is not thread safe.
+     *
+     * @param factory the query object model factory
+     * @param valueFactory the value factory
+     */
+    public ParserSQL2(QueryObjectModelFactory factory, ValueFactory valueFactory) {
+        this.factory = factory;
+        this.valueFactory = valueFactory;
+    }
+
+    /**
+     * Parse a JCR-SQL2 query and return the query object model
+     *
+     * @param query the query string
+     * @return the query object model
+     * @throws RepositoryException if parsing failed
+     */
+    public QueryObjectModel createQueryObjectModel(String query) throws RepositoryException {
+        initialize(query);
+        selectors = new ArrayList<Selector>();
+        expected = new ArrayList<String>();
+        bindVariables = new HashMap<String, BindVariableValue>();
+        read();
+        read("SELECT");
+        ArrayList<ColumnOrWildcard> list = parseColumns();
+        read("FROM");
+        Source source = parseSource();
+        Column[] columnArray = resolveColumns(list);
+        Constraint constraint = null;
+        if (readIf("WHERE")) {
+            constraint = parseConstraint();
+        }
+        Ordering[] orderings = null;
+        if (readIf("ORDER")) {
+            read("BY");
+            orderings = parseOrder();
+        }
+        if (currentToken.length() > 0) {
+            throw getSyntaxError("<end>");
+        }
+        return factory.createQuery(source, constraint, orderings, columnArray);
+    }
+
+    private Selector parseSelector() throws RepositoryException {
+        String nodeTypeName = readName();
+        if (readIf("AS")) {
+            String selectorName = readName();
+            return factory.selector(nodeTypeName, selectorName);
+        } else {
+            return factory.selector(nodeTypeName, nodeTypeName);
+        }
+    }
+
+    private String readName() throws RepositoryException {
+        if (readIf("[")) {
+            if (currentTokenType == VALUE) {
+                Value value = readString();
+                read("]");
+                return value.getString();
+            } else {
+                int level = 1;
+                StringBuilder buff = new StringBuilder();
+                while (true) {
+                    if (isToken("]")) {
+                        if (--level <= 0) {
+                            read();
+                            break;
+                        }
+                    } else if (isToken("[")) {
+                        level++;
+                    }
+                    buff.append(readAny());
+                }
+                return buff.toString();
+            }
+        } else {
+            return readAny();
+        }
+    }
+
+    private Source parseSource() throws RepositoryException {
+        Selector selector = parseSelector();
+        selectors.add(selector);
+        Source source = selector;
+        while (true) {
+            String joinType;
+            if (readIf("RIGHT")) {
+                read("OUTER");
+                joinType = QueryObjectModelConstants.JCR_JOIN_TYPE_RIGHT_OUTER;
+            } else if (readIf("LEFT")) {
+                read("OUTER");
+                joinType = QueryObjectModelConstants.JCR_JOIN_TYPE_LEFT_OUTER;
+            } else if (readIf("INNER")) {
+                joinType = QueryObjectModelConstants.JCR_JOIN_TYPE_INNER;
+            } else {
+                break;
+            }
+            read("JOIN");
+            selector = parseSelector();
+            selectors.add(selector);
+            read("ON");
+            JoinCondition on = parseJoinCondition();
+            source = factory.join(source, selector, joinType, on);
+        }
+        return source;
+    }
+
+    private JoinCondition parseJoinCondition() throws RepositoryException {
+        boolean identifier = currentTokenType == IDENTIFIER;
+        String name = readName();
+        JoinCondition c;
+        if (identifier && readIf("(")) {
+            if ("ISSAMENODE".equalsIgnoreCase(name)) {
+                String selector1 = readName();
+                read(",");
+                String selector2 = readName();
+                if (readIf(",")) {
+                    c = factory.sameNodeJoinCondition(selector1, selector2, readPath());
+                } else {
+                    // TODO verify "." is correct
+                    c = factory.sameNodeJoinCondition(selector1, selector2, ".");
+                }
+            } else if ("ISCHILDNODE".equalsIgnoreCase(name)) {
+                String childSelector = readName();
+                read(",");
+                c = factory.childNodeJoinCondition(childSelector, readName());
+            } else if ("ISDESCENDANTNODE".equalsIgnoreCase(name)) {
+                String descendantSelector = readName();
+                read(",");
+                c = factory.descendantNodeJoinCondition(descendantSelector, readName());
+            } else {
+                throw getSyntaxError("ISSAMENODE, ISCHILDNODE, or ISDESCENDANTNODE");
+            }
+            read(")");
+            return c;
+        } else {
+            String selector1 = name;
+            read(".");
+            String property1 = readName();
+            read("=");
+            String selector2 = readName();
+            read(".");
+            return factory.equiJoinCondition(selector1, property1, selector2, readName());
+        }
+    }
+
+    private Constraint parseConstraint() throws RepositoryException {
+        Constraint a = parseAnd();
+        while (readIf("OR")) {
+            a = factory.or(a, parseAnd());
+        }
+        return a;
+    }
+
+    private Constraint parseAnd() throws RepositoryException {
+        Constraint a = parseCondition();
+        while (readIf("AND")) {
+            a = factory.and(a, parseCondition());
+        }
+        return a;
+    }
+
+    private Constraint parseCondition() throws RepositoryException {
+        Constraint a;
+        if (readIf("NOT")) {
+            a = factory.not(parseConstraint());
+        } else if (readIf("(")) {
+            a = parseConstraint();
+            read(")");
+        } else if (currentTokenType == IDENTIFIER) {
+            String identifier = readName();
+            if (readIf("(")) {
+                a = parseConditionFuntionIf(identifier);
+                if (a == null) {
+                    DynamicOperand op = parseExpressionFunction(identifier);
+                    a = parseCondition(op);
+                }
+            } else if (readIf(".")) {
+                a = parseCondition(factory.propertyValue(identifier, readName()));
+            } else {
+                a = parseCondition(factory.propertyValue(getOnlySelectorName(), identifier));
+            }
+        } else if ("[".equals(currentToken)) {
+            String name = readName();
+            if (readIf(".")) {
+                a = parseCondition(factory.propertyValue(name, readName()));
+            } else {
+                a = parseCondition(factory.propertyValue(getOnlySelectorName(), name));
+            }
+        } else {
+            throw getSyntaxError();
+        }
+        return a;
+    }
+
+    private Constraint parseCondition(DynamicOperand left) throws RepositoryException {
+        Constraint c;
+
+        if (readIf("=")) {
+            String operator = QueryObjectModelConstants.JCR_OPERATOR_EQUAL_TO;
+            c = factory.comparison(left, operator, parseStaticOperand());
+        } else if (readIf("<>")) {
+            String operator = QueryObjectModelConstants.JCR_OPERATOR_NOT_EQUAL_TO;
+            c = factory.comparison(left, operator, parseStaticOperand());
+        } else if (readIf("<")) {
+            String operator = QueryObjectModelConstants.JCR_OPERATOR_LESS_THAN;
+            c = factory.comparison(left, operator, parseStaticOperand());
+        } else if (readIf(">")) {
+            String operator = QueryObjectModelConstants.JCR_OPERATOR_GREATER_THAN;
+            c = factory.comparison(left, operator, parseStaticOperand());
+        } else if (readIf("<=")) {
+            String operator = QueryObjectModelConstants.JCR_OPERATOR_LESS_THAN_OR_EQUAL_TO;
+            c = factory.comparison(left, operator, parseStaticOperand());
+        } else if (readIf(">=")) {
+            String operator = QueryObjectModelConstants.JCR_OPERATOR_GREATER_THAN_OR_EQUAL_TO;
+            c = factory.comparison(left, operator, parseStaticOperand());
+        } else if (readIf("LIKE")) {
+            String operator = QueryObjectModelConstants.JCR_OPERATOR_LIKE;
+            c = factory.comparison(left, operator, parseStaticOperand());
+        } else if (readIf("IS")) {
+            boolean not = readIf("NOT");
+            read("NULL");
+            if (!(left instanceof PropertyValue)) {
+                throw getSyntaxError("propertyName (NOT NULL is only supported for properties)");
+            }
+            PropertyValue p = (PropertyValue) left;
+            c = getPropertyExistence(p);
+            if (!not) {
+                c = factory.not(c);
+            }
+        } else if (readIf("NOT")) {
+            if (readIf("IS")) {
+                read("NULL");
+                if (!(left instanceof PropertyValue)) {
+                    throw ExceptionFactory.repository(
+                            "Only property values can be tested for NOT IS NULL; got: "
+                            + left.getClass().getName());
+                }
+                PropertyValue pv = (PropertyValue) left;
+                c = getPropertyExistence(pv);
+            } else {
+                read("LIKE");
+                String operator = QueryObjectModelConstants.JCR_OPERATOR_LIKE;
+                c = factory.comparison(left, operator, parseStaticOperand());
+                c = factory.not(c);
+            }
+        } else {
+            throw getSyntaxError();
+        }
+        return c;
+    }
+
+    private PropertyExistence getPropertyExistence(PropertyValue p) throws InvalidQueryException, RepositoryException {
+        return factory.propertyExistence(p.getSelectorName(), p.getPropertyName());
+    }
+
+    private Constraint parseConditionFuntionIf(String functionName) throws RepositoryException {
+        Constraint c;
+        if ("CONTAINS".equalsIgnoreCase(functionName)) {
+            String name = readName();
+            if (readIf(".")) {
+                if (readIf("*")) {
+                    read(",");
+                    c = factory.fullTextSearch(
+                            name, null, parseStaticOperand());
+                } else {
+                    String selector = name;
+                    name = readName();
+                    read(",");
+                    c = factory.fullTextSearch(
+                            selector, name, parseStaticOperand());
+                }
+            } else {
+                read(",");
+                c = factory.fullTextSearch(
+                        getOnlySelectorName(), name,
+                        parseStaticOperand());
+            }
+        } else if ("ISSAMENODE".equalsIgnoreCase(functionName)) {
+            String name = readName();
+            if (readIf(",")) {
+                c = factory.sameNode(name, readPath());
+            } else {
+                c = factory.sameNode(getOnlySelectorName(), name);
+            }
+        } else if ("ISCHILDNODE".equalsIgnoreCase(functionName)) {
+            String name = readName();
+            if (readIf(",")) {
+                c = factory.childNode(name, readPath());
+            } else {
+                c = factory.childNode(getOnlySelectorName(), name);
+            }
+        } else if ("ISDESCENDANTNODE".equalsIgnoreCase(functionName)) {
+            String name = readName();
+            if (readIf(",")) {
+                c = factory.descendantNode(name, readPath());
+            } else {
+                c = factory.descendantNode(getOnlySelectorName(), name);
+            }
+        } else {
+            return null;
+        }
+        read(")");
+        return c;
+    }
+
+    private String readPath() throws RepositoryException {
+        return readName();
+    }
+
+    private DynamicOperand parseDynamicOperand() throws RepositoryException {
+        boolean identifier = currentTokenType == IDENTIFIER;
+        String name = readName();
+        if (identifier && readIf("(")) {
+            return parseExpressionFunction(name);
+        } else {
+            return parsePropertyValue(name);
+        }
+    }
+
+    private DynamicOperand parseExpressionFunction(String functionName) throws RepositoryException {
+        DynamicOperand op;
+        if ("LENGTH".equalsIgnoreCase(functionName)) {
+            op = factory.length(parsePropertyValue(readName()));
+        } else if ("NAME".equalsIgnoreCase(functionName)) {
+            if (isToken(")")) {
+                op = factory.nodeName(getOnlySelectorName());
+            } else {
+                op = factory.nodeName(readName());
+            }
+        } else if ("LOCALNAME".equalsIgnoreCase(functionName)) {
+            if (isToken(")")) {
+                op = factory.nodeLocalName(getOnlySelectorName());
+            } else {
+                op = factory.nodeLocalName(readName());
+            }
+        } else if ("SCORE".equalsIgnoreCase(functionName)) {
+            if (isToken(")")) {
+                op = factory.fullTextSearchScore(getOnlySelectorName());
+            } else {
+                op = factory.fullTextSearchScore(readName());
+            }
+        } else if ("LOWER".equalsIgnoreCase(functionName)) {
+            op = factory.lowerCase(parseDynamicOperand());
+        } else if ("UPPER".equalsIgnoreCase(functionName)) {
+            op = factory.upperCase(parseDynamicOperand());
+        } else {
+            throw getSyntaxError("LENGTH, NAME, LOCALNAME, SCORE, LOWER, UPPER, or CAST");
+        }
+        read(")");
+        return op;
+    }
+
+    private PropertyValue parsePropertyValue(String name) throws RepositoryException {
+        if (readIf(".")) {
+            return factory.propertyValue(name, readName());
+        } else {
+            return factory.propertyValue(getOnlySelectorName(), name);
+        }
+    }
+
+    private StaticOperand parseStaticOperand() throws RepositoryException {
+        if (currentTokenType == PLUS) {
+            read();
+        } else if (currentTokenType == MINUS) {
+            read();
+            if (currentTokenType != VALUE) {
+                throw getSyntaxError("number");
+            }
+            int valueType = currentValue.getType();
+            switch (valueType) {
+            case PropertyType.LONG:
+                currentValue = valueFactory.createValue(-currentValue.getLong());
+                break;
+            case PropertyType.DOUBLE:
+                currentValue = valueFactory.createValue(-currentValue.getDouble());
+                break;
+            case PropertyType.BOOLEAN:
+                currentValue = valueFactory.createValue(!currentValue.getBoolean());
+                break;
+            case PropertyType.DECIMAL:
+                currentValue = valueFactory.createValue(currentValue.getDecimal().negate());
+                break;
+            default:
+                throw getSyntaxError("Illegal operation: -" + currentValue);
+            }
+        }
+        if (currentTokenType == VALUE) {
+            Literal literal = getUncastLiteral(currentValue);
+            read();
+            return literal;
+        } else if (currentTokenType == PARAMETER) {
+            read();
+            String name = readName();
+            if (readIf(":")) {
+                name = name + ":" + readName();
+            }
+            BindVariableValue var = bindVariables.get(name);
+            if (var == null) {
+                var = factory.bindVariable(name);
+                bindVariables.put(name, var);
+            }
+            return var;
+        } else if (readIf("TRUE")) {
+            Literal literal = getUncastLiteral(valueFactory.createValue(true));
+            return literal;
+        } else if (readIf("FALSE")) {
+            Literal literal = getUncastLiteral(valueFactory.createValue(false));
+            return literal;
+        } else if (readIf("CAST")) {
+            read("(");
+            StaticOperand op = parseStaticOperand();
+            if (!(op instanceof Literal)) {
+                throw getSyntaxError("literal");
+            }
+            Literal literal = (Literal) op;
+            Value value = literal.getLiteralValue();
+            read("AS");
+            value = parseCastAs(value);
+            read(")");
+            // CastLiteral
+            literal = factory.literal(value);
+            return literal;
+        } else {
+            throw getSyntaxError("static operand");
+        }
+    }
+
+    /**
+     * Create a literal from a parsed value.
+     *
+     * @param value the original value
+     * @return the literal
+     */
+    private Literal getUncastLiteral(Value value) throws RepositoryException {
+        return factory.literal(value);
+    }
+
+    private Value parseCastAs(Value value) throws RepositoryException {
+        if (readIf("STRING")) {
+            return valueFactory.createValue(value.getString());
+        } else if (readIf("BINARY")) {
+            return valueFactory.createValue(value.getBinary());
+        } else if (readIf("DATE")) {
+            return valueFactory.createValue(value.getDate());
+        } else if (readIf("LONG")) {
+            return valueFactory.createValue(value.getLong());
+        } else if (readIf("DOUBLE")) {
+            return valueFactory.createValue(value.getDouble());
+        } else if (readIf("DECIMAL")) {
+            return valueFactory.createValue(value.getDecimal());
+        } else if (readIf("BOOLEAN")) {
+            return valueFactory.createValue(value.getBoolean());
+        } else if (readIf("NAME")) {
+            return valueFactory.createValue(value.getString(), PropertyType.NAME);
+        } else if (readIf("PATH")) {
+            return valueFactory.createValue(value.getString(), PropertyType.PATH);
+        } else if (readIf("REFERENCE")) {
+            return valueFactory.createValue(value.getString(), PropertyType.REFERENCE);
+        } else if (readIf("WEAKREFERENCE")) {
+            return valueFactory.createValue(value.getString(), PropertyType.WEAKREFERENCE);
+        } else if (readIf("URI")) {
+            return valueFactory.createValue(value.getString(), PropertyType.URI);
+        } else {
+            throw getSyntaxError("data type (STRING|BINARY|...)");
+        }
+    }
+
+    private Ordering[] parseOrder() throws RepositoryException {
+        ArrayList<Ordering> orderList = new ArrayList<Ordering>();
+        do {
+            Ordering ordering;
+            DynamicOperand op = parseDynamicOperand();
+            if (readIf("DESC")) {
+                ordering = factory.descending(op);
+            } else {
+                readIf("ASC");
+                ordering = factory.ascending(op);
+            }
+            orderList.add(ordering);
+        } while (readIf(","));
+        Ordering[] orderings = new Ordering[orderList.size()];
+        orderList.toArray(orderings);
+        return orderings;
+    }
+
+    private ArrayList<ColumnOrWildcard> parseColumns() throws RepositoryException {
+        ArrayList<ColumnOrWildcard> list = new ArrayList<ColumnOrWildcard>();
+        if (readIf("*")) {
+            list.add(new ColumnOrWildcard());
+        } else {
+            do {
+                ColumnOrWildcard column = new ColumnOrWildcard();
+                column.propertyName = readName();
+                if (readIf(".")) {
+                    column.selectorName = column.propertyName;
+                    if (readIf("*")) {
+                        column.propertyName = null;
+                    } else {
+                        column.propertyName = readName();
+                        if (readIf("AS")) {
+                            column.columnName = readName();
+                        }
+                    }
+                } else {
+                    if (readIf("AS")) {
+                        column.columnName = readName();
+                    }
+                }
+                list.add(column);
+            } while (readIf(","));
+        }
+        return list;
+    }
+
+    private Column[] resolveColumns(ArrayList<ColumnOrWildcard> list) throws RepositoryException {
+        ArrayList<Column> columns = new ArrayList<Column>();
+        for (ColumnOrWildcard c : list) {
+            if (c.propertyName == null) {
+                for (Selector selector : selectors) {
+                    if (c.selectorName == null
+                            || c.selectorName
+                                    .equals(selector.getSelectorName())) {
+                        Column column = factory.column(selector
+                                .getSelectorName(), null, null);
+                        columns.add(column);
+                    }
+                }
+            } else {
+                Column column;
+                if (c.selectorName != null) {
+                    column = factory.column(c.selectorName, c.propertyName, c.columnName);
+                } else if (c.columnName != null) {
+                    column = factory.column(getOnlySelectorName(), c.propertyName, c.columnName);
+                } else {
+                    column = factory.column(getOnlySelectorName(), c.propertyName, c.propertyName);
+                }
+                columns.add(column);
+            }
+        }
+        Column[] array = new Column[columns.size()];
+        columns.toArray(array);
+        return array;
+    }
+
+    private boolean readIf(String token) throws RepositoryException {
+        if (isToken(token)) {
+            read();
+            return true;
+        }
+        return false;
+    }
+
+    private boolean isToken(String token) {
+        boolean result = token.equalsIgnoreCase(currentToken) && !currentTokenQuoted;
+        if (result) {
+            return true;
+        }
+        addExpected(token);
+        return false;
+    }
+
+    private void read(String expected) throws RepositoryException {
+        if (!expected.equalsIgnoreCase(currentToken) || currentTokenQuoted) {
+            throw getSyntaxError(expected);
+        }
+        read();
+    }
+
+    private String readAny() throws RepositoryException {
+        if (currentTokenType == END) {
+            throw getSyntaxError("a token");
+        }
+        String s;
+        if (currentTokenType == VALUE) {
+            s = currentValue.getString();
+        } else {
+            s = currentToken;
+        }
+        read();
+        return s;
+    }
+
+    private Value readString() throws RepositoryException {
+        if (currentTokenType != VALUE) {
+            throw getSyntaxError("string value");
+        }
+        Value value = currentValue;
+        read();
+        return value;
+    }
+
+    private void addExpected(String token) {
+        if (expected != null) {
+            expected.add(token);
+        }
+    }
+
+    private void initialize(String query) throws InvalidQueryException {
+        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 ']':
+                type = CHAR_SPECIAL_1;
+                break;
+            case '!':
+            case '<':
+            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 '\"':
+                type = CHAR_QUOTED;
+                types[i] = CHAR_QUOTED;
+                startLoop = i;
+                while (command[++i] != '\"') {
+                    checkRunOver(i, len, startLoop);
+                }
+                break;
+            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 InvalidQueryException {
+        if (i >= len) {
+            parseIndex = startLoop;
+            throw getSyntaxError();
+        }
+    }
+
+    private void read() throws RepositoryException {
+        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.length() == 0) {
+                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 = PARAMETER;
+                break;
+            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;
+                    }
+                    checkLiterals(false);
+                    currentValue = valueFactory.createValue(number);
+                    currentTokenType = VALUE;
+                    currentToken = "0";
+                    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_QUOTED:
+            readString(i, '\"');
+            return;
+        case CHAR_END:
+            currentToken = "";
+            currentTokenType = END;
+            parseIndex = i;
+            return;
+        default:
+            throw getSyntaxError();
+        }
+    }
+
+    private void readString(int i, char end) throws RepositoryException {
+        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 = "'";
+        checkLiterals(false);
+        currentValue = valueFactory.createValue(result);
+        parseIndex = i;
+        currentTokenType = VALUE;
+    }
+
+    private void checkLiterals(boolean text) throws InvalidQueryException {
+        if (text && !allowTextLiterals || (!text && !allowNumberLiterals)) {
+            throw getSyntaxError("bind variable (literals of this type not allowed)");
+        }
+    }
+
+    private void readDecimal(int start, int i) throws RepositoryException {
+        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);
+        BigDecimal bd;
+        try {
+            bd = new BigDecimal(sub);
+        } catch (NumberFormatException e) {
+            throw ExceptionFactory.invalidQuery("Data conversion error converting " + sub + " to BigDecimal: " + e);
+        }
+        checkLiterals(false);
+
+        currentValue = valueFactory.createValue(bd);
+        currentTokenType = VALUE;
+    }
+
+    private InvalidQueryException getSyntaxError() {
+        if (expected == null || expected.size() == 0) {
+            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 InvalidQueryException 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 InvalidQueryException("Query:\n" + query);
+    }
+
+    /**
+     * Represents a column or a wildcard in a SQL expression.
+     * This class is temporarily used during parsing.
+     */
+    static class ColumnOrWildcard {
+        String selectorName;
+        String propertyName;
+        String columnName;
+    }
+
+    /**
+     * Get the selector name if only one selector exists in the query.
+     * If more than one selector exists, an exception is thrown.
+     *
+     * @return the selector name
+     */
+    private String getOnlySelectorName() throws RepositoryException {
+        if (selectors.size() > 1) {
+            throw getSyntaxError("Need to specify the selector name because the query contains more than one selector.");
+        }
+        return selectors.get(0).getSelectorName();
+    }
+
+}

Added: jackrabbit/sandbox/jackrabbit-microkernel/src/main/java/org/apache/jackrabbit/query/ParserXPath.java
URL: http://svn.apache.org/viewvc/jackrabbit/sandbox/jackrabbit-microkernel/src/main/java/org/apache/jackrabbit/query/ParserXPath.java?rev=1292828&view=auto
==============================================================================
--- jackrabbit/sandbox/jackrabbit-microkernel/src/main/java/org/apache/jackrabbit/query/ParserXPath.java (added)
+++ jackrabbit/sandbox/jackrabbit-microkernel/src/main/java/org/apache/jackrabbit/query/ParserXPath.java Thu Feb 23 15:34:27 2012
@@ -0,0 +1,458 @@
+/*
+ * 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.query;
+
+import java.math.BigDecimal;
+import java.util.ArrayList;
+import java.util.HashMap;
+import javax.jcr.RepositoryException;
+import javax.jcr.Value;
+import javax.jcr.ValueFactory;
+import javax.jcr.query.InvalidQueryException;
+import javax.jcr.query.qom.BindVariableValue;
+import javax.jcr.query.qom.Column;
+import javax.jcr.query.qom.Constraint;
+import javax.jcr.query.qom.Ordering;
+import javax.jcr.query.qom.QueryObjectModel;
+import javax.jcr.query.qom.QueryObjectModelFactory;
+import javax.jcr.query.qom.Selector;
+import javax.jcr.query.qom.Source;
+import org.apache.jackrabbit.query.ParserSQL2.ColumnOrWildcard;
+import org.apache.jackrabbit.query.qom.QueryObjectModelFactoryImpl;
+import org.apache.jackrabbit.query.val.ExceptionFactory;
+import org.apache.jackrabbit.query.val.ValueFactoryImpl;
+
+
+/**
+ * The XPath parser can convert a XPATH query to a QueryObjectModel.
+ */
+public class ParserXPath {
+
+    // Character types, used during the tokenizer phase
+    private static final int CHAR_END = -1, CHAR_VALUE = 2, CHAR_QUOTED = 3;
+    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, PARAMETER = 3, END = 4, VALUE = 5;
+    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 Value currentValue;
+    private ArrayList<String> expected;
+
+    // Code injection protection: if disabled, literals are not allowed
+    private boolean allowTextLiterals = true, allowNumberLiterals = true;
+
+    private QueryObjectModelFactory factory;
+    private ValueFactory valueFactory;
+
+    /**
+     * Create a new parser. A parser can be re-used, but it is not thread safe.
+     *
+     * @param factory the query object model factory
+     * @param valueFactory the value factory
+     */
+    public ParserXPath(QueryObjectModelFactory factory, ValueFactory valueFactory) {
+        this.factory = factory;
+        this.valueFactory = valueFactory;
+    }
+
+    /**
+     * Parse a JCR-SQL2 query and return the query object model
+     *
+     * @param query the query string
+     * @return the query object model
+     * @throws RepositoryException if parsing failed
+     */
+    public QueryObjectModel createQueryObjectModel(String query) throws RepositoryException {
+        initialize(query);
+        expected = new ArrayList<String>();
+        read();
+        if (readIf("//")) {
+            System.out.println("any child node");
+        } else if (readIf("/")) {
+            System.out.println("any node: " + readString());
+        }
+        if (currentToken.length() > 0) {
+            throw getSyntaxError("<end>");
+        }
+        return null;
+        // return factory.createQuery(source, constraint, orderings, columnArray);
+    }
+
+    private boolean readIf(String token) throws RepositoryException {
+        if (isToken(token)) {
+            read();
+            return true;
+        }
+        return false;
+    }
+
+    private boolean isToken(String token) {
+        boolean result = token.equalsIgnoreCase(currentToken) && !currentTokenQuoted;
+        if (result) {
+            return true;
+        }
+        addExpected(token);
+        return false;
+    }
+
+    private void read(String expected) throws RepositoryException {
+        if (!expected.equalsIgnoreCase(currentToken) || currentTokenQuoted) {
+            throw getSyntaxError(expected);
+        }
+        read();
+    }
+
+    private String readAny() throws RepositoryException {
+        if (currentTokenType == END) {
+            throw getSyntaxError("a token");
+        }
+        String s;
+        if (currentTokenType == VALUE) {
+            s = currentValue.getString();
+        } else {
+            s = currentToken;
+        }
+        read();
+        return s;
+    }
+
+    private Value readString() throws RepositoryException {
+        if (currentTokenType != VALUE) {
+            throw getSyntaxError("string value");
+        }
+        Value value = currentValue;
+        read();
+        return value;
+    }
+
+    private void addExpected(String token) {
+        if (expected != null) {
+            expected.add(token);
+        }
+    }
+
+    private void initialize(String query) throws InvalidQueryException {
+        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 ']':
+                type = CHAR_SPECIAL_1;
+                break;
+            case '!':
+            case '<':
+            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 '\"':
+                type = CHAR_QUOTED;
+                types[i] = CHAR_QUOTED;
+                startLoop = i;
+                while (command[++i] != '\"') {
+                    checkRunOver(i, len, startLoop);
+                }
+                break;
+            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 InvalidQueryException {
+        if (i >= len) {
+            parseIndex = startLoop;
+            throw getSyntaxError();
+        }
+    }
+
+    private void read() throws RepositoryException {
+        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.length() == 0) {
+                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 = PARAMETER;
+                break;
+            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;
+                    }
+                    checkLiterals(false);
+                    currentValue = valueFactory.createValue(number);
+                    currentTokenType = VALUE;
+                    currentToken = "0";
+                    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_QUOTED:
+            readString(i, '\"');
+            return;
+        case CHAR_END:
+            currentToken = "";
+            currentTokenType = END;
+            parseIndex = i;
+            return;
+        default:
+            throw getSyntaxError();
+        }
+    }
+
+    private void readString(int i, char end) throws RepositoryException {
+        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 = "'";
+        checkLiterals(false);
+        currentValue = valueFactory.createValue(result);
+        parseIndex = i;
+        currentTokenType = VALUE;
+    }
+
+    private void checkLiterals(boolean text) throws InvalidQueryException {
+        if (text && !allowTextLiterals || (!text && !allowNumberLiterals)) {
+            throw getSyntaxError("bind variable (literals of this type not allowed)");
+        }
+    }
+
+    private void readDecimal(int start, int i) throws RepositoryException {
+        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);
+        BigDecimal bd;
+        try {
+            bd = new BigDecimal(sub);
+        } catch (NumberFormatException e) {
+            throw ExceptionFactory.invalidQuery("Data conversion error converting " + sub + " to BigDecimal: " + e);
+        }
+        checkLiterals(false);
+
+        currentValue = valueFactory.createValue(bd);
+        currentTokenType = VALUE;
+    }
+
+    private InvalidQueryException getSyntaxError() {
+        if (expected == null || expected.size() == 0) {
+            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 InvalidQueryException 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 InvalidQueryException("Query:\n" + query);
+    }
+
+
+}
+

Added: jackrabbit/sandbox/jackrabbit-microkernel/src/main/java/org/apache/jackrabbit/query/QueryImpl.java
URL: http://svn.apache.org/viewvc/jackrabbit/sandbox/jackrabbit-microkernel/src/main/java/org/apache/jackrabbit/query/QueryImpl.java?rev=1292828&view=auto
==============================================================================
--- jackrabbit/sandbox/jackrabbit-microkernel/src/main/java/org/apache/jackrabbit/query/QueryImpl.java (added)
+++ jackrabbit/sandbox/jackrabbit-microkernel/src/main/java/org/apache/jackrabbit/query/QueryImpl.java Thu Feb 23 15:34:27 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.query;
+
+import javax.jcr.ItemExistsException;
+import javax.jcr.ItemNotFoundException;
+import javax.jcr.Node;
+import javax.jcr.PathNotFoundException;
+import javax.jcr.RepositoryException;
+import javax.jcr.UnsupportedRepositoryOperationException;
+import javax.jcr.Value;
+import javax.jcr.lock.LockException;
+import javax.jcr.nodetype.ConstraintViolationException;
+import javax.jcr.query.InvalidQueryException;
+import javax.jcr.query.Query;
+import javax.jcr.query.QueryResult;
+import javax.jcr.query.qom.QueryObjectModel;
+import javax.jcr.version.VersionException;
+
+/**
+ * The implementation of the corresponding JCR interface.
+ */
+public class QueryImpl implements Query {
+
+    private final QueryObjectModel qom;
+
+    public QueryImpl(QueryObjectModel qom) {
+        this.qom = qom;
+    }
+
+    public void bindValue(String varName, Value value) throws IllegalArgumentException, RepositoryException {
+        qom.bindValue(varName, value);
+    }
+
+    public QueryResult execute() throws InvalidQueryException, RepositoryException {
+        return qom.execute();
+    }
+
+    public String[] getBindVariableNames() throws RepositoryException {
+        return qom.getBindVariableNames();
+    }
+
+    public String getLanguage() {
+        return qom.getLanguage();
+    }
+
+    public String getStatement() {
+        return qom.getStatement();
+    }
+
+    public String getStoredQueryPath() throws ItemNotFoundException, RepositoryException {
+        return qom.getStoredQueryPath();
+    }
+
+    public void setLimit(long limit) {
+        qom.setLimit(limit);
+    }
+
+    public void setOffset(long offset) {
+        qom.setOffset(offset);
+    }
+
+    public Node storeAsNode(String absPath) throws ItemExistsException, PathNotFoundException, VersionException,
+            ConstraintViolationException, LockException, UnsupportedRepositoryOperationException, RepositoryException {
+        return qom.storeAsNode(absPath);
+    }
+
+}

Added: jackrabbit/sandbox/jackrabbit-microkernel/src/main/java/org/apache/jackrabbit/query/QueryManagerImpl.java
URL: http://svn.apache.org/viewvc/jackrabbit/sandbox/jackrabbit-microkernel/src/main/java/org/apache/jackrabbit/query/QueryManagerImpl.java?rev=1292828&view=auto
==============================================================================
--- jackrabbit/sandbox/jackrabbit-microkernel/src/main/java/org/apache/jackrabbit/query/QueryManagerImpl.java (added)
+++ jackrabbit/sandbox/jackrabbit-microkernel/src/main/java/org/apache/jackrabbit/query/QueryManagerImpl.java Thu Feb 23 15:34:27 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.query;
+
+import javax.jcr.Node;
+import javax.jcr.RepositoryException;
+import javax.jcr.query.InvalidQueryException;
+import javax.jcr.query.Query;
+import javax.jcr.query.QueryManager;
+import javax.jcr.query.qom.QueryObjectModel;
+import org.apache.jackrabbit.query.qom.QueryObjectModelFactoryImpl;
+import org.apache.jackrabbit.query.val.ValueFactoryImpl;
+/**
+ * The implementation of the corresponding JCR interface.
+ */
+public class QueryManagerImpl implements QueryManager {
+
+    private final QueryObjectModelFactoryImpl qomFactory;
+    private final ParserSQL2 parserSQL2;
+    private final ParserXPath parserXPath;
+
+    @SuppressWarnings("deprecation")
+    private static final String[] SUPPOERTED_QUERY_LANGUAGES = {
+        Query.JCR_JQOM,
+        Query.JCR_SQL2,
+        Query.SQL,
+        Query.XPATH
+    };
+
+    public QueryManagerImpl(QueryObjectModelFactoryImpl qomFactory, ValueFactoryImpl valueFactory) {
+        this.qomFactory = qomFactory;
+        parserSQL2 = new ParserSQL2(qomFactory, valueFactory);
+        parserXPath = new ParserXPath(qomFactory, valueFactory);
+    }
+
+    public Query createQuery(String statement, String language) throws InvalidQueryException, RepositoryException {
+        if (Query.JCR_SQL2.equals(language)) {
+            QueryObjectModel qom = parserSQL2.createQueryObjectModel(statement);
+            return new QueryImpl(qom);
+        } else if (Query.XPATH.equals(language)) {
+            QueryObjectModel qom = parserXPath.createQueryObjectModel(statement);
+            return new QueryImpl(qom);
+        }
+        // TODO Auto-generated method stub
+        return null;
+    }
+
+    public QueryObjectModelFactoryImpl getQOMFactory() {
+        return qomFactory;
+    }
+
+    public Query getQuery(Node node) throws InvalidQueryException, RepositoryException {
+        // TODO Auto-generated method stub
+        return null;
+    }
+
+    public String[] getSupportedQueryLanguages() throws RepositoryException {
+        // TODO the list is mutable
+        return SUPPOERTED_QUERY_LANGUAGES;
+    }
+
+}

Added: jackrabbit/sandbox/jackrabbit-microkernel/src/main/java/org/apache/jackrabbit/query/QueryResultImpl.java
URL: http://svn.apache.org/viewvc/jackrabbit/sandbox/jackrabbit-microkernel/src/main/java/org/apache/jackrabbit/query/QueryResultImpl.java?rev=1292828&view=auto
==============================================================================
--- jackrabbit/sandbox/jackrabbit-microkernel/src/main/java/org/apache/jackrabbit/query/QueryResultImpl.java (added)
+++ jackrabbit/sandbox/jackrabbit-microkernel/src/main/java/org/apache/jackrabbit/query/QueryResultImpl.java Thu Feb 23 15:34:27 2012
@@ -0,0 +1,49 @@
+/*
+ * 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.query;
+
+import javax.jcr.NodeIterator;
+import javax.jcr.RepositoryException;
+import javax.jcr.query.QueryResult;
+import javax.jcr.query.RowIterator;
+
+/**
+ * The implementation of the corresponding JCR interface.
+ */
+public class QueryResultImpl implements QueryResult {
+
+    public String[] getColumnNames() throws RepositoryException {
+        // TODO Auto-generated method stub
+        return null;
+    }
+
+    public NodeIterator getNodes() throws RepositoryException {
+        // TODO Auto-generated method stub
+        return null;
+    }
+
+    public RowIterator getRows() throws RepositoryException {
+        // TODO Auto-generated method stub
+        return null;
+    }
+
+    public String[] getSelectorNames() throws RepositoryException {
+        // TODO Auto-generated method stub
+        return null;
+    }
+
+}

Added: jackrabbit/sandbox/jackrabbit-microkernel/src/main/java/org/apache/jackrabbit/query/RangeIteratorImpl.java
URL: http://svn.apache.org/viewvc/jackrabbit/sandbox/jackrabbit-microkernel/src/main/java/org/apache/jackrabbit/query/RangeIteratorImpl.java?rev=1292828&view=auto
==============================================================================
--- jackrabbit/sandbox/jackrabbit-microkernel/src/main/java/org/apache/jackrabbit/query/RangeIteratorImpl.java (added)
+++ jackrabbit/sandbox/jackrabbit-microkernel/src/main/java/org/apache/jackrabbit/query/RangeIteratorImpl.java Thu Feb 23 15:34:27 2012
@@ -0,0 +1,100 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.jackrabbit.query;
+
+import java.util.Iterator;
+import java.util.NoSuchElementException;
+import javax.jcr.RangeIterator;
+import javax.jcr.RepositoryException;
+
+/**
+ * The implementation of the corresponding JCR interface.
+ *
+ * @param <T> the type
+ */
+public abstract class RangeIteratorImpl<T> implements RangeIterator {
+
+    private final Iterator<T> iterator;
+    private T next;
+    private long size;
+    private long pos;
+
+    protected RangeIteratorImpl(Iterator<T> iterator, long size) {
+        this.iterator = iterator;
+        this.size = size;
+        fetchNext();
+    }
+
+    public boolean skip(T x) throws RepositoryException {
+        return false;
+    }
+
+    void fetchNext() {
+        try {
+            while (true) {
+                if (!iterator.hasNext()) {
+                    next = null;
+                    break;
+                }
+                next = iterator.next();
+                if (skip(next)) {
+                    continue;
+                }
+                break;
+            }
+        } catch (RepositoryException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    public Object next() {
+        return getNext();
+    }
+
+    public T getNext() {
+        pos++;
+        T result = next;
+        if (result == null) {
+            throw new NoSuchElementException();
+        }
+        fetchNext();
+        return result;
+    }
+
+    public long getPosition() {
+        return pos;
+    }
+
+    public long getSize() {
+        return size;
+    }
+
+    public void skip(long skipNum) {
+        for (int i = 0; i < skipNum; i++) {
+            getNext();
+        }
+    }
+
+    public boolean hasNext() {
+        return next != null;
+    }
+
+    public void remove() {
+        throw new UnsupportedOperationException();
+    }
+
+}

Added: jackrabbit/sandbox/jackrabbit-microkernel/src/main/java/org/apache/jackrabbit/query/RowImpl.java
URL: http://svn.apache.org/viewvc/jackrabbit/sandbox/jackrabbit-microkernel/src/main/java/org/apache/jackrabbit/query/RowImpl.java?rev=1292828&view=auto
==============================================================================
--- jackrabbit/sandbox/jackrabbit-microkernel/src/main/java/org/apache/jackrabbit/query/RowImpl.java (added)
+++ jackrabbit/sandbox/jackrabbit-microkernel/src/main/java/org/apache/jackrabbit/query/RowImpl.java Thu Feb 23 15:34:27 2012
@@ -0,0 +1,70 @@
+/*
+ * 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.query;
+
+import javax.jcr.ItemNotFoundException;
+import javax.jcr.Node;
+import javax.jcr.RepositoryException;
+import javax.jcr.Value;
+import javax.jcr.query.Row;
+
+/**
+ * The implementation of the corresponding JCR interface.
+ */
+public class RowImpl implements Row {
+
+    public Node getNode() throws RepositoryException {
+        // TODO
+        return null;
+    }
+
+    public Node getNode(String selectorName) throws RepositoryException {
+        // TODO
+        return null;
+    }
+
+    public String getPath() throws RepositoryException {
+        // TODO
+        return null;
+    }
+
+    public String getPath(String selectorName) throws RepositoryException {
+        // TODO
+        return null;
+    }
+
+    public double getScore() throws RepositoryException {
+        // TODO
+        return 0;
+    }
+
+    public double getScore(String selectorName) throws RepositoryException {
+        // TODO
+        return 0;
+    }
+
+    public Value getValue(String columnName) throws ItemNotFoundException, RepositoryException {
+        // TODO
+        return null;
+    }
+
+    public Value[] getValues() throws RepositoryException {
+        // TODO
+        return null;
+    }
+
+}

Added: jackrabbit/sandbox/jackrabbit-microkernel/src/main/java/org/apache/jackrabbit/query/RowIteratorImpl.java
URL: http://svn.apache.org/viewvc/jackrabbit/sandbox/jackrabbit-microkernel/src/main/java/org/apache/jackrabbit/query/RowIteratorImpl.java?rev=1292828&view=auto
==============================================================================
--- jackrabbit/sandbox/jackrabbit-microkernel/src/main/java/org/apache/jackrabbit/query/RowIteratorImpl.java (added)
+++ jackrabbit/sandbox/jackrabbit-microkernel/src/main/java/org/apache/jackrabbit/query/RowIteratorImpl.java Thu Feb 23 15:34:27 2012
@@ -0,0 +1,36 @@
+/*
+ * 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.query;
+
+import java.util.Collection;
+import javax.jcr.query.Row;
+import javax.jcr.query.RowIterator;
+
+/**
+ * The implementation of the corresponding JCR interface.
+ */
+public class RowIteratorImpl extends RangeIteratorImpl<Row> implements RowIterator {
+
+    protected RowIteratorImpl(Collection<Row> coll, long size) {
+        super(coll.iterator(), size);
+    }
+
+    public Row nextRow() {
+        return getNext();
+    }
+
+}

Added: jackrabbit/sandbox/jackrabbit-microkernel/src/main/java/org/apache/jackrabbit/query/index/Index.java
URL: http://svn.apache.org/viewvc/jackrabbit/sandbox/jackrabbit-microkernel/src/main/java/org/apache/jackrabbit/query/index/Index.java?rev=1292828&view=auto
==============================================================================
--- jackrabbit/sandbox/jackrabbit-microkernel/src/main/java/org/apache/jackrabbit/query/index/Index.java (added)
+++ jackrabbit/sandbox/jackrabbit-microkernel/src/main/java/org/apache/jackrabbit/query/index/Index.java Thu Feb 23 15:34:27 2012
@@ -0,0 +1,93 @@
+/*
+ * 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.query.index;
+
+public class Index {
+
+    /**
+     *  The path of what the index contains, or null if not set.
+     */
+    private String pathPrefix;
+
+    /**
+     *  The value prefix, or null if not set.
+     */
+    private String valuePrefix;
+
+    /**
+     *  The property name, or null if not set.
+     */
+    private String propertyName;
+
+    /**
+     *  The node type, or null if not set.
+     */
+    // TODO: use a hash set?
+    private String nodeType;
+
+    public String getPathPrefix() {
+        return pathPrefix;
+    }
+
+    public void setPathPrefix(String pathPrefix) {
+        this.pathPrefix = pathPrefix;
+    }
+
+    public String getValuePrefix() {
+        return valuePrefix;
+    }
+
+    public void setValuePrefix(String valuePrefix) {
+        this.valuePrefix = valuePrefix;
+    }
+
+    public String getPropertyName() {
+        return propertyName;
+    }
+
+    public void setPropertyName(String propertyName) {
+        this.propertyName = propertyName;
+    }
+
+    public String getNodeType() {
+        return nodeType;
+    }
+
+    public void setNodeType(String nodeType) {
+        this.nodeType = nodeType;
+    }
+
+    public String toString() {
+        StringBuilder buff = new StringBuilder("Index on ");
+        if (pathPrefix != null) {
+            buff.append("pathPrefix=").append(pathPrefix);
+        }
+        if (valuePrefix != null) {
+            buff.append("valuePrefix=").append(valuePrefix);
+        }
+        if (propertyName != null) {
+            buff.append("propertyName=").append(propertyName);
+        }
+        if (nodeType != null) {
+            buff.append("nodeType=").append(nodeType);
+        }
+        return buff.toString();
+    }
+
+}