Exceptional.java

/*-
 * #%L
 * io.earcam.unexceptional
 * %%
 * Copyright (C) 2016 - 2017 earcam
 * %%
 * SPDX-License-Identifier: (BSD-3-Clause OR EPL-1.0 OR Apache-2.0 OR MIT)
 * 
 * You <b>must</b> choose to accept, in full - any individual or combination of 
 * the following licenses:
 * <ul>
 * 	<li><a href="https://opensource.org/licenses/BSD-3-Clause">BSD-3-Clause</a></li>
 * 	<li><a href="https://www.eclipse.org/legal/epl-v10.html">EPL-1.0</a></li>
 * 	<li><a href="https://www.apache.org/licenses/LICENSE-2.0">Apache-2.0</a></li>
 * 	<li><a href="https://opensource.org/licenses/MIT">MIT</a></li>
 * </ul>
 * #L%
 */
package io.earcam.unexceptional;

import static java.util.Collections.unmodifiableMap;

import java.io.IOException;
import java.io.Serializable;
import java.io.UncheckedIOException;
import java.lang.Thread.UncaughtExceptionHandler;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.UndeclaredThrowableException;
import java.net.MalformedURLException;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.net.URLStreamHandler;
import java.security.GeneralSecurityException;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.Callable;
import java.util.function.BiConsumer;
import java.util.function.BiFunction;
import java.util.function.BinaryOperator;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.IntConsumer;
import java.util.function.Predicate;
import java.util.function.Supplier;
import java.util.function.ToDoubleFunction;
import java.util.function.ToIntBiFunction;
import java.util.function.ToIntFunction;
import java.util.function.ToLongFunction;
import java.util.stream.Stream;

/**
 * <p>
 * Static utility for easy conversion of checked exceptions, and invocation of methods declaring checked exceptions.
 * </p>
 * 
 * <p>
 * There are many cases where you <i>know</i>, within a given context that a checked exception will not be raised, but
 * if it is, then it's certainly <i>game over</i>. An example being URLs in server configuration.
 * </p>
 * 
 * <p>
 * The functionality centres around {@link java.util.function} given their beautifully broad application.
 * </p>
 * 
 * <p>
 * In addition, common standard library <i>annoyances</i> are included; e.g. {@link URL}, {@link URI}, and highly
 * concise (re-read as terse one-liner - arguably laconic over expressive) for common IO streams usage.
 * </p>
 * 
 * <p>
 * When dealing with {@link Stream} from an IO source (e.g. the file system), it may be preferable (to pay a
 * performance cost due to wrapping) and use {@link EmeticStream} which mirrors {@link Stream} functionality
 * but with checked equivalent types.
 * </p>
 * 
 * <h1>Exceptional</h1>
 * <b>/ɪkˈsɛpʃ(ə)n(ə)l,ɛkˈsɛpʃ(ə)n(ə)l/</b>
 * <p>
 * <i>adjective:</i>
 * </p>
 * <p>
 * 1. unusual; not typical.
 * </p>
 */
/* @javax.annotation.ParametersAreNonnullByDefault */
/* @edu.umd.cs.findbugs.annotations.ReturnValuesAreNonnullByDefault */
/* @net.jcip.annotations.Immutable */
@SuppressWarnings({
		"squid:S1905", // SonarQube false positives
		"squid:S1181"  // SonarQube necessary evil
})
public final class Exceptional implements Serializable {

	private static final long serialVersionUID = -1350140594550206145L;

	private static final Map<Class<? extends Throwable>, Function<Throwable, RuntimeException>> UNCHECK_MAP;

	static {
		Map<Class<? extends Throwable>, Function<Throwable, RuntimeException>> map = new HashMap<>();
		// @formatter:off
		map.put(ReflectiveOperationException.class, e -> new UncheckedReflectiveException((ReflectiveOperationException)e));
		map.put(    GeneralSecurityException.class, e -> new UncheckedSecurityException((GeneralSecurityException)e));
		map.put(        InterruptedException.class, e -> new UncheckedInterruptException((InterruptedException)e));
		map.put(          URISyntaxException.class, e -> new UncheckedUriSyntaxException((URISyntaxException)e));
		map.put(            RuntimeException.class, RuntimeException.class::cast);
		map.put(                 IOException.class, e -> new UncheckedIOException((IOException)e));
		// @formatter:on

		UNCHECK_MAP = unmodifiableMap(map);
	}

	/**
	 * An {@link UncaughtExceptionHandler} that simply rethrows,
	 * wrapping in an appropriate unchecked if necessary
	 * 
	 * @since 0.2.0
	 */
	public static final UncaughtExceptionHandler RETHROWING = (t, e) -> Exceptional.rethrow(e);

	/**
	 * An {@link UncaughtExceptionHandler} that simply swallows <i>all</i> exceptions,
	 * except subclasses of {@link Error}, which are rethrown.
	 * 
	 * @since 0.2.0
	 */
	public static final UncaughtExceptionHandler SWALLOWING = (t, e) -> Exceptional.swallow(e);


	private Exceptional()
	{
		throw new IllegalStateException("Why on earth would you want to instantiate this?");
	}


	/**
	 * @param earl the text URL
	 * @return a {@link URL} representation
	 * 
	 * @since 0.2.0
	 */
	public static URL url(CharSequence earl)
	{
		try {
			return new URL(earl.toString());
		} catch(MalformedURLException e) {
			throw uncheck(e);
		}
	}


	/**
	 * @param protocol e.g. https, ftp, sctp
	 * @param host hostname or IP
	 * @param port 0 to 65536
	 * @param path the "file" portion of the URL
	 * @param handler optional URLStreamHandler
	 * @return a {@link URL} representation
	 * 
	 * @since 0.2.0
	 */
	public static URL url(String protocol, String host, int port, String path, /* @Nullable */ URLStreamHandler handler)
	{
		try {
			return new URL(protocol, host, port, path, handler);
		} catch(MalformedURLException e) {
			throw uncheck(e);
		}
	}


	/**
	 * @param ʊri the text URI (as Earl is to URL, so ʊri (as in Uri Geller) is to URI)
	 * @return a {@link URI} representation
	 * 
	 * @since 0.2.0
	 */
	@SuppressWarnings("squid:S00117")  // utf8 in a parameter name is fine by me
	public static URI uri(CharSequence ʊri)
	{
		try {
			return new URI(ʊri.toString());
		} catch(URISyntaxException e) {
			throw uncheck(e);
		}
	}


	/**
	 * Invokes {@link URL#toURI()}, catching any {@link URISyntaxException} and rethrowing
	 * wrapped as {@link UncheckedUriSyntaxException}
	 * 
	 * @param earl the {@link URL} to convert
	 * @return the {@link URI} form of the <code>earl</code> argument
	 * 
	 * @since 0.3.0
	 */
	public static URI uri(URL earl)
	{
		try {
			return earl.toURI();
		} catch(URISyntaxException e) {
			throw uncheck(e);
		}
	}


	/**
	 * Will regurgitate any {@link Error} - otherwise will dutifully and silently <i>swallow</i> whole.
	 * Gulping of {@link InterruptedException}s will result in the current {@link Thread}'s interrupt
	 * flag being reset.
	 * 
	 * <b>Caution</b>: used carelessly/incorrectly this foul method will inevitably lead to a frustrating
	 * debugging session resulting in plenty of "doh"/"wtf" but never a "eureka" moment. Shooting of
	 * messenger is in breach of license terms.
	 * 
	 * @param caught the caught unmentionable
	 * 
	 * @since 0.2.0
	 */
	public static void swallow(Throwable caught)
	{
		resetIfInterrupt(caught);
		if(caught instanceof Error) {
			throw (Error) caught;
		}
	}


	/**
	 * Catching an {@link InterruptedException} clears the interrupt flag,
	 * this merely resets the flag IFF the <code>thrown</code> parameter is
	 * an instance of {@link InterruptedException}.
	 * 
	 * @param thrown possible {@link InterruptedException}
	 * 
	 * @since 0.2.0
	 */
	public static void resetIfInterrupt(Throwable thrown)
	{
		if(thrown instanceof InterruptedException) {
			Thread.currentThread().interrupt();
		}
	}


	/**
	 * Directly rethrows {@link Error}s or {@link RuntimeException}s, wraps
	 * checked exceptions appropriately
	 * 
	 * @param thrown the caught throwable to be rethrown as unchecked
	 * @return actually nothing, this just allows you to write {@code throw Exceptional.rethrow(e)} for methods than
	 * have return values.
	 * 
	 * @see #throwAsUnchecked(Throwable)
	 * 
	 * @since 0.2.0
	 */
	public static RuntimeException rethrow(Throwable thrown)
	{
		if(thrown instanceof Error) {
			throw (Error) thrown;
		}
		throw uncheck(thrown);
	}


	/**
	 * Invokes {@link CheckedRunnable#run()} catching any checked
	 * {@link Exception}s rethrowing them as unchecked.
	 * 
	 * @param runnable the checked runnable to run
	 * 
	 * @since 0.2.0
	 */
	public static void run(CheckedRunnable<?> runnable)
	{
		try {
			runnable.run();
		} catch(Error error) {  // repeated tedious (likewise with tests) as Jacoco does not add enough probes
			throw error;
		} catch(Throwable thrown) {
			throw uncheck(thrown);
		}
	}


	/**
	 * Convert a {@link CheckedRunnable} into a {@link Runnable}
	 * 
	 * @param runnable the checked runnable to wrap
	 * @return an unchecked wrapper around the <code>runnable</code> argument
	 * 
	 * @since 0.2.0
	 */
	public static Runnable uncheckRunnable(CheckedRunnable<?> runnable)
	{
		return () -> run(runnable);
	}


	/**
	 * Invokes {@link Callable#call()} catching any checked
	 * {@link Exception}s rethrowing as unchecked.
	 * 
	 * @param <T> the return type of the {@link Callable}
	 * 
	 * @param callable the {@link Callable} to execute
	 * @return the result of calling the {@link Callable}
	 * 
	 * @since 0.2.0
	 */
	public static <T> T call(Callable<T> callable)
	{
		try {
			return callable.call();
		} catch(Error error) {  // repeated tedious (likewise with tests) as Jacoco does not add enough probes
			throw error;
		} catch(Throwable thrown) {
			throw uncheck(thrown);
		}
	}


	/**
	 * <p>
	 * Converts {@link Throwable}s to {@link RuntimeException}s.
	 * </p>
	 * <p>
	 * If the supplied {@link Throwable} is already a {@link RuntimeException}, then it's simply cast and returned.
	 * </p>
	 * <p>
	 * {@link Error} subclasses will be wrapped in an {@link UncheckedException}.
	 * </p>
	 * <p>
	 * The interrupt flag will be reset IFF {@code caught instanceof InterruptedException}
	 * </p>
	 * 
	 * @param caught any {@link Throwable}
	 * @return a {@link RuntimeException}, typically a subclass of {@link UncheckedException} or an
	 * {@link UncheckedIOException}
	 * @see #rethrow(Throwable)
	 * 
	 * @since 0.2.0
	 */
	public static RuntimeException uncheck(Throwable caught)
	{
		return UNCHECK_MAP.entrySet().stream()
				.filter(e -> e.getKey().isInstance(caught))
				.map(e -> e.getValue().apply(caught))
				.findFirst()
				.orElseGet(() -> new UncheckedException(caught));
	}


	/**
	 * Converts {@link CheckedConsumer} to {@link Consumer}
	 * 
	 * @param <T> the consumed type
	 * 
	 * @param consumer a consumer that declares checked exception(s)
	 * @return a vanilla {@link java.util.function.Consumer}
	 * 
	 * @since 0.2.0
	 */
	@SuppressWarnings("unchecked")
	public static <T> Consumer<T> uncheckConsumer(CheckedConsumer<T, ?> consumer)
	{
		return (Consumer<T> & Serializable) t -> Exceptional.accept(consumer, t);
	}


	/**
	 * Invokes {@link CheckedConsumer#accept(Object)} catching any checked
	 * {@link Exception}s rethrowing as unchecked.
	 * 
	 * @param <T> the consumed type
	 * 
	 * @param consumer the consumer of the {@code value}
	 * @param value the value to be consumed
	 * 
	 * @since 0.2.0
	 */
	public static <T> void accept(CheckedConsumer<T, ?> consumer, T value)
	{
		try {
			consumer.accept(value);
		} catch(Error error) {  // repeated tedious (likewise with tests) as Jacoco does not add enough probes
			throw error;
		} catch(Throwable thrown) {
			throw uncheck(thrown);
		}
	}


	/**
	 * Converts {@link CheckedIntConsumer} to {@link IntConsumer}
	 * 
	 * @param consumer a consumer of primitive integer that declares checked exception(s)
	 * @return a vanilla {@link java.util.function.IntConsumer}
	 * 
	 * @since 0.5.0
	 */
	public static IntConsumer uncheckIntConsumer(CheckedIntConsumer<?> consumer)
	{
		return (IntConsumer & Serializable) t -> Exceptional.accept(consumer, t);
	}


	/**
	 * Invokes {@link CheckedIntConsumer#accept(int)} catching any checked
	 * {@link Exception}s rethrowing as unchecked.
	 * 
	 * @param consumer the consumer of the {@code int value}
	 * @param value the value to be consumed
	 * 
	 * @since 0.5.0
	 */
	public static void accept(CheckedIntConsumer<?> consumer, int value)
	{
		try {
			consumer.accept(value);
		} catch(Error error) {  // repeated tedious (likewise with tests) as Jacoco does not add enough probes
			throw error;
		} catch(Throwable thrown) {
			throw uncheck(thrown);
		}
	}


	/**
	 * Convert a {@link CheckedBiConsumer} into a {@link BiConsumer}.
	 * 
	 * @param <T> first argument type
	 * @param <U> last argument type
	 * 
	 * @param consumer the checked consumer
	 * @return an unchecked consumer wrapping the {@code consumer} argument
	 * 
	 * @since 0.2.0
	 */
	@SuppressWarnings("unchecked")
	public static <T, U> BiConsumer<T, U> uncheckBiConsumer(CheckedBiConsumer<T, U, ?> consumer)
	{
		return (BiConsumer<T, U> & Serializable) (t, u) -> Exceptional.accept(consumer, t, u);
	}


	/**
	 * Invokes {@link CheckedConsumer#accept(Object)} catching any checked
	 * {@link Exception}s rethrowing as unchecked.
	 * 
	 * @param <T> first argument type
	 * @param <U> last argument type
	 * 
	 * @param consumer the consumer of the {@code value}
	 * @param t first argument to be consumed
	 * @param u last argument to be consumed
	 * 
	 * @since 0.2.0
	 */
	public static <T, U> void accept(CheckedBiConsumer<T, U, ?> consumer, T t, U u)
	{
		try {
			consumer.accept(t, u);
		} catch(Error error) {  // repeated tedious (likewise with tests) as Jacoco does not add enough probes
			throw error;
		} catch(Throwable thrown) {
			throw uncheck(thrown);
		}
	}


	/**
	 * Converts a {@link CheckedFunction} into a {@link Function}.
	 * 
	 * @param <T> argument type
	 * @param <R> return type
	 * 
	 * @param function the checked function
	 * @return an unchecked function wrapping the {@code function} argument.
	 * 
	 * @since 0.2.0
	 */
	@SuppressWarnings("unchecked")
	public static <T, R> Function<T, R> uncheckFunction(CheckedFunction<T, R, ?> function)
	{
		return (Function<T, R> & Serializable) t -> Exceptional.apply(function, t);
	}


	/**
	 * Invokes {@link CheckedFunction#apply(Object)} catching any checked
	 * {@link Exception}s rethrowing as unchecked.
	 * 
	 * @param <T> argument type
	 * @param <R> return type
	 * 
	 * @param function the checked function to invoke with the {@code argument}
	 * @param argument the argument to apply to the function
	 * @return the result of applying {@code argument} to {@code function}
	 * 
	 * @since 0.2.0
	 */
	public static <T, R> R apply(CheckedFunction<T, R, ?> function, T argument)
	{
		try {
			return function.apply(argument);
		} catch(Error error) {  // repeated tedious (likewise with tests) as Jacoco does not add enough probes
			throw error;
		} catch(Throwable thrown) {
			throw uncheck(thrown);
		}
	}


	/**
	 * Convert a {@link CheckedBiFunction} into a {@link BiFunction}.
	 * 
	 * @param <T> first argument type
	 * @param <U> last argument type
	 * @param <R> return type
	 * 
	 * @param function the checked bi-function
	 * @return an unchecked bi-function wrapping the {@code function} argument.
	 * 
	 * @since 0.2.0
	 */
	@SuppressWarnings("unchecked")
	public static <T, U, R> BiFunction<T, U, R> uncheckBiFunction(CheckedBiFunction<T, U, R, ?> function)
	{
		return (BiFunction<T, U, R> & Serializable) (t, u) -> Exceptional.apply(function, t, u);
	}


	/**
	 * Invokes {@link CheckedFunction#apply(Object)} catching any checked
	 * {@link Exception}s rethrowing as unchecked.
	 * 
	 * @param <T> first argument type
	 * @param <U> last argument type
	 * @param <R> return type
	 * 
	 * @param function the checked function to invoke with the {@code argument}
	 * @param t the first argument to apply to the function
	 * @param u the second argument to apply to the function
	 * @return the result of applying {@code function} to the arguments {@code t} and {@code u}
	 * 
	 * @since 0.2.0
	 */
	public static <T, U, R> R apply(CheckedBiFunction<T, U, R, ?> function, T t, U u)
	{
		try {
			return function.apply(t, u);
		} catch(Error error) {  // repeated tedious (likewise with tests) as Jacoco does not add enough probes
			throw error;
		} catch(Throwable thrown) {
			throw uncheck(thrown);
		}
	}


	/**
	 * Converts a {@link CheckedBinaryOperator} into a {@link BinaryOperator}.
	 * 
	 * @param <T> operator type
	 * 
	 * @param operator the checked binary operator
	 * @return and unchecked wrapper around the {@code operator} argument
	 * 
	 * @since 0.2.0
	 */
	@SuppressWarnings("unchecked")
	public static <T> BinaryOperator<T> uncheckBinaryOperator(CheckedBinaryOperator<T, ?> operator)
	{
		return (BinaryOperator<T> & Serializable) (a, b) -> Exceptional.apply(operator, a, b);
	}


	/**
	 * Converts a {@link CheckedToDoubleFunction} into a {@link ToDoubleFunction}.
	 * 
	 * @param <T> argument type
	 * 
	 * @param function the checked to-double-function
	 * @return an unchecked to-double-function wrapping the {@code function} argument.
	 * 
	 * @since 0.2.0
	 */
	@SuppressWarnings("unchecked")
	public static <T> ToDoubleFunction<T> uncheckToDoubleFunction(CheckedToDoubleFunction<T, ?> function)
	{
		return (ToDoubleFunction<T> & Serializable) t -> Exceptional.applyAsDouble(function, t);
	}


	/**
	 * Invokes {@link CheckedToDoubleFunction#applyAsDouble(Object)} catching any checked
	 * {@link Exception}s rethrowing as unchecked.
	 * 
	 * @param <T> argument type
	 * 
	 * @param function the checked to-double function
	 * @param t the function argument
	 * @return the double result of applying the {@code function} to argument {@code t}
	 * 
	 * @since 0.2.0
	 */
	public static <T> double applyAsDouble(CheckedToDoubleFunction<T, ?> function, T t)
	{
		try {
			return function.applyAsDouble(t);
		} catch(Error error) {  // repeated tedious (likewise with tests) as Jacoco does not add enough probes
			throw error;
		} catch(Throwable thrown) {
			throw uncheck(thrown);
		}
	}


	/**
	 * Converts a {@link CheckedToIntFunction} into a {@link ToIntFunction}.
	 * 
	 * @param <T> argument type
	 * 
	 * @param function the checked to-int-function
	 * @return an unchecked to-int-function wrapping the {@code function} argument.
	 * 
	 * @since 0.2.0
	 */
	@SuppressWarnings("unchecked")
	public static <T> ToIntFunction<T> uncheckToIntFunction(CheckedToIntFunction<T, ?> function)
	{
		return (ToIntFunction<T> & Serializable) t -> Exceptional.applyAsInt(function, t);
	}


	/**
	 * Invokes {@link CheckedToIntFunction#applyAsInt(Object)} catching any checked
	 * {@link Exception}s rethrowing as unchecked.
	 * 
	 * @param <T> argument type
	 * 
	 * @param function the checked to-int function
	 * @param t the function argument
	 * @return the int result of applying the {@code function} to argument {@code t}
	 * 
	 * @since 0.2.0
	 */
	public static <T> int applyAsInt(CheckedToIntFunction<T, ?> function, T t)
	{
		try {
			return function.applyAsInt(t);
		} catch(Error error) {  // repeated tedious (likewise with tests) as Jacoco does not add enough probes
			throw error;
		} catch(Throwable thrown) {
			throw uncheck(thrown);
		}
	}


	/**
	 * Converts a {@link CheckedToLongFunction} into a {@link ToLongFunction}.
	 * 
	 * @param <T> argument type
	 * 
	 * @param function the checked to-long-function
	 * @return an unchecked to-long-function wrapping the {@code function} argument.
	 * 
	 * @since 0.2.0
	 */
	@SuppressWarnings("unchecked")
	public static <T> ToLongFunction<T> uncheckToLongFunction(CheckedToLongFunction<T, ?> function)
	{
		return (ToLongFunction<T> & Serializable) t -> Exceptional.applyAsLong(function, t);
	}


	/**
	 * Invokes {@link CheckedToLongFunction#applyAsLong(Object)} catching any checked
	 * {@link Exception}s rethrowing as unchecked.
	 * 
	 * @param <T> argument type
	 * 
	 * @param function the checked to-long function
	 * @param t the function argument
	 * @return the long result of applying the {@code function} to argument {@code t}
	 * 
	 * @since 0.2.0
	 */
	public static <T> long applyAsLong(CheckedToLongFunction<T, ?> function, T t)
	{
		try {
			return function.applyAsLong(t);
		} catch(Error error) {  // repeated tedious (likewise with tests) as Jacoco does not add enough probes
			throw error;
		} catch(Throwable thrown) {
			throw uncheck(thrown);
		}
	}


	/**
	 * Convert a {@link CheckedSupplier} into an unchecked {@link Supplier}.
	 * 
	 * @param <T> supplied type
	 * 
	 * @param supplier the checked supplier to wrap
	 * @return an unchecked supplier wrapping the {@code supplier} argument
	 * 
	 * @since 0.2.0
	 */
	@SuppressWarnings("unchecked")
	public static <T> Supplier<T> uncheckSupplier(CheckedSupplier<T, ?> supplier)
	{
		return (Supplier<T> & Serializable) () -> Exceptional.get(supplier);
	}


	/**
	 * Invokes {@link CheckedSupplier#get()} catching any checked
	 * {@link Exception}s rethrowing as unchecked.
	 * 
	 * @param <T> supplied type
	 * 
	 * @param supplier the checked supplier
	 * @return the result as supplied from the {@code supplier} argument
	 * 
	 * @since 0.2.0
	 */
	public static <T> T get(CheckedSupplier<T, ?> supplier)
	{
		try {
			return supplier.get();
		} catch(Error error) {  // repeated tedious (likewise with tests) as Jacoco does not add enough probes
			throw error;
		} catch(Throwable thrown) {
			throw uncheck(thrown);
		}
	}


	/**
	 * Converts a {@link CheckedPredicate} into a {@link Predicate}.
	 * 
	 * @param <T> tested type
	 * 
	 * @param predicate the checked predicate to wrap
	 * @return an unchecked predicate wrapping the {@code predicate} argument
	 * 
	 * @since 0.2.0
	 */
	@SuppressWarnings("unchecked")
	public static <T> Predicate<T> uncheckPredicate(CheckedPredicate<T, ?> predicate)
	{
		return (Predicate<T> & Serializable) t -> Exceptional.test(predicate, t);
	}


	/**
	 * Invokes {@link CheckedPredicate#test(Object)} catching any checked
	 * {@link Exception}s rethrowing as unchecked.
	 * 
	 * @param <T> tested type
	 * 
	 * @param predicate the checked predicate to test on the {@code value}
	 * @param value the value to be tested
	 * @return true IFF value passes predicate's test
	 * 
	 * @since 0.2.0
	 */
	public static <T> boolean test(CheckedPredicate<T, ?> predicate, T value)
	{
		try {
			return predicate.test(value);
		} catch(Error error) {  // repeated tedious (likewise with tests) as Jacoco does not add enough probes
			throw error;
		} catch(Throwable thrown) {
			throw uncheck(thrown);
		}
	}


	/**
	 * Converts a {@link CheckedComparator} into an unchecked {@link Comparator}.
	 * 
	 * @param <T> compared type
	 * 
	 * @param comparator the checked comparator to wrap
	 * @return an unchecked comparator wrapping the {@code comparator} argument
	 * 
	 * @since 0.2.0
	 */
	@SuppressWarnings("unchecked")
	public static <T> Comparator<T> uncheckComparator(CheckedComparator<T, ?> comparator)
	{
		return (Comparator<T> & Serializable) (a, b) -> Exceptional.applyAsInt(comparator, a, b);
	}


	/**
	 * Converts a {@link CheckedToIntBiFunction} into an unchecked {@link ToIntBiFunction}.
	 * 
	 * @param <T> first argument type
	 * @param <U> last argument type
	 * 
	 * @param function the checked bi-function
	 * @return an unchecked bi-function
	 */
	@SuppressWarnings("unchecked")
	public static <T, U> ToIntBiFunction<T, U> uncheckToIntBiFunction(CheckedToIntBiFunction<T, U, ?> function)
	{
		return (ToIntBiFunction<T, U> & Serializable) (a, b) -> Exceptional.applyAsInt(function, a, b);
	}


	/**
	 * Invokes {@link CheckedFunction#apply(Object)} catching any checked
	 * {@link Exception}s rethrowing as unchecked.
	 * 
	 * @param <T> first argument type
	 * @param <U> last argument type
	 * 
	 * @param function the checked function to invoke with the {@code argument}
	 * @param t the first argument to apply to the function
	 * @param u the second argument to apply to the function
	 * @return the result of applying {@code function} to the arguments {@code t} and {@code u}
	 * 
	 * @since 0.2.0
	 */
	public static <T, U> int applyAsInt(CheckedToIntBiFunction<T, U, ?> function, T t, U u)
	{
		try {
			return function.applyAsInt(t, u);
		} catch(Error error) {  // repeated tedious (likewise with tests) as Jacoco does not add enough probes
			throw error;
		} catch(Throwable thrown) {
			throw uncheck(thrown);
		}
	}


	/**
	 * <p>
	 * This fugly method relies on erasure to trick the compiler, allowing you to throw any checked
	 * exception without declaring so on the surrounding method. You are almost certainly better off
	 * using {@link #rethrow(Throwable)}.
	 * </p>
	 * <p>
	 * <b>Note</b>: this may well become an obsolete hack in future versions of Java if generics change.
	 * </p>
	 * 
	 * @param throwable the {@link Throwable} to be thrown
	 * @return this alleged return will never be received, but useful in that
	 * you may write <code>throws {@link Exceptional#throwAsUnchecked(Throwable)};</code>
	 * to placate the compiler WRT non-void method returns, and ensure Static Code
	 * Analysis doesn't accuse you of swallowing the exception.
	 * 
	 * @since 0.2.0
	 * 
	 * @see #rethrow(Throwable)
	 */
	public static RuntimeException throwAsUnchecked(Throwable throwable)
	{
		resetIfInterrupt(throwable);
		return Exceptional.<RuntimeException> eraseAndThrow(throwable);
	}


	@SuppressWarnings("unchecked")
	private static <T extends Throwable> RuntimeException eraseAndThrow(Throwable throwable) throws T
	{
		throw (T) throwable;
	}


	/**
	 * <p>
	 * Attempts to unwrap invocation and unchecked exceptions to their underlying cause.
	 * Unwrapping is recursively applied. The original wrapper exception is added to the suppressed
	 * throwables of the returned unwrapped throwable.
	 * </p>
	 * 
	 * <p>
	 * Unwraps: {@link InvocationTargetException}, {@link UndeclaredThrowableException}, {@link UncheckedIOException}
	 * and {@link UncheckedException}
	 * </p>
	 * 
	 * @param throwable to be unwrapped
	 * @return the root cause, or original throwable if not unwrapped
	 * 
	 * @since 0.2.0
	 * 
	 */
	public static Throwable unwrap(Throwable throwable)
	{
		Throwable unwrapped = unwrapping(throwable);
		if(unwrapped != throwable) {
			unwrapped.addSuppressed(throwable);
		}
		return unwrapped;
	}


	private static Throwable unwrapping(Throwable throwable)
	{
		Set<Throwable> seen = new HashSet<>();
		while(isUnwrappable(throwable) && !seen.contains(throwable)) {
			seen.add(throwable);
			throwable = throwable.getCause();
		}
		return throwable;
	}


	private static boolean isUnwrappable(Throwable throwable)
	{
		return throwable.getCause() != null
				&& (throwable instanceof InvocationTargetException
						|| throwable instanceof UndeclaredThrowableException
						|| throwable instanceof UncheckedIOException
						|| throwable instanceof UncheckedException);
	}


	/**
	 * <p>
	 * Recursively find the wrapped throwable
	 * </p>
	 * 
	 * <b>NOTE</b>: this method now just delegates to {@link #unwrap(Throwable)} and is therefore superfluous,
	 * as such it will be removed in the next major release without impact to client code; given
	 * this, it is not marked {@link Deprecated} to avoid unnecessary upcasting to {@link Throwable}.
	 *
	 * @param throwable to be unwrapped
	 * @return the value of {@link UndeclaredThrowableException#getUndeclaredThrowable()}
	 * 
	 * @since 0.2.0
	 * 
	 * @see #unwrap(Throwable)
	 */
	public static Throwable unwrap(UndeclaredThrowableException throwable)
	{
		return unwrap((Throwable) throwable);
	}


	/**
	 * <p>
	 * Recursively find the wrapped throwable
	 * </p>
	 * 
	 * <b>NOTE</b>: this method now just delegates to {@link #unwrap(Throwable)} and is therefore superfluous,
	 * as such it will be removed in the next major release without impact to client code; given
	 * this, it is not marked {@link Deprecated} to avoid unnecessary upcasting to {@link Throwable}.
	 *
	 * @param throwable to be unwrapped
	 * @return the value of {@link InvocationTargetException#getTargetException()}
	 * 
	 * @since 0.2.0
	 */
	public static Throwable unwrap(InvocationTargetException throwable)
	{
		return unwrap((Throwable) throwable);
	}


	/**
	 * Invokes the {@link Iterable#forEach(Consumer)} method having unchecked the consumer
	 * 
	 * @param <T> the consumed {@link Iterable}'s element type
	 * 
	 * @param iterable the Iterable to consume each element of
	 * @param consumer the checked consumer of the Iterable's elements
	 * 
	 * @since 0.4.0
	 * 
	 * @see #uncheckConsumer(CheckedConsumer)
	 */
	public static <T> void forEach(Iterable<T> iterable, CheckedConsumer<T, ?> consumer)
	{
		iterable.forEach(uncheckConsumer(consumer));
	}


	/**
	 * Invokes the {@link Map#forEach(BiConsumer)} method having unchecked the bi-consumer
	 * 
	 * @param <K> the consumed {@link Map}'s key type
	 * @param <V> the consumed {@link Map}'s value type
	 * 
	 * @param map the entries to consume
	 * @param consumer the consumer of the map's key value pairs
	 * 
	 * @since 0.4.0
	 * 
	 * @see #uncheckBiConsumer(CheckedBiConsumer)
	 */
	public static <K, V> void forEach(Map<K, V> map, CheckedBiConsumer<K, V, ?> consumer)
	{
		map.forEach(uncheckBiConsumer(consumer));
	}
}