
import java.io.IOException;
import java.io.File;
import java.io.FileReader;
import java.io.FileNotFoundException;

import org.xml.sax.SAXException;
import org.xml.sax.SAXParseException;
import org.w3c.dom.Document; 
import org.xml.sax.ErrorHandler;
import org.xml.sax.helpers.DefaultHandler;
import org.xml.sax.InputSource;

import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.ParserConfigurationException;

import javax.xml.transform.Source;
import javax.xml.transform.stream.StreamSource;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.sax.SAXSource;

import javax.xml.XMLConstants;
import javax.xml.validation.Schema;
import javax.xml.validation.SchemaFactory;
import javax.xml.validation.Validator;

/**
 * Die Klasse Validation parsed und validiert XML-Dateien (arg[1..]).
 * Erster Parameter wählt DTD oder Schema.
 * JDK &gt;= 1.5
 */

public class Validation {

    static void usage() {
        System.out.println("Usage: Validation [DOCTYPE|SCHEMA(schemafile)] <xml-files ...>");
    }


    public static void main(String[] args) {
        if ( args.length < 2 ) {
           usage();
           return;
        }
        String type = args[0];
        String[] files = new String[ args.length-1 ];
        System.arraycopy(args,1,files,0,files.length);
        if ( type.equals("DOCTYPE") ) {
            validateDTD( files );
        } else if ( type.startsWith("SCHEMA(") ) {
            int b = type.indexOf('(');
            int e = type.indexOf(')',b);
            String schema = type.substring(b+1,e);
            System.out.println("schema file = " + schema);
            validateSchema( schema, files );
        } else if ( type.startsWith("RELAXNG(") ) {
            int b = type.indexOf('(');
            int e = type.indexOf(')',b);
            String schema = type.substring(b+1,e);
            System.out.println("schema file = " + schema);
            System.out.println("RELAXNG() not jet implemented");
            return;
        } else {
            usage();
           return;
        }
    }



    static boolean validateDTD(String[] files) {
        DocumentBuilderFactory pfac = DocumentBuilderFactory.newInstance();
        DocumentBuilder parser = null;
        MyErrorHandler eh = new MyErrorHandler();
        try {
            pfac.setValidating(true);
            pfac.setNamespaceAware(true);
            pfac.setXIncludeAware(true);
            //pfac.setExpandEntityReferences(false);
            parser = pfac.newDocumentBuilder();
            parser.setErrorHandler( eh );
        } catch (ParserConfigurationException e) {
            e.printStackTrace();
            return false;
        }
        boolean valid = true;
        File xmlFile;
        for ( int i = 0; i < files.length; i++ ) {
            xmlFile = new File( files[i] );
            valid &= validateDTD( eh, parser, xmlFile );
            eh.reset();
        }
        return valid;
    }

    static boolean validateDTD(MyErrorHandler eh, 
                               DocumentBuilder parser, 
                               File file) {
        Document document = null;
        try {
            document = parser.parse( file );
        } catch (SAXException e) {
            System.out.println(file.getName() + " not valid" );
            e.printStackTrace();
            return false;
        } catch (IOException e) {
            e.printStackTrace();
            return false;
        }
        if ( eh.errors() > 0 || eh.fatalErrors() > 0 ) {
           System.out.println(file.getName() + " not valid, " + eh );
           return false;
        } else if ( eh.warnings() > 0 ) {
           System.out.println(file.getName() + " is valid, " + eh );
           return true;
        } else {
           System.out.println(file.getName() + " is valid" );
           return true;
        }
    }



    static boolean validateSchema(String sfile, String[] files) {
        File schemaFile = new File( sfile );
        StreamSource schemaSource = new StreamSource( schemaFile );
        SchemaFactory sfac = SchemaFactory.newInstance(
                             //    XMLConstants.XML_DTD_NS_URI
                                 XMLConstants.W3C_XML_SCHEMA_NS_URI 
                             //    XMLConstants.RELAXNG_NS_URI
                                                      );
        Schema schema = null;
        try {
            schema = sfac.newSchema( schemaSource );
        } catch (SAXException e) {
            e.printStackTrace();
            return false;
        }
        Validator validator = schema.newValidator();
        MyErrorHandler eh = new MyErrorHandler();
        validator.setErrorHandler( eh );

        DocumentBuilderFactory pfac = DocumentBuilderFactory.newInstance();
        DocumentBuilder parser = null;
        try {
            pfac.setNamespaceAware(true);
            pfac.setXIncludeAware(true);
            //pfac.setValidating(true); do not use in schema
            //pfac.setExpandEntityReferences(false);
            parser = pfac.newDocumentBuilder();
        } catch (ParserConfigurationException e) {
            e.printStackTrace();
            return false;
        }

        boolean valid = true;
        File xmlFile;
        for ( int i = 0; i < files.length; i++ ) {
            xmlFile = new File( files[i] );
            valid &= validateSchema( eh, validator, parser, xmlFile );
            eh.reset();
        }
        return valid;
    }

    static boolean validateSchema(MyErrorHandler eh,
                                  Validator validator, 
                                  DocumentBuilder parser,
                                  File xmlFile) {
        /* SAXSource saxSource;
            saxSource = new SAXSource( 
                            new InputSource( 
                                new FileReader(xmlFile) ) );
            validator.validate( saxSource );
        */
        DOMSource domSource = null;
        try {
            Document document = null;
            document = parser.parse( xmlFile );
            domSource = new DOMSource(document);
        } catch (SAXException e) {
            e.printStackTrace();
            return false;
        } catch (FileNotFoundException e) {
            e.printStackTrace();
            return false;
        } catch (IOException e) {
            e.printStackTrace();
            return false;
        }

        try {
            validator.validate( domSource );
        } catch (SAXException e) {
            //e.printStackTrace();
            return false;
        } catch (IOException e) {
            e.printStackTrace();
            return false;
        }
        if ( eh.errors() > 0 || eh.fatalErrors() > 0 ) {
           System.out.println(xmlFile.getName() + " not valid, " + eh );
           return false;
        } else if ( eh.warnings() > 0 ) {
           System.out.println(xmlFile.getName() + " is valid, " + eh );
           return true;
        } else {
           System.out.println(xmlFile.getName() + " is valid" );
           return true;
        }
    }

}


class MyErrorHandler extends DefaultHandler implements ErrorHandler {

    int errors = 0;
    int fatalErrors = 0;
    int warnings = 0;

    public void reset() {
        errors = 0;
        fatalErrors = 0;
        warnings = 0;
    }

    public void error(SAXParseException exception) {
        System.out.print("Error: ");
        errors++;
        message(exception);
    }

    public void fatalError(SAXParseException exception) {
        System.out.print("FatalError: ");
        fatalErrors++;
        message(exception);
    }

    public void warning(SAXParseException exception) {
        System.out.print("Warning: ");
        warnings++;
        message(exception);
    }

    void message(SAXParseException exception) {
        if ( exception.getSystemId() != null ) {
           System.out.print(exception.getSystemId());
           System.out.println(", line " + exception.getLineNumber());
        }
        System.out.println( exception.getMessage() );
        // System.out.println( exception.toString() );
    }



    public String toString() {
        return counters();
    }

    public String counters() {
        StringBuffer s = new StringBuffer();
        s.append(warnings + " warnings, ");
        s.append(errors + " errors, ");
        s.append(fatalErrors + " fatalErrors");
        return s.toString();
    }

    public int errors() {
        return errors;
    }

    public int fatalErrors() {
        return fatalErrors;
    }

    public int warnings() {
        return warnings;
    }

}