View Javadoc

1   /*
2    * ModeShape (http://www.modeshape.org)
3    * See the COPYRIGHT.txt file distributed with this work for information
4    * regarding copyright ownership.  Some portions may be licensed
5    * to Red Hat, Inc. under one or more contributor license agreements.
6    * See the AUTHORS.txt file in the distribution for a full listing of 
7    * individual contributors.
8    *
9    * ModeShape is free software. Unless otherwise indicated, all code in ModeShape
10   * is licensed to you under the terms of the GNU Lesser General Public License as
11   * published by the Free Software Foundation; either version 2.1 of
12   * the License, or (at your option) any later version.
13   * 
14   * ModeShape is distributed in the hope that it will be useful,
15   * but WITHOUT ANY WARRANTY; without even the implied warranty of
16   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17   * Lesser General Public License for more details.
18   *
19   * You should have received a copy of the GNU Lesser General Public
20   * License along with this software; if not, write to the Free
21   * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
22   * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
23   */
24  package org.modeshape.graph.query;
25  
26  import java.math.BigDecimal;
27  import java.net.URI;
28  import java.util.ArrayList;
29  import java.util.Collection;
30  import java.util.LinkedList;
31  import java.util.List;
32  import java.util.UUID;
33  import java.util.concurrent.atomic.AtomicBoolean;
34  import net.jcip.annotations.NotThreadSafe;
35  import org.modeshape.common.util.CheckArg;
36  import org.modeshape.graph.GraphI18n;
37  import org.modeshape.graph.property.Binary;
38  import org.modeshape.graph.property.DateTime;
39  import org.modeshape.graph.property.Name;
40  import org.modeshape.graph.property.Path;
41  import org.modeshape.graph.property.PropertyType;
42  import org.modeshape.graph.query.model.AllNodes;
43  import org.modeshape.graph.query.model.And;
44  import org.modeshape.graph.query.model.ArithmeticOperand;
45  import org.modeshape.graph.query.model.ArithmeticOperator;
46  import org.modeshape.graph.query.model.Between;
47  import org.modeshape.graph.query.model.BindVariableName;
48  import org.modeshape.graph.query.model.ChildNode;
49  import org.modeshape.graph.query.model.ChildNodeJoinCondition;
50  import org.modeshape.graph.query.model.Column;
51  import org.modeshape.graph.query.model.Comparison;
52  import org.modeshape.graph.query.model.Constraint;
53  import org.modeshape.graph.query.model.DescendantNode;
54  import org.modeshape.graph.query.model.DescendantNodeJoinCondition;
55  import org.modeshape.graph.query.model.DynamicOperand;
56  import org.modeshape.graph.query.model.EquiJoinCondition;
57  import org.modeshape.graph.query.model.FullTextSearch;
58  import org.modeshape.graph.query.model.FullTextSearchScore;
59  import org.modeshape.graph.query.model.Join;
60  import org.modeshape.graph.query.model.JoinCondition;
61  import org.modeshape.graph.query.model.JoinType;
62  import org.modeshape.graph.query.model.Length;
63  import org.modeshape.graph.query.model.Limit;
64  import org.modeshape.graph.query.model.Literal;
65  import org.modeshape.graph.query.model.LowerCase;
66  import org.modeshape.graph.query.model.NamedSelector;
67  import org.modeshape.graph.query.model.NodeDepth;
68  import org.modeshape.graph.query.model.NodeLocalName;
69  import org.modeshape.graph.query.model.NodeName;
70  import org.modeshape.graph.query.model.NodePath;
71  import org.modeshape.graph.query.model.Not;
72  import org.modeshape.graph.query.model.Operator;
73  import org.modeshape.graph.query.model.Or;
74  import org.modeshape.graph.query.model.Order;
75  import org.modeshape.graph.query.model.Ordering;
76  import org.modeshape.graph.query.model.PropertyExistence;
77  import org.modeshape.graph.query.model.PropertyValue;
78  import org.modeshape.graph.query.model.Query;
79  import org.modeshape.graph.query.model.QueryCommand;
80  import org.modeshape.graph.query.model.ReferenceValue;
81  import org.modeshape.graph.query.model.SameNode;
82  import org.modeshape.graph.query.model.SameNodeJoinCondition;
83  import org.modeshape.graph.query.model.Selector;
84  import org.modeshape.graph.query.model.SelectorName;
85  import org.modeshape.graph.query.model.SetCriteria;
86  import org.modeshape.graph.query.model.SetQuery;
87  import org.modeshape.graph.query.model.Source;
88  import org.modeshape.graph.query.model.StaticOperand;
89  import org.modeshape.graph.query.model.TypeSystem;
90  import org.modeshape.graph.query.model.UpperCase;
91  import org.modeshape.graph.query.model.Visitors;
92  import org.modeshape.graph.query.model.SetQuery.Operation;
93  
94  /**
95   * A component that can be used to programmatically create {@link QueryCommand} objects. Simply call methods to build the selector
96   * clause, from clause, join criteria, where criteria, limits, and ordering, and then {@link #query() obtain the query}. This
97   * builder should be adequate for most queries; however, any query that cannot be expressed by this builder can always be
98   * constructed by directly creating the Abstract Query Model classes.
99   * <p>
100  * This builder is stateful and therefore should only be used by one thread at a time. However, once a query has been built, the
101  * builder can be {@link #clear() cleared} and used to create another query.
102  * </p>
103  * <p>
104  * The order in which the methods are called are (for the most part) important. Simply call the methods in the same order that
105  * would be most natural in a normal SQL query. For example, the following code creates a Query object that is equivalent to "
106  * <code>SELECT * FROM table</code>":
107  * 
108  * <pre>
109  * QueryCommand query = builder.selectStar().from(&quot;table&quot;).query();
110  * </pre>
111  * 
112  * </p>
113  * <p>
114  * Here are a few other examples:
115  * <table border="1" cellspacing="0" cellpadding="3" summary="">
116  * <tr>
117  * <th>SQL Statement</th>
118  * <th>QueryBuilder code</th>
119  * </tr>
120  * <tr>
121  * <td>
122  * 
123  * <pre>
124  * SELECT * FROM table1
125  *    INNER JOIN table2
126  *            ON table2.c0 = table1.c0
127  * </pre>
128  * 
129  * </td>
130  * <td>
131  * 
132  * <pre>
133  * query = builder.selectStar().from(&quot;table1&quot;).join(&quot;table2&quot;).on(&quot;table2.c0=table1.c0&quot;).query();
134  * </pre>
135  * 
136  * </td>
137  * </tr>
138  * <tr>
139  * <td>
140  * 
141  * <pre>
142  * SELECT * FROM table1 AS t1
143  *    INNER JOIN table2 AS t2
144  *            ON t1.c0 = t2.c0
145  * </pre>
146  * 
147  * </td>
148  * <td>
149  * 
150  * <pre>
151  * query = builder.selectStar().from(&quot;table1 AS t1&quot;).join(&quot;table2 AS t2&quot;).on(&quot;t1.c0=t2.c0&quot;).query();
152  * </pre>
153  * 
154  * </td>
155  * </tr>
156  * <tr>
157  * <td>
158  * 
159  * <pre>
160  * SELECT * FROM table1 AS t1
161  *    INNER JOIN table2 AS t2
162  *            ON t1.c0 = t2.c0
163  *    INNER JOIN table3 AS t3
164  *            ON t1.c1 = t3.c1
165  * </pre>
166  * 
167  * </td>
168  * <td>
169  * 
170  * <pre>
171  * query = builder.selectStar()
172  *                .from(&quot;table1 AS t1&quot;)
173  *                .innerJoin(&quot;table2 AS t2&quot;)
174  *                .on(&quot;t1.c0=t2.c0&quot;)
175  *                .innerJoin(&quot;table3 AS t3&quot;)
176  *                .on(&quot;t1.c1=t3.c1&quot;)
177  *                .query();
178  * </pre>
179  * 
180  * </td>
181  * </tr>
182  * <tr>
183  * <td>
184  * 
185  * <pre>
186  * SELECT * FROM table1
187  * UNION
188  * SELECT * FROM table2
189  * </pre>
190  * 
191  * </td>
192  * <td>
193  * 
194  * <pre>
195  * query = builder.selectStar().from(&quot;table1&quot;).union().selectStar().from(&quot;table2&quot;).query();
196  * </pre>
197  * 
198  * </td>
199  * </tr>
200  * <tr>
201  * <td>
202  * 
203  * <pre>
204  * SELECT t1.c1,t1.c2,t2.c3 FROM table1 AS t1
205  *    INNER JOIN table2 AS t2
206  *            ON t1.c0 = t2.c0
207  * UNION ALL
208  * SELECT t3.c1,t3.c2,t4.c3 FROM table3 AS t3
209  *    INNER JOIN table4 AS t4
210  *            ON t3.c0 = t4.c0
211  * </pre>
212  * 
213  * </td>
214  * <td>
215  * 
216  * <pre>
217  * query = builder.select(&quot;t1.c1&quot;,&quot;t1.c2&quot;,&quot;t2.c3&quot;,)
218  *                .from(&quot;table1 AS t1&quot;)
219  *                .innerJoin(&quot;table2 AS t2&quot;)
220  *                .on(&quot;t1.c0=t2.c0&quot;)
221  *                .union()
222  *                .select(&quot;t3.c1&quot;,&quot;t3.c2&quot;,&quot;t4.c3&quot;,)
223  *                .from(&quot;table3 AS t3&quot;)
224  *                .innerJoin(&quot;table4 AS t4&quot;)
225  *                .on(&quot;t3.c0=t4.c0&quot;)
226  *                .query();
227  * </pre>
228  * 
229  * </td>
230  * </tr>
231  * </table>
232  * </pre>
233  */
234 @NotThreadSafe
235 public class QueryBuilder {
236 
237     protected final TypeSystem typeSystem;
238     protected Source source = new AllNodes();
239     protected Constraint constraint;
240     protected List<Column> columns = new LinkedList<Column>();
241     protected List<Ordering> orderings = new LinkedList<Ordering>();
242     protected Limit limit = Limit.NONE;
243     protected boolean distinct;
244     protected QueryCommand firstQuery;
245     protected Operation firstQuerySetOperation;
246     protected boolean firstQueryAll;
247 
248     /**
249      * Create a new builder that uses the supplied execution context.
250      * 
251      * @param context the execution context
252      * @throws IllegalArgumentException if the context is null
253      */
254     public QueryBuilder( TypeSystem context ) {
255         CheckArg.isNotNull(context, "context");
256         this.typeSystem = context;
257     }
258 
259     /**
260      * Clear this builder completely to start building a new query.
261      * 
262      * @return this builder object, for convenience in method chaining
263      */
264     public QueryBuilder clear() {
265         return clear(true);
266     }
267 
268     /**
269      * Utility method that does all the work of the clear, but with a flag that defines whether to clear the first query. This
270      * method is used by {@link #clear()} as well as the {@link #union() many} {@link #intersect() set} {@link #except()
271      * operations}.
272      * 
273      * @param clearFirstQuery true if the first query should be cleared, or false if the first query should be retained
274      * @return this builder object, for convenience in method chaining
275      */
276     protected QueryBuilder clear( boolean clearFirstQuery ) {
277         source = new AllNodes();
278         constraint = null;
279         columns = new LinkedList<Column>();
280         orderings = new LinkedList<Ordering>();
281         limit = Limit.NONE;
282         distinct = false;
283         if (clearFirstQuery) {
284             this.firstQuery = null;
285             this.firstQuerySetOperation = null;
286         }
287         return this;
288     }
289 
290     /**
291      * Convenience method that creates a selector name object using the supplied string.
292      * 
293      * @param name the name of the selector; may not be null
294      * @return the selector name; never null
295      */
296     protected SelectorName selector( String name ) {
297         return new SelectorName(name.trim());
298     }
299 
300     /**
301      * Convenience method that creates a {@link NamedSelector} object given a string that contains the selector name and
302      * optionally an alias. The format of the string parameter is <code>name [AS alias]</code>. Leading and trailing whitespace
303      * are trimmed.
304      * 
305      * @param nameWithOptionalAlias the name and optional alias; may not be null
306      * @return the named selector object; never null
307      */
308     protected NamedSelector namedSelector( String nameWithOptionalAlias ) {
309         String[] parts = nameWithOptionalAlias.split("\\sAS\\s");
310         if (parts.length == 2) {
311             return new NamedSelector(selector(parts[0]), selector(parts[1]));
312         }
313         return new NamedSelector(selector(parts[0]));
314     }
315 
316     /**
317      * Create a {@link Column} given the supplied expression. The expression has the form "<code>[tableName.]columnName</code>",
318      * where "<code>tableName</code>" must be a valid table name or alias. If the table name/alias is not specified, then there is
319      * expected to be a single FROM clause with a single named selector.
320      * 
321      * @param nameExpression the expression specifying the columm name and (optionally) the table's name or alias; may not be null
322      * @return the column; never null
323      * @throws IllegalArgumentException if the table's name/alias is not specified, but the query has more than one named source
324      */
325     protected Column column( String nameExpression ) {
326         String[] parts = nameExpression.split("(?<!\\\\)\\."); // a . not preceded by an escaping slash
327         for (int i = 0; i != parts.length; ++i) {
328             parts[i] = parts[i].trim();
329         }
330         SelectorName name = null;
331         String propertyName = null;
332         String columnName = null;
333         if (parts.length == 2) {
334             name = selector(parts[0]);
335             propertyName = parts[1];
336             columnName = parts[1];
337         } else {
338             if (source instanceof Selector) {
339                 Selector selector = (Selector)source;
340                 name = selector.hasAlias() ? selector.alias() : selector.name();
341                 propertyName = parts[0];
342                 columnName = parts[0];
343             } else {
344                 throw new IllegalArgumentException(GraphI18n.columnMustBeScoped.text(parts[0]));
345             }
346         }
347         return new Column(name, propertyName, columnName);
348     }
349 
350     /**
351      * Select all of the single-valued columns.
352      * 
353      * @return this builder object, for convenience in method chaining
354      */
355     public QueryBuilder selectStar() {
356         columns.clear();
357         return this;
358     }
359 
360     /**
361      * Add to the select clause the columns with the supplied names. Each column name has the form "
362      * <code>[tableName.]columnName</code>", where " <code>tableName</code>" must be a valid table name or alias. If the table
363      * name/alias is not specified, then there is expected to be a single FROM clause with a single named selector.
364      * 
365      * @param columnNames the column expressions; may not be null
366      * @return this builder object, for convenience in method chaining
367      * @throws IllegalArgumentException if the table's name/alias is not specified, but the query has more than one named source
368      */
369     public QueryBuilder select( String... columnNames ) {
370         for (String expression : columnNames) {
371             columns.add(column(expression));
372         }
373         return this;
374     }
375 
376     /**
377      * Select all of the distinct values from the single-valued columns.
378      * 
379      * @return this builder object, for convenience in method chaining
380      */
381     public QueryBuilder selectDistinctStar() {
382         distinct = true;
383         return selectStar();
384     }
385 
386     /**
387      * Select the distinct values from the columns with the supplied names. Each column name has the form "
388      * <code>[tableName.]columnName</code>", where " <code>tableName</code>" must be a valid table name or alias. If the table
389      * name/alias is not specified, then there is expected to be a single FROM clause with a single named selector.
390      * 
391      * @param columnNames the column expressions; may not be null
392      * @return this builder object, for convenience in method chaining
393      * @throws IllegalArgumentException if the table's name/alias is not specified, but the query has more than one named source
394      */
395     public QueryBuilder selectDistinct( String... columnNames ) {
396         distinct = true;
397         return select(columnNames);
398     }
399 
400     /**
401      * Specify that the query should select from the "__ALLNODES__" built-in table.
402      * 
403      * @return this builder object, for convenience in method chaining
404      */
405     public QueryBuilder fromAllNodes() {
406         this.source = new AllNodes();
407         return this;
408     }
409 
410     /**
411      * Specify that the query should select from the "__ALLNODES__" built-in table using the supplied alias.
412      * 
413      * @param alias the alias for the "__ALL_NODES" table; may not be null
414      * @return this builder object, for convenience in method chaining
415      */
416     public QueryBuilder fromAllNodesAs( String alias ) {
417         AllNodes allNodes = new AllNodes(selector(alias));
418         SelectorName oldName = this.source instanceof Selector ? ((Selector)source).name() : null;
419         // Go through the columns and change the selector name to use the new alias ...
420         for (int i = 0; i != columns.size(); ++i) {
421             Column old = columns.get(i);
422             if (old.selectorName().equals(oldName)) {
423                 columns.set(i, new Column(allNodes.aliasOrName(), old.propertyName(), old.columnName()));
424             }
425         }
426         this.source = allNodes;
427         return this;
428     }
429 
430     /**
431      * Specify the name of the table from which tuples should be selected. The supplied string is of the form "
432      * <code>tableName [AS alias]</code>".
433      * 
434      * @param tableNameWithOptionalAlias the name of the table, optionally including the alias
435      * @return this builder object, for convenience in method chaining
436      */
437     public QueryBuilder from( String tableNameWithOptionalAlias ) {
438         Selector selector = namedSelector(tableNameWithOptionalAlias);
439         SelectorName oldName = this.source instanceof Selector ? ((Selector)source).name() : null;
440         // Go through the columns and change the selector name to use the new alias ...
441         for (int i = 0; i != columns.size(); ++i) {
442             Column old = columns.get(i);
443             if (old.selectorName().equals(oldName)) {
444                 columns.set(i, new Column(selector.aliasOrName(), old.propertyName(), old.columnName()));
445             }
446         }
447         this.source = selector;
448         return this;
449     }
450 
451     /**
452      * Begin the WHERE clause for this query by obtaining the constraint builder. When completed, be sure to call
453      * {@link ConstraintBuilder#end() end()} on the resulting constraint builder, or else the constraint will not be applied to
454      * the current query.
455      * 
456      * @return the constraint builder that can be used to specify the criteria; never null
457      */
458     public ConstraintBuilder where() {
459         return new ConstraintBuilder(null);
460     }
461 
462     /**
463      * Perform an inner join between the already defined source with the supplied table. The supplied string is of the form "
464      * <code>tableName [AS alias]</code>".
465      * 
466      * @param tableName the name of the table, optionally including the alias
467      * @return the component that must be used to complete the join specification; never null
468      */
469     public JoinClause join( String tableName ) {
470         return innerJoin(tableName);
471     }
472 
473     /**
474      * Perform an inner join between the already defined source with the supplied table. The supplied string is of the form "
475      * <code>tableName [AS alias]</code>".
476      * 
477      * @param tableName the name of the table, optionally including the alias
478      * @return the component that must be used to complete the join specification; never null
479      */
480     public JoinClause innerJoin( String tableName ) {
481         // Expect there to be a source already ...
482         return new JoinClause(namedSelector(tableName), JoinType.INNER);
483     }
484 
485     /**
486      * Perform a cross join between the already defined source with the supplied table. The supplied string is of the form "
487      * <code>tableName [AS alias]</code>". Cross joins have a higher precedent than other join types, so if this is called after
488      * another join was defined, the resulting cross join will be between the previous join's right-hand side and the supplied
489      * table.
490      * 
491      * @param tableName the name of the table, optionally including the alias
492      * @return the component that must be used to complete the join specification; never null
493      */
494     public JoinClause crossJoin( String tableName ) {
495         // Expect there to be a source already ...
496         return new JoinClause(namedSelector(tableName), JoinType.CROSS);
497     }
498 
499     /**
500      * Perform a full outer join between the already defined source with the supplied table. The supplied string is of the form "
501      * <code>tableName [AS alias]</code>".
502      * 
503      * @param tableName the name of the table, optionally including the alias
504      * @return the component that must be used to complete the join specification; never null
505      */
506     public JoinClause fullOuterJoin( String tableName ) {
507         // Expect there to be a source already ...
508         return new JoinClause(namedSelector(tableName), JoinType.FULL_OUTER);
509     }
510 
511     /**
512      * Perform a left outer join between the already defined source with the supplied table. The supplied string is of the form "
513      * <code>tableName [AS alias]</code>".
514      * 
515      * @param tableName the name of the table, optionally including the alias
516      * @return the component that must be used to complete the join specification; never null
517      */
518     public JoinClause leftOuterJoin( String tableName ) {
519         // Expect there to be a source already ...
520         return new JoinClause(namedSelector(tableName), JoinType.LEFT_OUTER);
521     }
522 
523     /**
524      * Perform a right outer join between the already defined source with the supplied table. The supplied string is of the form "
525      * <code>tableName [AS alias]</code>".
526      * 
527      * @param tableName the name of the table, optionally including the alias
528      * @return the component that must be used to complete the join specification; never null
529      */
530     public JoinClause rightOuterJoin( String tableName ) {
531         // Expect there to be a source already ...
532         return new JoinClause(namedSelector(tableName), JoinType.RIGHT_OUTER);
533     }
534 
535     /**
536      * Perform an inner join between the already defined source with the "__ALLNODES__" table using the supplied alias.
537      * 
538      * @param alias the alias for the "__ALL_NODES" table; may not be null
539      * @return the component that must be used to complete the join specification; never null
540      */
541     public JoinClause joinAllNodesAs( String alias ) {
542         return innerJoinAllNodesAs(alias);
543     }
544 
545     /**
546      * Perform an inner join between the already defined source with the "__ALL_NODES" table using the supplied alias.
547      * 
548      * @param alias the alias for the "__ALL_NODES" table; may not be null
549      * @return the component that must be used to complete the join specification; never null
550      */
551     public JoinClause innerJoinAllNodesAs( String alias ) {
552         // Expect there to be a source already ...
553         return new JoinClause(namedSelector(AllNodes.ALL_NODES_NAME + " AS " + alias), JoinType.INNER);
554     }
555 
556     /**
557      * Perform a cross join between the already defined source with the "__ALL_NODES" table using the supplied alias. Cross joins
558      * have a higher precedent than other join types, so if this is called after another join was defined, the resulting cross
559      * join will be between the previous join's right-hand side and the supplied table.
560      * 
561      * @param alias the alias for the "__ALL_NODES" table; may not be null
562      * @return the component that must be used to complete the join specification; never null
563      */
564     public JoinClause crossJoinAllNodesAs( String alias ) {
565         // Expect there to be a source already ...
566         return new JoinClause(namedSelector(AllNodes.ALL_NODES_NAME + " AS " + alias), JoinType.CROSS);
567     }
568 
569     /**
570      * Perform a full outer join between the already defined source with the "__ALL_NODES" table using the supplied alias.
571      * 
572      * @param alias the alias for the "__ALL_NODES" table; may not be null
573      * @return the component that must be used to complete the join specification; never null
574      */
575     public JoinClause fullOuterJoinAllNodesAs( String alias ) {
576         // Expect there to be a source already ...
577         return new JoinClause(namedSelector(AllNodes.ALL_NODES_NAME + " AS " + alias), JoinType.FULL_OUTER);
578     }
579 
580     /**
581      * Perform a left outer join between the already defined source with the "__ALL_NODES" table using the supplied alias.
582      * 
583      * @param alias the alias for the "__ALL_NODES" table; may not be null
584      * @return the component that must be used to complete the join specification; never null
585      */
586     public JoinClause leftOuterJoinAllNodesAs( String alias ) {
587         // Expect there to be a source already ...
588         return new JoinClause(namedSelector(AllNodes.ALL_NODES_NAME + " AS " + alias), JoinType.LEFT_OUTER);
589     }
590 
591     /**
592      * Perform a right outer join between the already defined source with the "__ALL_NODES" table using the supplied alias.
593      * 
594      * @param alias the alias for the "__ALL_NODES" table; may not be null
595      * @return the component that must be used to complete the join specification; never null
596      */
597     public JoinClause rightOuterJoinAllNodesAs( String alias ) {
598         // Expect there to be a source already ...
599         return new JoinClause(namedSelector(AllNodes.ALL_NODES_NAME + " AS " + alias), JoinType.RIGHT_OUTER);
600     }
601 
602     /**
603      * Specify the maximum number of rows that are to be returned in the results. By default there is no limit.
604      * 
605      * @param rowLimit the maximum number of rows
606      * @return this builder object, for convenience in method chaining
607      * @throws IllegalArgumentException if the row limit is not a positive integer
608      */
609     public QueryBuilder limit( int rowLimit ) {
610         this.limit.withRowLimit(rowLimit);
611         return this;
612     }
613 
614     /**
615      * Specify the number of rows that results are to skip. The default offset is '0'.
616      * 
617      * @param offset the number of rows before the results are to begin
618      * @return this builder object, for convenience in method chaining
619      * @throws IllegalArgumentException if the row limit is a negative integer
620      */
621     public QueryBuilder offset( int offset ) {
622         this.limit.withOffset(offset);
623         return this;
624     }
625 
626     /**
627      * Perform a UNION between the query as defined prior to this method and the query that will be defined following this method.
628      * 
629      * @return this builder object, for convenience in method chaining
630      */
631     public QueryBuilder union() {
632         this.firstQuery = query();
633         this.firstQuerySetOperation = Operation.UNION;
634         this.firstQueryAll = false;
635         clear(false);
636         return this;
637     }
638 
639     /**
640      * Perform a UNION ALL between the query as defined prior to this method and the query that will be defined following this
641      * method.
642      * 
643      * @return this builder object, for convenience in method chaining
644      */
645     public QueryBuilder unionAll() {
646         this.firstQuery = query();
647         this.firstQuerySetOperation = Operation.UNION;
648         this.firstQueryAll = true;
649         clear(false);
650         return this;
651     }
652 
653     /**
654      * Perform an INTERSECT between the query as defined prior to this method and the query that will be defined following this
655      * method.
656      * 
657      * @return this builder object, for convenience in method chaining
658      */
659     public QueryBuilder intersect() {
660         this.firstQuery = query();
661         this.firstQuerySetOperation = Operation.INTERSECT;
662         this.firstQueryAll = false;
663         clear(false);
664         return this;
665     }
666 
667     /**
668      * Perform an INTERSECT ALL between the query as defined prior to this method and the query that will be defined following
669      * this method.
670      * 
671      * @return this builder object, for convenience in method chaining
672      */
673     public QueryBuilder intersectAll() {
674         this.firstQuery = query();
675         this.firstQuerySetOperation = Operation.INTERSECT;
676         this.firstQueryAll = true;
677         clear(false);
678         return this;
679     }
680 
681     /**
682      * Perform an EXCEPT between the query as defined prior to this method and the query that will be defined following this
683      * method.
684      * 
685      * @return this builder object, for convenience in method chaining
686      */
687     public QueryBuilder except() {
688         this.firstQuery = query();
689         this.firstQuerySetOperation = Operation.EXCEPT;
690         this.firstQueryAll = false;
691         clear(false);
692         return this;
693     }
694 
695     /**
696      * Perform an EXCEPT ALL between the query as defined prior to this method and the query that will be defined following this
697      * method.
698      * 
699      * @return this builder object, for convenience in method chaining
700      */
701     public QueryBuilder exceptAll() {
702         this.firstQuery = query();
703         this.firstQuerySetOperation = Operation.EXCEPT;
704         this.firstQueryAll = true;
705         clear(false);
706         return this;
707     }
708 
709     /**
710      * Obtain a builder that will create the order-by clause (with one or more {@link Ordering} statements) for the query. This
711      * method need be called only once to build the order-by clause, but can be called multiple times (it merely adds additional
712      * {@link Ordering} statements).
713      * 
714      * @return the order-by builder; never null
715      */
716     public OrderByBuilder orderBy() {
717         return new OrderByBuilder();
718     }
719 
720     /**
721      * Return a {@link QueryCommand} representing the currently-built query.
722      * 
723      * @return the resulting query command; never null
724      * @see #clear()
725      */
726     public QueryCommand query() {
727         QueryCommand result = new Query(source, constraint, orderings, columns, limit, distinct);
728         if (this.firstQuery != null) {
729             // EXCEPT has a higher precedence than INTERSECT or UNION, so if the first query is
730             // an INTERSECT or UNION SetQuery, the result should be applied to the RHS of the previous set ...
731             if (firstQuery instanceof SetQuery && firstQuerySetOperation == Operation.EXCEPT) {
732                 SetQuery setQuery = (SetQuery)firstQuery;
733                 QueryCommand left = setQuery.left();
734                 QueryCommand right = setQuery.right();
735                 SetQuery exceptQuery = new SetQuery(right, Operation.EXCEPT, result, firstQueryAll);
736                 result = new SetQuery(left, setQuery.operation(), exceptQuery, setQuery.isAll());
737             } else {
738                 result = new SetQuery(this.firstQuery, this.firstQuerySetOperation, result, this.firstQueryAll);
739             }
740         }
741         return result;
742     }
743 
744     public interface OrderByOperandBuilder {
745         /**
746          * Adds to the order-by clause by using the length of the value for the given table and property.
747          * 
748          * @param table the name of the table; may not be null and must refer to a valid name or alias of a table appearing in the
749          *        FROM clause
750          * @param property the name of the property; may not be null and must refer to a valid property name
751          * @return the interface for completing the order-by specification; never null
752          */
753         public OrderByBuilder length( String table,
754                                       String property );
755 
756         /**
757          * Adds to the order-by clause by using the value for the given table and property.
758          * 
759          * @param table the name of the table; may not be null and must refer to a valid name or alias of a table appearing in the
760          *        FROM clause
761          * @param property the name of the property; may not be null and must refer to a valid property name
762          * @return the interface for completing the order-by specification; never null
763          */
764         public OrderByBuilder propertyValue( String table,
765                                              String property );
766 
767         /**
768          * Constrains the nodes in the the supplied table such that they must have a matching value for any of the node's
769          * reference properties.
770          * 
771          * @param table the name of the table; may not be null and must refer to a valid name or alias of a table appearing in the
772          *        FROM clause
773          * @return the interface for completing the order-by specification; never null
774          */
775         public OrderByBuilder referenceValue( String table );
776 
777         /**
778          * Constrains the nodes in the the supplied table such that they must have a matching value for the named property.
779          * 
780          * @param table the name of the table; may not be null and must refer to a valid name or alias of a table appearing in the
781          *        FROM clause
782          * @param property the name of the reference property; may be null if the constraint applies to all/any reference
783          *        properties on the node
784          * @return the interface for completing the order-by specification; never null
785          */
786         public OrderByBuilder referenceValue( String table,
787                                               String property );
788 
789         /**
790          * Adds to the order-by clause by using the full-text search score for the given table.
791          * 
792          * @param table the name of the table; may not be null and must refer to a valid name or alias of a table appearing in the
793          *        FROM clause
794          * @return the interface for completing the order-by specification; never null
795          */
796         public OrderByBuilder fullTextSearchScore( String table );
797 
798         /**
799          * Adds to the order-by clause by using the depth of the node given by the named table.
800          * 
801          * @param table the name of the table; may not be null and must refer to a valid name or alias of a table appearing in the
802          *        FROM clause
803          * @return the interface for completing the order-by specification; never null
804          */
805         public OrderByBuilder depth( String table );
806 
807         /**
808          * Adds to the order-by clause by using the path of the node given by the named table.
809          * 
810          * @param table the name of the table; may not be null and must refer to a valid name or alias of a table appearing in the
811          *        FROM clause
812          * @return the interface for completing the order-by specification; never null
813          */
814         public OrderByBuilder path( String table );
815 
816         /**
817          * Adds to the order-by clause by using the local name of the node given by the named table.
818          * 
819          * @param table the name of the table; may not be null and must refer to a valid name or alias of a table appearing in the
820          *        FROM clause
821          * @return the interface for completing the order-by specification; never null
822          */
823         public OrderByBuilder nodeLocalName( String table );
824 
825         /**
826          * Adds to the order-by clause by using the node name (including namespace) of the node given by the named table.
827          * 
828          * @param table the name of the table; may not be null and must refer to a valid name or alias of a table appearing in the
829          *        FROM clause
830          * @return the interface for completing the order-by specification; never null
831          */
832         public OrderByBuilder nodeName( String table );
833 
834         /**
835          * Adds to the order-by clause by using the uppercase form of the next operand.
836          * 
837          * @return the interface for completing the order-by specification; never null
838          */
839         public OrderByOperandBuilder upperCaseOf();
840 
841         /**
842          * Adds to the order-by clause by using the lowercase form of the next operand.
843          * 
844          * @return the interface for completing the order-by specification; never null
845          */
846         public OrderByOperandBuilder lowerCaseOf();
847     }
848 
849     /**
850      * The component used to build the order-by clause. When the clause is completed, {@link #end()} should be called to return to
851      * the {@link QueryBuilder} instance.
852      */
853     public class OrderByBuilder {
854 
855         protected OrderByBuilder() {
856         }
857 
858         /**
859          * Begin specifying an order-by specification using {@link Order#ASCENDING ascending order}.
860          * 
861          * @return the interface for specifying the operand that is to be ordered; never null
862          */
863         public OrderByOperandBuilder ascending() {
864             return new SingleOrderByOperandBuilder(this, Order.ASCENDING);
865         }
866 
867         /**
868          * Begin specifying an order-by specification using {@link Order#DESCENDING descending order}.
869          * 
870          * @return the interface for specifying the operand that is to be ordered; never null
871          */
872         public OrderByOperandBuilder descending() {
873             return new SingleOrderByOperandBuilder(this, Order.DESCENDING);
874         }
875 
876         /**
877          * An optional convenience method that returns this builder, but which makes the code using this builder more readable.
878          * 
879          * @return this builder; never null
880          */
881         public OrderByBuilder then() {
882             return this;
883         }
884 
885         /**
886          * Complete the order-by clause and return the QueryBuilder instance.
887          * 
888          * @return the query builder instance; never null
889          */
890         public QueryBuilder end() {
891             return QueryBuilder.this;
892         }
893     }
894 
895     protected class SingleOrderByOperandBuilder implements OrderByOperandBuilder {
896         private final Order order;
897         private final OrderByBuilder builder;
898 
899         protected SingleOrderByOperandBuilder( OrderByBuilder builder,
900                                                Order order ) {
901             this.order = order;
902             this.builder = builder;
903         }
904 
905         protected OrderByBuilder addOrdering( DynamicOperand operand ) {
906             Ordering ordering = new Ordering(operand, order);
907             QueryBuilder.this.orderings.add(ordering);
908             return builder;
909         }
910 
911         /**
912          * {@inheritDoc}
913          * 
914          * @see org.modeshape.graph.query.QueryBuilder.OrderByOperandBuilder#propertyValue(java.lang.String, java.lang.String)
915          */
916         public OrderByBuilder propertyValue( String table,
917                                              String property ) {
918             return addOrdering(new PropertyValue(selector(table), property));
919         }
920 
921         /**
922          * {@inheritDoc}
923          * 
924          * @see org.modeshape.graph.query.QueryBuilder.OrderByOperandBuilder#referenceValue(java.lang.String)
925          */
926         public OrderByBuilder referenceValue( String table ) {
927             return addOrdering(new ReferenceValue(selector(table)));
928         }
929 
930         /**
931          * {@inheritDoc}
932          * 
933          * @see org.modeshape.graph.query.QueryBuilder.OrderByOperandBuilder#referenceValue(java.lang.String, java.lang.String)
934          */
935         public OrderByBuilder referenceValue( String table,
936                                               String property ) {
937             return addOrdering(new ReferenceValue(selector(table), property));
938         }
939 
940         /**
941          * {@inheritDoc}
942          * 
943          * @see org.modeshape.graph.query.QueryBuilder.OrderByOperandBuilder#length(java.lang.String, java.lang.String)
944          */
945         public OrderByBuilder length( String table,
946                                       String property ) {
947             return addOrdering(new Length(new PropertyValue(selector(table), property)));
948         }
949 
950         /**
951          * {@inheritDoc}
952          * 
953          * @see org.modeshape.graph.query.QueryBuilder.OrderByOperandBuilder#fullTextSearchScore(java.lang.String)
954          */
955         public OrderByBuilder fullTextSearchScore( String table ) {
956             return addOrdering(new FullTextSearchScore(selector(table)));
957         }
958 
959         /**
960          * {@inheritDoc}
961          * 
962          * @see org.modeshape.graph.query.QueryBuilder.OrderByOperandBuilder#depth(java.lang.String)
963          */
964         public OrderByBuilder depth( String table ) {
965             return addOrdering(new NodeDepth(selector(table)));
966         }
967 
968         /**
969          * {@inheritDoc}
970          * 
971          * @see org.modeshape.graph.query.QueryBuilder.OrderByOperandBuilder#path(java.lang.String)
972          */
973         public OrderByBuilder path( String table ) {
974             return addOrdering(new NodePath(selector(table)));
975         }
976 
977         /**
978          * {@inheritDoc}
979          * 
980          * @see org.modeshape.graph.query.QueryBuilder.OrderByOperandBuilder#nodeName(java.lang.String)
981          */
982         public OrderByBuilder nodeName( String table ) {
983             return addOrdering(new NodeName(selector(table)));
984         }
985 
986         /**
987          * {@inheritDoc}
988          * 
989          * @see org.modeshape.graph.query.QueryBuilder.OrderByOperandBuilder#nodeLocalName(java.lang.String)
990          */
991         public OrderByBuilder nodeLocalName( String table ) {
992             return addOrdering(new NodeLocalName(selector(table)));
993         }
994 
995         /**
996          * {@inheritDoc}
997          * 
998          * @see org.modeshape.graph.query.QueryBuilder.OrderByOperandBuilder#lowerCaseOf()
999          */
1000         public OrderByOperandBuilder lowerCaseOf() {
1001             return new SingleOrderByOperandBuilder(builder, order) {
1002                 /**
1003                  * {@inheritDoc}
1004                  * 
1005                  * @see org.modeshape.graph.query.QueryBuilder.SingleOrderByOperandBuilder#addOrdering(org.modeshape.graph.query.model.DynamicOperand)
1006                  */
1007                 @Override
1008                 protected OrderByBuilder addOrdering( DynamicOperand operand ) {
1009                     return super.addOrdering(new LowerCase(operand));
1010                 }
1011             };
1012         }
1013 
1014         /**
1015          * {@inheritDoc}
1016          * 
1017          * @see org.modeshape.graph.query.QueryBuilder.OrderByOperandBuilder#upperCaseOf()
1018          */
1019         public OrderByOperandBuilder upperCaseOf() {
1020             return new SingleOrderByOperandBuilder(builder, order) {
1021                 /**
1022                  * {@inheritDoc}
1023                  * 
1024                  * @see org.modeshape.graph.query.QueryBuilder.SingleOrderByOperandBuilder#addOrdering(org.modeshape.graph.query.model.DynamicOperand)
1025                  */
1026                 @Override
1027                 protected OrderByBuilder addOrdering( DynamicOperand operand ) {
1028                     return super.addOrdering(new UpperCase(operand));
1029                 }
1030             };
1031         }
1032     }
1033 
1034     /**
1035      * Class used to specify a join clause of a query.
1036      * 
1037      * @see QueryBuilder#join(String)
1038      * @see QueryBuilder#innerJoin(String)
1039      * @see QueryBuilder#leftOuterJoin(String)
1040      * @see QueryBuilder#rightOuterJoin(String)
1041      * @see QueryBuilder#fullOuterJoin(String)
1042      */
1043     public class JoinClause {
1044         private final NamedSelector rightSource;
1045         private final JoinType type;
1046 
1047         protected JoinClause( NamedSelector rightTable,
1048                               JoinType type ) {
1049             this.rightSource = rightTable;
1050             this.type = type;
1051         }
1052 
1053         /**
1054          * Walk the current source or the 'rightSource' to find the named selector with the supplied name or alias
1055          * 
1056          * @param tableName the table name
1057          * @return the selector name matching the supplied table name; never null
1058          * @throws IllegalArgumentException if the table name could not be resolved
1059          */
1060         protected SelectorName nameOf( String tableName ) {
1061             final SelectorName name = new SelectorName(tableName);
1062             // Look at the right source ...
1063             if (rightSource.aliasOrName().equals(name)) return name;
1064             // Look through the left source ...
1065             final AtomicBoolean notFound = new AtomicBoolean(true);
1066             Visitors.visitAll(source, new Visitors.AbstractVisitor() {
1067                 @Override
1068                 public void visit( AllNodes selector ) {
1069                     if (notFound.get() && selector.aliasOrName().equals(name)) notFound.set(false);
1070                 }
1071 
1072                 @Override
1073                 public void visit( NamedSelector selector ) {
1074                     if (notFound.get() && selector.aliasOrName().equals(name)) notFound.set(false);
1075                 }
1076             });
1077             if (notFound.get()) {
1078                 throw new IllegalArgumentException("Expected \"" + tableName + "\" to be a valid table name or alias");
1079             }
1080             return name;
1081         }
1082 
1083         /**
1084          * Define the join as using an equi-join criteria by specifying the expression equating two columns. Each column reference
1085          * must be qualified with the appropriate table name or alias.
1086          * 
1087          * @param columnEqualExpression the equality expression between the two tables; may not be null
1088          * @return the query builder instance, for method chaining purposes
1089          * @throws IllegalArgumentException if the supplied expression is not an equality expression
1090          */
1091         public QueryBuilder on( String columnEqualExpression ) {
1092             String[] parts = columnEqualExpression.split("=");
1093             if (parts.length != 2) {
1094                 throw new IllegalArgumentException("Expected equality expression for columns, but found \""
1095                                                    + columnEqualExpression + "\"");
1096             }
1097             return createJoin(new EquiJoinCondition(column(parts[0]), column(parts[1])));
1098         }
1099 
1100         /**
1101          * Define the join criteria to require the two tables represent the same node. The supplied tables must be a valid name or
1102          * alias.
1103          * 
1104          * @param table1 the name or alias of the first table
1105          * @param table2 the name or alias of the second table
1106          * @return the query builder instance, for method chaining purposes
1107          */
1108         public QueryBuilder onSameNode( String table1,
1109                                         String table2 ) {
1110             return createJoin(new SameNodeJoinCondition(nameOf(table1), nameOf(table2)));
1111         }
1112 
1113         /**
1114          * Define the join criteria to require the node in one table is a descendant of the node in another table. The supplied
1115          * tables must be a valid name or alias.
1116          * 
1117          * @param ancestorTable the name or alias of the table containing the ancestor node
1118          * @param descendantTable the name or alias of the table containing the descendant node
1119          * @return the query builder instance, for method chaining purposes
1120          */
1121         public QueryBuilder onDescendant( String ancestorTable,
1122                                           String descendantTable ) {
1123             return createJoin(new DescendantNodeJoinCondition(nameOf(ancestorTable), nameOf(descendantTable)));
1124         }
1125 
1126         /**
1127          * Define the join criteria to require the node in one table is a child of the node in another table. The supplied tables
1128          * must be a valid name or alias.
1129          * 
1130          * @param parentTable the name or alias of the table containing the parent node
1131          * @param childTable the name or alias of the table containing the child node
1132          * @return the query builder instance, for method chaining purposes
1133          */
1134         public QueryBuilder onChildNode( String parentTable,
1135                                          String childTable ) {
1136             return createJoin(new ChildNodeJoinCondition(nameOf(parentTable), nameOf(childTable)));
1137         }
1138 
1139         protected QueryBuilder createJoin( JoinCondition condition ) {
1140             // CROSS joins have a higher precedence, so we may need to adjust the existing left side in this case...
1141             if (type == JoinType.CROSS && source instanceof Join && ((Join)source).type() != JoinType.CROSS) {
1142                 // A CROSS join follows a non-CROSS join, so the CROSS join becomes precendent ...
1143                 Join left = (Join)source;
1144                 Join cross = new Join(left.right(), type, rightSource, condition);
1145                 source = new Join(left.left(), left.type(), cross, left.joinCondition());
1146             } else {
1147                 // Otherwise, just create using usual precedence ...
1148                 source = new Join(source, type, rightSource, condition);
1149             }
1150             return QueryBuilder.this;
1151         }
1152     }
1153 
1154     /**
1155      * Interface that defines a dynamic operand portion of a criteria.
1156      */
1157     public interface DynamicOperandBuilder {
1158         /**
1159          * Constrains the nodes in the the supplied table such that they must have a property value whose length matches the
1160          * criteria.
1161          * 
1162          * @param table the name of the table; may not be null and must refer to a valid name or alias of a table appearing in the
1163          *        FROM clause
1164          * @param property the name of the property; may not be null and must refer to a valid property name
1165          * @return the interface for completing the value portion of the criteria specification; never null
1166          */
1167         public ComparisonBuilder length( String table,
1168                                          String property );
1169 
1170         /**
1171          * Constrains the nodes in the the supplied table such that they must have a matching value for the named property.
1172          * 
1173          * @param table the name of the table; may not be null and must refer to a valid name or alias of a table appearing in the
1174          *        FROM clause
1175          * @param property the name of the property; may not be null and must refer to a valid property name
1176          * @return the interface for completing the value portion of the criteria specification; never null
1177          */
1178         public ComparisonBuilder propertyValue( String table,
1179                                                 String property );
1180 
1181         /**
1182          * Constrains the nodes in the the supplied table such that they must have a matching value for any of the node's
1183          * reference properties.
1184          * 
1185          * @param table the name of the table; may not be null and must refer to a valid name or alias of a table appearing in the
1186          *        FROM clause
1187          * @return the interface for completing the value portion of the criteria specification; never null
1188          */
1189         public ComparisonBuilder referenceValue( String table );
1190 
1191         /**
1192          * Constrains the nodes in the the supplied table such that they must have a matching value for the named property.
1193          * 
1194          * @param table the name of the table; may not be null and must refer to a valid name or alias of a table appearing in the
1195          *        FROM clause
1196          * @param property the name of the reference property; may be null if the constraint applies to all/any reference
1197          *        properties on the node
1198          * @return the interface for completing the value portion of the criteria specification; never null
1199          */
1200         public ComparisonBuilder referenceValue( String table,
1201                                                  String property );
1202 
1203         /**
1204          * Constrains the nodes in the the supplied table such that they must have a matching value for any of the node's non-weak
1205          * reference properties.
1206          * 
1207          * @param table the name of the table; may not be null and must refer to a valid name or alias of a table appearing in the
1208          *        FROM clause
1209          * @return the interface for completing the value portion of the criteria specification; never null
1210          */
1211         public ComparisonBuilder strongReferenceValue( String table );
1212 
1213         /**
1214          * Constrains the nodes in the the supplied table such that they must satisfy the supplied full-text search on the nodes'
1215          * property values.
1216          * 
1217          * @param table the name of the table; may not be null and must refer to a valid name or alias of a table appearing in the
1218          *        FROM clause
1219          * @return the interface for completing the value portion of the criteria specification; never null
1220          */
1221         public ComparisonBuilder fullTextSearchScore( String table );
1222 
1223         /**
1224          * Constrains the nodes in the the supplied table based upon criteria on the node's depth.
1225          * 
1226          * @param table the name of the table; may not be null and must refer to a valid name or alias of a table appearing in the
1227          *        FROM clause
1228          * @return the interface for completing the value portion of the criteria specification; never null
1229          */
1230         public ComparisonBuilder depth( String table );
1231 
1232         /**
1233          * Constrains the nodes in the the supplied table based upon criteria on the node's path.
1234          * 
1235          * @param table the name of the table; may not be null and must refer to a valid name or alias of a table appearing in the
1236          *        FROM clause
1237          * @return the interface for completing the value portion of the criteria specification; never null
1238          */
1239         public ComparisonBuilder path( String table );
1240 
1241         /**
1242          * Constrains the nodes in the the supplied table based upon criteria on the node's local name.
1243          * 
1244          * @param table the name of the table; may not be null and must refer to a valid name or alias of a table appearing in the
1245          *        FROM clause
1246          * @return the interface for completing the value portion of the criteria specification; never null
1247          */
1248         public ComparisonBuilder nodeLocalName( String table );
1249 
1250         /**
1251          * Constrains the nodes in the the supplied table based upon criteria on the node's name.
1252          * 
1253          * @param table the name of the table; may not be null and must refer to a valid name or alias of a table appearing in the
1254          *        FROM clause
1255          * @return the interface for completing the value portion of the criteria specification; never null
1256          */
1257         public ComparisonBuilder nodeName( String table );
1258 
1259         /**
1260          * Begin a constraint against the uppercase form of a dynamic operand.
1261          * 
1262          * @return the interface for completing the criteria specification; never null
1263          */
1264         public DynamicOperandBuilder upperCaseOf();
1265 
1266         /**
1267          * Begin a constraint against the lowercase form of a dynamic operand.
1268          * 
1269          * @return the interface for completing the criteria specification; never null
1270          */
1271         public DynamicOperandBuilder lowerCaseOf();
1272     }
1273 
1274     public class ConstraintBuilder implements DynamicOperandBuilder {
1275         private final ConstraintBuilder parent;
1276         /** Used for the current operations */
1277         private Constraint constraint;
1278         /** Set when a logical criteria is started */
1279         private Constraint left;
1280         private boolean and;
1281         private boolean negateConstraint;
1282         private boolean implicitParentheses = true;
1283 
1284         protected ConstraintBuilder( ConstraintBuilder parent ) {
1285             this.parent = parent;
1286         }
1287 
1288         /**
1289          * Complete this constraint specification.
1290          * 
1291          * @return the query builder, for method chaining purposes
1292          */
1293         public QueryBuilder end() {
1294             buildLogicalConstraint();
1295             QueryBuilder.this.constraint = constraint;
1296             return QueryBuilder.this;
1297         }
1298 
1299         /**
1300          * Simulate the use of an open parenthesis in the constraint. The resulting builder should be used to define the
1301          * constraint within the parenthesis, and should always be terminated with a {@link #closeParen()}.
1302          * 
1303          * @return the constraint builder that should be used to define the portion of the constraint within the parenthesis;
1304          *         never null
1305          * @see #closeParen()
1306          */
1307         public ConstraintBuilder openParen() {
1308             return new ConstraintBuilder(this);
1309         }
1310 
1311         /**
1312          * Complete the specification of a constraint clause, and return the builder for the parent constraint clause.
1313          * 
1314          * @return the constraint builder that was used to create this parenthetical constraint clause builder; never null
1315          * @throws IllegalStateException if there was not an {@link #openParen() open parenthesis} to close
1316          */
1317         public ConstraintBuilder closeParen() {
1318             if (parent == null) {
1319                 throw new IllegalStateException(GraphI18n.unexpectedClosingParenthesis.text());
1320             }
1321             buildLogicalConstraint();
1322             parent.implicitParentheses = false;
1323             return parent.setConstraint(constraint);
1324         }
1325 
1326         /**
1327          * Signal that the previous constraint clause be AND-ed together with another constraint clause that will be defined
1328          * immediately after this method call.
1329          * 
1330          * @return the constraint builder for the remaining constraint clause; never null
1331          */
1332         public ConstraintBuilder and() {
1333             buildLogicalConstraint();
1334             left = constraint;
1335             constraint = null;
1336             and = true;
1337             return this;
1338         }
1339 
1340         /**
1341          * Signal that the previous constraint clause be OR-ed together with another constraint clause that will be defined
1342          * immediately after this method call.
1343          * 
1344          * @return the constraint builder for the remaining constraint clause; never null
1345          */
1346         public ConstraintBuilder or() {
1347             buildLogicalConstraint();
1348             left = constraint;
1349             constraint = null;
1350             and = false;
1351             return this;
1352         }
1353 
1354         /**
1355          * Signal that the next constraint clause (defined immediately after this method) should be negated.
1356          * 
1357          * @return the constraint builder for the constraint clause that is to be negated; never null
1358          */
1359         public ConstraintBuilder not() {
1360             negateConstraint = true;
1361             return this;
1362         }
1363 
1364         protected ConstraintBuilder buildLogicalConstraint() {
1365             if (negateConstraint && constraint != null) {
1366                 constraint = new Not(constraint);
1367                 negateConstraint = false;
1368             }
1369             if (left != null && constraint != null) {
1370                 if (and) {
1371                     // If the left constraint is an OR, we need to rearrange things since AND is higher precedence ...
1372                     if (left instanceof Or && implicitParentheses) {
1373                         Or previous = (Or)left;
1374                         constraint = new Or(previous.left(), new And(previous.right(), constraint));
1375                     } else {
1376                         constraint = new And(left, constraint);
1377                     }
1378                 } else {
1379                     constraint = new Or(left, constraint);
1380                 }
1381                 left = null;
1382             }
1383             return this;
1384         }
1385 
1386         /**
1387          * Define a constraint clause that the node within the named table is the same node as that appearing at the supplied
1388          * path.
1389          * 
1390          * @param table the name of the table; may not be null and must refer to a valid name or alias of a table appearing in the
1391          *        FROM clause
1392          * @param asNodeAtPath the path to the node
1393          * @return the constraint builder that was used to create this clause; never null
1394          */
1395         public ConstraintBuilder isSameNode( String table,
1396                                              String asNodeAtPath ) {
1397             return setConstraint(new SameNode(selector(table), asNodeAtPath));
1398         }
1399 
1400         /**
1401          * Define a constraint clause that the node within the named table is the child of the node at the supplied path.
1402          * 
1403          * @param childTable the name of the table; may not be null and must refer to a valid name or alias of a table appearing
1404          *        in the FROM clause
1405          * @param parentPath the path to the parent node
1406          * @return the constraint builder that was used to create this clause; never null
1407          */
1408         public ConstraintBuilder isChild( String childTable,
1409                                           String parentPath ) {
1410             return setConstraint(new ChildNode(selector(childTable), parentPath));
1411         }
1412 
1413         /**
1414          * Define a constraint clause that the node within the named table is a descendant of the node at the supplied path.
1415          * 
1416          * @param descendantTable the name of the table; may not be null and must refer to a valid name or alias of a table
1417          *        appearing in the FROM clause
1418          * @param ancestorPath the path to the ancestor node
1419          * @return the constraint builder that was used to create this clause; never null
1420          */
1421         public ConstraintBuilder isBelowPath( String descendantTable,
1422                                               String ancestorPath ) {
1423             return setConstraint(new DescendantNode(selector(descendantTable), ancestorPath));
1424         }
1425 
1426         /**
1427          * Define a constraint clause that the node within the named table has at least one value for the named property.
1428          * 
1429          * @param table the name of the table; may not be null and must refer to a valid name or alias of a table appearing in the
1430          *        FROM clause
1431          * @param propertyName the name of the property
1432          * @return the constraint builder that was used to create this clause; never null
1433          */
1434         public ConstraintBuilder hasProperty( String table,
1435                                               String propertyName ) {
1436             return setConstraint(new PropertyExistence(selector(table), propertyName));
1437         }
1438 
1439         /**
1440          * Define a constraint clause that the node within the named table have at least one property that satisfies the full-text
1441          * search expression.
1442          * 
1443          * @param table the name of the table; may not be null and must refer to a valid name or alias of a table appearing in the
1444          *        FROM clause
1445          * @param searchExpression the full-text search expression
1446          * @return the constraint builder that was used to create this clause; never null
1447          */
1448         public ConstraintBuilder search( String table,
1449                                          String searchExpression ) {
1450             return setConstraint(new FullTextSearch(selector(table), searchExpression));
1451         }
1452 
1453         /**
1454          * Define a constraint clause that the node within the named table have a value for the named property that satisfies the
1455          * full-text search expression.
1456          * 
1457          * @param table the name of the table; may not be null and must refer to a valid name or alias of a table appearing in the
1458          *        FROM clause
1459          * @param propertyName the name of the property to be searched
1460          * @param searchExpression the full-text search expression
1461          * @return the constraint builder that was used to create this clause; never null
1462          */
1463         public ConstraintBuilder search( String table,
1464                                          String propertyName,
1465                                          String searchExpression ) {
1466             return setConstraint(new FullTextSearch(selector(table), propertyName, searchExpression));
1467         }
1468 
1469         protected ComparisonBuilder comparisonBuilder( DynamicOperand operand ) {
1470             return new ComparisonBuilder(this, operand);
1471         }
1472 
1473         /**
1474          * {@inheritDoc}
1475          * 
1476          * @see org.modeshape.graph.query.QueryBuilder.DynamicOperandBuilder#length(java.lang.String, java.lang.String)
1477          */
1478         public ComparisonBuilder length( String table,
1479                                          String property ) {
1480             return comparisonBuilder(new Length(new PropertyValue(selector(table), property)));
1481         }
1482 
1483         /**
1484          * {@inheritDoc}
1485          * 
1486          * @see org.modeshape.graph.query.QueryBuilder.DynamicOperandBuilder#propertyValue(String, String)
1487          */
1488         public ComparisonBuilder propertyValue( String table,
1489                                                 String property ) {
1490             return comparisonBuilder(new PropertyValue(selector(table), property));
1491         }
1492 
1493         /**
1494          * {@inheritDoc}
1495          * 
1496          * @see org.modeshape.graph.query.QueryBuilder.DynamicOperandBuilder#strongReferenceValue(java.lang.String)
1497          */
1498         public ComparisonBuilder strongReferenceValue( String table ) {
1499             return comparisonBuilder(new ReferenceValue(selector(table), null, false));
1500         }
1501 
1502         /**
1503          * {@inheritDoc}
1504          * 
1505          * @see org.modeshape.graph.query.QueryBuilder.DynamicOperandBuilder#referenceValue(java.lang.String)
1506          */
1507         public ComparisonBuilder referenceValue( String table ) {
1508             return comparisonBuilder(new ReferenceValue(selector(table)));
1509         }
1510 
1511         /**
1512          * {@inheritDoc}
1513          * 
1514          * @see org.modeshape.graph.query.QueryBuilder.DynamicOperandBuilder#referenceValue(java.lang.String, java.lang.String)
1515          */
1516         public ComparisonBuilder referenceValue( String table,
1517                                                  String property ) {
1518             return comparisonBuilder(new ReferenceValue(selector(table), property));
1519         }
1520 
1521         /**
1522          * {@inheritDoc}
1523          * 
1524          * @see org.modeshape.graph.query.QueryBuilder.DynamicOperandBuilder#fullTextSearchScore(String)
1525          */
1526         public ComparisonBuilder fullTextSearchScore( String table ) {
1527             return comparisonBuilder(new FullTextSearchScore(selector(table)));
1528         }
1529 
1530         /**
1531          * {@inheritDoc}
1532          * 
1533          * @see org.modeshape.graph.query.QueryBuilder.DynamicOperandBuilder#depth(java.lang.String)
1534          */
1535         public ComparisonBuilder depth( String table ) {
1536             return comparisonBuilder(new NodeDepth(selector(table)));
1537         }
1538 
1539         /**
1540          * {@inheritDoc}
1541          * 
1542          * @see org.modeshape.graph.query.QueryBuilder.DynamicOperandBuilder#path(java.lang.String)
1543          */
1544         public ComparisonBuilder path( String table ) {
1545             return comparisonBuilder(new NodePath(selector(table)));
1546         }
1547 
1548         /**
1549          * {@inheritDoc}
1550          * 
1551          * @see org.modeshape.graph.query.QueryBuilder.DynamicOperandBuilder#nodeLocalName(String)
1552          */
1553         public ComparisonBuilder nodeLocalName( String table ) {
1554             return comparisonBuilder(new NodeLocalName(selector(table)));
1555         }
1556 
1557         /**
1558          * {@inheritDoc}
1559          * 
1560          * @see org.modeshape.graph.query.QueryBuilder.DynamicOperandBuilder#nodeName(String)
1561          */
1562         public ComparisonBuilder nodeName( String table ) {
1563             return comparisonBuilder(new NodeName(selector(table)));
1564         }
1565 
1566         /**
1567          * {@inheritDoc}
1568          * 
1569          * @see org.modeshape.graph.query.QueryBuilder.DynamicOperandBuilder#upperCaseOf()
1570          */
1571         public DynamicOperandBuilder upperCaseOf() {
1572             return new UpperCaser(this);
1573         }
1574 
1575         /**
1576          * {@inheritDoc}
1577          * 
1578          * @see org.modeshape.graph.query.QueryBuilder.DynamicOperandBuilder#lowerCaseOf()
1579          */
1580         public DynamicOperandBuilder lowerCaseOf() {
1581             return new LowerCaser(this);
1582         }
1583 
1584         protected ConstraintBuilder setConstraint( Constraint constraint ) {
1585             if (this.constraint != null && this.left == null) {
1586                 and();
1587             }
1588             this.constraint = constraint;
1589             return buildLogicalConstraint();
1590         }
1591     }
1592 
1593     /**
1594      * A specialized form of the {@link ConstraintBuilder} that always wraps the generated constraint in a {@link UpperCase}
1595      * instance.
1596      */
1597     protected class UpperCaser extends ConstraintBuilder {
1598         private final ConstraintBuilder delegate;
1599 
1600         protected UpperCaser( ConstraintBuilder delegate ) {
1601             super(null);
1602             this.delegate = delegate;
1603         }
1604 
1605         @Override
1606         protected ConstraintBuilder setConstraint( Constraint constraint ) {
1607             Comparison comparison = (Comparison)constraint;
1608             return delegate.setConstraint(new Comparison(new UpperCase(comparison.operand1()), comparison.operator(),
1609                                                          comparison.operand2()));
1610         }
1611     }
1612 
1613     /**
1614      * A specialized form of the {@link ConstraintBuilder} that always wraps the generated constraint in a {@link LowerCase}
1615      * instance.
1616      */
1617     protected class LowerCaser extends ConstraintBuilder {
1618         private final ConstraintBuilder delegate;
1619 
1620         protected LowerCaser( ConstraintBuilder delegate ) {
1621             super(null);
1622             this.delegate = delegate;
1623         }
1624 
1625         @Override
1626         protected ConstraintBuilder setConstraint( Constraint constraint ) {
1627             Comparison comparison = (Comparison)constraint;
1628             return delegate.setConstraint(new Comparison(new LowerCase(comparison.operand1()), comparison.operator(),
1629                                                          comparison.operand2()));
1630         }
1631     }
1632 
1633     public abstract class CastAs<ReturnType> {
1634         protected final Object value;
1635 
1636         protected CastAs( Object value ) {
1637             this.value = value;
1638         }
1639 
1640         /**
1641          * Define the right-hand side literal value cast as the specified type.
1642          * 
1643          * @param type the property type; may not be null
1644          * @return the constraint builder; never null
1645          */
1646         public abstract ReturnType as( String type );
1647 
1648         /**
1649          * Define the right-hand side literal value cast as a {@link PropertyType#STRING}.
1650          * 
1651          * @return the constraint builder; never null
1652          */
1653         public ReturnType asString() {
1654             return as(typeSystem.getStringFactory().getTypeName());
1655         }
1656 
1657         /**
1658          * Define the right-hand side literal value cast as a {@link PropertyType#BOOLEAN}.
1659          * 
1660          * @return the constraint builder; never null
1661          */
1662         public ReturnType asBoolean() {
1663             return as(typeSystem.getBooleanFactory().getTypeName());
1664         }
1665 
1666         /**
1667          * Define the right-hand side literal value cast as a {@link PropertyType#LONG}.
1668          * 
1669          * @return the constraint builder; never null
1670          */
1671         public ReturnType asLong() {
1672             return as(typeSystem.getLongFactory().getTypeName());
1673         }
1674 
1675         /**
1676          * Define the right-hand side literal value cast as a {@link PropertyType#DOUBLE}.
1677          * 
1678          * @return the constraint builder; never null
1679          */
1680         public ReturnType asDouble() {
1681             return as(typeSystem.getDoubleFactory().getTypeName());
1682         }
1683 
1684         /**
1685          * Define the right-hand side literal value cast as a {@link PropertyType#DATE}.
1686          * 
1687          * @return the constraint builder; never null
1688          */
1689         public ReturnType asDate() {
1690             return as(typeSystem.getDateTimeFactory().getTypeName());
1691         }
1692 
1693         /**
1694          * Define the right-hand side literal value cast as a {@link PropertyType#PATH}.
1695          * 
1696          * @return the constraint builder; never null
1697          */
1698         public ReturnType asPath() {
1699             return as(typeSystem.getPathFactory().getTypeName());
1700         }
1701     }
1702 
1703     public class CastAsRightHandSide extends CastAs<ConstraintBuilder> {
1704         private final RightHandSide rhs;
1705 
1706         protected CastAsRightHandSide( RightHandSide rhs,
1707                                        Object value ) {
1708             super(value);
1709             this.rhs = rhs;
1710         }
1711 
1712         /**
1713          * Define the right-hand side literal value cast as the specified type.
1714          * 
1715          * @param type the property type; may not be null
1716          * @return the constraint builder; never null
1717          */
1718         @Override
1719         public ConstraintBuilder as( String type ) {
1720             return rhs.comparisonBuilder.is(rhs.operator, typeSystem.getTypeFactory(type).create(value));
1721         }
1722     }
1723 
1724     public class CastAsUpperBoundary extends CastAs<ConstraintBuilder> {
1725         private final UpperBoundary upperBoundary;
1726 
1727         protected CastAsUpperBoundary( UpperBoundary upperBoundary,
1728                                        Object value ) {
1729             super(value);
1730             this.upperBoundary = upperBoundary;
1731         }
1732 
1733         /**
1734          * Define the right-hand side literal value cast as the specified type.
1735          * 
1736          * @param type the property type; may not be null
1737          * @return the constraint builder; never null
1738          */
1739         @Override
1740         public ConstraintBuilder as( String type ) {
1741             return upperBoundary.comparisonBuilder.isBetween(upperBoundary.lowerBound, typeSystem.getTypeFactory(type)
1742                                                                                                  .create(value));
1743         }
1744     }
1745 
1746     public class CastAsLowerBoundary extends CastAs<AndBuilder<UpperBoundary>> {
1747         private final ComparisonBuilder builder;
1748 
1749         protected CastAsLowerBoundary( ComparisonBuilder builder,
1750                                        Object value ) {
1751             super(value);
1752             this.builder = builder;
1753         }
1754 
1755         /**
1756          * Define the left-hand side literal value cast as the specified type.
1757          * 
1758          * @param type the property type; may not be null
1759          * @return the builder to complete the constraint; never null
1760          */
1761         @Override
1762         public AndBuilder<UpperBoundary> as( String type ) {
1763             Object literal = typeSystem.getTypeFactory(type).create(value);
1764             return new AndBuilder<UpperBoundary>(new UpperBoundary(builder, new Literal(literal)));
1765         }
1766     }
1767 
1768     public class RightHandSide {
1769         protected final Operator operator;
1770         protected final ComparisonBuilder comparisonBuilder;
1771 
1772         protected RightHandSide( ComparisonBuilder comparisonBuilder,
1773                                  Operator operator ) {
1774             this.operator = operator;
1775             this.comparisonBuilder = comparisonBuilder;
1776         }
1777 
1778         /**
1779          * Define the right-hand side of a comparison.
1780          * 
1781          * @param literal the literal value;
1782          * @return the constraint builder; never null
1783          */
1784         public ConstraintBuilder literal( String literal ) {
1785             return comparisonBuilder.is(operator, literal);
1786         }
1787 
1788         /**
1789          * Define the right-hand side of a comparison.
1790          * 
1791          * @param literal the literal value;
1792          * @return the constraint builder; never null
1793          */
1794         public ConstraintBuilder literal( int literal ) {
1795             return comparisonBuilder.is(operator, literal);
1796         }
1797 
1798         /**
1799          * Define the right-hand side of a comparison.
1800          * 
1801          * @param literal the literal value;
1802          * @return the constraint builder; never null
1803          */
1804         public ConstraintBuilder literal( long literal ) {
1805             return comparisonBuilder.is(operator, literal);
1806         }
1807 
1808         /**
1809          * Define the right-hand side of a comparison.
1810          * 
1811          * @param literal the literal value;
1812          * @return the constraint builder; never null
1813          */
1814         public ConstraintBuilder literal( float literal ) {
1815             return comparisonBuilder.is(operator, literal);
1816         }
1817 
1818         /**
1819          * Define the right-hand side of a comparison.
1820          * 
1821          * @param literal the literal value;
1822          * @return the constraint builder; never null
1823          */
1824         public ConstraintBuilder literal( double literal ) {
1825             return comparisonBuilder.is(operator, literal);
1826         }
1827 
1828         /**
1829          * Define the right-hand side of a comparison.
1830          * 
1831          * @param literal the literal value;
1832          * @return the constraint builder; never null
1833          */
1834         public ConstraintBuilder literal( DateTime literal ) {
1835             return comparisonBuilder.is(operator, literal.toUtcTimeZone());
1836         }
1837 
1838         /**
1839          * Define the right-hand side of a comparison.
1840          * 
1841          * @param literal the literal value;
1842          * @return the constraint builder; never null
1843          */
1844         public ConstraintBuilder literal( Path literal ) {
1845             return comparisonBuilder.is(operator, literal);
1846         }
1847 
1848         /**
1849          * Define the right-hand side of a comparison.
1850          * 
1851          * @param literal the literal value;
1852          * @return the constraint builder; never null
1853          */
1854         public ConstraintBuilder literal( Name literal ) {
1855             return comparisonBuilder.is(operator, literal);
1856         }
1857 
1858         /**
1859          * Define the right-hand side of a comparison.
1860          * 
1861          * @param literal the literal value;
1862          * @return the constraint builder; never null
1863          */
1864         public ConstraintBuilder literal( URI literal ) {
1865             return comparisonBuilder.is(operator, literal);
1866         }
1867 
1868         /**
1869          * Define the right-hand side of a comparison.
1870          * 
1871          * @param literal the literal value;
1872          * @return the constraint builder; never null
1873          */
1874         public ConstraintBuilder literal( UUID literal ) {
1875             return comparisonBuilder.is(operator, literal);
1876         }
1877 
1878         /**
1879          * Define the right-hand side of a comparison.
1880          * 
1881          * @param literal the literal value;
1882          * @return the constraint builder; never null
1883          */
1884         public ConstraintBuilder literal( Binary literal ) {
1885             return comparisonBuilder.is(operator, literal);
1886         }
1887 
1888         /**
1889          * Define the right-hand side of a comparison.
1890          * 
1891          * @param literal the literal value;
1892          * @return the constraint builder; never null
1893          */
1894         public ConstraintBuilder literal( BigDecimal literal ) {
1895             return comparisonBuilder.is(operator, literal);
1896         }
1897 
1898         /**
1899          * Define the right-hand side of a comparison.
1900          * 
1901          * @param literal the literal value;
1902          * @return the constraint builder; never null
1903          */
1904         public ConstraintBuilder literal( boolean literal ) {
1905             return comparisonBuilder.is(operator, literal);
1906         }
1907 
1908         /**
1909          * Define the right-hand side of a comparison.
1910          * 
1911          * @param variableName the name of the variable
1912          * @return the constraint builder; never null
1913          */
1914         public ConstraintBuilder variable( String variableName ) {
1915             return comparisonBuilder.is(operator, variableName);
1916         }
1917 
1918         /**
1919          * Define the right-hand side of a comparison.
1920          * 
1921          * @param literal the literal value that is to be cast
1922          * @return the constraint builder; never null
1923          */
1924         public CastAs<ConstraintBuilder> cast( int literal ) {
1925             return new CastAsRightHandSide(this, literal);
1926         }
1927 
1928         /**
1929          * Define the right-hand side of a comparison.
1930          * 
1931          * @param literal the literal value that is to be cast
1932          * @return the constraint builder; never null
1933          */
1934         public CastAs<ConstraintBuilder> cast( String literal ) {
1935             return new CastAsRightHandSide(this, literal);
1936         }
1937 
1938         /**
1939          * Define the right-hand side of a comparison.
1940          * 
1941          * @param literal the literal value that is to be cast
1942          * @return the constraint builder; never null
1943          */
1944         public CastAs<ConstraintBuilder> cast( boolean literal ) {
1945             return new CastAsRightHandSide(this, literal);
1946         }
1947 
1948         /**
1949          * Define the right-hand side of a comparison.
1950          * 
1951          * @param literal the literal value that is to be cast
1952          * @return the constraint builder; never null
1953          */
1954         public CastAs<ConstraintBuilder> cast( long literal ) {
1955             return new CastAsRightHandSide(this, literal);
1956         }
1957 
1958         /**
1959          * Define the right-hand side of a comparison.
1960          * 
1961          * @param literal the literal value that is to be cast
1962          * @return the constraint builder; never null
1963          */
1964         public CastAs<ConstraintBuilder> cast( double literal ) {
1965             return new CastAsRightHandSide(this, literal);
1966         }
1967 
1968         /**
1969          * Define the right-hand side of a comparison.
1970          * 
1971          * @param literal the literal value that is to be cast
1972          * @return the constraint builder; never null
1973          */
1974         public CastAs<ConstraintBuilder> cast( BigDecimal literal ) {
1975             return new CastAsRightHandSide(this, literal);
1976         }
1977 
1978         /**
1979          * Define the right-hand side of a comparison.
1980          * 
1981          * @param literal the literal value that is to be cast
1982          * @return the constraint builder; never null
1983          */
1984         public CastAs<ConstraintBuilder> cast( DateTime literal ) {
1985             return new CastAsRightHandSide(this, literal.toUtcTimeZone());
1986         }
1987 
1988         /**
1989          * Define the right-hand side of a comparison.
1990          * 
1991          * @param literal the literal value that is to be cast
1992          * @return the constraint builder; never null
1993          */
1994         public CastAs<ConstraintBuilder> cast( Name literal ) {
1995             return new CastAsRightHandSide(this, literal);
1996         }
1997 
1998         /**
1999          * Define the right-hand side of a comparison.
2000          * 
2001          * @param literal the literal value that is to be cast
2002          * @return the constraint builder; never null
2003          */
2004         public CastAs<ConstraintBuilder> cast( Path literal ) {
2005             return new CastAsRightHandSide(this, literal);
2006         }
2007 
2008         /**
2009          * Define the right-hand side of a comparison.
2010          * 
2011          * @param literal the literal value that is to be cast
2012          * @return the constraint builder; never null
2013          */
2014         public CastAs<ConstraintBuilder> cast( UUID literal ) {
2015             return new CastAsRightHandSide(this, literal);
2016         }
2017 
2018         /**
2019          * Define the right-hand side of a comparison.
2020          * 
2021          * @param literal the literal value that is to be cast
2022          * @return the constraint builder; never null
2023          */
2024         public CastAs<ConstraintBuilder> cast( URI literal ) {
2025             return new CastAsRightHandSide(this, literal);
2026         }
2027     }
2028 
2029     public class UpperBoundary {
2030         protected final StaticOperand lowerBound;
2031         protected final ComparisonBuilder comparisonBuilder;
2032 
2033         protected UpperBoundary( ComparisonBuilder comparisonBuilder,
2034                                  StaticOperand lowerBound ) {
2035             this.lowerBound = lowerBound;
2036             this.comparisonBuilder = comparisonBuilder;
2037         }
2038 
2039         /**
2040          * Define the upper boundary value of a range.
2041          * 
2042          * @param literal the literal value;
2043          * @return the constraint builder; never null
2044          */
2045         public ConstraintBuilder literal( String literal ) {
2046             return comparisonBuilder.isBetween(lowerBound, literal);
2047         }
2048 
2049         /**
2050          * Define the upper boundary value of a range.
2051          * 
2052          * @param literal the literal value;
2053          * @return the constraint builder; never null
2054          */
2055         public ConstraintBuilder literal( int literal ) {
2056             return comparisonBuilder.isBetween(lowerBound, literal);
2057         }
2058 
2059         /**
2060          * Define the upper boundary value of a range.
2061          * 
2062          * @param literal the literal value;
2063          * @return the constraint builder; never null
2064          */
2065         public ConstraintBuilder literal( long literal ) {
2066             return comparisonBuilder.isBetween(lowerBound, literal);
2067         }
2068 
2069         /**
2070          * Define the upper boundary value of a range.
2071          * 
2072          * @param literal the literal value;
2073          * @return the constraint builder; never null
2074          */
2075         public ConstraintBuilder literal( float literal ) {
2076             return comparisonBuilder.isBetween(lowerBound, literal);
2077         }
2078 
2079         /**
2080          * Define the upper boundary value of a range.
2081          * 
2082          * @param literal the literal value;
2083          * @return the constraint builder; never null
2084          */
2085         public ConstraintBuilder literal( double literal ) {
2086             return comparisonBuilder.isBetween(lowerBound, literal);
2087         }
2088 
2089         /**
2090          * Define the upper boundary value of a range.
2091          * 
2092          * @param literal the literal value;
2093          * @return the constraint builder; never null
2094          */
2095         public ConstraintBuilder literal( DateTime literal ) {
2096             return comparisonBuilder.isBetween(lowerBound, literal);
2097         }
2098 
2099         /**
2100          * Define the upper boundary value of a range.
2101          * 
2102          * @param literal the literal value;
2103          * @return the constraint builder; never null
2104          */
2105         public ConstraintBuilder literal( Path literal ) {
2106             return comparisonBuilder.isBetween(lowerBound, literal);
2107         }
2108 
2109         /**
2110          * Define the upper boundary value of a range.
2111          * 
2112          * @param literal the literal value;
2113          * @return the constraint builder; never null
2114          */
2115         public ConstraintBuilder literal( Name literal ) {
2116             return comparisonBuilder.isBetween(lowerBound, literal);
2117         }
2118 
2119         /**
2120          * Define the upper boundary value of a range.
2121          * 
2122          * @param literal the literal value;
2123          * @return the constraint builder; never null
2124          */
2125         public ConstraintBuilder literal( URI literal ) {
2126             return comparisonBuilder.isBetween(lowerBound, literal);
2127         }
2128 
2129         /**
2130          * Define the upper boundary value of a range.
2131          * 
2132          * @param literal the literal value;
2133          * @return the constraint builder; never null
2134          */
2135         public ConstraintBuilder literal( UUID literal ) {
2136             return comparisonBuilder.isBetween(lowerBound, literal);
2137         }
2138 
2139         /**
2140          * Define the upper boundary value of a range.
2141          * 
2142          * @param literal the literal value;
2143          * @return the constraint builder; never null
2144          */
2145         public ConstraintBuilder literal( Binary literal ) {
2146             return comparisonBuilder.isBetween(lowerBound, literal);
2147         }
2148 
2149         /**
2150          * Define the upper boundary value of a range.
2151          * 
2152          * @param literal the literal value;
2153          * @return the constraint builder; never null
2154          */
2155         public ConstraintBuilder literal( BigDecimal literal ) {
2156             return comparisonBuilder.isBetween(lowerBound, literal);
2157         }
2158 
2159         /**
2160          * Define the upper boundary value of a range.
2161          * 
2162          * @param literal the literal value;
2163          * @return the constraint builder; never null
2164          */
2165         public ConstraintBuilder literal( boolean literal ) {
2166             return comparisonBuilder.isBetween(lowerBound, literal);
2167         }
2168 
2169         /**
2170          * Define the upper boundary value of a range.
2171          * 
2172          * @param variableName the name of the variable
2173          * @return the constraint builder; never null
2174          */
2175         public ConstraintBuilder variable( String variableName ) {
2176             return comparisonBuilder.constraintBuilder.setConstraint(new Between(comparisonBuilder.left, lowerBound,
2177                                                                                  new BindVariableName(variableName)));
2178         }
2179 
2180         /**
2181          * Define the upper boundary value of a range.
2182          * 
2183          * @param literal the literal value that is to be cast
2184          * @return the constraint builder; never null
2185          */
2186         public CastAs<ConstraintBuilder> cast( int literal ) {
2187             return new CastAsUpperBoundary(this, literal);
2188         }
2189 
2190         /**
2191          * Define the upper boundary value of a range.
2192          * 
2193          * @param literal the literal value that is to be cast
2194          * @return the constraint builder; never null
2195          */
2196         public CastAs<ConstraintBuilder> cast( String literal ) {
2197             return new CastAsUpperBoundary(this, literal);
2198         }
2199 
2200         /**
2201          * Define the upper boundary value of a range.
2202          * 
2203          * @param literal the literal value that is to be cast
2204          * @return the constraint builder; never null
2205          */
2206         public CastAs<ConstraintBuilder> cast( boolean literal ) {
2207             return new CastAsUpperBoundary(this, literal);
2208         }
2209 
2210         /**
2211          * Define the upper boundary value of a range.
2212          * 
2213          * @param literal the literal value that is to be cast
2214          * @return the constraint builder; never null
2215          */
2216         public CastAs<ConstraintBuilder> cast( long literal ) {
2217             return new CastAsUpperBoundary(this, literal);
2218         }
2219 
2220         /**
2221          * Define the upper boundary value of a range.
2222          * 
2223          * @param literal the literal value that is to be cast
2224          * @return the constraint builder; never null
2225          */
2226         public CastAs<ConstraintBuilder> cast( double literal ) {
2227             return new CastAsUpperBoundary(this, literal);
2228         }
2229 
2230         /**
2231          * Define the upper boundary value of a range.
2232          * 
2233          * @param literal the literal value that is to be cast
2234          * @return the constraint builder; never null
2235          */
2236         public CastAs<ConstraintBuilder> cast( BigDecimal literal ) {
2237             return new CastAsUpperBoundary(this, literal);
2238         }
2239 
2240         /**
2241          * Define the upper boundary value of a range.
2242          * 
2243          * @param literal the literal value that is to be cast
2244          * @return the constraint builder; never null
2245          */
2246         public CastAs<ConstraintBuilder> cast( DateTime literal ) {
2247             return new CastAsUpperBoundary(this, literal.toUtcTimeZone());
2248         }
2249 
2250         /**
2251          * Define the upper boundary value of a range.
2252          * 
2253          * @param literal the literal value that is to be cast
2254          * @return the constraint builder; never null
2255          */
2256         public CastAs<ConstraintBuilder> cast( Name literal ) {
2257             return new CastAsUpperBoundary(this, literal);
2258         }
2259 
2260         /**
2261          * Define the upper boundary value of a range.
2262          * 
2263          * @param literal the literal value that is to be cast
2264          * @return the constraint builder; never null
2265          */
2266         public CastAs<ConstraintBuilder> cast( Path literal ) {
2267             return new CastAsUpperBoundary(this, literal);
2268         }
2269 
2270         /**
2271          * Define the upper boundary value of a range.
2272          * 
2273          * @param literal the literal value that is to be cast
2274          * @return the constraint builder; never null
2275          */
2276         public CastAs<ConstraintBuilder> cast( UUID literal ) {
2277             return new CastAsUpperBoundary(this, literal);
2278         }
2279 
2280         /**
2281          * Define the upper boundary value of a range.
2282          * 
2283          * @param literal the literal value that is to be cast
2284          * @return the constraint builder; never null
2285          */
2286         public CastAs<ConstraintBuilder> cast( URI literal ) {
2287             return new CastAsUpperBoundary(this, literal);
2288         }
2289     }
2290 
2291     public class LowerBoundary {
2292         protected final ComparisonBuilder comparisonBuilder;
2293 
2294         protected LowerBoundary( ComparisonBuilder comparisonBuilder ) {
2295             this.comparisonBuilder = comparisonBuilder;
2296         }
2297 
2298         /**
2299          * Define the lower boundary value of a range.
2300          * 
2301          * @param literal the literal value;
2302          * @return the constraint builder; never null
2303          */
2304         public AndBuilder<UpperBoundary> literal( String literal ) {
2305             return new AndBuilder<UpperBoundary>(new UpperBoundary(comparisonBuilder, new Literal(literal)));
2306         }
2307 
2308         /**
2309          * Define the lower boundary value of a range.
2310          * 
2311          * @param literal the literal value;
2312          * @return the constraint builder; never null
2313          */
2314         public AndBuilder<UpperBoundary> literal( int literal ) {
2315             return new AndBuilder<UpperBoundary>(new UpperBoundary(comparisonBuilder, new Literal(literal)));
2316         }
2317 
2318         /**
2319          * Define the lower boundary value of a range.
2320          * 
2321          * @param literal the literal value;
2322          * @return the constraint builder; never null
2323          */
2324         public AndBuilder<UpperBoundary> literal( long literal ) {
2325             return new AndBuilder<UpperBoundary>(new UpperBoundary(comparisonBuilder, new Literal(literal)));
2326         }
2327 
2328         /**
2329          * Define the lower boundary value of a range.
2330          * 
2331          * @param literal the literal value;
2332          * @return the constraint builder; never null
2333          */
2334         public AndBuilder<UpperBoundary> literal( float literal ) {
2335             return new AndBuilder<UpperBoundary>(new UpperBoundary(comparisonBuilder, new Literal(literal)));
2336         }
2337 
2338         /**
2339          * Define the lower boundary value of a range.
2340          * 
2341          * @param literal the literal value;
2342          * @return the constraint builder; never null
2343          */
2344         public AndBuilder<UpperBoundary> literal( double literal ) {
2345             return new AndBuilder<UpperBoundary>(new UpperBoundary(comparisonBuilder, new Literal(literal)));
2346         }
2347 
2348         /**
2349          * Define the lower boundary value of a range.
2350          * 
2351          * @param literal the literal value;
2352          * @return the constraint builder; never null
2353          */
2354         public AndBuilder<UpperBoundary> literal( DateTime literal ) {
2355             return new AndBuilder<UpperBoundary>(new UpperBoundary(comparisonBuilder, new Literal(literal)));
2356         }
2357 
2358         /**
2359          * Define the lower boundary value of a range.
2360          * 
2361          * @param literal the literal value;
2362          * @return the constraint builder; never null
2363          */
2364         public AndBuilder<UpperBoundary> literal( Path literal ) {
2365             return new AndBuilder<UpperBoundary>(new UpperBoundary(comparisonBuilder, new Literal(literal)));
2366         }
2367 
2368         /**
2369          * Define the lower boundary value of a range.
2370          * 
2371          * @param literal the literal value;
2372          * @return the constraint builder; never null
2373          */
2374         public AndBuilder<UpperBoundary> literal( Name literal ) {
2375             return new AndBuilder<UpperBoundary>(new UpperBoundary(comparisonBuilder, new Literal(literal)));
2376         }
2377 
2378         /**
2379          * Define the lower boundary value of a range.
2380          * 
2381          * @param literal the literal value;
2382          * @return the constraint builder; never null
2383          */
2384         public AndBuilder<UpperBoundary> literal( URI literal ) {
2385             return new AndBuilder<UpperBoundary>(new UpperBoundary(comparisonBuilder, new Literal(literal)));
2386         }
2387 
2388         /**
2389          * Define the lower boundary value of a range.
2390          * 
2391          * @param literal the literal value;
2392          * @return the constraint builder; never null
2393          */
2394         public AndBuilder<UpperBoundary> literal( UUID literal ) {
2395             return new AndBuilder<UpperBoundary>(new UpperBoundary(comparisonBuilder, new Literal(literal)));
2396         }
2397 
2398         /**
2399          * Define the lower boundary value of a range.
2400          * 
2401          * @param literal the literal value;
2402          * @return the constraint builder; never null
2403          */
2404         public AndBuilder<UpperBoundary> literal( Binary literal ) {
2405             return new AndBuilder<UpperBoundary>(new UpperBoundary(comparisonBuilder, new Literal(literal)));
2406         }
2407 
2408         /**
2409          * Define the lower boundary value of a range.
2410          * 
2411          * @param literal the literal value;
2412          * @return the constraint builder; never null
2413          */
2414         public AndBuilder<UpperBoundary> literal( BigDecimal literal ) {
2415             return new AndBuilder<UpperBoundary>(new UpperBoundary(comparisonBuilder, new Literal(literal)));
2416         }
2417 
2418         /**
2419          * Define the lower boundary value of a range.
2420          * 
2421          * @param literal the literal value;
2422          * @return the constraint builder; never null
2423          */
2424         public AndBuilder<UpperBoundary> literal( boolean literal ) {
2425             return new AndBuilder<UpperBoundary>(new UpperBoundary(comparisonBuilder, new Literal(literal)));
2426         }
2427 
2428         /**
2429          * Define the lower boundary value of a range.
2430          * 
2431          * @param variableName the name of the variable
2432          * @return the constraint builder; never null
2433          */
2434         public AndBuilder<UpperBoundary> variable( String variableName ) {
2435             return new AndBuilder<UpperBoundary>(new UpperBoundary(comparisonBuilder, new BindVariableName(variableName)));
2436         }
2437 
2438         /**
2439          * Define the lower boundary value of a range.
2440          * 
2441          * @param literal the literal value that is to be cast
2442          * @return the constraint builder; never null
2443          */
2444         public CastAs<AndBuilder<UpperBoundary>> cast( int literal ) {
2445             return new CastAsLowerBoundary(comparisonBuilder, literal);
2446         }
2447 
2448         /**
2449          * Define the lower boundary value of a range.
2450          * 
2451          * @param literal the literal value that is to be cast
2452          * @return the constraint builder; never null
2453          */
2454         public CastAs<AndBuilder<UpperBoundary>> cast( String literal ) {
2455             return new CastAsLowerBoundary(comparisonBuilder, literal);
2456         }
2457 
2458         /**
2459          * Define the lower boundary value of a range.
2460          * 
2461          * @param literal the literal value that is to be cast
2462          * @return the constraint builder; never null
2463          */
2464         public CastAs<AndBuilder<UpperBoundary>> cast( boolean literal ) {
2465             return new CastAsLowerBoundary(comparisonBuilder, literal);
2466         }
2467 
2468         /**
2469          * Define the lower boundary value of a range.
2470          * 
2471          * @param literal the literal value that is to be cast
2472          * @return the constraint builder; never null
2473          */
2474         public CastAs<AndBuilder<UpperBoundary>> cast( long literal ) {
2475             return new CastAsLowerBoundary(comparisonBuilder, literal);
2476         }
2477 
2478         /**
2479          * Define the lower boundary value of a range.
2480          * 
2481          * @param literal the literal value that is to be cast
2482          * @return the constraint builder; never null
2483          */
2484         public CastAs<AndBuilder<UpperBoundary>> cast( double literal ) {
2485             return new CastAsLowerBoundary(comparisonBuilder, literal);
2486         }
2487 
2488         /**
2489          * Define the lower boundary value of a range.
2490          * 
2491          * @param literal the literal value that is to be cast
2492          * @return the constraint builder; never null
2493          */
2494         public CastAs<AndBuilder<UpperBoundary>> cast( BigDecimal literal ) {
2495             return new CastAsLowerBoundary(comparisonBuilder, literal);
2496         }
2497 
2498         /**
2499          * Define the lower boundary value of a range.
2500          * 
2501          * @param literal the literal value that is to be cast
2502          * @return the constraint builder; never null
2503          */
2504         public CastAs<AndBuilder<UpperBoundary>> cast( DateTime literal ) {
2505             return new CastAsLowerBoundary(comparisonBuilder, literal);
2506         }
2507 
2508         /**
2509          * Define the lower boundary value of a range.
2510          * 
2511          * @param literal the literal value that is to be cast
2512          * @return the constraint builder; never null
2513          */
2514         public CastAs<AndBuilder<UpperBoundary>> cast( Name literal ) {
2515             return new CastAsLowerBoundary(comparisonBuilder, literal);
2516         }
2517 
2518         /**
2519          * Define the lower boundary value of a range.
2520          * 
2521          * @param literal the literal value that is to be cast
2522          * @return the constraint builder; never null
2523          */
2524         public CastAs<AndBuilder<UpperBoundary>> cast( Path literal ) {
2525             return new CastAsLowerBoundary(comparisonBuilder, literal);
2526         }
2527 
2528         /**
2529          * Define the lower boundary value of a range.
2530          * 
2531          * @param literal the literal value that is to be cast
2532          * @return the constraint builder; never null
2533          */
2534         public CastAs<AndBuilder<UpperBoundary>> cast( UUID literal ) {
2535             return new CastAsLowerBoundary(comparisonBuilder, literal);
2536         }
2537 
2538         /**
2539          * Define the lower boundary value of a range.
2540          * 
2541          * @param literal the literal value that is to be cast
2542          * @return the constraint builder; never null
2543          */
2544         public CastAs<AndBuilder<UpperBoundary>> cast( URI literal ) {
2545             return new CastAsLowerBoundary(comparisonBuilder, literal);
2546         }
2547     }
2548 
2549     public class ArithmeticBuilder {
2550         protected final ArithmeticBuilder parent;
2551         protected final ArithmeticOperator operator;
2552         protected DynamicOperand left;
2553         protected final ComparisonBuilder comparisonBuilder;
2554 
2555         protected ArithmeticBuilder( ArithmeticOperator operator,
2556                                      ComparisonBuilder comparisonBuilder,
2557                                      DynamicOperand left,
2558                                      ArithmeticBuilder parent ) {
2559             this.operator = operator;
2560             this.left = left;
2561             this.comparisonBuilder = comparisonBuilder;
2562             this.parent = parent; // may be null
2563         }
2564 
2565         /**
2566          * Constrains the nodes in the the supplied table such that they must have a property value whose length matches the
2567          * criteria.
2568          * 
2569          * @param table the name of the table; may not be null and must refer to a valid name or alias of a table appearing in the
2570          *        FROM clause
2571          * @param property the name of the property; may not be null and must refer to a valid property name
2572          * @return the interface for completing the value portion of the criteria specification; never null
2573          */
2574         public ComparisonBuilder length( String table,
2575                                          String property ) {
2576             return comparisonBuilder(new Length(new PropertyValue(selector(table), property)));
2577         }
2578 
2579         /**
2580          * Constrains the nodes in the the supplied table such that they must have a matching value for the named property.
2581          * 
2582          * @param table the name of the table; may not be null and must refer to a valid name or alias of a table appearing in the
2583          *        FROM clause
2584          * @param property the name of the property; may not be null and must refer to a valid property name
2585          * @return the interface for completing the value portion of the criteria specification; never null
2586          */
2587         public ComparisonBuilder propertyValue( String table,
2588                                                 String property ) {
2589             return comparisonBuilder(new PropertyValue(selector(table), property));
2590         }
2591 
2592         /**
2593          * Constrains the nodes in the the supplied table such that they must have a matching value for any of the node's
2594          * reference properties.
2595          * 
2596          * @param table the name of the table; may not be null and must refer to a valid name or alias of a table appearing in the
2597          *        FROM clause
2598          * @return the interface for completing the value portion of the criteria specification; never null
2599          */
2600         public ComparisonBuilder referenceValue( String table ) {
2601             return comparisonBuilder(new ReferenceValue(selector(table)));
2602         }
2603 
2604         /**
2605          * Constrains the nodes in the the supplied table such that they must have a matching value for the named property.
2606          * 
2607          * @param table the name of the table; may not be null and must refer to a valid name or alias of a table appearing in the
2608          *        FROM clause
2609          * @param property the name of the reference property; may be null if the constraint applies to all/any reference
2610          *        properties on the node
2611          * @return the interface for completing the value portion of the criteria specification; never null
2612          */
2613         public ComparisonBuilder referenceValue( String table,
2614                                                  String property ) {
2615             return comparisonBuilder(new ReferenceValue(selector(table), property));
2616         }
2617 
2618         /**
2619          * Constrains the nodes in the the supplied table such that they must satisfy the supplied full-text search on the nodes'
2620          * property values.
2621          * 
2622          * @param table the name of the table; may not be null and must refer to a valid name or alias of a table appearing in the
2623          *        FROM clause
2624          * @return the interface for completing the value portion of the criteria specification; never null
2625          */
2626         public ComparisonBuilder fullTextSearchScore( String table ) {
2627             return comparisonBuilder(new FullTextSearchScore(selector(table)));
2628         }
2629 
2630         /**
2631          * Constrains the nodes in the the supplied table based upon criteria on the node's depth.
2632          * 
2633          * @param table the name of the table; may not be null and must refer to a valid name or alias of a table appearing in the
2634          *        FROM clause
2635          * @return the interface for completing the value portion of the criteria specification; never null
2636          */
2637         public ComparisonBuilder depth( String table ) {
2638             return comparisonBuilder(new NodeDepth(selector(table)));
2639         }
2640 
2641         // /**
2642         // * Simulate the use of an open parenthesis in the constraint. The resulting builder should be used to define the
2643         // * constraint within the parenthesis, and should always be terminated with a {@link #closeParen()}.
2644         // *
2645         // * @return the constraint builder that should be used to define the portion of the constraint within the parenthesis;
2646         // * never null
2647         // * @see #closeParen()
2648         // */
2649         // public ArithmeticBuilder openParen() {
2650         // return new ArithmeticBuilder(operator, comparisonBuilder, left, this);
2651         // }
2652         //
2653         // /**
2654         // * Complete the specification of a constraint clause, and return the builder for the parent constraint clause.
2655         // *
2656         // * @return the constraint builder that was used to create this parenthetical constraint clause builder; never null
2657         // * @throws IllegalStateException if there was not an {@link #openParen() open parenthesis} to close
2658         // */
2659         // public ComparisonBuilder closeParen() {
2660         // if (parent == null) {
2661         // throw new IllegalStateException(GraphI18n.unexpectedClosingParenthesis.text());
2662         // }
2663         // buildLogicalConstraint();
2664         // return parent.setLeft(left).comparisonBuilder;
2665         // }
2666         // protected ArithmeticBuilder setLeft( DynamicOperand left ) {
2667         // this.left = left;
2668         // return this;
2669         // }
2670 
2671         protected ComparisonBuilder comparisonBuilder( DynamicOperand right ) {
2672             DynamicOperand leftOperand = null;
2673             // If the left operand is an arithmetic operand, then we need to check the operator precedence ...
2674             if (left instanceof ArithmeticOperand) {
2675                 ArithmeticOperand leftArith = (ArithmeticOperand)left;
2676                 ArithmeticOperator operator = leftArith.operator();
2677                 if (this.operator.precedes(operator)) {
2678                     // Need to do create an operand with leftArith.right and right
2679                     DynamicOperand inner = new ArithmeticOperand(leftArith.right(), this.operator, right);
2680                     leftOperand = new ArithmeticOperand(leftArith.left(), operator, inner);
2681                 } else {
2682                     // the left preceds this, so we can add the new operand on top ...
2683                     leftOperand = new ArithmeticOperand(leftArith, operator, right);
2684                 }
2685             } else {
2686                 // The left isn't an arith ...
2687                 leftOperand = new ArithmeticOperand(left, operator, right);
2688             }
2689             return new ComparisonBuilder(comparisonBuilder.constraintBuilder, leftOperand);
2690         }
2691     }
2692 
2693     /**
2694      * An interface used to set the right-hand side of a constraint.
2695      */
2696     public class ComparisonBuilder {
2697         protected final DynamicOperand left;
2698         protected final ConstraintBuilder constraintBuilder;
2699 
2700         protected ComparisonBuilder( ConstraintBuilder constraintBuilder,
2701                                      DynamicOperand left ) {
2702             this.left = left;
2703             this.constraintBuilder = constraintBuilder;
2704         }
2705 
2706         public ConstraintBuilder isIn( Object... literals ) {
2707             CheckArg.isNotNull(literals, "literals");
2708             Collection<StaticOperand> right = new ArrayList<StaticOperand>();
2709             for (Object literal : literals) {
2710                 right.add(literal instanceof Literal ? (Literal)literal : new Literal(literal));
2711             }
2712             return this.constraintBuilder.setConstraint(new SetCriteria(left, right));
2713         }
2714 
2715         public ConstraintBuilder isIn( Iterable<Object> literals ) {
2716             CheckArg.isNotNull(literals, "literals");
2717             Collection<StaticOperand> right = new ArrayList<StaticOperand>();
2718             for (Object literal : literals) {
2719                 right.add(literal instanceof Literal ? (Literal)literal : new Literal(literal));
2720             }
2721             return this.constraintBuilder.setConstraint(new SetCriteria(left, right));
2722         }
2723 
2724         /**
2725          * Create a comparison object based upon the addition of the previously-constructed {@link DynamicOperand} and the next
2726          * DynamicOperand to be created with the supplied builder.
2727          * 
2728          * @return the builder that should be used to create the right-hand-side of the operation; never null
2729          */
2730         public ArithmeticBuilder plus() {
2731             return new ArithmeticBuilder(ArithmeticOperator.ADD, this, left, null);
2732         }
2733 
2734         /**
2735          * Create a comparison object based upon the subtraction of the next {@link DynamicOperand} (created using the builder
2736          * returned from this method) from the the previously-constructed DynamicOperand to be created with the supplied builder.
2737          * 
2738          * @return the builder that should be used to create the right-hand-side of the operation; never null
2739          */
2740         public ArithmeticBuilder minus() {
2741             return new ArithmeticBuilder(ArithmeticOperator.SUBTRACT, this, left, null);
2742         }
2743 
2744         /**
2745          * Define the operator that will be used in the comparison, returning an interface that can be used to define the
2746          * right-hand-side of the comparison.
2747          * 
2748          * @param operator the operator; may not be null
2749          * @return the interface used to define the right-hand-side of the comparison
2750          */
2751         public RightHandSide is( Operator operator ) {
2752             CheckArg.isNotNull(operator, "operator");
2753             return new RightHandSide(this, operator);
2754         }
2755 
2756         /**
2757          * Use the 'equal to' operator in the comparison, returning an interface that can be used to define the right-hand-side of
2758          * the comparison.
2759          * 
2760          * @return the interface used to define the right-hand-side of the comparison
2761          */
2762         public RightHandSide isEqualTo() {
2763             return is(Operator.EQUAL_TO);
2764         }
2765 
2766         /**
2767          * Use the 'equal to' operator in the comparison, returning an interface that can be used to define the right-hand-side of
2768          * the comparison.
2769          * 
2770          * @return the interface used to define the right-hand-side of the comparison
2771          */
2772         public RightHandSide isNotEqualTo() {
2773             return is(Operator.NOT_EQUAL_TO);
2774         }
2775 
2776         /**
2777          * Use the 'equal to' operator in the comparison, returning an interface that can be used to define the right-hand-side of
2778          * the comparison.
2779          * 
2780          * @return the interface used to define the right-hand-side of the comparison
2781          */
2782         public RightHandSide isGreaterThan() {
2783             return is(Operator.GREATER_THAN);
2784         }
2785 
2786         /**
2787          * Use the 'equal to' operator in the comparison, returning an interface that can be used to define the right-hand-side of
2788          * the comparison.
2789          * 
2790          * @return the interface used to define the right-hand-side of the comparison
2791          */
2792         public RightHandSide isGreaterThanOrEqualTo() {
2793             return is(Operator.GREATER_THAN_OR_EQUAL_TO);
2794         }
2795 
2796         /**
2797          * Use the 'equal to' operator in the comparison, returning an interface that can be used to define the right-hand-side of
2798          * the comparison.
2799          * 
2800          * @return the interface used to define the right-hand-side of the comparison
2801          */
2802         public RightHandSide isLessThan() {
2803             return is(Operator.LESS_THAN);
2804         }
2805 
2806         /**
2807          * Use the 'equal to' operator in the comparison, returning an interface that can be used to define the right-hand-side of
2808          * the comparison.
2809          * 
2810          * @return the interface used to define the right-hand-side of the comparison
2811          */
2812         public RightHandSide isLessThanOrEqualTo() {
2813             return is(Operator.LESS_THAN_OR_EQUAL_TO);
2814         }
2815 
2816         /**
2817          * Use the 'equal to' operator in the comparison, returning an interface that can be used to define the right-hand-side of
2818          * the comparison.
2819          * 
2820          * @return the interface used to define the right-hand-side of the comparison
2821          */
2822         public RightHandSide isLike() {
2823             return is(Operator.LIKE);
2824         }
2825 
2826         /**
2827          * Define the right-hand-side of the constraint using the supplied operator.
2828          * 
2829          * @param operator the operator; may not be null
2830          * @param variableName the name of the variable
2831          * @return the builder used to create the constraint clause, ready to be used to create other constraints clauses or
2832          *         complete already-started clauses; never null
2833          */
2834         public ConstraintBuilder isVariable( Operator operator,
2835                                              String variableName ) {
2836             CheckArg.isNotNull(operator, "operator");
2837             return this.constraintBuilder.setConstraint(new Comparison(left, operator, new BindVariableName(variableName)));
2838         }
2839 
2840         /**
2841          * Define the right-hand-side of the constraint using the supplied operator.
2842          * 
2843          * @param operator the operator; may not be null
2844          * @param literal the literal value
2845          * @return the builder used to create the constraint clause, ready to be used to create other constraints clauses or
2846          *         complete already-started clauses; never null
2847          */
2848         public ConstraintBuilder is( Operator operator,
2849                                      Object literal ) {
2850             assert operator != null;
2851             Literal value = literal instanceof Literal ? (Literal)literal : new Literal(literal);
2852             return this.constraintBuilder.setConstraint(new Comparison(left, operator, value));
2853         }
2854 
2855         /**
2856          * Define the right-hand-side of the constraint using the supplied operator.
2857          * 
2858          * @param lowerBoundLiteral the literal value that represents the lower bound of the range (inclusive)
2859          * @param upperBoundLiteral the literal value that represents the upper bound of the range (inclusive)
2860          * @return the builder used to create the constraint clause, ready to be used to create other constraints clauses or
2861          *         complete already-started clauses; never null
2862          */
2863         public ConstraintBuilder isBetween( Object lowerBoundLiteral,
2864                                             Object upperBoundLiteral ) {
2865             assert lowerBoundLiteral != null;
2866             assert upperBoundLiteral != null;
2867             Literal lower = lowerBoundLiteral instanceof Literal ? (Literal)lowerBoundLiteral : new Literal(lowerBoundLiteral);
2868             Literal upper = upperBoundLiteral instanceof Literal ? (Literal)upperBoundLiteral : new Literal(upperBoundLiteral);
2869             return this.constraintBuilder.setConstraint(new Between(left, lower, upper));
2870         }
2871 
2872         /**
2873          * Define the right-hand-side of the constraint to be equivalent to the value of the supplied variable.
2874          * 
2875          * @param variableName the name of the variable
2876          * @return the builder used to create the constraint clause, ready to be used to create other constraints clauses or
2877          *         complete already-started clauses; never null
2878          */
2879         public ConstraintBuilder isEqualToVariable( String variableName ) {
2880             return isVariable(Operator.EQUAL_TO, variableName);
2881         }
2882 
2883         /**
2884          * Define the right-hand-side of the constraint to be greater than the value of the supplied variable.
2885          * 
2886          * @param variableName the name of the variable
2887          * @return the builder used to create the constraint clause, ready to be used to create other constraints clauses or
2888          *         complete already-started clauses; never null
2889          */
2890         public ConstraintBuilder isGreaterThanVariable( String variableName ) {
2891             return isVariable(Operator.GREATER_THAN, variableName);
2892         }
2893 
2894         /**
2895          * Define the right-hand-side of the constraint to be greater than or equal to the value of the supplied variable.
2896          * 
2897          * @param variableName the name of the variable
2898          * @return the builder used to create the constraint clause, ready to be used to create other constraints clauses or
2899          *         complete already-started clauses; never null
2900          */
2901         public ConstraintBuilder isGreaterThanOrEqualToVariable( String variableName ) {
2902             return isVariable(Operator.GREATER_THAN_OR_EQUAL_TO, variableName);
2903         }
2904 
2905         /**
2906          * Define the right-hand-side of the constraint to be less than the value of the supplied variable.
2907          * 
2908          * @param variableName the name of the variable
2909          * @return the builder used to create the constraint clause, ready to be used to create other constraints clauses or
2910          *         complete already-started clauses; never null
2911          */
2912         public ConstraintBuilder isLessThanVariable( String variableName ) {
2913             return isVariable(Operator.LESS_THAN, variableName);
2914         }
2915 
2916         /**
2917          * Define the right-hand-side of the constraint to be less than or equal to the value of the supplied variable.
2918          * 
2919          * @param variableName the name of the variable
2920          * @return the builder used to create the constraint clause, ready to be used to create other constraints clauses or
2921          *         complete already-started clauses; never null
2922          */
2923         public ConstraintBuilder isLessThanOrEqualToVariable( String variableName ) {
2924             return isVariable(Operator.LESS_THAN_OR_EQUAL_TO, variableName);
2925         }
2926 
2927         /**
2928          * Define the right-hand-side of the constraint to be LIKE the value of the supplied variable.
2929          * 
2930          * @param variableName the name of the variable
2931          * @return the builder used to create the constraint clause, ready to be used to create other constraints clauses or
2932          *         complete already-started clauses; never null
2933          */
2934         public ConstraintBuilder isLikeVariable( String variableName ) {
2935             return isVariable(Operator.LIKE, variableName);
2936         }
2937 
2938         /**
2939          * Define the right-hand-side of the constraint to be not equal to the value of the supplied variable.
2940          * 
2941          * @param variableName the name of the variable
2942          * @return the builder used to create the constraint clause, ready to be used to create other constraints clauses or
2943          *         complete already-started clauses; never null
2944          */
2945         public ConstraintBuilder isNotEqualToVariable( String variableName ) {
2946             return isVariable(Operator.NOT_EQUAL_TO, variableName);
2947         }
2948 
2949         /**
2950          * Define the right-hand-side of the constraint to be equivalent to the supplied literal value.
2951          * 
2952          * @param literal the literal value
2953          * @return the builder used to create the constraint clause, ready to be used to create other constraints clauses or
2954          *         complete already-started clauses; never null
2955          */
2956         public ConstraintBuilder isEqualTo( Object literal ) {
2957             return is(Operator.EQUAL_TO, literal);
2958         }
2959 
2960         /**
2961          * Define the right-hand-side of the constraint to be greater than the supplied literal value.
2962          * 
2963          * @param literal the literal value
2964          * @return the builder used to create the constraint clause, ready to be used to create other constraints clauses or
2965          *         complete already-started clauses; never null
2966          */
2967         public ConstraintBuilder isGreaterThan( Object literal ) {
2968             return is(Operator.GREATER_THAN, literal);
2969         }
2970 
2971         /**
2972          * Define the right-hand-side of the constraint to be greater than or equal to the supplied literal value.
2973          * 
2974          * @param literal the literal value
2975          * @return the builder used to create the constraint clause, ready to be used to create other constraints clauses or
2976          *         complete already-started clauses; never null
2977          */
2978         public ConstraintBuilder isGreaterThanOrEqualTo( Object literal ) {
2979             return is(Operator.GREATER_THAN_OR_EQUAL_TO, literal);
2980         }
2981 
2982         /**
2983          * Define the right-hand-side of the constraint to be less than the supplied literal value.
2984          * 
2985          * @param literal the literal value
2986          * @return the builder used to create the constraint clause, ready to be used to create other constraints clauses or
2987          *         complete already-started clauses; never null
2988          */
2989         public ConstraintBuilder isLessThan( Object literal ) {
2990             return is(Operator.LESS_THAN, literal);
2991         }
2992 
2993         /**
2994          * Define the right-hand-side of the constraint to be less than or equal to the supplied literal value.
2995          * 
2996          * @param literal the literal value
2997          * @return the builder used to create the constraint clause, ready to be used to create other constraints clauses or
2998          *         complete already-started clauses; never null
2999          */
3000         public ConstraintBuilder isLessThanOrEqualTo( Object literal ) {
3001             return is(Operator.LESS_THAN_OR_EQUAL_TO, literal);
3002         }
3003 
3004         /**
3005          * Define the right-hand-side of the constraint to be LIKE the supplied literal value.
3006          * 
3007          * @param literal the literal value
3008          * @return the builder used to create the constraint clause, ready to be used to create other constraints clauses or
3009          *         complete already-started clauses; never null
3010          */
3011         public ConstraintBuilder isLike( Object literal ) {
3012             return is(Operator.LIKE, literal);
3013         }
3014 
3015         /**
3016          * Define the right-hand-side of the constraint to be not equal to the supplied literal value.
3017          * 
3018          * @param literal the literal value
3019          * @return the builder used to create the constraint clause, ready to be used to create other constraints clauses or
3020          *         complete already-started clauses; never null
3021          */
3022         public ConstraintBuilder isNotEqualTo( Object literal ) {
3023             return is(Operator.NOT_EQUAL_TO, literal);
3024         }
3025 
3026         /**
3027          * Define the constraint as a range between a lower boundary and an upper boundary.
3028          * 
3029          * @return the interface used to specify the lower boundary boundary, the upper boundary, and which will return the
3030          *         builder interface; never null
3031          */
3032         public LowerBoundary isBetween() {
3033             return new LowerBoundary(this);
3034         }
3035     }
3036 
3037     public class AndBuilder<T> {
3038         private final T object;
3039 
3040         protected AndBuilder( T object ) {
3041             assert object != null;
3042             this.object = object;
3043         }
3044 
3045         /**
3046          * Return the component
3047          * 
3048          * @return the component; never null
3049          */
3050         public T and() {
3051             return this.object;
3052         }
3053     }
3054 }