/*
 *    Geotools2 - OpenSource mapping toolkit
 *    http://geotools.org
 *    (C) 2002, Geotools Project Managment Committee (PMC)
 *
 *    This library is free software; you can redistribute it and/or
 *    modify it under the terms of the GNU Lesser General Public
 *    License as published by the Free Software Foundation;
 *    version 2.1 of the License.
 *
 *    This library 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
 *    Lesser General Public License for more details.
 *
 */
package org.geotools.xml;

import java.net.URI;
import java.util.Stack;
import java.util.logging.Level;
import java.util.logging.Logger;

import org.geotools.xml.handlers.xsi.IgnoreHandler;
import org.geotools.xml.handlers.xsi.RootHandler;
import org.geotools.xml.schema.Schema;
import org.xml.sax.Attributes;
import org.xml.sax.Locator;
import org.xml.sax.SAXException;
import org.xml.sax.SAXParseException;
import org.xml.sax.helpers.DefaultHandler;


/**
 * XSISAXHandler purpose.
 * 
 * <p>
 * This is a schema handler. Code here has been modified from code written by
 * Ian Schneider.
 * </p>
 * 
 * <p>
 * This class contains one stack used to store part of the parse tree. The
 * ElementHandlers found on the stack have direct next handlers placed on the
 * stack. So here's the warning, be careful to read how you may be affecting
 * (or forgetting to affect) the stack.
 * </p>
 *
 * @author dzwiers, Refractions Research, Inc. http://www.refractions.net
 * @author $Author:$ (last modification)
 * @source $URL: http://svn.geotools.org/geotools/tags/2.4.1/modules/library/xml/src/main/java/org/geotools/xml/XSISAXHandler.java $
 * @version $Id: XSISAXHandler.java 27862 2007-11-12 19:51:19Z desruisseaux $
 *
 * @see XSIElementHandler
 */
public class XSISAXHandler extends DefaultHandler {
    // the logger -- should be used for debugging (assuming there are bugs LOL)
    protected final static Logger logger = org.geotools.util.logging.Logging.getLogger(
            "net.refractions.xsi.sax");

    // the stack of handers representing a portion of the parse tree
    private Stack handlers = new Stack();

    // The schema being used to parse into
    private Schema schema = null;

    // The root parsing element
    protected RootHandler rootHandler = null;

    // the Locator is used for end-user debugging
    private Locator locator;

    /** Collects string chunks in {@link #characters(char[], int, int)} 
     * callback to be handled at the beggining of {@link #endElement(String, String, String)}
     */
    private StringBuffer characters = new StringBuffer();
    
    // the schema uri being parsed. This is important to resolve relative uris
    //    private URI uri;

    /**
     * should never be called
     */
    private XSISAXHandler() {
        /* not used*/
    }

    /**
     * Stores the uri being parsed to help resolve relative uris within the
     * document.
     *
     * @param uri
     */
    public XSISAXHandler(URI uri) {
        //        this.uri = uri;
        rootHandler = new RootHandler(uri);
    }

    /**
     * @see org.xml.sax.ContentHandler#startPrefixMapping(java.lang.String,
     *      java.lang.String)
     */
    public void startPrefixMapping(String arg0, String arg1){
        rootHandler.startPrefixMapping(arg0, arg1);
    }

    /**
     * Implementation of endDocument.
     *
     * @see org.xml.sax.ContentHandler#endDocument()
     */
    public void endDocument(){
        handlers.pop();
    }

    /**
     * Implementation of startDocument.
     *
     * @see org.xml.sax.ContentHandler#startDocument()
     */
    public void startDocument(){
        try {
            handlers.push(rootHandler);
        } catch (RuntimeException e) {
            logger.warning(e.toString());
            throw e;
        }
    }

    /**
     * Implementation of characters. push String
     *
     * @param ch
     * @param start
     * @param length
     *
     * @throws SAXException
     *
     * @see org.xml.sax.ContentHandler#characters(char[], int, int)
     */
    public void characters(char[] ch, int start, int length)
        throws SAXException {
    	characters.append(ch, start, length);
    }
    
    /**
     * Handles the string chunks collected in {@link #characters}.
     */
    private void handleCharacters() throws SAXException{
        final String text = characters.toString();
        characters.setLength(0);
    	if(text.length() == 0){
    		return;
    	}
    	XSIElementHandler peek = null;
        try {
            if ((text != null) && !"".equals(text.trim())) {
                peek = (XSIElementHandler) handlers.peek();
				peek.characters(text);
            }
        } catch (SAXException e) {
            logger.warning(e.toString());
            throw e;
        }
    }
    
    /**
     * Implementation of endElement. push NS,Name
     *
     * @param namespaceURI
     * @param localName
     * @param qName
     *
     * @throws SAXException
     *
     * @see org.xml.sax.ContentHandler#endElement(java.lang.String,
     *      java.lang.String, java.lang.String)
     */
    public void endElement(String namespaceURI, String localName, String qName)
        throws SAXException {
    	handleCharacters();
        logger.fine("END: " + qName);

        try {
            XSIElementHandler element = (XSIElementHandler) handlers.pop();
            element.endElement(namespaceURI,
                localName);
        } catch (SAXException e) {
            logger.warning(e.toString());
            throw e;
        }
    }

    /**
     * Implementation of startElement.
     *
     * @param namespaceURI
     * @param localName
     * @param qName
     * @param atts
     *
     * @throws SAXException
     *
     * @see org.xml.sax.ContentHandler#startElement(java.lang.String,
     *      java.lang.String, java.lang.String, org.xml.sax.Attributes)
     */
    public void startElement(String namespaceURI, String localName,
        String qName, Attributes atts) throws SAXException {
    	characters.setLength(0);
        logger.fine("START: " + qName);

        try {
            XSIElementHandler eh = ((XSIElementHandler) handlers.peek())
                .getHandler(namespaceURI, localName);
            logger.finest("Parent Node = "
                + ((XSIElementHandler) handlers.peek()).getClass().getName());

            if (eh == null) {
                eh = new IgnoreHandler();
            }

            logger.finest("This Node = " + eh.getClass().getName());
            logger.finest("This Node = " + localName + " :: " + namespaceURI);

            handlers.push(eh);
            eh.startElement(namespaceURI, localName, atts);
        } catch (SAXException e) {
            logger.warning(e.toString());
            throw e;
        }
    }

    /**
     * <p>
     * Sets the logging level for all the XSISAXHandlers.
     * </p>
     *
     * @param l
     */
    public static void setLogLevel(Level l) {
        logger.setLevel(l);
        XSIElementHandler.setLogLevel(l);
    }

    /**
     * getSchema purpose.
     * 
     * <p>
     * This method should be called only after the parse has been completed.
     * This method will then return a compressed schema instance.
     * </p>
     *
     * @return schema
     *
     * @throws SAXException
     */
    public Schema getSchema() throws SAXException {
        if (schema == null) {
            schema = rootHandler.getSchema();
        }

        return schema;
    }

    /**
     * Implementation of error.
     *
     * @param exception
     *
     * @see org.xml.sax.ErrorHandler#error(org.xml.sax.SAXParseException)
     */
    public void error(SAXParseException exception) {
        logger.severe("ERROR " + exception.getMessage());
        logger.severe("col " + locator.getColumnNumber() + ", line "
            + locator.getLineNumber());
    }

    /**
     * Implementation of fatalError.
     *
     * @param exception
     *
     * @throws SAXException
     *
     * @see org.xml.sax.ErrorHandler#fatalError(org.xml.sax.SAXParseException)
     */
    public void fatalError(SAXParseException exception)
        throws SAXException {
        logger.severe("FATAL " + exception.getMessage());
        logger.severe("col " + locator.getColumnNumber() + ", line "
            + locator.getLineNumber());
        throw exception;
    }

    /**
     * Implementation of warning.
     *
     * @param exception
     *
     * @see org.xml.sax.ErrorHandler#warning(org.xml.sax.SAXParseException)
     */
    public void warning(SAXParseException exception) {
        logger.warning("WARN " + exception.getMessage());
        logger.severe("col " + locator.getColumnNumber() + ", line "
            + locator.getLineNumber());
    }

    /**
     * @see org.xml.sax.ContentHandler#setDocumentLocator(org.xml.sax.Locator)
     */
    public void setDocumentLocator(Locator locator) {
        super.setDocumentLocator(locator);
        this.locator = locator;
    }
}
