package com.annimon.stream; import com.annimon.stream.function.Consumer; import com.annimon.stream.function.Function; import com.annimon.stream.function.Supplier; import com.annimon.stream.function.ThrowableFunction; import com.annimon.stream.function.ThrowableSupplier; /** * A container for values which provided by {@code ThrowableSupplier}. * * Stores value which provided by {@link ThrowableSupplier} or an exception which were thrown. * * <pre><code> * Exceptional.of(new ThrowableSupplier<String, Throwable>() { * @Override * public String get() throws Throwable { * return IOUtils.read(inputStream); * } * }).ifExceptionIs(IOException.class, new Consumer<IOException>() { * @Override * public void accept(IOException exception) { * logger.log(Level.WARNING, "read file", exception); * } * }).getOrElse("default string"); * * Exceptional.of(() -> IOUtils.readBytes(inputStream)).getOrElse(new byte[0]); * </code></pre> * * @param <T> the type of the inner value */ public class Exceptional<T> { /** * Returns an {@code Exceptional} with value provided by given {@code ThrowableSupplier} function. * * @param <T> the type of value * @param supplier a supplier function * @return an {@code Exceptional} */ public static <T> Exceptional<T> of(ThrowableSupplier<T, Throwable> supplier) { try { return new Exceptional<T>(supplier.get(), null); } catch (Throwable throwable) { return new Exceptional<T>(null, throwable); } } /** * Returns an {@code Exceptional} with throwable already set. * * @param <T> the type of value * @param throwable throwable instance * @return an {@code Exceptional} */ public static <T> Exceptional<T> of(Throwable throwable) { return new Exceptional<T>(null, throwable); } private final T value; private final Throwable throwable; private Exceptional(T value, Throwable throwable) { this.value = value; this.throwable = throwable; } /** * Returns inner value. * * @return inner value. */ public T get() { return value; } /** * Returns inner value if there were no exceptions, otherwise returns {@code other}. * * @param other the value to be returned if there were any exception * @return inner value if there were no exceptions, otherwise {@code other} */ public T getOrElse(T other) { return throwable == null ? value : other; } /** * Wraps inner value with {@code Optional} container * * @return an {@code Optional} */ public Optional<T> getOptional() { return Optional.ofNullable(value); } /** * Returns exception. * * @return exception */ public Throwable getException() { return throwable; } /** * Returns inner value if there were no exceptions, otherwise throws an exception. * * @return inner value if there were no exceptions * @throws Throwable that was thrown in supplier function */ public T getOrThrow() throws Throwable { if (throwable != null) { throw throwable; } return value; } /** * Returns inner value if there were no exceptions, otherwise throws {@code RuntimeException}. * * @return inner value if there were no exceptions * @throws RuntimeException with wrapped exception which was thrown in supplier function */ public T getOrThrowRuntimeException() throws RuntimeException { if (throwable != null) { throw new RuntimeException(throwable); } return value; } /** * Returns inner value if there were no exceptions, otherwise throws the given {@code exception}. * * @param <E> the type of exception * @param exception an exception to be thrown * @return inner value if there were no exceptions * @throws E if there were exceptions in supplier function */ public <E extends Throwable> T getOrThrow(E exception) throws E { if (throwable != null) { exception.initCause(throwable); throw exception; } return value; } /** * Returns current {@code Exceptional} if there were no exceptions, otherwise * returns an {@code Exceptional} produced by supplier function. * * @param supplier supplier function that produced an {@code Exceptional} to be returned * @return this {@code Exceptional} if there were no exceptions, otherwise * an {@code Exceptional} produced by supplier function * @throws NullPointerException if {@code supplier} or its result is null */ public Exceptional<T> or(Supplier<Exceptional<T>> supplier) { if (throwable == null) return this; Objects.requireNonNull(supplier); return Objects.requireNonNull(supplier.get()); } /** * Invokes mapping function on inner value if there were no exceptions. * * @param <U> the type of result value * @param mapper mapping function * @return an {@code Exceptional} with transformed value if there were no exceptions * @throws NullPointerException if {@code mapper} is null */ public <U> Exceptional<U> map(ThrowableFunction<? super T, ? extends U, Throwable> mapper) { if (throwable != null) { return new Exceptional<U>(null, throwable); } Objects.requireNonNull(mapper); try { return new Exceptional<U>(mapper.apply(value), null); } catch (Throwable t) { return new Exceptional<U>(null, t); } } /** * Invokes consumer function with value if present. * * @param consumer a consumer function * @return this {@code Exceptional} * @since 1.1.2 */ public Exceptional<T> ifPresent(Consumer<? super T> consumer) { if (throwable == null) { consumer.accept(value); } return this; } /** * Invokes consumer function if there were any exception. * * @param consumer a consumer function * @return an {@code Exceptional} */ public Exceptional<T> ifException(Consumer<Throwable> consumer) { if (throwable != null) { consumer.accept(throwable); } return this; } /** * Invokes consumer function if exception class matches {@code throwableClass}. * * @param <E> the type of exception * @param throwableClass the class of an exception to be compared * @param consumer a consumer function * @return an {@code Exceptional} */ @SuppressWarnings("unchecked") public <E extends Throwable> Exceptional<T> ifExceptionIs(Class<E> throwableClass, Consumer<? super E> consumer) { if ( (throwable != null) && (throwableClass.isAssignableFrom(throwable.getClass())) ) { consumer.accept((E) throwable); } return this; } /** * Returns current {@code Exceptional} if there were no exceptions, otherwise * calls {@code function} and wraps produced result with an {@code Exceptional}. * * @param function recovering function * @return this {@code Exceptional} if there were no exceptions, otherwise * an {@code Exceptional} with wrapped recovering function result * @throws NullPointerException if {@code function} is null * @since 1.1.2 */ public Exceptional<T> recover(final ThrowableFunction<Throwable, ? extends T, Throwable> function) { if (throwable == null) return this; Objects.requireNonNull(function); try { return new Exceptional<T>(function.apply(throwable), null); } catch (Throwable throwable) { return new Exceptional<T>(null, throwable); } } /** * Returns current {@code Exceptional} if there were no exceptions, otherwise * returns an {@code Exceptional} produced by {@code function}. * * @param function recovering function * @return this {@code Exceptional} if there were no exceptions, otherwise * an {@code Exceptional} produced by recovering function * @throws NullPointerException if {@code function} or produced result is null * @since 1.1.2 */ public Exceptional<T> recoverWith(final Function<Throwable, ? extends Exceptional<T>> function) { if (throwable == null) return this; Objects.requireNonNull(function); return Objects.requireNonNull(function.apply(throwable)); } @Override public boolean equals(Object obj) { if (this == obj) { return true; } if (!(obj instanceof Exceptional)) { return false; } Exceptional<?> other = (Exceptional<?>) obj; return Objects.equals(value, other.value) && Objects.equals(throwable, other.throwable); } @Override public int hashCode() { return Objects.hash(value, throwable); } @Override public String toString() { return throwable == null ? String.format("Exceptional value %s", value) : String.format("Exceptional throwable %s", throwable); } }