LineIterator.java

  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. import java.io.BufferedReader;
  19. import java.io.Closeable;
  20. import java.io.IOException;
  21. import java.io.Reader;
  22. import java.util.Iterator;
  23. import java.util.NoSuchElementException;
  24. import java.util.Objects;

  25. /**
  26.  * An Iterator over the lines in a {@link Reader}.
  27.  * <p>
  28.  * {@link LineIterator} holds a reference to an open {@link Reader}.
  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, StandardCharsets.UTF_8.name());
  37.  * try {
  38.  *   while (it.hasNext()) {
  39.  *     String line = it.nextLine();
  40.  *     // do something with line
  41.  *   }
  42.  * } finally {
  43.  *   it.close();
  44.  * }
  45.  * </pre>
  46.  *
  47.  * @since 1.2
  48.  */
  49. public class LineIterator implements Iterator<String>, Closeable {

  50.     // This class deliberately does not implement Iterable, see https://issues.apache.org/jira/browse/IO-181

  51.     /**
  52.      * Closes a {@link LineIterator} quietly.
  53.      *
  54.      * @param iterator The iterator to close, or {@code null}.
  55.      * @deprecated As of 2.6 deprecated without replacement. Please use the try-with-resources statement or handle
  56.      * suppressed exceptions manually.
  57.      * @see Throwable#addSuppressed(Throwable)
  58.      */
  59.     @Deprecated
  60.     public static void closeQuietly(final LineIterator iterator) {
  61.         IOUtils.closeQuietly(iterator);
  62.     }

  63.     /** The reader that is being read. */
  64.     private final BufferedReader bufferedReader;

  65.     /** The current line. */
  66.     private String cachedLine;

  67.     /** A flag indicating if the iterator has been fully read. */
  68.     private boolean finished;

  69.     /**
  70.      * Constructs an iterator of the lines for a {@link Reader}.
  71.      *
  72.      * @param reader the {@link Reader} to read from, not null
  73.      * @throws NullPointerException if the reader is null
  74.      */
  75.     @SuppressWarnings("resource") // Caller closes Reader
  76.     public LineIterator(final Reader reader) {
  77.         Objects.requireNonNull(reader, "reader");
  78.         if (reader instanceof BufferedReader) {
  79.             bufferedReader = (BufferedReader) reader;
  80.         } else {
  81.             bufferedReader = new BufferedReader(reader);
  82.         }
  83.     }

  84.     /**
  85.      * Closes the underlying {@link Reader}.
  86.      * This method is useful if you only want to process the first few
  87.      * lines of a larger file. If you do not close the iterator
  88.      * then the {@link Reader} remains open.
  89.      * This method can safely be called multiple times.
  90.      *
  91.      * @throws IOException if closing the underlying {@link Reader} fails.
  92.      */
  93.     @Override
  94.     public void close() throws IOException {
  95.         finished = true;
  96.         cachedLine = null;
  97.         IOUtils.close(bufferedReader);
  98.     }

  99.     /**
  100.      * Indicates whether the {@link Reader} has more lines.
  101.      * If there is an {@link IOException} then {@link #close()} will
  102.      * be called on this instance.
  103.      *
  104.      * @return {@code true} if the Reader has more lines
  105.      * @throws IllegalStateException if an IO exception occurs
  106.      */
  107.     @Override
  108.     public boolean hasNext() {
  109.         if (cachedLine != null) {
  110.             return true;
  111.         }
  112.         if (finished) {
  113.             return false;
  114.         }
  115.         try {
  116.             while (true) {
  117.                 final String line = bufferedReader.readLine();
  118.                 if (line == null) {
  119.                     finished = true;
  120.                     return false;
  121.                 }
  122.                 if (isValidLine(line)) {
  123.                     cachedLine = line;
  124.                     return true;
  125.                 }
  126.             }
  127.         } catch (final IOException ioe) {
  128.             IOUtils.closeQuietly(this, ioe::addSuppressed);
  129.             throw new IllegalStateException(ioe);
  130.         }
  131.     }

  132.     /**
  133.      * Overridable method to validate each line that is returned.
  134.      * This implementation always returns true.
  135.      * @param line  the line that is to be validated
  136.      * @return true if valid, false to remove from the iterator
  137.      */
  138.     protected boolean isValidLine(final String line) {
  139.         return true;
  140.     }

  141.     /**
  142.      * Returns the next line in the wrapped {@link Reader}.
  143.      *
  144.      * @return the next line from the input
  145.      * @throws NoSuchElementException if there is no line to return
  146.      */
  147.     @Override
  148.     public String next() {
  149.         return nextLine();
  150.     }

  151.     /**
  152.      * Returns the next line in the wrapped {@link Reader}.
  153.      *
  154.      * @return the next line from the input
  155.      * @throws NoSuchElementException if there is no line to return
  156.      * @deprecated Use {@link #next()}.
  157.      */
  158.     @Deprecated
  159.     public String nextLine() {
  160.         if (!hasNext()) {
  161.             throw new NoSuchElementException("No more lines");
  162.         }
  163.         final String currentLine = cachedLine;
  164.         cachedLine = null;
  165.         return currentLine;
  166.     }

  167.     /**
  168.      * Unsupported.
  169.      *
  170.      * @throws UnsupportedOperationException always
  171.      */
  172.     @Override
  173.     public void remove() {
  174.         throw new UnsupportedOperationException("remove not supported");
  175.     }

  176. }