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

com.github.robozonky.internal.util.stream.PagingSpliterator Maven / Gradle / Ivy

There is a newer version: 6.4.1
Show newest version
/*
 * Copyright 2020 The RoboZonky Project
 *
 * 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 com.github.robozonky.internal.util.stream;

import static java.lang.Math.min;

import java.util.List;
import java.util.Spliterator;
import java.util.Spliterators;
import java.util.function.Consumer;
import java.util.stream.StreamSupport;

final class PagingSpliterator implements Spliterator {

    private static final int SIZED_OR_SUBSIZED = SIZED
            | SUBSIZED;
    private static final int IMUTABLE_OR_ORDERED = IMMUTABLE
            | ORDERED;
    private final PageSource supplier;
    private final long pageSize;
    private long start;
    private long end;
    private Spliterator currentPage;
    private Spliterator danglingFirstPage;

    PagingSpliterator(final PageSource supplier, final long start, final long end, final long pageSize) {
        this.supplier = supplier;
        this.start = start;
        this.end = end;
        this.pageSize = pageSize;
    }

    /**
     * Ensure that the sub-spliterator provided by the List is compatible with
     * ours, i.e. is {@code SIZED_OR_SUBSIZED}. For standard List implementations,
     * the spliterators are, so the costs of dumping into an intermediate array
     * in the other case is irrelevant.
     */
    private static  Spliterator spliterator(final List list) {
        Spliterator original = list.spliterator();
        if ((original.characteristics()
                & SIZED_OR_SUBSIZED) == SIZED_OR_SUBSIZED) {
            return original;
        }
        var array = StreamSupport.stream(original, false)
            .toArray();
        return Spliterators.spliterator(array, IMUTABLE_OR_ORDERED);
    }

    public static  Spliterator build(PageSource source, long pageSize) {
        if (pageSize == 0) {
            return Spliterators.emptySpliterator();
        }
        final PagingSpliterator pgSp = new PagingSpliterator<>(source, 0, 0, pageSize);
        pgSp.danglingFirstPage = spliterator(source.fetch(0, pageSize, l -> pgSp.end = l));
        return pgSp;
    }

    @Override
    public final boolean tryAdvance(final Consumer action) {
        do {
            if (ensurePage().tryAdvance(action)) {
                return true;
            }
            if (start >= end) {
                return false;
            }
            currentPage = null;
        } while (true);
    }

    @Override
    public final void forEachRemaining(final Consumer action) {
        do {
            ensurePage().forEachRemaining(action);
            currentPage = null;
        } while (start < end);
    }

    @Override
    public final Spliterator trySplit() {
        if (danglingFirstPage != null) {
            final Spliterator fp = danglingFirstPage;
            danglingFirstPage = null;
            start = fp.getExactSizeIfKnown();
            return fp;
        } else if (currentPage != null) {
            return currentPage.trySplit();
        } else if (end - start > pageSize) {
            long mid = (start + end) >>> 1;
            mid = mid / pageSize * pageSize;
            if (mid == start) {
                mid += pageSize;
            }
            return new PagingSpliterator<>(supplier, start, start = mid, pageSize);
        }
        return ensurePage().trySplit();
    }

    @Override
    public final long estimateSize() {
        if (currentPage == null) {
            return end - start;
        }
        return currentPage.estimateSize();
    }

    @Override
    public final int characteristics() {
        return PagingStreams.CHARACTERISTICS;
    }

    /**
     * Fetch data immediately before traversing or sub-page splitting.
     */
    private Spliterator ensurePage() {
        if (danglingFirstPage != null) {
            final Spliterator fp = danglingFirstPage;
            danglingFirstPage = null;
            currentPage = fp;
            start = fp.getExactSizeIfKnown();
            return fp;
        } else if (currentPage != null) {
            return currentPage;
        } else if (start >= end) {
            return Spliterators.emptySpliterator();
        }
        var spliterator = spliterator(supplier.fetch(start, min(end - start, pageSize), l -> end = min(l, end)));
        start += spliterator.getExactSizeIfKnown();
        currentPage = spliterator;
        return spliterator;
    }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy