001: package net.sf.saxon.xpath;
002:
003: import net.sf.saxon.Configuration;
004: import net.sf.saxon.expr.*;
005: import net.sf.saxon.functions.ConstructorFunctionLibrary;
006: import net.sf.saxon.functions.FunctionLibrary;
007: import net.sf.saxon.functions.FunctionLibraryList;
008: import net.sf.saxon.functions.SystemFunctionLibrary;
009: import net.sf.saxon.instruct.LocationMap;
010: import net.sf.saxon.instruct.SlotManager;
011: import net.sf.saxon.om.*;
012: import net.sf.saxon.trans.StaticError;
013: import net.sf.saxon.trans.Variable;
014: import net.sf.saxon.trans.XPathException;
015: import net.sf.saxon.type.AtomicType;
016: import net.sf.saxon.type.SchemaException;
017: import net.sf.saxon.value.QNameValue;
018:
019: import javax.xml.namespace.NamespaceContext;
020: import javax.xml.transform.SourceLocator;
021: import javax.xml.transform.Source;
022: import javax.xml.xpath.XPathFunctionResolver;
023: import javax.xml.xpath.XPathVariableResolver;
024: import java.util.*;
025:
026: /**
027: * A StandaloneContext provides a context for parsing an XPath expression
028: * in a context other than a stylesheet. In particular, it is used to support
029: * the JAXP 1.3 XPath API. This API does not actually expose the StaticContext
030: * object directly; rather, the static context (namespaces, variables, and functions)
031: * is manipulated through the XPath object, implemented in Saxon by the {@link XPathEvaluator}
032: */
033:
034: public class StandaloneContext implements StaticContext,
035: NamespaceResolver {
036:
037: private NamePool namePool;
038: private HashMap namespaces = new HashMap(10);
039: private HashMap collations = new HashMap(10);
040: private HashMap variables = new HashMap(20);
041: private SlotManager stackFrameMap;
042: private String defaultCollationName = null;
043: private String baseURI = null;
044: private Configuration config;
045: private LocationMap locationMap = new LocationMap();
046: private FunctionLibrary functionLibrary;
047: private XPathFunctionLibrary xpathFunctionLibrary;
048: private String defaultFunctionNamespace = NamespaceConstant.FN;
049: private short defaultElementNamespace = NamespaceConstant.NULL_CODE;
050: private boolean backwardsCompatible = false;
051:
052: private NamespaceContext namespaceContext;
053: private XPathVariableResolver variableResolver;
054:
055: /**
056: * Create a StandaloneContext using the default Configuration and NamePool
057: */
058:
059: public StandaloneContext() {
060: this (new Configuration());
061: }
062:
063: /**
064: * Create a StandaloneContext using a specific Configuration.
065: * @param config the Configuration. For schema-aware XPath expressions, this must be a SchemaAwareConfiguration.
066: */
067:
068: public StandaloneContext(Configuration config) {
069: this .config = config;
070: namePool = config.getNamePool();
071: stackFrameMap = config.makeSlotManager();
072: clearNamespaces();
073:
074: // Set up a default function library. This can be overridden using setFunctionLibrary()
075:
076: FunctionLibraryList lib = new FunctionLibraryList();
077: lib.addFunctionLibrary(new SystemFunctionLibrary(
078: SystemFunctionLibrary.XPATH_ONLY));
079: lib.addFunctionLibrary(getConfiguration()
080: .getVendorFunctionLibrary());
081: lib.addFunctionLibrary(new ConstructorFunctionLibrary(
082: getConfiguration()));
083: if (config.isAllowExternalFunctions()) {
084: xpathFunctionLibrary = new XPathFunctionLibrary();
085: lib.addFunctionLibrary(xpathFunctionLibrary);
086: lib.addFunctionLibrary(config.getExtensionBinder());
087: }
088: functionLibrary = lib;
089: }
090:
091: /**
092: * Create a StandaloneContext using a specific Node. This node is used to initialize
093: * the NamePool and also to establish the initial set of in-scope namespaces.
094: */
095:
096: public StandaloneContext(NodeInfo node) {
097: DocumentInfo doc = node.getDocumentRoot();
098: if (doc == null) {
099: throw new IllegalArgumentException(
100: "The node used to establish a standalone context must be in a tree whose root is a document node");
101: }
102: config = doc.getConfiguration();
103: namePool = doc.getNamePool();
104: stackFrameMap = config.makeSlotManager();
105: setNamespaces(node);
106: }
107:
108: /**
109: * Get the system configuration
110: */
111:
112: public Configuration getConfiguration() {
113: return config;
114: }
115:
116: /**
117: * Construct a dynamic context for early evaluation of constant subexpressions
118: */
119:
120: public XPathContext makeEarlyEvaluationContext() {
121: return new EarlyEvaluationContext(this );
122: }
123:
124: public LocationMap getLocationMap() {
125: return locationMap;
126: }
127:
128: public void setLocationMap(LocationMap locationMap) {
129: this .locationMap = locationMap;
130: }
131:
132: /**
133: * Declare a namespace whose prefix can be used in expressions. Namespaces may either be
134: * pre-declared (the traditional Saxon interface), or they may be resolved on demand
135: * using a supplied NamespaceContext. When a prefix has to be resolved, the parser looks
136: * first in the pre-declared namespaces, then in the supplied NamespaceContext object.
137: * @param prefix The namespace prefix. Must not be null. Must not be the empty string
138: * ("") - unqualified names in an XPath expression always refer to the null namespace.
139: * @param uri The namespace URI. Must not be null.
140: */
141:
142: public void declareNamespace(String prefix, String uri) {
143: if (prefix == null) {
144: throw new NullPointerException(
145: "Null prefix supplied to declareNamespace()");
146: }
147: if (uri == null) {
148: throw new NullPointerException(
149: "Null namespace URI supplied to declareNamespace()");
150: }
151: namespaces.put(prefix, uri);
152: namePool.allocateNamespaceCode(prefix, uri);
153: }
154:
155: /**
156: * Supply the NamespaceContext used to resolve namespaces. This supplements namespaces
157: * that have been explicitly declared using {@link #declareNamespace} or
158: * that have been implicitly declared using {@link #setNamespaces(net.sf.saxon.om.NodeInfo)}
159: */
160:
161: public void setNamespaceContext(NamespaceContext context) {
162: this .namespaceContext = context;
163: }
164:
165: /**
166: * Get the NamespaceContext that was set using {@link #setNamespaceContext}
167: */
168:
169: public NamespaceContext getNamespaceContext() {
170: return namespaceContext;
171: }
172:
173: /**
174: * Clear all the declared namespaces, except for the standard ones (xml, xslt, saxon, xdt).
175: * This doesn't clear the namespace context set using {@link #setNamespaceContext}
176: */
177:
178: public void clearNamespaces() {
179: namespaces.clear();
180: declareNamespace("xml", NamespaceConstant.XML);
181: declareNamespace("xsl", NamespaceConstant.XSLT);
182: declareNamespace("saxon", NamespaceConstant.SAXON);
183: declareNamespace("xs", NamespaceConstant.SCHEMA);
184: declareNamespace("xdt", NamespaceConstant.XDT);
185: declareNamespace("", "");
186: }
187:
188: /**
189: * Clear all the declared namespaces, including the standard ones (xml, xslt, saxon).
190: * Leave only the XML namespace and the default namespace (xmlns="")
191: */
192:
193: public void clearAllNamespaces() {
194: namespaces.clear();
195: declareNamespace("xml", NamespaceConstant.XML);
196: declareNamespace("", "");
197: }
198:
199: /**
200: * Set all the declared namespaces to be the namespaces that are in-scope for a given node.
201: * In addition, the standard namespaces (xml, xslt, saxon) are declared.
202: * @param node The node whose in-scope namespaces are to be used as the context namespaces.
203: * Note that this will have no effect unless this node is an element.
204: */
205:
206: public void setNamespaces(NodeInfo node) {
207: namespaces.clear();
208: AxisIterator iter = node.iterateAxis(Axis.NAMESPACE);
209: while (true) {
210: NodeInfo ns = (NodeInfo) iter.next();
211: if (ns == null) {
212: return;
213: }
214: declareNamespace(ns.getLocalPart(), ns.getStringValue());
215: }
216: }
217:
218: /**
219: * Set the base URI in the static context
220: */
221:
222: public void setBaseURI(String baseURI) {
223: this .baseURI = baseURI;
224: }
225:
226: /**
227: * Declare a named collation
228: * @param name The name of the collation (technically, a URI)
229: * @param comparator The Java Comparator used to implement the collating sequence
230: * @param isDefault True if this is to be used as the default collation
231: */
232:
233: public void declareCollation(String name, Comparator comparator,
234: boolean isDefault) {
235: collations.put(name, comparator);
236: if (isDefault) {
237: defaultCollationName = name;
238: }
239: }
240:
241: /**
242: * Get the stack frame map containing the slot number allocations for the variables declared
243: * in this static context
244: */
245:
246: public SlotManager getStackFrameMap() {
247: return stackFrameMap;
248: }
249:
250: /**
251: * Declare a variable. A variable may be declared before an expression referring
252: * to it is compiled. Alternatively, a JAXP XPathVariableResolver may be supplied
253: * to perform the resolution. A variable that has been explicitly declared is
254: * used in preference.
255: * @param qname Lexical QName identifying the variable. The namespace prefix, if
256: * any, must have been declared before this method is called, or must be resolvable
257: * using the namespace context.
258: * @param initialValue The initial value of the variable. A Java object that can
259: * be converted to an XPath value.
260: */
261:
262: public Variable declareVariable(String qname, Object initialValue)
263: throws XPathException {
264: String prefix;
265: String localName;
266: final NameChecker checker = getConfiguration().getNameChecker();
267: try {
268: String[] parts = checker.getQNameParts(qname);
269: prefix = parts[0];
270: localName = parts[1];
271: } catch (QNameException err) {
272: throw new StaticError("Invalid QName for variable: "
273: + qname);
274: }
275: String uri = "";
276: if (!("".equals(prefix))) {
277: uri = getURIForPrefix(prefix);
278: }
279: QNameValue q = new QNameValue(prefix, uri, localName, null);
280: Variable var = Variable.make(q, getConfiguration());
281: if (initialValue instanceof ValueRepresentation) {
282: var.setXPathValue((ValueRepresentation) initialValue);
283: } else {
284: var.setValue(initialValue);
285: }
286: int fingerprint = namePool.allocate(prefix, uri, localName) & 0xfffff;
287: variables.put(new Integer(fingerprint), var);
288: stackFrameMap.allocateSlotNumber(fingerprint);
289: return var;
290: }
291:
292: /**
293: * Set an XPathVariableResolver. This is used to resolve variable references
294: * if no variable has been explicitly declared.
295: * @param resolver A JAXP 1.3 XPathVariableResolver
296: */
297:
298: public void setXPathVariableResolver(XPathVariableResolver resolver) {
299: this .variableResolver = resolver;
300: }
301:
302: /**
303: * Get the XPathVariableResolver
304: */
305:
306: public XPathVariableResolver getXPathVariableResolver() {
307: return variableResolver;
308: }
309:
310: public void setXPathFunctionResolver(
311: XPathFunctionResolver xPathFunctionResolver) {
312: if (xpathFunctionLibrary != null) {
313: xpathFunctionLibrary
314: .setXPathFunctionResolver(xPathFunctionResolver);
315: }
316: // otherwise, external functions are disabled for security reasons
317: }
318:
319: public XPathFunctionResolver getXPathFunctionResolver() {
320: if (xpathFunctionLibrary != null) {
321: return xpathFunctionLibrary.getXPathFunctionResolver();
322: } else {
323: return null;
324: }
325: }
326:
327: /**
328: * Get the NamePool used for compiling expressions
329: */
330:
331: public NamePool getNamePool() {
332: return namePool;
333: }
334:
335: /**
336: * Issue a compile-time warning. This method is used during XPath expression compilation to
337: * output warning conditions. The default implementation writes the message to System.err. To
338: * change the destination of messages, create a subclass of StandaloneContext that overrides
339: * this method.
340: */
341:
342: public void issueWarning(String s, SourceLocator locator) {
343: System.err.println(s);
344: }
345:
346: /**
347: * Get the system ID of the container of the expression. Used to construct error messages.
348: * @return "" always
349: */
350:
351: public String getSystemId() {
352: return "";
353: }
354:
355: /**
356: * Get the Base URI of the stylesheet element, for resolving any relative URI's used
357: * in the expression.
358: * Used by the document() function, resolve-uri(), etc.
359: * @return "" if no base URI has been set
360: */
361:
362: public String getBaseURI() {
363: return baseURI == null ? "" : baseURI;
364: }
365:
366: /**
367: * Get the line number of the expression within that container.
368: * Used to construct error messages.
369: * @return -1 always
370: */
371:
372: public int getLineNumber() {
373: return -1;
374: }
375:
376: /**
377: * Get the URI for a prefix, using the declared namespaces as
378: * the context for namespace resolution. The default namespace is NOT used
379: * when the prefix is empty.
380: * This method is provided for use by the XPath parser.
381: * @param prefix The prefix
382: * @throws net.sf.saxon.trans.XPathException if the prefix is not declared
383: */
384:
385: public String getURIForPrefix(String prefix) throws XPathException {
386: String uri = getURIForPrefix(prefix, false);
387: if (uri == null) {
388: throw new StaticError("Prefix " + prefix
389: + " has not been declared");
390: }
391: return uri;
392: }
393:
394: public NamespaceResolver getNamespaceResolver() {
395: return this ;
396: }
397:
398: /**
399: * Get the namespace URI corresponding to a given prefix. Return null
400: * if the prefix is not in scope. This method first searches any namespaces
401: * declared using {@link #declareNamespace(String, String)}, and then searches
402: * any namespace context supplied using {@link #setNamespaceContext(javax.xml.namespace.NamespaceContext)}.
403: * @param prefix the namespace prefix
404: * @param useDefault true if the default namespace is to be used when the
405: * prefix is ""
406: * @return the uri for the namespace, or null if the prefix is not in scope.
407: * Return "" if the prefix maps to the null namespace.
408: */
409:
410: public String getURIForPrefix(String prefix, boolean useDefault) {
411: if (prefix.equals("") && !useDefault) {
412: return "";
413: } else {
414: String uri = (String) namespaces.get(prefix);
415: if (uri == null && namespaceContext != null) {
416: return namespaceContext.getNamespaceURI(prefix);
417: } else {
418: return uri;
419: }
420: }
421: }
422:
423: /**
424: * Get an iterator over all the prefixes declared in this namespace context. This will include
425: * the default namespace (prefix="") and the XML namespace where appropriate. The iterator only
426: * covers namespaces explicitly declared using {@link #declareNamespace(String, String)}; it does not
427: * include namespaces declared using {@link #setNamespaceContext(javax.xml.namespace.NamespaceContext)},
428: * because the JAXP {@link NamespaceContext} class provides no way to discover all the namespaces
429: * available.
430: */
431:
432: public Iterator iteratePrefixes() {
433: return namespaces.keySet().iterator();
434: }
435:
436: /**
437: * Bind a variable used in an XPath Expression to the XSLVariable element in which it is declared.
438: * This method is provided for use by the XPath parser, and it should not be called by the user of
439: * the API, or overridden, unless variables are to be declared using a mechanism other than the
440: * declareVariable method of this class.
441: * <p>
442: * If the variable has been explicitly declared using {@link #declareVariable(String, Object)},
443: * that value is used; otherwise if a variable resolved has been supplied using
444: * {@link #setXPathVariableResolver(javax.xml.xpath.XPathVariableResolver)} then that is used.
445: * @throws StaticError If no variable with the given name is found, or if the value supplied
446: * for the variable cannot be converted to an XPath value.
447: */
448:
449: public VariableReference bindVariable(int fingerprint)
450: throws StaticError {
451: Variable var = (Variable) variables
452: .get(new Integer(fingerprint));
453: if (var != null) {
454: return new VariableReference(var);
455: }
456: // bindVariable is called at compile time, but the JAXP variable resolver
457: // is designed to be called at run time. So we need to create a variable now,
458: // which will call the variableResolver when called upon to return the run-time value
459: if (variableResolver != null) {
460: QNameValue qname = new QNameValue(namePool, fingerprint);
461:
462: return new VariableReference(new JAXPVariable(qname,
463: variableResolver));
464: }
465: throw new StaticError(
466: "Undeclared variable in a standalone expression");
467:
468: }
469:
470: /**
471: * Get the function library containing all the in-scope functions available in this static
472: * context
473: */
474:
475: public FunctionLibrary getFunctionLibrary() {
476: return functionLibrary;
477: }
478:
479: /**
480: * Set the function library to be used
481: */
482:
483: public void setFunctionLibrary(FunctionLibrary lib) {
484: functionLibrary = lib;
485: }
486:
487: /**
488: * Get a named collation.
489: * @return the collation identified by the given name, as set previously using declareCollation.
490: * Return null if no collation with this name is found.
491: */
492:
493: public Comparator getCollation(String name) {
494: Configuration config = getConfiguration();
495: return config.getCollationURIResolver().resolve(name,
496: getBaseURI(), config);
497: }
498:
499: /**
500: * Get the name of the default collation.
501: * @return the name of the default collation; or the name of the codepoint collation
502: * if no default collation has been defined
503: */
504:
505: public String getDefaultCollationName() {
506: if (defaultCollationName != null) {
507: return defaultCollationName;
508: } else {
509: return NamespaceConstant.CODEPOINT_COLLATION_URI;
510: }
511: }
512:
513: /**
514: * Set the default namespace for element and type names
515: */
516:
517: public void setDefaultElementNamespace(String uri) {
518: defaultElementNamespace = namePool.allocateCodeForURI(uri);
519: }
520:
521: /**
522: * Get the default XPath namespace, as a namespace code that can be looked up in the NamePool
523: */
524:
525: public short getDefaultElementNamespace() {
526: return defaultElementNamespace;
527: }
528:
529: /**
530: * Set the default function namespace
531: */
532:
533: public void setDefaultFunctionNamespace(String uri) {
534: defaultFunctionNamespace = uri;
535: }
536:
537: /**
538: * Get the default function namespace
539: */
540:
541: public String getDefaultFunctionNamespace() {
542: return defaultFunctionNamespace;
543: }
544:
545: /**
546: * Set XPath 1.0 backwards compatibility mode
547: * @param backwardsCompatible if true, expressions will be evaluated with
548: * XPath 1.0 compatibility mode set to true.
549: */
550:
551: public void setBackwardsCompatibilityMode(
552: boolean backwardsCompatible) {
553: this .backwardsCompatible = true;
554: }
555:
556: /**
557: * Determine whether Backwards Compatible Mode is used
558: * @return false; XPath 1.0 compatibility mode is not supported in the standalone
559: * XPath API
560: */
561:
562: public boolean isInBackwardsCompatibleMode() {
563: return backwardsCompatible;
564: }
565:
566: /**
567: * Import a schema. This is possible only if the schema-aware version of Saxon is being used,
568: * and if the Configuration is a SchemaAwareConfiguration. Having imported a schema, the types
569: * defined in that schema become part of the static context.
570: * @param source A Source object identifying the schema document to be loaded
571: * @throws net.sf.saxon.type.SchemaException if the schema contained in this document is invalid
572: * @throws UnsupportedOperationException if the configuration is not schema-aware
573: */
574:
575: public void importSchema(Source source) throws SchemaException {
576: config.addSchemaSource(source);
577: }
578:
579: /**
580: * Determine whether a Schema for a given target namespace has been imported. Note that the
581: * in-scope element declarations, attribute declarations and schema types are the types registered
582: * with the (schema-aware) configuration, provided that their namespace URI is registered
583: * in the static context as being an imported schema namespace. (A consequence of this is that
584: * within a Configuration, there can only be one schema for any given namespace, including the
585: * null namespace).
586: * @return This implementation always returns false: the standalone XPath API does not support
587: * schema-aware processing.
588: */
589:
590: public boolean isImportedSchema(String namespace) {
591: return config.getSchema(namespace) != null;
592: }
593:
594: /**
595: * Get the set of imported schemas
596: *
597: * @return a Set, the set of URIs representing the names of imported schemas
598: */
599:
600: public Set getImportedSchemaNamespaces() {
601: return config.getImportedNamespaces();
602: }
603:
604: /**
605: * Determine whether a built-in type is available in this context. This method caters for differences
606: * between host languages as to which set of types are built in.
607: *
608: * @param type the supposedly built-in type. This will always be a type in the
609: * XS or XDT namespace.
610: * @return true if this type can be used in this static context
611: */
612:
613: public boolean isAllowedBuiltInType(AtomicType type) {
614: return true;
615: }
616: }
617:
618: //
619: // The contents of this file are subject to the Mozilla Public License Version 1.0 (the "License");
620: // you may not use this file except in compliance with the License. You may obtain a copy of the
621: // License at http://www.mozilla.org/MPL/
622: //
623: // Software distributed under the License is distributed on an "AS IS" basis,
624: // WITHOUT WARRANTY OF ANY KIND, either express or implied.
625: // See the License for the specific language governing rights and limitations under the License.
626: //
627: // The Original Code is: all this file.
628: //
629: // The Initial Developer of the Original Code is Michael H. Kay
630: //
631: // Portions created by (your name) are Copyright (C) (your legal entity). All Rights Reserved.
632: //
633: // Contributor(s): none.
634: //
|