001/*
002 * ============================================================================
003 * Copyright © 2014 by Dominic Fox.
004 * All Rights Reserved.
005 * ============================================================================
006 * The MIT License (MIT)
007 *
008 * Permission is hereby granted, free of charge, to any person obtaining a copy
009 * of this software and associated documentation files (the "Software"), to
010 * deal in the Software without restriction, including without limitation the
011 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
012 * sell copies of the Software, and to permit persons to whom the Software is
013 * furnished to do so, subject to the following conditions:
014 *
015 * The above copyright notice and this permission notice shall be included in
016 * all copies or substantial portions of the Software.
017 *
018 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
019 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
020 * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE
021 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
022 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
023 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
024 * IN THE SOFTWARE.
025 */
026
027package org.tquadrat.foundation.stream.internal;
028
029import static org.apiguardian.api.API.Status.INTERNAL;
030import static org.tquadrat.foundation.lang.Objects.requireNonNullArgument;
031
032import java.util.ArrayList;
033import java.util.List;
034import java.util.Spliterator;
035import java.util.function.BiFunction;
036import java.util.function.Consumer;
037import java.util.function.Supplier;
038import java.util.stream.Stream;
039
040import org.apiguardian.api.API;
041import org.tquadrat.foundation.annotation.ClassVersion;
042import org.tquadrat.foundation.annotation.NotRecord;
043
044/**
045 *  An implementation of
046 *  {@link Spliterator}
047 *  that merges streams.
048 *
049 *  @author Dominic Fox
050 *  @modified Thomas Thrien - thomas.thrien@tquadrat.org
051 *  @version $Id: MergingSpliterator.java 1151 2025-10-01 21:32:15Z tquadrat $
052 *  @since 0.0.7
053 *
054 *  @param  <T> The type over which the merged streams stream.
055 *  @param  <O> The type of the accumulator, over which the constructed
056 *      stream streams.
057 *
058 *  @UMLGraph.link
059 */
060@ClassVersion( sourceVersion = "$Id: MergingSpliterator.java 1151 2025-10-01 21:32:15Z tquadrat $" )
061@API( status = INTERNAL, since = "0.0.7" )
062@NotRecord
063public final class MergingSpliterator<T,O> implements Spliterator<O>
064{
065        /*------------*\
066    ====** Attributes **=======================================================
067        \*------------*/
068    /**
069     *  The merger function.
070     */
071    private final BiFunction<O,T,O> m_Merger;
072
073    /**
074     *  The sources.
075     */
076    private final Spliterator<T> [] m_Sources;
077
078    /**
079     *  The unit supplier.
080     */
081    private final Supplier<O> m_UnitSupplier;
082
083        /*--------------*\
084    ====** Constructors **=====================================================
085        \*--------------*/
086    /**
087     *  Creates a new {@code MergingSpliterator} instance.
088     *
089     *  @param  sources The sources.
090     *  @param  unitSupplier    Supplies the initial &quot;zero&quot; or
091     *      &quot;unit&quot; value for the accumulator.
092     *  @param  merger  Merges each item from the collection of values taken
093     *      from the source streams into the accumulator value.
094     */
095    private MergingSpliterator( final Spliterator<T> [] sources, final Supplier<O> unitSupplier, final BiFunction<O,T,O> merger )
096    {
097        m_Sources = requireNonNullArgument( sources, "sources" );
098        m_UnitSupplier = requireNonNullArgument( unitSupplier, "unitSupplier" );
099        m_Merger = requireNonNullArgument( merger, "merger" );
100    }   //  MergingSpliterator()
101
102        /*---------*\
103    ====** Methods **==========================================================
104        \*---------*/
105    /**
106     *  {@inheritDoc}
107     */
108    @Override
109    public final int characteristics()
110    {
111        @SuppressWarnings( "ConstantExpression" )
112        final var retValue = NONNULL & ORDERED & IMMUTABLE;
113
114        //---* Done *----------------------------------------------------------
115        return retValue;
116    }   //  characteristics()
117
118    /**
119     *  {@inheritDoc}
120     */
121    @Override
122    public final long estimateSize()
123    {
124        final long retValue = Stream.of( m_Sources )
125            .map( Spliterator::estimateSize )
126            .max( Long::compare )
127            .orElse( 0L );
128
129        //---* Done *----------------------------------------------------------
130        return retValue;
131    }   //  estimateSize()
132
133    /**
134     *  Factory method for instances of {@code MergingSpliterator}.
135     *
136     *  @param  <O> The type of the elements in the resulting spliterator.
137     *  @param  <T> The type of the elements in the source spliterators.
138     *  @param  sources The sources.
139     *  @param  unitSupplier    Supplies the initial &quot;zero&quot; or
140     *      &quot;unit&quot; value for the accumulator.
141     *  @param  merger  Merges each item from the collection of values taken
142     *      from the source streams into the accumulator value.
143     *  @return The new instance.
144     */
145    public static final <T,O> Spliterator<O> merging( final Spliterator<T> [] sources, final Supplier<O> unitSupplier, final BiFunction<O,T,O> merger )
146    {
147        final Spliterator<O> retValue = new MergingSpliterator<>( sources, unitSupplier, merger );
148
149        //---* Done *----------------------------------------------------------
150        return retValue;
151    }   //  merging()
152
153    /**
154     *  {@inheritDoc}
155     */
156    @Override
157    public final boolean tryAdvance( final Consumer<? super O> action )
158    {
159        final List<T> mergeables = new ArrayList<>( m_Sources.length );
160        Stream.of( m_Sources )
161            .forEach( spliterator -> spliterator.tryAdvance( mergeables::add ) );
162
163        final var retValue = !mergeables.isEmpty();
164        if( retValue )
165        {
166            final var unit = m_UnitSupplier.get();
167
168            //---* We never do this in parallel, so fuck it *------------------
169            action.accept( mergeables.stream().reduce( unit, m_Merger, ( l1, _ ) -> l1 ) );
170        }
171
172        //---* Done *----------------------------------------------------------
173        return retValue;
174    }   //  tryAdvance()
175
176    /**
177     *  {@inheritDoc}
178     */
179    @Override
180    public final Spliterator<O> trySplit() { return null; }
181}
182//  class MergingSpliterator
183
184/*
185 *  End of File
186 */