001: package net.sf.saxon.expr;
002:
003: import net.sf.saxon.event.LocationProvider;
004: import net.sf.saxon.instruct.Executable;
005: import net.sf.saxon.instruct.Instruction;
006: import net.sf.saxon.instruct.InstructionDetails;
007: import net.sf.saxon.om.Item;
008: import net.sf.saxon.om.SequenceIterator;
009: import net.sf.saxon.om.SingletonIterator;
010: import net.sf.saxon.trace.InstructionInfo;
011: import net.sf.saxon.trace.InstructionInfoProvider;
012: import net.sf.saxon.trace.Location;
013: import net.sf.saxon.trans.DynamicError;
014: import net.sf.saxon.trans.XPathException;
015: import net.sf.saxon.type.SchemaType;
016: import net.sf.saxon.value.AtomicValue;
017: import net.sf.saxon.value.Cardinality;
018: import net.sf.saxon.value.StringValue;
019: import net.sf.saxon.sort.IntHashSet;
020: import net.sf.saxon.sort.IntIterator;
021:
022: import javax.xml.transform.SourceLocator;
023: import java.io.Serializable;
024: import java.util.*;
025:
026: /**
027: * <p>This class is an abstract superclass for different kinds of expression. This includes
028: * XSLT instructions, which are treated just like XPath expressions.
029: * Every expression is either a constant Value, or a ComputedExpression.</p>
030: *
031: * <p>There are three principal methods for evaluating an expression: iterate(), which
032: * an iterator over the result of the expression as a sequence; evaluateItem(), which returns an
033: * object that is an instance of net.sf.saxon.om.Item; and process(), which pushes the results of
034: * the expression to a Receiver. All three methods take an
035: * XPathContext object to supply the evaluation context; for an expression that is
036: * a Value, this argument is ignored and may be null. This root class provides an implementation
037: * of iterate() in terms of evaluateItem() that works only for singleton expressions, and an implementation
038: * of evaluateItem() in terms of iterate() that works only for non-singleton expressions. Subclasses
039: * of expression must therefore provide either iterate() or evaluateItem() or process(): they do not have to provide
040: * all three.</p>
041: *
042: * <p>Note that the methods that take an XPathContext argument are run-time methods.
043: * The methods without such an argument are compile-time methods. Run-time methods must not
044: * modify the state of the Expression object.</p>
045: */
046:
047: public abstract class ComputedExpression implements Serializable,
048: Expression, InstructionInfoProvider, Container {
049:
050: protected int staticProperties = -1;
051: protected int locationId = -1;
052: private Container parentExpression;
053:
054: // A list of slots containing local variables on which this expression is dependent. Calculated
055: // on demand (lazily) when the expression is used in a closure.
056:
057: private int[] slotsUsed;
058:
059: /**
060: * Get the expression that immediately contains this expression. This method
061: * returns null for an outermost expression; it also return null in the case
062: * of literal values. For an XPath expression occurring within an XSLT stylesheet,
063: * this method returns the XSLT instruction containing the XPath expression.
064: * @return the expression that contains this expression, if known; return null
065: * if there is no containing expression or if the containing expression is unknown.
066: */
067:
068: public Container getParentExpression() {
069: return parentExpression;
070: }
071:
072: public void setParentExpression(Container parent) {
073: if (this == parent) {
074: throw new AssertionError("Incestuous relationship!");
075: }
076: parentExpression = parent;
077: }
078:
079: public void adoptChildExpression(Expression child) {
080: if (child instanceof ComputedExpression) {
081: ComputedExpression cc = (ComputedExpression) child;
082: if (cc.getParentExpression() == this ) {
083: return;
084: }
085: cc.setParentExpression(this );
086: if (this .locationId == -1) {
087: ExpressionTool.copyLocationInfo(child, this );
088: } else if (((ComputedExpression) child).locationId == -1) {
089: ExpressionTool.copyLocationInfo(this , child);
090: }
091: }
092: if (staticProperties != -1) {
093: resetStaticProperties();
094: }
095: }
096:
097: /**
098: * An implementation of Expression must provide at least one of the methods evaluateItem(), iterate(), or process().
099: * This method indicates which of these methods is prefered.
100: */
101:
102: public int getImplementationMethod() {
103: if (Cardinality.allowsMany(getCardinality())) {
104: return ITERATE_METHOD;
105: } else {
106: return EVALUATE_METHOD;
107: }
108: }
109:
110: /**
111: * Set the location ID on an expression.
112: */
113:
114: public void setLocationId(int id) {
115: locationId = id;
116: }
117:
118: /**
119: * Get the location ID of the expression
120: */
121:
122: public final int getLocationId() {
123: return locationId;
124: }
125:
126: /**
127: * Get the line number of the expression
128: */
129:
130: public int getLineNumber() {
131: if (locationId == -1) {
132: if (parentExpression != null) {
133: return parentExpression.getLineNumber();
134: } else {
135: return -1;
136: }
137: }
138: return locationId & 0xfffff;
139: }
140:
141: /**
142: * Get the column number of the expression
143: */
144:
145: public int getColumnNumber() {
146: return -1;
147: }
148:
149: /**
150: * Get the systemId of the module containing the expression
151: */
152:
153: public String getSystemId() {
154: if (locationId == -1) {
155: if (parentExpression != null) {
156: return parentExpression.getSystemId();
157: } else {
158: return null;
159: }
160: }
161: Executable exec = getExecutable();
162: if (exec == null) {
163: if (parentExpression == null) {
164: return null;
165: }
166: if (parentExpression instanceof LocationProvider) {
167: return ((LocationProvider) parentExpression)
168: .getSystemId(locationId);
169: }
170: return parentExpression.getSystemId();
171: }
172: return exec.getLocationMap().getSystemId(locationId);
173: }
174:
175: /**
176: * Get the publicId of the module containing the expression (to satisfy the SourceLocator interface)
177: */
178:
179: public final String getPublicId() {
180: return null;
181: }
182:
183: /**
184: * Get the executable containing this expression
185: */
186:
187: public Executable getExecutable() {
188: Container container = getParentExpression();
189: if (container == null) {
190: return null;
191: }
192: if (container == this ) {
193: throw new IllegalStateException(
194: "Expression cannot contain itself");
195: } else {
196: return container.getExecutable();
197: }
198: }
199:
200: /**
201: * Get the LocationProvider allowing location identifiers to be resolved.
202: */
203:
204: public LocationProvider getLocationProvider() {
205: Executable exec = getExecutable();
206: if (exec != null) {
207: return exec.getLocationMap();
208: } else if (getParentExpression() instanceof LocationProvider) {
209: return ((LocationProvider) getParentExpression());
210: } else {
211: return null;
212: }
213: }
214:
215: /**
216: * Simplify an expression. This performs any static optimization (by rewriting the expression
217: * as a different expression). The default implementation does nothing.
218: *
219: * @exception XPathException if an error is discovered during expression
220: * rewriting
221: * @return the simplified expression
222: */
223:
224: public Expression simplify(StaticContext env) throws XPathException {
225: return this ;
226: }
227:
228: /**
229: * Offer promotion for this subexpression. The offer will be accepted if the subexpression
230: * is not dependent on the factors (e.g. the context item) identified in the PromotionOffer.
231: * By default the offer is not accepted - this is appropriate in the case of simple expressions
232: * such as constant values and variable references where promotion would give no performance
233: * advantage. This method is always called at compile time.
234: *
235: * @param offer details of the offer, for example the offer to move
236: * expressions that don't depend on the context to an outer level in
237: * the containing expression
238: * @exception net.sf.saxon.trans.XPathException if any error is detected
239: * @return if the offer is not accepted, return this expression unchanged.
240: * Otherwise return the result of rewriting the expression to promote
241: * this subexpression
242: */
243:
244: public Expression promote(PromotionOffer offer)
245: throws XPathException {
246: // The following temporary code checks that this method is implemented for all expressions
247: // that have subexpressions
248: // if (iterateSubExpressions().hasNext()) {
249: // throw new UnsupportedOperationException("promote is not implemented for " + this.getClass());
250: // }
251: return this ;
252: }
253:
254: /**
255: * Promote a subexpression if possible, and if the expression was changed, carry out housekeeping
256: * to reset the static properties and correct the parent pointers in the tree
257: */
258:
259: public final Expression doPromotion(Expression subexpression,
260: PromotionOffer offer) throws XPathException {
261: Expression e = subexpression.promote(offer);
262: if (e != subexpression) {
263: adoptChildExpression(e);
264: resetStaticProperties();
265: }
266: return e;
267: }
268:
269: /**
270: * Get the static properties of this expression (other than its type). The result is
271: * bit-signficant. These properties are used for optimizations. In general, if
272: * property bit is set, it is true, but if it is unset, the value is unknown.
273: *
274: * @return a set of flags indicating static properties of this expression
275: */
276:
277: public final int getSpecialProperties() {
278: if (staticProperties == -1) {
279: computeStaticProperties();
280: }
281: return staticProperties & StaticProperty.SPECIAL_PROPERTY_MASK;
282: }
283:
284: /**
285: * Compute the static properties. This should only be done once for each
286: * expression.
287: */
288:
289: public final void computeStaticProperties() {
290: staticProperties = computeDependencies() | computeCardinality()
291: | computeSpecialProperties();
292: }
293:
294: /**
295: * Reset the static properties. This should be done whenever the expression is changed in a way that might
296: * affect the properties. It causes the properties to be recomputed next time they are needed.
297: */
298:
299: public final void resetStaticProperties() {
300: staticProperties = -1;
301: if (parentExpression instanceof ComputedExpression) {
302: ((ComputedExpression) parentExpression)
303: .resetStaticProperties();
304: }
305: }
306:
307: protected abstract int computeCardinality();
308:
309: public int computeSpecialProperties() {
310: return 0;
311: }
312:
313: /**
314: * Determine the static cardinality of the expression. This establishes how many items
315: * there will be in the result of the expression, at compile time (i.e., without
316: * actually evaluating the result.
317: *
318: * @return one of the values Cardinality.ONE_OR_MORE,
319: * Cardinality.ZERO_OR_MORE, Cardinality.EXACTLY_ONE,
320: * Cardinality.ZERO_OR_ONE, Cardinality.EMPTY. This default
321: * implementation returns ZERO_OR_MORE (which effectively gives no
322: * information).
323: */
324:
325: public int getCardinality() {
326: if (staticProperties == -1) {
327: computeStaticProperties();
328: }
329: return staticProperties & StaticProperty.CARDINALITY_MASK;
330: }
331:
332: /**
333: * Determine which aspects of the context the expression depends on. The result is
334: * a bitwise-or'ed value composed from constants such as XPathContext.VARIABLES and
335: * XPathContext.CURRENT_NODE. The default implementation combines the intrinsic
336: * dependencies of this expression with the dependencies of the subexpressions,
337: * computed recursively. This is overridden for expressions such as FilterExpression
338: * where a subexpression's dependencies are not necessarily inherited by the parent
339: * expression.
340: *
341: * @return a set of bit-significant flags identifying the dependencies of
342: * the expression
343: */
344:
345: public int getDependencies() {
346: // Implemented as a memo function: we only compute the dependencies
347: // for each expression once
348: if (staticProperties == -1) {
349: computeStaticProperties();
350: }
351: return staticProperties & StaticProperty.DEPENDENCY_MASK;
352: }
353:
354: /**
355: * Compute the dependencies of an expression, as the union of the
356: * dependencies of its subexpressions. (This is overridden for path expressions
357: * and filter expressions, where the dependencies of a subexpression are not all
358: * propogated). This method should be called only once, to compute the dependencies;
359: * after that, getDependencies should be used.
360: * @return the depencies, as a bit-mask
361: */
362:
363: public int computeDependencies() {
364: int dependencies = (short) getIntrinsicDependencies();
365: for (Iterator children = iterateSubExpressions(); children
366: .hasNext();) {
367: dependencies |= (short) ((Expression) children.next())
368: .getDependencies();
369: }
370: return dependencies;
371: }
372:
373: /**
374: * Determine the intrinsic dependencies of an expression, that is, those which are not derived
375: * from the dependencies of its subexpressions. For example, position() has an intrinsic dependency
376: * on the context position, while (position()+1) does not. The default implementation
377: * of the method returns 0, indicating "no dependencies".
378: *
379: * @return a set of bit-significant flags identifying the "intrinsic"
380: * dependencies. The flags are documented in class net.sf.saxon.value.StaticProperty
381: */
382:
383: public int getIntrinsicDependencies() {
384: return 0;
385: }
386:
387: /**
388: * Get the immediate sub-expressions of this expression. Default implementation
389: * returns a zero-length array, appropriate for an expression that has no
390: * sub-expressions.
391: * @return an iterator containing the sub-expressions of this expression
392: */
393:
394: public Iterator iterateSubExpressions() {
395: return Collections.EMPTY_LIST.iterator();
396: }
397:
398: /**
399: * Suppress validation on contained element constructors, on the grounds that the parent element
400: * is already performing validation. The default implementation does nothing.
401: */
402:
403: public void suppressValidation(int validationMode) {
404: // do nothing
405: }
406:
407: /**
408: * Check that any elements and attributes constructed or returned by this expression are acceptable
409: * in the content model of a given complex type. It's always OK to say yes, since the check will be
410: * repeated at run-time. The process of checking element and attribute constructors against the content
411: * model of a complex type also registers the type of content expected of those constructors, so the
412: * static validation can continue recursively.
413: */
414:
415: public void checkPermittedContents(SchemaType parentType,
416: StaticContext env, boolean whole) throws XPathException {
417: return;
418: }
419:
420: /**
421: * Mark tail-recursive calls on stylesheet functions. For most expressions, this does nothing.
422: *
423: * @return true if a tail recursive call was found and if this call
424: * accounts for the whole of the value.
425: */
426:
427: public boolean markTailFunctionCalls() {
428: return false;
429: }
430:
431: /**
432: * Get the local variables (identified by their slot numbers) on which this expression depends.
433: * Should only be called if the caller has established that there is a dependency on local variables.
434: */
435:
436: public int[] getSlotsUsed() {
437: if (slotsUsed != null) {
438: return slotsUsed;
439: }
440: IntHashSet slots = new IntHashSet(10);
441: gatherSlotsUsed(this , slots);
442: slotsUsed = new int[slots.size()];
443: int i = 0;
444: IntIterator iter = slots.iterator();
445: while (iter.hasNext()) {
446: slotsUsed[i++] = iter.next();
447: }
448: Arrays.sort(slotsUsed);
449: return slotsUsed;
450: }
451:
452: private static void gatherSlotsUsed(Expression exp, IntHashSet slots) {
453: if (exp instanceof VariableReference) {
454: Binding binding = ((VariableReference) exp).getBinding();
455: if (!binding.isGlobal()) {
456: int slot = binding.getLocalSlotNumber();
457: if (slot != -1) {
458: if (!slots.contains(slot)) {
459: slots.add(slot);
460: }
461: }
462: }
463: } else {
464: Iterator iter = exp.iterateSubExpressions();
465: while (iter.hasNext()) {
466: Expression sub = (Expression) iter.next();
467: gatherSlotsUsed(sub, slots);
468: }
469: }
470: }
471:
472: /**
473: * Evaluate an expression as a single item. This always returns either a single Item or
474: * null (denoting the empty sequence). No conversion is done. This method should not be
475: * used unless the static type of the expression is a subtype of "item" or "item?": that is,
476: * it should not be called if the expression may return a sequence. There is no guarantee that
477: * this condition will be detected.
478: *
479: * @param context The context in which the expression is to be evaluated
480: * @exception XPathException if any dynamic error occurs evaluating the
481: * expression
482: * @return the node or atomic value that results from evaluating the
483: * expression; or null to indicate that the result is an empty
484: * sequence
485: */
486:
487: public Item evaluateItem(XPathContext context)
488: throws XPathException {
489: return iterate(context).next();
490: }
491:
492: /**
493: * Evaluate an expression as a String. This function must only be called in contexts
494: * where it is known that the expression will return a single string (or where an empty sequence
495: * is to be treated as a zero-length string). Implementations should not attempt to convert
496: * the result to a string, other than converting () to "". This method is used mainly to
497: * evaluate expressions produced by compiling an attribute value template.
498: *
499: * @exception XPathException if any dynamic error occurs evaluating the
500: * expression
501: * @exception ClassCastException if the result type of the
502: * expression is not xs:string?
503: * @param context The context in which the expression is to be evaluated
504: * @return the value of the expression, evaluated in the current context.
505: * The expression must return a string or (); if the value of the
506: * expression is (), this method returns "".
507: */
508:
509: public String evaluateAsString(XPathContext context)
510: throws XPathException {
511: Item o = evaluateItem(context);
512: if (o instanceof AtomicValue
513: && !((AtomicValue) o).hasBuiltInType()) {
514: o = ((AtomicValue) o).getPrimitiveValue();
515: }
516: StringValue value = (StringValue) o; // the ClassCastException is deliberate
517: if (value == null)
518: return "";
519: return value.getStringValue();
520: }
521:
522: /**
523: * Return an Iterator to iterate over the values of a sequence. The value of every
524: * expression can be regarded as a sequence, so this method is supported for all
525: * expressions. This default implementation handles iteration for expressions that
526: * return singleton values: for non-singleton expressions, the subclass must
527: * provide its own implementation.
528: *
529: * @exception XPathException if any dynamic error occurs evaluating the
530: * expression
531: * @param context supplies the context for evaluation
532: * @return a SequenceIterator that can be used to iterate over the result
533: * of the expression
534: */
535:
536: public SequenceIterator iterate(XPathContext context)
537: throws XPathException {
538: Item value = evaluateItem(context);
539: return SingletonIterator.makeIterator(value);
540: }
541:
542: /**
543: * Get the effective boolean value of the expression. This returns false if the value
544: * is the empty sequence, a zero-length string, a number equal to zero, or the boolean
545: * false. Otherwise it returns true.
546: *
547: * @param context The context in which the expression is to be evaluated
548: * @exception XPathException if any dynamic error occurs evaluating the
549: * expression
550: * @return the effective boolean value
551: */
552:
553: public boolean effectiveBooleanValue(XPathContext context)
554: throws XPathException {
555: return ExpressionTool.effectiveBooleanValue(iterate(context));
556: }
557:
558: /**
559: * Process the instruction, without returning any tail calls
560: * @param context The dynamic context, giving access to the current node,
561: * the current variables, etc.
562: */
563:
564: public void process(XPathContext context) throws XPathException {
565: int m = getImplementationMethod();
566:
567: if ((m & EVALUATE_METHOD) != 0) {
568: Item item = evaluateItem(context);
569: // Need to cater for it being a tailcall returned from a function
570: Instruction.appendItem(item, context.getReceiver(),
571: locationId);
572:
573: } else if ((m & ITERATE_METHOD) != 0) {
574:
575: SequenceIterator iter = iterate(context);
576: try {
577: while (true) {
578: Item it = iter.next();
579: if (it == null)
580: break;
581: // Need to cater for it being a tailcall returned from a function
582: Instruction.appendItem(it, context.getReceiver(),
583: locationId);
584: }
585: } catch (XPathException e) {
586: if (e.getLocator() == null) {
587: e.setLocator(this );
588: }
589: throw e;
590: }
591:
592: } else {
593: dynamicError(
594: "process() is not implemented in the subclass "
595: + this .getClass(), context);
596: }
597: }
598:
599: /**
600: * Method used in subclasses to signal a dynamic error
601: */
602:
603: protected void dynamicError(String message, XPathContext context)
604: throws DynamicError {
605: DynamicError err = new DynamicError(message, getSourceLocator());
606: err.setXPathContext(context);
607: throw err;
608: }
609:
610: /**
611: * Method used in subclasses to signal a dynamic error
612: */
613:
614: protected void dynamicError(String message, String code,
615: XPathContext context) throws DynamicError {
616: DynamicError err = new DynamicError(message, getSourceLocator());
617: err.setXPathContext(context);
618: err.setErrorCode(code);
619: throw err;
620: }
621:
622: /**
623: * Method used in subclasses to signal a runtime type error
624: */
625:
626: protected void typeError(String message, XPathContext context)
627: throws DynamicError {
628: DynamicError e = new DynamicError(message, getSourceLocator());
629: e.setIsTypeError(true);
630: e.setXPathContext(context);
631: throw e;
632: }
633:
634: /**
635: * Method used in subclasses to signal a runtime type error
636: */
637:
638: protected void typeError(String message, String errorCode,
639: XPathContext context) throws DynamicError {
640: DynamicError e = new DynamicError(message, getSourceLocator());
641: e.setIsTypeError(true);
642: e.setErrorCode(errorCode);
643: e.setXPathContext(context);
644: throw e;
645: }
646:
647: /**
648: * Get a SourceLocator for this expression
649: */
650:
651: private SourceLocator getSourceLocator() {
652: return ExpressionTool.getLocator(this );
653: }
654:
655: /**
656: * Get InstructionInfo for this expression
657: */
658:
659: public InstructionInfo getInstructionInfo() {
660: InstructionDetails details = new InstructionDetails();
661: details.setConstructType(getConstructType());
662: details.setProperty("expression", this );
663: details.setSystemId(getSystemId());
664: details.setLineNumber(getLineNumber());
665: details.setColumnNumber(getColumnNumber());
666: if (this instanceof Assignation) {
667: details.setObjectNameCode(((Assignation) this )
668: .getVariableNameCode());
669: }
670: return details;
671: }
672:
673: /**
674: * Get the type of this expression for use in tracing and diagnostics
675: * @return the type of expression, as enumerated in class {@link Location}
676: */
677:
678: protected int getConstructType() {
679: return Location.XPATH_EXPRESSION;
680: }
681:
682: /**
683: * Diagnostic method: search the tree for an expression whose parent expression is incorrectly set
684: */
685:
686: public boolean hasBadParentPointer() {
687: Iterator iter = iterateSubExpressions();
688: while (iter.hasNext()) {
689: Expression exp = (Expression) iter.next();
690: if (exp instanceof ComputedExpression) {
691: if (this != exp.getParentExpression()) {
692: System.err.println("Bad parent pointer to "
693: + exp.getParentExpression() + " found in "
694: + exp);
695: return true;
696: }
697: if (((ComputedExpression) exp).hasBadParentPointer()) {
698: System.err.println("Found in " + exp);
699: return true;
700: }
701: }
702: }
703: return false;
704: }
705:
706: }
707:
708: //
709: // The contents of this file are subject to the Mozilla Public License Version 1.0 (the "License");
710: // you may not use this file except in compliance with the License. You may obtain a copy of the
711: // License at http://www.mozilla.org/MPL/
712: //
713: // Software distributed under the License is distributed on an "AS IS" basis,
714: // WITHOUT WARRANTY OF ANY KIND, either express or implied.
715: // See the License for the specific language governing rights and limitations under the License.
716: //
717: // The Original Code is: all this file.
718: //
719: // The Initial Developer of the Original Code is Michael H. Kay.
720: //
721: // Contributor(s): Michael Kay
722: //
|