/*
 * Copyright (c) 2007 Henri Sivonen
 * Copyright (c) 2008-2009 Mozilla Foundation
 *
 * Permission is hereby granted, free of charge, to any person obtaining a 
 * copy of this software and associated documentation files (the "Software"), 
 * to deal in the Software without restriction, including without limitation 
 * the rights to use, copy, modify, merge, publish, distribute, sublicense, 
 * and/or sell copies of the Software, and to permit persons to whom the 
 * Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in 
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 
 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 
 * DEALINGS IN THE SOFTWARE.
 */

package nu.validator.htmlparser.sax;

import nu.validator.htmlparser.impl.HtmlAttributes;
import nu.validator.htmlparser.impl.TreeBuilder;

import org.xml.sax.Attributes;
import org.xml.sax.ContentHandler;
import org.xml.sax.SAXException;
import org.xml.sax.SAXParseException;
import org.xml.sax.ext.LexicalHandler;

class SAXStreamer extends TreeBuilder<Attributes>{

    private static final char[] ISINDEX_PROMPT = "This is a searchable index. Enter search keywords: ".toCharArray();

    private ContentHandler contentHandler = null;
    private LexicalHandler lexicalHandler = null;
    
    SAXStreamer() {
        super();
    }
    
    @Override
    protected void addAttributesToElement(Attributes element, HtmlAttributes attributes) throws SAXException {
        Attributes existingAttrs = element;
        for (int i = 0; i < attributes.getLength(); i++) {
            String qName = attributes.getQName(i);
            if (existingAttrs.getIndex(qName) < 0) {
                fatal();
            }
        }
    }

    @Override
    protected void appendCharacters(Attributes parent, char[] buf, int start, int length) throws SAXException {
        contentHandler.characters(buf, start, length);
    }

    /**
     * @see nu.validator.htmlparser.impl.TreeBuilder#appendIsindexPrompt(java.lang.Object)
     */
    @Override protected void appendIsindexPrompt(Attributes parent)
            throws SAXException {
        contentHandler.characters(ISINDEX_PROMPT, 0, ISINDEX_PROMPT.length);
    }

    @Override
    protected void appendChildrenToNewParent(Attributes oldParent, Attributes newParent) throws SAXException {
        fatal();
    }

    @Override
    protected void appendComment(Attributes parent, char[] buf, int start, int length) throws SAXException {
        if (lexicalHandler != null) {
            lexicalHandler.comment(buf, start, length);
        }
    }

    @Override
    protected void appendCommentToDocument(char[] buf, int start, int length)
            throws SAXException {
        if (lexicalHandler != null) {
            lexicalHandler.comment(buf, start, length);
        }
    }

    @Override
    protected Attributes createElement(String ns, String name, HtmlAttributes attributes) throws SAXException {
        return attributes;
    }

    @Override
    protected Attributes createHtmlElementSetAsRoot(HtmlAttributes attributes) throws SAXException {
        return attributes;
    }

    @Override
    protected void detachFromParent(Attributes element) throws SAXException {
        fatal();
    }

    @Override
    protected void appendElement(Attributes child, Attributes newParent) throws SAXException {
    }

    @Override
    protected boolean hasChildren(Attributes element) throws SAXException {
        return false;
    }
    
    public void setContentHandler(ContentHandler handler) {
        contentHandler = handler;
    }

    public void setLexicalHandler(LexicalHandler handler) {
        lexicalHandler = handler;
    }

    /**
     * @see nu.validator.htmlparser.impl.TreeBuilder#appendDoctypeToDocument(java.lang.String, java.lang.String, java.lang.String)
     */
    @Override
    protected void appendDoctypeToDocument(String name, String publicIdentifier, String systemIdentifier) throws SAXException {
        if (lexicalHandler != null) {
            lexicalHandler.startDTD(name, publicIdentifier, systemIdentifier);
            lexicalHandler.endDTD();
        }
    }

    /**
     * @see nu.validator.htmlparser.impl.TreeBuilder#elementPopped(String, java.lang.String, java.lang.Object)
     */
    @Override
    protected void elementPopped(String ns, String name, Attributes node) throws SAXException {
        contentHandler.endElement(ns, name, name);
    }

    /**
     * @see nu.validator.htmlparser.impl.TreeBuilder#elementPushed(String, java.lang.String, java.lang.Object)
     */
    @Override
    protected void elementPushed(String ns, String name, Attributes node) throws SAXException {
        contentHandler.startElement(ns, name, name, node);
    }

    /**
     * @see nu.validator.htmlparser.impl.TreeBuilder#end()
     */
    @Override
    protected void end() throws SAXException {
        contentHandler.endDocument();
    }

    /**
     * @see nu.validator.htmlparser.impl.TreeBuilder#start()
     */
    @Override
    protected void start(boolean fragment) throws SAXException {
        contentHandler.setDocumentLocator(tokenizer);
        if (!fragment) {
            contentHandler.startDocument();
        }
    }

    protected void fatal() throws SAXException {
        SAXParseException spe = new SAXParseException(
                "Cannot recover after last error. Any further errors will be ignored.",
                tokenizer);
        if (errorHandler != null) {
            errorHandler.fatalError(spe);
        }
        throw spe;
    }

    @Override protected void insertFosterParentedCharacters(char[] buf,
            int start, int length, Attributes table, Attributes stackParent)
            throws SAXException {
        fatal();
    }

    @Override protected void insertFosterParentedChild(Attributes child,
            Attributes table, Attributes stackParent) throws SAXException {
        fatal();
    }

}
