1 // Licensed to the .NET Foundation under one or more agreements.
2 // The .NET Foundation licenses this file to you under the MIT license.
3 // See the LICENSE file in the project root for more information.
4 
5 using System;
6 using System.Diagnostics;
7 using System.Collections;
8 using System.Collections.Generic;
9 using System.Xml;
10 using System.Xml.XPath;
11 using MS.Internal.Xml;
12 using System.Xml.Xsl.XPath;
13 using System.Xml.Xsl.Qil;
14 
15 namespace System.Xml.Xsl.Xslt
16 {
17     internal class KeyMatchBuilder : XPathBuilder, XPathPatternParser.IPatternBuilder
18     {
19         private int _depth = 0;
20         private PathConvertor _convertor;
21 
KeyMatchBuilder(IXPathEnvironment env)22         public KeyMatchBuilder(IXPathEnvironment env) : base(env)
23         {
24             _convertor = new PathConvertor(env.Factory);
25         }
26 
StartBuild()27         public override void StartBuild()
28         {
29             Debug.Assert(0 <= _depth && _depth <= 1, "this shouldn't happen");
30             if (_depth == 0)
31             {
32                 base.StartBuild();
33             }
34             _depth++;
35         }
36 
EndBuild(QilNode result)37         public override QilNode EndBuild(QilNode result)
38         {
39             _depth--;
40             Debug.Assert(0 <= _depth && _depth <= 1, "this shouldn't happen");
41             if (result == null)
42             { // special door to clean builder state in exception handlers
43                 return base.EndBuild(result);
44             }
45             if (_depth == 0)
46             {
47                 Debug.Assert(base.numFixupLast == 0);
48                 Debug.Assert(base.numFixupPosition == 0);
49                 result = _convertor.ConvertReletive2Absolute(result, base.fixupCurrent);
50                 result = base.EndBuild(result);
51             }
52             return result;
53         }
54 
55         // -------------------------------------- GetPredicateBuilder() ---------------------------------------
56 
GetPredicateBuilder(QilNode ctx)57         public virtual IXPathBuilder<QilNode> GetPredicateBuilder(QilNode ctx)
58         {
59             return this;
60         }
61 
62         // This code depends on particula shapes that XPathBuilder generates.
63         // It works only on pathes.
64         internal class PathConvertor : QilReplaceVisitor
65         {
66             private new XPathQilFactory f;
67             private QilNode _fixup;
PathConvertor(XPathQilFactory f)68             public PathConvertor(XPathQilFactory f) : base(f.BaseFactory)
69             {
70                 this.f = f;
71             }
72 
ConvertReletive2Absolute(QilNode node, QilNode fixup)73             public QilNode ConvertReletive2Absolute(QilNode node, QilNode fixup)
74             {
75                 QilDepthChecker.Check(node);
76                 Debug.Assert(node != null);
77                 Debug.Assert(fixup != null);
78                 _fixup = fixup;
79                 return this.Visit(node);
80             }
81 
82             // transparantly passing through Union and DocOrder
Visit(QilNode n)83             protected override QilNode Visit(QilNode n)
84             {
85                 if (
86                     n.NodeType == QilNodeType.Union ||
87                     n.NodeType == QilNodeType.DocOrderDistinct ||
88                     n.NodeType == QilNodeType.Filter ||
89                     n.NodeType == QilNodeType.Loop
90                 )
91                 {
92                     return base.Visit(n);
93                 }
94                 return n;
95             }
96             // Filers that travers Content being converted to global travers:
97             // Filter($j= ... Filter($i = Content(fixup), ...))  -> Filter($j= ... Filter($i = Loop($j = DesendentOrSelf(Root(fixup)), Content($j), ...)))
VisitLoop(QilLoop n)98             protected override QilNode VisitLoop(QilLoop n)
99             {
100                 if (n.Variable.Binding.NodeType == QilNodeType.Root || n.Variable.Binding.NodeType == QilNodeType.Deref)
101                 {
102                     // This is absolute path already. We shouldn't touch it
103                     return n;
104                 }
105                 if (n.Variable.Binding.NodeType == QilNodeType.Content)
106                 {
107                     // This is "begin" of reletive path. Let's rewrite it as absolute:
108                     QilUnary content = (QilUnary)n.Variable.Binding;
109                     Debug.Assert(content.Child == _fixup, "Unexpected content node");
110                     QilIterator it = f.For(f.DescendantOrSelf(f.Root(_fixup)));
111                     content.Child = it;
112                     n.Variable.Binding = f.Loop(it, content);
113                     return n;
114                 }
115                 n.Variable.Binding = Visit(n.Variable.Binding);
116                 return n;
117             }
118 
VisitFilter(QilLoop n)119             protected override QilNode VisitFilter(QilLoop n)
120             {
121                 return VisitLoop(n);
122             }
123         }
124     }
125 }
126