/*
* Copyright 2016 DiffPlug
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.diffplug.common.base;
import java.util.Objects;
import java.util.Optional;
import java.util.function.Consumer;
import java.util.function.Function;
/**
* A minimal implementation of Either.
*/
public abstract class Either<L, R> {
private Either() {}
/** True if it's left. */
public abstract boolean isLeft();
/** True if it's right. */
public final boolean isRight() {
return !isLeft();
}
/** Returns the left side. Throws an exception if it's really a Right. */
public abstract L getLeft();
/** Returns the right side. Throws an exception if it's really a Left. */
public abstract R getRight();
/** Performs the given action if this is a Left. */
public final void ifLeft(Consumer<? super L> consumer) {
if (isLeft()) {
consumer.accept(getLeft());
}
}
/** Performs the given action if this is a Right. */
public final void ifRight(Consumer<? super R> consumer) {
if (isRight()) {
consumer.accept(getRight());
}
}
/** Returns the left side as an Optional. */
public final Optional<L> asOptionalLeft() {
return fold(Optional::of, val -> Optional.<L> empty());
}
/** Returns the right side as an Optional. */
public final Optional<R> asOptionalRight() {
return fold(val -> Optional.<R> empty(), Optional::of);
}
/** Applies either the left or the right function as appropriate. */
public final <T> T fold(Function<? super L, ? extends T> left, Function<? super R, ? extends T> right) {
if (isLeft()) {
return left.apply(getLeft());
} else {
return right.apply(getRight());
}
}
/** Accepts either the left or the right consumer as appropriate. */
public final void accept(Consumer<? super L> left, Consumer<? super R> right) {
if (isLeft()) {
left.accept(getLeft());
} else {
right.accept(getRight());
}
}
@SuppressWarnings("unchecked")
public final <T> Either<T, R> mapLeft(Function<? super L, ? extends T> mapper) {
if (isLeft()) {
return Either.createLeft(mapper.apply(getLeft()));
} else {
return (Either<T, R>) this;
}
}
@SuppressWarnings("unchecked")
public final <T> Either<L, T> mapRight(Function<? super R, ? extends T> mapper) {
if (isLeft()) {
return (Either<L, T>) this;
} else {
return Either.createRight(mapper.apply(getRight()));
}
}
/** Accepts both the left and right consumers, using the default values to set the empty side. */
public final void acceptBoth(Consumer<? super L> left, Consumer<? super R> right, L defaultLeft, R defaultRight) {
left.accept(isLeft() ? getLeft() : defaultLeft);
right.accept(isRight() ? getRight() : defaultRight);
}
/** Creates a left or right, depending on which element is non-null. Precisely one element should be non-null. */
public static <L, R> Either<L, R> create(L l, R r) {
if (l == null && r != null) {
return createRight(r);
} else if (l != null && r == null) {
return createLeft(l);
} else {
if (l == null) {
throw new IllegalArgumentException("Both arguments were null.");
} else {
throw new IllegalArgumentException("Both arguments were non-null: " + l + " " + r);
}
}
}
/** Creates an instance of Left. */
public static <L, R> Either<L, R> createLeft(L l) {
return new Left<>(l);
}
/** Creates an instance of Right. */
public static <L, R> Either<L, R> createRight(R r) {
return new Right<>(r);
}
/** Implementation of left. */
private static final class Left<L, R> extends Either<L, R> {
private final L value;
private Left(L value) {
this.value = Objects.requireNonNull(value);
}
@Override
public final boolean isLeft() {
return true;
}
@Override
public final L getLeft() {
return value;
}
@Override
public final R getRight() {
throw new UnsupportedOperationException();
}
@Override
public final boolean equals(Object otherObj) {
if (otherObj instanceof Left) {
return Objects.equals(value, ((Left<?, ?>) otherObj).value);
} else {
return false;
}
}
@Override
public final int hashCode() {
return Objects.hash(Left.class, value);
}
@Override
public final String toString() {
return "Left[" + value.toString() + "]";
}
}
/** Implementation of right. */
private static final class Right<L, R> extends Either<L, R> {
private final R value;
private Right(R value) {
this.value = Objects.requireNonNull(value);
}
@Override
public final boolean isLeft() {
return false;
}
@Override
public final L getLeft() {
throw new UnsupportedOperationException();
}
@Override
public final R getRight() {
return value;
}
@Override
public final boolean equals(Object otherObj) {
if (otherObj instanceof Right) {
return Objects.equals(value, ((Right<?, ?>) otherObj).value);
} else {
return false;
}
}
@Override
public final int hashCode() {
return Objects.hash(Right.class, value);
}
@Override
public final String toString() {
return "Right[" + value.toString() + "]";
}
}
}