Home | History | Annotate | Download | only in io
      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      */
     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