Iterate.java

package no.motif;

import static java.util.Arrays.asList;
import static java.util.Collections.emptyList;
import static no.motif.Base.notNull;
import static no.motif.Base.when;
import static no.motif.Singular.optional;

import java.util.Collection;
import java.util.Comparator;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;

import no.motif.f.Fn;
import no.motif.f.Predicate;
import no.motif.iter.CharsInStringIterator;
import no.motif.iter.PreparedIterable;
import no.motif.iter.boxing.BytesIterable;
import no.motif.iter.ordering.ByPropertyComparator;
import no.motif.iter.ordering.EnhancedComparator;
import no.motif.iter.ordering.EnhancedComparatorImpl;
import no.motif.types.Elements;


/**
 * Operations for containers, e.g. arrays,
 * {@link List lists}, {@link Set sets}, or any implementation
 * of {@link Iterable}. Can also
 * {@link #on(CharSequence) treat Strings as a container of Characters}.
 *
 */
public final class Iterate {

    private static final Elements<?> NONE = new PreparedIterable<>(emptyList());

    /**
     * @return An empty <code>Elements</code> container.
     */
    @SuppressWarnings("unchecked")
    public static <T> Elements<T> none() {
        return (Elements<T>) NONE;
    }


    /**
     * Manipulate CharSequences/Strings as if they were iterables of
     * Characters.
     *
     * @param string The string
     * @return {@link Elements} characters
     */
    public static Elements<Character> on(final CharSequence string) {
        if (string == null) return Iterate.none();
        return newInstance(new Iterable<Character>() {
            @Override
            public Iterator<Character> iterator() {
                return new CharsInStringIterator(string);
            }
        });
    }


    /**
     * Work with multiple elements.
     *
     * @param elements the elements to manipulate as vararg/array.
     * @return {@link Elements}
     */
    @SafeVarargs
    @SuppressWarnings("varargs")
    public static <T> Elements<T> on(T ... elements) {
        return newInstance(elements != null ? asList(elements) : null);
    }

    /**
     * Work with multiple bytes.
     *
     * @param bytes the bytes to manipulate as vararg/array.
     */
    public static Elements<Byte> on(byte[] bytes) {
        return newInstance(bytes != null ? new BytesIterable(bytes) : null);
    }



    public static <K, V> Elements<Map.Entry<K, V>> on(Map<K, V> map) {
        return on(map != null ? map.entrySet() : null);
    }


    /**
     * Work with multiple elements.
     *
     * @param elements the elements to manipulate as an {@link Iterable}.
     * @return {@link Elements}
     */
    public static <T> Elements<T> on(Iterable<T> elements) {
        return elements instanceof Elements ? (Elements<T>) elements : newInstance(elements);
    }






    private static <T> Elements<T> newInstance(Iterable<T> elements) {
        if (elements == null || (elements instanceof Collection && ((Collection<?>)elements).isEmpty())) return none();
        else return new PreparedIterable<T>(elements);
    }



    /**
     * Create a {@link java.util.Comparator} from an {@link Fn}, typically to
     * sort using a {@link Comparable} property of a type.
     *
     * @param property the {@link Fn} which obtains the comparable property
     *
     * @return a {@link Comparator} using the value yielded from the given {@link Fn}.
     */
    public static <T, P extends Comparable<P>> EnhancedComparator<T> by(final Fn<T, P> property) {
        return new EnhancedComparatorImpl<>(new ByPropertyComparator<>(property));
    }


    /**
     * Convert a type with natural ordering, i.e. implements {@link Comparable}, to
     * an external {@link Comparator}. This is a little quirk to provide a typesafe
     * way to sort iterables containing <code>Comparables</code>, instead of risking
     * a runtime ClassCastException. Pass this to the
     * {@link no.motif.iter.CollectingIterable#sorted(Comparator) .sorted(..)}
     * method to get elements as a sorted list.
     *
     * @param comparableType The {@link Comparable} type to create a {@link Comparator} from.
     * @return the <code>Comparator</code>
     */
    public static <T extends Comparable<T>> EnhancedComparator<T> byOrderingOf(Class<T> comparableType) {
        return by(NOP.<T>fn()); }


    /**
     * Evaluates if an {@link Iterable} is empty.
     */
    public static final Predicate<Iterable<?>> empty = new Predicate<Iterable<?>>() {
        @Override public boolean $(Iterable<?> iterable) { return on(iterable).isEmpty(); }};


    /**
     * The {@link Iterator#hasNext()} method as a function.
     */
    public static final Predicate<Iterator<?>> hasNext = new Predicate<Iterator<?>>() {
        @Override public boolean $(Iterator<?> iterator) { return iterator.hasNext(); }};


    /**
     * Yields the {@link Collection#size() size} of collections, or <code>0</code> if
     * the collection is <code>null</code>.
     */
    public static final Fn<Collection<?>, Integer> size = when(notNull, new Fn<Collection<?>, Integer>() {
        @Override public Integer $(Collection<?> collection) { return collection.size(); }}).orElse(0);


    /**
     * @return The {@link Iterator#next()} as a function.
     */
    public static final <T> Fn<Iterator<T>, T> next() { return new Fn<Iterator<T>, T>() {
        @Override public T $(Iterator<T> iterator) { return iterator.next(); }};};



    /**
     * Using a function which takes an object and returns another object of the same type,
     * recursively call this function until it yields <code>null</code>, and ultimately yield the
     * last object.
     *
     * @param next The function which takes an object and yields another object of the same type.
     *             This is typically used for objects implementing a linked list-like structure,
     *             where each object refers to the next in a chain.
     *
     * @return A function which will descend the chain and yield the last object. If the given
     *         <code>next</code> {@link Fn} never returns <code>null</code>, calling this
     *         returned function will result in an infinite recursion.
     */
    public static <T> Fn<T, T> last(final Fn<? super T, ? extends T> next) {
        return new Fn<T, T>() {
        @Override public T $(T value) {
            return optional(next.$(value)).map(this).orElse(value);
        }};
    }


    /**
     * A bridge from functions yielding arrays, to functions yielding iterables.
     *
     * @param yieldsArray the array-yielding function.
     * @return an adapted function yielding an iterable of the array yielded from the original function.
     */
    public static <I, O> Fn<I, Iterable<O>> toIterable(final Fn<I, O[]> yieldsArray) {
        return new Fn<I, Iterable<O>>() { @Override public Iterable<O> $(I value) { return on(yieldsArray.$(value)); }}; }



    private Iterate() {}

}