001/*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements.  See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License.  You may obtain a copy of the License at
008 *
009 *      http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017package org.apache.commons.collections4.functors;
018
019import java.io.Serializable;
020import java.util.Comparator;
021import java.util.Objects;
022
023import org.apache.commons.collections4.Predicate;
024
025/**
026 * Predicate that compares the input object with the one stored in the predicate using a comparator.
027 * In addition, the comparator result can be evaluated in accordance to a supplied criterion value.
028 *
029 * <p>In order to demonstrate the use of the predicate, the following variables are declared:</p>
030 *
031 * <pre>
032 * Integer ONE = Integer.valueOf(1);
033 * Integer TWO = Integer.valueOf(2);
034 *
035 * Comparator comparator = new Comparator() {
036 *
037 *     public int compare(Object first, Object second) {
038 *         return ((Integer) second) - ((Integer) first);
039 *     }
040 *
041 * };
042 * </pre>
043 *
044 * <p>Using the declared variables, the {@code ComparatorPredicate} can be used in the
045 * following way:</p>
046 *
047 * <pre>
048 * ComparatorPredicate.comparatorPredicate(ONE, comparator).test(TWO);
049 * </pre>
050 *
051 * <p>The input variable {@code TWO} in compared to the stored variable {@code ONE} using
052 * the supplied {@code comparator}. This is the default usage of the predicate and will return
053 * {@code true} if the underlying comparator returns {@code 0}. In addition to the default
054 * usage of the predicate, it is possible to evaluate the comparator's result in several ways. The
055 * following {@link Criterion} enumeration values are provided by the predicate:
056 * </p>
057 *
058 * <ul>
059 *     <li>EQUAL</li>
060 *     <li>GREATER</li>
061 *     <li>GREATER_OR_EQUAL</li>
062 *     <li>LESS</li>
063 *     <li>LESS_OR_EQUAL</li>
064 * </ul>
065 *
066 * <p>The following examples demonstrates how these constants can be used in order to manipulate the
067 * evaluation of a comparator result.</p>
068 *
069 * <pre>
070 * ComparatorPredicate.comparatorPredicate(ONE, comparator,<strong>ComparatorPredicate.Criterion.GREATER</strong>).test(TWO);
071 * </pre>
072 *
073 * <p>The input variable TWO is compared to the stored variable ONE using the supplied {@code comparator}
074 * using the {@code GREATER} evaluation criterion constant. This instructs the predicate to
075 * return {@code true} if the comparator returns a value greater than {@code 0}.</p>
076 *
077 * @param <T> the type of the input to the predicate.
078 * @since 4.0
079 */
080public class ComparatorPredicate<T> extends AbstractPredicate<T> implements Serializable {
081
082    /**
083     * Enumerates the comparator criteria.
084     */
085    public enum Criterion {
086
087        /**
088         * Equal criterion.
089         */
090        EQUAL,
091
092        /**
093         * Greater criterion.
094         */
095        GREATER,
096
097        /**
098         * Less criterion.
099         */
100        LESS,
101
102        /**
103         * Greater or equal criterion.
104         */
105        GREATER_OR_EQUAL,
106
107        /**
108         * Less or equal Criterion.
109         */
110        LESS_OR_EQUAL,
111    }
112
113    private static final long serialVersionUID = -1863209236504077399L;
114
115    /**
116     * Creates the comparator predicate
117     *
118     * @param <T> the type that the predicate queries
119     * @param object  the object to compare to
120     * @param comparator  the comparator to use for comparison
121     * @return the predicate
122     * @throws NullPointerException if comparator is null
123     */
124    public static <T> Predicate<T> comparatorPredicate(final T object, final Comparator<T> comparator) {
125        return comparatorPredicate(object, comparator, Criterion.EQUAL);
126    }
127
128    /**
129     * Creates the comparator predicate
130     *
131     * @param <T> the type that the predicate queries
132     * @param object  the object to compare to
133     * @param comparator  the comparator to use for comparison
134     * @param criterion  the criterion to use to evaluate comparison
135     * @return the predicate
136     * @throws NullPointerException if comparator or criterion is null
137     */
138    public static <T> Predicate<T> comparatorPredicate(final T object, final Comparator<T> comparator,
139                                                       final Criterion criterion) {
140        return new ComparatorPredicate<>(object, Objects.requireNonNull(comparator, "comparator"),
141                Objects.requireNonNull(criterion, "criterion"));
142    }
143
144    /** The internal object to compare with */
145    private final T object;
146
147    /** The comparator to use for comparison */
148    private final Comparator<T> comparator;
149
150    /** The comparison evaluation criterion to use */
151    private final Criterion criterion;
152
153    /**
154     * Constructor that performs no validation.
155     * Use {@code comparatorPredicate} if you want that.
156     *
157     * @param object  the object to compare to
158     * @param comparator  the comparator to use for comparison
159     * @param criterion  the criterion to use to evaluate comparison
160     */
161    public ComparatorPredicate(final T object, final Comparator<T> comparator, final Criterion criterion) {
162        this.object = object;
163        this.comparator = comparator;
164        this.criterion = criterion;
165    }
166
167    /**
168     * Evaluates the predicate. The predicate evaluates to {@code true} in the following cases:
169     *
170     * <ul>
171     * <li>{@code comparator.compare(object, input) == 0 &amp;&amp; criterion == EQUAL}</li>
172     * <li>{@code comparator.compare(object, input) &lt; 0 &amp;&amp; criterion == LESS}</li>
173     * <li>{@code comparator.compare(object, input) &gt; 0 &amp;&amp; criterion == GREATER}</li>
174     * <li>{@code comparator.compare(object, input) &gt;= 0 &amp;&amp; criterion == GREATER_OR_EQUAL}</li>
175     * <li>{@code comparator.compare(object, input) &lt;= 0 &amp;&amp; criterion == LESS_OR_EQUAL}</li>
176     * </ul>
177     *
178     * @see org.apache.commons.collections4.Predicate#test(Object)
179     * @see java.util.Comparator#compare(Object first, Object second)
180     * @param target  the target object to compare to
181     * @return {@code true} if the comparison succeeds according to the selected criterion
182     * @throws IllegalStateException if the criterion is invalid (really not possible)
183     */
184    @Override
185    public boolean test(final T target) {
186
187        boolean result = false;
188        final int comparison = comparator.compare(object, target);
189        switch (criterion) {
190        case EQUAL:
191            result = comparison == 0;
192            break;
193        case GREATER:
194            result = comparison > 0;
195            break;
196        case LESS:
197            result = comparison < 0;
198            break;
199        case GREATER_OR_EQUAL:
200            result = comparison >= 0;
201            break;
202        case LESS_OR_EQUAL:
203            result = comparison <= 0;
204            break;
205        default:
206            throw new IllegalStateException("The current criterion '" + criterion + "' is invalid.");
207        }
208
209        return result;
210    }
211}