KickJava   Java API By Example, From Geeks To Geeks.

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


1 package net.sf.saxon.expr;
2 import net.sf.saxon.instruct.TailCall;
3 import net.sf.saxon.instruct.TailCallReturner;
4 import net.sf.saxon.om.Item;
5 import net.sf.saxon.om.NamePool;
6 import net.sf.saxon.om.SequenceIterator;
7 import net.sf.saxon.om.ValueRepresentation;
8 import net.sf.saxon.trans.XPathException;
9 import net.sf.saxon.type.ItemType;
10 import net.sf.saxon.type.SchemaType;
11 import net.sf.saxon.value.Value;
12
13 import java.io.PrintStream JavaDoc;
14
15 /**
16 * A LetExpression is modelled on the XQuery syntax let $x := expr return expr. This syntax
17 * is not available in the surface XPath language, but it is used internally in an optimized
18 * expression tree.
19 */

20
21 public class LetExpression extends Assignation implements TailCallReturner {
22
23     // This integer holds an approximation to the number of times that the declared variable is referenced.
24
// The value 1 means there is only one reference and it is not in a loop, which means that the value will
25
// not be retained in memory. If there are multiple references or references within a loop, the value will
26
// be a small integer > 1. The special value FILTERED indicates that there is a reference within a loop
27
// in the form $x[predicate], which indicates that the value should potentially be indexable.
28
int refCount;
29
30     public LetExpression() {}
31
32     /**
33     * Type-check the expression
34     */

35
36     public Expression typeCheck(StaticContext env, ItemType contextItemType) throws XPathException {
37
38         if (declaration==null) {
39             // we've already done the type checking, no need to do it again
40
return this;
41         }
42
43         // The order of events is critical here. First we ensure that the type of the
44
// sequence expression is established. This is used to establish the type of the variable,
45
// which in turn is required when type-checking the action part.
46

47         sequence = sequence.typeCheck(env, contextItemType);
48
49         RoleLocator role = new RoleLocator(RoleLocator.VARIABLE, new Integer JavaDoc(nameCode), 0, env.getNamePool());
50         role.setSourceLocator(this);
51         sequence = TypeChecker.strictTypeCheck(
52                         sequence, declaration.getRequiredType(), role, env);
53         ItemType actualItemType = sequence.getItemType();
54         declaration.refineTypeInformation(actualItemType,
55                         sequence.getCardinality(),
56                         (sequence instanceof Value ? (Value)sequence : null),
57                         sequence.getSpecialProperties());
58
59         action = action.typeCheck(env, contextItemType);
60         return this;
61     }
62
63     /**
64      * Perform optimisation of an expression and its subexpressions.
65      * <p/>
66      * <p>This method is called after all references to functions and variables have been resolved
67      * to the declaration of the function or variable, and after all type checking has been done.</p>
68      *
69      * @param opt the optimizer in use. This provides access to supporting functions; it also allows
70      * different optimization strategies to be used in different circumstances.
71      * @param env the static context of the expression
72      * @param contextItemType the static type of "." at the point where this expression is invoked.
73      * The parameter is set to null if it is known statically that the context item will be undefined.
74      * If the type of the context item is not known statically, the argument is set to
75      * {@link net.sf.saxon.type.Type#ITEM_TYPE}
76      * @return the original expression, rewritten if appropriate to optimize execution
77      * @throws net.sf.saxon.trans.StaticError if an error is discovered during this phase
78      * (typically a type error)
79      */

80
81     public Expression optimize(Optimizer opt, StaticContext env, ItemType contextItemType) throws XPathException {
82         if (declaration != null) {
83             refCount = declaration.getReferenceCount(this);
84             if (refCount == 0) {
85                 // variable is not used - no need to evaluate it
86
return action;
87             }
88             declaration = null; // let the garbage collector take it
89
}
90
91         int tries = 0;
92         while (tries++ < 5) {
93             Expression seq2 = sequence.optimize(opt, env, contextItemType);
94             if (seq2 == sequence) {
95                 break;
96             }
97             sequence = seq2;
98             adoptChildExpression(sequence);
99             resetStaticProperties();
100         }
101
102         tries = 0;
103         while (tries++ < 5) {
104             Expression act2 = action.optimize(opt, env, contextItemType);
105             if (act2 == action) {
106                 break;
107             }
108             action = act2;
109             adoptChildExpression(action);
110             resetStaticProperties();
111         }
112
113         // Try to promote any WHERE clause appearing within the LET expression
114

115         Expression p = promoteWhereClause(null);
116         if (p != null) {
117             return p;
118         }
119
120         return this;
121     }
122
123
124     /**
125      * Check that any elements and attributes constructed or returned by this expression are acceptable
126      * in the content model of a given complex type. It's always OK to say yes, since the check will be
127      * repeated at run-time. The process of checking element and attribute constructors against the content
128      * model of a complex type also registers the type of content expected of those constructors, so the
129      * static validation can continue recursively.
130      */

131
132     public void checkPermittedContents(SchemaType parentType, StaticContext env, boolean whole) throws XPathException {
133         action.checkPermittedContents(parentType, env, whole);
134     }
135
136     /**
137     * Iterate over the sequence of values
138     */

139
140     public SequenceIterator iterate(XPathContext context) throws XPathException {
141         // minimize stack consumption by evaluating nested LET expressions iteratively
142
LetExpression let = this;
143         while (true) {
144             ValueRepresentation val = let.eval(context);
145             context.setLocalVariable(let.slotNumber, val);
146             if (let.action instanceof LetExpression) {
147                 let = (LetExpression)let.action;
148             } else {
149                 break;
150             }
151         }
152         return let.action.iterate(context);
153     }
154
155     /**
156      * Evaluate the variable. (This is overridden in a subclass).
157      */

158
159     protected ValueRepresentation eval(XPathContext context) throws XPathException {
160         //System.err.println("Let line " + getLineNumber());
161
return ExpressionTool.lazyEvaluate(sequence, context, refCount);
162     }
163
164     /**
165     * Evaluate the expression as a singleton
166     */

167
168     public Item evaluateItem(XPathContext context) throws XPathException {
169         // minimize stack consumption by evaluating nested LET expressions iteratively
170
LetExpression let = this;
171         while (true) {
172             ValueRepresentation val = let.eval(context);
173             context.setLocalVariable(let.slotNumber, val);
174             if (let.action instanceof LetExpression) {
175                 let = (LetExpression)let.action;
176             } else {
177                 break;
178             }
179         }
180         return let.action.evaluateItem(context);
181     }
182
183     /**
184      * Process this expression as an instruction, writing results to the current
185      * outputter
186      */

187
188     public void process(XPathContext context) throws XPathException {
189         // minimize stack consumption by evaluating nested LET expressions iteratively
190
LetExpression let = this;
191         while (true) {
192             ValueRepresentation val = let.eval(context);
193             context.setLocalVariable(let.slotNumber, val);
194             if (let.action instanceof LetExpression) {
195                 let = (LetExpression)let.action;
196             } else {
197                 break;
198             }
199         }
200         let.action.process(context);
201     }
202
203
204     /**
205     * Determine the data type of the items returned by the expression, if possible
206     * @return one of the values Type.STRING, Type.BOOLEAN, Type.NUMBER, Type.NODE,
207     * or Type.ITEM (meaning not known in advance)
208     */

209
210     public ItemType getItemType() {
211         return action.getItemType();
212     }
213
214     /**
215     * Determine the static cardinality of the expression
216     */

217
218     public int computeCardinality() {
219         return action.getCardinality();
220     }
221
222     /**
223     * Get the static properties of this expression (other than its type). The result is
224     * bit-signficant. These properties are used for optimizations. In general, if
225     * property bit is set, it is true, but if it is unset, the value is unknown.
226     */

227
228     public int computeSpecialProperties() {
229         return action.getSpecialProperties();
230     }
231
232     /**
233      * Mark tail function calls
234      */

235
236     public boolean markTailFunctionCalls() {
237         return ExpressionTool.markTailFunctionCalls(action);
238     }
239
240     /**
241     * Promote this expression if possible
242     */

243
244     public Expression promote(PromotionOffer offer) throws XPathException {
245         Expression exp = offer.accept(this);
246         if (exp != null) {
247             return exp;
248         } else {
249             // pass the offer on to the sequence expression
250
sequence = doPromotion(sequence, offer);
251             if (offer.action == PromotionOffer.INLINE_VARIABLE_REFERENCES ||
252                     offer.action == PromotionOffer.UNORDERED ||
253                     offer.action == PromotionOffer.REPLACE_CURRENT) {
254                 action = doPromotion(action, offer);
255             } else if (offer.action == PromotionOffer.RANGE_INDEPENDENT
256 // || offer.action == PromotionOffer.WHERE_CLAUSE
257
) {
258                 // Pass the offer to the action expression only if the action isn't depending on the
259
// variable bound by this let expression
260
Binding[] savedBindingList = offer.bindingList;
261                 Binding[] newBindingList = new Binding[offer.bindingList.length+1];
262                 System.arraycopy(offer.bindingList, 0, newBindingList, 0, offer.bindingList.length);
263                 newBindingList[offer.bindingList.length] = this;
264                 offer.bindingList = newBindingList;
265                 action = doPromotion(action, offer);
266                 offer.bindingList = savedBindingList;
267             }
268             // if this results in the expression (let $x := $y return Z), replace all references to
269
// to $x by references to $y in the Z part, and eliminate this LetExpression by
270
// returning the action part.
271
if (sequence instanceof VariableReference) {
272                 action = replaceVariable(offer.getOptimizer(), sequence);
273                 return action;
274             }
275             // similarly, for (let $x := lazy($y) return Z)
276
if (sequence instanceof LazyExpression &&
277                     ((LazyExpression)sequence).getBaseExpression() instanceof VariableReference) {
278                 action = replaceVariable(offer.getOptimizer(), ((LazyExpression)sequence).getBaseExpression());
279                 return action;
280             }
281
282             return this;
283         }
284     }
285
286     private Expression replaceVariable(Optimizer opt, Expression seq) throws XPathException {
287         PromotionOffer offer2 = new PromotionOffer(opt);
288         offer2.action = PromotionOffer.INLINE_VARIABLE_REFERENCES;
289         Binding[] bindingList = {this};
290         offer2.bindingList = bindingList;
291         offer2.containingExpression = seq;
292         Expression newAction = doPromotion(action, offer2);
293         return newAction;
294     }
295
296     /**
297      * ProcessLeavingTail: called to do the real work of this instruction.
298      * The results of the instruction are written
299      * to the current Receiver, which can be obtained via the Controller.
300      *
301      * @param context The dynamic context of the transformation, giving access to the current node,
302      * the current variables, etc.
303      * @return null if the instruction has completed execution; or a TailCall indicating
304      * a function call or template call that is delegated to the caller, to be made after the stack has
305      * been unwound so as to save stack space.
306      */

307
308     public TailCall processLeavingTail(XPathContext context) throws XPathException {
309         // minimize stack consumption by evaluating nested LET expressions iteratively
310
LetExpression let = this;
311         while (true) {
312             ValueRepresentation val = let.eval(context);
313             context.setLocalVariable(let.slotNumber, val);
314             if (let.action instanceof LetExpression) {
315                 let = (LetExpression)let.action;
316             } else {
317                 break;
318             }
319         }
320         if (let.action instanceof TailCallReturner) {
321             return ((TailCallReturner)let.action).processLeavingTail(context);
322         } else {
323             let.action.process(context);
324             return null;
325         }
326     }
327
328     /**
329     * Diagnostic print of expression structure
330     */

331
332     public void display(int level, NamePool pool, PrintStream JavaDoc out) {
333         out.println(ExpressionTool.indent(level) + "let $" + getVariableName(pool) +
334                 "[refCount=" + refCount + "] :=");
335         sequence.display(level+1, pool, out);
336         out.println(ExpressionTool.indent(level) + "return");
337         action.display(level+1, pool, out);
338     }
339
340 }
341
342
343
344 //
345
// The contents of this file are subject to the Mozilla Public License Version 1.0 (the "License");
346
// you may not use this file except in compliance with the License. You may obtain a copy of the
347
// License at http://www.mozilla.org/MPL/
348
//
349
// Software distributed under the License is distributed on an "AS IS" basis,
350
// WITHOUT WARRANTY OF ANY KIND, either express or implied.
351
// See the License for the specific language governing rights and limitations under the License.
352
//
353
// The Original Code is: all this file.
354
//
355
// The Initial Developer of the Original Code is Michael H. Kay
356
//
357
// Portions created by (your name) are Copyright (C) (your legal entity). All Rights Reserved.
358
//
359
// Contributor(s): none.
360
//
361
Popular Tags