Class ResultOrProblems<T>

java.lang.Object
nz.org.riskscape.problem.ResultOrProblems<T>

public final class ResultOrProblems<T> extends Object

An optional-esque that contains the result of some result-yielding operation where it may have had problems which might have stopped it from producing a result.

Used as a functional alternative to using checked exceptions for user-facing errors, typically where the type of the problem is irrelevant - something the user has done has meant we can't proceed and we need to pass it back to the user with some context to help them fix it.

  • Field Details

    • NO_PROBLEMS

      public static final List<Problem> NO_PROBLEMS
  • Constructor Details

    • ResultOrProblems

      public ResultOrProblems(T computedResult, List<Problem> problems)
  • Method Details

    • of

      public static <T> ResultOrProblems<T> of(@NonNull T thing)
    • of

      public static <T> ResultOrProblems<T> of(@NonNull T thing, Problem... problems)
    • of

      public static <T> ResultOrProblems<T> of(@NonNull T thing, List<Problem> withProblems)
    • ofNullable

      public static <T> ResultOrProblems<T> ofNullable(T thing, List<Problem> withProblems)

      Constructor where thing may or may not have been successfully created.

    • failed

      public static <T> ResultOrProblems<T> failed(Collection<ResultOrProblems<?>> failures)

      Create a new failed ResultOrProblems that collects all problems from a set of other ResultOrProblems. The caller must ensure that there are at least some error level problems among the given arguments.

      Type Parameters:
      T - An arbitrary generic type
      Parameters:
      failures - a collection of results with some failures
      Returns:
      a new failed ResultOrProblems that includes all problems from the given problems
      Throws:
      IllegalArgumentException - if there are no errors among the results
    • failed

      public static <T> ResultOrProblems<T> failed(ResultOrProblems<?> firstFailure, ResultOrProblems<?>... restFailures) throws IllegalArgumentException

      Create a new failed ResultOrProblems that collects all problems from a set of other ResultOrProblems. The caller must ensure that there are at least some error level problems among the given arguments.

      Type Parameters:
      T - An arbitrary generic type
      Parameters:
      firstFailure - a possibly failed result
      restFailures - even more possibly failed results
      Returns:
      a new failed ResultOrProblems that includes all problems from the given problems
      Throws:
      IllegalArgumentException - if there are no errors among the results
    • failed

      public static <T> ResultOrProblems<T> failed(Problem... withProblems)
    • failed

      public static <T> ResultOrProblems<T> failed(List<Problem> withProblems)
    • error

      public static <T> ResultOrProblems<T> error(String msg)

      Shortcut to ResultOrProblems.failed(Problem.error)

    • error

      public static <T> ResultOrProblems<T> error(String msg, Object... args)

      Shortcut to ResultOrProblems.failed(Problem.error)

    • error

      public static <T> ResultOrProblems<T> error(RiskscapeException ex)
    • unchecked

      public static ResultOrProblems<Anything> unchecked(String message)
    • get

      public T get()
      Returns:
      the result of this ResultOrProblems
      Throws:
      ResultComputationException - if no result was set, with this ResultOrProblems list of problems attached to it.
    • getOrThrow

      public T getOrThrow() throws ProblemException

      Flow control helper for use with ResultOrProblems - see ProblemException for more details.

      Returns:
      a computed result if one is present, or throws a ProblemException with problems if none.
      Throws:
      ProblemException - if there is no computed result
    • getOrThrow

      public T getOrThrow(Problem parent) throws ProblemException

      Flow control helper for use with ResultOrProblems - see ProblemException for more details.

      Parameters:
      parent - a problem to wrap around any problems that might be present
      Returns:
      a computed result if one is present, or throws a ProblemException with problems if none.
      Throws:
      ProblemException - if there is no computed result
    • getOrThrow

      public T getOrThrow(Function<List<Problem>,Problem> problemFunction) throws ProblemException

      Flow control helper for use with ResultOrProblems - see ProblemException for more details. This variant gives more control over the problem that ultimately gets included in the ProblemException.

      Parameters:
      problemFunction - a function that will produce a single problem for the ProblemException that is thrown.
      Returns:
      a computed result if one is present, or throws a ProblemException with problems if none.
      Throws:
      ProblemException - if there is no computed result
    • getWithProblemsIgnored

      public T getWithProblemsIgnored()
      Returns:
      computedResult, regardless of any other conditions. Will return a computedResult even if problems occurred - this function is really here to allow debugging/messaging code to report back a semi-successful object back to the user.
    • drainWarnings

      public ResultOrProblems<T> drainWarnings(Consumer<Problem> problemConsumer)

      Removes any warnings or lower from a successful ResultOrProblems so that get() doesn't produce a warning

    • drainWarnings

      public ResultOrProblems<T> drainWarnings(Consumer<Problem> problemConsumer, BiFunction<Problem.Severity,List<Problem>,Problem> callback)

      Removes any warnings or lower from a successful ResultOrProblems so that get() doesn't produce a warning.

      Similar to drainWarnings(java.util.function.Consumer) except that if non error problems are found then the callback used to get a problem that is then passed to the problemConsumer

    • ifProblems

      public <U> Optional<U> ifProblems(Function<List<Problem>,U> function)

      Call the given function if there were any problems with this ResultOrProblems, returning whatever the function returned.

    • isPresent

      public boolean isPresent()
      Returns:
      true if result is set.
    • ifPresent

      public void ifPresent(Consumer<T> function)

      Call the given consumer function with the result, only if result is set

    • ifElse

      public void ifElse(Consumer<T> function, Consumer<List<Problem>> elseConsume)

      Call the given consumer function with the result, only if result is set, else call the consumer

    • ifElseReturn

      public <U> U ifElseReturn(Function<T,U> function, Function<List<Problem>,U> elseConsume)

      Call the given consumer function with the result, only if result is set, else call the consumer

    • ifElseReturn

      public <U> U ifElseReturn(Function<T,U> function, Consumer<List<Problem>> elseConsume, U elseReturn)

      Call the given consumer function with the result, only if result is set, else call the consumer

    • hasProblems

      public boolean hasProblems()
      Returns:
      true if this ResultOrProblems has any problems, regardless of severity.
    • hasProblems

      public boolean hasProblems(Problem.Severity greaterThanEqualTo)
      Returns:
      true if this object's list of problems contain any that are of an equal or greater severity to the given severity parameter.
    • filterProblems

      public List<Problem> filterProblems(Class<?> affects)

      Filters the Problems based on the class of thing the problem affects, e.g. find all the problems that relate to a parameter.

      Parameters:
      affects - the type of thing affected by the Problem, e.g. Parameter.class, Step.class
      Returns:
      A list of Problems that affect the given class
    • flatMap

      public <U> ResultOrProblems<U> flatMap(Function<? super T,ResultOrProblems<U>> mapper)

      like flatMap(BiFunction), but appends the problem lists together in a new ResultOrProblems object

    • flatMap

      public <U> ResultOrProblems<U> flatMap(BiFunction<T,List<Problem>,ResultOrProblems<U>> mapper)

      Maps this ResultOrProblems to another, only if the result has been set. It is up to the BiFunction to produce a new set of problems - normally you want to append problems, so use the Function form of this method.

    • map

      public <U> ResultOrProblems<U> map(Function<T,U> mapper)

      Maps the result of this ResultOrProblems to another result, only if the result has been set.

    • map

      public <U> ResultOrProblems<U> map(Function<T,U> mapper, Function<Problem,Problem> problemMapper)

      Maps the result of this ResultOrProblems to another result, only if the result has been set, as well as supporting remapping problems one at a time regardless of a result.

      Parameters:
      problemMapper - a function to convert the problem list.
    • mapOrCombine

      public <U> ResultOrProblems<U> mapOrCombine(Function<T,U> mapper, ResultOrProblems<?>... dependencies)

      Like map(Function), but will only map if this and all dependencies succeeded. A failed result includes problems from this and all dependencies.

      Type Parameters:
      U - The mapped result type
      Parameters:
      mapper - a mapping function for a successful result
      dependencies - other ResultOrProblems to check for failures before calling the mapping function
      Returns:
      a successful result of type or a failed result containing all problems.
    • orElse

      public T orElse(T otherThing)
      Returns:
      either the set result, or otherThing if result is not set. otherThing can be null.
    • orElse

      public T orElse(Consumer<List<Problem>> consumeProblems, T elseReturn)
      Returns:
      either the set result, or otherThing if result is not set. otherThing can be null.
    • orElseGet

      public T orElseGet(Function<List<Problem>,T> resultFunction)
      Returns:
      either the set result, or otherThing if result is not set. otherThing can be null.
    • withMoreProblems

      public ResultOrProblems<T> withMoreProblems(Problem... moreProblems)
      Returns:
      a new ResultOrProblems object that contains the existing list of problems as well as the given list of problems.
    • withMoreProblems

      public ResultOrProblems<T> withMoreProblems(Function<T,List<Problem>> validation)

      Applies the given validation function to the computed result, and returns a new ResultOrProblems with any additional problems that were found

    • withMoreProblems

      public ResultOrProblems<T> withMoreProblems(Collection<Problem> moreProblems)
      Returns:
      a new ResultOrProblems object that contains the existing list of problems as well as the given list of problems.
    • orElseThrow

      public <X extends Throwable> T orElseThrow(Function<List<Problem>,? extends X> exceptionSupplier) throws X

      Mirror of Optional.orElseThrow(Supplier) - throws the given exception if there is no computed result.

      Throws:
      X extends Throwable
    • orElseThrow

      public <X extends Throwable> T orElseThrow(Supplier<? extends X> exceptionSupplier) throws X

      Mirror of Optional.orElseThrow(Supplier) - throws the given exception if there is no computed result.

      Throws:
      X extends Throwable
    • toString

      public String toString()
      Overrides:
      toString in class Object
    • hasErrors

      public boolean hasErrors()

      Shortcut for hasProblems(Severity.ERROR)

      Returns:
      true if there are any errors
    • equals

      public boolean equals(Object obj)
      Overrides:
      equals in class Object
    • hashCode

      public int hashCode()
      Overrides:
      hashCode in class Object
    • composeProblems

      @Deprecated public ResultOrProblems<T> composeProblems(String message, Object... args)
      Deprecated.
      For new code, use the i18n Problem-based method below instead
    • composeProblems

      public ResultOrProblems<T> composeProblems(Problem parentProblem)

      If this result has problems, then this method will return a new ResultOrProblems with a single problem with a new message that has this objects problems as children. Acts as a conditional shortcut for Problem.composite(resultOr.getProblems()..) - a common pattern when nesting problems with more context, e.g an expression failed that was part of a step, that was part of a pipeline, etc.

    • composeProblems

      public ResultOrProblems<T> composeProblems(BiFunction<Problem.Severity,List<Problem>,Problem> callback)

      Variant of composeProblems(Problem) that allows the problem to be constructed via a callback. Can be more clear or possibly required if a problem can not be constructed without children.

      Parameters:
      callback - that will be invoked if there are problems on this ResultOrProblems object.
      Returns:
      a new ResultOrProblems with a single problem that was created by callback, or the original object if there are no problems on this ResultOrProblems object.
    • composeFailure

      public <U> ResultOrProblems<U> composeFailure(Problem parentProblem)

      Compose a failed result with a new type and a parent error message. Will fail if this is not already a failure

    • addProblemsTo

      public ResultOrProblems<T> addProblemsTo(Collection<Problem> collection)
    • getAsSingleProblem

      public Problem getAsSingleProblem()

      Returns the Problems found as a single Problem, rather than a List. This can be useful for propagating errors via a RiskScapeException. Use this API if: - you're pretty sure you're only ever going to have a single problem in the list. - Or, the code you're dealing with is so generic, there's not a more appropriate object/context to use (i.e. you don't really know what the underlying 'thing' is). Otherwise, use the Problems.foundWith() API instead.

    • getProblems

      public List<Problem> getProblems()