All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.opencastproject.util.data.Option Maven / Gradle / Ivy

There is a newer version: 16.6
Show newest version
/*
 * 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 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 implements Iterable {
  private Option() {
  }

  /** Safe decomposition of the option type. */
  public abstract  B fold(Match visitor);

  public abstract Option foreach(Function f);

  public abstract  Option fmap(Function f);

  public  Option map(Function f) {
    return fmap(f);
  }

  /** Monadic bind operation m a -> (a -> m b) -> m b. */
  public abstract  Option bind(Function> f);

  public  Option flatMap(Function> f) {
    return bind(f);
  }

  public abstract boolean isSome();

  public boolean isNone() {
    return !isSome();
  }

  /** If this is none return none else this. */
  public Option orElse(Option none) {
    return isSome() ? this : none;
  }

  /** Lazy version of {@link #orElse(Option)}. */
  public Option orElse(Function0> none) {
    return isSome() ? this : none.apply();
  }

  /** Throw none if none. */
  public  Option orError(T none) throws T {
    if (isSome())
      return this;
    else
      throw none;
  }

  public  Option> and(Option 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 none otherwise. */
  public abstract A getOrElse(A none);

  /** Get the contained value in case of being "some" or return the result of evaluating none otherwise. */
  public abstract A getOrElse(Function0 none);

  /** To interface with legacy applications or frameworks that still use null values. */
  public abstract A getOrElseNull();

  /** Transform an option into a list, either with a single element or an empty list. */
  public abstract List list();

  public abstract Opt toOpt();

  @Override
  public abstract int hashCode();

  @Override
  public abstract boolean equals(Object o);

  // -- constructor functions

  /** Create a new some. */
  public static  Option some(final A a) {
    if (a == null)
      throw new Error("null must not be wrapped in a some");
    return new Option() {
      @Override
      public  B fold(Match visitor) {
        return visitor.some(a);
      }

      @Override
      public Option foreach(Function f) {
        f.apply(a);
        return this;
      }

      @Override
      public  Option fmap(Function f) {
        B b = f.apply(a);
        return some(b);
      }

      @Override
      public  Option bind(Function> 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 none) {
        return a;
      }

      @Override
      public A getOrElseNull() {
        return a;
      }

      @Override
      public Iterator iterator() {
        return Collections.singletonList(a).iterator();
      }

      @Override
      public List list() {
        return Collections.singletonList(a);
      }

      @Override
      public Opt 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  Option none() {
    return new Option() {
      @Override
      public  B fold(Match visitor) {
        return visitor.none();
      }

      @Override
      public Option foreach(Function f) {
        return this;
      }

      @Override
      public  Option fmap(Function f) {
        return none();
      }

      @Override
      public  Option bind(Function> 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 none) {
        return none.apply();
      }

      @Override
      public A getOrElseNull() {
        return null;
      }

      @Override
      public Iterator iterator() {
        return new ArrayList().iterator();
      }

      @Override
      public List list() {
        return Collections.emptyList();
      }

      @Override
      public Opt 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 example. This saves some nasty typing, e.g.
   * Option.<String>none() vs. none("").
   * 

* Please note that this constructor is only due to Java's insufficient type inference. */ public static Option none(A example) { return none(); } /** Create a none with the given type. */ public static Option none(Class clazz) { return none(); } /** Wrap an arbitrary object into an option with null being mapped to none. */ public static Option option(A a) { if (a != null) return some(a); else return none(); } /** Convert an Opt into an Option. */ public static Option fromOpt(Opt a) { for (A x : a) { return some(x); } return none(); } /** * Use this function in getOrElse 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 Function0 error(final String message) { return new Function0() { @Override public A apply() { throw new Error(message); } }; } /** * Create an equals function. * *

   *   some("abc").map(eq("bcd")).getOrElse(false) // false
   *   some("abc").map(eq("abc")).getOrElse(false) // true
   * 
*/ public static Function eq(final String compare) { return new Function() { @Override public Boolean apply(String s) { return compare.equals(s); } }; } public interface Match { B some(A a); B none(); } /** Effect match. */ public abstract static class EMatch implements Match { @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(); } }