net.sf.saxon.query.QueryModule Maven / Gradle / Ivy
Show all versions of Saxon-HE Show documentation
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Copyright (c) 2018-2022 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.query;
import net.sf.saxon.Configuration;
import net.sf.saxon.expr.*;
import net.sf.saxon.expr.instruct.*;
import net.sf.saxon.expr.parser.*;
import net.sf.saxon.functions.FunctionLibrary;
import net.sf.saxon.functions.FunctionLibraryList;
import net.sf.saxon.functions.ResolveURI;
import net.sf.saxon.functions.registry.BuiltInFunctionSet;
import net.sf.saxon.functions.registry.ConstructorFunctionLibrary;
import net.sf.saxon.lib.NamespaceConstant;
import net.sf.saxon.lib.Validation;
import net.sf.saxon.om.*;
import net.sf.saxon.s9api.HostLanguage;
import net.sf.saxon.s9api.Location;
import net.sf.saxon.s9api.XmlProcessingError;
import net.sf.saxon.trace.ExpressionPresenter;
import net.sf.saxon.trace.XQueryTraceCodeInjector;
import net.sf.saxon.trans.*;
import net.sf.saxon.transpile.CSharpSuppressWarnings;
import net.sf.saxon.tree.util.IndexedStack;
import net.sf.saxon.type.AnyItemType;
import net.sf.saxon.type.ItemType;
import net.sf.saxon.type.SchemaType;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.*;
/**
* This class represents a query module, and includes information about the static context of the query module.
* The class is intended for internal Saxon use. User settings that affect the static context are made in the
* StaticQueryContext object, and those settings are copied to each QueryModule when the query module is compiled.
*/
public class QueryModule implements StaticContext {
private boolean moduleIsMainModule;
private final Configuration config;
private StaticQueryContext userQueryContext;
private final QueryModule topModule;
private URI locationURI;
private String baseURI;
private String moduleNamespace; // null only if moduleIsMainModule is false
private HashMap explicitPrologNamespaces;
private IndexedStack activeNamespaces; // The namespace bindings declared in element constructors
private HashMap variables;
// global variables declared in this module
private HashMap libraryVariables;
// all global variables defined in library modules
// defined only on the top-level module
private HashMap undeclaredVariables;
private HashSet importedSchemata; // The schema target namespaces imported into this module
private HashMap> loadedSchemata;
// For the top-level module only, all imported schemas for all modules,
// Key is the targetNamespace, value is the set of absolutized location URIs
private Executable executable;
private List importers; // A list of QueryModule objects representing the modules that import this one,
// Null for the main module
// This is needed *only* to implement the rules banning cyclic imports
private FunctionLibraryList functionLibraryList;
private XQueryFunctionLibrary globalFunctionLibrary; // used only on a top-level module
private int localFunctionLibraryNr;
private int importedFunctionLibraryNr;
private int unboundFunctionLibraryNr;
private Set importedModuleNamespaces;
private boolean inheritNamespaces = true;
private boolean preserveNamespaces = true;
private int constructionMode = Validation.PRESERVE;
private String defaultFunctionNamespace;
private String defaultElementNamespace;
private boolean preserveSpace = false;
private boolean defaultEmptyLeast = true;
private String defaultCollationName;
private int revalidationMode = Validation.SKIP;
private boolean updating = false;
private ItemType requiredContextItemType = AnyItemType.getInstance(); // must be the same for all modules
private DecimalFormatManager decimalFormatManager = null; // used only in XQuery 3.0
private CodeInjector codeInjector;
private PackageData packageData;
private RetainedStaticContext moduleStaticContext = null;
private Location moduleLocation;
private OptimizerOptions optimizerOptions;
private int languageLevel;
/**
* Create a QueryModule for a main module, copying the data that has been set up in a
* StaticQueryContext object
*
* @param sqc the StaticQueryContext object from which this module is initialized
* @throws XPathException if information supplied is invalid
*/
public QueryModule(/*@NotNull*/ StaticQueryContext sqc) throws XPathException {
config = sqc.getConfiguration();
moduleIsMainModule = true;
topModule = this;
languageLevel = sqc.getLanguageVersion();
activeNamespaces = new IndexedStack<>();
baseURI = sqc.getBaseURI();
defaultCollationName = sqc.getDefaultCollationName();
try {
locationURI = baseURI == null ? null : new URI(baseURI);
} catch (URISyntaxException err) {
throw new XPathException("Invalid location URI: " + baseURI);
}
executable = sqc.makeExecutable();
importers = null;
init(sqc);
PackageData pd = new PackageData(config);
pd.setHostLanguage(HostLanguage.XQUERY, getXPathVersion());
pd.setSchemaAware(isSchemaAware());
packageData = pd;
for (GlobalVariable var : sqc.iterateDeclaredGlobalVariables()) {
declareVariable(var);
pd.addGlobalVariable(var);
var.setPackageData(pd);
}
executable.setTopLevelPackage(pd);
executable.addPackage(pd);
if (sqc.getModuleLocation() == null) {
moduleLocation = new Loc(sqc.getSystemId(), 1, -1);
} else {
moduleLocation = sqc.getModuleLocation();
}
optimizerOptions = sqc.getOptimizerOptions();
}
/**
* Create a QueryModule for a library module.
*
* @param config the Saxon configuration
* @param importer the module that imported this module. This may be null, in the case where
* the library module is being imported into an XSLT stylesheet
*/
public QueryModule(Configuration config, /*@Nullable*/ QueryModule importer) {
this.config = config;
importers = null;
if (importer == null) {
topModule = this;
} else {
topModule = importer.topModule;
userQueryContext = importer.userQueryContext;
importers = new ArrayList<>(2);
importers.add(importer);
}
init(userQueryContext);
packageData = importer.getPackageData();
activeNamespaces = new IndexedStack<>();
executable = null;
optimizerOptions = importer.optimizerOptions;
}
/**
* Initialize data from a user-supplied StaticQueryContext object
*
* @param sqc the user-supplied StaticQueryContext. Null if this is a library module imported
* into XSLT.
*/
private void init(/*@Nullable*/ StaticQueryContext sqc) {
//reset();
userQueryContext = sqc;
variables = new HashMap<>(10);
undeclaredVariables = new HashMap<>(5);
if (isTopLevelModule()) {
libraryVariables = new HashMap<>(10);
}
importedSchemata = new HashSet<>(5);
//importedSchemata.add(NamespaceConstant.JSON);
importedModuleNamespaces = new HashSet<>(5);
moduleNamespace = null;
activeNamespaces = new IndexedStack<>();
explicitPrologNamespaces = new HashMap<>(10);
if (sqc != null) {
//executable = sqc.getExecutable();
inheritNamespaces = sqc.isInheritNamespaces();
preserveNamespaces = sqc.isPreserveNamespaces();
preserveSpace = sqc.isPreserveBoundarySpace();
defaultEmptyLeast = sqc.isEmptyLeast();
defaultFunctionNamespace = sqc.getDefaultFunctionNamespace();
defaultElementNamespace = sqc.getDefaultElementNamespace();
defaultCollationName = sqc.getDefaultCollationName();
constructionMode = sqc.getConstructionMode();
if (constructionMode == Validation.PRESERVE && !sqc.isSchemaAware()) {
// if not schema-aware, generate untyped output by default
constructionMode = Validation.STRIP;
}
requiredContextItemType = sqc.getRequiredContextItemType();
updating = sqc.isUpdatingEnabled();
codeInjector = sqc.getCodeInjector();
optimizerOptions = sqc.getOptimizerOptions();
}
initializeFunctionLibraries(sqc);
}
/**
* Supporting method to load an imported library module.
* Used also by saxon:import-query in XSLT.
* This method is intended for internal use only.
*
* @param baseURI The base URI and location URI of the module. Must not be null.
* @param executable The Executable
* @param importer The importing query module (used to check for cycles). This is null
* when loading a query module from XSLT.
* @param query The text of the query, after decoding and normalizing line endings
* @param namespaceURI namespace of the query module to be loaded
* @return The StaticQueryContext representing the loaded query module
* @throws XPathException if an error occurs
*/
/*@NotNull*/
public static QueryModule makeQueryModule(
String baseURI, /*@NotNull*/ Executable executable, /*@NotNull*/ QueryModule importer,
String query, String namespaceURI) throws XPathException {
Objects.requireNonNull(baseURI, "Base URI of XQuery module must not be null");
Configuration config = executable.getConfiguration();
QueryModule module = new QueryModule(config, importer);
try {
module.setLocationURI(new URI(baseURI));
} catch (URISyntaxException e) {
throw new XPathException("Invalid location URI " + baseURI, e);
}
module.setBaseURI(baseURI);
module.setExecutable(executable);
module.setModuleNamespace(namespaceURI);
executable.addQueryLibraryModule(module);
XQueryParser qp = (XQueryParser) config.newExpressionParser(
"XQ", importer.isUpdating(), 31);
if (importer.getCodeInjector() != null) {
qp.setCodeInjector(importer.getCodeInjector());
} else if (config.isCompileWithTracing()) {
qp.setCodeInjector(new XQueryTraceCodeInjector());
}
QNameParser qnp = new QNameParser(module.getLiveNamespaceResolver())
.withAcceptEQName(importer.getXPathVersion() >= 30)
.withUnescaper(new XQueryParser.Unescaper(config.getValidCharacterChecker()));
qp.setQNameParser(qnp);
qp.parseLibraryModule(query, module);
String namespace = module.getModuleNamespace();
if (namespace == null) {
XPathException err = new XPathException("Imported module must be a library module");
err.setErrorCode("XQST0059");
err.setIsStaticError(true);
throw err;
}
if (!namespace.equals(namespaceURI)) {
XPathException err = new XPathException("Imported module's namespace does not match requested namespace");
err.setErrorCode("XQST0059");
err.setIsStaticError(true);
throw err;
}
return module;
}
/**
* Reset function libraries
*
* @param sqc The static query context set up by the caller
*/
private void initializeFunctionLibraries(/*@Nullable*/ StaticQueryContext sqc) {
Configuration config = getConfiguration();
if (isTopLevelModule()) {
globalFunctionLibrary = new XQueryFunctionLibrary(config);
}
functionLibraryList = new FunctionLibraryList();
functionLibraryList.addFunctionLibrary(getBuiltInFunctionSet());
functionLibraryList.addFunctionLibrary(config.getBuiltInExtensionLibraryList());
functionLibraryList.addFunctionLibrary(new ConstructorFunctionLibrary(config));
localFunctionLibraryNr = functionLibraryList.addFunctionLibrary(
new XQueryFunctionLibrary(config));
importedFunctionLibraryNr = functionLibraryList.addFunctionLibrary(
new ImportedFunctionLibrary(this, getTopLevelModule().getGlobalFunctionLibrary()));
if (sqc != null && sqc.getExtensionFunctionLibrary() != null) {
functionLibraryList.addFunctionLibrary(sqc.getExtensionFunctionLibrary());
}
functionLibraryList.addFunctionLibrary(config.getIntegratedFunctionLibrary());
config.addExtensionBinders(functionLibraryList);
unboundFunctionLibraryNr = functionLibraryList.addFunctionLibrary(
new UnboundFunctionLibrary());
}
public BuiltInFunctionSet getBuiltInFunctionSet() {
if (isUpdating()) {
return config.getXQueryUpdateFunctionSet();
} else if (languageLevel == 40) {
return config.getXPath40FunctionSet();
} else {
return config.getXPath31FunctionSet();
}
}
/**
* Get the Saxon Configuration
*
* @return the Saxon Configuration
*/
@Override
public Configuration getConfiguration() {
return config;
}
/**
* Get package data. This is a small data object containing information about the unit
* of compilation, which in the case of XQuery is a query module
*
* @return data about this query module
*/
@Override
public PackageData getPackageData() {
return packageData;
}
/**
* Set the package data. This method is used when we want the QueryModule to share the same
* package data as another module: notably when fn:load-query-module creates a "dummy" main
* module to go with the dynamic library module
* @param packageData the package information
*/
public void setPackageData(PackageData packageData) {
this.packageData = packageData;
}
/**
* Test whether this is a "top-level" module. This is true for a main module and also for a
* module directly imported into an XSLT stylesheet. It may also be true in future for independently-compiled
* modules
*
* @return true if this is top-level module
*/
public boolean isTopLevelModule() {
return this == topModule;
}
/**
* Set whether this is a "Main" module, in the sense of the XQuery language specification
*
* @param main true if this is a main module, false if it is a library module
*/
public void setIsMainModule(boolean main) {
moduleIsMainModule = main;
}
/**
* Ask whether this is a "main" module, in the sense of the XQuery language specification
*
* @return true if this is a main module, false if it is a library model
*/
public boolean isMainModule() {
return moduleIsMainModule;
}
/**
* Check whether this module is allowed to import a module with namespace N. Note that before
* calling this we have already handled the exception case where a module imports another in the same
* namespace (this is the only case where cycles are allowed, though as a late change to the spec they
* are no longer useful, since they cannot depend on each other cyclically)
*
* @param namespace the namespace to be tested
* @return true if the import is permitted
*/
public boolean mayImportModule(/*@NotNull*/ String namespace) {
if (namespace.equals(moduleNamespace)) {
return false;
}
if (importers == null) {
return true;
}
for (QueryModule importer : importers) {
if (!importer.mayImportModule(namespace)) {
return false;
}
}
return true;
}
/**
* Ask whether expressions compiled under this static context are schema-aware.
* They must be schema-aware if the expression is to handle typed (validated) nodes
*
* @return true if expressions are schema-aware
*/
public boolean isSchemaAware() {
return executable.isSchemaAware();
}
/**
* Get the optimization options in use. By default these are taken from the
* {@link Configuration}
*
* @return the optimization options in use
*/
@Override
public OptimizerOptions getOptimizerOptions() {
return optimizerOptions;
}
/**
* Construct a RetainedStaticContext, which extracts information from this StaticContext
* to provide the subset of static context information that is potentially needed
* during expression evaluation
*
* @return a RetainedStaticContext object: either a newly created one, or one that is
* reused from a previous invocation.
*/
@Override
public RetainedStaticContext makeRetainedStaticContext() {
// The only part of the RetainedStaticContext that can change as the query module is parsed is the
// "activeNamespaces", that is, namespaces declared on direct element constructors. If this is empty,
// we can reuse the top-level static context on each request.
if (activeNamespaces.isEmpty()) {
if (moduleStaticContext == null) {
moduleStaticContext = new RetainedStaticContext(this);
}
return moduleStaticContext;
} else {
return new RetainedStaticContext(this);
}
}
/**
* Set the namespace inheritance mode
*
* @param inherit true if namespaces are inherited, false if not
* @since 8.4
*/
public void setInheritNamespaces(boolean inherit) {
inheritNamespaces = inherit;
}
/**
* Get the namespace inheritance mode
*
* @return true if namespaces are inherited, false if not
* @since 8.4
*/
public boolean isInheritNamespaces() {
return inheritNamespaces;
}
/**
* Set the namespace copy mode
*
* @param inherit true if namespaces are preserved, false if not
*/
public void setPreserveNamespaces(boolean inherit) {
preserveNamespaces = inherit;
}
/**
* Get the namespace copy mode
*
* @return true if namespaces are preserved, false if not
*/
public boolean isPreserveNamespaces() {
return preserveNamespaces;
}
/**
* Set the construction mode for this module
*
* @param mode one of {@link net.sf.saxon.lib.Validation#STRIP}, {@link net.sf.saxon.lib.Validation#PRESERVE}
*/
public void setConstructionMode(int mode) {
constructionMode = mode;
}
/**
* Get the current construction mode
*
* @return one of {@link net.sf.saxon.lib.Validation#STRIP}, {@link net.sf.saxon.lib.Validation#PRESERVE}
*/
public int getConstructionMode() {
return constructionMode;
}
/**
* Set the policy for preserving boundary space
*
* @param preserve true if boundary space is to be preserved, false if it is to be stripped
*/
public void setPreserveBoundarySpace(boolean preserve) {
preserveSpace = preserve;
}
/**
* Ask whether the policy for boundary space is "preserve" or "strip"
*
* @return true if the policy is to preserve boundary space, false if it is to strip it
*/
public boolean isPreserveBoundarySpace() {
return preserveSpace;
}
/**
* Set the option for where an empty sequence appears in the collation order, if not otherwise
* specified in the "order by" clause
*
* @param least true if the empty sequence is considered less than any other value (the default),
* false if it is considered greater than any other value
*/
public void setEmptyLeast(boolean least) {
defaultEmptyLeast = least;
}
/**
* Ask what is the option for where an empty sequence appears in the collation order, if not otherwise
* specified in the "order by" clause
*
* @return true if the empty sequence is considered less than any other value (the default),
* false if it is considered greater than any other value
*/
public boolean isEmptyLeast() {
return defaultEmptyLeast;
}
/**
* Get the function library object that holds details of global functions
*
* @return the library of global functions
*/
public XQueryFunctionLibrary getGlobalFunctionLibrary() {
return globalFunctionLibrary;
}
/**
* Get the function library object that holds details of imported functions
*
* @return the library of imported functions
*/
/*@NotNull*/
public ImportedFunctionLibrary getImportedFunctionLibrary() {
return (ImportedFunctionLibrary) functionLibraryList.get(importedFunctionLibraryNr);
}
/**
* Register that this module imports a particular module namespace
* This method is intended for internal use.
*
* @param uri the URI of the imported namespace.
*/
public void addImportedNamespace(String uri) {
if (importedModuleNamespaces == null) {
importedModuleNamespaces = new HashSet<>(5);
}
importedModuleNamespaces.add(uri);
getImportedFunctionLibrary().addImportedNamespace(uri);
}
/**
* Ask whether this module directly imports a particular namespace
* This method is intended for internal use.
*
* @param uri the URI of the possibly-imported namespace.
* @return true if the schema for the namespace has been imported
*/
public boolean importsNamespace(String uri) {
return importedModuleNamespaces != null &&
importedModuleNamespaces.contains(uri);
}
/**
* Get the QueryModule for the top-level module. This will normally be a main module,
* but in the case of saxon:import-query it will be the library module that is imported into
* the stylesheet
*
* @return the StaticQueryContext object associated with the top level module
*/
public QueryModule getTopLevelModule() {
return topModule;
}
/**
* Get the Executable, an object representing the compiled query and its environment.
* This method is intended for internal use only.
*
* @return the Executable
*/
/*@Nullable*/
public Executable getExecutable() {
return executable;
}
/**
* Set the executable.
* This method is intended for internal use only.
*
* @param executable the Executable
*/
public void setExecutable(Executable executable) {
this.executable = executable;
// if (!executable.isSchemaAware()) {
// constructionMode = Validation.STRIP;
// }
}
/**
* Get the StaticQueryContext object containing options set up by the user
*
* @return the user-created StaticQueryContext object
*/
/*@Nullable*/
public StaticQueryContext getUserQueryContext() {
return userQueryContext;
}
/**
* Get the LocationMap, an data structure used to identify the location of compiled expressions within
* the query source text.
* This method is intended for internal use only.
*
* @return the LocationMap
*/
@Override
public Location getContainingLocation() {
return moduleLocation;
}
/**
* Set the namespace for a library module.
* This method is for internal use only.
*
* @param uri the module namespace URI of the library module. Null is allowed only
* for a main module, not for a library module.
*/
public void setModuleNamespace(/*@Nullable*/ String uri) {
moduleNamespace = uri;
}
/**
* Get the namespace of the current library module.
* This method is intended primarily for internal use.
*
* @return the module namespace, or null if this is a main module
*/
/*@Nullable*/
public String getModuleNamespace() {
return moduleNamespace;
}
/**
* Set the location URI for a module
*
* @param uri the location URI
*/
public void setLocationURI(URI uri) {
locationURI = uri;
moduleLocation = new Loc(locationURI.toString(), 1, -1);
}
/**
* Get the location URI for a module
*
* @return the location URI
*/
/*@Nullable*/
public URI getLocationURI() {
return locationURI;
}
/**
* Get the System ID for a module
*
* @return the location URI
*/
/*@Nullable*/
@Override
public String getSystemId() {
return locationURI == null ? null : locationURI.toString();
}
/**
* Set the base URI for a module
*
* @param uri the base URI
*/
public void setBaseURI(String uri) {
baseURI = uri;
}
/**
* Get the base URI for a module
*
* @return the base URI
*/
@Override
public String getStaticBaseURI() {
return baseURI;
}
/**
* Get the stack frame map for global variables.
* This method is intended for internal use.
*
* @return the stack frame map (a SlotManager) for global variables.
*/
public SlotManager getGlobalStackFrameMap() {
return getPackageData().getGlobalSlotManager();
}
/**
* Declare a global variable. A variable must normally be declared before an expression referring
* to it is compiled, but there are exceptions where a set of modules in the same namespace
* import each other cyclically. Global variables are normally declared in the Query Prolog, but
* they can also be predeclared using the Java API. All global variables are held in the QueryModule
* for the main module. The fact that a global variable is present therefore does not mean that it
* is visible: there are two additional conditions (a) the module namespace must be imported into the
* module where the reference appears, and (b) the declaration must not be in the same module and textually
* after the reference.
* Note that the same VariableDeclaration object cannot be used with more than one query. This is because
* the VariableDeclaration is modified internally to hold a list of references to all the places where
* the variable is used.
*
* @param var the Variable declaration being declared
* @throws XPathException if a static error is detected
*/
public void declareVariable(/*@NotNull*/ GlobalVariable var) throws XPathException {
StructuredQName key = var.getVariableQName();
if (variables.get(key) != null) {
GlobalVariable oldVar = variables.get(key);
if (oldVar == var || oldVar.getUltimateOriginalVariable() == var.getUltimateOriginalVariable()) {
// do nothing
} else {
String oldloc = " (see line " + oldVar.getLineNumber();
String oldSysId = oldVar.getSystemId();
if (oldSysId != null &&
!oldSysId.equals(var.getSystemId())) {
oldloc += " in module " + oldVar.getSystemId();
}
oldloc += ")";
XPathException err = new XPathException("Duplicate definition of global variable "
+ var.getVariableQName().getDisplayName()
+ oldloc);
err.setErrorCode("XQST0049");
err.setIsStaticError(true);
err.setLocation(var);
throw err;
}
}
variables.put(key, var);
getPackageData().addGlobalVariable(var);
final HashMap libVars = getTopLevelModule().libraryVariables;
GlobalVariable old = libVars.get(key);
if (old == null || old == var) {
// do nothing
} else {
XPathException err = new XPathException("Duplicate definition of global variable "
+ var.getVariableQName().getDisplayName()
+ " (see line " + old.getLineNumber() + " in module " + old.getSystemId() + ')');
err.setErrorCode("XQST0049");
err.setIsStaticError(true);
err.setLocation(var);
throw err;
}
if (!isMainModule()) {
libVars.put(key, var);
}
}
/**
* Get all global variables imported into this module
*
* @return a collection of global variables.
* In the case of a main module, this includes only variables imported into this module,
* it does not include variables declared within this module. In the case of a library module,
* it includes both locally declared and imported variables. Blame history.
*/
public Iterable getImportedGlobalVariables() {
return libraryVariables.values();
}
/**
* Get all global variables declared anywhere in the query
*
* @return a collection of global variables.
*/
public Iterable getAllGlobalVariables() {
if (isMainModule()) {
List allVars = new ArrayList<>(libraryVariables.values());
allVars.addAll(variables.values());
return allVars;
} else {
return getTopLevelModule().getAllGlobalVariables();
}
}
/**
* Fixup all references to global variables.
* This method is for internal use by the Query Parser only.
*
* @param globalVariableMap a SlotManager that holds details of the assignment of slots to global variables.
* @return a list containing the global variable definitions.
* @throws XPathException if compiling a global variable definition fails
*/
@CSharpSuppressWarnings("UnsafeIteratorConversion")
public List fixupGlobalVariables(SlotManager globalVariableMap) throws XPathException {
List varDefinitions = new ArrayList<>(20);
List> iters = new ArrayList<>();
iters.add(variables.values());
iters.add(libraryVariables.values());
for (Iterable iter : iters) {
for (GlobalVariable var : iter) {
if (!varDefinitions.contains(var)) {
int slot = globalVariableMap.allocateSlotNumber(var.getVariableQName());
var.compile(getExecutable(), slot);
varDefinitions.add(var);
}
}
}
return varDefinitions;
}
/**
* Get global variables declared in this module
*
* @return an Iterator whose items are GlobalVariable objects
*/
public Iterator getModuleVariables() {
return variables.values().iterator();
}
/**
* Check for circular definitions of global variables.
* This method is intended for internal use
*
* @param compiledVars a list of {@link GlobalVariable} objects to be checked
* @param globalFunctionLibrary the library of global functions
* @throws net.sf.saxon.trans.XPathException
* if a circularity is found
*/
public void checkForCircularities(/*@NotNull*/ List compiledVars, /*@NotNull*/ XQueryFunctionLibrary globalFunctionLibrary) throws XPathException {
Iterator iter = compiledVars.iterator();
IndexedStack