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