diff options
author | Rob Mensching <rob@firegiant.com> | 2018-10-18 11:21:03 -0700 |
---|---|---|
committer | Rob Mensching <rob@robmensching.com> | 2018-10-24 21:17:34 -0700 |
commit | d98126dc766b9b063d2d26ced62553d4a5a218b5 (patch) | |
tree | 33a6fce8eaf14a2d548bb0b3559cae2dbe15146d /src/WixToolset.Core | |
parent | 6a6653c3fbda30e194d80f917bae5d7366c26644 (diff) | |
download | wix-d98126dc766b9b063d2d26ced62553d4a5a218b5.tar.gz wix-d98126dc766b9b063d2d26ced62553d4a5a218b5.tar.bz2 wix-d98126dc766b9b063d2d26ced62553d4a5a218b5.zip |
Minor project cleanup
Diffstat (limited to 'src/WixToolset.Core')
-rw-r--r-- | src/WixToolset.Core/Converter.cs | 619 |
1 files changed, 0 insertions, 619 deletions
diff --git a/src/WixToolset.Core/Converter.cs b/src/WixToolset.Core/Converter.cs deleted file mode 100644 index 1f83c88f..00000000 --- a/src/WixToolset.Core/Converter.cs +++ /dev/null | |||
@@ -1,619 +0,0 @@ | |||
1 | // Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. | ||
2 | |||
3 | namespace WixToolset.Core | ||
4 | { | ||
5 | using System; | ||
6 | using System.Collections.Generic; | ||
7 | using System.Globalization; | ||
8 | using System.IO; | ||
9 | using System.Linq; | ||
10 | using System.Xml; | ||
11 | using System.Xml.Linq; | ||
12 | using WixToolset.Data; | ||
13 | using WixToolset.Extensibility.Services; | ||
14 | |||
15 | /// <summary> | ||
16 | /// WiX source code converter. | ||
17 | /// </summary> | ||
18 | internal class Converter | ||
19 | { | ||
20 | private const string XDocumentNewLine = "\n"; // XDocument normlizes "\r\n" to just "\n". | ||
21 | private static readonly XNamespace WixNamespace = "http://wixtoolset.org/schemas/v4/wxs"; | ||
22 | |||
23 | private static readonly XName FileElementName = WixNamespace + "File"; | ||
24 | private static readonly XName ExePackageElementName = WixNamespace + "ExePackage"; | ||
25 | private static readonly XName MsiPackageElementName = WixNamespace + "MsiPackage"; | ||
26 | private static readonly XName MspPackageElementName = WixNamespace + "MspPackage"; | ||
27 | private static readonly XName MsuPackageElementName = WixNamespace + "MsuPackage"; | ||
28 | private static readonly XName PayloadElementName = WixNamespace + "Payload"; | ||
29 | private static readonly XName WixElementWithoutNamespaceName = XNamespace.None + "Wix"; | ||
30 | |||
31 | private static readonly Dictionary<string, XNamespace> OldToNewNamespaceMapping = new Dictionary<string, XNamespace>() | ||
32 | { | ||
33 | { "http://schemas.microsoft.com/wix/BalExtension", "http://wixtoolset.org/schemas/v4/wxs/bal" }, | ||
34 | { "http://schemas.microsoft.com/wix/ComPlusExtension", "http://wixtoolset.org/schemas/v4/wxs/complus" }, | ||
35 | { "http://schemas.microsoft.com/wix/DependencyExtension", "http://wixtoolset.org/schemas/v4/wxs/dependency" }, | ||
36 | { "http://schemas.microsoft.com/wix/DifxAppExtension", "http://wixtoolset.org/schemas/v4/wxs/difxapp" }, | ||
37 | { "http://schemas.microsoft.com/wix/FirewallExtension", "http://wixtoolset.org/schemas/v4/wxs/firewall" }, | ||
38 | { "http://schemas.microsoft.com/wix/GamingExtension", "http://wixtoolset.org/schemas/v4/wxs/gaming" }, | ||
39 | { "http://schemas.microsoft.com/wix/IIsExtension", "http://wixtoolset.org/schemas/v4/wxs/iis" }, | ||
40 | { "http://schemas.microsoft.com/wix/MsmqExtension", "http://wixtoolset.org/schemas/v4/wxs/msmq" }, | ||
41 | { "http://schemas.microsoft.com/wix/NetFxExtension", "http://wixtoolset.org/schemas/v4/wxs/netfx" }, | ||
42 | { "http://schemas.microsoft.com/wix/PSExtension", "http://wixtoolset.org/schemas/v4/wxs/powershell" }, | ||
43 | { "http://schemas.microsoft.com/wix/SqlExtension", "http://wixtoolset.org/schemas/v4/wxs/sql" }, | ||
44 | { "http://schemas.microsoft.com/wix/TagExtension", "http://wixtoolset.org/schemas/v4/wxs/tag" }, | ||
45 | { "http://schemas.microsoft.com/wix/UtilExtension", "http://wixtoolset.org/schemas/v4/wxs/util" }, | ||
46 | { "http://schemas.microsoft.com/wix/VSExtension", "http://wixtoolset.org/schemas/v4/wxs/vs" }, | ||
47 | { "http://wixtoolset.org/schemas/thmutil/2010", "http://wixtoolset.org/schemas/v4/thmutil" }, | ||
48 | { "http://schemas.microsoft.com/wix/2009/Lux", "http://wixtoolset.org/schemas/v4/lux" }, | ||
49 | { "http://schemas.microsoft.com/wix/2006/wi", "http://wixtoolset.org/schemas/v4/wxs" }, | ||
50 | { "http://schemas.microsoft.com/wix/2006/localization", "http://wixtoolset.org/schemas/v4/wxl" }, | ||
51 | { "http://schemas.microsoft.com/wix/2006/libraries", "http://wixtoolset.org/schemas/v4/wixlib" }, | ||
52 | { "http://schemas.microsoft.com/wix/2006/objects", "http://wixtoolset.org/schemas/v4/wixobj" }, | ||
53 | { "http://schemas.microsoft.com/wix/2006/outputs", "http://wixtoolset.org/schemas/v4/wixout" }, | ||
54 | { "http://schemas.microsoft.com/wix/2007/pdbs", "http://wixtoolset.org/schemas/v4/wixpdb" }, | ||
55 | { "http://schemas.microsoft.com/wix/2003/04/actions", "http://wixtoolset.org/schemas/v4/wi/actions" }, | ||
56 | { "http://schemas.microsoft.com/wix/2006/tables", "http://wixtoolset.org/schemas/v4/wi/tables" }, | ||
57 | { "http://schemas.microsoft.com/wix/2006/WixUnit", "http://wixtoolset.org/schemas/v4/wixunit" }, | ||
58 | }; | ||
59 | |||
60 | private Dictionary<XName, Action<XElement>> ConvertElementMapping; | ||
61 | |||
62 | /// <summary> | ||
63 | /// Instantiate a new Converter class. | ||
64 | /// </summary> | ||
65 | /// <param name="indentationAmount">Indentation value to use when validating leading whitespace.</param> | ||
66 | /// <param name="errorsAsWarnings">Test errors to display as warnings.</param> | ||
67 | /// <param name="ignoreErrors">Test errors to ignore.</param> | ||
68 | internal Converter(IMessaging messaging, int indentationAmount, IEnumerable<string> errorsAsWarnings = null, IEnumerable<string> ignoreErrors = null) | ||
69 | { | ||
70 | this.ConvertElementMapping = new Dictionary<XName, Action<XElement>>() | ||
71 | { | ||
72 | { FileElementName, this.ConvertFileElement }, | ||
73 | { ExePackageElementName, this.ConvertSuppressSignatureValidation }, | ||
74 | { MsiPackageElementName, this.ConvertSuppressSignatureValidation }, | ||
75 | { MspPackageElementName, this.ConvertSuppressSignatureValidation }, | ||
76 | { MsuPackageElementName, this.ConvertSuppressSignatureValidation }, | ||
77 | { PayloadElementName, this.ConvertSuppressSignatureValidation }, | ||
78 | { WixElementWithoutNamespaceName, this.ConvertWixElementWithoutNamespace }, | ||
79 | }; | ||
80 | |||
81 | this.Messaging = messaging; | ||
82 | |||
83 | this.IndentationAmount = indentationAmount; | ||
84 | |||
85 | this.ErrorsAsWarnings = new HashSet<ConverterTestType>(this.YieldConverterTypes(errorsAsWarnings)); | ||
86 | |||
87 | this.IgnoreErrors = new HashSet<ConverterTestType>(this.YieldConverterTypes(ignoreErrors)); | ||
88 | } | ||
89 | |||
90 | private int Errors { get; set; } | ||
91 | |||
92 | private HashSet<ConverterTestType> ErrorsAsWarnings { get; set; } | ||
93 | |||
94 | private HashSet<ConverterTestType> IgnoreErrors { get; set; } | ||
95 | |||
96 | private IMessaging Messaging { get; } | ||
97 | |||
98 | private int IndentationAmount { get; set; } | ||
99 | |||
100 | private string SourceFile { get; set; } | ||
101 | |||
102 | /// <summary> | ||
103 | /// Convert a file. | ||
104 | /// </summary> | ||
105 | /// <param name="sourceFile">The file to convert.</param> | ||
106 | /// <param name="saveConvertedFile">Option to save the converted errors that are found.</param> | ||
107 | /// <returns>The number of errors found.</returns> | ||
108 | public int ConvertFile(string sourceFile, bool saveConvertedFile) | ||
109 | { | ||
110 | XDocument document; | ||
111 | |||
112 | // Set the instance info. | ||
113 | this.Errors = 0; | ||
114 | this.SourceFile = sourceFile; | ||
115 | |||
116 | try | ||
117 | { | ||
118 | document = XDocument.Load(this.SourceFile, LoadOptions.PreserveWhitespace | LoadOptions.SetLineInfo); | ||
119 | } | ||
120 | catch (XmlException e) | ||
121 | { | ||
122 | this.OnError(ConverterTestType.XmlException, (XObject)null, "The xml is invalid. Detail: '{0}'", e.Message); | ||
123 | |||
124 | return this.Errors; | ||
125 | } | ||
126 | |||
127 | this.ConvertDocument(document); | ||
128 | |||
129 | // Fix errors if requested and necessary. | ||
130 | if (saveConvertedFile && 0 < this.Errors) | ||
131 | { | ||
132 | try | ||
133 | { | ||
134 | using (StreamWriter writer = File.CreateText(this.SourceFile)) | ||
135 | { | ||
136 | document.Save(writer, SaveOptions.DisableFormatting | SaveOptions.OmitDuplicateNamespaces); | ||
137 | } | ||
138 | } | ||
139 | catch (UnauthorizedAccessException) | ||
140 | { | ||
141 | this.OnError(ConverterTestType.UnauthorizedAccessException, (XObject)null, "Could not write to file."); | ||
142 | } | ||
143 | } | ||
144 | |||
145 | return this.Errors; | ||
146 | } | ||
147 | |||
148 | /// <summary> | ||
149 | /// Convert a document. | ||
150 | /// </summary> | ||
151 | /// <param name="document">The document to convert.</param> | ||
152 | /// <returns>The number of errors found.</returns> | ||
153 | public int ConvertDocument(XDocument document) | ||
154 | { | ||
155 | XDeclaration declaration = document.Declaration; | ||
156 | |||
157 | // Convert the declaration. | ||
158 | if (null != declaration) | ||
159 | { | ||
160 | if (!String.Equals("utf-8", declaration.Encoding, StringComparison.OrdinalIgnoreCase)) | ||
161 | { | ||
162 | if (this.OnError(ConverterTestType.DeclarationEncodingWrong, document.Root, "The XML declaration encoding is not properly set to 'utf-8'.")) | ||
163 | { | ||
164 | declaration.Encoding = "utf-8"; | ||
165 | } | ||
166 | } | ||
167 | } | ||
168 | else // missing declaration | ||
169 | { | ||
170 | if (this.OnError(ConverterTestType.DeclarationMissing, (XNode)null, "This file is missing an XML declaration on the first line.")) | ||
171 | { | ||
172 | document.Declaration = new XDeclaration("1.0", "utf-8", null); | ||
173 | document.Root.AddBeforeSelf(new XText(XDocumentNewLine)); | ||
174 | } | ||
175 | } | ||
176 | |||
177 | // Start converting the nodes at the top. | ||
178 | this.ConvertNode(document.Root, 0); | ||
179 | |||
180 | return this.Errors; | ||
181 | } | ||
182 | |||
183 | /// <summary> | ||
184 | /// Convert a single xml node. | ||
185 | /// </summary> | ||
186 | /// <param name="node">The node to convert.</param> | ||
187 | /// <param name="level">The depth level of the node.</param> | ||
188 | /// <returns>The converted node.</returns> | ||
189 | private void ConvertNode(XNode node, int level) | ||
190 | { | ||
191 | // Convert this node's whitespace. | ||
192 | if ((XmlNodeType.Comment == node.NodeType && 0 > ((XComment)node).Value.IndexOf(XDocumentNewLine, StringComparison.Ordinal)) || | ||
193 | XmlNodeType.CDATA == node.NodeType || XmlNodeType.Element == node.NodeType || XmlNodeType.ProcessingInstruction == node.NodeType) | ||
194 | { | ||
195 | this.ConvertWhitespace(node, level); | ||
196 | } | ||
197 | |||
198 | // Convert this node if it is an element. | ||
199 | XElement element = node as XElement; | ||
200 | |||
201 | if (null != element) | ||
202 | { | ||
203 | this.ConvertElement(element); | ||
204 | |||
205 | // Convert all children of this element. | ||
206 | IEnumerable<XNode> children = element.Nodes().ToList(); | ||
207 | |||
208 | foreach (XNode child in children) | ||
209 | { | ||
210 | this.ConvertNode(child, level + 1); | ||
211 | } | ||
212 | } | ||
213 | } | ||
214 | |||
215 | private void ConvertElement(XElement element) | ||
216 | { | ||
217 | // Gather any deprecated namespaces, then update this element tree based on those deprecations. | ||
218 | Dictionary<XNamespace, XNamespace> deprecatedToUpdatedNamespaces = new Dictionary<XNamespace, XNamespace>(); | ||
219 | |||
220 | foreach (XAttribute declaration in element.Attributes().Where(a => a.IsNamespaceDeclaration)) | ||
221 | { | ||
222 | XNamespace ns; | ||
223 | |||
224 | if (Converter.OldToNewNamespaceMapping.TryGetValue(declaration.Value, out ns)) | ||
225 | { | ||
226 | if (this.OnError(ConverterTestType.XmlnsValueWrong, declaration, "The namespace '{0}' is out of date. It must be '{1}'.", declaration.Value, ns.NamespaceName)) | ||
227 | { | ||
228 | deprecatedToUpdatedNamespaces.Add(declaration.Value, ns); | ||
229 | } | ||
230 | } | ||
231 | } | ||
232 | |||
233 | if (deprecatedToUpdatedNamespaces.Any()) | ||
234 | { | ||
235 | Converter.UpdateElementsWithDeprecatedNamespaces(element.DescendantsAndSelf(), deprecatedToUpdatedNamespaces); | ||
236 | } | ||
237 | |||
238 | // Convert the node in much greater detail. | ||
239 | Action<XElement> convert; | ||
240 | |||
241 | if (this.ConvertElementMapping.TryGetValue(element.Name, out convert)) | ||
242 | { | ||
243 | convert(element); | ||
244 | } | ||
245 | } | ||
246 | |||
247 | private void ConvertFileElement(XElement element) | ||
248 | { | ||
249 | if (null == element.Attribute("Id")) | ||
250 | { | ||
251 | XAttribute attribute = element.Attribute("Name"); | ||
252 | |||
253 | if (null == attribute) | ||
254 | { | ||
255 | attribute = element.Attribute("Source"); | ||
256 | } | ||
257 | |||
258 | if (null != attribute) | ||
259 | { | ||
260 | string name = Path.GetFileName(attribute.Value); | ||
261 | |||
262 | if (this.OnError(ConverterTestType.AssignAnonymousFileId, element, "The file id is being updated to '{0}' to ensure it remains the same as the default", name)) | ||
263 | { | ||
264 | IEnumerable<XAttribute> attributes = element.Attributes().ToList(); | ||
265 | element.RemoveAttributes(); | ||
266 | element.Add(new XAttribute("Id", Common.GetIdentifierFromName(name))); | ||
267 | element.Add(attributes); | ||
268 | } | ||
269 | } | ||
270 | } | ||
271 | } | ||
272 | |||
273 | private void ConvertSuppressSignatureValidation(XElement element) | ||
274 | { | ||
275 | XAttribute suppressSignatureValidation = element.Attribute("SuppressSignatureValidation"); | ||
276 | |||
277 | if (null != suppressSignatureValidation) | ||
278 | { | ||
279 | if (this.OnError(ConverterTestType.SuppressSignatureValidationDeprecated, element, "The chain package element contains deprecated '{0}' attribute. Use the 'EnableSignatureValidation' instead.", suppressSignatureValidation)) | ||
280 | { | ||
281 | if ("no" == suppressSignatureValidation.Value) | ||
282 | { | ||
283 | element.Add(new XAttribute("EnableSignatureValidation", "yes")); | ||
284 | } | ||
285 | } | ||
286 | |||
287 | suppressSignatureValidation.Remove(); | ||
288 | } | ||
289 | } | ||
290 | |||
291 | /// <summary> | ||
292 | /// Converts a Wix element. | ||
293 | /// </summary> | ||
294 | /// <param name="element">The Wix element to convert.</param> | ||
295 | /// <returns>The converted element.</returns> | ||
296 | private void ConvertWixElementWithoutNamespace(XElement element) | ||
297 | { | ||
298 | if (this.OnError(ConverterTestType.XmlnsMissing, element, "The xmlns attribute is missing. It must be present with a value of '{0}'.", WixNamespace.NamespaceName)) | ||
299 | { | ||
300 | element.Name = WixNamespace.GetName(element.Name.LocalName); | ||
301 | |||
302 | element.Add(new XAttribute("xmlns", WixNamespace.NamespaceName)); // set the default namespace. | ||
303 | |||
304 | foreach (XElement elementWithoutNamespace in element.Elements().Where(e => XNamespace.None == e.Name.Namespace)) | ||
305 | { | ||
306 | elementWithoutNamespace.Name = WixNamespace.GetName(elementWithoutNamespace.Name.LocalName); | ||
307 | } | ||
308 | } | ||
309 | } | ||
310 | |||
311 | /// <summary> | ||
312 | /// Convert the whitespace adjacent to a node. | ||
313 | /// </summary> | ||
314 | /// <param name="node">The node to convert.</param> | ||
315 | /// <param name="level">The depth level of the node.</param> | ||
316 | private void ConvertWhitespace(XNode node, int level) | ||
317 | { | ||
318 | // Fix the whitespace before this node. | ||
319 | XText whitespace = node.PreviousNode as XText; | ||
320 | |||
321 | if (null != whitespace) | ||
322 | { | ||
323 | if (XmlNodeType.CDATA == node.NodeType) | ||
324 | { | ||
325 | if (this.OnError(ConverterTestType.WhitespacePrecedingCDATAWrong, node, "There should be no whitespace preceding a CDATA node.")) | ||
326 | { | ||
327 | whitespace.Remove(); | ||
328 | } | ||
329 | } | ||
330 | else | ||
331 | { | ||
332 | if (!Converter.IsLegalWhitespace(this.IndentationAmount, level, whitespace.Value)) | ||
333 | { | ||
334 | if (this.OnError(ConverterTestType.WhitespacePrecedingNodeWrong, node, "The whitespace preceding this node is incorrect.")) | ||
335 | { | ||
336 | Converter.FixWhitespace(this.IndentationAmount, level, whitespace); | ||
337 | } | ||
338 | } | ||
339 | } | ||
340 | } | ||
341 | |||
342 | // Fix the whitespace after CDATA nodes. | ||
343 | XCData cdata = node as XCData; | ||
344 | |||
345 | if (null != cdata) | ||
346 | { | ||
347 | whitespace = cdata.NextNode as XText; | ||
348 | |||
349 | if (null != whitespace) | ||
350 | { | ||
351 | if (this.OnError(ConverterTestType.WhitespaceFollowingCDATAWrong, node, "There should be no whitespace following a CDATA node.")) | ||
352 | { | ||
353 | whitespace.Remove(); | ||
354 | } | ||
355 | } | ||
356 | } | ||
357 | else | ||
358 | { | ||
359 | // Fix the whitespace inside and after this node (except for Error which may contain just whitespace). | ||
360 | XElement element = node as XElement; | ||
361 | |||
362 | if (null != element && "Error" != element.Name.LocalName) | ||
363 | { | ||
364 | if (!element.HasElements && !element.IsEmpty && String.IsNullOrEmpty(element.Value.Trim())) | ||
365 | { | ||
366 | if (this.OnError(ConverterTestType.NotEmptyElement, element, "This should be an empty element since it contains nothing but whitespace.")) | ||
367 | { | ||
368 | element.RemoveNodes(); | ||
369 | } | ||
370 | } | ||
371 | |||
372 | whitespace = node.NextNode as XText; | ||
373 | |||
374 | if (null != whitespace) | ||
375 | { | ||
376 | if (!Converter.IsLegalWhitespace(this.IndentationAmount, level - 1, whitespace.Value)) | ||
377 | { | ||
378 | if (this.OnError(ConverterTestType.WhitespacePrecedingEndElementWrong, whitespace, "The whitespace preceding this end element is incorrect.")) | ||
379 | { | ||
380 | Converter.FixWhitespace(this.IndentationAmount, level - 1, whitespace); | ||
381 | } | ||
382 | } | ||
383 | } | ||
384 | } | ||
385 | } | ||
386 | } | ||
387 | |||
388 | private IEnumerable<ConverterTestType> YieldConverterTypes(IEnumerable<string> types) | ||
389 | { | ||
390 | if (null != types) | ||
391 | { | ||
392 | foreach (string type in types) | ||
393 | { | ||
394 | ConverterTestType itt; | ||
395 | |||
396 | if (Enum.TryParse<ConverterTestType>(type, true, out itt)) | ||
397 | { | ||
398 | yield return itt; | ||
399 | } | ||
400 | else // not a known ConverterTestType | ||
401 | { | ||
402 | this.OnError(ConverterTestType.ConverterTestTypeUnknown, (XObject)null, "Unknown error type: '{0}'.", type); | ||
403 | } | ||
404 | } | ||
405 | } | ||
406 | } | ||
407 | |||
408 | private static void UpdateElementsWithDeprecatedNamespaces(IEnumerable<XElement> elements, Dictionary<XNamespace, XNamespace> deprecatedToUpdatedNamespaces) | ||
409 | { | ||
410 | foreach (XElement element in elements) | ||
411 | { | ||
412 | XNamespace ns; | ||
413 | |||
414 | if (deprecatedToUpdatedNamespaces.TryGetValue(element.Name.Namespace, out ns)) | ||
415 | { | ||
416 | element.Name = ns.GetName(element.Name.LocalName); | ||
417 | } | ||
418 | |||
419 | // Remove all the attributes and add them back to with their namespace updated (as necessary). | ||
420 | IEnumerable<XAttribute> attributes = element.Attributes().ToList(); | ||
421 | element.RemoveAttributes(); | ||
422 | |||
423 | foreach (XAttribute attribute in attributes) | ||
424 | { | ||
425 | XAttribute convertedAttribute = attribute; | ||
426 | |||
427 | if (attribute.IsNamespaceDeclaration) | ||
428 | { | ||
429 | if (deprecatedToUpdatedNamespaces.TryGetValue(attribute.Value, out ns)) | ||
430 | { | ||
431 | convertedAttribute = ("xmlns" == attribute.Name.LocalName) ? new XAttribute(attribute.Name.LocalName, ns.NamespaceName) : new XAttribute(XNamespace.Xmlns + attribute.Name.LocalName, ns.NamespaceName); | ||
432 | } | ||
433 | } | ||
434 | else if (deprecatedToUpdatedNamespaces.TryGetValue(attribute.Name.Namespace, out ns)) | ||
435 | { | ||
436 | convertedAttribute = new XAttribute(ns.GetName(attribute.Name.LocalName), attribute.Value); | ||
437 | } | ||
438 | |||
439 | element.Add(convertedAttribute); | ||
440 | } | ||
441 | } | ||
442 | } | ||
443 | |||
444 | /// <summary> | ||
445 | /// Determine if the whitespace preceding a node is appropriate for its depth level. | ||
446 | /// </summary> | ||
447 | /// <param name="indentationAmount">Indentation value to use when validating leading whitespace.</param> | ||
448 | /// <param name="level">The depth level that should match this whitespace.</param> | ||
449 | /// <param name="whitespace">The whitespace to validate.</param> | ||
450 | /// <returns>true if the whitespace is legal; false otherwise.</returns> | ||
451 | private static bool IsLegalWhitespace(int indentationAmount, int level, string whitespace) | ||
452 | { | ||
453 | // strip off leading newlines; there can be an arbitrary number of these | ||
454 | while (whitespace.StartsWith(XDocumentNewLine, StringComparison.Ordinal)) | ||
455 | { | ||
456 | whitespace = whitespace.Substring(XDocumentNewLine.Length); | ||
457 | } | ||
458 | |||
459 | // check the length | ||
460 | if (whitespace.Length != level * indentationAmount) | ||
461 | { | ||
462 | return false; | ||
463 | } | ||
464 | |||
465 | // check the spaces | ||
466 | foreach (char character in whitespace) | ||
467 | { | ||
468 | if (' ' != character) | ||
469 | { | ||
470 | return false; | ||
471 | } | ||
472 | } | ||
473 | |||
474 | return true; | ||
475 | } | ||
476 | |||
477 | /// <summary> | ||
478 | /// Fix the whitespace in a Whitespace node. | ||
479 | /// </summary> | ||
480 | /// <param name="indentationAmount">Indentation value to use when validating leading whitespace.</param> | ||
481 | /// <param name="level">The depth level of the desired whitespace.</param> | ||
482 | /// <param name="whitespace">The whitespace node to fix.</param> | ||
483 | private static void FixWhitespace(int indentationAmount, int level, XText whitespace) | ||
484 | { | ||
485 | int newLineCount = 0; | ||
486 | |||
487 | for (int i = 0; i + 1 < whitespace.Value.Length; ++i) | ||
488 | { | ||
489 | if (XDocumentNewLine == whitespace.Value.Substring(i, 2)) | ||
490 | { | ||
491 | ++i; // skip an extra character | ||
492 | ++newLineCount; | ||
493 | } | ||
494 | } | ||
495 | |||
496 | if (0 == newLineCount) | ||
497 | { | ||
498 | newLineCount = 1; | ||
499 | } | ||
500 | |||
501 | // reset the whitespace value | ||
502 | whitespace.Value = String.Empty; | ||
503 | |||
504 | // add the correct number of newlines | ||
505 | for (int i = 0; i < newLineCount; ++i) | ||
506 | { | ||
507 | whitespace.Value = String.Concat(whitespace.Value, XDocumentNewLine); | ||
508 | } | ||
509 | |||
510 | // add the correct number of spaces based on configured indentation amount | ||
511 | whitespace.Value = String.Concat(whitespace.Value, new string(' ', level * indentationAmount)); | ||
512 | } | ||
513 | |||
514 | /// <summary> | ||
515 | /// Output an error message to the console. | ||
516 | /// </summary> | ||
517 | /// <param name="converterTestType">The type of converter test.</param> | ||
518 | /// <param name="node">The node that caused the error.</param> | ||
519 | /// <param name="message">Detailed error message.</param> | ||
520 | /// <param name="args">Additional formatted string arguments.</param> | ||
521 | /// <returns>Returns true indicating that action should be taken on this error, and false if it should be ignored.</returns> | ||
522 | private bool OnError(ConverterTestType converterTestType, XObject node, string message, params object[] args) | ||
523 | { | ||
524 | if (this.IgnoreErrors.Contains(converterTestType)) // ignore the error | ||
525 | { | ||
526 | return false; | ||
527 | } | ||
528 | |||
529 | // Increase the error count. | ||
530 | this.Errors++; | ||
531 | |||
532 | SourceLineNumber sourceLine = (null == node) ? new SourceLineNumber(this.SourceFile ?? "wixcop.exe") : new SourceLineNumber(this.SourceFile, ((IXmlLineInfo)node).LineNumber); | ||
533 | bool warning = this.ErrorsAsWarnings.Contains(converterTestType); | ||
534 | string display = String.Format(CultureInfo.CurrentCulture, message, args); | ||
535 | |||
536 | var msg = new Message(sourceLine, warning ? MessageLevel.Warning : MessageLevel.Error, (int)converterTestType, "{0} ({1})", display, converterTestType.ToString()); | ||
537 | |||
538 | this.Messaging.Write(msg); | ||
539 | |||
540 | return true; | ||
541 | } | ||
542 | |||
543 | /// <summary> | ||
544 | /// Converter test types. These are used to condition error messages down to warnings. | ||
545 | /// </summary> | ||
546 | private enum ConverterTestType | ||
547 | { | ||
548 | /// <summary> | ||
549 | /// Internal-only: displayed when a string cannot be converted to an ConverterTestType. | ||
550 | /// </summary> | ||
551 | ConverterTestTypeUnknown, | ||
552 | |||
553 | /// <summary> | ||
554 | /// Displayed when an XML loading exception has occurred. | ||
555 | /// </summary> | ||
556 | XmlException, | ||
557 | |||
558 | /// <summary> | ||
559 | /// Displayed when a file cannot be accessed; typically when trying to save back a fixed file. | ||
560 | /// </summary> | ||
561 | UnauthorizedAccessException, | ||
562 | |||
563 | /// <summary> | ||
564 | /// Displayed when the encoding attribute in the XML declaration is not 'UTF-8'. | ||
565 | /// </summary> | ||
566 | DeclarationEncodingWrong, | ||
567 | |||
568 | /// <summary> | ||
569 | /// Displayed when the XML declaration is missing from the source file. | ||
570 | /// </summary> | ||
571 | DeclarationMissing, | ||
572 | |||
573 | /// <summary> | ||
574 | /// Displayed when the whitespace preceding a CDATA node is wrong. | ||
575 | /// </summary> | ||
576 | WhitespacePrecedingCDATAWrong, | ||
577 | |||
578 | /// <summary> | ||
579 | /// Displayed when the whitespace preceding a node is wrong. | ||
580 | /// </summary> | ||
581 | WhitespacePrecedingNodeWrong, | ||
582 | |||
583 | /// <summary> | ||
584 | /// Displayed when an element is not empty as it should be. | ||
585 | /// </summary> | ||
586 | NotEmptyElement, | ||
587 | |||
588 | /// <summary> | ||
589 | /// Displayed when the whitespace following a CDATA node is wrong. | ||
590 | /// </summary> | ||
591 | WhitespaceFollowingCDATAWrong, | ||
592 | |||
593 | /// <summary> | ||
594 | /// Displayed when the whitespace preceding an end element is wrong. | ||
595 | /// </summary> | ||
596 | WhitespacePrecedingEndElementWrong, | ||
597 | |||
598 | /// <summary> | ||
599 | /// Displayed when the xmlns attribute is missing from the document element. | ||
600 | /// </summary> | ||
601 | XmlnsMissing, | ||
602 | |||
603 | /// <summary> | ||
604 | /// Displayed when the xmlns attribute on the document element is wrong. | ||
605 | /// </summary> | ||
606 | XmlnsValueWrong, | ||
607 | |||
608 | /// <summary> | ||
609 | /// Assign an identifier to a File element when on Id attribute is specified. | ||
610 | /// </summary> | ||
611 | AssignAnonymousFileId, | ||
612 | |||
613 | /// <summary> | ||
614 | /// SuppressSignatureValidation attribute is deprecated and replaced with EnableSignatureValidation. | ||
615 | /// </summary> | ||
616 | SuppressSignatureValidationDeprecated, | ||
617 | } | ||
618 | } | ||
619 | } | ||