Updates referencesource to .NET 4.7
[mono-project.git] / mcs / class / referencesource / System.Data.SqlXml / System / Xml / Xsl / Xslt / Compiler.cs
blob68fb7af2a6010227fb3b9d2980fb23dac1c4cc49
1 //------------------------------------------------------------------------------
2 // <copyright file="Compiler.cs" company="Microsoft">
3 // Copyright (c) Microsoft Corporation. All rights reserved.
4 // </copyright>
5 // <owner current="true" primary="true">Microsoft</owner>
6 //------------------------------------------------------------------------------
8 using System.CodeDom.Compiler;
9 using System.Collections.Generic;
10 using System.Collections.ObjectModel;
11 using System.Diagnostics;
12 using System.Xml.XPath;
13 using System.Xml.Xsl.Qil;
14 using System.Xml.Xsl.XPath;
15 using System.Runtime.Versioning;
17 namespace System.Xml.Xsl.Xslt {
18 using Res = System.Xml.Utils.Res;
19 using TypeFactory = XmlQueryTypeFactory;
20 #if DEBUG
21 using XmlILTrace = System.Xml.Xsl.IlGen.XmlILTrace;
22 #endif
24 internal enum XslVersion {
25 Version10 = 0,
26 ForwardsCompatible = 1,
27 Current = Version10,
30 // RootLevel is underdeveloped consept currently. I plane to move here more collections from Compiler.
31 // Compiler is like a stylesheet in some sense. it has a lot of properties of stylesheet. Instead of
32 // inhereting from Styleseet (or StylesheetLevel) I desided to agregate special subclass of StylesheetLevel.
33 // One more reason to for this design is to normolize apply-templates and apply-imports to one concept:
34 // apply-templates is apply-imports(compiler.Root).
35 // For now I don't create new files for these new classes to simplify integrations WebData <-> WebData_xsl
36 internal class RootLevel : StylesheetLevel {
37 public RootLevel(Stylesheet principal) {
38 base.Imports = new Stylesheet[] { principal };
42 internal class Compiler {
43 public XsltSettings Settings;
44 public bool IsDebug;
45 public string ScriptAssemblyPath;
46 public int Version; // 0 - Auto; 1 - XSLT 1.0; 2 - XSLT 2.0
47 public string inputTypeAnnotations; // null - "unspecified"; "preserve"; "strip"
49 public CompilerResults CompilerResults; // Results of the compilation
50 public int CurrentPrecedence = 0; // Decreases by 1 with each import
51 public XslNode StartApplyTemplates;
52 public RootLevel Root;
53 public Scripts Scripts;
54 public Output Output = new Output();
55 public List<VarPar> ExternalPars = new List<VarPar>();
56 public List<VarPar> GlobalVars = new List<VarPar>();
57 public List<WhitespaceRule> WhitespaceRules = new List<WhitespaceRule>();
58 public DecimalFormats DecimalFormats = new DecimalFormats();
59 public Keys Keys = new Keys();
60 public List<ProtoTemplate> AllTemplates = new List<ProtoTemplate>();
62 public Dictionary<QilName, VarPar> AllGlobalVarPars = new Dictionary<QilName, VarPar>();
63 public Dictionary<QilName, Template> NamedTemplates = new Dictionary<QilName, Template>();
64 public Dictionary<QilName, AttributeSet> AttributeSets = new Dictionary<QilName, AttributeSet>();
65 public Dictionary<string, NsAlias> NsAliases = new Dictionary<string, NsAlias>();
67 private Dictionary<string, int> moduleOrder = new Dictionary<string,int>();
69 public Compiler(XsltSettings settings, bool debug, string scriptAssemblyPath) {
70 Debug.Assert(CompilerResults == null, "Compiler cannot be reused");
72 // Keep all intermediate files if tracing is enabled
73 TempFileCollection tempFiles = settings.TempFiles ?? new TempFileCollection();
75 #if DEBUG
76 if (XmlILTrace.IsEnabled) {
77 tempFiles.KeepFiles = true;
79 #endif
81 Settings = settings;
82 IsDebug = settings.IncludeDebugInformation | debug;
83 ScriptAssemblyPath = scriptAssemblyPath;
85 CompilerResults = new CompilerResults(tempFiles);
86 Scripts = new Scripts(this);
89 [ResourceConsumption(ResourceScope.Machine)]
90 [ResourceExposure(ResourceScope.Machine)]
91 public CompilerResults Compile(object stylesheet, XmlResolver xmlResolver, out QilExpression qil) {
92 Debug.Assert(stylesheet != null);
93 Debug.Assert(Root == null, "Compiler cannot be reused");
95 new XsltLoader().Load(this, stylesheet, xmlResolver);
96 qil = QilGenerator.CompileStylesheet(this);
97 SortErrors();
98 return CompilerResults;
101 public Stylesheet CreateStylesheet() {
102 Stylesheet sheet = new Stylesheet(this, CurrentPrecedence);
103 if (CurrentPrecedence-- == 0) {
104 Root = new RootLevel(sheet);
106 return sheet;
109 public void AddModule(string baseUri) {
110 if (!moduleOrder.ContainsKey(baseUri)) {
111 moduleOrder[baseUri] = moduleOrder.Count;
115 public void ApplyNsAliases(ref string prefix, ref string nsUri) {
116 NsAlias alias;
117 if (NsAliases.TryGetValue(nsUri, out alias)) {
118 nsUri = alias.ResultNsUri;
119 prefix = alias.ResultPrefix;
123 // Returns true in case of redefinition
124 public bool SetNsAlias(string ssheetNsUri, string resultNsUri, string resultPrefix, int importPrecedence) {
125 NsAlias oldNsAlias;
126 if (NsAliases.TryGetValue(ssheetNsUri, out oldNsAlias)) {
127 // Namespace alias for this stylesheet namespace URI has already been defined
128 Debug.Assert(importPrecedence <= oldNsAlias.ImportPrecedence, "Stylesheets must be processed in the order of decreasing import precedence");
129 if (importPrecedence < oldNsAlias.ImportPrecedence || resultNsUri == oldNsAlias.ResultNsUri) {
130 // Either the identical definition or lower precedence - ignore it
131 return false;
133 // Recover by choosing the declaration that occurs later in the stylesheet
135 NsAliases[ssheetNsUri] = new NsAlias(resultNsUri, resultPrefix, importPrecedence);
136 return oldNsAlias != null;
139 private void MergeWhitespaceRules(Stylesheet sheet) {
140 for (int idx = 0; idx <= 2; idx++) {
141 sheet.WhitespaceRules[idx].Reverse();
142 this.WhitespaceRules.AddRange(sheet.WhitespaceRules[idx]);
144 sheet.WhitespaceRules = null;
147 private void MergeAttributeSets(Stylesheet sheet) {
148 foreach (QilName attSetName in sheet.AttributeSets.Keys) {
149 AttributeSet attSet;
150 if (!this.AttributeSets.TryGetValue(attSetName, out attSet)) {
151 this.AttributeSets[attSetName] = sheet.AttributeSets[attSetName];
152 } else {
153 // Lower import precedence - insert before all previous definitions
154 attSet.MergeContent(sheet.AttributeSets[attSetName]);
157 sheet.AttributeSets = null;
160 private void MergeGlobalVarPars(Stylesheet sheet) {
161 foreach (VarPar var in sheet.GlobalVarPars) {
162 Debug.Assert(var.NodeType == XslNodeType.Variable || var.NodeType == XslNodeType.Param);
163 if (!AllGlobalVarPars.ContainsKey(var.Name)) {
164 if (var.NodeType == XslNodeType.Variable) {
165 GlobalVars.Add(var);
166 } else {
167 ExternalPars.Add(var);
169 AllGlobalVarPars[var.Name] = var;
172 sheet.GlobalVarPars = null;
175 public void MergeWithStylesheet(Stylesheet sheet) {
176 MergeWhitespaceRules(sheet);
177 MergeAttributeSets(sheet);
178 MergeGlobalVarPars(sheet);
181 public static string ConstructQName(string prefix, string localName) {
182 if (prefix.Length == 0) {
183 return localName;
184 } else {
185 return prefix + ':' + localName;
189 public bool ParseQName(string qname, out string prefix, out string localName, IErrorHelper errorHelper) {
190 Debug.Assert(qname != null);
191 try {
192 ValidateNames.ParseQNameThrow(qname, out prefix, out localName);
193 return true;
195 catch (XmlException e) {
196 errorHelper.ReportError(/*[XT_042]*/e.Message, null);
197 prefix = PhantomNCName;
198 localName = PhantomNCName;
199 return false;
203 public bool ParseNameTest(string nameTest, out string prefix, out string localName, IErrorHelper errorHelper) {
204 Debug.Assert(nameTest != null);
205 try {
206 ValidateNames.ParseNameTestThrow(nameTest, out prefix, out localName);
207 return true;
209 catch (XmlException e) {
210 errorHelper.ReportError(/*[XT_043]*/e.Message, null);
211 prefix = PhantomNCName;
212 localName = PhantomNCName;
213 return false;
217 public void ValidatePiName(string name, IErrorHelper errorHelper) {
218 Debug.Assert(name != null);
219 try {
220 ValidateNames.ValidateNameThrow(
221 /*prefix:*/string.Empty, /*localName:*/name, /*ns:*/string.Empty,
222 XPathNodeType.ProcessingInstruction, ValidateNames.Flags.AllExceptPrefixMapping
225 catch (XmlException e) {
226 errorHelper.ReportError(/*[XT_044]*/e.Message, null);
230 public readonly string PhantomNCName = "error";
231 private int phantomNsCounter = 0;
233 public string CreatePhantomNamespace() {
234 // Prepend invalid XmlChar to ensure this name would not clash with any namespace name in the stylesheet
235 return "\0namespace" + phantomNsCounter++;
238 public bool IsPhantomNamespace(string namespaceName) {
239 return namespaceName.Length > 0 && namespaceName[0] == '\0';
242 public bool IsPhantomName(QilName qname) {
243 string nsUri = qname.NamespaceUri;
244 return nsUri.Length > 0 && nsUri[0] == '\0';
247 // -------------------------------- Error Handling --------------------------------
249 private int ErrorCount {
250 get {
251 return CompilerResults.Errors.Count;
253 set {
254 Debug.Assert(value <= ErrorCount);
255 for (int idx = ErrorCount - 1; idx >= value; idx--) {
256 CompilerResults.Errors.RemoveAt(idx);
261 private int savedErrorCount = -1;
263 public void EnterForwardsCompatible() {
264 Debug.Assert(savedErrorCount == -1, "Nested EnterForwardsCompatible calls");
265 savedErrorCount = ErrorCount;
268 // Returns true if no errors were suppressed
269 public bool ExitForwardsCompatible(bool fwdCompat) {
270 Debug.Assert(savedErrorCount != -1, "ExitForwardsCompatible without EnterForwardsCompatible");
271 if (fwdCompat && ErrorCount > savedErrorCount) {
272 ErrorCount = savedErrorCount;
273 Debug.Assert((savedErrorCount = -1) < 0);
274 return false;
276 Debug.Assert((savedErrorCount = -1) < 0);
277 return true;
280 public CompilerError CreateError(ISourceLineInfo lineInfo, string res, params string[] args) {
281 AddModule(lineInfo.Uri);
282 return new CompilerError(
283 lineInfo.Uri, lineInfo.Start.Line, lineInfo.Start.Pos, /*errorNumber:*/string.Empty,
284 /*errorText:*/XslTransformException.CreateMessage(res, args)
288 public void ReportError(ISourceLineInfo lineInfo, string res, params string[] args) {
289 CompilerError error = CreateError(lineInfo, res, args);
290 CompilerResults.Errors.Add(error);
293 public void ReportWarning(ISourceLineInfo lineInfo, string res, params string[] args) {
294 int warningLevel = 1;
295 if (0 <= Settings.WarningLevel && Settings.WarningLevel < warningLevel) {
296 // Ignore warning
297 return;
299 CompilerError error = CreateError(lineInfo, res, args);
300 if (Settings.TreatWarningsAsErrors) {
301 error.ErrorText = XslTransformException.CreateMessage(Res.Xslt_WarningAsError, error.ErrorText);
302 CompilerResults.Errors.Add(error);
303 } else {
304 error.IsWarning = true;
305 CompilerResults.Errors.Add(error);
309 private void SortErrors() {
310 CompilerErrorCollection errorColl = this.CompilerResults.Errors;
311 if (errorColl.Count > 1) {
312 CompilerError[] errors = new CompilerError[errorColl.Count];
313 errorColl.CopyTo(errors, 0);
314 Array.Sort<CompilerError>(errors, new CompilerErrorComparer(this.moduleOrder));
315 errorColl.Clear();
316 errorColl.AddRange(errors);
320 private class CompilerErrorComparer : IComparer<CompilerError> {
321 Dictionary<string, int> moduleOrder;
323 public CompilerErrorComparer(Dictionary<string, int> moduleOrder) {
324 this.moduleOrder = moduleOrder;
327 public int Compare(CompilerError x, CompilerError y) {
328 if ((object)x == (object)y)
329 return 0;
331 if (x == null)
332 return -1;
334 if (y == null)
335 return 1;
337 int result = moduleOrder[x.FileName].CompareTo(moduleOrder[y.FileName]);
338 if (result != 0)
339 return result;
341 result = x.Line.CompareTo(y.Line);
342 if (result != 0)
343 return result;
345 result = x.Column.CompareTo(y.Column);
346 if (result != 0)
347 return result;
349 result = x.IsWarning.CompareTo(y.IsWarning);
350 if (result != 0)
351 return result;
353 result = string.CompareOrdinal(x.ErrorNumber, y.ErrorNumber);
354 if (result != 0)
355 return result;
357 return string.CompareOrdinal(x.ErrorText, y.ErrorText);
362 internal class Output {
363 public XmlWriterSettings Settings;
364 public string Version;
365 public string Encoding;
366 public XmlQualifiedName Method;
368 // All the xsl:output elements occurring in a stylesheet are merged into a single effective xsl:output element.
369 // We store the import precedence of each attribute value to catch redefinitions with the same import precedence.
370 public const int NeverDeclaredPrec = int.MinValue;
371 public int MethodPrec = NeverDeclaredPrec;
372 public int VersionPrec = NeverDeclaredPrec;
373 public int EncodingPrec = NeverDeclaredPrec;
374 public int OmitXmlDeclarationPrec = NeverDeclaredPrec;
375 public int StandalonePrec = NeverDeclaredPrec;
376 public int DocTypePublicPrec = NeverDeclaredPrec;
377 public int DocTypeSystemPrec = NeverDeclaredPrec;
378 public int IndentPrec = NeverDeclaredPrec;
379 public int MediaTypePrec = NeverDeclaredPrec;
381 public Output() {
382 Settings = new XmlWriterSettings();
383 Settings.OutputMethod = XmlOutputMethod.AutoDetect;
384 Settings.AutoXmlDeclaration = true;
385 Settings.ConformanceLevel = ConformanceLevel.Auto;
386 Settings.MergeCDataSections = true;
390 internal class DecimalFormats : KeyedCollection<XmlQualifiedName, DecimalFormatDecl> {
391 protected override XmlQualifiedName GetKeyForItem(DecimalFormatDecl format) {
392 return format.Name;
396 internal class DecimalFormatDecl {
397 public readonly XmlQualifiedName Name;
398 public readonly string InfinitySymbol;
399 public readonly string NanSymbol;
400 public readonly char[] Characters;
402 public static DecimalFormatDecl Default = new DecimalFormatDecl(new XmlQualifiedName(), "Infinity", "NaN", ".,%\u20300#;-");
404 public DecimalFormatDecl(XmlQualifiedName name, string infinitySymbol, string nanSymbol, string characters) {
405 Debug.Assert(characters.Length == 8);
406 this.Name = name;
407 this.InfinitySymbol = infinitySymbol;
408 this.NanSymbol = nanSymbol;
409 this.Characters = characters.ToCharArray();
413 internal class NsAlias {
414 public readonly string ResultNsUri;
415 public readonly string ResultPrefix;
416 public readonly int ImportPrecedence;
418 public NsAlias(string resultNsUri, string resultPrefix, int importPrecedence) {
419 this.ResultNsUri = resultNsUri;
420 this.ResultPrefix = resultPrefix;
421 this.ImportPrecedence = importPrecedence;