com.crabshue.commons.xslt.XsltExecutor Maven / Gradle / Ivy
package com.crabshue.commons.xslt;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.URI;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import javax.xml.transform.Result;
import javax.xml.transform.Source;
import javax.xml.transform.Templates;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerConfigurationException;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.URIResolver;
import javax.xml.transform.stream.StreamResult;
import javax.xml.transform.stream.StreamSource;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.w3c.dom.Node;
import org.xml.sax.InputSource;
import com.crabshue.commons.exceptions.ApplicationException;
import com.crabshue.commons.exceptions.SystemException;
import com.crabshue.commons.file.FileSystemUtils;
import com.crabshue.commons.xml.inputsource.InputSourceBuilder;
import com.crabshue.commons.xslt.exceptions.XsltErrorContext;
import com.crabshue.commons.xslt.exceptions.XsltErrorType;
import lombok.NonNull;
/**
* XSLT executor.
*/
public class XsltExecutor {
private static final String FACTORY_CLASS_NAME = net.sf.saxon.TransformerFactoryImpl.class.getName();
private static TransformerFactory factory = TransformerFactory.newInstance(FACTORY_CLASS_NAME, XsltExecutor.class.getClassLoader());
private static Logger logger = LoggerFactory.getLogger(XsltExecutor.class);
private URI xsltUri;
private File xmlFile;
private InputSource xmlInputSource;
private File outputFile;
private File workingFolder = new File(".");
private Map parameters = new HashMap<>();
private URIResolver uriResolver = null;
private URIResolver transformerUriResolver = null;
/**
* Prepare an XSLT executor with a {@link URI XSLT URI}.
*
* @param xsltUri the XSLT URI.
* @return instance.
*/
public static XsltExecutor of(@NonNull final URI xsltUri) {
final XsltExecutor ret = new XsltExecutor();
ret.xsltUri = xsltUri;
ret.uriResolver = new RelativeXsltUriResolver(ret.xsltUri);
return ret;
}
/**
* Prepare an XSLT executor with a {@link File XSLT file}.
*
* @param xsltFile the XSLT file.
* @return instance.
*/
public static XsltExecutor of(@NonNull final File xsltFile) {
return of(xsltFile.toURI());
}
/**
* Set the XML {@link File}.
*
* @param xmlFile the XML file.
* @return instance.
*/
public XsltExecutor withXmlFile(@NonNull final File xmlFile) {
this.xmlFile = xmlFile;
return this;
}
/**
* Set the XML {@link InputSource}.
*
* @param xmlInputSource the XML input source.
* @return instance.
*/
public XsltExecutor withXmlInputSource(@NonNull final InputSource xmlInputSource) {
this.xmlInputSource = xmlInputSource;
return this;
}
/**
* Set the XML {@link Node}.
*
* @param xmlNode the XML input node.
* @return instance.
*/
public XsltExecutor withXmlNode(@NonNull final Node xmlNode) {
this.xmlInputSource = InputSourceBuilder.newInputSource(xmlNode);
return this;
}
/**
* Set the output {@link File}.
*
* @param outputFile the output file.
* @return instance
*/
public XsltExecutor withOutputFile(@NonNull final File outputFile) {
this.outputFile = outputFile;
return this;
}
/**
* Set the working {@link File folder} for the XSLT execution.
* Default value is {@code new File("."}
*
* @param workingFolder the working folder.
* @return instance.
*/
public XsltExecutor withWorkingFolder(@NonNull final File workingFolder) {
this.workingFolder = workingFolder;
return this;
}
/**
* Set parameters map for the XSLT execution.
*
* @param parameters the parameters.
* @return instance.
*/
public XsltExecutor withParameters(@NonNull final Map parameters) {
this.parameters.clear();
this.parameters.putAll(parameters);
return this;
}
/**
* Add parameter for the XSLT execution.
*
* @param name the parameter name.
* @param value the parameter value.
* @return instance.
*/
public XsltExecutor withParameter(@NonNull final String name,
@NonNull final String value) {
this.parameters.put(name, value);
return this;
}
/**
* Set the XSLT factory URI resolver to use during the XSLT compilation.
*
* @param uriResolver the URI resolver.
* @return instance.
*/
public XsltExecutor withUriResolver(@NonNull final URIResolver uriResolver) {
this.uriResolver = uriResolver;
return this;
}
/**
* Set the XSLT transformer URI resolver to use during the XSLT execution.
*
* @param transformerUriResolver the URI resolver.
* @return instance.
*/
public XsltExecutor withTransformerUriResolver(@NonNull final URIResolver transformerUriResolver) {
this.transformerUriResolver = transformerUriResolver;
return this;
}
/**
* Execute the XSLT stylesheet.
*
* @return the output file.
*/
public File execute() {
FileSystemUtils.provideFolder(outputFile.getParentFile());
try {
final Transformer xsltTransformer = newTransformer();
Optional.ofNullable(this.transformerUriResolver)
.ifPresent(xsltTransformer::setURIResolver);
// passing parameters
parameters.forEach(xsltTransformer::setParameter);
final String backupWorkingDirectory = System.getProperty("user.dir");
System.setProperty("user.dir", workingFolder.getAbsolutePath());
try (InputStream is = resolveInputStream();
OutputStream os = new FileOutputStream(outputFile)) {
// Prepare the input and output files
final Source source = new StreamSource(is);
final Result result = new StreamResult(os);
// Apply the xsl file to the source file and write the result to the output file
xsltTransformer.transform(source, result);
} finally {
System.setProperty("user.dir", backupWorkingDirectory);
}
logger.info("Applied XSLT conversion [{}] on input file [{}] with output file [{}]", xsltUri, xmlFile, outputFile);
return outputFile;
} catch (IOException | TransformerException e) {
throw new ApplicationException(XsltErrorType.ERROR_APPLYING_XSLT, e)
.addContextValue(XsltErrorContext.XSLT, xsltUri)
.addContextValue(XsltErrorContext.XML, xmlFile)
.addContextValue(XsltErrorContext.OUTPUT, outputFile);
}
}
/**
* Build input stream on one of the possible input sources (xml file, input source).
*
* @return the input stream.
*/
private InputStream resolveInputStream() {
final InputStream ret;
// open input stream on xml file
if (Objects.nonNull(this.xmlFile)) {
try {
if (!this.xmlFile.exists()) {
throw new ApplicationException(XsltErrorType.XML_FILE_DOES_NOT_EXIST)
.addContextValue(XsltErrorContext.XML, this.xmlFile);
}
ret = new FileInputStream(this.xmlFile);
} catch (FileNotFoundException e) {
throw new SystemException(XsltErrorType.ERROR_OPENING_STREAM_ON_XML_FILE, e)
.addContextValue(XsltErrorContext.XML, this.xmlFile);
}
}
// open input stream on input source
else if (Objects.nonNull(this.xmlInputSource)) {
ret = this.xmlInputSource.getByteStream();
} else {
throw new ApplicationException(XsltErrorType.NO_XML_INPUT);
}
return ret;
}
private Transformer newTransformer() {
// resolve xslt includes
factory.setURIResolver(uriResolver);
// Use the factory to create a template containing the xsl file
final Transformer xsltTransformer;
try {
final Templates template = factory.newTemplates(new StreamSource(xsltUri.toURL().openStream()));
xsltTransformer = template.newTransformer();
} catch (TransformerConfigurationException e) {
throw new SystemException(XsltErrorType.ERROR_APPLYING_XSLT, e);
} catch (IOException e) {
throw new ApplicationException(XsltErrorType.ERROR_FINDING_XSLT, e)
.addContextValue(XsltErrorContext.XSLT, xsltUri);
}
return xsltTransformer;
}
private XsltExecutor() {
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy