1 //------------------------------------------------------------------------------ 2 // <copyright file="XPathContext.cs" company="Microsoft"> 3 // Copyright (c) Microsoft Corporation. All rights reserved. 4 // </copyright> 5 // <owner current="true" primary="true">sdub</owner> 6 //------------------------------------------------------------------------------ 7 8 #if DontUse 9 // XPathContext is not used any more but comments in it and Replacer visitor may be used to 10 // optimize code XSLT generates on last(). 11 using System; 12 using System.Diagnostics; 13 using System.Collections; 14 using System.Xml; 15 using System.Xml.XPath; 16 using MS.Internal.Xml; 17 18 namespace System.Xml.Xsl.XPath { 19 20 internal class XPathContext { 21 // Context is the most fundamental concept of XPath 22 // In docs it is -- "current node-set" and "current node in this node-set" 23 // on practice in this implementation we have "current node" (C), "position of current node in current node-set" (P) 24 // and "size of the current node-set" (S) 25 // All XPath expressions consume context, "step" expressions change it 26 // In this XPath implementation we passing context as Tuple. 27 // In most cases this Tuple has For iterator bind to some expression. 28 // (1): Tuple1( For1(bind1), cond{For1}, For1 ) 29 // To take (C) from such context XPath uses its For1 iterator. 30 // 31 // (P) is little bit more complex. It should be PositionOf(For1) but this will be correct only if 32 // cond{For1} == True 33 // To enforce this (1) can be rewritten as 34 // (2): Tuple1( For1(Tuple2( For2(bind1), cond{For2}, For2 ), True, For1 ) 35 // Note: cond{For2} is result of replacing For1 to For2 in cond{For1} 36 // 37 // (S) most complex case. We want to cash with Let variable node-set we are iterating: 38 // (3): Tuple1( Let(Tuple2( For2(bind1), cond{For2}, For2) ), True, Tuple3( For1(Let), True, For1 ) ) 39 // (S) = SetLength(Let); (P) = PositionOf(For1) 40 // 41 // So 3: is most generic representation of XPath context while (1) & (2) is its simplified form. 42 // We always create context node as (1) and convert it to (2) & (3) on demand. 43 // XPath function position() force (1) -> (2) transformation if condition != true 44 // XPath function last() force (1) -> (3) or (2) -> (3) transformation if node-set wasn't cashed yet. 45 // 46 // Consider expression "foo[bar + position() + last()]" 47 // "foo" ==> Tuple1(For1(Content(XmlContext)), Name(For1)=='foo', For1)) 48 // "bar" ==> Tuple2(For2(Content(For1 )), Name(For2)=='bar', For2)) 49 // 50 // "position()" ==> PositionOf(For1) 51 // After "foo" was rewritten as: 52 // "for" ==> Tuple1(For1(Tuple3(For3(Content(XmlContext)), Name(For3)=='foo', For3)), True, For1) 53 // Note1: all external references to "foo" expression are still valid because we didn't change Tuple1 54 // Note2: "bar" is still valid because it refers to For1. 55 // Note3: "Name(For1)=='foo'" was rewritten as "Name(For3)=='foo'" 56 // "last()" ==> SetLength(Let) 57 // "for" ==> Tuple1(Let(Tuple3(For3(Content(XmlContext)), Name(For3)=='foo', For3)), True, Tuple4(For1(Let), True, For1)) 58 // Note2: "bar" and "position" are still valid because they refer to For1. 59 60 // Issue: 61 // What restriction we should put on return value of context tuple. 62 // In XSLT when we fixing up call-templates return value shouldn't be touched 63 // In simple XPath a/b[2] return value is pure iterator and can be left as it is 64 // In more complex XPath (a/b)[2] resturn value is Tuple and should be isolated with context. 65 // this can be done as grouping as well. 66 // XSLT complies for-each and apply-templates in form (2) so converting to (3) shouldn't be a problem 67 // -- It look like solution is group (a/b) with For(DocOrderDistinct(...)) and we are set. 68 69 // Methods that deal with XPath context. Xslt.QilGenerator calls these method as well: GetCurrentNode(QilTuple context)70 public static QilNode GetCurrentNode(QilTuple context) { 71 Debug.Assert(context != null); 72 Debug.Assert(GetTuple(context).For.Type == QilNodeType.For); 73 return GetTuple(context).For; 74 } 75 GetCurrentPosition(QilFactory f, QilTuple context)76 public static QilNode GetCurrentPosition(QilFactory f, QilTuple context) { 77 Debug.Assert(context != null); 78 if (context.Where.Type != QilNodeType.True) { 79 Debug.Assert(context.For.Type == QilNodeType.For); 80 // convert context (1) --> (2) 81 QilIterator for2 = f.For(context.For.Binding); 82 QilNode cnd2 = new Replacer(f).Replace(/*inExpr:*/context.Where, /*from:*/context.For, /*to:*/for2); 83 context.For.Binding = f.OldTuple(for2, cnd2, for2); 84 context.Where = f.True(); 85 } 86 return f.Convert(f.PositionOf((QilIterator)XPathContext.GetCurrentNode(context)), f.TypeFactory.Double()); 87 } 88 GetLastPosition(QilFactory f, QilTuple context)89 public static QilNode GetLastPosition(QilFactory f, QilTuple context) { 90 return f.Convert(f.Length(context.Clone(f)), f.TypeFactory.Double()); 91 } 92 GetTuple(QilTuple context)93 public static QilTuple GetTuple(QilTuple context) { 94 Debug.Assert(context != null); 95 if (context.For.Type == QilNodeType.Let) { 96 Debug.Assert(context.Where.Type == QilNodeType.True); 97 Debug.Assert(context.Return.Type == QilNodeType.OldTuple); 98 return (QilTuple) context.Return; 99 } 100 return context; 101 } 102 103 private class Replacer : QilActiveVisitor { 104 QilIterator from, to; 105 Replacer(QilFactory f)106 public Replacer(QilFactory f) : base(f) {} 107 Replace(QilNode inExpr, QilIterator from, QilIterator to)108 public QilNode Replace(QilNode inExpr, QilIterator from, QilIterator to) { 109 this.from = from; 110 this.to = to ; 111 return Visit(inExpr); 112 } 113 VisitClassReference(QilNode it)114 protected override QilNode VisitClassReference(QilNode it) { 115 if (it == from) { 116 return to; 117 } 118 return it; 119 } 120 } 121 } 122 } 123 #endif