package com.codepoetics.protonpack; import java.util.*; import java.util.function.*; import java.util.stream.Collector; import java.util.stream.Collectors; import java.util.stream.Stream; /** * A source of Streams that can be repeatedly streamed. * @param <T> The type over which the streamable's streams stream. */ public interface Streamable<T> extends Supplier<Stream<T>> { /** * Gets an empty streamable. * @param <T> The type of the values that aren't in the streamable's streams. * @return An empty streamable. */ static <T> Streamable<T> empty() { return Stream::empty; } /** * For converting method references to no-arg methods that return streams into streamable. * @param streamable Anything that can be cast to a Streamable. * @param <T> The type over which the streamable's streams stream. * @return The streamable. */ static <T> Streamable<T> of(Supplier<Stream<T>> streamable) { return streamable::get; } /** * Create a streamable that produces streams over an array of items. * @param items The items that the streamable's streams will stream. * @param <T> The type of the values in the array. * @return The streamable. */ static <T> Streamable<T> of(T...items) { return () -> Stream.of(items); } /** * Create a streamable that produces streams over a collection of items. * @param collection The items that the streamable's streams will stream. * @param <T> The type of the values in the collection. * @return The streamable. */ static <T> Streamable<T> of(Collection<T> collection) { return collection::stream; } /** * Create a streamable that produces streams over an iterable of items. * @param iterable The items that the streamable's streams will stream. * @param <T> The type of the values in the iterable. * @return The streamable. */ static <T> Streamable<T> of(Iterable<T> iterable) { return () -> StreamUtils.ofNullable(iterable); } /** * Create a streamable that produces streams of 0 or 1 elements over an optional value. * @param optional The optional item that the streamable's streams will stream. * @param <T> The type of the optional. * @return The streamable. */ static <T> Streamable<T> of(Optional<T> optional) { return () -> StreamUtils.stream(optional); } /** * Concatenate a series of streamables together. * @param streamables The streamables to concatenate. * @param <T> The type of the streamables. * @return A streamable which streams over the concatenation of the streams produced by all the source streamables. */ @SafeVarargs static <T> Streamable<T> ofAll(Streamable<T>...streamables) { return () -> Stream.of(streamables).flatMap(Streamable::stream); } /** * Synonym for "get" * @return A stream over the streamable */ default Stream<T> stream() { return get(); } /** * Concatenate this streamable with another streamable. * @param streamable The streamable to concatenate. * @return A concatenated streamable, which streams over the concatenation of the streams produces by its source streamables. */ default Streamable<T> concat(Streamable<T> streamable) { return () -> Stream.concat(stream(), streamable.stream()); } /** * Create a streamable that transforms the streams produced by this streamable with a stream transformer. * @param transformer The transformer to apply to this streamable's streams. * @param <T2> The type of the streams produced by the transformation. * @return A streamable which produces the transformed streams. */ default <T2> Streamable<T2> transform(Function<Stream<T>, Stream<T2>> transformer) { return () -> transformer.apply(stream()); } /** * Transform this streamable's streams with the supplied map. * @param f The map to apply. * @param <T2> The mapped-to type. * @return A streamable which produces the transformed streams. */ default <T2> Streamable<T2> map(Function<? super T, ? extends T2> f) { return transform(s -> s.map(f)); } /** * Transform this streamable's streams with the supplied flatmap. * @param f The flatmap to apply. * @param <T2> The flatmapped-to type. * @return A streamable which produces the transformed streams. */ default <T2> Streamable<T2> flatMap(Function<? super T, Stream<? extends T2>> f) { return transform(s -> s.flatMap(f)); } /** * Transform this streamable's streams with the supplied filter predicate. * @param predicate The filter predicate to apply. * @return A streamable which produces the transformed streams. */ default Streamable<T> filter(Predicate<? super T> predicate) { return transform(s -> s.filter(predicate)); } /** * Transform this streamable's streams with the supplied filter predicate, rejecting items which match the predicate. * @param predicate The filter predicate to apply. * @return A streamable which produces the transformed streams. */ default Streamable<T> reject(Predicate<? super T> predicate) { return transform(s -> s.filter(predicate.negate())); } /** * Transform this streamable's streams by sorting them. * @param comparator The comparator to use in sorting. * @return A streamable which produces the transformed streams. */ default Streamable<T> sorted(Comparator<? super T> comparator) { return () -> stream().sorted(comparator); } /** * Transform this streamable's streams by skipping elements * @param n The number of elements to skip * @return A streamable which produces the transformed streams. */ default Streamable<T> skip(long n) { return () -> stream().skip(n); } /** * Transform this streamable's streams by limiting the number of elements they can contain. * @param n The number of elements to limit to. * @return A streamable which produces the transformed streams. */ default Streamable<T> limit(long n) { return () -> stream().limit(n); } /** * Stream this streamable, and call forEach on the resulting stream with the supplied action. * @param action The action to apply to each stream element. */ default void forEach(Consumer<T> action) { stream().forEach(action); } /** * Stream this streamable, and call forEach on the resulting stream in order with the supplied action. * @param action The action to apply to each stream element. */ default void forEachOrdered(Consumer<T> action) { stream().forEachOrdered(action); } /** * Stream this streamable, and collect the stream with the supplied collector. * @param collector The collector to use to collect streamed values. * @param <O> The output type of the collector. * @return The collected result. */ default <O> O collect(Collector<T, ?, O> collector) { return stream().collect(collector); } /** * Stream this streamable, and collect the stream to a list. * @return The collected result. */ default List<T> toList() { return collect(Collectors.toList()); } /** * Stream this streamable, and collect the stream to a set. * @return The collected result. */ default Set<T> toSet() { return collect(Collectors.toSet()); } /** * Stream this streamable, and collect the stream to a map, extracting keys with the supplied index function. * @param indexFunction The function to use to extract keys from the streamed values. * @param <K> The type of the keys. * @return The collected result. */ default <K> Map<K, T> toMap(Function<? super T, ? extends K> indexFunction) { return collect(Collectors.toMap(indexFunction, v -> v)); } /** * Stream this streamable, and collect the stream to a map, extracting keys and values with the supplied functions. * @param keyFunction The function to use to extract keys from the stream. * @param valueFunction The function to use to extract values from the stream. * @param <K> The type of the keys. * @param <V> The type of the values. * @return The collected result. */ default <K, V> Map<K, V> toMap(Function<? super T, ? extends K> keyFunction, Function<? super T, ? extends V> valueFunction) { return collect(Collectors.toMap(keyFunction, valueFunction)); } /** * Stream this streamable, and collect the stream to an array. * @param arrayConstructor A function that will construct a new empty array of the required size. * @return The collected result. */ default T[] toArray(IntFunction<T[]> arrayConstructor) { return stream().toArray(arrayConstructor); } /** * Stream this streamable, and collect the stream into a Seq. * @return The collected result. */ default Seq<T> toSeq() { return Seq.of(stream()); } /** * Stream and reduce the streamable, using the supplied identity, accumulator and combiner. * @param identity The identity to use when reducing. * @param accumulator The accumulator to use when reducing. * @param combiner The combiner to use when reducing. * @param <U> The type of the result. * @return The reduced result. */ default <U> U reduce(U identity, BiFunction<U, T, U> accumulator, BinaryOperator<U> combiner){ return get().reduce(identity, accumulator, combiner); } /** * Stream and reduce the streamable, using the supplied accumulator. * @param accumulator The accumulator to use when reducing. * @return The reduced result. */ default Optional<T> reduce(BinaryOperator<T> accumulator) { return get().reduce(accumulator); } /** * Stream and reduce the streamable, using the supplied identity and accumulator. * @param identity The identity to use when reducing. * @param accumulator The accumulator to use when reducing. * @return The reduced result. */ default T reduce(T identity, BinaryOperator<T> accumulator) { return get().reduce(identity, accumulator); } }