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