package w3c.dbwg.saxon;

import java.io.File;
import java.io.IOException;
import java.net.URI;
import java.util.Enumeration;
import java.util.Vector;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerConfigurationException;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.stream.StreamResult;
import javax.xml.transform.stream.StreamSource;

import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;
import org.xml.sax.SAXParseException;

public class SaxonTransform {

	public static final String JAXP_SCHEMA_LANGUAGE =
	    "http://java.sun.com/xml/jaxp/properties/schemaLanguage";
	public static final String W3C_XML_SCHEMA =
	    "http://www.w3.org/2001/XMLSchema";
	public static final String JAXP_SCHEMA_SOURCE =
        "http://java.sun.com/xml/jaxp/properties/schemaSource";
	// xml DOM
	private static Document document;
	private static DocumentBuilder parser;	
	/*
	 * 4 parameters are required with one optional
	 * 1 - list of examples requiring pattern checking eg mkdir-example.xml 
	 * 2 - input directory eg /examples/6/09
	 * 3 - output file name suffix eg -patterns.txt (which produces for example BooleanElement-patterns.txt)
	 * 4 - stylesheet eg patterns.xsl 
	 * 5 - validate the schema
	 */
    public static void main(String[] args)	    {

    	// set saxon as transformer
    	System.setProperty("javax.xml.transform.TransformerFactory", "net.sf.saxon.TransformerFactoryImpl");
    	// collection of examples
    	Vector examples = new Vector();
    	String directory = "";
    	String outputSuffix = "";
    	String styleSheet = "";
    	boolean validationRequired = false;
		try {
			if (args.length < 4 ) {
				System.out.println("Please provide required parameters ");
				System.out.println("1) xml input eg mkdir-example.xml");
				System.out.println("2) and directory for output eg ../../examples/6/09");
				System.out.println("3) and the output file suffix eg -patterns.xml which results in <example>-patterns.xml");
				System.out.println("4) and the stylesheet eg patternsdetector.xsl");
				System.out.println("5) and OPTIONALLY whether schema validation is required Y or N (default N)");
				System.exit(0);
			}
			else {
				File examplesFile = new File(args[0]);
				// get the collection of example names
				examples = parseFile(examplesFile);
				directory = args[1];
				outputSuffix = args[2];
				styleSheet = args[3];
			}	
		}
		catch (Exception e) {
			System.out.println(e.getMessage());
			System.exit(0);
		}
		//  check for validation flag 
		if (args.length == 5 ) {
			String vFlag = args[4];
			vFlag = vFlag.toUpperCase();
			if (vFlag.equalsIgnoreCase("validate"))
				validationRequired = true;
		}
    	
		// for each example check the example schema
	    TransformerFactory tfactory = TransformerFactory.newInstance();
	    Transformer transformer = null;
	    // Create a transformer for the stylesheet (there is only one stylesheet - used repeatedly).
	    try {
	    	transformer = tfactory.newTransformer(new StreamSource(styleSheet));
	    } 
		catch (TransformerConfigurationException e) {
			System.out.println(e.getMessage());
		}
		
		// setup the documentBuilderFactory to parse for validation of schema using XMLSchema.xsd
		try {
			if (validationRequired) {
				setSchemaValidationParser();
			}
		}
		catch (ParserConfigurationException e) {
			System.out.println(e.getMessage());
		}
			
		// process all the examples
    	Enumeration enumeration = examples.elements();
    	while (enumeration.hasMoreElements()) {
    		String example = (String)enumeration.nextElement();
    		String source = directory + "/" + example + "/" + example +".xsd";
	        File schemaFile = new File(source);
    		try {
    		    // validate the XML schema
    			if (validationRequired) {
    				System.out.println("Validating schema " + example + ".xsd");
    				validateSchema(schemaFile);
    			}	
    	        String output = directory + "/" + example + "/" + example + outputSuffix;
    	        File outputFile = new File(output);
    		    // Transform the source XML to System.out.
		    // System.out.println("Detecting patterns in " + example);
    		    transformer.transform(new StreamSource(source), new StreamResult(outputFile));
    		}	
    		catch (TransformerException e) {
    			System.out.println(e.getMessage());
    		}
    		catch (SAXException e) {
    			System.out.println(e.getMessage());
    		}
    		catch (ParserConfigurationException e) {
    			System.out.println(e.getMessage());
    		}
    		catch (IOException e) {
    			System.out.println(e.getMessage());
    		}
    		catch (Exception e) {
    			System.out.println(e.getMessage());
    		}
    	}	
    }    
        
    /*
     * parse the xml file with list of examples
     */
	public static Vector parseFile(File xmlfile) throws Exception, IOException, SAXException, ParserConfigurationException {
		// Get a JAXP parser factory object
		DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
		// Tell the factory what kind of parser we want 
		dbf.setValidating(false);
		// Use the factory to get a JAXP parser object
		DocumentBuilder parser = dbf.newDocumentBuilder();
	
		// Tell the parser how to handle errors.  Note that in the JAXP API,
		// DOM parsers rely on the SAX API for error handling
		parser.setErrorHandler(new org.xml.sax.ErrorHandler() {
			public void warning(SAXParseException e) {
				System.err.println("WARNING: " + e.getMessage());
			}
			public void error(SAXParseException e) {
				System.err.println("ERROR: " + e.getMessage());
			}
			public void fatalError(SAXParseException e) throws SAXException {
				System.err.println("FATAL: " + e.getMessage());
				throw e; // re-throw the error
			}
		});
		// Finally, use the JAXP parser to parse the file.  This call returns
		// A Document object.  Now that we have this object, the rest of this
		// class uses the DOM API to work with it; JAXP is no longer required.
		document = parser.parse(xmlfile);
		// Obtain the NodeList from the DOM :-
		NodeList dirNodes = document.getElementsByTagName("mkdir");
	
		// Obtain the NodeList number of entries :-
		int numDirs = dirNodes.getLength();
	
		// Iterate through the NodeList processing each directory :-
		Vector examples = new Vector();
		for (int i = 0; i < numDirs; i++) {
			Element dirTag = (Element) dirNodes.item(i);
			if (dirTag != null) {
				String dir = dirTag.getAttribute("dir");
			//	System.out.println(dir);
				examples.add(dir);
			}
		}
		return examples;
	}
	/*
	 * Get a parser for schema validation
	 */
	public static void setSchemaValidationParser() throws ParserConfigurationException {
		// Get a JAXP parser factory object
		DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();

		// Tell the factory what kind of parser we want 
		dbf.setValidating(true);
		dbf.setNamespaceAware(true);
		
		dbf.setAttribute(JAXP_SCHEMA_LANGUAGE, W3C_XML_SCHEMA);			
		File schemaSource = new File("XMLSchema.xsd");
		dbf.setAttribute(JAXP_SCHEMA_SOURCE, schemaSource);

		// Use the factory to get a JAXP parser object
		parser = dbf.newDocumentBuilder();
		// Tell the parser how to handle errors.  Note that in the JAXP API,
		// DOM parsers rely on the SAX API for error handling
		parser.setErrorHandler(new org.xml.sax.ErrorHandler() {
			public void warning(SAXParseException e) {
				System.err.println("WARNING: " + e.getMessage() + " at line no. " + e.getLineNumber() + " col " + e.getColumnNumber());
			}
			public void error(SAXParseException e) {
				System.err.println("ERROR: " + e.getMessage() + " at line no. " + e.getLineNumber() + " col " + e.getColumnNumber());
			}
			public void fatalError(SAXParseException e) throws SAXException {
				System.err.println("FATAL: " + e.getMessage() + " at line no. " + e.getLineNumber() + " col " + e.getColumnNumber());
				throw e; // re-throw the error
			}
		});
	}
	
	
	/*
	 * validate the example schema - errors are thrown if an invalid schema is found
	 */
	public static void validateSchema(File xmlschema) throws Exception, IOException, SAXException {
		parser.parse(xmlschema);
	}	
}

