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.verifyFunction;
024import static java.util.Objects.requireNonNull;
025
026import java.util.Optional;
027import java.util.concurrent.CompletionStage;
028import java.util.function.BiFunction;
029import java.util.function.Function;
030import java.util.function.Supplier;
031
032/**
033 * Represents a function that accepts two arguments, may thrown exception and
034 * produces a result. This is the two-arity specialization of
035 * {@link FunctionWithException}.
036 *
037 * <h3>General contract</h3>
038 * <ul>
039 * <li><b>{@link #apply(Object, Object) R apply(T t, U u) throws
040 * E}</b>&nbsp;-&nbsp;The functional method.</li>
041 * <li><b>uncheck</b>&nbsp;-&nbsp;Return a {@code BiFunction<T,U,R>}</li>
042 * <li><b>lift</b>&nbsp;-&nbsp;Return a
043 * {@code BiFunction<T,U,<Optional<R>>}</li>
044 * <li><b>ignore</b>&nbsp;-&nbsp;Return a {@code BiFunction<T,U,R>}</li>
045 * </ul>
046 *
047 * @see BiFunction
048 * @param <T>
049 *            the type of the first argument to the function
050 * @param <U>
051 *            the type of the second argument to the function
052 * @param <R>
053 *            the type of the result of the function
054 * @param <E>
055 *            the type of the potential exception of the function
056 */
057@FunctionalInterface
058public interface BiFunctionWithException<T, U, R, E extends Exception> extends
059                ObjectReturnExceptionHandlerSupport<BiFunction<T, U, R>, BiFunction<T, U, Optional<R>>, BiFunction<T, U, CompletionStage<R>>, R, BiFunctionWithException<T, U, R, E>> {
060
061        /**
062         * Applies this function to the given arguments.
063         *
064         * @param t
065         *            the first function argument
066         * @param u
067         *            the second function argument
068         * @return the function result
069         * @throws E
070         *             any exception
071         * @see BiFunction#apply(Object,Object)
072         */
073        R apply(T t, U u) throws E;
074
075        /**
076         * Converts this {@code BiFunctionWithException} to a {@code BiFunction} that
077         * wraps exception to {@code RuntimeException}.
078         *
079         * @return the unchecked function
080         * @see #unchecked(BiFunctionWithException)
081         * @see #unchecked(BiFunctionWithException, Function)
082         * @see BiFunction
083         */
084        @Override
085        default BiFunction<T, U, R> uncheck() {
086                return (t, u) -> ObjectReturnExceptionHandlerSupport.unchecked(() -> apply(t, u), throwingHandler());
087
088        }
089
090        /**
091         * Converts this {@code BiFunctionWithException} to a lifted {@code BiFunction}
092         * using {@code Optional} as return value.
093         *
094         * @return the lifted function
095         * @see #lifted(BiFunctionWithException)
096         * @see BiFunction
097         */
098        @Override
099        default BiFunction<T, U, Optional<R>> lift() {
100                return (t, u) -> ObjectReturnExceptionHandlerSupport.unchecked(() -> Optional.ofNullable(apply(t, u)),
101                                notThrowingHandler());
102        }
103
104        /**
105         * Converts this {@code BiFunctionWithException} to a lifted {@code BiFunction}
106         * returning {@code null} (or the value redefined by the method
107         * {@link #defaultValue()}) in case of exception.
108         *
109         * @return the function that ignore error
110         * @see #ignored(BiFunctionWithException)
111         * @see BiFunction
112         */
113        @Override
114        default BiFunction<T, U, R> ignore() {
115                return lift().andThen(o -> o.orElse(defaultValue()));
116        }
117
118        /**
119         * Convert this {@code BiFunctionWithException} to a lifted {@code BiFunction}
120         * that uses {@code CompletionStage} as return value.
121         *
122         * @return the lifted function
123         * @see #staged(BiFunctionWithException)
124         * @see BiFunction
125         * @see CompletionStage
126         */
127        @Override
128        default BiFunction<T, U, CompletionStage<R>> stage() {
129                return (t, u) -> ObjectReturnExceptionHandlerSupport.staged(() -> apply(t, u));
130        }
131
132        /**
133         * Returns a composed {@code FunctionWithException} that first applies this
134         * {@code FunctionWithException} to its input, and then applies the
135         * {@code after} {@code FunctionWithException} to the result. If evaluation of
136         * either {@code FunctionWithException} throws an exception, it is relayed to
137         * the caller of the composed function.
138         *
139         * @param <V>
140         *            the type of output of the {@code after}
141         *            {@code FunctionWithException}, and of the composed
142         *            {@code FunctionWithException}
143         * @param after
144         *            the function to apply after this {@code FunctionWithException} is
145         *            applied
146         * @return a composed function that first applies this function and then applies
147         *         the {@code after} function
148         * @throws NullPointerException
149         *             if after is null
150         *
151         * @see BiFunction#andThen(Function)
152         */
153        default <V> BiFunctionWithException<T, U, V, E> andThen(
154                        FunctionWithException<? super R, ? extends V, ? extends E> after) {
155                requireNonNull(after);
156                return (T t, U u) -> after.apply(apply(t, u));
157        }
158
159        /**
160         * Returns a {@code FunctionWithException} that always throw exception.
161         *
162         * @param exceptionBuilder
163         *            the supplier to create the exception
164         * @param <T>
165         *            the type of the first argument to the function
166         * @param <U>
167         *            the type of the second argument to the function
168         * @param <R>
169         *            the type of the result of the function
170         * @param <E>
171         *            the type of the exception
172         * @return a function that always throw exception
173         */
174        static <T, U, R, E extends Exception> BiFunctionWithException<T, U, R, E> failing(Supplier<E> exceptionBuilder) {
175                return (t, u) -> {
176                        throw exceptionBuilder.get();
177                };
178        }
179
180        /**
181         * Converts a {@code BiFunctionWithException} to a {@code BiFunction} that
182         * convert exception to {@code RuntimeException}.
183         * <p>
184         * For example :
185         *
186         * <pre>
187         * BiFunction&lt;String, String, String&gt; biFunctionThrowingRuntimeException = BiFunctionWithException
188         *              .unchecked(biFouctionThrowingException);
189         * </pre>
190         *
191         * Will generate a {@code BiFunction} throwing {@code RuntimeException} in case
192         * of error.
193         *
194         * @param function
195         *            to be unchecked
196         * @param <T>
197         *            the type of the first argument to the function
198         * @param <U>
199         *            the type of the second argument to the function
200         * @param <R>
201         *            the type of the result of the function
202         * @param <E>
203         *            the type of the potential exception
204         * @return the unchecked exception
205         * @see #uncheck()
206         * @see #unchecked(BiFunctionWithException, Function)
207         * @throws NullPointerException
208         *             if function is null
209         */
210        static <T, U, R, E extends Exception> BiFunction<T, U, R> unchecked(BiFunctionWithException<T, U, R, E> function) {
211                return verifyFunction(function).uncheck();
212        }
213
214        /**
215         * Converts a {@code BiFunctionWithException} to a {@code BiFunction} that
216         * convert exception to {@code RuntimeException} by using the provided mapping
217         * function.
218         * <p>
219         * For example :
220         *
221         * <pre>
222         * BiFunction&lt;String, String, String&gt; functionThrowingRuntimeException = BiFunctionWithException
223         *              .unchecked(fonctionThrowingException, IllegalArgumentException::new);
224         * </pre>
225         *
226         * Will generate a {@code BiFunction} throwing {@code IllegalArgumentException}
227         * in case of error.
228         *
229         * @param function
230         *            the be unchecked
231         * @param exceptionMapper
232         *            a function to convert the exception to the runtime exception.
233         * @param <T>
234         *            the type of the first argument to the function
235         * @param <U>
236         *            the type of the second argument to the function
237         * @param <R>
238         *            the type of the result of the function
239         * @param <E>
240         *            the type of the potential exception
241         * @return the unchecked exception
242         * @see #uncheck()
243         * @see #unchecked(BiFunctionWithException)
244         * @throws NullPointerException
245         *             if function or exceptionMapper is null
246         */
247        static <T, U, R, E extends Exception> BiFunction<T, U, R> unchecked(BiFunctionWithException<T, U, R, E> function,
248                        Function<Exception, RuntimeException> exceptionMapper) {
249                verifyFunction(function);
250                verifyExceptionMapper(exceptionMapper);
251                return new BiFunctionWithException<T, U, R, E>() {
252
253                        @Override
254                        public R apply(T t, U u) throws E {
255                                return function.apply(t, u);
256                        }
257
258                        @Override
259                        public Function<Exception, RuntimeException> exceptionMapper() {
260                                return exceptionMapper;
261                        }
262
263                }.uncheck();
264        }
265
266        /**
267         * Converts a {@code BiFunctionWithException} to a lifted {@code BiFunction}
268         * using {@code Optional} as return value.
269         *
270         * @param function
271         *            to be lifted
272         * @param <T>
273         *            the type of the first argument to the function
274         * @param <U>
275         *            the type of the second argument to the function
276         * @param <R>
277         *            the type of the result of the function
278         * @param <E>
279         *            the type of the potential exception
280         * @return the lifted function
281         * @see #lift()
282         * @throws NullPointerException
283         *             if function is null
284         */
285        static <T, U, R, E extends Exception> BiFunction<T, U, Optional<R>> lifted(
286                        BiFunctionWithException<T, U, R, E> function) {
287                return verifyFunction(function).lift();
288        }
289
290        /**
291         * Converts a {@code BiFunctionWithException} to a lifted {@code BiFunction}
292         * returning {@code null} in case of exception.
293         *
294         * @param function
295         *            to be lifted
296         * @param <T>
297         *            the type of the first argument to the function
298         * @param <U>
299         *            the type of the second argument to the function
300         * @param <R>
301         *            the type of the result of the function
302         * @param <E>
303         *            the type of the potential exception
304         * @return the lifted function
305         * @see #ignore()
306         * @throws NullPointerException
307         *             if function is null
308         */
309        static <T, U, R, E extends Exception> BiFunction<T, U, R> ignored(BiFunctionWithException<T, U, R, E> function) {
310                return verifyFunction(function).ignore();
311        }
312
313        /**
314         * Converts a {@code BiFunctionWithException} to a lifted {@code BiFunction}
315         * returning a default in case of exception.
316         *
317         * @param function
318         *            to be lifted
319         * @param defaultValue
320         *            the value to be returned in case of error
321         * @param <T>
322         *            the type of the first argument to the function
323         * @param <U>
324         *            the type of the second argument to the function
325         * @param <R>
326         *            the type of the result of the function
327         * @param <E>
328         *            the type of the potential exception
329         * @return the lifted function
330         * @see #ignore()
331         * @see #ignored(BiFunctionWithException)
332         * @throws NullPointerException
333         *             if function is null
334         * @since 3.0.0
335         */
336        static <T, U, R, E extends Exception> BiFunction<T, U, R> ignored(BiFunctionWithException<T, U, R, E> function,
337                        R defaultValue) {
338                verifyFunction(function);
339                return new BiFunctionWithException<T, U, R, E>() {
340
341                        @Override
342                        public R apply(T t, U u) throws E {
343                                return function.apply(t, u);
344                        }
345
346                        @Override
347                        public R defaultValue() {
348                                return defaultValue;
349                        }
350
351                }.ignore();
352
353        }
354
355        /**
356         * Convert this {@code BiFunctionWithException} to a lifted {@code BiFunction}
357         * return {@code CompletionStage} as return value.
358         *
359         * @param function
360         *            to be lifted
361         * @param <T>
362         *            the type of the first argument to the function
363         * @param <U>
364         *            the type of the second argument to the function
365         * @param <R>
366         *            the type of the result of the function
367         * @param <E>
368         *            the type of the potential exception
369         * @return the lifted function
370         * @see #stage()
371         * @throws NullPointerException
372         *             if function is null
373         */
374        static <T, U, R, E extends Exception> BiFunction<T, U, CompletionStage<R>> staged(
375                        BiFunctionWithException<T, U, R, E> function) {
376                return verifyFunction(function).stage();
377        }
378
379        /**
380         * Converts a {@code BiFunctionWithException} to a
381         * {@code BiConsumerWithException}.
382         *
383         * @param function
384         *            to be converter
385         * @param <T>
386         *            the type of the first argument to the operation
387         * @param <U>
388         *            the type of the second argument to the operation
389         * @param <R>
390         *            the type of the return value of the function
391         * @param <E>
392         *            the type of the potential exception of the operation
393         * @return the consumer
394         * @throws NullPointerException
395         *             if function is null
396         * @since 1.2.0
397         */
398        static <T, U, R, E extends Exception> BiConsumerWithException<T, U, E> asBiConsumer(
399                        BiFunctionWithException<T, U, R, E> function) {
400                return verifyFunction(function).asBiConsumer();
401        }
402
403        /**
404         * Converts a {@code BiFunctionWithException} to a
405         * {@code BiConsumerWithException}.
406         *
407         * @return the consumer
408         * @since 1.2.0
409         */
410        default BiConsumerWithException<T, U, E> asBiConsumer() {
411                return this::apply;
412        }
413
414        /**
415         * Converts a {@code BiFunctionWithException} to a
416         * {@code FunctionWithException}.
417         *
418         * @param function
419         *            to be converter
420         *
421         * @param u
422         *            the second parameter of the {@code BiFunctionWithException} that
423         *            will be used inside the new {code FunctionWithException}.
424         * @param <T>
425         *            the type of the first argument to the operation
426         * @param <U>
427         *            the type of the second argument to the operation
428         * @param <R>
429         *            the type of the return value of the function
430         * @param <E>
431         *            the type of the potential exception of the operation
432         * @return the consumer
433         * @throws NullPointerException
434         *             if function is null
435         * @since 1.2.0
436         */
437        static <T, U, R, E extends Exception> FunctionWithException<T, R, E> asFunction(
438                        BiFunctionWithException<T, U, R, E> function, U u) {
439                return verifyFunction(function).asFunction(u);
440        }
441
442        /**
443         * Converts a {@code BiFunctionWithException} to a
444         * {@code FunctionWithException}.
445         *
446         * @param u
447         *            the second parameter of the {@code BiFunctionWithException} that
448         *            will be used inside the new {code FunctionWithException}.
449         *
450         *
451         * @return the consumer
452         * @since 1.2.0
453         */
454        default FunctionWithException<T, R, E> asFunction(U u) {
455                return t -> apply(t, u);
456        }
457
458}