aboutsummaryrefslogtreecommitdiff
path: root/src/WixToolset.Converters/WixConverter.cs
diff options
context:
space:
mode:
Diffstat (limited to 'src/WixToolset.Converters/WixConverter.cs')
-rw-r--r--src/WixToolset.Converters/WixConverter.cs179
1 files changed, 109 insertions, 70 deletions
diff --git a/src/WixToolset.Converters/WixConverter.cs b/src/WixToolset.Converters/WixConverter.cs
index a05c7f58..bfeed03e 100644
--- a/src/WixToolset.Converters/WixConverter.cs
+++ b/src/WixToolset.Converters/WixConverter.cs
@@ -19,6 +19,12 @@ namespace WixToolset.Converters
19 /// </summary> 19 /// </summary>
20 public class WixConverter 20 public class WixConverter
21 { 21 {
22 private enum ConvertOperation
23 {
24 Convert,
25 Format,
26 }
27
22 private static readonly Regex AddPrefix = new Regex(@"^[^a-zA-Z_]", RegexOptions.Compiled); 28 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 29 private static readonly Regex IllegalIdentifierCharacters = new Regex(@"[^A-Za-z0-9_\.]|\.{2,}", RegexOptions.Compiled); // non 'words' and assorted valid characters
24 30
@@ -107,18 +113,6 @@ namespace WixToolset.Converters
107 { "http://schemas.microsoft.com/wix/2006/WixUnit", "http://wixtoolset.org/schemas/v4/wixunit" }, 113 { "http://schemas.microsoft.com/wix/2006/WixUnit", "http://wixtoolset.org/schemas/v4/wixunit" },
108 }; 114 };
109 115
110 private readonly static SortedSet<string> Wix3Namespaces = new SortedSet<string>
111 {
112 "http://schemas.microsoft.com/wix/2006/wi",
113 "http://schemas.microsoft.com/wix/2006/localization",
114 };
115
116 private readonly static SortedSet<string> Wix4Namespaces = new SortedSet<string>
117 {
118 "http://wixtoolset.org/schemas/v4/wxs",
119 "http://wixtoolset.org/schemas/v4/wxl",
120 };
121
122 private readonly Dictionary<XName, Action<XElement>> ConvertElementMapping; 116 private readonly Dictionary<XName, Action<XElement>> ConvertElementMapping;
123 117
124 /// <summary> 118 /// <summary>
@@ -193,6 +187,8 @@ namespace WixToolset.Converters
193 187
194 private int IndentationAmount { get; set; } 188 private int IndentationAmount { get; set; }
195 189
190 private ConvertOperation Operation { get; set; }
191
196 private string SourceFile { get; set; } 192 private string SourceFile { get; set; }
197 193
198 private int SourceVersion { get; set; } 194 private int SourceVersion { get; set; }
@@ -205,22 +201,11 @@ namespace WixToolset.Converters
205 /// <returns>The number of errors found.</returns> 201 /// <returns>The number of errors found.</returns>
206 public int ConvertFile(string sourceFile, bool saveConvertedFile) 202 public int ConvertFile(string sourceFile, bool saveConvertedFile)
207 { 203 {
208 XDocument document; 204 var document = this.OpenSourceFile(sourceFile);
209
210 // Set the instance info.
211 this.Errors = 0;
212 this.SourceFile = sourceFile;
213 this.SourceVersion = 0;
214 205
215 try 206 if (document is null)
216 { 207 {
217 document = XDocument.Load(this.SourceFile, LoadOptions.PreserveWhitespace | LoadOptions.SetLineInfo); 208 return 1;
218 }
219 catch (XmlException e)
220 {
221 this.OnError(ConverterTestType.XmlException, null, "The xml is invalid. Detail: '{0}'", e.Message);
222
223 return this.Errors;
224 } 209 }
225 210
226 this.ConvertDocument(document); 211 this.ConvertDocument(document);
@@ -228,17 +213,7 @@ namespace WixToolset.Converters
228 // Fix errors if requested and necessary. 213 // Fix errors if requested and necessary.
229 if (saveConvertedFile && 0 < this.Errors) 214 if (saveConvertedFile && 0 < this.Errors)
230 { 215 {
231 try 216 this.SaveDocument(document);
232 {
233 using (var writer = XmlWriter.Create(this.SourceFile, new XmlWriterSettings { OmitXmlDeclaration = true }))
234 {
235 document.Save(writer);
236 }
237 }
238 catch (UnauthorizedAccessException)
239 {
240 this.OnError(ConverterTestType.UnauthorizedAccessException, null, "Could not write to file.");
241 }
242 } 217 }
243 218
244 return this.Errors; 219 return this.Errors;
@@ -251,13 +226,68 @@ namespace WixToolset.Converters
251 /// <returns>The number of errors found.</returns> 226 /// <returns>The number of errors found.</returns>
252 public int ConvertDocument(XDocument document) 227 public int ConvertDocument(XDocument document)
253 { 228 {
229 // Reset the instance info.
254 this.Errors = 0; 230 this.Errors = 0;
255 this.SourceVersion = 0; 231 this.SourceVersion = 0;
232 this.Operation = ConvertOperation.Convert;
233
234 // Remove the declaration.
235 if (null != document.Declaration)
236 {
237 if (this.OnError(ConverterTestType.DeclarationPresent, null, "This file contains an XML declaration on the first line."))
238 {
239 document.Declaration = null;
240 }
241 }
242
243 TrimLeadingText(document);
244
245 // Start converting the nodes at the top.
246 this.ConvertNodes(document.Nodes(), 0);
247
248 return this.Errors;
249 }
256 250
257 var declaration = document.Declaration; 251 /// <summary>
252 /// Format a file.
253 /// </summary>
254 /// <param name="sourceFile">The file to format.</param>
255 /// <param name="saveConvertedFile">Option to save the format errors that are found.</param>
256 /// <returns>The number of errors found.</returns>
257 public int FormatFile(string sourceFile, bool saveConvertedFile)
258 {
259 var document = this.OpenSourceFile(sourceFile);
260
261 if (document is null)
262 {
263 return 1;
264 }
265
266 this.FormatDocument(document);
267
268 // Fix errors if requested and necessary.
269 if (saveConvertedFile && 0 < this.Errors)
270 {
271 this.SaveDocument(document);
272 }
273
274 return this.Errors;
275 }
276
277 /// <summary>
278 /// Format a document.
279 /// </summary>
280 /// <param name="document">The document to format.</param>
281 /// <returns>The number of errors found.</returns>
282 public int FormatDocument(XDocument document)
283 {
284 // Reset the instance info.
285 this.Errors = 0;
286 this.SourceVersion = 0;
287 this.Operation = ConvertOperation.Format;
258 288
259 // Remove the declaration. 289 // Remove the declaration.
260 if (null != declaration) 290 if (null != document.Declaration)
261 { 291 {
262 if (this.OnError(ConverterTestType.DeclarationPresent, null, "This file contains an XML declaration on the first line.")) 292 if (this.OnError(ConverterTestType.DeclarationPresent, null, "This file contains an XML declaration on the first line."))
263 { 293 {
@@ -273,6 +303,37 @@ namespace WixToolset.Converters
273 return this.Errors; 303 return this.Errors;
274 } 304 }
275 305
306 private XDocument OpenSourceFile(string sourceFile)
307 {
308 this.SourceFile = sourceFile;
309
310 try
311 {
312 return XDocument.Load(this.SourceFile, LoadOptions.PreserveWhitespace | LoadOptions.SetLineInfo);
313 }
314 catch (XmlException e)
315 {
316 this.OnError(ConverterTestType.XmlException, null, "The xml is invalid. Detail: '{0}'", e.Message);
317 }
318
319 return null;
320 }
321
322 private void SaveDocument(XDocument document)
323 {
324 try
325 {
326 using (var writer = XmlWriter.Create(this.SourceFile, new XmlWriterSettings { OmitXmlDeclaration = true }))
327 {
328 document.Save(writer);
329 }
330 }
331 catch (UnauthorizedAccessException)
332 {
333 this.OnError(ConverterTestType.UnauthorizedAccessException, null, "Could not write to file.");
334 }
335 }
336
276 private void ConvertNodes(IEnumerable<XNode> nodes, int level) 337 private void ConvertNodes(IEnumerable<XNode> nodes, int level)
277 { 338 {
278 // Note we operate on a copy of the node list since we may 339 // Note we operate on a copy of the node list since we may
@@ -901,7 +962,10 @@ namespace WixToolset.Converters
901 /// <returns>Returns true indicating that action should be taken on this error, and false if it should be ignored.</returns> 962 /// <returns>Returns true indicating that action should be taken on this error, and false if it should be ignored.</returns>
902 private bool OnError(ConverterTestType converterTestType, XObject node, string message, params object[] args) 963 private bool OnError(ConverterTestType converterTestType, XObject node, string message, params object[] args)
903 { 964 {
904 if (this.IgnoreErrors.Contains(converterTestType)) // ignore the error 965 // Ignore the error if explicitly ignored or outside the range of the current operation.
966 if (this.IgnoreErrors.Contains(converterTestType) ||
967 (this.Operation == ConvertOperation.Convert && converterTestType < ConverterTestType.DeclarationPresent) ||
968 (this.Operation == ConvertOperation.Format && converterTestType > ConverterTestType.DeclarationPresent))
905 { 969 {
906 return false; 970 return false;
907 } 971 }
@@ -909,7 +973,7 @@ namespace WixToolset.Converters
909 // Increase the error count. 973 // Increase the error count.
910 this.Errors++; 974 this.Errors++;
911 975
912 var sourceLine = (null == node) ? new SourceLineNumber(this.SourceFile ?? "wixcop.exe") : new SourceLineNumber(this.SourceFile, ((IXmlLineInfo)node).LineNumber); 976 var sourceLine = (null == node) ? new SourceLineNumber(this.SourceFile ?? "wix.exe") : new SourceLineNumber(this.SourceFile, ((IXmlLineInfo)node).LineNumber);
913 var warning = this.ErrorsAsWarnings.Contains(converterTestType); 977 var warning = this.ErrorsAsWarnings.Contains(converterTestType);
914 var display = String.Format(CultureInfo.CurrentCulture, message, args); 978 var display = String.Format(CultureInfo.CurrentCulture, message, args);
915 979
@@ -1050,39 +1114,19 @@ namespace WixToolset.Converters
1050 UnauthorizedAccessException, 1114 UnauthorizedAccessException,
1051 1115
1052 /// <summary> 1116 /// <summary>
1053 /// Displayed when the encoding attribute in the XML declaration is not 'UTF-8'.
1054 /// </summary>
1055 DeclarationEncodingWrong,
1056
1057 /// <summary>
1058 /// Displayed when the XML declaration is missing from the source file.
1059 /// </summary>
1060 DeclarationMissing,
1061
1062 /// <summary>
1063 /// Displayed when the whitespace preceding a CDATA node is wrong.
1064 /// </summary>
1065 WhitespacePrecedingCDATAWrong,
1066
1067 /// <summary>
1068 /// Displayed when the whitespace preceding a node is wrong. 1117 /// Displayed when the whitespace preceding a node is wrong.
1069 /// </summary> 1118 /// </summary>
1070 WhitespacePrecedingNodeWrong, 1119 WhitespacePrecedingNodeWrong,
1071 1120
1072 /// <summary> 1121 /// <summary>
1073 /// Displayed when an element is not empty as it should be. 1122 /// Displayed when the whitespace preceding an end element is wrong.
1074 /// </summary>
1075 NotEmptyElement,
1076
1077 /// <summary>
1078 /// Displayed when the whitespace following a CDATA node is wrong.
1079 /// </summary> 1123 /// </summary>
1080 WhitespaceFollowingCDATAWrong, 1124 WhitespacePrecedingEndElementWrong,
1081 1125
1082 /// <summary> 1126 /// <summary>
1083 /// Displayed when the whitespace preceding an end element is wrong. 1127 /// Displayed when the XML declaration is present in the source file.
1084 /// </summary> 1128 /// </summary>
1085 WhitespacePrecedingEndElementWrong, 1129 DeclarationPresent,
1086 1130
1087 /// <summary> 1131 /// <summary>
1088 /// Displayed when the xmlns attribute is missing from the document element. 1132 /// Displayed when the xmlns attribute is missing from the document element.
@@ -1155,11 +1199,6 @@ namespace WixToolset.Converters
1155 AutoGuidUnnecessary, 1199 AutoGuidUnnecessary,
1156 1200
1157 /// <summary> 1201 /// <summary>
1158 /// Displayed when the XML declaration is present in the source file.
1159 /// </summary>
1160 DeclarationPresent,
1161
1162 /// <summary>
1163 /// The Feature Absent attribute renamed to AllowAbsent. 1202 /// The Feature Absent attribute renamed to AllowAbsent.
1164 /// </summary> 1203 /// </summary>
1165 FeatureAbsentAttributeReplaced, 1204 FeatureAbsentAttributeReplaced,