• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
19 import java.io.BufferedReader;
20 import java.io.Closeable;
21 import java.io.IOException;
22 import java.io.Reader;
23 import java.util.Iterator;
24 import java.util.NoSuchElementException;
25 import java.util.Objects;
26 
27 /**
28  * An Iterator over the lines in a {@link Reader}.
29  * <p>
30  * {@link LineIterator} holds a reference to an open {@link Reader}.
31  * When you have finished with the iterator you should close the reader
32  * to free internal resources. This can be done by closing the reader directly,
33  * or by calling the {@link #close()} or {@link #closeQuietly(LineIterator)}
34  * method on the iterator.
35  * <p>
36  * The recommended usage pattern is:
37  * <pre>
38  * LineIterator it = FileUtils.lineIterator(file, StandardCharsets.UTF_8.name());
39  * try {
40  *   while (it.hasNext()) {
41  *     String line = it.nextLine();
42  *     // do something with line
43  *   }
44  * } finally {
45  *   it.close();
46  * }
47  * </pre>
48  *
49  * @since 1.2
50  */
51 public class LineIterator implements Iterator<String>, Closeable {
52 
53     // N.B. This class deliberately does not implement Iterable, see https://issues.apache.org/jira/browse/IO-181
54 
55     /**
56      * Closes a {@link LineIterator} quietly.
57      *
58      * @param iterator The iterator to close, or {@code null}.
59      * @deprecated As of 2.6 deprecated without replacement. Please use the try-with-resources statement or handle
60      * suppressed exceptions manually.
61      * @see Throwable#addSuppressed(Throwable)
62      */
63     @Deprecated
closeQuietly(final LineIterator iterator)64     public static void closeQuietly(final LineIterator iterator) {
65         IOUtils.closeQuietly(iterator);
66     }
67 
68     /** The reader that is being read. */
69     private final BufferedReader bufferedReader;
70 
71     /** The current line. */
72     private String cachedLine;
73 
74     /** A flag indicating if the iterator has been fully read. */
75     private boolean finished;
76 
77     /**
78      * Constructs an iterator of the lines for a {@link Reader}.
79      *
80      * @param reader the {@link Reader} to read from, not null
81      * @throws IllegalArgumentException if the reader is null
82      */
LineIterator(final Reader reader)83     public LineIterator(final Reader reader) throws IllegalArgumentException {
84         Objects.requireNonNull(reader, "reader");
85         if (reader instanceof BufferedReader) {
86             bufferedReader = (BufferedReader) reader;
87         } else {
88             bufferedReader = new BufferedReader(reader);
89         }
90     }
91 
92     /**
93      * Closes the underlying {@link Reader}.
94      * This method is useful if you only want to process the first few
95      * lines of a larger file. If you do not close the iterator
96      * then the {@link Reader} remains open.
97      * This method can safely be called multiple times.
98      *
99      * @throws IOException if closing the underlying {@link Reader} fails.
100      */
101     @Override
close()102     public void close() throws IOException {
103         finished = true;
104         cachedLine = null;
105         IOUtils.close(bufferedReader);
106     }
107 
108     /**
109      * Indicates whether the {@link Reader} has more lines.
110      * If there is an {@link IOException} then {@link #close()} will
111      * be called on this instance.
112      *
113      * @return {@code true} if the Reader has more lines
114      * @throws IllegalStateException if an IO exception occurs
115      */
116     @Override
hasNext()117     public boolean hasNext() {
118         if (cachedLine != null) {
119             return true;
120         }
121         if (finished) {
122             return false;
123         }
124         try {
125             while (true) {
126                 final String line = bufferedReader.readLine();
127                 if (line == null) {
128                     finished = true;
129                     return false;
130                 }
131                 if (isValidLine(line)) {
132                     cachedLine = line;
133                     return true;
134                 }
135             }
136         } catch(final IOException ioe) {
137             IOUtils.closeQuietly(this, ioe::addSuppressed);
138             throw new IllegalStateException(ioe);
139         }
140     }
141 
142     /**
143      * Overridable method to validate each line that is returned.
144      * This implementation always returns true.
145      * @param line  the line that is to be validated
146      * @return true if valid, false to remove from the iterator
147      */
isValidLine(final String line)148     protected boolean isValidLine(final String line) {
149         return true;
150     }
151 
152     /**
153      * Returns the next line in the wrapped {@link Reader}.
154      *
155      * @return the next line from the input
156      * @throws NoSuchElementException if there is no line to return
157      */
158     @Override
next()159     public String next() {
160         return nextLine();
161     }
162 
163     /**
164      * Returns the next line in the wrapped {@link Reader}.
165      *
166      * @return the next line from the input
167      * @throws NoSuchElementException if there is no line to return
168      */
nextLine()169     public String nextLine() {
170         if (!hasNext()) {
171             throw new NoSuchElementException("No more lines");
172         }
173         final String currentLine = cachedLine;
174         cachedLine = null;
175         return currentLine;
176     }
177 
178     /**
179      * Unsupported.
180      *
181      * @throws UnsupportedOperationException always
182      */
183     @Override
remove()184     public void remove() {
185         throw new UnsupportedOperationException("remove not supported");
186     }
187 
188 }
189