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

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

Go to download

Collections library of the Staccato-Commons project, focused on providing new abstractions that mix object oriented and functional programming style for dealing with iterable objects.

The newest version!
/**
 *  Copyright (c) 2011, The Staccato-Commons 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.stream.Streams.*;
import static net.sf.staccatocommons.lang.Compare.*;
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.check.Validate;
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.TakeWhileIterator;
import net.sf.staccatocommons.collections.iterable.Iterables;
import net.sf.staccatocommons.collections.iterable.internal.IterablesInternal;
import net.sf.staccatocommons.collections.stream.internal.ListStream;
import net.sf.staccatocommons.collections.stream.internal.algorithms.AppendIterableStream;
import net.sf.staccatocommons.collections.stream.internal.algorithms.AppendStream;
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.FlatMapStream;
import net.sf.staccatocommons.collections.stream.internal.algorithms.MapStream;
import net.sf.staccatocommons.collections.stream.internal.algorithms.MemorizedStream;
import net.sf.staccatocommons.collections.stream.internal.algorithms.PrependStream;
import net.sf.staccatocommons.collections.stream.internal.algorithms.SortedStream;
import net.sf.staccatocommons.collections.stream.internal.algorithms.TakeStream;
import net.sf.staccatocommons.collections.stream.internal.algorithms.TransformStream;
import net.sf.staccatocommons.collections.stream.internal.algorithms.ZipStream;
import net.sf.staccatocommons.collections.stream.internal.algorithms.delayed.DelayedAppendStream;
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.thriter.Thriter;
import net.sf.staccatocommons.iterators.thriter.Thriterator;
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
 * 
 * @author flbulgarelli
 * 
 * @param 
 */
public abstract class AbstractStream implements Stream {

  protected static final Validate VALIDATE_ELEMENT = Validate
    .throwing(NoSuchElementException.class);

  @Override
  public int size() {
    int size = 0;
    Thriter iter = this.iterator();
    while (iter.hasNext()) {
      iter.advanceNext();
      size++;
    }
    return size;
  }

  @Override
  public boolean isEmpty() {
    return iterator().isEmpty();
  }

  public void forEach(Executable block) {
    for (A element : this)
      block.exec(element);
  }

  public Stream each(final Executable block) {
    return map(Functions.impure(block));
  }

  public boolean contains(A element) {
    return IterablesInternal.containsInternal(this, element);
  }

  @Override
  public Stream filter(final Evaluable predicate) {
    return Streams.from(new FilterIterator(iterator(), predicate));
  }

  public Stream skip(A element) {
    return filter(Predicates.equal(element).not());
  }

  @Override
  public Stream takeWhile(final Evaluable predicate) {
    return Streams.from(new TakeWhileIterator(iterator(), predicate));
  }

  @Override
  public Stream take(@NotNegative final int amountOfElements) {
    return new TakeStream(iterator(), 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 A reduce(Applicable2 function) {
    try {
      return Iterables.reduce(this, function);
    } catch (IllegalArgumentException e) { // FIXME why illegal argument ???
      return VALIDATE_ELEMENT.fail("Can not reduce an empty stream");
    }
  }

  @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 A any() {
    return Iterables.any(this);
  }

  @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 map(Applicable function) {
    return map(Functions.from(function));
  }

  @Override
  public  Stream flatMap(final Function> function) {
    return new FlatMapStream(this, function);
  }

  public  Stream flatMapArray(@NonNull Function function) {
    return flatMap(AbstractStream. toIterable().of(function));
  }

  @Override
  public Stream append(final Iterable other) {
    return new AppendIterableStream(this, other);
  }

  public Stream appendUndefined() {
    return append(Streams. undefined());
  }

  @Override
  public A first() {
    return get(0);
  }

  @Override
  public A second() {
    return get(1);
  }

  @Override
  public A third() {
    return get(2);
  }

  @Override
  public A last() {
    Thriterator iter = iterator();
    VALIDATE_ELEMENT.that(iter.hasNext(), "Empty streams have no elements");
    while (iter.hasNext())
      iter.advanceNext();
    return iter.current();
  }

  @Override
  public A get(int n) {
    Thriterator iter = this.iterator();
    for (int i = 0; i <= n; i++)
      try {
        iter.advanceNext();
      } catch (NoSuchElementException e) {
        throw new IndexOutOfBoundsException("At " + n);
      }
    return iter.current();
  }

  public final Stream filterIndex(Evaluable predicate) {
    return Streams.from(new FilterIndexIterator(iterator(), predicate));
  }

  public final Stream skipIndex(int index) {
    return filterIndex(Predicates.equal(index).not());
  }

  @Override
  public int indexOf(A element) {
    return Iterables.indexOf(this, element);
  }

  @Override
  public final int positionOf(A element) {
    int index = indexOf(element);
    if (index == -1)
      throw new NoSuchElementException(element.toString());
    return index;
  }

  @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);
  }

  @Override
  public Stream force() {
    return new ListStream(toList()) {
      @Override
      public List toList() {
        return Collections.unmodifiableList(getList());
      }
    };
  }

  @Override
  public Stream memorize() {
    return new MemorizedStream(iterator());
  }

  @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 final boolean equiv(A... elements) {
    return equiv(Arrays.asList(elements));
  }

  @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 final boolean equivBy(Evaluable2 equalityTest, A... elements) {
    return equivBy(equalityTest, Arrays.asList(elements));
  }

  @Override
  public final  boolean equivOn(Applicable function,
    Iterable iterable) {
    return equivBy(Equiv.on(function), iterable);
  }

  @Override
  public final  boolean equivOn(Applicable function, A... elements) {
    return equivOn(function, Arrays.asList(elements));
  }

  @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 AppendStream(this, element); } @Override public Stream append(Thunk element) { return new DelayedAppendStream(this, element); } @Override public Stream prepend(A element) { return new PrependStream(element, this); } @Override public Stream prepend(Thunk element) { return new DelayedPrependStream(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 Stream> zip(Iterable iterable) { return zip(iterable, Tuples. toTuple2()); } @Override public Tuple2> decons() { Iterator iter = iterator(); VALIDATE_ELEMENT.that(iter.hasNext(), "Empty streams can not be deconstructed"); return _(iter.next(), Streams.from(iter)); } @Override public Tuple2, Stream> delayedDecons() { Thriterator iter = iterator(); VALIDATE_ELEMENT.that(iter.hasNext(), "Empty streams can not be deconstructed"); return _(iter.delayedNext(), Streams.from(iter)); } @Override public Stream tail() { VALIDATE_ELEMENT.that(!isEmpty(), "Empty streams have not tail"); return drop(1); } @Override public A head() { try { return first(); } catch (IndexOutOfBoundsException e) { return VALIDATE_ELEMENT.fail("Empty streams have no head"); } } public Stream zip(@NonNull final Iterable iterable, @NonNull final Function2 function) { return new ZipStream(this, iterable, function); } @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 reduce(max(comparator)); } @Override public A minimumBy(Comparator comparator) { return reduce(min(comparator)); } @Override public final > A maximumOn(Applicable function) throws NoSuchElementException { return maximumBy(Compare.on(function)); } @Override public final > A minimumOn(Applicable function) throws NoSuchElementException { return minimumBy(Compare.on(function)); } public Stream sort() { return sortBy(natural()); } public Stream sortBy(Comparator comparator) { return new SortedStream(this, comparator); } public final > Stream sortOn(Applicable function) { return sortBy(Compare.on(function)); } 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 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_.memorize(); 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('['); 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 void println() { try { println(System.out); } catch (IOException e) { throw new AssertionError(e); } } @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); } }