net.sf.saxon.functions.Doc_2 Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of Saxon-HE Show documentation
Show all versions of Saxon-HE Show documentation
The XSLT and XQuery Processor
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// 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.functions;
import net.sf.saxon.Configuration;
import net.sf.saxon.Controller;
import net.sf.saxon.event.Builder;
import net.sf.saxon.event.Sender;
import net.sf.saxon.expr.*;
import net.sf.saxon.expr.accum.Accumulator;
import net.sf.saxon.expr.accum.AccumulatorRegistry;
import net.sf.saxon.expr.parser.RetainedStaticContext;
import net.sf.saxon.lib.ParseOptions;
import net.sf.saxon.lib.Validation;
import net.sf.saxon.ma.map.MapItem;
import net.sf.saxon.om.*;
import net.sf.saxon.style.StylesheetPackage;
import net.sf.saxon.trans.XPathException;
import net.sf.saxon.trans.XsltController;
import net.sf.saxon.tree.tiny.TinyBuilder;
import net.sf.saxon.type.BuiltInAtomicType;
import net.sf.saxon.value.*;
import javax.xml.transform.Source;
import javax.xml.transform.stream.StreamSource;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
/**
* Implement the saxon:doc() function - a variant of the fn:doc function with
* a second argument to supply option parameters. Used to underpin the xsl:source-document
* instruction with streamable="no".
*/
public class Doc_2 extends SystemFunction implements Callable {
public Doc_2() {
}
public static OptionsParameter makeOptionsParameter() {
SequenceType listOfQNames = SequenceType.makeSequenceType(BuiltInAtomicType.QNAME, StaticProperty.ALLOWS_ZERO_OR_MORE);
OptionsParameter op = new OptionsParameter();
op.addAllowedOption("base-uri", SequenceType.SINGLE_STRING);
op.addAllowedOption("validation", SequenceType.SINGLE_STRING);
op.setAllowedValues("validation", "SXZZ0001", "strict", "lax", "preserve", "strip", "skip");
op.addAllowedOption("type", SequenceType.SINGLE_QNAME);
op.addAllowedOption("strip-space", SequenceType.SINGLE_STRING);
op.setAllowedValues("strip-space", "SXZZ0001", "none", "all", "ignorable", "package-defined", "default");
op.addAllowedOption("stable", SequenceType.SINGLE_BOOLEAN);
op.addAllowedOption("dtd-validation", SequenceType.SINGLE_BOOLEAN);
op.addAllowedOption("accumulators", listOfQNames);
op.addAllowedOption("use-xsi-schema-location", SequenceType.SINGLE_BOOLEAN);
return op;
}
public static ParseOptions setParseOptions(
RetainedStaticContext rsc, Map checkedOptions, XPathContext context) throws XPathException {
ParseOptions result = context.getConfiguration().getParseOptions();
Sequence value = checkedOptions.get("validation");
if (value != null) {
String valStr = value.head().getStringValue();
if ("skip".equals(valStr)) {
valStr = "strip";
}
int v = Validation.getCode(valStr);
if (v == Validation.INVALID) {
throw new XPathException("Invalid validation value " + valStr, "SXZZ0002");
}
result = result.withSchemaValidationMode(v);
}
value = checkedOptions.get("type");
if (value != null) {
QNameValue qval = (QNameValue) value.head();
result = result
.withTopLevelType(context.getConfiguration().getSchemaType(qval.getStructuredQName()))
.withSchemaValidationMode(Validation.BY_TYPE);
}
value = checkedOptions.get("strip-space");
if (value != null) {
String s = value.head().getStringValue();
switch (s) {
case "all":
result = result.withSpaceStrippingRule(AllElementsSpaceStrippingRule.getInstance());
break;
case "none":
result = result.withSpaceStrippingRule(NoElementsSpaceStrippingRule.getInstance());
break;
case "ignorable":
result = result.withSpaceStrippingRule(IgnorableSpaceStrippingRule.getInstance());
break;
case "package-defined":
case "default":
PackageData data = rsc.getPackageData();
if (data instanceof StylesheetPackage) {
result = result.withSpaceStrippingRule(((StylesheetPackage) data).getSpaceStrippingRule());
}
break;
}
}
value = checkedOptions.get("dtd-validation");
if (value != null) {
result = result.withDTDValidationMode(((BooleanValue)value.head()).getBooleanValue() ? Validation.STRICT : Validation.SKIP);
}
value = checkedOptions.get("accumulators");
if (value != null) {
AccumulatorRegistry reg = rsc.getPackageData().getAccumulatorRegistry();
Set accumulators = new HashSet<>();
SequenceIterator iter = value.iterate();
Item it;
while ((it = iter.next()) != null) {
QNameValue name = (QNameValue)it;
Accumulator acc = reg.getAccumulator(name.getStructuredQName());
accumulators.add(acc);
}
result = result.withApplicableAccumulators(accumulators);
}
value = checkedOptions.get("use-xsi-schema-location");
if (value != null) {
result = result.withUseXsiSchemaLocation(((BooleanValue) value.head()).getBooleanValue());
}
return result;
}
/**
* Evaluate the expression
*
* @param context the dynamic evaluation context
* @param arguments the values of the arguments, supplied as SequenceIterators
* @return the result of the evaluation, in the form of a SequenceIterator
* @throws XPathException if a dynamic error occurs during the evaluation of the expression
*/
@Override
public Sequence call(XPathContext context, Sequence[] arguments) throws XPathException {
AtomicValue hrefVal = (AtomicValue) arguments[0].head();
if (hrefVal == null) {
return EmptySequence.getInstance();
}
String href = hrefVal.getStringValue();
Item param = arguments[1].head();
Map checkedOptions =
getDetails().optionDetails.processSuppliedOptions((MapItem) param, context);
ParseOptions parseOptions = setParseOptions(getRetainedStaticContext(), checkedOptions, context);
NodeInfo item = fetch(href, parseOptions, context).getRootNode();
if (item == null) {
// we failed to read the document
throw new XPathException("Failed to load document " + href, "FODC0002", context);
}
Controller controller = context.getController();
if (controller instanceof XsltController) {
((XsltController) controller).getAccumulatorManager().setApplicableAccumulators(
item.getTreeInfo(), parseOptions.getApplicableAccumulators()
);
}
return item;
}
private TreeInfo fetch(String href, ParseOptions options, XPathContext context) throws XPathException {
// Get a Source from the URIResolver
Configuration config = context.getConfiguration();
Controller controller = context.getController();
URI abs;
try {
abs = ResolveURI.makeAbsolute(href, getStaticBaseUriString());
} catch (URISyntaxException e) {
throw new XPathException("Invalid URI supplied to saxon:doc - " + e.getMessage(), "FODC0002");
}
Source source = config.getSourceResolver().resolveSource(new StreamSource(abs.toASCIIString()), config);
TreeInfo newdoc;
if (DocumentFn.sourceIsTree(source)) {
NodeInfo startNode = controller.prepareInputTree(source);
newdoc = startNode.getTreeInfo();
} else {
Builder b = controller.makeBuilder();
b.setDurability(Durability.LASTING);
if (b instanceof TinyBuilder) {
((TinyBuilder) b).setStatistics(config.getTreeStatistics().SOURCE_DOCUMENT_STATISTICS);
}
b.setPipelineConfiguration(b.getPipelineConfiguration());
try {
Sender.send(source, b, options);
newdoc = b.getCurrentRoot().getTreeInfo();
b.reset();
} finally {
if (options.isPleaseCloseAfterUse()) {
ParseOptions.close(source);
}
}
}
return newdoc;
}
/**
* Get the static properties of this expression (other than its type). The result is
* bit-signficant. These properties are used for optimizations. In general, if
* property bit is set, it is true, but if it is unset, the value is unknown.
*
* @param arguments the expressions supplied as arguments to the function
*/
@Override
public int getSpecialProperties(Expression[] arguments) {
return StaticProperty.ORDERED_NODESET |
StaticProperty.PEER_NODESET |
StaticProperty.NO_NODES_NEWLY_CREATED |
StaticProperty.SINGLE_DOCUMENT_NODESET;
// Declaring it as a peer node-set expression avoids sorting of expressions such as
// doc(XXX)/a/b/c
// The doc() function might appear to be creative: but it isn't, because multiple calls
// with the same arguments will produce identical results.
}
}