package scone.util.tokenstream;


import java.io.IOException;
import java.io.Reader;
import java.util.Hashtable;

import com.ibm.wbi.Copyright;


/**
 * converts a Reader into a TokenInputStream. If no Tokenizer is specified, the
 * standard HTML tokenizer is used. Any Tokenizer can be specified to process
 * HTML, XML or any other doument type.
 * 
 * @author Volkert Buchmann, 
 *         IBM Almaden Research Center, 
 *         (c) Copyright IBM Corp 2000
 * @version 1.0
 */

public class TokenInputStreamTokenizerImpl extends TokenInputStream {

    private final static String COPYRIGHT = Copyright.COPYRIGHT;

    private Reader reader = null;
    private Tokenizer tokenizer = null;

    private boolean closed = false;
    
    private Hashtable metaInfo = new Hashtable();

    
    /**
     * creates a new TokenInputStreamTokenizer that parses a Reader into HTML tokens.
     * @param reader the Reader
     */
    public TokenInputStreamTokenizerImpl(Reader reader) {
        try {
            this.reader = reader;
            this.tokenizer = new HtmlTokenizer();
            this.tokenizer.assign(reader);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * creates a new TokenInputStreamTokenizerImpl that parses a Reader into Tokens 
     * using the specified Tokenizer.
     * @param reader the Reader
     * @param tokenizer the complete class name of the Tokenizer
     */
    public TokenInputStreamTokenizerImpl(Reader reader, String tokenizer) {
        try {
            this.reader = reader;
            this.tokenizer = (Tokenizer) ((Class.forName(tokenizer)).newInstance());
            this.tokenizer.assign(reader);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * Read a Token from the input stream.  Blocks if no data available and
     * end of stream not yet reached. If no Token is available because the
     * end of the stream has been reached, null is returned.
     * @return  The next Token from the stream, or null if the end of the stream has been reached
     * @exception   IOException     If stream closed.
     * @exception   MarkPreservedException If read would invalidate preserved
     *              mark
     */
    public Token read() throws IOException {
        checkClosed();
        // read the next Token from the Tokenizer
        return tokenizer.nextToken();
    }

    /**
     * Read multiple Tokens from the input stream.  Blocks if no data available
     * and end of stream not yet reached.
     * @param   buf     The Token buffer to populate.
     * @param   offset  The offset in <code>buf</code> to starting writing to.
     * @param   length  The amount of Tokens to write.
     * @return  The amount of Tokens read, or -1 if no more Tokens available to
     *          read.
     * @exception   IOException     If stream closed.
     * @exception   MarkPreservedException If read would invalidate preserved
     *              mark
     */
    public int read(Token buf[], int offset, int length) throws IOException {
        checkClosed();
        int end = offset + length;
        Token t = null;
        int i;

        for (i = offset; (i < end) && ((t = read()) != null); i++) {
            buf[i] = t;
        }
        if (t == null && i == offset) {
            return -1;
        }
        return i - offset;
    }

    /**
     * Read Tokens from the input stream filling the specified Token array.
     * Blocks if no data available and end of stream not yet reached.
     * @param   buf     The Token buffer to populate.
     * @return  The amount of Tokens read, or -1 if no more Tokens available to
     *          read.
     * @exception   IOException     If stream closed.
     * @exception   MarkPreservedException If read would invalidate preserved
     *              mark
     */
    public int read(Token buf[]) throws IOException {
        return read(buf, 0, buf.length);
    }

    /**
     * Close the input stream.  If the stream is already closed, this method
     * has not effect.
     */
    public void close() throws IOException {
        if (!closed) {
            closed = true;
            reader.close();
        }
    }

    /**
     * Determines if the input stream is closed.
     * @return  <code>true</code> if stream closed; else <code>false</code>.
     */
    public boolean isClosed() {
        return closed;
    }

    /**
     * Test whether <code>mark()</code> and <code>reset()</code> are supported.
     * @return <code>true</code> if <code>mark()</code> and
     * <code>reset()</code> are supported, <code>false</code>
     * otherwise.
     */
    public boolean markSupported() {
        return reader.markSupported();
    }

    /**
     * Mark the stream for later resetting
     * @param readlimit The number of Tokens read beyond which the mark
     * will become invalid.
     */
    public void mark(int readAheadLimit)throws IOException {
        reader.mark(readAheadLimit);
    }

    /**
     * Reset the stream to its previous mark
     * @exception IOException if the stream was not marked or the mark
     * has become invalid.
     */
    public void reset()throws IOException {
        reader.reset();
    }

    /**
     * Check and determine if the stream is closed.  If it is closed, then
     * throw an exception.
     * @exception   IOException     If stream closed.
     */
    private void checkClosed() throws IOException {
        if (closed) {
            throw new IOException("MegObjectInputStream is closed to read.");
        }
    }

    /**
     * returns the meta info associated with this TokenInputStream
     * @return the metaInfo
     */
    public Hashtable getMetaInfo() {
    	//System.out.print("TokenInputStreamTokenizerImpl says:");
		//System.out.println(metaInfo);
        return metaInfo;
    }
}
