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