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

net.sf.saxon.expr.instruct.ResultDocument 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.expr.instruct;





import net.sf.saxon.Configuration;
import net.sf.saxon.Controller;
import net.sf.saxon.event.LocationProvider;
import net.sf.saxon.event.PipelineConfiguration;
import net.sf.saxon.event.Receiver;
import net.sf.saxon.event.SequenceReceiver;
import net.sf.saxon.expr.Expression;
import net.sf.saxon.expr.Literal;
import net.sf.saxon.expr.StaticProperty;
import net.sf.saxon.expr.XPathContext;
import net.sf.saxon.expr.parser.ExpressionVisitor;
import net.sf.saxon.expr.parser.PathMap;
import net.sf.saxon.expr.parser.PromotionOffer;
import net.sf.saxon.functions.EscapeURI;
import net.sf.saxon.lib.*;
import net.sf.saxon.om.*;
import net.sf.saxon.trace.ExpressionPresenter;
import net.sf.saxon.trans.Err;
import net.sf.saxon.trans.SaxonErrorCode;
import net.sf.saxon.trans.XPathException;
import net.sf.saxon.type.*;
import net.sf.saxon.value.Whitespace;
import net.sf.saxon.z.IntHashMap;
import net.sf.saxon.z.IntIterator;

import javax.xml.transform.OutputKeys;
import javax.xml.transform.Result;
import javax.xml.transform.TransformerException;
import javax.xml.transform.dom.DOMResult;
import javax.xml.transform.sax.SAXResult;
import javax.xml.transform.stream.StreamResult;
import java.net.URI;
import java.util.*;



/**
 * The compiled form of an xsl:result-document element in the stylesheet.
 * 

* The xsl:result-document element takes an attribute href="filename". The filename will * often contain parameters, e.g. {position()} to ensure that a different file is produced * for each element instance. *

* There is a further attribute "format" which determines the format of the * output file, it identifies the name of an xsl:output element containing the output * format details. In addition, individual serialization properties may be specified as attributes. * These are attribute value templates, so they may need to be computed at run-time. */ public class ResultDocument extends Instruction implements ValidatingInstruction { /*@Nullable*/ protected Expression href; protected Expression formatExpression; // null if format was known at compile time protected Expression content; private boolean async = false; protected Properties globalProperties; protected Properties localProperties; protected String baseURI; // needed only for saxon:next-in-chain, or with fn:put() protected ParseOptions validationOptions; protected IntHashMap serializationAttributes; protected NamespaceResolver nsResolver; protected Expression dynamicOutputElement; // used in saxon:result-document() extension function protected boolean resolveAgainstStaticBase = false; // used with fn:put() /** * Create a result-document instruction * @param globalProperties properties defined on static xsl:output * @param localProperties non-AVT properties defined on result-document element * @param href href attribute of instruction * @param formatExpression format attribute of instruction * @param baseURI base URI of the instruction * @param validationAction for example {@link net.sf.saxon.lib.Validation#STRICT} * @param schemaType schema type against which output is to be validated * @param serializationAttributes computed local properties * @param nsResolver namespace resolver */ public ResultDocument(Properties globalProperties, // properties defined on static xsl:output Properties localProperties, // non-AVT properties defined on result-document element Expression href, Expression formatExpression, // AVT defining the output format String baseURI, int validationAction, SchemaType schemaType, IntHashMap serializationAttributes, // computed local properties only NamespaceResolver nsResolver) { this.globalProperties = globalProperties; this.localProperties = localProperties; this.href = href; this.formatExpression = formatExpression; this.baseURI = baseURI; setValidationAction(validationAction, schemaType); this.serializationAttributes = serializationAttributes; this.nsResolver = nsResolver; adoptChildExpression(href); for (Iterator it = serializationAttributes.valueIterator(); it.hasNext();) { adoptChildExpression((Expression)it.next()); } } /** * Set the expression that constructs the content * @param content the expression defining the content of the result document */ public void setContentExpression(Expression content) { this.content = content; adoptChildExpression(content); } /** * Get the expression that constructs the content * @return the content expression */ public Expression getContentExpression() { return content; } /** * Set the schema type to be used for validation * * @param type the type to be used for validation. (For a document constructor, this is the required * type of the document element) */ public void setSchemaType(SchemaType type) { if (validationOptions == null) { validationOptions = new ParseOptions(); } validationOptions.setSchemaValidationMode(Validation.BY_TYPE); validationOptions.setTopLevelType(type); } /** * Get the schema type chosen for validation; null if not defined * * @return the type to be used for validation. (For a document constructor, this is the required * type of the document element) */ public SchemaType getSchemaType() { return validationOptions==null ? null : validationOptions.getTopLevelType(); } /** * Get the expression that computes the href attribute * @return the href expression, or null if there is no href attribute */ public Expression getHrefExpression() { return href; } /** * Get the static base URI of the expression * @return the static base URI */ public String getStaticBaseURI() { return baseURI; } public boolean isResolveAgainstStaticBase() { return resolveAgainstStaticBase; } /** * Get the validation options * @return the validation options for the content of the constructed node. May be null if no * validation was requested. */ public ParseOptions getValidationOptions() { return validationOptions; } /** * Set the validation mode for the new document * * @param mode the validation mode, for example {@link Validation#STRICT} * @param schemaType the required type (for validation by type). Null if not * validating by type */ public void setValidationAction(int mode, /*@Nullable*/ SchemaType schemaType) { boolean preservingTypes = (mode == Validation.PRESERVE && schemaType == null); if (!preservingTypes) { if (validationOptions == null) { validationOptions = new ParseOptions(); validationOptions.setSchemaValidationMode(mode); validationOptions.setTopLevelType(schemaType); } } } /** * Get the validation mode for this instruction * * @return the validation mode, for example {@link Validation#STRICT} or {@link Validation#PRESERVE} */ public int getValidationAction() { return validationOptions == null ? Validation.PRESERVE : validationOptions.getSchemaValidationMode(); } public Expression getFormatExpression() { return formatExpression; } /** * Set an expression that evaluates to a run-time xsl:output element, used in the saxon:result-document() * extension function designed for use in XQuery * @param exp the expression whose result should be an xsl:output element */ public void setDynamicOutputElement(Expression exp) { dynamicOutputElement = exp; } /** * Set whether the the instruction should resolve the href relative URI against the static * base URI (rather than the dynamic base output URI) * @param staticBase set to true by fn:put(), to resolve against the static base URI of the query. * Default is false, which causes resolution against the base output URI obtained dynamically * from the Controller */ public void setUseStaticBaseUri(boolean staticBase) { resolveAgainstStaticBase = staticBase; } public void setAsynchronous(boolean async) { this.async = async; } /** * Ask if the instruction is to be asynchronous * @return true unless saxon:asynchronous="no" was specified (regardless of other options that might * suppress asychronous operation) */ public boolean isAsynchronous() { return async; } /** * Simplify an expression. This performs any static optimization (by rewriting the expression * as a different expression). The default implementation does nothing. * @param visitor an expression visitor * @return the simplified expression * @throws net.sf.saxon.trans.XPathException * if an error is discovered during expression rewriting */ /*@NotNull*/ public Expression simplify(ExpressionVisitor visitor) throws XPathException { content = visitor.simplify(content); href = visitor.simplify(href); for (IntIterator it = serializationAttributes.keyIterator(); it.hasNext();) { int key = it.next(); Expression value = serializationAttributes.get(key); if (!(value instanceof Literal)) { value = visitor.simplify(value); serializationAttributes.put(key, value); } } return this; } /*@NotNull*/ public Expression typeCheck(ExpressionVisitor visitor, ExpressionVisitor.ContextItemType contextItemType) throws XPathException { content = visitor.typeCheck(content, contextItemType); adoptChildExpression(content); if (href != null) { href = visitor.typeCheck(href, contextItemType); adoptChildExpression(href); } if (formatExpression != null) { formatExpression = visitor.typeCheck(formatExpression, contextItemType); adoptChildExpression(formatExpression); } for (IntIterator it = serializationAttributes.keyIterator(); it.hasNext();) { int key = it.next(); Expression value = serializationAttributes.get(key); if (!(value instanceof Literal)) { value = visitor.typeCheck(value, contextItemType); adoptChildExpression(value); serializationAttributes.put(key, value); } } try { DocumentInstr.checkContentSequence(visitor.getStaticContext(), content, validationOptions); } catch (XPathException err) { err.maybeSetLocation(this); throw err; } return this; } /*@NotNull*/ public Expression optimize(ExpressionVisitor visitor, ExpressionVisitor.ContextItemType contextItemType) throws XPathException { content = visitor.optimize(content, contextItemType); adoptChildExpression(content); if (href != null) { href = visitor.optimize(href, contextItemType); adoptChildExpression(href); } if (formatExpression != null) { formatExpression = visitor.optimize(formatExpression, contextItemType); adoptChildExpression(formatExpression); // TODO: if the formatExpression is now a constant, could get the output properties now } for (IntIterator it = serializationAttributes.keyIterator(); it.hasNext();) { int key = it.next(); Expression value = serializationAttributes.get(key); if (!(value instanceof Literal)) { value = visitor.optimize(value, contextItemType); adoptChildExpression(value); serializationAttributes.put(key, value); } } return this; } public int getIntrinsicDependencies() { return StaticProperty.HAS_SIDE_EFFECTS; } /** * Copy an expression. This makes a deep copy. * @return the copy of the original expression */ /*@NotNull*/ public Expression copy() { ResultDocument r = new ResultDocument( globalProperties, localProperties, (href == null ? null : href.copy()), (formatExpression == null ? null : formatExpression.copy()), baseURI, getValidationAction(), getSchemaType(), serializationAttributes, nsResolver); r.content = content.copy(); r.dynamicOutputElement = (dynamicOutputElement == null ? null : dynamicOutputElement.copy()); r.resolveAgainstStaticBase = resolveAgainstStaticBase; r.async = async; return r; } /** * Handle promotion offers, that is, non-local tree rewrites. * @param offer The type of rewrite being offered * @throws XPathException */ protected void promoteInst(PromotionOffer offer) throws XPathException { content = doPromotion(content, offer); if (href != null) { href = doPromotion(href, offer); } for (IntIterator it = serializationAttributes.keyIterator(); it.hasNext();) { int key = it.next(); Expression value = serializationAttributes.get(key); if (!(value instanceof Literal)) { value = doPromotion(value, offer); serializationAttributes.put(key, value); } } } /** * Get the name of this instruction for diagnostic and tracing purposes * (the string "xsl:result-document") */ public int getInstructionNameCode() { return StandardNames.XSL_RESULT_DOCUMENT; } /** * Get the item type of the items returned by evaluating this instruction * @param th the type hierarchy cache * @return the static item type of the instruction. This is empty: the result-document instruction * returns nothing. */ /*@NotNull*/ public ItemType getItemType(TypeHierarchy th) { return ErrorType.getInstance(); } /** * Get all the XPath expressions associated with this instruction * (in XSLT terms, the expression present on attributes of the instruction, * as distinct from the child instructions in a sequence construction) */ /*@NotNull*/ public Iterator iterateSubExpressions() { ArrayList list = new ArrayList(6); list.add(content); if (href != null) { list.add(href); } if (formatExpression != null) { list.add(formatExpression); } for (Iterator it = serializationAttributes.valueIterator(); it.hasNext();) { list.add(it.next()); } if (dynamicOutputElement != null) { list.add(dynamicOutputElement); } return list.iterator(); } /** * Replace one subexpression by a replacement subexpression * @param original the original subexpression * @param replacement the replacement subexpression * @return true if the original subexpression is found */ public boolean replaceSubExpression(Expression original, Expression replacement) { boolean found = false; if (content == original) { content = replacement; found = true; } if (href == original) { href = replacement; found = true; } for (IntIterator it = serializationAttributes.keyIterator(); it.hasNext();) { int k = it.next(); if (serializationAttributes.get(k) == original) { serializationAttributes.put(k, replacement); found = true; } } if (dynamicOutputElement == original) { dynamicOutputElement = replacement; found = true; } return found; } /** * Add a representation of this expression to a PathMap. The PathMap captures a map of the nodes visited * by an expression in a source tree. *

*

The default implementation of this method assumes that an expression does no navigation other than * the navigation done by evaluating its subexpressions, and that the subexpressions are evaluated in the * same context as the containing expression. The method must be overridden for any expression * where these assumptions do not hold. For example, implementations exist for AxisExpression, ParentExpression, * and RootExpression (because they perform navigation), and for the doc(), document(), and collection() * functions because they create a new navigation root. Implementations also exist for PathExpression and * FilterExpression because they have subexpressions that are evaluated in a different context from the * calling expression.

* * @param pathMap the PathMap to which the expression should be added * @param pathMapNodeSet the PathMapNodeSet to which the paths embodied in this expression should be added * @return the pathMapNodeSet representing the points in the source document that are both reachable by this * expression, and that represent possible results of this expression. For an expression that does * navigation, it represents the end of the arc in the path map that describes the navigation route. For other * expressions, it is the same as the input pathMapNode. */ public PathMap.PathMapNodeSet addToPathMap(PathMap pathMap, PathMap.PathMapNodeSet pathMapNodeSet) { PathMap.PathMapNodeSet result = super.addToPathMap(pathMap, pathMapNodeSet); result.setReturnable(false); return new PathMap.PathMapNodeSet(pathMap.makeNewRoot(this)); } public TailCall processLeavingTail(XPathContext context) throws XPathException { process(content, context); return null; } public void process(Expression content, XPathContext context) throws XPathException { context.getConfiguration().processResultDocument(this, content, context); } /** * Evaluation method designed for calling from compiled bytecode. * @param content The content expression. When called from bytecode, this will be the compiled version * of the interpreted content expression * @param context dynamic evaluation context * @throws XPathException if a dynamic error occurs */ public void processInstruction(Expression content, XPathContext context) throws XPathException { final Controller controller = context.getController(); assert controller != null; SequenceReceiver saved = context.getReceiver(); if (context.isTemporaryOutputState()) { XPathException err = new XPathException("Cannot execute xsl:result-document while writing a temporary tree"); err.setErrorCode("XTDE1480"); err.setLocator(this); throw err; } Result result; OutputURIResolver resolver = (href == null ? null : controller.getOutputURIResolver().newInstance()); try { result = getResult(href, baseURI, context, resolver, resolveAgainstStaticBase); } catch (XPathException e) { e.maybeSetLocation(this); throw e; } SerializerFactory sf = context.getConfiguration().getSerializerFactory(); Properties computedLocalProps = gatherOutputProperties(context); String nextInChain = computedLocalProps.getProperty(SaxonOutputKeys.NEXT_IN_CHAIN); if (nextInChain != null && nextInChain.length() > 0) { try { result = sf.prepareNextStylesheet(controller, nextInChain, baseURI, result); } catch (TransformerException e) { throw XPathException.makeXPathException(e); } } // TODO: cache the serializer and reuse it if the serialization properties are fixed at // compile time (that is, if serializationAttributes.isEmpty). Need to save the serializer // in a form where the final output destination can be changed. PipelineConfiguration pipe = controller.makePipelineConfiguration(); pipe.setHostLanguage(Configuration.XSLT); LocationProvider provider = pipe.getLocationProvider(); Receiver receiver = sf.getReceiver(result, pipe, computedLocalProps); context.changeOutputDestination(receiver, validationOptions); // changeOutputDestination changes the location provider in a way we don't want (affects validation errors) pipe.setLocationProvider(provider); SequenceReceiver out = context.getReceiver(); out.open(); try { out.startDocument(0); content.process(context); out.endDocument(); } catch (XPathException err) { err.setXPathContext(context); err.maybeSetLocation(this); throw err; } finally { out.close(); } context.setReceiver(saved); if (resolver != null && result != controller.getPrincipalResult()) { try { //System.err.println("Trying to close " + result); resolver.close(result); } catch (TransformerException e) { throw XPathException.makeXPathException(e); } } } public static Result getResult(Expression href, String baseURI, XPathContext context, OutputURIResolver resolver, boolean resolveAgainstStaticBase) throws XPathException { String resultURI; Result result; Controller controller = context.getController(); if (href == null) { result = controller.getPrincipalResult(); resultURI = controller.getBaseOutputURI(); if (resultURI == null) { resultURI = Controller.ANONYMOUS_PRINCIPAL_OUTPUT_URI; } } else { try { String base; if (resolveAgainstStaticBase) { base = baseURI; } else { base = controller.getCookedBaseOutputURI(); } String hrefValue = EscapeURI.iriToUri(href.evaluateAsString(context)).toString(); if (hrefValue.equals("")) { result = controller.getPrincipalResult(); resultURI = controller.getBaseOutputURI(); if (resultURI == null) { resultURI = Controller.ANONYMOUS_PRINCIPAL_OUTPUT_URI; } } else { try { result = (resolver==null ? null : resolver.resolve(hrefValue, base)); //System.err.println("Resolver returned " + result); } catch (TransformerException err) { throw XPathException.makeXPathException(err); } catch (Exception err) { err.printStackTrace(); throw new XPathException("Exception thrown by OutputURIResolver", err); } if (result == null) { resolver = StandardOutputResolver.getInstance(); result = resolver.resolve(hrefValue, base); } resultURI = result.getSystemId(); if (resultURI == null) { try { resultURI = new URI(base).resolve(hrefValue).toString(); result.setSystemId(resultURI); } catch (Exception err) { // no action } } } } catch (TransformerException e) { throw XPathException.makeXPathException(e); } } checkAcceptableUri(context, resultURI); traceDestination(context, result); return result; } public static void traceDestination(XPathContext context, Result result) { Configuration config = context.getConfiguration(); boolean timing = config.isTiming(); if (timing) { String dest = result.getSystemId(); if (dest == null) { if (result instanceof StreamResult) { dest = "anonymous output stream"; } else if (result instanceof SAXResult) { dest = "SAX2 ContentHandler"; } else if (result instanceof DOMResult) { dest = "DOM tree"; } else { dest = result.getClass().getName(); } } config.getStandardErrorOutput().println("Writing to " + dest); } } public static void checkAcceptableUri(XPathContext context, String uri) throws XPathException { Controller controller = context.getController(); assert controller != null; //String uri = result.getSystemId(); if (uri != null) { if (controller.getDocumentPool().find(uri) != null) { XPathException err = new XPathException("Cannot write to a URI that has already been read: " + (uri.equals(Controller.ANONYMOUS_PRINCIPAL_OUTPUT_URI) ? "(implicit output URI)" : uri)); err.setXPathContext(context); err.setErrorCode("XTRE1500"); throw err; } DocumentURI documentKey = new DocumentURI(uri); //noinspection SynchronizationOnLocalVariableOrMethodParameter synchronized (controller) { if (!controller.checkUniqueOutputDestination(documentKey)) { XPathException err = new XPathException("Cannot write more than one result document to the same URI: " + (uri.equals(Controller.ANONYMOUS_PRINCIPAL_OUTPUT_URI) ? "(implicit output URI)" : uri)); err.setXPathContext(context); err.setErrorCode("XTDE1490"); throw err; } else { controller.addUnavailableOutputDestination(documentKey); } } } controller.setThereHasBeenAnExplicitResultDocument(); } /** * Create a properties object that combines the serialization properties specified * on the xsl:result-document itself with those specified in the referenced xsl:output declaration * @param context The XPath evaluation context * @return the assembled properties * @throws XPathException if invalid properties are found */ public Properties gatherOutputProperties(XPathContext context) throws XPathException { Controller controller = context.getController(); assert controller != null; Configuration config = context.getConfiguration(); NamePool namePool = config.getNamePool(); Properties computedGlobalProps = globalProperties; if (formatExpression != null) { // format was an AVT and now needs to be computed CharSequence format = formatExpression.evaluateAsString(context); String[] parts; try { parts = config.getNameChecker().getQNameParts(format); } catch (QNameException e) { XPathException err = new XPathException("The requested output format " + Err.wrap(format) + " is not a valid QName"); err.setErrorCode("XTDE1460"); err.setXPathContext(context); throw err; } String uri = nsResolver.getURIForPrefix(parts[0], false); if (uri == null) { XPathException err = new XPathException("The namespace prefix in the format name " + format + " is undeclared"); err.setErrorCode("XTDE1460"); err.setXPathContext(context); throw err; } StructuredQName qName = new StructuredQName(parts[0], uri, parts[1]); computedGlobalProps = getExecutable().getOutputProperties(qName); if (computedGlobalProps == null) { XPathException err = new XPathException("There is no xsl:output format named " + format); err.setErrorCode("XTDE1460"); err.setXPathContext(context); throw err; } } // Now combine the properties specified on xsl:result-document with those specified on xsl:output Properties computedLocalProps = new Properties(computedGlobalProps); // First handle the properties with fixed values on xsl:result-document for (Object keyo : localProperties.keySet()) { String key = (String)keyo; String[] parts = NamePool.parseClarkName(key); try { setSerializationProperty(computedLocalProps, parts[0], parts[1], localProperties.getProperty(key), nsResolver, true, config); } catch (XPathException e) { e.setErrorCode("XTDE0030"); e.maybeSetLocation(this); throw e; } } // Now add the properties that were specified as AVTs if (serializationAttributes.size() > 0) { for (IntIterator it = serializationAttributes.keyIterator(); it.hasNext();) { int key = it.next(); Expression exp = serializationAttributes.get(key); String value = exp.evaluateAsString(context).toString(); String lname = namePool.getLocalName(key); String uri = namePool.getURI(key); try { setSerializationProperty(computedLocalProps, uri, lname, value, nsResolver, false, config); } catch (XPathException e) { e.setErrorCode("XTDE0030"); e.maybeSetLocation(this); e.maybeSetContext(context); if (NamespaceConstant.SAXON.equals(e.getErrorCodeNamespace()) && "SXWN".equals(e.getErrorCodeLocalPart().substring(0, 4))) { try { controller.getErrorListener().warning(e); } catch (TransformerException e2) { throw XPathException.makeXPathException(e2); } } else { throw e; } } } } // Handle properties specified using a dynamic xsl:output element // (Used when the instruction is generated from a saxon:result-document extension function call) if (dynamicOutputElement != null) { Item outputArg = dynamicOutputElement.evaluateItem(context); if (!(outputArg instanceof NodeInfo && ((NodeInfo)outputArg).getNodeKind() == Type.ELEMENT && ((NodeInfo)outputArg).getFingerprint() == StandardNames.XSL_OUTPUT)) { XPathException err = new XPathException( "The third argument of saxon:result-document must be an element"); err.setLocator(this); err.setXPathContext(context); throw err; } Properties dynamicProperties = new Properties(); processXslOutputElement((NodeInfo)outputArg, dynamicProperties, context); for (Object o : dynamicProperties.keySet()) { String key = (String) o; StructuredQName name = StructuredQName.fromClarkName(key); String value = dynamicProperties.getProperty(key); try { setSerializationProperty( computedLocalProps, name.getURI(), name.getLocalPart(), value, nsResolver, false, config); } catch (XPathException e) { e.setErrorCode("XTDE0030"); e.maybeSetLocation(this); e.maybeSetContext(context); throw e; } } } return computedLocalProps; } /** * Validate a serialization property and add its value to a Properties collection * @param details the properties to be updated * @param uri the uri of the property name * @param lname the local part of the property name * @param value the value of the serialization property. In the case of QName-valued values, * this will use lexical QNames if prevalidated is false and a NamespaceResolver is supplied; * otherwise they will use Clark-format names * @param nsResolver resolver for lexical QNames; not needed if prevalidated, or if QNames are supplied in Clark * format * @param prevalidated true if values are already known to be valid and lexical QNames have been * expanded into Clark notation * @param config the Saxon configuration * @throws XPathException if any serialization property has an invalid value */ public static void setSerializationProperty(Properties details, String uri, String lname, String value, /*@Nullable*/ NamespaceResolver nsResolver, boolean prevalidated, Configuration config) throws XPathException { NameChecker checker = config.getNameChecker(); if (uri.length() == 0 || NamespaceConstant.SAXON.equals(uri)) { if (lname.equals(StandardNames.METHOD)) { if (value.equals("xml") || value.equals("html") || value.equals("text") || value.equals("xhtml") || prevalidated || value.startsWith("{")) { details.setProperty(OutputKeys.METHOD, value); } else { String[] parts; try { parts = checker.getQNameParts(value); String prefix = parts[0]; if (prefix.length() == 0) { XPathException err = new XPathException("method must be xml, html, xhtml, or text, or a prefixed name"); err.setErrorCode("SEPM0016"); err.setIsStaticError(true); throw err; } else if (nsResolver != null) { String muri = nsResolver.getURIForPrefix(prefix, false); if (muri == null) { XPathException err = new XPathException("Namespace prefix '" + prefix + "' has not been declared"); err.setErrorCode("SEPM0016"); err.setIsStaticError(true); throw err; } details.setProperty(OutputKeys.METHOD, '{' + muri + '}' + parts[1]); } else { details.setProperty(OutputKeys.METHOD, value); } } catch (QNameException e) { XPathException err = new XPathException("Invalid method name. " + e.getMessage()); err.setErrorCode("SEPM0016"); err.setIsStaticError(true); throw err; } } } else if (lname.equals(StandardNames.USE_CHARACTER_MAPS)) { // The use-character-maps attribute is always turned into a Clark-format name at compile time String existing = details.getProperty(SaxonOutputKeys.USE_CHARACTER_MAPS); if (existing == null) { existing = ""; } details.setProperty(SaxonOutputKeys.USE_CHARACTER_MAPS, existing + value); } else if (lname.equals("cdata-section-elements")) { processListOfNodeNames(details, OutputKeys.CDATA_SECTION_ELEMENTS, value, nsResolver, true, prevalidated, checker); } else if (lname.equals("suppress-indentation")) { processListOfNodeNames(details, SaxonOutputKeys.SUPPRESS_INDENTATION, value, nsResolver, true, prevalidated, checker); } else if (lname.equals("double-space")) { processListOfNodeNames(details, SaxonOutputKeys.DOUBLE_SPACE, value, nsResolver, true, prevalidated, checker); } else if (lname.equals("attribute-order")) { processListOfNodeNames(details, SaxonOutputKeys.ATTRIBUTE_ORDER, value, nsResolver, false, prevalidated, checker); } else if (lname.equals("next-in-chain")) { XPathException e = new XPathException("saxon:next-in-chain property is available only on xsl:output"); e.setErrorCodeQName( new StructuredQName("saxon", NamespaceConstant.SAXON, SaxonErrorCode.SXWN9004)); throw e; } else { // all other properties in the default or Saxon namespaces if (lname.equals("output-version")) { lname = "version"; } String clarkName = lname; if (uri.length() != 0) { clarkName = '{' + uri + '}' + lname; } if (!prevalidated) { try { SaxonOutputKeys.checkOutputProperty(clarkName, value, config); } catch (XPathException err) { err.maybeSetErrorCode("SEPM0016"); throw err; } } details.setProperty(clarkName, value); } } else { // properties in user-defined namespaces details.setProperty('{' + uri + '}' + lname, value); } } private static void processListOfNodeNames(Properties details, String key, String value, NamespaceResolver nsResolver, boolean useDefaultNS, boolean prevalidated, NameChecker checker) throws XPathException { String existing = details.getProperty(key); if (existing == null) { existing = ""; } String s = SaxonOutputKeys.parseListOfNodeNames(value, nsResolver, useDefaultNS, prevalidated, checker, "SEPM0016"); details.setProperty(key, existing + s); } /** * Diagnostic print of expression structure. The abstract expression tree * is written to the supplied output destination. */ public void explain(ExpressionPresenter out) { out.startElement("resultDocument"); if (href != null) { out.startSubsidiaryElement("href"); href.explain(out); out.endSubsidiaryElement(); } out.startSubsidiaryElement("content"); content.explain(out); out.endSubsidiaryElement(); out.endElement(); } /** * Construct a set of output properties from an xsl:output element supplied at run-time * @param element an xsl:output element * @param props Properties object to which will be added the values of those serialization properties * that were specified * @param c the XPath dynamic context * @throws net.sf.saxon.trans.XPathException if a dynamic error occurs */ public static void processXslOutputElement(NodeInfo element, Properties props, XPathContext c) throws XPathException { SequenceIterator iter = element.iterateAxis(AxisInfo.ATTRIBUTE); NamespaceResolver resolver = new InscopeNamespaceResolver(element); while (true) { NodeInfo att = (NodeInfo)iter.next(); if (att == null) { break; } String uri = att.getURI(); String local = att.getLocalPart(); String val = Whitespace.trim(att.getStringValueCS()); setSerializationProperty(props, uri, local, val, resolver, false, c.getConfiguration()); } } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy