

package gov.va.med.cds.testharness.xml;


import gov.va.med.cds.testharness.util.TemplateJarHelper;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;

import javax.xml.transform.TransformerConfigurationException;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.dom4j.DocumentException;
import org.dom4j.io.SAXReader;
import org.dom4j.util.XMLErrorHandler;
import org.xml.sax.SAXException;


/**
 * <p>
 * XmlValidator class is used to validate Xml instance documents against Xsd
 * schema definition documents.
 * </p>
 * The current naming convention of schema jar file mandates, template name and
 * schema jar filename to be same. Code might need to be refactored if the
 * convention changes.
 * 
 * @date March 26th, 2007
 * 
 */
public class XmlValidator
{
    private static Log logger = LogFactory.getLog( XmlValidator.class );

    private static String templateCachePath;


    public static void setTemplateCachePath( String templateCachePath )
    {
        XmlValidator.templateCachePath = templateCachePath;
    }


    /**
     * Validates a Xml instance document against the provided XSD schema
     * definition file
     * 
     * @param xml
     *            is the string representation of the XML instance document
     * @param templateId
     *            tempalte id of the template aganist which the xml has to be
     *            validated
     * @throws XmlValidationException
     *             if an error occurs while validation Xml instance document
     * @throws IOException
     * @throws TransformerConfigurationException
     */
    public static void validateXml( String xml, String templateId )
        throws XmlValidationException,
            TransformerConfigurationException,
            IOException
    {
        validateXml( xml, templateId, "Clinicaldata" );
    }


    /**
     * Validates a Xml instance document against the provided XSD schema
     * definition file
     * 
     * @param xml
     *            is the string representation of the XML instance document
     * @param templateId
     *            tempalte id of the template aganist which the xml has to be
     *            validated
     * @throws XmlValidationException
     *             if an error occurs while validation Xml instance document
     * @throws IOException
     * @throws TransformerConfigurationException
     */
    public static void validateXml( String xml, String templateId, String schemaNamespace )
        throws XmlValidationException,
            TransformerConfigurationException,
            IOException
    {
        try
        {
            TemplateJarHelper.unjar( templateCachePath, templateId );
        }
        catch ( IOException e )
        {
            throw new XmlValidationException( "Error extracting template schema files.", e );
        }

        String pathToXsd = String.format( "%s %s/%s/template/%s.xsd", schemaNamespace, templateCachePath, templateId, templateId );

        validate( xml, pathToXsd );
    }


    private static void validate( String xml, String xsdPath, XMLErrorHandler errorHandler )
        throws XmlValidationException
    {
        SAXReader reader = new SAXReader();

        // add error handler which turns any errors into XML
        reader.setErrorHandler( errorHandler );

        try
        {
            InputStream is = new ByteArrayInputStream( xml.getBytes() );
            // specify the schema to use

            reader.setProperty( "http://apache.org/xml/properties/schema/external-schemaLocation", xsdPath );
            // checking
            reader.setFeature( "http://apache.org/xml/features/validation/schema-full-checking", true );
            reader.setFeature( "http://apache.org/xml/features/validation/schema", true );
            reader.setFeature( "http://xml.org/sax/features/namespaces", true );
            // set the validation feature to true to report validation errors
            reader.setFeature( "http://xml.org/sax/features/validation", true );
            // Enable validation
            reader.setValidation( true );

            // parse the document
            reader.read( is );
        }
        catch ( SAXException e )
        {
            throw new XmlValidationException( e );
        }
        catch ( DocumentException e )
        {
            throw new XmlValidationException( e );
        }
        catch ( Exception exception )
        {
            throw new XmlValidationException( exception );
        }
    }


    private static void validate( String xml, String xsdPath )
        throws XmlValidationException
    {
        XMLErrorHandler errorHandler = new XMLErrorHandler();
        validate( xml, xsdPath, errorHandler );

        if ( errorHandler.getErrors().elements().size() > 0 )
        {
            throw new XmlValidationException( errorHandler.getErrors().asXML() );
        }
        else
        {
            if ( logger.isDebugEnabled() )
            {
                logger.debug( "Validation Success \n" + errorHandler.getErrors().asXML() );
            }
        }
    }

}
