PreparedIterable.java

package no.motif.iter;

import static no.motif.Base.not;
import static no.motif.Iterate.none;
import static no.motif.Iterate.on;
import static no.motif.Singular.optional;

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

import no.motif.f.Fn;
import no.motif.f.Predicate;
import no.motif.single.Elem;
import no.motif.types.Elements;

/**
 * This class wraps an arbitrary {@link Iterable} and provides access to
 * various operations to view and/or transform the wrapped iterable.
 *
 * @param <T> The type of the elements in the iterable.
 */
public class PreparedIterable<T> extends CollectingIterable<T> implements Elements<T>, Serializable {

    private final Iterable<T> elements;

    public PreparedIterable(Iterable<T> elements) {
        this.elements = elements;
    }


    @Override
    public Elements<Elem<T>> indexed() {
        return indexedFrom(0);
    }

    @Override
    public Elements<Elem<T>> indexedFrom(int startIndex) {
        return new PreparedIterable<>(new IndexedIterable<>(startIndex, elements));
    }


    @Override
    public PreparedIterable<T> filter(Predicate<? super T> filter) {
        return new PreparedIterable<>(new FilteredIterable<>(elements, filter));
    }


    @Override
    public <O> PreparedIterable<O> map(Fn<? super T, O> fn) {
        return new PreparedIterable<>(new MappingIterable<>(elements, fn));
    }

    @Override
    public <O> Elements<O> flatMap(Fn<? super T, ? extends Iterable<O>> fn) {
        return new PreparedIterable<>(new FlatMappingIterable<>(elements, fn));
    }


    @Override
    public PreparedIterable<T> append(T value) {
        return append(optional(value));
    }


    @Override
    public PreparedIterable<T> append(Iterable<? extends T> trailingElements) {
        return new PreparedIterable<>(new ConcatenatedIterable<>(elements, trailingElements));
    }


    @Override
    public PreparedIterable<T> prepend(T value) {
        return prepend(optional(value));
    }


    @Override
    public PreparedIterable<T> prepend(Iterable<? extends T> leadingElements) {
        return new PreparedIterable<>(new ConcatenatedIterable<>(leadingElements, elements));
    }


    @Override
    public PreparedIterable<T> take(int amount) {
        return new PreparedIterable<>(new BoundedIterable<>(amount, elements));
    }


    @Override
    public PreparedIterable<T> takeWhile(Predicate<? super T> predicate) {
        return new PreparedIterable<>(new PredicateBoundedIterable<>(predicate, elements));
    }


    @Override
    public PreparedIterable<T> takeUntil(Predicate<? super T> predicate) {
        return new PreparedIterable<>(new PredicateBoundedIterable<>(not(predicate), elements));
    }


    @Override
    public PreparedIterable<T> tail() {
        if (isEmpty()) throw new NoSuchElementException();
        else return new PreparedIterable<>(new SkipLeadingIterable<>(1, elements));
    }



    @Override
    public Elements<T> repeat(int times) {
        if (isEmpty() || times == 1) return this;
        else if (times == 0) return none();
        else if (times < 0) throw new IllegalArgumentException("Can not repeat anything " + times + " times");
        else return new PreparedIterable<>(new CyclingIterable<T>(times, elements));
    }


    @Override
    public Elements<T> eval() {
        return on(collect());
    }

    @Override
    public Iterator<T> iterator() {
        return elements.iterator();
    }



}