/*
 * PartReader.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.IOException;
import java.io.Reader;
// needed for the (commented) main
/*import java.io.StringReader;/**/

/**
 * A reader that reads until a certain character is found. After that it ignores
 * everything until the next line separator and returns -1 (an end of input
 * sign). It can be used to read 2 sections in a single string (or stream or
 * file or whatever else that can be processed by some {@link Reader}) separated
 * from each other by some character (a '$' for instance) as 2 separated things.
 * For example, the following code:
 *
 *<pre>
 *String s = "first section$\n"
 *        + "second section$\n"
 *        + "third section";
 *PartReader pr = new PartReader(new StringReader(s), '$');
 *int c;
 *for (int i = 0; i < 3; i++) {
 *    System.out.print("<section number=\"" + (i+1) + "\">");
 *    c = 0;
 *    while ((c = pr.read()) != -1)
 *        System.out.print((char) c);
 *    System.out.println("</section>");
 *}
 *</pre>
 *
 * would produce the following output:
 *
 *<pre>
 *&lt;section number="1"&gt;first section&lt;/section&gt;
 *&lt;section number="2"&gt;second section&lt;/section&gt;
 *&lt;section number="3"&gt;third section&lt;/section&gt;
 *</pre>
 *
 * @author Martin Slota
 * @version 1.0.0
 */
public class PartReader extends Reader {
	/**
	 * The underlying {@link Reader} instance.
	 */
	private final Reader in;
	
	/**
	 * The character that, when found, is handled as end of input.
	 */
	private final char stopChar;
	
	/**
	 * Creates a new instance for the given {@link Reader} and with the given
	 * stop character.
	 *
	 * @param in the underlying {@link Reader} instance
	 * @param stopChar the stop character
	 */
	public PartReader(Reader in, char stopChar) {
		super();
		this.in = in;
		this.stopChar = stopChar;
	}
	
	/**
	 * Closes the underlying {@link Reader}.
	 *
	 * @throws IOException if a I/O error occurs while closing the underlying
	 * {@link Reader}
	 */
	@Override
	public void close() throws IOException {
		in.close();
	}
	
	/**
	 * Reads one character from the underlying reader. In case it is the stop
	 * character, ignores everything until the end of line and returns -1.
	 *
	 * @return the character read or -1
	 * @throws IOException if a I/O error occurs while reading from the
	 * underlying {@link Reader}
	 */
	@Override
	public int read() throws IOException {
		int c = in.read();
		if (c == stopChar) {
			ignoreUntilEOL();
			return -1;
		}
		return c;
	}
	
	/**
	 * Reads {@code len} characters and puts them to {@code cbuf} beginning at
	 * position {@code off}. The stop character is handled differently&#8212;if
	 * it is found, the rest of the line is ignored and no more characters are
	 * read.
	 *
	 * @param cbuf the character buffer where the characters are stored
	 * @param off starting position in the buffer
	 * @param len number of characters to read
	 * @return number of characters actually read or -1 if the end of stream or
	 * the stop character has been found
	 * @throws IOException if a I/O error occurs while reading from the
	 * underlying {@link Reader}
	 */
	@Override
	public int read(char[] cbuf, int off, int len) throws IOException {
		if ((off < 0) || (off > cbuf.length) || (len < 0)
		|| ((off + len) > cbuf.length) || ((off + len - 1) < 0)) {
			throw new IndexOutOfBoundsException();
		} else if (len == 0) {
			return 0;
		}
		
		int end = off + len;
		int c;
		int count = 0;
		for (int pos = off; pos < end; pos++, count++) {
			c = in.read();
			if (c == -1 || c == stopChar) {
				ignoreUntilEOL();
				return (pos == off) ? -1 : count;
			}
			cbuf[pos] = (char) c;
		}
		return len;
	}
	
	/**
	 * Ignores all characters until (and including) the next end of line. If the
	 * line ends with "\r\n" and the underlying reader supports marking (see
	 * {@link Reader#markSupported()}, then both characters are ignored.
	 * Otherwise only '\r' is ignored and '\n' stays unread.
	 *
	 * @throws IOException if a I/O error occurs while reading from the
	 * underlying {@link Reader}
	 */
	private void ignoreUntilEOL() throws IOException {
		int c = in.read();
		while (c != -1 && c != '\n' && c != '\r')
			c = in.read();
		if (c == '\r' && in.markSupported()) {
			in.mark(1);
			if (in.read() != '\n')
				in.reset();
		}
	}
	
	/**
	 * Main method containing the example code from class description.
	 *
	 * @param args input arguments (ignored)
	 */
	/*public static void main(String [] args) throws IOException {
		String s = "first section$\n"
				+ "second section$\n"
				+ "third section";
		PartReader pr = new PartReader(new StringReader(s), '$');
		int c;
		for (int i = 0; i < 3; i++) {
			System.out.print("<section number=\"" + (i+1) + "\">");
			c = 0;
			while ((c = pr.read()) != -1)
				System.out.print((char) c);
			System.out.println("</section>");
		}
	}/**/
}