/** * 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 org.opencastproject.util.data.Tuple.tuple; import static org.opencastproject.util.data.functions.Misc.chuck; import com.entwinemedia.fn.data.Opt; import java.util.ArrayList; import java.util.Collections; import java.util.Iterator; import java.util.List; /** * The option type encapsulates on optional value. It contains either some value or is empty. Please make sure to NEVER * wrap null into a some. Instead use none. */ // todo clean up the mix of abstract methods and concrete implementations based on the isSome() decision public abstract class Option<A> implements Iterable<A> { private Option() { } /** Safe decomposition of the option type. */ public abstract <B> B fold(Match<A, B> visitor); /** Safe decomposition of the option type using functions. */ public <B> B fold(Function<A, B> some, Function0<B> none) { return isSome() ? some.apply(get()) : none.apply(); } public abstract Option<A> foreach(Function<? super A, Void> f); public abstract <B> Option<B> fmap(Function<? super A, ? extends B> f); public <B> Option<B> map(Function<? super A, ? extends B> f) { return fmap(f); } /** Monadic bind operation <code>m a -> (a -> m b) -> m b</code>. */ public abstract <B> Option<B> bind(Function<A, Option<B>> f); /** @see org.opencastproject.util.data.functions.Functions#bind(Function) */ public <B> Option<B> flatMap(Function<A, Option<B>> f) { return bind(f); } public abstract boolean isSome(); public boolean isNone() { return !isSome(); } /** If this is some return <code>some</code>. Like {@link #bind(Function)} but ignores the option's content. */ public <B> Option<B> andThen(Option<B> some) { return isSome() ? some : Option.<B> none(); } /** If this is some return <code>some</code>. Like {@link #map(Function)} but ignores the option's content. */ public <B> Option<B> andThenV(B some) { return isSome() ? some(some) : Option.<B> none(); } /** Lazy version of {@link #andThen(Option)}. */ public <B> Option<B> andThen(Function0<Option<B>> some) { return isSome() ? some.apply() : Option.<B> none(); } /** Lazy version of {@link #andThenV(Object)}. */ public <B> Option<B> andThenV(Function0<B> some) { return isSome() ? some(some.apply()) : Option.<B> none(); } /** If this is none return <code>none</code> else this. */ public Option<A> orElse(Option<A> none) { return isSome() ? this : none; } /** Lazy version of {@link #orElse(Option)}. */ public Option<A> orElse(Function0<Option<A>> none) { return isSome() ? this : none.apply(); } /** Throw <code>none</code> if none. */ public <T extends Throwable> Option<A> orError(T none) throws T { if (isSome()) return this; else throw none; } /** Throw <code>none</code> if none. */ public <T extends Throwable> Option<A> orError(Class<T> none) throws T { if (isSome()) return this; else { T t; try { t = none.newInstance(); } catch (InstantiationException e) { return chuck(new Error("Error creating exception", e)); } catch (IllegalAccessException e) { return chuck(new Error("Error creating exception", e)); } throw t; } } /** Throw exception returned by <code>none</code> if none. */ public <T extends Throwable> Option<A> orError(Function0<T> none) throws T { if (isSome()) return this; else throw none.apply(); } public <B> Option<Tuple<A, B>> and(Option<B> b) { if (isSome() && b.isSome()) { return some(tuple(get(), b.get())); } else { return none(); } } /** Get the contained value or throw an exception. */ public abstract A get(); /** Get the contained value in case of being "some" or return parameter <code>none</code> otherwise. */ public abstract A getOrElse(A none); /** Get the contained value in case of being "some" or return the result of evaluating <code>none</code> otherwise. */ public abstract A getOrElse(Function0<A> none); /** To interface with legacy applications or frameworks that still use <code>null</code> values. */ public abstract A getOrElseNull(); /** Transform the option into a monadic list. */ public abstract Monadics.ListMonadic<A> mlist(); /** Transform an option into a list, either with a single element or an empty list. */ public abstract List<A> list(); /** * Left projection of this option. If the option is <code>some</code> return the value in an * {@link Either#left(Object)} else return <code>right</code> in an {@link Either#right(Object)}. */ public abstract <B> Either<A, B> left(B right); /** * Right projection of this optio. If the option is <code>some</code> return the value in an * {@link Either#left(Object)} else return <code>right</code> in an {@link Either#right(Object)}. */ public abstract <B> Either<B, A> right(B left); public abstract Opt<A> toOpt(); /** Inversion. If some return none. If none return some(zero). */ public Option<A> inv(A zero) { return isSome() ? Option.<A> none() : some(zero); } @Override public abstract int hashCode(); @Override public abstract boolean equals(Object o); // -- constructor functions /** Create a new some. */ public static <A> Option<A> some(final A a) { if (a == null) throw new Error("null must not be wrapped in a some"); return new Option<A>() { @Override public <B> B fold(Match<A, B> visitor) { return visitor.some(a); } @Override public Option<A> foreach(Function<? super A, Void> f) { f.apply(a); return this; } @Override public <B> Option<B> fmap(Function<? super A, ? extends B> f) { B b = f.apply(a); return some(b); } @Override public <B> Option<B> bind(Function<A, Option<B>> f) { return f.apply(a); } @Override public boolean isSome() { return true; } @Override public A get() { return a; } @Override public A getOrElse(A none) { return a; } @Override public A getOrElse(Function0<A> none) { return a; } @Override public A getOrElseNull() { return a; } @Override public Iterator<A> iterator() { return Collections.singletonList(a).iterator(); } @Override public Monadics.ListMonadic<A> mlist() { return Monadics.mlist(list()); } @Override public List<A> list() { return Collections.singletonList(a); } @Override public <B> Either<A, B> left(B right) { return Either.left(a); } @Override public <B> Either<B, A> right(B left) { return Either.right(a); } @Override public Opt<A> toOpt() { return Opt.some(a); } @Override public int hashCode() { // since an Option should NEVER contain any null this is safe return a.hashCode(); } @Override public boolean equals(Object o) { if (o instanceof Option) { Option<?> opt = (Option<?>) o; // since an Option should NEVER contain any null this is safe return opt.isSome() && a.equals(opt.get()); } else { return false; } } @Override public String toString() { return "Some(" + a + ")"; } }; } /** Create a new none. */ public static <A> Option<A> none() { return new Option<A>() { @Override public <B> B fold(Match<A, B> visitor) { return visitor.none(); } @Override public Option<A> foreach(Function<? super A, Void> f) { return this; } @Override public <B> Option<B> fmap(Function<? super A, ? extends B> f) { return none(); } @Override public <B> Option<B> bind(Function<A, Option<B>> f) { return none(); } @Override public boolean isSome() { return false; } @Override public A get() { throw new IllegalStateException("a none does not contain a value"); } @Override public A getOrElse(A none) { return none; } @Override public A getOrElse(Function0<A> none) { return none.apply(); } @Override public A getOrElseNull() { return null; } @Override public Iterator<A> iterator() { return new ArrayList<A>().iterator(); } @Override public Monadics.ListMonadic<A> mlist() { return Monadics.<A> mlist(list()); } @Override public List<A> list() { return Collections.emptyList(); } @Override public <B> Either<A, B> left(B right) { return Either.right(right); } @Override public <B> Either<B, A> right(B left) { return Either.left(left); } @Override public Opt<A> toOpt() { return Opt.none(); } @Override public int hashCode() { return -1; } @Override public boolean equals(Object o) { return o instanceof Option && ((Option) o).isNone(); } @Override public String toString() { return "None"; } }; } /** * Create a none with the type of <code>example</code>. This saves some nasty typing, e.g. * <code>Option.<String>none()</code> vs. <code>none("")</code>. * <p> * Please note that this constructor is only due to Java's insufficient type inference. */ public static <A> Option<A> none(A example) { return none(); } /** Create a none with the given type. */ public static <A> Option<A> none(Class<A> clazz) { return none(); } /** Wrap an arbitrary object into an option with <code>null</code> being mapped to none. */ public static <A> Option<A> option(A a) { if (a != null) return some(a); else return none(); } /** Convert an <code>Opt</code> into an <code>Option</code>. */ public static <A> Option<A> fromOpt(Opt<A> a) { for (A x : a) { return some(x); } return none(); } /** {@link #option(Object)} as a function. */ public static <A> Function<A, Option<A>> option() { return new Function<A, Option<A>>() { @Override public Option<A> apply(A a) { return option(a); } }; } /** * Use this function in <code>getOrElse</code> if it is an error being none. * * @deprecated use {@link #orError(Throwable)} or {@link #orElse(Function0)} instead since it saves the need for * creating new objects just for the sake of type soundness. Java unfortunately lacks a bottom type. */ @Deprecated public static <A> Function0<A> error(final String message) { return new Function0<A>() { @Override public A apply() { throw new Error(message); } }; } /** * Create an equals function. * * <pre> * some("abc").map(eq("bcd")).getOrElse(false) // false * some("abc").map(eq("abc")).getOrElse(false) // true * </pre> */ public static Function<String, Boolean> eq(final String compare) { return new Function<String, Boolean>() { @Override public Boolean apply(String s) { return compare.equals(s); } }; } public interface Match<A, B> { B some(A a); B none(); } /** Effect match. */ public abstract static class EMatch<A> implements Match<A, Void> { @Override public final Void some(A a) { esome(a); return null; } @Override public final Void none() { enone(); return null; } protected abstract void esome(A a); protected abstract void enone(); } }