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

net.diversionmc.error.Functionals Maven / Gradle / Ivy

There is a newer version: 1.29.2
Show newest version
package net.diversionmc.error;

import java.util.Map;
import java.util.Map.Entry;
import java.util.NoSuchElementException;
import java.util.Objects;
import java.util.Optional;
import java.util.function.*;
import java.util.stream.Collector;
import java.util.stream.Stream;

import static java.util.function.Function.identity;
import static java.util.stream.Collectors.toMap;
import static net.diversionmc.error.Result.tryGet;

/**
 * Utils for {@link Result}, {@link Success}, {@link Optional}, {@link Stream} types.
 */
@SuppressWarnings({"unused", "NewMethodNamingConvention"})
public class Functionals {
    public static void noop() {
    }

    //
    // Optional, Stream
    //

    /**
     * @return {@link Optional#of(Object)}
     */
    public static  Optional some(T t) {
        return Optional.of(t);
    }

    /**
     * @return {@link Optional#ofNullable(Object)}
     */
    public static  Optional optional(T t) {
        return Optional.ofNullable(t);
    }

    /**
     * @return {@link Stream#ofNullable(Object)}
     */
    @SafeVarargs
    public static  Stream stream(T... t) {
        return Stream.of(t)
            .filter(Objects::nonNull);
    }

    /**
     * @return {@link Optional#empty()}
     */
    public static  Optional none() {
        return Optional.empty();
    }

    /**
     * Convert boolean to optional.
     *
     * @param test   Boolean to convert.
     * @param ifTrue Object to wrap on true test value.
     * @param     Optional type.
     * @return Optional wrapping value if check has passed.
     */
    public static  Optional optional(boolean test, Supplier ifTrue) {
        return test ? some(ifTrue.get()) : none();
    }

    /**
     * Convert boolean to optional.
     *
     * @param test   Boolean to convert.
     * @param ifTrue Object to wrap on true test value.
     * @param     Optional type.
     * @return Optional wrapping value if check has passed.
     */
    public static  Optional optional(boolean test, T ifTrue) {
        return test ? some(ifTrue) : none();
    }

    public static  Function> optionalF(Function f) {
        return f.andThen(Functionals::optional);
    }

    public static  Function> streamF(Function> f) {
        return f.andThen(Optional::stream);
    }

    //
    // Reflective Casts
    //

    /**
     * Try to dynamically cast an object to a type.
     * Used for functional style castings in streams and such.
     *
     * @param obj Object to cast.
     * @param  Type to cast to.
     * @return Cast type or empty optional.
     */
    public static  Optional cast(Object obj, Class c) {
        return tryGet(
            ClassCastException.class,
            TryS.of(applyF2(Class::cast, c, obj)))
            .mapOk(Functionals::optional)
            .ok()
            .flatMap(f());
    }

    public static  Function> cast(Class c) {
        return applyF2F(Functionals::cast, c);
    }

    public static  Function> castStream(Class c) {
        return Functionals
            .cast(c)
            .andThen(Optional::stream);
    }

    //
    // Entry
    //

    public static  Predicate byKey(T key) {
        return byKey(
            key,
            f(),
            Objects::equals);
    }

    public static  Predicate byKey(K key, Function keyExtractor) {
        return byKey(
            key,
            keyExtractor,
            Objects::equals);
    }

    public static  Predicate byKey(K key, BiPredicate keyEquality) {
        return byKey(
            key,
            f(),
            keyEquality);
    }

    public static  Predicate byKey(K1 key,
                                                 Function keyExtractor,
                                                 BiPredicate keyEquality) {
        return repeatP(mapP(
            keyEquality,
            ignoreS(applyS(key)),
            keyExtractor));
    }

    public static  Predicate> byEntryKey(Predicate keyCheck) {
        return mapP(
            keyCheck,
            Entry::getKey);
    }

    public static  Predicate> byEntryValue(Predicate valueCheck) {
        return mapP(
            valueCheck,
            Entry::getValue);
    }

    public static  Predicate> byEntry(BiPredicate entryCheck) {
        return repeatP(mapP(
            entryCheck,
            Entry::getKey,
            Entry::getValue));
    }

    public static  Collector, ?, Map> toEntryMap() {
        return toMap(
            Entry::getKey,
            Entry::getValue);
    }

    public static > Collector, ?, M> toEntryMap(Supplier map) {
        return toMap(
            Entry::getKey,
            Entry::getValue,
            Functionals.ignoreF()::apply,
            map);
    }

    //
    // Lambda Conversion (keeping the parameter count)
    //

    // BiP -> BiP
    //   P -> P
    // P,P -> BiP

    public static  BiPredicate not(BiPredicate f) {
        return f.negate();
    }

    public static  Predicate not(Predicate f) {
        return f.negate();
    }

    public static  BiPredicate and(Predicate fa, Predicate fb) {
        return mapP(
            Boolean::logicalAnd,
            fa::test,
            fb::test);
    }

    public static  BiPredicate or(Predicate fa, Predicate fb) {
        return mapP(
            Boolean::logicalOr,
            fa::test,
            fb::test);
    }

    // Lambda quick cast

    public static  Function f() {
        return identity();
    }

    public static  Function f(Function f) {
        return f;
    }

    public static  BiFunction f2(BiFunction f) {
        return f;
    }

    public static Predicate p() {
        return Functionals.f()::apply;
    }

    public static  Predicate p(Predicate f) {
        return f;
    }

    public static  BiPredicate p2(BiPredicate f) {
        return f;
    }

    public static  Consumer c() {
        return ignoreR();
    }

    public static  Consumer c(Consumer f) {
        return f;
    }

    public static  BiConsumer c2(BiConsumer f) {
        return f;
    }

    public static  Supplier s(Supplier f) {
        return f;
    }

    public static Runnable r() {
        return Functionals::noop;
    }

    public static Runnable r(Runnable f) {
        return f;
    }

    // Other

    public static  T map(T t, Predicate check, Function ifTrue) {
        return map(t, check, ifTrue, f());
    }

    public static  T map(T t, Predicate check,
                            Function ifTrue,
                            Function ifFalse) {
        return (check.test(t) ? ifTrue : ifFalse).apply(t);
    }

    // Flip

    // BiF(A, B)->BiF(B, A)
    // BiP(A, B)->BiP(B, A)
    // BiC(A, B)->BiC(B, A)

    public static  BiFunction flipF(BiFunction f) {
        return (b, a) -> f.apply(a, b);
    }

    public static  BiPredicate flipP(BiPredicate f) {
        return flipF(f::test)::apply;
    }

    public static  BiConsumer flipC(BiConsumer f) {
        return (b, a) -> f.accept(a, b);
    }

    //
    // Partial Application (lower the parameter count by supplying them)
    //

    // Function

    // BiF(A, B)->R -> F(B)->R
    // BiF(A, B)->R -> F(A)->R
    // BiF(A, B)->R -> S()->R
    //      F(T)->R -> S()->R

    public static  Function applyF2(BiFunction f, A a) {
        return b -> f.apply(a, b);
    }

    public static  Function applyF2F(BiFunction f, B b) {
        return applyF2(flipF(f), b);
    }

    public static  Supplier applyF2(BiFunction f, A a, B b) {
        return () -> f.apply(a, b);
    }

    public static  Supplier applyF(Function f, T t) {
        return () -> f.apply(t);
    }

    //      BiF(S()->A, B)->R -> F(B)->R
    //      BiF(A, S()->B)->R -> F(A)->R
    // BiF(S()->A, S()->B)->R -> S()->R
    //           F(S()->T)->R -> S()->R

    public static  Function applyF2S(BiFunction f, Supplier a) {
        return b -> f.apply(a.get(), b);
    }

    public static  Function applyF2SF(BiFunction f, Supplier b) {
        return applyF2S(flipF(f), b);
    }

    public static  Supplier applyF2S(BiFunction f, Supplier a, Supplier b) {
        return () -> f.apply(a.get(), b.get());
    }

    public static  Supplier applyFS(Function f, Supplier t) {
        return () -> f.apply(t.get());
    }

    // Predicate

    // BiP(A, B) -> P(B)
    // BiP(A, B) -> P(A)
    // BiP(A, B) -> S()->bool
    //      P(T) -> S()->bool

    public static  Predicate applyP2(BiPredicate f, A a) {
        return applyF2(f::test, a)::apply;
    }

    public static  Predicate applyP2F(BiPredicate f, B b) {
        return applyF2F(f::test, b)::apply;
    }

    public static  BooleanSupplier applyP2(BiPredicate f, A a, B b) {
        return applyF2(f::test, a, b)::get;
    }

    public static  BooleanSupplier applyP(Predicate f, T t) {
        return applyF(f::test, t)::get;
    }

    //      BiP(S()->A, B) -> P(B)
    //      BiP(A, S()->B) -> P(A)
    // BiP(S()->A, S()->B) -> S()->bool
    //           P(S()->T) -> S()->bool

    public static  Predicate applyP2S(BiPredicate f, Supplier a) {
        return applyF2S(f::test, a)::apply;
    }

    public static  Predicate applyP2SF(BiPredicate f, Supplier b) {
        return applyF2SF(f::test, b)::apply;
    }

    public static  BooleanSupplier applyP2S(BiPredicate f, Supplier a, Supplier b) {
        return applyF2S(f::test, a, b)::get;
    }

    public static  BooleanSupplier applyPS(Predicate f, Supplier t) {
        return applyFS(f::test, t)::get;
    }

    // Consumer

    // BiC(A, B) -> C(A)
    // BiC(A, B) -> C(B)
    // BiC(A, B) -> R()
    //      C(T) -> R()

    public static  Consumer applyC2(BiConsumer f, A a) {
        return b -> f.accept(a, b);
    }

    public static  Consumer applyC2F(BiConsumer f, B b) {
        return applyC2(flipC(f), b);
    }

    public static  Runnable applyC2(BiConsumer f, A a, B b) {
        return () -> f.accept(a, b);
    }

    public static  Runnable applyC(Consumer f, T t) {
        return () -> f.accept(t);
    }

    //      BiC(S()->A, B) -> C(A)
    //      BiC(A, S()->B) -> C(B)
    // BiC(S()->A, S()->B) -> R()
    //           C(S()->T) -> R()

    public static  Consumer applyC2S(BiConsumer f, Supplier a) {
        return b -> f.accept(a.get(), b);
    }

    public static  Consumer applyC2SF(BiConsumer f, Supplier b) {
        return applyC2S(flipC(f), b);
    }

    public static  Runnable applyC2S(BiConsumer f, Supplier a, Supplier b) {
        return () -> f.accept(a.get(), b.get());
    }

    public static  Runnable applyCS(Consumer f, Supplier t) {
        return () -> f.accept(t.get());
    }

    // Supplier

    // T -> S()->T

    public static  Supplier applyS(T t) {
        return () -> t;
    }

    //
    // Parameter Ignorance (raise the parameter count by ignoring them)
    //

    // Function

    // F(A)->R -> BiF(A, B)->R

    public static  BiFunction ignoreF(Function f) {
        return (a, b) -> f.apply(a);
    }

    public static  BiFunction ignoreF() {
        return ignoreF(f());
    }

    // Predicate

    // P(A) -> BiP(A, B)

    public static  BiPredicate ignoreP(Predicate f) {
        return ignoreF(f::test)::apply;
    }

    // Consumer

    // C(A) -> BiC(A, B)

    public static  BiConsumer ignoreC(Consumer f) {
        return (a, b) -> f.accept(a);
    }

    public static  BiConsumer ignoreC() {
        return ignoreC(c());
    }

    // Supplier

    // S()->R -> BiF(A, B)->R
    // S()->R -> F(T)->R

    public static  BiFunction ignoreS2(Supplier f) {
        return (a, b) -> f.get();
    }

    public static  Function ignoreS(Supplier f) {
        return t -> f.get();
    }

    // Runnable

    // R() -> BiC(A, B)
    // R() -> C(T)

    public static  BiConsumer ignoreR2(Runnable f) {
        return (a, b) -> f.run();
    }

    public static  BiConsumer ignoreR2() {
        return ignoreR2(r());
    }

    public static  Consumer ignoreR(Runnable f) {
        return t -> f.run();
    }

    public static  Consumer ignoreR() {
        return ignoreR(r());
    }

    //
    // Parameter Repeating (lower the parameter count by repeating them)
    //

    // Function

    public static  Function repeatF(BiFunction f) {
        return t -> f.apply(t, t);
    }

    // Predicate

    public static  Predicate repeatP(BiPredicate f) {
        return repeatF(f::test)::apply;
    }

    // Consumer

    public static  Consumer repeatC(BiConsumer f) {
        return t -> f.accept(t, t);
    }

    //
    // Mapping (keeping the parameter count, but converting input types)
    //

    // Function

    public static  BiFunction mapF(BiFunction f,
                                                           Function na,
                                                           Function mb) {
        return (n, m) -> f.apply(na.apply(n), mb.apply(m));
    }

    public static  Function mapF(Function f,
                                                Function na) {
        return f.compose(na);
    }

    // Predicate

    public static  BiPredicate mapP(BiPredicate f,
                                                      Function na,
                                                      Function mb) {
        return mapF(f::test, na, mb)::apply;
    }

    public static  Predicate mapP(Predicate f,
                                           Function na) {
        return mapF(f::test, na)::apply;
    }

    // Consumer

    public static  BiConsumer mapC(BiConsumer f,
                                                     Function na,
                                                     Function mb) {
        return (n, m) -> f.accept(na.apply(n), mb.apply(m));
    }

    public static  Consumer mapC(Consumer f,
                                          Function na) {
        return n -> f.accept(na.apply(n));
    }

    // Supplier

    public static  Supplier mapS(Supplier f,
                                          Function na) {
        return applyFS(na, f);
    }

    //
    // Peeking (keeping the parameter count, but performing a side effect action)
    //

    // Function

    public static  BiFunction peekF2(BiConsumer action, BiFunction f) {
        return (a, b) -> {
            action.accept(a, b);
            return f.apply(a, b);
        };
    }

    public static  Function peekF(Consumer action, Function f) {
        return t -> {
            action.accept(t);
            return f.apply(t);
        };
    }

    public static  BiFunction peekF2O(Consumer action, BiFunction f) {
        return (a, b) -> {
            var r = f.apply(a, b);
            action.accept(r);
            return r;
        };
    }

    public static  Function peekFO(Consumer action, Function f) {
        return t -> {
            var r = f.apply(t);
            action.accept(r);
            return r;
        };
    }

    // Predicate

    public static  BiPredicate peekP2(BiConsumer action, BiPredicate f) {
        return peekF2(action, f::test)::apply;
    }

    public static  Predicate peekP(Consumer action, Predicate f) {
        return peekF(action, f::test)::apply;
    }

    // Consumer

    public static  BiConsumer peekC2(BiConsumer action, BiConsumer f) {
        return action.andThen(f);
    }

    public static  Consumer peekC(Consumer action, Consumer f) {
        return action.andThen(f);
    }

    public static  BiConsumer peekC2O(BiConsumer action, BiConsumer f) {
        return f.andThen(action);
    }

    public static  Consumer peekCO(Consumer action, Consumer f) {
        return f.andThen(action);
    }

    // Supplier

    public static  Supplier peekSO(Consumer action, Supplier f) {
        return () -> {
            var t = f.get();
            action.accept(t);
            return t;
        };
    }

    // Runnable

    public static Runnable peekR(Runnable action, Runnable f) {
        return () -> {
            action.run();
            f.run();
        };
    }

    public static Runnable peekRO(Runnable action, Runnable f) {
        return () -> {
            f.run();
            action.run();
        };
    }

    //
    // Higher Order Partial Application
    //

    // Function

    public static  Function, R> flatApplyF(T t) {
        return applyF2F(Function::apply, t);
    }

    // Predicate

    public static  Predicate> flatApplyP(T t) {
        return applyP2F(Predicate::test, t);
    }

    // Consumer

    public static  Consumer> flatApplyC(T t) {
        return applyC2F(Consumer::accept, t);
    }

    // Supplier

    public static  Supplier> flatApplyS(T t) {
        return applyS(applyS(t));
    }

    //
    // Propagation
    //

    public static boolean isError(boolean res) {
        return !res;
    }

    public static boolean isError(Optional res) {
        return res.isEmpty();
    }

    public static boolean isError(Success res) {
        return res.isError();
    }

    public static boolean isError(Result res) {
        return res.isError();
    }

    public static boolean propagate(boolean res) {
        return false; // default propagation assumed false
    }

    public static  Optional propagate(Optional opt) {
        return none(); // default propagation assumed none()
    }

    public static  Success propagate(Success res) {
        return Success.error(res.error().get());
    }

    public static  Result propagate(Result res) {
        return Result.error(res.error().get());
    }

    public static  E2 propagate(boolean res, Supplier orReturn) {
        return orReturn.get();
    }

    public static  E2 propagate(boolean res, E2 orReturn) {
        return orReturn;
    }

    public static  E2 propagate(Optional res, Supplier orReturn) {
        return orReturn.get();
    }

    public static  E2 propagate(Optional res, E2 orReturn) {
        return orReturn;
    }

    public static  E2 propagate(Success res, Function orReturn) {
        return orReturn.apply(res.error().get());
    }

    public static  E2 propagate(Success res, Supplier orReturn) {
        return orReturn.get();
    }

    public static  E2 propagate(Success res, E2 orReturn) {
        return orReturn;
    }

    public static  E2 propagate(Result res, Function orReturn) {
        return orReturn.apply(res.error().get());
    }

    public static  E2 propagate(Result res, Supplier orReturn) {
        return orReturn.get();
    }

    public static  E2 propagate(Result res, E2 orReturn) {
        return orReturn;
    }

    public static  boolean Try(boolean res, Supplier orReturn) {
        if (isError(res)) propagate(false, orReturn);
        if (!res) throw new NoSuchElementException("Cannot unwrap error");
        return true;
    }

    public static  boolean Try(boolean res, T orReturn) {
        if (isError(res)) propagate(false, orReturn);
        if (!res) throw new NoSuchElementException("Cannot unwrap error");
        return true;
    }

    public static  boolean Try(boolean res) {
        if (isError(res)) propagate(false);
        if (!res) throw new NoSuchElementException("Cannot unwrap error");
        return true;
    }

    public static  S Try(Optional res, Supplier orReturn) {
        if (isError(res)) propagate(res, orReturn);
        return res.get();
    }

    public static  S Try(Optional res, T orReturn) {
        if (isError(res)) propagate(res, orReturn);
        return res.get();
    }

    public static  S Try(Optional res) {
        if (isError(res)) propagate(res);
        return res.get();
    }

    public static  void Try(Success res, Function orReturn) {
        if (isError(res)) propagate(res, orReturn);
        res.get();
    }

    public static  void Try(Success res, Supplier orReturn) {
        if (isError(res)) propagate(res, orReturn);
        res.get();
    }

    public static  void Try(Success res, T orReturn) {
        if (isError(res)) propagate(res, orReturn);
        res.get();
    }

    public static  void Try(Success res) {
        if (isError(res)) propagate(res);
        res.get();
    }

    public static  S Try(Result res, Function orReturn) {
        if (isError(res)) propagate(res, orReturn);
        return res.get();
    }

    public static  S Try(Result res, Supplier orReturn) {
        if (isError(res)) propagate(res, orReturn);
        return res.get();
    }

    public static  S Try(Result res, T orReturn) {
        if (isError(res)) propagate(res, orReturn);
        return res.get();
    }

    public static  S Try(Result res) {
        if (isError(res)) propagate(res);
        return res.get();
    }
}