001: package net.sf.saxon.query;
002:
003: import net.sf.saxon.Configuration;
004: import net.sf.saxon.expr.Expression;
005: import net.sf.saxon.expr.UserFunctionCall;
006: import net.sf.saxon.functions.FunctionLibrary;
007: import net.sf.saxon.instruct.UserFunction;
008: import net.sf.saxon.om.NamePool;
009: import net.sf.saxon.trans.StaticError;
010: import net.sf.saxon.trans.XPathException;
011:
012: import java.util.HashMap;
013: import java.util.Iterator;
014:
015: /**
016: * An XQueryFunctionLibrary is a function library containing all the user-defined functions available for use within a
017: * particular XQuery module: that is, the functions declared in that module, and the functions imported from other
018: * modules. It also contains (transiently during compilation) a list of function calls within the module that have not
019: * yet been bound to a specific function declaration.
020: */
021:
022: public class XQueryFunctionLibrary implements FunctionLibrary,
023: XQueryFunctionBinder {
024:
025: private Configuration config;
026:
027: // The functions in this library are represented using a HashMap
028: // The key of the hashmap is a Long whose two halves hold (a) the fingerprint, and (b) the arity
029: // The valye in the hashmap is an XQueryFunction
030: private HashMap functions = new HashMap(20);
031:
032: /**
033: * Create an XQueryFunctionLibrary
034: */
035:
036: public XQueryFunctionLibrary(Configuration config) {
037: this .config = config;
038: }
039:
040: /**
041: * Set the Configuration options
042: */
043:
044: public void setConfiguration(Configuration config) {
045: this .config = config;
046: }
047:
048: /**
049: * Get the Configuration options
050: */
051:
052: public Configuration getConfiguration() {
053: return config;
054: }
055:
056: private NamePool getNamePool() {
057: return config.getNamePool();
058: }
059:
060: /**
061: * Register a user-defined XQuery function
062: */
063:
064: public void declareFunction(XQueryFunction function)
065: throws StaticError {
066: int fp = function.getFunctionFingerprint();
067: int arity = function.getNumberOfArguments();
068: Long keyObj = functionKey(fp, arity);
069: if (functions.get(keyObj) != null) {
070: XQueryFunction old = (XQueryFunction) functions.get(keyObj);
071: StaticError err = new StaticError(
072: "Duplicate definition of function "
073: + getNamePool().getDisplayName(fp)
074: + " (see line " + old.getLineNumber()
075: + " in " + old.getSystemId() + ')');
076: err.setErrorCode("XQST0034");
077: err.setLocator(function);
078: throw err;
079: }
080: functions.put(keyObj, function);
081: }
082:
083: /**
084: * Compute a unique key for a function based on its name and arity
085: * @param fp the fingerprint of the function name
086: * @param arity the arity (number of arguments) of the function
087: * @return a unique key used to identify the function
088: */
089: private static Long functionKey(int fp, int arity) {
090: return new Long((((long) arity) << 32) + (long) fp);
091: }
092:
093: /**
094: * Test whether a function with a given name and arity is available. This supports
095: * the function-available() function in XSLT. This method may be called either at compile time
096: * or at run time.
097: * @param uri The URI of the function name
098: * @param local The local part of the function name
099: * @param arity The number of arguments. This is set to -1 in the case of the single-argument
100: * function-available() function; in this case the method should return true if there is some
101: * matching extension function, regardless of its arity.
102: */
103:
104: public boolean isAvailable(int fingerprint, String uri,
105: String local, int arity) {
106: if (arity == -1) {
107: // we'll just test arity 0..20. Since this method is supporting an XSLT-only interrogative
108: // on an XQuery function library, the outcome isn't too important.
109: for (int i = 0; i < 20; i++) {
110: if (isAvailable(fingerprint, uri, local, i)) {
111: return true;
112: }
113: }
114: return false;
115: }
116:
117: return (functions.get(functionKey(fingerprint, arity)) != null);
118: }
119:
120: /**
121: * Identify a (namespace-prefixed) function appearing in the expression. This
122: * method is called by the XQuery parser to resolve function calls found within
123: * the query.
124: * <p>Note that a function call may appear earlier in the query than the definition
125: * of the function to which it is bound. Unlike XSLT, we cannot search forwards to
126: * find the function definition. Binding of function calls is therefore a two-stage
127: * process; at the time the function call is parsed, we simply register it as
128: * pending; subsequently at the end of query parsing all the pending function
129: * calls are resolved. Another consequence of this is that we cannot tell at the time
130: * a function call is parsed whether it is a call to an internal (XSLT or XQuery)
131: * function or to an extension function written in Java.
132: * @return an Expression representing the function call. This will normally be
133: * a FunctionCall, but it may be rewritten as some other expression.
134: * @throws XPathException if the function call is invalid, either because it is
135: * an unprefixed call to a non-system function, or because it is calling a system
136: * function that is available in XSLT only. A prefixed function call that cannot
137: * be recognized at this stage is assumed to be a forwards reference, and is bound
138: * later when bindUnboundFunctionCalls() is called.
139: */
140:
141: public Expression bind(int nameCode, String uri, String local,
142: Expression[] arguments) throws XPathException {
143: int fp = nameCode & 0xfffff;
144: XQueryFunction fd = (XQueryFunction) functions.get(functionKey(
145: fp, arguments.length));
146: if (fd != null) {
147: UserFunctionCall ufc = new UserFunctionCall();
148: ufc.setFunctionNameCode(nameCode);
149: ufc.setArguments(arguments);
150: ufc.setStaticType(fd.getResultType());
151: UserFunction fn = fd.getUserFunction();
152: if (fn == null) {
153: // not yet compiled
154: fd.registerReference(ufc);
155: ufc.setConfirmed(true);
156: } else {
157: ufc.setFunction(fn, fd.getStaticContext());
158: ufc.checkFunctionCall(fn, fd.getStaticContext());
159: }
160: return ufc;
161: } else {
162: return null;
163: }
164: }
165:
166: /**
167: * Get the function declaration corresponding to a given function name and arity
168: * @return the XQueryFunction if there is one, or null if not.
169: */
170:
171: public XQueryFunction getDeclaration(int nameCode, String uri,
172: String local, Expression[] staticArgs) {
173: return (XQueryFunction) functions.get(functionKey(nameCode
174: & NamePool.FP_MASK, staticArgs.length));
175: }
176:
177: /**
178: * Get an iterator over the Functions defined in this module
179: * @return an Iterator, whose items are {@link XQueryFunction} objects. It returns
180: * all function known to this module including those imported from elsewhere; they
181: * can be distinguished by their namespace.
182: */
183:
184: public Iterator getFunctionDefinitions() {
185: return functions.values().iterator();
186: }
187:
188: /**
189: * Fixup all references to global functions. This method is called
190: * on completion of query parsing. Each XQueryFunction is required to
191: * bind all references to that function to the object representing the run-time
192: * executable code of the function.
193: * <p>
194: * This method is for internal use.
195: */
196:
197: protected void fixupGlobalFunctions(StaticQueryContext env)
198: throws XPathException {
199: Iterator iter = functions.values().iterator();
200: while (iter.hasNext()) {
201: XQueryFunction fn = (XQueryFunction) iter.next();
202: fn.compile(env);
203: }
204: iter = functions.values().iterator();
205: while (iter.hasNext()) {
206: XQueryFunction fn = (XQueryFunction) iter.next();
207: fn.checkReferences(env);
208: }
209: }
210:
211: /**
212: * Output "explain" information about each declared function
213: */
214:
215: public void explainGlobalFunctions() throws XPathException {
216: Iterator iter = functions.values().iterator();
217: while (iter.hasNext()) {
218: XQueryFunction fn = (XQueryFunction) iter.next();
219: fn.explain(getNamePool());
220: }
221: }
222:
223: /**
224: * Get the function with a given name and arity. This method is provided so that XQuery functions
225: * can be called directly from a Java application. Note that there is no type checking or conversion
226: * of arguments when this is done: the arguments must be provided in exactly the form that the function
227: * signature declares them.
228: * @param uri the uri of the function name
229: * @param localName the local part of the function name
230: * @param arity the number of arguments.
231: */
232:
233: public UserFunction getUserDefinedFunction(String uri,
234: String localName, int arity) {
235: int fp = getNamePool().allocate("", uri, localName) & 0xfffff;
236: XQueryFunction function = (XQueryFunction) functions
237: .get(functionKey(fp, arity));
238: if (function == null) {
239: return null;
240: }
241: return function.getUserFunction();
242: }
243:
244: /**
245: * This method creates a copy of a FunctionLibrary: if the original FunctionLibrary allows
246: * new functions to be added, then additions to this copy will not affect the original, or
247: * vice versa.
248: *
249: * @return a copy of this function library. This must be an instance of the original class.
250: */
251:
252: public FunctionLibrary copy() {
253: XQueryFunctionLibrary qfl = new XQueryFunctionLibrary(config);
254: qfl.functions = new HashMap(functions);
255: return qfl;
256: }
257:
258: }
259:
260: //
261: // The contents of this file are subject to the Mozilla Public License Version 1.0 (the "License");
262: // you may not use this file except in compliance with the License. You may obtain a copy of the
263: // License at http://www.mozilla.org/MPL/
264: //
265: // Software distributed under the License is distributed on an "AS IS" basis,
266: // WITHOUT WARRANTY OF ANY KIND, either express or implied.
267: // See the License for the specific language governing rights and limitations under the License.
268: //
269: // The Original Code is: all this file.
270: //
271: // The Initial Developer of the Original Code is Michael H. Kay.
272: //
273: // Portions created by (your name) are Copyright (C) (your legal entity). All Rights Reserved.
274: //
275: // Contributor(s): none.
276: //
|