/**
* Copyright 2008-2016 Qualogy Solutions B.V.
*
* Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.qualogy.qafe.bind.io.document;
import java.io.BufferedInputStream;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.util.HashMap;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.xml.XMLConstants;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.Source;
import javax.xml.transform.stream.StreamSource;
import javax.xml.validation.SchemaFactory;
import org.w3c.dom.Document;
import org.xml.sax.ErrorHandler;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import org.xml.sax.SAXParseException;
import com.qualogy.qafe.bind.orm.jibx.BindException;
public class DocumentLoader {
/**
* JAXP attribute used to configure the schema language for validation.
*/
//private static final String SCHEMA_LANGUAGE_ATTRIBUTE = "http://java.sun.com/xml/jaxp/properties/schemaLanguage";
/**
* JAXP attribute value indicating the XSD schema language.
*/
//private static final String XSD_SCHEMA_LANGUAGE = "http://www.w3.org/2001/XMLSchema";
public static final Logger logger = Logger.getLogger(DocumentLoader.class.getName());
private ErrorHandler errorHandler = new SimpleSaxErrorHandler(logger);
private static final Map<String, DocumentBuilder> schemaValidators = new HashMap<String, DocumentBuilder>();
private Document loadDocument(InputSource xmlInputSource, boolean validationMode) {
Document doc = null;
try {
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
logger.log(Level.FINE, "Using JAXP provider [{0}]", factory.getClass().getName());
// In case of validationMode, xmlInputSource will be copied to newXMLInputSource for the second parsing,
// because after the first parsing the xmlInputSource is at the end,
// so when use it again the 'Premature end of file.' exception will raise
InputSource newXMLInputSource = null;
if (validationMode) {
if (xmlInputSource.getSystemId() != null) {
newXMLInputSource = new InputSource(xmlInputSource.getSystemId());
} else if (xmlInputSource.getByteStream() != null) {
BufferedInputStream bufferedXMLInputStream = new BufferedInputStream(xmlInputSource.getByteStream());
ByteArrayOutputStream xmlOutputStream1 = new ByteArrayOutputStream();
ByteArrayOutputStream xmlOutputStream2 = new ByteArrayOutputStream();
try {
while (true) {
int data = bufferedXMLInputStream.read();
if (data == -1) {
break;
}
xmlOutputStream1.write(data);
xmlOutputStream2.write(data);
}
xmlOutputStream1.flush();
xmlOutputStream2.flush();
xmlInputSource = new InputSource((InputStream)new ByteArrayInputStream(xmlOutputStream1.toByteArray()));
newXMLInputSource = new InputSource((InputStream)new ByteArrayInputStream(xmlOutputStream2.toByteArray()));
} finally {
xmlOutputStream1.close();
xmlOutputStream2.close();
}
}
}
DocumentBuilder builder = factory.newDocumentBuilder();
builder.setErrorHandler(errorHandler);
doc = builder.parse(xmlInputSource);
if (validationMode) {
String xsdName = getSchemaResourceName(doc);
builder = schemaValidators.get(xsdName);
if (builder == null) {
SchemaFactory schemaFactory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
InputStream xsdInputStream = getContextClassLoader().getResourceAsStream(xsdName);
LocalXSDResolver localXSDResolver = new LocalXSDResolver();
xsdInputStream = localXSDResolver.resolveToLocalXSDs(xsdInputStream);
Source xsdSource = new StreamSource(xsdInputStream);
factory.setSchema(schemaFactory.newSchema(xsdSource));
factory.setNamespaceAware(true);
builder = factory.newDocumentBuilder();
builder.setErrorHandler(errorHandler);
schemaValidators.put(xsdName, builder);
}
builder.parse(newXMLInputSource);
}
} catch (ParserConfigurationException e) {
throw new BindException(e);
} catch (SAXParseException e) {
StringBuffer message = new StringBuffer();
String fileName = e.getSystemId();
if (fileName != null) {
fileName = " '" + fileName.substring(fileName.lastIndexOf("/") + 1) + "' ";
}
if (fileName == null) {
fileName = " ";
}
message.append("XML document" + fileName + "is invalid: ");
message.append("[line: " + e.getLineNumber() + ", column: " + e.getColumnNumber() + "]");
message.append(" - " + e.getMessage());
throw new BindException(message.toString());
} catch (SAXException e) {
throw new BindException("XML document is invalid", e);
} catch (IOException e) {
throw new BindException("Parser configuration exception parsing XML", e);
}
return doc;
}
public Document loadDocument(InputStream is, boolean validationMode){
return loadDocument(new InputSource(is), validationMode);
}
public Document loadDocument(File file, boolean validationMode){
if (file == null) {
throw new IllegalArgumentException("File cannot be null");
}
String uri = "file:" + file.getAbsolutePath();
if (File.separatorChar == '\\') {
uri = uri.replace('\\', '/');
}
uri = file.toURI().toString();
return loadDocument(new InputSource(uri), validationMode);
}
public static ClassLoader getContextClassLoader() {
ClassLoader cl = null;
try {
cl = Thread.currentThread().getContextClassLoader();
} catch (Throwable ex) {
// Cannot access thread context ClassLoader - falling back to system class loader...
}
return cl;
}
public static String getSchemaResourceName(Document document) {
String xsi_schemaLocation = document.getDocumentElement().getAttribute("xsi:schemaLocation");
String resouceName = xsi_schemaLocation.substring(xsi_schemaLocation.lastIndexOf("/") + 1);
return resouceName;
}
}