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 "zero" or 091 * "unit" 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 "zero" or 140 * "unit" 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 */