net.sf.saxon.s9api.Xslt30Transformer Maven / Gradle / Ivy
Show all versions of Saxon-HE Show documentation
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Copyright (c) 2018-2023 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.PreparedStylesheet;
import net.sf.saxon.event.ComplexContentOutputter;
import net.sf.saxon.event.PipelineConfiguration;
import net.sf.saxon.event.Receiver;
import net.sf.saxon.event.SequenceNormalizerWithSpaceSeparator;
import net.sf.saxon.expr.Component;
import net.sf.saxon.expr.XPathContextMajor;
import net.sf.saxon.expr.instruct.GlobalParameterSet;
import net.sf.saxon.expr.instruct.UserFunction;
import net.sf.saxon.expr.instruct.UserFunctionParameter;
import net.sf.saxon.expr.parser.Loc;
import net.sf.saxon.expr.parser.RoleDiagnostic;
import net.sf.saxon.lib.NamespaceConstant;
import net.sf.saxon.om.*;
import net.sf.saxon.serialize.SerializationProperties;
import net.sf.saxon.trans.*;
import net.sf.saxon.transpile.CSharpInnerClass;
import net.sf.saxon.transpile.CSharpModifiers;
import javax.xml.transform.Source;
import javax.xml.transform.dom.DOMSource;
import java.io.File;
import java.io.OutputStream;
import java.io.Writer;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import java.util.function.Supplier;
/**
* An Xslt30Transformer
represents a compiled and loaded stylesheet ready for execution.
* The Xslt30Transformer
holds details of the dynamic evaluation context for the stylesheet.
* The Xslt30Transformer
differs from {@link XsltTransformer} is supporting new options
* for invoking a stylesheet, corresponding to facilities defined in the XSLT 3.0 specification. However,
* it is not confined to use with XSLT 3.0, and most of the new invocation facilities (for example,
* calling a stylesheet-defined function directly) work equally well with XSLT 2.0 and in some cases
* XSLT 1.0 stylesheets.
* An Xslt30Transformer
must not be used concurrently in multiple threads.
* It is safe to reuse the object within a single thread to run several transformations using
* the same stylesheet, but the values of the global context item and of stylesheet parameters
* must be initialized before any transformations are run, and must remain unchanged thereafter.
* The date/time value returned by fn:current-dateTime()
also remains constant for the
* duration of an Xslt30Transformer.
* Some of the entry point methods are synchronized. This is not
* because multi-threaded execution is permitted; rather it is to reduce the damage if it is attempted.
* An Xslt30Transformer
is always constructed by running the
* method {@link XsltExecutable#load30()}.
* Unlike XsltTransformer
, an Xslt30Transformer
is not a Destination
.
* To pipe the results of one transformation into another, the target should be an XsltTransfomer
* rather than an Xslt30Transformer
.
* Evaluation of an Xslt30Transformer proceeds in a number of phases:
*
* - First, values may be supplied for stylesheet parameters and for the global context item. The
* global context item is used when initializing global variables. Unlike earlier transformation APIs,
* the global context item is quite independent of the "principal Source document".
* - The stylesheet may now be repeatedly invoked. Each invocation takes one of three forms:
*
* - Invocation by applying templates. In this case, the information required is (i) an initial
* mode (which defaults to the unnamed mode), (ii) an initial match sequence, which is any
* XDM value, which is used as the effective "select" expression of the implicit apply-templates
* call, and (iii) optionally, values for the tunnel and non-tunnel parameters defined on the
* templates that get invoked (equivalent to using
xsl:with-param
on the implicit
* apply-templates
call).
* - Invocation by calling a named template. In this case, the information required is
* (i) the name of the initial template (which defaults to "xsl:initial-template"), and
* (ii) optionally, values for the tunnel and non-tunnel parameters defined on the
* templates that get invoked (equivalent to using
xsl:with-param
on the implicit
* call-template
instruction).
* - Invocation by calling a named function. In this case, the information required is
* the sequence of arguments to the function call.
*
*
* - Whichever invocation method is chosen, the result may either be returned directly, as an arbitrary
* XDM value, or it may effectively be wrapped in an XML document. If it is wrapped in an XML document,
* that document can be processed in a number of ways, for example it can be materialized as a tree in
* memory, it can be serialized as XML or HTML, or it can be subjected to further transformation.
*
* Once the stylesheet has been invoked (using any of these methods), the values of the global context
* item and stylesheet parameters cannot be changed. If it is necessary to run another transformation with
* a different context item or different stylesheet parameters, a new Xslt30Transformer
* should be created from the original XsltExecutable
.
*
* @since 9.6
*/
@CSharpModifiers(code = {"internal"})
public class Xslt30Transformer extends AbstractXsltTransformer {
private GlobalParameterSet globalParameterSet;
private boolean primed = false;
private Item globalContextItem = null;
private boolean alreadyStripped;
/**
* Protected constructor
*
* @param processor the S9API processor
* @param controller the Saxon controller object
* @param staticParameters the static parameters supplied at stylesheet compile time
*/
protected Xslt30Transformer(Processor processor, XsltController controller, GlobalParameterSet staticParameters) {
super(processor, controller);
globalParameterSet = new GlobalParameterSet(/*staticParameters*/);
}
/**
* Supply the context item to be used when evaluating global variables and parameters. A call on
* setGlobalContextItem(node)
is equivalent to a call on setGlobalContextItem(node, false)
:
* that is, it is assumed that stripping of type annotations and whitespace text nodes has not yet been
* performed.
*
* Note: It is more efficient to do whitespace stripping while constructing the source tree. This can be
* achieved by building the source tree using a {@link DocumentBuilder} initialized using
* {@link DocumentBuilder#setWhitespaceStrippingPolicy(WhitespaceStrippingPolicy)}, supplying a
* {@link WhitespaceStrippingPolicy} obtained by calling {@link XsltExecutable#getWhitespaceStrippingPolicy()}.
*
* @param globalContextItem the item to be used as the context item within the initializers
* of global variables and parameters. This argument can be null if no context item is to be
* supplied.
* @throws IllegalStateException if the transformation has already been evaluated by calling one of the methods
* applyTemplates
, callTemplate
, or callFunction
* @throws SaxonApiException Not thrown, but retained in the method signature for compatibility reasons. Note that
* no error is thrown at this stage if the context item is of the wrong type.
*/
public void setGlobalContextItem(XdmItem globalContextItem) throws SaxonApiException {
setGlobalContextItem(globalContextItem, false);
}
/**
* Supply the context item to be used when evaluating global variables and parameters.
*
* @param globalContextItem the item to be used as the context item within the initializers
* of global variables and parameters. This argument can be null if no context item is to be
* supplied.
* @param alreadyStripped true if any stripping of type annotations or whitespace text nodes specified
* by the stylesheet has already taken place
* @throws IllegalStateException if the transformation has already been evaluated by calling one of the methods
* applyTemplates
, callTemplate
, or callFunction
* @throws SaxonApiException Not thrown, but retained in the method signature for compatibility reasons. Note that
* no error is thrown at this stage if the context item is of the wrong type.
*/
public synchronized void setGlobalContextItem(XdmItem globalContextItem, boolean alreadyStripped) throws SaxonApiException {
if (primed) {
throw new IllegalStateException("Stylesheet has already been evaluated");
}
this.globalContextItem = globalContextItem == null ? null : globalContextItem.getUnderlyingValue();
this.alreadyStripped = alreadyStripped;
}
/**
* Get the global context item
* @return the global context item if it has been set, or null otherwise
*/
public XdmItem getGlobalContextItem() {
return XdmItem.wrapItem(this.globalContextItem);
}
/**
* Supply the values of global stylesheet parameters.
*
* If a value is supplied for a parameter with a particular name, and the stylesheet does not declare
* a parameter with that name, then the value is simply ignored.
*
* If a value is supplied for a parameter with a particular name, and the stylesheet declaration of
* that parameter declares a required type, then the supplied value will be checked against that type
* when the value of the parameter is first used (and any error will be reported only then). Parameter
* values are converted to the required type using the function conversion rules.
*
* If a value is supplied for a parameter with a particular name, and the stylesheet declaration of
* that parameter declares it with the attribute static="yes"
, then an error will be reported
* when the transformation is initiated.
*
* If a value is supplied for a parameter with a particular name, and a parameter with the same
* name was supplied at compile time using {@link XsltCompiler#setParameter(QName, XdmValue)},
* then an error will be reported with the transformation is initiated.
*
* @param parameters a map whose keys are QNames identifying global stylesheet parameters,
* and whose corresponding values are the values to be assigned to those parameters.
* The contents of the supplied map are copied by this method,
* so subsequent changes to the map have no effect.
* @param the type of values for the parameters
* @throws IllegalStateException if the transformation has already been evaluated by calling one of the methods
* applyTemplates
, callTemplate
, or callFunction
* @throws SaxonApiException currently occurs only if the supplied value of a parameter cannot be evaluated
*/
public synchronized void setStylesheetParameters(Map parameters) throws SaxonApiException {
if (primed) {
throw new IllegalStateException("Stylesheet has already been evaluated");
}
if (globalParameterSet == null) {
globalParameterSet = new GlobalParameterSet();
}
for (Map.Entry param : parameters.entrySet()) {
StructuredQName name = param.getKey().getStructuredQName();
XdmValue value = param.getValue();
try {
globalParameterSet.put(name, value.getUnderlyingValue());
} catch (UncheckedXPathException e) {
throw new SaxonApiException(e);
}
}
}
private void prime() throws SaxonApiException {
if (!primed) {
if (globalParameterSet == null) {
globalParameterSet = new GlobalParameterSet();
}
try {
controller.setGlobalContextItem(globalContextItem, alreadyStripped);
controller.initializeController(globalParameterSet);
} catch (XPathException e) {
throw new SaxonApiException(e);
}
}
primed = true;
}
/**
* Set parameters to be passed to the initial template. These are used
* whether the transformation is invoked by applying templates to an initial source item,
* or by invoking a named template. The parameters in question are the xsl:param elements
* appearing as children of the xsl:template element.
* The parameters are supplied in the form of a map; the key is a QName which must
* match the name of the parameter; the associated value is an XdmValue containing the
* value to be used for the parameter. If the initial template defines any required
* parameters, the map must include a corresponding value. If the initial template defines
* any parameters that are not present in the map, the default value is used. If the map
* contains any parameters that are not defined in the initial template, these values
* are silently ignored.
* The supplied values are converted to the required type using the function conversion
* rules. If conversion is not possible, a run-time error occurs (not now, but later, when
* the transformation is actually run).
* The XsltTransformer
retains a reference to the supplied map, so parameters can be added or
* changed until the point where the transformation is run.
* The XSLT 3.0 specification makes provision for supplying parameters to the initial
* template, as well as global stylesheet parameters. Although there is no similar provision
* in the XSLT 1.0 or 2.0 specifications, this method works for all stylesheets, regardless whether
* XSLT 3.0 is enabled or not.
*
* @param parameters the parameters to be used for the initial template
* @param tunnel true if these values are to be used for setting tunnel parameters;
* false if they are to be used for non-tunnel parameters
* @param the type of values for the parameters
* @throws SaxonApiException not currently used, but retained in the method signature for compatibility reasons
*/
public synchronized void setInitialTemplateParameters(Map parameters, boolean tunnel) throws SaxonApiException {
Map templateParams = new HashMap<>();
for (Map.Entry entry : parameters.entrySet()) {
QName key = entry.getKey();
XdmValue value = entry.getValue();
templateParams.put(key.getStructuredQName(), value.getUnderlyingValue());
}
controller.setInitialTemplateParameters(templateParams, tunnel);
}
/**
* Invoke the stylesheet by applying templates to a supplied Source document, sending the results (wrapped
* in a document node) to a given Destination. The invocation uses any initial mode set using {@link #setInitialMode(QName)},
* and any template parameters set using {@link #setInitialTemplateParameters(java.util.Map, boolean)}.
*
* This method does not set the global context item for the transformation. If that is required, it
* can be done separately using the method {@link #setGlobalContextItem(XdmItem)}.
*
* @param source the source document. For streamed processing, this must be a SAXSource or StreamSource.
* Note: supplying a DOMSource
is allowed, but is much less efficient than using a
* StreamSource
or SAXSource
and leaving Saxon to build the tree in its own
* internal format. To apply more than one transformation to the same source document, the source document
* tree can be pre-built using a {@link DocumentBuilder}.
* @param destination the destination of the principal result of the transformation.
* If the destination is a {@link Serializer}, then the serialization
* parameters set in the serializer are combined with those defined in the stylesheet
* (the parameters set in the serializer take precedence).
* @throws SaxonApiException if the transformation fails
*/
public synchronized void applyTemplates(Source source, Destination destination) throws SaxonApiException {
Objects.requireNonNull(destination);
if (source == null) {
XPathException err = new XPathException("No initial match selection supplied", "XTDE0044");
throw new SaxonApiException(err);
}
prime();
try {
Receiver sOut = getDestinationReceiver(controller, destination);
applyTemplatesToSource(source, sOut);
destination.closeAndNotify();
} catch (XPathException e) {
if (!e.hasBeenReported()) {
getErrorReporter().report(new XmlProcessingException(e));
}
throw new SaxonApiException(e);
}
}
/**
* Invoke the stylesheet by applying templates to a supplied Source document, returning the raw results
* as an {@link XdmValue}. The invocation uses any initial mode set using {@link #setInitialMode(QName)},
* and any template parameters set using {@link #setInitialTemplateParameters(java.util.Map, boolean)}.
*
* @param source the source document. For streamed processing, this must be a SAXSource or StreamSource.
* Note: supplying a DOMSource
is allowed, but is much less efficient than using a
* StreamSource
or SAXSource
and leaving Saxon to build the tree in its own
* internal format. To apply more than one transformation to the same source document, the source document
* tree can be pre-built using a {@link DocumentBuilder}.
* @return the raw result of processing the supplied Source using the selected template rule, without
* wrapping the returned sequence in a document node
* @throws SaxonApiException if the transformation fails
*/
public synchronized XdmValue applyTemplates(Source source) throws SaxonApiException {
Objects.requireNonNull(source);
RawDestination raw = new RawDestination();
applyTemplates(source, raw);
return raw.getXdmValue();
}
/**
* Invoke the stylesheet by applying templates to a supplied Source document, sending the results
* to a given Destination. The invocation uses the initial mode set using {@link #setInitialMode(QName)}
* (defaulting to the default mode defined in the stylesheet itself, which by default is the unnamed mode).
* It also uses any template parameters set using {@link #setInitialTemplateParameters(java.util.Map, boolean)}.
*
* The document supplied in the source
argument also acts as the global
* context item for the transformation. Any item previously supplied using {@link #setGlobalContextItem(XdmItem)}
* is ignored.
*
* Because this method sets the global context item to the root node of the supplied source
,
* it cannot be used when the stylesheet is designed to process streamed input. For streamed processing,
* use {@link #applyTemplates(Source, Destination)} instead.
*
* @param source the source document.
* Note: supplying a DOMSource
is allowed, but is much less efficient than using a
* StreamSource
or SAXSource
and leaving Saxon to build the tree in its own
* internal format. To apply more than one transformation to the same source document, the source document
* tree can be pre-built using a {@link DocumentBuilder}.
* @param destination the destination of the principal result of the transformation.
* If the destination is a {@link Serializer}, then the serialization
* parameters set in the serializer are combined with those defined in the stylesheet
* (the parameters set in the serializer take precedence).
* @throws SaxonApiException if the transformation fails, or if the initial mode in the stylesheet is
* declared to be streamable.
* @since 9.9.1.1
*/
public synchronized void transform(Source source, Destination destination) throws SaxonApiException {
Objects.requireNonNull(destination);
if (source == null) {
XPathException err = new XPathException("No initial match selection supplied", "XTDE0044");
throw new SaxonApiException(err);
}
if (controller.getInitialMode().isDeclaredStreamable()) {
throw new SaxonApiException("Cannot use the transform() method when the initial mode is streamable");
}
prime();
try {
NodeInfo sourceNode;
if (source instanceof NodeInfo) {
controller.setGlobalContextItem((NodeInfo)source);
sourceNode = (NodeInfo) source;
} else if (source instanceof DOMSource) {
sourceNode = controller.prepareInputTree(source);
controller.setGlobalContextItem(sourceNode);
} else {
sourceNode = controller.makeSourceTree(source, getSchemaValidationMode().getNumber());
controller.setGlobalContextItem(sourceNode);
}
Receiver sOut = getDestinationReceiver(controller, destination);
controller.applyTemplates(sourceNode, sOut);
destination.closeAndNotify();
} catch (XPathException e) {
if (!e.hasBeenReported()) {
getErrorReporter().report(new XmlProcessingException(e)) ;
}
throw new SaxonApiException(e);
}
}
/**
* Invoke the stylesheet by applying templates to a supplied input sequence, sending the results (wrapped
* in a document node) to a given Destination. The invocation uses any initial mode set using {@link #setInitialMode(QName)},
* and any template parameters set using {@link #setInitialTemplateParameters(java.util.Map, boolean)}.
*
* @param selection the initial value to which templates are to be applied (equivalent to the select
* attribute of xsl:apply-templates
)
* @param destination the destination for the result document. In most cases this causes the raw result of
* the transformation to be wrapped in a document node. However, if the destination
* is a Serializer and the output method is "json" or "adaptive", then
* no wrapping takes place.
* @throws SaxonApiException if the transformation fails
* @since 9.6. Changed in 9.7.0.1 so that if a Serializer is supplied as the Destination, it will not
* be modified by this method to set output properties from the stylesheet; instead, the Serializer
* should be initialized by calling the newSerializer
method on this Xslt30Transformer
*/
public synchronized void applyTemplates(XdmValue selection, Destination destination) throws SaxonApiException {
Objects.requireNonNull(selection);
Objects.requireNonNull(destination);
prime();
try {
Receiver sOut = getDestinationReceiver(controller, destination);
if (baseOutputUriWasSet) {
sOut.setSystemId(getBaseOutputURI());
}
controller.applyTemplates(selection.getUnderlyingValue(), sOut);
destination.closeAndNotify();
} catch (XPathException e) {
if (!e.hasBeenReported()) {
getErrorReporter().report(new XmlProcessingException(e));
}
throw new SaxonApiException(e);
}
}
/**
* Invoke the stylesheet by applying templates to a supplied input sequence, returning the raw results.
* as an {@link XdmValue}. The invocation uses any initial mode set using {@link #setInitialMode(QName)},
* and any template parameters set using {@link #setInitialTemplateParameters(java.util.Map, boolean)}.
*
* @param selection the initial value to which templates are to be applied (equivalent to the select
* attribute of xsl:apply-templates
)
* @return the raw result of applying templates to the supplied selection value, without wrapping in
* a document node or serializing the result. If there is more that one item in the selection, the result
* is the concatenation of the results of applying templates to each item in turn.
* @throws SaxonApiException if the transformation fails
*/
public synchronized XdmValue applyTemplates(XdmValue selection) throws SaxonApiException {
Objects.requireNonNull(selection);
RawDestination raw = new RawDestination();
applyTemplates(selection, raw);
return raw.getXdmValue();
}
/**
* Invoke a transformation by calling a named template. The results of calling
* the template are wrapped in a document node, which is then sent to the specified
* destination. If {@link #setInitialTemplateParameters(java.util.Map, boolean)} has been
* called, then the parameters supplied are made available to the called template (no error
* occurs if parameters are supplied that are not used).
*
* @param templateName the name of the initial template. This must match the name of a
* public named template in the stylesheet. If the value is null,
* the QName xsl:initial-template
is used.
* @param destination the destination for the result document.
* @throws SaxonApiException if there is no named template with this name, or if any dynamic
* error occurs during the transformation
* @since 9.6. Changed in 9.7.0.1 so that if a Serializer is supplied as the Destination, it will not
* be modified by this method to set output properties from the stylesheet; instead, the Serializer
* should be initialized by calling the newSerializer
method on this Xslt30Transformer
*/
public synchronized void callTemplate(QName templateName, Destination destination) throws SaxonApiException {
Objects.requireNonNull(destination);
prime();
if (templateName == null) {
templateName = new QName("xsl", NamespaceConstant.XSLT, "initial-template");
}
try {
Receiver sOut = getDestinationReceiver(controller, destination);
if (baseOutputUriWasSet) {
sOut.setSystemId(getBaseOutputURI());
}
//sOut.open();
controller.callTemplate(templateName.getStructuredQName(), sOut);
//sOut.close();
destination.closeAndNotify();
} catch (XPathException e) {
destination.closeAndNotify();
if (!e.hasBeenReported()) {
getErrorReporter().report(new XmlProcessingException(e));
}
throw new SaxonApiException(e);
}
}
/**
* Invoke a transformation by calling a named template. The results of calling
* the template are returned as a raw value, without wrapping in a document node
* or serializing.
*
* @param templateName the name of the initial template. This must match the name of a
* public named template in the stylesheet. If the value is null,
* the QName xsl:initial-template
is used.
* @return the raw results of the called template, without wrapping in a document node
* or serialization.
* @throws SaxonApiException if there is no named template with this name, or if any dynamic
* error occurs during the transformation
*/
public synchronized XdmValue callTemplate(QName templateName) throws SaxonApiException {
RawDestination dest = new RawDestination();
callTemplate(templateName, dest);
return dest.getXdmValue();
}
/**
* Call a public user-defined function in the stylesheet.
*
* @param function The name of the function to be called
* @param arguments The values of the arguments to be supplied to the function. These
* will be converted if necessary to the type as defined in the function signature, using
* the function conversion rules.
* @return the result of calling the function. This is the raw result, without wrapping in a document
* node and without serialization.
* @throws SaxonApiException if no function has been defined with the given name and arity;
* or if any of the arguments does not match its required type according to the function
* signature; or if a dynamic error occurs in evaluating the function.
*/
public synchronized XdmValue callFunction(QName function, XdmValue[] arguments) throws SaxonApiException {
Objects.requireNonNull(function);
Objects.requireNonNull(arguments);
prime();
try {
Component f = getFunctionComponent(function, arguments);
UserFunction uf = (UserFunction) f.getActor();
Sequence[] vr = typeCheckFunctionArguments(uf, arguments);
XPathContextMajor context = controller.newXPathContext();
context.setCurrentComponent(f);
context.setTemporaryOutputState(StandardNames.XSL_FUNCTION);
context.setCurrentOutputUri(null);
Sequence result = uf.call(context, vr);
result = result.materialize();
return XdmValue.wrap(result);
} catch (XPathException e) {
if (!e.hasBeenReported()) {
getErrorReporter().report(new XmlProcessingException(e));
}
throw new SaxonApiException(e);
}
}
private synchronized Component getFunctionComponent(QName function, XdmValue[] arguments) throws XPathException {
SymbolicName fName = new SymbolicName.F(function.getStructuredQName(), arguments.length);
PreparedStylesheet pss = (PreparedStylesheet) controller.getExecutable();
Component f = pss.getComponent(fName);
if (f == null) {
throw new XPathException("No public function with name " + function.getClarkName() +
" and arity " + arguments.length + " has been declared in the stylesheet", "XTDE0041");
} else if (f.getVisibility() != Visibility.FINAL && f.getVisibility() != Visibility.PUBLIC) {
throw new XPathException("Cannot invoke " + fName + " externally, because it is not public", "XTDE0041");
}
return f;
}
private Sequence[] typeCheckFunctionArguments(UserFunction uf, XdmValue[] arguments) throws XPathException {
Configuration config = processor.getUnderlyingConfiguration();
UserFunctionParameter[] params = uf.getParameterDefinitions();
GroundedValue[] vr =
new GroundedValue[arguments.length];
for (int i = 0; i < arguments.length; i++) {
net.sf.saxon.value.SequenceType type = params[i].getRequiredType();
vr[i] = arguments[i].getUnderlyingValue();
if (!type.matches(vr[i], config.getTypeHierarchy())) {
final int pos = i;
Supplier role =
() -> new RoleDiagnostic(RoleDiagnostic.FUNCTION, uf.getFunctionName().getDisplayName(), pos);
Sequence converted = config.getTypeHierarchy().applyFunctionConversionRules(vr[i], type, role, Loc.NONE);
vr[i] = converted.materialize();
}
}
return vr;
}
/**
* Call a public user-defined function in the stylesheet, wrapping the result in an XML document, and sending
* this document to a specified destination
*
* @param function The name of the function to be called
* @param arguments The values of the arguments to be supplied to the function. These
* will be converted if necessary to the type as defined in the function signature, using
* the function conversion rules.
* @param destination the destination of the result document produced by wrapping the result of the apply-templates
* call in a document node.
* @throws SaxonApiException in the event of a dynamic error
* @since 9.6. Changed in 9.7.0.1 so that if a Serializer is supplied as the Destination, it will not
* be modified by this method to set output properties from the stylesheet; instead, the Serializer
* should be initialized by calling the newSerializer
method on this Xslt30Transformer
*/
public synchronized void callFunction(QName function, XdmValue[] arguments, Destination destination) throws SaxonApiException {
prime();
try {
Component f = getFunctionComponent(function, arguments);
UserFunction uf = (UserFunction) f.getActor();
Sequence[] vr = typeCheckFunctionArguments(uf, arguments);
XPathContextMajor context = controller.newXPathContext();
context.setCurrentComponent(f);
context.setTemporaryOutputState(StandardNames.XSL_FUNCTION);
context.setCurrentOutputUri(null);
SerializationProperties params = controller.getExecutable().getPrimarySerializationProperties();
Receiver receiver = destination.getReceiver(controller.makePipelineConfiguration(), params);
receiver.open();
uf.process(context, vr, new ComplexContentOutputter(receiver));
receiver.close();
} catch (XPathException e) {
getErrorReporter().report(new XmlProcessingException(e));
throw new SaxonApiException(e);
}
destination.closeAndNotify();
}
/**
* Construct a {@link Destination} object whose effect is to perform this transformation
* on any input that is sent to that {@link Destination}: for example, it allows this transformation
* to post-process the results of another transformation.
*
* This method allows a pipeline of transformations to be created in which
* one transformation is used as the destination of another. The transformations
* may use streaming, in which case intermediate results will not be materialized
* in memory. If a transformation does not use streaming, then its input will
* first be assembled in memory as a node tree.
*
* The {@link Destination} returned by this method performs sequence normalization
* as defined in the serialization specification: that is, the raw result of the transformation
* sent to this destination is wrapped into a document node. Any item-separator present in
* any serialization parameters is ignored (adjacent atomic values are separated by whitespace).
* This makes the method unsuitable for passing intermediate results other than XML document
* nodes.
*
* @param finalDestination the destination for the principal result of this transformation
*
* @return a {@link Destination} which accepts an XML document (typically as a stream
* of events) and which transforms this supplied XML document (possibly using streaming)
* as defined by the stylesheet from which which this {@code Xslt30Transformer} was generated,
* sending the principal result of the transformation to the supplied {@code finalDestination}.
* The transformation is performed as if by the {@link #applyTemplates(Source, Destination)}
* method: that is, by applying templates to the root node of the supplied XML document.
* @since 9.9
*/
@CSharpInnerClass(outer=true, extra={"Saxon.Hej.s9api.Destination finalDestination"})
public Destination asDocumentDestination(Destination finalDestination) {
return new AbstractDestination() {
private Receiver receiver;
@Override
@CSharpModifiers(code={"public", "override"})
public Receiver getReceiver(PipelineConfiguration pipe, SerializationProperties params) throws SaxonApiException {
Receiver rt = getReceivingTransformer(controller, globalParameterSet, finalDestination);
rt = new SequenceNormalizerWithSpaceSeparator(rt);
rt.setPipelineConfiguration(pipe);
return receiver = rt;
}
@Override
@CSharpModifiers(code = {"public", "override"})
public void close() throws SaxonApiException {
try {
receiver.close();
} catch (XPathException e) {
throw new SaxonApiException(e);
}
}
};
}
/**
* Create a serializer initialised to use the default output parameters defined in the stylesheet.
* These serialization parameters can be overridden by use of
* {@link Serializer#setOutputProperty(Serializer.Property, String)}.
*
* @return the new serializer
* @since 9.7.0.1
*/
public Serializer newSerializer() {
Serializer serializer = processor.newSerializer();
serializer.setOutputProperties(controller.getExecutable().getPrimarySerializationProperties());
return serializer;
}
/**
* Create a serializer initialised to use the default output parameters defined in the stylesheet.
* These serialization parameters can be overridden by use of
* {@link Serializer#setOutputProperty(Serializer.Property, String)}.
*
* @param file the output file to which the serializer will write its output. As well as initializing
* the serializer to write to this output file, this method sets the base output URI of this
* Xslt30Transformer to be the URI of this file.
* @return the new serializer
* @since 9.7.0.1
*/
public Serializer newSerializer(File file) {
Serializer serializer = processor.newSerializer(file);
serializer.setOutputProperties(controller.getExecutable().getPrimarySerializationProperties());
setBaseOutputURI(file.toURI().toString());
return serializer;
}
/**
* Create a serializer initialised to use the default output parameters defined in the stylesheet.
* These serialization parameters can be overridden by use of
* {@link Serializer#setOutputProperty(Serializer.Property, String)}.
*
* @param writer the Writer to which the serializer will write
* @return the new serializer
* @since 9.7.0.1
*/
public Serializer newSerializer(Writer writer) {
Serializer serializer = newSerializer();
serializer.setOutputWriter(writer);
return serializer;
}
/**
* Create a serializer initialised to use the default output parameters defined in the stylesheet.
* These serialization parameters can be overridden by use of
* {@link Serializer#setOutputProperty(Serializer.Property, String)}.
*
* @param stream the output stream to which the serializer will write
* @return the new serializer
* @since 9.7.0.1
*/
public Serializer newSerializer(OutputStream stream) {
Serializer serializer = newSerializer();
serializer.setOutputStream(stream);
return serializer;
}
}