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

org.jooby.funzy.Try Maven / Gradle / Ivy

There is a newer version: 1.0.0
Show newest version
package org.jooby.funzy;

import java.util.Objects;
import java.util.Optional;
import java.util.concurrent.Callable;
import java.util.function.Consumer;
import java.util.function.Supplier;

/**
 * Functional try and try-with-resources implementation.
 */
public abstract class Try {

  /** Try with a value. */
  public static abstract class Value extends Try {

    /**
     * Gets the success result or {@link Throwing#sneakyThrow(Throwable)} the exception.
     *
     * @return The success result or {@link Throwing#sneakyThrow(Throwable)} the exception.
     */
    public abstract V get();

    /**
     * Get the success value or use the given function on failure.
     *
     * @param value Default value provider.
     * @return Success or default value.
     */
    public V orElseGet(Supplier value) {
      return isSuccess() ? get() : value.get();
    }

    /**
     * Get the success value or use the given default value on failure.
     *
     * @param value Default value.
     * @return Success or default value.
     */
    public V orElse(V value) {
      return isSuccess() ? get() : value;
    }

    /**
     * Get the success value or throw an exception created by the exception provider.
     *
     * @param provider Exception provider.
     * @return Success value.
     */
    public V orElseThrow(Throwing.Function provider) {
      if (isSuccess()) {
        return get();
      }
      throw Throwing.sneakyThrow(provider.apply(getCause().get()));
    }

    /**
     * Always run the given action, works like a finally clause.
     *
     * @param action Finally action.
     * @return This try result.
     */
    @Override public Value onComplete(Throwing.Runnable action) {
      return (Value) super.onComplete(action);
    }

    @Override public Value onComplete(final Throwing.Consumer action) {
      return (Value) super.onComplete(action);
    }

    /**
     * Always run the given action, works like a finally clause. Exception and value might be null.
     * Exception will be null in case of success.
     *
     * @param action Finally action.
     * @return This try result.
     */
    public Value onComplete(final Throwing.Consumer2 action) {
      try {
        V value = isSuccess() ? get() : null;
        action.accept(value, getCause().orElse(null));
        return this;
      } catch (Throwable x) {
        return (Value) failure(x);
      }
    }

    /**
     * Run the given action if and only if this is a failure.
     *
     * @param action Failure action/listener.
     * @return This try.
     */
    @Override public Value onFailure(final Consumer action) {
      super.onFailure(action);
      return this;
    }

    /**
     * Run the given action if and only if this is a success.
     *
     * @param action Success listener.
     * @return This try.
     */
    @Override public Value onSuccess(final Runnable action) {
      super.onSuccess(action);
      return this;
    }

    /**
     * Run the given action if and only if this is a success.
     *
     * @param action Success listener.
     * @return This try.
     */
    public Value onSuccess(final Consumer action) {
      if (isSuccess()) {
        action.accept(get());
      }
      return this;
    }

    /**
     * Recover from failure. The recover function will be executed in case of failure.
     *
     * @param fn Recover function.
     * @return This try on success, a new success try from recover or a failure try in case of exception.
     */
    public Value recoverWith(Throwing.Function> fn) {
      return recoverWith(Throwable.class, fn);
    }

    /**
     * Recover from failure if and only if the exception is a subclass of the given exception filter.
     * The recover function will be executed in case of failure.
     *
     * @param exception Exception filter.
     * @param fn Recover function.
     * @param  Exception type.
     * @return This try on success, a new success try from recover or a failure try in case of exception.
     */
    public  Value recoverWith(Class exception,
      Throwing.Function> fn) {
      return (Value) getCause()
        .filter(exception::isInstance)
        .map(x -> {
          try {
            return fn.apply((X) x);
          } catch (Throwable ex) {
            return failure(ex);
          }
        })
        .orElse(this);
    }

    /**
     * Recover from failure. The recover function will be executed in case of failure.
     *
     * @param fn Recover function.
     * @return This try on success, a new success try from recover or a failure try in case of exception.
     */
    public Value recover(Throwing.Function fn) {
      return recover(Throwable.class, fn);
    }

    /**
     * Recover from failure if and only if the exception is a subclass of the given exception filter.
     * The recover function will be executed in case of failure.
     *
     * @param exception Exception filter.
     * @param value Recover value.
     * @param  Exception type.
     * @return This try on success, a new success try from recover or a failure try in case of exception.
     */
    public  Value recover(Class exception, V value) {
      return recoverWith(exception, x -> Try.success(value));
    }

    /**
     * Recover from failure if and only if the exception is a subclass of the given exception filter.
     * The recover function will be executed in case of failure.
     *
     * @param exception Exception filter.
     * @param fn Recover function.
     * @param  Exception type.
     * @return This try on success, a new success try from recover or a failure try in case of exception.
     */
    public  Value recover(Class exception, Throwing.Function fn) {
      return recoverWith(exception, x -> Try.apply(() -> fn.apply(x)));
    }

    /**
     * Flat map the success value.
     *
     * @param mapper Mapper.
     * @param  New type.
     * @return A new try value for success or failure.
     */
    public  Value flatMap(Throwing.Function> mapper) {
      if (isFailure()) {
        return (Value) this;
      }
      try {
        return mapper.apply(get());
      } catch (Throwable x) {
        return new Failure<>(x);
      }
    }

    /**
     * Map the success value.
     *
     * @param mapper Mapper.
     * @param  New type.
     * @return A new try value for success or failure.
     */
    public  Value map(Throwing.Function mapper) {
      return flatMap(v -> new Success<>(mapper.apply(v)));
    }

    /**
     * Get an empty optional in case of failure.
     *
     * @return An empty optional in case of failure.
     */
    public Optional toOptional() {
      return isFailure() ? Optional.empty() : Optional.ofNullable(get());
    }

    @Override public  Value unwrap(Class type) {
      return (Value) super.unwrap(type);
    }

    @Override public Value unwrap(final Throwing.Predicate predicate) {
      return (Value) super.unwrap(predicate);
    }

    @Override public Value wrap(final Throwing.Function wrapper) {
      return (Value) super.wrap(wrapper);
    }

    @Override public  Value wrap(final Class predicate,
      final Throwing.Function wrapper) {
      return (Value) super.wrap(predicate, wrapper);
    }

    @Override public  Value wrap(final Throwing.Predicate predicate,
      final Throwing.Function wrapper) {
      return (Value) super.wrap(predicate, wrapper);
    }
  }

  private static class Success extends Value {
    private final V value;

    public Success(V value) {
      this.value = value;
    }

    @Override public V get() {
      return value;
    }

    @Override public Optional getCause() {
      return Optional.empty();
    }
  }

  private static class Failure extends Value {
    private final Throwable x;

    public Failure(Throwable x) {
      this.x = x;
    }

    @Override public V get() {
      throw Throwing.sneakyThrow(x);
    }

    @Override public Optional getCause() {
      return Optional.of(x);
    }
  }

  /**
   * Try with resource implementation.
   *
   * @param  Resource type.
   */
  public static class ResourceHandler {

    private static class ProxyCloseable

implements AutoCloseable, Throwing.Supplier { private final Throwing.Supplier

parent; private final Throwing.Function mapper; private P parentResource; private R resource; public ProxyCloseable(Throwing.Supplier

parent, Throwing.Function mapper) { this.parent = parent; this.mapper = mapper; } @Override public void close() throws Exception { try { Optional.ofNullable(resource) .ifPresent(Throwing.throwingConsumer(AutoCloseable::close)); } finally { if (parent instanceof ProxyCloseable) { ((ProxyCloseable) parent).close(); } else { Optional.ofNullable(parentResource) .ifPresent(Throwing.throwingConsumer(AutoCloseable::close)); } } } @Override public R tryGet() throws Throwable { if (parent instanceof ProxyCloseable) { ProxyCloseable proxy = (ProxyCloseable) parent; if (proxy.resource == null) { proxy.get(); } this.parentResource = (P) proxy.resource; } else { this.parentResource = parent.get(); } this.resource = mapper.apply(parentResource); return (R) this; } } private final Throwing.Supplier r; private ResourceHandler(Throwing.Supplier r1) { this.r = r1; } /** * Map the resource to a new closeable resource. * * @param fn Mapper. * @param New resource type. * @return A new resource handler. */ public ResourceHandler map(Throwing.Function fn) { return new ResourceHandler<>(new ProxyCloseable<>(this.r, fn)); } /** * Apply the resource and produces an output. * * @param fn Function to apply. * @param Output type. * @return A new try result. */ public Value apply(Throwing.Function fn) { return Try.apply(() -> { try (R r1 = this.r.get()) { if (r1 instanceof ProxyCloseable) { return fn.apply((R) ((ProxyCloseable) r1).resource); } return fn.apply(r1); } }); } /** * Run an operation over the resource. * * @param fn Function to apply. * @return A new try result. */ public Try run(Throwing.Consumer fn) { return Try.run(() -> { try (R r1 = this.r.get()) { fn.accept(r1); } }); } } /** * Try with resource implementation. * * @param Resource type. * @param Resource type. */ public static class ResourceHandler2 { private final Throwing.Supplier r1; private final Throwing.Supplier r2; private ResourceHandler2(Throwing.Supplier r1, Throwing.Supplier r2) { this.r1 = r1; this.r2 = r2; } public Value apply(Throwing.Function2 fn) { return Try.apply(() -> { try (R1 r1 = this.r1.get(); R2 r2 = this.r2.get()) { return fn.apply(r1, r2); } }); } public Try run(Throwing.Consumer2 fn) { return Try.run(() -> { try (R1 r1 = this.r1.get(); R2 r2 = this.r2.get()) { fn.accept(r1, r2); } }); } } public static class ResourceHandler3 { private final Throwing.Supplier r1; private final Throwing.Supplier r2; private final Throwing.Supplier r3; private ResourceHandler3(Throwing.Supplier r1, Throwing.Supplier r2, Throwing.Supplier r3) { this.r1 = r1; this.r2 = r2; this.r3 = r3; } public Value apply(Throwing.Function3 fn) { return Try.apply(() -> { try (R1 r1 = this.r1.get(); R2 r2 = this.r2.get(); R3 r3 = this.r3.get()) { return fn.apply(r1, r2, r3); } }); } public Try run(Throwing.Consumer3 fn) { return Try.run(() -> { try (R1 r1 = this.r1.get(); R2 r2 = this.r2.get(); R3 r3 = this.r3.get()) { fn.accept(r1, r2, r3); } }); } } public static class ResourceHandler4 { private final Throwing.Supplier r1; private final Throwing.Supplier r2; private final Throwing.Supplier r3; private final Throwing.Supplier r4; private ResourceHandler4(Throwing.Supplier r1, Throwing.Supplier r2, Throwing.Supplier r3, Throwing.Supplier r4) { this.r1 = r1; this.r2 = r2; this.r3 = r3; this.r4 = r4; } public Value apply(Throwing.Function4 fn) { return Try.apply(() -> { try (R1 r1 = this.r1.get(); R2 r2 = this.r2.get(); R3 r3 = this.r3.get(); R4 r4 = this.r4.get()) { return fn.apply(r1, r2, r3, r4); } }); } public Try run(Throwing.Consumer4 fn) { return Try.run(() -> { try (R1 r1 = this.r1.get(); R2 r2 = this.r2.get(); R3 r3 = this.r3.get(); R4 r4 = this.r4.get()) { fn.accept(r1, r2, r3, r4); } }); } } /** * Functional try-with-resources: * *

{@code
   *  InputStream in = ...;
   *
   *  byte[] content = Try.of(in)
   *    .apply(in -> read(in))
   *    .get();
   *
   * }
* * Jdbc example: * *
{@code
   *  Connection connection = ...;
   *
   *  Try.of(connection)
   *     .map(c -> c.preparedStatement("..."))
   *     .map(stt -> stt.executeQuery())
   *     .apply(rs-> {
   *       return res.getString("column");
   *     })
   *     .get();
   *
   * }
* * @param r1 Input resource. * @param Resource type. * @return A resource handler. */ public final static ResourceHandler of(R r1) { return with(() -> r1); } /** * Functional try-with-resources: * *
{@code
   *  InputStream in = ...;
   *  OutputStream out = ...;
   *
   *  Try.of(in, out)
   *    .run((from, to) -> copy(from, to))
   *    .onFailure(Throwable::printStacktrace);
   *
   * }
* * @param r1 Input resource. * @param r2 Input resource. * @param Resource type. * @param Resource type. * @return A resource handler. */ public final static ResourceHandler2 of( R1 r1, R2 r2) { return with(() -> r1, () -> r2); } /** * Functional try-with-resources with 3 inputs. * * @param r1 Input resource. * @param r2 Input resource. * @param r3 Input resource. * @param Resource type. * @param Resource type. * @param Resource type. * @return A resource handler. */ public final static ResourceHandler3 of( R1 r1, R2 r2, R3 r3) { return with(() -> r1, () -> r2, () -> r3); } /** * Functional try-with-resources with 4 inputs. * * @param r1 Input resource. * @param r2 Input resource. * @param r3 Input resource. * @param r4 Input resource. * @param Resource type. * @param Resource type. * @param Resource type. * @param Resource type. * @return A resource handler. */ public final static ResourceHandler4 of( R1 r1, R2 r2, R3 r3, R4 r4) { return with(() -> r1, () -> r2, () -> r3, () -> r4); } /** * Functional try-with-resources: * *
{@code
   *  byte[] content = Try.with(() -> newInputStream())
   *    .apply(in -> read(in))
   *    .get();
   *
   * }
* * Jdbc example: * *
{@code
   *  Try.with(() -> newConnection())
   *     .map(c -> c.preparedStatement("..."))
   *     .map(stt -> stt.executeQuery())
   *     .apply(rs-> {
   *       return res.getString("column");
   *     })
   *     .get();
   *
   * }
* * @param r1 Input resource. * @param Resource type. * @return A resource handler. */ public final static ResourceHandler with(Throwing.Supplier r1) { return new ResourceHandler<>(r1); } /** * Functional try-with-resources: * *
{@code
   *  Try.with(() -> newIn(), () -> newOut())
   *    .run((from, to) -> copy(from, to))
   *    .onFailure(Throwable::printStacktrace);
   * }
* * @param r1 Input resource. * @param r2 Input resource. * @param Resource type. * @param Resource type. * @return A resource handler. */ public final static ResourceHandler2 with( Throwing.Supplier r1, Throwing.Supplier r2) { return new ResourceHandler2<>(r1, r2); } /** * Functional try-with-resources with 3 inputs. * * @param r1 Input resource. * @param r2 Input resource. * @param r3 Input resource. * @param Resource type. * @param Resource type. * @param Resource type. * @return A resource handler. */ public final static ResourceHandler3 with( Throwing.Supplier r1, Throwing.Supplier r2, Throwing.Supplier r3) { return new ResourceHandler3<>(r1, r2, r3); } /** * Functional try-with-resources with 4 inputs. * * @param r1 Input resource. * @param r2 Input resource. * @param r3 Input resource. * @param r4 Input resource. * @param Resource type. * @param Resource type. * @param Resource type. * @param Resource type. * @return A resource handler. */ public final static ResourceHandler4 with( Throwing.Supplier r1, Throwing.Supplier r2, Throwing.Supplier r3, Throwing.Supplier r4) { return new ResourceHandler4<>(r1, r2, r3, r4); } /** * Get a new success value. * * @param value Value. * @param Value type. * @return A new success value. */ public final static Value success(V value) { return new Success<>(value); } /** * Get a new failure value. * * @param x Exception. * @return A new failure value. */ public final static Value failure(Throwable x) { return new Failure<>(x); } /** * Creates a new try from given value provider. * * @param fn Value provider. * @param Value type. * @return A new success try or failure try in case of exception. */ public static Value apply(Throwing.Supplier fn) { try { return new Success<>(fn.get()); } catch (Throwable x) { return new Failure(x); } } /** * Creates a new try from given callable. * * @param fn Callable. * @param Value type. * @return A new success try or failure try in case of exception. */ public static Value call(Callable fn) { return apply(fn::call); } /** * Creates a side effect try from given runnable. Don't forget to either throw or log the exception * in case of failure. Unless, of course you don't care about the exception. * * Log the exception: *
{@code
   *   Try.run(() -> ...)
   *     .onFailure(x -> x.printStacktrace());
   * }
* * Throw the exception: *
{@code
   *   Try.run(() -> ...)
   *     .throwException();
   * }
* * @param runnable Runnable. * @return A void try. */ public static Try run(Throwing.Runnable runnable) { try { runnable.run(); return new Success<>(null); } catch (Throwable x) { return new Failure(x); } } /** * True in case of failure. * * @return True in case of failure. */ public boolean isFailure() { return getCause().isPresent(); } /** * True in case of success. * * @return True in case of success. */ public boolean isSuccess() { return !isFailure(); } /** * Run the given action if and only if this is a failure. * * @param action Failure listener. * @return This try. */ public Try onFailure(Consumer action) { getCause().ifPresent(action); return this; } /** * Run the given action if and only if this is a success. * * @param action Success listener. * @return This try. */ public Try onSuccess(Runnable action) { if (isSuccess()) { action.run(); } return this; } /** * In case of failure unwrap the exception provided by calling {@link Throwable#getCause()}. * Useful for clean/shorter stackstrace. * * Example for {@link java.lang.reflect.InvocationTargetException}: * *
{@code
   * Try.run(() -> {
   *   Method m = ...;
   *   m.invoke(...); //might throw InvocationTargetException
   * }).unwrap(InvocationTargetException.class)
   *   .throwException();
   * }
* * @param type Exception filter. * @param Exception type. * @return This try for success or a new failure with exception unwrap. */ public Try unwrap(Class type) { return unwrap(type::isInstance); } /** * In case of failure unwrap the exception provided by calling {@link Throwable#getCause()}. * Useful for clean/shorter stackstrace. * * Example for {@link java.lang.reflect.InvocationTargetException}: * *
{@code
   * Try.run(() -> {
   *   Method m = ...;
   *   m.invoke(...); //might throw InvocationTargetException
   * }).unwrap(InvocationTargetException.class::isInstance)
   *   .throwException();
   * }
* * @param predicate Exception filter. * @return This try for success or a new failure with exception unwrap. */ public Try unwrap(Throwing.Predicate predicate) { try { return getCause() .filter(predicate) .map(Throwable::getCause) .filter(Objects::nonNull) .map(x -> (Try) Try.failure(x)) .orElse(this); } catch (Throwable x) { return failure(x); } } /** * In case of failure wrap an exception matching the given predicate to something else. * * @param wrapper Exception mapper. * @return This try for success or a new failure with exception wrapped. */ public Try wrap(Throwing.Function wrapper) { return wrap(Throwable.class, wrapper); } /** * In case of failure wrap an exception matching the given predicate to something else. * * @param predicate Exception predicate. * @param wrapper Exception mapper. * @param Exception type. * @return This try for success or a new failure with exception wrapped. */ public Try wrap(Class predicate, Throwing.Function wrapper) { return wrap(predicate::isInstance, wrapper); } /** * In case of failure wrap an exception matching the given predicate to something else. * * @param predicate Exception predicate. * @param wrapper Exception mapper. * @param Exception type. * @return This try for success or a new failure with exception wrapped. */ public Try wrap(Throwing.Predicate predicate, Throwing.Function wrapper) { try { return getCause() .filter(x -> predicate.test((X) x)) .map(x -> (Try) Try.failure(wrapper.apply((X) x))) .orElse(this); } catch (Throwable x) { return failure(x); } } /** * Always run the given action, works like a finally clause. * * @param action Finally action. * @return This try result. */ public Try onComplete(Throwing.Runnable action) { try { action.run(); return this; } catch (Throwable x) { return Try.failure(x); } } /** * Always run the given action, works like a finally clause. Exception will be null in case of success. * * @param action Finally action. * @return This try result. */ public Try onComplete(Throwing.Consumer action) { try { action.accept(getCause().orElse(null)); return this; } catch (Throwable x) { return Try.failure(x); } } /** * Propagate/throw the exception in case of failure. */ public void throwException() { getCause().ifPresent(Throwing::sneakyThrow); } /** * Cause for failure or empty optional for success result. * * @return Cause for failure or empty optional for success result. */ public abstract Optional getCause(); }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy