/** * Licensed to The Apereo Foundation under one or more contributor license * agreements. See the NOTICE file distributed with this work for additional * information regarding copyright ownership. * * * The Apereo Foundation licenses this file to you under the Educational * Community 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://opensource.org/licenses/ecl2.txt * * 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 org.opencastproject.util.data; import static java.util.Collections.emptyList; import static java.util.Collections.singletonList; import static org.opencastproject.util.data.Option.some; import java.util.Iterator; import java.util.List; /** * An algebraic data type representing a disjoint union. By convention left is considered to represent an error while * right represents a value. * * This implementation of <code>Either</code> is much simpler than implementations you may know from other languages or * libraries. */ public abstract class Either<A, B> { private Either() { } public abstract LeftProjection<A, B> left(); public abstract RightProjection<A, B> right(); public abstract <X> X fold(Match<A, B, X> visitor); public abstract <X> X fold(Function<? super A, ? extends X> left, Function<? super B, ? extends X> right); public abstract boolean isLeft(); public abstract boolean isRight(); public interface Match<A, B, X> { X left(A a); X right(B b); } /** * Left projection of either. */ public abstract class LeftProjection<A, B> implements Iterable<A> { private LeftProjection() { } public abstract Either<A, B> either(); public abstract <X> Either<X, B> map(Function<A, X> f); public abstract <X> Either<X, B> bind(Function<A, Either<X, B>> f); public <X> Either<X, B> flatMap(Function<A, Either<X, B>> f) { return bind(f); } public abstract A value(); public abstract A getOrElse(A right); public abstract Option<A> toOption(); public abstract List<A> toList(); } /** * Right projection of either. */ public abstract class RightProjection<A, B> implements Iterable<B> { private RightProjection() { } public abstract Either<A, B> either(); public abstract <X> Either<A, X> map(Function<B, X> f); public abstract <X> Either<A, X> bind(Function<B, Either<A, X>> f); public <X> Either<A, X> flatMap(Function<B, Either<A, X>> f) { return bind(f); } public abstract B value(); public abstract B getOrElse(B left); public abstract Option<B> toOption(); public abstract List<B> toList(); } /** * Create a left. */ public static <A, B> Either<A, B> left(final A left) { return new Either<A, B>() { @Override public <C> C fold(Match<A, B, C> visitor) { return visitor.left(left); } @Override public LeftProjection<A, B> left() { final Either<A, B> self = this; return new LeftProjection<A, B>() { @Override public A value() { return left; } @Override public Option<A> toOption() { return some(left); } @Override public List<A> toList() { return singletonList(left); } @Override public Iterator<A> iterator() { return toList().iterator(); } @Override public A getOrElse(A right) { return left; } @Override public Either<A, B> either() { return self; } @Override public <X> Either<X, B> map(Function<A, X> f) { return left(f.apply(left)); } @Override public <X> Either<X, B> bind(Function<A, Either<X, B>> f) { return f.apply(left); } }; } @Override public RightProjection<A, B> right() { final Either<A, B> self = this; return new RightProjection<A, B>() { @Override public Either<A, B> either() { return self; } @Override public <X> Either<A, X> map(Function<B, X> f) { return left(left); } @Override public <X> Either<A, X> bind(Function<B, Either<A, X>> f) { return left(left); } @Override public B value() { throw new Error("right projection on left does not have a value"); } @Override public B getOrElse(B left) { return left; } @Override public Option<B> toOption() { return Option.none(); } @Override public List<B> toList() { return emptyList(); } @Override public Iterator<B> iterator() { return toList().iterator(); } }; } @Override public <C> C fold(Function<? super A, ? extends C> leftf, Function<? super B, ? extends C> rightf) { return leftf.apply(left); } @Override public boolean isLeft() { return true; } @Override public boolean isRight() { return false; } }; } /** * Create a right. */ public static <A, B> Either<A, B> right(final B right) { return new Either<A, B>() { @Override public <C> C fold(Match<A, B, C> visitor) { return visitor.right(right); } @Override public LeftProjection<A, B> left() { final Either<A, B> self = this; return new LeftProjection<A, B>() { @Override public A value() { throw new Error("left projection on right does not have a value"); } @Override public Option<A> toOption() { return Option.none(); } @Override public List<A> toList() { return emptyList(); } @Override public Iterator<A> iterator() { return toList().iterator(); } @Override public A getOrElse(A right) { return right; } @Override public Either<A, B> either() { return self; } @Override public <X> Either<X, B> map(Function<A, X> f) { return right(right); } @Override public <X> Either<X, B> bind(Function<A, Either<X, B>> f) { return right(right); } }; } @Override public RightProjection<A, B> right() { final Either<A, B> self = this; return new RightProjection<A, B>() { @Override public Either<A, B> either() { return self; } @Override public <X> Either<A, X> map(Function<B, X> f) { return right(f.apply(right)); } @Override public <X> Either<A, X> bind(Function<B, Either<A, X>> f) { return f.apply(right); } @Override public B value() { return right; } @Override public B getOrElse(B left) { return right; } @Override public Option<B> toOption() { return some(right); } @Override public List<B> toList() { return singletonList(right); } @Override public Iterator<B> iterator() { return toList().iterator(); } }; } @Override public <X> X fold(Function<? super A, ? extends X> leftf, Function<? super B, ? extends X> rightf) { return rightf.apply(right); } @Override public boolean isLeft() { return false; } @Override public boolean isRight() { return true; } }; } }