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}