f728fec60e7d18da218cdd0b81c16559602bf69b
[mono.git] / mcs / class / referencesource / System.Data.SqlXml / System / Xml / Xsl / XsltOld / TemplateAction.cs
1 //------------------------------------------------------------------------------
2 // <copyright file="TemplateAction.cs" company="Microsoft">
3 //     Copyright (c) Microsoft Corporation.  All rights reserved.
4 // </copyright>                                                                
5 // <owner current="true" primary="true">Microsoft</owner>
6 //------------------------------------------------------------------------------
7
8 namespace System.Xml.Xsl.XsltOld {
9     using Res = System.Xml.Utils.Res;
10     using System;
11     using System.Diagnostics;
12     using System.Collections;
13     using System.Xml;
14     using System.Xml.XPath;
15     using MS.Internal.Xml.XPath;
16     using System.Globalization;
17
18     internal class TemplateAction : TemplateBaseAction {
19         private int               matchKey = Compiler.InvalidQueryKey;
20         private XmlQualifiedName  name;
21         private double            priority = double.NaN;
22         private XmlQualifiedName  mode;
23         private int               templateId;
24         private bool              replaceNSAliasesDone;
25
26         internal int MatchKey {
27             get { return this.matchKey; }
28         }
29
30         internal XmlQualifiedName Name {
31             get { return this.name; }
32         }
33
34         internal double Priority {
35             get { return this.priority; }
36         }
37
38         internal XmlQualifiedName Mode {
39             get { return this.mode; }
40         }
41
42         internal int TemplateId {
43             get { return this.templateId; }
44             set {
45                 Debug.Assert(this.templateId == 0);
46                 this.templateId = value;
47             }
48         }
49
50         internal override void Compile(Compiler compiler) {
51             CompileAttributes(compiler);
52             if (this.matchKey == Compiler.InvalidQueryKey) {
53                 if (this.name == null) {
54                     throw XsltException.Create(Res.Xslt_TemplateNoAttrib);
55                 }
56                 if (this.mode != null ) {
57                     throw XsltException.Create(Res.Xslt_InvalidModeAttribute);
58                 }
59             }
60             compiler.BeginTemplate(this);
61
62             if (compiler.Recurse()) {
63                 CompileParameters(compiler);
64                 CompileTemplate(compiler);
65
66                 compiler.ToParent();
67             }
68
69             compiler.EndTemplate();
70             AnalyzePriority(compiler);
71         }
72
73         internal virtual void CompileSingle(Compiler compiler) {
74             this.matchKey = compiler.AddQuery("/", /*allowVars:*/false, /*allowKey:*/true, /*pattern*/true);
75             this.priority   = Compiler.RootPriority;
76
77             CompileOnceTemplate(compiler);
78         }
79
80         internal override bool CompileAttribute(Compiler compiler) {
81             string name   = compiler.Input.LocalName;
82             string value  = compiler.Input.Value;
83
84             if (Ref.Equal(name, compiler.Atoms.Match)) {
85                 Debug.Assert(this.matchKey == Compiler.InvalidQueryKey);
86                 this.matchKey = compiler.AddQuery(value, /*allowVars:*/false, /*allowKey:*/true, /*pattern*/true);
87             }
88             else if (Ref.Equal(name, compiler.Atoms.Name)) {
89                 Debug.Assert(this.name == null);
90                 this.name = compiler.CreateXPathQName(value);
91             }
92             else if (Ref.Equal(name, compiler.Atoms.Priority)) {
93                 Debug.Assert(Double.IsNaN(this.priority));
94                 this.priority = XmlConvert.ToXPathDouble(value);
95                 if (double.IsNaN(this.priority) && ! compiler.ForwardCompatibility) {
96                     throw XsltException.Create(Res.Xslt_InvalidAttrValue, "priority", value);
97                 }
98             }
99             else if (Ref.Equal(name, compiler.Atoms.Mode)) {
100                 Debug.Assert(this.mode == null);
101                 if (compiler.AllowBuiltInMode && value == "*") {
102                     this.mode = Compiler.BuiltInMode;
103                 }
104                 else {
105                     this.mode = compiler.CreateXPathQName(value);
106                 }
107             }
108             else {
109                 return false;
110             }
111
112             return true;
113         }
114
115         private void AnalyzePriority(Compiler compiler) {
116             NavigatorInput input = compiler.Input;
117
118             if (!Double.IsNaN(this.priority) || this.matchKey == Compiler.InvalidQueryKey) {
119                 return;
120             }
121             // Split Unions:
122             TheQuery theQuery = (TheQuery)compiler.QueryStore[this.MatchKey];
123             CompiledXpathExpr expr = (CompiledXpathExpr)theQuery.CompiledQuery;
124             Query query = expr.QueryTree;
125             UnionExpr union;
126             while ((union = query as UnionExpr) != null) {
127                 Debug.Assert(!(union.qy2 is UnionExpr), "only qy1 can be union");
128                 TemplateAction copy = this.CloneWithoutName();
129                 compiler.QueryStore.Add(new TheQuery(
130                     new CompiledXpathExpr(union.qy2, expr.Expression, false),
131                     theQuery._ScopeManager
132                 ));
133                 copy.matchKey = compiler.QueryStore.Count - 1;
134                 copy.priority = union.qy2.XsltDefaultPriority;
135                 compiler.AddTemplate(copy);
136
137                 query = union.qy1;
138             }
139             if (expr.QueryTree != query) {
140                 // query was splitted and we need create new TheQuery for this template
141                 compiler.QueryStore[this.MatchKey] = new TheQuery(
142                     new CompiledXpathExpr(query, expr.Expression, false),
143                     theQuery._ScopeManager
144                 );
145             }
146             this.priority = query.XsltDefaultPriority;
147         }
148         
149         protected void CompileParameters(Compiler compiler) {
150             NavigatorInput input = compiler.Input;
151             do {
152                 switch(input.NodeType) {
153                 case XPathNodeType.Element:
154                     if (Ref.Equal(input.NamespaceURI, input.Atoms.UriXsl) &&
155                         Ref.Equal(input.LocalName, input.Atoms.Param)) {
156                         compiler.PushNamespaceScope();
157                         AddAction(compiler.CreateVariableAction(VariableType.LocalParameter));
158                         compiler.PopScope();
159                         continue;
160                     }
161                     else {
162                         return;
163                     }
164                 case XPathNodeType.Text:
165                     return;
166                 case XPathNodeType.SignificantWhitespace:
167                     this.AddEvent(compiler.CreateTextEvent());
168                     continue;
169                 default :
170                     continue;
171                 }
172             }
173             while (input.Advance());
174         }
175
176         //
177         // Priority calculation plus template splitting
178         //
179
180         private TemplateAction CloneWithoutName() {
181             TemplateAction clone    = new TemplateAction(); {
182                 clone.containedActions = this.containedActions;
183                 clone.mode             = this.mode;
184                 clone.variableCount    = this.variableCount;
185                 clone.replaceNSAliasesDone = true; // We shouldn't replace NS in clones.
186             }
187             return clone;
188         }
189
190         internal override void ReplaceNamespaceAlias(Compiler compiler) {
191             // if template has both name and match it will be twice caled by stylesheet to replace NS aliases.
192             if (! replaceNSAliasesDone) {
193                 base.ReplaceNamespaceAlias(compiler);
194                 replaceNSAliasesDone = true;
195             }
196         }
197         //
198         // Execution
199         //
200
201         internal override void Execute(Processor processor, ActionFrame frame) {
202             Debug.Assert(processor != null && frame != null);
203
204             switch (frame.State) {
205             case Initialized:
206                 if (this.variableCount > 0) {
207                     frame.AllocateVariables(this.variableCount);
208                 }
209                 if (this.containedActions != null &&  this.containedActions.Count > 0) {
210                     processor.PushActionFrame(frame);
211                     frame.State = ProcessingChildren;
212                 }
213                 else {
214                     frame.Finished();
215                 }
216                 break;                              // Allow children to run
217             case ProcessingChildren:
218                 frame.Finished();
219                 break;
220             default:
221                 Debug.Fail("Invalid Container action execution state");
222                     break;
223             }
224         }
225     }
226 }