View Javadoc
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  
18  package org.apache.commons.io;
19  
20  import java.util.Iterator;
21  import java.util.Objects;
22  import java.util.stream.Stream;
23  
24  /**
25   * Wraps and presents a {@link Stream} as a {@link AutoCloseable} {@link Iterator} resource that automatically closes itself when reaching the end of stream.
26   *
27   * <h2>Warning</h2>
28   * <p>
29   * In order to close the stream, the call site MUST either close the stream it allocated OR call this iterator until the end.
30   * </p>
31   *
32   * @param <E> The {@link Stream} and {@link Iterator} type.
33   * @since 2.15.0
34   */
35  public final class StreamIterator<E> implements Iterator<E>, AutoCloseable {
36  
37      /**
38       * Wraps and presents a stream as a closable resource that automatically closes itself when reaching the end of stream.
39       * <p>
40       * <b>Warning</b>
41       * </p>
42       * <p>
43       * In order to close the stream, the call site MUST either close the stream it allocated OR call this iterator until the end.
44       * </p>
45       *
46       * @param <T>    The stream and iterator type.
47       * @param stream The stream iterate.
48       * @return A new iterator.
49       */
50      public static <T> StreamIterator<T> iterator(final Stream<T> stream) {
51          return new StreamIterator<>(stream);
52      }
53  
54      /**
55       * The given stream's Iterator.
56       */
57      private final Iterator<E> iterator;
58  
59      /**
60       * The given stream.
61       */
62      private final Stream<E> stream;
63  
64      /**
65       * Whether {@link #close()} has been called.
66       */
67      private boolean closed;
68  
69      private StreamIterator(final Stream<E> stream) {
70          this.stream = Objects.requireNonNull(stream, "stream");
71          this.iterator = stream.iterator();
72      }
73  
74      /**
75       * Closes the underlying stream.
76       */
77      @Override
78      public void close() {
79          closed = true;
80          stream.close();
81      }
82  
83      @Override
84      public boolean hasNext() {
85          if (closed) {
86              // Calling Iterator#hasNext() on a closed java.nio.file.FileTreeIterator causes an IllegalStateException.
87              return false;
88          }
89          final boolean hasNext = iterator.hasNext();
90          if (!hasNext) {
91              close();
92          }
93          return hasNext;
94      }
95  
96      @Override
97      public E next() {
98          final E next = iterator.next();
99          if (next == null) {
100             close();
101         }
102         return next;
103     }
104 
105 }