KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > net > sf > saxon > expr > UserFunctionCall


1 package net.sf.saxon.expr;
2 import net.sf.saxon.instruct.InstructionDetails;
3 import net.sf.saxon.instruct.UserFunction;
4 import net.sf.saxon.om.*;
5 import net.sf.saxon.trace.InstructionInfo;
6 import net.sf.saxon.trace.InstructionInfoProvider;
7 import net.sf.saxon.trace.Location;
8 import net.sf.saxon.trans.DynamicError;
9 import net.sf.saxon.trans.XPathException;
10 import net.sf.saxon.type.AnyItemType;
11 import net.sf.saxon.type.ItemType;
12 import net.sf.saxon.value.*;
13
14 import java.io.PrintStream JavaDoc;
15
16
17 /**
18  * This class represents a call to a function defined in the stylesheet or query.
19  * It is used for all user-defined functions in XQuery, and for a limited class of
20  * user-defined functions in XSLT: those that can be reduced to the evaluation
21  * of a single expression.
22 */

23
24 public class UserFunctionCall extends FunctionCall implements InstructionInfoProvider {
25
26     private SequenceType staticType;
27     private UserFunction function;
28     private boolean tailRecursive = false;
29     private boolean confirmed = false;
30         // A functionCall is confirmed if the function being called has been located. Generally in this
31
// case the value of 'function' will be non-null; but in XSLT it is possible to look-ahead to confirm
32
// a function binding before the relevant function is actually compiled.
33

34     public UserFunctionCall() {}
35
36     /**
37     * Set the static type
38     */

39
40     public void setStaticType(SequenceType type) {
41         staticType = type;
42     }
43
44     /**
45     * Create the reference to the function to be called, and validate for consistency
46     */

47
48     public void setFunction(UserFunction compiledFunction,
49                             StaticContext env) throws XPathException {
50         function = compiledFunction;
51         confirmed = true;
52     }
53
54     /**
55     * Check the function call against the declared function signature
56     */

57
58     public void checkFunctionCall(UserFunction compiledFunction,
59                             StaticContext env) throws XPathException {
60         int n = compiledFunction.getNumberOfArguments();
61         for (int i=0; i<n; i++) {
62             RoleLocator role = new RoleLocator(
63                     RoleLocator.FUNCTION, new Integer JavaDoc(compiledFunction.getFunctionNameCode()), i, env.getNamePool());
64             role.setSourceLocator(this);
65             argument[i] = TypeChecker.staticTypeCheck(
66                                 argument[i],
67                                 compiledFunction.getArgumentType(i),
68                                 false,
69                                 role, env);
70         }
71     }
72
73
74     /**
75      * Get the function that is being called by this function call
76      */

77
78     public UserFunction getFunction() {
79         return function;
80     }
81
82     /**
83      * Set this function as confirmed (the function being called is known to exist) or not
84      */

85
86     public void setConfirmed(boolean conf) {
87         confirmed = conf;
88     }
89
90     /**
91      * Determine whether this function call is confirmed
92      */

93
94     public boolean isConfirmed() {
95         return confirmed;
96     }
97
98     /**
99     * Method called during the type checking phase
100     */

101
102     public void checkArguments(StaticContext env) throws XPathException {
103         // these checks are now done in setFunction(), at the time when the function
104
// call is bound to an actual function
105
}
106
107     /**
108     * Pre-evaluate a function at compile time. This version of the method suppresses
109     * early evaluation by doing nothing.
110     */

111
112     public Expression preEvaluate(StaticContext env) {
113         return this;
114     }
115
116     /**
117     * Determine the data type of the expression, if possible
118     * @return Type.ITEM (meaning not known in advance)
119     */

120
121     public ItemType getItemType() {
122         if (staticType == null) {
123             // the actual type is not known yet, so we return an approximation
124
return AnyItemType.getInstance();
125         } else {
126             return staticType.getPrimaryType();
127         }
128     }
129
130     public int getIntrinsicDependencies() {
131         return StaticProperty.DEPENDS_ON_USER_FUNCTIONS;
132     }
133
134     /**
135     * Determine the cardinality of the result
136     */

137
138     public int computeCardinality() {
139         if (staticType == null) {
140             // the actual type is not known yet, so we return an approximation
141
return StaticProperty.ALLOWS_ZERO_OR_MORE;
142         } else {
143             return staticType.getCardinality();
144         }
145     }
146
147     /**
148     * Simplify the function call
149     */

150
151      public Expression simplify(StaticContext env) throws XPathException {
152         for (int i=0; i<argument.length; i++) {
153             argument[i] = argument[i].simplify(env);
154         }
155         return this;
156     }
157
158     /**
159     * Mark tail-recursive calls on stylesheet functions. For most expressions, this does nothing.
160     */

161
162     public boolean markTailFunctionCalls() {
163         tailRecursive = true;
164         return true;
165     }
166
167     // TODO: attempt to establish whether the function is capable of creating new nodes. This
168
// enables non-creative functions to be moved out of loops. The problem is how to achieve this
169
// without looping in the case of recursive functions. A simple solution might be to go only
170
// one level deep: if the body of a function is known (without analysing function calls) to be
171
// non-creative, then all calls on that function can be marked as non-creative. Note also that
172
// a function is creative if one of its arguments is creative and the result of the function
173
// depends on the identity of that argument.
174

175     /**
176     * Call the function, returning the value as an item. This method will be used
177     * only when the cardinality is zero or one. If the function is tail recursive,
178     * it returns an Object representing the arguments to the next (recursive) call
179     */

180
181     public Item evaluateItem(XPathContext c) throws XPathException {
182         ValueRepresentation val = callFunction(c);
183         if (val instanceof Item) {
184             return (Item)val;
185         } else {
186             return Value.getIterator(val).next();
187         }
188     }
189
190     /**
191     * Call the function, returning an iterator over the results. (But if the function is
192     * tail recursive, it returns an iterator over the arguments of the recursive call)
193     */

194
195     public SequenceIterator iterate(XPathContext c) throws XPathException {
196         ValueRepresentation result = callFunction(c);
197         if (result instanceof FunctionCallPackage) {
198             return SingletonIterator.makeIterator(((FunctionCallPackage)result));
199         }
200         return Value.getIterator(result);
201     }
202
203     /**
204      * This is the method that actually does the function call
205      * @param c the dynamic context
206      * @return the result of the function
207      * @throws XPathException if dynamic errors occur
208      */

209     private ValueRepresentation callFunction(XPathContext c) throws XPathException {
210         int numArgs = argument.length;
211
212         ValueRepresentation[] actualArgs = new ValueRepresentation[numArgs];
213         for (int i=0; i<numArgs; i++) {
214             // Decide what form of lazy evaluation to use based on the number of references to the argument
215
int refs = function.getParameterDefinitions()[i].getReferenceCount();
216             if (argument[i] instanceof Value) {
217                 actualArgs[i] = (Value)argument[i];
218             } else {
219                 if (refs == 0) {
220                     // the argument is never referenced, so don't evaluate it
221
actualArgs[i] = EmptySequence.getInstance();
222                 } else if ((argument[i].getDependencies() & StaticProperty.DEPENDS_ON_USER_FUNCTIONS) != 0) {
223                     // if the argument contains a call to a user-defined function, then it might be a recursive call.
224
// It's better to evaluate it now, rather than waiting until we are on a new stack frame, as
225
// that can blow the stack if done repeatedly.
226
actualArgs[i] = ExpressionTool.eagerEvaluate(argument[i], c);
227                 } else {
228                     actualArgs[i] = ExpressionTool.lazyEvaluate(argument[i], c, refs);
229                 }
230             }
231             // If the argument has come in as a (non-memo) closure but there are multiple references to it,
232
// then we materialize it in memory now. This shouldn't really happen but it does (tour.xq)
233
if (refs > 1 && actualArgs[i] instanceof Closure && !(actualArgs[i] instanceof MemoClosure)) {
234                 actualArgs[i] = ((Closure)actualArgs[i]).reduce();
235             }
236         }
237
238         if (tailRecursive) {
239             return new FunctionCallPackage(function, actualArgs, c);
240         }
241
242         XPathContextMajor c2 = c.newCleanContext();
243         c2.setOrigin(this);
244         try {
245             return function.call(actualArgs, c2, true);
246         } catch (StackOverflowError JavaDoc err) {
247             throw new DynamicError("Too many nested function calls. May be due to infinite recursion.", this);
248         }
249     }
250
251     /**
252      * Call the function dynamically. For this to be possible, the static arguments of the function call
253      * must have been set up as SuppliedParameterReference objects. The actual arguments are placed on the
254      * callee's stack, and the type conversion takes place "in situ".
255      */

256
257     public ValueRepresentation dynamicCall(ValueRepresentation[] suppliedArguments, XPathContext context) throws XPathException {
258         ValueRepresentation[] convertedArgs = new ValueRepresentation[suppliedArguments.length];
259         XPathContextMajor c2 = context.newCleanContext();
260         c2.setOrigin(this);
261         c2.setCaller(context);
262         c2.openStackFrame(suppliedArguments.length);
263         for (int i=0; i<suppliedArguments.length; i++) {
264             c2.setLocalVariable(i, suppliedArguments[i]);
265             convertedArgs[i] = ExpressionTool.lazyEvaluate(argument[i], c2, 10);
266         }
267         XPathContextMajor c3 = c2.newCleanContext();
268         c3.setOrigin(this);
269         return function.call(convertedArgs, c3, true);
270     }
271
272     public void display(int level, NamePool pool, PrintStream JavaDoc out) {
273         out.println(ExpressionTool.indent(level) + "function " + getDisplayName(pool) +
274                 (tailRecursive ? " (tail call)" : ""));
275         for (int a=0; a<argument.length; a++) {
276             argument[a].display(level+1, pool, out);
277         }
278     }
279
280     /**
281      * Get diagnostic information about this expression
282      */

283
284     public InstructionInfo getInstructionInfo() {
285         InstructionDetails details = new InstructionDetails();
286         details.setConstructType(Location.FUNCTION_CALL);
287         details.setLineNumber(getLineNumber());
288         details.setSystemId(getSystemId());
289         details.setObjectNameCode(getFunctionNameCode());
290         details.setProperty("expression", this);
291         details.setProperty("target", function);
292         return details;
293     }
294
295     /**
296     * Inner class used to wrap up the set of actual arguments to a tail-recursive call of
297     * the containing function. This argument package is passed back to the calling code
298     * in place of a function result; the caller then loops to re-invoke the function
299     * with these arguments, avoiding the creation of an additional stack frame.
300     */

301
302     public class FunctionCallPackage extends ObjectValue {
303
304         private UserFunction function;
305         private ValueRepresentation[] actualArgs;
306         private XPathContext evaluationContext;
307
308         public FunctionCallPackage(UserFunction function, ValueRepresentation[] actualArgs, XPathContext c) {
309             super(function);
310             this.function = function;
311             this.actualArgs = actualArgs;
312             this.evaluationContext = c;
313         }
314
315         /**
316          * Determine the item type of the expression
317          */

318
319         public ItemType getItemType() {
320             return UserFunctionCall.this.getItemType();
321         }
322
323         public ValueRepresentation call() throws XPathException {
324             XPathContextMajor c2 = evaluationContext.newCleanContext();
325             c2.setOrigin(UserFunctionCall.this);
326             return function.call(actualArgs, c2, false);
327         }
328
329         public SequenceIterator iterateResults(XPathContext context) throws XPathException {
330             ValueRepresentation result = call();
331             return new MappingIterator(Value.getIterator(result), Flattener.THE_INSTANCE, context);
332         }
333
334
335 // public ValueRepresentation appendTo(SequenceReceiver out) throws XPathException {
336
// // This method has been combined with the only method that called it (Instruction.appendItem())
337
// ValueRepresentation v = call();
338
// SequenceIterator fv = Value.getIterator(v);
339
// while (true) {
340
// Item fvit = fv.next();
341
// if (fvit == null) return null;
342
// if (fvit instanceof UserFunctionCall.FunctionCallPackage) {
343
// return (Value)fvit;
344
// } else {
345
// out.append(fvit, locationId, NodeInfo.ALL_NAMESPACES);
346
// }
347
// }
348
// }
349

350          /**
351          * Reduce a value to its simplest form. If the value is a closure or some other form of deferred value
352          * such as a FunctionCallPackage, then it is reduced to a SequenceExtent. If it is a SequenceExtent containing
353          * a single item, then it is reduced to that item. One consequence that is exploited by class FilterExpression
354          * is that if the value is a singleton numeric value, then the result will be an instance of NumericValue
355          */

356
357         public Value reduce() throws XPathException {
358             return new SequenceExtent(iterateResults(null)).reduce();
359         }
360
361         /**
362          * Get the primitive value (the value in the value space). This returns an
363          * AtomicValue of a class that would be used to represent the primitive value.
364          * In effect this means that for built-in types, it returns the value itself,
365          * but for user-defined type, it returns the primitive value minus the type
366          * annotation. Note that getItemType() when applied to the result of this
367          * function does not not necessarily return a primitive type: for example, this
368          * function may return a value of type xdt:dayTimeDuration, which is not a
369          * primitive type as defined by {@link net.sf.saxon.type.Type#isPrimitiveType(int)}
370          */

371
372         public AtomicValue getPrimitiveValue() {
373             try {
374                 return ((AtomicValue)reduce()).getPrimitiveValue();
375             } catch (XPathException e) {
376                 throw new RuntimeException JavaDoc(e);
377             }
378         }
379     }
380
381     /**
382      * Mapping function that converts a sequence possibly containing embedded FunctionCallPackage items into
383      * one in which any such items are fully expanded
384      */

385
386     private static class Flattener implements MappingFunction {
387
388         public static final Flattener THE_INSTANCE = new Flattener();
389
390         private Flattener() {}
391         /**
392          * Map one item to a sequence.
393          *
394          * @param item The item to be mapped.
395          * If context is supplied, this must be the same as context.currentItem().
396          * @param context The processing context. Some mapping functions use this because they require
397          * context information. Some mapping functions modify the context by maintaining the context item
398          * and position. In other cases, the context may be null.
399          * @return either (a) a SequenceIterator over the sequence of items that the supplied input
400          * item maps to, or (b) an Item if it maps to a single item, or (c) null if it maps to an empty
401          * sequence.
402          */

403
404         public Object JavaDoc map(Item item, XPathContext context) throws XPathException {
405             if (item instanceof FunctionCallPackage) {
406                 return (((FunctionCallPackage)item).iterateResults(context));
407             } else {
408                 return item;
409             }
410         }
411
412
413     }
414
415
416
417 }
418
419 //
420
// The contents of this file are subject to the Mozilla Public License Version 1.0 (the "License");
421
// you may not use this file except in compliance with the License. You may obtain a copy of the
422
// License at http://www.mozilla.org/MPL/
423
//
424
// Software distributed under the License is distributed on an "AS IS" basis,
425
// WITHOUT WARRANTY OF ANY KIND, either express or implied.
426
// See the License for the specific language governing rights and limitations under the License.
427
//
428
// The Original Code is: all this file.
429
//
430
// The Initial Developer of the Original Code is Michael H. Kay.
431
//
432
// Portions created by (your name) are Copyright (C) (your legal entity). All Rights Reserved.
433
//
434
// Contributor(s): none.
435
//
436
Popular Tags