KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > net > sf > saxon > query > XQueryFunctionLibrary


1 package net.sf.saxon.query;
2 import net.sf.saxon.Configuration;
3 import net.sf.saxon.expr.Expression;
4 import net.sf.saxon.expr.ExpressionTool;
5 import net.sf.saxon.expr.UserFunctionCall;
6 import net.sf.saxon.functions.FunctionLibrary;
7 import net.sf.saxon.instruct.UserFunction;
8 import net.sf.saxon.om.NamePool;
9 import net.sf.saxon.trans.StaticError;
10 import net.sf.saxon.trans.XPathException;
11
12 import java.util.ArrayList JavaDoc;
13 import java.util.HashMap JavaDoc;
14 import java.util.Iterator JavaDoc;
15 import java.util.List JavaDoc;
16
17 /**
18  * An XQueryFunctionLibrary is a function library containing all the user-defined functions available for use within a
19  * particular XQuery module: that is, the functions declared in that module, and the functions imported from other
20  * modules. It also contains (transiently during compilation) a list of function calls within the module that have not
21  * yet been bound to a specific function declaration.
22 */

23
24 public class XQueryFunctionLibrary implements FunctionLibrary {
25
26     private Configuration config;
27     private HashMap JavaDoc functions = new HashMap JavaDoc(20);
28     private List JavaDoc unboundFunctionCalls = new ArrayList JavaDoc(20);
29     private boolean allowForwardsReferences = false;
30
31     /**
32     * Create an XQueryFunctionLibrary
33     */

34
35     public XQueryFunctionLibrary(Configuration config, boolean allowForwardsReferences) {
36         this.config = config;
37         this.allowForwardsReferences = allowForwardsReferences;
38     }
39
40     /**
41      * Set the Configuration options
42      */

43
44     public void setConfiguration(Configuration config) {
45         this.config = config;
46     }
47
48     /**
49      * Get the Configuration options
50      */

51
52     public Configuration getConfiguration() {
53         return config;
54     }
55
56     private NamePool getNamePool() {
57         return config.getNamePool();
58     }
59
60     /**
61      * Disallow any further forwards references
62      */

63
64     public void setAllowForwardsReferences(boolean allow) {
65         allowForwardsReferences = allow;
66     }
67
68     /**
69      * Register a user-defined XQuery function
70      */

71
72     public void declareFunction(XQueryFunction function) throws StaticError {
73         int fp = function.getFunctionFingerprint();
74         int arity = function.getNumberOfArguments();
75         Long JavaDoc keyObj = functionKey(fp, arity);
76         if (functions.get(keyObj) != null) {
77             XQueryFunction old = (XQueryFunction)functions.get(keyObj);
78             StaticError err = new StaticError("Duplicate definition of function " +
79                     getNamePool().getDisplayName(fp) +
80                     " (see line " + old.getLineNumber() + " in " + old.getSystemId() + ')');
81             err.setErrorCode("XQST0034");
82             err.setLocator(function);
83             throw err;
84         }
85         functions.put(keyObj, function);
86     }
87
88     /**
89      * Compute a unique key for a function based on its name and arity
90      * @param fp the fingerprint of the function name
91      * @param arity the arity (number of arguments) of the function
92      * @return a unique key used to identify the function
93      */

94     private static Long JavaDoc functionKey(int fp, int arity) {
95         return new Long JavaDoc((((long)arity)<<32) + (long)fp);
96     }
97
98     /**
99      * Test whether a function with a given name and arity is available. This supports
100      * the function-available() function in XSLT. This method may be called either at compile time
101      * or at run time.
102      * @param uri The URI of the function name
103      * @param local The local part of the function name
104      * @param arity The number of arguments. This is set to -1 in the case of the single-argument
105      * function-available() function; in this case the method should return true if there is some
106      * matching extension function, regardless of its arity.
107      */

108
109     public boolean isAvailable(int fingerprint, String JavaDoc uri, String JavaDoc local, int arity) {
110         if (arity == -1) {
111             // we'll just test arity 0..20. Since this method is supporting an XSLT-only interrogative
112
// on an XQuery function library, the outcome isn't too important.
113
for (int i=0; i<20; i++) {
114                 if (isAvailable(fingerprint, uri, local, i)) {
115                     return true;
116                 }
117             }
118             return false;
119         }
120
121         return (functions.get(functionKey(fingerprint, arity)) != null);
122     }
123
124     /**
125      * Identify a (namespace-prefixed) function appearing in the expression. This
126      * method is called by the XQuery parser to resolve function calls found within
127      * the query.
128      * <p>Note that a function call may appear earlier in the query than the definition
129      * of the function to which it is bound. Unlike XSLT, we cannot search forwards to
130      * find the function definition. Binding of function calls is therefore a two-stage
131      * process; at the time the function call is parsed, we simply register it as
132      * pending; subsequently at the end of query parsing all the pending function
133      * calls are resolved. Another consequence of this is that we cannot tell at the time
134      * a function call is parsed whether it is a call to an internal (XSLT or XQuery)
135      * function or to an extension function written in Java.
136      * @return an Expression representing the function call. This will normally be
137      * a FunctionCall, but it may be rewritten as some other expression.
138      * @throws XPathException if the function call is invalid, either because it is
139      * an unprefixed call to a non-system function, or because it is calling a system
140      * function that is available in XSLT only. A prefixed function call that cannot
141      * be recognized at this stage is assumed to be a forwards reference, and is bound
142      * later when bindUnboundFunctionCalls() is called.
143     */

144
145     public Expression bind(int nameCode, String JavaDoc uri, String JavaDoc local, Expression[] arguments) throws XPathException {
146         int fp = getNamePool().allocate("", uri, local) & 0xfffff;
147         XQueryFunction fd = (XQueryFunction)functions.get(functionKey(fp, arguments.length));
148         if (fd != null) {
149             UserFunctionCall ufc = new UserFunctionCall();
150             ufc.setFunctionNameCode(nameCode);
151             ufc.setArguments(arguments);
152             ufc.setStaticType(fd.getResultType());
153             UserFunction fn = fd.getUserFunction();
154             if (fn == null) {
155                 // not yet compiled
156
fd.registerReference(ufc);
157                 ufc.setConfirmed(true);
158             } else {
159                 ufc.setFunction(fn, fd.getStaticContext());
160                 ufc.checkFunctionCall(fn, fd.getStaticContext());
161             }
162             return ufc;
163         } else if (allowForwardsReferences) {
164             UserFunctionCall ufc = new UserFunctionCall();
165             ufc.setFunctionNameCode(nameCode);
166             ufc.setArguments(arguments);
167             unboundFunctionCalls.add(ufc);
168             return ufc;
169         } else {
170             return null;
171         }
172     }
173
174
175     /**
176      * Bind function calls that could not be bound when first encountered. These
177      * will either be forwards references to functions declared later in the query,
178      * or errors. This method is for internal use.
179      * @throws net.sf.saxon.trans.StaticError if a function call refers to a function that has
180      * not been declared
181      */

182
183     public void bindUnboundFunctionCalls() throws StaticError {
184         Iterator JavaDoc iter = unboundFunctionCalls.iterator();
185         while (iter.hasNext()) {
186             UserFunctionCall ufc = (UserFunctionCall)iter.next();
187             int fp = ufc.getFunctionNameCode() & 0xfffff;
188             int arity = ufc.getNumberOfArguments();
189
190             XQueryFunction fd = (XQueryFunction)functions.get(functionKey(fp, arity));
191             if (fd != null) {
192                 ufc.setStaticType(fd.getResultType());
193                 fd.registerReference(ufc);
194             } else {
195                 String JavaDoc msg = "Cannot find a matching " + arity +
196                                     "-argument function named " + getNamePool().getClarkName(fp) + "()";
197                 if (!config.isAllowExternalFunctions()) {
198                     msg += ". Note: external function calls have been disabled";
199                 }
200                 StaticError err = new StaticError(msg,
201                         ExpressionTool.getLocator(ufc));
202                 err.setErrorCode("XPST0017");
203                 throw err;
204             }
205         }
206     }
207
208     /**
209      * Get an iterator over the Functions defined in this module
210      * @return an Iterator, whose items are {@link XQueryFunction} objects. It returns
211      * all function known to this module including those imported from elsewhere; they
212      * can be distinguished by their namespace.
213      */

214
215     public Iterator JavaDoc getFunctionDefinitions() {
216         return functions.values().iterator();
217     }
218
219     /**
220      * Fixup all references to global functions. This method is called
221      * on completion of query parsing. Each XQueryFunction is required to
222      * bind all references to that function to the object representing the run-time
223      * executable code of the function.
224      * <p>
225      * This method is for internal use.
226      */

227
228     protected void fixupGlobalFunctions(StaticQueryContext env) throws XPathException {
229         Iterator JavaDoc iter = functions.values().iterator();
230         while (iter.hasNext()) {
231             XQueryFunction fn = (XQueryFunction)iter.next();
232             fn.compile(env);
233         }
234         iter = functions.values().iterator();
235         while (iter.hasNext()) {
236             XQueryFunction fn = (XQueryFunction)iter.next();
237             fn.checkReferences(env);
238         }
239     }
240
241     /**
242      * Output "explain" information about each declared function
243      */

244
245      public void explainGlobalFunctions() throws XPathException {
246         Iterator JavaDoc iter = functions.values().iterator();
247         while (iter.hasNext()) {
248             XQueryFunction fn = (XQueryFunction)iter.next();
249             fn.explain(getNamePool());
250         }
251     }
252
253     /**
254      * Get the function with a given name and arity. This method is provided so that XQuery functions
255      * can be called directly from a Java application. Note that there is no type checking or conversion
256      * of arguments when this is done: the arguments must be provided in exactly the form that the function
257      * signature declares them.
258      * @param uri the uri of the function name
259      * @param localName the local part of the function name
260      * @param arity the number of arguments.
261      */

262
263     public UserFunction getUserDefinedFunction(String JavaDoc uri, String JavaDoc localName, int arity) {
264         int fp = getNamePool().allocate("", uri, localName) & 0xfffff;
265         XQueryFunction function = (XQueryFunction)functions.get(functionKey(fp, arity));
266         if (function==null) {
267             return null;
268         }
269         return function.getUserFunction();
270     }
271
272     /**
273      * This method creates a copy of a FunctionLibrary: if the original FunctionLibrary allows
274      * new functions to be added, then additions to this copy will not affect the original, or
275      * vice versa.
276      *
277      * @return a copy of this function library. This must be an instance of the original class.
278      */

279
280     public FunctionLibrary copy() {
281         XQueryFunctionLibrary qfl = new XQueryFunctionLibrary(config, allowForwardsReferences);
282         qfl.functions = new HashMap JavaDoc(functions);
283         qfl.unboundFunctionCalls = new ArrayList JavaDoc(unboundFunctionCalls);
284         return qfl;
285     }
286
287 }
288
289 //
290
// The contents of this file are subject to the Mozilla Public License Version 1.0 (the "License");
291
// you may not use this file except in compliance with the License. You may obtain a copy of the
292
// License at http://www.mozilla.org/MPL/
293
//
294
// Software distributed under the License is distributed on an "AS IS" basis,
295
// WITHOUT WARRANTY OF ANY KIND, either express or implied.
296
// See the License for the specific language governing rights and limitations under the License.
297
//
298
// The Original Code is: all this file.
299
//
300
// The Initial Developer of the Original Code is Michael H. Kay.
301
//
302
// Portions created by (your name) are Copyright (C) (your legal entity). All Rights Reserved.
303
//
304
// Contributor(s): none.
305
//
306
Popular Tags