/* This file is part of the Joshua Machine Translation System. * * Joshua is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 * of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free * Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, * MA 02111-1307 USA */ package joshua.util.io; import java.util.Iterator; import java.util.NoSuchElementException; import java.io.IOException; /** * Wraps a reader with "line" index information. * * @author wren ng thornton <wren@users.sourceforge.net> * @version $LastChangedDate: 2009-03-26 15:06:57 -0400 (Thu, 26 Mar 2009) $ */ public class IndexedReader<E> implements Reader<E> { /** A name for the type of elements the reader produces. */ private final String elementName; /** The number of elements the reader has delivered so far. */ private int lineNumber; /** The underlying reader. */ private final Reader<E> reader; public IndexedReader(String elementName, Reader<E> reader) { this.elementName = elementName; this.lineNumber = 0; this.reader = reader; } //=============================================================== // Public (non-interface) methods //=============================================================== /** Return the number of elements delivered so far. */ public int index() { return this.lineNumber; } /** * Wrap an IOException's message with the index when it * occured. */ public IOException wrapIOException(IOException oldError) { IOException newError = new IOException( "At " + this.elementName + " " + this.lineNumber + ": " + oldError.getMessage()); newError.initCause(oldError); return newError; } //=============================================================== // Reader //=============================================================== /** Delegated to the underlying reader. */ public boolean ready() throws IOException { try { return this.reader.ready(); } catch (IOException oldError) { throw wrapIOException(oldError); } } /** * Delegated to the underlying reader. Note that we do not * have a <code>finalize()</code> method; however, when we * fall out of scope, the underlying reader will too, so its * finalizer may be called. For correctness, be sure to * manually close all readers. */ public void close() throws IOException { try { this.reader.close(); } catch (IOException oldError) { throw wrapIOException(oldError); } } /** Delegated to the underlying reader. */ public E readLine() throws IOException { E line; try { line = this.reader.readLine(); } catch (IOException oldError) { throw wrapIOException(oldError); } ++this.lineNumber; return line; } //=============================================================== // Iterable -- because sometimes Java can be very stupid //=============================================================== /** Return self as an iterator. */ public Iterator<E> iterator() { return this; } //=============================================================== // Iterator //=============================================================== /** Delegated to the underlying reader. */ public boolean hasNext() { return this.reader.hasNext(); } /** Delegated to the underlying reader. */ public E next() throws NoSuchElementException { E line = this.reader.next(); // Let exceptions out, we'll wrap any errors a closing time. ++this.lineNumber; return line; } /** * If the underlying reader supports removal, then so do * we. Note that the {@link #index()} method returns the * number of elements delivered to the client, so removing * an element from the underlying collection does not affect * that number. */ public void remove() throws UnsupportedOperationException { this.reader.remove(); } }