001/*
002 * JDrupes Builder
003 * Copyright (C) 2026 Michael N. Lipp
004 * 
005 * This program is free software: you can redistribute it and/or modify
006 * it under the terms of the GNU Affero General Public License as
007 * published by the Free Software Foundation, either version 3 of the
008 * License, or (at your option) any later version.
009 *
010 * This program is distributed in the hope that it will be useful,
011 * but WITHOUT ANY WARRANTY; without even the implied warranty of
012 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
013 * GNU Affero General Public License for more details.
014 *
015 * You should have received a copy of the GNU Affero General Public License
016 * along with this program.  If not, see <https://www.gnu.org/licenses/>.
017 */
018
019package org.jdrupes.builder.core;
020
021import java.util.Collection;
022import java.util.Iterator;
023import java.util.Spliterators;
024import java.util.function.Consumer;
025import java.util.function.Supplier;
026import java.util.stream.Stream;
027import java.util.stream.StreamSupport;
028
029/// A stream of elements whose backing collection is obtained lazily from
030/// a supplier when the stream is first consumed.
031///
032/// @param <T> the element type
033///
034public final class LazyCollectionStream<T>
035        extends Spliterators.AbstractSpliterator<T> {
036
037    private final Supplier<Collection<T>> supplier;
038    private Iterator<T> theIterator;
039
040    private LazyCollectionStream(Supplier<Collection<T>> supplier) {
041        super(Long.MAX_VALUE, IMMUTABLE);
042        this.supplier = supplier;
043    }
044
045    private Iterator<T> iterator() {
046        if (theIterator == null) {
047            theIterator = supplier.get().iterator();
048        }
049        return theIterator;
050    }
051
052    @Override
053    public void forEachRemaining(Consumer<? super T> action) {
054        iterator().forEachRemaining(action);
055    }
056
057    @Override
058    public boolean tryAdvance(Consumer<? super T> action) {
059        if (!iterator().hasNext()) {
060            return false;
061        }
062        action.accept(iterator().next());
063        return true;
064    }
065
066    /// Creates a stream backed by a collection supplied lazily by the
067    /// given supplier. The supplier is invoked only when the stream is
068    /// first consumed.
069    ///
070    /// @param <T> the element type
071    /// @param supplier supplies the collection
072    /// @return the stream
073    ///
074    @SuppressWarnings("PMD.ShortMethodName")
075    public static <T> Stream<T> of(Supplier<Collection<T>> supplier) {
076        return StreamSupport.stream(
077            new LazyCollectionStream<>(supplier), false);
078    }
079}