// XML parser (uses SAX)
// usage java XMLValid file.xml >outputfile.xml
// Sends report to console

import java.io.*;
import org.xml.sax.*;
import org.xml.sax.helpers.DefaultHandler;
import javax.xml.parsers.SAXParserFactory;  
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.parsers.SAXParser;

public class XMLValid extends DefaultHandler {
    static private Writer out;
    public static void main(String args[]) throws Exception {
        // Use an instance of ourselves as the SAX event handler
        try{
            DefaultHandler handler = new XMLValid();
        
            SAXParserFactory factory = SAXParserFactory.newInstance();
            factory.setValidating(true);
            
            // Set up output stream
            out = new OutputStreamWriter(System.out, "UTF8");
            
            // Parse the input 
            SAXParser saxParser = factory.newSAXParser();
            File ifile = new File(args[0]);
            saxParser.parse( ifile, handler );
        } catch (SAXParseException ex) {
           System.out.println("\n** Parsing error"
              + ", line " + ex.getLineNumber()
              + ", uri " + ex.getSystemId());
           System.out.println("   " + ex.getMessage() );

           // Use the contained exception, if any
           Exception  x = ex;
           if (ex.getException() != null)
               x = ex.getException();
           x.printStackTrace();

        } catch (SAXException sxe) {
           // Error generated by this application
           // (or a parser-initialization error)
           Exception  x = sxe;
           if (sxe.getException() != null)
               x = sxe.getException();
           x.printStackTrace();

        } catch (ParserConfigurationException pce) {
            // Parser with specified options can't be built
            pce.printStackTrace();

        } catch (IOException ioe) {
           // I/O error
           ioe.printStackTrace();
        }

    }

    private void emit(String s)  throws SAXException     {
        try {
            out.write(s);
            out.flush();
        } catch (IOException e) {
            throw new SAXException("I/O error", e);
        }
    }


    private void nl()   throws SAXException   {
        String lineEnd =  System.getProperty("line.separator");
        try {
            out.write(lineEnd);
       
        } catch (IOException e) {
            throw new SAXException("I/O error", e);
        }
    }

    public void startDocument() throws SAXException    {
        emit("<?xml version='1.0' encoding='UTF-8'?>");
        nl();
    }

    public void endDocument()  throws SAXException     {
        try {
            nl();
            out.flush();
        } catch (IOException e) {
            throw new SAXException("I/O error", e);
        }
    }



    public void startElement(String namespaceURI,
                             String sName, // simple name (localName)
                             String qName, // qualified name
                             Attributes attrs)
     throws SAXException    {
        String eName = sName; // element name
        if ("".equals(eName)) eName = qName; // namespaceAware = false
        emit("<"+eName);
        if (attrs != null) {
            for (int i = 0; i < attrs.getLength(); i++) {
                String aName = attrs.getLocalName(i); // Attr name 
                if ("".equals(aName)) aName = attrs.getQName(i);
                emit(" ");
                emit(aName+"=\""+attrs.getValue(i)+"\"");
            }
        }
        emit(">");
    }

    public void endElement(String namespaceURI,
                           String sName, // simple name
                           String qName  // qualified name
                          )   throws SAXException     {
        String eName = sName; // element name
        if ("".equals(eName)) eName = qName; // namespaceAware = false
        emit("</"+eName+">");
    }


    public void characters(char buf[], int offset, int len)
    throws SAXException     {
        String s = new String(buf, offset, len);
        emit(s);
    }

// ErrorHandler Interface

    // watch for errors
    public void error(SAXParseException e) 
        throws SAXParseException {
        throw e;
    }

    // and warnings
    public void warning(SAXParseException e) 
        throws SAXParseException {
        System.out.println("Warning at " + e.getLineNumber() + " " +
                           e.getMessage());
    }

}
