net.sf.saxon.query.QueryModule Maven / Gradle / Ivy
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Copyright (c) 2015 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.*;
import net.sf.saxon.lib.NamespaceConstant;
import net.sf.saxon.lib.Validation;
import net.sf.saxon.om.*;
import net.sf.saxon.trace.ExpressionPresenter;
import net.sf.saxon.trace.TraceCodeInjector;
import net.sf.saxon.trans.DecimalFormatManager;
import net.sf.saxon.trans.KeyManager;
import net.sf.saxon.trans.XPathException;
import net.sf.saxon.type.*;
import net.sf.saxon.value.SequenceType;
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 isMainModule;
private Configuration config;
private StaticQueryContext userQueryContext;
private QueryModule topModule;
private URI locationURI;
private String baseURI;
private String moduleNamespace; // null only if isMainModule is false
private HashMap explicitPrologNamespaces;
private Stack activeNamespaces;
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 isUpdating = false;
private int languageVersion = 10; // The decimal version times ten
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;
/**
* 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();
isMainModule = true;
topModule = this;
activeNamespaces = new Stack();
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.setXPathVersion(getXPathVersion());
pd.setHostLanguage(Configuration.XQUERY);
pd.setSchemaAware(isSchemaAware());
packageData = pd;
for (Iterator vars = sqc.iterateDeclaredGlobalVariables(); vars.hasNext(); ) {
GlobalVariable var = vars.next();
declareVariable(var);
pd.addGlobalVariable(var);
var.setPackageData(pd);
}
executable.setTopLevelPackage(pd);
executable.addPackage(pd);
if (sqc.getModuleLocation() == null) {
moduleLocation = new ExplicitLocation(sqc.getSystemId(), 1, -1);
} else {
moduleLocation = sqc.getModuleLocation();
}
}
/**
* 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 Stack();
executable = null;
}
/**
* 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 Stack();
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();
isUpdating = sqc.isUpdatingEnabled();
languageVersion = sqc.getLanguageVersion();
codeInjector = sqc.getCodeInjector();
//allowTypedNodes = sqc.isAllowTypedNodes();
}
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
* @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
* @param allowCycles True if cycles of module imports (disallowed by the spec) are to be permitted
* @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, boolean allowCycles) throws XPathException {
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(), importer.getLanguageVersion());
if (importer.getCodeInjector() != null) {
qp.setCodeInjector(importer.getCodeInjector());
} else if (config.isCompileWithTracing()) {
qp.setCodeInjector(new TraceCodeInjector());
}
qp.setDisableCycleChecks(allowCycles);
QNameParser qnp = new QNameParser(module.getLiveNamespaceResolver());
qnp.setAcceptEQName(importer.getXPathVersion() >= 30);
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);
}
int functionSet = StandardFunction.CORE;
if (isUpdating()) {
functionSet |= StandardFunction.XQUPDATE;
}
if (getLanguageVersion() >= 30) {
functionSet |= StandardFunction.XPATH30;
}
if (getLanguageVersion() >= 31) {
functionSet |= StandardFunction.XPATH31;
}
functionLibraryList = new FunctionLibraryList();
functionLibraryList.addFunctionLibrary(
SystemFunctionLibrary.getSystemFunctionLibrary(functionSet, config));
functionLibraryList.addFunctionLibrary(config.getVendorFunctionLibrary());
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());
}
/**
* Get the Saxon Configuration
*
* @return the Saxon Configuration
*/
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
*/
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) {
isMainModule = 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 isMainModule;
}
/**
* 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();
}
/**
* 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.
*/
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.empty()) {
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
*/
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 ExplicitLocation(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*/
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
*/
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 old = variables.get(key);
if (old == var || old.getUltimateOriginalVariable() == var.getUltimateOriginalVariable()) {
// do nothing
} else {
String oldloc = " (see line " + old.getLineNumber();
String oldSysId = old.getSystemId();
if (oldSysId != null &&
!oldSysId.equals(var.getSystemId())) {
oldloc += " in module " + old.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 declared in or imported into this module
*
* @return a collection of global variables
*/
public Iterable getGlobalVariables() {
return libraryVariables.values();
}
/**
* 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
*/
public List fixupGlobalVariables(SlotManager globalVariableMap) throws XPathException {
List varDefinitions = new ArrayList(20);
List> iters = new ArrayList>();
iters.add(variables.values().iterator());
iters.add(libraryVariables.values().iterator());
for (Iterator iter : iters) {
while (iter.hasNext()) {
GlobalVariable var = iter.next();
if (!varDefinitions.contains(var)) {
int slot = globalVariableMap.allocateSlotNumber(var.getVariableQName());
var.compile(getExecutable(), slot);
varDefinitions.add(var);
}
}
}
return varDefinitions;
}
/**
* Look for module cycles. This is a restriction introduced in the PR specification because of
* difficulties in defining the formal semantics.
*
* [Definition: A module M1 directly depends on another module M2 (different from M1) if a
* variable or function declared in M1 depends on a variable or function declared in M2.]
* It is a static error [err:XQST0093] to import a module M1 if there exists a sequence
* of modules M1 ... Mi ... M1 such that each module directly depends on the next module
* in the sequence (informally, if M1 depends on itself through some chain of module dependencies.)
*
* @param referees a Stack containing the chain of module import references leading to this
* module
* @param lineNumber used for diagnostics
* @throws net.sf.saxon.trans.XPathException
* if cycles are found
*/
public void lookForModuleCycles(/*@NotNull*/ Stack referees, int lineNumber) throws XPathException {
if (referees.contains(this)) {
int s = referees.indexOf(this);
referees.push(this);
String message = "Circular dependency between modules. ";
for (int i = s; i < referees.size() - 1; i++) {
QueryModule next = referees.get(i + 1);
if (i == s) {
message += "Module " + getSystemId() + " references module " + next.getSystemId();
} else {
message += ", which references module " + next.getSystemId();
}
}
message += '.';
XPathException err = new XPathException(message);
err.setErrorCode("XQST0093");
err.setIsStaticError(true);
ExplicitLocation loc = new ExplicitLocation(getSystemId(), lineNumber, -1);
err.setLocator(loc);
throw err;
} else {
referees.push(this);
Iterator viter = getModuleVariables();
while (viter.hasNext()) {
GlobalVariable gv = viter.next();
//GlobalVariable gvc = gv.getCompiledVariable(); // will be null if the global variable is unreferenced
Expression select = gv.getSelectExpression();
if (select != null) {
List list = new ArrayList(10);
ExpressionTool.gatherReferencedVariables(select, list);
for (Binding b : list) {
if (b instanceof GlobalVariable) {
String uri = ((GlobalVariable) b).getSystemId();
StructuredQName qName = b.getVariableQName();
boolean synthetic = qName.hasURI(NamespaceConstant.SAXON_GENERATED_VARIABLE);
if (!synthetic && uri != null && !uri.equals(getSystemId())) {
QueryModule sqc = executable.getQueryModuleWithSystemId(uri, topModule);
if (sqc != null) {
sqc.lookForModuleCycles(referees, ((GlobalVariable) b).getLineNumber());
}
}
}
}
List fList = new ArrayList(5);
ExpressionTool.gatherCalledFunctions(select, fList);
for (UserFunction f : fList) {
String uri = f.getSystemId();
if (uri != null && !uri.equals(getSystemId())) {
QueryModule sqc = executable.getQueryModuleWithSystemId(uri, topModule);
if (sqc != null) {
sqc.lookForModuleCycles(referees, f.getLineNumber());
}
}
}
}
}
Iterator fiter = getLocalFunctionLibrary().getFunctionDefinitions();
while (fiter.hasNext()) {
XQueryFunction gf = fiter.next();
Expression body = gf.getUserFunction().getBody();
if (body != null) {
List vList = new ArrayList(10);
ExpressionTool.gatherReferencedVariables(body, vList);
for (Binding b : vList) {
if (b instanceof GlobalVariable) {
String uri = ((GlobalVariable) b).getSystemId();
StructuredQName qName = b.getVariableQName();
boolean synthetic = qName.hasURI(NamespaceConstant.SAXON) && "gg".equals(qName.getPrefix());
if (!synthetic && uri != null && !uri.equals(getSystemId())) {
QueryModule sqc = executable.getQueryModuleWithSystemId(uri, topModule);
if (sqc != null) {
sqc.lookForModuleCycles(referees, ((GlobalVariable) b).getLineNumber());
}
}
}
}
List fList = new ArrayList(10);
ExpressionTool.gatherCalledFunctions(body, fList);
for (UserFunction f : fList) {
String uri = f.getSystemId();
if (uri != null && !uri.equals(getSystemId())) {
QueryModule sqc = executable.getQueryModuleWithSystemId(uri, topModule);
if (sqc != null) {
sqc.lookForModuleCycles(referees, f.getLineNumber());
}
}
}
}
}
referees.pop();
}
}
/**
* 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();
Stack