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 // 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 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 NullPointerException if the reader is null 82 */ 83 @SuppressWarnings("resource") // Caller closes Reader 84 public LineIterator(final Reader reader) { 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 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 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 */ 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 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 * @deprecated Use {@link #next()}. 170 */ 171 @Deprecated 172 public String nextLine() { 173 if (!hasNext()) { 174 throw new NoSuchElementException("No more lines"); 175 } 176 final String currentLine = cachedLine; 177 cachedLine = null; 178 return currentLine; 179 } 180 181 /** 182 * Unsupported. 183 * 184 * @throws UnsupportedOperationException always 185 */ 186 @Override 187 public void remove() { 188 throw new UnsupportedOperationException("remove not supported"); 189 } 190 191 }