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