package com.twitter.common.base; import javax.annotation.Nullable; import com.google.common.base.Objects; import com.google.common.base.Optional; import com.google.common.collect.Iterables; import com.google.common.collect.Lists; /** * A value of one of two possible types. * * <p>Often Either processing is used as an alternative exception flow control. In these uses the * left type represents failure by convention and the right type the success path result. * * @param <L> The left type. * @param <R> The right type. */ public final class Either<L, R> { private final Optional<L> left; private final Optional<R> right; private Either(Optional<L> left, Optional<R> right) { this.left = left; this.right = right; } /** * Turns a left into a right and vice-versa. * * @return A new swapped either instance. */ public Either<R, L> swap() { return new Either<R, L>(right, left); } /** * Returns an optional the will be {@link Optional#isPresent() present} is this is a left * instance. * * @return An optional value for the left. */ public Optional<L> left() { return left; } /** * Returns an optional the will be {@link Optional#isPresent() present} is this is a right * instance. * * @return An optional value for the right. */ public Optional<R> right() { return right; } /** * Returns {@code true} if this is a left instance. * * @return {@code true} if this is a left. */ public boolean isLeft() { return left().isPresent(); } /** * Returns {@code true} if this is a right instance. * * @return {@code true} if this is a right. */ public boolean isRight() { return right().isPresent(); } /** * Returns the underlying value if this is a left; otherwise, throws. * * @return The underlying value. * @throws IllegalStateException if this is a right instance. */ public L getLeft() { return left().get(); } /** * Returns the underlying value if this is a right; otherwise, throws. * * @return The underlying value. * @throws IllegalStateException if this is a right instance. */ public R getRight() { return right().get(); } /** * If this is a left, maps its value into a new left; otherwise just returns this right. * * @param transformer The transformation to apply to the left value. * @param <M> The type a left value will be mapped to. * @return The mapped left or else the right. */ public <M> Either<M, R> mapLeft(Function<? super L, M> transformer) { if (isLeft()) { return left(transformer.apply(getLeft())); } else { @SuppressWarnings("unchecked") // I am a right so my left is never accessible Either<M, R> self = (Either<M, R>) this; return self; } } /** * If this is a right, maps its value into a new right; otherwise just returns this left. * * @param transformer The transformation to apply to the left value. * @param <M> The type a right value will be mapped to. * @return The mapped right or else the left. */ public <M> Either<L, M> mapRight(Function<? super R, M> transformer) { if (isRight()) { return right(transformer.apply(getRight())); } else { @SuppressWarnings("unchecked") // I am a left so my right is never accessible Either<L, M> self = (Either<L, M>) this; return self; } } /** * Can transform either a left or a right into a result. * * @param <L> The left type. * @param <R> The right type. * @param <T> The transformation result type. */ public abstract static class Transformer<L, R, T> implements Function<Either<L, R>, T> { /** * Maps left values to a result. * * @param left the left value to map. * @return The mapped value. */ public abstract T mapLeft(L left); /** * Maps right values to a result. * * @param right the right value to map. * @return The mapped value. */ public abstract T mapRight(R right); @Override public final T apply(Either<L, R> either) { return either.map(this); } } /** * Creates a transformer from left and right transformation functions. * * @param leftTransformer A transformer to process left values. * @param rightTransformer A transformer to process right values. * @param <L> The left type. * @param <R> The right type. * @param <T> The transformation result type. * @return A new transformer composed of left and right transformer functions. */ public static <L, R, T> Transformer<L, R, T> transformer( final Function<? super L, T> leftTransformer, final Function<? super R, T> rightTransformer) { return new Transformer<L, R, T>() { @Override public T mapLeft(L item) { return leftTransformer.apply(item); } @Override public T mapRight(R item) { return rightTransformer.apply(item); } }; } /** * Transforms this either instance to a value regardless of whether it is a left or a right. * * @param transformer The transformer to map this either instance. * @param <T> The type the transformer produces. * @return A value mapped by the transformer from this left or right instance. */ public <T> T map(final Transformer<? super L, ? super R, T> transformer) { if (isLeft()) { return transformer.mapLeft(getLeft()); } else { return transformer.mapRight(getRight()); } } @Override public boolean equals(@Nullable Object o) { if (!(o instanceof Either)) { return false; } Either<?, ?> other = (Either<?, ?>) o; return Objects.equal(left, other.left) && Objects.equal(right, other.right); } @Override public int hashCode() { return Objects.hashCode(left, right); } @Override public String toString() { if (isLeft()) { return String.format("Left(%s)", getLeft()); } else { return String.format("Right(%s)", getRight()); } } /** * Creates a left either instance. * * @param value The left value to wrap - may not be null. * @param <L> The left type. * @param <R> The right type. * @return A left either instance wrapping {@code value}. */ public static <L, R> Either<L, R> left(L value) { return new Either<L, R>(Optional.of(value), Optional.<R>absent()); } /** * Creates a right either instance. * * @param value The right value to wrap - may not be null. * @param <L> The left type. * @param <R> The right type. * @return A right either instance wrapping {@code value}. */ public static <L, R> Either<L, R> right(R value) { return new Either<L, R>(Optional.<L>absent(), Optional.of(value)); } /** * Extracts all the lefts from a sequence of eithers lazily. * * @param results A sequence of either's to process. * @param <L> The left type. * @param <R> The right type. * @return A lazy iterable that will produce the lefts present in results in order. */ public static <L, R> Iterable<L> lefts(Iterable<Either<L, R>> results) { return Optional.presentInstances(Iterables.transform(results, new Function<Either<L, R>, Optional<L>>() { @Override public Optional<L> apply(Either<L, R> item) { return item.left(); } })); } /** * Extracts all the rights from a sequence of eithers lazily. * * @param results A sequence of either's to process. * @param <L> The left type. * @param <R> The right type. * @return A lazy iterable that will produce the rights present in results in order. */ public static <L, R> Iterable<R> rights(Iterable<Either<L, R>> results) { return Optional.presentInstances(Iterables.transform(results, new Function<Either<L, R>, Optional<R>>() { @Override public Optional<R> apply(Either<L, R> item) { return item.right(); } })); } /** * A convenience method equivalent to calling {@code guard(work, exceptionType)}. */ public static <X extends Exception, R> Either<X, R> guard( Class<X> exceptionType, ExceptionalSupplier<R, X> work) { @SuppressWarnings("unchecked") Either<X, R> either = guard(work, exceptionType); return either; } /** * A convenience method equivalent to calling * {@code guard(Lists.asList(execpetionType, rest), work)}. */ public static <X extends Exception, R> Either<X, R> guard( ExceptionalSupplier<R, X> work, Class<? extends X> exceptionType, Class<? extends X>... rest) { return guard(Lists.asList(exceptionType, rest), work); } /** * Thrown when guarded work throws an unguarded exception. The {@link #getCause() cause} will * contain the original unguarded exception. */ public static class UnguardedException extends RuntimeException { public UnguardedException(Throwable cause) { super(cause); } } /** * Converts work that can throw exceptions into an either with a left exception base type. This * can be useful to fold an exception throwing library call into an either processing style * pipeline. * * @param exceptionTypes The expected exception types. * @param work The work to perform to get a result produce an error. * @param <X> The base error type. * @param <R> The success type. * @return An either wrapping the result of performing {@code work}. * @throws UnguardedException if work throws an unguarded exception type. */ public static <X extends Exception, R> Either<X, R> guard( Iterable<Class<? extends X>> exceptionTypes, ExceptionalSupplier<R, X> work) throws UnguardedException { try { return right(work.get()); // We're explicitly dealing with generic exception types here by design. // SUPPRESS CHECKSTYLE RegexpSinglelineJava } catch (Exception e) { for (Class<? extends X> exceptionType : exceptionTypes) { if (exceptionType.isInstance(e)) { X exception = exceptionType.cast(e); return left(exception); } } throw new UnguardedException(e); } } }