ExUnwrapper.java

package net.dapete.exceptional.wrap;

import net.dapete.exceptional.ExException;
import net.dapete.exceptional.internal.ExUtils;

import java.util.Set;
import java.util.function.Supplier;

/**
 * Unwraps exceptions of the supplied type. All or some of these types may be identical if less than 3 are supplied.
 *
 * @param <E1> the type of the first exception to unwrap.
 * @param <E2> the type of the second exception to unwrap.
 * @param <E3> the type of the third exception to unwrap.
 */
public final class ExUnwrapper<E1 extends Exception, E2 extends Exception, E3 extends Exception> {

    private final Class<E1> exceptionClass1;
    private final Class<E2> exceptionClass2;
    private final Class<E3> exceptionClass3;

    private final Set<Class<? extends Exception>> exceptionClasses;

    ExUnwrapper(Class<E1> exceptionClass1, Class<E2> exceptionClass2, Class<E3> exceptionClass3) {
        this.exceptionClass1 = exceptionClass1;
        this.exceptionClass2 = exceptionClass2;
        this.exceptionClass3 = exceptionClass3;
        this.exceptionClasses = ExUtils.setOf3(exceptionClass1, exceptionClass2, exceptionClass3);
    }

    /**
     * Create an instance to unwrap the supplied exception class.
     *
     * @param exceptionClass the exception class.
     * @param <E>            the type of the exception class.
     * @return an instance to unwrap the supplied exception class.
     */
    public static <E extends Exception> ExUnwrapper<E, E, E> of(Class<E> exceptionClass) {
        return new ExUnwrapper<>(exceptionClass, exceptionClass, exceptionClass);
    }

    /**
     * Create an instance to unwrap the supplied exception classes.
     *
     * @param exceptionClass1 the first exception class.
     * @param exceptionClass2 the second exception class.
     * @param <E1>            the type of the first exception class.
     * @param <E2>            the type of the second exception class.
     * @return an instance to unwrap the supplied exception classes.
     */
    public static <E1 extends Exception, E2 extends Exception> ExUnwrapper<E1, E2, E2> of(Class<E1> exceptionClass1, Class<E2> exceptionClass2) {
        return new ExUnwrapper<>(exceptionClass1, exceptionClass2, exceptionClass2);
    }

    /**
     * Create an instance to unwrap the supplied exception classes.
     *
     * @param exceptionClass1 the first exception class.
     * @param exceptionClass2 the second exception class.
     * @param exceptionClass3 the third exception class.
     * @param <E1>            the type of the first exception class.
     * @param <E2>            the type of the second exception class.
     * @param <E3>            the type of the third exception class.
     * @return an instance to unwrap the supplied exception classes.
     */
    public static <E1 extends Exception, E2 extends Exception, E3 extends Exception> ExUnwrapper<E1, E2, E3> of(
            Class<E1> exceptionClass1, Class<E2> exceptionClass2, Class<E3> exceptionClass3) {
        return new ExUnwrapper<>(exceptionClass1, exceptionClass2, exceptionClass3);
    }

    public void unwrap(Runnable runnable) throws E1, E2, E3 {
        try {
            ExUnwrap.unwrapScope(exceptionClasses, runnable);
        } catch (ExException e) {
            e.unwrap(exceptionClass1, exceptionClass2, exceptionClass3);
            // the compiler doesn't know that unwrap always throws an exception
            throw e;
        }
    }

    public <T> T unwrap(Supplier<T> supplier) throws E1, E2, E3 {
        try {
            return ExUnwrap.unwrapScope(exceptionClasses, supplier);
        } catch (ExException e) {
            e.unwrap(exceptionClass1, exceptionClass2, exceptionClass3);
            // the compiler doesn't know that unwrap always throws an exception
            throw e;
        }
    }

}