package bjc.data; import java.util.*; import java.util.function.*; /** * Represents a pair where only one side has a value. * * @author ben * * @param * The type that could be on the left. * * @param * The type that could be on the right. * */ public class Either { /** * Create a new either with the left value occupied. * * @param * The type of the left value. * * @param * The type of the empty right value. * * @param left * The value to put on the left. * * @return An either with the left side occupied. */ public static Either left(final LeftType left) { return new Either<>(left, null); } /** * Create a new either with the right value occupied. * * @param * The type of the empty left value. * * @param * The type of the right value. * * @param right * The value to put on the right. * * @return An either with the right side occupied. */ public static Either right(final RightType right) { return new Either<>(null, right); } /* The left value of the either. */ private LeftType leftVal; /* The right value of the either. */ private RightType rightVal; /* Whether the left value is the one filled out. */ private boolean isLeft; /* Create a new either with specifed values. */ private Either(final LeftType left, final RightType right) { if (left == null) { rightVal = right; } else { leftVal = left; isLeft = true; } } /** * Perform a mapping over this either. * * @param The new left type. * @param The new right type. * * @param leftFunc The function to apply if this is a left either. * @param rightFunc The function to apply if this is a right either. * * @return A new either, containing a value transformed by the appropriate function. */ public Either map( Function leftFunc, Function rightFunc) { if (isLeft) return left(leftFunc.apply(leftVal)); else return right(rightFunc.apply(rightVal)); } /** * Extract the value from this Either. * * @param The common type to extract. * * @param leftHandler The function to handle left-values. * @param rightHandler The function to handle right-values. * * @return The result of applying the proper function. */ public Common extract( Function leftHandler, Function rightHandler) { if (isLeft) return leftHandler.apply(leftVal); else return rightHandler.apply(rightVal); } /** * Perform an action on this either. * * @param leftHandler The handler of left values. * @param rightHandler The handler of right values. */ public void pick( Consumer leftHandler, Consumer rightHandler) { if (isLeft) leftHandler.accept(leftVal); else rightHandler.accept(rightVal); } /** * Check if this either is left-aligned (has the left value filled, * not the right value). * * @return Whether this either is left-aligned. */ public boolean isLeft() { return isLeft; } /** * Get the left value of this either if there is one. * * @return An optional containing the left value, if there is one. */ public Optional getLeft() { return Optional.ofNullable(leftVal); } /** * Get the left value of this either, or get a {@link NoSuchElementException} * if there isn't one. * * @return The left value of this either. * * @throws NoSuchElementException If this either doesn't have a left value. */ public LeftType forceLeft() { if (isLeft) { return leftVal; } else { throw new NoSuchElementException("Either has no left value, is right value"); } } /** * Get the right value of this either if there is one. * * @return An optional containing the right value, if there is one. */ public Optional getRight() { return Optional.ofNullable(rightVal); } /** * Get the right value of this either, or get a {@link NoSuchElementException} * if there isn't one. * * @return The right value of this either. * * @throws NoSuchElementException If this either doesn't have a right value. */ public RightType forceRight() { if (isLeft) { throw new NoSuchElementException("Either has no right value, has left value"); } else { return rightVal; } } // Misc. overrides @Override public int hashCode() { return Objects.hash(isLeft, leftVal, rightVal); } @Override public boolean equals(Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; Either other = (Either) obj; return isLeft == other.isLeft && Objects.equals(leftVal, other.leftVal) && Objects.equals(rightVal, other.rightVal); } @Override public String toString() { return String.format("Either [leftVal='%s', rightVal='%s', isLeft=%s]", leftVal, rightVal, isLeft); } }