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.verifyOperation;
024import static java.util.Objects.requireNonNull;
025
026import java.util.concurrent.CompletionStage;
027import java.util.function.Function;
028import java.util.function.Supplier;
029
030/**
031 * Represents an operation that accepts no input argument and returns no result
032 * and may throw exception. Unlike most other functional interfaces,
033 * {@code RunnableWithException} is expected to operate via side-effects.
034 * <h3>General contract</h3>
035 * <ul>
036 * <li><b>{@link #run() void run() throws E}</b>&nbsp;-&nbsp;The functional
037 * method.</li>
038 * <li><b>uncheck</b>&nbsp;-&nbsp;Return a {@code Runnable}</li>
039 * <li><b>lift</b>&nbsp;-&nbsp;Return a {@code Runnable}</li>
040 * <li><b>ignore</b>&nbsp;-&nbsp;Return a {@code Runnable}</li>
041 * </ul>
042 *
043 * @see Runnable
044 * @param <E>
045 *            the type of the potential exception of the operation
046 */
047@FunctionalInterface
048public interface RunnableWithException<E extends Exception>
049                extends NoReturnExceptionHandlerSupport<Runnable, Supplier<CompletionStage<Void>>, RunnableWithException<E>> {
050
051        /**
052         * Performs this operation.
053         *
054         * @throws E
055         *             any exception
056         * @see Runnable#run()
057         */
058        void run() throws E;
059
060        /**
061         * Converts this {@code RunnableWithException} to a {@code Runnable} that wraps
062         * exception to {@code RuntimeException}.
063         *
064         * @return the unchecked operation
065         * @see #unchecked(RunnableWithException)
066         * @see #unchecked(RunnableWithException, Function)
067         */
068        @Override
069        default Runnable uncheck() {
070                return () -> NoReturnExceptionHandlerSupport.unchecked(this::run, throwingHandler());
071        }
072
073        /**
074         * Converts this {@code RunnableWithException} to a <i>lifted</i>
075         * {@code Runnable} ignoring exception.
076         *
077         * @return the operation that ignore error
078         * @see #ignored(RunnableWithException)
079         */
080        @Override
081        default Runnable ignore() {
082                return () -> NoReturnExceptionHandlerSupport.unchecked(this::run, notThrowingHandler());
083        }
084
085        /**
086         * Converts this {@code RunnableWithException} to a <i>staged</i>
087         * {@code Supplier} that return a {@code CompletionStage}.
088         *
089         * @return the staged operation.
090         * @since 1.1.0
091         */
092        @Override
093        default Supplier<CompletionStage<Void>> stage() {
094                return () -> NoReturnExceptionHandlerSupport.staged(this);
095        }
096
097        /**
098         * Returns a composed {@code RunnableWithException} that performs, in sequence,
099         * this operation followed by the {@code after} operation. If performing either
100         * operation throws an exception, it is relayed to the caller of the composed
101         * operation. If performing this operation throws an exception, the
102         * {@code after} operation will not be performed.
103         *
104         * @param after
105         *            the operation to perform after this operation
106         * @return a composed {@code RunnableWithException} that performs in sequence
107         *         this operation followed by the {@code after} operation
108         * @throws NullPointerException
109         *             if {@code after} is null
110         * @since 1.2.0
111         */
112        default RunnableWithException<E> andThen(RunnableWithException<? extends E> after) {
113                requireNonNull(after);
114                return () -> {
115                        run();
116                        after.run();
117                };
118        }
119
120        /**
121         * Returns an operation that always throw exception.
122         *
123         * @param exceptionBuilder
124         *            the supplier to create the exception
125         * @param <E>
126         *            the type of the exception
127         * @return an operation that always throw exception
128         */
129        static <E extends Exception> RunnableWithException<E> failing(Supplier<E> exceptionBuilder) {
130                return () -> {
131                        throw exceptionBuilder.get();
132                };
133        }
134
135        /**
136         * Converts a {@code RunnableWithException} to a {@code Runnable} that wraps
137         * exception to {@code RuntimeException}.
138         *
139         * @param operation
140         *            to be unchecked
141         * @param <E>
142         *            the type of the potential exception
143         * @return the unchecked exception
144         * @see #uncheck()
145         * @see #unchecked(RunnableWithException, Function)
146         * @throws NullPointerException
147         *             if operation is null
148         */
149        static <E extends Exception> Runnable unchecked(RunnableWithException<E> operation) {
150                return verifyOperation(operation).uncheck();
151        }
152
153        /**
154         * Converts a {@code RunnableWithException} to a {@code Runnable} that wraps
155         * exception to {@code RuntimeException} by using the provided mapping function.
156         *
157         * @param operation
158         *            the be unchecked
159         * @param exceptionMapper
160         *            a function to convert the exception to the runtime exception.
161         * @param <E>
162         *            the type of the potential exception
163         * @return the unchecked exception
164         * @see #uncheck()
165         * @see #unchecked(RunnableWithException)
166         * @throws NullPointerException
167         *             if operation or exceptionMapper is null
168         */
169        static <E extends Exception> Runnable unchecked(RunnableWithException<E> operation,
170                        Function<Exception, RuntimeException> exceptionMapper) {
171                verifyOperation(operation);
172                verifyExceptionMapper(exceptionMapper);
173                return new RunnableWithException<E>() {
174
175                        @Override
176                        public void run() throws E {
177                                operation.run();
178                        }
179
180                        @Override
181                        public Function<Exception, RuntimeException> exceptionMapper() {
182                                return exceptionMapper;
183                        }
184
185                }.uncheck();
186        }
187
188        /**
189         * Converts a {@code RunnableWithException} to a lifted {@code Runnable}
190         * ignoring exception.
191         *
192         * @param operation
193         *            to be lifted
194         * @param <E>
195         *            the type of the potential exception
196         * @return the lifted operation
197         * @see #lift()
198         * @throws NullPointerException
199         *             if operation is null
200         */
201        static <E extends Exception> Runnable lifted(RunnableWithException<E> operation) {
202                return verifyOperation(operation).lift();
203        }
204
205        /**
206         * Converts a {@code RunnableWithException} to a lifted {@code Runnable}
207         * ignoring exception.
208         *
209         * @param operation
210         *            to be lifted
211         * @param <E>
212         *            the type of the potential exception
213         * @return the lifted operation
214         * @see #ignore()
215         * @throws NullPointerException
216         *             if operation is null
217         * @since 1.1.0
218         */
219        static <E extends Exception> Runnable ignored(RunnableWithException<E> operation) {
220                return verifyOperation(operation).ignore();
221        }
222
223        /**
224         * Converts a {@code RunnableWithException} to a staged {@code Supplier}.
225         *
226         * @param operation
227         *            to be staged
228         * @param <E>
229         *            the type of the potential exception
230         * @return the staged operation
231         * @throws NullPointerException
232         *             if operation is null
233         */
234        static <E extends Exception> Supplier<CompletionStage<Void>> staged(RunnableWithException<E> operation) {
235                return verifyOperation(operation).stage();
236        }
237
238        /**
239         * Converts a {@code RunnableWithException} to a {@code FunctionWithException}
240         * returning {@code null} and ignoring input.
241         *
242         * @param operation
243         *            to be converted
244         * @param <T>
245         *            the type of the input to the operation
246         * @param <R>
247         *            the type of the return value
248         * @param <E>
249         *            the type of the potential exception
250         * @return the function
251         * @throws NullPointerException
252         *             if operation is null
253         * @since 1.2.0
254         */
255        static <T, R, E extends Exception> FunctionWithException<T, R, E> asFunction(RunnableWithException<E> operation) {
256                verifyOperation(operation);
257                return t -> {
258                        operation.run();
259                        return null;
260                };
261        }
262
263}