Optional.java

package no.motif.single;

import static no.motif.Base.notNull;

import java.io.Serializable;
import java.util.Iterator;
import java.util.NoSuchElementException;
import java.util.Objects;

import no.motif.Iterate;
import no.motif.Singular;
import no.motif.f.Fn;
import no.motif.f.Fn0;
import no.motif.f.Predicate;
import no.motif.iter.EmptyIterator;
import no.motif.iter.SingularIterator;
import no.motif.types.Appendable;
import no.motif.types.Elements;
import no.motif.types.Filterable;
import no.motif.types.Mappable;
import no.motif.types.Prependable;

/**
 * An <code>Optional</code> wraps a value that is either defined or
 * undefined. Usually, undefined means <code>null</code>, and defined means
 * an object instance, but the distinction between defined and undefined can
 * be customized with a {@link Predicate}.
 *
 * @param <V> The type of the wrapped object.
 */
public abstract class Optional<V>
    implements Iterable<V>, Mappable<V>, Filterable<V>, Appendable<V>, Prependable<V>, Serializable {


    /**
     * <h1>*** Not part of the public API! ***</h1>
     * Factory method for resolving a value to {@link Some} or {@link None}.
     *
     * @see Singular#optional(Object)
     * @see Singular#optional(Predicate, Object)
     * @see Singular#none()
     */
    public static <V> Optional<V> resolve(Predicate<? super V> isPresent, V value) {
        if (isPresent.$(value)) {
            return some(value);
        } else {
            return None.getInstance();
        }
    }

    /**
     * <h1>*** Not part of the public API! ***</h1>
     *
     * @see Singular#the(Object)
     */
    public static <V> Some<V> some(V value) {
        return new Some<>(value);
    }


    /**
     * Wrapper for a 'defined' value. You never under normal
     * circumstances refer to this type.
     *
     * @param <V> The type of the wrapped object.
     */
    public static final class Some<V> extends Optional<V> implements A<V> {

        private final V value;

        private Some(V value) {
            this.value = value;
        }

        @Override
        public V get() {
            return value;
        }

        @Override
        public final Iterator<V> iterator() {
            return new SingularIterator<V>(value);
        }

        @Override
        public final boolean isSome() {
            return true;
        }

        @Override
        public final V orElse(V fallback) {
            return get();
        }

        @Override
        public V orNull() {
            return value;
        }

        @Override
        public <X extends Throwable> V orElseThrow(Fn0<? extends X> exceptionProvider) throws X {
            return value;
        }

        @Override
        public Optional<V> or(Optional<V> otherOptional) {
            return this;
        }

        @Override
        public Elements<V> or(Iterable<V> elements) {
            return Iterate.on(this);
        }

        @Override
        public <O> Optional<O> map(Fn<? super V, O> mapper) {
            O mapped = mapper.$(this.value);
            return resolve(notNull, mapped);
        }

        @Override
        public <O> Optional<O> map(Predicate<? super O> isPresent, Fn<? super V, O> mapper) {
            O mapped = mapper.$(this.value);
            return resolve(isPresent, mapped);
        }

        @Override
        public <O> Optional<O> flatMap(Fn<? super V, Optional<O>> mapper) {
            return mapper.$(value);
        }

        @Override
        public Optional<V> filter(Predicate<? super V> accepted) {
            return resolve(accepted, value);
        }

        @Override
        public <O> Elements<O> split(Fn<? super V, ? extends Iterable<O>> splitter) {
            return Iterate.on(splitter.$(value));
        }

        @Override
        public boolean equals(Object o) {
            if (o instanceof Some) {
                Some<?> other = (Some<?>) o;
                return Objects.equals(this.value, other.value);
            }
            return false;
        }

        @Override
        public int hashCode() {
            return Objects.hashCode(value);
        }

        @Override
        public String toString() {
            return "Some(" + value + ")";
        }

        @Override
        public Optional<V> asOptional() {
            return this;
        }

    }

    /**
     * Wrapper for an 'undefined' value. You never under normal
     * circumstances refer to this type.
     *
     * @param <V> The type of the undefined value.
     */
    public static final class None<V> extends Optional<V> {

        private static final None<?> INSTANCE = new None<Object>();

        @SuppressWarnings("unchecked")
        public static <V> None<V> getInstance() {
            return (None<V>) INSTANCE;
        }

        private None() {}

        @Override
        public V get() {
            throw new NoSuchElementException("Called get() on " + this);
        }

        @Override
        public final Iterator<V> iterator() {
            return EmptyIterator.instance();
        }

        @Override
        public final boolean isSome() {
            return false;
        }

        @Override
        public final V orElse(V fallback) {
            return fallback;
        }

        @Override
        public V orNull() {
            return null;
        }

        @Override
        public <X extends Throwable> V orElseThrow(Fn0<? extends X> exceptionProvider) throws X {
            throw exceptionProvider.$();
        }

        @Override
        public Optional<V> or(Optional<V> otherOptional) {
            return otherOptional;
        }

        @Override
        public Elements<V> or(Iterable<V> elements) {
            return Iterate.<V>on(elements);
        }

        @Override
        public <O> Optional<O> map(Fn<? super V, O> mapper) {
            return None.getInstance();
        }

        @Override
        public <O> Optional<O> map(Predicate<? super O> isPresent, Fn<? super V, O> mapper) {
            return None.getInstance();
        }

        @Override
        public <O> Optional<O> flatMap(Fn<? super V, Optional<O>> mapper) {
            return None.getInstance();
        }

        @Override
        public Optional<V> filter(Predicate<? super V> filter) {
            return None.getInstance();
        }

        @Override
        public <O> Elements<O> split(Fn<? super V, ? extends Iterable<O>> splitter) {
            return Iterate.none();
        }

        @Override
        public String toString() {
            return "None";
        }

        private Object readResolve() {
            return None.getInstance();
        }

        @Override
        public boolean equals(Object other) {
            return this == other || other instanceof None;
        }

        @Override
        public int hashCode() {
            return 43;
        }

    }



    /**
     * Obtain the value contained in the <code>Optional</code>. To use
     * this method one <em>must</em> first determine that the
     * <code>Optional</code> does in fact hold a value.
     *
     * To obtain the wrapped value, it is in general preferable to favor
     * <em>iterating</em> or using {@link #orElse(Object)}, which will never
     * throw an exception.
     *
     * @throws NoSuchElementException if this method is called on an
     *         <code>Optional</code> without a defined value.
     *
     * @see #isSome()
     */
    public abstract V get();



    /**
     * Map this <code>Optional</code> to another type of <code>Optional</code>.
     *
     * @param mapper      A {@link Fn function} which transforms
     *                    (or maps) the wrapped value to another value. This
     *                    function will only ever be called if the
     *                    <code>Optional</code> holds a defined value.
     *
     * @return A new <code>Optional</code> from which the new value can be
     *         obtained, if it is defined.
     */
    @Override
    public abstract <O> Optional<O> map(Fn<? super V, O> mapper);


    /**
     * Map this <code>Optional</code> to another type of <code>Optional</code>.
     *
     * @see #map(Fn)
     * @see #resolve(Predicate, Object)
     */
    public abstract <O> Optional<O> map(Predicate<? super O> isPresent, Fn<? super V, O> mapper);


    public abstract <O> Optional<O> flatMap(Fn<? super V, Optional<O>> mapper);


    @Override
    public abstract Optional<V> filter(Predicate<? super V> filter);


    /**
     * @return <code>true</code> if the <code>Optional</code> holds a
     *         defined value, or <code>false</code> otherwise, which usually
     *         means that the wrapped value is <code>null</code>.
     */
    public abstract boolean isSome();


    /**
     *
     * @param fallback A value to return if called on a {@link None}.
     * @return The wrapped value, or the fallback value if it is undefined.
     */
    public abstract V orElse(V fallback);


    /**
     * Shortcut for {@link #orElse(Object) .orElse(null)}
     * @return The wrapped value, or <code>null</code> if it is undefined.
     */
    public abstract V orNull();


    /**
     * @param exceptionProvider The function which will provide the exception to be thrown.
     * @param <X> Type of the exception to be thrown.
     * @return The wrapped value.
     * @throws X if this method is called on an <code>Optional</code> without a defined value.
     */
    public abstract <X extends Throwable> V orElseThrow(Fn0<? extends X> exceptionProvider) throws X;


    /**
     * @return If this <code>Optional</code> is not defined, the given <code>otherOptional</code>
     *         is returned, otherwise the original <code>Optional</code> is returned as-is.
     */
    public abstract Optional<V> or(Optional<V> otherOptional);


    /**
     * @return If this <code>Optional</code> is not defined, the given <code>elements</code>
     *         is returned, otherwise the value in this <code>Optional</code> is returned as
     *         the only contained element.
     */
    public abstract Elements<V> or(Iterable<V> elements);


    /**
     * Split an optional value, if defined, into multiple values.
     * <p>
     * Due to limitations of the type system in Java, it is not possible to overload this
     * method to accept both functions yielding an {@link Iterable} (as this method does)
     * as well as functions yielding an array. It is recommended to prefer implementing
     * {@link Fn functions} yielding Iterables. If implementing an array-yielding
     * function is most appropriate for your case, and you wish to use the function to split
     * an optional value using this method, it is possible to adapt the function using
     * {@link Iterate#toIterable(Fn)}
     * </p>
     *
     * @param splitter a function yielding an iterable.
     * @return the elements, or empty iterable if undefined value.
     */
    public abstract <O> Elements<O> split(Fn<? super V, ? extends Iterable<O>> splitter);


    @Override
    public Elements<V> append(Iterable<? extends V> trailingElements) {
        return Iterate.on(this).append(trailingElements);
    }

    @Override
    public Elements<V> append(V value) {
        return Iterate.on(this).append(value);
    }

    @Override
    public Elements<V> prepend(Iterable<? extends V> leadingElements) {
        return Iterate.on(this).prepend(leadingElements);
    }

    @Override
    public Elements<V> prepend(V value) {
        return Iterate.on(this).prepend(value);
    }


    private Optional() {}
}