001/**
002 * Powerunit - A JDK1.8 test framework
003 * Copyright (C) 2014 Mathieu Boretti.
004 *
005 * This file is part of Powerunit
006 *
007 * Powerunit is free software: you can redistribute it and/or modify
008 * it under the terms of the GNU General Public License as published by
009 * the Free Software Foundation, either version 3 of the License, or
010 * (at your option) any later version.
011 *
012 * Powerunit is distributed in the hope that it will be useful,
013 * but WITHOUT ANY WARRANTY; without even the implied warranty of
014 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
015 * GNU General Public License for more details.
016 *
017 * You should have received a copy of the GNU General Public License
018 * along with Powerunit. If not, see <http://www.gnu.org/licenses/>.
019 */
020package ch.powerunit.extensions.exceptions;
021
022import static ch.powerunit.extensions.exceptions.Constants.verifyExceptionMapper;
023import static ch.powerunit.extensions.exceptions.Constants.verifyPredicate;
024import static java.util.Objects.requireNonNull;
025
026import java.util.function.BiPredicate;
027import java.util.function.Function;
028import java.util.function.Supplier;
029
030/**
031 * Represents a predicate (boolean-valued function) of two arguments that may
032 * throw exception. This is the two-arity specialization of
033 * {@link PredicateWithException}.
034 * <h3>General contract</h3>
035 * <ul>
036 * <li><b>{@link #test(Object, Object) boolean test(T t, U u) throws
037 * E}</b>&nbsp;-&nbsp;The functional method.</li>
038 * <li><b>uncheck</b>&nbsp;-&nbsp;Return a {@code BiPredicate<T, U>}</li>
039 * <li><b>lift</b>&nbsp;-&nbsp;Return a {@code BiPredicate<T, U>}</li>
040 * <li><b>ignore</b>&nbsp;-&nbsp;Return a {@code BiPredicate<T, U>}</li>
041 * </ul>
042 *
043 *
044 * @see BiPredicate
045 * @param <T>
046 *            the type of the first argument to the predicate
047 * @param <U>
048 *            the type of the second argument the predicate
049 * @param <E>
050 *            the type of the potential exception of the function
051 */
052@FunctionalInterface
053public interface BiPredicateWithException<T, U, E extends Exception>
054                extends PrimitiveReturnExceptionHandlerSupport<BiPredicate<T, U>, BiPredicateWithException<T, U, E>>,
055                BooleanDefaultValue {
056
057        /**
058         * Evaluates this predicate on the given arguments.
059         *
060         * @param t
061         *            the first input argument
062         * @param u
063         *            the second input argument
064         * @return {@code true} if the input arguments match the predicate, otherwise
065         *         {@code false}
066         * @throws E
067         *             any exception
068         * @see BiPredicate#test(Object, Object)
069         */
070        boolean test(T t, U u) throws E;
071
072        @Override
073        default BiPredicate<T, U> uncheckOrIgnore(boolean uncheck) {
074                return (t, u) -> {
075                        try {
076                                return test(t, u);
077                        } catch (Exception e) {
078                                PrimitiveReturnExceptionHandlerSupport.handleException(uncheck, e, exceptionMapper());
079                                return defaultValue();
080                        }
081                };
082        }
083
084        /**
085         * Returns a composed predicate that represents a short-circuiting logical AND
086         * of this predicate and another. When evaluating the composed predicate, if
087         * this predicate is {@code false}, then the {@code other} predicate is not
088         * evaluated.
089         *
090         * <p>
091         * Any exceptions thrown during evaluation of either predicate are relayed to
092         * the caller; if evaluation of this predicate throws an exception, the
093         * {@code other} predicate will not be evaluated.
094         *
095         * @param other
096         *            a predicate that will be logically-ANDed with this predicate
097         * @return a composed predicate that represents the short-circuiting logical AND
098         *         of this predicate and the {@code other} predicate
099         * @throws NullPointerException
100         *             if other is null
101         * @see #or(BiPredicateWithException)
102         * @see #negate()
103         */
104        default BiPredicateWithException<T, U, E> and(BiPredicateWithException<? super T, ? super U, ? extends E> other) {
105                requireNonNull(other);
106                return (t, u) -> test(t, u) && other.test(t, u);
107        }
108
109        /**
110         * Returns a predicate that represents the logical negation of this predicate.
111         *
112         * @return a predicate that represents the logical negation of this predicate
113         * @see #and(BiPredicateWithException)
114         * @see #or(BiPredicateWithException)
115         */
116        default BiPredicateWithException<T, U, E> negate() {
117                return (t, u) -> !test(t, u);
118        }
119
120        /**
121         * Negate a {@code DoublePredicateWithException}.
122         *
123         * @param predicate
124         *            to be negate
125         * @param <T>
126         *            the type of the first argument to the predicate
127         * @param <U>
128         *            the type of the second argument the predicate
129         * @param <E>
130         *            the type of the potential exception
131         * @return the negated predicate
132         * @see #negate()
133         */
134        static <T, U, E extends Exception> BiPredicateWithException<T, U, E> negate(
135                        BiPredicateWithException<T, U, E> predicate) {
136                return verifyPredicate(predicate).negate();
137        }
138
139        /**
140         * Returns a composed predicate that represents a short-circuiting logical OR of
141         * this predicate and another. When evaluating the composed predicate, if this
142         * predicate is {@code true}, then the {@code other} predicate is not evaluated.
143         *
144         * <p>
145         * Any exceptions thrown during evaluation of either predicate are relayed to
146         * the caller; if evaluation of this predicate throws an exception, the
147         * {@code other} predicate will not be evaluated.
148         *
149         * @param other
150         *            a predicate that will be logically-ORed with this predicate
151         * @return a composed predicate that represents the short-circuiting logical OR
152         *         of this predicate and the {@code other} predicate
153         * @throws NullPointerException
154         *             if other is null
155         * @see #and(BiPredicateWithException)
156         * @see #negate()
157         */
158        default BiPredicateWithException<T, U, E> or(BiPredicateWithException<? super T, ? super U, ? extends E> other) {
159                requireNonNull(other);
160                return (t, u) -> test(t, u) || other.test(t, u);
161        }
162
163        /**
164         * Returns a predicate that always throw exception.
165         *
166         * @param exceptionBuilder
167         *            the supplier to create the exception
168         * @param <T>
169         *            the type of the first argument to the predicate
170         * @param <U>
171         *            the type of the second argument the predicate
172         * @param <E>
173         *            the type of the exception
174         * @return a predicate that always throw exception
175         */
176        static <T, U, E extends Exception> BiPredicateWithException<T, U, E> failing(Supplier<E> exceptionBuilder) {
177                return (t, u) -> {
178                        throw exceptionBuilder.get();
179                };
180        }
181
182        /**
183         * Converts a {@code BiPredicateWithException} to a {@code BiPredicate} that
184         * wraps to {@code RuntimeException}.
185         *
186         * @param predicate
187         *            to be unchecked
188         * @param <T>
189         *            the type of the first argument to the predicate
190         * @param <U>
191         *            the type of the second argument the predicate
192         * @param <E>
193         *            the type of the potential exception
194         * @return the unchecked predicate
195         * @see #uncheck()
196         * @see #unchecked(BiPredicateWithException, Function)
197         * @throws NullPointerException
198         *             if predicate is null
199         */
200        static <T, U, E extends Exception> BiPredicate<T, U> unchecked(BiPredicateWithException<T, U, E> predicate) {
201                return verifyPredicate(predicate).uncheck();
202        }
203
204        /**
205         * Converts a {@code BiPredicateWithException} to a {@code BiPredicate} that
206         * wraps to {@code RuntimeException} by using the provided mapping function.
207         *
208         * @param predicate
209         *            the be unchecked
210         * @param exceptionMapper
211         *            a function to convert the exception to the runtime exception.
212         * @param <T>
213         *            the type of the first argument to the predicate
214         * @param <U>
215         *            the type of the second argument the predicate
216         * @param <E>
217         *            the type of the potential exception
218         * @return the unchecked predicate
219         * @see #uncheck()
220         * @see #unchecked(BiPredicateWithException)
221         * @throws NullPointerException
222         *             if predicate or exceptionMapper is null
223         */
224        static <T, U, E extends Exception> BiPredicate<T, U> unchecked(BiPredicateWithException<T, U, E> predicate,
225                        Function<Exception, RuntimeException> exceptionMapper) {
226                verifyPredicate(predicate);
227                verifyExceptionMapper(exceptionMapper);
228                return new BiPredicateWithException<T, U, E>() {
229
230                        @Override
231                        public boolean test(T t, U u) throws E {
232                                return predicate.test(t, u);
233                        }
234
235                        @Override
236                        public Function<Exception, RuntimeException> exceptionMapper() {
237                                return exceptionMapper;
238                        }
239
240                }.uncheck();
241        }
242
243        /**
244         * Converts a {@code BiPredicateWithException} to a lifted {@code BiPredicate}
245         * returning {@code false} in case of exception.
246         *
247         * @param predicate
248         *            to be lifted
249         * @param <T>
250         *            the type of the first argument to the predicate
251         * @param <U>
252         *            the type of the second argument the predicate
253         * @param <E>
254         *            the type of the potential exception
255         * @return the lifted predicate
256         * @see #lift()
257         * @throws NullPointerException
258         *             if predicate is null
259         */
260        static <T, U, E extends Exception> BiPredicate<T, U> lifted(BiPredicateWithException<T, U, E> predicate) {
261                return verifyPredicate(predicate).lift();
262        }
263
264        /**
265         * Converts a {@code BiPredicateWithException} to a lifted {@code BiPredicate}
266         * returning {@code false} in case of exception.
267         *
268         * @param predicate
269         *            to be lifted
270         * @param <T>
271         *            the type of the first argument to the predicate
272         * @param <U>
273         *            the type of the second argument the predicate
274         * @param <E>
275         *            the type of the potential exception
276         * @return the lifted predicate
277         * @see #ignore()
278         * @throws NullPointerException
279         *             if predicate is null
280         */
281        static <T, U, E extends Exception> BiPredicate<T, U> ignored(BiPredicateWithException<T, U, E> predicate) {
282                return verifyPredicate(predicate).ignore();
283        }
284
285        /**
286         * Converts a {@code BiPredicateWithException} to a lifted {@code BiPredicate}
287         * returning a default value in case of exception.
288         *
289         * @param predicate
290         *            to be lifted
291         * @param defaultValue
292         *            value in case of exception
293         * @param <T>
294         *            the type of the first argument to the predicate
295         * @param <U>
296         *            the type of the second argument the predicate
297         * @param <E>
298         *            the type of the potential exception
299         * @return the lifted predicate
300         * @see #ignore()
301         * @see #ignored(BiPredicateWithException)
302         * @throws NullPointerException
303         *             if predicate is null
304         * @since 3.0.0
305         */
306        static <T, U, E extends Exception> BiPredicate<T, U> ignored(BiPredicateWithException<T, U, E> predicate,
307                        boolean defaultValue) {
308                verifyPredicate(predicate);
309                return new BiPredicateWithException<T, U, E>() {
310
311                        @Override
312                        public boolean test(T t, U u) throws E {
313                                return predicate.test(t, u);
314                        }
315
316                        @Override
317                        public boolean defaultValue() {
318                                return defaultValue;
319                        }
320
321                }.ignore();
322        }
323
324}