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 * In order to demonstrate the use of the predicate, the following variables are declared:
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 * Using the declared variables, the <code>ComparatorPredicate</code> can be used used in the
044 * following way:
045 *
046 * <pre>
047 * ComparatorPredicate.comparatorPredicate(ONE, comparator).evaluate(TWO);
048 * </pre>
049 *
050 * 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 * The following examples demonstrates how these constants can be used in order to manipulate the
066 * evaluation of a comparator result.
067 *
068 * <pre>
069 * ComparatorPredicate.comparatorPredicate(ONE, comparator,<b>ComparatorPredicate.Criterion.GREATER</b>).evaluate(TWO);
070 * </pre>
071 *
072 * 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>.
075 *
076 * @since 4.0
077 * @version $Id: ComparatorPredicate.html 972421 2015-11-14 20:00:04Z tn $
078 */
079public class ComparatorPredicate<T> implements Predicate<T>, Serializable {
080
081    private static final long serialVersionUID = -1863209236504077399L;
082
083    public enum Criterion {
084        EQUAL, GREATER, LESS, GREATER_OR_EQUAL, LESS_OR_EQUAL,
085    }
086
087    // Instance variables:
088
089    /** The internal object to compare with */
090    private final T object;
091
092    /** The comparator to use for comparison */
093    private final Comparator<T> comparator;
094
095    /** The comparison evaluation criterion to use */
096    private final Criterion criterion;
097
098    /**
099     * Factory to create the comparator predicate
100     *
101     * @param <T> the type that the predicate queries
102     * @param object  the object to compare to
103     * @param comparator  the comparator to use for comparison
104     * @return the predicate
105     * @throws IllegalArgumentException if comparator is null
106     */
107    public static <T> Predicate<T> comparatorPredicate(final T object, final Comparator<T> comparator) {
108        return comparatorPredicate(object, comparator, Criterion.EQUAL);
109    }
110
111    /**
112     * Factory to create the comparator predicate
113     *
114     * @param <T> the type that the predicate queries
115     * @param object  the object to compare to
116     * @param comparator  the comparator to use for comparison
117     * @param criterion  the criterion to use to evaluate comparison
118     * @return the predicate
119     * @throws IllegalArgumentException if comparator is null of criterion is invalid
120     */
121    public static <T> Predicate<T> comparatorPredicate(final T object, final Comparator<T> comparator,
122                                                       final Criterion criterion) {
123        if (comparator == null) {
124            throw new IllegalArgumentException("Comparator must not be null.");
125        }
126        if (criterion == null) {
127            throw new IllegalArgumentException("Criterion must not be null.");
128        }
129        return new ComparatorPredicate<T>(object, comparator, criterion);
130    }
131
132    /**
133     * Constructor that performs no validation.
134     * Use <code>comparatorPredicate</code> if you want that.
135     *
136     * @param object  the object to compare to
137     * @param comparator  the comparator to use for comparison
138     * @param criterion  the criterion to use to evaluate comparison
139     */
140    public ComparatorPredicate(final T object, final Comparator<T> comparator, final Criterion criterion) {
141        super();
142        this.object = object;
143        this.comparator = comparator;
144        this.criterion = criterion;
145    }
146
147    /**
148     * Evaluates the predicate. The predicate evaluates to <code>true</code> in the following cases:
149     *
150     * <ul>
151     *     <li><code>comparator.compare(object, input) == 0 && criterion == EQUAL</code></li>
152     *     <li><code>comparator.compare(object, input) < 0 && criterion == LESS</code></li>
153     *     <li><code>comparator.compare(object, input) > 0 && criterion == GREATER</code></li>
154     *     <li><code>comparator.compare(object, input) >= 0 && criterion == GREATER_OR_EQUAL</code></li>
155     *     <li><code>comparator.compare(object, input) <= 0 && criterion == LESS_OR_EQUAL</code></li>
156     * </ul>
157     *
158     * @see org.apache.commons.collections4.Predicate#evaluate(java.lang.Object)
159     * @see java.util.Comparator#compare(java.lang.Object first, java.lang.Object second)
160     *
161     * @param target  the target object to compare to
162     * @return {@code true} if the comparison succeeds according to the selected criterion
163     * @throws IllegalStateException if the criterion is invalid (really not possible)
164     */
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}