001: package net.sf.saxon.expr;
002:
003: import net.sf.saxon.om.Axis;
004: import net.sf.saxon.om.Item;
005: import net.sf.saxon.om.NamePool;
006: import net.sf.saxon.om.SequenceIterator;
007: import net.sf.saxon.pattern.AnyNodeTest;
008: import net.sf.saxon.pattern.NodeKindTest;
009: import net.sf.saxon.pattern.NodeTest;
010: import net.sf.saxon.sort.DocumentSorter;
011: import net.sf.saxon.sort.Reverser;
012: import net.sf.saxon.trace.Location;
013: import net.sf.saxon.trans.XPathException;
014: import net.sf.saxon.type.ItemType;
015: import net.sf.saxon.type.Type;
016: import net.sf.saxon.type.TypeHierarchy;
017: import net.sf.saxon.value.Cardinality;
018: import net.sf.saxon.value.EmptySequence;
019: import net.sf.saxon.value.SequenceType;
020:
021: import java.io.PrintStream;
022: import java.util.Iterator;
023:
024: /**
025: * An expression that establishes a set of nodes by following relationships between nodes
026: * in the document. Specifically, it consists of a start expression which defines a set of
027: * nodes, and a Step which defines a relationship to be followed from those nodes to create
028: * a new set of nodes.
029: */
030:
031: public final class PathExpression extends ComputedExpression implements
032: MappingFunction {
033:
034: private Expression start;
035: private Expression step;
036: private transient int state = 0; // 0 = raw, 1 = simplified, 2 = analyzed, 3 = optimized
037:
038: /**
039: * Constructor
040: * @param start A node-set expression denoting the absolute or relative set of nodes from which the
041: * navigation path should start.
042: * @param step The step to be followed from each node in the start expression to yield a new
043: * node-set
044: */
045:
046: public PathExpression(Expression start, Expression step) {
047: this .start = start;
048: this .step = step;
049: adoptChildExpression(start);
050: adoptChildExpression(step);
051:
052: // If start is a path expression such as a, and step is b/c, then
053: // instead of a/(b/c) we construct (a/b)/c. This is because it often avoids
054: // a sort.
055:
056: // The "/" operator in XPath 2.0 is not always associative. Problems
057: // can occur if position() and last() are used on the rhs, or if node-constructors
058: // appear, e.g. //b/../<d/>. So we only do this rewrite if the step is a path
059: // expression in which both operands are axis expressions optionally with predicates
060:
061: if (step instanceof PathExpression) {
062: PathExpression stepPath = (PathExpression) step;
063: if (isFilteredAxisPath(stepPath.start)
064: && isFilteredAxisPath(stepPath.step)) {
065: this .start = new PathExpression(start, stepPath.start);
066: ExpressionTool.copyLocationInfo(start, this .start);
067: this .step = stepPath.step;
068: resetStaticProperties();
069: }
070: }
071: }
072:
073: /**
074: * Get the start expression (the left-hand operand)
075: */
076:
077: public Expression getStartExpression() {
078: return start;
079: }
080:
081: /**
082: * Get the step expression (the right-hand operand)
083: */
084:
085: public Expression getStepExpression() {
086: return step;
087: }
088:
089: /**
090: * Determine whether an operand of a PathExpression is an
091: * axis step with optional filter predicates.
092: */
093:
094: private static boolean isFilteredAxisPath(Expression exp) {
095: if (exp instanceof AxisExpression) {
096: return true;
097: } else {
098: while (exp instanceof FilterExpression) {
099: exp = ((FilterExpression) exp).getBaseExpression();
100: }
101: return exp instanceof AxisExpression;
102: }
103: }
104:
105: /**
106: * Determine the data type of the items returned by this exprssion
107: * @return the type of the step
108: * @param th
109: */
110:
111: public final ItemType getItemType(TypeHierarchy th) {
112: return step.getItemType(th);
113: }
114:
115: /**
116: * Simplify an expression
117: * @return the simplified expression
118: */
119:
120: public Expression simplify(StaticContext env) throws XPathException {
121: if (state > 0)
122: return this ;
123: state = 1;
124: start = start.simplify(env);
125: step = step.simplify(env);
126: resetStaticProperties();
127:
128: // if the start expression is an empty sequence, then the whole PathExpression is empty
129: if (start instanceof EmptySequence) {
130: return start;
131: }
132:
133: // if the simplified Step is an empty sequence, then the whole PathExpression is empty
134: if (step instanceof EmptySequence) {
135: return step;
136: }
137:
138: // Remove a redundant "." from the path
139: // Note: we are careful not to do this unless the other operand is an ordered node-set.
140: // In other cases, ./E (or E/.) is not a no-op, because it forces sorting.
141:
142: if (start instanceof ContextItemExpression) {
143: if (step instanceof PathExpression
144: || (step.getSpecialProperties() & StaticProperty.ORDERED_NODESET) != 0) {
145: return step;
146: }
147: }
148:
149: if (step instanceof ContextItemExpression
150: && (start instanceof PathExpression || (start
151: .getSpecialProperties() & StaticProperty.ORDERED_NODESET) != 0)) {
152: return start;
153: }
154:
155: // Remove a redundant "." in the middle of a path expression
156:
157: if (step instanceof PathExpression
158: && ((PathExpression) step).getFirstStep() instanceof ContextItemExpression) {
159: return new PathExpression(start, ((PathExpression) step)
160: .getRemainingSteps());
161: }
162:
163: if (start instanceof PathExpression
164: && ((PathExpression) start).getLastStep() instanceof ContextItemExpression) {
165: return new PathExpression(((PathExpression) start)
166: .getLeadingSteps(), step);
167: }
168:
169: // the expression /.. is sometimes used to represent the empty node-set
170:
171: if (start instanceof RootExpression
172: && step instanceof ParentNodeExpression) {
173: return EmptySequence.getInstance();
174: }
175:
176: return this ;
177: }
178:
179: // Simplify an expression of the form a//b, where b has no positional filters.
180: // This comes out of the contructor above as (a/descendent-or-self::node())/child::b,
181: // but it is equivalent to a/descendant::b; and the latter is better as it
182: // doesn't require sorting. Note that we can't do this until type information is available,
183: // as we need to know whether any filters are positional or not.
184:
185: private PathExpression simplifyDescendantPath(StaticContext env)
186: throws XPathException {
187:
188: Expression st = start;
189:
190: // detect .//x as a special case; this will appear as descendant-or-self::node()/x
191:
192: if (start instanceof AxisExpression) {
193: AxisExpression stax = (AxisExpression) start;
194: if (stax.getAxis() != Axis.DESCENDANT_OR_SELF) {
195: return null;
196: }
197: ContextItemExpression cie = new ContextItemExpression();
198: ExpressionTool.copyLocationInfo(this , cie);
199: st = new PathExpression(cie, stax);
200: ExpressionTool.copyLocationInfo(this , st);
201: }
202:
203: if (!(st instanceof PathExpression)) {
204: return null;
205: }
206:
207: PathExpression startPath = (PathExpression) st;
208: if (!(startPath.step instanceof AxisExpression)) {
209: return null;
210: }
211:
212: AxisExpression mid = (AxisExpression) startPath.step;
213: if (mid.getAxis() != Axis.DESCENDANT_OR_SELF) {
214: return null;
215: }
216:
217: NodeTest test = mid.getNodeTest();
218: if (!(test == null || test instanceof AnyNodeTest)) {
219: return null;
220: }
221:
222: Expression underlyingStep = step;
223: while (underlyingStep instanceof FilterExpression) {
224: if (((FilterExpression) underlyingStep).isPositional(env
225: .getNamePool().getTypeHierarchy())) {
226: return null;
227: }
228: underlyingStep = ((FilterExpression) underlyingStep)
229: .getBaseExpression();
230: }
231:
232: if (!(underlyingStep instanceof AxisExpression)) {
233: return null;
234: }
235:
236: AxisExpression underlyingAxis = (AxisExpression) underlyingStep;
237: if (underlyingAxis.getAxis() == Axis.CHILD) {
238:
239: ComputedExpression newStep = new AxisExpression(
240: Axis.DESCENDANT, ((AxisExpression) underlyingStep)
241: .getNodeTest());
242: ExpressionTool.copyLocationInfo(this , newStep);
243:
244: underlyingStep = step;
245: while (underlyingStep instanceof FilterExpression) {
246: // Add any filters to the new expression. We know they aren't
247: // positional, so the order of the filters doesn't matter.
248: newStep = new FilterExpression(
249: newStep,
250: ((FilterExpression) underlyingStep).getFilter(),
251: env);
252: ExpressionTool
253: .copyLocationInfo(underlyingStep, newStep);
254: underlyingStep = ((FilterExpression) underlyingStep)
255: .getBaseExpression();
256: }
257:
258: //System.err.println("Simplified this:");
259: // display(10);
260: //System.err.println("as this:");
261: // new PathExpression(startPath.start, newStep).display(10);
262:
263: PathExpression newPath = new PathExpression(
264: startPath.start, newStep);
265: ExpressionTool.copyLocationInfo(this , newPath);
266: return newPath;
267: }
268:
269: if (underlyingAxis.getAxis() == Axis.ATTRIBUTE) {
270:
271: // turn the expression a//@b into a/descendant-or-self::*/@b
272:
273: ComputedExpression newStep = new AxisExpression(
274: Axis.DESCENDANT_OR_SELF, NodeKindTest.ELEMENT);
275: ExpressionTool.copyLocationInfo(this , newStep);
276:
277: PathExpression newPath = new PathExpression(
278: new PathExpression(startPath.start, newStep), step);
279: ExpressionTool.copyLocationInfo(this , newPath);
280: return newPath;
281: }
282:
283: return null;
284: }
285:
286: /**
287: * Optimize the expression and perform type analysis
288: */
289:
290: public Expression typeCheck(StaticContext env,
291: ItemType contextItemType) throws XPathException {
292:
293: final TypeHierarchy th = env.getNamePool().getTypeHierarchy();
294: if (state >= 2) {
295: // we've already done the main analysis, and we don't want to do it again because
296: // decisions on sorting get upset. But we have new information, namely the contextItemType,
297: // so we use that to check that it's a node
298: Expression start2 = start.typeCheck(env, contextItemType);
299: if (start2 != start) {
300: adoptChildExpression(start2);
301: start = start2;
302: }
303: Expression step2 = step.typeCheck(env, start
304: .getItemType(th));
305: if (step2 != step) {
306: adoptChildExpression(step2);
307: step = step2;
308: }
309: return this ;
310: }
311: ;
312: state = 2;
313:
314: Expression start2 = start.typeCheck(env, contextItemType);
315: if (start2 != start) {
316: adoptChildExpression(start2);
317: start = start2;
318: }
319:
320: // The first operand must be of type node()*
321:
322: RoleLocator role0 = new RoleLocator(RoleLocator.BINARY_EXPR,
323: "/", 0, null);
324: role0.setSourceLocator(this );
325: role0.setErrorCode("XPTY0019");
326: start2 = TypeChecker.staticTypeCheck(start,
327: SequenceType.NODE_SEQUENCE, false, role0, env);
328: if (start2 != start) {
329: adoptChildExpression(start2);
330: start = start2;
331: }
332:
333: // Now check the second operand
334:
335: Expression step2 = step.typeCheck(env, start.getItemType(th));
336: if (step2 != step) {
337: adoptChildExpression(step2);
338: step = step2;
339: }
340:
341: // We distinguish three cases for the second operand: either it is known statically to deliver
342: // nodes only (a traditional path expression), or it is known statically to deliver atomic values
343: // only (a simple mapping expression), or we don't yet know.
344:
345: ItemType stepType = step.getItemType(th);
346: if (th.isSubType(stepType, Type.NODE_TYPE)) {
347:
348: if ((step.getSpecialProperties() & StaticProperty.NON_CREATIVE) != 0) {
349:
350: // A traditional path expression
351:
352: // We don't need the operands to be sorted; any sorting that's needed
353: // will be done at the top level
354:
355: Optimizer opt = env.getConfiguration().getOptimizer();
356: start2 = ExpressionTool.unsorted(opt, start, false);
357: if (start2 != start) {
358: resetStaticProperties();
359: adoptChildExpression(start2);
360: start = start2;
361: }
362: step2 = ExpressionTool.unsorted(opt, step, false);
363: if (step2 != step) {
364: resetStaticProperties();
365: adoptChildExpression(step2);
366: step = step2;
367: }
368:
369: // Try to simplify expressions such as a//b
370: PathExpression p = simplifyDescendantPath(env);
371: if (p != null) {
372: p.setParentExpression(getParentExpression());
373: return p.simplify(env).typeCheck(env,
374: contextItemType);
375: }
376: }
377:
378: // Decide whether the result needs to be wrapped in a sorting
379: // expression to deliver the results in document order
380:
381: int props = getSpecialProperties();
382:
383: if ((props & StaticProperty.ORDERED_NODESET) != 0) {
384: return this ;
385: } else if ((props & StaticProperty.REVERSE_DOCUMENT_ORDER) != 0) {
386: return new Reverser(this );
387: } else {
388: return new DocumentSorter(this );
389: }
390:
391: } else if (stepType.isAtomicType()) {
392: // This is a simple mapping expression: a/b where b returns atomic values
393: return new SimpleMappingExpression(start, step, false)
394: .simplify(env).typeCheck(env, contextItemType);
395: } else {
396: // This is a hybrid mapping expression, one where we don't know the type of the step
397: // (and therefore, we don't know whether sorting into document order is required) until run-time
398: return new SimpleMappingExpression(start, step, true)
399: .simplify(env).typeCheck(env, contextItemType);
400: }
401: }
402:
403: /**
404: * Optimize the expression and perform type analysis
405: */
406:
407: public Expression optimize(Optimizer opt, StaticContext env,
408: ItemType contextItemType) throws XPathException {
409:
410: final TypeHierarchy th = env.getNamePool().getTypeHierarchy();
411: if (state >= 3) {
412: // we've already done the main analysis, and we don't want to do it again because
413: // decisions on sorting get upset. But we have new information, namely the contextItemType,
414: // so we use that to check that it's a node
415: Expression start2 = start.optimize(opt, env,
416: contextItemType);
417: if (start2 != start) {
418: adoptChildExpression(start2);
419: start = start2;
420: }
421: Expression step2 = step.optimize(opt, env, start
422: .getItemType(th));
423: if (step2 != step) {
424: adoptChildExpression(step2);
425: step = step2;
426: }
427: return this ;
428: }
429: ;
430: state = 3;
431:
432: Expression k = opt.convertPathExpressionToKey(this , env);
433: if (k != null) {
434: return k;
435: }
436:
437: Expression start2 = start.optimize(opt, env, contextItemType);
438: if (start2 != start) {
439: adoptChildExpression(start2);
440: start = start2;
441: }
442: Expression step2 = step.optimize(opt, env, start
443: .getItemType(th));
444: if (step2 != step) {
445: adoptChildExpression(step2);
446: step = step2;
447: }
448:
449: // If any subexpressions within the step are not dependent on the focus,
450: // and if they are not "creative" expressions (expressions that can create new nodes), then
451: // promote them: this causes them to be evaluated once, outside the path expression
452:
453: PromotionOffer offer = new PromotionOffer(opt);
454: offer.action = PromotionOffer.FOCUS_INDEPENDENT;
455: offer.promoteDocumentDependent = (start.getSpecialProperties() & StaticProperty.CONTEXT_DOCUMENT_NODESET) != 0;
456: offer.containingExpression = this ;
457:
458: step = doPromotion(step, offer);
459: resetStaticProperties();
460: if (offer.containingExpression != this ) {
461: state = 0; // allow reanalysis (see test axes286)
462: offer.containingExpression = offer.containingExpression
463: .typeCheck(env, contextItemType).optimize(opt, env,
464: contextItemType);
465: return offer.containingExpression;
466: }
467: return this ;
468:
469: }
470:
471: /**
472: * Promote this expression if possible
473: */
474:
475: public Expression promote(PromotionOffer offer)
476: throws XPathException {
477: Expression p = this ;
478: if (offer.action == PromotionOffer.RANGE_INDEPENDENT) {
479: // try converting the expression first from a/b/c[pred] to (a/b/c)[pred] so that a/b/c can be promoted
480:
481: final Optimizer optimizer = offer.getOptimizer();
482: FilterExpression p2 = optimizer.convertToFilterExpression(
483: this , optimizer.getConfiguration().getNamePool()
484: .getTypeHierarchy());
485: if (p2 != null) {
486: return p2.promote(offer);
487: }
488: }
489: Expression exp = offer.accept(p);
490: if (exp != null) {
491: return exp;
492: } else {
493: start = doPromotion(start, offer);
494: if (offer.action == PromotionOffer.INLINE_VARIABLE_REFERENCES
495: || offer.action == PromotionOffer.REPLACE_CURRENT) {
496: // Don't pass on other requests. We could pass them on, but only after augmenting
497: // them to say we are interested in subexpressions that don't depend on either the
498: // outer context or the inner context.
499: step = doPromotion(step, offer);
500: }
501: return this ;
502: }
503: }
504:
505: /**
506: * Get the immediate subexpressions of this expression
507: */
508:
509: public Iterator iterateSubExpressions() {
510: return new PairIterator(start, step);
511: }
512:
513: /**
514: * Determine which aspects of the context the expression depends on. The result is
515: * a bitwise-or'ed value composed from constants such as XPathContext.VARIABLES and
516: * XPathContext.CURRENT_NODE
517: */
518:
519: public int computeDependencies() {
520: return start.getDependencies() |
521: // not all dependencies in the step matter, because the context node, etc,
522: // are not those of the outer expression
523: (step.getDependencies() & (StaticProperty.DEPENDS_ON_XSLT_CONTEXT
524: | StaticProperty.DEPENDS_ON_LOCAL_VARIABLES | StaticProperty.DEPENDS_ON_USER_FUNCTIONS));
525: }
526:
527: /**
528: * Get the static properties of this expression (other than its type). The result is
529: * bit-signficant. These properties are used for optimizations. In general, if
530: * property bit is set, it is true, but if it is unset, the value is unknown.
531: */
532:
533: public int computeSpecialProperties() {
534: int startProperties = start.getSpecialProperties();
535: int stepProperties = step.getSpecialProperties();
536:
537: int p = 0;
538: if (!Cardinality.allowsMany(start.getCardinality())) {
539: startProperties |= StaticProperty.ORDERED_NODESET
540: | StaticProperty.PEER_NODESET;
541: }
542: if (!Cardinality.allowsMany(step.getCardinality())) {
543: stepProperties |= StaticProperty.ORDERED_NODESET
544: | StaticProperty.PEER_NODESET;
545: }
546:
547: if ((startProperties & stepProperties & StaticProperty.CONTEXT_DOCUMENT_NODESET) != 0) {
548: p |= StaticProperty.CONTEXT_DOCUMENT_NODESET;
549: }
550: if (((startProperties & StaticProperty.SINGLE_DOCUMENT_NODESET) != 0)
551: && ((stepProperties & StaticProperty.CONTEXT_DOCUMENT_NODESET) != 0)) {
552: p |= StaticProperty.SINGLE_DOCUMENT_NODESET;
553: }
554: if ((startProperties & stepProperties & StaticProperty.PEER_NODESET) != 0) {
555: p |= StaticProperty.PEER_NODESET;
556: }
557: if ((startProperties & stepProperties & StaticProperty.SUBTREE_NODESET) != 0) {
558: p |= StaticProperty.SUBTREE_NODESET;
559: }
560:
561: if (testNaturallySorted(startProperties, stepProperties)) {
562: p |= StaticProperty.ORDERED_NODESET;
563: }
564:
565: if (testNaturallyReverseSorted()) {
566: p |= StaticProperty.REVERSE_DOCUMENT_ORDER;
567: }
568:
569: if ((startProperties & stepProperties & StaticProperty.NON_CREATIVE) != 0) {
570: p |= StaticProperty.NON_CREATIVE;
571: }
572:
573: return p;
574: }
575:
576: /**
577: * Determine if we can guarantee that the nodes are delivered in document order.
578: * This is true if the start nodes are sorted peer nodes
579: * and the step is based on an Axis within the subtree rooted at each node.
580: * It is also true if the start is a singleton node and the axis is sorted.
581: */
582:
583: private boolean testNaturallySorted(int startProperties,
584: int stepProperties) {
585:
586: // System.err.println("**** Testing pathExpression.isNaturallySorted()");
587: // display(20);
588: // System.err.println("Start is ordered node-set? " + start.isOrderedNodeSet());
589: // System.err.println("Start is naturally sorted? " + start.isNaturallySorted());
590: // System.err.println("Start is singleton? " + start.isSingleton());
591:
592: if ((stepProperties & StaticProperty.ORDERED_NODESET) == 0) {
593: return false;
594: }
595: if (Cardinality.allowsMany(start.getCardinality())) {
596: if ((startProperties & StaticProperty.ORDERED_NODESET) == 0) {
597: return false;
598: }
599: } else {
600: //if ((stepProperties & StaticProperty.ORDERED_NODESET) != 0) {
601: return true;
602: //}
603: }
604:
605: // We know now that both the start and the step are sorted. But this does
606: // not necessarily mean that the combination is sorted.
607:
608: // The result is sorted if the start is sorted and the step selects attributes
609: // or namespaces
610:
611: if ((stepProperties & StaticProperty.ATTRIBUTE_NS_NODESET) != 0) {
612: return true;
613: }
614:
615: // The result is sorted if the start selects "peer nodes" (that is, a node-set in which
616: // no node is an ancestor of another) and the step selects within the subtree rooted
617: // at the context node
618:
619: if (((startProperties & StaticProperty.PEER_NODESET) != 0)
620: && ((stepProperties & StaticProperty.SUBTREE_NODESET) != 0)) {
621: return true;
622: }
623:
624: return false;
625: }
626:
627: /**
628: * Determine if the path expression naturally returns nodes in reverse document order
629: */
630:
631: private boolean testNaturallyReverseSorted() {
632:
633: // Some examples of expressions that are naturally reverse sorted:
634: // ../@x
635: // ancestor::*[@lang]
636: // ../preceding-sibling::x
637: // $x[1]/preceding-sibling::node()
638:
639: // This information is used to do a simple reversal of the nodes
640: // instead of a full sort, which is significantly cheaper, especially
641: // when using tree models (such as DOM and JDOM) in which comparing
642: // nodes in document order is an expensive operation.
643:
644: if (!Cardinality.allowsMany(start.getCardinality())
645: && (step instanceof AxisExpression)) {
646: return !Axis.isForwards[((AxisExpression) step).getAxis()];
647: }
648:
649: if (!(start instanceof AxisExpression)) {
650: return false;
651: }
652:
653: if (Axis.isForwards[((AxisExpression) start).getAxis()]) {
654: return false;
655: }
656:
657: // if (step instanceof AttributeReference) {
658: // return true;
659: // }
660:
661: return false;
662: }
663:
664: /**
665: * Determine the static cardinality of the expression
666: */
667:
668: public int computeCardinality() {
669: int c1 = start.getCardinality();
670: int c2 = step.getCardinality();
671: return Cardinality.multiply(c1, c2);
672: }
673:
674: /**
675: * Is this expression the same as another expression?
676: */
677:
678: public boolean equals(Object other) {
679: if (!(other instanceof PathExpression)) {
680: return false;
681: }
682: PathExpression p = (PathExpression) other;
683: return (start.equals(p.start) && step.equals(p.step));
684: }
685:
686: /**
687: * get HashCode for comparing two expressions
688: */
689:
690: public int hashCode() {
691: return "PathExpression".hashCode() + start.hashCode()
692: + step.hashCode();
693: }
694:
695: /**
696: * Get the first step in this expression. A path expression A/B/C is represented as (A/B)/C, but
697: * the first step is A
698: */
699:
700: public Expression getFirstStep() {
701: if (start instanceof PathExpression) {
702: return ((PathExpression) start).getFirstStep();
703: } else {
704: return start;
705: }
706: }
707:
708: /**
709: * Get all steps after the first.
710: * This is complicated by the fact that A/B/C is represented as ((A/B)/C; we are required
711: * to return B/C
712: */
713:
714: public Expression getRemainingSteps() {
715: if (start instanceof PathExpression) {
716: PathExpression rem = new PathExpression(
717: ((PathExpression) start).getRemainingSteps(), step);
718: rem.setParentExpression(getParentExpression());
719: ExpressionTool.copyLocationInfo(start, rem);
720: return rem;
721: } else {
722: return step;
723: }
724: }
725:
726: /**
727: * Get the last step of the path expression
728: */
729:
730: public Expression getLastStep() {
731: if (step instanceof PathExpression) {
732: return ((PathExpression) step).getLastStep();
733: } else {
734: return step;
735: }
736: }
737:
738: /**
739: * Get a path expression consisting of all steps except the last
740: */
741:
742: public Expression getLeadingSteps() {
743: if (step instanceof PathExpression) {
744: PathExpression rem = new PathExpression(start,
745: ((PathExpression) step).getLeadingSteps());
746: ExpressionTool.copyLocationInfo(start, rem);
747: return rem;
748: } else {
749: return start;
750: }
751: }
752:
753: /**
754: * Test whether a path expression is an absolute path - that is, a path whose first step selects a
755: * document node
756: */
757:
758: public boolean isAbsolute(TypeHierarchy th) {
759: return getFirstStep().getItemType(th).getPrimitiveType() == Type.DOCUMENT;
760: }
761:
762: /**
763: * Iterate the path-expression in a given context
764: * @param context the evaluation context
765: */
766:
767: public SequenceIterator iterate(XPathContext context)
768: throws XPathException {
769:
770: // This class delivers the result of the path expression in unsorted order,
771: // without removal of duplicates. If sorting and deduplication are needed,
772: // this is achieved by wrapping the path expression in a DocumentSorter
773:
774: SequenceIterator master = start.iterate(context);
775: XPathContext context2 = context.newMinorContext();
776: context2.setCurrentIterator(master);
777: context2.setOriginatingConstructType(Location.PATH_EXPRESSION);
778:
779: master = new MappingIterator(master, this , context2);
780: return master;
781:
782: }
783:
784: /**
785: * Mapping function, from a node returned by the start iteration, to a sequence
786: * returned by the child.
787: */
788:
789: public Object map(Item item, XPathContext context)
790: throws XPathException {
791: return step.iterate(context);
792: }
793:
794: /**
795: * Diagnostic print of expression structure
796: */
797:
798: public void display(int level, NamePool pool, PrintStream out) {
799: out.println(ExpressionTool.indent(level) + "path /");
800: start.display(level + 1, pool, out);
801: step.display(level + 1, pool, out);
802: }
803:
804: }
805:
806: //
807: // The contents of this file are subject to the Mozilla Public License Version 1.0 (the "License");
808: // you may not use this file except in compliance with the License. You may obtain a copy of the
809: // License at http://www.mozilla.org/MPL/
810: //
811: // Software distributed under the License is distributed on an "AS IS" basis,
812: // WITHOUT WARRANTY OF ANY KIND, either express or implied.
813: // See the License for the specific language governing rights and limitations under the License.
814: //
815: // The Original Code is: all this file.
816: //
817: // The Initial Developer of the Original Code is Michael H. Kay.
818: //
819: // Portions created by (your name) are Copyright (C) (your legal entity). All Rights Reserved.
820: //
821: // Contributor(s): none.
822: //
|