io.github.oliviercailloux.jaris.exceptions.TryOptionalImpl Maven / Gradle / Ivy
package io.github.oliviercailloux.jaris.exceptions;
import static com.google.common.base.Preconditions.checkNotNull;
import com.google.common.base.MoreObjects;
import com.google.common.base.MoreObjects.ToStringHelper;
import io.github.oliviercailloux.jaris.throwing.TBiFunction;
import io.github.oliviercailloux.jaris.throwing.TConsumer;
import io.github.oliviercailloux.jaris.throwing.TFunction;
import io.github.oliviercailloux.jaris.throwing.TRunnable;
import io.github.oliviercailloux.jaris.throwing.TSupplier;
import java.util.Objects;
import java.util.Optional;
import java.util.function.Function;
/**
* See this class in branch doc-try for some thoughts about possible extensions; and extensive
* (draft!) documentation about the design choices.
*
* @param the type of result possibly kept in this object.
* @param the type of cause kept in this object if it is a failure.
*/
abstract class TryOptionalImpl implements TryOptional {
/**
* A sort of try optional that guarantees that a success has a (non-{@code null}) associated
* value. Is homeomorphic to a {@code T} xor {@code X}. Suitable for {@link Try} and
* {@link TryCatchAll}, depending on the catching strategy. The name (“variable catch”) indicates
* that this interface applies to both catching strategies.
*
* @param the type of result kept in this object if it is a success.
* @param the type of cause kept in this object if it is a failure.
* @param a priori constraint applied to some functionals on the type of throwable that they
* may throw – when catching all, it sometimes makes sense to authorize functionals to
* throw {@code Throwable}; when catching species of exceptions, this makes no sense and we
* reduce possible signatures to clarify the intended use.
*/
public interface TryVariableCatchInterface
extends TryOptional {
/**
* Returns {@code true} iff this instance contains a result (and not a cause).
*
* @return {@code true} iff {@link #isFailure()} returns {@code false}
*/
@Override
public boolean isSuccess();
/**
* Return {@code true} iff this instance contains a cause (and not a result).
*
* @return {@code true} iff {@link #isSuccess()} returns {@code false}
*/
@Override
public boolean isFailure();
/**
* Returns the transformed result contained in this instance if it is a success, using the
* provided {@code transformation}; or the transformed cause contained in this instance if it is
* a failure, using the provided {@code causeTransformation}.
*
* This method necessarily applies exactly one of the provided functions.
*
* @param the type of transformed result to return
* @param a type of exception that the provided functions may throw
* @param transformation a function to apply to the result if this instance is a success
* @param causeTransformation a function to apply to the cause if this instance is a failure
* @return the transformed result or cause
* @throws Y if the function that was applied threw an exception of type {@code Y}
* @throws NullPointerException if the function that was applied returned {@code null}
*/
public U map(
TFunction super T, ? extends U, ? extends Y> transformation,
TFunction super X, ? extends U, ? extends Y> causeTransformation) throws Y;
/**
* Returns the result contained in this instance if it is a success, without applying the
* provided function; or returns the transformed cause contained in this instance if it is a
* failure, using the provided {@code causeTransformation}.
*
* Equivalent to {@code map(t -> t, causeTransformation)}.
*
* @param a type of exception that the provided function may throw
* @param causeTransformation the function to apply iff this instance is a failure
* @return the result, or the transformed cause
* @throws Y if the function was applied and threw an exception of type {@code Y}
* @throws NullPointerException if the function was applied and returned {@code null}
*/
public T
orMapCause(TFunction super X, ? extends T, Y> causeTransformation) throws Y;
/**
* Returns an optional containing the result of this instance, without invoking the given
* consumer, if this try is a success; otherwise, invokes the given consumer and returns an
* empty optional.
*
* @param a type of exception that the provided consumer may throw
* @param consumer the consumer to invoke if this instance is a failure
* @return an optional, containing the result if this instance is a success, empty otherwise
* @throws Y if the consumer was invoked and threw an exception of type {@code Y}
*/
public Optional
orConsumeCause(TConsumer super X, Y> consumer) throws Y;
/**
* Returns the result contained in this instance if this instance is a success, or throws the
* cause contained in this instance.
*
* Equivalent to: {@link #orThrow(Function) orThrow(t -> t)}.
*
* @return the result that this success contains
* @throws X iff this instance is a failure
*/
public T orThrow() throws X;
/**
* Returns the result contained in this instance if this instance is a success, or throws the
* transformed cause contained in this instance.
*
* @param the type of throwable to throw if this instance is a failure
* @param causeTransformation the function to apply to the cause iff this instance is a failure
* @return the result that this success contains
* @throws Y if this instance is a failure
* @throws NullPointerException if the provided function was applied and returned {@code null}
*/
public T orThrow(Function causeTransformation) throws Y;
/**
* Runs the runnable iff this instance is a success, and returns this instance if it succeeds
* and the cause of failure if it throws a catchable throwable; otherwise, returns this
* instance.
*
* @param runnable the runnable to invoke iff this instance is a success
* @return a success iff this instance is a success and the provided runnable does not throw
*/
public TryVariableCatchInterface andRun(TRunnable extends X> runnable);
/**
* Runs the consumer iff this instance is a success, and returns this instance if it succeeds
* and the cause of failure if it throws a catchable throwable; otherwise, returns this
* instance.
*
* @param consumer the consumer to invoke iff this instance is a success
* @return a success iff this instance is a success and the provided consumer does not throw
*/
public TryVariableCatchInterface
andConsume(TConsumer super T, ? extends X> consumer);
/**
* Applies the given mapper iff this instance is a success, and returns the transformed success
* if it returns a non-{@code null} value, the cause of the failure if it throws a catchable
* throwable (including possibly a {@link NullPointerException} cause if this is considered
* catchable and the function returns {@code null}); otherwise, returns this instance.
*
* Equivalent to {@code t.map(s -> Try.get(() -> mapper.apply(s)), t)}.
*
* @param the type of result that the returned try will be declared to contain
* @param mapper the mapper to apply to the result iff this instance is a success
* @return a success iff this instance is a success and the provided mapper does not throw
*/
public TryVariableCatchInterface
andApply(TFunction super T, ? extends U, ? extends X> mapper);
/**
* Returns {@code true} iff the given object is a {@code TryOptional}, this and the given object
* are both successes and have equal results, or are both failures and have equal causes.
*
* @param o2 the object to compare this instance to
* @return {@code true} iff {@code o2} is a {@code TryOptional} and this instance and {@code o2}
* have present and equal results or present and equal causes
*/
@Override
public boolean equals(Object o2);
}
/**
* A sort of try optional that guarantees that a success has no associated value. Is homeomorphic
* to an {@code Optional} (plus indication of catching checked or catching all). Suitable for
* {@link TryVoid} and {@link TryCatchAllVoid}, depending on the catching strategy. The name
* (“variable catch”) indicates that this interface applies to both catching strategies.
*
* @param the type of cause kept in this object if it is a failure.
* @param a priori constraint applied to some functionals on the type of throwable that they
* may throw (see {@link TryVariableCatchInterface}).
*/
public interface TryVariableCatchVoidInterface
extends TryOptional {
/**
* Returns {@code true} iff this instance is a success, hence, contains no cause.
*
* @return {@code true} iff {@link #isFailure()} returns {@code false}
*/
@Override
public boolean isSuccess();
/**
* Return {@code true} iff this instance contains a cause.
*
* @return {@code true} iff {@link #isSuccess()} returns {@code false}
*/
@Override
public boolean isFailure();
/**
* Returns the supplied result if this instance is a success, using the provided
* {@code supplier}; or the transformed cause contained in this instance if it is a failure,
* using the provided {@code causeTransformation}; unless the invoked function returns
* {@code null}.
*
* This method necessarily invokes exactly one of the provided functional interfaces.
*
* @param the type of (supplied or transformed) result to return
* @param a type of exception that the provided functions may throw
* @param supplier a supplier to get a result from if this instance is a success
* @param causeTransformation a function to apply to the cause if this instance is a failure
* @return the supplied result or transformed cause
* @throws Y iff the functional interface that was invoked threw an exception of type {@code Y}
* @throws NullPointerException if the function that was applied returned {@code null}
*/
public T map(TSupplier extends T, ? extends Y> supplier,
TFunction super X, ? extends T, ? extends Y> causeTransformation) throws Y;
/**
* If this instance is a failure, invokes the given consumer using the cause contained in this
* instance. If this instance is a success, do nothing.
*
* @param a type of exception that the provided consumer may throw
* @param consumer the consumer to invoke if this instance is a failure
* @throws Y iff the consumer was invoked and threw an exception of type {@code Y}
*/
public void ifFailed(TConsumer super X, Y> consumer) throws Y;
/**
* If this instance is a failure, throws the cause it contains. Otherwise, do nothing.
*
* Equivalent to: {@link #orThrow(Function) orThrow(t -> t)}.
*
* @throws X iff this instance contains a cause
*/
public void orThrow() throws X;
/**
* If this instance is a failure, throws the transformed cause it contains. Otherwise, do
* nothing.
*
* @param the type of throwable to throw if this instance is a failure
* @param causeTransformation the function to apply to the cause iff this instance is a failure
* @throws Y if this instance is a failure
* @throws NullPointerException if the provided function was applied and returned {@code null}
*/
public void orThrow(Function causeTransformation) throws Y;
/**
* If this instance is a success, returns a try representing the result of invoking the given
* supplier if it supplies a non-{@code null} result, and a failure if the supplier throws a
* catchable throwable (or if the supplier returns {@code null} and a
* {@link NullPointerException} is considered catchable); otherwise, returns this failure.
*
* If this instance is a failure, it is returned, without invoking the given supplier.
* Otherwise, the given supplier is invoked. If it supplies a non-{@code null} result, a success
* is returned, containing the just supplied result. If the supplier throws a catchable
* throwable, a failure is returned, containing the cause it threw. If the supplier returns
* {@code null} and a {@link NullPointerException} is considered catchable, a failure is
* returned, containing a {@link NullPointerException} as a cause.
*
* @param the type of result that the returned try will be declared to contain
* @param supplier the supplier to attempt to get a result from if this instance is a success.
* @return a success iff this instance is a success and the given supplier returned a
* non-{@code null} result.
*/
public TryVariableCatchInterface
andGet(TSupplier extends T, ? extends X> supplier);
/**
* If this instance is a success, returns a try void representing the result of invoking the
* given runnable; otherwise, returns this failure.
*
* If this instance is a failure, it is returned, without invoking the given runnable.
* Otherwise, the given runnable is invoked. If it terminates without throwing, a success is
* returned. If the runnable throws a catchable throwable, a failure is returned, containing the
* cause it threw.
*
* @param runnable the runnable to attempt to run if this instance is a success.
* @return a success iff this instance is a success and the given runnable terminated without
* throwing.
*/
public TryVariableCatchVoidInterface andRun(TRunnable extends X> runnable);
/**
* Returns this instance if it is a success; otherwise, returns a try void representing the
* result of invoking the given runnable.
*
* If this instance is a success, it is returned, without invoking the given runnable.
* Otherwise, the given runnable is invoked. If it terminates without throwing, a success is
* returned. If the runnable throws a catchable throwable, a failure is returned, containing the
* cause it threw.
*
* @param runnable the runnable to attempt to invoke if this instance is a failure.
* @return a success iff this instance is a success or the given runnable terminated without
* throwing.
*/
public TryVariableCatchVoidInterface or(TRunnable extends X> runnable);
/**
* Returns {@code true} iff the given object is a {@code TryOptional}, this and the given object
* are both successes, or are both failures and have equal causes.
*
* @param o2 the object to compare this instance to
* @return {@code true} iff {@code o2} is a {@code TryOptional} and this instance and {@code o2}
* are both successes or have present and equal causes
*/
@Override
public boolean equals(Object o2);
}
public abstract static class TryVariableCatch
extends TryOptionalImpl implements TryVariableCatchInterface {
@Override
public T
orMapCause(TFunction super X, ? extends T, Y> causeTransformation) throws Y {
return map(t -> t, causeTransformation);
}
@Override
public T orThrow() throws X {
return orThrow(t -> t);
}
@Override
public String toString() {
final ToStringHelper stringHelper = MoreObjects.toStringHelper(this);
orConsumeCause(e -> stringHelper.add("cause", e))
.ifPresent(r -> stringHelper.add("result", r));
return stringHelper.toString();
}
}
public abstract static class TryVariableCatchSuccess
extends TryVariableCatch {
protected final T result;
protected TryVariableCatchSuccess(T result) {
this.result = checkNotNull(result);
}
@Override
public Optional getResult() {
return Optional.of(result);
}
@Override
public Optional getCause() {
return Optional.empty();
}
@Override
public U map(
TFunction super T, ? extends U, ? extends Y> transformation,
TFunction super X, ? extends U, ? extends Y> causeTransformation) throws Y {
final U transformed = transformation.apply(result);
checkNotNull(transformed, "Transformation returned null");
return transformed;
}
@Override
public Optional
orConsumeCause(TConsumer super X, Y> consumer) throws Y {
return Optional.of(result);
}
@Override
public T orThrow(Function causeTransformation) {
return result;
}
}
public abstract static class TryVariableCatchFailure
extends TryVariableCatch