001/* 002 * JDrupes Builder 003 * Copyright (C) 2025 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.ArrayList; 022import java.util.Arrays; 023import java.util.List; 024import java.util.stream.Stream; 025 026/// A cached stream allows the user of a [Stream] to access it multiple 027/// times. The stream is evaluated on the first call to [#stream] and 028/// then cached. 029/// 030/// As a convenience, the class supports combining the contents of multiple 031/// streams. 032/// 033/// @param <T> the generic type 034/// 035public class CachedStream<T> { 036 037 private List<Stream<T>> sources = new ArrayList<>(); 038 private List<T> cache; 039 040 /// Instantiates a new cached stream. 041 /// 042 @SuppressWarnings("PMD.UnnecessaryConstructor") 043 public CachedStream() { 044 // Make javadoc happy. 045 } 046 047 /// Use all given streams as sources. 048 /// 049 /// @param sources the sources 050 /// 051 @SafeVarargs 052 public final void add(Stream<T>... sources) { 053 if (sources == null) { 054 throw new IllegalStateException( 055 "Cannot add sources after stream() has been called."); 056 } 057 this.sources.addAll(Arrays.asList(sources)); 058 } 059 060 /// Provide the contents from the stream(s). 061 /// 062 /// @return the stream<? extends t> 063 /// 064 @SuppressWarnings("PMD.AvoidSynchronizedStatement") 065 public Stream<T> stream() { 066 synchronized (this) { 067 if (cache == null) { 068 cache = sources.stream().flatMap(s -> s).toList(); 069 sources = null; 070 } 071 return cache.stream(); 072 } 073 } 074 075 /// Create a cached stream from a single source stream. 076 /// 077 /// @param <T> the generic type 078 /// @param source the source 079 /// @return the cached stream 080 /// 081 @SuppressWarnings("PMD.ShortMethodName") 082 public static <T> CachedStream<T> of(Stream<T> source) { 083 var result = new CachedStream<T>(); 084 result.add(source); 085 return result; 086 } 087}