All Downloads are FREE. Search and download functionalities are using the official Maven repository.

net.sf.saxon.s9api.XsltTransformer Maven / Gradle / Ivy

There is a newer version: 10.5
Show newest version
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Copyright (c) 2013 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.s9api;

import net.sf.saxon.Configuration;
import net.sf.saxon.Controller;
import net.sf.saxon.event.Builder;
import net.sf.saxon.event.Receiver;
import net.sf.saxon.lib.TraceListener;
import net.sf.saxon.om.NodeInfo;
import net.sf.saxon.om.Sequence;
import net.sf.saxon.trans.XPathException;

import javax.xml.transform.*;
import javax.xml.transform.dom.DOMSource;
import java.io.File;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.Properties;

/**
 * An XsltTransformer represents a compiled and loaded stylesheet ready for execution.
 * The XsltTransformer holds details of the dynamic evaluation context for the stylesheet.
 * 

*

An XsltTransformer must not be used concurrently in multiple threads. * It is safe, however, to reuse the object within a single thread to run the same * stylesheet several times. Running the stylesheet does not change the context * that has been established.

*

*

An XsltTransformer is always constructed by running the Load * method of an {@link XsltExecutable}.

*

*

An XsltTransformer is itself a Destination. This means it is possible to use * one XsltTransformer as the destination to receive the results of another transformation, * this providing a simple way for transformations to be chained into a pipeline. Note however that a * when the input to a transformation is supplied in this way, it will always be built as a tree in * memory, rather than the transformation being streamed.

* * @since 9.0 */ public class XsltTransformer implements Destination { // TODO: when input is piped into an XsltTransformer, do a streamed transformation where appropriate. private Processor processor; private Controller controller; /*@Nullable*/ private Source initialSource; private Destination destination; private Builder sourceTreeBuilder; boolean baseOutputUriWasSet = false; /** * Protected constructor * * @param processor the S9API processor * @param controller the Saxon controller object */ protected XsltTransformer(Processor processor, Controller controller) { this.processor = processor; this.controller = controller; } /** * Set the initial named template for the transformation * * @param templateName the name of the initial template, or null to indicate * that there should be no initial named template * @throws SaxonApiException if there is no named template with this name */ public void setInitialTemplate(QName templateName) throws SaxonApiException { try { controller.setInitialTemplate( templateName == null ? null : templateName.getClarkName()); } catch (XPathException e) { throw new SaxonApiException(e); } } /** * Get the initial named template for the transformation, if one has been set * * @return the name of the initial template, or null if none has been set */ public QName getInitialTemplate() { String template = controller.getInitialTemplate(); return (template == null ? null : QName.fromClarkName(template)); } /** * Set the initial mode for the transformation * * @param modeName the name of the initial mode, or null to indicate the default * (unnamed) mode */ public void setInitialMode(QName modeName) { controller.setInitialMode(modeName == null ? null : modeName.getClarkName()); } /** * Get the name of the initial mode for the transformation, if one has been set. * * @return the initial mode for the transformation. Returns null if no mode has been set, * or if the mode was set to null to represent the default (unnamed) mode */ public QName getInitialMode() { String mode = controller.getInitialModeName(); if (mode == null) { return null; } else { return QName.fromClarkName(mode); } } /** * Set the schema validation mode for the transformation. This indicates how source documents * loaded specifically for this transformation will be handled. This applies to the * principal source document if supplied as a SAXSource or StreamSource, and to all * documents loaded during the transformation using the doc(), document(), * or collection() functions. * * @param mode the validation mode. Passing null causes no change to the existing value. * Passing {@link ValidationMode#DEFAULT} resets to the initial value, which determines * the validation requirements from the Saxon Configuration. */ public void setSchemaValidationMode(ValidationMode mode) { if (mode != null) { controller.setSchemaValidationMode(mode.getNumber()); } } /** * Get the schema validation mode for the transformation. This indicates how source documents * loaded specifically for this transformation will be handled. This applies to the * principal source document if supplied as a SAXSource or StreamSource, and to all * documents loaded during the transformation using the doc(), document(), * or collection() functions. * * @return the validation mode. */ public ValidationMode getSchemaValidationMode() { return ValidationMode.get(controller.getSchemaValidationMode()); } /** * Set the source document for the transformation. *

*

If the source is an instance of {@link net.sf.saxon.om.NodeInfo}, the supplied node is used * directly as the initial context item of the transformation.

*

*

If the source is an instance of {@link javax.xml.transform.dom.DOMSource}, the DOM node identified * by the DOMSource is wrapped as a Saxon node, and this is then used as the context item

*

*

In other cases a new Saxon tree will be built by the transformation engine when the * transformation starts, unless it is a Saxon-EE streaming transformation, in which case the * document is processed in streaming fashion as it is being parsed.

*

*

To run a transformation in streaming mode, the source should be supplied as an instance * of {@link javax.xml.transform.stream.StreamSource}, {@link javax.xml.transform.sax.SAXSource}, * or {@link net.sf.saxon.event.Transmitter}.

* * @param source the principal source document for the transformation * @throws SaxonApiException if for example the Source is an unrecognized type of source */ public void setSource(Source source) throws SaxonApiException { if (source instanceof NodeInfo) { setInitialContextNode(new XdmNode((NodeInfo) source)); } else if (source instanceof DOMSource) { setInitialContextNode(processor.newDocumentBuilder().wrap(source)); } else { initialSource = source; } } /** * Set the initial context node for the transformation. *

This is ignored in the case where the {@link XsltTransformer} is used as the * {@link Destination} of another process. In that case the initial context node will always * be the document node of the document that is being streamed to this destination.

*

Calling this method has the side-effect of setting the initial source to null.

* * @param node the initial context node, or null if there is to be no initial context node */ public void setInitialContextNode(XdmNode node) { initialSource = (node == null ? null : node.getUnderlyingNode()); } /** * Get the initial context node for the transformation, if one has been set * * @return the initial context node, or null if none has been set. This will not necessarily * be the same {@link XdmNode} instance as was supplied, but it will be an XdmNode object that represents * the same underlying node. */ public XdmNode getInitialContextNode() { if (initialSource instanceof NodeInfo) { return (XdmNode) XdmValue.wrap(((NodeInfo) initialSource)); } else { return null; } } /** * Set the value of a stylesheet parameter * * @param name the name of the stylesheet parameter, as a QName * @param value the value of the stylesheet parameter, or null to clear a previously set value */ public void setParameter(QName name, XdmValue value) { controller.setParameter(name.getStructuredQName(), (value == null ? null : value.getUnderlyingValue())); } /** * Get the value that has been set for a stylesheet parameter * * @param name the parameter whose name is required * @return the value that has been set for the parameter, or null if no value has been set */ public XdmValue getParameter(QName name) { Object oval = controller.getParameter(name.getClarkName()); if (oval == null) { return null; } if (oval instanceof Sequence) { return XdmValue.wrap((Sequence) oval); } throw new IllegalStateException(oval.getClass().getName()); } /** * Set the destination to be used for the result of the transformation. *

This method can be used to chain transformations into a pipeline, by using one * {@link XsltTransformer} as the destination of another

*

If the destination is a {@link Serializer}, then a side-effect of this method is to set * the base output URI for the transformation. This acts as the base URI for resolving * the href attribute of any xsl:result-document instruction. * The serialiation parameters defined in the Serializer override any * serialization parameters defined using xsl:output for the principal * output of the transformation. However, they have no effect on any output produced * using xsl:result-document. * * @param destination the destination to be used */ public void setDestination(Destination destination) { this.destination = destination; } /** * Get the destination that was specified in a previous call of {@link #setDestination} * * @return the destination, or null if none has been supplied */ public Destination getDestination() { return destination; } /** * Set the base output URI. *

*

This defaults to the system ID of the Destination for the principal output * of the transformation if this is known; if it is not known, it defaults * to the current directory.

*

*

If no base output URI is supplied, but the Destination of the transformation * is a Serializer that writes to a file, then the URI of this file is used as * the base output URI.

*

*

The base output URI is used for resolving relative URIs in the href attribute * of the xsl:result-document instruction.

* * @param uri the base output URI * @since 9.2 */ public void setBaseOutputURI(String uri) { controller.setBaseOutputURI(uri); baseOutputUriWasSet = (uri != null); } /** * Get the base output URI. *

*

This returns the value set using the {@link #setBaseOutputURI} method. If no value has been set * explicitly, then the method returns null if called before the transformation, or the computed * default base output URI if called after the transformation.

*

*

The base output URI is used for resolving relative URIs in the href attribute * of the xsl:result-document instruction.

* * @return the base output URI * @since 9.2 */ public String getBaseOutputURI() { return controller.getBaseOutputURI(); } /** * Set an object that will be used to resolve URIs used in * fn:doc() and related functions. * * @param resolver An object that implements the URIResolver interface, or * null. * @since 9.3 */ public void setURIResolver(URIResolver resolver) { controller.setURIResolver(resolver); } /** * Get the URI resolver. * * @return the user-supplied URI resolver if there is one, or null otherwise * @since 9.3 */ public URIResolver getURIResolver() { return controller.getURIResolver(); } /** * Set the ErrorListener to be used during this transformation * * @param listener The error listener to be used. This is notified of all dynamic errors detected during the * transformation. * @since 9.2 */ public void setErrorListener(ErrorListener listener) { controller.setErrorListener(listener); } /** * Get the ErrorListener being used during this compilation episode * * @return listener The error listener in use. This is notified of all dynamic errors detected during the * transformation. If no user-supplied ErrorListener has been set the method will return a system-supplied * ErrorListener. * @since 9.2 */ public ErrorListener getErrorListener() { return controller.getErrorListener(); } /** * Set the MessageListener to be notified whenever the stylesheet evaluates an * xsl:message instruction. If no MessageListener is nominated, * the output of xsl:message instructions will be serialized and sent * to the standard error stream. * * @param listener the MessageListener to be used * @since 9.1 */ public void setMessageListener(MessageListener listener) { controller.setMessageEmitter(new MessageListenerProxy(listener, controller.makePipelineConfiguration())); } /** * Get the MessageListener to be notified whenever the stylesheet evaluates an * xsl:message instruction. If no MessageListener has been nominated, * return null * * @return the user-supplied MessageListener, or null if none has been supplied * @since 9.1 */ public MessageListener getMessageListener() { Receiver r = controller.getMessageEmitter(); if (r instanceof MessageListenerProxy) { return ((MessageListenerProxy) r).getMessageListener(); } else { return null; } } /** * Set a TraceListener to be notified of all events occurring during the transformation. * This will only be effective if the stylesheet was compiled with trace code enabled * (see {@link XsltCompiler#setCompileWithTracing(boolean)}) * * @param listener the TraceListener to be used. Note that the TraceListener has access to * interal Saxon interfaces which may vary from one release to the next. It is also possible that * the TraceListener interface itself may be changed in future releases. * @since 9.2 */ public void setTraceListener(TraceListener listener) { controller.setTraceListener(listener); } /** * Get the TraceListener to be notified of all events occurring during the transformation. * If no TraceListener has been nominated, return null * * @return the user-supplied TraceListener, or null if none has been supplied * @since 9.2 */ public TraceListener getTraceListener() { return controller.getTraceListener(); } /** * Perform the transformation. If this method is used, a destination must have been supplied * previously * * @throws SaxonApiException if any dynamic error occurs during the transformation * @throws IllegalStateException if no destination has been supplied */ public void transform() throws SaxonApiException { if (destination == null) { throw new IllegalStateException("No destination has been supplied"); } if (baseOutputUriWasSet && destination instanceof XdmDestination && ((XdmDestination)destination).getBaseURI() == null && controller.getBaseOutputURI() != null ) { try { ((XdmDestination)destination).setBaseURI(new URI(controller.getBaseOutputURI())); } catch (URISyntaxException e) { // no action } } try { controller.transform(initialSource, getDestinationResult()); destination.close(); } catch (TransformerException e) { throw new SaxonApiException(e); } } private Result getDestinationResult() throws SaxonApiException { if (destination instanceof Serializer) { Properties props = ((Serializer) destination).getOutputProperties(); controller.setOutputProperties(props); if (!baseOutputUriWasSet) { Object dest = ((Serializer) destination).getOutputDestination(); if (dest instanceof File) { controller.setBaseOutputURI(((File) dest).toURI().toString()); } } return ((Serializer)destination).getResult(); } else { return destination.getReceiver(controller.getConfiguration()); } } /** * Return a Receiver which can be used to supply the principal source document for the transformation. * This method is intended primarily for internal use, though it can also * be called by a user application that wishes to feed events into the transformation engine. *

*

Saxon calls this method to obtain a Receiver, to which it then sends * a sequence of events representing the content of an XML document. This method is provided so that * XsltTransformer implements Destination, allowing one transformation * to receive the results of another in a pipeline.

*

*

Before calling this method, the {@link #setDestination} method must be called to supply a destination * for the transformation.

*

*

Note that when an XsltTransformer is used as a Destination, the initial * context node set on that XsltTransformer using {@link #setInitialContextNode(XdmNode)} is ignored, * as is the source set using {@link #setSource(javax.xml.transform.Source)}.

* * @param config The Saxon configuration. This is supplied so that the destination can * use information from the configuration (for example, a reference to the name pool) * to construct or configure the returned Receiver. * @return the Receiver to which events are to be sent. * @throws SaxonApiException if the Receiver cannot be created * @throws IllegalStateException if no Destination has been supplied */ public Receiver getReceiver(Configuration config) throws SaxonApiException { if (destination == null) { throw new IllegalStateException("No destination has been supplied"); } if (controller.getInitialMode().isStreamable()) { try { return controller.getStreamingReceiver(controller.getInitialMode(), getDestinationResult()); } catch (TransformerException e) { throw new SaxonApiException(e); } } else { sourceTreeBuilder = controller.makeBuilder(); Receiver stripper = controller.makeStripper(sourceTreeBuilder); if (controller.getExecutable().stripsInputTypeAnnotations()) { stripper = controller.getConfiguration().getAnnotationStripper(stripper); } return stripper; } } /** * Close this destination, allowing resources to be released. Used when this XsltTransformer is acting * as the destination of another transformation. Saxon calls this method when it has finished writing * to the destination. */ public void close() throws SaxonApiException { if (sourceTreeBuilder != null) { NodeInfo doc = sourceTreeBuilder.getCurrentRoot(); //sourceTreeBuilder.reset(); sourceTreeBuilder = null; if (doc == null) { throw new SaxonApiException("No source document has been built by the previous pipeline stage"); } Result result = getDestinationResult(); try { controller.transformDocument(doc, result); } catch (TransformerException e) { throw new SaxonApiException(e); } destination.close(); } } /** * Get the underlying Controller used to implement this XsltTransformer. This provides access * to lower-level methods not otherwise available in the s9api interface. Note that classes * and methods obtained by this route cannot be guaranteed stable from release to release. * * @since 9.0.0.4 */ public Controller getUnderlyingController() { return controller; } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy