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.io.File;
027import java.io.FileFilter;
028import java.util.function.Function;
029import java.util.function.Supplier;
030
031/**
032 * A filter for abstract pathnames and may throw an exception.
033 * <h3>General contract</h3>
034 * <ul>
035 * <li><b>{@link #accept(File) boolean accept(File path) throws
036 * E}</b>&nbsp;-&nbsp;The functional method.</li>
037 * <li><b>uncheck</b>&nbsp;-&nbsp;Return a {@code FileFilter}</li>
038 * <li><b>lift</b>&nbsp;-&nbsp;Return a {@code FileFilter}</li>
039 * <li><b>ignore</b>&nbsp;-&nbsp;Return a {@code FileFilter}</li>
040 * </ul>
041 *
042 * @see FileFilter
043 * @param <E>
044 *            the type of the potential exception of the function
045 * @since 1.1.0
046 */
047@FunctionalInterface
048public interface FileFilterWithException<E extends Exception>
049                extends PrimitiveReturnExceptionHandlerSupport<FileFilter, FileFilterWithException<E>>, BooleanDefaultValue {
050
051        /**
052         * Tests whether or not the specified abstract pathname should be included in a
053         * pathname list.
054         *
055         * @param pathname
056         *            The abstract pathname to be tested
057         * @return <code>true</code> if and only if <code>pathname</code> should be
058         *         included
059         * @throws E
060         *             any exception
061         * @see FileFilter#accept(File)
062         */
063        boolean accept(File pathname) throws E;
064
065        @Override
066        default FileFilter uncheckOrIgnore(boolean uncheck) {
067                return pathname -> {
068                        try {
069                                return accept(pathname);
070                        } catch (Exception e) {
071                                PrimitiveReturnExceptionHandlerSupport.handleException(uncheck, e, exceptionMapper());
072                                return defaultValue();
073                        }
074                };
075        }
076
077        /**
078         * Returns a composed FileFilter that represents a short-circuiting logical AND
079         * of this FileFilter and another. When evaluating the composed predicate, if
080         * this predicate is {@code false}, then the {@code other} predicate is not
081         * evaluated.
082         *
083         * <p>
084         * Any exceptions thrown during evaluation of either FileFilter are relayed to
085         * the caller; if evaluation of this predicate throws an exception, the
086         * {@code other} FileFilter will not be evaluated.
087         *
088         * @param other
089         *            a FileFilter that will be logically-ANDed with this predicate
090         * @return a composed FileFilter that represents the short-circuiting logical
091         *         AND of this FileFilter and the {@code other} FileFilter
092         * @throws NullPointerException
093         *             if other is null
094         * @see #or(FileFilterWithException)
095         * @see #negate()
096         */
097        default FileFilterWithException<E> and(FileFilterWithException<? extends E> other) {
098                requireNonNull(other);
099                return t -> accept(t) && other.accept(t);
100        }
101
102        /**
103         * Returns a FileFilter that represents the logical negation of this FileFilter.
104         *
105         * @return a FileFilter that represents the logical negation of this FileFilter
106         * @see #and(FileFilterWithException)
107         * @see #or(FileFilterWithException)
108         */
109        default FileFilterWithException<E> negate() {
110                return t -> !accept(t);
111        }
112
113        /**
114         * Negate a {@code PredicateWithException}.
115         *
116         * @param predicate
117         *            to be negate
118         * @param <E>
119         *            the type of the potential exception
120         * @return the negated FileFilter
121         * @see #negate()
122         * @throws NullPointerException
123         *             if predicate is null
124         */
125        static <E extends Exception> FileFilterWithException<E> negate(FileFilterWithException<E> predicate) {
126                return verifyPredicate(predicate).negate();
127        }
128
129        /**
130         * Returns a composed FileFilter that represents a short-circuiting logical OR
131         * of this FileFilter and another. When evaluating the composed FileFilter, if
132         * this FileFilter is {@code true}, then the {@code other} predicate is not
133         * evaluated.
134         *
135         * <p>
136         * Any exceptions thrown during evaluation of either FileFilter are relayed to
137         * the caller; if evaluation of this FileFilter throws an exception, the
138         * {@code other} FileFilter will not be evaluated.
139         *
140         * @param other
141         *            a FileFilter that will be logically-ORed with this predicate
142         * @return a composed FileFilter that represents the short-circuiting logical OR
143         *         of this predicate and the {@code other} FileFilter
144         * @throws NullPointerException
145         *             if other is null
146         * @see #and(FileFilterWithException)
147         * @see #negate()
148         */
149        default FileFilterWithException<E> or(FileFilterWithException<? extends E> other) {
150                requireNonNull(other);
151                return t -> accept(t) || other.accept(t);
152        }
153
154        /**
155         * Returns a FileFilter that always throw exception.
156         *
157         * @param exceptionBuilder
158         *            the supplier to create the exception
159         * @param <E>
160         *            the type of the exception
161         * @return a FileFilter that always throw exception
162         */
163        static <E extends Exception> FileFilterWithException<E> failing(Supplier<E> exceptionBuilder) {
164                return t -> {
165                        throw exceptionBuilder.get();
166                };
167        }
168
169        /**
170         * Converts a {@code FileFilterWithException} to a {@code FileFilter} that wraps
171         * exception to {@code RuntimeException}.
172         *
173         * @param predicate
174         *            to be unchecked
175         * @param <E>
176         *            the type of the potential exception
177         * @return the unchecked FileFilter
178         * @see #uncheck()
179         * @see #unchecked(FileFilterWithException, Function)
180         * @throws NullPointerException
181         *             if predicate is null
182         */
183        static <E extends Exception> FileFilter unchecked(FileFilterWithException<E> predicate) {
184                return verifyPredicate(predicate).uncheck();
185        }
186
187        /**
188         * Converts a {@code FileFilterWithException} to a {@code FileFilter} that wraps
189         * exception to {@code RuntimeException} by using the provided mapping function.
190         *
191         * @param predicate
192         *            the be unchecked
193         * @param exceptionMapper
194         *            a function to convert the exception to the runtime exception.
195         * @param <E>
196         *            the type of the potential exception
197         * @return the unchecked FileFilter
198         * @see #uncheck()
199         * @see #unchecked(FileFilterWithException)
200         * @throws NullPointerException
201         *             if predicate or exceptionMapper is null
202         */
203        static <E extends Exception> FileFilter unchecked(FileFilterWithException<E> predicate,
204                        Function<Exception, RuntimeException> exceptionMapper) {
205                verifyPredicate(predicate);
206                verifyExceptionMapper(exceptionMapper);
207                return new FileFilterWithException<E>() {
208
209                        @Override
210                        public boolean accept(File pathname) throws E {
211                                return predicate.accept(pathname);
212                        }
213
214                        @Override
215                        public Function<Exception, RuntimeException> exceptionMapper() {
216                                return exceptionMapper;
217                        }
218
219                }.uncheck();
220        }
221
222        /**
223         * Converts a {@code FileFilterWithException} to a lifted {@code FileFilter}
224         * returning {@code false} in case of exception.
225         *
226         * @param predicate
227         *            to be lifted
228         * @param <E>
229         *            the type of the potential exception
230         * @return the lifted FileFilter
231         * @see #lift()
232         * @throws NullPointerException
233         *             if predicate is null
234         */
235        static <E extends Exception> FileFilter lifted(FileFilterWithException<E> predicate) {
236                return verifyPredicate(predicate).lift();
237        }
238
239        /**
240         * Converts a {@code FileFilterWithException} to a lifted {@code FileFilter}
241         * returning {@code false} in case of exception.
242         *
243         * @param predicate
244         *            to be lifted
245         * @param <E>
246         *            the type of the potential exception
247         * @return the lifted FileFilter
248         * @see #ignore()
249         * @throws NullPointerException
250         *             if predicate is null
251         */
252        static <E extends Exception> FileFilter ignored(FileFilterWithException<E> predicate) {
253                return verifyPredicate(predicate).ignore();
254        }
255
256        /**
257         * Converts a {@code FileFilterWithException} to a lifted {@code FileFilter}
258         * returning a default value in case of exception.
259         *
260         * @param predicate
261         *            to be lifted
262         * @param defaultValue
263         *            value in case of exception
264         * @param <E>
265         *            the type of the potential exception
266         * @return the lifted FileFilter
267         * @see #ignore()
268         * @see #ignored(FileFilterWithException)
269         * @throws NullPointerException
270         *             if predicate is null
271         * @since 3.0.0
272         */
273        static <E extends Exception> FileFilter ignored(FileFilterWithException<E> predicate, boolean defaultValue) {
274                verifyPredicate(predicate);
275                return new FileFilterWithException<E>() {
276
277                        @Override
278                        public boolean accept(File pathname) throws E {
279                                return predicate.accept(pathname);
280                        }
281
282                        @Override
283                        public boolean defaultValue() {
284                                return defaultValue;
285                        }
286
287                }.ignore();
288        }
289
290}