functionalj.stream.StreamPlus Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of functionalj-core Show documentation
Show all versions of functionalj-core Show documentation
The module for FunctionalJ Core.
// ============================================================================
// Copyright (c) 2017-2019 Nawapunth Manusitthipol (NawaMan - http://nawaman.net).
// ----------------------------------------------------------------------------
// MIT License
//
// 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 functionalj.stream;
import static functionalj.function.Func.f;
import static functionalj.function.Func.themAll;
import static functionalj.functions.ObjFuncs.notEqual;
import static functionalj.stream.ZipWithOption.AllowUnpaired;
import static java.lang.Boolean.TRUE;
import java.io.ByteArrayOutputStream;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Comparator;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.NoSuchElementException;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
import java.util.Spliterator;
import java.util.Spliterators;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.BiConsumer;
import java.util.function.BiFunction;
import java.util.function.BinaryOperator;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.IntFunction;
import java.util.function.Predicate;
import java.util.function.Supplier;
import java.util.function.ToDoubleFunction;
import java.util.function.ToIntFunction;
import java.util.function.ToLongFunction;
import java.util.function.UnaryOperator;
import java.util.stream.Collector;
import java.util.stream.Collectors;
import java.util.stream.DoubleStream;
import java.util.stream.IntStream;
import java.util.stream.LongStream;
import java.util.stream.Stream;
import java.util.stream.StreamSupport;
import functionalj.function.Func;
import functionalj.function.Func1;
import functionalj.function.Func2;
import functionalj.function.Func3;
import functionalj.function.Func4;
import functionalj.function.Func5;
import functionalj.function.Func6;
import functionalj.function.FuncUnit1;
import functionalj.functions.StrFuncs;
import functionalj.functions.ThrowFuncs;
import functionalj.lens.core.WriteLens;
import functionalj.lens.lenses.AnyLens;
import functionalj.list.FuncList;
import functionalj.list.ImmutableList;
import functionalj.map.FuncMap;
import functionalj.map.ImmutableMap;
import functionalj.pipeable.Pipeable;
import functionalj.promise.DeferAction;
import functionalj.promise.UncompleteAction;
import functionalj.result.NoMoreResultException;
import functionalj.result.Result;
import functionalj.tuple.Tuple;
import functionalj.tuple.Tuple2;
import functionalj.tuple.Tuple3;
import functionalj.tuple.Tuple4;
import functionalj.tuple.Tuple5;
import functionalj.tuple.Tuple6;
import lombok.val;
class StreamPlusMapAddOnHelper {
@SafeVarargs
public static final StreamPlus mapFirst(
StreamPlus stream,
Function super D, T> ... mappers) {
return stream.map(f(d -> {
Exception exception = null;
boolean hasNull = false;
for(val mapper : mappers) {
try {
val res = mapper.apply(d);
if (res == null)
hasNull = true;
else return (T)res;
} catch (Exception e) {
if (exception == null)
exception = e;
}
}
if (hasNull)
return (T)null;
throw exception;
}));
}
}
// TODO - Intersect
@SuppressWarnings("javadoc")
@FunctionalInterface
public interface StreamPlus
extends Iterable, Stream, StreamPlusWithGet {
public static D noMoreElement() throws NoMoreResultException {
ThrowFuncs.doThrowFrom(()->new NoMoreResultException());
return (D)null;
}
@SuppressWarnings({ "unchecked", "rawtypes" })
public static StreamPlus from(Stream stream) {
if (stream == null)
return StreamPlus.empty();
return (stream instanceof StreamPlus)
? (StreamPlus)stream
: (StreamPlus)(()->stream);
}
public static StreamPlus from(Iterator iterator) {
return IteratorPlus.of(iterator).stream();
}
public static StreamPlus from(Enumeration enumeration) {
Iterable iterable = new Iterable() {
public Iterator iterator() {
return new Iterator() {
private D next;
@Override
public boolean hasNext() {
try {
next = enumeration.nextElement();
return true;
} catch (NoSuchElementException e) {
return false;
}
}
@Override
public D next() {
return next;
}
};
}
};
return StreamPlus.from(StreamSupport.stream(iterable.spliterator(), false));
}
@SafeVarargs
public static StreamPlus of(D ... data) {
return ArrayBackedStream.from(data);
}
@SafeVarargs
public static StreamPlus cycle(D ... data) {
return StreamPlus.from(IntStream.iterate(0, i -> i + 1).mapToObj(i -> data[i % data.length]));
}
public static StreamPlus loop(int time) {
return StreamPlus.infiniteInt().limit(time);
}
public static StreamPlus loop() {
return StreamPlus.infiniteInt();
}
public static StreamPlus infiniteInt() {
return IntStreamPlus.from(IntStream.iterate(0, i -> i + 1)).mapToObj(i -> i);
}
public static StreamPlus range(int startInclusive, int endExclusive) {
return IntStreamPlus.range(startInclusive, endExclusive).mapToObj(i -> i);
}
public static StreamPlus empty() {
return StreamPlus.from(Stream.empty());
}
// Because people know this.
@SafeVarargs
public static StreamPlus concat(Stream ... streams) {
return StreamPlus.of(streams).flatMap(Func.themAll());
}
@SafeVarargs
public static StreamPlus concat(Supplier> ... streams) {
return StreamPlus.of(streams).map(Supplier::get).flatMap(Func.themAll());
}
public static StreamPlus generate(Supplier supplier) {
return StreamPlus.from(Stream.generate(supplier));
}
public static StreamPlus generateBy(Supplier supplier) {
Iterable iterable = new Iterable() {
public Iterator iterator() {
return new Iterator() {
private D next;
@Override
public boolean hasNext() {
try {
next = supplier.get();
return true;
} catch (NoMoreResultException e) {
return false;
}
}
@Override
public D next() {
return next;
}
};
}
};
return StreamPlus.from(StreamSupport.stream(iterable.spliterator(), false));
}
public static StreamPlus iterate(D seed, UnaryOperator f) {
return StreamPlus.from(Stream.iterate(seed, f));
}
public static StreamPlus compound(D seed, UnaryOperator f) {
return iterate(seed, f);
}
public static StreamPlus iterate(D seed1, D seed2, BinaryOperator f) {
AtomicInteger counter = new AtomicInteger(0);
AtomicReference d1 = new AtomicReference(seed1);
AtomicReference d2 = new AtomicReference(seed2);
return StreamPlus.generate(()->{
if (counter.getAndIncrement() == 0)
return seed1;
if (counter.getAndIncrement() == 2)
return seed2;
D i2 = d2.get();
D i1 = d1.getAndSet(i2);
D i = f.apply(i1, i2);
d2.set(i);
return i;
});
}
public static StreamPlus> zipOf(StreamPlus stream1, StreamPlus stream2) {
return stream1.zipWith(stream2);
}
public static StreamPlus zipOf(StreamPlus stream1, StreamPlus stream2, Func2 merger) {
return stream1.zipWith(stream2, merger);
}
//== Stream ==
public Stream stream();
public default TARGET terminate(Func1, TARGET> action) {
val stream = stream();
try {
val result = action.apply(stream);
return result;
} finally {
stream.close();
}
}
public default void terminate(FuncUnit1> action) {
val stream = stream();
try {
action.accept(stream);
} finally {
stream.close();
}
}
public default StreamPlus deriveWith(Function, Stream> action) {
return StreamPlus.from(
action.apply(
this.stream()));
}
@SuppressWarnings("unchecked")
public default StreamPlus concatWith(Stream ... tails) {
return concat(
StreamPlus.of(this),
StreamPlus.of(tails)
)
.flatMap(themAll());
}
//== Functionalities ==
@Override
public default IntStreamPlus mapToInt(ToIntFunction super DATA> mapper) {
val intStreamPlus = IntStreamPlus.from(stream().mapToInt(mapper));
intStreamPlus.onClose(()->{
close();
});
return intStreamPlus;
}
@Override
public default LongStreamPlus mapToLong(ToLongFunction super DATA> mapper) {
return LongStreamPlus.from(stream().mapToLong(mapper));
}
@Override
public default DoubleStreamPlus mapToDouble(ToDoubleFunction super DATA> mapper) {
return DoubleStreamPlus.from(stream().mapToDouble(mapper));
}
@Override
public default IntStreamPlus flatMapToInt(Function super DATA, ? extends IntStream> mapper) {
return IntStreamPlus.from(stream().flatMapToInt(mapper));
}
@Override
public default LongStreamPlus flatMapToLong(Function super DATA, ? extends LongStream> mapper) {
return LongStreamPlus.from(stream().flatMapToLong(mapper));
}
@Override
public default DoubleStreamPlus flatMapToDouble(Function super DATA, ? extends DoubleStream> mapper) {
return DoubleStreamPlus.from(stream().flatMapToDouble(mapper));
}
public default void forEach(Consumer super DATA> action) {
terminate(stream -> {
if (action == null)
return;
stream
.forEach(action);
});
}
public default void forEachWithIndex(BiConsumer super Integer, ? super DATA> action) {
terminate(stream -> {
if (action == null)
return;
val index = new AtomicInteger();
stream
.forEach(each -> action.accept(index.getAndIncrement(), each));
});
}
public default void forEachOrdered(Consumer super DATA> action) {
terminate(stream -> {
if (action == null)
return;
stream
.forEachOrdered(action);
});
}
public default DATA reduce(DATA identity, BinaryOperator accumulator) {
return terminate(stream -> {
return stream.reduce(identity, accumulator);
});
}
public default Optional reduce(BinaryOperator accumulator) {
return terminate(stream -> {
return stream.reduce(accumulator);
});
}
public default U reduce(
U identity,
BiFunction accumulator,
BinaryOperator combiner) {
return terminate(stream -> {
return stream.reduce(identity, accumulator, combiner);
});
}
public default R collect(
Supplier supplier,
BiConsumer accumulator,
BiConsumer combiner) {
return terminate(stream -> {
return stream.collect(supplier, accumulator, combiner);
});
}
public default R collect(Collector super DATA, A, R> collector) {
return terminate(stream -> {
return stream.collect(collector);
});
}
public default FuncMap histogram() {
return histogram(Func.itself());
}
public default FuncMap histogram(Func1 mapper) {
return groupingBy(mapper)
.mapValue(FuncList::size)
.sortedByValue((a, b)->Integer.compare(b, a));
}
public default Optional> mostFrequence() {
return histogram()
.entries()
.findFirst();
}
public default StreamPlus> spawn(Func1> mapToAction) {
val results = new ArrayList>();
val index = new AtomicInteger(0);
val actions
= stream()
.map (mapToAction)
.peek(action -> results.add(DeferAction.createNew()))
.peek(action -> action.getPromise().onComplete(result -> {
val thisIndex = index.getAndIncrement();
val thisAction = results.get(thisIndex);
if (result.isValue())
thisAction.complete(result.value());
else thisAction.fail (result.exception());
}))
.peek(action -> action.start())
.collect(Collectors.toList())
;
val stream
= StreamPlus
.from(results.stream().map(action -> action.getResult()))
;
stream
.onClose(()->actions.forEach(action -> action.abort("Stream closed!")));
return stream;
}
public default Optional min(Comparator super DATA> comparator) {
return terminate(stream -> {
return stream.min(comparator);
});
}
public default Optional max(Comparator super DATA> comparator) {
return terminate(stream -> {
return stream.max(comparator);
});
}
@SuppressWarnings("unchecked")
public default Tuple2, Optional> minMax(Comparator super DATA> comparator) {
return terminate(stream -> {
val minRef = new AtomicReference