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.verifyFunction;
024
025import java.io.ObjectInputFilter;
026import java.io.ObjectInputFilter.FilterInfo;
027import java.io.ObjectInputFilter.Status;
028import java.io.ObjectInputStream;
029import java.util.Objects;
030import java.util.Optional;
031import java.util.concurrent.CompletionStage;
032import java.util.function.Function;
033import java.util.function.Supplier;
034
035/**
036 * Filter classes, array lengths, and graph metrics during deserialization that
037 * may throw exception. If set on an {@link ObjectInputStream}, the
038 * {@link #checkInput checkInput(FilterInfo)} method is called to validate
039 * classes, the length of each array, the number of objects being read from the
040 * stream, the depth of the graph, and the total number of bytes read from the
041 * stream.
042 * <h3>General contract</h3>
043 * <ul>
044 * <li><b>{@link #checkInput(FilterInfo) Status checkInput(FilterInfo
045 * filterInfo) throws E}</b>&nbsp;-&nbsp;The functional method.</li>
046 * <li><b>uncheck</b>&nbsp;-&nbsp;Return a {@code ObjectInputFilter}</li>
047 * <li><b>lift</b>&nbsp;-&nbsp;Return a
048 * {@code Function<FilterInfo, Optional<Status>>}</li>
049 * <li><b>ignore</b>&nbsp;-&nbsp;Return a {@code ObjectInputFilter}</li>
050 * </ul>
051 *
052 * @see ObjectInputFilter
053 * @param <E>
054 *            the type of the potential exception of the function
055 * @since 2.0.0
056 */
057@FunctionalInterface
058public interface ObjectInputFilterWithException<E extends Exception> extends
059                ObjectReturnExceptionHandlerSupport<ObjectInputFilter, Function<FilterInfo, Optional<Status>>, Function<FilterInfo, CompletionStage<Status>>, Status, ObjectInputFilterWithException<E>> {
060
061        /**
062         * Check the class, array length, number of object references, depth, stream
063         * size, and other available filtering information. Implementations of this
064         * method check the contents of the object graph being created during
065         * deserialization. The filter returns {@link Status#ALLOWED Status.ALLOWED},
066         * {@link Status#REJECTED Status.REJECTED}, or {@link Status#UNDECIDED
067         * Status.UNDECIDED}.
068         *
069         * @param filterInfo
070         *            provides information about the current object being deserialized,
071         *            if any, and the status of the {@link ObjectInputStream}
072         * @return {@link Status#ALLOWED Status.ALLOWED} if accepted,
073         *         {@link Status#REJECTED Status.REJECTED} if rejected,
074         *         {@link Status#UNDECIDED Status.UNDECIDED} if undecided.
075         * @throws E
076         *             any exception
077         * @see ObjectInputFilter#checkInput(java.io.ObjectInputFilter.FilterInfo)
078         */
079        Status checkInput(FilterInfo filterInfo) throws E;
080
081        /**
082         * Converts this {@code ObjectInputFilterWithException} to a
083         * {@code ObjectInputFilter} that convert exception to {@code RuntimeException}.
084         *
085         * @return the unchecked ObjectInputFilter
086         * @see #unchecked(ObjectInputFilterWithException)
087         * @see #unchecked(ObjectInputFilterWithException, Function)
088         * @see ObjectInputFilter
089         */
090        @Override
091        default ObjectInputFilter uncheck() {
092                return t -> ObjectReturnExceptionHandlerSupport.unchecked(() -> checkInput(t), throwingHandler());
093        }
094
095        /**
096         * Converts this {@code ObjectInputFilterWithException} to a lifted
097         * {@code Function} using {@code Optional} as return value.
098         *
099         * @return the lifted function
100         * @see #lifted(ObjectInputFilterWithException)
101         * @see Function
102         */
103        @Override
104        default Function<FilterInfo, Optional<Status>> lift() {
105                return t -> ObjectReturnExceptionHandlerSupport.unchecked(() -> Optional.ofNullable(checkInput(t)),
106                                notThrowingHandler());
107        }
108
109        @Override
110        default Status defaultValue() {
111                return Status.UNDECIDED;
112        }
113
114        /**
115         * Converts this {@code ObjectInputFilterWithException} to a lifted
116         * {@code ObjectInputFilter} returning {@link Status#UNDECIDED Status.UNDECIDED}
117         * (or the value redefined by the method {@link #defaultValue()}) in case of
118         * exception.
119         *
120         * @return the function that ignore error
121         * @see #ignored(ObjectInputFilterWithException)
122         * @see Function
123         */
124        @Override
125        default ObjectInputFilter ignore() {
126                return t -> lift().apply(t).orElse(defaultValue());
127        }
128
129        /**
130         * Convert this {@code ObjectInputFilterWithException} to a lifted
131         * {@code ObjectInputFilter} return {@code CompletionStage} as return value.
132         *
133         * @return the lifted function
134         * @see #staged(ObjectInputFilterWithException)
135         * @see CompletionStage
136         */
137        @Override
138        default Function<FilterInfo, CompletionStage<Status>> stage() {
139                return t -> ObjectReturnExceptionHandlerSupport.staged(() -> checkInput(t));
140        }
141
142        /**
143         * Returns a ObjectInputFilter that always throw exception.
144         *
145         * @param exceptionBuilder
146         *            the supplier to create the ObjectInputFilter the type of the
147         *            result of the function
148         * @param <E>
149         *            the type of the exception
150         * @return a ObjectInputFilter that always throw exception
151         */
152        static <E extends Exception> ObjectInputFilterWithException<E> failing(Supplier<E> exceptionBuilder) {
153                return t -> {
154                        throw exceptionBuilder.get();
155                };
156        }
157
158        /**
159         * Converts a {@code ObjectInputFilterWithException} to a
160         * {@code ObjectInputFilter} that convert exception to {@code RuntimeException}.
161         *
162         * @param function
163         *            to be unchecked
164         * @param <E>
165         *            the type of the potential exception
166         * @return the unchecked exception
167         * @see #uncheck()
168         * @see #unchecked(ObjectInputFilterWithException, Function)
169         * @throws NullPointerException
170         *             if function is null
171         */
172        static <E extends Exception> ObjectInputFilter unchecked(ObjectInputFilterWithException<E> function) {
173                return verifyFunction(function).uncheck();
174        }
175
176        /**
177         * Converts a {@code ObjectInputFilterWithException} to a
178         * {@code ObjectInputFilter} that wraps exception to {@code RuntimeException} by
179         * using the provided mapping function.
180         *
181         * @param function
182         *            the be unchecked
183         * @param exceptionMapper
184         *            a function to convert the exception to the runtime exception.
185         * @param <E>
186         *            the type of the potential exception
187         * @return the unchecked exception
188         * @see #uncheck()
189         * @see #unchecked(ObjectInputFilterWithException)
190         * @throws NullPointerException
191         *             if function or exceptionMapper is null
192         */
193        static <E extends Exception> ObjectInputFilter unchecked(ObjectInputFilterWithException<E> function,
194                        Function<Exception, RuntimeException> exceptionMapper) {
195                verifyFunction(function);
196                verifyExceptionMapper(exceptionMapper);
197                return new ObjectInputFilterWithException<E>() {
198
199                        @Override
200                        public Status checkInput(FilterInfo filterInfo) throws E {
201                                return function.checkInput(filterInfo);
202                        }
203
204                        @Override
205                        public Function<Exception, RuntimeException> exceptionMapper() {
206                                return exceptionMapper;
207                        }
208
209                }.uncheck();
210        }
211
212        /**
213         * Converts a {@code ObjectInputFilterWithException} to a lifted
214         * {@code ObjectInputFilter} using {@code Optional} as return value.
215         *
216         * @param function
217         *            to be lifted
218         * @param <E>
219         *            the type of the potential exception
220         * @return the lifted function
221         * @see #lift()
222         * @throws NullPointerException
223         *             if function is null
224         */
225        static <E extends Exception> Function<FilterInfo, Optional<Status>> lifted(
226                        ObjectInputFilterWithException<E> function) {
227                return verifyFunction(function).lift();
228        }
229
230        /**
231         * Converts a {@code ObjectInputFilterWithException} to a lifted
232         * {@code ObjectInputFilter} returning {@link Status#UNDECIDED Status.UNDECIDED}
233         * in case of exception.
234         *
235         * @param function
236         *            to be lifted
237         * @param <E>
238         *            the type of the potential exception
239         * @return the lifted function
240         * @see #ignore()
241         * @throws NullPointerException
242         *             if function is null
243         */
244        static <E extends Exception> ObjectInputFilter ignored(ObjectInputFilterWithException<E> function) {
245                return verifyFunction(function).ignore();
246        }
247
248        /**
249         * Converts a {@code ObjectInputFilterWithException} to a lifted
250         * {@code ObjectInputFilter} returning a default value in case of exception.
251         *
252         * @param function
253         *            to be lifted
254         * @param defaultValue
255         *            the default value in case of exception. <b>Can't be null</b>.
256         * @param <E>
257         *            the type of the potential exception
258         * @return the lifted function
259         * @see #ignore()
260         * @see #ignored(ObjectInputFilterWithException)
261         * @throws NullPointerException
262         *             if function or defaultValue is null
263         * @since 3.0.0
264         */
265        static <E extends Exception> ObjectInputFilter ignored(ObjectInputFilterWithException<E> function,
266                        Status defaultValue) {
267                verifyFunction(function);
268                Objects.requireNonNull(defaultValue, "defaultValue can't be null");
269                return new ObjectInputFilterWithException<E>() {
270
271                        @Override
272                        public Status checkInput(FilterInfo filterInfo) throws E {
273                                return function.checkInput(filterInfo);
274                        }
275
276                        @Override
277                        public Status defaultValue() {
278                                return defaultValue;
279                        }
280
281                }.ignore();
282        }
283
284        /**
285         * Convert this {@code ObjectInputFilterWithException} to a lifted
286         * {@code Function} return {@code CompletionStage} as return value.
287         *
288         * @param function
289         *            to be lifted
290         * @param <E>
291         *            the type of the potential exception
292         * @return the lifted function
293         * @see #stage()
294         * @throws NullPointerException
295         *             if function is null
296         */
297        static <E extends Exception> Function<FilterInfo, CompletionStage<Status>> staged(
298                        ObjectInputFilterWithException<E> function) {
299                return verifyFunction(function).stage();
300        }
301
302}