/*
 * The contents of this file are subject to the Mozilla Public
 * License Version 1.1 (the "License"); you may not use this file
 * except in compliance with the License. You may obtain a copy of
 * the License at http://www.mozilla.org/MPL/
 *
 * Software distributed under the License is distributed on an "AS
 * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
 * implied. See the License for the specific language governing
 * rights and limitations under the License.
 *
 * The Original Code is Web Questionnaires 2
 *
 * The Initial Owner of the Original Code is European Environment
 * Agency. Portions created by TripleDev are Copyright
 * (C) European Environment Agency.  All Rights Reserved.
 *
 * Contributor(s):
 *        Anton Dmitrijev
 */
package eionet.webq.converter;

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

import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.xpath.XPathFactory;

import org.apache.log4j.Logger;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.convert.converter.Converter;
import org.springframework.web.multipart.MultipartFile;
import org.w3c.dom.Document;

import eionet.webq.dto.UploadedXmlFile;

/**
 * Performs converting from {@link MultipartFile} to {@link UploadedXmlFile}.
 *
 * @see Converter
 */
public class MultipartFileConverter implements Converter<MultipartFile, UploadedXmlFile> {
    /**
     * Logger for this class.
     */
    private static final Logger LOGGER = Logger.getLogger(MultipartFileConverter.class);

    @Value("#{application_properties['xmlschema.extract.xpath']}")
    private String xmlSchemaExtractXpath;

    @Override
    public UploadedXmlFile convert(MultipartFile multipartFile) {
        byte[] bytes = toByteArray(multipartFile);
        return new UploadedXmlFile().setContent(bytes).setName(multipartFile.getOriginalFilename())
                .setSizeInBytes(multipartFile.getSize()).setXmlSchema(extractXmlSchema(bytes));
    }

    /**
     * Extracts {@code @xsi:noNamespaceSchemaLocation} or {@code @xsi:schemaLocation} attribute value from xml root element.
     *
     * @param bytes uploaded file bytes
     * @return {@code @xsi:noNamespaceSchemaLocation} or {@code @xsi:schemaLocation} attribute value
     */
    private String extractXmlSchema(byte[] bytes) {
        DocumentBuilderFactory builderFactory = DocumentBuilderFactory.newInstance();
        XPathFactory xPathFactory = XPathFactory.newInstance();
        try {
            Document xml = builderFactory.newDocumentBuilder().parse(new ByteArrayInputStream(bytes));
            return xPathFactory.newXPath().evaluate(
                    xmlSchemaExtractXpath, xml);
        } catch (Exception e) {
            LOGGER.warn("exception thrown during extracting xml schema", e);
            return null;
        }
    }

    /**
     * Calls {@link org.springframework.web.multipart.MultipartFile#getBytes()} wrapping {@link IOException}.
     *
     * @param multipartFile uploaded file to be converted
     * @return uploaded file bytes.
     */
    private byte[] toByteArray(MultipartFile multipartFile) {
        try {
            return multipartFile.getBytes();
        } catch (IOException e) {
            LOGGER.warn("unable to transform uploaded file to bytes", e);
            return new byte[0];
        }
    }
}

@@ -804,153 +804,85 @@
 ort 
-java.io.ByteArrayInputStream;%0Aimport java.io.IOException;%0A%0Aimport javax.xml.parsers.DocumentBuilderFactory;%0Aimport javax.xml.xpath.XPathFactory;%0A
+eionet.webq.dto.UploadedXmlFile;%0Aimport org.apache.commons.lang3.StringUtils;
 %0Aimp
@@ -914,67 +914,8 @@
 er;%0A
-import org.springframework.beans.factory.annotation.Value;%0A
 impo
@@ -1031,76 +1031,198 @@
 le;%0A
+%0A
 import 
-org.w3c.dom.Document;%0A%0Aimport eionet.webq.dto.UploadedXmlFile
+javax.xml.stream.XMLInputFactory;%0Aimport javax.xml.stream.XMLStreamConstants;%0Aimport javax.xml.stream.XMLStreamReader;%0Aimport java.io.ByteArrayInputStream;%0Aimport java.io.IOException
 ;%0A%0A/
@@ -1300,24 +1300,25 @@
 XmlFile%7D.%0A *
+ 
 %0A * @see Con
@@ -1558,116 +1558,145 @@
 s);%0A
-%0A    @Value(%22#%7Bapplication_properties%5B'xmlschema.extract.xpath'%5D%7D%22)%0A    private String xmlSchemaExtractXpath
+    /**%0A     * Xsi namespace URI.%0A     */%0A    private static final String XSI_NAMESPACE_URI = %22http://www.w3.org/2001/XMLSchema-instance%22
 ;%0A%0A 
@@ -2381,112 +2381,32 @@
     
-DocumentBuilderFactory builderFactory = DocumentBuilderFactory.newInstance();%0A        XPathFactory xPath
+XMLInputFactory xmlInput
 Fact
@@ -2412,20 +2412,23 @@
 tory = X
-Path
+MLInput
 Factory.
@@ -2472,200 +2472,541 @@
     
-Document xml = builderFactory.newDocumentBuilder().parse(new ByteArrayInputStream(bytes));%0A            return xPathFactory.newXPath().evaluate(%0A                    xmlSchemaExtractXpath, xml);
+XMLStreamReader xmlStreamReader = xmlInputFactory.createXMLStreamReader(new ByteArrayInputStream(bytes));%0A            while (xmlStreamReader.hasNext()) %7B%0A                if (xmlStreamReader.next() == XMLStreamConstants.START_ELEMENT) %7B%0A                    return StringUtils.defaultString(%0A                            xmlStreamReader.getAttributeValue(XSI_NAMESPACE_URI, %22noNamespaceSchemaLocation%22),%0A                            xmlStreamReader.getAttributeValue(XSI_NAMESPACE_URI, %22schemaLocation%22));%0A                %7D%0A            %7D
 %0A   
@@ -3107,32 +3107,38 @@
 chema%22, e);%0A    
+    %7D%0A
         return n
@@ -3142,26 +3142,16 @@
 n null;%0A
-        %7D%0A
     %7D%0A%0A 

use SAX for getting schemaLocation. Refs #15619