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}