/*
 * TabSeparatedReader.java
 *
 * Copyright (C) 2006 - 2007 Martin Slota
 *
 * This program is free software; you can redistribute it and/or modify it under
 * the terms of the GNU General Public License as published by the Free Software
 * Foundation; either version 2 of the License, or (at your option) any later
 * version.
 *
 * This program 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 General Public License for more
 * details.
 *
 * You should have received a copy of the GNU General Public License along with
 * this program; if not, write to the Free Software Foundation, Inc., 51
 * Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 */

/*
 * History:
 * v0.1 (2006-12-31): taken from the propositional EVOLP implementation
 * v0.2 (2007-01-02): documentation added
 * v1.0.0 (2007-05-05):
 * - promoted to version 1.0.0 :o)
 */

package lp.test.util;

import java.io.Closeable;
import java.io.IOException;
import java.io.Reader;

/**
 * Reads strings separated by tabs, newline characters or comments from a
 * stream. They are returned one by one by the {@link #getNext()} method.
 *
 * @author Martin Slota
 * @version 1.0.0
 */
public class TabSeparatedReader implements Closeable {
	/**
	 * The stream from which the strings are read.
	 */
	private final Reader in;
	
	/**
	 * Used to build a string that is returned.
	 */
	private final StringBuilder sb;
	
	/**
	 * A lookahead character.
	 */
	private int la;
	
	/**
	 * Creates a new instance that reads from the given stream.
	 *
	 * @param in the stream to read from
	 * @throws IOException if an I/O error occurs while reading the first
	 * character from {@code in}
	 */
	public TabSeparatedReader(Reader in) throws IOException {
		this.in = in;
		sb = new StringBuilder();
		newLA();
	}
	
	/**
	 * Closes the underlying {@link Reader}.
	 *
	 * @throws IOException if a I/O error occurs while closing the underlying
	 * {@link Reader}
	 */
	public void close() throws IOException {
		in.close();
	}
	
	
	/**
	 * Returns strings separated by tabs, newline characters or comments in the
	 * underlying stream one by one. A comment is anything from a '%' character
	 * until the next line break. The separating parts of input (tabs, newline
	 * characters and comments) are never part of the returned string. A
	 * sequence of separating parts is handled as a single separating part (this
	 * means that an empty string is never returned). {@code null} is returned
	 * if the end of stream is reached.
	 *
	 * @return the string found or {@code null} if the end of input is reached
	 * @throws IOException if a I/O error occurs while reading from the
	 * underlying {@link Reader}
	 */
	public String getNext() throws IOException {
		while (Character.isWhitespace(la))
			newLA();
		while (la == '%') {
			while (la != -1 && la != '\r' && la != '\n')
				newLA();
			while (Character.isWhitespace(la))
				newLA();
		}
		
		if (la == -1)
			return null;
		
		sb.setLength(0);
		while (la != -1 && (!Character.isWhitespace(la) || la == ' ')) {
			sb.append((char) la);
			newLA();
		}
		return sb.toString();
	}
	
	/**
	 * Reads the next character from the underlying {@link Reader} and stores it
	 * in {@link #la}.
	 */
	private void newLA() throws IOException {
		if (la != -1)
			la = in.read();
	}
}
