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