001/* 002 * The contents of this file are subject to the terms of the Common Development and 003 * Distribution License (the License). You may not use this file except in compliance with the License. 004 * 005 * You can obtain a copy of the License at legal/CDDLv1.0.txt. See the License for the 006 * specific language governing permission and limitations under the License. 007 * 008 * When distributing Covered Software, include this CDDL Header Notice in each file and include 009 * the License file at legal/CDDLv1.0.txt. If applicable, add the following below the CDDL 010 * Header, with the fields enclosed by brackets [] replaced by your own identifying 011 * information: "Portions copyright [year] [name of copyright owner]". 012 * 013 * Copyright 2015 ForgeRock AS. 014 */ 015 016package org.forgerock.util.query; 017 018import java.util.ArrayList; 019import java.util.Arrays; 020import java.util.Collection; 021import java.util.Collections; 022import java.util.List; 023 024/** 025 * A filter which can be used to select resources, which is compatible with the CREST query filters: 026 * 027 * @param <F> 028 * The type of the field specification. 029 */ 030public class QueryFilter<F> { 031 032 /** 033 * @param <FF> The type of the field specification. Named to be distinct from the type of the parent class. 034 */ 035 private static final class AndImpl<FF> extends Impl<FF> { 036 private final List<QueryFilter<FF>> subFilters; 037 038 private AndImpl(final List<QueryFilter<FF>> subFilters) { 039 this.subFilters = subFilters; 040 } 041 042 @Override 043 public boolean equals(final Object obj) { 044 if (this == obj) { 045 return true; 046 } else if (obj instanceof AndImpl) { 047 return subFilters.equals(((AndImpl<?>) obj).subFilters); 048 } else { 049 return false; 050 } 051 } 052 053 @Override 054 public int hashCode() { 055 return subFilters.hashCode(); 056 } 057 058 @Override 059 protected <R, P> R accept(final QueryFilterVisitor<R, P, FF> v, final P p) { 060 return v.visitAndFilter(p, subFilters); 061 } 062 063 @Override 064 public String toString() { 065 StringBuilder builder = new StringBuilder(); 066 builder.append("And("); 067 boolean isFirst = true; 068 for (final QueryFilter<FF> subFilter : subFilters) { 069 if (isFirst) { 070 isFirst = false; 071 } else { 072 builder.append(","); 073 } 074 builder.append(subFilter.pimpl.toString()); 075 } 076 builder.append(')'); 077 return builder.toString(); 078 } 079 } 080 081 /** 082 * @param <FF> The type of the field specification. Named to be distinct from the type of the parent class. 083 */ 084 private static final class BooleanLiteralImpl<FF> extends Impl<FF> { 085 private final boolean value; 086 087 private BooleanLiteralImpl(final boolean value) { 088 this.value = value; 089 } 090 091 @Override 092 public boolean equals(final Object obj) { 093 if (this == obj) { 094 return true; 095 } else if (obj instanceof BooleanLiteralImpl) { 096 return value == ((BooleanLiteralImpl<?>) obj).value; 097 } else { 098 return false; 099 } 100 } 101 102 @Override 103 public int hashCode() { 104 return Boolean.valueOf(value).hashCode(); 105 } 106 107 @Override 108 protected <R, P> R accept(final QueryFilterVisitor<R, P, FF> v, final P p) { 109 return v.visitBooleanLiteralFilter(p, value); 110 } 111 112 @Override 113 public String toString() { 114 return "BooleanLiteral(" + value + ")"; 115 } 116 } 117 118 /* 119 * TODO: should value assertions be Objects or Strings? Objects allows use 120 * of numbers, during construction, but visitors may need to handle 121 * different types (e.g. Date or String representation of a date). 122 */ 123 124 /** 125 * @param <FF> The type of the field specification. Named to be distinct from the type of the parent class. 126 */ 127 private static abstract class ComparatorImpl<FF> extends Impl<FF> { 128 protected final FF field; 129 protected final Object valueAssertion; 130 131 protected ComparatorImpl(final FF field, final Object valueAssertion) { 132 this.field = field; 133 this.valueAssertion = valueAssertion; 134 } 135 136 @Override 137 public final boolean equals(final Object obj) { 138 if (this == obj) { 139 return true; 140 } else if (obj instanceof ComparatorImpl) { 141 final ComparatorImpl<?> o = (ComparatorImpl<?>) obj; 142 return field.equals(o.field) && getClass().equals(o.getClass()) 143 && valueAssertion.equals(o.valueAssertion); 144 } else { 145 return false; 146 } 147 } 148 149 @Override 150 public int hashCode() { 151 return (field.hashCode() * 31 + getClass().hashCode()) * 31 152 + valueAssertion.hashCode(); 153 } 154 155 @Override 156 public String toString() { 157 return getClass().getSimpleName() + "[" + field.toString() + "," + valueAssertion + "]"; 158 } 159 } 160 161 /** 162 * @param <FF> The type of the field specification. Named to be distinct from the type of the parent class. 163 */ 164 private static final class ContainsImpl<FF> extends ComparatorImpl<FF> { 165 private ContainsImpl(final FF field, final Object valueAssertion) { 166 super(field, valueAssertion); 167 } 168 169 @Override 170 protected <R, P> R accept(final QueryFilterVisitor<R, P, FF> v, final P p) { 171 return v.visitContainsFilter(p, field, valueAssertion); 172 } 173 174 } 175 176 /** 177 * @param <FF> The type of the field specification. Named to be distinct from the type of the parent class. 178 */ 179 private static final class EqualsImpl<FF> extends ComparatorImpl<FF> { 180 private EqualsImpl(final FF field, final Object valueAssertion) { 181 super(field, valueAssertion); 182 } 183 184 @Override 185 protected <R, P> R accept(final QueryFilterVisitor<R, P, FF> v, final P p) { 186 return v.visitEqualsFilter(p, field, valueAssertion); 187 } 188 189 } 190 191 /** 192 * @param <FF> The type of the field specification. Named to be distinct from the type of the parent class. 193 */ 194 private static final class ExtendedMatchImpl<FF> extends ComparatorImpl<FF> { 195 private final String operator; 196 197 private ExtendedMatchImpl(final FF field, final String operator, 198 final Object valueAssertion) { 199 super(field, valueAssertion); 200 this.operator = operator; 201 } 202 203 @Override 204 protected <R, P> R accept(final QueryFilterVisitor<R, P, FF> v, final P p) { 205 return v.visitExtendedMatchFilter(p, field, operator, valueAssertion); 206 } 207 208 public int hashCode() { 209 return (field.hashCode() * 31 + operator.hashCode()) * 31 210 + valueAssertion.hashCode(); 211 } 212 213 @Override 214 public String toString() { 215 return getClass().getSimpleName() + "[" + field.toString() + "," + operator + "," + valueAssertion + "]"; 216 } 217 } 218 219 /** 220 * @param <FF> The type of the field specification. Named to be distinct from the type of the parent class. 221 */ 222 private static final class GreaterThanImpl<FF> extends ComparatorImpl<FF> { 223 private GreaterThanImpl(final FF field, final Object valueAssertion) { 224 super(field, valueAssertion); 225 } 226 227 @Override 228 protected <R, P> R accept(final QueryFilterVisitor<R, P, FF> v, final P p) { 229 return v.visitGreaterThanFilter(p, field, valueAssertion); 230 } 231 232 } 233 234 /** 235 * @param <FF> The type of the field specification. Named to be distinct from the type of the parent class. 236 */ 237 private static final class GreaterThanOrEqualToImpl<FF> extends ComparatorImpl<FF> { 238 private GreaterThanOrEqualToImpl(final FF field, final Object valueAssertion) { 239 super(field, valueAssertion); 240 } 241 242 @Override 243 protected <R, P> R accept(final QueryFilterVisitor<R, P, FF> v, final P p) { 244 return v.visitGreaterThanOrEqualToFilter(p, field, valueAssertion); 245 } 246 247 } 248 249 /** 250 * @param <FF> The type of the field specification. Named to be distinct from the type of the parent class. 251 */ 252 private static abstract class Impl<FF> { 253 protected Impl() { 254 // Nothing to do. 255 } 256 257 @Override 258 public abstract boolean equals(Object obj); 259 260 @Override 261 public abstract int hashCode(); 262 263 protected abstract <R, P> R accept(QueryFilterVisitor<R, P, FF> v, P p); 264 265 } 266 267 /** 268 * @param <FF> The type of the field specification. Named to be distinct from the type of the parent class. 269 */ 270 private static final class LessThanImpl<FF> extends ComparatorImpl<FF> { 271 private LessThanImpl(final FF field, final Object valueAssertion) { 272 super(field, valueAssertion); 273 } 274 275 @Override 276 protected <R, P> R accept(final QueryFilterVisitor<R, P, FF> v, final P p) { 277 return v.visitLessThanFilter(p, field, valueAssertion); 278 } 279 280 } 281 282 /** 283 * @param <FF> The type of the field specification. Named to be distinct from the type of the parent class. 284 */ 285 private static final class LessThanOrEqualToImpl<FF> extends ComparatorImpl<FF> { 286 private LessThanOrEqualToImpl(final FF field, final Object valueAssertion) { 287 super(field, valueAssertion); 288 } 289 290 @Override 291 protected <R, P> R accept(final QueryFilterVisitor<R, P, FF> v, final P p) { 292 return v.visitLessThanOrEqualToFilter(p, field, valueAssertion); 293 } 294 295 } 296 297 /** 298 * @param <FF> The type of the field specification. Named to be distinct from the type of the parent class. 299 */ 300 private static final class NotImpl<FF> extends Impl<FF> { 301 private final QueryFilter<FF> subFilter; 302 303 private NotImpl(final QueryFilter<FF> subFilter) { 304 this.subFilter = subFilter; 305 } 306 307 @Override 308 public boolean equals(final Object obj) { 309 if (this == obj) { 310 return true; 311 } else if (obj instanceof NotImpl) { 312 return subFilter.equals(((NotImpl<?>) obj).subFilter); 313 } else { 314 return false; 315 } 316 } 317 318 @Override 319 public int hashCode() { 320 return subFilter.hashCode(); 321 } 322 323 @Override 324 protected <R, P> R accept(final QueryFilterVisitor<R, P, FF> v, final P p) { 325 return v.visitNotFilter(p, subFilter); 326 } 327 328 @Override 329 public String toString() { 330 // This is not officially supported in SCIM. 331 return "Not(" + subFilter.pimpl.toString() + ")"; 332 } 333 } 334 335 /** 336 * @param <FF> The type of the field specification. Named to be distinct from the type of the parent class. 337 */ 338 private static final class OrImpl<FF> extends Impl<FF> { 339 private final List<QueryFilter<FF>> subFilters; 340 341 private OrImpl(final List<QueryFilter<FF>> subFilters) { 342 this.subFilters = subFilters; 343 } 344 345 @Override 346 public boolean equals(final Object obj) { 347 if (this == obj) { 348 return true; 349 } else if (obj instanceof OrImpl) { 350 return subFilters.equals(((OrImpl<?>) obj).subFilters); 351 } else { 352 return false; 353 } 354 } 355 356 @Override 357 public int hashCode() { 358 return subFilters.hashCode(); 359 } 360 361 @Override 362 protected <R, P> R accept(final QueryFilterVisitor<R, P, FF> v, final P p) { 363 return v.visitOrFilter(p, subFilters); 364 } 365 366 @Override 367 public String toString() { 368 StringBuilder builder = new StringBuilder(); 369 builder.append("Or("); 370 boolean isFirst = true; 371 for (final QueryFilter<FF> subFilter : subFilters) { 372 if (isFirst) { 373 isFirst = false; 374 } else { 375 builder.append(","); 376 } 377 builder.append(subFilter.pimpl.toString()); 378 } 379 builder.append(')'); 380 return builder.toString(); 381 } 382 } 383 384 /** 385 * @param <FF> The type of the field specification. Named to be distinct from the type of the parent class. 386 */ 387 private static final class PresentImpl<FF> extends Impl<FF> { 388 private final FF field; 389 390 private PresentImpl(final FF field) { 391 this.field = field; 392 } 393 394 @Override 395 public boolean equals(final Object obj) { 396 if (this == obj) { 397 return true; 398 } else if (obj instanceof PresentImpl) { 399 final PresentImpl<?> o = (PresentImpl<?>) obj; 400 return field.equals(o.field); 401 } else { 402 return false; 403 } 404 } 405 406 @Override 407 public int hashCode() { 408 return field.hashCode(); 409 } 410 411 @Override 412 protected <R, P> R accept(final QueryFilterVisitor<R, P, FF> v, final P p) { 413 return v.visitPresentFilter(p, field); 414 } 415 416 @Override 417 public String toString() { 418 return field.toString() + " pr"; 419 } 420 } 421 422 /** 423 * @param <FF> The type of the field specification. Named to be distinct from the type of the parent class. 424 */ 425 private static final class StartsWithImpl<FF> extends ComparatorImpl<FF> { 426 private StartsWithImpl(final FF field, final Object valueAssertion) { 427 super(field, valueAssertion); 428 } 429 430 @Override 431 protected <R, P> R accept(final QueryFilterVisitor<R, P, FF> v, final P p) { 432 return v.visitStartsWithFilter(p, field, valueAssertion); 433 } 434 435 } 436 437 /** 438 * Returns a filter which does not match any resources. 439 * 440 * @return A filter which does not match any resources. 441 * 442 * @param <FF> The type of the field specification. Named to be distinct from the type of the parent class. 443 */ 444 @SuppressWarnings("unchecked") 445 public static <FF> QueryFilter<FF> alwaysFalse() { 446 return ALWAYS_FALSE; 447 } 448 449 /** 450 * Returns a filter which matches all resources. 451 * 452 * @return A filter which matches all resources. 453 * 454 * @param <FF> The type of the field specification. Named to be distinct from the type of the parent class. 455 */ 456 @SuppressWarnings("unchecked") 457 public static <FF> QueryFilter<FF> alwaysTrue() { 458 return ALWAYS_TRUE; 459 } 460 461 /** 462 * Creates a new {@code and} filter using the provided list of sub-filters. 463 * <p> 464 * Creating a new {@code and} filter with a {@code null} or empty list of 465 * sub-filters is equivalent to calling {@link #alwaysTrue()}. 466 * 467 * @param subFilters 468 * The list of sub-filters, may be empty or {@code null}. 469 * @return The newly created {@code and} filter. 470 * 471 * @param <FF> The type of the field specification. Named to be distinct from the type of the parent class. 472 */ 473 public static <FF> QueryFilter<FF> and(final Collection<QueryFilter<FF>> subFilters) { 474 switch (subFilters.size()) { 475 case 0: 476 return alwaysTrue(); 477 case 1: 478 return subFilters.iterator().next(); 479 default: 480 return new QueryFilter<>(new AndImpl<>(Collections.unmodifiableList(new ArrayList<>(subFilters)))); 481 } 482 } 483 484 /** 485 * Creates a new {@code and} filter using the provided list of sub-filters. 486 * <p> 487 * Creating a new {@code and} filter with a {@code null} or empty list of 488 * sub-filters is equivalent to calling {@link #alwaysTrue()}. 489 * 490 * @param subFilters 491 * The list of sub-filters, may be empty or {@code null}. 492 * @return The newly created {@code and} filter. 493 * 494 * @param <FF> The type of the field specification. Named to be distinct from the type of the parent class. 495 */ 496 @SafeVarargs 497 public static <FF> QueryFilter<FF> and(final QueryFilter<FF>... subFilters) { 498 return and(Arrays.asList(subFilters)); 499 } 500 501 /** 502 * Creates a new generic comparison filter using the provided field name, 503 * operator, and value assertion. When the provided operator name represents 504 * a core operator, e.g. "eq", then this method is equivalent to calling the 505 * equivalent constructor, e.g. {@link #equalTo(Object, Object)}. 506 * Otherwise, when the operator name does not correspond to a core operator, 507 * an extended comparison filter will be returned. 508 * 509 * @param field 510 * The name of field within the JSON resource to be compared. 511 * @param operator 512 * The operator to use for the comparison, which must be one of 513 * the core operator names, or a string matching the regular 514 * expression {@code [a-zA-Z_0-9.]+}. 515 * @param valueAssertion 516 * The assertion value. 517 * @return The newly created generic comparison filter. 518 * @throws IllegalArgumentException 519 * If {@code operator} is not a valid operator name. 520 */ 521 public static <FF> QueryFilter<FF> comparisonFilter(final FF field, final String operator, 522 final Object valueAssertion) { 523 if (operator.equalsIgnoreCase("eq")) { 524 return QueryFilter.equalTo(field, valueAssertion); 525 } else if (operator.equalsIgnoreCase("gt")) { 526 return QueryFilter.greaterThan(field, valueAssertion); 527 } else if (operator.equalsIgnoreCase("ge")) { 528 return QueryFilter.greaterThanOrEqualTo(field, valueAssertion); 529 } else if (operator.equalsIgnoreCase("lt")) { 530 return QueryFilter.lessThan(field, valueAssertion); 531 } else if (operator.equalsIgnoreCase("le")) { 532 return QueryFilter.lessThanOrEqualTo(field, valueAssertion); 533 } else if (operator.equalsIgnoreCase("co")) { 534 return QueryFilter.contains(field, valueAssertion); 535 } else if (operator.equalsIgnoreCase("sw")) { 536 return QueryFilter.startsWith(field, valueAssertion); 537 } else if (operator.matches("[a-zA-Z_0-9.]+")) { 538 return new QueryFilter<>(new ExtendedMatchImpl<>(field, operator, valueAssertion)); 539 } else { 540 throw new IllegalArgumentException("\"" + operator 541 + "\" is not a valid filter operator"); 542 } 543 } 544 545 /** 546 * Creates a new {@code contains} filter using the provided field name and 547 * value assertion. This method is used to check that the string representation 548 * of the field contains the provided substring. When operating on a collection 549 * of values the operation should be evaluated on each element in the collection, 550 * passing if any of the element's string representations contain the provided 551 * substring. 552 * 553 * @param field 554 * The name of field to be compared. 555 * @param valueAssertion 556 * The assertion value. 557 * @return The newly created {@code contains} filter. 558 * 559 * @param <FF> The type of the field specification. Named to be distinct from the type of the parent class. 560 */ 561 public static <FF> QueryFilter<FF> contains(final FF field, final Object valueAssertion) { 562 return new QueryFilter<>(new ContainsImpl<>(field, valueAssertion)); 563 } 564 565 /** 566 * Creates a new {@code equality} filter using the provided field name and 567 * value assertion. This would represent either equality for single values, or 568 * contains and equal value for a collection of values. 569 * 570 * @param field 571 * The name of field to be compared. 572 * @param valueAssertion 573 * The assertion value. 574 * @return The newly created {@code equality} filter. 575 * 576 * @param <FF> The type of the field specification. Named to be distinct from the type of the parent class. 577 */ 578 public static <FF> QueryFilter<FF> equalTo(final FF field, final Object valueAssertion) { 579 return new QueryFilter<>(new EqualsImpl<>(field, valueAssertion)); 580 } 581 582 /** 583 * Creates a new {@code greater than} filter using the provided field name 584 * and value assertion. This method is used to check that the value of the field 585 * is greater than the provided value. When operating on a collection of values 586 * the operation should be evaluated on each element in the collection, passing 587 * if any of the element's values are greater than the provided value. 588 * 589 * @param field 590 * The name of field to be compared. 591 * @param valueAssertion 592 * The assertion value. 593 * @return The newly created {@code greater than} filter. 594 * 595 * @param <FF> The type of the field specification. Named to be distinct from the type of the parent class. 596 */ 597 public static <FF> QueryFilter<FF> greaterThan(final FF field, final Object valueAssertion) { 598 return new QueryFilter<>(new GreaterThanImpl<>(field, valueAssertion)); 599 } 600 601 /** 602 * Creates a new {@code greater than or equal to} filter using the provided 603 * field name and value assertion. This method is used to check that the value 604 * of the field is greater than or equal to the provided value. When operating 605 * on a collection of values the operation should be evaluated on each element 606 * in the collection, passing if any of the element's values are greater than 607 * or equal to the provided value. 608 * 609 * @param field 610 * The name of field to be compared. 611 * @param valueAssertion 612 * The assertion value. 613 * @return The newly created {@code greater than or equal to} filter. 614 * 615 * @param <FF> The type of the field specification. Named to be distinct from the type of the parent class. 616 */ 617 public static <FF> QueryFilter<FF> greaterThanOrEqualTo(final FF field, 618 final Object valueAssertion) { 619 return new QueryFilter<>(new GreaterThanOrEqualToImpl<>(field, valueAssertion)); 620 } 621 622 /** 623 * Creates a new {@code less than} filter using the provided field name and 624 * value assertion. This method is used to check that the value of the field 625 * is less than the provided value. When operating on a collection of values 626 * the operation should be evaluated on each element in the collection, passing 627 * if any of the element's values are less than the provided value. 628 * 629 * @param field 630 * The name of field to be compared. 631 * @param valueAssertion 632 * The assertion value. 633 * @return The newly created {@code less than} filter. 634 * 635 * @param <FF> The type of the field specification. Named to be distinct from the type of the parent class. 636 */ 637 public static <FF> QueryFilter<FF> lessThan(final FF field, final Object valueAssertion) { 638 return new QueryFilter<>(new LessThanImpl<>(field, valueAssertion)); 639 } 640 641 /** 642 * Creates a new {@code extended match} filter using the provided 643 * field name, operator and value assertion. 644 * 645 * @param field 646 * The name of field to be compared. 647 * @param operator 648 * The operator. 649 * @param valueAssertion 650 * The assertion value. 651 * @return The newly created {@code less than or equal to} filter. 652 * 653 * @param <FF> The type of the field specification. Named to be distinct from the type of the parent class. 654 */ 655 public static <FF> QueryFilter<FF> extendedMatch(final FF field, final String operator, final Object valueAssertion) { 656 return new QueryFilter<>(new ExtendedMatchImpl<>(field, operator, valueAssertion)); 657 } 658 659 /** 660 * Creates a new {@code less than or equal to} filter using the provided 661 * field name and value assertion. This method is used to check that the value 662 * of the field is less than or equal to the provided value. When operating 663 * on a collection of values the operation should be evaluated on each element 664 * in the collection, passing if any of the element's values are less than 665 * or equal to the provided value. 666 * 667 * @param field 668 * The name of field to be compared. 669 * @param valueAssertion 670 * The assertion value. 671 * @return The newly created {@code less than or equal to} filter. 672 * 673 * @param <FF> The type of the field specification. Named to be distinct from the type of the parent class. 674 */ 675 public static <FF> QueryFilter<FF> lessThanOrEqualTo(final FF field, final Object valueAssertion) { 676 return new QueryFilter<>(new LessThanOrEqualToImpl<>(field, valueAssertion)); 677 } 678 679 /** 680 * Creates a new {@code not} filter using the provided sub-filter. 681 * 682 * @param subFilter 683 * The sub-filter. 684 * @return The newly created {@code not} filter. 685 * 686 * @param <FF> The type of the field specification. Named to be distinct from the type of the parent class. 687 */ 688 public static <FF> QueryFilter<FF> not(final QueryFilter<FF> subFilter) { 689 return new QueryFilter<>(new NotImpl<>(subFilter)); 690 } 691 692 /** 693 * Creates a new {@code or} filter using the provided list of sub-filters. 694 * <p> 695 * Creating a new {@code or} filter with a {@code null} or empty list of 696 * sub-filters is equivalent to calling {@link #alwaysFalse()}. 697 * 698 * @param subFilters 699 * The list of sub-filters, may be empty or {@code null}. 700 * @return The newly created {@code or} filter. 701 * 702 * @param <FF> The type of the field specification. Named to be distinct from the type of the parent class. 703 */ 704 public static <FF> QueryFilter<FF> or(final Collection<QueryFilter<FF>> subFilters) { 705 switch (subFilters.size()) { 706 case 0: 707 return alwaysFalse(); 708 case 1: 709 return subFilters.iterator().next(); 710 default: 711 return new QueryFilter<>(new OrImpl<>(Collections.unmodifiableList(new ArrayList<>(subFilters)))); 712 } 713 } 714 715 /** 716 * Creates a new {@code or} filter using the provided list of sub-filters. 717 * <p> 718 * Creating a new {@code or} filter with a {@code null} or empty list of 719 * sub-filters is equivalent to calling {@link #alwaysFalse()}. 720 * 721 * @param subFilters 722 * The list of sub-filters, may be empty or {@code null}. 723 * @return The newly created {@code or} filter. 724 * 725 * @param <FF> The type of the field specification. Named to be distinct from the type of the parent class. 726 */ 727 @SafeVarargs 728 public static <FF> QueryFilter<FF> or(final QueryFilter<FF>... subFilters) { 729 return or(Arrays.asList(subFilters)); 730 } 731 732 /** 733 * Creates a new {@code presence} filter using the provided field name. 734 * 735 * @param field 736 * The name of field which must be 737 * present. 738 * @return The newly created {@code presence} filter. 739 * 740 * @param <FF> The type of the field specification. Named to be distinct from the type of the parent class. 741 */ 742 public static <FF> QueryFilter<FF> present(final FF field) { 743 return new QueryFilter<>(new PresentImpl<>(field)); 744 } 745 746 /** 747 * Creates a new {@code starts with} filter using the provided field name 748 * and value assertion. This method is used to check that the string representation 749 * of the field starts with the provided substring. When operating on a collection 750 * of values the operation should be evaluated on each element in the collection, 751 * passing if any of the element's string representations starts with the provided 752 * substring. 753 * 754 * @param field 755 * The name of field to be compared. 756 * @param valueAssertion 757 * The assertion value. 758 * @return The newly created {@code starts with} filter. 759 * 760 * @param <FF> The type of the field specification. Named to be distinct from the type of the parent class. 761 */ 762 public static <FF> QueryFilter<FF> startsWith(final FF field, final Object valueAssertion) { 763 return new QueryFilter<>(new StartsWithImpl<>(field, valueAssertion)); 764 } 765 766 private static final QueryFilterVisitor<StringBuilder, StringBuilder, Object> TO_STRING_VISITOR = 767 new QueryFilterVisitor<StringBuilder, StringBuilder, Object>() { 768 @Override 769 public StringBuilder visitAndFilter(StringBuilder builder, List<QueryFilter<Object>> subFilters) { 770 return visitCompositeFilter(" and ", builder, subFilters); 771 } 772 773 @Override 774 public StringBuilder visitOrFilter(StringBuilder builder, List<QueryFilter<Object>> subFilters) { 775 return visitCompositeFilter(" or ", builder, subFilters); 776 } 777 778 public StringBuilder visitCompositeFilter(String operation, StringBuilder builder, List<QueryFilter<Object>> subFilters) { 779 builder.append('('); 780 boolean first = true; 781 for (QueryFilter<Object> subfilter : subFilters) { 782 if (!first) { 783 builder.append(operation); 784 } else { 785 first = false; 786 } 787 subfilter.accept(this, builder); 788 } 789 return builder.append(')'); 790 } 791 792 @Override 793 public StringBuilder visitBooleanLiteralFilter(StringBuilder builder, boolean value) { 794 builder.append(value); 795 return builder; 796 } 797 798 @Override 799 public StringBuilder visitContainsFilter(StringBuilder builder, Object field, Object valueAssertion) { 800 return visitExtendedMatchFilter(builder, field, "co", valueAssertion); 801 } 802 803 @Override 804 public StringBuilder visitEqualsFilter(StringBuilder builder, Object field, Object valueAssertion) { 805 return visitExtendedMatchFilter(builder, field, "eq", valueAssertion); 806 } 807 808 @Override 809 public StringBuilder visitExtendedMatchFilter(StringBuilder builder, Object field, String operator, Object valueAssertion) { 810 builder.append(field.toString()).append(" ").append(operator).append(" "); 811 if (valueAssertion instanceof Boolean || valueAssertion instanceof Number) { 812 // No need for quotes. 813 builder.append(valueAssertion); 814 } else { 815 builder.append('"'); 816 builder.append(valueAssertion); 817 builder.append('"'); 818 } 819 return builder; 820 } 821 822 @Override 823 public StringBuilder visitGreaterThanFilter(StringBuilder builder, Object field, Object valueAssertion) { 824 return visitExtendedMatchFilter(builder, field, "gt", valueAssertion); 825 } 826 827 @Override 828 public StringBuilder visitGreaterThanOrEqualToFilter(StringBuilder builder, Object field, Object valueAssertion) { 829 return visitExtendedMatchFilter(builder, field, "ge", valueAssertion); 830 } 831 832 @Override 833 public StringBuilder visitLessThanFilter(StringBuilder builder, Object field, Object valueAssertion) { 834 return visitExtendedMatchFilter(builder, field, "lt", valueAssertion); 835 } 836 837 @Override 838 public StringBuilder visitLessThanOrEqualToFilter(StringBuilder builder, Object field, Object valueAssertion) { 839 return visitExtendedMatchFilter(builder, field, "le", valueAssertion); 840 } 841 842 @Override 843 public StringBuilder visitStartsWithFilter(StringBuilder builder, Object field, Object valueAssertion) { 844 return visitExtendedMatchFilter(builder, field, "sw", valueAssertion); 845 } 846 847 @Override 848 public StringBuilder visitNotFilter(StringBuilder builder, QueryFilter<Object> subFilter) { 849 builder.append("! ("); 850 subFilter.accept(this, builder); 851 return builder.append(')'); 852 } 853 854 @Override 855 public StringBuilder visitPresentFilter(StringBuilder builder, Object field) { 856 return builder.append(field.toString()).append(" pr"); 857 } 858 }; 859 860 @SuppressWarnings({"unchecked", "rawtypes"}) 861 private static final QueryFilter ALWAYS_FALSE = new QueryFilter(new BooleanLiteralImpl(false)); 862 @SuppressWarnings({"unchecked", "rawtypes"}) 863 private static final QueryFilter ALWAYS_TRUE = new QueryFilter(new BooleanLiteralImpl(true)); 864 865 protected final Impl<F> pimpl; 866 private final QueryFilterVisitor<StringBuilder, StringBuilder, F> toStringVisitor; 867 868 @SuppressWarnings("unchecked") 869 protected QueryFilter(final Impl<F> pimpl) { 870 this(pimpl, (QueryFilterVisitor<StringBuilder, StringBuilder, F>) TO_STRING_VISITOR); 871 } 872 873 protected QueryFilter(final Impl<F> pimpl, QueryFilterVisitor<StringBuilder, StringBuilder, F> toStringVisitor) { 874 this.pimpl = pimpl; 875 this.toStringVisitor = toStringVisitor; 876 } 877 878 /** 879 * Applies a {@code QueryFilterVisitor} to this {@code QueryFilter}. 880 * 881 * @param <R> 882 * The return type of the visitor's methods. 883 * @param <P> 884 * The type of the additional parameters to the visitor's 885 * methods. 886 * @param v 887 * The filter visitor. 888 * @param p 889 * Optional additional visitor parameter. 890 * @return A result as specified by the visitor. 891 */ 892 public <R, P> R accept(final QueryFilterVisitor<R, P, F> v, final P p) { 893 return pimpl.accept(v, p); 894 } 895 896 @Override 897 public boolean equals(final Object obj) { 898 if (this == obj) { 899 return true; 900 } else if (obj instanceof QueryFilter) { 901 return pimpl.equals(((QueryFilter<?>) obj).pimpl); 902 } else { 903 return false; 904 } 905 } 906 907 @Override 908 public int hashCode() { 909 return pimpl.hashCode(); 910 } 911 912 /** 913 * Returns the string representation of this query filter. The string 914 * representation is defined to be similar to that of SCIM's, with the 915 * following differences: 916 * <ul> 917 * <li>field references are JSON pointers 918 * <li>support for boolean literal expressions, e.g. {@code (true)} 919 * <li>support for the logical not operator, e.g. 920 * {@code (! /role eq "user")} 921 * </ul> 922 * 923 * @return The string representation of this query filter. 924 */ 925 @Override 926 public String toString() { 927 return accept(toStringVisitor, new StringBuilder()).toString(); 928 } 929 930}