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 inputStream = new ByteArrayInputStream(xmlBytes);
        if (!isEnabled()) {
            log.debug("validation skipped; checking only for well-formness");
            try {
                XMLReader reader = XMLReaderFactory.createXMLReader();
                reader.parse(new InputSource(inputStream));
            } catch (Exception e) {
                ValidationUtils.addConstraintViolation(context, "xml error: " + e.getMessage());
                return false;
            }
            return true;
        }
        
        SchemaFactory schemaFactory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
        Source schemaSource = new StreamSource(xsd);
        Schema schema;
        try {
            schema = schemaFactory.newSchema(schemaSource);
            Validator validator = schema.newValidator();
            Source xml = new StreamSource(inputStream);
            validator.validate(xml);
        } catch (Exception e) {
            ValidationUtils.addConstraintViolation(context, "xml error: " + e.getMessage());
            return false;
        }

        return true;
    }

    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;
    }

}

@@ -909,20 +909,16 @@
 class);%0A
-    
 %0A    Str
@@ -926,21 +926,16 @@
 ng xsd;%0A
-     
 %0A    boo
@@ -1327,27 +1327,19 @@
 tStream 
-inputStream
+xml
  = new B
@@ -1369,16 +1369,34 @@
 Bytes);%0A
+        try %7B%0A    
         
@@ -1399,17 +1399,16 @@
     if (
-!
 isEnable
@@ -1410,24 +1410,90 @@
 nabled()) %7B%0A
+                checkValidity(xml, xsd);%0A            %7D else %7B%0A    
             
@@ -1550,16 +1550,18 @@
 ell-form
+ed
 ness%22);%0A
@@ -1576,149 +1576,52 @@
     
-try %7B%0A                XMLReader reader = XMLReaderFactory.createXMLReader();%0A                reader.parse(new InputSource(inputStream));%0A    
+    checkWellFormedness(xml);%0A            %7D%0A
     
@@ -1640,36 +1640,32 @@
 (Exception e) %7B%0A
-    
             Vali
@@ -1745,36 +1745,32 @@
 ));%0A            
-    
 return false;%0A  
@@ -1779,57 +1779,303 @@
     
+%7D%0A        return true;%0A
     %7D%0A
+%0A
     
-        return true
+private void checkWellFormedness(InputStream xml) throws Exception %7B%0A        XMLReader reader = XMLReaderFactory.createXMLReader();%0A        reader.parse(new InputSource(xml))
 ;%0A    
+%7D%0A%0A
     
-%7D%0A        
+private void checkValidity(InputStream xml, String xsd) throws Exception %7B
 %0A   
@@ -2244,42 +2244,8 @@
 ema 
-schema;%0A        try %7B%0A            
 sche
@@ -2288,20 +2288,16 @@
 ource);%0A
-    
         
@@ -2345,28 +2345,24 @@
 ();%0A        
-  
-  
 Source xml =
@@ -2355,24 +2355,30 @@
   Source xml
+Source
  = new Strea
@@ -2389,19 +2389,11 @@
 rce(
-inputStream
+xml
 );%0A 
@@ -2399,20 +2399,16 @@
         
-  
-  
 validato
@@ -2425,192 +2425,15 @@
 (xml
-);%0A        %7D catch (Exception e) %7B%0A            ValidationUtils.addConstraintViolation(context, %22xml error: %22 + e.getMessage());%0A            return false;%0A        %7D%0A%0A        return true
+Source)
 ;%0A  

clean code