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

com.github.sirikid.Maybe Maven / Gradle / Ivy

The newest version!
/*
 * Copyright (c) 2016, Ivan Sokolov. All rights reserved.
 * This code is licensed under MIT license (see LICENSE for details)
 */

package com.github.sirikid;

import com.github.sirikid.iterators.EmptyIterator;
import com.github.sirikid.iterators.EmptySpliterator;
import com.github.sirikid.iterators.SingletonIterator;
import com.github.sirikid.iterators.SingletonSpliterator;
import java.util.Iterator;
import java.util.NoSuchElementException;
import java.util.Objects;
import java.util.Spliterator;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.function.Supplier;
import javax.annotation.Nullable;

import static java.util.Objects.requireNonNull;

/**
 * Implementation Maybe monad in Java.
 *
 * @author Ivan Sokolov
 * @version 1.0.0
 * @param  type of value, if exists
 * @since 1.0.0
 */

// TODO JavaDoc

public abstract class Maybe implements Supplier, Iterable {
	private Maybe() { /* Get out, looser */ }

	/**
	 * @return value, if present
	 * @throws NoSuchElementException otherwise
	 */
	@Nullable
	@Override
	public abstract T get();

	/**
	 * Just synonym for {@link #get()}.
	 * @return value, if present
	 * @throws NoSuchElementException otherwise
	 */
	@Nullable
	public final T unwrap() {
		return get();
	}

	/**
	 * @param defaultValue default value
	 * @return value, if present, defaultValue otherwise
	 */
	@Nullable
	public abstract T or(@Nullable T defaultValue);

	/**
	 * Returns value or throws user defined exception.
	 *
	 * @param  type of exception you want
	 * @param exceptionSupplier supplier, that supply exception you want
	 *
	 * @return value, if present
	 * @throws X instance of X otherwise
	 */
	@Nullable
	public abstract  T orThrow(Supplier exceptionSupplier) throws X;

	/**
	 * @return true, if value present, false otherwise
	 */
	public abstract boolean exists();

	/**
	 * If value exists consume value with given consumer.
	 *
	 * @param consumer just consumer
	 * @throws NullPointerException if consumer is null
	 */
	public abstract void ifExists(Consumer consumer);

	public abstract  Maybe map(Function mapper);

	public abstract  Maybe flatMap(Function> mapper);

	public abstract Maybe filter(Predicate predicate);

	public static  Maybe nothing() {
		return new Nothing<>();
	}

	public static  Maybe just(@Nullable final T value) {
		return new Just<>(value);
	}

	public static  Maybe nonNull(@Nullable final T value) {
		return (value == null) ? nothing() : just(value);
	}

	/**
	 * @since 1.1.0
	 * @throws NullPointerException if supplier is null
	 */
	public static  Maybe exceptionally(Supplier supplier) {
		return exceptionally(supplier, Throwable.class);
	}

	/**
	 * @since 1.1.0
	 * @throws NullPointerException if supplier or exceptionType is null
	 */
	public static  Maybe exceptionally(Supplier supplier,
	                                                              Class exceptionType) {
		requireNonNull(supplier);
		requireNonNull(exceptionType);
		try {
			return just(supplier.get());
		} catch (Throwable t) {
			if (exceptionType.isInstance(t)) return nothing();
			throw t;
		}
	}

	/**
	 * @since 1.1.0
	 * @throws NullPointerException if supplier or exceptionsTypes is null
	 */
	@SafeVarargs
	public static  Maybe exceptionally(Supplier supplier,
	                                         Class... exceptionsTypes) {
		requireNonNull(supplier);
		requireNonNull(exceptionsTypes);
		try {
			return just(supplier.get());
		} catch (Throwable t) {
			for (final Class type : exceptionsTypes)
				if (type.isInstance(t)) return nothing();
			throw t;
		}
	}

	private final static class Nothing extends Maybe {
		private Nothing() {}

		@Nullable
		@Override
		public T get() {
			throw new NoSuchElementException();
		}

		@Nullable
		@Override
		public T or(@Nullable final T defaultValue) {
			return defaultValue;
		}

		@Nullable
		@Override
		public  T orThrow(Supplier exceptionSupplier) throws X {
			throw exceptionSupplier.get();
		}

		@Override
		public boolean exists() {
			return false;
		}

		@Override
		public void ifExists(Consumer consumer) {
			requireNonNull(consumer);
		}

		@Override
		public  Maybe map(Function mapper) {
			requireNonNull(mapper);
			return nothing();
		}

		@Override
		public  Maybe flatMap(Function> mapper) {
			requireNonNull(mapper);
			return nothing();
		}

		@Override
		public Maybe filter(Predicate predicate) {
			requireNonNull(predicate);
			return nothing();
		}

		@Override
		public Iterator iterator() {
			return new EmptyIterator<>();
		}

		@Override
		public Spliterator spliterator() {
			return new EmptySpliterator<>();
		}

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

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

		@Override
		public String toString() {
			return "Maybe.nothing";
		}
	}

	private final static class Just extends Maybe {
		@Nullable private final T value;

		private Just(@Nullable final T value) {
			this.value = value;
		}

		@Nullable
		@Override
		public T get() {
			return value;
		}

		@Nullable
		@Override
		public T or(@Nullable T defaultValue) {
			return value;
		}

		@Nullable
		@Override
		public  T orThrow(Supplier exceptionSupplier) throws X {
			return value;
		}

		@Override
		public boolean exists() {
			return true;
		}

		@Override
		public void ifExists(Consumer consumer) {
			requireNonNull(consumer).accept(value);
		}

		@Override
		public  Maybe map(Function mapper) {
			return just(requireNonNull(mapper).apply(value));
		}

		@Override
		public  Maybe flatMap(Function> mapper) {
			return requireNonNull(mapper).apply(value);
		}

		@Override
		public Maybe filter(Predicate predicate) {
			requireNonNull(predicate);
			return predicate.test(value) ? just(value) : nothing();
		}

		@Override
		public Iterator iterator() {
			return new SingletonIterator<>(value);
		}

		@Override
		public Spliterator spliterator() {
			return new SingletonSpliterator<>(value);
		}

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

		@Override
		public boolean equals(Object obj) {
			if (obj == this) return true;

			if (obj instanceof Just) {
				Just other = (Just) obj;
				return Objects.equals(value, other.value);
			}

			return false;
		}

		@Override
		public String toString() {
			return "Maybe.just(" + String.valueOf(value) + ")";
		}
	}
}