com.github.jonross.seq4j.Seq.java Source code

Java tutorial

Introduction

Here is the source code for com.github.jonross.seq4j.Seq.java

Source

/*
 * Copyright (c) 2012, Jonathan Ross <jonross@alum.mit.edu>
 * 
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 * and/or sell copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included
 * in all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 * DEALINGS IN THE SOFTWARE.
 */

package com.github.jonross.seq4j;

import java.util.Arrays;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import com.google.common.base.Function;
import com.google.common.base.Joiner;
import com.google.common.base.Optional;
import com.google.common.base.Predicate;
import com.google.common.collect.AbstractIterator;
import com.google.common.collect.Iterators;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Multimap;
import com.google.common.collect.Multimaps;
import com.google.common.collect.UnmodifiableIterator;

import static com.github.jonross.seq4j.SeqMisc.*;

/**
 * A fluent iteration API for Google Guava, a <code>Seq</code> extends
 * {@link UnmodifiableIterator} with chainable behaviors.
 * <p>
 * Static methods on this class return sequences that wrap individual values, arrays,
 * collections, other iterables and iterators.
 * <p>
 * NOTE: As of this writing, the API is highly subject to change.
 * <p>
 * Seq4J was inspired by Python, Scala, Guava itself, and most of all, by the ongoing
 * lack of terseness and fluency in Java.
 * 
 * @author Jonathan Ross
 * @version 0.1.0
 */

public abstract class Seq<T> extends UnmodifiableIterator<T> {
    //  factory methods  `==========================================================================

    /**
     * Create a {@link Seq} wrapping an immutable iterator for any {@link Iterable}.
     * A null argument is acceptable and is the same as creating a sequence around an
     * empty list. 
     */

    public static <T> Seq<T> seq(Iterable<T> iterable) {
        if (iterable != null) {
            return new IteratorSeq<T>(iterable.iterator());
        } else {
            return new IteratorSeq<T>(Collections.<T>emptyList().iterator());
        }
    }

    /**
     * Create a {@link Seq} wrapping an immutable iterator for the specified objects.
     */

    public static <T> Seq<T> seq(T... objects) {
        return seq(Arrays.asList(objects));
    }

    /**
     * Create a {@link Seq} wrapping an {@link Iterator}.
     */

    public static <T> Seq<T> seq(Iterator<T> iterator) {
        return new IteratorSeq<T>(iterator);
    }

    //  instance methods  ==========================================================================

    /**
     * Wraps {@link Iterators#all(Iterator, Predicate); returns true if every element in the
     * sequence satisfies the given predicate.
     */

    boolean all(Predicate<? super T> p) {
        return Iterators.all(this, p);
    }

    /**
     * Wraps {@link Iterators#any(Iterator, Predicate); returns true if any element in the
     * sequence satisfies the given predicate.
     */

    boolean any(Predicate<? super T> p) {
        return Iterators.any(this, p);
    }

    /**
     * Wraps {@link Iterators#concat(Iterator)}; combines this sequence and another iterator
     * into one sequence.
     */

    Seq<T> concat(Iterator<? extends T> iter) {
        return seq(Iterators.concat(this, iter));
    }

    /**
     * Wraps {@link Iterators#skip(Iterator, int)}; skips the first <code>count</code>
     * items that are available from this iterator.  Note this is not a lazy sequence; the
     * iterator is immediately advanced <code>count</code> items.
     * 
     * @param count The number of elements to skip.
     * @return A new <code>Seq</code> for which the limit applies.
     */

    public Seq<T> drop(int count) {
        Iterators.skip(this, count);
        return this;
    }

    /**
     * Like {@link #drop(int)}, but returns a sequence that skips leading elements
     * as long as a predicate returns true.
     */

    public Seq<T> dropWhile(final Predicate<T> p) {
        return seq(new AbstractIterator<T>() {
            private boolean done = false;

            protected T computeNext() {
                if (!done) {
                    while (Seq.this.hasNext()) {
                        T t = Seq.this.next();
                        if (!p.apply(t)) {
                            done = true;
                            return t;
                        }
                    }
                }
                if (Seq.this.hasNext()) {
                    return Seq.this.next();
                } else {
                    return endOfData();
                }
            }
        });
    }

    /**
     * Make the sequence usable as the <code>{@link Iterable} in a for loop.  NOTE: this
     * does not return a general-purpose <code>Iterable</code>; the sequence will be consumed
     * in the loop.  (It's for this reason that <code>Seq</code> does not implement
     * <code>Iterable</code> directly.)
     */

    public Iterable<T> each() {
        return new Iterable<T>() {
            public Iterator<T> iterator() {
                return Seq.this;
            }
        };
    }

    /**
     * Wraps {@link Iterators#filter(Iterator, Predicate)}; returns a new sequence
     * that provides the elements of the given sequence that satisfy a predicate.
     */

    public Seq<T> filter(final Predicate<? super T> p) {
        return seq(Iterators.filter(this, p));
    }

    /**
     * Uses {@link Iterators#find}; returns the first element in the sequence that
     * satisfies a predicate, as an {@link Optional<T>}, which contains either the
     * found element or <code>null</code> if not fo und.
     */

    public Optional<T> find(Predicate<? super T> p) {
        return Optional.fromNullable(Iterators.find(this, p, null));
    }

    /**
     * Invokes a function on each element of the sequence, ignoring the return value;
     * returns the number of elements that were present.
     */

    public int foreach(final Function<? super T, ?> f) {
        int count = 0;
        while (hasNext()) {
            f.apply(next());
            ++count;
        }
        return count;
    }

    /**
     * Wraps {@link Multimaps#index(Iterator, Function); treating each element in the
     * sequence as a value, applies a function t determine the key and uses the
     * multimap to group all values with the same key.
     */

    public <K> Multimap<K, T> groupBy(Function<? super T, K> f) {
        return Multimaps.index(this, f);
    }

    /**
     * Return a string that is the result of concatenating the string representation
     * of each element in the sequence, using the given separator.  Nulls will be
     * represented by <code>"null"</code>.
     */

    public String join(String separator) {
        return join(Joiner.on(separator));
    }

    /**
     * Return the result of applying a {@link Joiner} to the elements in the sequence.
     */

    public String join(Joiner joiner) {
        return joiner.join(this.each());
    }

    /**
     * Wraps {@link Iterators#transform(Iterator, Function)}; returns a sequence that
     * applies a function to each element of this sequence as they are retrieved.
     */

    public <R> Seq<R> map(final Function<? super T, ? extends R> function) {
        return seq(Iterators.transform(this, function));
    }

    /**
     * Return a <code>List</code> of two <code>Lists</code> generated by applying
     * a predicate to each element; the first list contains those for which it
     * returns true, the second, false.
     */

    public List<List<T>> split(Predicate<? super T> p) {
        final List<T> a = Lists.newArrayList(), b = Lists.newArrayList();
        for (T elt : each()) {
            (p.apply(elt) ? a : b).add(elt);
        }
        List<List<T>> result = Lists.newArrayList();
        result.add(a);
        result.add(b);
        return result;
    }

    /**
     * Wraps {@link Iterators#limit(Iterator, int)}; limits the number of items
     * that are available from this iterator.
     * 
     * @param count The number of elements after which <code>hasNext</code> will return false.
     * @return A new <code>Seq</code> for which the limit applies.
     */

    public Seq<T> take(int count) {
        return seq(Iterators.limit(this, count));
    }

    /**
     * Like {@link #take(int)} but returns a sequence that provides elements as long
     * as a predicate returns true.
     */

    public Seq<T> takeWhile(final Predicate<T> p) {
        return seq(new AbstractIterator<T>() {
            protected T computeNext() {
                if (!Seq.this.hasNext()) {
                    return endOfData();
                }
                T t = Seq.this.next();
                return p.apply(t) ? t : endOfData();
            }
        });
    }

    /**
     * Wraps {@link Iterators#toArray(Iterator, Class); returns an array of the given
     * type containing the elements of the sequence.
     */

    public T[] toArray(Class<T> klass) {
        return Iterators.toArray(this, klass);
    }

    /**
     * Returns a new <code>List</code> containing the remaining values in the sequence.
     * This is a shortcut for
     * <pre>
     * List<T> = Lists.newArrayList();
     * theSeq.foreach(addTo(list));
     * </pre>
     */

    public List<T> toList() {
        List<T> list = Lists.newArrayList();
        foreach(addTo(list));
        return list;
    }

    /**
     * Returns a map whose values are the sequence items and whose keys are the result
     * of applying <code>keyMaker</code> to each value.
     * This is a shortcut for
     * <pre>
     * Map<K,T> = Maps.newHashMap();
     * theSeq.foreach(addTo(map, keyMaker));
     * </pre>
     */

    public <K> Map<K, T> toMap(Function<T, K> keyMaker) {
        Map<K, T> map = Maps.newHashMap();
        foreach(addTo(map, keyMaker));
        return map;
    }
}