package com.github.jeysal.java.util.function;

import lombok.experimental.UtilityClass;

import java.util.Arrays;
import java.util.Optional;
import java.util.function.Function;
import java.util.function.Supplier;

/**
 * @author Tim Seckinger
 * @since 12.03.2016
 */
@UtilityClass
public class SupplierUtils {

    /**
     * Returns a {@link Supplier} that supplies the first present {@link Optional} supplied by given suppliers in order,
     * or an empty {@link Optional} if none of the suppliers return a present {@link Optional}.
     *
     * @param suppliers The suppliers to call in order
     * @param <T>       The type of the {@link Optional}
     * @return The {@link Supplier} that supplies the first present {@link Optional}
     */
    @SafeVarargs
    public static <T> Supplier<Optional<T>> firstPresent(Supplier<Optional<T>>... suppliers) {
        return () -> Arrays.stream(suppliers)
                .map(Supplier::get)
                .filter(Optional::isPresent)
                .findFirst().orElse(Optional.empty());
    }

    /**
     * Returns a {@link Supplier} that supplies the content of the first present {@link Optional} supplied by given
     * suppliers in order, or the object supplied by given fallback supplier if none of the suppliers return a present
     * {@link Optional}.
     *
     * @param fallback  The fallback supplier
     * @param suppliers The suppliers to call in order
     * @param <T>       The type of the {@link Optional} content or fallback object
     * @return The {@link Supplier} that supplies the first present {@link Optional}
     */
    @SafeVarargs
    public static <T> Supplier<T> firstPresent(Supplier<T> fallback, Supplier<Optional<T>>... suppliers) {
        return () -> Arrays.stream(suppliers)
                .map(Supplier::get)
                .filter(Optional::isPresent)
                .findFirst().orElseGet(() ->
                        Optional.of(fallback.get())
                ).get();
    }

    /**
     * Returns a {@link Supplier} that delegates calls to given throwingSupplier and returns an {@link Optional} of the
     * result or an empty {@link Optional} if given throwingSupplier returned null or threw an {@link Exception}.
     *
     * @param throwingSupplier The {@link Supplier} to delegate to
     * @param <R>              The return type of the {@link Supplier}
     * @return The {@link Supplier} that delegates calls to throwingSupplier
     */
    public static <R> Supplier<Optional<R>> trying(ThrowingSupplier<R, ?> throwingSupplier) {
        return () -> {
            try {
                return Optional.ofNullable(throwingSupplier.get());
            } catch (Exception e) {
                return Optional.empty();
            }
        };
    }

    public static <R> Supplier<R> rethrowing(ThrowingSupplier<R, ?> throwingSupplier) {
        return rethrowing(throwingSupplier, RuntimeException::new);
    }

    public static <R> Supplier<R> rethrowing(
            ThrowingSupplier<R, ?> throwingSupplier,
            Function<Exception, ? extends RuntimeException> exceptionMapper) {
        return () -> {
            try {
                return throwingSupplier.get();
            } catch (Exception e) {
                throw exceptionMapper.apply(e);
            }
        };
    }

}

@@ -2768,166 +2768,1507 @@
     
-public static %3CR%3E Supplier%3CR%3E rethrowing(ThrowingSupplier%3CR, ?%3E throwingSupplier) %7B%0A        return rethrowing(throwingSupplier, RuntimeException::new);%0A    %7D%0A
+/**%0A     * Returns a %7B@link Supplier%7D that delegates calls to given throwingSupplier and throws a new%0A     * %7B@link RuntimeException%7D if given throwingSupplier threw an %7B@link Exception%7D.%3Cbr%3E%0A     * The %7B@link RuntimeException%7D has the original %7B@link Exception%7D as its %7B@link Throwable#getCause() cause%7D.%0A     *%0A     * @param throwingSupplier The %7B@link Supplier%7D to delegate to%0A     * @param %3CR%3E              The return type of the %7B@link Supplier%7D%0A     * @return The %7B@link Supplier%7D that delegates calls to throwingSupplier%0A     */%0A    public static %3CR%3E Supplier%3CR%3E rethrowing(ThrowingSupplier%3CR, ?%3E throwingSupplier) %7B%0A        return rethrowing(throwingSupplier, RuntimeException::new);%0A    %7D%0A%0A    /**%0A     * Returns a %7B@link Supplier%7D that delegates calls to given throwingSupplier and throws a %7B@link RuntimeException%7D%0A     * created by given exceptionMapper if given throwingSupplier threw an %7B@link Exception%7D.%3Cbr%3E%0A     * The original %7B@link Exception%7D is passed as the argument to given exceptionMapper.%0A     * %3Cp%3E%0A     * %3Cb%3EExample:%3C/b%3E%3Cbr%3E%0A     * %7B@code rethrowing(() -%3E %7B throw new Exception(%22example%22); %7D, IllegalArgumentException::new)%7D%0A     * %3C/p%3E%0A     *%0A     * @param throwingSupplier The %7B@link Supplier%7D to delegate to%0A     * @param exceptionMapper  The original %7B@link Exception%7D to rethrown %7B@link RuntimeException%7D mapper%0A     * @param %3CR%3E              The return type of the %7B@link Supplier%7D%0A     * @return The %7B@link Supplier%7D that delegates calls to throwingSupplier%0A     */
 %0A   

add SupplierUtils.rethrowing doc