net.sf.staccatocommons.collections.stream.AbstractStream Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of commons-collections Show documentation
Show all versions of commons-collections Show documentation
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);
}
}