001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one or more
003: * contributor license agreements. See the NOTICE file distributed with
004: * this work for additional information regarding copyright ownership.
005: * The ASF licenses this file to You under the Apache License, Version 2.0
006: * (the "License"); you may not use this file except in compliance with
007: * the License. You may obtain a copy of the License at
008: *
009: * http://www.apache.org/licenses/LICENSE-2.0
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: * See the License for the specific language governing permissions and
015: * limitations under the License.
016: */
017: package org.apache.commons.io;
018:
019: import java.io.BufferedReader;
020: import java.io.IOException;
021: import java.io.Reader;
022: import java.util.Iterator;
023: import java.util.NoSuchElementException;
024:
025: /**
026: * An Iterator over the lines in a <code>Reader</code>.
027: * <p>
028: * <code>LineIterator</code> holds a reference to an open <code>Reader</code>.
029: * When you have finished with the iterator you should close the reader
030: * to free internal resources. This can be done by closing the reader directly,
031: * or by calling the {@link #close()} or {@link #closeQuietly(LineIterator)}
032: * method on the iterator.
033: * <p>
034: * The recommended usage pattern is:
035: * <pre>
036: * LineIterator it = FileUtils.lineIterator(file, "UTF-8");
037: * try {
038: * while (it.hasNext()) {
039: * String line = it.nextLine();
040: * /// do something with line
041: * }
042: * } finally {
043: * LineIterator.closeQuietly(iterator);
044: * }
045: * </pre>
046: *
047: * @author Niall Pemberton
048: * @author Stephen Colebourne
049: * @author Sandy McArthur
050: * @version $Id: LineIterator.java 437567 2006-08-28 06:39:07Z bayard $
051: * @since Commons IO 1.2
052: */
053: public class LineIterator implements Iterator {
054:
055: /** The reader that is being read. */
056: private final BufferedReader bufferedReader;
057: /** The current line. */
058: private String cachedLine;
059: /** A flag indicating if the iterator has been fully read. */
060: private boolean finished = false;
061:
062: /**
063: * Constructs an iterator of the lines for a <code>Reader</code>.
064: *
065: * @param reader the <code>Reader</code> to read from, not null
066: * @throws IllegalArgumentException if the reader is null
067: */
068: public LineIterator(final Reader reader)
069: throws IllegalArgumentException {
070: if (reader == null) {
071: throw new IllegalArgumentException(
072: "Reader must not be null");
073: }
074: if (reader instanceof BufferedReader) {
075: bufferedReader = (BufferedReader) reader;
076: } else {
077: bufferedReader = new BufferedReader(reader);
078: }
079: }
080:
081: //-----------------------------------------------------------------------
082: /**
083: * Indicates whether the <code>Reader</code> has more lines.
084: * If there is an <code>IOException</code> then {@link #close()} will
085: * be called on this instance.
086: *
087: * @return <code>true</code> if the Reader has more lines
088: * @throws IllegalStateException if an IO exception occurs
089: */
090: public boolean hasNext() {
091: if (cachedLine != null) {
092: return true;
093: } else if (finished) {
094: return false;
095: } else {
096: try {
097: while (true) {
098: String line = bufferedReader.readLine();
099: if (line == null) {
100: finished = true;
101: return false;
102: } else if (isValidLine(line)) {
103: cachedLine = line;
104: return true;
105: }
106: }
107: } catch (IOException ioe) {
108: close();
109: throw new IllegalStateException(ioe.toString());
110: }
111: }
112: }
113:
114: /**
115: * Overridable method to validate each line that is returned.
116: *
117: * @param line the line that is to be validated
118: * @return true if valid, false to remove from the iterator
119: */
120: protected boolean isValidLine(String line) {
121: return true;
122: }
123:
124: /**
125: * Returns the next line in the wrapped <code>Reader</code>.
126: *
127: * @return the next line from the input
128: * @throws NoSuchElementException if there is no line to return
129: */
130: public Object next() {
131: return nextLine();
132: }
133:
134: /**
135: * Returns the next line in the wrapped <code>Reader</code>.
136: *
137: * @return the next line from the input
138: * @throws NoSuchElementException if there is no line to return
139: */
140: public String nextLine() {
141: if (!hasNext()) {
142: throw new NoSuchElementException("No more lines");
143: }
144: String currentLine = cachedLine;
145: cachedLine = null;
146: return currentLine;
147: }
148:
149: /**
150: * Closes the underlying <code>Reader</code> quietly.
151: * This method is useful if you only want to process the first few
152: * lines of a larger file. If you do not close the iterator
153: * then the <code>Reader</code> remains open.
154: * This method can safely be called multiple times.
155: */
156: public void close() {
157: finished = true;
158: IOUtils.closeQuietly(bufferedReader);
159: cachedLine = null;
160: }
161:
162: /**
163: * Unsupported.
164: *
165: * @throws UnsupportedOperationException always
166: */
167: public void remove() {
168: throw new UnsupportedOperationException(
169: "Remove unsupported on LineIterator");
170: }
171:
172: //-----------------------------------------------------------------------
173: /**
174: * Closes the iterator, handling null and ignoring exceptions.
175: *
176: * @param iterator the iterator to close
177: */
178: public static void closeQuietly(LineIterator iterator) {
179: if (iterator != null) {
180: iterator.close();
181: }
182: }
183:
184: }
|