KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > net > sf > saxon > instruct > UserFunction


1 package net.sf.saxon.instruct;
2
3 import net.sf.saxon.Controller;
4 import net.sf.saxon.expr.Expression;
5 import net.sf.saxon.expr.ExpressionTool;
6 import net.sf.saxon.expr.UserFunctionCall;
7 import net.sf.saxon.expr.XPathContextMajor;
8 import net.sf.saxon.om.*;
9 import net.sf.saxon.style.StandardNames;
10 import net.sf.saxon.trace.InstructionInfo;
11 import net.sf.saxon.trace.InstructionInfoProvider;
12 import net.sf.saxon.trans.XPathException;
13 import net.sf.saxon.type.Type;
14 import net.sf.saxon.value.SequenceType;
15 import net.sf.saxon.value.Value;
16
17 import java.util.HashMap JavaDoc;
18
19 /**
20  * This object represents the compiled form of a user-written function
21  * (the source can be either an XSLT stylesheet function or an XQuery function).
22  *
23  * <p>It is assumed that type-checking, of both the arguments and the results,
24  * has been handled at compile time. That is, the expression supplied as the body
25  * of the function must be wrapped in code to check or convert the result to the
26  * required type, and calls on the function must be wrapped at compile time to check or
27  * convert the supplied arguments.
28  */

29
30 public final class UserFunction extends Procedure implements InstructionInfoProvider {
31
32     private int functionNameCode;
33     private boolean memoFunction = false;
34     private boolean tailRecursive = false;
35             // this actually means the function contains tail calls,
36
// they are not necessarily recursive calls
37
private UserFunctionParameter[] parameterDefinitions;
38     private SequenceType resultType;
39     private transient InstructionDetails details = null;
40
41     public UserFunction() {}
42
43     public UserFunction(Expression body) {
44         setBody(body);
45     };
46
47     public void setParameterDefinitions(UserFunctionParameter[] params) {
48         this.parameterDefinitions = params;
49     }
50
51     public UserFunctionParameter[] getParameterDefinitions() {
52         return parameterDefinitions;
53     }
54
55     public void setResultType(SequenceType resultType) {
56         this.resultType = resultType;
57     }
58
59     public void setTailRecursive(boolean tailCalls) {
60         tailRecursive = tailCalls;
61     }
62
63     /**
64      * Get the type of value returned by this function
65      * @return the declared result type, or the inferred result type
66      * if this is more precise
67      */

68     public SequenceType getResultType() {
69         return resultType;
70     }
71
72     /**
73      * Get the required types of an argument to this function
74      * @param n identifies the argument in question, starting at 0
75      * @return a SequenceType object, indicating the required type of the argument
76      */

77
78     public SequenceType getArgumentType(int n) {
79         return parameterDefinitions[n].getRequiredType();
80     }
81
82     /**
83      * Get the arity of this function
84      * @return the number of arguments
85      */

86
87     public int getNumberOfArguments() {
88         return parameterDefinitions.length;
89     }
90
91     /**
92      * Mark this function as a memo function (or not)
93      * @param isMemo true if this is a memo function
94      */

95
96     public void setMemoFunction(boolean isMemo) {
97         memoFunction = isMemo;
98     }
99
100     /**
101      * Set the namepool name code of the function
102      * @param nameCode represents the function name
103      */

104
105     public void setFunctionNameCode(int nameCode) {
106         functionNameCode = nameCode;
107     }
108
109     /**
110      * Get the namepool name code of the function
111      * @return a name code representing the function name
112      */

113
114     public int getFunctionNameCode() {
115         return functionNameCode;
116     }
117
118     /**
119      * Call this function.
120      * @param actualArgs the arguments supplied to the function. These must have the correct
121      * types required by the function signature (it is the caller's responsibility to check this).
122      * It is acceptable to supply a {@link net.sf.saxon.value.Closure} to represent a value whose
123      * evaluation will be delayed until it is needed. The array must be the correct size to match
124      * the number of arguments: again, it is the caller's responsibility to check this.
125      * @param context This provides the run-time context for evaluating the function. It is the caller's
126      * responsibility to allocate a "clean" context for the function to use; the context that is provided
127      * will be overwritten by the function.
128      * @param evaluateTailCalls if true, then any function calls contained in the body of the function
129      * are evaluated in the normal way, whether or not they are marked as tail calls. If the argument
130      * is false, then tail calls are not evaluated, and instead a FunctionCallPackage is returned containing
131      * the information needed to evaluate the function. The caller must then be prepared to deal with this
132      * returned value by evaluating the packaged function call (which may return further packaged function
133      * calls, and so on).
134      * @return a Value representing the result of the function.
135      */

136
137     public ValueRepresentation call(ValueRepresentation[] actualArgs, XPathContextMajor context, boolean evaluateTailCalls)
138             throws XPathException {
139
140         // If this is a memo function, see if the result is already known
141
Controller controller = context.getController();
142         if (memoFunction) {
143             ValueRepresentation value = getCachedValue(controller, actualArgs);
144             if (value != null) return value;
145         }
146
147         // Otherwise evaluate the function
148

149         context.setStackFrame(getStackFrameMap(), actualArgs);
150         ValueRepresentation result;
151         try {
152             if (tailRecursive || memoFunction) {
153                 // If this function contains tail calls, we evaluate it eagerly, because
154
// the caller needs to know whether a tail call was returned or not: if we
155
// return a Closure, the tail call escapes into the wild and can reappear anywhere...
156
// Eager evaluation also makes sense if it's a memo function.
157
result = ExpressionTool.eagerEvaluate(getBody(), context);
158             } else {
159                 result = ExpressionTool.lazyEvaluate(getBody(), context, 1);
160             }
161         } catch (XPathException err) {
162             if (err.getLocator() == null) {
163                 err.setLocator(this);
164             }
165             throw err;
166         }
167
168         if (evaluateTailCalls) {
169             while (result instanceof UserFunctionCall.FunctionCallPackage) {
170                 result = ((UserFunctionCall.FunctionCallPackage)result).call();
171             }
172         }
173
174         // If this is a memo function, save the result in the cache
175
if (memoFunction) {
176             putCachedValue(controller, actualArgs, result);
177         }
178
179         return result;
180     }
181
182     /**
183      * Call this function. This method allows an XQuery function to be called directly from a Java
184      * application. It creates the environment needed to achieve this
185      * @param actualArgs the arguments supplied to the function. These must have the correct
186      * types required by the function signature (it is the caller's responsibility to check this).
187      * It is acceptable to supply a {@link net.sf.saxon.value.Closure} to represent a value whose
188      * evaluation will be delayed until it is needed. The array must be the correct size to match
189      * the number of arguments: again, it is the caller's responsibility to check this.
190      * @param controller This provides the run-time context for evaluating the function. A Controller
191      * may be obtained by calling {@link net.sf.saxon.query.XQueryExpression#newController}. This may
192      * be used for a series of calls on functions defined in the same module as the XQueryExpression.
193      * @return a Value representing the result of the function.
194      */

195
196     public ValueRepresentation call(ValueRepresentation[] actualArgs, Controller controller) throws XPathException {
197         return call(actualArgs, controller.newXPathContext(), true);
198     }
199
200     /**
201      * For memo functions, get a saved value from the cache.
202      * @return the cached value, or null if no value has been saved for these parameters
203      */

204
205     private ValueRepresentation getCachedValue(Controller controller, ValueRepresentation[] params) throws XPathException {
206         HashMap JavaDoc map = (HashMap JavaDoc) controller.getUserData(this, "memo-function-cache");
207         if (map == null) {
208             return null;
209         }
210         String JavaDoc key = getCombinedKey(params);
211         //System.err.println("Used cached value");
212
return (ValueRepresentation) map.get(key);
213     }
214
215     /**
216      * For memo functions, put the computed value in the cache.
217      */

218
219     private void putCachedValue(Controller controller, ValueRepresentation[] params, ValueRepresentation value) throws XPathException {
220         HashMap JavaDoc map = (HashMap JavaDoc) controller.getUserData(this, "memo-function-cache");
221         if (map == null) {
222             map = new HashMap JavaDoc(32);
223             controller.setUserData(this, "memo-function-cache", map);
224         }
225         String JavaDoc key = getCombinedKey(params);
226         map.put(key, value);
227     }
228
229     /**
230      * Get a key value representing the values of all the supplied arguments
231      */

232
233     private static String JavaDoc getCombinedKey(ValueRepresentation[] params) throws XPathException {
234         FastStringBuffer sb = new FastStringBuffer(120);
235
236         for (int i = 0; i < params.length; i++) {
237             ValueRepresentation val = params[i];
238             // TODO: if the argument value is a sequence, use the identity of the sequence, not its content
239
SequenceIterator iter = Value.getIterator(val);
240             while (true) {
241                 Item item = iter.next();
242                 if (item == null) {
243                     break;
244                 }
245                 if (item instanceof NodeInfo) {
246                     NodeInfo node = (NodeInfo) item;
247                     //sb.append(""+node.getDocumentRoot().getDocumentNumber());
248
//sb.append('/');
249
sb.append(node.generateId());
250                 } else {
251                     sb.append("" + Type.displayTypeName(item));
252                     sb.append('/');
253                     sb.append(item.getStringValueCS());
254                 }
255                 sb.append('\u0001');
256             }
257             sb.append('\u0002');
258         }
259         return sb.toString();
260     }
261
262     /**
263      * Get the InstructionInfo details about the construct. This information isn't used for tracing,
264      * but it is available when inspecting the context stack.
265      */

266
267     public InstructionInfo getInstructionInfo() {
268         if (details == null) {
269             details = new InstructionDetails();
270             details.setSystemId(getSystemId());
271             details.setLineNumber(getLineNumber());
272             details.setConstructType(StandardNames.XSL_FUNCTION);
273             details.setObjectNameCode(functionNameCode);
274             details.setProperty("function", this);
275         }
276         return details;
277     }
278 }
279
280
281 //
282
// The contents of this file are subject to the Mozilla Public License Version 1.0 (the "License");
283
// you may not use this file except in compliance with the License. You may obtain a copy of the
284
// License at http://www.mozilla.org/MPL/
285
//
286
// Software distributed under the License is distributed on an "AS IS" basis,
287
// WITHOUT WARRANTY OF ANY KIND, either express or implied.
288
// See the License for the specific language governing rights and limitations under the License.
289
//
290
// The Original Code is: all this file.
291
//
292
// The Initial Developer of the Original Code is Michael H. Kay
293
//
294
// Portions created by (your name) are Copyright (C) (your legal entity). All Rights Reserved.
295
//
296
// Contributor(s): none
297
//
298
Popular Tags