Index: src/main/java/org/apache/jackrabbit/core/query/DefaultQueryNodeFactory.java
===================================================================
--- src/main/java/org/apache/jackrabbit/core/query/DefaultQueryNodeFactory.java	(revision 0)
+++ src/main/java/org/apache/jackrabbit/core/query/DefaultQueryNodeFactory.java	(revision 0)
@@ -0,0 +1,115 @@
+/*
+ * 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.core.query;
+
+import org.apache.jackrabbit.name.QName;
+
+/**
+ * Default implementetation of a {@link QueryNodeFactory}.
+ */
+public class DefaultQueryNodeFactory implements QueryNodeFactory {
+
+    /**
+     * {@inheritDoc}
+     */
+    public NodeTypeQueryNode createNodeTypeQueryNode(QueryNode parent,
+                                                     QName nodeType) {
+        return new NodeTypeQueryNode(parent, nodeType);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public AndQueryNode createAndQueryNode(QueryNode parent) {
+        return new AndQueryNode(parent);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public LocationStepQueryNode createLocationStepQueryNode(QueryNode parent) {
+        return new LocationStepQueryNode(parent);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public DerefQueryNode createDerefQueryNode(QueryNode parent,
+                                               QName nameTest,
+                                               boolean descendants) {
+        return new DerefQueryNode(parent, nameTest, descendants);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public NotQueryNode createNotQueryNode(QueryNode parent) {
+        return new NotQueryNode(parent);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public OrQueryNode createOrQueryNode(QueryNode parent) {
+        return new OrQueryNode(parent);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public RelationQueryNode createRelationQueryNode(QueryNode parent,
+                                                     int operation) {
+        return new RelationQueryNode(parent, operation);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public PathQueryNode createPathQueryNode(QueryNode parent) {
+        return new PathQueryNode(parent);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public OrderQueryNode createOrderQueryNode(QueryNode parent) {
+        return new OrderQueryNode(parent);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public PropertyFunctionQueryNode createPropertyFunctionQueryNode(
+            QueryNode parent, String functionName) {
+        return new PropertyFunctionQueryNode(parent, functionName);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public QueryRootNode createQueryRootNode() {
+        return new QueryRootNode();
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public TextsearchQueryNode createTextsearchQueryNode(QueryNode parent,
+                                                         String query) {
+        return new TextsearchQueryNode(parent, query);
+    }
+}

Property changes on: src\main\java\org\apache\jackrabbit\core\query\DefaultQueryNodeFactory.java
___________________________________________________________________
Name: svn:eol-style
   + native

Index: src/main/java/org/apache/jackrabbit/core/query/lucene/QueryImpl.java
===================================================================
--- src/main/java/org/apache/jackrabbit/core/query/lucene/QueryImpl.java	(revision 568580)
+++ src/main/java/org/apache/jackrabbit/core/query/lucene/QueryImpl.java	(working copy)
@@ -30,6 +30,7 @@
 import org.apache.jackrabbit.core.query.QueryParser;
 import org.apache.jackrabbit.core.query.QueryRootNode;
 import org.apache.jackrabbit.core.query.AndQueryNode;
+import org.apache.jackrabbit.core.query.QueryNodeFactory;
 import org.apache.jackrabbit.name.QName;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -124,6 +125,36 @@
     }
 
     /**
+     * Creates a new query instance from a query string.
+     *
+     * @param session   the session of the user executing this query.
+     * @param itemMgr   the item manager of the session executing this query.
+     * @param index     the search index.
+     * @param propReg   the property type registry.
+     * @param statement the query statement.
+     * @param language  the syntax of the query statement.
+     * @param factory   the query node factory.
+     * @throws InvalidQueryException if the query statement is invalid according
+     *                               to the specified <code>language</code>.
+     */
+    public QueryImpl(SessionImpl session,
+                     ItemManager itemMgr,
+                     SearchIndex index,
+                     PropertyTypeRegistry propReg,
+                     String statement,
+                     String language,
+                     QueryNodeFactory factory) throws InvalidQueryException {
+        this.session = session;
+        this.itemMgr = itemMgr;
+        this.index = index;
+        this.propReg = propReg;
+        // parse query according to language
+        // build query tree using the passed factory
+        this.root = QueryParser.parse(statement, language,
+                session.getNamespaceResolver(), factory);
+    }
+
+    /**
      * Executes this query and returns a <code>{@link QueryResult}</code>.
      *
      * @param offset the offset in the total result set
Index: src/main/java/org/apache/jackrabbit/core/query/PathQueryNode.java
===================================================================
--- src/main/java/org/apache/jackrabbit/core/query/PathQueryNode.java	(revision 568580)
+++ src/main/java/org/apache/jackrabbit/core/query/PathQueryNode.java	(working copy)
@@ -16,6 +16,10 @@
  */
 package org.apache.jackrabbit.core.query;
 
+import java.util.Arrays;
+import java.util.List;
+import java.util.Collections;
+
 import org.apache.jackrabbit.name.QName;
 
 /**
@@ -23,23 +27,58 @@
  */
 public class PathQueryNode extends NAryQueryNode {
 
+    public static final List VALID_SYSTEM_INDEX_NODE_TYPE_NAMES
+            = Collections.unmodifiableList(Arrays.asList(new QName[]{
+                QName.NT_CHILDNODEDEFINITION,
+                QName.NT_FROZENNODE,
+                QName.NT_NODETYPE,
+                QName.NT_PROPERTYDEFINITION,
+                QName.NT_VERSION,
+                QName.NT_VERSIONEDCHILD,
+                QName.NT_VERSIONHISTORY,
+                QName.NT_VERSIONLABELS,
+                QName.REP_NODETYPES,
+                QName.REP_SYSTEM,
+                QName.REP_VERSIONSTORAGE,
+                // Supertypes
+                QName.NT_BASE,
+                QName.MIX_REFERENCEABLE
+            }));
+
     /**
      * Flag indicating whether this path is absolute.
      */
     private boolean absolute = false;
 
     /**
+     * List of valid node type names under /jcr:system
+     */
+    private final List validJcrSystemNodeTypeNames;
+
+    /**
      * Empty step node array.
      */
     private static final LocationStepQueryNode[] EMPTY = new LocationStepQueryNode[0];
 
     /**
-     * Creates a relative <code>PathQueryNode</code> with no location steps.
+     * Creates a relative <code>PathQueryNode</code> with no location steps and
+     * the default list of node types under /jcr:system.
      *
      * @param parent the parent query node.
      */
     public PathQueryNode(QueryNode parent) {
+        this(parent, VALID_SYSTEM_INDEX_NODE_TYPE_NAMES);
+    }
+
+    /**
+     * Creates a relative <code>PathQueryNode</code> with no location steps and
+     * the default list of node types under /jcr:system.
+     *
+     * @param parent the parent query node.
+     */
+    public PathQueryNode(QueryNode parent, List validJcrSystemNodeTypeNames) {
         super(parent);
+        this.validJcrSystemNodeTypeNames = validJcrSystemNodeTypeNames;
     }
 
     /**
@@ -125,10 +164,23 @@
         }
 
         QName firstPathStepName = pathSteps[0].getNameTest();
-        // If the first location step has a null name test we need to include
-        // the system tree ("*")
-        if (firstPathStepName == null)
+        if (firstPathStepName == null) {
+            // If the first operand of the path steps is a node type query
+            // we do not need to include the system index if the node type is
+            // none of the node types that may occur in the system index.
+            QueryNode[] pathStepOperands = pathSteps[0].getOperands();
+            if (pathStepOperands.length > 0) {
+                if (pathStepOperands[0] instanceof NodeTypeQueryNode) {
+                    NodeTypeQueryNode nodeTypeQueryNode = (NodeTypeQueryNode) pathStepOperands[0];
+                    if (!validJcrSystemNodeTypeNames.contains(nodeTypeQueryNode.getValue())) {
+                        return false;
+                    }
+                }
+            }
+            // If the first location step has a null name test we need to include
+            // the system tree ("*")
             return true;
+        }
         
         // Calculate the first workspace relative location step
         LocationStepQueryNode firstWorkspaceRelativeStep = pathSteps[0];
Index: src/main/java/org/apache/jackrabbit/core/query/QueryNodeFactory.java
===================================================================
--- src/main/java/org/apache/jackrabbit/core/query/QueryNodeFactory.java	(revision 0)
+++ src/main/java/org/apache/jackrabbit/core/query/QueryNodeFactory.java	(revision 0)
@@ -0,0 +1,137 @@
+/*
+ * 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.core.query;
+
+import org.apache.jackrabbit.name.QName;
+
+/**
+ * A factory for {@link QueryNode}s.
+ */
+public interface QueryNodeFactory {
+
+    /**
+     * Creates a {@link NodeTypeQueryNode} instance.
+     *
+     * @param parent the parent node.
+     * @param nodeType the name of the node type.
+     * @return a {@link NodeTypeQueryNode}.
+     */
+    public NodeTypeQueryNode createNodeTypeQueryNode(
+            QueryNode parent, QName nodeType);
+
+    /**
+     * Creates a {@link AndQueryNode} instance.
+     *
+     * @param parent the parent node.
+     * @return a {@link AndQueryNode}.
+     */
+    public AndQueryNode createAndQueryNode(
+            QueryNode parent);
+
+    /**
+     * Creates a {@link LocationStepQueryNode} instance.
+     *
+     * @param parent the parent node.
+     * @return a {@link LocationStepQueryNode}.
+     */
+    public LocationStepQueryNode createLocationStepQueryNode(
+            QueryNode parent);
+
+    /**
+     * Creates a {@link DerefQueryNode} instance.
+     *
+     * @param parent the parent node.
+     * @param nameTest the name test on the referenced target node.
+     * @param descendants if the axis is //
+     * @return a {@link DerefQueryNode}.
+     */
+    public DerefQueryNode createDerefQueryNode(
+            QueryNode parent, QName nameTest, boolean descendants);
+
+    /**
+     * Creates a {@link NotQueryNode} instance.
+     *
+     * @param parent the parent node.
+     * @return a {@link NotQueryNode}.
+     */
+    public NotQueryNode createNotQueryNode(
+            QueryNode parent);
+
+    /**
+     * Creates a {@link OrQueryNode} instance.
+     *
+     * @param parent the parent node.
+     * @return a {@link OrQueryNode}.
+     */
+    public OrQueryNode createOrQueryNode(
+            QueryNode parent);
+
+    /**
+     * Creates a {@link RelationQueryNode} instance.
+     *
+     * @param parent the parent node.
+     * @param operation the operation type.
+     * @return a {@link RelationQueryNode}.
+     */
+    public RelationQueryNode createRelationQueryNode(
+            QueryNode parent, int operation);
+
+    /**
+     * Creates a {@link PathQueryNode} instance.
+     *
+     * @param parent the parent node.
+     * @return a {@link PathQueryNode}.
+     */
+    public PathQueryNode createPathQueryNode(
+            QueryNode parent);
+
+    /**
+     * Creates a {@link OrderQueryNode} instance.
+     *
+     * @param parent the parent node.
+     * @return a {@link OrderQueryNode}.
+     */
+    public OrderQueryNode createOrderQueryNode(
+            QueryNode parent);
+
+    /**
+     * Creates a {@link PropertyFunctionQueryNode} instance.
+     *
+     * @param parent the parent node.
+     * @param functionName the name of the function.
+     * @return a {@link PropertyFunctionQueryNode}.
+     */
+    public PropertyFunctionQueryNode createPropertyFunctionQueryNode(
+            QueryNode parent, String functionName);
+
+    /**
+     * Creates a {@link QueryRootNode} instance.
+     *
+     * @return a {@link QueryRootNode}.
+     */
+    public QueryRootNode createQueryRootNode();
+
+    /**
+     * Creates a {@link TextsearchQueryNode} instance.
+     *
+     * @param parent the parent node.
+     * @param query the textsearch statement.
+     * @return a {@link TextsearchQueryNode}.
+     */
+    public TextsearchQueryNode createTextsearchQueryNode(
+            QueryNode parent, String query);
+}

Property changes on: src\main\java\org\apache\jackrabbit\core\query\QueryNodeFactory.java
___________________________________________________________________
Name: svn:eol-style
   + native

Index: src/main/java/org/apache/jackrabbit/core/query/QueryParser.java
===================================================================
--- src/main/java/org/apache/jackrabbit/core/query/QueryParser.java	(revision 568580)
+++ src/main/java/org/apache/jackrabbit/core/query/QueryParser.java	(working copy)
@@ -55,6 +55,30 @@
     }
 
     /**
+     * Parses a query <code>statement</code> according to a query
+     * <code>language</code> into a query tree.
+     * <p/>
+     * <code>language</code> must be one of: {@link javax.jcr.query.Query#SQL},
+     * {@link javax.jcr.query.Query#XPATH}.
+     *
+     * @param statement the query statement.
+     * @param language  the language of the query statement.
+     * @param factory   the query node factory.
+     * @return the root node of the generated query tree.
+     * @throws InvalidQueryException if an error occurs while parsing the
+     *                               statement.
+     */
+    public static QueryRootNode parse(String statement,
+                                      String language,
+                                      NamespaceResolver resolver,
+                                      QueryNodeFactory factory)
+            throws InvalidQueryException {
+
+        QueryTreeBuilder builder = QueryTreeBuilderRegistry.getQueryTreeBuilder(language);
+        return builder.createQueryTree(statement, resolver, factory);
+    }
+
+    /**
      * Creates a String representation of the QueryNode tree argument
      * <code>root</code>. The argument <code>language</code> specifies the
      * syntax.
Index: src/main/java/org/apache/jackrabbit/core/query/QueryTreeBuilder.java
===================================================================
--- src/main/java/org/apache/jackrabbit/core/query/QueryTreeBuilder.java	(revision 568580)
+++ src/main/java/org/apache/jackrabbit/core/query/QueryTreeBuilder.java	(working copy)
@@ -38,6 +38,22 @@
             throws InvalidQueryException;
 
     /**
+     * Creates a <code>QueryNode</code> tree from a statement using the passed
+     * query node factory.
+     *
+     * @param statement the statement.
+     * @param resolver  the namespace resolver to use.
+     * @param factory   the query node factory to use.
+     * @return the <code>QueryNode</code> tree for the statement.
+     * @throws javax.jcr.query.InvalidQueryException
+     *          if the statement is malformed.
+     */
+    QueryRootNode createQueryTree(String statement,
+                                  NamespaceResolver resolver,
+                                  QueryNodeFactory factory)
+            throws InvalidQueryException;
+
+    /**
      * Returns <code>true</code> if this query tree builder can handle a
      * statement in <code>language</code>.
      *
Index: src/main/java/org/apache/jackrabbit/core/query/sql/JCRSQLQueryBuilder.java
===================================================================
--- src/main/java/org/apache/jackrabbit/core/query/sql/JCRSQLQueryBuilder.java	(revision 568580)
+++ src/main/java/org/apache/jackrabbit/core/query/sql/JCRSQLQueryBuilder.java	(working copy)
@@ -30,6 +30,8 @@
 import org.apache.jackrabbit.core.query.RelationQueryNode;
 import org.apache.jackrabbit.core.query.TextsearchQueryNode;
 import org.apache.jackrabbit.core.query.PropertyFunctionQueryNode;
+import org.apache.jackrabbit.core.query.QueryNodeFactory;
+import org.apache.jackrabbit.core.query.DefaultQueryNodeFactory;
 import org.apache.jackrabbit.name.NameException;
 import org.apache.jackrabbit.name.NamespaceResolver;
 import org.apache.jackrabbit.name.QName;
@@ -74,6 +76,10 @@
      */
     private static Map parsers = new ReferenceMap(ReferenceMap.WEAK, ReferenceMap.WEAK);
 
+    /**
+     * The default query node factory.
+     */
+    private static final QueryNodeFactory DEFAULT_QUERY_NODE_FACTORY = new DefaultQueryNodeFactory();
 
     /**
      * The root node of the sql query syntax tree
@@ -93,7 +99,7 @@
     /**
      * Query node to gather the constraints defined in the WHERE clause
      */
-    private final AndQueryNode constraintNode = new AndQueryNode(null);
+    private final AndQueryNode constraintNode;
 
     /**
      * The QName of the node type in the from clause.
@@ -106,19 +112,30 @@
     private final List pathConstraints = new ArrayList();
 
     /**
+     * The query node factory.
+     */
+    private final QueryNodeFactory factory;
+
+    /**
      * Creates a new <code>JCRSQLQueryBuilder</code>.
      *
      * @param statement the root node of the SQL syntax tree.
      * @param resolver  a namespace resolver to use for names in the
      *                  <code>statement</code>.
+     * @param factory   the query node factory.
      */
-    private JCRSQLQueryBuilder(ASTQuery statement, NamespaceResolver resolver) {
+    private JCRSQLQueryBuilder(ASTQuery statement,
+                               NamespaceResolver resolver,
+                               QueryNodeFactory factory) {
         this.stmt = statement;
         this.resolver = resolver;
+        this.factory = factory;
+        this.constraintNode =  factory.createAndQueryNode(null);
     }
 
     /**
-     * Creates a <code>QueryNode</code> tree from a SQL <code>statement</code>.
+     * Creates a <code>QueryNode</code> tree from a SQL <code>statement</code>
+     * using the {@link DefaultQueryNodeFactory}.
      *
      * @param statement the SQL statement.
      * @param resolver  the namespace resolver to use.
@@ -127,6 +144,22 @@
      */
     public static QueryRootNode createQuery(String statement, NamespaceResolver resolver)
             throws InvalidQueryException {
+        return createQuery(statement, resolver, DEFAULT_QUERY_NODE_FACTORY);
+    }
+        
+    /**
+     * Creates a <code>QueryNode</code> tree from a SQL <code>statement</code>
+     * using the passed query node <code>factory</code>.
+     *
+     * @param statement the SQL statement.
+     * @param resolver  the namespace resolver to use.
+     * @return the <code>QueryNode</code> tree.
+     * @throws InvalidQueryException if <code>statement</code> is malformed.
+     */
+    public static QueryRootNode createQuery(String statement,
+                                            NamespaceResolver resolver,
+                                            QueryNodeFactory factory)
+            throws InvalidQueryException {
         try {
             // get parser
             JCRSQLParser parser;
@@ -143,7 +176,7 @@
             // guard against concurrent use within same session
             synchronized (parser) {
                 parser.ReInit(new StringReader(statement));
-                builder = new JCRSQLQueryBuilder(parser.Query(), resolver);
+                builder = new JCRSQLQueryBuilder(parser.Query(), resolver, factory);
             }
             return builder.getRootNode();
         } catch (ParseException e) {
@@ -191,8 +224,8 @@
     }
 
     public Object visit(ASTQuery node, Object data) {
-        root = new QueryRootNode();
-        root.setLocationNode(new PathQueryNode(root));
+        root = factory.createQueryRootNode();
+        root.setLocationNode(factory.createPathQueryNode(root));
 
         // pass to select, from, where, ...
         node.childrenAccept(this, root);
@@ -201,7 +234,10 @@
         PathQueryNode pathNode = root.getLocationNode();
         pathNode.setAbsolute(true);
         if (pathConstraints.size() == 0) {
-            pathNode.addPathStep(new LocationStepQueryNode(pathNode, null, true));
+            LocationStepQueryNode step = factory.createLocationStepQueryNode(pathNode);
+            step.setNameTest(null);
+            step.setIncludeDescendants(true);
+            pathNode.addPathStep(step);
         } else {
             try {
                 while (pathConstraints.size() > 1) {
@@ -231,7 +267,9 @@
             MergingPathQueryNode path = (MergingPathQueryNode) pathConstraints.get(0);
             LocationStepQueryNode[] steps = path.getPathSteps();
             for (int i = 0; i < steps.length; i++) {
-                LocationStepQueryNode step = new LocationStepQueryNode(pathNode, steps[i].getNameTest(), steps[i].getIncludeDescendants());
+                LocationStepQueryNode step = factory.createLocationStepQueryNode(pathNode);
+                step.setNameTest(steps[i].getNameTest());
+                step.setIncludeDescendants(steps[i].getIncludeDescendants());
                 step.setIndex(steps[i].getIndex());
                 pathNode.addPathStep(step);
             }
@@ -247,7 +285,7 @@
             // add node type constraint
             LocationStepQueryNode[] steps = pathNode.getPathSteps();
             NodeTypeQueryNode nodeType
-                    = new NodeTypeQueryNode(steps[steps.length - 1], nodeTypeName);
+                    = factory.createNodeTypeQueryNode(steps[steps.length - 1], nodeTypeName);
             steps[steps.length - 1].addPredicate(nodeType);
         }
 
@@ -351,7 +389,7 @@
             }
 
             if (type == QueryConstants.OPERATION_BETWEEN) {
-                AndQueryNode between = new AndQueryNode(parent);
+                AndQueryNode between = factory.createAndQueryNode(parent);
                 RelationQueryNode rel = createRelationQueryNode(between,
                         identifier, QueryConstants.OPERATION_GE_GENERAL, (ASTLiteral) node.children[1]);
                 node.childrenAccept(this, rel);
@@ -389,7 +427,7 @@
                         identifier, type, pattern);
                 node.childrenAccept(this, predicateNode);
             } else if (type == QueryConstants.OPERATION_IN) {
-                OrQueryNode in = new OrQueryNode(parent);
+                OrQueryNode in = factory.createOrQueryNode(parent);
                 for (int i = 1; i < node.children.length; i++) {
                     RelationQueryNode rel = createRelationQueryNode(in,
                             identifier, QueryConstants.OPERATION_EQ_VALUE, (ASTLiteral) node.children[i]);
@@ -424,7 +462,7 @@
 
     public Object visit(ASTOrExpression node, Object data) {
         NAryQueryNode parent = (NAryQueryNode) data;
-        OrQueryNode orQuery = new OrQueryNode(parent);
+        OrQueryNode orQuery = factory.createOrQueryNode(parent);
         // pass to operands
         node.childrenAccept(this, orQuery);
 
@@ -436,7 +474,7 @@
 
     public Object visit(ASTAndExpression node, Object data) {
         NAryQueryNode parent = (NAryQueryNode) data;
-        AndQueryNode andQuery = new AndQueryNode(parent);
+        AndQueryNode andQuery = factory.createAndQueryNode(parent);
         // pass to operands
         node.childrenAccept(this, andQuery);
 
@@ -446,7 +484,7 @@
 
     public Object visit(ASTNotExpression node, Object data) {
         NAryQueryNode parent = (NAryQueryNode) data;
-        NotQueryNode notQuery = new NotQueryNode(parent);
+        NotQueryNode notQuery = factory.createNotQueryNode(parent);
         // pass to operand
         node.childrenAccept(this, notQuery);
 
@@ -474,7 +512,7 @@
     public Object visit(ASTOrderByClause node, Object data) {
         QueryRootNode root = (QueryRootNode) data;
 
-        OrderQueryNode order = new OrderQueryNode(root);
+        OrderQueryNode order = factory.createOrderQueryNode(root);
         root.setOrderNode(order);
         node.childrenAccept(this, order);
         return root;
@@ -521,7 +559,10 @@
                 builder.addLast(node.getPropertyName());
                 relPath = builder.getPath();
             }
-            parent.addOperand(new TextsearchQueryNode(parent, node.getQuery(), relPath, true));
+            TextsearchQueryNode tsNode = factory.createTextsearchQueryNode(parent, node.getQuery());
+            tsNode.setRelativePath(relPath);
+            tsNode.setReferencesProperty(true);
+            parent.addOperand(tsNode);
         } catch (MalformedPathException e) {
             // path is always valid
         }
@@ -534,7 +575,7 @@
             String msg = "LOWER() function is only supported for String literal";
             throw new IllegalArgumentException(msg);
         }
-        parent.addOperand(new PropertyFunctionQueryNode(parent, PropertyFunctionQueryNode.LOWER_CASE));
+        parent.addOperand(factory.createPropertyFunctionQueryNode(parent, PropertyFunctionQueryNode.LOWER_CASE));
         return parent;
     }
 
@@ -544,7 +585,7 @@
             String msg = "UPPER() function is only supported for String literal";
             throw new IllegalArgumentException(msg);
         }
-        parent.addOperand(new PropertyFunctionQueryNode(parent, PropertyFunctionQueryNode.UPPER_CASE));
+        parent.addOperand(factory.createPropertyFunctionQueryNode(parent, PropertyFunctionQueryNode.UPPER_CASE));
         return parent;
     }
 
@@ -585,18 +626,28 @@
             if (literal.getType() == QueryConstants.TYPE_DATE) {
                 SimpleDateFormat format = new SimpleDateFormat(DATE_PATTERN);
                 Date date = format.parse(stringValue);
-                node = new RelationQueryNode(parent, relPath, date, operationType);
+                node = factory.createRelationQueryNode(parent, operationType);
+                node.setRelativePath(relPath);
+                node.setDateValue(date);
             } else if (literal.getType() == QueryConstants.TYPE_DOUBLE) {
                 double d = Double.parseDouble(stringValue);
-                node = new RelationQueryNode(parent, relPath, d, operationType);
+                node = factory.createRelationQueryNode(parent, operationType);
+                node.setRelativePath(relPath);
+                node.setDoubleValue(d);
             } else if (literal.getType() == QueryConstants.TYPE_LONG) {
                 long l = Long.parseLong(stringValue);
-                node = new RelationQueryNode(parent, relPath, l, operationType);
+                node = factory.createRelationQueryNode(parent, operationType);
+                node.setRelativePath(relPath);
+                node.setLongValue(l);
             } else if (literal.getType() == QueryConstants.TYPE_STRING) {
-                node = new RelationQueryNode(parent, relPath, stringValue, operationType);
+                node = factory.createRelationQueryNode(parent, operationType);
+                node.setRelativePath(relPath);
+                node.setStringValue(stringValue);
             } else if (literal.getType() == QueryConstants.TYPE_TIMESTAMP) {
                 Calendar c = ISO8601.parse(stringValue);
-                node = new RelationQueryNode(parent, relPath, c.getTime(), operationType);
+                node = factory.createRelationQueryNode(parent, operationType);
+                node.setRelativePath(relPath);
+                node.setDateValue(c.getTime());
             }
         } catch (java.text.ParseException e) {
             throw new IllegalArgumentException(e.toString());
@@ -624,7 +675,7 @@
         pathNode.setAbsolute(true);
 
         if (path.equals("/")) {
-            pathNode.addPathStep(new LocationStepQueryNode(pathNode));
+            pathNode.addPathStep(factory.createLocationStepQueryNode(pathNode));
             pathConstraints.add(pathNode);
             return;
         }
@@ -635,13 +686,13 @@
             if (names[i].length() == 0) {
                 if (i == 0) {
                     // root
-                    pathNode.addPathStep(new LocationStepQueryNode(pathNode));
+                    pathNode.addPathStep(factory.createLocationStepQueryNode(pathNode));
                 } else {
                     // descendant '//' -> invalid path
                     // todo throw or ignore?
                     // we currently do not throw and add location step for an
                     // empty name (which is basically the root node)
-                    pathNode.addPathStep(new LocationStepQueryNode(pathNode));
+                    pathNode.addPathStep(factory.createLocationStepQueryNode(pathNode));
                 }
             } else {
                 int idx = names[i].indexOf('[');
@@ -686,7 +737,9 @@
                 }
                 // if name test is % this means also search descendants
                 boolean descendant = name == null;
-                LocationStepQueryNode step = new LocationStepQueryNode(pathNode, qName, descendant);
+                LocationStepQueryNode step = factory.createLocationStepQueryNode(pathNode);
+                step.setNameTest(qName);
+                step.setIncludeDescendants(descendant);
                 if (index > 0) {
                     step.setIndex(index);
                 }
Index: src/main/java/org/apache/jackrabbit/core/query/sql/QueryBuilder.java
===================================================================
--- src/main/java/org/apache/jackrabbit/core/query/sql/QueryBuilder.java	(revision 568580)
+++ src/main/java/org/apache/jackrabbit/core/query/sql/QueryBuilder.java	(working copy)
@@ -18,6 +18,7 @@
 
 import org.apache.jackrabbit.core.query.QueryTreeBuilder;
 import org.apache.jackrabbit.core.query.QueryRootNode;
+import org.apache.jackrabbit.core.query.QueryNodeFactory;
 import org.apache.jackrabbit.name.NamespaceResolver;
 
 import javax.jcr.query.InvalidQueryException;
@@ -40,6 +41,16 @@
     /**
      * @inheritDoc
      */
+    public QueryRootNode createQueryTree(String statement,
+                                         NamespaceResolver resolver,
+                                         QueryNodeFactory factory)
+            throws InvalidQueryException {
+        return JCRSQLQueryBuilder.createQuery(statement, resolver, factory);
+    }
+
+    /**
+     * @inheritDoc
+     */
     public boolean canHandle(String language) {
         return Query.SQL.equals(language);
     }
Index: src/main/java/org/apache/jackrabbit/core/query/xpath/QueryBuilder.java
===================================================================
--- src/main/java/org/apache/jackrabbit/core/query/xpath/QueryBuilder.java	(revision 568580)
+++ src/main/java/org/apache/jackrabbit/core/query/xpath/QueryBuilder.java	(working copy)
@@ -18,6 +18,7 @@
 
 import org.apache.jackrabbit.core.query.QueryTreeBuilder;
 import org.apache.jackrabbit.core.query.QueryRootNode;
+import org.apache.jackrabbit.core.query.QueryNodeFactory;
 import org.apache.jackrabbit.name.NamespaceResolver;
 
 import javax.jcr.query.InvalidQueryException;
@@ -40,6 +41,16 @@
     /**
      * @inheritDoc
      */
+    public QueryRootNode createQueryTree(String statement,
+                                         NamespaceResolver resolver,
+                                         QueryNodeFactory factory)
+            throws InvalidQueryException {
+        return XPathQueryBuilder.createQuery(statement, resolver, factory);
+    }
+
+    /**
+     * @inheritDoc
+     */
     public boolean canHandle(String language) {
         return Query.XPATH.equals(language);
     }
Index: src/main/java/org/apache/jackrabbit/core/query/xpath/XPathQueryBuilder.java
===================================================================
--- src/main/java/org/apache/jackrabbit/core/query/xpath/XPathQueryBuilder.java	(revision 568580)
+++ src/main/java/org/apache/jackrabbit/core/query/xpath/XPathQueryBuilder.java	(working copy)
@@ -16,13 +16,11 @@
  */
 package org.apache.jackrabbit.core.query.xpath;
 
-import org.apache.jackrabbit.core.query.AndQueryNode;
 import org.apache.jackrabbit.core.query.DerefQueryNode;
 import org.apache.jackrabbit.core.query.LocationStepQueryNode;
 import org.apache.jackrabbit.core.query.NAryQueryNode;
 import org.apache.jackrabbit.core.query.NodeTypeQueryNode;
 import org.apache.jackrabbit.core.query.NotQueryNode;
-import org.apache.jackrabbit.core.query.OrQueryNode;
 import org.apache.jackrabbit.core.query.OrderQueryNode;
 import org.apache.jackrabbit.core.query.PathQueryNode;
 import org.apache.jackrabbit.core.query.QueryConstants;
@@ -32,6 +30,8 @@
 import org.apache.jackrabbit.core.query.TextsearchQueryNode;
 import org.apache.jackrabbit.core.query.PropertyFunctionQueryNode;
 import org.apache.jackrabbit.core.query.DefaultQueryNodeVisitor;
+import org.apache.jackrabbit.core.query.QueryNodeFactory;
+import org.apache.jackrabbit.core.query.DefaultQueryNodeFactory;
 import org.apache.jackrabbit.name.NameException;
 import org.apache.jackrabbit.name.NamespaceResolver;
 import org.apache.jackrabbit.name.NoPrefixDeclaredException;
@@ -221,9 +221,14 @@
     private static final Map parsers = new ReferenceMap(ReferenceMap.WEAK, ReferenceMap.WEAK);
 
     /**
+     * The default query node factory.
+     */
+    private static final QueryNodeFactory DEFAULT_QUERY_NODE_FACTORY = new DefaultQueryNodeFactory();
+
+    /**
      * The root <code>QueryNode</code>
      */
-    private final QueryRootNode root = new QueryRootNode();
+    private final QueryRootNode root;
 
     /**
      * The {@link NamespaceResolver} in use
@@ -241,15 +246,25 @@
     private Path.PathBuilder tmpRelPath;
 
     /**
+     * The query node factory.
+     */
+    private final QueryNodeFactory factory;
+
+    /**
      * Creates a new <code>XPathQueryBuilder</code> instance.
      *
      * @param statement the XPath statement.
      * @param resolver  the namespace resolver to use.
+     * @param factory   the query node factory.
      * @throws InvalidQueryException if the XPath statement is malformed.
      */
-    private XPathQueryBuilder(String statement, NamespaceResolver resolver)
+    private XPathQueryBuilder(String statement,
+                              NamespaceResolver resolver,
+                              QueryNodeFactory factory)
             throws InvalidQueryException {
         this.resolver = resolver;
+        this.factory = factory;
+        this.root = factory.createQueryRootNode();
         try {
             // create an XQuery statement because we're actually using an
             // XQuery parser.
@@ -291,7 +306,8 @@
     }
 
     /**
-     * Creates a <code>QueryNode</code> tree from a XPath statement.
+     * Creates a <code>QueryNode</code> tree from a XPath statement using the
+     * default query node factory.
      *
      * @param statement the XPath statement.
      * @param resolver  the namespace resolver to use.
@@ -301,10 +317,27 @@
     public static QueryRootNode createQuery(String statement,
                                             NamespaceResolver resolver)
             throws InvalidQueryException {
-        return new XPathQueryBuilder(statement, resolver).getRootNode();
+        return createQuery(statement, resolver, DEFAULT_QUERY_NODE_FACTORY);
     }
 
     /**
+     * Creates a <code>QueryNode</code> tree from a XPath statement using the
+     * passed query node <code>factory</code>.
+     *
+     * @param statement the XPath statement.
+     * @param resolver  the namespace resolver to use.
+     * @param factory   the query node factory.
+     * @return the <code>QueryNode</code> tree for the XPath statement.
+     * @throws InvalidQueryException if the XPath statement is malformed.
+     */
+    public static QueryRootNode createQuery(String statement,
+                                            NamespaceResolver resolver,
+                                            QueryNodeFactory factory)
+            throws InvalidQueryException {
+        return new XPathQueryBuilder(statement, resolver, factory).getRootNode();
+    }
+
+    /**
      * Creates a String representation of the query node tree in XPath syntax.
      *
      * @param root     the root of the query node tree.
@@ -365,7 +398,7 @@
                     } else if (queryNode.getType() == QueryNode.TYPE_NOT) {
                         // is null expression
                         RelationQueryNode isNull
-                                = new RelationQueryNode(queryNode,
+                                = factory.createRelationQueryNode(queryNode,
                                         RelationQueryNode.OPERATION_NULL);
                         applyRelativePath(isNull);
                         node.childrenAccept(this, isNull);
@@ -376,7 +409,7 @@
                     } else {
                         // not null expression
                         RelationQueryNode notNull =
-                                new RelationQueryNode(queryNode,
+                                factory.createRelationQueryNode(queryNode,
                                         RelationQueryNode.OPERATION_NOT_NULL);
                         applyRelativePath(notNull);
                         node.childrenAccept(this, notNull);
@@ -390,7 +423,7 @@
                         node.childrenAccept(this, queryNode);
                     } else {
                         // step within a predicate
-                        RelationQueryNode tmp = new RelationQueryNode(
+                        RelationQueryNode tmp = factory.createRelationQueryNode(
                                 null, RelationQueryNode.OPERATION_NOT_NULL);
                         node.childrenAccept(this, tmp);
                         if (tmpRelPath == null) {
@@ -434,7 +467,7 @@
                     String ntName = ((SimpleNode) node.jjtGetChild(0)).getValue();
                     try {
                         QName nt = NameFormat.parse(ntName, resolver);
-                        NodeTypeQueryNode nodeType = new NodeTypeQueryNode(loc, nt);
+                        NodeTypeQueryNode nodeType = factory.createNodeTypeQueryNode(loc, nt);
                         loc.addPredicate(nodeType);
                     } catch (NameException e) {
                         exceptions.add(new InvalidQueryException("Not a valid name: " + ntName));
@@ -443,14 +476,14 @@
                 break;
             case JJTOREXPR:
                 NAryQueryNode parent = (NAryQueryNode) queryNode;
-                QueryNode orQueryNode = new OrQueryNode(parent);
+                QueryNode orQueryNode = factory.createOrQueryNode(parent);
                 parent.addOperand(orQueryNode);
                 // traverse
                 node.childrenAccept(this, orQueryNode);
                 break;
             case JJTANDEXPR:
                 parent = (NAryQueryNode) queryNode;
-                QueryNode andQueryNode = new AndQueryNode(parent);
+                QueryNode andQueryNode = factory.createAndQueryNode(parent);
                 parent.addOperand(andQueryNode);
                 // traverse
                 node.childrenAccept(this, andQueryNode);
@@ -486,7 +519,7 @@
                 queryNode = createFunction(node, queryNode);
                 break;
             case JJTORDERBYCLAUSE:
-                root.setOrderNode(new OrderQueryNode(root));
+                root.setOrderNode(factory.createOrderQueryNode(root));
                 queryNode = root.getOrderNode();
                 node.childrenAccept(this, queryNode);
                 break;
@@ -561,7 +594,9 @@
         for (int i = 0; i < p.jjtGetNumChildren(); i++) {
             SimpleNode c = (SimpleNode) p.jjtGetChild(i);
             if (c == node) {
-                queryNode = new LocationStepQueryNode(parent, null, descendant);
+                queryNode = factory.createLocationStepQueryNode(parent);
+                queryNode.setNameTest(null);
+                queryNode.setIncludeDescendants(descendant);
                 parent.addOperand(queryNode);
                 break;
             }
@@ -670,7 +705,7 @@
             exceptions.add(new InvalidQueryException("Unsupported ComparisonExpr type:" + node.getValue()));
         }
 
-        final RelationQueryNode rqn = new RelationQueryNode(queryNode, type);
+        final RelationQueryNode rqn = factory.createRelationQueryNode(queryNode, type);
 
         // traverse
         node.childrenAccept(this, rqn);
@@ -699,7 +734,7 @@
      * @return the path qurey node
      */
     private PathQueryNode createPathQueryNode(SimpleNode node) {
-        root.setLocationNode(new PathQueryNode(root));
+        root.setLocationNode(factory.createPathQueryNode(root));
         node.childrenAccept(this, root.getLocationNode());
         return root.getLocationNode();
     }
@@ -746,7 +781,7 @@
             if (NameFormat.format(FN_NOT, resolver).equals(fName)
                     || NameFormat.format(FN_NOT_10, resolver).equals(fName)) {
                 if (queryNode instanceof NAryQueryNode) {
-                    QueryNode not = new NotQueryNode(queryNode);
+                    QueryNode not = factory.createNotQueryNode(queryNode);
                     ((NAryQueryNode) queryNode).addOperand(not);
                     // @todo is this needed?
                     queryNode = not;
@@ -791,7 +826,7 @@
                     if (queryNode instanceof NAryQueryNode) {
                         SimpleNode literal = (SimpleNode) node.jjtGetChild(2).jjtGetChild(0);
                         if (literal.getId() == JJTSTRINGLITERAL) {
-                            TextsearchQueryNode contains = new TextsearchQueryNode(
+                            TextsearchQueryNode contains = factory.createTextsearchQueryNode(
                                     queryNode, unescapeQuotes(literal.getValue()));
                             // assign property name
                             SimpleNode path = (SimpleNode) node.jjtGetChild(1);
@@ -809,7 +844,8 @@
                 // check number of arguments
                 if (node.jjtGetNumChildren() == 3) {
                     if (queryNode instanceof NAryQueryNode) {
-                        RelationQueryNode like = new RelationQueryNode(queryNode, RelationQueryNode.OPERATION_LIKE);
+                        RelationQueryNode like = factory.createRelationQueryNode(
+                                queryNode, RelationQueryNode.OPERATION_LIKE);
                         ((NAryQueryNode) queryNode).addOperand(like);
 
                         // assign property name
@@ -889,7 +925,7 @@
                     }
                     if (queryNode.getType() == QueryNode.TYPE_PATH) {
                         PathQueryNode pathNode = (PathQueryNode) queryNode;
-                        DerefQueryNode derefNode = new DerefQueryNode(pathNode, null, false);
+                        DerefQueryNode derefNode = factory.createDerefQueryNode(pathNode, null, false);
 
                         // assign property name
                         node.jjtGetChild(1).jjtAccept(this, derefNode);
@@ -944,7 +980,8 @@
                 if (node.jjtGetNumChildren() == 2) {
                     if (queryNode.getType() == QueryNode.TYPE_RELATION) {
                         RelationQueryNode relNode = (RelationQueryNode) queryNode;
-                        relNode.addOperand(new PropertyFunctionQueryNode(relNode, PropertyFunctionQueryNode.LOWER_CASE));
+                        relNode.addOperand(factory.createPropertyFunctionQueryNode(
+                                relNode, PropertyFunctionQueryNode.LOWER_CASE));
                         // get property name
                         node.jjtGetChild(1).jjtAccept(this, relNode);
                     } else {
@@ -957,7 +994,8 @@
                 if (node.jjtGetNumChildren() == 2) {
                     if (queryNode.getType() == QueryNode.TYPE_RELATION) {
                         RelationQueryNode relNode = (RelationQueryNode) queryNode;
-                        relNode.addOperand(new PropertyFunctionQueryNode(relNode, PropertyFunctionQueryNode.UPPER_CASE));
+                        relNode.addOperand(factory.createPropertyFunctionQueryNode(
+                                relNode, PropertyFunctionQueryNode.UPPER_CASE));
                         // get property name
                         node.jjtGetChild(1).jjtAccept(this, relNode);
                     } else {
@@ -970,7 +1008,7 @@
                 if (node.jjtGetNumChildren() == 3) {
                     if (queryNode instanceof NAryQueryNode) {
                         NAryQueryNode parent = (NAryQueryNode) queryNode;
-                        RelationQueryNode rel = new RelationQueryNode(
+                        RelationQueryNode rel = factory.createRelationQueryNode(
                                 parent, RelationQueryNode.OPERATION_SIMILAR);
                         parent.addOperand(rel);
                         // assign path
Index: src/test/java/org/apache/jackrabbit/core/query/PathQueryNodeTest.java
===================================================================
--- src/test/java/org/apache/jackrabbit/core/query/PathQueryNodeTest.java	(revision 568580)
+++ src/test/java/org/apache/jackrabbit/core/query/PathQueryNodeTest.java	(working copy)
@@ -26,21 +26,23 @@
 
     private static final NamespaceResolver JCR_RESOLVER = new NamespaceResolver() {
         public String getJCRName(QName qName) {
-            return this.getPrefix(qName.getNamespaceURI()) + ":"
-                    + qName.getLocalName();
+            throw new UnsupportedOperationException();
         }
 
         public String getPrefix(String uri) {
-            return QName.NS_JCR_PREFIX;
+            throw new UnsupportedOperationException();
         }
 
         public QName getQName(String jcrName) {
-            return new QName(QName.NS_JCR_URI,
-                    jcrName.substring(jcrName.indexOf(':')));
+            throw new UnsupportedOperationException();
         }
 
         public String getURI(String prefix) {
-            return QName.NS_JCR_URI;
+            if (QName.NS_JCR_PREFIX.equals(prefix))
+                return QName.NS_JCR_URI;
+            if (QName.NS_NT_PREFIX.equals(prefix))
+                return QName.NS_NT_URI;
+            return "";
         }
     };    
     
@@ -64,4 +66,15 @@
         assertTrue(queryRootNode.needsSystemTree());         
     }
     
+    public void testNeedsSystemTreeForAllNodesByNodeType() throws Exception {
+        QueryRootNode queryRootNode = XPathQueryBuilder.createQuery("//element(*, nt:resource)", JCR_RESOLVER);
+        assertFalse(queryRootNode.needsSystemTree());
+
+        queryRootNode = XPathQueryBuilder.createQuery("//element(*, nt:resource)[@jcr:test = 'foo']", JCR_RESOLVER);
+        assertFalse(queryRootNode.needsSystemTree());
+
+        queryRootNode = XPathQueryBuilder.createQuery("//element(*, nt:nodeType)", JCR_RESOLVER);
+        assertTrue(queryRootNode.needsSystemTree());
+    }
+
 }
