• 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      */
83     @SuppressWarnings("resource") // Caller closes Reader
LineIterator(final Reader reader)84     public LineIterator(final Reader reader) throws IllegalArgumentException {
85         Objects.requireNonNull(reader, "reader");
86         if (reader instanceof BufferedReader) {
87             bufferedReader = (BufferedReader) reader;
88         } else {
89             bufferedReader = new BufferedReader(reader);
90         }
91     }
92 
93     /**
94      * Closes the underlying {@link Reader}.
95      * This method is useful if you only want to process the first few
96      * lines of a larger file. If you do not close the iterator
97      * then the {@link Reader} remains open.
98      * This method can safely be called multiple times.
99      *
100      * @throws IOException if closing the underlying {@link Reader} fails.
101      */
102     @Override
close()103     public void close() throws IOException {
104         finished = true;
105         cachedLine = null;
106         IOUtils.close(bufferedReader);
107     }
108 
109     /**
110      * Indicates whether the {@link Reader} has more lines.
111      * If there is an {@link IOException} then {@link #close()} will
112      * be called on this instance.
113      *
114      * @return {@code true} if the Reader has more lines
115      * @throws IllegalStateException if an IO exception occurs
116      */
117     @Override
hasNext()118     public boolean hasNext() {
119         if (cachedLine != null) {
120             return true;
121         }
122         if (finished) {
123             return false;
124         }
125         try {
126             while (true) {
127                 final String line = bufferedReader.readLine();
128                 if (line == null) {
129                     finished = true;
130                     return false;
131                 }
132                 if (isValidLine(line)) {
133                     cachedLine = line;
134                     return true;
135                 }
136             }
137         } catch(final IOException ioe) {
138             IOUtils.closeQuietly(this, ioe::addSuppressed);
139             throw new IllegalStateException(ioe);
140         }
141     }
142 
143     /**
144      * Overridable method to validate each line that is returned.
145      * This implementation always returns true.
146      * @param line  the line that is to be validated
147      * @return true if valid, false to remove from the iterator
148      */
isValidLine(final String line)149     protected boolean isValidLine(final String line) {
150         return true;
151     }
152 
153     /**
154      * Returns the next line in the wrapped {@link Reader}.
155      *
156      * @return the next line from the input
157      * @throws NoSuchElementException if there is no line to return
158      */
159     @Override
next()160     public String next() {
161         return nextLine();
162     }
163 
164     /**
165      * Returns the next line in the wrapped {@link Reader}.
166      *
167      * @return the next line from the input
168      * @throws NoSuchElementException if there is no line to return
169      */
nextLine()170     public String nextLine() {
171         if (!hasNext()) {
172             throw new NoSuchElementException("No more lines");
173         }
174         final String currentLine = cachedLine;
175         cachedLine = null;
176         return currentLine;
177     }
178 
179     /**
180      * Unsupported.
181      *
182      * @throws UnsupportedOperationException always
183      */
184     @Override
remove()185     public void remove() {
186         throw new UnsupportedOperationException("remove not supported");
187     }
188 
189 }
190