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.getAlias() : selector.getName();
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).getName() : 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.getSelectorName().equals(oldName)) {
423                 columns.set(i, new Column(allNodes.getAliasOrName(), old.getPropertyName(), old.getColumnName()));
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).getName() : 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.getSelectorName().equals(oldName)) {
444                 columns.set(i, new Column(selector.getAliasOrName(), old.getPropertyName(), old.getColumnName()));
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.getLeft();
734                 QueryCommand right = setQuery.getRight();
735                 SetQuery exceptQuery = new SetQuery(right, Operation.EXCEPT, result, firstQueryAll);
736                 result = new SetQuery(left, setQuery.getOperation(), 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.getAliasOrName().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.getAliasOrName().equals(name)) notFound.set(false);
1070                 }
1071 
1072                 @Override
1073                 public void visit( NamedSelector selector ) {
1074                     if (notFound.get() && selector.getAliasOrName().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).getType() != 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.getRight(), type, rightSource, condition);
1145                 source = new Join(left.getLeft(), left.getType(), cross, left.getJoinCondition());
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 satisfy the supplied full-text search on the nodes'
1205          * property values.
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 fullTextSearchScore( String table );
1212 
1213         /**
1214          * Constrains the nodes in the the supplied table based upon criteria on the node's depth.
1215          * 
1216          * @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
1217          *        FROM clause
1218          * @return the interface for completing the value portion of the criteria specification; never null
1219          */
1220         public ComparisonBuilder depth( String table );
1221 
1222         /**
1223          * Constrains the nodes in the the supplied table based upon criteria on the node's path.
1224          * 
1225          * @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
1226          *        FROM clause
1227          * @return the interface for completing the value portion of the criteria specification; never null
1228          */
1229         public ComparisonBuilder path( String table );
1230 
1231         /**
1232          * Constrains the nodes in the the supplied table based upon criteria on the node's local name.
1233          * 
1234          * @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
1235          *        FROM clause
1236          * @return the interface for completing the value portion of the criteria specification; never null
1237          */
1238         public ComparisonBuilder nodeLocalName( String table );
1239 
1240         /**
1241          * Constrains the nodes in the the supplied table based upon criteria on the node's name.
1242          * 
1243          * @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
1244          *        FROM clause
1245          * @return the interface for completing the value portion of the criteria specification; never null
1246          */
1247         public ComparisonBuilder nodeName( String table );
1248 
1249         /**
1250          * Begin a constraint against the uppercase form of a dynamic operand.
1251          * 
1252          * @return the interface for completing the criteria specification; never null
1253          */
1254         public DynamicOperandBuilder upperCaseOf();
1255 
1256         /**
1257          * Begin a constraint against the lowercase form of a dynamic operand.
1258          * 
1259          * @return the interface for completing the criteria specification; never null
1260          */
1261         public DynamicOperandBuilder lowerCaseOf();
1262     }
1263 
1264     public class ConstraintBuilder implements DynamicOperandBuilder {
1265         private final ConstraintBuilder parent;
1266         /** Used for the current operations */
1267         private Constraint constraint;
1268         /** Set when a logical criteria is started */
1269         private Constraint left;
1270         private boolean and;
1271         private boolean negateConstraint;
1272 
1273         protected ConstraintBuilder( ConstraintBuilder parent ) {
1274             this.parent = parent;
1275         }
1276 
1277         /**
1278          * Complete this constraint specification.
1279          * 
1280          * @return the query builder, for method chaining purposes
1281          */
1282         public QueryBuilder end() {
1283             buildLogicalConstraint();
1284             QueryBuilder.this.constraint = constraint;
1285             return QueryBuilder.this;
1286         }
1287 
1288         /**
1289          * Simulate the use of an open parenthesis in the constraint. The resulting builder should be used to define the
1290          * constraint within the parenthesis, and should always be terminated with a {@link #closeParen()}.
1291          * 
1292          * @return the constraint builder that should be used to define the portion of the constraint within the parenthesis;
1293          *         never null
1294          * @see #closeParen()
1295          */
1296         public ConstraintBuilder openParen() {
1297             return new ConstraintBuilder(this);
1298         }
1299 
1300         /**
1301          * Complete the specification of a constraint clause, and return the builder for the parent constraint clause.
1302          * 
1303          * @return the constraint builder that was used to create this parenthetical constraint clause builder; never null
1304          * @throws IllegalStateException if there was not an {@link #openParen() open parenthesis} to close
1305          */
1306         public ConstraintBuilder closeParen() {
1307             if (parent == null) {
1308                 throw new IllegalStateException(GraphI18n.unexpectedClosingParenthesis.text());
1309             }
1310             buildLogicalConstraint();
1311             return parent.setConstraint(constraint);
1312         }
1313 
1314         /**
1315          * Signal that the previous constraint clause be AND-ed together with another constraint clause that will be defined
1316          * immediately after this method call.
1317          * 
1318          * @return the constraint builder for the remaining constraint clause; never null
1319          */
1320         public ConstraintBuilder and() {
1321             buildLogicalConstraint();
1322             left = constraint;
1323             constraint = null;
1324             and = true;
1325             return this;
1326         }
1327 
1328         /**
1329          * Signal that the previous constraint clause be OR-ed together with another constraint clause that will be defined
1330          * immediately after this method call.
1331          * 
1332          * @return the constraint builder for the remaining constraint clause; never null
1333          */
1334         public ConstraintBuilder or() {
1335             buildLogicalConstraint();
1336             left = constraint;
1337             constraint = null;
1338             and = false;
1339             return this;
1340         }
1341 
1342         /**
1343          * Signal that the next constraint clause (defined immediately after this method) should be negated.
1344          * 
1345          * @return the constraint builder for the constraint clause that is to be negated; never null
1346          */
1347         public ConstraintBuilder not() {
1348             negateConstraint = true;
1349             return this;
1350         }
1351 
1352         protected ConstraintBuilder buildLogicalConstraint() {
1353             if (negateConstraint && constraint != null) {
1354                 constraint = new Not(constraint);
1355                 negateConstraint = false;
1356             }
1357             if (left != null && constraint != null) {
1358                 if (and) {
1359                     // If the left constraint is an OR, we need to rearrange things since AND is higher precedence ...
1360                     if (left instanceof Or) {
1361                         Or previous = (Or)left;
1362                         constraint = new Or(previous.getLeft(), new And(previous.getRight(), constraint));
1363                     } else {
1364                         constraint = new And(left, constraint);
1365                     }
1366                 } else {
1367                     constraint = new Or(left, constraint);
1368                 }
1369                 left = null;
1370             }
1371             return this;
1372         }
1373 
1374         /**
1375          * Define a constraint clause that the node within the named table is the same node as that appearing at the supplied
1376          * path.
1377          * 
1378          * @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
1379          *        FROM clause
1380          * @param asNodeAtPath the path to the node
1381          * @return the constraint builder that was used to create this clause; never null
1382          */
1383         public ConstraintBuilder isSameNode( String table,
1384                                              String asNodeAtPath ) {
1385             return setConstraint(new SameNode(selector(table), asNodeAtPath));
1386         }
1387 
1388         /**
1389          * Define a constraint clause that the node within the named table is the child of the node at the supplied path.
1390          * 
1391          * @param childTable the name of the table; may not be null and must refer to a valid name or alias of a table appearing
1392          *        in the FROM clause
1393          * @param parentPath the path to the parent node
1394          * @return the constraint builder that was used to create this clause; never null
1395          */
1396         public ConstraintBuilder isChild( String childTable,
1397                                           String parentPath ) {
1398             return setConstraint(new ChildNode(selector(childTable), parentPath));
1399         }
1400 
1401         /**
1402          * Define a constraint clause that the node within the named table is a descendant of the node at the supplied path.
1403          * 
1404          * @param descendantTable the name of the table; may not be null and must refer to a valid name or alias of a table
1405          *        appearing in the FROM clause
1406          * @param ancestorPath the path to the ancestor node
1407          * @return the constraint builder that was used to create this clause; never null
1408          */
1409         public ConstraintBuilder isBelowPath( String descendantTable,
1410                                               String ancestorPath ) {
1411             return setConstraint(new DescendantNode(selector(descendantTable), ancestorPath));
1412         }
1413 
1414         /**
1415          * Define a constraint clause that the node within the named table has at least one value for the named property.
1416          * 
1417          * @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
1418          *        FROM clause
1419          * @param propertyName the name of the property
1420          * @return the constraint builder that was used to create this clause; never null
1421          */
1422         public ConstraintBuilder hasProperty( String table,
1423                                               String propertyName ) {
1424             return setConstraint(new PropertyExistence(selector(table), propertyName));
1425         }
1426 
1427         /**
1428          * Define a constraint clause that the node within the named table have at least one property that satisfies the full-text
1429          * search expression.
1430          * 
1431          * @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
1432          *        FROM clause
1433          * @param searchExpression the full-text search expression
1434          * @return the constraint builder that was used to create this clause; never null
1435          */
1436         public ConstraintBuilder search( String table,
1437                                          String searchExpression ) {
1438             return setConstraint(new FullTextSearch(selector(table), searchExpression));
1439         }
1440 
1441         /**
1442          * Define a constraint clause that the node within the named table have a value for the named property that satisfies the
1443          * full-text search expression.
1444          * 
1445          * @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
1446          *        FROM clause
1447          * @param propertyName the name of the property to be searched
1448          * @param searchExpression the full-text search expression
1449          * @return the constraint builder that was used to create this clause; never null
1450          */
1451         public ConstraintBuilder search( String table,
1452                                          String propertyName,
1453                                          String searchExpression ) {
1454             return setConstraint(new FullTextSearch(selector(table), propertyName, searchExpression));
1455         }
1456 
1457         protected ComparisonBuilder comparisonBuilder( DynamicOperand operand ) {
1458             return new ComparisonBuilder(this, operand);
1459         }
1460 
1461         /**
1462          * {@inheritDoc}
1463          * 
1464          * @see org.modeshape.graph.query.QueryBuilder.DynamicOperandBuilder#length(java.lang.String, java.lang.String)
1465          */
1466         public ComparisonBuilder length( String table,
1467                                          String property ) {
1468             return comparisonBuilder(new Length(new PropertyValue(selector(table), property)));
1469         }
1470 
1471         /**
1472          * {@inheritDoc}
1473          * 
1474          * @see org.modeshape.graph.query.QueryBuilder.DynamicOperandBuilder#propertyValue(String, String)
1475          */
1476         public ComparisonBuilder propertyValue( String table,
1477                                                 String property ) {
1478             return comparisonBuilder(new PropertyValue(selector(table), property));
1479         }
1480 
1481         /**
1482          * {@inheritDoc}
1483          * 
1484          * @see org.modeshape.graph.query.QueryBuilder.DynamicOperandBuilder#referenceValue(java.lang.String)
1485          */
1486         public ComparisonBuilder referenceValue( String table ) {
1487             return comparisonBuilder(new ReferenceValue(selector(table)));
1488         }
1489 
1490         /**
1491          * {@inheritDoc}
1492          * 
1493          * @see org.modeshape.graph.query.QueryBuilder.DynamicOperandBuilder#referenceValue(java.lang.String, java.lang.String)
1494          */
1495         public ComparisonBuilder referenceValue( String table,
1496                                                  String property ) {
1497             return comparisonBuilder(new ReferenceValue(selector(table), property));
1498         }
1499 
1500         /**
1501          * {@inheritDoc}
1502          * 
1503          * @see org.modeshape.graph.query.QueryBuilder.DynamicOperandBuilder#fullTextSearchScore(String)
1504          */
1505         public ComparisonBuilder fullTextSearchScore( String table ) {
1506             return comparisonBuilder(new FullTextSearchScore(selector(table)));
1507         }
1508 
1509         /**
1510          * {@inheritDoc}
1511          * 
1512          * @see org.modeshape.graph.query.QueryBuilder.DynamicOperandBuilder#depth(java.lang.String)
1513          */
1514         public ComparisonBuilder depth( String table ) {
1515             return comparisonBuilder(new NodeDepth(selector(table)));
1516         }
1517 
1518         /**
1519          * {@inheritDoc}
1520          * 
1521          * @see org.modeshape.graph.query.QueryBuilder.DynamicOperandBuilder#path(java.lang.String)
1522          */
1523         public ComparisonBuilder path( String table ) {
1524             return comparisonBuilder(new NodePath(selector(table)));
1525         }
1526 
1527         /**
1528          * {@inheritDoc}
1529          * 
1530          * @see org.modeshape.graph.query.QueryBuilder.DynamicOperandBuilder#nodeLocalName(String)
1531          */
1532         public ComparisonBuilder nodeLocalName( String table ) {
1533             return comparisonBuilder(new NodeLocalName(selector(table)));
1534         }
1535 
1536         /**
1537          * {@inheritDoc}
1538          * 
1539          * @see org.modeshape.graph.query.QueryBuilder.DynamicOperandBuilder#nodeName(String)
1540          */
1541         public ComparisonBuilder nodeName( String table ) {
1542             return comparisonBuilder(new NodeName(selector(table)));
1543         }
1544 
1545         /**
1546          * {@inheritDoc}
1547          * 
1548          * @see org.modeshape.graph.query.QueryBuilder.DynamicOperandBuilder#upperCaseOf()
1549          */
1550         public DynamicOperandBuilder upperCaseOf() {
1551             return new UpperCaser(this);
1552         }
1553 
1554         /**
1555          * {@inheritDoc}
1556          * 
1557          * @see org.modeshape.graph.query.QueryBuilder.DynamicOperandBuilder#lowerCaseOf()
1558          */
1559         public DynamicOperandBuilder lowerCaseOf() {
1560             return new LowerCaser(this);
1561         }
1562 
1563         protected ConstraintBuilder setConstraint( Constraint constraint ) {
1564             if (this.constraint != null && this.left == null) {
1565                 and();
1566             }
1567             this.constraint = constraint;
1568             return buildLogicalConstraint();
1569         }
1570     }
1571 
1572     /**
1573      * A specialized form of the {@link ConstraintBuilder} that always wraps the generated constraint in a {@link UpperCase}
1574      * instance.
1575      */
1576     protected class UpperCaser extends ConstraintBuilder {
1577         private final ConstraintBuilder delegate;
1578 
1579         protected UpperCaser( ConstraintBuilder delegate ) {
1580             super(null);
1581             this.delegate = delegate;
1582         }
1583 
1584         @Override
1585         protected ConstraintBuilder setConstraint( Constraint constraint ) {
1586             Comparison comparison = (Comparison)constraint;
1587             return delegate.setConstraint(new Comparison(new UpperCase(comparison.getOperand1()), comparison.getOperator(),
1588                                                          comparison.getOperand2()));
1589         }
1590     }
1591 
1592     /**
1593      * A specialized form of the {@link ConstraintBuilder} that always wraps the generated constraint in a {@link LowerCase}
1594      * instance.
1595      */
1596     protected class LowerCaser extends ConstraintBuilder {
1597         private final ConstraintBuilder delegate;
1598 
1599         protected LowerCaser( ConstraintBuilder delegate ) {
1600             super(null);
1601             this.delegate = delegate;
1602         }
1603 
1604         @Override
1605         protected ConstraintBuilder setConstraint( Constraint constraint ) {
1606             Comparison comparison = (Comparison)constraint;
1607             return delegate.setConstraint(new Comparison(new LowerCase(comparison.getOperand1()), comparison.getOperator(),
1608                                                          comparison.getOperand2()));
1609         }
1610     }
1611 
1612     public abstract class CastAs<ReturnType> {
1613         protected final Object value;
1614 
1615         protected CastAs( Object value ) {
1616             this.value = value;
1617         }
1618 
1619         /**
1620          * Define the right-hand side literal value cast as the specified type.
1621          * 
1622          * @param type the property type; may not be null
1623          * @return the constraint builder; never null
1624          */
1625         public abstract ReturnType as( String type );
1626 
1627         /**
1628          * Define the right-hand side literal value cast as a {@link PropertyType#STRING}.
1629          * 
1630          * @return the constraint builder; never null
1631          */
1632         public ReturnType asString() {
1633             return as(typeSystem.getStringFactory().getTypeName());
1634         }
1635 
1636         /**
1637          * Define the right-hand side literal value cast as a {@link PropertyType#BOOLEAN}.
1638          * 
1639          * @return the constraint builder; never null
1640          */
1641         public ReturnType asBoolean() {
1642             return as(typeSystem.getBooleanFactory().getTypeName());
1643         }
1644 
1645         /**
1646          * Define the right-hand side literal value cast as a {@link PropertyType#LONG}.
1647          * 
1648          * @return the constraint builder; never null
1649          */
1650         public ReturnType asLong() {
1651             return as(typeSystem.getLongFactory().getTypeName());
1652         }
1653 
1654         /**
1655          * Define the right-hand side literal value cast as a {@link PropertyType#DOUBLE}.
1656          * 
1657          * @return the constraint builder; never null
1658          */
1659         public ReturnType asDouble() {
1660             return as(typeSystem.getDoubleFactory().getTypeName());
1661         }
1662 
1663         /**
1664          * Define the right-hand side literal value cast as a {@link PropertyType#DATE}.
1665          * 
1666          * @return the constraint builder; never null
1667          */
1668         public ReturnType asDate() {
1669             return as(typeSystem.getDateTimeFactory().getTypeName());
1670         }
1671 
1672         /**
1673          * Define the right-hand side literal value cast as a {@link PropertyType#PATH}.
1674          * 
1675          * @return the constraint builder; never null
1676          */
1677         public ReturnType asPath() {
1678             return as(typeSystem.getPathFactory().getTypeName());
1679         }
1680     }
1681 
1682     public class CastAsRightHandSide extends CastAs<ConstraintBuilder> {
1683         private final RightHandSide rhs;
1684 
1685         protected CastAsRightHandSide( RightHandSide rhs,
1686                                        Object value ) {
1687             super(value);
1688             this.rhs = rhs;
1689         }
1690 
1691         /**
1692          * Define the right-hand side literal value cast as the specified type.
1693          * 
1694          * @param type the property type; may not be null
1695          * @return the constraint builder; never null
1696          */
1697         @Override
1698         public ConstraintBuilder as( String type ) {
1699             return rhs.comparisonBuilder.is(rhs.operator, typeSystem.getTypeFactory(type).create(value));
1700         }
1701     }
1702 
1703     public class CastAsUpperBoundary extends CastAs<ConstraintBuilder> {
1704         private final UpperBoundary upperBoundary;
1705 
1706         protected CastAsUpperBoundary( UpperBoundary upperBoundary,
1707                                        Object value ) {
1708             super(value);
1709             this.upperBoundary = upperBoundary;
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 upperBoundary.comparisonBuilder.isBetween(upperBoundary.lowerBound, typeSystem.getTypeFactory(type)
1721                                                                                                  .create(value));
1722         }
1723     }
1724 
1725     public class CastAsLowerBoundary extends CastAs<AndBuilder<UpperBoundary>> {
1726         private final ComparisonBuilder builder;
1727 
1728         protected CastAsLowerBoundary( ComparisonBuilder builder,
1729                                        Object value ) {
1730             super(value);
1731             this.builder = builder;
1732         }
1733 
1734         /**
1735          * Define the left-hand side literal value cast as the specified type.
1736          * 
1737          * @param type the property type; may not be null
1738          * @return the builder to complete the constraint; never null
1739          */
1740         @Override
1741         public AndBuilder<UpperBoundary> as( String type ) {
1742             Object literal = typeSystem.getTypeFactory(type).create(value);
1743             return new AndBuilder<UpperBoundary>(new UpperBoundary(builder, new Literal(literal)));
1744         }
1745     }
1746 
1747     public class RightHandSide {
1748         protected final Operator operator;
1749         protected final ComparisonBuilder comparisonBuilder;
1750 
1751         protected RightHandSide( ComparisonBuilder comparisonBuilder,
1752                                  Operator operator ) {
1753             this.operator = operator;
1754             this.comparisonBuilder = comparisonBuilder;
1755         }
1756 
1757         /**
1758          * Define the right-hand side of a comparison.
1759          * 
1760          * @param literal the literal value;
1761          * @return the constraint builder; never null
1762          */
1763         public ConstraintBuilder literal( String literal ) {
1764             return comparisonBuilder.is(operator, literal);
1765         }
1766 
1767         /**
1768          * Define the right-hand side of a comparison.
1769          * 
1770          * @param literal the literal value;
1771          * @return the constraint builder; never null
1772          */
1773         public ConstraintBuilder literal( int literal ) {
1774             return comparisonBuilder.is(operator, literal);
1775         }
1776 
1777         /**
1778          * Define the right-hand side of a comparison.
1779          * 
1780          * @param literal the literal value;
1781          * @return the constraint builder; never null
1782          */
1783         public ConstraintBuilder literal( long literal ) {
1784             return comparisonBuilder.is(operator, literal);
1785         }
1786 
1787         /**
1788          * Define the right-hand side of a comparison.
1789          * 
1790          * @param literal the literal value;
1791          * @return the constraint builder; never null
1792          */
1793         public ConstraintBuilder literal( float literal ) {
1794             return comparisonBuilder.is(operator, literal);
1795         }
1796 
1797         /**
1798          * Define the right-hand side of a comparison.
1799          * 
1800          * @param literal the literal value;
1801          * @return the constraint builder; never null
1802          */
1803         public ConstraintBuilder literal( double literal ) {
1804             return comparisonBuilder.is(operator, literal);
1805         }
1806 
1807         /**
1808          * Define the right-hand side of a comparison.
1809          * 
1810          * @param literal the literal value;
1811          * @return the constraint builder; never null
1812          */
1813         public ConstraintBuilder literal( DateTime literal ) {
1814             return comparisonBuilder.is(operator, literal.toUtcTimeZone());
1815         }
1816 
1817         /**
1818          * Define the right-hand side of a comparison.
1819          * 
1820          * @param literal the literal value;
1821          * @return the constraint builder; never null
1822          */
1823         public ConstraintBuilder literal( Path literal ) {
1824             return comparisonBuilder.is(operator, literal);
1825         }
1826 
1827         /**
1828          * Define the right-hand side of a comparison.
1829          * 
1830          * @param literal the literal value;
1831          * @return the constraint builder; never null
1832          */
1833         public ConstraintBuilder literal( Name literal ) {
1834             return comparisonBuilder.is(operator, literal);
1835         }
1836 
1837         /**
1838          * Define the right-hand side of a comparison.
1839          * 
1840          * @param literal the literal value;
1841          * @return the constraint builder; never null
1842          */
1843         public ConstraintBuilder literal( URI literal ) {
1844             return comparisonBuilder.is(operator, literal);
1845         }
1846 
1847         /**
1848          * Define the right-hand side of a comparison.
1849          * 
1850          * @param literal the literal value;
1851          * @return the constraint builder; never null
1852          */
1853         public ConstraintBuilder literal( UUID literal ) {
1854             return comparisonBuilder.is(operator, literal);
1855         }
1856 
1857         /**
1858          * Define the right-hand side of a comparison.
1859          * 
1860          * @param literal the literal value;
1861          * @return the constraint builder; never null
1862          */
1863         public ConstraintBuilder literal( Binary literal ) {
1864             return comparisonBuilder.is(operator, literal);
1865         }
1866 
1867         /**
1868          * Define the right-hand side of a comparison.
1869          * 
1870          * @param literal the literal value;
1871          * @return the constraint builder; never null
1872          */
1873         public ConstraintBuilder literal( BigDecimal literal ) {
1874             return comparisonBuilder.is(operator, literal);
1875         }
1876 
1877         /**
1878          * Define the right-hand side of a comparison.
1879          * 
1880          * @param literal the literal value;
1881          * @return the constraint builder; never null
1882          */
1883         public ConstraintBuilder literal( boolean literal ) {
1884             return comparisonBuilder.is(operator, literal);
1885         }
1886 
1887         /**
1888          * Define the right-hand side of a comparison.
1889          * 
1890          * @param variableName the name of the variable
1891          * @return the constraint builder; never null
1892          */
1893         public ConstraintBuilder variable( String variableName ) {
1894             return comparisonBuilder.is(operator, variableName);
1895         }
1896 
1897         /**
1898          * Define the right-hand side of a comparison.
1899          * 
1900          * @param literal the literal value that is to be cast
1901          * @return the constraint builder; never null
1902          */
1903         public CastAs<ConstraintBuilder> cast( int literal ) {
1904             return new CastAsRightHandSide(this, literal);
1905         }
1906 
1907         /**
1908          * Define the right-hand side of a comparison.
1909          * 
1910          * @param literal the literal value that is to be cast
1911          * @return the constraint builder; never null
1912          */
1913         public CastAs<ConstraintBuilder> cast( String literal ) {
1914             return new CastAsRightHandSide(this, literal);
1915         }
1916 
1917         /**
1918          * Define the right-hand side of a comparison.
1919          * 
1920          * @param literal the literal value that is to be cast
1921          * @return the constraint builder; never null
1922          */
1923         public CastAs<ConstraintBuilder> cast( boolean literal ) {
1924             return new CastAsRightHandSide(this, literal);
1925         }
1926 
1927         /**
1928          * Define the right-hand side of a comparison.
1929          * 
1930          * @param literal the literal value that is to be cast
1931          * @return the constraint builder; never null
1932          */
1933         public CastAs<ConstraintBuilder> cast( long literal ) {
1934             return new CastAsRightHandSide(this, literal);
1935         }
1936 
1937         /**
1938          * Define the right-hand side of a comparison.
1939          * 
1940          * @param literal the literal value that is to be cast
1941          * @return the constraint builder; never null
1942          */
1943         public CastAs<ConstraintBuilder> cast( double literal ) {
1944             return new CastAsRightHandSide(this, literal);
1945         }
1946 
1947         /**
1948          * Define the right-hand side of a comparison.
1949          * 
1950          * @param literal the literal value that is to be cast
1951          * @return the constraint builder; never null
1952          */
1953         public CastAs<ConstraintBuilder> cast( BigDecimal literal ) {
1954             return new CastAsRightHandSide(this, literal);
1955         }
1956 
1957         /**
1958          * Define the right-hand side of a comparison.
1959          * 
1960          * @param literal the literal value that is to be cast
1961          * @return the constraint builder; never null
1962          */
1963         public CastAs<ConstraintBuilder> cast( DateTime literal ) {
1964             return new CastAsRightHandSide(this, literal.toUtcTimeZone());
1965         }
1966 
1967         /**
1968          * Define the right-hand side of a comparison.
1969          * 
1970          * @param literal the literal value that is to be cast
1971          * @return the constraint builder; never null
1972          */
1973         public CastAs<ConstraintBuilder> cast( Name literal ) {
1974             return new CastAsRightHandSide(this, literal);
1975         }
1976 
1977         /**
1978          * Define the right-hand side of a comparison.
1979          * 
1980          * @param literal the literal value that is to be cast
1981          * @return the constraint builder; never null
1982          */
1983         public CastAs<ConstraintBuilder> cast( Path literal ) {
1984             return new CastAsRightHandSide(this, literal);
1985         }
1986 
1987         /**
1988          * Define the right-hand side of a comparison.
1989          * 
1990          * @param literal the literal value that is to be cast
1991          * @return the constraint builder; never null
1992          */
1993         public CastAs<ConstraintBuilder> cast( UUID literal ) {
1994             return new CastAsRightHandSide(this, literal);
1995         }
1996 
1997         /**
1998          * Define the right-hand side of a comparison.
1999          * 
2000          * @param literal the literal value that is to be cast
2001          * @return the constraint builder; never null
2002          */
2003         public CastAs<ConstraintBuilder> cast( URI literal ) {
2004             return new CastAsRightHandSide(this, literal);
2005         }
2006     }
2007 
2008     public class UpperBoundary {
2009         protected final StaticOperand lowerBound;
2010         protected final ComparisonBuilder comparisonBuilder;
2011 
2012         protected UpperBoundary( ComparisonBuilder comparisonBuilder,
2013                                  StaticOperand lowerBound ) {
2014             this.lowerBound = lowerBound;
2015             this.comparisonBuilder = comparisonBuilder;
2016         }
2017 
2018         /**
2019          * Define the upper boundary value of a range.
2020          * 
2021          * @param literal the literal value;
2022          * @return the constraint builder; never null
2023          */
2024         public ConstraintBuilder literal( String literal ) {
2025             return comparisonBuilder.isBetween(lowerBound, literal);
2026         }
2027 
2028         /**
2029          * Define the upper boundary value of a range.
2030          * 
2031          * @param literal the literal value;
2032          * @return the constraint builder; never null
2033          */
2034         public ConstraintBuilder literal( int literal ) {
2035             return comparisonBuilder.isBetween(lowerBound, literal);
2036         }
2037 
2038         /**
2039          * Define the upper boundary value of a range.
2040          * 
2041          * @param literal the literal value;
2042          * @return the constraint builder; never null
2043          */
2044         public ConstraintBuilder literal( long literal ) {
2045             return comparisonBuilder.isBetween(lowerBound, literal);
2046         }
2047 
2048         /**
2049          * Define the upper boundary value of a range.
2050          * 
2051          * @param literal the literal value;
2052          * @return the constraint builder; never null
2053          */
2054         public ConstraintBuilder literal( float literal ) {
2055             return comparisonBuilder.isBetween(lowerBound, literal);
2056         }
2057 
2058         /**
2059          * Define the upper boundary value of a range.
2060          * 
2061          * @param literal the literal value;
2062          * @return the constraint builder; never null
2063          */
2064         public ConstraintBuilder literal( double literal ) {
2065             return comparisonBuilder.isBetween(lowerBound, literal);
2066         }
2067 
2068         /**
2069          * Define the upper boundary value of a range.
2070          * 
2071          * @param literal the literal value;
2072          * @return the constraint builder; never null
2073          */
2074         public ConstraintBuilder literal( DateTime literal ) {
2075             return comparisonBuilder.isBetween(lowerBound, literal);
2076         }
2077 
2078         /**
2079          * Define the upper boundary value of a range.
2080          * 
2081          * @param literal the literal value;
2082          * @return the constraint builder; never null
2083          */
2084         public ConstraintBuilder literal( Path literal ) {
2085             return comparisonBuilder.isBetween(lowerBound, literal);
2086         }
2087 
2088         /**
2089          * Define the upper boundary value of a range.
2090          * 
2091          * @param literal the literal value;
2092          * @return the constraint builder; never null
2093          */
2094         public ConstraintBuilder literal( Name literal ) {
2095             return comparisonBuilder.isBetween(lowerBound, literal);
2096         }
2097 
2098         /**
2099          * Define the upper boundary value of a range.
2100          * 
2101          * @param literal the literal value;
2102          * @return the constraint builder; never null
2103          */
2104         public ConstraintBuilder literal( URI literal ) {
2105             return comparisonBuilder.isBetween(lowerBound, literal);
2106         }
2107 
2108         /**
2109          * Define the upper boundary value of a range.
2110          * 
2111          * @param literal the literal value;
2112          * @return the constraint builder; never null
2113          */
2114         public ConstraintBuilder literal( UUID literal ) {
2115             return comparisonBuilder.isBetween(lowerBound, literal);
2116         }
2117 
2118         /**
2119          * Define the upper boundary value of a range.
2120          * 
2121          * @param literal the literal value;
2122          * @return the constraint builder; never null
2123          */
2124         public ConstraintBuilder literal( Binary literal ) {
2125             return comparisonBuilder.isBetween(lowerBound, literal);
2126         }
2127 
2128         /**
2129          * Define the upper boundary value of a range.
2130          * 
2131          * @param literal the literal value;
2132          * @return the constraint builder; never null
2133          */
2134         public ConstraintBuilder literal( BigDecimal literal ) {
2135             return comparisonBuilder.isBetween(lowerBound, literal);
2136         }
2137 
2138         /**
2139          * Define the upper boundary value of a range.
2140          * 
2141          * @param literal the literal value;
2142          * @return the constraint builder; never null
2143          */
2144         public ConstraintBuilder literal( boolean literal ) {
2145             return comparisonBuilder.isBetween(lowerBound, literal);
2146         }
2147 
2148         /**
2149          * Define the upper boundary value of a range.
2150          * 
2151          * @param variableName the name of the variable
2152          * @return the constraint builder; never null
2153          */
2154         public ConstraintBuilder variable( String variableName ) {
2155             return comparisonBuilder.constraintBuilder.setConstraint(new Between(comparisonBuilder.left, lowerBound,
2156                                                                                  new BindVariableName(variableName)));
2157         }
2158 
2159         /**
2160          * Define the upper boundary value of a range.
2161          * 
2162          * @param literal the literal value that is to be cast
2163          * @return the constraint builder; never null
2164          */
2165         public CastAs<ConstraintBuilder> cast( int literal ) {
2166             return new CastAsUpperBoundary(this, literal);
2167         }
2168 
2169         /**
2170          * Define the upper boundary value of a range.
2171          * 
2172          * @param literal the literal value that is to be cast
2173          * @return the constraint builder; never null
2174          */
2175         public CastAs<ConstraintBuilder> cast( String literal ) {
2176             return new CastAsUpperBoundary(this, literal);
2177         }
2178 
2179         /**
2180          * Define the upper boundary value of a range.
2181          * 
2182          * @param literal the literal value that is to be cast
2183          * @return the constraint builder; never null
2184          */
2185         public CastAs<ConstraintBuilder> cast( boolean literal ) {
2186             return new CastAsUpperBoundary(this, literal);
2187         }
2188 
2189         /**
2190          * Define the upper boundary value of a range.
2191          * 
2192          * @param literal the literal value that is to be cast
2193          * @return the constraint builder; never null
2194          */
2195         public CastAs<ConstraintBuilder> cast( long literal ) {
2196             return new CastAsUpperBoundary(this, literal);
2197         }
2198 
2199         /**
2200          * Define the upper boundary value of a range.
2201          * 
2202          * @param literal the literal value that is to be cast
2203          * @return the constraint builder; never null
2204          */
2205         public CastAs<ConstraintBuilder> cast( double literal ) {
2206             return new CastAsUpperBoundary(this, literal);
2207         }
2208 
2209         /**
2210          * Define the upper boundary value of a range.
2211          * 
2212          * @param literal the literal value that is to be cast
2213          * @return the constraint builder; never null
2214          */
2215         public CastAs<ConstraintBuilder> cast( BigDecimal literal ) {
2216             return new CastAsUpperBoundary(this, literal);
2217         }
2218 
2219         /**
2220          * Define the upper boundary value of a range.
2221          * 
2222          * @param literal the literal value that is to be cast
2223          * @return the constraint builder; never null
2224          */
2225         public CastAs<ConstraintBuilder> cast( DateTime literal ) {
2226             return new CastAsUpperBoundary(this, literal.toUtcTimeZone());
2227         }
2228 
2229         /**
2230          * Define the upper boundary value of a range.
2231          * 
2232          * @param literal the literal value that is to be cast
2233          * @return the constraint builder; never null
2234          */
2235         public CastAs<ConstraintBuilder> cast( Name literal ) {
2236             return new CastAsUpperBoundary(this, literal);
2237         }
2238 
2239         /**
2240          * Define the upper boundary value of a range.
2241          * 
2242          * @param literal the literal value that is to be cast
2243          * @return the constraint builder; never null
2244          */
2245         public CastAs<ConstraintBuilder> cast( Path literal ) {
2246             return new CastAsUpperBoundary(this, literal);
2247         }
2248 
2249         /**
2250          * Define the upper boundary value of a range.
2251          * 
2252          * @param literal the literal value that is to be cast
2253          * @return the constraint builder; never null
2254          */
2255         public CastAs<ConstraintBuilder> cast( UUID literal ) {
2256             return new CastAsUpperBoundary(this, literal);
2257         }
2258 
2259         /**
2260          * Define the upper boundary value of a range.
2261          * 
2262          * @param literal the literal value that is to be cast
2263          * @return the constraint builder; never null
2264          */
2265         public CastAs<ConstraintBuilder> cast( URI literal ) {
2266             return new CastAsUpperBoundary(this, literal);
2267         }
2268     }
2269 
2270     public class LowerBoundary {
2271         protected final ComparisonBuilder comparisonBuilder;
2272 
2273         protected LowerBoundary( ComparisonBuilder comparisonBuilder ) {
2274             this.comparisonBuilder = comparisonBuilder;
2275         }
2276 
2277         /**
2278          * Define the lower boundary value of a range.
2279          * 
2280          * @param literal the literal value;
2281          * @return the constraint builder; never null
2282          */
2283         public AndBuilder<UpperBoundary> literal( String literal ) {
2284             return new AndBuilder<UpperBoundary>(new UpperBoundary(comparisonBuilder, new Literal(literal)));
2285         }
2286 
2287         /**
2288          * Define the lower boundary value of a range.
2289          * 
2290          * @param literal the literal value;
2291          * @return the constraint builder; never null
2292          */
2293         public AndBuilder<UpperBoundary> literal( int literal ) {
2294             return new AndBuilder<UpperBoundary>(new UpperBoundary(comparisonBuilder, new Literal(literal)));
2295         }
2296 
2297         /**
2298          * Define the lower boundary value of a range.
2299          * 
2300          * @param literal the literal value;
2301          * @return the constraint builder; never null
2302          */
2303         public AndBuilder<UpperBoundary> literal( long literal ) {
2304             return new AndBuilder<UpperBoundary>(new UpperBoundary(comparisonBuilder, new Literal(literal)));
2305         }
2306 
2307         /**
2308          * Define the lower boundary value of a range.
2309          * 
2310          * @param literal the literal value;
2311          * @return the constraint builder; never null
2312          */
2313         public AndBuilder<UpperBoundary> literal( float literal ) {
2314             return new AndBuilder<UpperBoundary>(new UpperBoundary(comparisonBuilder, new Literal(literal)));
2315         }
2316 
2317         /**
2318          * Define the lower boundary value of a range.
2319          * 
2320          * @param literal the literal value;
2321          * @return the constraint builder; never null
2322          */
2323         public AndBuilder<UpperBoundary> literal( double literal ) {
2324             return new AndBuilder<UpperBoundary>(new UpperBoundary(comparisonBuilder, new Literal(literal)));
2325         }
2326 
2327         /**
2328          * Define the lower boundary value of a range.
2329          * 
2330          * @param literal the literal value;
2331          * @return the constraint builder; never null
2332          */
2333         public AndBuilder<UpperBoundary> literal( DateTime literal ) {
2334             return new AndBuilder<UpperBoundary>(new UpperBoundary(comparisonBuilder, new Literal(literal)));
2335         }
2336 
2337         /**
2338          * Define the lower boundary value of a range.
2339          * 
2340          * @param literal the literal value;
2341          * @return the constraint builder; never null
2342          */
2343         public AndBuilder<UpperBoundary> literal( Path literal ) {
2344             return new AndBuilder<UpperBoundary>(new UpperBoundary(comparisonBuilder, new Literal(literal)));
2345         }
2346 
2347         /**
2348          * Define the lower boundary value of a range.
2349          * 
2350          * @param literal the literal value;
2351          * @return the constraint builder; never null
2352          */
2353         public AndBuilder<UpperBoundary> literal( Name literal ) {
2354             return new AndBuilder<UpperBoundary>(new UpperBoundary(comparisonBuilder, new Literal(literal)));
2355         }
2356 
2357         /**
2358          * Define the lower boundary value of a range.
2359          * 
2360          * @param literal the literal value;
2361          * @return the constraint builder; never null
2362          */
2363         public AndBuilder<UpperBoundary> literal( URI literal ) {
2364             return new AndBuilder<UpperBoundary>(new UpperBoundary(comparisonBuilder, new Literal(literal)));
2365         }
2366 
2367         /**
2368          * Define the lower boundary value of a range.
2369          * 
2370          * @param literal the literal value;
2371          * @return the constraint builder; never null
2372          */
2373         public AndBuilder<UpperBoundary> literal( UUID literal ) {
2374             return new AndBuilder<UpperBoundary>(new UpperBoundary(comparisonBuilder, new Literal(literal)));
2375         }
2376 
2377         /**
2378          * Define the lower boundary value of a range.
2379          * 
2380          * @param literal the literal value;
2381          * @return the constraint builder; never null
2382          */
2383         public AndBuilder<UpperBoundary> literal( Binary literal ) {
2384             return new AndBuilder<UpperBoundary>(new UpperBoundary(comparisonBuilder, new Literal(literal)));
2385         }
2386 
2387         /**
2388          * Define the lower boundary value of a range.
2389          * 
2390          * @param literal the literal value;
2391          * @return the constraint builder; never null
2392          */
2393         public AndBuilder<UpperBoundary> literal( BigDecimal literal ) {
2394             return new AndBuilder<UpperBoundary>(new UpperBoundary(comparisonBuilder, new Literal(literal)));
2395         }
2396 
2397         /**
2398          * Define the lower boundary value of a range.
2399          * 
2400          * @param literal the literal value;
2401          * @return the constraint builder; never null
2402          */
2403         public AndBuilder<UpperBoundary> literal( boolean literal ) {
2404             return new AndBuilder<UpperBoundary>(new UpperBoundary(comparisonBuilder, new Literal(literal)));
2405         }
2406 
2407         /**
2408          * Define the lower boundary value of a range.
2409          * 
2410          * @param variableName the name of the variable
2411          * @return the constraint builder; never null
2412          */
2413         public AndBuilder<UpperBoundary> variable( String variableName ) {
2414             return new AndBuilder<UpperBoundary>(new UpperBoundary(comparisonBuilder, new BindVariableName(variableName)));
2415         }
2416 
2417         /**
2418          * Define the lower boundary value of a range.
2419          * 
2420          * @param literal the literal value that is to be cast
2421          * @return the constraint builder; never null
2422          */
2423         public CastAs<AndBuilder<UpperBoundary>> cast( int literal ) {
2424             return new CastAsLowerBoundary(comparisonBuilder, literal);
2425         }
2426 
2427         /**
2428          * Define the lower boundary value of a range.
2429          * 
2430          * @param literal the literal value that is to be cast
2431          * @return the constraint builder; never null
2432          */
2433         public CastAs<AndBuilder<UpperBoundary>> cast( String literal ) {
2434             return new CastAsLowerBoundary(comparisonBuilder, literal);
2435         }
2436 
2437         /**
2438          * Define the lower boundary value of a range.
2439          * 
2440          * @param literal the literal value that is to be cast
2441          * @return the constraint builder; never null
2442          */
2443         public CastAs<AndBuilder<UpperBoundary>> cast( boolean literal ) {
2444             return new CastAsLowerBoundary(comparisonBuilder, literal);
2445         }
2446 
2447         /**
2448          * Define the lower boundary value of a range.
2449          * 
2450          * @param literal the literal value that is to be cast
2451          * @return the constraint builder; never null
2452          */
2453         public CastAs<AndBuilder<UpperBoundary>> cast( long literal ) {
2454             return new CastAsLowerBoundary(comparisonBuilder, literal);
2455         }
2456 
2457         /**
2458          * Define the lower boundary value of a range.
2459          * 
2460          * @param literal the literal value that is to be cast
2461          * @return the constraint builder; never null
2462          */
2463         public CastAs<AndBuilder<UpperBoundary>> cast( double literal ) {
2464             return new CastAsLowerBoundary(comparisonBuilder, literal);
2465         }
2466 
2467         /**
2468          * Define the lower boundary value of a range.
2469          * 
2470          * @param literal the literal value that is to be cast
2471          * @return the constraint builder; never null
2472          */
2473         public CastAs<AndBuilder<UpperBoundary>> cast( BigDecimal literal ) {
2474             return new CastAsLowerBoundary(comparisonBuilder, literal);
2475         }
2476 
2477         /**
2478          * Define the lower boundary value of a range.
2479          * 
2480          * @param literal the literal value that is to be cast
2481          * @return the constraint builder; never null
2482          */
2483         public CastAs<AndBuilder<UpperBoundary>> cast( DateTime literal ) {
2484             return new CastAsLowerBoundary(comparisonBuilder, literal);
2485         }
2486 
2487         /**
2488          * Define the lower boundary value of a range.
2489          * 
2490          * @param literal the literal value that is to be cast
2491          * @return the constraint builder; never null
2492          */
2493         public CastAs<AndBuilder<UpperBoundary>> cast( Name literal ) {
2494             return new CastAsLowerBoundary(comparisonBuilder, literal);
2495         }
2496 
2497         /**
2498          * Define the lower boundary value of a range.
2499          * 
2500          * @param literal the literal value that is to be cast
2501          * @return the constraint builder; never null
2502          */
2503         public CastAs<AndBuilder<UpperBoundary>> cast( Path literal ) {
2504             return new CastAsLowerBoundary(comparisonBuilder, literal);
2505         }
2506 
2507         /**
2508          * Define the lower boundary value of a range.
2509          * 
2510          * @param literal the literal value that is to be cast
2511          * @return the constraint builder; never null
2512          */
2513         public CastAs<AndBuilder<UpperBoundary>> cast( UUID literal ) {
2514             return new CastAsLowerBoundary(comparisonBuilder, literal);
2515         }
2516 
2517         /**
2518          * Define the lower boundary value of a range.
2519          * 
2520          * @param literal the literal value that is to be cast
2521          * @return the constraint builder; never null
2522          */
2523         public CastAs<AndBuilder<UpperBoundary>> cast( URI literal ) {
2524             return new CastAsLowerBoundary(comparisonBuilder, literal);
2525         }
2526     }
2527 
2528     public class ArithmeticBuilder {
2529         protected final ArithmeticBuilder parent;
2530         protected final ArithmeticOperator operator;
2531         protected DynamicOperand left;
2532         protected final ComparisonBuilder comparisonBuilder;
2533 
2534         protected ArithmeticBuilder( ArithmeticOperator operator,
2535                                      ComparisonBuilder comparisonBuilder,
2536                                      DynamicOperand left,
2537                                      ArithmeticBuilder parent ) {
2538             this.operator = operator;
2539             this.left = left;
2540             this.comparisonBuilder = comparisonBuilder;
2541             this.parent = parent; // may be null
2542         }
2543 
2544         /**
2545          * Constrains the nodes in the the supplied table such that they must have a property value whose length matches the
2546          * criteria.
2547          * 
2548          * @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
2549          *        FROM clause
2550          * @param property the name of the property; may not be null and must refer to a valid property name
2551          * @return the interface for completing the value portion of the criteria specification; never null
2552          */
2553         public ComparisonBuilder length( String table,
2554                                          String property ) {
2555             return comparisonBuilder(new Length(new PropertyValue(selector(table), property)));
2556         }
2557 
2558         /**
2559          * Constrains the nodes in the the supplied table such that they must have a matching value for the named property.
2560          * 
2561          * @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
2562          *        FROM clause
2563          * @param property the name of the property; may not be null and must refer to a valid property name
2564          * @return the interface for completing the value portion of the criteria specification; never null
2565          */
2566         public ComparisonBuilder propertyValue( String table,
2567                                                 String property ) {
2568             return comparisonBuilder(new PropertyValue(selector(table), property));
2569         }
2570 
2571         /**
2572          * Constrains the nodes in the the supplied table such that they must have a matching value for any of the node's
2573          * reference properties.
2574          * 
2575          * @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
2576          *        FROM clause
2577          * @return the interface for completing the value portion of the criteria specification; never null
2578          */
2579         public ComparisonBuilder referenceValue( String table ) {
2580             return comparisonBuilder(new ReferenceValue(selector(table)));
2581         }
2582 
2583         /**
2584          * Constrains the nodes in the the supplied table such that they must have a matching value for the named property.
2585          * 
2586          * @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
2587          *        FROM clause
2588          * @param property the name of the reference property; may be null if the constraint applies to all/any reference
2589          *        properties on the node
2590          * @return the interface for completing the value portion of the criteria specification; never null
2591          */
2592         public ComparisonBuilder referenceValue( String table,
2593                                                  String property ) {
2594             return comparisonBuilder(new ReferenceValue(selector(table), property));
2595         }
2596 
2597         /**
2598          * Constrains the nodes in the the supplied table such that they must satisfy the supplied full-text search on the nodes'
2599          * property values.
2600          * 
2601          * @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
2602          *        FROM clause
2603          * @return the interface for completing the value portion of the criteria specification; never null
2604          */
2605         public ComparisonBuilder fullTextSearchScore( String table ) {
2606             return comparisonBuilder(new FullTextSearchScore(selector(table)));
2607         }
2608 
2609         /**
2610          * Constrains the nodes in the the supplied table based upon criteria on the node's depth.
2611          * 
2612          * @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
2613          *        FROM clause
2614          * @return the interface for completing the value portion of the criteria specification; never null
2615          */
2616         public ComparisonBuilder depth( String table ) {
2617             return comparisonBuilder(new NodeDepth(selector(table)));
2618         }
2619 
2620         // /**
2621         // * Simulate the use of an open parenthesis in the constraint. The resulting builder should be used to define the
2622         // * constraint within the parenthesis, and should always be terminated with a {@link #closeParen()}.
2623         // *
2624         // * @return the constraint builder that should be used to define the portion of the constraint within the parenthesis;
2625         // * never null
2626         // * @see #closeParen()
2627         // */
2628         // public ArithmeticBuilder openParen() {
2629         // return new ArithmeticBuilder(operator, comparisonBuilder, left, this);
2630         // }
2631         //
2632         // /**
2633         // * Complete the specification of a constraint clause, and return the builder for the parent constraint clause.
2634         // *
2635         // * @return the constraint builder that was used to create this parenthetical constraint clause builder; never null
2636         // * @throws IllegalStateException if there was not an {@link #openParen() open parenthesis} to close
2637         // */
2638         // public ComparisonBuilder closeParen() {
2639         // if (parent == null) {
2640         // throw new IllegalStateException(GraphI18n.unexpectedClosingParenthesis.text());
2641         // }
2642         // buildLogicalConstraint();
2643         // return parent.setLeft(left).comparisonBuilder;
2644         // }
2645         // protected ArithmeticBuilder setLeft( DynamicOperand left ) {
2646         // this.left = left;
2647         // return this;
2648         // }
2649 
2650         protected ComparisonBuilder comparisonBuilder( DynamicOperand right ) {
2651             DynamicOperand leftOperand = null;
2652             // If the left operand is an arithmetic operand, then we need to check the operator precedence ...
2653             if (left instanceof ArithmeticOperand) {
2654                 ArithmeticOperand leftArith = (ArithmeticOperand)left;
2655                 ArithmeticOperator operator = leftArith.getOperator();
2656                 if (this.operator.precedes(operator)) {
2657                     // Need to do create an operand with leftArith.right and right
2658                     DynamicOperand inner = new ArithmeticOperand(leftArith.getRight(), this.operator, right);
2659                     leftOperand = new ArithmeticOperand(leftArith.getLeft(), operator, inner);
2660                 } else {
2661                     // the left preceds this, so we can add the new operand on top ...
2662                     leftOperand = new ArithmeticOperand(leftArith, operator, right);
2663                 }
2664             } else {
2665                 // The left isn't an arith ...
2666                 leftOperand = new ArithmeticOperand(left, operator, right);
2667             }
2668             return new ComparisonBuilder(comparisonBuilder.constraintBuilder, leftOperand);
2669         }
2670     }
2671 
2672     /**
2673      * An interface used to set the right-hand side of a constraint.
2674      */
2675     public class ComparisonBuilder {
2676         protected final DynamicOperand left;
2677         protected final ConstraintBuilder constraintBuilder;
2678 
2679         protected ComparisonBuilder( ConstraintBuilder constraintBuilder,
2680                                      DynamicOperand left ) {
2681             this.left = left;
2682             this.constraintBuilder = constraintBuilder;
2683         }
2684 
2685         public ConstraintBuilder isIn( Object... literals ) {
2686             CheckArg.isNotNull(literals, "literals");
2687             Collection<StaticOperand> right = new ArrayList<StaticOperand>();
2688             for (Object literal : literals) {
2689                 right.add(literal instanceof Literal ? (Literal)literal : new Literal(literal));
2690             }
2691             return this.constraintBuilder.setConstraint(new SetCriteria(left, right));
2692         }
2693 
2694         public ConstraintBuilder isIn( Iterable<Object> literals ) {
2695             CheckArg.isNotNull(literals, "literals");
2696             Collection<StaticOperand> right = new ArrayList<StaticOperand>();
2697             for (Object literal : literals) {
2698                 right.add(literal instanceof Literal ? (Literal)literal : new Literal(literal));
2699             }
2700             return this.constraintBuilder.setConstraint(new SetCriteria(left, right));
2701         }
2702 
2703         /**
2704          * Create a comparison object based upon the addition of the previously-constructed {@link DynamicOperand} and the next
2705          * DynamicOperand to be created with the supplied builder.
2706          * 
2707          * @return the builder that should be used to create the right-hand-side of the operation; never null
2708          */
2709         public ArithmeticBuilder plus() {
2710             return new ArithmeticBuilder(ArithmeticOperator.ADD, this, left, null);
2711         }
2712 
2713         /**
2714          * Create a comparison object based upon the subtraction of the next {@link DynamicOperand} (created using the builder
2715          * returned from this method) from the the previously-constructed DynamicOperand to be created with the supplied builder.
2716          * 
2717          * @return the builder that should be used to create the right-hand-side of the operation; never null
2718          */
2719         public ArithmeticBuilder minus() {
2720             return new ArithmeticBuilder(ArithmeticOperator.SUBTRACT, this, left, null);
2721         }
2722 
2723         /**
2724          * Define the operator that will be used in the comparison, returning an interface that can be used to define the
2725          * right-hand-side of the comparison.
2726          * 
2727          * @param operator the operator; may not be null
2728          * @return the interface used to define the right-hand-side of the comparison
2729          */
2730         public RightHandSide is( Operator operator ) {
2731             CheckArg.isNotNull(operator, "operator");
2732             return new RightHandSide(this, operator);
2733         }
2734 
2735         /**
2736          * Use the 'equal to' operator in the comparison, returning an interface that can be used to define the right-hand-side of
2737          * the comparison.
2738          * 
2739          * @return the interface used to define the right-hand-side of the comparison
2740          */
2741         public RightHandSide isEqualTo() {
2742             return is(Operator.EQUAL_TO);
2743         }
2744 
2745         /**
2746          * Use the 'equal to' operator in the comparison, returning an interface that can be used to define the right-hand-side of
2747          * the comparison.
2748          * 
2749          * @return the interface used to define the right-hand-side of the comparison
2750          */
2751         public RightHandSide isNotEqualTo() {
2752             return is(Operator.NOT_EQUAL_TO);
2753         }
2754 
2755         /**
2756          * Use the 'equal to' operator in the comparison, returning an interface that can be used to define the right-hand-side of
2757          * the comparison.
2758          * 
2759          * @return the interface used to define the right-hand-side of the comparison
2760          */
2761         public RightHandSide isGreaterThan() {
2762             return is(Operator.GREATER_THAN);
2763         }
2764 
2765         /**
2766          * Use the 'equal to' operator in the comparison, returning an interface that can be used to define the right-hand-side of
2767          * the comparison.
2768          * 
2769          * @return the interface used to define the right-hand-side of the comparison
2770          */
2771         public RightHandSide isGreaterThanOrEqualTo() {
2772             return is(Operator.GREATER_THAN_OR_EQUAL_TO);
2773         }
2774 
2775         /**
2776          * Use the 'equal to' operator in the comparison, returning an interface that can be used to define the right-hand-side of
2777          * the comparison.
2778          * 
2779          * @return the interface used to define the right-hand-side of the comparison
2780          */
2781         public RightHandSide isLessThan() {
2782             return is(Operator.LESS_THAN);
2783         }
2784 
2785         /**
2786          * Use the 'equal to' operator in the comparison, returning an interface that can be used to define the right-hand-side of
2787          * the comparison.
2788          * 
2789          * @return the interface used to define the right-hand-side of the comparison
2790          */
2791         public RightHandSide isLessThanOrEqualTo() {
2792             return is(Operator.LESS_THAN_OR_EQUAL_TO);
2793         }
2794 
2795         /**
2796          * Use the 'equal to' operator in the comparison, returning an interface that can be used to define the right-hand-side of
2797          * the comparison.
2798          * 
2799          * @return the interface used to define the right-hand-side of the comparison
2800          */
2801         public RightHandSide isLike() {
2802             return is(Operator.LIKE);
2803         }
2804 
2805         /**
2806          * Define the right-hand-side of the constraint using the supplied operator.
2807          * 
2808          * @param operator the operator; may not be null
2809          * @param variableName the name of the variable
2810          * @return the builder used to create the constraint clause, ready to be used to create other constraints clauses or
2811          *         complete already-started clauses; never null
2812          */
2813         public ConstraintBuilder isVariable( Operator operator,
2814                                              String variableName ) {
2815             CheckArg.isNotNull(operator, "operator");
2816             return this.constraintBuilder.setConstraint(new Comparison(left, operator, new BindVariableName(variableName)));
2817         }
2818 
2819         /**
2820          * Define the right-hand-side of the constraint using the supplied operator.
2821          * 
2822          * @param operator the operator; may not be null
2823          * @param literal the literal value
2824          * @return the builder used to create the constraint clause, ready to be used to create other constraints clauses or
2825          *         complete already-started clauses; never null
2826          */
2827         public ConstraintBuilder is( Operator operator,
2828                                      Object literal ) {
2829             assert operator != null;
2830             Literal value = literal instanceof Literal ? (Literal)literal : new Literal(literal);
2831             return this.constraintBuilder.setConstraint(new Comparison(left, operator, value));
2832         }
2833 
2834         /**
2835          * Define the right-hand-side of the constraint using the supplied operator.
2836          * 
2837          * @param lowerBoundLiteral the literal value that represents the lower bound of the range (inclusive)
2838          * @param upperBoundLiteral the literal value that represents the upper bound of the range (inclusive)
2839          * @return the builder used to create the constraint clause, ready to be used to create other constraints clauses or
2840          *         complete already-started clauses; never null
2841          */
2842         public ConstraintBuilder isBetween( Object lowerBoundLiteral,
2843                                             Object upperBoundLiteral ) {
2844             assert lowerBoundLiteral != null;
2845             assert upperBoundLiteral != null;
2846             Literal lower = lowerBoundLiteral instanceof Literal ? (Literal)lowerBoundLiteral : new Literal(lowerBoundLiteral);
2847             Literal upper = upperBoundLiteral instanceof Literal ? (Literal)upperBoundLiteral : new Literal(upperBoundLiteral);
2848             return this.constraintBuilder.setConstraint(new Between(left, lower, upper));
2849         }
2850 
2851         /**
2852          * Define the right-hand-side of the constraint to be equivalent to the value of the supplied variable.
2853          * 
2854          * @param variableName the name of the variable
2855          * @return the builder used to create the constraint clause, ready to be used to create other constraints clauses or
2856          *         complete already-started clauses; never null
2857          */
2858         public ConstraintBuilder isEqualToVariable( String variableName ) {
2859             return isVariable(Operator.EQUAL_TO, variableName);
2860         }
2861 
2862         /**
2863          * Define the right-hand-side of the constraint to be greater than the value of the supplied variable.
2864          * 
2865          * @param variableName the name of the variable
2866          * @return the builder used to create the constraint clause, ready to be used to create other constraints clauses or
2867          *         complete already-started clauses; never null
2868          */
2869         public ConstraintBuilder isGreaterThanVariable( String variableName ) {
2870             return isVariable(Operator.GREATER_THAN, variableName);
2871         }
2872 
2873         /**
2874          * Define the right-hand-side of the constraint to be greater than or equal to the value of the supplied variable.
2875          * 
2876          * @param variableName the name of the variable
2877          * @return the builder used to create the constraint clause, ready to be used to create other constraints clauses or
2878          *         complete already-started clauses; never null
2879          */
2880         public ConstraintBuilder isGreaterThanOrEqualToVariable( String variableName ) {
2881             return isVariable(Operator.GREATER_THAN_OR_EQUAL_TO, variableName);
2882         }
2883 
2884         /**
2885          * Define the right-hand-side of the constraint to be less than the value of the supplied variable.
2886          * 
2887          * @param variableName the name of the variable
2888          * @return the builder used to create the constraint clause, ready to be used to create other constraints clauses or
2889          *         complete already-started clauses; never null
2890          */
2891         public ConstraintBuilder isLessThanVariable( String variableName ) {
2892             return isVariable(Operator.LESS_THAN, variableName);
2893         }
2894 
2895         /**
2896          * Define the right-hand-side of the constraint to be less than or equal to the value of the supplied variable.
2897          * 
2898          * @param variableName the name of the variable
2899          * @return the builder used to create the constraint clause, ready to be used to create other constraints clauses or
2900          *         complete already-started clauses; never null
2901          */
2902         public ConstraintBuilder isLessThanOrEqualToVariable( String variableName ) {
2903             return isVariable(Operator.LESS_THAN_OR_EQUAL_TO, variableName);
2904         }
2905 
2906         /**
2907          * Define the right-hand-side of the constraint to be LIKE the value of the supplied variable.
2908          * 
2909          * @param variableName the name of the variable
2910          * @return the builder used to create the constraint clause, ready to be used to create other constraints clauses or
2911          *         complete already-started clauses; never null
2912          */
2913         public ConstraintBuilder isLikeVariable( String variableName ) {
2914             return isVariable(Operator.LIKE, variableName);
2915         }
2916 
2917         /**
2918          * Define the right-hand-side of the constraint to be not equal to the value of the supplied variable.
2919          * 
2920          * @param variableName the name of the variable
2921          * @return the builder used to create the constraint clause, ready to be used to create other constraints clauses or
2922          *         complete already-started clauses; never null
2923          */
2924         public ConstraintBuilder isNotEqualToVariable( String variableName ) {
2925             return isVariable(Operator.NOT_EQUAL_TO, variableName);
2926         }
2927 
2928         /**
2929          * Define the right-hand-side of the constraint to be equivalent to the supplied literal value.
2930          * 
2931          * @param literal the literal value
2932          * @return the builder used to create the constraint clause, ready to be used to create other constraints clauses or
2933          *         complete already-started clauses; never null
2934          */
2935         public ConstraintBuilder isEqualTo( Object literal ) {
2936             return is(Operator.EQUAL_TO, literal);
2937         }
2938 
2939         /**
2940          * Define the right-hand-side of the constraint to be greater than the supplied literal value.
2941          * 
2942          * @param literal the literal value
2943          * @return the builder used to create the constraint clause, ready to be used to create other constraints clauses or
2944          *         complete already-started clauses; never null
2945          */
2946         public ConstraintBuilder isGreaterThan( Object literal ) {
2947             return is(Operator.GREATER_THAN, literal);
2948         }
2949 
2950         /**
2951          * Define the right-hand-side of the constraint to be greater than or equal to the supplied literal value.
2952          * 
2953          * @param literal the literal value
2954          * @return the builder used to create the constraint clause, ready to be used to create other constraints clauses or
2955          *         complete already-started clauses; never null
2956          */
2957         public ConstraintBuilder isGreaterThanOrEqualTo( Object literal ) {
2958             return is(Operator.GREATER_THAN_OR_EQUAL_TO, literal);
2959         }
2960 
2961         /**
2962          * Define the right-hand-side of the constraint to be less than the supplied literal value.
2963          * 
2964          * @param literal the literal value
2965          * @return the builder used to create the constraint clause, ready to be used to create other constraints clauses or
2966          *         complete already-started clauses; never null
2967          */
2968         public ConstraintBuilder isLessThan( Object literal ) {
2969             return is(Operator.LESS_THAN, literal);
2970         }
2971 
2972         /**
2973          * Define the right-hand-side of the constraint to be less than or equal to the supplied literal value.
2974          * 
2975          * @param literal the literal value
2976          * @return the builder used to create the constraint clause, ready to be used to create other constraints clauses or
2977          *         complete already-started clauses; never null
2978          */
2979         public ConstraintBuilder isLessThanOrEqualTo( Object literal ) {
2980             return is(Operator.LESS_THAN_OR_EQUAL_TO, literal);
2981         }
2982 
2983         /**
2984          * Define the right-hand-side of the constraint to be LIKE the supplied literal value.
2985          * 
2986          * @param literal the literal value
2987          * @return the builder used to create the constraint clause, ready to be used to create other constraints clauses or
2988          *         complete already-started clauses; never null
2989          */
2990         public ConstraintBuilder isLike( Object literal ) {
2991             return is(Operator.LIKE, literal);
2992         }
2993 
2994         /**
2995          * Define the right-hand-side of the constraint to be not equal to the supplied literal value.
2996          * 
2997          * @param literal the literal value
2998          * @return the builder used to create the constraint clause, ready to be used to create other constraints clauses or
2999          *         complete already-started clauses; never null
3000          */
3001         public ConstraintBuilder isNotEqualTo( Object literal ) {
3002             return is(Operator.NOT_EQUAL_TO, literal);
3003         }
3004 
3005         /**
3006          * Define the constraint as a range between a lower boundary and an upper boundary.
3007          * 
3008          * @return the interface used to specify the lower boundary boundary, the upper boundary, and which will return the
3009          *         builder interface; never null
3010          */
3011         public LowerBoundary isBetween() {
3012             return new LowerBoundary(this);
3013         }
3014     }
3015 
3016     public class AndBuilder<T> {
3017         private final T object;
3018 
3019         protected AndBuilder( T object ) {
3020             assert object != null;
3021             this.object = object;
3022         }
3023 
3024         /**
3025          * Return the component
3026          * 
3027          * @return the component; never null
3028          */
3029         public T and() {
3030             return this.object;
3031         }
3032     }
3033 }