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