All Downloads are FREE. Search and download functionalities are using the official Maven repository.

net.yetamine.lang.exceptions.Throwing Maven / Gradle / Ivy

The newest version!
/*
 * Copyright 2016 Yetamine
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package net.yetamine.lang.exceptions;

import java.util.Objects;
import java.util.Optional;
import java.util.function.Function;
import java.util.function.Predicate;

/**
 * Provides a fluent handling of exceptions where catch handlers are not
 * available, e.g., for resolving causes of an exception.
 *
 * 

* Be extremely careful when applying on general exception classes, when * incorrect handling of {@link InterruptedException} can happen easily, leaving * possibly dire consequences in non-responsive threads that were interrupted in * order to finish etc. * *

* When unsure how the exception could be handled, a good prevention of such * problems is applying code like {@code throwing.then(Interruption::renew)} * early in the handling pipeline. * * @param * the type of the exception to handle */ public final class Throwing { /** Sole instance of {@code null} exception. */ private static final Throwing NONE = new Throwing<>(null); /** Exception to handle. */ private final T throwable; /** * Creates a new instance. * * @param t * the exception to handle */ private Throwing(T t) { throwable = t; } /** * Narrows a widened type performing a safe type cast (thanks to the safe * covariant changes for immutable types). * * @param * the type of the exception to handle * * @param instance * the instance to narrow * * @return the narrowed instance */ @SuppressWarnings("unchecked") public static Throwing narrow(Throwing instance) { return (Throwing) instance; } /** * Guards the operation and catches any {@link Throwable}. * * @param operation * the operation to guard. It must not be {@code null}. * * @return an instance containing the caught exception if any * * @throws NullPointerException * if the operation is {@code null}; this is the case which is * not intentionally protected as it indicates an error in the * code which needs correction */ public static Throwing sandbox(ThrowingRunnable operation) { Objects.requireNonNull(operation); try { // Execute in sandbox operation.run(); } catch (Throwable t) { return some(t); } return none(); } /** * Guards the operation and catches any {@link Exception} (however, no * {@link Error} is caught intentionally). * * @param operation * the operation to guard. It must not be {@code null}. * * @return an instance containing the caught exception if any * * @throws NullPointerException * if the operation is {@code null}; this is the case which is * not intentionally protected as it indicates an error in the * code which needs correction */ public static Throwing guard(ThrowingRunnable operation) { Objects.requireNonNull(operation); try { // Execute in sandbox operation.run(); } catch (Exception e) { return some(e); } return none(); } /** * Returns an instance for handling the given exception. * * @param * the type of the exception * @param throwable * the exception to handle. It must not be {@code null}. * * @return an instance for handling the given exception */ public static Throwing some(T throwable) { return new Throwing<>(Objects.requireNonNull(throwable)); } /** * Returns an instance for handling the given exception. * * @param * the type of the exception * @param throwable * the exception to handle. It may be {@code null} unlike in the * case of {@link #some(Throwable)}. * * @return an instance for handling the given exception */ public static Throwing maybe(T throwable) { return (throwable != null) ? new Throwing<>(throwable) : none(); } /** * Returns an instance that throws nothing. * * @param * the type of the exception * * @return an instance throwing nothing */ @SuppressWarnings("unchecked") public static Throwing none() { return (Throwing) NONE; } /** * Returns an instance for handling the cause (if any) of the given * exception. * * @param throwable * the throwable whose cause shall be handled. It must not be * {@code null}. * * @return an instance for handling the cause of the given exception */ public static Throwing cause(Throwable throwable) { return maybe(throwable.getCause()); } /** * Returns an instance for handling the cause (if any) of the current * exception. * * @return an instance for handling the cause of the current exception */ public Throwing cause() { return (throwable != null) ? maybe(throwable.getCause()) : none(); } /** * Throws the exception if it is of the given type and satisfies the given * predicate. * * @param * the desired type of the exception * @param clazz * the desired type of the exception * @param condition * the condition for throwing the exception. It must not be * {@code null}. * * @return this instance * * @throws X * if the exception to handle is of the desired type and the * predicate succeeds */ public Throwing throwIf(Class clazz, Predicate condition) throws X { if (clazz.isInstance(throwable)) { final X t = clazz.cast(throwable); if (condition.test(t)) { throw t; } } assert (condition != null); return this; } /** * Throws the exception if it is of the given type and satisfies the given * predicate. * * @param * the desired type of the exception * @param clazz * the desired type of the exception * * @return this instance * * @throws X * if the exception to handle is of the desired type and the * predicate succeeds */ public Throwing throwIf(Class clazz) throws X { if (clazz.isInstance(throwable)) { throw clazz.cast(throwable); } return this; } /** * Throws the exception if unchecked (i.e., {@link RuntimeException} or * {@link Error}). * * @return this instance */ public Throwing throwIfUnchecked() { return throwIf(RuntimeException.class).throwIf(Error.class); } /** * Throws the exception that the given mapping function returns. * * @param mapping * the function to map the current exception to another. It must * not be {@code null}. * * @param * the type of the exception to possibly throw * * @return this instance * * @throws X * if the mapping function returns this exception */ public Throwing throwAs(Function mapping) throws X { final X t = mapping.apply(throwable); if (t != null) { throw t; } return this; } /** * Throws the exception that the given mapping function returns if the * current exception is of the given type. * * @param mapping * the function to map the current exception to another. It must * not be {@code null}. * @param clazz * the desired type of the exception * * @param * the desired type of the current exception * @param * the type of the exception to possibly throw * @return this instance * * @throws Z * if the exception to handle is of the desired type and the * mapping function returns this exception to throw */ public Throwing throwAs(Function mapping, Class clazz) throws Z { if (clazz.isInstance(throwable)) { final Z t = mapping.apply(clazz.cast(throwable)); if (t != null) { throw t; } } return this; } /** * Maps the exception with the given function to a different one, if any * present. * * @param * the type of the resulting exception * @param mapping * the mapping function. It must not be {@code null}. * * @return an instance representing the mapped exception, possibly * {@link #none()} */ public Throwing map(Function mapping) { return (throwable != null) ? maybe(mapping.apply(throwable)) : none(); } /** * Invokes the given handler with the exception of the desired type if any. * * @param * the type of the exception to process * @param * the type of the exception that the handler declares to throw * @param type * the type of the exception to process. It must not be * {@code null}. * @param handler * the handler to use. It must not be {@code null}. * * @return this instance * * @throws X * if the handler throws the exception */ public Throwing when(Class type, ThrowingConsumer handler) throws X { if (type.isInstance(throwable)) { handler.accept(type.cast(throwable)); } return this; } /** * Invokes the given handler when the exception has the desired type. * * @param * the type of the exception that the handler declares to throw * @param type * the type of the exception to process. It must not be * {@code null}. * @param handler * the handler to use. It must not be {@code null}. * * @return this instance * * @throws X * if the handler throws the exception */ public Throwing when(Class type, ThrowingRunnable handler) throws X { if (type.isInstance(throwable)) { handler.run(); } return this; } /** * Passes the exception to handle, if any, to the given handler. * * @param * the type of the exception that the handler declares to throw * @param handler * the handler to use. It must not be {@code null}. * * @return this instance * * @throws X * if the handler throws the exception */ public Throwing then(ThrowingConsumer handler) throws X { if (throwable != null) { handler.accept(throwable); } return this; } /** * Executes the given action regardless of an exception pending to handle. * *

* If the action throws an exception, the exception is thrown and any * pending exception shall be added as a suppressed one. * * @param * the type of the exception that the action may throw * @param action * the action to execute. It must not be {@code null}. * * @return this instance * * @throws X * if the action fails */ public Throwing anyway(ThrowingRunnable action) throws X { try { action.run(); } catch (Throwable t) { if (throwable != null) { t.addSuppressed(throwable); } throw t; } return this; } /** * Executes the given action if no exception pending to handle. * *

* If the action throws an exception, the exception is thrown. * * @param * the type of the exception that the action may throw * @param action * the action to execute. It must not be {@code null}. * * @return this instance * * @throws X * if the action fails */ public Throwing otherwise(ThrowingRunnable action) throws X { if (throwable == null) { action.run(); } return this; } /** * Executes the given operation regardless of an exception pending to * handle. * *

* If the action throws an exception, the exception is thrown and any * pending exception shall be added as a suppressed one. * * @param * the type of the result * @param * the type of the exception that the action may throw * @param operation * the operation to execute. It must not be {@code null}. * * @return the result of the operation * * @throws X * if the action fails */ public R yield(ThrowingOperation operation) throws X { try { return operation.execute(throwable); } catch (Throwable t) { if (throwable != null) { t.addSuppressed(throwable); } throw t; } } /** * Rethrows the exception to handle if any. * * @return this instance * * @throws T * if there is any exception to handle */ public Throwing rethrow() throws T { if (throwable != null) { throw throwable; } return this; } /** * Returns the exception provided by this instance. * * @return the exception provided by this instance */ public Optional throwable() { return Optional.ofNullable(throwable); } /** * Indicates if an actual exception, which could be thrown, is represented. * * @return {@code true} iff {@code throwable().isPresent()} */ public boolean couldThrow() { return (throwable != null); } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy