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;
021
022import org.apache.commons.collections4.Predicate;
023
024/**
025 * Predicate that compares the input object with the one stored in the predicate using a comparator.
026 * In addition, the comparator result can be evaluated in accordance to a supplied criterion value.
027 *
028 * <p>In order to demonstrate the use of the predicate, the following variables are declared:</p>
029 *
030 * <pre>
031 * Integer ONE = Integer.valueOf(1);
032 * Integer TWO = Integer.valueOf(2);
033 *
034 * Comparator comparator = new Comparator() {
035 *
036 *     public int compare(Object first, Object second) {
037 *         return ((Integer) second) - ((Integer) first);
038 *     }
039 *
040 * };
041 * </pre>
042 *
043 * <p>Using the declared variables, the <code>ComparatorPredicate</code> can be used used in the
044 * following way:</p>
045 *
046 * <pre>
047 * ComparatorPredicate.comparatorPredicate(ONE, comparator).evaluate(TWO);
048 * </pre>
049 *
050 * <p>The input variable <code>TWO</code> in compared to the stored variable <code>ONE</code> using
051 * the supplied <code>comparator</code>. This is the default usage of the predicate and will return
052 * <code>true</code> if the underlying comparator returns <code>0</code>. In addition to the default
053 * usage of the predicate, it is possible to evaluate the comparator's result in several ways. The
054 * following {@link Criterion} enumeration values are provided by the predicate:
055 * </p>
056 *
057 * <ul>
058 *     <li>EQUAL</li>
059 *     <li>GREATER</li>
060 *     <li>GREATER_OR_EQUAL</li>
061 *     <li>LESS</li>
062 *     <li>LESS_OR_EQUAL</li>
063 * </ul>
064 *
065 * <p>The following examples demonstrates how these constants can be used in order to manipulate the
066 * evaluation of a comparator result.</p>
067 *
068 * <pre>
069 * ComparatorPredicate.comparatorPredicate(ONE, comparator,<b>ComparatorPredicate.Criterion.GREATER</b>).evaluate(TWO);
070 * </pre>
071 *
072 * <p>The input variable TWO is compared to the stored variable ONE using the supplied <code>comparator</code>
073 * using the <code>GREATER</code> evaluation criterion constant. This instructs the predicate to
074 * return <code>true</code> if the comparator returns a value greater than <code>0</code>.</p>
075 *
076 * @since 4.0
077 */
078public class ComparatorPredicate<T> implements Predicate<T>, Serializable {
079
080    private static final long serialVersionUID = -1863209236504077399L;
081
082    public enum Criterion {
083        EQUAL, GREATER, LESS, GREATER_OR_EQUAL, LESS_OR_EQUAL,
084    }
085
086    // Instance variables:
087
088    /** The internal object to compare with */
089    private final T object;
090
091    /** The comparator to use for comparison */
092    private final Comparator<T> comparator;
093
094    /** The comparison evaluation criterion to use */
095    private final Criterion criterion;
096
097    /**
098     * Factory to create the comparator predicate
099     *
100     * @param <T> the type that the predicate queries
101     * @param object  the object to compare to
102     * @param comparator  the comparator to use for comparison
103     * @return the predicate
104     * @throws NullPointerException if comparator is null
105     */
106    public static <T> Predicate<T> comparatorPredicate(final T object, final Comparator<T> comparator) {
107        return comparatorPredicate(object, comparator, Criterion.EQUAL);
108    }
109
110    /**
111     * Factory to create the comparator predicate
112     *
113     * @param <T> the type that the predicate queries
114     * @param object  the object to compare to
115     * @param comparator  the comparator to use for comparison
116     * @param criterion  the criterion to use to evaluate comparison
117     * @return the predicate
118     * @throws NullPointerException if comparator or criterion is null
119     */
120    public static <T> Predicate<T> comparatorPredicate(final T object, final Comparator<T> comparator,
121                                                       final Criterion criterion) {
122        if (comparator == null) {
123            throw new NullPointerException("Comparator must not be null.");
124        }
125        if (criterion == null) {
126            throw new NullPointerException("Criterion must not be null.");
127        }
128        return new ComparatorPredicate<>(object, comparator, criterion);
129    }
130
131    /**
132     * Constructor that performs no validation.
133     * Use <code>comparatorPredicate</code> if you want that.
134     *
135     * @param object  the object to compare to
136     * @param comparator  the comparator to use for comparison
137     * @param criterion  the criterion to use to evaluate comparison
138     */
139    public ComparatorPredicate(final T object, final Comparator<T> comparator, final Criterion criterion) {
140        super();
141        this.object = object;
142        this.comparator = comparator;
143        this.criterion = criterion;
144    }
145
146    /**
147     * Evaluates the predicate. The predicate evaluates to <code>true</code> in the following cases:
148     *
149     * <ul>
150     * <li><code>comparator.compare(object, input) == 0 &amp;&amp; criterion == EQUAL</code></li>
151     * <li><code>comparator.compare(object, input) &lt; 0 &amp;&amp; criterion == LESS</code></li>
152     * <li><code>comparator.compare(object, input) &gt; 0 &amp;&amp; criterion == GREATER</code></li>
153     * <li><code>comparator.compare(object, input) &gt;= 0 &amp;&amp; criterion == GREATER_OR_EQUAL</code></li>
154     * <li><code>comparator.compare(object, input) &lt;= 0 &amp;&amp; criterion == LESS_OR_EQUAL</code></li>
155     * </ul>
156     *
157     * @see org.apache.commons.collections4.Predicate#evaluate(java.lang.Object)
158     * @see java.util.Comparator#compare(java.lang.Object first, java.lang.Object second)
159     *
160     * @param target  the target object to compare to
161     * @return {@code true} if the comparison succeeds according to the selected criterion
162     * @throws IllegalStateException if the criterion is invalid (really not possible)
163     */
164    @Override
165    public boolean evaluate(final T target) {
166
167        boolean result = false;
168        final int comparison = comparator.compare(object, target);
169        switch (criterion) {
170        case EQUAL:
171            result = comparison == 0;
172            break;
173        case GREATER:
174            result = comparison > 0;
175            break;
176        case LESS:
177            result = comparison < 0;
178            break;
179        case GREATER_OR_EQUAL:
180            result = comparison >= 0;
181            break;
182        case LESS_OR_EQUAL:
183            result = comparison <= 0;
184            break;
185        default:
186            throw new IllegalStateException("The current criterion '" + criterion + "' is invalid.");
187        }
188
189        return result;
190    }
191}