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.verifyConsumer;
023import static ch.powerunit.extensions.exceptions.Constants.verifyExceptionMapper;
024import static java.util.Objects.requireNonNull;
025
026import java.util.concurrent.CompletionStage;
027import java.util.function.BiConsumer;
028import java.util.function.BiFunction;
029import java.util.function.Function;
030import java.util.function.Supplier;
031
032/**
033 * Represents an operation that accepts two input arguments and returns no
034 * result and may throw exception. Unlike most other functional interfaces,
035 * {@code BiConsumerWithException} is expected to operate via side-effects.
036 *
037 * <h3>General contract</h3>
038 * <ul>
039 * <li><b>{@link #accept(Object, Object) void accept(T t,U u) throws
040 * E}</b>&nbsp;-&nbsp;The functional method.</li>
041 * <li><b>uncheck</b>&nbsp;-&nbsp;Return a {@code Biconsumer<T,U>}</li>
042 * <li><b>lift</b>&nbsp;-&nbsp;Return a {@code Biconsumer<T,U>}</li>
043 * <li><b>ignore</b>&nbsp;-&nbsp;Return a {@code Biconsumer<T,U>}</li>
044 * </ul>
045 *
046 * @see BiConsumer
047 * @param <T>
048 *            the type of the first argument to the operation
049 * @param <U>
050 *            the type of the second argument to the operation
051 * @param <E>
052 *            the type of the potential exception of the operation
053 */
054@FunctionalInterface
055public interface BiConsumerWithException<T, U, E extends Exception> extends
056                NoReturnExceptionHandlerSupport<BiConsumer<T, U>, BiFunction<T, U, CompletionStage<Void>>, BiConsumerWithException<T, U, E>> {
057
058        /**
059         * Performs this operation on the given arguments.
060         *
061         * @param t
062         *            the first input argument
063         * @param u
064         *            the second input argument
065         * @throws E
066         *             any exception
067         * @see BiConsumer#accept(Object,Object)
068         */
069        void accept(T t, U u) throws E;
070
071        /**
072         * Converts this {@code BiConsumerWithException} to a {@code BiConsumer} that
073         * wraps exception into {@code RuntimeException}.
074         *
075         * @return the unchecked operation
076         * @see #unchecked(BiConsumerWithException)
077         * @see #unchecked(BiConsumerWithException, Function)
078         * @see BiConsumer
079         */
080        @Override
081        default BiConsumer<T, U> uncheck() {
082                return (t, u) -> NoReturnExceptionHandlerSupport.unchecked(() -> accept(t, u), throwingHandler());
083        }
084
085        /**
086         * Converts this {@code BiConsumerWithException} to a <i>lifted</i>
087         * {@code BiConsumer} that ignore exception.
088         *
089         * @return the operation that ignore error
090         * @see #ignored(BiConsumerWithException)
091         * @see BiConsumer
092         */
093        @Override
094        default BiConsumer<T, U> ignore() {
095                return (t, u) -> NoReturnExceptionHandlerSupport.unchecked(() -> accept(t, u), notThrowingHandler());
096        }
097
098        /**
099         * Converts this {@code BiConsumerWithException} to a <i>staged</i>
100         * {@code BiFunction} that return a {@code CompletionStage}.
101         *
102         * @return the staged operation.
103         * @since 1.1.0
104         */
105        @Override
106        default BiFunction<T, U, CompletionStage<Void>> stage() {
107                return (t, u) -> NoReturnExceptionHandlerSupport.staged(() -> accept(t, u));
108        }
109
110        /**
111         * Returns a composed {@code BiConsumerWithException} that performs, in
112         * sequence, this operation followed by the {@code after} operation. If
113         * performing either operation throws an exception, it is relayed to the caller
114         * of the composed operation. If performing this operation throws an exception,
115         * the {@code after} operation will not be performed.
116         *
117         * @param after
118         *            the operation to perform after this operation
119         * @return a composed {@code BiConsumerWithException} that performs in sequence
120         *         this operation followed by the {@code after} operation
121         * @throws NullPointerException
122         *             if {@code after} is null
123         * @see BiConsumer#andThen(BiConsumer)
124         */
125        default BiConsumerWithException<T, U, E> andThen(BiConsumerWithException<? super T, ? super U, ? extends E> after) {
126                requireNonNull(after);
127                return (T t, U u) -> {
128                        accept(t, u);
129                        after.accept(t, u);
130                };
131        }
132
133        /**
134         * Returns a {@code BiConsumerWithException} that always throw exception.
135         *
136         * @param exceptionBuilder
137         *            the supplier to create the exception
138         * @param <T>
139         *            the type of the first argument to the operation
140         * @param <U>
141         *            the type of the second argument to the operation
142         * @param <E>
143         *            the type of the potential exception of the operation
144         *
145         * @return an operation that always throw exception
146         */
147        static <T, U, E extends Exception> BiConsumerWithException<T, U, E> failing(Supplier<E> exceptionBuilder) {
148                return (t, u) -> {
149                        throw exceptionBuilder.get();
150                };
151        }
152
153        /**
154         * Converts a {@code BiConsumerWithException} to a {@code BiConsumer} that wraps
155         * exception to {@code RuntimeException}.
156         * <p>
157         * For example :
158         *
159         * <pre>
160         * BiConsumerWithException&lt;String, String, IOException&gt; consumerThrowingException = ...;
161         *
162         * BiConsumer&lt;String, String&gt; consumerThrowingRuntimeException =
163         *   ConsumerWithException.unchecked(consumerThrowingException);
164         *
165         * myMap.forEach(consumerThrowingRuntimeException);
166         * </pre>
167         *
168         * In case of exception inside {@code consumerThrowingRuntimeException} an
169         * instance of {@code WrappedException} with the original exception as cause
170         * will be thrown.
171         *
172         * @param consumer
173         *            to be unchecked
174         * @param <T>
175         *            the type of the first argument to the operation
176         * @param <U>
177         *            the type of the second argument to the operation
178         * @param <E>
179         *            the type of the potential exception of the operation
180         * @return the unchecked exception
181         * @see #uncheck()
182         * @see #unchecked(BiConsumerWithException, Function)
183         * @throws NullPointerException
184         *             if consumer is null
185         */
186        static <T, U, E extends Exception> BiConsumer<T, U> unchecked(BiConsumerWithException<T, U, E> consumer) {
187                return verifyConsumer(consumer).uncheck();
188        }
189
190        /**
191         * Converts a {@code BiConsumerWithException} to a {@code BiConsumer} that wraps
192         * exception to {@code RuntimeException} by using the provided mapping function.
193         *
194         * <p>
195         * For example :
196         *
197         * <pre>
198         * BiConsumerWithException&lt;String, String, IOException&gt; consumerThrowingException = ...;
199         *
200         * BiConsumer&lt;String, String&gt; consumerThrowingRuntimeException =
201         *   ConsumerWithException.unchecked(
202         *     consumerThrowingException,
203         *     IllegalArgumentException::new);
204         *
205         * myMap.forEach(consumerThrowingRuntimeException)
206         * </pre>
207         *
208         * In case of exception inside {@code consumerThrowingRuntimeException} an
209         * instance of {@code IllegalArgumentException} with the original exception as
210         * cause will be thrown.
211         *
212         * @param consumer
213         *            the be unchecked
214         * @param exceptionMapper
215         *            a function to convert the exception to the runtime exception.
216         * @param <T>
217         *            the type of the first argument to the operation
218         * @param <U>
219         *            the type of the second argument to the operation
220         * @param <E>
221         *            the type of the potential exception of the operation
222         * @return the unchecked exception
223         * @see #uncheck()
224         * @see #unchecked(BiConsumerWithException)
225         * @throws NullPointerException
226         *             if consumer or exceptionMapper is null
227         */
228        static <T, U, E extends Exception> BiConsumer<T, U> unchecked(BiConsumerWithException<T, U, E> consumer,
229                        Function<Exception, RuntimeException> exceptionMapper) {
230                verifyConsumer(consumer);
231                verifyExceptionMapper(exceptionMapper);
232                return new BiConsumerWithException<T, U, E>() {
233
234                        @Override
235                        public void accept(T t, U u) throws E {
236                                consumer.accept(t, u);
237                        }
238
239                        @Override
240                        public Function<Exception, RuntimeException> exceptionMapper() {
241                                return exceptionMapper;
242                        }
243
244                }.uncheck();
245        }
246
247        /**
248         * Converts a {@code BiConsumerWithException} to a lifted {@code BiConsumer}
249         * ignoring exception.
250         * <p>
251         * For example :
252         *
253         * <pre>
254         * BiConsumerWithException&lt;String, String, IOException&gt; consumerThrowingException = ...;
255         *
256         * BiConsumer&lt;String, String&gt; consumerThrowingRuntimeException =
257         *   ConsumerWithException.lifted(consumerThrowingException);
258         *
259         * myMap.forEach(consumerThrowingRuntimeException);
260         * </pre>
261         *
262         * In case of exception inside {@code consumerThrowingRuntimeException} the
263         * exception will be ignored.
264         *
265         * @param consumer
266         *            to be lifted
267         * @param <T>
268         *            the type of the first argument to the operation
269         * @param <U>
270         *            the type of the second argument to the operation
271         * @param <E>
272         *            the type of the potential exception of the operation
273         * @return the lifted operation
274         * @see #lift()
275         * @throws NullPointerException
276         *             if consumer is null
277         */
278        static <T, U, E extends Exception> BiConsumer<T, U> lifted(BiConsumerWithException<T, U, E> consumer) {
279                return verifyConsumer(consumer).lift();
280        }
281
282        /**
283         * Converts a {@code BiConsumerWithException} to a lifted {@code BiConsumer}
284         * ignoring exception.
285         * <p>
286         * For example :
287         *
288         * <pre>
289         * BiConsumerWithException&lt;String, String, IOException&gt; consumerThrowingException = ...;
290         *
291         * BiConsumer&lt;String, String&gt; consumerThrowingRuntimeException =
292         *   ConsumerWithException.ignored(consumerThrowingException);
293         *
294         * myMap.forEach(consumerThrowingRuntimeException);
295         * </pre>
296         *
297         * In case of exception inside {@code consumerThrowingRuntimeException} the
298         * exception will be ignored.
299         *
300         * @param consumer
301         *            to be lifted
302         * @param <T>
303         *            the type of the first argument to the operation
304         * @param <U>
305         *            the type of the second argument to the operation
306         * @param <E>
307         *            the type of the potential exception of the operation
308         * @return the lifted operation
309         * @see #ignore()
310         * @throws NullPointerException
311         *             if consumer is null
312         */
313        static <T, U, E extends Exception> BiConsumer<T, U> ignored(BiConsumerWithException<T, U, E> consumer) {
314                return verifyConsumer(consumer).ignore();
315        }
316
317        /**
318         * Converts a {@code BiConsumerWithException} to a staged {@code BiFunction}.
319         *
320         * @param consumer
321         *            to be staged
322         * @param <T>
323         *            the type of the first argument to the operation
324         * @param <U>
325         *            the type of the second argument to the operation
326         * @param <E>
327         *            the type of the potential exception
328         * @return the staged operation
329         * @throws NullPointerException
330         *             if consumer is null
331         * @since 1.1.0
332         */
333        static <T, U, E extends Exception> BiFunction<T, U, CompletionStage<Void>> staged(
334                        BiConsumerWithException<T, U, E> consumer) {
335                return verifyConsumer(consumer).stage();
336        }
337
338        /**
339         * Converts a {@code BiConsumerWithException} to a
340         * {@code BiFunctionWithException} returning {@code null}.
341         *
342         * @param consumer
343         *            to be converter
344         * @param <T>
345         *            the type of the first argument to the operation
346         * @param <U>
347         *            the type of the second argument to the operation
348         * @param <R>
349         *            the type of the return value of the function
350         * @param <E>
351         *            the type of the potential exception of the operation
352         * @return the function
353         * @throws NullPointerException
354         *             if consumer is null
355         */
356        static <T, U, R, E extends Exception> BiFunctionWithException<T, U, R, E> asBiFunction(
357                        BiConsumerWithException<T, U, E> consumer) {
358                return verifyConsumer(consumer).asBiFunction();
359        }
360
361        /**
362         * Converts a {@code BiConsumerWithException} to a
363         * {@code BiFunctionWithException} returning {@code null}.
364         *
365         * @param <R>
366         *            the type of the return value of the function
367         * @return the function
368         * @since 1.2.0
369         */
370        default <R> BiFunctionWithException<T, U, R, E> asBiFunction() {
371                return (t, u) -> {
372                        accept(t, u);
373                        return null;
374                };
375        }
376
377}