no.digipost.stream.NonEmptyStream Maven / Gradle / Ivy
Show all versions of digg Show documentation
/*
* Copyright (C) Posten Norge AS
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package no.digipost.stream;
import java.util.Arrays;
import java.util.Comparator;
import java.util.Iterator;
import java.util.Optional;
import java.util.Spliterator;
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.DoubleStream;
import java.util.stream.IntStream;
import java.util.stream.LongStream;
import java.util.stream.Stream;
import static no.digipost.DiggBase.friendlyName;
/**
* A stream which is guarantied to produce at least one element. It provides
* some extensions to the general {@link Stream} API for processing the stream knowing
* it is non-empty, including, but not limited to:
*
* - {@link #first()} and {@link #any()}
* - {@link #limitToNonEmpty(long)}
* - {@link #collect(EmptyResultIfEmptySourceCollector)}
* - {@link #reduceFromFirst(BinaryOperator)}
*
*
* A selection of operations which maintains the cardinality of the stream returns {@code NonEmptyStream},
* such as:
*
* - {@link #map(Function)}
*
* This selection may be extended in the future. For operations which can not guarantie the
* result will still be a non-empty stream, such as {@code filter} and {@code limit}, will yield a
* regular {@link Stream}.
*
*
* @param the type of the stream elements
*/
public class NonEmptyStream implements Stream {
/**
* Create a non-empty stream containing a single element.
*
* @param the type of the single element in the stream
* @param singleElement the element
*
* @return the new singleton non-empty stream
*/
public static NonEmptyStream of(T singleElement) {
return of(singleElement, Stream.empty());
}
/**
* Create a non-empty stream whose elements are the specified values.
*
* @param the type of stream elements
* @param firstElement the first element
* @param remainingElements the remaining elements after the first
*
* @return the new non-empty stream
*/
@SafeVarargs
@SuppressWarnings("varargs")
public static NonEmptyStream of(T firstElement, T ... remainingElements) {
return of(firstElement, Arrays.stream(remainingElements));
}
/**
* Create a non-empty stream whose elements are a given first value,
* and remaining elements are provided from another stream.
*
* @param the type of stream elements
* @param firstElement the first element
* @param remainingElements the remaining elements after the first
*
* @return the new non-empty stream
*/
public static NonEmptyStream of(T firstElement, Stream remainingElements) {
return of((Supplier) () -> firstElement, remainingElements);
}
/**
* Create a non-empty stream where the first element is resolved
* from a {@link Supplier}, and remaining elements are provided
* from another stream.
*
* @param the type of stream elements
* @param firstElement the supplier of the first element
* @param remainingElements the remaining elements after the first
*
* @return the new non-empty stream
*/
public static NonEmptyStream of(Supplier extends T> firstElement, Stream remainingElements) {
return new NonEmptyStream<>(firstElement, remainingElements);
}
/**
* Create a stream by concatenating a {@link NonEmptyStream}
* followed by a regular {@link Stream}, in the same manner as
* {@link Stream#concat(Stream, Stream)}. The resulting stream
* is also non-empty.
*
* @param The type of stream elements
* @param a the first stream, non-empty
* @param b the second stream
*
* @return the concatenation of the two input streams
*
* @see Stream#concat(Stream, Stream)
*/
public static NonEmptyStream concat(NonEmptyStream extends T> a, Stream extends T> b) {
return new NonEmptyStream<>(Stream.concat(a, b));
}
/**
* Create a stream by concatenating a regular {@link Stream}
* followed by a {@link NonEmptyStream}, in the same manner as
* {@link Stream#concat(Stream, Stream)}. The resulting stream
* is also non-empty.
*
* @param The type of stream elements
* @param a the first stream
* @param b the second stream, non-empty
*
* @return the concatenation of the two input streams
*
* @see Stream#concat(Stream, Stream)
*/
public static NonEmptyStream concat(Stream extends T> a, NonEmptyStream extends T> b) {
return new NonEmptyStream<>(Stream.concat(a, b));
}
/**
* Create a stream by concatenating two non-empty streams,
* in the same manner as {@link Stream#concat(Stream, Stream)}.
* The resulting stream is also non-empty.
*
* This method overload is needed to avoid ambiguity with
* {@link #concat(NonEmptyStream, Stream)} and
* {@link #concat(Stream, NonEmptyStream)} when concatenating
* two non-empty streams.
*
* @param The type of stream elements
* @param a the first non-empty stream
* @param b the second non-empty stream
*
* @return the concatenation of the two input streams
*
* @see Stream#concat(Stream, Stream)
*/
public static NonEmptyStream concat(NonEmptyStream extends T> a, NonEmptyStream extends T> b) {
return new NonEmptyStream<>(Stream.concat(a, b));
}
/**
* Create the same stream as produced by {@link Stream#iterate(Object, UnaryOperator)},
* but typed as {@link NonEmptyStream}.
*
* @param the type of stream elements
* @param seed the initial element
* @param f a function to be applied to to the previous element to produce a new element
*
* @return the new infinite non-empty stream
*
* @see Stream#iterate(Object, UnaryOperator)
*/
public static NonEmptyStream iterate(T seed, UnaryOperator f) {
return new NonEmptyStream<>(Stream.iterate(seed, f));
}
/**
* Create the same stream as produced by {@link Stream#generate(Supplier)},
* but typed as {@link NonEmptyStream}.
*
* @param the type of stream elements
* @param s the {@code Supplier} of generated elements
*
* @return a new infinite non-empty stream
*
* @see Stream#generate(Supplier)
*/
public static NonEmptyStream generate(Supplier s) {
return new NonEmptyStream<>(Stream.generate(s));
}
private final Stream completeStream;
private NonEmptyStream(Supplier extends T> firstElement, Stream remainingElements) {
this(Stream.concat(Stream.generate(firstElement).limit(1), remainingElements));
}
/**
* Constructor is only for internal use to wrap streams
* already guarantied to be non-empty.
*/
private NonEmptyStream(Stream completeNonEmptyStream) {
this.completeStream = completeNonEmptyStream;
}
@Override
public R collect(Supplier supplier, BiConsumer accumulator, BiConsumer combiner) {
return completeStream.collect(supplier, accumulator, combiner);
}
@Override
public R collect(Collector super T, A, R> collector) {
return completeStream.collect(collector);
}
/**
* Collect the stream elements by using a {@link EmptyResultIfEmptySourceCollector}.
*
* This is an extension to the general {@link Stream} API, as a non-empty stream can always produce
* a value without needing a provided initial "identity" value.
*
* @param collector the {@code Collector} describing the reduction
* @return the result from collecting the elements
*
* @see EmptyResultIfEmptySourceCollector
*/
public R collect(EmptyResultIfEmptySourceCollector super T, A, R> collector) {
@SuppressWarnings("unchecked")
Collector