package org.datacite.mds.validation.constraints.impl;

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

import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;
import javax.xml.XMLConstants;
import javax.xml.transform.Source;
import javax.xml.transform.stream.StreamSource;
import javax.xml.validation.Schema;
import javax.xml.validation.SchemaFactory;
import javax.xml.validation.Validator;

import org.apache.log4j.Logger;
import org.datacite.mds.util.ValidationUtils;
import org.datacite.mds.validation.constraints.ValidXML;
import org.springframework.beans.factory.annotation.Configurable;
import org.xml.sax.InputSource;
import org.xml.sax.XMLReader;
import org.xml.sax.helpers.XMLReaderFactory;

@Configurable
public class ValidXMLValidator implements ConstraintValidator<ValidXML, byte[]> {
    Logger log = Logger.getLogger(ValidXMLValidator.class);

    String xsd;

    boolean enabled;

    public void initialize(ValidXML constraintAnnotation) {
        if (!constraintAnnotation.xsd().isEmpty()) {
            this.xsd = constraintAnnotation.xsd();
        }
        log.debug("init: xsd=" + getXsd());
        log.debug("init: enabled=" + isEnabled());
    }

    public boolean isValid(byte[] xmlBytes, ConstraintValidatorContext context) {
        InputStream xml = new ByteArrayInputStream(xmlBytes);
        try {
            if (isEnabled()) {
                checkValidity(xml, xsd);
            } else {
                log.debug("validation skipped; checking only for well-formedness");
                checkWellFormedness(xml);
            }
        } catch (Exception e) {
            ValidationUtils.addConstraintViolation(context, "xml error: " + e.getMessage());
            return false;
        }
        return true;
    }

    private void checkWellFormedness(InputStream xml) throws Exception {
        XMLReader reader = XMLReaderFactory.createXMLReader();
        reader.parse(new InputSource(xml));
    }

    private void checkValidity(InputStream xml, String xsd) throws Exception {
        SchemaFactory schemaFactory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
        Source schemaSource = new StreamSource(xsd);
        Schema schema = schemaFactory.newSchema(schemaSource);
        Validator validator = schema.newValidator();
        Source xmlSource = new StreamSource(xml);
        validator.validate(xmlSource);
    }

    public String getXsd() {
        return xsd;
    }

    public void setXsd(String xsd) {
        this.xsd = xsd;
    }

    public boolean isEnabled() {
        return enabled;
    }

    public void setEnabled(boolean enabled) {
        this.enabled = enabled;
    }

}

@@ -215,39 +215,8 @@
 xt;%0A
-import javax.xml.XMLConstants;%0A
 impo
@@ -298,87 +298,8 @@
 ce;%0A
-import javax.xml.validation.Schema;%0Aimport javax.xml.validation.SchemaFactory;%0A
 impo
@@ -362,24 +362,71 @@
 g4j.Logger;%0A
+import org.datacite.mds.service.SchemaService;%0A
 import org.d
@@ -516,16 +516,79 @@
 lidXML;%0A
+import org.springframework.beans.factory.annotation.Autowired;%0A
 import o
@@ -645,16 +645,65 @@
 urable;%0A
+import org.springframework.stereotype.Component;%0A
 import o
@@ -816,16 +816,27 @@
 gurable%0A
+@Component%0A
 public c
@@ -1007,16 +1007,69 @@
 enabled;
+%0A    %0A    @Autowired%0A    SchemaService schemaService;
 %0A%0A    pu
@@ -1375,21 +1375,16 @@
 te%5B%5D xml
-Bytes
 , Constr
@@ -1419,70 +1419,8 @@
 ) %7B%0A
-        InputStream xml = new ByteArrayInputStream(xmlBytes);%0A
     
@@ -1497,13 +1497,8 @@
 (xml
-, xsd
 );%0A 
@@ -1883,27 +1883,22 @@
 medness(
-InputStream
+byte%5B%5D
  xml) th
@@ -1973,24 +1973,87 @@
 MLReader();%0A
+        InputStream xmlStream = new ByteArrayInputStream(xml);%0A
         read
@@ -2080,16 +2080,22 @@
 urce(xml
+Stream
 ));%0A    
@@ -2132,35 +2132,18 @@
 ity(
-InputStream xml, String xsd
+byte%5B%5D xml
 ) th
@@ -2172,266 +2172,264 @@
    S
-chemaFactory schemaFactory = S
+tring schemaLocation = s
 chema
-Factory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);%0A        Source schemaSource = new StreamSource(xsd);%0A        Schema schema = schemaFactory.newSchema(schemaSource);%0A        Validator validator = schema.newValidator(
+Service.getSchemaLocation(xml);%0A        log.debug(%22schemaLocation=%22 + schemaLocation);%0A        Validator validator = schemaService.getSchemaValidator(schemaLocation);%0A        InputStream xmlStream = new ByteArrayInputStream(xml
 );%0A 
@@ -2470,24 +2470,30 @@
 amSource(xml
+Stream
 );%0A        v

use parsed schema location for validation