net.sf.saxon.Configuration 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;
import net.sf.saxon.event.*;
import net.sf.saxon.expr.*;
import net.sf.saxon.expr.accum.AccumulatorRegistry;
import net.sf.saxon.expr.compat.TypeChecker10;
import net.sf.saxon.expr.elab.PushEvaluator;
import net.sf.saxon.expr.instruct.*;
import net.sf.saxon.expr.number.Numberer_en;
import net.sf.saxon.expr.parser.*;
import net.sf.saxon.expr.sort.AlphanumericCollator;
import net.sf.saxon.expr.sort.CodepointCollator;
import net.sf.saxon.expr.sort.HTML5CaseBlindCollator;
import net.sf.saxon.functions.*;
import net.sf.saxon.functions.registry.*;
import net.sf.saxon.lib.*;
import net.sf.saxon.ma.arrays.ArrayFunctionSet;
import net.sf.saxon.ma.map.MapFunctionSet;
import net.sf.saxon.ma.map.MapItem;
import net.sf.saxon.om.*;
import net.sf.saxon.pattern.PatternParser;
import net.sf.saxon.query.QueryModule;
import net.sf.saxon.query.StaticQueryContext;
import net.sf.saxon.query.XQueryExpression;
import net.sf.saxon.query.XQueryParser;
import net.sf.saxon.regex.RegularExpression;
import net.sf.saxon.resource.*;
import net.sf.saxon.s9api.HostLanguage;
import net.sf.saxon.s9api.Location;
import net.sf.saxon.s9api.Xslt30Transformer;
import net.sf.saxon.serialize.CharacterMap;
import net.sf.saxon.serialize.SerializationProperties;
import net.sf.saxon.serialize.charcode.CharacterSetFactory;
import net.sf.saxon.serialize.charcode.XMLCharacterData;
import net.sf.saxon.str.StringView;
import net.sf.saxon.str.UnicodeString;
import net.sf.saxon.style.*;
import net.sf.saxon.sxpath.IndependentContext;
import net.sf.saxon.trace.ExpressionPresenter;
import net.sf.saxon.trace.XQueryTraceCodeInjector;
import net.sf.saxon.trace.XSLTTraceCodeInjector;
import net.sf.saxon.trans.*;
import net.sf.saxon.trans.packages.IPackageLoader;
import net.sf.saxon.transpile.CSharp;
import net.sf.saxon.transpile.CSharpInnerClass;
import net.sf.saxon.transpile.CSharpModifiers;
import net.sf.saxon.transpile.CSharpReplaceBody;
import net.sf.saxon.tree.tiny.TreeStatistics;
import net.sf.saxon.tree.util.DocumentNumberAllocator;
import net.sf.saxon.type.*;
import net.sf.saxon.value.ObjectValue;
import net.sf.saxon.value.SequenceType;
import net.sf.saxon.value.StringToDouble11;
import net.sf.saxon.z.*;
import org.xml.sax.*;
import org.xml.sax.ext.DefaultHandler2;
import org.xml.sax.ext.LexicalHandler;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;
import javax.xml.transform.*;
import java.io.*;
import java.lang.ref.Cleaner;
import java.net.URISyntaxException;
import java.net.URLDecoder;
import java.nio.charset.StandardCharsets;
import java.util.*;
import java.util.concurrent.ConcurrentLinkedQueue;
/**
* This class holds details of user-selected configuration options for a set of transformations
* and/or queries. When running XSLT, the preferred way of setting configuration options is via
* the JAXP TransformerFactory interface, but the Configuration object provides a finer
* level of control. As yet there is no standard API for XQuery, so the only way of setting
* Configuration information is to use the methods on this class directly.
* As well as holding configuration settings, this class acts as a factory for classes
* providing service in particular areas: error handling, URI resolution, and the like. Some
* of these services are chosen on the basis of the current platform (Java or .NET), some vary
* depending whether the environment is schema-aware or not.
* The Configuration
provides access to a {@link NamePool} which is used to manage
* all the names used in stylesheets, queries, schemas, and source and documents: the NamePool
* allocates integer codes to these names allowing efficient storage and comparison. Normally
* there will be a one-to-one relationship between a NamePool
and a Configuration
.
* It is possible, however, for several Configuration
objects to share the same
* NamePool
. Until Saxon 8.9, by default all Configuration
objects
* shared a single NamePool
unless configured otherwise; this changed in 8.9 so that
* the default is to allocate a new NamePool
for each Configuration
.
* The Configuration
establishes the scope within which node identity is managed.
* Every document belongs to a Configuration
, and every node has a distinct identity
* within that Configuration
. In consequence, it is not possible for any query or
* transformation to manipulate multiple documents unless they all belong to the same
* Configuration
.
* Saxon-EE has a subclass of the Configuration
class which provides the additional
* services needed for schema-aware processing. The {@link com.saxonica.config.EnterpriseConfiguration}
* also holds a cache of loaded schema components used for compiling schema-aware transformations
* and queries, and for validating instance documents.
* Since Saxon 8.4, the JavaDoc documentation for Saxon attempts to identify interfaces
* that are considered stable, and will only be changed in a backwards-incompatible way
* if there is an overriding reason to do so. These interfaces and methods are labelled
* with the JavaDoc "since" tag. The value 8.n indicates a method in this category that
* was introduced in Saxon version 8.n: or in the case of 8.4, that was present in Saxon 8.4
* and possibly in earlier releases. (In some cases, these methods have been unchanged for
* a long time.) Methods without a "since" tag, although public, are provided for internal
* use or for use by advanced users, and are subject to change from one release to the next.
* The presence of a "since" tag on a class or interface indicates that there are one or more
* methods in the class that are considered stable; it does not mean that all methods are
* stable.
*
* @since 8.4
*/
//@CSharpInjectMembers(code = {
// " public void setErrorReporter(System.Action reporter) {"
// + " setErrorReporter(new Saxon.Impl.Helpers.ErrorReportingAction(reporter));"
// + " }"
//})
public class Configuration implements SourceResolver, NotationSet {
protected static IntSet booleanFeatures = new IntHashSet(40);
protected static IntSet stringFeatures = new IntHashSet(40);
private transient ApiProvider apiProcessor = null;
private transient CharacterSetFactory characterSetFactory;
private final Map collationMap = new HashMap<>(10);
private CollationURIResolver collationResolver = new StandardCollationURIResolver();
private String defaultCollationName = NamespaceConstant.CODEPOINT_COLLATION_URI;
private Map registeredCollections = new HashMap<>();
private CollectionFinder collectionFinder;
private EnvironmentVariableResolver environmentVariableResolver = new StandardEnvironmentVariableResolver();
private String defaultCollection = null;
private ParseOptions defaultParseOptions = new ParseOptions();
protected transient StaticQueryContext defaultStaticQueryContext;
private StaticQueryContextFactory staticQueryContextFactory = new StaticQueryContextFactory();
protected OptimizerOptions optimizerOptions = OptimizerOptions.FULL_HE_OPTIMIZATION;
protected CompilerInfo defaultXsltCompilerInfo;
private java.util.function.Function errorReporterFactory;
protected IndependentContext staticContextForSystemFunctions;
private String label = null;
private DocumentNumberAllocator documentNumberAllocator = new DocumentNumberAllocator();
/*@Nullable*/
private transient Debugger debugger = null;
private String defaultLanguage = Version.platform.getDefaultLanguage();
private String defaultCountry = Version.platform.getDefaultCountry();
private Properties defaultOutputProperties = new Properties();
private transient IDynamicLoader dynamicLoader = Version.platform.getDefaultDynamicLoader();
private final IntSet enabledProperties = new IntHashSet(64);
private final IntHashMap stringProperties = new IntHashMap<>(); // TODO: not yet widely used
private List externalObjectModels = new ArrayList<>(4);
private final DocumentPool globalDocumentPool = new DocumentPool();
private final IntegratedFunctionLibrary integratedFunctionLibrary = new IntegratedFunctionLibrary();
private transient LocalizerFactory localizerFactory;
private NamePool namePool = new NamePool();
protected Optimizer optimizer = null;
private SerializerFactory serializerFactory;
private volatile ConcurrentLinkedQueue sourceParserPool = new ConcurrentLinkedQueue<>();
private volatile ConcurrentLinkedQueue styleParserPool = new ConcurrentLinkedQueue<>();
private String sourceParserClass;
private transient SourceResolver sourceResolver;
private transient Logger traceOutput = new StandardLogger();
private ModuleURIResolver standardModuleURIResolver;
private String styleParserClass;
private UnparsedTextURIResolver unparsedTextURIResolver;
private transient XPathContext theConversionContext = null;
private ConversionRules theConversionRules = null;
private transient TraceListener traceListener = null;
private String traceListenerClass = null;
private String traceListenerOutput = null;
private String defaultRegexEngine = "S";
protected transient TypeHierarchy typeHierarchy;
private final TypeChecker typeChecker = new TypeChecker();
private final TypeChecker10 typeChecker10 = new TypeChecker10();
private transient ResourceResolver commonResolver;
private ProtocolRestrictor protocolRestrictor = new ProtocolRestrictor("all");
protected IntHashMap builtInExtensionLibraryList = new IntHashMap<>(4);
protected int xsdVersion = XSD11;
private int xmlVersion = XML10;
private int xpathVersionForXsd = 20;
private int xpathVersionForXslt = 31;
// Plug-in to allow media queries in an xml-stylesheet processing instruction to be evaluated
private Comparator mediaQueryEvaluator;
private final Map fileExtensions = new HashMap<>();
private final Map resourceFactoryMapping = new HashMap<>();
private final Map functionAnnotationHandlers = new HashMap<>();
private int regexBacktrackingLimit = 10000000;
private final TreeStatistics treeStatistics = new TreeStatistics();
private Cleaner cleaner = null; // created on demand
/**
* Constant indicating the XML Version 1.0
*/
public static final int XML10 = 10;
/**
* Constant indicating the XML Version 1.1
*/
public static final int XML11 = 11;
/**
* Language versions for XML Schema
*/
public static final int XSD10 = 10;
public static final int XSD11 = 11;
/**
* Create a non-schema-aware configuration object with default settings for all options.
*
* @since 8.4
*/
public Configuration() {
init();
}
/**
* Factory method to create a Configuration, of the class defined using conditional
* compilation tags when compiling this version of Saxon: that is,
* the type of Configuration appropriate to the edition of the software
* being used. This method does not check that the Configuration is licensed.
*
* @return a Configuration object of the class appropriate to the Saxon edition in use.
* @since 9.2
*/
public static Configuration newConfiguration() {
Class extends Configuration> configurationClass = Configuration.class;
try {
return configurationClass.newInstance();
} catch (Exception e) {
e.printStackTrace();
throw new RuntimeException("Cannot instantiate a Configuration", e);
}
}
/**
* Factory method to create a Configuration, of the class defined using conditional
* compilation tags when compiling this version of Saxon: that is,
* the type of Configuration appropriate to the edition of the software
* being used, taking into account the license available. For example if the
* software is EE, but only a PE license is available, the method returns
* a ProfessionalConfiguration.
*
* @return a Configuration object of the class appropriate to the Saxon edition in use.
* @since 9.2
*/
public static Configuration newLicensedConfiguration() {
return new Configuration();
}
/**
* Factory method to construct a Configuration object by reading a configuration file.
*
* @param source Source object containing the configuration file
* @return the resulting Configuration
* @throws net.sf.saxon.trans.XPathException if the configuration file cannot be read
* or is invalid
*/
public static Configuration readConfiguration(Source source) throws XPathException {
Configuration tempConfig = newConfiguration();
return tempConfig.readConfigurationFile(source);
}
/**
* Factory method to construct a Configuration object by reading a configuration file.
* This version of the method creates a configuration that is "compatible" with the
* supplied configuration, in that it shares the same NamePool and DocumentNumberAllocator.
* (This is used by fn:transform)
*
* @param source Source object containing the configuration file
* @param baseConfiguration an existing configuration whose NamePool and DocumentNumberAllocator
* will be used in the new Configuration; the license details from
* the base configuration will also be shared
* @return the resulting Configuration
* @throws net.sf.saxon.trans.XPathException if the configuration file cannot be read
* or is invalid
*/
public static Configuration readConfiguration(Source source, Configuration baseConfiguration) throws XPathException {
Configuration tempConfig = newConfiguration();
return tempConfig.readConfigurationFile(source, baseConfiguration);
}
/**
* Instantiate a Configuration object with a given class name
* @param className the class name
* @param classLoader the class loader to be used
* @return a Configuration of the required class
* @throws ClassNotFoundException if the class is not found
* @throws InstantiationException if the class cannot be instantiated
* @throws IllegalAccessException if the class is not accessible
*/
@CSharpReplaceBody(code="return new Saxon.Eej.config.EnterpriseConfiguration();")
public static Configuration instantiateConfiguration(String className, ClassLoader classLoader) throws ClassNotFoundException, InstantiationException, IllegalAccessException {
Class theClass;
ClassLoader loader = classLoader;
if (loader == null) {
try {
loader = Thread.currentThread().getContextClassLoader();
} catch (Exception err) {
System.err.println("Failed to getContextClassLoader() - continuing");
}
}
if (loader != null) {
try {
theClass = loader.loadClass(className);
} catch (Exception ex) {
theClass = Class.forName(className);
}
} else {
theClass = Class.forName(className);
}
return (Configuration) theClass.newInstance();
}
/**
* Ask if Java is being run with assertions enabled (-ea option)
* @return true if the -ea option is set
*/
public static boolean isAssertionsEnabled() {
// Highly devious logic here. If assertions are enabled, the assertion is false, and a deliberate side-effect
// of evaluating the assertion is that assertsEnabled is set to true. If assertions are not enabled, the assert
// statement is not executed, so assertsEnabled is left as false.
boolean assertsEnabled = false;
//noinspection AssertWithSideEffects
assert assertsEnabled = true;
return assertsEnabled;
}
/**
* Read the configuration file an construct a new Configuration (the real one)
*
* @param source the source of the configuration file
* @return the Configuration that will be used for real work
* @throws XPathException if the configuration file cannot be read or is invalid
*/
protected Configuration readConfigurationFile(Source source) throws XPathException {
return makeConfigurationReader().makeConfiguration(source);
}
protected Configuration readConfigurationFile(Source source, Configuration baseConfiguration) throws XPathException {
ConfigurationReader reader = makeConfigurationReader();
reader.setBaseConfiguration(baseConfiguration);
return reader.makeConfiguration(source);
}
protected ConfigurationReader makeConfigurationReader() {
return new ConfigurationReader();
}
protected void init() {
// Note that some initializations have been moved here because C# is more restrictive about
// how fields can be initialized when first declared.
// There's a temptation to be lazy about instantiating the catalog resolver, but we want
// to make sure that if someone subsequently overrides, for example, the URIResolver,
// that their override wins.
commonResolver = new CatalogResourceResolver();
defaultXsltCompilerInfo = makeCompilerInfo();
//systemURIResolver = new StandardURIResolver(this);
standardModuleURIResolver = Version.platform.makeStandardModuleURIResolver(this);
serializerFactory = new net.sf.saxon.lib.SerializerFactory(this);
sourceResolver = this;
unparsedTextURIResolver = new StandardUnparsedTextResolver();
mediaQueryEvaluator = (o1, o2) -> 0;
Version.platform.initialize(this);
// If the call to makeStandardModuleURIResolver during initialization, created
// a StandardModuleURIResolver without a config, make sure it gets this config.
if (standardModuleURIResolver instanceof StandardModuleURIResolver) {
((StandardModuleURIResolver) standardModuleURIResolver).setConfiguration(this);
}
//internalSetBooleanProperty(FeatureCode.PREFER_JAXP_PARSER, FeatureKeys.PREFER_JAXP_PARSER, true);
internalSetBooleanProperty(FeatureCode.ALLOW_EXTERNAL_FUNCTIONS, FeatureKeys.ALLOW_EXTERNAL_FUNCTIONS, true);
internalSetBooleanProperty(FeatureCode.DISABLE_XSL_EVALUATE, FeatureKeys.DISABLE_XSL_EVALUATE, false);
//internalSetBooleanProperty(FeatureKeys.STABLE_COLLECTION_URI, true);
//@if CSHARP==false
String initializationClass = System.getProperty("SAXON_INITIALIZER");
if (initializationClass != null) {
try {
Initializer initializer = (Initializer) getInstance(initializationClass);
initializer.initialize(this);
} catch (TransformerException e) {
System.err.println("Warning: Failed to invoke Saxon Initializer " + initializationClass + ": " + e.getMessage());
}
}
//@endif
registerFileExtension("xml", "application/xml");
registerFileExtension("html", "application/html");
registerFileExtension("atom", "application/atom");
registerFileExtension("xsl", "application/xml+xslt");
registerFileExtension("xslt", "application/xml+xslt");
registerFileExtension("xsd", "application/xml+xsd");
registerFileExtension("txt", "text/plain");
registerFileExtension("MF", "text/plain");
registerFileExtension("class", "application/java");
registerFileExtension("json", "application/json");
registerFileExtension("", "application/unknown");
registerMediaType("application/xml", XmlResource.FACTORY);
registerMediaType("text/xml", XmlResource.FACTORY);
registerMediaType("application/html", XmlResource.FACTORY);
registerMediaType("text/html", XmlResource.FACTORY);
registerMediaType("application/atom", XmlResource.FACTORY);
registerMediaType("application/xml+xslt", XmlResource.FACTORY);
registerMediaType("application/xml+xsd", XmlResource.FACTORY);
registerMediaType("application/rdf+xml", XmlResource.FACTORY);
registerMediaType("text/plain", UnparsedTextResource.FACTORY);
registerMediaType("application/java", BinaryResource.FACTORY);
registerMediaType("application/binary", BinaryResource.FACTORY);
registerMediaType("application/json", JSONResource.FACTORY);
registerMediaType("application/unknown", UnknownResource.FACTORY);
registerFunctionAnnotationHandler(new XQueryFunctionAnnotationHandler());
}
/**
* Static method to instantiate a professional or enterprise configuration.
* This method fails if the specified configuration class cannot be loaded,
* but it does not check whether there is a license available.
*
* @param classLoader - the class loader to be used. If null, the context class loader for the current
* thread is used.
* @param className - the name of the configuration class. Defaults to
* "com.saxonica.config.ProfessionalConfiguration" if null is supplied. This allows an assembly
* qualified name to be supplied under .NET. The class, once instantiated, must be an instance
* of Configuration.
* @return the new ProfessionalConfiguration or EnterpriseConfiguration
* @throws RuntimeException if the required Saxon edition cannot be loaded
* @since 9.2 (renamed from makeSchemaAwareConfiguration)
*/
@CSharpReplaceBody(code = "return new Saxon.Eej.config.EnterpriseConfiguration();")
public static Configuration makeLicensedConfiguration(ClassLoader classLoader, /*@Nullable*/ String className)
throws RuntimeException {
if (className == null) {
className = "com.saxonica.config.ProfessionalConfiguration";
}
try {
return instantiateConfiguration(className, classLoader);
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException e) {
throw new RuntimeException(e);
}
}
public void importLicenseDetails(Configuration config) {
// No action for an HE configuration
}
/**
* Get the edition code identifying this configuration: "HE", "PE" or "EE"
*
* @return the code identifying the Saxon edition associated with this configuration
*/
public String getEditionCode() {
return "HE";
}
/**
* Marker interface to represent an API that is provided as a layer on top of this
* {@code Configuration}
*/
public interface ApiProvider {}
/**
* Save the ApiProvider object that owns this {@code Configuration} in the relevant API.
*
Note: it is possible to use multiple APIs over the same {@code Configuration}. This mechanism
* is only capable of holding one of these, and is therefore only really useful in cases where
* the API in use is homogeneous.
*
* @param processor This can be any ApiProvider, but it is typically one of the
* following:
*
* - When using the Java s9api interface, the
net.sf.saxon.s9api.Processor
* - When using the .NET interface, the
Saxon.Api.Processor
* - When using the JAXP transformation interface, the JAXP
TransformerFactory
* - When using the JAXP XPath interface, the JAXP
XPathFactory
* - When using the JAXP Schema interface, the JAXP
SchemaFactory
* - When using XQJ, the
XQDataSource
*
* @since 9.2. Changed in 9.9 to require an {@link ApiProvider} rather than any Object.
*/
public void setProcessor(ApiProvider processor) {
this.apiProcessor = processor;
}
/**
* Get the Processor object that created this Configuration in the relevant API.
* The main purpose of this interface is to allow extension functions called from
* a stylesheet or query to get access to the originating processor. This is particularly
* useful when methods are static as there is then limited scope for passing data from the
* calling application to the code of the extension function.
*
* @return the processor that was supplied to the {@link #setProcessor(ApiProvider)} method, or null
* if this method has not been called. In practice this property is used to hold one of the
* following:
*
* - When using the Java s9api interface, the
net.sf.saxon.s9api.Processor
* - When using the .NET interface, the
Saxon.Api.Processor
* - When using the JAXP transformation interface, the JAXP
TransformerFactory
* - When using the JAXP XPath interface, the JAXP
XPathFactory
* - When using the JAXP Schema interface, the JAXP
SchemaFactory
* - When using XQJ, the
XQDataSource
*
* @since 9.2. Changed in 9.9 to return an {@link ApiProvider} rather than any Object.
*/
/*@Nullable*/
public ApiProvider getProcessor() {
return apiProcessor;
}
/**
* Get a message used to identify this product when a transformation is run using the -t option
*
* @return A string containing both the product name and the product
* version
* @since 8.4
*/
public String getProductTitle() {
return "Saxon" + Version.platform.getPlatformSuffix() + "-" + getEditionCode() + " " +
Version.getProductVersion() + " from Saxonica";
}
/**
* Check whether a particular feature is licensed, with a fatal error if it is not
*
* @param feature the feature in question, identified by a constant in class {@link LicenseFeature}
* @param name the name of the feature for use in diagnostics
* @param localLicenseId identifies an embedded license, or -1 if not applicable
* @throws LicenseException if the feature is not licensed. This is a RunTimeException, so it will normally be fatal.
*/
public void checkLicensedFeature(int feature, String name, int localLicenseId) throws LicenseException {
String require = feature == LicenseFeature.PROFESSIONAL_EDITION ? "PE" : "EE";
String message = "Requested feature (" + name + ") requires Saxon-" + require;
if (!Version.softwareEdition.equals("HE")) {
String packageNs = Version.platform.isDotNet() ? "Saxon.Eej.config" : "com.saxonica.config";
message += ". You are using Saxon-" + Version.softwareEdition + " software, but the Configuration is an instance of " +
getClass() + "; to use this feature you need to create an instance of " +
(feature == LicenseFeature.PROFESSIONAL_EDITION ?
packageNs + ".ProfessionalConfiguration" :
packageNs + ".EnterpriseConfiguration");
}
throw new LicenseException(message, LicenseException.WRONG_CONFIGURATION);
}
/**
* Instruct Saxon to run as if no license is available. This method is provided for testing purposes,
* so that tests with and without a license can be run without making changes to the classpath.
*/
public void disableLicensing() {
}
/**
* Ask whether a particular feature is enabled by an embedded license (embedded in a SEF file)
* @param localLicenseId the identifier of the embedded license
* @param feature the feature in question, identified by a constant in class {@link net.sf.saxon.Configuration.LicenseFeature}
*
* @return true if the embedded license exists and enables the requested feature
*/
public boolean isFeatureAllowedBySecondaryLicense(int localLicenseId, int feature) {
return false;
}
/**
* Determine if a particular feature is licensed.
*
* @param feature the feature in question, identified by a constant in class {@link net.sf.saxon.Configuration.LicenseFeature}
* @return true if the feature is licensed, false if it is not.
*/
public boolean isLicensedFeature(int feature) {
// changing this to true will do no good; it will cause Saxon to attempt to use the unavailable feature, rather than
// recovering from its absence.
return false;
}
/**
* Assert that a PE license is required, and fail if none is available
*
* @param featureName name of the required feature, to include in the error message
* @throws LicenseException if no license key is available
*/
public void requireProfessionalLicense(String featureName) throws LicenseException {
if (!isLicensedFeature(LicenseFeature.PROFESSIONAL_EDITION)) {
throw new LicenseException("Use of " + featureName + " requires a license key for Saxon-PE or Saxon-EE", LicenseException.NOT_FOUND);
}
}
/**
* Get the value of a named license feature
*
* @param name the name of the feature
* @return the value of the feature if present, or null otherwise
*/
public String getLicenseFeature(String name) {
return null;
}
/**
* Display a message about the license status
*/
public void displayLicenseMessage() {
}
/**
* Register a local license file (for use within a single transformation (etc))
*
* @param dmk the license in encoded form
* @return an integer identifying this license uniquely within the configuration, or -1 if not accepted
*/
public int registerLocalLicense(String dmk) {
return -1;
}
/**
* Set the DynamicLoader to be used. By default an instance of {@link DynamicLoader} is used
* for all dynamic loading of Java classes. This method allows the actions of the standard
* DynamicLoader to be overridden
*
* @param dynamicLoader the DynamicLoader to be used by this Configuration
*/
public void setDynamicLoader(IDynamicLoader dynamicLoader) {
this.dynamicLoader = dynamicLoader;
}
/**
* Get the DynamicLoader used by this Configuration. By default the standard system-supplied
* dynamic loader is returned.
*
* @return the DynamicLoader in use - either a user-supplied DynamicLoader, or the standard one
* supplied by the system.
*/
public IDynamicLoader getDynamicLoader() {
return dynamicLoader;
}
/**
* Load a class using the class name provided.
* Note that the method does not check that the object is of the right class.
* This method is intended for internal use only. The call is delegated to the
* DynamicLoader
, which may be overridden by a user-defined DynamicLoader
.
*
* @param className A string containing the name of the
* class, for example "com.microstar.sax.LarkDriver"
* @param tracing true if diagnostic tracing is required
* @return an instance of the class named, or null if it is not
* loadable.
* @throws XPathException if the class cannot be loaded.
*/
public Class> getClass(String className, boolean tracing) throws XPathException {
return dynamicLoader.getClass(className, tracing ? traceOutput : null, null);
}
/**
* Instantiate a class using the class name provided.
* Note that the method does not check that the object is of the right class.
* This method is intended for internal use only. The call is delegated to the
* DynamicLoader
, which may be overridden by a user-defined DynamicLoader
.
* Diagnostic output is produced if the option "isTiming" is set (corresponding to the -t option on
* the command line).
*
* @param className A string containing the name of the
* class, for example "com.microstar.sax.LarkDriver"
* @return an instance of the class named, or null if it is not
* loadable.
* @throws XPathException if the class cannot be loaded.
*/
public Object getInstance(String className) throws XPathException {
return dynamicLoader.getInstance(className, isTiming() ? traceOutput : null, null);
}
// /**
// * Set a Predicate that is applied to a URI to determine whether the standard resource resolvers
// * ({@link URIResolver}, {@link UnparsedTextURIResolver}, {@link SchemaURIResolver},
// * {@link CollationURIResolver}, {@link ModuleURIResolver}) should accept it.
// *
// * It is possible to set a predicate by means of the configuration property
// * {@link Feature#ALLOWED_PROTOCOLS}. This method, however, allows an arbitrary predicate to
// * be supplied.
// *
// * The predicate is only applicable to resolvers that choose to use it. This includes
// * all the standard Saxon-supplied resolvers, but user-supplied resolvers can bypass this
// * check.
// *
// * @param test a condition that a URI must satisfy if access to a resource with this URI
// * is to be permitted
// */
//
// public void setAllowedUriTest(Predicate test) {
// this.allowedUriTest = test;
// }
// /**
// * Get the Predicate that is applied to a URI to determine whether the standard resource resolvers
// * ({@link URIResolver}, {@link UnparsedTextURIResolver}, {@link SchemaURIResolver},
// * {@link CollationURIResolver}, {@link ModuleURIResolver}) should accept it.
// *
// * It is possible to set a predicate by means of the configuration property
// * {@link Feature#ALLOWED_PROTOCOLS}.
// *
// * The predicate is only applicable to resolvers that choose to use it. This includes
// * all the standard Saxon-supplied resolvers, but user-supplied resolvers can bypass this
// * check.
// *
// * @return a condition that a URI must satisfy if access to a resource with this URI
// * is to be permitted
// */
//
// public Predicate getAllowedUriTest() {
// return this.allowedUriTest;
// }
/**
* Returns the current resource resolver. If the configuration does
* not have a resolver when this method is called, a default CatalogResourceResolver
* will be constructed and returned.
* @return The resolver.
*/
public ResourceResolver getResourceResolver() {
if (commonResolver == null) {
setResourceResolver(new CatalogResourceResolver());
}
return commonResolver;
}
/**
* Set the ResourceResolver to be used in this configuration. This is used as a fallback
* whenever the local ResourceResolver for a particular task (for example the XsltCompiler
* or the XQueryEvaluator) either (a) has not been set, or (b) has been called and returns null.
*
* If a user-written ResourceResolver
is supplied then it must be able
* to handle a wide range of requests, not just for XML source documents and stylesheets,
* but also for schemas, unparsed text resources, and external entities. One way
* to achieve this by inheriting from the {@link CatalogResourceResolver},
* only overriding those methods that need to change. Another method is to
* construct a {@link ChainedResourceResolver} that first handles specific
* cases, and then passes control to the standard ResourceResolver
.
*
* In cases where the request is for a non-XML resource, the object delivered by the
* ResourceResolver
must be a StreamSource
.
*
* The ResourceResolver
may return null to indicate that the URI is to
* be resolved by the fallback DirectResourceResolver
.
*
* @param resolver The ResourceResolver to be used.
* @since 11.1. Replaces setURIResolver()
*/
public void setResourceResolver(ResourceResolver resolver) {
commonResolver = resolver;
}
/**
* Set the URIResolver to a URI resolver that allows query parameters after the URI
* @deprecated since 11.1 - use setBooleanProperty(Feature.RECOGNIZE_URI_QUERY_PARAMETERS, true);
*/
@Deprecated
public void setParameterizedURIResolver() {
setBooleanProperty(Feature.RECOGNIZE_URI_QUERY_PARAMETERS, true);
}
/**
* Get the ProtocolRestrictor in use. This restricts the URI schemes that may be used
* when dereferencing a resource. The ProtocolRestrictor can be set by setting the
* property {@link Feature#ALLOWED_PROTOCOLS} to a comma-separated list of permitted
* protocols.
* @return the ProtocolRestrictor in use
*/
public ProtocolRestrictor getProtocolRestrictor() {
return protocolRestrictor;
}
/**
* Create an instance of a URIResolver with a specified class name.
* Note that this method does not register the URIResolver with this Configuration.
*
* @param className The fully-qualified name of the URIResolver class
* @return The newly created URIResolver
* @throws XPathException if the requested class does not
* implement the {@code ResourceResolver} interface
*/
public ResourceResolver makeResourceResolver(String className) throws XPathException {
Object obj = dynamicLoader.getInstance(className, null);
if (obj instanceof URIResolver) {
getLogger().warning("From Saxon 11.1, the value of the -r option should be a ResourceResolver, not a URIResolver");
obj = new ResourceResolverWrappingURIResolver((URIResolver)obj);
}
if (obj instanceof ResourceResolver) {
return (ResourceResolver) obj;
}
throw new XPathException("Class " + className + " is not a ResourceResolver");
}
public void setErrorReporterFactory(java.util.function.Function factory) {
errorReporterFactory = factory;
}
public ErrorReporter makeErrorReporter() {
if (errorReporterFactory == null) {
errorReporterFactory = config -> {
StandardErrorReporter reporter = new StandardErrorReporter();
reporter.setLogger(config.getLogger());
return reporter;
};
}
return errorReporterFactory.apply(this);
}
public Logger getLogger() {
return traceOutput;
}
/**
* Report a fatal error
*
* @param err the exception to be reported
*/
public void reportFatalError(XPathException err) {
if (!err.hasBeenReported()) {
makeErrorReporter().report(new XmlProcessingException(err));
err.setHasBeenReported(true);
}
}
/**
* Set the standard error output to be used in all cases where no more specific destination
* is defined. This defaults to System.err.
*
* The method has no effect unless the {@link Configuration#getLogger()} object
* is an instance of {@link StandardLogger}. In that case it calls
* {@link StandardLogger#setPrintStream(PrintStream)}
*
* @param out the stream to be used for error output where no more specific destination
* has been supplied. The caller is responsible for closing this stream after use
* (if necessary).
* @since 9.3
*/
public void setStandardErrorOutput(PrintStream out) {
if (traceOutput instanceof StandardLogger) {
((StandardLogger) traceOutput).setPrintStream(out);
}
}
/**
* Register a new logger to be used in the Saxon event logging mechanism
*
* @param logger the Logger to be used as default. The caller is responsible for
* ensuring that this is closed after use (if necessary), which can
* be achieved by calling either {@link Logger#close} or
* {@link Configuration#close};
* @since 9.6
*/
public void setLogger(Logger logger) {
traceOutput = logger;
}
/**
* Set the XML version to be used by default for validating characters and names.
* Note that source documents specifying xml version="1.0" or "1.1" are accepted
* regardless of this setting. The effect of this switch is to change the validation
* rules for types such as Name and NCName, to change the meaning of \i and \c in
* regular expressions, and to determine whether the serializer allows XML 1.1 documents
* to be constructed.
*
* @param version one of the constants XML10 or XML11
* @since 8.6
*/
public void setXMLVersion(int version) {
xmlVersion = version;
theConversionRules = null;
}
/**
* Get the XML version to be used by default for validating characters and names
*
* @return one of the constants {@link #XML10} or {@link #XML11}
* @since 8.6
*/
public int getXMLVersion() {
return xmlVersion;
}
/**
* Get the parsing and document building options defined in this configuration
*
* @return the parsing and document building options. Note that any changes to this
* ParseOptions object will be reflected back in the Configuration; if changes are to be made
* locally, the caller should create a copy.
* @since 9.2. From 12.x the ParseOptions object is immutable, which means any changes must be
* written back using {@link #setParseOptions}
*/
public ParseOptions getParseOptions() {
return defaultParseOptions;
}
/**
* Set the parsing and document building options to be used in this configuration.
*/
public void setParseOptions(ParseOptions options) {
defaultParseOptions = options;
}
/**
* Set a comparator which can be used to assess whether the media pseudo-attribute
* in an xml-stylesheet processing instruction matches the media requested in the API
* for the transformation
*
* @param comparator a comparator which returns 0 (equals) when the first argument of the compare
* method is the value of the media attribute in the xml-stylesheet processing
* instruction, and the second argument is the value of the required media given
* in the calling API. The default implementation always returns 0, indicating that
* the media pseudo-attribute is ignored. An alternative implementation, consistent
* with previous Saxon releases, would be compare the strings for equality. A fully
* conformant implementation could implement the syntax and semantics of media queries
* as defined in CSS 3.
*/
public void setMediaQueryEvaluator(Comparator comparator) {
this.mediaQueryEvaluator = comparator;
}
/**
* Get a comparator which can be used to assess whether the media pseudo-attribute
* in an xml-stylesheet processing instruction matches the media requested in the API
* for the transformation
*
* @return a comparator which returns 0 (equals) when the first argument of the compare
* method is the value of the media attribute in the xml-stylesheet processing
* instruction, and the second argument is the value of the required media given
* in the calling API. The default implementation always returns 0, indicating that
* the media pseudo-attribute is ignored. An alternative implementation, consistent
* with previous Saxon releases, would be to compare the strings for equality. A fully
* conformant implementation could implement the syntax and semantics of media queries
* as defined in CSS 3.
*/
public Comparator getMediaQueryEvaluator() {
return mediaQueryEvaluator;
}
/**
* Set the conversion rules to be used to convert between atomic types. By default,
* The rules depend on the versions of XML and XSD in use by the configuration.
*
* @param rules the conversion rules to be used
* @since 9.3
*/
public void setConversionRules(/*@NotNull*/ ConversionRules rules) {
this.theConversionRules = rules;
}
/**
* Get the conversion rules used to convert between atomic types. By default, the rules depend on the versions
* of XML and XSD in use by the configuration
*
* @return the appropriate conversion rules
* @since 9.3
*/
/*@NotNull*/
public ConversionRules getConversionRules() {
if (theConversionRules == null) {
synchronized(this) {
ConversionRules cv = new ConversionRules();
cv.setTypeHierarchy(getTypeHierarchy());
cv.setNotationSet(this);
if (xsdVersion == XSD10) {
cv.setStringToDoubleConverter(StringToDouble.getInstance());
cv.setURIChecker(StandardURIChecker.getInstance());
// In XSD 1.1, there is no checking
} else {
cv.setStringToDoubleConverter(StringToDouble11.getInstance());
}
cv.setAllowYearZero(xsdVersion != XSD10);
return theConversionRules = cv;
}
} else {
return theConversionRules;
}
}
/**
* Get the version of XML Schema to be used
*
* @return {@link #XSD10} or {@link #XSD11}
* @since 9.2
*/
public int getXsdVersion() {
return xsdVersion;
}
/**
* Get an XPathContext object with sufficient capability to perform comparisons and conversions
*
* @return a dynamic context for performing conversions
*/
/*@NotNull*/
public XPathContext getConversionContext() {
if (theConversionContext == null) {
theConversionContext = new EarlyEvaluationContext(this);
}
return theConversionContext;
}
/**
* Get an IntPredicate that tests whether a given character is valid in the selected
* version of XML
*
* @return an IntPredicate whose matches() method tests a character for validity against
* the version of XML (1.0 or 1.1) selected by this configuration
*/
public IntPredicateProxy getValidCharacterChecker() {
if (xmlVersion == XML10) {
return IntPredicateLambda.of(CSharp.staticRef(XMLCharacterData::isValid10));
} else {
return IntPredicateLambda.of(CSharp.staticRef(XMLCharacterData::isValid11));
}
}
/**
* Get the Tree Model used by this Configuration. This is typically
* {@link Builder#LINKED_TREE}, {@link Builder#TINY_TREE}, or {@link Builder#TINY_TREE_CONDENSED}.
* The default is Builder.TINY_TREE
.
*
* @return the selected Tree Model
* @since 8.4 (Condensed tinytree added in 9.2)
*/
public int getTreeModel() {
return defaultParseOptions.getModel().getSymbolicValue();
}
/**
* Set the Tree Model used by this Configuration. This is typically
* {@link Builder#LINKED_TREE} or {@link Builder#TINY_TREE}, or {@link Builder#TINY_TREE_CONDENSED}.
* The default is Builder.TINY_TREE
.
*
* @param treeModel the integer constant representing the selected Tree Model
* @since 8.4 (Condensed tinytree added in 9.2)
*/
public void setTreeModel(int treeModel) {
defaultParseOptions = defaultParseOptions.withModel(TreeModel.getTreeModel(treeModel));
}
/**
* Determine whether source documents will maintain line numbers, for the
* benefit of the saxon:line-number() extension function as well as run-time
* tracing.
*
* @return true if line numbers are maintained in source documents
* @since 8.4
*/
public boolean isLineNumbering() {
return defaultParseOptions.isLineNumbering();
}
/**
* Determine whether source documents will maintain line numbers, for the
* benefit of the saxon:line-number() extension function as well as run-time
* tracing.
*
* @param lineNumbering true if line numbers are maintained in source documents
* @since 8.4
*/
public void setLineNumbering(boolean lineNumbering) {
defaultParseOptions = defaultParseOptions.withLineNumbering(lineNumbering);
}
/**
* Set whether or not source documents (including stylesheets and schemas) are have
* XInclude processing applied to them, or not. Default is false.
*
* @param state true if XInclude elements are to be expanded, false if not
* @since 8.9
*/
public void setXIncludeAware(boolean state) {
defaultParseOptions = defaultParseOptions.withXIncludeAware(state);
}
/**
* Test whether or not source documents (including stylesheets and schemas) are to have
* XInclude processing applied to them, or not
*
* @return true if XInclude elements are to be expanded, false if not
* @since 8.9
*/
public boolean isXIncludeAware() {
return defaultParseOptions.isXIncludeAware();
}
/**
* Get the TraceListener used for run-time tracing of instruction execution.
*
* @return the TraceListener that was set using {@link #setTraceListener} if set.
* Otherwise, returns null.
* @since 8.4. Modified in 9.1.
*/
/*@Nullable*/
public TraceListener getTraceListener() {
return traceListener;
}
/**
* Get or create the TraceListener used for run-time tracing of instruction execution.
*
* @return If a TraceListener has been set using {@link #setTraceListener(net.sf.saxon.lib.TraceListener)},
* returns that TraceListener. Otherwise, if a TraceListener class has been set using
* {@link #setTraceListenerClass(String)}, returns a newly created instance of that class.
* Otherwise, returns null.
* @throws XPathException if the supplied TraceListenerClass cannot be instantiated as an instance
* of TraceListener
* @since 9.1.
*/
/*@Nullable*/
public TraceListener makeTraceListener() throws XPathException {
if (traceListener != null) {
return traceListener;
} else if (traceListenerClass != null) {
try {
return makeTraceListener(traceListenerClass);
} catch (ClassCastException e) {
throw new XPathException(e);
}
} else {
return null;
}
}
/**
* Set the TraceListener to be used for run-time tracing of instruction execution.
* Note: this method should not be used if the Configuration is multithreading. In that situation,
* use {@link #setCompileWithTracing(boolean)} to force stylesheets and queries to be compiled
* with trace code enabled, and use {@link Controller#addTraceListener(net.sf.saxon.lib.TraceListener)} to
* supply a TraceListener at run time.
*
* @param traceListener The TraceListener to be used. If null is supplied, any existing TraceListener is removed
* @since 8.4
*/
public void setTraceListener(/*@Nullable*/ TraceListener traceListener) {
this.traceListener = traceListener;
setCompileWithTracing(traceListener != null);
internalSetBooleanProperty(FeatureCode.ALLOW_MULTITHREADING, FeatureKeys.ALLOW_MULTITHREADING, false);
}
/**
* Set the name of the trace listener class to be used for run-time tracing of instruction
* execution. A new instance of this class will be created for each query or transformation
* that requires tracing. The class must be an instance of {@link TraceListener}.
*
* @param className the name of the trace listener class. If null, any existing trace listener is
* removed from the configuration.
* @throws IllegalArgumentException if the class cannot be instantiated or does not implement
* TraceListener
* @since 9.1. Changed in 9.4 to allow null to be supplied.
*/
public void setTraceListenerClass(/*@Nullable*/ String className) {
if (className == null) {
traceListenerClass = null;
setCompileWithTracing(false);
} else {
try {
makeTraceListener(className);
} catch (XPathException err) {
throw new IllegalArgumentException(className + ": " + err.getMessage());
}
this.traceListenerClass = className;
setCompileWithTracing(true);
}
}
/**
* Get the name of the trace listener class to be used for run-time tracing of instruction
* execution. A new instance of this class will be created for each query or transformation
* that requires tracing. The class must be an instance of {@link net.sf.saxon.lib.TraceListener}.
*
* @return the name of the trace listener class, or null if no trace listener class
* has been nominated.
* @since 9.1
*/
/*@Nullable*/
@SuppressWarnings({"UnusedDeclaration"})
public String getTraceListenerClass() {
return traceListenerClass;
}
/**
* Set the name of the file to be used for TraceListener output. (By default,
* it is sent to System.err)
*
* @param filename the filename for TraceListener output
* @since 9.8
*/
public void setTraceListenerOutputFile(String filename) {
traceListenerOutput = filename;
}
/**
* Get the name of the file to be used for TraceListener output. Returns
* null if no value has been set
*
* @return the filename to be used for TraceListener output, or null
* if none has been set
* @since 9.8
*/
public String getTraceListenerOutputFile() {
return traceListenerOutput;
}
/**
* Determine whether compile-time generation of trace code was requested
*
* @return true if compile-time generation of code was requested at the Configuration level.
* Note that tracing can also be requested at the XQueryCompiler or XsltCompiler level.
* @since 8.8
*/
public boolean isCompileWithTracing() {
return getBooleanProperty(Feature.COMPILE_WITH_TRACING);
}
/**
* Request compile-time generation of trace code (or not)
*
* @param trace true if compile-time generation of trace code is required
* @since 8.8
*/
public void setCompileWithTracing(boolean trace) {
internalSetBooleanProperty(FeatureCode.COMPILE_WITH_TRACING, FeatureKeys.COMPILE_WITH_TRACING, trace);
if (defaultXsltCompilerInfo != null) {
if (trace) {
defaultXsltCompilerInfo.setCodeInjector(new XSLTTraceCodeInjector());
} else {
defaultXsltCompilerInfo.setCodeInjector(null);
}
}
getDefaultStaticQueryContext().setCodeInjector(trace ? new XQueryTraceCodeInjector() : null);
}
/**
* Create an instance of a TraceListener with a specified class name
*
* @param className The fully qualified class name of the TraceListener to
* be constructed
* @return the newly constructed TraceListener
* @throws net.sf.saxon.trans.XPathException if the requested class does not
* implement the net.sf.saxon.trace.TraceListener interface
*/
public TraceListener makeTraceListener(String className)
throws XPathException {
Object obj = dynamicLoader.getInstance(className, null);
if (obj instanceof TraceListener) {
String destination = getTraceListenerOutputFile();
if (destination != null) {
try {
((TraceListener) obj).setOutputDestination(new StandardLogger(new PrintStream(destination)));
} catch (FileNotFoundException e) {
throw new XPathException(e);
}
}
return (TraceListener) obj;
}
throw new XPathException("Class " + className + " is not a TraceListener");
}
/**
* Get the set of functions in the "fn" namespace defined in the XSLT and F&O specifications
* @param version The XSLT version (30 or 40)
* @return the function library
*/
public BuiltInFunctionSet getXSLTFunctionSet(int version) {
if (version == 30) {
return XSLT30FunctionSet.getInstance();
} else if (version == 40) {
throw new IllegalArgumentException("XSLT 4.0 requires Saxon-PE or higher");
} else {
throw new IllegalArgumentException("Unsupported XSLT version " + version + " (must be 30)");
}
}
/**
* Get the set of functions in the "fn" namespace defined in the F&O specification
* @param version The XPath version (eg 20, 30, 31, 40)
* @return the function library
*/
public BuiltInFunctionSet getXPathFunctionSet(int version) {
switch (version) {
case 20:
return XPath20FunctionSet.getInstance();
case 30:
case 305:
return XPath30FunctionSet.getInstance();
case 31:
return XPath31FunctionSet.getInstance();
case 40:
throw new IllegalArgumentException("Version 4.0 requires Saxon-PE or higher");
default:
return XPath31FunctionSet.getInstance();
}
}
public BuiltInFunctionSet getXQueryUpdateFunctionSet() {
return null;
}
/**
* Make a function in the "fn" namespace
*
* @param localName the local name of the function
* @param arity the arity of the function
* @return the function
*/
public SystemFunction makeSystemFunction(String localName, int arity) {
try {
return getXSLTFunctionSet(30).makeFunction(localName, arity);
} catch (XPathException e) {
return null;
}
}
/**
* Register an extension function that is to be made available within any stylesheet, query,
* or XPath expression compiled under the control of this processor. This method
* registers an extension function implemented as an instance of
* {@link net.sf.saxon.lib.ExtensionFunctionDefinition}, using an arbitrary name and namespace.
* This supplements the ability to call arbitrary Java methods using a namespace and local name
* that are related to the Java class and method name.
*
* @param function the object that implements the extension function.
* @since 9.2
*/
public void registerExtensionFunction(ExtensionFunctionDefinition function) {
integratedFunctionLibrary.registerFunction(function);
}
/**
* Get the IntegratedFunction library containing integrated extension functions
*
* @return the IntegratedFunctionLibrary
* @since 9.2
*/
/*@NotNull*/
public IntegratedFunctionLibrary getIntegratedFunctionLibrary() {
return integratedFunctionLibrary;
}
public synchronized FunctionLibraryList getBuiltInExtensionLibraryList(int version) {
FunctionLibraryList result = builtInExtensionLibraryList.get(version);
if (result == null) {
result = makeBuiltInExtensionLibraryList(version);
builtInExtensionLibraryList.put(version, result);
}
return result;
}
protected FunctionLibraryList makeBuiltInExtensionLibraryList(int version) {
FunctionLibraryList result = new FunctionLibraryList();
result.addFunctionLibrary(VendorFunctionSetHE.getInstance());
result.addFunctionLibrary(MathFunctionSet.getInstance());
result.addFunctionLibrary(MapFunctionSet.getInstance(version));
result.addFunctionLibrary(ArrayFunctionSet.getInstance(version));
result.addFunctionLibrary(ExsltCommonFunctionSet.getInstance());
return result;
}
/**
* Get the function library for Saxon-defined extension functions available in this edition
*
* @return the function library
*/
public BuiltInFunctionSet getVendorFunctionSet() {
return VendorFunctionSetHE.getInstance();
}
/**
* Instantiate a Saxon extension function. This generally requires Saxon-PE or higher,
* so it will fail with this Configuration
* @param localName the local name of the function in the Saxon namespace
* @param arity the function arity
* @return the system function with this name and arity
* @throws XPathException if no suitable function is available
*/
public SystemFunction bindSaxonExtensionFunction(String localName, int arity) throws XPathException {
throw new UnsupportedOperationException("The extension function saxon:" + localName + "#" + arity + " requires Saxon-PE or higher");
}
/**
* Add the registered extension binders to a function library.
* This method is intended primarily for internal use
*
* @param list the function library list
*/
public void addExtensionBinders(FunctionLibraryList list) {
// no action in this class
}
/**
* Get a system function. This can be any function defined in XPath functions and operators,
* including functions in the math, map, and array namespaces. Experimental 4.0 functions
* are available if the configuration property {@link Feature#XQUERY_VERSION}
* is set to 4.0.
*
* Constructor functions for known atomic types are also available, as are integrated
* extension functions registered with the configuration.
*
* Saxon extension functions and reflexive Java extensions are available provided
* that a licensed Processor is used.
*
* @param name the name of the required function
* @param arity the arity of the required function
* @return the requested function, or null if there is no such function. Note that some functions
* (those with particular context dependencies) may be unsuitable for dynamic calling.
*/
public FunctionItem getSystemFunction(StructuredQName name, int arity) {
try {
if (staticContextForSystemFunctions == null) {
staticContextForSystemFunctions = new IndependentContext(this);
}
FunctionLibrary lib;
NamespaceUri ns = name.getNamespaceUri();
int version = getDefaultStaticQueryContext().getLanguageVersion();
if (ns.equals(NamespaceUri.FN)) {
lib = getXPathFunctionSet(version);
} else if (ns.equals(NamespaceUri.SCHEMA)) {
lib = new ConstructorFunctionLibrary(this);
} else if (ns.equals(NamespaceUri.MATH)) {
lib = MathFunctionSet.getInstance();
} else if (ns.equals(NamespaceUri.MAP_FUNCTIONS)) {
lib = MapFunctionSet.getInstance(version);
} else if (ns.equals(NamespaceUri.ARRAY_FUNCTIONS)) {
lib = ArrayFunctionSet.getInstance(version);
} else {
FunctionLibraryList fll = new FunctionLibraryList();
fll.addFunctionLibrary(getBuiltInExtensionLibraryList(31));
fll.addFunctionLibrary(new ConstructorFunctionLibrary(this));
fll.addFunctionLibrary(getIntegratedFunctionLibrary());
lib = fll;
}
SymbolicName.F symbolicName = new SymbolicName.F(name, arity);
return lib.getFunctionItem(symbolicName, staticContextForSystemFunctions);
} catch (XPathException e) {
return null;
}
}
/**
* Make a UserFunction object.
* This method is for internal use.
*
* @param memoFunction true if the function is to be a memo function.
* @param streamability the declared streamability of the function
* @return the newly created user-defined function
*/
public UserFunction newUserFunction(boolean memoFunction, FunctionStreamability streamability) {
if (memoFunction) {
return new MemoFunction();
} else {
return new UserFunction();
}
}
/**
* Register a collation. Collation URIs registered using this interface will be
* picked up before calling the CollationURIResolver.
*
* @param collationURI the URI of the collation. This should be an absolute URI,
* though it is not checked
* @param collator the implementation of the collation
* @since 9.6
*/
public void registerCollation(String collationURI, StringCollator collator) {
collationMap.put(collationURI, collator);
}
/**
* Set a CollationURIResolver to be used to resolve collation URIs (that is,
* to take a URI identifying a collation, and return the corresponding collation).
* Note that Saxon attempts first to resolve a collation URI using the resolver
* registered with the Controller; if that returns null, it tries again using the
* resolver registered with the Configuration.
* Note that it is undefined whether collation URIs are resolved at compile time
* or at run-time. It is therefore inadvisable to change the CollationURIResolver after
* compiling a query or stylesheet and before running it.
*
* @param resolver the collation URI resolver to be used. This replaces any collation
* URI resolver previously registered.
* @since 8.5
*/
public void setCollationURIResolver(CollationURIResolver resolver) {
collationResolver = resolver;
}
/**
* Get the collation URI resolver associated with this configuration. This will
* return the CollationURIResolver previously set using the {@link #setCollationURIResolver}
* method; if this has not been called, it returns the system-defined collation URI resolver
*
* @return the registered CollationURIResolver
* @since 8.5
*/
public CollationURIResolver getCollationURIResolver() {
return collationResolver;
}
/**
* Get the collation with a given collation name. If the collation name has
* not been registered in this CollationMap, the CollationURIResolver registered
* with the Configuration is called. If this cannot resolve the collation name,
* it should return null.
*
* @param collationName the collation name as an absolute URI
* @return the StringCollator with this name if known, or null if not known
* @throws XPathException if the collation URI is recognized but is invalid; for example,
* if it is a URI that takes parameters, and the parameters are invalid. If a user-supplied
* collation URI resolver is in use, then any exception thrown by that resolver is passed
* on to the caller.
*/
public StringCollator getCollation(String collationName) throws XPathException {
if (collationName == null || collationName.equals(NamespaceConstant.CODEPOINT_COLLATION_URI)) {
return CodepointCollator.getInstance();
}
if (collationName.equals(NamespaceConstant.HTML5_CASE_BLIND_COLLATION_URI)) {
return HTML5CaseBlindCollator.getInstance();
}
if (collationName.startsWith(AlphanumericCollator.PREFIX)) {
return new AlphanumericCollator(getCollation(
collationName.substring(AlphanumericCollator.PREFIX.length())));
}
StringCollator collator = collationMap.get(collationName);
if (collator == null) {
collator = getCollationURIResolver().resolve(collationName, this);
}
return collator;
}
/**
* Get the collation with a given collation name, supplying a relative URI and base
* URI separately. If the collation name has
* not been registered in this CollationMap, the CollationURIResolver registered
* with the Configuration is called. If this cannot resolve the collation name,
* it should return null.
*
* @param collationURI the collation name as a relative or absolute URI
* @param baseURI the base URI to be used to resolve the collationURI if it is relative
* @return the StringCollator with this name if known, or null if not known
* @throws XPathException if a failure occurs during URI resolution
*/
public StringCollator getCollation(String collationURI, String baseURI) throws XPathException {
if (collationURI.equals(NamespaceConstant.CODEPOINT_COLLATION_URI)) {
return CodepointCollator.getInstance();
}
try {
String absoluteURI = ResolveURI.makeAbsolute(collationURI, baseURI).toString();
return getCollation(absoluteURI);
} catch (URISyntaxException e) {
throw new XPathException("Collation name is not a valid URI: " + collationURI +
" (base = " + baseURI + ")", "FOCH0002");
}
}
/**
* Get the collation with a given collation name, supplying a relative URI and base
* URI separately, and throwing an error if it cannot be found. If the collation name has
* not been registered in this CollationMap, the CollationURIResolver registered
* with the Configuration is called. If this cannot resolve the collation name,
* it should return null.
*
* @param collationURI the collation name as a relative or absolute URI
* @param baseURI the base URI to be used to resolve the collationURI if it is relative;
* may be null if the supplied collationURI is known to be absolute
* @param errorCode the error to raise if the collation is not known
* @return the StringCollator with this name if known, or null if not known
* @throws XPathException if a failure occurs during URI resolution
*/
public StringCollator getCollation(String collationURI, String baseURI, String errorCode) throws XPathException {
if (collationURI.equals(NamespaceConstant.CODEPOINT_COLLATION_URI)) {
return CodepointCollator.getInstance();
}
try {
String absoluteURI = collationURI;
if (baseURI != null) {
absoluteURI = ResolveURI.makeAbsolute(collationURI, baseURI).toString();
}
StringCollator collator = getCollation(absoluteURI);
if (collator == null) {
throw new XPathException("Unknown collation " + absoluteURI, errorCode);
}
return collator;
} catch (URISyntaxException e) {
throw new XPathException("Collation name is not a valid URI: " + collationURI +
" (base = " + baseURI + ")", errorCode);
}
}
/**
* Get the name of the global default collation
*
* @return the name of the default collation. If none has been set, returns
* the URI of the Unicode codepoint collation
*/
public String getDefaultCollationName() {
return defaultCollationName;
}
/**
* Set the default collection.
* If no default collection URI is specified, then a request for the default collection
* is handled by calling the registered collection URI resolver with an argument of null.
*
* @param uri the URI of the default collection. Calling the collection() function
* with no arguments is equivalent to calling collection() with this URI as an argument.
* The URI will be dereferenced by passing it to the registered CollectionFinder.
* If null is supplied, any existing default collection is removed.
* @since 9.2
*/
public void setDefaultCollection(/*@Nullable*/ String uri) {
defaultCollection = uri;
}
/**
* Get the URI of the default collection. Returns null if no default collection URI has
* been registered.
*
* @return the default collection URI. This is dereferenced in the same way as a normal
* collection URI (via the CollectionFinder) to return a sequence of nodes
* @since 9.2
*/
/*@Nullable*/
public String getDefaultCollection() {
return defaultCollection;
}
/**
* Set the collection finder associated with this configuration. This is used to dereference
* collection URIs used in the fn:collection and fn:uri-collection functions
*
* @param cf the CollectionFinder to be used
* @since 9.7
*/
public void setCollectionFinder(CollectionFinder cf) {
collectionFinder = cf;
}
/**
* Get the collection finder associated with this configuration. This is used to dereference
* collection URIs used in the fn:collection and fn:uri-collection functions
*
* @return the CollectionFinder to be used
* @since 9.7
*/
public CollectionFinder getCollectionFinder() {
return collectionFinder;
}
/**
* Register a specific URI and bind it to a specific ResourceCollection. A collection that is
* registered in this way will be returned prior to calling any registered {@link CollectionFinder}.
* This method should only be used while the configuration is being initialized for use;
* the effect of adding or replacing collections dynamically while a configuration is in use
* is undefined.
*
* @param collectionURI the collection URI to be registered. Must not be null.
* @param collection the ResourceCollection to be associated with this URI. Must not be null.
* @since 9.7.0.2. Modified in 11.0 to work independently of (and prior to invoking) the
* registered {@link CollectionFinder}.
*/
public void registerCollection(String collectionURI, ResourceCollection collection) {
registeredCollections.put(collectionURI, collection);
}
/**
* Get a registered collection with a given URI if there is one
* @param uri the collection URI
* @return the registered collection with this URI if there is one, or null otherwise
*/
public ResourceCollection getRegisteredCollection(String uri) {
return registeredCollections.get(uri);
}
/**
* Set the media type to be associated with a file extension by the standard
* collection handler
*
* @param extension the file extension, for example "xml". The value "" sets
* the default media type to be used for unregistered file extensions.
* @param mediaType the corresponding media type, for example "application/xml". The
* choice of media type determines how a resource with this extension
* gets parsed, when the file appears as part of a collection.
* @since 9.7.0.1
*/
public void registerFileExtension(String extension, String mediaType) {
fileExtensions.put(extension, mediaType);
}
/**
* Associate a media type with a resource factory. This method may
* be called to customize the behaviour of a collection to recognize different file extensions
*
* @param contentType a media type or MIME type, for example application/xsd+xml
* @param factory a ResourceFactory used to parse (or otherwise process) resources of that type
* @since 9.7.0.6
*/
public void registerMediaType(String contentType, ResourceFactory factory) {
resourceFactoryMapping.put(contentType, factory);
}
/**
* Get the media type to be associated with a file extension by the standard
* collection handler
*
* @param extension the file extension, for example "xml". The value "" gets
* the default media type to be used for unregistered file extensions.
* the default media type is also returned if the supplied file
* extension is not registered.
* @return the corresponding media type, for example "application/xml". The
* choice of media type determines how a resource with this extension
* gets parsed, when the file appears as part of a collection.
* @since 9.7.0.1
*/
public String getMediaTypeForFileExtension(String extension) {
String mediaType = fileExtensions.get(extension);
if (mediaType == null) {
mediaType = fileExtensions.get("");
}
return mediaType;
}
/**
* Get the resource factory associated with a media type
*
* @param mediaType the media type or MIME type, for example "application/xml"
* @return the associated resource factory if one has been registered for this media type,
* or null otherwise
*/
public ResourceFactory getResourceFactoryForMediaType(String mediaType) {
return resourceFactoryMapping.get(mediaType);
}
/**
* Set the localizer factory to be used
*
* @param factory the LocalizerFactory
* @since 9.2
*/
public void setLocalizerFactory(LocalizerFactory factory) {
this.localizerFactory = factory;
}
/**
* Get the localizer factory in use
*
* @return the LocalizerFactory, if any. If none has been set, returns null.
* @since 9.2
*/
public LocalizerFactory getLocalizerFactory() {
return localizerFactory;
}
/**
* Set the default language to be used for number and date formatting when no language is specified.
* If none is set explicitly, the default Locale for the Java Virtual Machine is used.
*
* @param language the default language to be used, as an ISO code for example "en" or "fr-CA"
* @throws IllegalArgumentException if not valid as an xs:language instance.
* @since 9.2. Validated from 9.7 against xs:language type.
*/
public void setDefaultLanguage(String language) {
ValidationFailure vf = StringConverter.StringToLanguage.INSTANCE.validate(StringView.of(language).tidy());
if (vf != null) {
throw new IllegalArgumentException("The default language must be a valid language code");
}
defaultLanguage = language;
}
/**
* Get the default language. Unless an explicit default is set, this will be the language
* of the default Locale for the Java Virtual Machine
*
* @return the default language
* @since 9.2
*/
public String getDefaultLanguage() {
return defaultLanguage;
}
/**
* Set the default country to be used for number and date formatting when no country is specified.
* If none is set explicitly, the default Locale for the Java Virtual Machine is used.
*
* @param country the default country to be used, as an ISO code for example "US" or "GB"
* @since 9.2
*/
public void setDefaultCountry(String country) {
defaultCountry = country;
}
/**
* Get the default country to be used for number and date formatting when no country is specified.
* If none is set explicitly, the default Locale for the Java Virtual Machine is used.
*
* @return the default country to be used, as an ISO code for example "US" or "GB"
* @since 9.2
*/
public String getDefaultCountry() {
return defaultCountry;
}
/**
* Set the default regular expression engine to be used
*
* @param engine the default regular expression engine to be used. The value must be one of:
*
* - S - the Saxon regular expression engine (derived form Apache Jakarta)
* - J - the Java JDK regular expression engine
* - N - (on .NET only) - the .NET regular expression engine
*
*/
public void setDefaultRegexEngine(String engine) {
if (!("J".equals(engine) || "N".equals(engine) || "S".equals(engine))) {
throw new IllegalArgumentException("Regex engine must be S|J|N");
}
defaultRegexEngine = engine;
}
/**
* Get the default regular expression to be used
*
* @return the default regular expression to be used. Returns "S" if no value has been
* explicitly set.
*/
public String getDefaultRegexEngine() {
return defaultRegexEngine;
}
/**
* Compile a regular expression (or, in some configurations, get a compiled
* regular expression from a cache
*
* @param regex the regular expression as a string
* @param flags the value of the flags attribute
* @param hostLanguage one of "XSD10", "XSD11", XP20" or "XP30". Also allow combinations, e.g. "XP20/XSD11".
* @param warnings if non-null, any warnings from the regular expression compiler will be added to this list.
* If null, the warnings are ignored.
* @return the compiled regular expression
* @throws XPathException if the regular expression or the flags are invalid
*/
public RegularExpression compileRegularExpression(UnicodeString regex, String flags, String hostLanguage, List warnings)
throws XPathException {
return Version.platform.compileRegularExpression(this, regex, flags, hostLanguage, warnings);
}
/**
* Load a Numberer class for a given language and check it is OK.
* This method is provided primarily for internal use.
*
* @param language the language for which a Numberer is required. May be null,
* indicating default language. The language may include variants
* such as "en-GB" for British English or "fr-CA" for
* Canadian French.
* @param country the country for which a Numberer is required. May be null,
* indicating default country. This is the country of a date/time
* to be formatted, and does not contribute to selection
* of a locale.
* @return a suitable numberer. If no specific numberer is available
* for the language, the default numberer (normally English) is used.
*/
public Numberer makeNumberer(/*@Nullable*/ String language, /*@Nullable*/ String country) {
if (localizerFactory == null) {
Numberer_en numberer = new Numberer_en();
if (language != null) {
numberer.setLanguage(language);
}
if (country != null) {
numberer.setCountry(country);
}
return numberer;
} else {
Numberer numberer = localizerFactory.getNumberer(language, country);
if (numberer == null) {
numberer = new Numberer_en();
}
return numberer;
}
}
/**
* Set a user-defined ModuleURIResolver for resolving URIs used in "import module"
* declarations in an XQuery prolog.
* This acts as the default value for the ModuleURIResolver in the StaticQueryContext, and may be
* overridden by a more specific ModuleURIResolver nominated as part of the StaticQueryContext.
*
* @param resolver the URI resolver for XQuery modules. May be null, in which case any existing
* Module URI Resolver is removed from the configuration
*/
public void setModuleURIResolver(/*@Nullable*/ ModuleURIResolver resolver) {
getDefaultStaticQueryContext().setModuleURIResolver(resolver);
}
/**
* Create and register an instance of a ModuleURIResolver with a specified class name.
* This will be used for resolving URIs in XQuery "import module" declarations, unless
* a more specific ModuleURIResolver has been nominated as part of the StaticQueryContext.
*
* @param className The fully-qualified name of the LocationHintResolver class
* @throws TransformerException if the requested class does not
* implement the net.sf.saxon.LocationHintResolver interface
*/
public void setModuleURIResolver(String className) throws TransformerException {
Object obj = dynamicLoader.getInstance(className, null);
if (obj instanceof ModuleURIResolver) {
if (obj instanceof StandardModuleURIResolver) {
((StandardModuleURIResolver) obj).setConfiguration(this);
}
setModuleURIResolver((ModuleURIResolver) obj);
} else {
throw new XPathException("Class " + className + " is not a ModuleURIResolver");
}
}
/**
* Get the user-defined ModuleURIResolver for resolving URIs used in "import module"
* declarations in the XQuery prolog; returns null if none has been explicitly set.
*
* @return the resolver for Module URIs
*/
/*@Nullable*/
public ModuleURIResolver getModuleURIResolver() {
return getDefaultStaticQueryContext().getModuleURIResolver();
}
/**
* Get the standard system-defined ModuleURIResolver for resolving URIs used in "import module"
* declarations in the XQuery prolog.
*
* @return the standard system-defined ModuleURIResolver for resolving URIs
*/
public ModuleURIResolver getStandardModuleURIResolver() {
return standardModuleURIResolver;
}
/**
* Get the URI resolver used for resolving URIs passed to the unparsed-text(),
* unparsed-text-available(), and unparsed-text-lines() functions, as well as json-doc().
*
* @return the URI resolver set for these functions, if one has been set, or the
* standard system-supplied resolver otherwise
*/
public UnparsedTextURIResolver getUnparsedTextURIResolver() {
return unparsedTextURIResolver;
}
/**
* Set the URI resolver to be used for resolving URIs passed to the unparsed-text(),
* unparsed-text-available(), and unparsed-text-lines() functions, as well as json-doc().
* @param resolver the URI resolver to be used for these functions.
*/
public void setUnparsedTextURIResolver(UnparsedTextURIResolver resolver) {
this.unparsedTextURIResolver = resolver;
}
/**
* Get the default options for XSLT compilation
*
* @return the default options for XSLT compilation. The CompilerInfo object will reflect any options
* set using other methods available for this Configuration object
*/
public CompilerInfo getDefaultXsltCompilerInfo() {
return defaultXsltCompilerInfo;
}
/**
* Get the default options for XQuery compilation
*
* @return the default XQuery static context for this configuration
*/
public StaticQueryContext getDefaultStaticQueryContext() {
if (defaultStaticQueryContext == null) {
defaultStaticQueryContext = makeStaticQueryContext(false);
}
return defaultStaticQueryContext;
}
/**
*
* Make a new static query context object using the factory registered with this configuration
* @param copyFromDefault true if the new static query context is to copy the properties
* of the default static query context held in the configuration
* @return the new static query context object
*/
protected StaticQueryContext makeStaticQueryContext(boolean copyFromDefault) {
return staticQueryContextFactory.newStaticQueryContext(this, copyFromDefault);
}
/**
* Register a FunctionAnnotationHandler to handle XQuery function annotations
* in a particular namespace
*
* @param handler a function annotation handler to be invoked in respect of function
* annotations in the relevant namespace
*/
public void registerFunctionAnnotationHandler(FunctionAnnotationHandler handler) {
functionAnnotationHandlers.put(handler.getAssertionNamespace(), handler);
}
/**
* Get the FunctionAnnotationHandler used to handle XQuery function annotations
* in a particular namespace
*
* @param namespace the namespace
* @return a function annotation handler to be invoked in respect of function
* annotations in the relevant namespace, or null if none has
* been registered
*/
public FunctionAnnotationHandler getFunctionAnnotationHandler(NamespaceUri namespace) {
return functionAnnotationHandlers.get(namespace);
}
/**
* Ask whether streamability checking is enabled for this configuration
* @return always false for Saxon-HE
*/
public boolean isStreamabilityEnabled() {
return false;
}
/**
* Get the name of the class that will be instantiated to create an XML parser
* for parsing source documents (for example, documents loaded using the document()
* or doc() functions).
* This method is retained in Saxon for backwards compatibility, but the preferred way
* of choosing an XML parser is to use JAXP interfaces, for example by supplying a
* JAXP SAXSource object initialized with an appropriate implementation of org.xml.sax.XMLReader.
*
* @return the fully qualified name of the XML parser class
*/
public String getSourceParserClass() {
return sourceParserClass;
}
/**
* Set the name of the class that will be instantiated to create an XML parser
* for parsing source documents (for example, documents loaded using the document()
* or doc() functions).
* This method is retained in Saxon for backwards compatibility, but the preferred way
* of choosing an XML parser is to use JAXP interfaces, for example by supplying a
* JAXP SAXSource object initialized with an appropriate implementation of org.xml.sax.XMLReader.
*
* @param sourceParserClass the fully qualified name of the XML parser class. This must implement
* the SAX2 XMLReader interface.
*/
public void setSourceParserClass(String sourceParserClass) {
this.sourceParserClass = sourceParserClass;
}
/**
* Get the name of the class that will be instantiated to create an XML parser
* for parsing stylesheet modules.
* This method is retained in Saxon for backwards compatibility, but the preferred way
* of choosing an XML parser is to use JAXP interfaces, for example by supplying a
* JAXP Source object initialized with an appropriate implementation of org.xml.sax.XMLReader.
*
* @return the fully qualified name of the XML parser class
*/
public String getStyleParserClass() {
return styleParserClass;
}
/**
* Set the name of the class that will be instantiated to create an XML parser
* for parsing stylesheet modules.
* This method is retained in Saxon for backwards compatibility, but the preferred way
* of choosing an XML parser is to use JAXP interfaces, for example by supplying a
* JAXP Source object initialized with an appropriate implementation of org.xml.sax.XMLReader.
*
* @param parser the fully qualified name of the XML parser class
*/
public void setStyleParserClass(String parser) {
this.styleParserClass = parser;
}
/**
* Get the OutputURIResolver that will be used to resolve URIs used in the
* href attribute of the xsl:result-document instruction.
*
* @return the OutputURIResolver. If none has been supplied explicitly, the
* default OutputURIResolver is returned.
* @since 8.4
*/
public OutputURIResolver getOutputURIResolver() {
return defaultXsltCompilerInfo.getOutputURIResolver();
}
/**
* Set the OutputURIResolver that will be used to resolve URIs used in the
* href attribute of the xsl:result-document instruction.
*
* @param outputURIResolver the OutputURIResolver to be used.
* @since 8.4
* @deprecated since 9.9. Use {@link Xslt30Transformer#setResultDocumentHandler(java.util.function.Function)}
* or {@link XsltController#setResultDocumentResolver(ResultDocumentResolver)} instead.
*/
@Deprecated
public void setOutputURIResolver(OutputURIResolver outputURIResolver) {
defaultXsltCompilerInfo.setOutputURIResolver(outputURIResolver);
}
/**
* Set a custom SerializerFactory. This will be used to create a serializer for a given
* set of output properties and result destination.
*
* @param factory a custom SerializerFactory
* @since 8.8
*/
public void setSerializerFactory(SerializerFactory factory) {
serializerFactory = factory;
}
/**
* Get the SerializerFactory. This returns the standard built-in SerializerFactory, unless
* a custom SerializerFactory has been registered.
*
* @return the SerializerFactory in use
* @since 8.8
*/
public SerializerFactory getSerializerFactory() {
return serializerFactory;
}
/**
* Get the CharacterSetFactory. Note: at present this cannot be changed.
*
* @return the CharacterSetFactory in use.
* @since 9.2
*/
public CharacterSetFactory getCharacterSetFactory() {
if (characterSetFactory == null) {
characterSetFactory = new CharacterSetFactory();
}
return characterSetFactory;
}
/**
* Set the default serialization properties.
* The method name is a misnomer, retained for backwards compatibility. A {@link SerializationProperties}
* object contains a set of {@link Properties} holding simple properties, together with an optional
* {@link CharacterMap} holding a character map. This method returns the simple properties only.
* In fact the default properties held in the Configuration
only contain simple properties,
* and never a character map.
*
* @param props the default properties
*/
public void setDefaultSerializationProperties(Properties props) {
defaultOutputProperties = props;
}
/**
* Get the default output properties. The returned value can be modified in-situ using
* methods such as {@link Properties#setProperty(String, String)} to change the defaults
* in force for the {@code Configuration}.
* The method name is a misnomer, retained for backwards compatibility. A {@link SerializationProperties}
* object contains a set of {@link Properties} holding simple properties, together with an optional
* {@link CharacterMap} holding a character map. This method returns the simple properties only.
* In fact the default properties held in the Configuration
only contain simple properties,
* and never a character map.
*
* @return the default output properties.
*/
public Properties getDefaultSerializationProperties() {
return defaultOutputProperties;
}
/**
* Obtain a default SerializationProperties
object.
*
* @return the default serialization properties. This is obtained by wrapping the default output
* properties maintained in the {@code Configuration} within a {@link SerializationProperties} wrapper.
* The returned value can be modified in-situ using
* methods such as {@link SerializationProperties#setProperty(String, String)} to change the defaults
* in force for the {@code Configuration}.
*/
public SerializationProperties obtainDefaultSerializationProperties() {
return new SerializationProperties(defaultOutputProperties);
}
/**
* Process an xsl:result-document instruction. The Saxon-HE version of this method simply executes the instruction.
* The Saxon-EE version starts a new thread, and executes the instruction in that thread.
*
* @param instruction the instruction to be executed
* @param content the expression that defines the content of the new result document
* @param context the evaluation context
* @throws XPathException if any dynamic error occurs
*/
public void processResultDocument(ResultDocument instruction, PushEvaluator content, XPathContext context) throws XPathException {
instruction.processInstruction(content, context);
}
/**
* Get an item mapping iterator suitable for multi-threaded execution, if this is permitted
*
* @param base iterator over the input sequence
* @param action mapping function to be applied to each item in the input sequence.
* @return an iterator over the result sequence
* @throws XPathException if (for example) a dynamic error occurs while priming the queue
*/
public SequenceIterator getMultithreadedItemMappingIterator(
SequenceIterator base, ItemMappingFunction action) throws XPathException {
return new ItemMappingIterator(base, action);
}
/**
* Determine whether brief progress messages and timing information will be output.
*
* This method is provided largely for internal use. Progress messages are normally
* controlled directly from the command line interfaces, and are not normally used when
* driving Saxon from the Java API.
*
* @return true if these messages are to be output.
*/
public boolean isTiming() {
return enabledProperties.contains(FeatureCode.TIMING);
}
/**
* Determine whether brief progress messages and timing information will be output.
* This method is provided largely for internal use. Progress messages are normally
* controlled directly from the command line interfaces, and are not normally used when
* running from a Java application.
* The name of the method is poorly chosen, since many of the progress messages that
* are output have little to do with timing or instrumentation.
* If enabled, the relevant messages will be sent either to the destination set using
* {@link #setLogger(Logger)} or to the destination set using {@link #setStandardErrorOutput(PrintStream)},
* depending on the message.
*
* @param timing true if these messages are to be output.
*/
public void setTiming(boolean timing) {
if (timing) {
enabledProperties.add(FeatureCode.TIMING);
} else {
enabledProperties.remove(FeatureCode.TIMING);
}
}
/**
* Determine whether a warning is to be output when running against a stylesheet labelled
* as version="1.0". The XSLT specification requires such a warning unless the user disables it.
*
* @return always false.
* @since 8.4
* @deprecated since 10.0; the method has had no effect since Saxon 9.8
*/
@Deprecated
public boolean isVersionWarning() {
return false;
}
/**
* Determine whether a warning is to be output when the version attribute of the stylesheet does
* not match the XSLT processor version. (In the case where the stylesheet version is "1.0",
* the XSLT specification requires such a warning unless the user disables it.)
*
* @param warn ignored.
* @since 8.4
* @deprecated since 10.0; the method has had no effect since Saxon 9.8
*/
@Deprecated
public void setVersionWarning(boolean warn) {
// no action
}
/**
* Determine whether the XML parser for source documents will be asked to perform
* validation of source documents
*
* @return true if DTD validation is requested.
* @since 8.4
*/
public boolean isValidation() {
return defaultParseOptions.getDTDValidationMode() == Validation.STRICT ||
defaultParseOptions.getDTDValidationMode() == Validation.LAX;
}
/**
* Determine whether the XML parser for source documents will be asked to perform
* DTD validation of source documents
*
* @param validation true if DTD validation is to be requested.
* @since 8.4
*/
public void setValidation(boolean validation) {
defaultParseOptions = defaultParseOptions.withDTDValidationMode(validation ? Validation.STRICT : Validation.STRIP);
}
/**
* Create a document projector for a given path map root. Document projection is available only
* in Saxon-EE, so the Saxon-HE version of this method throws an exception
*
* @param map a path map root in a path map. This might represent the call to the initial
* context item for a query, or it might represent a call on the doc() function. The path map
* contains information about the paths that the query uses within this document.
* @return a push filter that implements document projection
* @throws UnsupportedOperationException if this is not a schema-aware configuration, or
* if no Saxon-EE license is available
*/
public FilterFactory makeDocumentProjector(PathMap.PathMapRoot map) {
throw new UnsupportedOperationException("Document projection requires Saxon-EE");
}
/**
* Create a document projector for the document supplied as the initial context item
* in a query. Document projection is available only
* in Saxon-EE, so the Saxon-HE version of this method throws an exception
*
* @param exp an XQuery expression. The document projector that is returned will
* be for the document supplied as the context item to this query.
* @return a push filter that implements document projection
* @throws UnsupportedOperationException if this is not a schema-aware configuration, or
* if no Saxon-EE license is available
*/
public FilterFactory makeDocumentProjector(XQueryExpression exp) {
throw new UnsupportedOperationException("Document projection requires Saxon-EE");
}
/**
* Ask whether source documents (supplied as a StreamSource or SAXSource)
* should be subjected to schema validation, and if so, in what validation mode
*
* @return the schema validation mode previously set using setSchemaValidationMode(),
* or the default mode {@link Validation#STRIP} otherwise.
*/
public int getSchemaValidationMode() {
return defaultParseOptions.getSchemaValidationMode();
}
/**
* Say whether source documents (supplied as a StreamSource or SAXSource)
* should be subjected to schema validation, and if so, in what validation mode.
* This value may be overridden at the level of a Controller for an individual transformation or query.
*
* @param validationMode the validation (or construction) mode to be used for source documents.
* One of {@link Validation#STRIP}, {@link Validation#PRESERVE}, {@link Validation#STRICT},
* {@link Validation#LAX}
* @since 8.4
*/
public void setSchemaValidationMode(int validationMode) {
// switch (validationMode) {
// case Validation.STRIP:
// case Validation.PRESERVE:
// break;
// case Validation.LAX:
// if (!isLicensedFeature(LicenseFeature.SCHEMA_VALIDATION)) {
// // if schema processing isn't supported, then there's never a schema, so lax validation is a no-op.
// validationMode = Validation.STRIP;
// }
// break;
// case Validation.STRICT:
// checkLicensedFeature(LicenseFeature.SCHEMA_VALIDATION, "strict validation", -1);
// break;
// default:
// throw new IllegalArgumentException("Unsupported validation mode " + validationMode);
// }
defaultParseOptions = defaultParseOptions.withSchemaValidationMode(validationMode);
}
/**
* Indicate whether schema validation failures on result documents are to be treated
* as fatal errors or as warnings. Note that all validation
* errors are reported to the error() method of the ErrorListener, and processing always
* continues except when that method chooses to throw an exception. At the end of the document,
* a fatal error is thrown if (a) there have been any validation errors, and (b) this option
* is not set.
*
* @param warn true if schema validation failures are to be treated as warnings; false if they
* are to be treated as fatal errors.
* @since 8.4
*/
public void setValidationWarnings(boolean warn) {
defaultParseOptions = defaultParseOptions.withContinueAfterValidationErrors(warn);
}
/**
* Determine whether schema validation failures on result documents are to be treated
* as fatal errors or as warnings. Note that all validation
* errors are reported to the error() method of the ErrorListener, and processing always
* continues except when that method chooses to throw an exception. At the end of the document,
* a fatal error is thrown if (a) there have been any validation errors, and (b) this option
* is not set.
*
* @return true if validation errors are to be treated as warnings (that is, the
* validation failure is reported but processing continues as normal); false
* if validation errors are fatal.
* @since 8.4
*/
public boolean isValidationWarnings() {
return defaultParseOptions.isContinueAfterValidationErrors();
}
/**
* Indicate whether attributes that have a fixed or default value are to be expanded when
* generating a final result tree. By default (and for conformance with the W3C specifications)
* it is required that fixed and default values should be expanded. However, there are use cases
* for example when generating XHTML when this serves no useful purpose and merely bloats the output.
* This option can be overridden at the level of a PipelineConfiguration
*
* @param expand true if fixed and default values are to be expanded as required by the W3C
* specifications; false if this action is to be disabled. Note that this only affects the validation
* of final result trees; it is not possible to suppress expansion of fixed or default values on input
* documents, as this would make the type annotations on input nodes unsound.
* @since 9.0
*/
public void setExpandAttributeDefaults(boolean expand) {
defaultParseOptions = defaultParseOptions.withExpandAttributeDefaults(expand);
}
/**
* Determine whether elements and attributes that have a fixed or default value are to be expanded.
* This option applies both to DTD-defined attribute defaults and to schema-defined defaults for
* elements and attributes. If an XML parser is used that does not report whether defaults have
* been used, this option is ignored.
* This option can be overridden at the level of a PipelineConfiguration
*
* @return true if elements and attributes that have a fixed or default value are to be expanded,
* false if defaults are not to be expanded. The default value is true. Note that the setting "false"
* is potentially non-conformant with the W3C specifications.
* @since 9.0
*/
public boolean isExpandAttributeDefaults() {
return defaultParseOptions.isExpandAttributeDefaults();
}
/**
* Get the target namepool to be used for stylesheets/queries and for source documents.
*
* @return the target name pool. If no NamePool has been specified explicitly, the
* default NamePool is returned.
* @since 8.4
*/
public NamePool getNamePool() {
return namePool;
}
/**
* Set the NamePool to be used for stylesheets/queries and for source documents.
* Using this method allows several Configurations to share the same NamePool. This
* was the normal default arrangement until Saxon 8.9, which changed the default so
* that each Configuration uses its own NamePool.
* Sharing a NamePool creates a potential bottleneck, since changes to the namepool are
* synchronized.
*
* @param targetNamePool The NamePool to be used.
* @since 8.4
*/
public void setNamePool(NamePool targetNamePool) {
namePool = targetNamePool;
}
/**
* Get the TypeHierarchy: a cache holding type information
*
* @return the type hierarchy cache
*/
public TypeHierarchy getTypeHierarchy() {
if (typeHierarchy == null) {
typeHierarchy = new TypeHierarchy(this);
}
return typeHierarchy;
}
/**
* Get an appropriate type-checker. The type-checker that is
* returned depends on whether backwards compatible (XPath 1.0) mode
* is enabled (which requires Saxon-PE or higher)
*
* @param backwardsCompatible set to true if XPath 1.0 compatibility mode is required
* @return a suitable TypeChecker
*/
public TypeChecker getTypeChecker(boolean backwardsCompatible) {
if (backwardsCompatible) {
return typeChecker10;
} else {
return typeChecker;
}
}
/**
* Make a TypeAliasManager appropriate to the type of Configuration
*
* @return a new TypeAliasManager
*/
public TypeAliasManager makeTypeAliasManager() {
return new TypeAliasManager();
}
/**
* Get the document number allocator.
* The document number allocator is used to allocate a unique number to each document built under this
* configuration. The document number forms the basis of all tests for node identity; it is therefore essential
* that when two documents are accessed in the same XPath expression, they have distinct document numbers.
* Normally this is ensured by building them under the same Configuration. Using this method together with
* {@link #setDocumentNumberAllocator}, however, it is possible to have two different Configurations that share
* a single DocumentNumberAllocator
*
* @return the current DocumentNumberAllocator
* @since 9.0
*/
public DocumentNumberAllocator getDocumentNumberAllocator() {
return documentNumberAllocator;
}
/**
* Set the document number allocator.
* The document number allocator is used to allocate a unique number to each document built under this
* configuration. The document number forms the basis of all tests for node identity; it is therefore essential
* that when two documents are accessed in the same XPath expression, they have distinct document numbers.
* Normally this is ensured by building them under the same Configuration. Using this method together with
* {@link #getDocumentNumberAllocator}, however, it is possible to have two different Configurations that share
* a single DocumentNumberAllocator
* This method is for advanced applications only. Misuse of the method can cause problems with node identity.
* The method should not be used except while initializing a Configuration, and it should be used only to
* arrange for two different configurations to share the same DocumentNumberAllocators. In this case they
* should also share the same NamePool.
*
* @param allocator the DocumentNumberAllocator to be used
* @since 9.0
*/
public void setDocumentNumberAllocator(DocumentNumberAllocator allocator) {
documentNumberAllocator = allocator;
}
/**
* Determine whether two Configurations are compatible. When queries, transformations, and path expressions
* are run, all the Configurations used to build the documents and to compile the queries and stylesheets
* must be compatible. Two Configurations are compatible if they share the same NamePool and the same
* DocumentNumberAllocator.
*
* @param other the other Configuration to be compared with this one
* @return true if the two configurations are compatible
*/
public boolean isCompatible(Configuration other) {
return namePool == other.namePool && documentNumberAllocator == other.documentNumberAllocator;
}
/**
* Get the global document pool. This is used for documents preloaded during query or stylesheet
* compilation. The user application can preload documents into the global pool, where they will be found
* if any query or stylesheet requests the specified document using the doc() or document() function.
*
* @return the global document pool
* @since 9.1
*/
public DocumentPool getGlobalDocumentPool() {
return globalDocumentPool;
}
/**
* Determine whether whitespace-only text nodes are to be stripped unconditionally
* from source documents.
*
* @return true if the space stripping rules for the default parseOptions cause whitespace text
* nodes to be stripped from all elements.
* @since 8.4
*/
public boolean isStripsAllWhiteSpace() {
return defaultParseOptions.getSpaceStrippingRule() == AllElementsSpaceStrippingRule.getInstance();
}
// //#if CSHARP==false
// /**
// * Get an XML parser for source documents.
// * This method is intended primarily for internal use.
// *
// * @return a parser
// */
//
// public XMLReader createXMLParser() {
// XMLReader parser;
//
// if (getSourceParserClass() != null) {
// parser = makeParser(getSourceParserClass());
// } else {
// parser = loadParser();
// }
//
// EntityResolver resolver = new ChainedEntityResolver(getResourceResolver(), parser.getEntityResolver());
// parser.setEntityResolver(resolver);
//
// return parser;
// }
/**
* Get a parser for source documents. The parser is allocated from a pool if any are available
* from the pool: the client should ideally return the parser to the pool after use, so that it
* can be reused.
* This method is intended primarily for internal use.
*
* @return a parser, in which the namespace properties must be set as follows:
* namespaces=true; namespace-prefixes=false. The DTD validation feature of the parser will be set
* on or off depending on the {@link #setValidation(boolean)} setting.
* @throws javax.xml.transform.TransformerFactoryConfigurationError if a failure occurs
* configuring the parser for use.
*/
public XMLReader getSourceParser() throws TransformerFactoryConfigurationError {
if (sourceParserPool == null) {
sourceParserPool = new ConcurrentLinkedQueue<>();
}
XMLReader parser = sourceParserPool.poll();
if (parser != null) {
return parser;
}
if (getSourceParserClass() != null) {
parser = makeParser(getSourceParserClass());
} else {
parser = loadParser();
}
EntityResolver resolver = new EntityResolverWrappingResourceResolver(getResourceResolver());
if (parser.getEntityResolver() != null) {
resolver = new ChainedEntityResolver(resolver, parser.getEntityResolver());
}
parser.setEntityResolver(resolver);
if (isTiming()) {
reportParserDetails(parser);
}
try {
ActiveSAXSource.configureParser(parser);
} catch (XPathException err) {
throw new TransformerFactoryConfigurationError(err);
}
if (isValidation()) {
try {
parser.setFeature("http://xml.org/sax/features/validation", true);
} catch (SAXException err) {
throw new TransformerFactoryConfigurationError("The XML parser does not support validation");
}
}
return parser;
}
/**
* Report the parser details to the standard error output
*
* @param reader the parser
*/
private void reportParserDetails(XMLReader reader) {
String name = reader.getClass().getName();
// if (name.equals("com.sun.org.apache.xerces.internal.parsers.SAXParser")) {
// name += " version " + com.sun.org.apache.xerces.internal.impl.Version.getVersion();
// }
traceOutput.info("Using parser " + name);
}
/**
* Return a source parser to the pool, for reuse
*
* @param parser The parser: the caller must not supply a parser that was obtained by any
* mechanism other than calling the getSourceParser() method.
* Must not be null.
*/
public synchronized void reuseSourceParser(/*@NotNull*/ XMLReader parser) {
if (sourceParserPool == null) {
sourceParserPool = new ConcurrentLinkedQueue<>();
}
try {
try {
// give things back to the garbage collecter
parser.setContentHandler(null);
if (parser.getEntityResolver() == defaultParseOptions.getEntityResolver()) {
parser.setEntityResolver(null);
}
parser.setDTDHandler(null);
parser.setErrorHandler(null);
// Unfortunately setting the lexical handler to null doesn't work on Xerces, because
// it tests "value instanceof LexicalHandler". So we set it to a lexical handler that
// holds no references
parser.setProperty("http://xml.org/sax/properties/lexical-handler", dummyLexicalHandler);
} catch (SAXNotRecognizedException | SAXNotSupportedException err) {
//
}
sourceParserPool.offer(parser);
} catch (Exception e) {
// setting the callbacks on an XMLReader to null doesn't always work; some parsers throw a
// NullPointerException. If anything goes wrong, the simplest approach is to ignore the error
// and not attempt to reuse the parser.
}
}
/**
* Get a parser by instantiating the SAXParserFactory
*
* @return the parser (XMLReader)
*/
private static XMLReader loadParser() {
return Version.platform.loadParser();
}
/**
* Get the parser for stylesheet documents. This parser is also used for schema documents.
* This method is intended for internal use only.
*
* @return an XML parser (a SAX2 parser) that can be used for stylesheets and schema documents
* @throws javax.xml.transform.TransformerFactoryConfigurationError if an error occurs
* configuring the parser
*/
public synchronized XMLReader getStyleParser() throws TransformerFactoryConfigurationError {
if (styleParserPool == null) {
styleParserPool = new ConcurrentLinkedQueue<>();
}
XMLReader parser = styleParserPool.poll();
if (parser != null) {
return parser;
}
if (getStyleParserClass() != null) {
parser = makeParser(getStyleParserClass());
} else {
parser = loadParser();
}
EntityResolver resolver = new EntityResolverWrappingResourceResolver(getResourceResolver());
if (parser.getEntityResolver() != null) {
resolver = new ChainedEntityResolver(resolver, parser.getEntityResolver());
}
parser.setEntityResolver(resolver);
try {
parser.setFeature("http://xml.org/sax/features/namespaces", true);
parser.setFeature("http://xml.org/sax/features/namespace-prefixes", false);
} catch (SAXNotRecognizedException | SAXNotSupportedException e) {
throw new TransformerFactoryConfigurationError(e);
}
// Following code attempts to bypass attribute value normalization for stylesheets: see bug 2445.
// It works (the XercesAdapter has been dropped, but will be found somewhere in Subversion) but
// there are question marks over conformance, as there will be incompatibilities in edge cases
// for example when newlines appear within string literals within XPath expressions.
// if (parser.getClass().getCanonicalName().equals("org.apache.xerces.jaxp.SAXParserImpl.JAXPSAXParser")) {
// try {
// parser = new net.sf.saxon.event.XercesAdapter(parser);
// } catch (ClassNotFoundException err) {
// // ignore the error;
// }
// }
return parser;
}
private static final LexicalHandler dummyLexicalHandler = new DefaultHandler2();
/**
* Return a stylesheet (or schema) parser to the pool, for reuse
*
* @param parser The parser: the caller must not supply a parser that was obtained by any
* mechanism other than calling the getStyleParser() method.
*/
public synchronized void reuseStyleParser(XMLReader parser) {
if (styleParserPool == null) {
styleParserPool = new ConcurrentLinkedQueue<>();
}
try {
try {
// give things back to the garbage collecter
parser.setContentHandler(null);
//parser.setEntityResolver(null);
parser.setDTDHandler(null);
parser.setErrorHandler(null);
// Unfortunately setting the lexical handler to null doesn't work on Xerces, because
// it tests "value instanceof LexicalHandler". Instead we set the lexical handler to one
// that holds no references
parser.setProperty("http://xml.org/sax/properties/lexical-handler", dummyLexicalHandler);
} catch (SAXNotRecognizedException | SAXNotSupportedException err) {
//
}
styleParserPool.offer(parser);
} catch (Exception e) {
// setting the callbacks on an XMLReader to null doesn't always work; some parsers throw a
// NullPointerException. If anything goes wrong, the simplest approach is to ignore the error
// and not attempt to reuse the parser.
}
}
/**
* Simple interface to load a schema document
*
* @param absoluteURI the absolute URI of the location of the schema document
* @throws net.sf.saxon.type.SchemaException if the schema document at the given location cannot be read or is invalid
*/
public void loadSchema(String absoluteURI) throws SchemaException {
readSchema(makePipelineConfiguration(), "", absoluteURI, null);
}
/**
* Read a schema from a given schema location
* This method is intended for internal use.
*
* @param pipe the PipelineConfiguration
* @param baseURI the base URI of the instruction requesting the reading of the schema
* @param schemaLocation the location of the schema to be read
* @param expected The expected targetNamespace of the schema being read, or null if there is no expectation
* @return the target namespace of the schema; null if no schema has been read
* @throws UnsupportedOperationException when called in the non-schema-aware version of the product
* @throws net.sf.saxon.type.SchemaException if the schema cannot be read
*/
/*@Nullable*/
public NamespaceUri readSchema(PipelineConfiguration pipe, String baseURI, String schemaLocation, /*@Nullable*/ NamespaceUri expected)
throws SchemaException {
needEnterpriseEdition();
return null;
}
/**
* Read schemas from a list of schema locations.
* This method is intended for internal use.
*
* @param pipe the pipeline configuration
* @param baseURI the base URI against which the schema locations are to be resolved
* @param schemaLocations the relative URIs specified as schema locations
* @param expected the namespace URI which is expected as the target namespace of the loaded schema
* @throws net.sf.saxon.type.SchemaException if an error occurs
*/
public void readMultipleSchemas(PipelineConfiguration pipe, String baseURI, List schemaLocations, NamespaceUri expected)
throws SchemaException {
needEnterpriseEdition();
}
/**
* Read an inline schema from a stylesheet.
* This method is intended for internal use.
*
* @param root the xs:schema element in the stylesheet
* @param expected the target namespace expected; null if there is no
* expectation.
* @param errorReporter The destination for error messages. May be null, in which case
* the errorListener registered with this Configuration is used.
* @return the actual target namespace of the schema
* @throws net.sf.saxon.type.SchemaException if the schema cannot be processed
*/
/*@Nullable*/
public NamespaceUri readInlineSchema(NodeInfo root, NamespaceUri expected, ErrorReporter errorReporter)
throws SchemaException {
needEnterpriseEdition();
return null;
}
/**
* Throw an error indicating that a request cannot be satisfied because it requires
* the schema-aware edition of Saxon
*/
protected void needEnterpriseEdition() {
throw new UnsupportedOperationException(
"You need the Enterprise Edition of Saxon (with an EnterpriseConfiguration) for this operation");
}
/**
* Load a schema, which will be available for use by all subsequent operations using
* this Configuration. Any errors will be notified to the ErrorListener associated with
* this Configuration.
*
* @param schemaSource the JAXP Source object identifying the schema document to be loaded
* @throws SchemaException if the schema cannot be read or parsed or if it is invalid
* @throws UnsupportedOperationException if the configuration is not schema-aware
* @since 8.4
*/
public void addSchemaSource(Source schemaSource) throws SchemaException {
addSchemaSource(schemaSource, makeErrorReporter());
}
/**
* Load a schema, which will be available for use by all subsequent operations using
* this EnterpriseConfiguration.
*
* @param schemaSource the JAXP Source object identifying the schema document to be loaded
* @param errorReporter the ErrorListener to be notified of any errors in the schema.
* @throws SchemaException if the schema cannot be read or parsed or if it is invalid
*/
public void addSchemaSource(Source schemaSource, ErrorReporter errorReporter) throws SchemaException {
needEnterpriseEdition();
}
/**
* Add a built-in schema for a given namespace. This is a no-op if the configuration is not schema-aware
*
* @param namespace the namespace. Currently built-in schemas are available for the XML and FN namespaces
*/
public void addSchemaForBuiltInNamespace(NamespaceUri namespace) {
// no action
}
/**
* Determine whether the Configuration contains a cached schema for a given target namespace
*
* @param targetNamespace the target namespace of the schema being sought (supply "" for the
* unnamed namespace)
* @return true if the schema for this namespace is available, false if not.
*/
public boolean isSchemaAvailable(NamespaceUri targetNamespace) {
return false;
}
/**
* Remove all schema components that have been loaded into this Configuration.
* This method must not be used if any processes (such as stylesheet or query compilations
* or executions) are currently active. In a multi-threaded environment, it is the user's
* responsibility to ensure that this method is not called unless it is safe to do so.
*/
@SuppressWarnings({"UnusedDeclaration"})
public void clearSchemaCache() {
// no-op except in Saxon-EE
}
/**
* Get the set of namespaces of imported schemas
*
* @return a Set whose members are the namespaces of all schemas in the schema cache, as
* String objects
*/
public Set getImportedNamespaces() {
return Collections.emptySet();
}
/**
* Mark a schema namespace as being sealed. This is done when components from this namespace
* are first used for validating a source document or compiling a source document or query. Once
* a namespace has been sealed, it is not permitted to change the schema components in that namespace
* by redefining them, deriving new types by extension, or adding to their substitution groups.
*
* @param namespace the namespace URI of the components to be sealed
*/
public void sealNamespace(NamespaceUri namespace) {
//
}
/**
* Get the set of saxon:param schema parameters declared in the schema held by this Configuration.
*
* @return the set of parameters. May return null if none have been declared.
*/
public Collection getDeclaredSchemaParameters() {
return null;
}
/**
* Get the set of complex types that have been defined as extensions of a given type.
* Note that we do not seal the schema namespace, so this list is not necessarily final; we must
* assume that new extensions of built-in simple types can be added at any time
*
* @param type the type whose extensions are required
* @return an iterator over the types that are derived from the given type by extension
*/
public Iterable extends SchemaType> getExtensionsOfType(SchemaType type) {
return Collections.emptyList();
}
/**
* Import a precompiled Schema Component Model from a given Source. The schema components derived from this schema
* document are added to the cache of schema components maintained by this SchemaManager
*
* @param source the XML file containing the schema component model, as generated by a previous call on
* {@link #exportComponents}
* @throws net.sf.saxon.trans.XPathException if an error occurs
*/
public void importComponents(Source source) throws XPathException {
needEnterpriseEdition();
}
/**
* Export a precompiled Schema Component Model containing all the components (except built-in components)
* that have been loaded into this Processor.
*
* @param out the destination to recieve the precompiled Schema Component Model in the form of an
* XML document
* @throws net.sf.saxon.trans.XPathException if a failure occurs
*/
public void exportComponents(Receiver out) throws XPathException {
needEnterpriseEdition();
}
/**
* Get a global element declaration by fingerprint
*
* @param fingerprint the NamePool fingerprint of the element name
* @return the element declaration whose name matches the given
* fingerprint, or null if no element declaration with this name has
* been registered.
*/
public SchemaDeclaration getElementDeclaration(int fingerprint) {
return null;
}
/**
* Get a global element declaration by name.
*
* @param qName the name of the required
* element declaration
* @return the element declaration whose name matches the given
* name, or null if no element declaration with this name has
* been registered.
*/
public SchemaDeclaration getElementDeclaration(StructuredQName qName) {
return null;
}
/**
* Get a global attribute declaration by fingerprint
*
* @param fingerprint the NamePool fingerprint of the element name
* @return the attribute declaration whose name matches the given
* fingerprint, or null if no element declaration with this name has
* been registered.
*/
public SchemaDeclaration getAttributeDeclaration(int fingerprint) {
return null;
}
/**
* Get a global attribute declaration by name
*
* @param attributeName the name of the required attribute declaration
* @return the attribute declaration whose name matches the given
* fingerprint, or null if no element declaration with this name has
* been registered.
*/
public SchemaDeclaration getAttributeDeclaration(StructuredQName attributeName) {
return null;
}
/**
* Get the top-level schema type definition with a given QName.
*
* @param name the name of the required schema type
* @return the schema type , or null if there is none
* with this name.
* @since 9.7
*/
/*@Nullable*/
public SchemaType getSchemaType(StructuredQName name) {
if (name.hasURI(NamespaceUri.SCHEMA)) {
return BuiltInType.getSchemaTypeByLocalName(name.getLocalPart());
}
return null;
}
/**
* Make a union type with a given list of member types
*
* @param memberTypes the member types
* @return null for a Saxon-HE or -PE Configuration
*/
public ItemType makeUserUnionType(List memberTypes) {
return null;
}
/**
* Ask whether a given notation has been declared in the schema
*
* @param uri the targetNamespace of the notation
* @param local the local part of the notation name
* @return true if the notation has been declared, false if not
* @since 9.3
*/
@Override
public boolean isDeclaredNotation(NamespaceUri uri, String local) {
return false;
}
/**
* Check that a type is validly derived from another type, following the rules for the Schema Component
* Constraint "Is Type Derivation OK (Simple)" (3.14.6) or "Is Type Derivation OK (Complex)" (3.4.6) as
* appropriate.
*
* @param derived the derived type
* @param base the base type; the algorithm tests whether derivation from this type is permitted
* @param block the derivations that are blocked by the relevant element declaration
* @throws SchemaException if the derivation is not allowed
*/
public void checkTypeDerivationIsOK(SchemaType derived, SchemaType base, int block)
throws SchemaException {
// no action. Although the method can be used to check built-in types, it is never
// needed in the non-schema-aware product
}
/**
* Set validation reporting options. Called by instructions that invoke validation
* to set up an appropriate invalidity handler
*
* @param context the XPath evaluation context
* @param options the parser options, to be updated
*/
public void prepareValidationReporting(XPathContext context, ParseOptions options) {
}
/**
* Get a document-level validator to add to a Receiver pipeline.
* This method is intended for internal use.
*
* @param receiver The receiver to which events should be sent after validation
* @param systemId the base URI of the document being validated
* @param validationOptions Supplies options relevant to XSD validation
* @param initiatingLocation The location of the expression that requested validation
* @return A Receiver to which events can be sent for validation
*/
public Receiver getDocumentValidator(Receiver receiver,
String systemId,
ParseOptions validationOptions,
Location initiatingLocation) {
// non-schema-aware version
return receiver;
}
/**
* Get a Receiver that can be used to validate an element, and that passes the validated
* element on to a target receiver. If validation is not supported, the returned receiver
* will be the target receiver.
* This method is intended for internal use.
*
* @param receiver the target receiver tp receive the validated element
* @param validationOptions options affecting the way XSD validation is done
* @param locationId current location in the stylesheet or query
* @return The target receiver, indicating that with this configuration, no validation
* is performed.
* @throws net.sf.saxon.trans.XPathException if a validator for the element cannot be created
*/
public Receiver getElementValidator(Receiver receiver,
ParseOptions validationOptions,
Location locationId)
throws XPathException {
return receiver;
}
/**
* Validate an attribute value.
* This method is intended for internal use.
*
* @param nodeName the name of the attribute
* @param value the value of the attribute as a string
* @param validation STRICT or LAX
* @return the type annotation to apply to the attribute node
* @throws ValidationException if the value is invalid
*/
public SimpleType validateAttribute(StructuredQName nodeName, UnicodeString value, int validation)
throws ValidationException, MissingComponentException {
return BuiltInAtomicType.UNTYPED_ATOMIC;
}
/**
* Add to a pipeline a receiver that strips all type annotations. This
* has a null implementation in the Saxon-HE product, because type annotations
* can never arise.
* This method is intended for internal use.
*
* @param destination the Receiver that events will be written to after whitespace stripping
* @return the Receiver to which events should be sent for stripping
*/
public Receiver getAnnotationStripper(Receiver destination) {
return destination;
}
/**
* Create a new SAX XMLReader object using the class name provided.
* The named class must exist and must implement the
* org.xml.sax.XMLReader or Parser interface.
* This method returns an instance of the parser named.
* This method is intended for internal use.
*
* @param className A string containing the name of the
* SAX parser class, for example "com.microstar.sax.LarkDriver"
* @return an instance of the Parser class named, or null if it is not
* loadable or is not a Parser.
* @throws javax.xml.transform.TransformerFactoryConfigurationError if a failure
* occurs configuring the parser of this class
*/
public XMLReader makeParser(String className)
throws TransformerFactoryConfigurationError {
try {
Object obj = dynamicLoader.getInstance(className, null);
if (obj instanceof XMLReader) {
return (XMLReader) obj;
}
if (obj instanceof SAXParserFactory) {
try {
SAXParser saxParser = ((SAXParserFactory) obj).newSAXParser();
return saxParser.getXMLReader();
} catch (ParserConfigurationException | SAXException e) {
throw new XPathException(e);
}
}
} catch (XPathException err) {
throw new TransformerFactoryConfigurationError(err);
}
throw new TransformerFactoryConfigurationError("Class " + className +
" is not a SAX2 XMLReader or SAXParserFactory");
}
/**
* Make an expression Parser for a specified version of XPath or XQuery
*
* @param language set to "XP" (XPath) or "XQ" (XQuery) or "PATTERN" (XSLT Patterns)
* @param updating indicates whether or not XQuery update syntax may be used. Note that XQuery Update
* is supported only in Saxon-EE
* @return the XPath or Query parser
* @throws net.sf.saxon.trans.XPathException if this version of Saxon does not support the
* requested options
*/
public XPathParser newExpressionParser(String language, boolean updating) throws XPathException {
if ("XQ".equals(language)) {
if (updating) {
throw new XPathException("XQuery Update is supported only in Saxon-EE");
} else {
return new XQueryParser();
}
} else if ("XP".equals(language)) {
return new XPathParser();
} else if ("PATTERN".equals(language)) {
return new PatternParser();
} else {
throw new XPathException("Unknown expression language " + language);
}
}
/**
* Get a new ExpressionPresenter capable of exporting a compiled stylesheet
* @param target the target environment, e.g. EE, JS
* @param destination the destination for the output file
* @param rootPackage the root package of the export
* @return a suitable Expression Presenter
* @throws XPathException if none is available (e.g. because this is not Saxon-EE)
*/
public ExpressionPresenter newExpressionExporter(String target, OutputStream destination, StylesheetPackage rootPackage) throws XPathException {
throw new XPathException("Exporting a stylesheet requires Saxon-EE");
}
/**
* Set the debugger to be used.
* This method is provided for advanced users only, and is subject to change.
*
* @param debugger the debugger to be used, or null if no debugger is to be used
*/
@SuppressWarnings({"UnusedDeclaration"})
public void setDebugger(/*@Nullable*/ Debugger debugger) {
this.debugger = debugger;
}
/**
* Get the debugger in use. This will be null if no debugger has been registered.
* This method is provided for advanced users only, and is subject to change.
*
* @return the debugger in use, or null if none is in use
*/
/*@Nullable*/
@SuppressWarnings({"UnusedDeclaration"})
public Debugger getDebugger() {
return debugger;
}
/**
* Factory method to create a SlotManager.
* This method is provided for advanced users only, and is subject to change.
*
* @return a SlotManager (which is a skeletal stack frame representing the mapping of variable
* names to slots on the stack frame)
*/
public SlotManager makeSlotManager() {
if (debugger == null) {
return new SlotManager();
} else {
return debugger.makeSlotManager();
}
}
/**
* Create a streaming transformer
*
* @param mode the initial mode, which must be a streaming mode
* @param ordinaryParams the parameters that are not tunnel parameters
* @param tunnelParams the tunnel parameters
* @param output the destination for the result
* @param context the initial XPath context
* @return a Receiver to which the streamed input document will be pushed
* @throws XPathException if a streaming transformer cannot be created (which
* is always the case in Saxon-HE and Saxon-PE)
*/
/*@NotNull*/
public Receiver makeStreamingTransformer(Mode mode, ParameterSet ordinaryParams, ParameterSet tunnelParams, Outputter output, XPathContext context) throws XPathException {
throw new XPathException("Streaming is only available in Saxon-EE");
}
public Expression makeStreamInstruction(Expression hrefExp, Expression body, boolean streaming, ParseOptions options,
PackageData packageData, Location location, RetainedStaticContext rsc) throws XPathException {
SourceDocument si = new SourceDocument(hrefExp, body, options);
si.setLocation(location);
si.setRetainedStaticContext(rsc);
return si;
}
/**
* Get a factory function that can be used to wrap a SequenceIterator in a FocusTrackingIterator. This
* is called by the {@link Controller} to get a default factory function; the value can be overridden
* by the application using {@link Controller#setFocusTrackerFactory(java.util.function.Function)}.
* The {@link FocusTrackingIterator} that is created by the factory must be thread-safe if it is
* to be used for iterating over a sequence where the items might be processed asynchronously using
* xsl:result-document
; for this reason this method is overridden for a Saxon-EE configuration.
* @param exec the executable; supplied so that the factory can be sensitive to whether calls on xsl:result-document
* are possible
* @param multithreaded set to true to get a factory suitable for creating focus tracking iterators for a
* multi-threaded xsl:for-each instruction
* @return a suitable factory function
*/
public java.util.function.Function getFocusTrackerFactory(
Executable exec, boolean multithreaded) {
return CSharp.constructorRef(FocusTrackingIterator::new, 1);
}
/**
* Check the streamability of a template rule
* @param template the xsl:template element in the stylesheet tree
* @param body of the compiled body of the template rule
* @throws XPathException if streamability problems are found
*/
public void checkStrictStreamability(XSLTemplate template, Expression body) throws XPathException {
// no action in Saxon-HE
}
/**
* Ask whether a given node is a streamed node
* @param node the node in question
* @return true if the node is a node in a streamed document
*/
public boolean isStreamedNode(NodeInfo node) {
return false; // streaming needs Saxon-EE
// TODO: make this a property of a node (or of a TreeInfo)
}
/**
* Get the optimization options in use
* @return the configured optimization options
*/
public OptimizerOptions getOptimizerOptions() {
return optimizerOptions.intersect(OptimizerOptions.FULL_HE_OPTIMIZATION);
}
/**
* Get the optimization options permitted in this configuration
* @return the permitted optimization options
*/
public OptimizerOptions getPermittedOptimizerOptions() {
return OptimizerOptions.FULL_HE_OPTIMIZATION;
}
/**
* Factory method to get an Optimizer.
* This method is intended for internal use only.
*
* @return the optimizer used in this configuration, which is created if necessary
*/
/*@NotNull*/
public Optimizer obtainOptimizer() {
if (optimizer == null) {
optimizer = new Optimizer(this);
optimizer.setOptimizerOptions(
optimizerOptions.intersect(OptimizerOptions.FULL_HE_OPTIMIZATION));
return optimizer;
} else {
return optimizer;
}
}
/**
* Factory method to get an Optimizer with specified optimizer options.
* This method is intended for internal use only.
*
* @param options the optimizer options
* @return a new optimizer with the specified options set (provided the optimizations
* are available in this Saxon configuration)
*/
/*@NotNull*/
public Optimizer obtainOptimizer(OptimizerOptions options) {
Optimizer optimizer = new Optimizer(this);
optimizer.setOptimizerOptions(
options.intersect(OptimizerOptions.FULL_HE_OPTIMIZATION));
return optimizer;
}
/**
* Factory method to make a ContextItemStaticInfo
*
* @param itemType the item type of the context item. If the context item is absent, set this to
* {@link net.sf.saxon.type.ErrorType#getInstance()}.
* @param maybeUndefined set to true if it is possible (or certain) that the context item will be absent.
* @return the ContextItemStaticInfo
*/
public ContextItemStaticInfo makeContextItemStaticInfo(ItemType itemType, boolean maybeUndefined) {
return new ContextItemStaticInfo(itemType, maybeUndefined);
}
/**
* Get a default ContextItemStaticInfo, indication no information is available about the context item
* type
*
* @return the default ContextItemStaticInfo
*/
public ContextItemStaticInfo getDefaultContextItemStaticInfo() {
return ContextItemStaticInfo.DEFAULT;
}
/**
* Factory method to make an XQueryExpression
*
* @param exp the expression forming the body of the query
* @param mainModule the query module containing the expression
* @param streaming true if streamed execution is requested
* @return the XQueryExpression
* @throws XPathException if an error occurs
*/
public XQueryExpression makeXQueryExpression(Expression exp, QueryModule mainModule, boolean streaming) throws XPathException {
XQueryExpression xqe = new XQueryExpression(exp, mainModule, false);
if (mainModule.getCodeInjector() != null) {
mainModule.getCodeInjector().process(xqe);
}
return xqe;
}
// /**
// * Make a Closure, given the expected reference count
// *
// * @param expression the expression to be evaluated
// * @param ref the (nominal) number of times the value of the expression is required
// * @param context the XPath dynamic evaluation context
// * @return the constructed Closure
// * @throws XPathException if a failure occurs constructing the Closure
// */
//
// public Sequence makeClosure(Expression expression, int ref, XPathContext context) throws XPathException {
// if (getBooleanProperty(Feature.EAGER_EVALUATION)) {
// // Using eager evaluation can make for easier debugging
// SequenceIterator iter = expression.iterate(context);
// return SequenceTool.toGroundedValue(iter);
// }
//
// Closure closure = ref > 1 ? new MemoClosure() : new Closure();
// closure.setExpression(expression);
// closure.setSavedXPathContext(context.newContext());
// closure.saveContext(expression, context);
// return closure;
//
// }
/**
* Make a SequenceExtent, given the expected reference count
*
* @param expression the expression to be evaluated
* @param ref the (nominal) number of times the value of the expression is required
* @param context the XPath dynamic evaluation context
* @return the constructed SequenceExtent
* @throws XPathException if evaluation of the expression fails
*/
public GroundedValue makeSequenceExtent(Expression expression, int ref, XPathContext context) throws XPathException {
return SequenceTool.toGroundedValue(expression.iterate(context));
}
/**
* Factory method to make the StyleNodeFactory, used for constructing elements
* in a stylesheet document
*
* @param compilation the compilation episode (compiling one package)
* @return the StyleNodeFactory used in this Configuration
*/
public StyleNodeFactory makeStyleNodeFactory(Compilation compilation) {
return new StyleNodeFactory(this, compilation);
}
/**
* Make an instruction to implement xsl:evaluate
* @param source the xsl:evaluate element in the raw stylesheet tree
* @param decl the corresponding component declaration
* @return the compiled instruction
* @throws XPathException if static errors are found
*/
public Expression makeEvaluateInstruction(XSLEvaluate source, ComponentDeclaration decl) throws XPathException {
Expression xpath = source.getTargetExpression();
SequenceType requiredType = source.getRequiredType();
Expression contextItem = source.getContextItemExpression();
Expression baseUri = source.getBaseUriExpression();
Expression namespaceContext = source.getNamespaceContextExpression();
Expression schemaAware = source.getSchemaAwareExpression();
Expression withParams = source.getWithParamsExpression();
EvaluateInstr inst = new EvaluateInstr(xpath, requiredType, contextItem, baseUri, namespaceContext, schemaAware);
WithParam[] params = source.getWithParamInstructions(inst, source.getCompilation(), decl, false);
inst.setActualParams(params);
inst.setDynamicParams(withParams);
inst.setDefaultXPathNamespace(source.getDefaultXPathNamespace());
inst.setOptionsExpression(source.getOptionsExpression());
return inst;
}
/**
* Factory method to make a StylesheetPackage
*
* @return a StylesheetPackage suitable for use in this Configuration
*/
public StylesheetPackage makeStylesheetPackage() {
return new StylesheetPackage(this);
}
/**
* Factory method to make the AccumulatorRegistry, used for static information
* about the accumulators defined in a package
*
* @return a new AccumulatorRegistry appropriate to this Configuration
*/
public AccumulatorRegistry makeAccumulatorRegistry() {
return new AccumulatorRegistry();
}
/**
* Register an external object model with this Configuration.
*
* @param model The external object model.
* This can either be one of the system-supplied external
* object models such as Axiom, DOM4J, JDOM, or XOM, or a user-supplied external object model.
* @see net.sf.saxon.option.axiom.AxiomObjectModel
* @see net.sf.saxon.option.dom4j.DOM4JObjectModel
* @see net.sf.saxon.option.jdom2.JDOM2ObjectModel
* @see net.sf.saxon.option.xom.XOMObjectModel
*/
public void registerExternalObjectModel(ExternalObjectModel model) {
// code removed by bug 5725
// try {
// getClass(model.getDocumentClassName(), false);
// } catch (XPathException e) {
// // If the model can't be loaded, do nothing
// return;
// }
if (externalObjectModels == null) {
externalObjectModels = new ArrayList<>(4);
}
if (!externalObjectModels.contains(model)) {
externalObjectModels.add(model);
}
}
/**
* Get the external object model with a given URI, if registered
*
* @param uri the identifying URI of the required external object model
* @return the requested external object model if available, or null otherwise
*/
/*@Nullable*/
public ExternalObjectModel getExternalObjectModel(String uri) {
for (ExternalObjectModel model : externalObjectModels) {
if (model.getIdentifyingURI().equals(uri)) {
return model;
}
}
return null;
}
/**
* Get the external object model that recognizes a particular class of node, if available
*
* @param nodeClass the class of the Node object in the external object model
* @return the requested external object model if available, or null otherwise
*/
/*@Nullable*/
public ExternalObjectModel getExternalObjectModel(Class> nodeClass) {
for (ExternalObjectModel model : externalObjectModels) {
PJConverter converter = model.getPJConverter(nodeClass);
if (converter != null) {
return model;
}
}
return null;
}
/**
* Get all the registered external object models.
* This method is intended for internal use only.
*
* @return a list of external object models supported. The members of the list are of
* type {@link ExternalObjectModel}
*/
public List getExternalObjectModels() {
return externalObjectModels;
}
/**
* Make a map representing the methods defined in a class. This map is specific to the class, not to
* a particular instance. The functions present in this map take an extra first argument representing
* the target instance; the functions returned in the final instance-level map will be partial applications
* of the functions in the class-level map.
*
* @param javaClass the Java class whose methods are required
* @param required if non-null, indicates the key of the entry that is required in the map. If
* this parameter is supplied, then the map will be limited to a single entry
* with this key, since it is known that the other entries would never be used
* @return a map whose entries represent public instance-level methods in the supplied Java class,
* to the extent that these methods have unique names.
* @throws UnsupportedOperationException except in subclasses
*/
public Map makeMethodMap(Class> javaClass, String required) {
throw new UnsupportedOperationException();
}
/**
* Convert a Java object to a map
*
* @param value the (wrapped) Java object to be converted
* @param required if non-null, indicates the key of the entry that is required in the map. If
* this parameter is supplied, then the map will be limited to a single entry
* with this key, since it is known that the other entries would never be used.
* @return an XDM map containing entries representing the public instance-level methods
* available in the object, to the extent that they have unique names.
* @throws UnsupportedOperationException except in subclasses
*/
public MapItem externalObjectAsMap(ObjectValue> value, String required) {
throw new UnsupportedOperationException();
}
/**
* Make an object lookup expression: supports the construct X?Y where X is an external Java object.
* Requires Saxon-PE or higher
*
* @param lhs the left-hand operand
* @param rhs the right-hand operand
* @return the constructed expression
* @throws XPathException if anything goes wrong
*/
public Expression makeObjectLookupExpression(Expression lhs, Expression rhs) throws XPathException {
throw new UnsupportedOperationException();
}
/**
* Get a NodeInfo corresponding to a DOM or other external Node,
* either by wrapping or unwrapping the external Node.
* This method is intended for internal use.
*
* @param source A Source representing the wrapped or unwrapped external Node. This will typically
* be a DOMSource, but it may be a similar Source recognized by some other registered external
* object model.
* @return If the Source is a DOMSource and the underlying node is a wrapper around a Saxon NodeInfo,
* returns the wrapped Saxon NodeInfo. If the Source is a DOMSource and the undelying node is not such a wrapper,
* returns a new Saxon NodeInfo that wraps the DOM Node. If the Source is any other kind of source, it
* is offered to each registered external object model for similar treatment. The result is the
* NodeInfo object obtained by wrapping or unwrapping the supplied external node.
* @throws IllegalArgumentException if the source object is not of a recognized class. This method does
* not call the registered {@link SourceResolver to resolve the Source}.
*/
public NodeInfo unravel(Source source) {
List externalObjectModels = getExternalObjectModels();
if (!(source instanceof NodeInfo)) {
for (ExternalObjectModel model : externalObjectModels) {
NodeInfo node = model.unravel(source, this);
if (node != null) {
if (!node.getConfiguration().isCompatible(this)) {
throw new IllegalArgumentException("Externally supplied Node belongs to the wrong Configuration");
}
return node;
}
}
}
NodeInfo suppliedNode = null;
if (source instanceof NodeInfo) {
suppliedNode = (NodeInfo)source;
}
if (source instanceof NodeSource) {
suppliedNode = ((NodeSource)source).getNode();
}
if (suppliedNode != null) {
if (!suppliedNode.getConfiguration().isCompatible(this)) {
throw new IllegalArgumentException("Externally supplied NodeInfo belongs to the wrong Configuration");
}
return suppliedNode;
}
throw new IllegalArgumentException("A source of class " +
source.getClass() + " is not recognized by any registered object model");
}
/**
* Ask whether an extension element with a particular name is available
*
* @param qName the extension element name
* @return false (always, in the case of Saxon-HE)
* @since 9.7
*/
public boolean isExtensionElementAvailable(StructuredQName qName) {
return false;
}
/**
* Set the StaticQueryContextFactory used for creating instances of StaticQueryContext
*
* @param factory the factory class to be used when a new StaticQueryContext is required.
* Note that this is not used for the default StaticQueryContext held in the Configuration itself.
* @since 9.5.1.2.
*/
public void setStaticQueryContextFactory(StaticQueryContextFactory factory) {
staticQueryContextFactory = factory;
}
/**
* Get a new StaticQueryContext (which is also the factory class for creating a query parser).
* Note that this method is used to underpin the s9api and XQJ APIs for XQuery compilation, and
* that modifying the behaviour of the StaticQueryContext can affect the behaviour of those APIs
*
* @return a new StaticQueryContext
*/
public StaticQueryContext newStaticQueryContext() {
return makeStaticQueryContext(true);
}
/**
* Get a new Pending Update List
*
* @return the new Pending Update List
* @throws UnsupportedOperationException if called when using Saxon-HE
*/
public PendingUpdateList newPendingUpdateList() {
throw new UnsupportedOperationException("XQuery update is supported only in Saxon-EE");
}
/**
* Make a PipelineConfiguration from the properties of this Configuration
*
* @return a new PipelineConfiguration
* @since 8.4
*/
public PipelineConfiguration makePipelineConfiguration() {
PipelineConfiguration pipe = new PipelineConfiguration(this, defaultParseOptions);
pipe.setErrorReporter(makeErrorReporter());
return pipe;
}
/**
* Make a SchemaURIResolver that wraps a supplied ResourceResolver
*
* @param resolver the underlying ResourceResolver
* @return a new SchemaURIResolver (or null if this is not an EnterpriseConfiguration)
* @since 10.0
*/
public SchemaURIResolver makeSchemaURIResolver(ResourceResolver resolver) {
return null;
}
/**
* Get the configuration, given the context. This is provided as a static method to make it accessible
* as an extension function.
*
* @param context the XPath dynamic context
* @return the Saxon Configuration for a given XPath dynamic context
*/
public static Configuration getConfiguration(XPathContext context) {
return context.getConfiguration();
}
/**
* Supply a SourceResolver. This is used for handling unknown implementations of the
* {@link javax.xml.transform.Source} interface: a user-supplied SourceResolver can handle
* such Source objects and translate them to a kind of Source that Saxon understands.
*
* @param resolver the source resolver.
*/
public void setSourceResolver(SourceResolver resolver) {
sourceResolver = resolver;
}
/**
* Get the current SourceResolver. If none has been supplied, a system-defined SourceResolver
* is returned.
*
* @return the current SourceResolver
*/
public SourceResolver getSourceResolver() {
return sourceResolver;
}
/**
* Resolve a Source.
*
* @param source A source object, typically the source supplied as the first
* argument to {@link javax.xml.transform.Transformer#transform(javax.xml.transform.Source, javax.xml.transform.Result)}
* or similar methods.
* @param config The Configuration. This provides the SourceResolver with access to
* configuration information; it also allows the SourceResolver to invoke the
* resolveSource() method on the Configuration object as a fallback implementation.
* @return a source object that Saxon knows how to process. This must be an instance of one
* of the classes StreamSource, SAXSource, DOMSource, {@link net.sf.saxon.lib.AugmentedSource},
* {@link net.sf.saxon.om.NodeInfo},
* or {@link net.sf.saxon.pull.PullSource}. Return null if the Source object is not
* recognized
* @throws XPathException if the Source object is recognized but cannot be processed
*/
/*@Nullable*/
@Override
@CSharpInnerClass(outer=true, extra={"Saxon.Ejavax.xml.transform.Source source", "Saxon.Hej.Configuration config"})
public ActiveSource resolveSource(Source source, Configuration config) throws XPathException {
if (source instanceof ActiveSource) {
return (ActiveSource) source;
}
if (source instanceof AugmentedSource) {
return new ActiveSource() {
@Override
public void deliver(Receiver receiver, ParseOptions options) throws XPathException {
options = options.merge(((AugmentedSource) source).getParseOptions());
resolveSource(((AugmentedSource) source).getContainedSource(), config)
.deliver(receiver, options);
}
@Override
public void setSystemId(String systemId) {
source.setSystemId(systemId);
}
@Override
public String getSystemId() {
return source.getSystemId();
}
};
}
// Try delegating to the Platform for platform-specific parsing strategies
ActiveSource activeSource = Version.platform.resolveSource(source, config);
if (activeSource != null) {
return activeSource;
}
// Try delegating to registered object models to see if the source is recognised
List externalObjectModels = config.getExternalObjectModels();
for (ExternalObjectModel model : externalObjectModels) {
ActiveSource a = model.getActiveSource(source);
if (a != null) {
return a;
}
}
return null;
}
/**
* Build a document tree, using options set on this Configuration and on the supplied source
* object. Options set on the source object override options set in the Configuration. The Source
* object must be one of the kinds of source recognized by Saxon, or a source that can be resolved
* using the registered {@link SourceResolver}. This method always constructs a new tree, it never
* wraps or returns an existing tree.
*
* @param source the Source to be used. This may be an {@link AugmentedSource}, allowing options
* to be specified for the way in which this document will be built. If an AugmentedSource
* is supplied then options set in the AugmentedSource take precedence over options
* set in the Configuration.
* If any error occurs reading or parsing the supplied Source, the error is notified
* to the {@link ErrorListener} registered with this {@link Configuration}.
* @return the constructed document as a TreeInfo
* @throws XPathException if any errors occur during document parsing or validation. Detailed
* errors occurring during schema validation will be written to the ErrorListener associated
* with the AugmentedSource, if supplied, or with the Configuration otherwise.
* @since 9.7; based on the original buildDocument(Source) method, but adapted to return the
* TreeInfo containing information about the constructed tree, including a reference to its root node.
*/
public TreeInfo buildDocumentTree(Source source) throws XPathException {
if (source == null) {
throw new NullPointerException("source");
}
if (source instanceof AugmentedSource) {
return buildDocumentTree(
((AugmentedSource) source).getContainedSource(),
((AugmentedSource) source).getParseOptions());
} else {
return buildDocumentTree(source, defaultParseOptions);
}
}
/**
* Build a document, using specified options for parsing and building. This method always
* constructs a new tree, it never wraps an existing document (regardless of anything in
* the parseOptions)
*
* @param source the source of the document to be constructed. If this is an
* AugmentedSource, then any parser options contained in the AugmentedSource take precedence
* over options specified in the parseOptions argument.
* @param parseOptions options for parsing and constructing the document. Any options that
* are not explicitly set in parseOptions default first to the values supplied in the source
* argument if it is an AugmentedSource, and then to the values set in this Configuration.
* The supplied parseOptions object is not modified.
* @return the constructed document as a TreeInfo
* @throws XPathException if parsing fails, or if the Source represents a node other than
* a document node
* @since 9.7; based on the original buildDocument(Source, ParseOptions) method, but adapted to return the
* TreeInfo containing information about the constructed tree, including a reference to its root node.
*/
public TreeInfo buildDocumentTree(/*@Nullable*/ Source source, ParseOptions parseOptions) throws XPathException {
if (source == null) {
throw new NullPointerException("source");
}
boolean finallyClose = false;
try {
ParseOptions options = parseOptions;
// Resolve user-defined implementations of Source
Source src2 = resolveSource(source, this);
if (src2 == null) {
throw new XPathException("Unknown source class " + source.getClass().getName());
}
source = src2;
if (source instanceof AugmentedSource) {
options = options.merge(((AugmentedSource) source).getParseOptions());
}
options = options.applyDefaults(this);
finallyClose = options.isPleaseCloseAfterUse();
// Create an appropriate Builder
TreeModel treeModel = options.getModel();
// Decide whether line numbering is in use
boolean lineNumbering = options.isLineNumbering();
PipelineConfiguration pipe = makePipelineConfiguration();
pipe.setParseOptions(options);
Builder builder = treeModel.makeBuilder(pipe);
builder.setTiming(isTiming());
builder.setLineNumbering(lineNumbering);
builder.setPipelineConfiguration(pipe);
builder.setSystemId(source.getSystemId());
Sender.send(source, builder, options);
// Get the constructed document
NodeInfo newdoc = builder.getCurrentRoot();
if (newdoc.getNodeKind() != Type.DOCUMENT) {
throw new XPathException("Source object represents a node other than a document node");
}
// Reset the builder, detaching it from the constructed document
builder.reset();
// Return the constructed document
return newdoc.getTreeInfo();
} finally {
// If requested, close the input stream
if (finallyClose) {
ParseOptions.close(source);
}
}
}
/**
* Get the collection of tree-builder statistics for this configuration, used
* for learning suitable amounts of space to allocate for different kinds of tree
* @return the object in which tree statistics are accumulated
*/
public TreeStatistics getTreeStatistics() {
return treeStatistics;
}
/**
* Load a named output emitter or SAX2 ContentHandler and check it is OK.
*
* @param eqName the EQName of the user-supplied ContentHandler (requested as a prefixed
* value of the method attribute in xsl:output, or anywhere that serialization parameters
* are allowed), encoded in EQName format as Q{uri}local
* @param props the properties to be used in the case of a dynamically-loaded ContentHandler.
* @return a Receiver (despite the name, it is not required to be an Emitter)
* @throws net.sf.saxon.trans.XPathException if a failure occurs creating the Emitter
*/
public Receiver makeEmitter(String eqName, Properties props) throws XPathException {
StructuredQName sqName = StructuredQName.fromEQName(eqName);
String className = sqName.getLocalPart();
Object handler;
try {
handler = dynamicLoader.getInstance(className, null);
} catch (XPathException e) {
throw new XPathException("Cannot create user-supplied output method. " + e.getMessage(),
SaxonErrorCode.SXCH0004);
}
if (handler instanceof Receiver) {
return (Receiver) handler;
} else if (handler instanceof ContentHandler) {
ContentHandlerProxy emitter = new ContentHandlerProxy();
emitter.setUnderlyingContentHandler((ContentHandler) handler);
emitter.setOutputProperties(props);
return emitter;
} else {
throw new XPathException("Output method " + className +
" is neither a Receiver nor a SAX2 ContentHandler");
}
}
/**
* Set a property of the configuration. This method underpins the setAttribute() method of the
* TransformerFactory implementation, and is provided
* to enable setting of Configuration properties using URIs without instantiating a TransformerFactory:
* specifically, this may be useful when running XQuery, and it is also used by the Validator API.
*
* From Saxon 9.9, an alternative interface is available: {@link #setConfigurationProperty(Feature, Object)}.
* The new interface is more efficient because it avoids expensive string comparisons. The old interface is
* retained mainly for compatibility, and also because there are a few cases where it cannot easily be replaced,
* for example when using composite feature URIs to delegate configuration options to the XML parser.
*
* @param name the URI identifying the property to be set. See the class {@link FeatureKeys} for
* constants representing the property names that can be set.
* @param value the value of the property. Note that boolean values may be supplied either as a Boolean,
* or as one of the strings "0", "1", "true", "false", "yes", "no", "on", or "off".
* @throws IllegalArgumentException if the property name is not recognized or if the value is not
* a valid value for the named property
*/
public void setConfigurationProperty(String name, Object value) {
if (FeatureIndex.exists(name)) {
setFeature(FeatureIndex.getData(name), value);
} else if (name.startsWith(FeatureKeys.XML_PARSER_FEATURE)) {
String uri = name.substring(FeatureKeys.XML_PARSER_FEATURE.length());
try {
uri = URLDecoder.decode(uri, "utf-8");
} catch (UnsupportedEncodingException e) {
throw new IllegalArgumentException(e);
}
defaultParseOptions = defaultParseOptions.withParserFeature(uri, requireBoolean(name, value));
} else if (name.startsWith(FeatureKeys.XML_PARSER_PROPERTY)) {
String uri = name.substring(FeatureKeys.XML_PARSER_PROPERTY.length());
uri = URLDecoder.decode(uri, StandardCharsets.UTF_8);
defaultParseOptions = defaultParseOptions.withParserProperty(uri, value);
} else {
throw new IllegalArgumentException("Unrecognized configuration feature: " + name);
}
}
/**
* Set a property of the configuration.
*
* This is the preferred way of setting configuration options from application
* code where it is known statically which feature is being set. Other methods are
* provided for use by wrapper APIs (such as the JAXP API) where the feature name
* is supplied dynamically as a string.
* @param the type of value required for this particular feature
* @param feature the property to be set. See the class {@link Feature} for
* constants representing the property names that can be set.
* @param value the value of the property. This must be of the correct type corresponding
* to the chosen {@link Feature}.
* @throws IllegalArgumentException if the property name is not recognized or if the value is not
* a valid value for the named property
*/
public void setConfigurationProperty(Feature feature, T value) {
setFeature(FeatureIndex.getData(feature.code), value);
}
/**
* Internal supporting method for setting configuration properties
* @param feature details of the property to be set
* @param value value of the property to be set
*/
@CSharpModifiers(code={"public", "virtual"})
protected void setFeature(FeatureData feature, Object value) {
String name = feature.uri;
int code = feature.code;
if (booleanFeatures.contains(code)) {
if (code == FeatureCode.COMPILE_WITH_TRACING) {
boolean b = requireBoolean(name, value);
setCompileWithTracing(b);
} else if (code == FeatureCode.DTD_VALIDATION) {
boolean b = requireBoolean(name, value);
setValidation(b);
} else if (code == FeatureCode.EXPAND_ATTRIBUTE_DEFAULTS) {
boolean b = requireBoolean(name, value);
setExpandAttributeDefaults(b);
} else if (code == FeatureCode.ALLOW_SYNTAX_EXTENSIONS) {
boolean b = requireBoolean(name, value);
defaultXsltCompilerInfo.setXsltVersion(b ? 40 : 30);
if (defaultStaticQueryContext != null) {
defaultStaticQueryContext.setLanguageVersion(b ? 40 : 30);
}
}
internalSetBooleanProperty(code, name, value);
} else if (stringFeatures.contains(code)) {
stringProperties.put(code, requireString(name, value));
} else {
switch (code) {
case FeatureCode.ALLOWED_PROTOCOLS:
protocolRestrictor = new ProtocolRestrictor((String)value);
break;
case FeatureCode.COLLATION_URI_RESOLVER:
if (!(value instanceof CollationURIResolver)) {
throw new IllegalArgumentException(
"COLLATION_URI_RESOLVER value must be an instance of net.sf.saxon.lib.CollationURIResolver");
}
setCollationURIResolver((CollationURIResolver) value);
break;
case FeatureCode.COLLATION_URI_RESOLVER_CLASS:
setCollationURIResolver(
(CollationURIResolver) instantiateClassName(name, value, CollationURIResolver.class));
break;
case FeatureCode.COLLECTION_FINDER:
if (!(value instanceof CollectionFinder)) {
throw new IllegalArgumentException(
"COLLECTION_FINDER value must be an instance of net.sf.saxon.lib.ICollectionFinder");
}
setCollectionFinder((CollectionFinder) value);
break;
case FeatureCode.COLLECTION_FINDER_CLASS:
setCollectionFinder(
(CollectionFinder) instantiateClassName(name, value, CollectionFinder.class));
break;
case FeatureCode.DEFAULT_COLLATION:
defaultCollationName = value.toString();
break;
case FeatureCode.DEFAULT_COLLECTION:
setDefaultCollection(value.toString());
break;
case FeatureCode.DEFAULT_COUNTRY:
setDefaultCountry(value.toString());
break;
case FeatureCode.DEFAULT_LANGUAGE:
setDefaultLanguage(value.toString());
break;
case FeatureCode.DEFAULT_REGEX_ENGINE:
setDefaultRegexEngine(value.toString());
break;
case FeatureCode.DTD_VALIDATION_RECOVERABLE: {
boolean b = requireBoolean(name, value);
if (b) {
defaultParseOptions = defaultParseOptions.withDTDValidationMode(Validation.LAX);
} else {
defaultParseOptions = defaultParseOptions.withDTDValidationMode(isValidation() ? Validation.STRICT : Validation.SKIP);
}
internalSetBooleanProperty(code, name, b);
break;
}
case FeatureCode.ENTITY_RESOLVER_CLASS:
if ("".equals(value)) {
defaultParseOptions = defaultParseOptions.withEntityResolver(null);
} else {
defaultParseOptions = defaultParseOptions.withEntityResolver(
(EntityResolver) instantiateClassName(name, value, EntityResolver.class));
}
break;
case FeatureCode.ENVIRONMENT_VARIABLE_RESOLVER:
if (!(value instanceof EnvironmentVariableResolver)) {
throw new IllegalArgumentException(
"ENVIRONMENT_VARIABLE_RESOLVER value must be an instance of net.sf.saxon.lib.EnvironmentVariableResolver");
}
environmentVariableResolver = (EnvironmentVariableResolver) value;
break;
case FeatureCode.ENVIRONMENT_VARIABLE_RESOLVER_CLASS:
environmentVariableResolver =
(EnvironmentVariableResolver) instantiateClassName(name, value, EnvironmentVariableResolver.class);
break;
case FeatureCode.ERROR_LISTENER_CLASS:
// No action, obsolete
break;
case FeatureCode.LINE_NUMBERING: {
boolean b = requireBoolean(name, value);
setLineNumbering(b);
break;
}
case FeatureCode.MODULE_URI_RESOLVER:
if (!(value instanceof ModuleURIResolver)) {
throw new IllegalArgumentException(
"MODULE_URI_RESOLVER value must be an instance of net.sf.saxon.lib.ModuleURIResolver");
}
setModuleURIResolver((ModuleURIResolver) value);
break;
case FeatureCode.MODULE_URI_RESOLVER_CLASS:
ModuleURIResolver resolver = (ModuleURIResolver) instantiateClassName(name, value, ModuleURIResolver.class);
if (resolver instanceof StandardModuleURIResolver) {
((StandardModuleURIResolver) resolver).setConfiguration(this);
}
setModuleURIResolver(resolver);
break;
case FeatureCode.NAME_POOL:
if (!(value instanceof NamePool)) {
throw new IllegalArgumentException("NAME_POOL value must be an instance of net.sf.saxon.om.NamePool");
}
setNamePool((NamePool) value);
break;
case FeatureCode.OPTIMIZATION_LEVEL:
if (value instanceof Integer) {
// See Saxon bug 2076. It seems Ant passes an integer value as an integer, not as a string. Not tested.
// Integer values retained for compatibility: 0=none, 10 = all
int v = (Integer) value;
optimizerOptions = v == 0
? new OptimizerOptions(0)
: OptimizerOptions.FULL_EE_OPTIMIZATION.intersect(getPermittedOptimizerOptions());
} else {
String s = requireString(name, value);
if (s.matches("[0-9]+")) {
// For backwards compatibility
optimizerOptions = "0".equals(s)
? new OptimizerOptions(0)
: OptimizerOptions.FULL_EE_OPTIMIZATION.intersect(getPermittedOptimizerOptions());
} else {
optimizerOptions = new OptimizerOptions(s).intersect(getPermittedOptimizerOptions());
}
}
if (optimizer != null) {
optimizer.setOptimizerOptions(optimizerOptions);
}
defaultXsltCompilerInfo.setOptimizerOptions(optimizerOptions);
break;
case FeatureCode.OUTPUT_URI_RESOLVER:
if (!(value instanceof OutputURIResolver)) {
throw new IllegalArgumentException(
"OUTPUT_URI_RESOLVER value must be an instance of net.sf.saxon.lib.OutputURIResolver");
}
setOutputURIResolver((OutputURIResolver) value);
break;
case FeatureCode.OUTPUT_URI_RESOLVER_CLASS:
setOutputURIResolver(
(OutputURIResolver) instantiateClassName(name, value, OutputURIResolver.class));
break;
// //#if CSHARP==false
// case FeatureCode.RECOGNIZE_URI_QUERY_PARAMETERS: {
// boolean b = requireBoolean(name, value);
// getSystemURIResolver().setRecognizeQueryParameters(b);
// break;
// }
// //#endif
case FeatureCode.RECOVERY_POLICY:
// Obsolete: no action
break;
case FeatureCode.RECOVERY_POLICY_NAME:
// Obsolete: no action
break;
case FeatureCode.REGEX_BACKTRACKING_LIMIT:
regexBacktrackingLimit = requireInteger(name, value);
break;
case FeatureCode.SERIALIZER_FACTORY_CLASS:
setSerializerFactory(
(SerializerFactory) instantiateClassName(name, value, SerializerFactory.class));
break;
case FeatureCode.SCHEMA_VALIDATION: {
setSchemaValidationMode(requireInteger(name, value));
break;
}
case FeatureCode.SCHEMA_VALIDATION_MODE:
String mode = requireString(name, value);
setSchemaValidationMode(Validation.getCode(mode));
break;
case FeatureCode.SOURCE_PARSER_CLASS:
setSourceParserClass(requireString(name, value));
break;
case FeatureCode.SOURCE_RESOLVER_CLASS:
setSourceResolver(
(SourceResolver) instantiateClassName(name, value, SourceResolver.class));
break;
case FeatureCode.STANDARD_ERROR_OUTPUT_FILE:
// Note, this property is write-only
try {
boolean append = true;
boolean autoFlush = true;
setStandardErrorOutput(
new PrintStream(new FileOutputStream((String) value, append), autoFlush));
} catch (FileNotFoundException fnf) {
throw new IllegalArgumentException(fnf);
}
break;
case FeatureCode.STRIP_WHITESPACE: {
String s = requireString(name, value);
SpaceStrippingRule rule;
switch (s) {
case "all":
rule = AllElementsSpaceStrippingRule.getInstance();
break;
case "none":
rule = NoElementsSpaceStrippingRule.getInstance();
break;
case "ignorable":
rule = IgnorableSpaceStrippingRule.getInstance();
break;
default:
throw new IllegalArgumentException(
"Unrecognized value STRIP_WHITESPACE = '" + value +
"': must be 'all', 'none', or 'ignorable'");
}
defaultParseOptions = defaultParseOptions.withSpaceStrippingRule(rule);
break;
}
case FeatureCode.STYLE_PARSER_CLASS:
setStyleParserClass(requireString(name, value));
break;
case FeatureCode.TIMING:
setTiming(requireBoolean(name, value));
break;
case FeatureCode.TRACE_LISTENER:
if (!(value instanceof TraceListener)) {
throw new IllegalArgumentException("TRACE_LISTENER is of wrong class");
}
setTraceListener((TraceListener) value);
break;
case FeatureCode.TRACE_LISTENER_CLASS:
setTraceListenerClass(requireString(name, value));
break;
case FeatureCode.TRACE_LISTENER_OUTPUT_FILE:
setTraceListenerOutputFile(requireString(name, value));
break;
case FeatureCode.TREE_MODEL:
setTreeModel(requireInteger(name, value));
break;
case FeatureCode.TREE_MODEL_NAME: {
String s = requireString(name, value);
switch (s) {
case "tinyTree":
setTreeModel(Builder.TINY_TREE);
break;
case "tinyTreeCondensed":
setTreeModel(Builder.TINY_TREE_CONDENSED);
break;
case "linkedTree":
setTreeModel(Builder.LINKED_TREE);
break;
case "jdom":
setTreeModel(Builder.JDOM_TREE);
break;
case "jdom2":
setTreeModel(Builder.JDOM2_TREE);
break;
default:
throw new IllegalArgumentException(
"Unrecognized value TREE_MODEL_NAME = '" + value +
"': must be linkedTree|tinyTree|tinyTreeCondensed");
}
break;
}
case FeatureCode.UNPARSED_TEXT_URI_RESOLVER:
setUnparsedTextURIResolver((UnparsedTextURIResolver) value);
break;
case FeatureCode.UNPARSED_TEXT_URI_RESOLVER_CLASS:
setUnparsedTextURIResolver(
(UnparsedTextURIResolver) instantiateClassName(name, value, UnparsedTextURIResolver.class));
break;
case FeatureCode.URI_RESOLVER_CLASS:
URIResolver u = (URIResolver) instantiateClassName(name, value, URIResolver.class);
setResourceResolver(new ResourceResolverWrappingURIResolver(u));
break;
case FeatureCode.USE_XSI_SCHEMA_LOCATION:
defaultParseOptions = defaultParseOptions.withUseXsiSchemaLocation(requireBoolean(name, value));
break;
case FeatureCode.VALIDATION_COMMENTS:
defaultParseOptions = defaultParseOptions.withAddCommentsAfterValidationErrors(requireBoolean(name, value));
break;
case FeatureCode.VALIDATION_WARNINGS:
setValidationWarnings(requireBoolean(name, value));
break;
case FeatureCode.VERSION_WARNING:
// no action
break;
case FeatureCode.XINCLUDE:
setXIncludeAware(requireBoolean(name, value));
break;
case FeatureCode.XPATH_VERSION_FOR_XSD: {
int val = requireInteger(name, value);
if (val != 20 && val != 30 && val != 31) {
throw new IllegalArgumentException("XPath version for XSD must be 20 (XPath 2.0), 30 (XPath 3.0), or 31 (XPath 3.1)");
}
xpathVersionForXsd = val;
break;
}
case FeatureCode.XPATH_VERSION_FOR_XSLT: {
int val = requireInteger(name, value);
if (val != 20 && val != 30 && val != 305 && val != 31 && val != 40) {
throw new IllegalArgumentException("XPath version for XSLT must be 20 (XPath 2.0), 30 (XPath 3.0), 31 (XPath 3.1), or 305 (XPath 3.0 with XSLT-defined extensions), or 40 (XPath 4.0 proposal)");
}
xpathVersionForXslt = val;
break;
}
case FeatureCode.XQUERY_ALLOW_UPDATE:
getDefaultStaticQueryContext().setUpdatingEnabled(requireBoolean(name, value));
break;
case FeatureCode.XQUERY_CONSTRUCTION_MODE:
getDefaultStaticQueryContext().setConstructionMode(Validation.getCode(value.toString()));
break;
case FeatureCode.XQUERY_DEFAULT_ELEMENT_NAMESPACE:
getDefaultStaticQueryContext().setDefaultElementNamespace(NamespaceUri.of(value.toString()));
break;
case FeatureCode.XQUERY_DEFAULT_FUNCTION_NAMESPACE:
getDefaultStaticQueryContext().setDefaultFunctionNamespace(NamespaceUri.of(value.toString()));
break;
case FeatureCode.XQUERY_EMPTY_LEAST:
getDefaultStaticQueryContext().setEmptyLeast(requireBoolean(name, value));
break;
case FeatureCode.XQUERY_INHERIT_NAMESPACES:
getDefaultStaticQueryContext().setInheritNamespaces(requireBoolean(name, value));
break;
case FeatureCode.XQUERY_PRESERVE_BOUNDARY_SPACE:
getDefaultStaticQueryContext().setPreserveBoundarySpace(requireBoolean(name, value));
break;
case FeatureCode.XQUERY_PRESERVE_NAMESPACES:
getDefaultStaticQueryContext().setPreserveNamespaces(requireBoolean(name, value));
break;
case FeatureCode.XQUERY_REQUIRED_CONTEXT_ITEM_TYPE:
XPathParser parser = new XPathParser();
IndependentContext env = new IndependentContext(this);
env.setXPathLanguageLevel(31);
try {
SequenceType type = parser.parseSequenceType(value.toString(), env);
if (type.getCardinality() != StaticProperty.EXACTLY_ONE) {
throw new IllegalArgumentException("Context item type must have no occurrence indicator");
}
getDefaultStaticQueryContext().setRequiredContextItemType(type.getPrimaryType());
} catch (XPathException err) {
throw new IllegalArgumentException(err);
}
break;
case FeatureCode.XQUERY_SCHEMA_AWARE:
getDefaultStaticQueryContext().setSchemaAware(requireBoolean(name, value));
break;
case FeatureCode.XQUERY_STATIC_ERROR_LISTENER_CLASS:
getDefaultStaticQueryContext().setErrorListener(
(ErrorListener) instantiateClassName(name, value, ErrorListener.class));
break;
case FeatureCode.XQUERY_VERSION: {
int qvn;
switch (value.toString()) {
case "3.1":
qvn = 31;
break;
case "4.0":
qvn = 40;
break;
default:
makeErrorReporter().report(
new XmlProcessingIncident("XQuery version ignored: only \"3.1\" and \"4.0\" are recognized",
SaxonErrorCode.SXWN9049).asWarning());
qvn = 40;
break;
}
getDefaultStaticQueryContext().setLanguageVersion(qvn);
break;
}
case FeatureCode.XML_VERSION:
String xv = requireString(name, value);
if (!(xv.equals("1.0") || xv.equals("1.1"))) {
throw new IllegalArgumentException(
"XML_VERSION value must be \"1.0\" or \"1.1\" as a String");
}
setXMLVersion(xv.equals("1.0") ? XML10 : XML11);
break;
case FeatureCode.XSD_VERSION: {
String xsdVn = requireString(name, value);
if (!(xsdVn.equals("1.0") || xsdVn.equals("1.1"))) {
throw new IllegalArgumentException(
"XSD_VERSION value must be \"1.0\" or \"1.1\" as a String");
}
xsdVersion = xsdVn.equals("1.0") ? XSD10 : XSD11;
theConversionRules = null;
break;
}
case FeatureCode.XSLT_ENABLE_ASSERTIONS:
getDefaultXsltCompilerInfo().setAssertionsEnabled(requireBoolean(name, value));
break;
case FeatureCode.XSLT_INITIAL_MODE: {
String s = requireString(name, value);
getDefaultXsltCompilerInfo().setDefaultInitialMode(StructuredQName.fromClarkName(s));
break;
}
case FeatureCode.XSLT_INITIAL_TEMPLATE: {
String s = requireString(name, value);
getDefaultXsltCompilerInfo().setDefaultInitialTemplate(StructuredQName.fromClarkName(s));
break;
}
case FeatureCode.XSLT_SCHEMA_AWARE:
getDefaultXsltCompilerInfo().setSchemaAware(requireBoolean(name, value));
break;
case FeatureCode.XSLT_STATIC_ERROR_LISTENER_CLASS:
getDefaultXsltCompilerInfo().setErrorListener(
(ErrorListener) instantiateClassName(name, value, ErrorListener.class));
break;
case FeatureCode.XSLT_STATIC_URI_RESOLVER_CLASS:
getDefaultXsltCompilerInfo().setURIResolver(
(URIResolver) instantiateClassName(name, value, URIResolver.class));
break;
case FeatureCode.XSLT_VERSION: {
int xsltVersion;
switch (value.toString()) {
case "3.0":
xsltVersion = 30;
break;
case "4.0":
xsltVersion = 40;
break;
default:
makeErrorReporter().report(
new XmlProcessingIncident("XSLT version ignored: only \"3.0\" and \"4.0\" are recognized",
SaxonErrorCode.SXWN9020).asWarning());
xsltVersion = 30;
break;
}
getDefaultXsltCompilerInfo().setXsltVersion(xsltVersion);
break;
}
case FeatureCode.RESOURCE_RESOLVER:
if (!(value instanceof ResourceResolver)) {
throw new IllegalArgumentException(
"RESOURCE_RESOLVER value must be an instance of net.sf.saxon.lib.ResourceResolver");
}
setResourceResolver((ResourceResolver) value);
break;
case FeatureCode.RESOURCE_RESOLVER_CLASS:
ResourceResolver rresolver = (ResourceResolver) instantiateClassName(name, value, ResourceResolver.class);
setResourceResolver(rresolver);
break;
default:
throw new IllegalArgumentException("Unknown configuration property " + name);
}
}
}
/**
* Validate a property value where the required type is boolean
*
* @param propertyName the name of the property
* @param value the supplied value of the property. This may be either a java.lang.Boolean, or a string
* taking one of the values on|off, true|false, yes|no, or 1|0 (suited to the conventions of different
* configuration APIs that end up calling this method)
* @return the value as a boolean
* @throws IllegalArgumentException if the supplied value cannot be validated as a recognized boolean value
*/
public static boolean requireBoolean(String propertyName, Object value) {
if (value instanceof Boolean) {
return (Boolean) value;
} else if (value instanceof String) {
value = ((String)value).trim();
if ("true".equals(value) || "on".equals(value) || "yes".equals(value) || "1".equals(value)) {
return true;
} else if ("false".equals(value) || "off".equals(value) || "no".equals(value) || "0".equals(value)) {
return false;
} else {
throw new IllegalArgumentException(propertyName + " must be 'true' or 'false' (or on|off, yes|no, 1|0)");
}
} else {
throw new IllegalArgumentException(propertyName + " must be a boolean (or a string representing a boolean)");
}
}
/**
* Validate a property value where the required type is integer
*
* @param propertyName the name of the property
* @param value the supplied value of the property. This may be either a java.lang.Integer, or a string
* that can be parsed as an integer (suited to the conventions of different
* configuration APIs that end up calling this method)
* @return the value as an integer
* @throws IllegalArgumentException if the supplied value cannot be validated as a recognized boolean value
*/
protected int requireInteger(String propertyName, Object value) {
if (value instanceof Integer) {
return (Integer) value;
} else if (value instanceof String) {
try {
return Integer.parseInt((String) value);
} catch (NumberFormatException nfe) {
throw new IllegalArgumentException(propertyName + " must be an integer");
}
} else {
throw new IllegalArgumentException(propertyName + " must be an integer (or a string representing an integer)");
}
}
/**
* Set a boolean property value, without checking that it is a recognized property name
*
* @param code the numeric code of the property to be set
* @param name the name of the property (used only for diagnostics)
* @param value a representation of the boolean value.
* This may be either a java.lang.Boolean, or a string
* taking one of the values on|off, true|false, yes|no, or 1|0 (suited to the conventions of different
* configuration APIs that end up calling this method)
*/
protected void internalSetBooleanProperty(int code, String name, Object value) {
boolean b = requireBoolean(name, value);
if (b) {
enabledProperties.add(code);
} else {
enabledProperties.remove(code);
}
}
/**
* Get a boolean property of the configuration
*
* @param feature the integer code of the required property. See the class {@link FeatureCode} for
* constants representing the property names that can be requested. This class only recognizes
* properties whose type is boolean.
* @return the value of the property. In the case of an unrecognized property name, the value returned is
* false: no error is thrown.
*/
public boolean getBooleanProperty(Feature feature) {
return enabledProperties.contains(feature.code);
}
/**
* Set a boolean property of the configuration
*
* @param propertyName the name of the required property. See the class {@link FeatureKeys} for
* constants representing the property names that can be requested. This class only recognizes
* properties whose type is boolean.
* @param value the value of the property.
* @throws IllegalArgumentException if the property name is not recognized (as a property whose expected
* value is boolean)
*/
public void setBooleanProperty(String propertyName, boolean value) {
setConfigurationProperty(propertyName, value);
}
/**
* Set a boolean property of the configuration
*
* @param feature the required property. See the class {@link Feature} for
* constants representing the property names that can be requested. This class only recognizes
* properties whose type is boolean.
* @param value the value of the property.
* @throws IllegalArgumentException if the feature is not recognized (as a feature whose expected
* value is boolean)
*/
public void setBooleanProperty(Feature feature, boolean value) {
setConfigurationProperty(feature, value);
}
protected String requireString(String propertyName, Object value) {
if (value instanceof String) {
return (String) value;
} else {
throw new IllegalArgumentException("The value of " + propertyName + " must be a string");
}
}
protected Object instantiateClassName(String propertyName, Object value, Class> requiredClass) {
if (!(value instanceof String)) {
throw new IllegalArgumentException(
propertyName + " must be a String");
}
try {
Object obj = getInstance((String) value);
if (!requiredClass.isAssignableFrom(obj.getClass())) {
throw new IllegalArgumentException("Error in " + propertyName +
": Class " + value + " does not implement " + requiredClass.getName());
}
return obj;
} catch (XPathException err) {
throw new IllegalArgumentException(
"Cannot use " + value + " as the value of " + propertyName + ". " + err.getMessage());
}
}
static {
booleanFeatures.add(FeatureCode.ALLOW_EXTERNAL_FUNCTIONS);
booleanFeatures.add(FeatureCode.ALLOW_MULTITHREADING);
booleanFeatures.add(FeatureCode.ALLOW_SYNTAX_EXTENSIONS);
booleanFeatures.add(FeatureCode.ASSERTIONS_CAN_SEE_COMMENTS);
booleanFeatures.add(FeatureCode.COMPILE_WITH_TRACING);
booleanFeatures.add(FeatureCode.DEBUG_BYTE_CODE);
booleanFeatures.add(FeatureCode.DISABLE_XSL_EVALUATE);
booleanFeatures.add(FeatureCode.DISPLAY_BYTE_CODE);
booleanFeatures.add(FeatureCode.DTD_VALIDATION);
booleanFeatures.add(FeatureCode.EAGER_EVALUATION);
booleanFeatures.add(FeatureCode.EXPAND_ATTRIBUTE_DEFAULTS);
booleanFeatures.add(FeatureCode.EXPATH_FILE_DELETE_TEMPORARY_FILES);
booleanFeatures.add(FeatureCode.GENERATE_BYTE_CODE);
booleanFeatures.add(FeatureCode.IGNORE_SAX_SOURCE_PARSER);
booleanFeatures.add(FeatureCode.IMPLICIT_SCHEMA_IMPORTS);
booleanFeatures.add(FeatureCode.MARK_DEFAULTED_ATTRIBUTES);
booleanFeatures.add(FeatureCode.MONITOR_HOT_SPOT_BYTE_CODE);
booleanFeatures.add(FeatureCode.MULTIPLE_SCHEMA_IMPORTS);
booleanFeatures.add(FeatureCode.PRE_EVALUATE_DOC_FUNCTION);
//booleanFeatures.add(FeatureCode.PREFER_JAXP_PARSER);
booleanFeatures.add(FeatureCode.RECOGNIZE_URI_QUERY_PARAMETERS);
booleanFeatures.add(FeatureCode.RETAIN_DTD_ATTRIBUTE_TYPES);
booleanFeatures.add(FeatureCode.STABLE_COLLECTION_URI);
booleanFeatures.add(FeatureCode.STABLE_UNPARSED_TEXT);
booleanFeatures.add(FeatureCode.STREAMING_FALLBACK);
booleanFeatures.add(FeatureCode.STRICT_STREAMABILITY);
booleanFeatures.add(FeatureCode.SUPPRESS_EVALUATION_EXPIRY_WARNING);
booleanFeatures.add(FeatureCode.SUPPRESS_XPATH_WARNINGS);
booleanFeatures.add(FeatureCode.SUPPRESS_XSLT_NAMESPACE_CHECK);
booleanFeatures.add(FeatureCode.TRACE_EXTERNAL_FUNCTIONS);
booleanFeatures.add(FeatureCode.TRACE_OPTIMIZER_DECISIONS);
booleanFeatures.add(FeatureCode.USE_PI_DISABLE_OUTPUT_ESCAPING);
booleanFeatures.add(FeatureCode.USE_TYPED_VALUE_CACHE);
booleanFeatures.add(FeatureCode.XQUERY_MULTIPLE_MODULE_IMPORTS);
booleanFeatures.add(FeatureCode.RETAIN_NODE_FOR_DIAGNOSTICS);
booleanFeatures.add(FeatureCode.ALLOW_UNRESOLVED_SCHEMA_COMPONENTS);
stringFeatures.add(FeatureCode.ZIP_URI_PATTERN);
}
/**
* Get a property of the configuration
*
* @param name the name of the required property. See the class {@link FeatureKeys} for
* constants representing the property names that can be requested.
* @return the value of the property. Note that boolean values are returned as a Boolean,
* even if the value was supplied as a string (for example "true" or "on").
* @throws IllegalArgumentException thrown if the property is not one that Saxon recognizes.
*/
/*@NotNull*/
public Object getConfigurationProperty(String name) {
if (FeatureIndex.exists(name)) {
return getFeature(FeatureIndex.getData(name));
} else {
throw new IllegalArgumentException("Unknown configuration property " + name);
}
}
/**
* Get a property of the configuration.
*
* This is the preferred way of getting configuration options from application
* code where it is known statically which feature is being set. Other methods are
* provided for use by wrapper APIs (such as the JAXP API) where the feature name
* is supplied dynamically as a string.
* @param the type of value returned for this particular feature
* @param feature the required property. See the class {@link Feature} for
* constants representing the properties that can be requested.
* @return the value of the property; the type of the result depends on the chosen
* {@link Feature}.
* @since 9.9
*/
/*@NotNull*/
@SuppressWarnings("unchecked")
public T getConfigurationProperty(Feature feature) {
FeatureData data = FeatureIndex.getData(feature.code);
return (T)getFeature(data);
}
protected Object getFeature(FeatureData feature) {
int code = feature.code;
if (booleanFeatures.contains(code)) {
return enabledProperties.contains(code);
}
if (stringFeatures.contains(code)) {
String value = stringProperties.get(code);
if (value == null) {
return feature.defaultValue;
} else {
return value;
}
}
switch (code) {
case FeatureCode.ALLOWED_PROTOCOLS:
return protocolRestrictor.toString();
case FeatureCode.COLLATION_URI_RESOLVER:
return getCollationURIResolver();
case FeatureCode.COLLATION_URI_RESOLVER_CLASS:
return getCollationURIResolver().getClass().getName();
case FeatureCode.CONFIGURATION:
return this;
case FeatureCode.DEFAULT_COLLATION:
return defaultCollationName;
case FeatureCode.DEFAULT_COLLECTION:
return getDefaultCollection();
case FeatureCode.DEFAULT_COUNTRY:
return getDefaultCountry();
case FeatureCode.DEFAULT_LANGUAGE:
return getDefaultLanguage();
case FeatureCode.DTD_VALIDATION:
return isValidation();
case FeatureCode.DTD_VALIDATION_RECOVERABLE:
return defaultParseOptions.getDTDValidationMode() == Validation.LAX;
case FeatureCode.ERROR_LISTENER_CLASS:
// Obsolete
return null;
case FeatureCode.ENTITY_RESOLVER_CLASS:
EntityResolver er = defaultParseOptions.getEntityResolver();
if (er == null) {
return "";
} else {
return er.getClass().getName();
}
case FeatureCode.ENVIRONMENT_VARIABLE_RESOLVER:
return environmentVariableResolver;
case FeatureCode.ENVIRONMENT_VARIABLE_RESOLVER_CLASS:
return environmentVariableResolver.getClass().getName();
case FeatureCode.EXPAND_ATTRIBUTE_DEFAULTS:
return isExpandAttributeDefaults();
case FeatureCode.LINE_NUMBERING:
return isLineNumbering();
case FeatureCode.MODULE_URI_RESOLVER:
return getModuleURIResolver();
case FeatureCode.MODULE_URI_RESOLVER_CLASS:
return getModuleURIResolver().getClass().getName();
case FeatureCode.NAME_POOL:
return getNamePool();
case FeatureCode.OPTIMIZATION_LEVEL:
return optimizerOptions.toString();
case FeatureCode.OUTPUT_URI_RESOLVER:
return getOutputURIResolver();
case FeatureCode.OUTPUT_URI_RESOLVER_CLASS:
return getOutputURIResolver().getClass().getName();
// //#if CSHARP==false
// case FeatureCode.RECOGNIZE_URI_QUERY_PARAMETERS:
// return getSystemURIResolver().queryParametersAreRecognized();
// //#endif
case FeatureCode.RECOVERY_POLICY:
return 0;
case FeatureCode.RECOVERY_POLICY_NAME:
return "recoverWithWarnings";
case FeatureCode.REGEX_BACKTRACKING_LIMIT:
return regexBacktrackingLimit;
case FeatureCode.SCHEMA_VALIDATION:
return getSchemaValidationMode();
case FeatureCode.SCHEMA_VALIDATION_MODE:
return Validation.describe(getSchemaValidationMode());
case FeatureCode.SERIALIZER_FACTORY_CLASS:
return getSerializerFactory().getClass().getName();
case FeatureCode.SOURCE_PARSER_CLASS:
return getSourceParserClass();
case FeatureCode.SOURCE_RESOLVER_CLASS:
return getSourceResolver().getClass().getName();
case FeatureCode.STRIP_WHITESPACE:
SpaceStrippingRule rule = getParseOptions().getSpaceStrippingRule();
if (rule == AllElementsSpaceStrippingRule.getInstance()) {
return "all";
} else if (rule == null || rule == IgnorableSpaceStrippingRule.getInstance()) {
return "ignorable";
} else {
return "none";
}
case FeatureCode.STYLE_PARSER_CLASS:
return getStyleParserClass();
case FeatureCode.TIMING:
return isTiming();
case FeatureCode.TRACE_LISTENER:
return traceListener;
case FeatureCode.TRACE_LISTENER_CLASS:
return traceListenerClass;
case FeatureCode.TRACE_LISTENER_OUTPUT_FILE:
return traceListenerOutput;
case FeatureCode.TREE_MODEL:
return getTreeModel();
case FeatureCode.TREE_MODEL_NAME:
switch (getTreeModel()) {
case Builder.TINY_TREE:
default:
return "tinyTree";
case Builder.TINY_TREE_CONDENSED:
return "tinyTreeCondensed";
case Builder.LINKED_TREE:
return "linkedTree";
}
case FeatureCode.UNPARSED_TEXT_URI_RESOLVER:
return getUnparsedTextURIResolver();
case FeatureCode.UNPARSED_TEXT_URI_RESOLVER_CLASS:
return getUnparsedTextURIResolver().getClass().getName();
case FeatureCode.URI_RESOLVER_CLASS:
if (getResourceResolver() instanceof ResourceResolverWrappingURIResolver) {
return ((ResourceResolverWrappingURIResolver) getResourceResolver()).getClass().getName();
} else {
return null;
}
case FeatureCode.USE_XSI_SCHEMA_LOCATION:
return defaultParseOptions.isUseXsiSchemaLocation();
case FeatureCode.VALIDATION_COMMENTS:
return defaultParseOptions.isAddCommentsAfterValidationErrors();
case FeatureCode.VALIDATION_WARNINGS:
return isValidationWarnings();
case FeatureCode.VERSION_WARNING:
return false;
case FeatureCode.XINCLUDE:
return isXIncludeAware();
case FeatureCode.XML_VERSION:
return getXMLVersion() == XML10 ? "1.0" : "1.1";
case FeatureCode.XQUERY_ALLOW_UPDATE:
return getDefaultStaticQueryContext().isUpdatingEnabled();
case FeatureCode.XQUERY_CONSTRUCTION_MODE:
return getDefaultStaticQueryContext().getConstructionMode();
case FeatureCode.XQUERY_DEFAULT_ELEMENT_NAMESPACE:
return getDefaultStaticQueryContext().getDefaultElementNamespace();
case FeatureCode.XQUERY_DEFAULT_FUNCTION_NAMESPACE:
return getDefaultStaticQueryContext().getDefaultFunctionNamespace();
case FeatureCode.XQUERY_EMPTY_LEAST:
return getDefaultStaticQueryContext().isEmptyLeast();
case FeatureCode.XQUERY_INHERIT_NAMESPACES:
return getDefaultStaticQueryContext().isInheritNamespaces();
case FeatureCode.XQUERY_PRESERVE_BOUNDARY_SPACE:
return getDefaultStaticQueryContext().isPreserveBoundarySpace();
case FeatureCode.XQUERY_PRESERVE_NAMESPACES:
return getDefaultStaticQueryContext().isPreserveNamespaces();
case FeatureCode.XQUERY_REQUIRED_CONTEXT_ITEM_TYPE:
return getDefaultStaticQueryContext().getRequiredContextItemType();
case FeatureCode.XQUERY_SCHEMA_AWARE:
return getDefaultStaticQueryContext().isSchemaAware();
case FeatureCode.XQUERY_STATIC_ERROR_LISTENER_CLASS:
return getDefaultStaticQueryContext().getErrorListener().getClass().getName();
case FeatureCode.XQUERY_VERSION:
return "3.1";
case FeatureCode.XPATH_VERSION_FOR_XSD:
return xpathVersionForXsd;
case FeatureCode.XPATH_VERSION_FOR_XSLT:
return xpathVersionForXslt;
case FeatureCode.XSD_VERSION:
return xsdVersion == XSD10 ? "1.0" : "1.1";
case FeatureCode.XSLT_ENABLE_ASSERTIONS:
return getDefaultXsltCompilerInfo().isAssertionsEnabled();
case FeatureCode.XSLT_INITIAL_MODE:
return getDefaultXsltCompilerInfo().getDefaultInitialMode().getClarkName();
case FeatureCode.XSLT_INITIAL_TEMPLATE:
return getDefaultXsltCompilerInfo().getDefaultInitialTemplate().getClarkName();
case FeatureCode.XSLT_SCHEMA_AWARE:
return getDefaultXsltCompilerInfo().isSchemaAware();
case FeatureCode.XSLT_STATIC_ERROR_LISTENER_CLASS:
return getDefaultXsltCompilerInfo().getErrorListener().getClass().getName();
case FeatureCode.XSLT_STATIC_URI_RESOLVER_CLASS:
return null; // TODO: drop this
case FeatureCode.XSLT_VERSION: {
int vn = getDefaultXsltCompilerInfo().getXsltVersion();
return vn == 40 ? "4.0" : "3.0";
}
case FeatureCode.RESOURCE_RESOLVER:
return getResourceResolver();
case FeatureCode.RESOURCE_RESOLVER_CLASS:
return getResourceResolver().getClass().getName();
}
throw new IllegalArgumentException("Unknown configuration property " );
}
/**
* Ask whether just-in-time compilation of XSLT template rules is in force
* @return true if just-in-time compilation is enabled (this is the default in Saxon-EE and
* not available in other configurations)
*/
public boolean isJITEnabled() {
return false;
}
/**
* Close any resources held by the Configuration. This implementation
* closes the Logger and/or trace output file if one has been allocated.
* It also makes the Cleaner unreachable, allowing the relevant thread to terminate.
*/
public void close() {
if (traceOutput != null) {
traceOutput.close();
}
cleaner = null;
}
/**
* Create a package loader, for reloading SEF files, appropriate to the Saxon edition being used
* @return a package loader
*/
public IPackageLoader makePackageLoader() {
return new PackageLoaderHE(this);
}
/**
* Register a report generator for reporting invalidities detected in the course
* of schema validation
* @return a report generator.
* @throws UnsupportedOperationException (always) in Saxon-HE
*/
public InvalidityReportGenerator createValidityReporter() {
throw new UnsupportedOperationException("Schema validation requires Saxon-EE");
}
/**
* This class contains constants representing features of the software that may or may
* not be licensed. (Note, this list is at a finer-grained level than the actual
* purchasing options.)
*/
public static class LicenseFeature {
public static final int SCHEMA_VALIDATION = 1;
public static final int ENTERPRISE_XSLT = 2;
public static final int ENTERPRISE_XQUERY = 4;
public static final int PROFESSIONAL_EDITION = 8;
}
/**
* Make a new Mode - this can be overridden in subclasses to produce optimized variants
*
* @param modeName the name of the mode
* @param compilerInfo information on the compiler, that can alter rule optimization
* @return an instantiated Mode
*/
public SimpleMode makeMode(StructuredQName modeName, CompilerInfo compilerInfo) {
return new SimpleMode(modeName);
}
/**
* Factory method to create a Template Rule
*
* @return a new TemplateRule appropriate to this Configuration
*/
public TemplateRule makeTemplateRule() {
return new TemplateRule();
}
/**
* Make a ThreadManager for asynchronous xsl:result-document instructions
*
* @return a new ThreadManager (or null in the case of Saxon-HE)
*/
public XPathContextMajor.ThreadManager makeThreadManager() {
return null;
}
/**
* Make an XSLT CompilerInfo object - can be overridden in a subclass to produce variants
* capable of optimization
*
* @return a new CompilerInfo object
*/
public CompilerInfo makeCompilerInfo() {
return new CompilerInfo(this);
}
/**
* Make a CompilerService object, to handle byte code generation, or null if byte code
* generation is not available
*
* @param hostLanguage eg Configuration.XSLT
* @return a CompilerService, or null
*/
public ICompilerService makeCompilerService(HostLanguage hostLanguage) {
return null;
}
/**
* Set a label for this configuration
*
* @param label the label to associate
*/
public void setLabel(String label) {
this.label = label;
}
/**
* Get the associated label for this configuration (typically, the value of the @label
* attribute in the configuration file)
*
* @return the associated label
*/
public String getLabel() {
return label;
}
public Cleaner getCleaner() {
if (cleaner == null) {
cleaner = Cleaner.create();
}
return cleaner;
}
}