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