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("table").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("table1").join("table2").on("table2.c0=table1.c0").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("table1 AS t1").join("table2 AS t2").on("t1.c0=t2.c0").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("table1 AS t1")
173 * .innerJoin("table2 AS t2")
174 * .on("t1.c0=t2.c0")
175 * .innerJoin("table3 AS t3")
176 * .on("t1.c1=t3.c1")
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("table1").union().selectStar().from("table2").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("t1.c1","t1.c2","t2.c3",)
218 * .from("table1 AS t1")
219 * .innerJoin("table2 AS t2")
220 * .on("t1.c0=t2.c0")
221 * .union()
222 * .select("t3.c1","t3.c2","t4.c3",)
223 * .from("table3 AS t3")
224 * .innerJoin("table4 AS t4")
225 * .on("t3.c0=t4.c0")
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 }