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