package scone.util.tokenstream;


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

import com.ibm.wbi.RequestEvent;
import com.ibm.wbi.protocol.http.DocumentInfo;


/**
 * an InputStream that works on Tokens.
 * <br>TokenInputStreams are similar to MegInputStreams, but they operate
 * on Token objects instead of bytes. TokenInputStream and TokenOutputStream
 * are examples for the use of MegObject.
 * This class provides static methods that create TokenInputStreams that read
 * data from RequestEvents.
 * 
 * @author Volkert Buchmann, 
 *         IBM Almaden Research Center, 
 *         (c) Copyright IBM Corp 2000
 * @version 1.0
 */
public abstract class TokenInputStream {

    /**
     * Returns a TokenInputStream that allows to acces the input data provided by the RequestEvent as Tokens. 
     * If the RequestEvent does not provide a TokenOutputStream as token source, the Tokenizer specified by the
     * tokenizer string will parse the MegInputStream.
     * @param e the RequestEvent
     * @param tokenizer the class name of the tokenizer
     * @return the TokenInputStream
     */ 
    public static synchronized TokenInputStream create(RequestEvent e, String tokenizer) throws Exception {
        if (e.getMegObjectType() == Class.forName("scone.util.tokenstream.TokenOutputStream")) {
            return new TokenInputStreamBufferImpl((TokenOutputStream) e.getMegObject());	    
        } else {
            ((DocumentInfo) e.getRequestInfo()).getHttpResponseHeader().remove("content-length");	    
            return new TokenInputStreamTokenizerImpl(e.getMegReader(), tokenizer);	    
        }
    }

    /**
     * Returns a TokenInputStream that allows to acces the input data provided by the RequestEvent as Tokens. 
     * If the RequestEvent does not provide a TokenOutputStream as token source, the HtmlTokenizer is 
     * used to parse the MegReader.
     * @param e the RequestEvent
     * @return the TokenInputStream
     */
    public static synchronized TokenInputStream create(RequestEvent e) throws Exception {
        if (e.getMegObjectType() == Class.forName("scone.util.tokenstream.TokenOutputStream")) {
        	try {
        		return new TokenInputStreamBufferImpl((TokenOutputStream) e.getMegObject());	    
            } catch (Exception ex) {
                ex.printStackTrace();
                return null;
            }

        } else {

            // Remove the content-length, as the parser may change (and fix!) the HTML-Code. So the length of the page may change as well.
            ((DocumentInfo) e.getRequestInfo()).getHttpResponseHeader().remove("content-length");

            // Fix the content-type for documents with encoding-inforamtion -> Workaround for WBI-Bug!            
            // System.out.println(((DocumentInfo) e.getRequestInfo()).getHttpResponseHeader().get("content-type"));
            // ((DocumentInfo) e.getRequestInfo()).getHttpResponseHeader().set("Content-Type","text/html");
            try {
            	if (((DocumentInfo) e.getRequestInfo()).getHttpResponseHeader().get("content-type").toString().toLowerCase().indexOf("utf-8") >= 0) {
            		return new TokenInputStreamTokenizerImpl(e.getMegReader("iso-8859-1")); // Avoid parsing of utf-8 as this causes representation errors...
            	} else {
            		return new TokenInputStreamTokenizerImpl(e.getMegReader());
            	}
            } catch (java.io.UnsupportedEncodingException ex) {
            	System.out.print("Illegal Character Set: ");
            	System.out.println(((DocumentInfo) e.getRequestInfo()).getHttpResponseHeader().get("content-type"));
            	return new TokenInputStreamTokenizerImpl(e.getMegReader("iso-8859-1"));	    
            }
        }
    }

    /**
     * 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 abstract Token read() throws IOException;

    /**
     * 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 abstract int read(Token buf[], int offset, int length) throws IOException;

    /**
     * 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 abstract int read(Token buf[]) throws IOException;

    /**
     * Close the input stream.  If the stream is already closed, this method
     * has not effect.
     */
    public abstract void close() throws IOException;

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

    /**
     * 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 abstract boolean markSupported();

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

    /**
     * Reset the stream to its previous mark
     * @exception IOException if the stream was not marked or the mark
     * has become invalid.
     */
    public abstract void reset()throws IOException;
    
    /**
     * returns the meta info associated with this TokenInputStream
     * @return the metaInfo
     */
    public abstract Hashtable getMetaInfo(); 
}
