/*$cb
 *    This file is a part of libopenmhp.
 *
 *    Copyright (C) 2005 Mikko Novaro <miknov@gmail.com>
 *
 *    See main header file (libopenmhp/openmhp.h) for licensing and
 *    contact information.
 $ce*/

package libopenmhp.xmlhandler;

import org.xml.sax.*;
import org.xml.sax.helpers.*;
// import javax.xml.parsers.*;
import org.apache.xerces.parsers.SAXParser;

/*
import java.io.IOException; 
import javax.xml.parsers.FactoryConfigurationError;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;
*/

import org.xml.sax.*;
import org.xml.sax.helpers.*;

import java.io.*;

import libopenmhp.Debug;

/** A class to handle the incoming XML communication from the "C" side
 * of the library. Currently the incomming communication consists of
 * remote control key events, that are parsed and fed to OpenMHP. XML
 * handling can be easily extended by simply writing a new class for
 * each new XML element. This class will extend the
 * <code>XMLElement</code> and should be placed in the
 * <code>libopenmhp.xmlhandler.elem</code> package.
 * @see XMLElement
 * @see libopenmhp.xmlhandler.elem
 */
public class XMLHandler extends DefaultHandler {

    public static final boolean DEBUG = false;

    private XMLElement curElem;
    private InputStream inStream;

    public XMLHandler(java.io.InputStream input) {
        super();
        curElem = new RootElement();
        inStream = input;
    }

    public void runHandler() throws Exception {
	/*
	SAXParserFactory factory = SAXParserFactory.newInstance();
	SAXParser p = null;
	try {
            SAXParser saxParser = factory.newSAXParser();
	    saxParser.parse(inStream, this);
        } catch (Throwable t) {
            t.printStackTrace();
	    System.exit(1);
        }
	*/

	// Below: parsing with "Xerces".

	/*
	try {
	    SAXParserFactory factory = SAXParserFactory.newInstance();
	    SAXParser parser = factory.newSAXParser();
	    // non-validating:
            parser.setFeature("http://xml.org/sax/features/validation", false);
	    // validating:
            // parser.setFeature("http://xml.org/sax/features/validation", true);
	    DefaultHandler handler = this;
	    parser.parse(inStream, handler);
	} 
	catch (FactoryConfigurationError e) {
	    Debug.err(this, "unable to get a document builder factory");
	} 
	catch (ParserConfigurationException e) {
	    Debug.err(this, "parser was unable to be configured");
	}
	catch (SAXException e) {
	    Debug.err(this, "parsing error");
	} 
	catch (IOException e) {
	    Debug.err(this, "i/o error");
	}
	*/


	SAXParser p = new SAXParser();
        try {
	    // non-validating:
            p.setFeature("http://xml.org/sax/features/validation", false);
	    // validating:
            // p.setFeature("http://xml.org/sax/features/validation", true);
        } catch (SAXException e) {
            Debug.err(this, "error in setting up parser feature");
        }

        System.setProperty("org.xml.sax.driver",
                   "org.apache.xerces.parsers.SAXParser" );

        XMLReader xr = p; // XMLReaderFactory.createXMLReader();
        xr.setContentHandler(this);

        InputStreamReader r = new InputStreamReader(inStream);
        xr.parse(new InputSource(r));
    }

    /** Return a new element object for a given name. */
    public XMLElement getElement(String name) {
        Class elemClass = null;
        XMLElement elem = null;
        String elemClassName = getElementClassName(name);
        try {
            elemClass = Class.forName(elemClassName);
            elem = (XMLElement)elemClass.newInstance();
        } catch (Exception e) {
            Debug.err(this, e.toString());
            System.exit(0);
        }
        return elem;
    }

    /** Gets the name of a class that represents the given
     * xml-element. (NOTE: class names in package libopenmhp.xmlhandler.elem
     * equal -- case sensitively -- to xml element names.)*/
    public String getElementClassName(String name) {
	/* // the commented part gets class names from the actual class file names...
        String elemLocation = ELEM_DIR;
        String elemPackage = this.getClass().getPackage().getName() + ".elem.";
        File elemDir = new File(elemLocation);
        String classFiles[] = elemDir.list(new FilenameFilter() {
            public boolean accept(File dir, String name) {
                return name.endsWith(".class");
            }
        });
        for (int i=0; i<classFiles.length; i++) {
            // Remove ".class" from the filename:
            classFiles[i] =
                classFiles[i].substring(0, classFiles[i].length()-6);
            if (name.toLowerCase().equals(classFiles[i].toLowerCase()))
                return elemPackage+classFiles[i];
        }
        throw new RuntimeException("Element class for XML-element \"" +
            name + "\" was not found.");
	*/

	/* Violating good java programming practices, the names of the
	 * classes in libopenmhp.xmlhandler.elem package are written
	 * in lower case... */
	return "libopenmhp.xmlhandler.elem." + name.toLowerCase();
    }

    // SAX ContentHandler / DefaultHandler -- overwritten methods :

    public void startElement(String uri, String localName, String qName,
        Attributes attributes)
    {
        if (DEBUG) Debug.dbg(this, "begun XML element: " + localName);
        XMLElement elem = getElement(localName);
        /* Add the new element as a child of the previous current element.
         * Set the previous current element as the parent of the new
         * element and set the new element as the current element: */
        curElem.addChildNode(elem); // TODO: move setting parent to "addChildNode"?
        elem.setParentNode(curElem);
        elem.setAttributes(attributes);
        curElem = elem;
        elem.start();
    }

    public void endElement(String uri, String localName, String qName) {
        curElem.end();
        // Restore the parent element of the previous
        // current element as the current element:
        curElem = curElem.getParentNode();
    }

    public void characters(char[] ch, int start, int length) {
        String s = "";
        for (int i=start; i<start+length; i++) {
            s += ch[i];
        }
        curElem.characters(s);
    }

    public void ignorableWhitespace(char[] ch, int start, int length)
    { }

    // ErrorHandler's overwritten methods:

    /*
    public void error(SAXParseException exception) throws SAXException {
        super.error(exception);
        System.exit(0);
    }

    public void fatalError(SAXParseException exception) throws SAXException {
        super.fatalError(exception);
        System.exit(0);
    }

    public void warning(SAXParseException exception) throws SAXException {
        super.warning(exception);
        System.exit(0);
    }
    */

}
