import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;

import org.xml.sax.helpers.DefaultHandler;

import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
import org.xml.sax.SAXParseException;


/**
 * Lecture 10's demonstration of validation
 * by XML DTD or XML Schema.  Just like Lecture 9's,
 * except that SAX events are reported, whitespace
 * characters are displayed as "\n", "\t", and "\r"
 * explicitly, and line numbers are reported in errors.
 *
 * @author  Computer Science E-259
 **/

public class SAXValidator3 extends DefaultHandler
{
    /**
     * Main driver.  Expects one command-line argument: the name of the 
     * XML file to validate
     *
     * @param argv [0] - filename
     */

    public static void main(String [] argv)
    {
        if (argv.length == 0)
        {
            System.out.println("Usage: SAXValidator3 file [dtd|xsd]");
            System.exit(1);
        }

        // grab filename
        String input = argv[0];

        // grab validation mechanism, if any
        String validator = (argv.length > 1) ? argv[1] : null;

        try 
        {
            // instantiate a reference to a SAX parser
            SAXParser parser = null;
        
            // instantiate a SAX parser factory
            SAXParserFactory factory = SAXParserFactory.newInstance();

            // instantiate a SAX parser, enabling validation as requested
            if (validator != null && validator.equals("dtd"))
            {
                factory.setValidating(true);
                parser = factory.newSAXParser();
                System.out.println("Validation by DTD on.");
            }
            else if (validator != null && validator.equals("xsd"))
            {
                factory.setNamespaceAware(true);
                factory.setValidating(true);
                parser = factory.newSAXParser();
                parser.setProperty
                 (
                  "http://java.sun.com/xml/jaxp/properties/schemaLanguage",
                  "http://www.w3.org/2001/XMLSchema"
                 );
                System.out.println("Validation by XML Schema on.");
            }
            else
            {
                factory.setValidating(false);
                parser = factory.newSAXParser();
                System.out.println("Validation off.");
            }

            // instantiate our little handler
            SAXValidator3 handler = new SAXValidator3();

            // parse the file
            parser.parse(input, handler);

        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * Report a startElement event.
     *
     * @param   uri namespace
     * @param   localName   name of element, sans namespace
     * @param   qName       name of element, with namespace
     * @param   attributes  element's collection of attributes
     * 
     * @throws  SAXException    general SAX error or warning
     */

    public void startElement(String uri, String localName, 
                             String qName, Attributes atts)
     throws SAXException
    {
        System.out.print("startElement(\"" + qName + ", {");
        for (int i = 0; i < atts.getLength(); i++)
        {
            System.out.print("(\"" + atts.getQName(i) + "\", \"" + 
                             atts.getValue(i) + "\")");
            if (i != atts.getLength() - 1)
                System.out.print(", ");
        }
        System.out.println("});");
    }


    /**
     * Report a characters event.
     *
     * @param   ch      characters
     * @param   start   start position in the character array
     * @param   length  number of characters to use from the character array
     * 
     * @throws  SAXException    general SAX error or warning
     */

    public void characters(char[] ch, int start, int length)
     throws SAXException
    {
        String s = new String(ch, start, length);
        s = s.replaceAll("\n", "\\\\n");
        s = s.replaceAll("\t", "\\\\t");
        s = s.replaceAll("\r", "\\\\r");
        System.out.println("characters(\"" + s + "\");");
    }


    /**
     * Report an endElement event.
     *
     * @param   uri         namespace
     * @param   localName   name of element, sans namespace
     * @param   qName       name of element, with namespace
     * 
     * @throws  SAXException    general SAX error or warning
     */

    public void endElement(String uri, String localName, String qName)
     throws SAXException
    {
        System.out.println("endElement(\"" + qName + "\");");
    }


    /**
     * Report a startDocument event.
     */

    public void startDocument() throws SAXException
    {
        System.out.println("\nstartDocument();");
    }


    /**
     * Report an endDocument event.
     * 
     * @throws  SAXException    general SAX error or warning
     */

    public void endDocument() throws SAXException
    {
        System.out.println("endDocument();\n");
    }
    

    /**
     * Receive notification of a parser warning.
     *
     * @param e  the exception thrown
     */

    public void warning (SAXParseException e) 
    {
        System.out.println("Parsing warning at line " + e.getLineNumber() + 
                           ":  " + e.getMessage());
    }


    /**
     * Receive notification of a recoverable parser error. 
     *
     * @param e  the exception thrown
     */

    public void error (SAXParseException e) 
    {
        System.out.println("Parsing error at line " + e.getLineNumber() + 
                           ": " + e.getMessage());
    }


    /**
     * Report a fatal XML parsing error. 
     *
     * @param e  the exception thrown
     */

    public void fatalError (SAXParseException e) 
    {
        System.out.println("Fatal parsing error at line " + e.getLineNumber() +
                           ":  " + e.getMessage());
        System.exit(1);
    }
}
