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

net.sf.staccatocommons.collections.stream.AbstractStream Maven / Gradle / Ivy

/**
 *  Copyright (c) 2010-2012, The StaccatoCommons Team
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU Lesser General Public License as published by
 *  the Free Software Foundation; version 3 of the License.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU Lesser General Public License for more details.
 */

package net.sf.staccatocommons.collections.stream;

import static net.sf.staccatocommons.collections.iterable.internal.IterablesInternal.*;
import static net.sf.staccatocommons.collections.stream.Streams.*;
import static net.sf.staccatocommons.lang.tuple.Tuples.*;

import java.io.IOException;
import java.lang.reflect.Array;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.NoSuchElementException;
import java.util.Set;

import net.sf.staccatocommons.check.Ensure;
import net.sf.staccatocommons.collections.internal.iterator.ConcatIterator;
import net.sf.staccatocommons.collections.internal.iterator.DropIterator;
import net.sf.staccatocommons.collections.internal.iterator.FilterIndexIterator;
import net.sf.staccatocommons.collections.internal.iterator.FilterIterator;
import net.sf.staccatocommons.collections.internal.iterator.FlatMapIterator;
import net.sf.staccatocommons.collections.internal.iterator.IndicesIterator;
import net.sf.staccatocommons.collections.internal.iterator.InsertBeforeIndexIterator;
import net.sf.staccatocommons.collections.internal.iterator.InsertBeforeIterator;
import net.sf.staccatocommons.collections.internal.iterator.TakeIterator;
import net.sf.staccatocommons.collections.internal.iterator.TakeWhileIterator;
import net.sf.staccatocommons.collections.internal.iterator.ZipIterator;
import net.sf.staccatocommons.collections.iterable.Iterables;
import net.sf.staccatocommons.collections.iterable.internal.IterablesInternal;
import net.sf.staccatocommons.collections.stream.internal.IteratorStream;
import net.sf.staccatocommons.collections.stream.internal.ListStream;
import net.sf.staccatocommons.collections.stream.internal.NonEmptyIteratorStream;
import net.sf.staccatocommons.collections.stream.internal.algorithms.DeconsTransformStream;
import net.sf.staccatocommons.collections.stream.internal.algorithms.DropWhileStream;
import net.sf.staccatocommons.collections.stream.internal.algorithms.MapStream;
import net.sf.staccatocommons.collections.stream.internal.algorithms.MemoizedStream;
import net.sf.staccatocommons.collections.stream.internal.algorithms.PrependStream;
import net.sf.staccatocommons.collections.stream.internal.algorithms.SizeLimitedStream;
import net.sf.staccatocommons.collections.stream.internal.algorithms.SortedStream;
import net.sf.staccatocommons.collections.stream.internal.algorithms.TransformStream;
import net.sf.staccatocommons.collections.stream.internal.algorithms.delayed.DelayedDeconsTransformStream;
import net.sf.staccatocommons.collections.stream.internal.algorithms.delayed.DelayedPrependStream;
import net.sf.staccatocommons.defs.Applicable;
import net.sf.staccatocommons.defs.Applicable2;
import net.sf.staccatocommons.defs.Evaluable;
import net.sf.staccatocommons.defs.Evaluable2;
import net.sf.staccatocommons.defs.Executable;
import net.sf.staccatocommons.defs.Thunk;
import net.sf.staccatocommons.defs.function.Function;
import net.sf.staccatocommons.defs.function.Function2;
import net.sf.staccatocommons.defs.partial.EmptyAware;
import net.sf.staccatocommons.defs.predicate.Predicate2;
import net.sf.staccatocommons.defs.reduction.Accumulator;
import net.sf.staccatocommons.defs.reduction.Reduction;
import net.sf.staccatocommons.defs.tuple.Tuple2;
import net.sf.staccatocommons.defs.type.NumberType;
import net.sf.staccatocommons.iterators.AppendThriterator;
import net.sf.staccatocommons.iterators.delayed.DelayedAppendIterator;
import net.sf.staccatocommons.iterators.thriter.Thriter;
import net.sf.staccatocommons.iterators.thriter.Thriterator;
import net.sf.staccatocommons.iterators.thriter.Thriterators;
import net.sf.staccatocommons.lang.Compare;
import net.sf.staccatocommons.lang.Option;
import net.sf.staccatocommons.lang.function.AbstractFunction;
import net.sf.staccatocommons.lang.function.AbstractFunction2;
import net.sf.staccatocommons.lang.function.Functions;
import net.sf.staccatocommons.lang.internal.ToString;
import net.sf.staccatocommons.lang.predicate.AbstractPredicate2;
import net.sf.staccatocommons.lang.predicate.Equiv;
import net.sf.staccatocommons.lang.predicate.Predicates;
import net.sf.staccatocommons.lang.tuple.Tuples;
import net.sf.staccatocommons.restrictions.Constant;
import net.sf.staccatocommons.restrictions.check.NonNull;
import net.sf.staccatocommons.restrictions.check.NotNegative;

import org.apache.commons.lang.StringUtils;

/**
 * An abstract implementation of a {@link Stream}. Only it {@link Iterator}
 * method is abstract.
 * 
 * Implementors must grant that the following methods remain consistent:
 * 
    *
  • {@link #first()} and {@link #get(int)}
  • *
  • {@link #iterator()} and {@link #isEmpty()}
  • *
* * @author flbulgarelli * * @param */ public abstract class AbstractStream extends AbstractBasicStream { @Override public int size() { int size = 0; Thriter iter = this.iterator(); while (iter.hasNext()) { iter.advanceNext(); size++; } return size; } public int countOf(Evaluable predicate) { return Iterables.countOf(this, predicate); } @Override public boolean isEmpty() { return iterator().isEmpty(); } public void forEach(Executable block) { for (A element : this) block.exec(element); } public boolean contains(A element) { return IterablesInternal.containsInternal(this, element); } @Override public Stream filter(final Evaluable predicate) { return Streams.from(new FilterIterator(iterator(), predicate)); } @Override public Stream takeWhile(final Evaluable predicate) { return Streams.from(new TakeWhileIterator(iterator(), predicate)); } @Override public Stream take(@NotNegative final int amountOfElements) { return new SizeLimitedStream(new TakeIterator(iterator(), amountOfElements), amountOfElements); } @Override public Stream dropWhile(Evaluable predicate) { return new DropWhileStream(this, predicate); } @Override public Stream drop(@NotNegative int amountOfElements) { return Streams.from(new DropIterator(amountOfElements, iterator())); } @Override public Stream slice(@NotNegative int lowerBound, @NotNegative int upperBound) { return drop(lowerBound).take(upperBound - lowerBound); } @Override public A reduce(Applicable2 function) { return Iterables.reduce(this, function); } @Override public B reduce(Reduction reduction) throws NoSuchElementException { return Iterables.reduce(this, reduction); } @Override public O fold(O initial, Applicable2 function) { return Iterables.fold(this, initial, function); } @Override public Option anyOrNone() { return Iterables.anyOrNone(this); } @Override public A anyOrNull() { return anyOrNone().valueOrNull(); } @Override public A anyOrElse(Thunk thunk) { return anyOrNone().valueOrElse(thunk); } @Override public A anyOrElse(A value) { return anyOrNone().valueOrElse(value); } @Override public A find(Evaluable predicate) { return Iterables.find(this, predicate); } @Override public Option findOrNone(Evaluable predicate) { return Iterables.findOrNone(this, predicate); } @Override public A findOrNull(Evaluable predicate) { return findOrNone(predicate).valueOrNull(); } @Override public A findOrElse(Evaluable predicate, Thunk thunk) { return findOrNone(predicate).valueOrElse(thunk); } @Override public A findOrElse(Evaluable predicate, A element) { return findOrNone(predicate).valueOrElse(element); } @Override public boolean all(Evaluable predicate) { return Iterables.all(this, predicate); } @Override public boolean allEquiv() { return Iterables.allEqual(this); } @Override public boolean allEquivBy(Evaluable2 equivTest) { return Iterables.allEquivBy(this, equivTest); } @Override public boolean any(Evaluable predicate) { return Iterables.any(this, predicate); } public Stream> clone(Applicable function) { return map(Tuples.clone(function)); } public Stream> branch(Applicable function0, Applicable function1) { return map(Tuples.branch(function0, function1)); } @Override public Stream map(final Function function) { return new MapStream(this, function); } @Override public Stream flatMap(final Function> function) { return Streams.from(new FlatMapIterator(iterator(), function)); } public Stream flatMapArray(@NonNull Function function) { return flatMap(AbstractStream. toIterable().of(function)); } public Stream appendUndefined() { return append(Streams. undefined()); } @Override public A head() { Iterator iterator = iterator(); checkNotEmpty(iterator); return iterator.next(); } @Override public A last() { Thriterator iter = iterator(); checkNotEmpty(iter); while (iter.hasNext()) iter.advanceNext(); return iter.current(); } @Override public A get(int n) { try { Thriterator iter = this.iterator(); for (int i = 0; i <= n; i++) iter.advanceNext(); return iter.current(); } catch (NoSuchElementException e) { throw new IndexOutOfBoundsException("At " + n); } } public Stream filterIndex(Evaluable predicate) { return Streams.from(new FilterIndexIterator(iterator(), predicate)); } public Stream skipIndex(int index) { return filterIndex(Predicates.equal(index).not()); } @Override public int indexOf(A element) { return Iterables.indexOf(this, element); } public int findIndex(Evaluable predicate) { int i = 0; for (A element : this) { if (predicate.eval(element)) return i; i++; } return -1; } public Stream indices(Evaluable predicate) { return new IteratorStream(new IndicesIterator(iterator(), predicate)); } @Override public boolean isBefore(A previous, A next) { return Iterables.isBefore(this, previous, next); } @Override public Set toSet() { return Iterables.toSet(this); } @Override public List toList() { return Iterables.toList(this); } // public List toReverseList() { // return reverse().toList(); // } @Override public Stream force() { return new ListStream(toList()) { @Override public List toList() { return Collections.unmodifiableList(getList()); } }; } @Override public A[] toArray(Class clazz) { return toArray(clazz, toList()); } protected A[] toArray(Class clazz, Collection readOnlyColView) { return readOnlyColView.toArray((A[]) Array.newInstance(clazz, readOnlyColView.size())); } @Override public String joinStrings(@NonNull String separator) { return StringUtils.join(iterator(), separator); } @Override public Tuple2, List> partition(Evaluable predicate) { return Iterables.partition(this, predicate); } @Override public final Tuple2, Stream> streamPartition(Evaluable predicate) { Tuple2, List> partition = partition(predicate); return _(Streams.from(partition._0()), Streams.from(partition._1())); } @Override public boolean equiv(Iterable other) { return equivBy(equalOrEquiv(), other); } @Constant private static Predicate2 equalOrEquiv() { return Equiv. equalNullSafe().or(new AbstractPredicate2() { public boolean eval(A arg0, A arg1) { return arg0 instanceof Stream && arg1 instanceof Stream && ((Stream) arg0).equiv((Stream) arg1); } }); } @Override public boolean equivBy(Evaluable2 equalty, Iterable other) { return Iterables.equivBy(this, other, equalty); } @Override public Stream transform(final Applicable, ? extends Stream> function) { return new TransformStream(this, function); } /** *
   * intersperse _   []      = []
   * intersperse _   [x]     = [x]
   * intersperse sep (x:xs)  = x : sep : intersperse sep xs
   * 
* */ @Override public Stream
intersperse(final A sep) { return transform(new AbstractDelayedDeconsApplicable() { public Stream apply(Thunk head, Stream tail) { if (tail.isEmpty()) return cons(head); return cons(head, cons(sep, tail.intersperse(sep))); } }); } public Stream incorporate(final Function function) { return flatMap(new AbstractFunction>() { public Iterable apply(A arg) { return cons(arg, function.apply(arg)); } }); } public Stream incorporate(A element) { return incorporate(Functions.constant(element)); } @Override public Stream append(A element) { return new NonEmptyIteratorStream(new AppendThriterator(this.iterator(), element)); } @Override public Stream prepend(A element) { return new PrependStream(element, this); } @Override public Stream transform(final DeconsApplicable function) { return new DeconsTransformStream(this, function); } @Override public Stream transform(final DelayedDeconsApplicable function) { return new DelayedDeconsTransformStream(this, function); } @Override public Tuple2> decons() { Iterator iter = iterator(); checkNotEmpty(iter); return _(iter.next(), Streams.from(iter)); } @Override public Tuple2, Stream> delayedDecons() { Thriterator iter = iterator(); checkNotEmpty(iter); return _(iter.delayedNext(), Streams.from(iter)); } @Override public Stream tail() { checkNotEmpty(this); return drop(1); } @Override public A sum(NumberType numberType) { return Iterables.sum(this, numberType); } @Override public A product(NumberType numberType) { return Iterables.product(this, numberType); } @Override public A average(final NumberType numberType) { class Ref { A val = numberType.zero(); } final Ref size = new Ref(); return numberType.divide(fold(numberType.zero(), new AbstractFunction2() { public A apply(A arg0, A arg1) { size.val = numberType.increment(size.val); return numberType.add(arg0, arg1); } }), size.val); } @Override public A maximum() { return maximumBy(natural()); } @Override public A minimum() { return minimumBy(natural()); } @Override public A maximumBy(Comparator comparator) { return reduceWithComparator(comparator, 1); } private A reduceWithComparator(Comparator comparator, int sign) { Iterator iter = this.iterator(); checkNotEmpty(iter); A result = iter.next(); while (iter.hasNext()) { A next = iter.next(); result = comparator.compare(result, next) * sign >= 0 ? result : next; } return result; } @Override public A minimumBy(Comparator comparator) { return reduceWithComparator(comparator, -1); } public Stream sort() { return sortBy(natural()); } public Stream sortBy(Comparator comparator) { return new SortedStream(this, comparator); } private Comparator natural() { return (Comparator) Compare. natural(); } public Stream reverse() { if (this.isEmpty()) return Streams.empty(); LinkedList reversedList = new LinkedList(); for (A element : this) reversedList.addFirst(element); return Streams.from((List) reversedList); } public Map groupOn(Applicable groupFunction, Reduction reduction) { Map> map = new LinkedHashMap>(); for (A element : this) { K key = groupFunction.apply(element); Accumulator accum = map.get(key); if (accum == null) { accum = reduction.newAccumulator(); map.put(key, accum); } accum.accumulate(element); } for (Entry> e : map.entrySet()) ((Entry) e).setValue(e.getValue().value()); return (Map) map; } public Stream> cross() { return cross(this); } public final Stream> cross(B... elements) { return cross(Streams.from(elements)); } public final Stream> cross(Iterator other) { return cross(Streams.from(other)); } public Stream> cross(@NonNull Iterable other) { return cross(Streams.from(other)); } // this >>= (\x -> other >>= (\y -> return (x,y))) public Stream> cross(@NonNull final Stream other) { return transform(new AbstractFunction, Stream>>() { public Stream> apply(Stream stram) { return flatMap(new AbstractFunction>>() { public Stream> apply(final A x) { return other.flatMap(new AbstractFunction>>() { public Stream> apply(B y) { return cons(_(x, y)); } }); } }); } }); } public Stream> crossStreams(@NonNull Stream> other) { Ensure.that().isNotEmpty("other", (EmptyAware) other); return fcross(other.prepend(this)); } // fcross [xs,ys] = xs >>= \x -> ys >>= \y -> return [x,y] // fcross (xs:xss) = xs >>= \x -> (fcross xss) >>= \ys -> return (x:ys) private static Stream> fcross(Stream> other) { return other.transform(new AbstractFunction>, Stream>>() { public Stream> apply(Stream> xss_) { final Stream> xss = xss_.memoize(); if (xss.size() == 2) return xss.first().flatMap(new AbstractFunction>>() { public Stream> apply(final A x) { return xss.second().flatMap(new AbstractFunction>>() { public Stream> apply(A y) { return cons(cons(x, y)); } }); } }); return xss.head().flatMap(new AbstractFunction>>() { public Stream> apply(final A x) { return fcross(xss.tail()).flatMap(new AbstractFunction, Stream>>() { public Stream> apply(Stream ys) { return cons(cons(x, ys)); } }); } }); } }); } @Override public final void print(java.lang.Appendable o) throws IOException { o.append('['); if (!isEmpty()) { Tuple2> ht = decons(); printElement(o, ht._0()); for (A element : ht._1()) { o.append(", "); printElement(o, element); } } o.append(']'); } private void printElement(java.lang.Appendable o, A element) throws IOException { if (element instanceof Stream) ((Stream) element).print(o); else o.append(String.valueOf(element)); } @Override public final void print() { try { println(System.out); } catch (IOException e) { throw new AssertionError(e); } } @Override public final void println(java.lang.Appendable o) throws IOException { print(o); o.append('\n'); } @Override public final String printString() { try { StringBuilder sb = new StringBuilder(); print(sb); return sb.toString(); } catch (IOException e) { throw new AssertionError(e); } } @Constant private static Function> toIterable() { return new AbstractFunction>() { public Iterable apply(A[] arg) { return Arrays.asList(arg); } }; } public final String toString() { return ToString.toString(this); } public final Stream concat(Iterable other) { return concat(other.iterator()); } public final Stream concat(A... elements) { return concat(Thriterators.from(elements)); } public Stream concat(Iterator other) { return Streams.from(new ConcatIterator(this.iterator(), other)); } public Stream delayedAppend(Thunk thunk) { return new NonEmptyIteratorStream(new DelayedAppendIterator(iterator(), thunk)); } public Stream delayedPrepend(Thunk thunk) { return new DelayedPrependStream(thunk, this); } public final Stream zipWith(Function2 function, B... elements) { return zipWith(function, Thriterators.from(elements)); } public final Stream zipWith(Function2 function, Iterable other) { return zipWith(function, other.iterator()); } public Stream zipWith(Function2 function, Iterator other) { return Streams.from(new ZipIterator(iterator(), Thriterators.from(other), function)); } public Stream memoize() { return new MemoizedStream(iterator()); } /* //TODO public Stream memoize(int numberOfElements) { return memoize(); } @Override public Tuple2, Stream> splitBeforeIndex(@NotNegative int position) { Stream stream = this.memoize(position); //TODO improve performance return _(stream.take(position), stream.drop(position)); } public Tuple2, Stream> splitBefore(A element) { Stream stream = this.memoize(); //TODO improve performance Predicate notEq = Predicates.equal(element).not(); return _(stream.takeWhile(notEq), stream.dropWhile(notEq)); } */ @Override public Stream insertBeforeIndex(A element, @NotNegative int position) { return Streams.from(new InsertBeforeIndexIterator(position, element, iterator())); } public Stream insertBefore(A element, A reference) { return Streams.from(new InsertBeforeIterator(reference, element, iterator())); } public boolean containsBeforeIndex(A element, @NotNegative int index) { return Iterables.containsBeforeIndex(this, element, index); } public boolean containsBefore(A element, A reference) { return Iterables.containsBefore(this, element, reference); } public boolean intersects(Collection other) { return any(Predicates.in(other)); } }