com.crabshue.commons.xml.validation.XmlValidator Maven / Gradle / Ivy
package com.crabshue.commons.xml.validation;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.util.Optional;
import javax.xml.XMLConstants;
import javax.xml.transform.stream.StreamSource;
import javax.xml.validation.Schema;
import javax.xml.validation.SchemaFactory;
import javax.xml.validation.Validator;
import org.w3c.dom.ls.LSResourceResolver;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import com.crabshue.commons.exceptions.ApplicationException;
import com.crabshue.commons.exceptions.SystemException;
import com.crabshue.commons.exceptions.ValidationException;
import com.crabshue.commons.file.exceptions.FileErrorType;
import com.crabshue.commons.xml.exceptions.XmlErrorContext;
import com.crabshue.commons.xml.exceptions.XmlErrorType;
import com.crabshue.commons.xml.inputsource.InputSourceBuilder;
import com.crabshue.commons.xml.schema.XmlSchemaUtils;
import lombok.NonNull;
import lombok.extern.slf4j.Slf4j;
/**
* Utility class for XML validation.
*/
@Slf4j
public class XmlValidator {
private Object xml;
private InputSource inputSource;
private File schemaFolder = new File(".");
private URL schemaUrl;
private LSResourceResolver resourceResolver;
public static XmlValidator of(@NonNull final File xmlFile) {
final XmlValidator ret = new XmlValidator();
ret.inputSource = InputSourceBuilder.newInputSource(xmlFile);
ret.xml = xmlFile;
return ret;
}
public static XmlValidator of(@NonNull final String xml) {
final XmlValidator ret = new XmlValidator();
ret.inputSource = InputSourceBuilder.newInputSource(xml);
ret.xml = xml;
return ret;
}
public static XmlValidator of(@NonNull final byte[] xml) {
final XmlValidator ret = new XmlValidator();
ret.inputSource = InputSourceBuilder.newInputSource(xml);
ret.xml = xml;
return ret;
}
/**
* Define the folder where the schemas of the XML to validate are stored.
* If not defined, the default value is the current folder of execution.
*
* @param schemaFolder the schema folder
* @return the instance.
*/
public XmlValidator withSchemaFolder(@NonNull final File schemaFolder) {
this.schemaFolder = schemaFolder;
return this;
}
/**
* Define the URL of the schema against which to validate the XML.
* If not defined, the validator tries to extract the schema URL from the XML itself.
*
* @param schemaUrl the schema URL.
* @return the instance.
*/
public XmlValidator withSchemaUrl(@NonNull final URL schemaUrl) {
this.schemaUrl = schemaUrl;
return this;
}
/**
* Define the resource resolver to use when validating the XML.
*
* @param resourceResolver the resource resolver.
* @return the instance.
*/
public XmlValidator withResourceResolver(@NonNull final LSResourceResolver resourceResolver) {
this.resourceResolver = resourceResolver;
return this;
}
public void validate() {
try {
final SchemaFactory schemaFactory = SchemaFactory
.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
Optional.ofNullable(this.resourceResolver)
.ifPresent(schemaFactory::setResourceResolver);
final URL schemaUrl = Optional.ofNullable(this.schemaUrl)
.orElseGet(() -> XmlSchemaUtils.extractSchemaUrl(this.inputSource, this.schemaFolder));
if (schemaUrl == null) {
throw new ApplicationException(XmlErrorType.NO_SCHEMA_FOUND, "Cannot find schema URL in XML")
.addContextValue(XmlErrorContext.XML_FILE, this.xml);
}
final Schema schema = schemaFactory.newSchema(schemaUrl);
final Validator validator = schema.newValidator();
try (final InputStream xmlInputStream = this.inputSource.getByteStream()) {
if (xmlInputStream.available() == 0) {
xmlInputStream.reset();
}
validator.validate(new StreamSource(xmlInputStream));
}
} catch (SAXException e) {
throw new ValidationException(XmlErrorType.XML_INVALID_AGAINST_SCHEMA, e)
.addContextValue(XmlErrorContext.XML_FILE, this.xml);
} catch (IOException e) {
throw new SystemException(FileErrorType.ERROR_READING_FILE, e)
.addContextValue(XmlErrorContext.XML_FILE, this.xml);
}
}
}