net.sf.saxon.jaxp.TransformerImpl Maven / Gradle / Ivy
Show all versions of Saxon-HE Show documentation
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Copyright (c) 2015 Saxonica Limited.
// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
package net.sf.saxon.jaxp;
import net.sf.saxon.Configuration;
import net.sf.saxon.Controller;
import net.sf.saxon.event.Receiver;
import net.sf.saxon.expr.JPConverter;
import net.sf.saxon.expr.XPathContext;
import net.sf.saxon.expr.parser.ExplicitLocation;
import net.sf.saxon.expr.parser.RoleDiagnostic;
import net.sf.saxon.lib.SerializerFactory;
import net.sf.saxon.om.Sequence;
import net.sf.saxon.s9api.*;
import net.sf.saxon.trans.XPathException;
import net.sf.saxon.type.JavaExternalObjectType;
import net.sf.saxon.value.EmptySequence;
import net.sf.saxon.value.ObjectValue;
import net.sf.saxon.value.UntypedAtomicValue;
import org.w3c.dom.Node;
import org.xml.sax.XMLFilter;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.ErrorListener;
import javax.xml.transform.Result;
import javax.xml.transform.Source;
import javax.xml.transform.URIResolver;
import javax.xml.transform.dom.DOMResult;
import javax.xml.transform.sax.SAXResult;
import javax.xml.transform.sax.TransformerHandler;
import javax.xml.transform.stream.StreamResult;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;
/**
* Saxon implementation of the JAXP Transformer interface.
*
* Since Saxon 9.6, JAXP interfaces are implemented as a layer above the s9api interface
*/
public class TransformerImpl extends IdentityTransformer {
private XsltExecutable xsltExecutable;
private XsltTransformer xsltTransformer;
private Map parameters = new HashMap(8);
protected TransformerImpl(XsltExecutable e, XsltTransformer t) {
super(e.getProcessor().getUnderlyingConfiguration());
this.xsltExecutable = e;
this.xsltTransformer = t;
}
/**
* Transform the XML Source
to a Result
.
* Specific transformation behavior is determined by the settings of the
* TransformerFactory
in effect when the
* Transformer
was instantiated and any modifications made to
* the Transformer
instance.
*
* An empty Source
is represented as an empty document
* as constructed by {@link javax.xml.parsers.DocumentBuilder#newDocument()}.
* The result of transforming an empty Source
depends on
* the transformation behavior; it is not always an empty
* Result
.
*
* @param xmlSource The XML input to transform.
* @param outputTarget The Result
of transforming the
* xmlSource
.
* @throws javax.xml.transform.TransformerException
* If an unrecoverable error occurs
* during the course of the transformation.
*/
@Override
public void transform(Source xmlSource, final Result outputTarget) throws XPathException {
boolean closeResultAfterUse = false;
try {
xsltTransformer.setSource(xmlSource);
if (outputTarget.getSystemId() != null) { //bug 2214
xsltTransformer.setBaseOutputURI(outputTarget.getSystemId());
}
Destination destination;
if (outputTarget instanceof StreamResult) {
StreamResult sr = (StreamResult) outputTarget;
if (sr.getOutputStream() != null) {
destination = xsltExecutable.getProcessor().newSerializer(sr.getOutputStream());
} else if (sr.getWriter() != null) {
destination = xsltExecutable.getProcessor().newSerializer(sr.getWriter());
} else if (sr.getSystemId() != null) {
URI uri;
try {
uri = new URI(sr.getSystemId());
} catch (URISyntaxException e) {
throw new XPathException("System ID in Result object is not a valid URI: " + sr.getSystemId(), e);
}
if (!uri.isAbsolute()) {
try {
uri = new File(sr.getSystemId()).getAbsoluteFile().toURI();
} catch (Exception e) {
// if we fail, we'll get another exception
}
}
File file = new File(uri);
try {
if ("file".equals(uri.getScheme()) && !file.exists()) {
File directory = file.getParentFile();
if (directory != null && !directory.exists()) {
directory.mkdirs();
}
file.createNewFile();
}
} catch (IOException err) {
throw new XPathException("Failed to create output file " + uri, err);
}
FileOutputStream stream;
try {
stream = new FileOutputStream(file);
closeResultAfterUse = true;
} catch (FileNotFoundException e) {
throw new XPathException("Failed to create output file", e);
}
destination = xsltExecutable.getProcessor().newSerializer(stream);
((Serializer)destination).setCloseOnCompletion(true);
} else {
throw new IllegalArgumentException("StreamResult supplies neither an OutputStream nor a Writer");
}
// Copy the local output properties to the Serializer
Properties localOutputProperties = getLocalOutputProperties();
for (String key : localOutputProperties.stringPropertyNames()) {
((Serializer) destination).setOutputProperty(QName.fromClarkName(key),
localOutputProperties.getProperty(key));
}
} else if (outputTarget instanceof SAXResult) {
destination = new SAXDestination(((SAXResult) outputTarget).getHandler());
} else if (outputTarget instanceof DOMResult) {
Node root = ((DOMResult) outputTarget).getNode();
if (root == null) {
try {
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
dbf.setNamespaceAware(true);
root = dbf.newDocumentBuilder().newDocument();
((DOMResult) outputTarget).setNode(root);
} catch (ParserConfigurationException e) {
throw new XPathException(e);
}
}
destination = new DOMDestination(root);
} else if (outputTarget instanceof Receiver) {
destination = new Destination() {
public Receiver getReceiver(Configuration config) throws SaxonApiException {
return (Receiver) outputTarget;
}
public void close() throws SaxonApiException {
try {
((Receiver) outputTarget).close();
} catch (XPathException e) {
throw new SaxonApiException(e);
}
}
};
} else {
SerializerFactory sf = getConfiguration().getSerializerFactory();
Receiver r = sf.getReceiver(outputTarget, getConfiguration().makePipelineConfiguration(), getLocalOutputProperties());
transform(xmlSource, r);
return;
//throw new IllegalArgumentException("Unknown Result class " + outputTarget.getClass());
}
xsltTransformer.setDestination(destination);
xsltTransformer.transform();
if(closeResultAfterUse) {
destination.close();
}
} catch (SaxonApiException e) {
throw XPathException.makeXPathException(e);
}
}
/**
* Add a parameter for the transformation.
*
* Pass a qualified name as a two-part string, the namespace URI
* enclosed in curly braces ({}), followed by the local name. If the
* name has a null URL, the String only contain the local name. An
* application can safely check for a non-null URI by testing to see if the
* first character of the name is a '{' character.
* For example, if a URI and local name were obtained from an element
* defined with <xyz:foo
* xmlns:xyz="http://xyz.foo.com/yada/baz.html"/>,
* then the qualified name would be "{http://xyz.foo.com/yada/baz.html}foo".
* Note that no prefix is used.
*
* @param name The name of the parameter, which may begin with a
* namespace URI in curly braces ({}).
* @param value The value object. This can be any valid Java object. It is
* up to the processor to provide the proper object coersion or to simply
* pass the object on for use in an extension.
* @throws NullPointerException If value is null.
* @throws IllegalArgumentException If the supplied value cannot be converted to the declared
* type of the corresponding stylesheet parameter
*/
@Override
public void setParameter(String name, Object value) {
if (name == null) {
throw new NullPointerException("Transformer.setParameter() - name is null");
}
if (value == null) {
throw new NullPointerException("Transformer.setParameter() - value is null");
}
parameters.put(name, value);
QName qName = QName.fromClarkName(name);
XsltExecutable.ParameterDetails details = xsltExecutable.getGlobalParameters().get(qName);
if (details == null) {
// no parameter with this name is defined; we can simply ignore it
return;
}
Configuration config = getConfiguration();
net.sf.saxon.value.SequenceType required = details.getUnderlyingDeclaredType();
Sequence converted;
try {
if (value instanceof Sequence) {
converted = (Sequence) value;
} else if (value instanceof String) {
converted = new UntypedAtomicValue((String) value);
} else if (required.getPrimaryType() instanceof JavaExternalObjectType) {
converted = new ObjectValue