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 */ 17package org.apache.commons.io; 18 19import java.io.BufferedReader; 20import java.io.IOException; 21import java.io.Reader; 22import java.util.Iterator; 23import 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 */ 53public 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 175 public static void closeQuietly(LineIterator iterator) { 176 if (iterator != null) { 177 iterator.close(); 178 } 179 } 180 181} 182