0001: package net.sf.saxon.query;
0002:
0003: import net.sf.saxon.Configuration;
0004: import net.sf.saxon.event.Builder;
0005: import net.sf.saxon.event.Stripper;
0006: import net.sf.saxon.expr.*;
0007: import net.sf.saxon.functions.ConstructorFunctionLibrary;
0008: import net.sf.saxon.functions.FunctionLibrary;
0009: import net.sf.saxon.functions.FunctionLibraryList;
0010: import net.sf.saxon.functions.SystemFunctionLibrary;
0011: import net.sf.saxon.instruct.*;
0012: import net.sf.saxon.om.*;
0013: import net.sf.saxon.sort.CodepointCollator;
0014: import net.sf.saxon.sort.IntHashMap;
0015: import net.sf.saxon.trans.StaticError;
0016: import net.sf.saxon.trans.XPathException;
0017: import net.sf.saxon.type.AtomicType;
0018: import net.sf.saxon.type.SchemaType;
0019: import org.xml.sax.SAXParseException;
0020:
0021: import javax.xml.transform.Source;
0022: import javax.xml.transform.SourceLocator;
0023: import javax.xml.transform.TransformerException;
0024: import java.io.IOException;
0025: import java.io.InputStream;
0026: import java.io.Reader;
0027: import java.util.*;
0028:
0029: /**
0030: * StaticQueryContext is the implementation of StaticContext used when processing XQuery
0031: * expressions.
0032: * <p/>
0033: * The StaticQueryContext object has two different usages. The application constructs a StaticQueryContext
0034: * and initializes it with information about the context, for example, default namespaces, base URI, and so on.
0035: * When a query is compiled using this StaticQueryContext, the query parser makes a copy of the StaticQueryContext
0036: * and uses this internally, modifying it with information obtained from the query prolog, as well as information
0037: * such as namespace and variable declarations that can occur at any point in the query. The query parser does
0038: * not modify the original StaticQueryContext supplied by the calling application, which may therefore be used
0039: * for compiling multiple queries, serially or even in multiple threads.
0040: * <p/>
0041: * This class forms part of Saxon's published XQuery API.
0042: * Note that some of the methods are intended for use internally by the
0043: * query processor itself: these are labelled as such. Methods that
0044: * are considered stable are labelled with the JavaDoc "since" tag.
0045: * The value 8.4 indicates a method introduced at or before Saxon 8.4; other
0046: * values indicate the version at which the method was introduced.
0047: * <p/>
0048: * In the longer term, this entire API may at some stage be superseded by a proposed
0049: * standard Java API for XQuery.
0050: *
0051: * @since 8.4
0052: */
0053:
0054: public class StaticQueryContext implements StaticContext {
0055:
0056: private boolean isMainModule;
0057: private Configuration config;
0058: private NamePool namePool;
0059: private String locationURI;
0060: private String moduleNamespace;
0061: private String baseURI;
0062: private HashMap passiveNamespaces;
0063: private HashSet explicitPrologNamespaces;
0064: private Stack activeNamespaces;
0065: private boolean inheritNamespaces = true;
0066: private boolean preserveNamespaces = true;
0067: private NamespaceResolver externalNamespaceResolver = null;
0068: private HashMap collations;
0069: private IntHashMap variables; // global variables declared in this module
0070: private IntHashMap libraryVariables; // all variables defined in library modules
0071: // defined only on the top-level module
0072: private IntHashMap undeclaredVariables;
0073: private HashSet importedSchemata;
0074: private String defaultCollationName;
0075: private String defaultFunctionNamespace;
0076: private short defaultElementNamespace;
0077: private SlotManager stackFrameMap; // map of the outermost local stackframe
0078: private short moduleNamespaceURICode;
0079: private int constructionMode;
0080: private Executable executable;
0081: private List importers; // A list of StaticQueryContext objects representing the modules that import this one,
0082: // Null for the main module
0083: private FunctionLibraryList functionLibraryList;
0084: private XQueryFunctionLibrary globalFunctionLibrary; // used only on a top-level module
0085: //private XQueryFunctionLibrary localFunctionLibrary; // contains functions declared in this module
0086: //private ImportedFunctionLibrary importedFunctionLibrary; // contains imported functions
0087: //private UnboundFunctionLibrary unboundFunctionLibrary;
0088: private int localFunctionLibraryNr;
0089: private int importedFunctionLibraryNr;
0090: private int unboundFunctionLibraryNr;
0091: private Set importedModuleNamespaces;
0092:
0093: /**
0094: * Private constructor used when copying a context
0095: */
0096:
0097: private StaticQueryContext() {
0098: }
0099:
0100: /**
0101: * Create a StaticQueryContext using a given Configuration. This creates a StaticQueryContext for a main module
0102: * (that is, a module that is not a library module).
0103: *
0104: * @since 8.4
0105: */
0106:
0107: public StaticQueryContext(Configuration config) {
0108: this .config = config;
0109: this .namePool = config.getNamePool();
0110: isMainModule = true;
0111: reset();
0112: }
0113:
0114: /**
0115: * Create a StaticQueryContext for a library module.
0116: * @param importer the module that imported this module. This may be null, in which case the
0117: * library module is treated as a "top-level" library module.
0118: */
0119:
0120: public StaticQueryContext(Configuration config,
0121: StaticQueryContext importer) {
0122: this .config = config;
0123: this .namePool = config.getNamePool();
0124: isMainModule = false;
0125: if (importers == null && importer != null) {
0126: importers = new ArrayList(2);
0127: importers.add(importer);
0128: }
0129: reset();
0130: }
0131:
0132: /**
0133: * Reset the state of this StaticQueryContext to an uninitialized state
0134: *
0135: * @since 8.4
0136: */
0137:
0138: public void reset() {
0139: passiveNamespaces = new HashMap(10);
0140: explicitPrologNamespaces = new HashSet(10);
0141: activeNamespaces = new Stack();
0142: externalNamespaceResolver = null;
0143: collations = new HashMap(5);
0144: variables = new IntHashMap(10);
0145: undeclaredVariables = new IntHashMap(5);
0146: if (isTopLevelModule()) {
0147: globalFunctionLibrary = new XQueryFunctionLibrary(config);
0148: libraryVariables = new IntHashMap(10);
0149: }
0150: importedSchemata = new HashSet(5);
0151: importedModuleNamespaces = new HashSet(5);
0152: defaultFunctionNamespace = NamespaceConstant.FN;
0153: defaultElementNamespace = NamespaceConstant.NULL_CODE;
0154: moduleNamespace = null;
0155: moduleNamespaceURICode = 0;
0156: constructionMode = Validation.PRESERVE;
0157: defaultCollationName = NamespaceConstant.CODEPOINT_COLLATION_URI;
0158: declareCollation(defaultCollationName, CodepointCollator
0159: .getInstance());
0160: functionLibraryList = new FunctionLibraryList();
0161: functionLibraryList
0162: .addFunctionLibrary(new SystemFunctionLibrary(
0163: SystemFunctionLibrary.XPATH_ONLY));
0164: functionLibraryList.addFunctionLibrary(config
0165: .getVendorFunctionLibrary());
0166: functionLibraryList
0167: .addFunctionLibrary(new ConstructorFunctionLibrary(
0168: config));
0169: if (config.isAllowExternalFunctions()) {
0170: functionLibraryList.addFunctionLibrary(config
0171: .getExtensionBinder());
0172: }
0173: localFunctionLibraryNr = functionLibraryList
0174: .addFunctionLibrary(new XQueryFunctionLibrary(config));
0175:
0176: importedFunctionLibraryNr = functionLibraryList
0177: .addFunctionLibrary(new ImportedFunctionLibrary(
0178: getTopLevelModule(this )
0179: .getGlobalFunctionLibrary()));
0180:
0181: unboundFunctionLibraryNr = functionLibraryList
0182: .addFunctionLibrary(new UnboundFunctionLibrary());
0183:
0184: clearPassiveNamespaces();
0185: }
0186:
0187: /**
0188: * Test whether this is a "top-level" module. This is true for a main module and also for a
0189: * module directly imported into an XSLT stylesheet. It may also be true in future for independently-compiled
0190: * modules
0191: */
0192:
0193: public boolean isTopLevelModule() {
0194: return importers == null;
0195: }
0196:
0197: /**
0198: * Test whether this is a "main" module, in the sense of the XQuery language specification
0199: */
0200:
0201: public boolean isMainModule() {
0202: return isMainModule;
0203: }
0204:
0205: /**
0206: * Check whether this module is allowed to import a module with namespace N. Note that before
0207: * calling this we have already handled the exception case where a module imports another in the same
0208: * namespace (the only case where cycles are allowed)
0209: */
0210:
0211: public boolean mayImport(String namespace) {
0212: if (namespace.equals(moduleNamespace)) {
0213: return false;
0214: }
0215: if (importers == null) {
0216: return true;
0217: }
0218: for (int i = 0; i < importers.size(); i++) {
0219: if (!((StaticQueryContext) importers.get(i))
0220: .mayImport(namespace)) {
0221: return false;
0222: }
0223: }
0224: return true;
0225: }
0226:
0227: public XQueryFunctionLibrary getGlobalFunctionLibrary() {
0228: return globalFunctionLibrary;
0229: }
0230:
0231: public ImportedFunctionLibrary getImportedFunctionLibrary() {
0232: return (ImportedFunctionLibrary) functionLibraryList
0233: .get(importedFunctionLibraryNr);
0234: }
0235:
0236: /**
0237: * Register that this module imports a particular module namespace
0238: */
0239:
0240: public void addImportedNamespace(String uri) {
0241: if (importedModuleNamespaces == null) {
0242: importedModuleNamespaces = new HashSet(5);
0243: }
0244: importedModuleNamespaces.add(uri);
0245: getImportedFunctionLibrary().addImportedNamespace(uri);
0246: }
0247:
0248: /**
0249: * Test whether this module directly imports a particular namespace
0250: */
0251:
0252: public boolean importsNamespace(String uri) {
0253: return importedModuleNamespaces != null
0254: && importedModuleNamespaces.contains(uri);
0255: }
0256:
0257: /**
0258: * Test whether this module imports a particular namespace directly or indirectly
0259: */
0260:
0261: public boolean importsNamespaceIndirectly(String uri) {
0262: if (importsNamespace(uri)) {
0263: return true;
0264: }
0265: for (Iterator it = iterateImportedNamespaces(); it.hasNext();) {
0266: String moduleURI = (String) it.next();
0267: List list = executable.getQueryLibraryModules(moduleURI);
0268: for (Iterator i2 = list.iterator(); i2.hasNext();) {
0269: StaticQueryContext sqc = (StaticQueryContext) i2.next();
0270: if (sqc.importsNamespaceIndirectly(uri)) {
0271: return true;
0272: }
0273: }
0274: }
0275: return false;
0276: }
0277:
0278: /**
0279: * Get an iterator over all the module namespaces that this module imports
0280: */
0281:
0282: public Iterator iterateImportedNamespaces() {
0283: if (importedModuleNamespaces == null) {
0284: return Collections.EMPTY_LIST.iterator();
0285: }
0286: return importedModuleNamespaces.iterator();
0287: }
0288:
0289: /**
0290: * Get the Static Query Context for the top-level module. This will normally be a main module,
0291: * but in the case of saxon:import-query it will be the library module that is imported into
0292: * the stylesheet
0293: */
0294:
0295: public StaticQueryContext getTopLevelModule(StaticQueryContext start) {
0296: if (importers == null) {
0297: return this ;
0298: }
0299: // There may be import cycles of modules using the same namespace. So first we
0300: // try to find an importer in a different namespace; if this fails we try the first
0301: // importer. Somewhere in the cycle there must be a module that was imported from a different
0302: // namespace (we hope!)
0303: for (int i = 0; i < importers.size(); i++) {
0304: StaticQueryContext importer = (StaticQueryContext) importers
0305: .get(i);
0306: if (!start.importsNamespace(importer.getModuleNamespace())) {
0307: return importer.getTopLevelModule(start);
0308: }
0309: }
0310: return ((StaticQueryContext) importers.get(0))
0311: .getTopLevelModule(start);
0312: }
0313:
0314: /**
0315: * Make a copy of this StaticQueryContext. The StaticQueryContext that is constructed by a user
0316: * application and passed to Saxon when a query is compiled should not be modified by the query
0317: * compiler. Saxon therefore makes a copy of the StaticQueryContext and uses this copy internally,
0318: * to capture any changes to the StaticQueryContext defined in the query prolog.
0319: *
0320: * @return a copy of this StaticQueryContext
0321: */
0322:
0323: public StaticQueryContext copy() {
0324: StaticQueryContext n = new StaticQueryContext();
0325: n.config = config;
0326: n.namePool = namePool;
0327: n.isMainModule = isMainModule;
0328: n.passiveNamespaces = new HashMap(passiveNamespaces);
0329: n.explicitPrologNamespaces = new HashSet(
0330: explicitPrologNamespaces);
0331: n.activeNamespaces = new Stack();
0332: n.externalNamespaceResolver = externalNamespaceResolver;
0333: n.inheritNamespaces = inheritNamespaces;
0334: n.preserveNamespaces = preserveNamespaces;
0335: n.collations = new HashMap(collations);
0336: n.variables = variables.copy();
0337: n.undeclaredVariables = undeclaredVariables.copy();
0338: if (libraryVariables != null) {
0339: n.libraryVariables = libraryVariables.copy();
0340: }
0341: //n.variableList = new ArrayList(variableList);
0342: n.importedSchemata = new HashSet(importedSchemata);
0343: n.defaultCollationName = defaultCollationName;
0344: n.defaultFunctionNamespace = defaultFunctionNamespace;
0345: n.defaultElementNamespace = defaultElementNamespace;
0346: n.locationURI = locationURI;
0347: n.baseURI = baseURI;
0348: n.stackFrameMap = stackFrameMap;
0349: n.moduleNamespace = moduleNamespace;
0350: n.moduleNamespaceURICode = moduleNamespaceURICode;
0351: n.constructionMode = constructionMode;
0352: n.executable = executable;
0353: n.importers = importers;
0354: n.functionLibraryList = (FunctionLibraryList) functionLibraryList
0355: .copy();
0356: n.localFunctionLibraryNr = localFunctionLibraryNr;
0357: n.importedFunctionLibraryNr = importedFunctionLibraryNr;
0358: n.unboundFunctionLibraryNr = unboundFunctionLibraryNr;
0359: n.globalFunctionLibrary = (XQueryFunctionLibrary) globalFunctionLibrary;
0360: n.importedModuleNamespaces = new HashSet(
0361: importedModuleNamespaces);
0362: return n;
0363: }
0364:
0365: /**
0366: * Set the Configuration options
0367: *
0368: * @throws IllegalArgumentException if the configuration supplied is different from the existing
0369: * configuration
0370: * @since 8.4
0371: * @deprecated This method serves no purpose, since it is not possible to change the configuration
0372: * once the StaticQueryContext has been initialized.
0373: */
0374:
0375: public void setConfiguration(Configuration config) {
0376: if (this .config != config) {
0377: throw new IllegalArgumentException(
0378: "Configuration cannot be changed dynamically");
0379: }
0380: this .config = config;
0381: }
0382:
0383: /**
0384: * Get the Configuration options
0385: *
0386: * @since 8.4
0387: */
0388:
0389: public Configuration getConfiguration() {
0390: return config;
0391: }
0392:
0393: /**
0394: * Construct a dynamic context for early evaluation of constant subexpressions
0395: */
0396:
0397: public XPathContext makeEarlyEvaluationContext() {
0398: return new EarlyEvaluationContext(this );
0399: }
0400:
0401: /**
0402: * Convenience method for building Saxon's internal representation of a source XML
0403: * document. The document will be built using the default NamePool, which means that
0404: * any process that uses it must also use the default NamePool.
0405: *
0406: * @param source Any javax.xml.transform.Source object representing the document against
0407: * which queries will be executed. Note that a Saxon {@link net.sf.saxon.om.DocumentInfo DocumentInfo}
0408: * (indeed any {@link net.sf.saxon.om.NodeInfo NodeInfo})
0409: * can be used as a Source. To use a third-party DOM Document as a source, create an instance of
0410: * {@link javax.xml.transform.dom.DOMSource DOMSource} to wrap it.
0411: * <p>For additional control over the way in which the source document is processed,
0412: * supply an {@link net.sf.saxon.AugmentedSource AugmentedSource} object and set appropriate
0413: * options on the object.</p>
0414: * @return the DocumentInfo representing the root node of the resulting document object.
0415: * @since 8.4
0416: */
0417:
0418: public DocumentInfo buildDocument(Source source)
0419: throws XPathException {
0420: Source s2 = config.getSourceResolver().resolveSource(source,
0421: config);
0422: if (s2 != null) {
0423: source = s2;
0424: }
0425: Stripper stripper = null;
0426: if (config.isStripsAllWhiteSpace()) {
0427: stripper = AllElementStripper.getInstance();
0428: stripper.setStripAll();
0429: }
0430: try {
0431: NodeInfo contextNode = Builder.build(source, stripper,
0432: config);
0433: return contextNode.getDocumentRoot();
0434: } catch (XPathException err) {
0435: Throwable cause = err.getException();
0436: if (cause != null && cause instanceof SAXParseException) {
0437: // This generally means the error was already reported.
0438: // But if a RuntimeException occurs in Saxon during a callback from
0439: // the Crimson parser, Crimson wraps this in a SAXParseException without
0440: // reporting it further.
0441: SAXParseException spe = (SAXParseException) cause;
0442: cause = spe.getException();
0443: if (cause instanceof RuntimeException) {
0444: config.reportFatalError(err);
0445: }
0446: } else {
0447: while (err.getException() instanceof XPathException) {
0448: err = (XPathException) err.getException();
0449: }
0450: config.reportFatalError(err);
0451: }
0452: throw err;
0453: }
0454: }
0455:
0456: /**
0457: * Prepare an XQuery query for subsequent evaluation. The source text of the query
0458: * is supplied as a String. The base URI of the query is taken from the static context,
0459: * and defaults to the current working directory.
0460: *
0461: * @param query The XQuery query to be evaluated, supplied as a string.
0462: * @return an XQueryExpression object representing the prepared expression
0463: * @throws net.sf.saxon.trans.XPathException
0464: * if the syntax of the expression is wrong,
0465: * or if it references namespaces, variables, or functions that have not been declared,
0466: * or contains other static errors.
0467: * @since 8.4
0468: */
0469:
0470: public XQueryExpression compileQuery(String query)
0471: throws XPathException {
0472: QueryParser qp = new QueryParser();
0473: XQueryExpression queryExp = qp.makeXQueryExpression(query,
0474: copy(), config);
0475: return queryExp;
0476: }
0477:
0478: /**
0479: * Prepare an XQuery query for subsequent evaluation. The Query is supplied
0480: * in the form of a Reader. The base URI of the query is taken from the static context,
0481: * and defaults to the current working directory.
0482: *
0483: * @param source A Reader giving access to the text of the XQuery query to be compiled.
0484: * @return an XPathExpression object representing the prepared expression.
0485: * @throws net.sf.saxon.trans.XPathException
0486: * if the syntax of the expression is wrong, or if it references namespaces,
0487: * variables, or functions that have not been declared, or any other static error is reported.
0488: * @throws java.io.IOException if a failure occurs reading the supplied input.
0489: * @since 8.4
0490: */
0491:
0492: public XQueryExpression compileQuery(Reader source)
0493: throws XPathException, IOException {
0494: char[] buffer = new char[4096];
0495: StringBuffer sb = new StringBuffer(4096);
0496: while (true) {
0497: int n = source.read(buffer);
0498: if (n > 0) {
0499: sb.append(buffer, 0, n);
0500: } else {
0501: break;
0502: }
0503: }
0504: return compileQuery(sb.toString());
0505: }
0506:
0507: /**
0508: * Prepare an XQuery query for subsequent evaluation. The Query is supplied
0509: * in the form of a InputStream, with an optional encoding. If the encoding is not specified,
0510: * the query parser attempts to obtain the encoding by inspecting the input stream: it looks specifically
0511: * for a byte order mark, and for the encoding option in the version declaration of an XQuery prolog.
0512: * The encoding defaults to UTF-8.
0513: * The base URI of the query is taken from the static context,
0514: * and defaults to the current working directory.
0515: *
0516: * @param source An InputStream giving access to the text of the XQuery query to be compiled, as a stream
0517: * of octets
0518: * @param encoding The encoding used to translate characters to octets in the query source. The parameter
0519: * may be null: in this case the query parser attempts to infer the encoding by inspecting the source,
0520: * and if that fails, it assumes UTF-8 encoding
0521: * @return an XPathExpression object representing the prepared expression.
0522: * @throws net.sf.saxon.trans.XPathException
0523: * if the syntax of the expression is wrong, or if it references namespaces,
0524: * variables, or functions that have not been declared, or any other static error is reported.
0525: * @throws java.io.IOException if a failure occurs reading the supplied input.
0526: * @since 8.5
0527: */
0528:
0529: public XQueryExpression compileQuery(InputStream source,
0530: String encoding) throws XPathException, IOException {
0531: String query = QueryReader.readInputStream(source, encoding,
0532: config.getNameChecker());
0533: return compileQuery(query);
0534: }
0535:
0536: /**
0537: * Get the Executable, an object representing the compiled query and its environment.
0538: * <p/>
0539: * This method is intended for internal use only.
0540: *
0541: * @return the Executable
0542: */
0543:
0544: public Executable getExecutable() {
0545: return executable;
0546: }
0547:
0548: /**
0549: * Set the executable.
0550: * <p/>
0551: * This method is intended for internal use only.
0552: *
0553: * @param executable the Executable
0554: */
0555:
0556: public void setExecutable(Executable executable) {
0557: this .executable = executable;
0558: }
0559:
0560: /**
0561: * Get the LocationMap, an data structure used to identify the location of compiled expressions within
0562: * the query source text.
0563: * <p/>
0564: * This method is intended for internal use only.
0565: *
0566: * @return the LocationMap
0567: */
0568:
0569: public LocationMap getLocationMap() {
0570: return executable.getLocationMap();
0571: }
0572:
0573: /**
0574: * Declare a namespace whose prefix can be used in expressions. This is
0575: * a passive namespace, it won't be copied into the result tree. Passive
0576: * namespaces are never undeclared, and active namespaces override them.
0577: *
0578: * @param prefix The namespace prefix. Must not be null.
0579: * @param uri The namespace URI. Must not be null. The value "" (zero-length string) is used
0580: * to undeclare a namespace; it is not an error if there is no existing binding for
0581: * the namespace prefix.
0582: * @param explicit This parameter is set to true when Saxon calls the method internally to
0583: * define a namespace declared within the query prolog. It should normally be set to false
0584: * in the case of a call from a user application.
0585: * @since 8.4
0586: */
0587:
0588: public void declarePassiveNamespace(String prefix, String uri,
0589: boolean explicit) throws StaticError {
0590: if (prefix == null) {
0591: throw new NullPointerException(
0592: "Null prefix supplied to declarePassiveNamespace()");
0593: }
0594: if (uri == null) {
0595: throw new NullPointerException(
0596: "Null namespace URI supplied to declarePassiveNamespace()");
0597: }
0598: if (explicit) {
0599: if (uri.equals("") && !prefix.equals("")) {
0600: explicitPrologNamespaces.remove(prefix);
0601: } else {
0602: if (explicitPrologNamespaces.contains(prefix)) {
0603: StaticError err = new StaticError(
0604: "Duplicate declaration of namespace prefix \""
0605: + prefix + '"');
0606: err.setErrorCode("XQST0033");
0607: throw err;
0608: } else {
0609: explicitPrologNamespaces.add(prefix);
0610: }
0611: }
0612: }
0613: if (uri.equals("") && !prefix.equals("")) {
0614: passiveNamespaces.remove(prefix);
0615: } else {
0616: passiveNamespaces.put(prefix, uri);
0617: namePool.allocateNamespaceCode(prefix, uri);
0618: }
0619: }
0620:
0621: /**
0622: * Declare an active namespace, that is, a namespace which as well as affecting the static
0623: * context of the query, will also be copied to the result tree when element constructors
0624: * are evaluated. When searching for a prefix-URI binding, active namespaces are searched
0625: * first, then passive namespaces. Active namespaces may be undeclared (in reverse sequence)
0626: * using {@link #undeclareNamespace()}.
0627: * <p/>
0628: * This method is intended for internal use only.
0629: */
0630:
0631: public void declareActiveNamespace(String prefix, String uri) {
0632: if (prefix == null) {
0633: throw new NullPointerException(
0634: "Null prefix supplied to declareActiveNamespace()");
0635: }
0636: if (uri == null) {
0637: throw new NullPointerException(
0638: "Null namespace URI supplied to declareActiveNamespace()");
0639: }
0640:
0641: int nscode = namePool.allocateNamespaceCode(prefix, uri);
0642: ActiveNamespace entry = new ActiveNamespace();
0643: entry.prefix = prefix;
0644: entry.uri = uri;
0645: entry.code = nscode;
0646: activeNamespaces.push(entry);
0647:
0648: if (prefix.equals("")) {
0649: defaultElementNamespace = (short) (nscode & 0xffff);
0650: }
0651:
0652: }
0653:
0654: /**
0655: * Undeclare the most recently-declared active namespace. This method is called
0656: * when a namespace declaration goes out of scope (while processing an element end tag).
0657: * It is NOT called when an XML 1.1-style namespace undeclaration is encountered.
0658: * <p/>
0659: * This method is intended for internal use only.
0660: *
0661: * @see #declareActiveNamespace(String, String)
0662: */
0663:
0664: public void undeclareNamespace() {
0665: ActiveNamespace entry = (ActiveNamespace) activeNamespaces
0666: .pop();
0667: if (entry.prefix.equals("")) {
0668: for (int i = activeNamespaces.size() - 1; i >= 0; i--) {
0669: if (((ActiveNamespace) activeNamespaces.get(i)).prefix
0670: .equals("")) {
0671: defaultElementNamespace = (short) (((ActiveNamespace) activeNamespaces
0672: .get(i)).code & 0xffff);
0673: return;
0674: }
0675: }
0676: String defaultNS = (String) passiveNamespaces.get("");
0677: if ("".equals(defaultNS)) {
0678: defaultElementNamespace = NamespaceConstant.NULL_CODE;
0679: } else {
0680: defaultElementNamespace = getNamePool().getCodeForURI(
0681: defaultNS);
0682: }
0683: }
0684: }
0685:
0686: /**
0687: * Clear all the declared passive namespaces, except for the standard ones (xml, saxon, etc)
0688: *
0689: * @since 8.4
0690: */
0691:
0692: public void clearPassiveNamespaces() {
0693: try {
0694: if (passiveNamespaces != null) {
0695: passiveNamespaces.clear();
0696: declarePassiveNamespace("xml", NamespaceConstant.XML,
0697: false);
0698: declarePassiveNamespace("saxon",
0699: NamespaceConstant.SAXON, false);
0700: declarePassiveNamespace("xs", NamespaceConstant.SCHEMA,
0701: false);
0702: declarePassiveNamespace("fn", NamespaceConstant.FN,
0703: false);
0704: declarePassiveNamespace("xdt", NamespaceConstant.XDT,
0705: false);
0706: declarePassiveNamespace("xsi",
0707: NamespaceConstant.SCHEMA_INSTANCE, false);
0708: declarePassiveNamespace("local",
0709: NamespaceConstant.LOCAL, false);
0710: declarePassiveNamespace("", "", false);
0711: }
0712: } catch (StaticError staticError) {
0713: // can't happen when third argument is "false"
0714: throw new IllegalStateException(
0715: "Internal Failure initializing namespace declarations");
0716: }
0717: }
0718:
0719: /**
0720: * Set an external namespace resolver. If a namespace prefix cannot be resolved using any
0721: * other mechanism, then as a last resort the external namespace resolver is called to
0722: * obtain a URI for the given prefix.
0723: * <p>
0724: * Note that the external namespace resolver is used only for namespace prefixes that are
0725: * resolved at compile time. Where namespace prefixes are resolved at run-time (which happens
0726: * very rarely with XQuery: the only case is to resolve the computed name used in a computed
0727: * element or attribute constructor), the external namespace resolver is not invoked.
0728: * <p>
0729: * Although the supplied object must implement the NamespaceResolver interface, the only method
0730: * actually used is the method {@link NamespaceResolver#getURIForPrefix(String, boolean)}. Other
0731: * methods may throw an UnsupportedOperationException.
0732: */
0733:
0734: public void setExternalNamespaceResolver(NamespaceResolver resolver) {
0735: externalNamespaceResolver = resolver;
0736: }
0737:
0738: /**
0739: * Get the external namespace resolver that has been registered using
0740: * setExternalNamespaceResolver(), if any.
0741: */
0742:
0743: public NamespaceResolver getExternalNamespaceResolver() {
0744: return externalNamespaceResolver;
0745: }
0746:
0747: /**
0748: * Get the URI for a prefix.
0749: * This method is used by the XQuery parser to resolve namespace prefixes.
0750: * <p/>
0751: * This method is intended primarily for internal use.
0752: *
0753: * @param prefix The prefix
0754: * @return the corresponding namespace URI
0755: * @throws net.sf.saxon.trans.XPathException
0756: * if the prefix has not been declared
0757: */
0758:
0759: public String getURIForPrefix(String prefix) throws XPathException {
0760: String uri = checkURIForPrefix(prefix);
0761: if (uri == null) {
0762: StaticError err = new StaticError("Prefix " + prefix
0763: + " has not been declared");
0764: err.setErrorCode("XPST0008");
0765: throw err;
0766: }
0767: return uri;
0768: }
0769:
0770: /**
0771: * Get the URI for a prefix if there is one, return null if not.
0772: * This method is used by the XQuery parser to resolve namespace prefixes.
0773: * <p/>
0774: * This method is intended primarily for internal use.
0775: *
0776: * @param prefix The prefix. Supply "" to obtain the default namespace.
0777: * @return the corresponding namespace URI, or null if the prefix has not
0778: * been declared. If the prefix is "" and the default namespace is the non-namespace,
0779: * return "".
0780: */
0781:
0782: public String checkURIForPrefix(String prefix) {
0783: // Search the active namespaces first, then the passive ones.
0784: for (int i = activeNamespaces.size() - 1; i >= 0; i--) {
0785: if (((ActiveNamespace) activeNamespaces.get(i)).prefix
0786: .equals(prefix)) {
0787: return ((ActiveNamespace) activeNamespaces.get(i)).uri;
0788: }
0789: }
0790: String uri = (String) passiveNamespaces.get(prefix);
0791: if (uri != null) {
0792: return uri;
0793: }
0794: if (externalNamespaceResolver != null) {
0795: return externalNamespaceResolver.getURIForPrefix(prefix,
0796: true);
0797: }
0798: return null;
0799: }
0800:
0801: /**
0802: * Get an array containing the namespace codes of all active
0803: * namespaces.
0804: * <p/>
0805: * This method is for internal use only.
0806: */
0807:
0808: public int[] getActiveNamespaceCodes() {
0809: int[] nscodes = new int[activeNamespaces.size()];
0810: int used = 0;
0811: HashSet prefixes = new HashSet(10);
0812: for (int n = activeNamespaces.size() - 1; n >= 0; n--) {
0813: ActiveNamespace an = (ActiveNamespace) activeNamespaces
0814: .get(n);
0815: if (!prefixes.contains(an.prefix)) {
0816: prefixes.add(an.prefix);
0817: nscodes[used++] = an.code;
0818: }
0819: }
0820: if (used < nscodes.length) {
0821: int[] nscodes2 = new int[used];
0822: System.arraycopy(nscodes, 0, nscodes2, 0, used);
0823: nscodes = nscodes2;
0824: }
0825: return nscodes;
0826: }
0827:
0828: /**
0829: * Get a copy of the Namespace Context. This method is used internally
0830: * by the query parser when a construct is encountered that needs
0831: * to save the namespace context for use at run-time.
0832: * <p/>
0833: * This method is for internal use only.
0834: */
0835:
0836: public NamespaceResolver getNamespaceResolver() {
0837: int[] active = getActiveNamespaceCodes();
0838: int[] nscodes = new int[passiveNamespaces.size()
0839: + active.length];
0840:
0841: int used = 0;
0842: for (Iterator iter = passiveNamespaces.keySet().iterator(); iter
0843: .hasNext();) {
0844: String prefix = (String) iter.next();
0845: String uri = (String) passiveNamespaces.get(prefix);
0846: nscodes[used++] = namePool.getNamespaceCode(prefix, uri);
0847: ;
0848: }
0849: for (int a = 0; a < active.length; a++) {
0850: nscodes[used++] = active[a];
0851: }
0852:
0853: return new SavedNamespaceContext(nscodes, namePool);
0854: }
0855:
0856: /**
0857: * Get the default function namespace
0858: *
0859: * @return the default function namespace (defaults to the fn: namespace)
0860: * @since 8.4
0861: */
0862:
0863: public String getDefaultFunctionNamespace() {
0864: return defaultFunctionNamespace;
0865: }
0866:
0867: /**
0868: * Set the default function namespace
0869: *
0870: * @param defaultFunctionNamespace The namespace to be used for unprefixed function calls
0871: * @since 8.4
0872: */
0873:
0874: public void setDefaultFunctionNamespace(
0875: String defaultFunctionNamespace) {
0876: this .defaultFunctionNamespace = defaultFunctionNamespace;
0877: }
0878:
0879: /**
0880: * Set the default element namespace
0881: *
0882: * @since 8.4
0883: */
0884:
0885: public void setDefaultElementNamespace(String uri)
0886: throws StaticError {
0887: int nscode = namePool.allocateNamespaceCode("", uri);
0888: defaultElementNamespace = (short) (nscode & 0xffff);
0889: declarePassiveNamespace("", uri, true);
0890: }
0891:
0892: /**
0893: * Get the default XPath namespace, as a namespace URI code that can be looked up in the NamePool
0894: *
0895: * @since 8.4
0896: */
0897:
0898: public short getDefaultElementNamespace() {
0899: return defaultElementNamespace;
0900: }
0901:
0902: /**
0903: * Set the namespace for a library module.
0904: * <p/>
0905: * This method is for internal use only.
0906: */
0907:
0908: public void setModuleNamespace(String uri) {
0909: moduleNamespace = uri;
0910: moduleNamespaceURICode = namePool.getCodeForURI(uri);
0911: }
0912:
0913: /**
0914: * Get the namespace of the current library module.
0915: * <p/>
0916: * This method is intended primarily for internal use.
0917: *
0918: * @return the module namespace, or null if this is a main module
0919: */
0920:
0921: public String getModuleNamespace() {
0922: return moduleNamespace;
0923: }
0924:
0925: /**
0926: * Get the namesapce code of the current library module.
0927: * <p/>
0928: * This method is intended primarily for internal use.
0929: *
0930: * @return the module namespace, or null if this is a main module
0931: */
0932:
0933: public short getModuleNamespaceCode() {
0934: return moduleNamespaceURICode;
0935: }
0936:
0937: /**
0938: * Set the location URI for a module
0939: */
0940:
0941: public void setLocationURI(String uri) {
0942: locationURI = uri;
0943: }
0944:
0945: /**
0946: * Get the location URI for a module
0947: */
0948:
0949: public String getLocationURI() {
0950: return locationURI;
0951: }
0952:
0953: /**
0954: * Set the namespace inheritance mode
0955: *
0956: * @param inherit true if namespaces are inherited, false if not
0957: * @since 8.4
0958: */
0959:
0960: public void setInheritNamespaces(boolean inherit) {
0961: inheritNamespaces = inherit;
0962: }
0963:
0964: /**
0965: * Get the namespace inheritance mode
0966: *
0967: * @return true if namespaces are inherited, false if not
0968: * @since 8.4
0969: */
0970:
0971: public boolean isInheritNamespaces() {
0972: return inheritNamespaces;
0973: }
0974:
0975: /**
0976: * Set the namespace copy mode
0977: *
0978: * @param inherit true if namespaces are preserved, false if not
0979: * @since 8.4
0980: */
0981:
0982: public void setPreserveNamespaces(boolean inherit) {
0983: preserveNamespaces = inherit;
0984: }
0985:
0986: /**
0987: * Get the namespace copy mode
0988: *
0989: * @return true if namespaces are preserved, false if not
0990: * @since 8.4
0991: */
0992:
0993: public boolean isPreserveNamespaces() {
0994: return preserveNamespaces;
0995: }
0996:
0997: /**
0998: * Declare a named collation. Collations are only available in a query if this method
0999: * has been called externally to declare the collation and associate it with an
1000: * implementation, in the form of a Java Comparator. The default collation is the
1001: * Unicode codepoint collation, unless otherwise specified.
1002: *
1003: * @param name The name of the collation (technically, a URI)
1004: * @param comparator The Java Comparator used to implement the collating sequence
1005: * @since 8.4
1006: */
1007:
1008: public void declareCollation(String name, Comparator comparator) {
1009: collations.put(name, comparator);
1010: }
1011:
1012: /**
1013: * Set the default collation.
1014: * @param name The collation name, as specified in the query prolog. The name
1015: * is not validated until it is used.
1016: * @since 8.4. Changed in 8.6 so it no longer validates the collation name: this is
1017: * because the base URI is not necessarily known at the point where the default
1018: * collation is declared.
1019: */
1020:
1021: public void declareDefaultCollation(String name) {
1022: defaultCollationName = name;
1023: }
1024:
1025: /**
1026: * Get a named collation.
1027: * @param name the name of the collation, as an absolute URI
1028: * @return the collation identified by the given name, as set previously using declareCollation.
1029: * If no collation with this name has been declared, the method calls the CollationURIResolver
1030: * to locate a collation with this name.
1031: * Return null if no collation with this name is found.
1032: * @since 8.4
1033: */
1034:
1035: public Comparator getCollation(String name) {
1036: Comparator c = (Comparator) collations.get(name);
1037: if (c != null) {
1038: return c;
1039: }
1040: return config.getCollationURIResolver().resolve(name,
1041: getBaseURI(), config);
1042: }
1043:
1044: /**
1045: * Get the name of the default collation.
1046: *
1047: * @return the name of the default collation; or the name of the codepoint collation
1048: * if no default collation has been defined. The name is returned in the form
1049: * it was specified; that is, it is not yet resolved against the base URI. (This
1050: * is because the base URI declaration can follow the default collation declaration
1051: * in the query prolog.) If no default collation has been specified, the "default default"
1052: * (that is, the Unicode codepoint collation) is returned.
1053: * @since 8.4
1054: */
1055:
1056: public String getDefaultCollationName() {
1057: if (defaultCollationName != null) {
1058: return defaultCollationName;
1059: } else {
1060: return NamespaceConstant.CODEPOINT_COLLATION_URI;
1061: }
1062: }
1063:
1064: /**
1065: * Get a HashMap that maps all registered collations to Comparators.
1066: * Note that this returns a snapshot copy of the data held by the static context.
1067: * This method is provided for internal use by the query processor.
1068: * <p/>
1069: * This method is intended for internal use.
1070: */
1071:
1072: public HashMap getAllCollations() {
1073: return new HashMap(collations);
1074: }
1075:
1076: /**
1077: * Get the stack frame map for global variables.
1078: * <p/>
1079: * This method is intended for internal use.
1080: */
1081:
1082: public SlotManager getGlobalStackFrameMap() {
1083: return executable.getGlobalVariableMap();
1084: }
1085:
1086: /**
1087: * Declare a global variable. A variable must normally be declared before an expression referring
1088: * to it is compiled, but there are exceptions where a set of modules in the same namespace
1089: * import each other cyclically. Global variables are normally declared in the Query Prolog, but
1090: * they can also be predeclared using this API. All global variables are held in the StaticQueryContext
1091: * for the main module. The fact that a global variable is present therefore does not mean that it
1092: * is visible: there are two additional conditions (a) the namespace must be imported into the
1093: * module where the reference appears, and (b) the declaration must not be in the same module and textually
1094: * after the reference.
1095: *
1096: * @since 8.4
1097: */
1098:
1099: public void declareVariable(VariableDeclaration var)
1100: throws StaticError {
1101: int key = var.getNameCode() & NamePool.FP_MASK;
1102: if (variables.get(key) != null) {
1103: GlobalVariableDefinition old = (GlobalVariableDefinition) variables
1104: .get(key);
1105: if (old == var) {
1106: // do nothing
1107: } else {
1108: StaticError err = new StaticError(
1109: "Duplicate definition of global variable "
1110: + var.getVariableName() + " (see line "
1111: + old.getLineNumber() /*+ " in module " + old.getSystemId()*/
1112: + ')');
1113: err.setErrorCode("XQST0049");
1114: if (var instanceof GlobalVariableDefinition) {
1115: ExpressionLocation loc = new ExpressionLocation();
1116: loc.setLineNumber(((GlobalVariableDefinition) var)
1117: .getLineNumber());
1118: loc.setSystemId(((GlobalVariableDefinition) var)
1119: .getSystemId());
1120: err.setLocator(loc);
1121: }
1122: throw err;
1123: }
1124: }
1125: variables.put(key, var);
1126: if (!isTopLevelModule()) {
1127: final IntHashMap libVars = getTopLevelModule(this ).libraryVariables;
1128: GlobalVariableDefinition old = (GlobalVariableDefinition) libVars
1129: .get(key);
1130: if (old == null || old == var) {
1131: // do nothing
1132: } else {
1133: StaticError err = new StaticError(
1134: "Duplicate definition of global variable "
1135: + var.getVariableName() + " (see line "
1136: + old.getLineNumber() + " in module "
1137: + old.getSystemId() + ')');
1138: err.setErrorCode("XQST0049");
1139: if (var instanceof GlobalVariableDefinition) {
1140: ExpressionLocation loc = new ExpressionLocation();
1141: loc.setLineNumber(((GlobalVariableDefinition) var)
1142: .getLineNumber());
1143: loc.setSystemId(((GlobalVariableDefinition) var)
1144: .getSystemId());
1145: err.setLocator(loc);
1146: }
1147: throw err;
1148: }
1149:
1150: libVars.put(key, var);
1151: }
1152: }
1153:
1154: /**
1155: * Fixup all references to global variables.
1156: * <p/>
1157: * This method is for internal use by the Query Parser only.
1158: */
1159:
1160: public List fixupGlobalVariables(SlotManager globalVariableMap)
1161: throws StaticError {
1162: List compiledVars = new ArrayList(20);
1163: Iterator[] iters = { variables.valueIterator(),
1164: libraryVariables.valueIterator() };
1165: for (int i = 0; i < 2; i++) {
1166: while (iters[i].hasNext()) {
1167: GlobalVariableDefinition var = (GlobalVariableDefinition) iters[i]
1168: .next();
1169: try {
1170: int slot = globalVariableMap.allocateSlotNumber(var
1171: .getNameCode()
1172: & NamePool.FP_MASK);
1173: GlobalVariable gv = var.getCompiledVariable();
1174: if (gv == null) {
1175: gv = var.compile(this , slot);
1176: }
1177: if (!compiledVars.contains(gv)) {
1178: compiledVars.add(gv);
1179: }
1180: } catch (XPathException err) {
1181: throw StaticError.makeStaticError(err);
1182: }
1183: }
1184: }
1185: return compiledVars;
1186: }
1187:
1188: /**
1189: * Get global variables declared in this module
1190: * @return an Iterator whose items are GlobalVariableDefinition objects
1191: */
1192:
1193: public Iterator getModuleVariables() {
1194: return variables.valueIterator();
1195: }
1196:
1197: /**
1198: * Get references to undeclared variables.
1199: * @return a list of XPathException objects, one for each undeclared variable
1200: */
1201:
1202: public List getUndeclaredVariables(SlotManager globalVariableMap)
1203: throws StaticError {
1204: List undeclaredVars = new ArrayList(20);
1205: Iterator iter = variables.valueIterator();
1206: while (iter.hasNext()) {
1207: GlobalVariableDefinition var = (GlobalVariableDefinition) iter
1208: .next();
1209: if (var instanceof UndeclaredVariable) {
1210: Iterator refs = var.iterateReferences();
1211: while (refs.hasNext()) {
1212: VariableReference ref = (VariableReference) refs
1213: .next();
1214: StaticError err = new StaticError(
1215: "Unresolved reference to variable $"
1216: + var.getVariableName());
1217: err.setLocator(ref);
1218: err.setErrorCode("XPST0008");
1219: undeclaredVars.add(err);
1220: }
1221: // Note: not an error if there are no references
1222: } else {
1223: // A GlobalVariableDeclaration that was introduced by importing may still need to have its
1224: // references bound
1225: Iterator refs = var.iterateReferences();
1226: while (refs.hasNext()) {
1227: VariableReference ref = (VariableReference) refs
1228: .next();
1229: if (ref.getBinding() == null) {
1230: ref.fixup(var.getCompiledVariable());
1231: }
1232: }
1233: }
1234: }
1235: return undeclaredVars;
1236: }
1237:
1238: public void typeCheckGlobalVariables(List compiledVars)
1239: throws StaticError {
1240: try {
1241: Iterator iter = compiledVars.iterator();
1242: Stack stack = new Stack();
1243: while (iter.hasNext()) {
1244: GlobalVariable gv = (GlobalVariable) iter.next();
1245: gv.lookForCycles(stack);
1246: GlobalVariableDefinition.typeCheck(this , gv);
1247: }
1248: } catch (XPathException e) {
1249: throw StaticError.makeStaticError(e);
1250: }
1251: }
1252:
1253: /**
1254: * Produce "explain" output for all global variables.
1255: * <p/>
1256: * This method is intended primarily for internal use.
1257: */
1258:
1259: public void explainGlobalVariables() {
1260: Iterator iter = variables.valueIterator();
1261: while (iter.hasNext()) {
1262: GlobalVariableDefinition var = (GlobalVariableDefinition) iter
1263: .next();
1264: var.explain(getNamePool());
1265: }
1266: }
1267:
1268: /**
1269: * Get an iterator over the variables defined in this module.
1270: * <p/>
1271: * This method is intended primarily for internal use.
1272: *
1273: * @return an Iterator, whose items are VariableDeclaration objects. It returns
1274: * all variables known to this module including those imported from elsewhere; they
1275: * can be distinguished by their namespace. The variables are returned in order of
1276: * declaration.
1277: */
1278:
1279: // public Iterator getVariableDeclarations() {
1280: // return variableList.iterator();
1281: // }
1282: /**
1283: * Get the stack frame map for local variables in the "main" query expression.
1284: * <p/>
1285: * This method is intended for internal use only.
1286: */
1287:
1288: public SlotManager getStackFrameMap() {
1289: if (stackFrameMap == null) {
1290: stackFrameMap = getConfiguration().makeSlotManager();
1291: }
1292: return stackFrameMap;
1293: }
1294:
1295: /**
1296: * Get the NamePool used for compiling expressions
1297: *
1298: * @since 8.4
1299: */
1300:
1301: public NamePool getNamePool() {
1302: return namePool;
1303: }
1304:
1305: /**
1306: * Issue a compile-time warning. This method is used during XQuery expression compilation to
1307: * output warning conditions.
1308: * <p/>
1309: * This method is intended for internal use only.
1310: */
1311:
1312: public void issueWarning(String s, SourceLocator locator) {
1313: StaticError err = new StaticError(s);
1314: err.setLocator(locator);
1315: try {
1316: config.getErrorListener().warning(err);
1317: } catch (TransformerException e) {
1318: // ignore any error thrown
1319: }
1320: }
1321:
1322: /**
1323: * Set the Base URI of the query
1324: *
1325: * @since 8.4
1326: */
1327:
1328: public void setBaseURI(String baseURI) {
1329: this .baseURI = baseURI;
1330: }
1331:
1332: /**
1333: * Get the system ID of the container of the expression. Used to construct error messages.
1334: * Note that the systemID and the Base URI are currently identical, but they might be distinguished
1335: * in the future.
1336: *
1337: * @return the Base URI
1338: * @since 8.4
1339: */
1340:
1341: public String getSystemId() {
1342: return baseURI;
1343: }
1344:
1345: /**
1346: * Get the Base URI of the query, for resolving any relative URI's used
1347: * in the expression.
1348: * Note that the systemID and the Base URI are currently identical, but they might be distinguished
1349: * in the future.
1350: * Used by the document() function.
1351: *
1352: * @return the base URI of the query
1353: * @since 8.4
1354: */
1355:
1356: public String getBaseURI() {
1357: return baseURI;
1358: }
1359:
1360: /**
1361: * Get the line number of the expression within that container.
1362: * Used to construct error messages. This method is provided to satisfy the StaticContext interface,
1363: * but the value is meaningful only for XPath expressions within a document such as a stylesheet.
1364: *
1365: * @return -1 always
1366: */
1367:
1368: public int getLineNumber() {
1369: return -1;
1370: }
1371:
1372: /**
1373: * Bind a variable used in a query to the expression in which it is declared.
1374: * <p/>
1375: * This method is provided for use by the XQuery parser, and it should not be called by the user of
1376: * the API, or overridden, unless variables are to be declared using a mechanism other than the
1377: * declareVariable method of this class.
1378: */
1379:
1380: public VariableReference bindVariable(int fingerprint)
1381: throws StaticError {
1382: VariableDeclaration var = (VariableDeclaration) variables
1383: .get(fingerprint);
1384: if (var == null) {
1385: String uri = getNamePool().getURI(fingerprint);
1386: //String local = getNamePool().getLocalName(fingerprint);
1387: if (importedModuleNamespaces.contains(uri)) {
1388: StaticQueryContext main = getTopLevelModule(this );
1389: var = (VariableDeclaration) main.libraryVariables
1390: .get(fingerprint);
1391: if (var == null) {
1392: // If the namespace has been imported there's the possibility that
1393: // the variable declaration hasn't yet been read, because of the limited provision
1394: // for cyclic imports
1395: UndeclaredVariable uvar = new UndeclaredVariable();
1396: uvar.setNameCode(fingerprint);
1397: uvar.setVariableName(getNamePool().getDisplayName(
1398: fingerprint));
1399: VariableReference ref = new VariableReference(uvar);
1400: undeclaredVariables.put(fingerprint, uvar);
1401: return ref;
1402: }
1403: } else {
1404: // If the namespace hasn't been imported then we might as well throw the error right away
1405: StaticError err = new StaticError(
1406: "Unresolved reference to variable");
1407: // the message isn't used...
1408: throw err;
1409: }
1410: }
1411: return new VariableReference(var);
1412: }
1413:
1414: /**
1415: * Set the function library used for binding any function call appearing within the query module.
1416: * <p/>
1417: * This method is available for use by advanced applications. The details of the FunctionLibrary
1418: * interface are subject to change. Applications using this interface take responsibility for
1419: * ensuring that the results conform to the constraints imposed by the XQuery language specification,
1420: * for example that one function within a query module can call other functions defined in the same
1421: * query module.
1422: *
1423: * @param functionLibrary the FunctionLibrary to be used. This will typically be a
1424: * FunctionLibraryList; in most cases it will be a slightly modified copy of a FunctionLibraryList
1425: * constructed by the system and obtained using the {@link #getFunctionLibrary} method.
1426: * @see FunctionLibraryList
1427: */
1428:
1429: public void setFunctionLibraryList(
1430: FunctionLibraryList functionLibrary) {
1431: this .functionLibraryList = functionLibrary;
1432: }
1433:
1434: /**
1435: * Get the function library containing all the in-scope functions available in this static
1436: * context (that is, the functions available in this query module).
1437: * <p/>
1438: * This method is provided for use by advanced applications.
1439: * The details of the interface are subject to change.
1440: *
1441: * @return the FunctionLibrary used. For XQuery, this will always be a FunctionLibraryList.
1442: * @see FunctionLibraryList
1443: */
1444:
1445: public FunctionLibrary getFunctionLibrary() {
1446: return functionLibraryList;
1447: }
1448:
1449: /**
1450: * Get the functions declared locally within this module
1451: */
1452:
1453: public XQueryFunctionLibrary getLocalFunctionLibrary() {
1454: return (XQueryFunctionLibrary) functionLibraryList
1455: .get(localFunctionLibraryNr);
1456: }
1457:
1458: /**
1459: * Register a user-defined XQuery function.
1460: * <p/>
1461: * This method is intended for internal use only.
1462: */
1463:
1464: public void declareFunction(XQueryFunction function)
1465: throws StaticError {
1466: if (function.getNumberOfArguments() == 1) {
1467: SchemaType t = config.getSchemaType(function.getNameCode()
1468: & NamePool.FP_MASK);
1469: if (t != null && t instanceof AtomicType) {
1470: StaticError err = new StaticError(
1471: "Function name "
1472: + function
1473: .getFunctionDisplayName(getNamePool())
1474: + " clashes with the name of the constructor function for an atomic type");
1475: err.setErrorCode("XQST0034");
1476: throw err;
1477: }
1478: }
1479: XQueryFunctionLibrary local = getLocalFunctionLibrary();
1480: local.declareFunction(function);
1481: StaticQueryContext main = getTopLevelModule(this );
1482: main.globalFunctionLibrary.declareFunction(function);
1483: }
1484:
1485: /**
1486: * Bind function calls that could not be bound when first encountered. These
1487: * will either be forwards references to functions declared later in the same query module,
1488: * or in modules that are being imported recursively, or errors.
1489: * <p/>
1490: * This method is for internal use only.
1491: *
1492: * @throws net.sf.saxon.trans.StaticError if a function call refers to a function that has
1493: * not been declared
1494: */
1495:
1496: public void bindUnboundFunctionCalls() throws XPathException {
1497: UnboundFunctionLibrary lib = (UnboundFunctionLibrary) functionLibraryList
1498: .get(unboundFunctionLibraryNr);
1499: lib.bindUnboundFunctionCalls(functionLibraryList, config);
1500: }
1501:
1502: /**
1503: * Fixup all references to global functions. This method is called
1504: * on completion of query parsing. Each XQueryFunction is required to
1505: * bind all references to that function to the object representing the run-time
1506: * executable code of the function.
1507: * <p/>
1508: * This method is for internal use only.
1509: */
1510:
1511: public void fixupGlobalFunctions() throws XPathException {
1512: globalFunctionLibrary.fixupGlobalFunctions(this );
1513: }
1514:
1515: /**
1516: * Output "explain" information about each declared function.
1517: * <p/>
1518: * This method is intended primarily for internal use.
1519: */
1520:
1521: public void explainGlobalFunctions() throws XPathException {
1522: globalFunctionLibrary.explainGlobalFunctions();
1523: }
1524:
1525: /**
1526: * Get the function with a given name and arity. This method is provided so that XQuery functions
1527: * can be called directly from a Java application. Note that there is no type checking or conversion
1528: * of arguments when this is done: the arguments must be provided in exactly the form that the function
1529: * signature declares them.
1530: *
1531: * @param uri the uri of the function name
1532: * @param localName the local part of the function name
1533: * @param arity the number of arguments.
1534: * @since 8.4
1535: */
1536:
1537: public UserFunction getUserDefinedFunction(String uri,
1538: String localName, int arity) {
1539: return globalFunctionLibrary.getUserDefinedFunction(uri,
1540: localName, arity);
1541: }
1542:
1543: /**
1544: * Bind unbound variables (these are typically variables that reference another module
1545: * participating in a same-namespace cycle, since local forwards references are not allowed
1546: */
1547:
1548: public void bindUnboundVariables() throws XPathException {
1549: for (Iterator iter = undeclaredVariables.valueIterator(); iter
1550: .hasNext();) {
1551: UndeclaredVariable uv = (UndeclaredVariable) iter.next();
1552: int fingerprint = uv.getNameCode() & NamePool.FP_MASK;
1553: VariableDeclaration var = (VariableDeclaration) variables
1554: .get(fingerprint);
1555: if (var == null) {
1556: String uri = getNamePool().getURI(fingerprint);
1557: if (importedModuleNamespaces.contains(uri)) {
1558: StaticQueryContext main = getTopLevelModule(this );
1559: var = (VariableDeclaration) main.libraryVariables
1560: .get(fingerprint);
1561: }
1562: }
1563: if (var == null) {
1564: StaticError err = new StaticError(
1565: "Unresolved reference to variable $"
1566: + uv.getVariableName());
1567: err.setErrorCode("XPST0008");
1568: throw err;
1569: } else {
1570: uv.transferReferences(var);
1571: }
1572: }
1573: }
1574:
1575: /**
1576: * Determine whether Backwards Compatible Mode is used
1577: *
1578: * @return false; XPath 1.0 compatibility mode is not supported in XQuery
1579: * @since 8.4
1580: */
1581:
1582: public boolean isInBackwardsCompatibleMode() {
1583: return false;
1584: }
1585:
1586: /**
1587: * Add an imported schema to this static context. A query module can reference
1588: * types in a schema provided two conditions are satisfied: the schema containing those
1589: * types has been loaded into the Configuration, and the target namespace has been imported
1590: * by this query module. This method achieves the second of these conditions. It does not
1591: * cause the schema to be loaded.
1592: * <p/>
1593: *
1594: * @param targetNamespace The target namespace of the schema to be added
1595: * @since 8.4
1596: */
1597:
1598: public void addImportedSchema(String targetNamespace) {
1599: if (importedSchemata == null) {
1600: importedSchemata = new HashSet(5);
1601: }
1602: importedSchemata.add(targetNamespace);
1603: }
1604:
1605: /**
1606: * Get the schema for a given namespace, if it has been imported
1607: *
1608: * @param namespace The namespace of the required schema. Supply "" for
1609: * a no-namespace schema.
1610: * @return The schema if found, or null if not found.
1611: * @since 8.4
1612: */
1613:
1614: public boolean isImportedSchema(String namespace) {
1615: if (importedSchemata == null) {
1616: return false;
1617: }
1618: return importedSchemata.contains(namespace);
1619: }
1620:
1621: /**
1622: * Get the set of imported schemas
1623: *
1624: * @return a Set, the set of URIs representing the names of imported schemas
1625: */
1626:
1627: public Set getImportedSchemaNamespaces() {
1628: return importedSchemata;
1629: }
1630:
1631: /**
1632: * Determine whether a built-in type is available in this context. This method caters for differences
1633: * between host languages as to which set of types are built in.
1634: *
1635: * @param type the supposedly built-in type. This will always be a type in the
1636: * XS or XDT namespace.
1637: * @return true if this type can be used in this static context
1638: */
1639:
1640: public boolean isAllowedBuiltInType(AtomicType type) {
1641: return true;
1642: }
1643:
1644: /**
1645: * Set the construction mode for this module
1646: *
1647: * @param mode one of {@link Validation#STRIP}, {@link Validation#PRESERVE}
1648: * @since 8.4
1649: */
1650:
1651: public void setConstructionMode(int mode) {
1652: constructionMode = mode;
1653: }
1654:
1655: /**
1656: * Get the current validation mode
1657: *
1658: * @return one of {@link Validation#STRIP}, {@link Validation#PRESERVE}
1659: * @since 8.4
1660: */
1661:
1662: public int getConstructionMode() {
1663: return constructionMode;
1664: }
1665:
1666: /**
1667: * Supporting method to load a query module. Used also by saxon:import-query in XSLT.
1668: * <p/>
1669: * This method is intended for internal use only.
1670: *
1671: * @param baseURI The base URI and location URI of the module
1672: * @param executable The Executable
1673: * @param importer The importing query module (used to check for cycles). This is null
1674: * when loading a query module from XSLT.
1675: * @param query The text of the query, after decoding and normalizing line endings
1676: * @param namespaceURI namespace of the query module to be loaded
1677: * @return The StaticQueryContext representing the loaded query module
1678: * @throws net.sf.saxon.trans.StaticError
1679: */
1680:
1681: public static StaticQueryContext makeStaticQueryContext(
1682: String baseURI, Executable executable,
1683: StaticQueryContext importer, String query,
1684: String namespaceURI) throws StaticError {
1685: Configuration config = executable.getConfiguration();
1686: StaticQueryContext module = new StaticQueryContext(config,
1687: importer);
1688: module.setLocationURI(baseURI);
1689: module.setBaseURI(baseURI);
1690: module.setModuleNamespace(namespaceURI);
1691: module.setExecutable(executable);
1692:
1693: executable.addQueryLibraryModule(module);
1694: QueryParser qp = new QueryParser();
1695: qp.parseLibraryModule(query, module);
1696: if (module.getModuleNamespace() == null) {
1697: throw new StaticError(
1698: "Imported module must be a library module");
1699: }
1700: if (!module.getModuleNamespace().equals(namespaceURI)) {
1701: throw new StaticError(
1702: "Imported module's namespace does not match requested namespace");
1703: }
1704:
1705: return module;
1706: }
1707:
1708: /**
1709: * Inner class containing information about an active namespace entry
1710: */
1711:
1712: private static class ActiveNamespace {
1713: public String prefix;
1714: public String uri;
1715: public int code;
1716: }
1717:
1718: }
1719:
1720: //
1721: // The contents of this file are subject to the Mozilla Public License Version 1.0 (the "License");
1722: // you may not use this file except in compliance with the License. You may obtain a copy of the
1723: // License at http://www.mozilla.org/MPL/
1724: //
1725: // Software distributed under the License is distributed on an "AS IS" basis,
1726: // WITHOUT WARRANTY OF ANY KIND, either express or implied.
1727: // See the License for the specific language governing rights and limitations under the License.
1728: //
1729: // The Original Code is: all this file.
1730: //
1731: // The Initial Developer of the Original Code is Michael H. Kay.
1732: //
1733: // Portions created by (your name) are Copyright (C) (your legal entity). All Rights Reserved.
1734: //
1735: // Contributor(s): none.
1736: //
|