KickJava   Java API By Example, From Geeks To Geeks.

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


1 package net.sf.saxon.expr;
2
3 import net.sf.saxon.event.LocationProvider;
4 import net.sf.saxon.instruct.Executable;
5 import net.sf.saxon.instruct.Instruction;
6 import net.sf.saxon.instruct.InstructionDetails;
7 import net.sf.saxon.om.Item;
8 import net.sf.saxon.om.SequenceIterator;
9 import net.sf.saxon.om.SingletonIterator;
10 import net.sf.saxon.trace.InstructionInfo;
11 import net.sf.saxon.trace.InstructionInfoProvider;
12 import net.sf.saxon.trace.Location;
13 import net.sf.saxon.trans.DynamicError;
14 import net.sf.saxon.trans.XPathException;
15 import net.sf.saxon.type.SchemaType;
16 import net.sf.saxon.value.AtomicValue;
17 import net.sf.saxon.value.Cardinality;
18 import net.sf.saxon.value.StringValue;
19
20 import javax.xml.transform.SourceLocator JavaDoc;
21 import java.io.Serializable JavaDoc;
22 import java.util.*;
23
24 /**
25  * <p>This class is an abstract superclass for different kinds of expression. This includes
26  * XSLT instructions, which are treated just like XPath expressions.
27  * Every expression is either a constant Value, or a ComputedExpression.</p>
28  *
29  * <p>There are three principal methods for evaluating an expression: iterate(), which
30  * an iterator over the result of the expression as a sequence; evaluateItem(), which returns an
31  * object that is an instance of net.sf.saxon.om.Item; and process(), which pushes the results of
32  * the expression to a Receiver. All three methods take an
33  * XPathContext object to supply the evaluation context; for an expression that is
34  * a Value, this argument is ignored and may be null. This root class provides an implementation
35  * of iterate() in terms of evaluateItem() that works only for singleton expressions, and an implementation
36  * of evaluateItem() in terms of iterate() that works only for non-singleton expressions. Subclasses
37  * of expression must therefore provide either iterate() or evaluateItem() or process(): they do not have to provide
38  * all three.</p>
39  *
40  * <p>Note that the methods that take an XPathContext argument are run-time methods.
41  * The methods without such an argument are compile-time methods. Run-time methods must not
42  * modify the state of the Expression object.</p>
43  */

44
45 public abstract class ComputedExpression
46         implements Serializable JavaDoc, Expression, InstructionInfoProvider, Container {
47
48     protected int staticProperties = -1;
49     protected int locationId = -1;
50     private Container parentExpression;
51
52     // A list of slots containing local variables on which this expression is dependent. Calculated
53
// on demand (lazily) when the expression is used in a closure.
54

55     private int[] slotsUsed;
56
57     /**
58      * Get the expression that immediately contains this expression. This method
59      * returns null for an outermost expression; it also return null in the case
60      * of literal values. For an XPath expression occurring within an XSLT stylesheet,
61      * this method returns the XSLT instruction containing the XPath expression.
62      * @return the expression that contains this expression, if known; return null
63      * if there is no containing expression or if the containing expression is unknown.
64      */

65
66     public Container getParentExpression() {
67         return parentExpression;
68     }
69
70     public void setParentExpression(Container parent) {
71         if (this == parent) {
72             throw new AssertionError JavaDoc("Incestuous relationship!");
73         }
74         parentExpression = parent;
75     }
76
77     public void adoptChildExpression(Expression child) {
78         if (child instanceof ComputedExpression) {
79             ComputedExpression cc = (ComputedExpression)child;
80             if (cc.getParentExpression() == this) {
81                 return;
82             }
83             cc.setParentExpression(this);
84             if (this.locationId == -1) {
85                 ExpressionTool.copyLocationInfo(child, this);
86             } else if (((ComputedExpression)child).locationId == -1) {
87                 ExpressionTool.copyLocationInfo(this, child);
88             }
89         }
90         if (staticProperties != -1) {
91             resetStaticProperties();
92         }
93     }
94
95     /**
96      * An implementation of Expression must provide at least one of the methods evaluateItem(), iterate(), or process().
97      * This method indicates which of these methods is prefered.
98      */

99
100     public int getImplementationMethod() {
101         if (Cardinality.allowsMany(getCardinality())) {
102             return ITERATE_METHOD;
103         } else {
104             return EVALUATE_METHOD;
105         }
106     }
107
108     /**
109      * Set the location ID on an expression.
110      */

111
112     public void setLocationId(int id) {
113         locationId = id;
114     }
115
116     /**
117      * Get the location ID of the expression
118      */

119
120     public final int getLocationId() {
121         return locationId;
122     }
123
124     /**
125      * Get the line number of the expression
126      */

127
128     public int getLineNumber() {
129         if (locationId == -1) {
130             if (parentExpression != null) {
131                 return parentExpression.getLineNumber();
132             } else {
133                 return -1;
134             }
135         }
136         return locationId & 0xfffff;
137     }
138
139     /**
140      * Get the column number of the expression
141      */

142
143     public int getColumnNumber() {
144         return -1;
145     }
146
147     /**
148      * Get the systemId of the module containing the expression
149      */

150
151     public String JavaDoc getSystemId() {
152         if (locationId == -1) {
153             if (parentExpression != null) {
154                 return parentExpression.getSystemId();
155             } else {
156                 return null;
157             }
158         }
159         Executable exec = getExecutable();
160         if (exec == null) {
161             if (parentExpression == null) {
162                 return null;
163             }
164             if (parentExpression instanceof LocationProvider) {
165                 return ((LocationProvider)parentExpression).getSystemId(locationId);
166             }
167             return parentExpression.getSystemId();
168         }
169         return exec.getLocationMap().getSystemId(locationId);
170     }
171
172     /**
173      * Get the publicId of the module containing the expression (to satisfy the SourceLocator interface)
174      */

175
176     public final String JavaDoc getPublicId() {
177         return null;
178     }
179
180     /**
181      * Get the executable containing this expression
182      */

183
184     public Executable getExecutable() {
185         Container container = getParentExpression();
186         if (container == null) {
187             return null;
188         } if (container == this) {
189             throw new IllegalStateException JavaDoc("Expression cannot contain itself");
190         } else {
191             return container.getExecutable();
192         }
193     }
194
195     /**
196      * Get the LocationProvider allowing location identifiers to be resolved.
197      */

198
199     public LocationProvider getLocationProvider() {
200         Executable exec = getExecutable();
201         if (exec != null) {
202             return exec.getLocationMap();
203         } else if (getParentExpression() instanceof LocationProvider) {
204             return ((LocationProvider)getParentExpression());
205         } else {
206             return null;
207         }
208     }
209
210     /**
211      * Simplify an expression. This performs any static optimization (by rewriting the expression
212      * as a different expression). The default implementation does nothing.
213      *
214      * @exception XPathException if an error is discovered during expression
215      * rewriting
216      * @return the simplified expression
217      */

218
219     public Expression simplify(StaticContext env) throws XPathException {
220         return this;
221     }
222
223     /**
224      * Offer promotion for this subexpression. The offer will be accepted if the subexpression
225      * is not dependent on the factors (e.g. the context item) identified in the PromotionOffer.
226      * By default the offer is not accepted - this is appropriate in the case of simple expressions
227      * such as constant values and variable references where promotion would give no performance
228      * advantage. This method is always called at compile time.
229      *
230      * @param offer details of the offer, for example the offer to move
231      * expressions that don't depend on the context to an outer level in
232      * the containing expression
233      * @exception net.sf.saxon.trans.XPathException if any error is detected
234      * @return if the offer is not accepted, return this expression unchanged.
235      * Otherwise return the result of rewriting the expression to promote
236      * this subexpression
237      */

238
239     public Expression promote(PromotionOffer offer) throws XPathException {
240         // The following temporary code checks that this method is implemented for all expressions
241
// that have subexpressions
242
// if (iterateSubExpressions().hasNext()) {
243
// throw new UnsupportedOperationException("promote is not implemented for " + this.getClass());
244
// }
245
return this;
246     }
247
248     /**
249      * Promote a subexpression if possible, and if the expression was changed, carry out housekeeping
250      * to reset the static properties and correct the parent pointers in the tree
251      */

252
253     public final Expression doPromotion(Expression subexpression, PromotionOffer offer) throws XPathException {
254         Expression e = subexpression.promote(offer);
255         if (e != subexpression) {
256             adoptChildExpression(e);
257             resetStaticProperties();
258         }
259         return e;
260     }
261
262     /**
263      * Get the static properties of this expression (other than its type). The result is
264      * bit-signficant. These properties are used for optimizations. In general, if
265      * property bit is set, it is true, but if it is unset, the value is unknown.
266      *
267      * @return a set of flags indicating static properties of this expression
268      */

269
270     public final int getSpecialProperties() {
271         if (staticProperties == -1) {
272             computeStaticProperties();
273         }
274         return staticProperties & StaticProperty.SPECIAL_PROPERTY_MASK;
275     }
276
277     /**
278      * Compute the static properties. This should only be done once for each
279      * expression.
280      */

281
282     public final void computeStaticProperties() {
283         staticProperties =
284                 computeDependencies() |
285                 computeCardinality() |
286                 computeSpecialProperties();
287     }
288
289     /**
290      * Reset the static properties. This should be done whenever the expression is changed in a way that might
291      * affect the properties. It causes the properties to be recomputed next time they are needed.
292      */

293
294     public final void resetStaticProperties() {
295         staticProperties = -1;
296         if (parentExpression instanceof ComputedExpression) {
297             ((ComputedExpression)parentExpression).resetStaticProperties();
298         }
299     }
300
301     protected abstract int computeCardinality();
302
303     public int computeSpecialProperties() {
304         return 0;
305     }
306
307     /**
308      * Determine the static cardinality of the expression. This establishes how many items
309      * there will be in the result of the expression, at compile time (i.e., without
310      * actually evaluating the result.
311      *
312      * @return one of the values Cardinality.ONE_OR_MORE,
313      * Cardinality.ZERO_OR_MORE, Cardinality.EXACTLY_ONE,
314      * Cardinality.ZERO_OR_ONE, Cardinality.EMPTY. This default
315      * implementation returns ZERO_OR_MORE (which effectively gives no
316      * information).
317      */

318
319     public int getCardinality() {
320         if (staticProperties == -1) {
321             computeStaticProperties();
322         }
323         return staticProperties & StaticProperty.CARDINALITY_MASK;
324     }
325
326
327     /**
328      * Determine which aspects of the context the expression depends on. The result is
329      * a bitwise-or'ed value composed from constants such as XPathContext.VARIABLES and
330      * XPathContext.CURRENT_NODE. The default implementation combines the intrinsic
331      * dependencies of this expression with the dependencies of the subexpressions,
332      * computed recursively. This is overridden for expressions such as FilterExpression
333      * where a subexpression's dependencies are not necessarily inherited by the parent
334      * expression.
335      *
336      * @return a set of bit-significant flags identifying the dependencies of
337      * the expression
338      */

339
340     public int getDependencies() {
341         // Implemented as a memo function: we only compute the dependencies
342
// for each expression once
343
if (staticProperties == -1) {
344             computeStaticProperties();
345         }
346         return staticProperties & StaticProperty.DEPENDENCY_MASK;
347     }
348
349     /**
350      * Compute the dependencies of an expression, as the union of the
351      * dependencies of its subexpressions. (This is overridden for path expressions
352      * and filter expressions, where the dependencies of a subexpression are not all
353      * propogated). This method should be called only once, to compute the dependencies;
354      * after that, getDependencies should be used.
355      * @return the depencies, as a bit-mask
356      */

357
358     public int computeDependencies() {
359         int dependencies = (short) getIntrinsicDependencies();
360         for (Iterator children = iterateSubExpressions(); children.hasNext();) {
361             dependencies |= (short) ((Expression)children.next()).getDependencies();
362         }
363         return dependencies;
364     }
365
366     /**
367      * Determine the intrinsic dependencies of an expression, that is, those which are not derived
368      * from the dependencies of its subexpressions. For example, position() has an intrinsic dependency
369      * on the context position, while (position()+1) does not. The default implementation
370      * of the method returns 0, indicating "no dependencies".
371      *
372      * @return a set of bit-significant flags identifying the "intrinsic"
373      * dependencies. The flags are documented in class net.sf.saxon.value.StaticProperty
374      */

375
376     public int getIntrinsicDependencies() {
377         return 0;
378     }
379
380
381     /**
382      * Get the immediate sub-expressions of this expression. Default implementation
383      * returns a zero-length array, appropriate for an expression that has no
384      * sub-expressions.
385      * @return an iterator containing the sub-expressions of this expression
386      */

387
388     public Iterator iterateSubExpressions() {
389         return Collections.EMPTY_LIST.iterator();
390     }
391
392     /**
393      * Check that any elements and attributes constructed or returned by this expression are acceptable
394      * in the content model of a given complex type. It's always OK to say yes, since the check will be
395      * repeated at run-time. The process of checking element and attribute constructors against the content
396      * model of a complex type also registers the type of content expected of those constructors, so the
397      * static validation can continue recursively.
398      */

399
400     public void checkPermittedContents(SchemaType parentType, StaticContext env, boolean whole) throws XPathException {
401         return;
402     }
403
404     /**
405      * Mark tail-recursive calls on stylesheet functions. For most expressions, this does nothing.
406      *
407      * @return true if a tail recursive call was found and if this call
408      * accounts for the whole of the value.
409      */

410
411     public boolean markTailFunctionCalls() {
412         return false;
413     }
414
415     /**
416      * Get the local variables (identified by their slot numbers) on which this expression depends.
417      * Should only be called if the caller has established that there is a dependency on local variables.
418      */

419
420     public int[] getSlotsUsed() {
421         if (slotsUsed != null) {
422             return slotsUsed;
423         }
424         Set slots = new HashSet(10);
425         gatherSlotsUsed(this, slots);
426         slotsUsed = new int[slots.size()];
427         int i=0;
428         Iterator iter = slots.iterator();
429         while (iter.hasNext()) {
430             Integer JavaDoc islot = (Integer JavaDoc)iter.next();
431             slotsUsed[i++] = islot.intValue();
432         }
433         Arrays.sort(slotsUsed);
434         return slotsUsed;
435     }
436
437     private static void gatherSlotsUsed(Expression exp, Set slots) {
438         if (exp instanceof VariableReference) {
439             Binding binding = ((VariableReference)exp).getBinding();
440             if (!binding.isGlobal()) {
441                 int slot = binding.getLocalSlotNumber();
442                 if (slot != -1) {
443                     Integer JavaDoc islot = new Integer JavaDoc(slot);
444                     if (!slots.contains(islot)) {
445                         slots.add(islot);
446                     }
447                 }
448             }
449         } else {
450             Iterator iter = exp.iterateSubExpressions();
451             while (iter.hasNext()) {
452                 Expression sub = (Expression)iter.next();
453                 gatherSlotsUsed(sub, slots);
454             }
455         }
456     }
457
458     /**
459      * Evaluate an expression as a single item. This always returns either a single Item or
460      * null (denoting the empty sequence). No conversion is done. This method should not be
461      * used unless the static type of the expression is a subtype of "item" or "item?": that is,
462      * it should not be called if the expression may return a sequence. There is no guarantee that
463      * this condition will be detected.
464      *
465      * @param context The context in which the expression is to be evaluated
466      * @exception XPathException if any dynamic error occurs evaluating the
467      * expression
468      * @return the node or atomic value that results from evaluating the
469      * expression; or null to indicate that the result is an empty
470      * sequence
471      */

472
473     public Item evaluateItem(XPathContext context) throws XPathException {
474         return iterate(context).next();
475     }
476
477     /**
478      * Evaluate an expression as a String. This function must only be called in contexts
479      * where it is known that the expression will return a single string (or where an empty sequence
480      * is to be treated as a zero-length string). Implementations should not attempt to convert
481      * the result to a string, other than converting () to "". This method is used mainly to
482      * evaluate expressions produced by compiling an attribute value template.
483      *
484      * @exception XPathException if any dynamic error occurs evaluating the
485      * expression
486      * @exception ClassCastException if the result type of the
487      * expression is not xs:string?
488      * @param context The context in which the expression is to be evaluated
489      * @return the value of the expression, evaluated in the current context.
490      * The expression must return a string or (); if the value of the
491      * expression is (), this method returns "".
492      */

493
494     public String JavaDoc evaluateAsString(XPathContext context) throws XPathException {
495         Item o = evaluateItem(context);
496         if (o instanceof AtomicValue && !((AtomicValue)o).hasBuiltInType()) {
497             o = ((AtomicValue) o).getPrimitiveValue();
498         }
499         StringValue value = (StringValue) o; // the ClassCastException is deliberate
500
if (value == null) return "";
501         return value.getStringValue();
502     }
503
504     /**
505      * Return an Iterator to iterate over the values of a sequence. The value of every
506      * expression can be regarded as a sequence, so this method is supported for all
507      * expressions. This default implementation handles iteration for expressions that
508      * return singleton values: for non-singleton expressions, the subclass must
509      * provide its own implementation.
510      *
511      * @exception XPathException if any dynamic error occurs evaluating the
512      * expression
513      * @param context supplies the context for evaluation
514      * @return a SequenceIterator that can be used to iterate over the result
515      * of the expression
516      */

517
518     public SequenceIterator iterate(XPathContext context) throws XPathException {
519         Item value = evaluateItem(context);
520         return SingletonIterator.makeIterator(value);
521     }
522
523
524     /**
525      * Get the effective boolean value of the expression. This returns false if the value
526      * is the empty sequence, a zero-length string, a number equal to zero, or the boolean
527      * false. Otherwise it returns true.
528      *
529      * @param context The context in which the expression is to be evaluated
530      * @exception XPathException if any dynamic error occurs evaluating the
531      * expression
532      * @return the effective boolean value
533      */

534
535     public boolean effectiveBooleanValue(XPathContext context) throws XPathException {
536         return ExpressionTool.effectiveBooleanValue(iterate(context));
537     }
538
539     /**
540      * Process the instruction, without returning any tail calls
541      * @param context The dynamic context, giving access to the current node,
542      * the current variables, etc.
543      */

544
545     public void process(XPathContext context) throws XPathException {
546         int m = getImplementationMethod();
547
548         if ((m & EVALUATE_METHOD) != 0) {
549             Item item = evaluateItem(context);
550             // Need to cater for it being a tailcall returned from a function
551
Instruction.appendItem(item, context.getReceiver(), locationId);
552
553         } else if ((m & ITERATE_METHOD) != 0) {
554
555             SequenceIterator iter = iterate(context);
556             try {
557                 while (true) {
558                     Item it = iter.next();
559                     if (it == null) break;
560                     // Need to cater for it being a tailcall returned from a function
561
Instruction.appendItem(it, context.getReceiver(), locationId);
562                 }
563             } catch (XPathException e) {
564                 if (e.getLocator() == null) {
565                     e.setLocator(this);
566                 }
567                 throw e;
568             }
569
570         } else {
571             dynamicError("process() is not implemented in the subclass " + this.getClass(), context);
572         }
573     }
574
575
576     /**
577      * Method used in subclasses to signal a dynamic error
578      */

579
580     protected void dynamicError(String JavaDoc message, XPathContext context) throws DynamicError {
581         DynamicError err = new DynamicError(message, getSourceLocator());
582         err.setXPathContext(context);
583         throw err;
584     }
585
586     /**
587      * Method used in subclasses to signal a dynamic error
588      */

589
590     protected void dynamicError(String JavaDoc message, String JavaDoc code, XPathContext context) throws DynamicError {
591         DynamicError err = new DynamicError(message, getSourceLocator());
592         err.setXPathContext(context);
593         err.setErrorCode(code);
594         throw err;
595     }
596
597     /**
598      * Method used in subclasses to signal a runtime type error
599      */

600
601     protected void typeError(String JavaDoc message, XPathContext context) throws DynamicError {
602         DynamicError e = new DynamicError(message, getSourceLocator());
603         e.setIsTypeError(true);
604         e.setXPathContext(context);
605         throw e;
606     }
607
608     /**
609      * Method used in subclasses to signal a runtime type error
610      */

611
612     protected void typeError(String JavaDoc message, String JavaDoc errorCode, XPathContext context) throws DynamicError {
613         DynamicError e = new DynamicError(message, getSourceLocator());
614         e.setIsTypeError(true);
615         e.setErrorCode(errorCode);
616         e.setXPathContext(context);
617         throw e;
618     }
619
620     /**
621      * Get a SourceLocator for this expression
622      */

623
624     private SourceLocator JavaDoc getSourceLocator() {
625         return ExpressionTool.getLocator(this);
626     }
627
628     /**
629      * Get InstructionInfo for this expression
630      */

631
632     public InstructionInfo getInstructionInfo() {
633         InstructionDetails details = new InstructionDetails();
634         details.setConstructType(getConstructType());
635         details.setProperty("expression", this);
636         details.setSystemId(getSystemId());
637         details.setLineNumber(getLineNumber());
638         details.setColumnNumber(getColumnNumber());
639         if (this instanceof Assignation) {
640             details.setObjectNameCode(((Assignation)this).getVariableNameCode());
641         }
642         return details;
643     }
644
645     /**
646      * Get the type of this expression for use in tracing and diagnostics
647      * @return the type of expression, as enumerated in class {@link Location}
648      */

649
650     protected int getConstructType() {
651         return Location.XPATH_EXPRESSION;
652     }
653
654     /**
655      * Diagnostic method: search the tree for an expression whose parent expression is incorrectly set
656      */

657
658     public boolean hasBadParentPointer() {
659         Iterator iter = iterateSubExpressions();
660         while (iter.hasNext()) {
661             Expression exp = (Expression)iter.next();
662             if (exp instanceof ComputedExpression) {
663                 if (this != exp.getParentExpression()) {
664                     System.err.println("Bad parent pointer to " + exp.getParentExpression() + " found in " + exp);
665                     return true;
666                 }
667                 if (((ComputedExpression)exp).hasBadParentPointer()) {
668                     System.err.println("Found in "+ exp);
669                     return true;
670                 }
671             }
672         }
673         return false;
674     }
675
676 }
677
678 //
679
// The contents of this file are subject to the Mozilla Public License Version 1.0 (the "License");
680
// you may not use this file except in compliance with the License. You may obtain a copy of the
681
// License at http://www.mozilla.org/MPL/
682
//
683
// Software distributed under the License is distributed on an "AS IS" basis,
684
// WITHOUT WARRANTY OF ANY KIND, either express or implied.
685
// See the License for the specific language governing rights and limitations under the License.
686
//
687
// The Original Code is: all this file.
688
//
689
// The Initial Developer of the Original Code is Michael H. Kay.
690
//
691
// Contributor(s): Michael Kay
692
//
693
Popular Tags