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

no.digipost.DiggStreams Maven / Gradle / Ivy

The newest version!
/*
 * 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;

import no.digipost.function.ObjIntFunction;
import no.digipost.function.ObjLongFunction;

import java.util.Collection;
import java.util.Objects;
import java.util.Spliterator;
import java.util.Spliterators;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.BiFunction;
import java.util.function.Consumer;
import java.util.function.IntFunction;
import java.util.function.Predicate;
import java.util.stream.IntStream;
import java.util.stream.LongStream;
import java.util.stream.Stream;
import java.util.stream.StreamSupport;

import static java.lang.Integer.toBinaryString;
import static java.util.Spliterator.CONCURRENT;
import static java.util.Spliterator.DISTINCT;
import static java.util.Spliterator.IMMUTABLE;
import static java.util.Spliterator.NONNULL;
import static java.util.Spliterator.ORDERED;
import static java.util.Spliterator.SIZED;
import static java.util.Spliterator.SORTED;
import static java.util.Spliterator.SUBSIZED;
import static java.util.stream.Collectors.joining;
import static no.digipost.DiggBase.friendlyName;

/**
 * Utilities for working with {@link Stream}s.
 */
public final class DiggStreams {

    /**
     * Stream the elements from a container which allows access to the elements by an {@code int} index, but does not itself
     * offer any means for traversal/iteration/streaming. This method will stream all elements from index 0 up to,
     * but not including, the given {@code endExclusive} index.
     *
     * @param  The type of the resolved elements yielded by the {@link Stream}.
     * @param  The type of the source object which yields elements of type {@code V} by index.
     *
     * @param source The source object which yields the elements by an index
     * @param endExclusive The end index. This is also effectively the resulting size of the stream
     * @param resolveValue How to query the {@code source} object for each index. This can usually be a method
     *                     reference like {@code S::get} or similar.
     *
     * @return the stream yielding the elements
     */
    public static final  Stream streamByIntIndex(S source, int endExclusive, ObjIntFunction resolveValue) {
        return streamByIndex(source, IntStream.range(0, endExclusive), resolveValue);
    }

    /**
     * Stream the elements from a container which allows access to the by through an {@code int} index, but does not itself
     * offer any means for traversal/iteration/streaming. This method will stream the elements with indexes from
     * the given {@link IntStream}.
     *
     *
     * @param  The type of the resolved elements yielded by the {@link Stream}.
     * @param  The type of the source object which yields elements of type {@code V} by index.
     *
     * @param source The source object which yields the elements by an index
     * @param indexes The indexes
     * @param resolveValue How to query the {@code source} object for each index. This can usually be a method
     *                     reference like {@code S::get} or similar.
     *
     * @return the stream yielding the elements
     */
    public static final  Stream streamByIndex(S source, IntStream indexes, ObjIntFunction resolveValue) {
        return indexes.mapToObj(index -> resolveValue.apply(source, index));
    }

    /**
     * Stream the elements from a container which allows access to the elements by a {@code long} index, but does not itself
     * offer any means for traversal/iteration/streaming. This method will stream all elements from index 0 up to,
     * but not including, the given {@code endExclusive} index.
     *
     * @param  The type of the resolved elements yielded by the {@link Stream}.
     * @param  The type of the source object which yields elements of type {@code V} by index.
     *
     * @param source The source object which yields the elements by an index
     * @param endExclusive The end index. This is also effectively the resulting size of the stream
     * @param resolveValue How to query the {@code source} object for each index. This can usually be a method
     *                     reference like {@code S::get} or similar.
     *
     * @return the stream yielding the elements
     */
    public static final  Stream streamByLongIndex(S source, long endExclusive, ObjLongFunction resolveValue) {
        return streamByIndex(source, LongStream.range(0, endExclusive), resolveValue);
    }

    /**
     * Stream the elements from a container which allows access to the by through a {@code long} index, but does not itself
     * offer any means for traversal/iteration/streaming. This method will stream the elements with indexes from
     * the given {@link IntStream}.
     *
     *
     * @param  The type of the resolved elements yielded by the {@link Stream}.
     * @param  The type of the source object which yields elements of type {@code V} by index.
     *
     * @param source The source object which yields the elements by an index
     * @param indexes The indexes
     * @param resolveValue How to query the {@code source} object for each index. This can usually be a method
     *                     reference like {@code S::get} or similar.
     *
     * @return the stream yielding the elements
     */
    public static final  Stream streamByIndex(S source, LongStream indexes, ObjLongFunction resolveValue) {
        return indexes.mapToObj(index -> resolveValue.apply(source, index));
    }


    /**
     * Stream the elements from a container which allows access to the by through a key, but does not itself
     * offer any means for traversal/iteration/streaming. This method will stream the elements with keys from
     * the given {@link Stream Stream<K>}.
     *
     *
     * @param  The type of the resolved elements yielded by the {@link Stream}.
     * @param  The type of the source object which yields elements of type {@code V} by key.
     * @param  The type of the key used to query elements of type {@code V}.
     *
     * @param source The source object which yields the elements by a key
     * @param keys The keys
     * @param resolveValue How to query the {@code source} object for each key. This can usually be a method
     *                     reference like {@code S::get} or similar.
     *
     * @return the stream yielding the elements
     */
    public static final  Stream streamByKey(S source, Stream keys, BiFunction resolveValue) {
        return keys.map(key -> resolveValue.apply(source, key));
    }



    /**
     * Stream elements retrieved from resolved collections while they are non-empty.
     * The first empty collection will end the stream.
     *
     * @param  The type of the elements in the resolved collections, and elements in the resulting stream
     *
     * @param resolveCollection a function accepting an int indicating the page number and returns a {@link Collection} with
     *                          elements to include in the resulting stream
     *
     * @return the stream yielding the elements of the resolved collections
     */
    public static  Stream streamWhileNonEmpty(IntFunction> resolveCollection) {
        return streamPages(resolveCollection, c -> !c.isEmpty()).flatMap(Collection::stream);
    }


    /**
     * Generate a stream of objects resolved from an incrementing int (page number), while a predicate is accepting
     * the resolved objects. The first object not accepted by the predicate will end the stream.
     *
     * @param resolvePage a function accepting an int indicating the page number and returns a page to include
     *                    in the resulting stream
     * @param includeWhile the predicate accepting the objects to include in the stream
     *
     * @return the stream of the resolved objects
     */
    public static 

Stream

streamPages(IntFunction

resolvePage, Predicate includeWhile) { return streamPages(0, resolvePage, includeWhile); } /** * Generate a stream of objects resolved from an incrementing int (page number), while a predicate is accepting * the resolved objects. The first object not accepted by the predicate will end the stream. * * @param firstPageNum the initial page number * @param resolvePage a function accepting an int indicating the page number and returns a page to include * in the resulting stream * @param includeWhile the predicate accepting the objects to include in the stream * * @return the stream of the resolved objects */ public static

Stream

streamPages(int firstPageNum, IntFunction

resolvePage, Predicate includeWhile) { Spliterator

spliterator = new Spliterators.AbstractSpliterator

(Long.MAX_VALUE, 0) { final AtomicInteger pageNum = new AtomicInteger(firstPageNum); @Override public boolean tryAdvance(Consumer action) { P nextPage = resolvePage.apply(pageNum.getAndIncrement()); if (includeWhile.test(nextPage)) { action.accept(nextPage); return true; } else { return false; } } }; return StreamSupport.stream(spliterator, false); } /** * Get a description of {@link Spliterator#characteristics() characteristics} of a * {@code Spliterator}. The returned text is is solely intended for debugging and * logging purposes, and contents and format may change at any time. * * @param spliterator the Spliterator * @return the description */ public static String describeCharacteristics(Spliterator spliterator) { int value = spliterator.characteristics(); if (value == 0) { return friendlyName(spliterator.getClass()) + " with no enabled characteristics"; } else { String enabledCharacteristics = Stream.of( spliterator.hasCharacteristics(SIZED) ? "sized" : null, spliterator.hasCharacteristics(SUBSIZED) ? "subsized" : null, spliterator.hasCharacteristics(DISTINCT) ? "distinct" : null, spliterator.hasCharacteristics(NONNULL) ? "non-null" : null, spliterator.hasCharacteristics(IMMUTABLE) ? "immutable" : null, spliterator.hasCharacteristics(ORDERED) ? "ordered" : null, spliterator.hasCharacteristics(CONCURRENT) ? "concurrent" : null, spliterator.hasCharacteristics(SORTED) ? "sorted" : null) .filter(Objects::nonNull) .collect(joining(", ", "", "")); return enabledCharacteristics + " " + friendlyName(spliterator.getClass()) + " (" + toBinaryString(value) + ")"; } } private DiggStreams() { } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy