StreamIterator.java

  1. /*
  2.  * Licensed to the Apache Software Foundation (ASF) under one or more
  3.  * contributor license agreements.  See the NOTICE file distributed with
  4.  * this work for additional information regarding copyright ownership.
  5.  * The ASF licenses this file to You under the Apache License, Version 2.0
  6.  * (the "License"); you may not use this file except in compliance with
  7.  * the License.  You may obtain a copy of the License at
  8.  *
  9.  *      http://www.apache.org/licenses/LICENSE-2.0
  10.  *
  11.  * Unless required by applicable law or agreed to in writing, software
  12.  * distributed under the License is distributed on an "AS IS" BASIS,
  13.  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14.  * See the License for the specific language governing permissions and
  15.  * limitations under the License.
  16.  */

  17. package org.apache.commons.io;

  18. import java.util.Iterator;
  19. import java.util.Objects;
  20. import java.util.stream.Stream;

  21. /**
  22.  * Wraps and presents a {@link Stream} as a {@link AutoCloseable} {@link Iterator} resource that automatically closes itself when reaching the end of stream.
  23.  *
  24.  * <h2>Warning</h2>
  25.  * <p>
  26.  * In order to close the stream, the call site MUST either close the stream it allocated OR call this iterator until the end.
  27.  * </p>
  28.  *
  29.  * @param <E> The {@link Stream} and {@link Iterator} type.
  30.  * @since 2.15.0
  31.  */
  32. public final class StreamIterator<E> implements Iterator<E>, AutoCloseable {

  33.     /**
  34.      * Wraps and presents a stream as a closable resource that automatically closes itself when reaching the end of stream.
  35.      * <p>
  36.      * <strong>Warning</strong>
  37.      * </p>
  38.      * <p>
  39.      * In order to close the stream, the call site MUST either close the stream it allocated OR call this iterator until the end.
  40.      * </p>
  41.      *
  42.      * @param <T>    The stream and iterator type.
  43.      * @param stream The stream iterate.
  44.      * @return A new iterator.
  45.      */
  46.     public static <T> StreamIterator<T> iterator(final Stream<T> stream) {
  47.         return new StreamIterator<>(stream);
  48.     }

  49.     /**
  50.      * The given stream's Iterator.
  51.      */
  52.     private final Iterator<E> iterator;

  53.     /**
  54.      * The given stream.
  55.      */
  56.     private final Stream<E> stream;

  57.     /**
  58.      * Whether {@link #close()} has been called.
  59.      */
  60.     private boolean closed;

  61.     private StreamIterator(final Stream<E> stream) {
  62.         this.stream = Objects.requireNonNull(stream, "stream");
  63.         this.iterator = stream.iterator();
  64.     }

  65.     /**
  66.      * Closes the underlying stream.
  67.      */
  68.     @Override
  69.     public void close() {
  70.         closed = true;
  71.         stream.close();
  72.     }

  73.     @Override
  74.     public boolean hasNext() {
  75.         if (closed) {
  76.             // Calling Iterator#hasNext() on a closed java.nio.file.FileTreeIterator causes an IllegalStateException.
  77.             return false;
  78.         }
  79.         final boolean hasNext = iterator.hasNext();
  80.         if (!hasNext) {
  81.             close();
  82.         }
  83.         return hasNext;
  84.     }

  85.     @Override
  86.     public E next() {
  87.         final E next = iterator.next();
  88.         if (next == null) {
  89.             close();
  90.         }
  91.         return next;
  92.     }

  93. }