KickJava   Java API By Example, From Geeks To Geeks.

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


1 package net.sf.saxon.expr;
2
3 import net.sf.saxon.om.Axis;
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.pattern.AnyNodeTest;
8 import net.sf.saxon.pattern.NodeKindTest;
9 import net.sf.saxon.pattern.NodeTest;
10 import net.sf.saxon.sort.DocumentSorter;
11 import net.sf.saxon.sort.Reverser;
12 import net.sf.saxon.trace.Location;
13 import net.sf.saxon.trans.XPathException;
14 import net.sf.saxon.type.ItemType;
15 import net.sf.saxon.type.Type;
16 import net.sf.saxon.value.Cardinality;
17 import net.sf.saxon.value.EmptySequence;
18 import net.sf.saxon.value.SequenceType;
19
20 import java.io.PrintStream JavaDoc;
21 import java.util.Iterator JavaDoc;
22
23 /**
24  * An expression that establishes a set of nodes by following relationships between nodes
25  * in the document. Specifically, it consists of a start expression which defines a set of
26  * nodes, and a Step which defines a relationship to be followed from those nodes to create
27  * a new set of nodes.
28  */

29
30 public final class PathExpression extends ComputedExpression implements MappingFunction {
31
32     private Expression start;
33     private Expression step;
34     private transient int state = 0; // 0 = raw, 1 = simplified, 2 = analyzed, 3 = optimized
35

36     /**
37      * Constructor
38      * @param start A node-set expression denoting the absolute or relative set of nodes from which the
39      * navigation path should start.
40      * @param step The step to be followed from each node in the start expression to yield a new
41      * node-set
42      */

43
44     public PathExpression(Expression start, Expression step) {
45         this.start = start;
46         this.step = step;
47         adoptChildExpression(start);
48         adoptChildExpression(step);
49
50         // If start is a path expression such as a, and step is b/c, then
51
// instead of a/(b/c) we construct (a/b)/c. This is because it often avoids
52
// a sort.
53

54         // The "/" operator in XPath 2.0 is not always associative. Problems
55
// can occur if position() and last() are used on the rhs, or if node-constructors
56
// appear, e.g. //b/../<d/>. So we only do this rewrite if the step is a path
57
// expression in which both operands are axis expressions optionally with predicates
58

59         if (step instanceof PathExpression) {
60             PathExpression stepPath = (PathExpression) step;
61             if (isFilteredAxisPath(stepPath.start) && isFilteredAxisPath(stepPath.step)) {
62                 this.start = new PathExpression(start, stepPath.start);
63                 ExpressionTool.copyLocationInfo(start, this.start);
64                 this.step = stepPath.step;
65                 resetStaticProperties();
66             }
67         }
68     }
69
70     /**
71      * Get the start expression (the left-hand operand)
72      */

73
74     public Expression getStartExpression() {
75         return start;
76     }
77
78     /**
79      * Get the step expression (the right-hand operand)
80      */

81
82     public Expression getStepExpression() {
83         return step;
84     }
85
86     /**
87      * Determine whether an operand of a PathExpression is an
88      * axis step with optional filter predicates.
89      */

90
91     private static boolean isFilteredAxisPath(Expression exp) {
92         if (exp instanceof AxisExpression) {
93             return true;
94         } else {
95             while (exp instanceof FilterExpression) {
96                 exp = ((FilterExpression) exp).getBaseExpression();
97             }
98             return exp instanceof AxisExpression;
99         }
100     }
101
102     /**
103      * Determine the data type of the items returned by this exprssion
104      * @return the type of the step
105      */

106
107     public final ItemType getItemType() {
108         return step.getItemType();
109     }
110
111     /**
112      * Simplify an expression
113      * @return the simplified expression
114      */

115
116      public Expression simplify(StaticContext env) throws XPathException {
117         if (state > 0) return this;
118         state = 1;
119         start = start.simplify(env);
120         step = step.simplify(env);
121         resetStaticProperties();
122
123         // if the start expression is an empty sequence, then the whole PathExpression is empty
124
if (start instanceof EmptySequence) {
125             return start;
126         }
127
128         // if the simplified Step is an empty sequence, then the whole PathExpression is empty
129
if (step instanceof EmptySequence) {
130             return step;
131         }
132
133         // Remove a redundant "." from the path
134
// Note: we are careful not to do this unless the other operand is an ordered node-set.
135
// In other cases, ./E (or E/.) is not a no-op, because it forces sorting.
136

137         if (start instanceof ContextItemExpression) {
138             if (step instanceof PathExpression || (step.getSpecialProperties() & StaticProperty.ORDERED_NODESET) != 0) {
139                 return step;
140             }
141         }
142
143         if (step instanceof ContextItemExpression &&
144                 (start instanceof PathExpression || (start.getSpecialProperties() & StaticProperty.ORDERED_NODESET) != 0)) {
145             return start;
146         }
147
148         // Remove a redundant "." in the middle of a path expression
149

150         if (step instanceof PathExpression && ((PathExpression)step).getFirstStep() instanceof ContextItemExpression) {
151             return new PathExpression(start, ((PathExpression)step).getRemainingSteps());
152         }
153
154         if (start instanceof PathExpression && ((PathExpression)start).getLastStep() instanceof ContextItemExpression) {
155             return new PathExpression(((PathExpression)start).getLeadingSteps(), step);
156         }
157
158         // the expression /.. is sometimes used to represent the empty node-set
159

160         if (start instanceof RootExpression && step instanceof ParentNodeExpression) {
161             return EmptySequence.getInstance();
162         }
163
164         return this;
165     }
166
167     // Simplify an expression of the form a//b, where b has no positional filters.
168
// This comes out of the contructor above as (a/descendent-or-self::node())/child::b,
169
// but it is equivalent to a/descendant::b; and the latter is better as it
170
// doesn't require sorting. Note that we can't do this until type information is available,
171
// as we need to know whether any filters are positional or not.
172

173     private PathExpression simplifyDescendantPath(StaticContext env) throws XPathException {
174
175         Expression st = start;
176
177         // detect .//x as a special case; this will appear as descendant-or-self::node()/x
178

179         if (start instanceof AxisExpression) {
180             AxisExpression stax = (AxisExpression) start;
181             if (stax.getAxis() != Axis.DESCENDANT_OR_SELF) {
182                 return null;
183             }
184             ContextItemExpression cie = new ContextItemExpression();
185             ExpressionTool.copyLocationInfo(this, cie);
186             st = new PathExpression(cie, stax);
187             ExpressionTool.copyLocationInfo(this, st);
188         }
189
190         if (!(st instanceof PathExpression)) {
191             return null;
192         }
193
194         PathExpression startPath = (PathExpression) st;
195         if (!(startPath.step instanceof AxisExpression)) {
196             return null;
197         }
198
199         AxisExpression mid = (AxisExpression) startPath.step;
200         if (mid.getAxis() != Axis.DESCENDANT_OR_SELF) {
201             return null;
202         }
203
204
205         NodeTest test = mid.getNodeTest();
206         if (!(test == null || test instanceof AnyNodeTest)) {
207             return null;
208         }
209
210         Expression underlyingStep = step;
211         while (underlyingStep instanceof FilterExpression) {
212             if (((FilterExpression) underlyingStep).isPositional()) {
213                 return null;
214             }
215             underlyingStep = ((FilterExpression) underlyingStep).getBaseExpression();
216         }
217
218         if (!(underlyingStep instanceof AxisExpression)) {
219             return null;
220         }
221
222         AxisExpression underlyingAxis = (AxisExpression) underlyingStep;
223         if (underlyingAxis.getAxis() == Axis.CHILD) {
224
225             ComputedExpression newStep =
226                     new AxisExpression(Axis.DESCENDANT,
227                             ((AxisExpression) underlyingStep).getNodeTest());
228             ExpressionTool.copyLocationInfo(this, newStep);
229
230             underlyingStep = step;
231             while (underlyingStep instanceof FilterExpression) {
232                 // Add any filters to the new expression. We know they aren't
233
// positional, so the order of the filters doesn't matter.
234
newStep = new FilterExpression(newStep,
235                         ((FilterExpression) underlyingStep).getFilter(), env);
236                 ExpressionTool.copyLocationInfo(underlyingStep, newStep);
237                 underlyingStep = ((FilterExpression) underlyingStep).getBaseExpression();
238             }
239
240             //System.err.println("Simplified this:");
241
// display(10);
242
//System.err.println("as this:");
243
// new PathExpression(startPath.start, newStep).display(10);
244

245             PathExpression newPath = new PathExpression(startPath.start, newStep);
246             ExpressionTool.copyLocationInfo(this, newPath);
247             return newPath;
248         }
249
250         if (underlyingAxis.getAxis() == Axis.ATTRIBUTE) {
251
252             // turn the expression a//@b into a/descendant-or-self::*/@b
253

254             ComputedExpression newStep =
255                     new AxisExpression(Axis.DESCENDANT_OR_SELF, NodeKindTest.ELEMENT);
256             ExpressionTool.copyLocationInfo(this, newStep);
257
258             PathExpression newPath = new PathExpression(
259                     new PathExpression(startPath.start, newStep),
260                     step);
261             ExpressionTool.copyLocationInfo(this, newPath);
262             return newPath;
263         }
264
265         return null;
266     }
267
268     /**
269      * Optimize the expression and perform type analysis
270      */

271
272     public Expression typeCheck(StaticContext env, ItemType contextItemType) throws XPathException {
273
274         if (state >= 2) {
275             // we've already done the main analysis, and we don't want to do it again because
276
// decisions on sorting get upset. But we have new information, namely the contextItemType,
277
// so we use that to check that it's a node
278
Expression start2 = start.typeCheck(env, contextItemType);
279             if (start2 != start) {
280                 adoptChildExpression(start2);
281                 start = start2;
282             }
283             Expression step2 = step.typeCheck(env, start.getItemType());
284             if (step2 != step) {
285                 adoptChildExpression(step2);
286                 step = step2;
287             }
288             return this;
289         };
290         state = 2;
291
292         Expression start2 = start.typeCheck(env, contextItemType);
293         if (start2 != start) {
294             adoptChildExpression(start2);
295             start = start2;
296         }
297         Expression step2 = step.typeCheck(env, start.getItemType());
298         if (step2 != step) {
299             adoptChildExpression(step2);
300             step = step2;
301         }
302
303         // The first operand must be of type node()*
304

305         RoleLocator role0 = new RoleLocator(RoleLocator.BINARY_EXPR, "/", 0, null);
306         role0.setSourceLocator(this);
307         role0.setErrorCode("XPTY0019");
308         start2 = TypeChecker.staticTypeCheck(start,
309                 SequenceType.NODE_SEQUENCE,
310                 false, role0, env);
311         if (start2 != start) {
312             adoptChildExpression(start2);
313             start = start2;
314         }
315
316 // // If any subexpressions within the step are not dependent on the focus,
317
// // and if they are not "creative" expressions (expressions that can create new nodes), then
318
// // promote them: this causes them to be evaluated once, outside the path expression
319
//
320
// PromotionOffer offer = new PromotionOffer();
321
// offer.action = PromotionOffer.FOCUS_INDEPENDENT;
322
// offer.promoteDocumentDependent = (start.getSpecialProperties() & StaticProperty.CONTEXT_DOCUMENT_NODESET) != 0;
323
// offer.containingExpression = this;
324
//
325
// step2 = step.promote(offer);
326
// if (step2 != step) {
327
// adoptChildExpression(step2);
328
// step = step2;
329
// }
330
// resetStaticProperties();
331
// if (offer.containingExpression != this) {
332
// state = 0; // allow reanalysis (see test axes286)
333
// offer.containingExpression = offer.containingExpression.analyze(env, contextItemType);
334
// return offer.containingExpression;
335
// }
336

337         // We distinguish three cases for the second operand: either it is known statically to deliver
338
// nodes only (a traditional path expression), or it is known statically to deliver atomic values
339
// only (a simple mapping expression), or we don't yet know.
340

341         ItemType stepType = step.getItemType();
342         if (Type.isSubType(stepType, Type.NODE_TYPE)) {
343
344             if ((step.getSpecialProperties() & StaticProperty.NON_CREATIVE) != 0) {
345
346                 // A traditional path expression
347

348                 // We don't need the operands to be sorted; any sorting that's needed
349
// will be done at the top level
350

351                 Optimizer opt = env.getConfiguration().getOptimizer();
352                 start2 = ExpressionTool.unsorted(opt, start, false);
353                 if (start2 != start) {
354                     resetStaticProperties();
355                     adoptChildExpression(start2);
356                     start = start2;
357                 }
358                 step2 = ExpressionTool.unsorted(opt, step, false);
359                 if (step2 != step) {
360                     resetStaticProperties();
361                     adoptChildExpression(step2);
362                     step = step2;
363                 }
364
365                 // Try to simplify expressions such as a//b
366
PathExpression p = simplifyDescendantPath(env);
367                 if (p != null) {
368                     p.setParentExpression(getParentExpression());
369                     return p.simplify(env).typeCheck(env, contextItemType);
370                 }
371             }
372
373             // Decide whether the result needs to be wrapped in a sorting
374
// expression to deliver the results in document order
375

376             int props = getSpecialProperties();
377
378             if ((props & StaticProperty.ORDERED_NODESET) != 0) {
379                 return this;
380             } else if ((props & StaticProperty.REVERSE_DOCUMENT_ORDER) != 0) {
381                 return new Reverser(this);
382             } else {
383                 return new DocumentSorter(this);
384             }
385
386         } else if (Type.isSubType(stepType, Type.ANY_ATOMIC_TYPE)) {
387             // This is a simple mapping expression: a/b where b returns atomic values
388
return new SimpleMappingExpression(start, step, false).simplify(env).typeCheck(env, contextItemType);
389         } else {
390             // This is a hybrid mapping expression, one where we don't know the type of the step
391
// (and therefore, we don't know whether sorting into document order is required) until run-time
392
return new SimpleMappingExpression(start, step, true).simplify(env).typeCheck(env, contextItemType);
393         }
394     }
395
396     /**
397      * Optimize the expression and perform type analysis
398      */

399
400     public Expression optimize(Optimizer opt, StaticContext env, ItemType contextItemType) throws XPathException {
401
402         if (state >= 3) {
403             // we've already done the main analysis, and we don't want to do it again because
404
// decisions on sorting get upset. But we have new information, namely the contextItemType,
405
// so we use that to check that it's a node
406
Expression start2 = start.optimize(opt, env, contextItemType);
407             if (start2 != start) {
408                 adoptChildExpression(start2);
409                 start = start2;
410             }
411             Expression step2 = step.optimize(opt, env, start.getItemType());
412             if (step2 != step) {
413                 adoptChildExpression(step2);
414                 step = step2;
415             }
416             return this;
417         };
418         state = 3;
419
420         Expression k = opt.convertPathExpressionToKey(this, env);
421         if (k != null) {
422             return k;
423         }
424
425         Expression start2 = start.optimize(opt, env, contextItemType);
426         if (start2 != start) {
427             adoptChildExpression(start2);
428             start = start2;
429         }
430         Expression step2 = step.optimize(opt, env, start.getItemType());
431         if (step2 != step) {
432             adoptChildExpression(step2);
433             step = step2;
434         }
435
436         // If any subexpressions within the step are not dependent on the focus,
437
// and if they are not "creative" expressions (expressions that can create new nodes), then
438
// promote them: this causes them to be evaluated once, outside the path expression
439

440         PromotionOffer offer = new PromotionOffer(opt);
441         offer.action = PromotionOffer.FOCUS_INDEPENDENT;
442         offer.promoteDocumentDependent = (start.getSpecialProperties() & StaticProperty.CONTEXT_DOCUMENT_NODESET) != 0;
443         offer.containingExpression = this;
444
445         step = doPromotion(step, offer);
446         resetStaticProperties();
447         if (offer.containingExpression != this) {
448             state = 0; // allow reanalysis (see test axes286)
449
offer.containingExpression =
450                     offer.containingExpression.typeCheck(env, contextItemType).optimize(opt, env, contextItemType);
451             return offer.containingExpression;
452         }
453         return this;
454
455     }
456
457     /**
458      * Promote this expression if possible
459      */

460
461     public Expression promote(PromotionOffer offer) throws XPathException {
462         Expression p = this;
463         if (offer.action == PromotionOffer.RANGE_INDEPENDENT) {
464             // try converting the expression first from a/b/c[pred] to (a/b/c)[pred] so that a/b/c can be promoted
465
FilterExpression p2 = offer.getOptimizer().convertToFilterExpression(this);
466             if (p2 != null) {
467                 return p2.promote(offer);
468             }
469         }
470         Expression exp = offer.accept(p);
471         if (exp != null) {
472             return exp;
473         } else {
474             start = doPromotion(start, offer);
475             if (offer.action == PromotionOffer.INLINE_VARIABLE_REFERENCES ||
476                     offer.action == PromotionOffer.REPLACE_CURRENT) {
477                 // Don't pass on other requests. We could pass them on, but only after augmenting
478
// them to say we are interested in subexpressions that don't depend on either the
479
// outer context or the inner context.
480
step = doPromotion(step, offer);
481             }
482             return this;
483         }
484     }
485
486
487
488     /**
489      * Get the immediate subexpressions of this expression
490      */

491
492     public Iterator iterateSubExpressions() {
493         return new PairIterator(start, step);
494     }
495     /**
496      * Determine which aspects of the context the expression depends on. The result is
497      * a bitwise-or'ed value composed from constants such as XPathContext.VARIABLES and
498      * XPathContext.CURRENT_NODE
499      */

500
501     public int computeDependencies() {
502         return start.getDependencies() |
503                 // not all dependencies in the step matter, because the context node, etc,
504
// are not those of the outer expression
505
(step.getDependencies() &
506                 (StaticProperty.DEPENDS_ON_XSLT_CONTEXT |
507                     StaticProperty.DEPENDS_ON_LOCAL_VARIABLES |
508                     StaticProperty.DEPENDS_ON_USER_FUNCTIONS));
509     }
510
511     /**
512      * Get the static properties of this expression (other than its type). The result is
513      * bit-signficant. These properties are used for optimizations. In general, if
514      * property bit is set, it is true, but if it is unset, the value is unknown.
515      */

516
517     public int computeSpecialProperties() {
518         int startProperties = start.getSpecialProperties();
519         int stepProperties = step.getSpecialProperties();
520
521         int p = 0;
522         if (!Cardinality.allowsMany(start.getCardinality())) {
523             startProperties |= StaticProperty.ORDERED_NODESET | StaticProperty.PEER_NODESET;
524         }
525         if (!Cardinality.allowsMany(step.getCardinality())) {
526             stepProperties |= StaticProperty.ORDERED_NODESET | StaticProperty.PEER_NODESET;
527         }
528
529
530         if ((startProperties & stepProperties & StaticProperty.CONTEXT_DOCUMENT_NODESET) != 0) {
531             p |= StaticProperty.CONTEXT_DOCUMENT_NODESET;
532         }
533         if (((startProperties & StaticProperty.SINGLE_DOCUMENT_NODESET) != 0) &&
534             ((stepProperties & StaticProperty.CONTEXT_DOCUMENT_NODESET) != 0)) {
535             p |= StaticProperty.SINGLE_DOCUMENT_NODESET;
536         }
537         if ((startProperties & stepProperties & StaticProperty.PEER_NODESET) != 0) {
538             p |= StaticProperty.PEER_NODESET;
539         }
540         if ((startProperties & stepProperties & StaticProperty.SUBTREE_NODESET) != 0) {
541             p |= StaticProperty.SUBTREE_NODESET;
542         }
543
544         if (testNaturallySorted(startProperties, stepProperties)) {
545             p |= StaticProperty.ORDERED_NODESET;
546         }
547
548         if (testNaturallyReverseSorted()) {
549             p |= StaticProperty.REVERSE_DOCUMENT_ORDER;
550         }
551
552         if ((startProperties & stepProperties & StaticProperty.NON_CREATIVE) != 0) {
553             p |= StaticProperty.NON_CREATIVE;
554         }
555
556         return p;
557     }
558
559     /**
560      * Determine if we can guarantee that the nodes are delivered in document order.
561      * This is true if the start nodes are sorted peer nodes
562      * and the step is based on an Axis within the subtree rooted at each node.
563      * It is also true if the start is a singleton node and the axis is sorted.
564      */

565
566     private boolean testNaturallySorted(int startProperties, int stepProperties) {
567
568         // System.err.println("**** Testing pathExpression.isNaturallySorted()");
569
// display(20);
570
// System.err.println("Start is ordered node-set? " + start.isOrderedNodeSet());
571
// System.err.println("Start is naturally sorted? " + start.isNaturallySorted());
572
// System.err.println("Start is singleton? " + start.isSingleton());
573

574         if ((stepProperties & StaticProperty.ORDERED_NODESET) == 0) {
575             return false;
576         }
577         if (Cardinality.allowsMany(start.getCardinality())) {
578             if ((startProperties & StaticProperty.ORDERED_NODESET) == 0) {
579                 return false;
580             }
581         } else {
582             //if ((stepProperties & StaticProperty.ORDERED_NODESET) != 0) {
583
return true;
584             //}
585
}
586
587         // We know now that both the start and the step are sorted. But this does
588
// not necessarily mean that the combination is sorted.
589

590         // The result is sorted if the start is sorted and the step selects attributes
591
// or namespaces
592

593         if ((stepProperties & StaticProperty.ATTRIBUTE_NS_NODESET) != 0) {
594             return true;
595         }
596
597         // The result is sorted if the start selects "peer nodes" (that is, a node-set in which
598
// no node is an ancestor of another) and the step selects within the subtree rooted
599
// at the context node
600

601         if (((startProperties & StaticProperty.PEER_NODESET) != 0) && ((stepProperties & StaticProperty.SUBTREE_NODESET) != 0)) {
602             return true;
603         }
604
605         return false;
606     }
607
608     /**
609      * Determine if the path expression naturally returns nodes in reverse document order
610      */

611
612     private boolean testNaturallyReverseSorted() {
613
614         // Some examples of expressions that are naturally reverse sorted:
615
// ../@x
616
// ancestor::*[@lang]
617
// ../preceding-sibling::x
618
// $x[1]/preceding-sibling::node()
619

620         // This information is used to do a simple reversal of the nodes
621
// instead of a full sort, which is significantly cheaper, especially
622
// when using tree models (such as DOM and JDOM) in which comparing
623
// nodes in document order is an expensive operation.
624

625
626         if (!Cardinality.allowsMany(start.getCardinality()) &&
627                 (step instanceof AxisExpression)) {
628             return !Axis.isForwards[((AxisExpression) step).getAxis()];
629         }
630
631         if (!(start instanceof AxisExpression)) {
632             return false;
633         }
634
635         if (Axis.isForwards[((AxisExpression) start).getAxis()]) {
636             return false;
637         }
638
639 // if (step instanceof AttributeReference) {
640
// return true;
641
// }
642

643         return false;
644     }
645
646     /**
647      * Determine the static cardinality of the expression
648      */

649
650     public int computeCardinality() {
651         int c1 = start.getCardinality();
652         int c2 = step.getCardinality();
653         return Cardinality.multiply(c1, c2);
654     }
655
656     /**
657      * Is this expression the same as another expression?
658      */

659
660     public boolean equals(Object JavaDoc other) {
661         if (!(other instanceof PathExpression)) {
662             return false;
663         }
664         PathExpression p = (PathExpression) other;
665         return (start.equals(p.start) && step.equals(p.step));
666     }
667
668     /**
669      * get HashCode for comparing two expressions
670      */

671
672     public int hashCode() {
673         return "PathExpression".hashCode() + start.hashCode() + step.hashCode();
674     }
675
676     /**
677      * Get the first step in this expression. A path expression A/B/C is represented as (A/B)/C, but
678      * the first step is A
679      */

680
681     public Expression getFirstStep() {
682         if (start instanceof PathExpression) {
683             return ((PathExpression) start).getFirstStep();
684         } else {
685             return start;
686         }
687     }
688
689     /**
690      * Get all steps after the first.
691      * This is complicated by the fact that A/B/C is represented as ((A/B)/C; we are required
692      * to return B/C
693      */

694
695     public Expression getRemainingSteps() {
696         if (start instanceof PathExpression) {
697             PathExpression rem =
698                     new PathExpression(((PathExpression) start).getRemainingSteps(), step);
699             rem.setParentExpression(getParentExpression());
700             ExpressionTool.copyLocationInfo(start, rem);
701             return rem;
702         } else {
703             return step;
704         }
705     }
706
707     /**
708      * Get the last step of the path expression
709      */

710
711     public Expression getLastStep() {
712         if (step instanceof PathExpression) {
713             return ((PathExpression)step).getLastStep();
714         } else {
715             return step;
716         }
717     }
718
719     /**
720      * Get a path expression consisting of all steps except the last
721      */

722
723     public Expression getLeadingSteps() {
724         if (step instanceof PathExpression) {
725             PathExpression rem =
726                     new PathExpression(start, ((PathExpression) step).getLeadingSteps());
727             ExpressionTool.copyLocationInfo(start, rem);
728             return rem;
729         } else {
730             return start;
731         }
732     }
733
734     /**
735      * Test whether a path expression is an absolute path - that is, a path whose first step selects a
736      * document node
737      */

738
739     public boolean isAbsolute() {
740         return getFirstStep().getItemType().getPrimitiveType() == Type.DOCUMENT;
741     }
742
743     /**
744      * Iterate the path-expression in a given context
745      * @param context the evaluation context
746      */

747
748     public SequenceIterator iterate(XPathContext context) throws XPathException {
749
750         // This class delivers the result of the path expression in unsorted order,
751
// without removal of duplicates. If sorting and deduplication are needed,
752
// this is achieved by wrapping the path expression in a DocumentSorter
753

754         SequenceIterator master = start.iterate(context);
755         XPathContext context2 = context.newMinorContext();
756         context2.setCurrentIterator(master);
757         context2.setOriginatingConstructType(Location.PATH_EXPRESSION);
758
759         master = new MappingIterator(master, this, context2);
760         return master;
761
762     }
763
764     /**
765      * Mapping function, from a node returned by the start iteration, to a sequence
766      * returned by the child.
767      */

768
769     public Object JavaDoc map(Item item, XPathContext context) throws XPathException {
770         return step.iterate(context);
771     }
772
773     /**
774      * Diagnostic print of expression structure
775      */

776
777     public void display(int level, NamePool pool, PrintStream JavaDoc out) {
778         out.println(ExpressionTool.indent(level) + "path /");
779         start.display(level + 1, pool, out);
780         step.display(level + 1, pool, out);
781     }
782
783
784 }
785
786
787 //
788
// The contents of this file are subject to the Mozilla Public License Version 1.0 (the "License");
789
// you may not use this file except in compliance with the License. You may obtain a copy of the
790
// License at http://www.mozilla.org/MPL/
791
//
792
// Software distributed under the License is distributed on an "AS IS" basis,
793
// WITHOUT WARRANTY OF ANY KIND, either express or implied.
794
// See the License for the specific language governing rights and limitations under the License.
795
//
796
// The Original Code is: all this file.
797
//
798
// The Initial Developer of the Original Code is Michael H. Kay.
799
//
800
// Portions created by (your name) are Copyright (C) (your legal entity). All Rights Reserved.
801
//
802
// Contributor(s): none.
803
//
804
Popular Tags