KickJava   Java API By Example, From Geeks To Geeks.

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


1 package net.sf.saxon.expr;
2
3 import net.sf.saxon.Configuration;
4 import net.sf.saxon.Controller;
5 import net.sf.saxon.event.SequenceOutputter;
6 import net.sf.saxon.instruct.*;
7 import net.sf.saxon.om.*;
8 import net.sf.saxon.sort.SortExpression;
9 import net.sf.saxon.trace.InstructionInfoProvider;
10 import net.sf.saxon.trans.DynamicError;
11 import net.sf.saxon.trans.XPathException;
12 import net.sf.saxon.type.AnyItemType;
13 import net.sf.saxon.value.*;
14
15 import javax.xml.transform.SourceLocator JavaDoc;
16 import java.util.Iterator JavaDoc;
17 import java.util.List JavaDoc;
18
19 /**
20  * This class, ExpressionTool, contains a number of useful static methods
21  * for manipulating expressions. Most importantly, it provides the factory
22  * method make() for constructing a new expression
23  */

24
25 public class ExpressionTool
26  {
27     private ExpressionTool() {}
28
29     /**
30      * Parse an expression. This performs the basic analysis of the expression against the
31      * grammar, it binds variable references and function calls to variable definitions and
32      * function definitions, and it performs context-independent expression rewriting for
33      * optimization purposes.
34      *
35      * @exception net.sf.saxon.trans.XPathException if the expression contains a static error
36      * @param expression The expression (as a character string)
37      * @param env An object giving information about the compile-time
38      * context of the expression
39      * @param terminator The token that marks the end of this expression; typically
40      * Tokenizer.EOF, but may for example be a right curly brace
41      * @param lineNumber the line number of the start of the expression
42      * @return an object of type Expression
43      */

44
45     public static Expression make(String JavaDoc expression, StaticContext env,
46                                   int start, int terminator, int lineNumber) throws XPathException {
47         ExpressionParser parser = new ExpressionParser();
48         if (terminator == -1) {
49             terminator = Token.EOF;
50         }
51         Expression exp = parser.parse(expression, start, terminator, lineNumber, env);
52         exp = exp.simplify(env);
53         makeParentReferences(exp);
54         return exp;
55     }
56
57     /**
58      * Copy location information (currently the line number and parent) from one expression
59      * to another
60      */

61
62     public static void copyLocationInfo(Expression from, Expression to) {
63         if (from instanceof ComputedExpression && to instanceof ComputedExpression) {
64             ((ComputedExpression)to).setLocationId(((ComputedExpression)from).getLocationId());
65         }
66     }
67
68     /**
69      * Establish the links from subexpressions to their parent expressions,
70      * by means of a recursive tree walk.
71      */

72
73     public static void makeParentReferences(Expression top) {
74         // This costly method is now unnecessary; we are setting the parent expression
75
// as the tree is built. But it can be put back in if needed.
76
// if (top==null) {
77
// return;
78
// }
79
// for (Iterator children = top.iterateSubExpressions(); children.hasNext();) {
80
// Expression child = (Expression)children.next();
81
// if (child instanceof ComputedExpression) {
82
// ((ComputedExpression)child).setParentExpression((ComputedExpression)top);
83
// makeParentReferences(child);
84
// }
85
// }
86
}
87
88     /**
89      * Get location information for an expression in the form of a SourceLocator
90      */

91
92     public static SourceLocator JavaDoc getLocator(Expression exp) {
93         if (exp instanceof ComputedExpression) {
94             return (ComputedExpression)exp;
95         } else {
96             return null;
97         }
98     }
99
100     /**
101      * Determine whether an expression is a repeatedly-evaluated subexpression
102      * of a parent expression. For example, the predicate in a filter expression is
103      * a repeatedly-evaluated subexpression of the filter expression.
104      */

105
106     public static boolean isRepeatedSubexpression(Expression parent, Expression child) {
107         if (parent instanceof PathExpression) {
108             return child == ((PathExpression)parent).getStepExpression();
109         }
110         if (parent instanceof FilterExpression) {
111             return child == ((FilterExpression)parent).getFilter();
112         }
113         if (parent instanceof ForExpression) {
114             return child == ((ForExpression)parent).getAction();
115         }
116         if (parent instanceof QuantifiedExpression) {
117             return child == ((QuantifiedExpression)parent).getAction();
118         }
119         if (parent instanceof SimpleMappingExpression) {
120             return child == ((SimpleMappingExpression)parent).getStepExpression();
121         }
122         if (parent instanceof SortExpression) {
123             return ((SortExpression)parent).isSortKey(child);
124         }
125 // if (parent instanceof TupleSorter) {
126
// return ((TupleSorter)parent).isSortKey(child);
127
// }
128
if (parent instanceof AnalyzeString) {
129             return child == ((AnalyzeString)parent).getMatchingExpression() ||
130                     child == ((AnalyzeString)parent).getNonMatchingExpression();
131         }
132         if (parent instanceof ForEach) {
133             return child == ((ForEach)parent).getActionExpression();
134         }
135         if (parent instanceof ForEachGroup) {
136             return child == ((ForEachGroup)parent).getActionExpression();
137         }
138         if (parent instanceof While) {
139             return child == ((While)parent).getActionExpression();
140         }
141         if (parent instanceof GeneralComparison) {
142             return child == ((GeneralComparison)parent).getOperands()[1];
143         }
144         if (parent instanceof GeneralComparison10) {
145             Expression[] ops = ((GeneralComparison10)parent).getOperands();
146             return child == ops[0] || child == ops[1];
147         }
148         return false;
149     }
150     /**
151      * Remove unwanted sorting from an expression, at compile time
152      */

153
154     public static Expression unsorted(Optimizer opt, Expression exp, boolean eliminateDuplicates)
155     throws XPathException {
156         if (exp instanceof Value) return exp; // fast exit
157
PromotionOffer offer = new PromotionOffer(opt);
158         offer.action = PromotionOffer.UNORDERED;
159         offer.mustEliminateDuplicates = eliminateDuplicates;
160         Expression exp2 = exp.promote(offer);
161         if (exp2 != exp) {
162             if (exp2 instanceof ComputedExpression) {
163                 ((ComputedExpression)exp2).setParentExpression(exp.getParentExpression());
164             }
165             return exp2;
166         }
167         return exp;
168     }
169
170     /**
171      * Remove unwanted sorting from an expression, at compile time, if and only if it is known
172      * that the result of the expression will be homogeneous (all nodes, or all atomic values).
173      * This is done when we need the effective boolean value of a sequence: the EBV of a
174      * homogenous sequence does not depend on its order, but this is not true when atomic
175      * values and nodes are mixed: (N, AV) is true, but (AV, N) is an error.
176      */

177
178     public static Expression unsortedIfHomogeneous(Optimizer opt, Expression exp, boolean eliminateDuplicates)
179     throws XPathException {
180         if (exp instanceof Value) {
181             return exp; // fast exit
182
}
183         if (exp.getItemType() instanceof AnyItemType) {
184             return exp;
185         } else {
186             PromotionOffer offer = new PromotionOffer(opt);
187             offer.action = PromotionOffer.UNORDERED;
188             offer.mustEliminateDuplicates = eliminateDuplicates;
189             return exp.promote(offer);
190         }
191     }
192
193
194     /**
195      * Do lazy evaluation of an expression. This will return a value, which may optionally
196      * be a SequenceIntent, which is a wrapper around an iterator over the value of the expression.
197      *
198      * @param context the run-time evaluation context for the expression. If
199      * the expression is not evaluated immediately, then parts of the
200      * context on which the expression depends need to be saved as part of
201      * the Closure
202      * @param ref an indication of how the value will be used. The value 1 indicates that the value
203      * is only expected to be used once, so that there is no need to keep it in memory. A small value >1
204      * indicates multiple references, so the value will be saved when first evaluated. The special value
205      * FILTERED indicates a reference within a loop of the form $x[predicate], indicating that the value
206      * should be saved in a way that permits indexing.
207      * @exception XPathException if any error occurs in evaluating the
208      * expression
209      * @return a value: either the actual value obtained by evaluating the
210      * expression, or a Closure containing all the information needed to
211      * evaluate it later
212      */

213
214     public static ValueRepresentation lazyEvaluate(Expression exp, XPathContext context, int ref) throws XPathException {
215         // this sequence of tests rearranged 7 Apr 2005 because opt017 was failing. In 8.4 the test for a LazyExpression
216
// had been commented out - don't know why.
217
if (exp instanceof Value) {
218             return (Value)exp;
219
220         } else if (exp instanceof VariableReference) {
221             return ((VariableReference)exp).evaluateVariable(context);
222
223         } else if ((exp.getDependencies() &
224                 ( StaticProperty.DEPENDS_ON_POSITION |
225                     StaticProperty.DEPENDS_ON_LAST |
226                     StaticProperty.DEPENDS_ON_CURRENT_ITEM |
227                     StaticProperty.DEPENDS_ON_CURRENT_GROUP |
228                     StaticProperty.DEPENDS_ON_REGEX_GROUP )) != 0) {
229             // we can't save these values in the closure, so we evaluate
230
// the expression now if they are needed
231
return eagerEvaluate(exp, context);
232
233         } else if (exp instanceof LazyExpression) {
234             // A LazyExpression is always evaluated lazily (if at all possible) to
235
// prevent spurious errors (see opt017)
236
return Closure.make((LazyExpression)exp, context, (ref==1 ? 10 : ref));
237
238         } else if (!Cardinality.allowsMany(exp.getCardinality())) {
239             // singleton expressions are always evaluated eagerly
240
return eagerEvaluate(exp, context);
241
242         } else {
243             // create a Closure, a wrapper for the expression and its context
244
return Closure.make((ComputedExpression)exp, context, ref);
245         }
246
247     }
248
249     /**
250      * Evaluate an expression now; lazy evaluation is not permitted in this case
251      * @param exp the expression to be evaluated
252      * @param context the run-time evaluation context
253      * @exception net.sf.saxon.trans.XPathException if any dynamic error occurs evaluating the
254      * expression
255      * @return the result of evaluating the expression
256      */

257
258     public static Value eagerEvaluate(Expression exp, XPathContext context) throws XPathException {
259         if (exp instanceof Value && !(exp instanceof Closure)) {
260             return (Value)exp;
261         }
262         if (exp instanceof VariableReference) {
263             ValueRepresentation v = ((VariableReference)exp).evaluateVariable(context);
264             if (v instanceof Closure) {
265                 return SequenceExtent.makeSequenceExtent(((Closure)v).iterate(null));
266             } else if (v instanceof Value) {
267                 return (Value)v;
268             } else if (v instanceof NodeInfo) {
269                 return new SingletonNode((NodeInfo)v);
270             }
271         }
272         int m = exp.getImplementationMethod();
273         if ((m & Expression.ITERATE_METHOD) != 0) {
274             SequenceIterator iterator = exp.iterate(context);
275             if (iterator instanceof EmptyIterator) {
276                 return EmptySequence.getInstance();
277             } else if (iterator instanceof SingletonIterator) {
278                 Item item = ((SingletonIterator)iterator).getValue();
279                 return Value.asValue(item);
280             }
281             Value extent = SequenceExtent.makeSequenceExtent(iterator);
282             int len = extent.getLength();
283             if (len==0) {
284                 return EmptySequence.getInstance();
285             } else if (len==1) {
286                 return Value.asValue(extent.itemAt(0));
287             } else {
288                 return extent;
289             }
290
291         } else if ((m & Expression.EVALUATE_METHOD) != 0) {
292             Item item = exp.evaluateItem(context);
293             return Value.asValue(item);
294
295         } else {
296             // Use PROCESS_METHOD
297
Controller controller = context.getController();
298             XPathContext c2 = context.newMinorContext();
299             c2.setOrigin((InstructionInfoProvider)exp);
300             SequenceOutputter seq = new SequenceOutputter();
301             seq.setPipelineConfiguration(controller.makePipelineConfiguration());
302             c2.setTemporaryReceiver(seq);
303             seq.open();
304             exp.process(c2);
305             seq.close();
306             return Value.asValue(seq.getSequence());
307         }
308     }
309
310     public static boolean markTailFunctionCalls(Expression exp) {
311         if (exp instanceof ComputedExpression) {
312             return ((ComputedExpression)exp).markTailFunctionCalls();
313         } else {
314             return false;
315         }
316     }
317
318     /**
319      * Construct indent string, for diagnostic output
320      *
321      * @param level the indentation level (the number of spaces to return)
322      * @return a string of "level*2" spaces
323      */

324
325     public static String JavaDoc indent(int level) {
326         String JavaDoc s = "";
327         for (int i=0; i<level; i++) {
328             s += " ";
329         }
330         return s;
331     }
332
333     /**
334      * Allocate slot numbers to range variables
335      * @param exp the expression whose range variables need to have slot numbers assigned
336      * @param nextFree the next slot number that is available for allocation
337      * @param frame a SlotManager object that is used to track the mapping of slot numbers
338      * to variable names for debugging purposes. May be null.
339      * @return the next unallocated slot number.
340     */

341
342     public static int allocateSlots(Expression exp, int nextFree, SlotManager frame) {
343         if (exp instanceof Assignation) {
344             ((Assignation)exp).setSlotNumber(nextFree);
345             int count = ((Assignation)exp).getRequiredSlots();
346             nextFree += count;
347             if (frame != null) {
348                 for (int i=0; i<count; i++) {
349                     frame.allocateSlotNumber(((Assignation)exp).getVariableFingerprint());
350                 }
351             }
352         }
353         for (Iterator JavaDoc children = exp.iterateSubExpressions(); children.hasNext();) {
354             Expression child = (Expression)children.next();
355             nextFree = allocateSlots(child, nextFree, frame);
356         }
357         return nextFree;
358
359         // Note, we allocate a distinct slot to each range variable, even if the
360
// scopes don't overlap. This isn't strictly necessary, but might help
361
// debugging.
362
}
363
364     /**
365      * Determine the effective boolean value of a sequence, given an iterator over the sequence
366      * @param iterator An iterator over the sequence whose effective boolean value is required
367      * @return the effective boolean value
368      * @throws XPathException if a dynamic error occurs
369      */

370     public static boolean effectiveBooleanValue(SequenceIterator iterator) throws XPathException {
371         Item first = iterator.next();
372         if (first == null) {
373             return false;
374         }
375         if (first instanceof NodeInfo) {
376             return true;
377         } else {
378             if (first instanceof BooleanValue) {
379                 if (iterator.next() != null) {
380                     ebvError("sequence of two or more items starting with an atomic value");
381                 }
382                 return ((BooleanValue)first).getBooleanValue();
383             } else if (first instanceof StringValue) {
384                 if (iterator.next() != null) {
385                     ebvError("sequence of two or more items starting with an atomic value");
386                 }
387                 return (first.getStringValueCS().length()!=0);
388             } else if (first instanceof NumericValue) {
389                 if (iterator.next() != null) {
390                     ebvError("sequence of two or more items starting with an atomic value");
391                 }
392                 // first==first is a test for NaN
393
return (!(first.equals(DoubleValue.ZERO)) && first.equals(first));
394             } else {
395                 ebvError("sequence starting with an atomic value other than a boolean, number, or string");
396                 return false;
397             }
398         }
399     }
400
401     public static void ebvError(String JavaDoc reason) throws XPathException {
402         DynamicError err = new DynamicError("Effective boolean value is not defined for a " + reason);
403         err.setIsTypeError(true);
404         throw err;
405     }
406
407
408     /**
409      * Determine whether an expression depends on any one of a set of variables
410      * @param e the expression being tested
411      * @param bindingList the set of variables being tested
412      * @return true if the expression depends on one of the given variables
413      */

414
415     public static boolean dependsOnVariable(Expression e, Binding[] bindingList) {
416         if (e instanceof VariableReference) {
417             for (int i=0; i<bindingList.length; i++) {
418                 if (((VariableReference)e).getBinding() == bindingList[i]) {
419                     return true;
420                 }
421             }
422             return false;
423         } else {
424             for (Iterator JavaDoc children = e.iterateSubExpressions(); children.hasNext();) {
425                 Expression child = (Expression)children.next();
426                 if (dependsOnVariable(child, bindingList)) {
427                     return true;
428                 }
429             }
430             return false;
431         }
432     }
433
434     /**
435      * Gather a list of all the variable bindings on which a given expression depends
436      * @param e the expression being tested
437      * @param list a list to which the bindings are to be added. The items in this list must
438      * implement {@link Binding}
439      */

440
441     public static void gatherReferencedVariables(Expression e, List JavaDoc list) {
442         if (e instanceof VariableReference) {
443             Binding binding = ((VariableReference)e).getBinding();
444             if (!list.contains(binding)) {
445                 list.add(binding);
446             }
447         } else {
448             for (Iterator JavaDoc children = e.iterateSubExpressions(); children.hasNext();) {
449                 Expression child = (Expression)children.next();
450                 gatherReferencedVariables(child, list);
451             }
452         }
453     }
454
455     /**
456      * Determine whether an expression contains a call on the function with a given fingerprint
457      * @param exp The expression being tested
458      * @param fp The fingerprint of the name of the function
459      * @return true if the expression contains a call on the function
460      */

461
462     public static boolean callsFunction(Expression exp, int fp) {
463         if (exp instanceof FunctionCall && (((FunctionCall)exp).getFunctionNameCode() & NamePool.FP_MASK) == fp) {
464             return true;
465         }
466         Iterator JavaDoc iter = exp.iterateSubExpressions();
467         while (iter.hasNext()) {
468             Expression e = (Expression)iter.next();
469             if (callsFunction(e, fp)) {
470                 return true;
471             }
472         }
473         return false;
474     }
475
476     /**
477      * Gather a list of all the user-defined functions which a given expression calls directly
478      * @param e the expression being tested
479      * @param list a list of the functions that are called. The items in this list must
480      * be objects of class {@link UserFunction}
481      */

482
483     public static void gatherCalledFunctions(Expression e, List JavaDoc list) {
484         if (e instanceof UserFunctionCall) {
485             UserFunction function = ((UserFunctionCall)e).getFunction();
486             if (!list.contains(function)) {
487                 list.add(function);
488             }
489         } else {
490             for (Iterator JavaDoc children = e.iterateSubExpressions(); children.hasNext();) {
491                 Expression child = (Expression)children.next();
492                 gatherCalledFunctions(child, list);
493             }
494         }
495     }
496
497     /**
498      * Resolve calls to the current() function within an expression
499      */

500
501     public static Expression resolveCallsToCurrentFunction(Expression exp, Configuration config) throws XPathException {
502         int current = config.getNamePool().getFingerprint(NamespaceConstant.FN, "current");
503         if (current == -1) {
504             return exp;
505             // if the name fn:current is not in the name pool, then the expression can't contain any calls to it!
506
}
507         if (callsFunction(exp, current)) {
508             RangeVariableDeclaration decl = new RangeVariableDeclaration();
509             decl.setNameCode(config.getNamePool().allocate("saxon", NamespaceConstant.SAXON, "current" + exp.hashCode()));
510             decl.setVariableName("saxon:current");
511             decl.setRequiredType(SequenceType.SINGLE_ITEM);
512             LetExpression let = new LetExpression();
513             let.setSequence(new ContextItemExpression());
514             let.setVariableDeclaration(decl);
515             PromotionOffer offer = new PromotionOffer(config.getOptimizer());
516             offer.action = PromotionOffer.REPLACE_CURRENT;
517             offer.containingExpression = let;
518             exp = exp.promote(offer);
519             let.setAction(exp);
520             return let;
521         } else {
522             return exp;
523         }
524     }
525
526     /**
527      * Determine whether it is possible to rearrange an expression so that all references to a given
528      * variable are replaced by a reference to ".". This is true of there are no references to the variable
529      * within a filter predicate or on the rhs of a "/" operator.
530      */

531
532     public static boolean isVariableReplaceableByDot(Expression exp, Binding[] binding) {
533         if (exp instanceof ComputedExpression) {
534             if (exp instanceof FilterExpression) {
535                 Expression start = ((FilterExpression)exp).getBaseExpression();
536                 Expression filter = ((FilterExpression)exp).getFilter();
537                 return isVariableReplaceableByDot(start, binding) &&
538                         !dependsOnVariable(filter, binding);
539             } else if (exp instanceof PathExpression) {
540                 Expression start = ((PathExpression)exp).getFirstStep();
541                 Expression rest = ((PathExpression)exp).getRemainingSteps();
542                 return isVariableReplaceableByDot(start, binding) &&
543                         !dependsOnVariable(rest, binding);
544             } else {
545                 Iterator JavaDoc iter = exp.iterateSubExpressions();
546                 while (iter.hasNext()) {
547                     Expression sub = (Expression)iter.next();
548                     if (!isVariableReplaceableByDot(sub, binding)) {
549                         return false;
550                     }
551                 }
552                 return true;
553             }
554         } else {
555             return true;
556         }
557     }
558 }
559
560 //
561
// The contents of this file are subject to the Mozilla Public License Version 1.0 (the "License");
562
// you may not use this file except in compliance with the License. You may obtain a copy of the
563
// License at http://www.mozilla.org/MPL/
564
//
565
// Software distributed under the License is distributed on an "AS IS" basis,
566
// WITHOUT WARRANTY OF ANY KIND, either express or implied.
567
// See the License for the specific language governing rights and limitations under the License.
568
//
569
// The Original Code is: all this file.
570
//
571
// The Initial Developer of the Original Code is Michael H. Kay.
572
//
573
// Portions created by (your name) are Copyright (C) (your legal entity). All Rights Reserved.
574
//
575
// Contributor(s): none.
576
//
577
Popular Tags