diff options
| author | Rob Mensching <rob@firegiant.com> | 2018-10-04 13:54:59 -0700 |
|---|---|---|
| committer | Bob Arnson <bob@firegiant.com> | 2018-10-04 18:20:52 -0400 |
| commit | 784208bd46ce05025d8ccaef8542328350038d90 (patch) | |
| tree | 595db4df20b8681fe613c5bd176c0c5c866bc1f4 /src | |
| parent | eb4e0c543148f70a0c442d829848a3e3e1d33a91 (diff) | |
| download | wix-784208bd46ce05025d8ccaef8542328350038d90.tar.gz wix-784208bd46ce05025d8ccaef8542328350038d90.tar.bz2 wix-784208bd46ce05025d8ccaef8542328350038d90.zip | |
Simplify whitespace handling and fix some long standing bugs in it
Fixes wixtoolset/issues#5883
Diffstat (limited to 'src')
| -rw-r--r-- | src/test/WixToolsetTest.WixCop/ConverterFixture.cs | 74 | ||||
| -rw-r--r-- | src/wixcop/Converter.cs | 267 |
2 files changed, 171 insertions, 170 deletions
diff --git a/src/test/WixToolsetTest.WixCop/ConverterFixture.cs b/src/test/WixToolsetTest.WixCop/ConverterFixture.cs index 86931d5a..863a781a 100644 --- a/src/test/WixToolsetTest.WixCop/ConverterFixture.cs +++ b/src/test/WixToolsetTest.WixCop/ConverterFixture.cs | |||
| @@ -94,8 +94,82 @@ namespace WixToolsetTest.WixCop | |||
| 94 | 94 | ||
| 95 | var actual = UnformattedDocumentString(document); | 95 | var actual = UnformattedDocumentString(document); |
| 96 | 96 | ||
| 97 | Assert.Equal(expected, actual); | ||
| 97 | Assert.Equal(4, errors); | 98 | Assert.Equal(4, errors); |
| 99 | } | ||
| 100 | |||
| 101 | [Fact] | ||
| 102 | public void CanPreserveNewLines() | ||
| 103 | { | ||
| 104 | var parse = String.Join(Environment.NewLine, | ||
| 105 | "<?xml version='1.0' encoding='utf-8'?>", | ||
| 106 | "<Wix xmlns='http://wixtoolset.org/schemas/v4/wxs'>", | ||
| 107 | " <Fragment>", | ||
| 108 | "", | ||
| 109 | " <Property Id='Prop' Value='Val' />", | ||
| 110 | "", | ||
| 111 | " </Fragment>", | ||
| 112 | "</Wix>"); | ||
| 113 | |||
| 114 | var expected = String.Join(Environment.NewLine, | ||
| 115 | "<?xml version=\"1.0\" encoding=\"utf-16\"?>", | ||
| 116 | "<Wix xmlns=\"http://wixtoolset.org/schemas/v4/wxs\">", | ||
| 117 | " <Fragment>", | ||
| 118 | "", | ||
| 119 | " <Property Id=\"Prop\" Value=\"Val\" />", | ||
| 120 | "", | ||
| 121 | " </Fragment>", | ||
| 122 | "</Wix>"); | ||
| 123 | |||
| 124 | var document = XDocument.Parse(parse, LoadOptions.PreserveWhitespace | LoadOptions.SetLineInfo); | ||
| 125 | |||
| 126 | var messaging = new DummyMessaging(); | ||
| 127 | var converter = new Converter(messaging, 4, null, null); | ||
| 128 | |||
| 129 | var conversions = converter.ConvertDocument(document); | ||
| 130 | |||
| 131 | var actual = UnformattedDocumentString(document); | ||
| 132 | |||
| 133 | Assert.Equal(expected, actual); | ||
| 134 | Assert.Equal(3, conversions); | ||
| 135 | } | ||
| 136 | |||
| 137 | [Fact] | ||
| 138 | public void CanConvertWithNewLineAtEndOfFile() | ||
| 139 | { | ||
| 140 | var parse = String.Join(Environment.NewLine, | ||
| 141 | "<?xml version='1.0' encoding='utf-8'?>", | ||
| 142 | "<Wix xmlns='http://wixtoolset.org/schemas/v4/wxs'>", | ||
| 143 | " <Fragment>", | ||
| 144 | "", | ||
| 145 | " <Property Id='Prop' Value='Val' />", | ||
| 146 | "", | ||
| 147 | " </Fragment>", | ||
| 148 | "</Wix>", | ||
| 149 | ""); | ||
| 150 | |||
| 151 | var expected = String.Join(Environment.NewLine, | ||
| 152 | "<?xml version=\"1.0\" encoding=\"utf-16\"?>", | ||
| 153 | "<Wix xmlns=\"http://wixtoolset.org/schemas/v4/wxs\">", | ||
| 154 | " <Fragment>", | ||
| 155 | "", | ||
| 156 | " <Property Id=\"Prop\" Value=\"Val\" />", | ||
| 157 | "", | ||
| 158 | " </Fragment>", | ||
| 159 | "</Wix>", | ||
| 160 | ""); | ||
| 161 | |||
| 162 | var document = XDocument.Parse(parse, LoadOptions.PreserveWhitespace | LoadOptions.SetLineInfo); | ||
| 163 | |||
| 164 | var messaging = new DummyMessaging(); | ||
| 165 | var converter = new Converter(messaging, 4, null, null); | ||
| 166 | |||
| 167 | var conversions = converter.ConvertDocument(document); | ||
| 168 | |||
| 169 | var actual = UnformattedDocumentString(document); | ||
| 170 | |||
| 98 | Assert.Equal(expected, actual); | 171 | Assert.Equal(expected, actual); |
| 172 | Assert.Equal(3, conversions); | ||
| 99 | } | 173 | } |
| 100 | 174 | ||
| 101 | [Fact] | 175 | [Fact] |
diff --git a/src/wixcop/Converter.cs b/src/wixcop/Converter.cs index 7e8486ab..e125b39c 100644 --- a/src/wixcop/Converter.cs +++ b/src/wixcop/Converter.cs | |||
| @@ -7,6 +7,7 @@ namespace WixToolset.Tools.WixCop | |||
| 7 | using System.Globalization; | 7 | using System.Globalization; |
| 8 | using System.IO; | 8 | using System.IO; |
| 9 | using System.Linq; | 9 | using System.Linq; |
| 10 | using System.Text; | ||
| 10 | using System.Xml; | 11 | using System.Xml; |
| 11 | using System.Xml.Linq; | 12 | using System.Xml.Linq; |
| 12 | using WixToolset.Data; | 13 | using WixToolset.Data; |
| @@ -18,7 +19,7 @@ namespace WixToolset.Tools.WixCop | |||
| 18 | /// </summary> | 19 | /// </summary> |
| 19 | public class Converter | 20 | public class Converter |
| 20 | { | 21 | { |
| 21 | private const string XDocumentNewLine = "\n"; // XDocument normlizes "\r\n" to just "\n". | 22 | private const char XDocumentNewLine = '\n'; // XDocument normalizes "\r\n" to just "\n". |
| 22 | private static readonly XNamespace WixNamespace = "http://wixtoolset.org/schemas/v4/wxs"; | 23 | private static readonly XNamespace WixNamespace = "http://wixtoolset.org/schemas/v4/wxs"; |
| 23 | 24 | ||
| 24 | private static readonly XName FileElementName = WixNamespace + "File"; | 25 | private static readonly XName FileElementName = WixNamespace + "File"; |
| @@ -58,7 +59,7 @@ namespace WixToolset.Tools.WixCop | |||
| 58 | { "http://schemas.microsoft.com/wix/2006/WixUnit", "http://wixtoolset.org/schemas/v4/wixunit" }, | 59 | { "http://schemas.microsoft.com/wix/2006/WixUnit", "http://wixtoolset.org/schemas/v4/wixunit" }, |
| 59 | }; | 60 | }; |
| 60 | 61 | ||
| 61 | private Dictionary<XName, Action<XElement>> ConvertElementMapping; | 62 | private readonly Dictionary<XName, Action<XElement>> ConvertElementMapping; |
| 62 | 63 | ||
| 63 | /// <summary> | 64 | /// <summary> |
| 64 | /// Instantiate a new Converter class. | 65 | /// Instantiate a new Converter class. |
| @@ -79,14 +80,16 @@ namespace WixToolset.Tools.WixCop | |||
| 79 | { Converter.PayloadElementName, this.ConvertSuppressSignatureValidation }, | 80 | { Converter.PayloadElementName, this.ConvertSuppressSignatureValidation }, |
| 80 | { Converter.WixElementWithoutNamespaceName, this.ConvertWixElementWithoutNamespace }, | 81 | { Converter.WixElementWithoutNamespaceName, this.ConvertWixElementWithoutNamespace }, |
| 81 | };*/ | 82 | };*/ |
| 82 | this.ConvertElementMapping = new Dictionary<XName, Action<XElement>>(); | 83 | this.ConvertElementMapping = new Dictionary<XName, Action<XElement>> |
| 83 | this.ConvertElementMapping.Add(Converter.FileElementName, this.ConvertFileElement); | 84 | { |
| 84 | this.ConvertElementMapping.Add(Converter.ExePackageElementName, this.ConvertSuppressSignatureValidation); | 85 | { Converter.FileElementName, this.ConvertFileElement }, |
| 85 | this.ConvertElementMapping.Add(Converter.MsiPackageElementName, this.ConvertSuppressSignatureValidation); | 86 | { Converter.ExePackageElementName, this.ConvertSuppressSignatureValidation }, |
| 86 | this.ConvertElementMapping.Add(Converter.MspPackageElementName, this.ConvertSuppressSignatureValidation); | 87 | { Converter.MsiPackageElementName, this.ConvertSuppressSignatureValidation }, |
| 87 | this.ConvertElementMapping.Add(Converter.MsuPackageElementName, this.ConvertSuppressSignatureValidation); | 88 | { Converter.MspPackageElementName, this.ConvertSuppressSignatureValidation }, |
| 88 | this.ConvertElementMapping.Add(Converter.PayloadElementName, this.ConvertSuppressSignatureValidation); | 89 | { Converter.MsuPackageElementName, this.ConvertSuppressSignatureValidation }, |
| 89 | this.ConvertElementMapping.Add(Converter.WixElementWithoutNamespaceName, this.ConvertWixElementWithoutNamespace); | 90 | { Converter.PayloadElementName, this.ConvertSuppressSignatureValidation }, |
| 91 | { Converter.WixElementWithoutNamespaceName, this.ConvertWixElementWithoutNamespace } | ||
| 92 | }; | ||
| 90 | 93 | ||
| 91 | this.Messaging = messaging; | 94 | this.Messaging = messaging; |
| 92 | 95 | ||
| @@ -129,7 +132,7 @@ namespace WixToolset.Tools.WixCop | |||
| 129 | } | 132 | } |
| 130 | catch (XmlException e) | 133 | catch (XmlException e) |
| 131 | { | 134 | { |
| 132 | this.OnError(ConverterTestType.XmlException, (XObject)null, "The xml is invalid. Detail: '{0}'", e.Message); | 135 | this.OnError(ConverterTestType.XmlException, null, "The xml is invalid. Detail: '{0}'", e.Message); |
| 133 | 136 | ||
| 134 | return this.Errors; | 137 | return this.Errors; |
| 135 | } | 138 | } |
| @@ -148,7 +151,7 @@ namespace WixToolset.Tools.WixCop | |||
| 148 | } | 151 | } |
| 149 | catch (UnauthorizedAccessException) | 152 | catch (UnauthorizedAccessException) |
| 150 | { | 153 | { |
| 151 | this.OnError(ConverterTestType.UnauthorizedAccessException, (XObject)null, "Could not write to file."); | 154 | this.OnError(ConverterTestType.UnauthorizedAccessException, null, "Could not write to file."); |
| 152 | } | 155 | } |
| 153 | } | 156 | } |
| 154 | 157 | ||
| @@ -177,51 +180,90 @@ namespace WixToolset.Tools.WixCop | |||
| 177 | } | 180 | } |
| 178 | else // missing declaration | 181 | else // missing declaration |
| 179 | { | 182 | { |
| 180 | if (this.OnError(ConverterTestType.DeclarationMissing, (XNode)null, "This file is missing an XML declaration on the first line.")) | 183 | if (this.OnError(ConverterTestType.DeclarationMissing, null, "This file is missing an XML declaration on the first line.")) |
| 181 | { | 184 | { |
| 182 | document.Declaration = new XDeclaration("1.0", "utf-8", null); | 185 | document.Declaration = new XDeclaration("1.0", "utf-8", null); |
| 183 | document.Root.AddBeforeSelf(new XText(XDocumentNewLine)); | 186 | document.Root.AddBeforeSelf(new XText(XDocumentNewLine.ToString())); |
| 184 | } | 187 | } |
| 185 | } | 188 | } |
| 186 | 189 | ||
| 187 | // Start converting the nodes at the top. | 190 | // Start converting the nodes at the top. |
| 188 | this.ConvertNode(document.Root, 0); | 191 | this.ConvertNodes(document.Nodes(), 0); |
| 189 | 192 | ||
| 190 | return this.Errors; | 193 | return this.Errors; |
| 191 | } | 194 | } |
| 192 | 195 | ||
| 193 | /// <summary> | 196 | private void ConvertNodes(IEnumerable<XNode> nodes, int level) |
| 194 | /// Convert a single xml node. | ||
| 195 | /// </summary> | ||
| 196 | /// <param name="node">The node to convert.</param> | ||
| 197 | /// <param name="level">The depth level of the node.</param> | ||
| 198 | /// <returns>The converted node.</returns> | ||
| 199 | private void ConvertNode(XNode node, int level) | ||
| 200 | { | 197 | { |
| 201 | // Convert this node's whitespace. | 198 | // Note we operate on a copy of the node list since we may |
| 202 | if ((XmlNodeType.Comment == node.NodeType && 0 > ((XComment)node).Value.IndexOf(XDocumentNewLine, StringComparison.Ordinal)) || | 199 | // remove some whitespace nodes during this processing. |
| 203 | XmlNodeType.CDATA == node.NodeType || XmlNodeType.Element == node.NodeType || XmlNodeType.ProcessingInstruction == node.NodeType) | 200 | foreach (var node in nodes.ToList()) |
| 204 | { | 201 | { |
| 205 | this.ConvertWhitespace(node, level); | 202 | if (node is XText text) |
| 206 | } | 203 | { |
| 204 | if (!String.IsNullOrWhiteSpace(text.Value)) | ||
| 205 | { | ||
| 206 | text.Value = text.Value.Trim(); | ||
| 207 | } | ||
| 208 | else if (node.NextNode is XCData cdata) | ||
| 209 | { | ||
| 210 | this.EnsurePrecedingWhitespaceRemoved(text, node, ConverterTestType.WhitespacePrecedingNodeWrong); | ||
| 211 | } | ||
| 212 | else if (node.NextNode is XElement element) | ||
| 213 | { | ||
| 214 | this.EnsurePrecedingWhitespaceCorrect(text, node, level, ConverterTestType.WhitespacePrecedingNodeWrong); | ||
| 215 | } | ||
| 216 | else if (node.NextNode is null) // this is the space before the close element | ||
| 217 | { | ||
| 218 | if (node.PreviousNode is null || node.PreviousNode is XCData) | ||
| 219 | { | ||
| 220 | this.EnsurePrecedingWhitespaceRemoved(text, node.Parent, ConverterTestType.WhitespacePrecedingEndElementWrong); | ||
| 221 | } | ||
| 222 | else if (level == 0) // root element's close tag | ||
| 223 | { | ||
| 224 | this.EnsurePrecedingWhitespaceCorrect(text, node, 0, ConverterTestType.WhitespacePrecedingEndElementWrong); | ||
| 225 | } | ||
| 226 | else | ||
| 227 | { | ||
| 228 | this.EnsurePrecedingWhitespaceCorrect(text, node, level - 1, ConverterTestType.WhitespacePrecedingEndElementWrong); | ||
| 229 | } | ||
| 230 | } | ||
| 231 | } | ||
| 232 | else if (node is XElement element) | ||
| 233 | { | ||
| 234 | this.ConvertElement(element); | ||
| 207 | 235 | ||
| 208 | // Convert this node if it is an element. | 236 | this.ConvertNodes(element.Nodes(), level + 1); |
| 209 | var element = node as XElement; | 237 | } |
| 238 | } | ||
| 239 | } | ||
| 210 | 240 | ||
| 211 | if (null != element) | 241 | private void EnsurePrecedingWhitespaceCorrect(XText whitespace, XNode node, int level, ConverterTestType testType) |
| 242 | { | ||
| 243 | if (!Converter.LeadingWhitespaceValid(this.IndentationAmount, level, whitespace.Value)) | ||
| 212 | { | 244 | { |
| 213 | this.ConvertElement(element); | 245 | var message = testType == ConverterTestType.WhitespacePrecedingEndElementWrong ? "The whitespace preceding this end element is incorrect." : "The whitespace preceding this node is incorrect."; |
| 214 | |||
| 215 | // Convert all children of this element. | ||
| 216 | IEnumerable<XNode> children = element.Nodes().ToList(); | ||
| 217 | 246 | ||
| 218 | foreach (var child in children) | 247 | if (this.OnError(testType, node, message)) |
| 219 | { | 248 | { |
| 220 | this.ConvertNode(child, level + 1); | 249 | Converter.FixupWhitespace(this.IndentationAmount, level, whitespace); |
| 221 | } | 250 | } |
| 222 | } | 251 | } |
| 223 | } | 252 | } |
| 224 | 253 | ||
| 254 | private void EnsurePrecedingWhitespaceRemoved(XText whitespace, XNode node, ConverterTestType testType) | ||
| 255 | { | ||
| 256 | if (!String.IsNullOrEmpty(whitespace.Value)) | ||
| 257 | { | ||
| 258 | var message = testType == ConverterTestType.WhitespacePrecedingEndElementWrong ? "The whitespace preceding this end element is incorrect." : "The whitespace preceding this node is incorrect."; | ||
| 259 | |||
| 260 | if (this.OnError(testType, node, message)) | ||
| 261 | { | ||
| 262 | whitespace.Remove(); | ||
| 263 | } | ||
| 264 | } | ||
| 265 | } | ||
| 266 | |||
| 225 | private void ConvertElement(XElement element) | 267 | private void ConvertElement(XElement element) |
| 226 | { | 268 | { |
| 227 | // Gather any deprecated namespaces, then update this element tree based on those deprecations. | 269 | // Gather any deprecated namespaces, then update this element tree based on those deprecations. |
| @@ -229,9 +271,7 @@ namespace WixToolset.Tools.WixCop | |||
| 229 | 271 | ||
| 230 | foreach (var declaration in element.Attributes().Where(a => a.IsNamespaceDeclaration)) | 272 | foreach (var declaration in element.Attributes().Where(a => a.IsNamespaceDeclaration)) |
| 231 | { | 273 | { |
| 232 | XNamespace ns; | 274 | if (Converter.OldToNewNamespaceMapping.TryGetValue(declaration.Value, out var ns)) |
| 233 | |||
| 234 | if (Converter.OldToNewNamespaceMapping.TryGetValue(declaration.Value, out ns)) | ||
| 235 | { | 275 | { |
| 236 | if (this.OnError(ConverterTestType.XmlnsValueWrong, declaration, "The namespace '{0}' is out of date. It must be '{1}'.", declaration.Value, ns.NamespaceName)) | 276 | if (this.OnError(ConverterTestType.XmlnsValueWrong, declaration, "The namespace '{0}' is out of date. It must be '{1}'.", declaration.Value, ns.NamespaceName)) |
| 237 | { | 277 | { |
| @@ -245,10 +285,8 @@ namespace WixToolset.Tools.WixCop | |||
| 245 | Converter.UpdateElementsWithDeprecatedNamespaces(element.DescendantsAndSelf(), deprecatedToUpdatedNamespaces); | 285 | Converter.UpdateElementsWithDeprecatedNamespaces(element.DescendantsAndSelf(), deprecatedToUpdatedNamespaces); |
| 246 | } | 286 | } |
| 247 | 287 | ||
| 248 | // Convert the node in much greater detail. | 288 | // Apply any specialized conversion actions. |
| 249 | Action<XElement> convert; | 289 | if (this.ConvertElementMapping.TryGetValue(element.Name, out var convert)) |
| 250 | |||
| 251 | if (this.ConvertElementMapping.TryGetValue(element.Name, out convert)) | ||
| 252 | { | 290 | { |
| 253 | convert(element); | 291 | convert(element); |
| 254 | } | 292 | } |
| @@ -318,96 +356,20 @@ namespace WixToolset.Tools.WixCop | |||
| 318 | } | 356 | } |
| 319 | } | 357 | } |
| 320 | 358 | ||
| 321 | /// <summary> | ||
| 322 | /// Convert the whitespace adjacent to a node. | ||
| 323 | /// </summary> | ||
| 324 | /// <param name="node">The node to convert.</param> | ||
| 325 | /// <param name="level">The depth level of the node.</param> | ||
| 326 | private void ConvertWhitespace(XNode node, int level) | ||
| 327 | { | ||
| 328 | // Fix the whitespace before this node. | ||
| 329 | if (node.PreviousNode is XText whitespace) | ||
| 330 | { | ||
| 331 | if (XmlNodeType.CDATA == node.NodeType) | ||
| 332 | { | ||
| 333 | if (this.OnError(ConverterTestType.WhitespacePrecedingCDATAWrong, node, "There should be no whitespace preceding a CDATA node.")) | ||
| 334 | { | ||
| 335 | whitespace.Remove(); | ||
| 336 | } | ||
| 337 | } | ||
| 338 | else | ||
| 339 | { | ||
| 340 | // TODO: this code complains about whitespace even after the file has been fixed. | ||
| 341 | if (!Converter.IsLegalWhitespace(this.IndentationAmount, level, whitespace.Value)) | ||
| 342 | { | ||
| 343 | if (this.OnError(ConverterTestType.WhitespacePrecedingNodeWrong, node, "The whitespace preceding this node is incorrect.")) | ||
| 344 | { | ||
| 345 | Converter.FixWhitespace(this.IndentationAmount, level, whitespace); | ||
| 346 | } | ||
| 347 | } | ||
| 348 | } | ||
| 349 | } | ||
| 350 | |||
| 351 | // Fix the whitespace after CDATA nodes. | ||
| 352 | if (node is XCData cdata) | ||
| 353 | { | ||
| 354 | whitespace = cdata.NextNode as XText; | ||
| 355 | |||
| 356 | if (null != whitespace) | ||
| 357 | { | ||
| 358 | if (this.OnError(ConverterTestType.WhitespaceFollowingCDATAWrong, node, "There should be no whitespace following a CDATA node.")) | ||
| 359 | { | ||
| 360 | whitespace.Remove(); | ||
| 361 | } | ||
| 362 | } | ||
| 363 | } | ||
| 364 | else | ||
| 365 | { | ||
| 366 | // Fix the whitespace inside and after this node (except for Error which may contain just whitespace). | ||
| 367 | if (node is XElement element && "Error" != element.Name.LocalName) | ||
| 368 | { | ||
| 369 | if (!element.HasElements && !element.IsEmpty && String.IsNullOrEmpty(element.Value.Trim())) | ||
| 370 | { | ||
| 371 | if (this.OnError(ConverterTestType.NotEmptyElement, element, "This should be an empty element since it contains nothing but whitespace.")) | ||
| 372 | { | ||
| 373 | element.RemoveNodes(); | ||
| 374 | } | ||
| 375 | } | ||
| 376 | |||
| 377 | whitespace = node.NextNode as XText; | ||
| 378 | |||
| 379 | if (null != whitespace) | ||
| 380 | { | ||
| 381 | // TODO: this code crashes when level is 0, | ||
| 382 | // complains about whitespace even after the file has been fixed, | ||
| 383 | // and the error text doesn't match the error. | ||
| 384 | if (!Converter.IsLegalWhitespace(this.IndentationAmount, level - 1, whitespace.Value)) | ||
| 385 | { | ||
| 386 | if (this.OnError(ConverterTestType.WhitespacePrecedingEndElementWrong, whitespace, "The whitespace preceding this end element is incorrect.")) | ||
| 387 | { | ||
| 388 | Converter.FixWhitespace(this.IndentationAmount, level - 1, whitespace); | ||
| 389 | } | ||
| 390 | } | ||
| 391 | } | ||
| 392 | } | ||
| 393 | } | ||
| 394 | } | ||
| 395 | |||
| 396 | private IEnumerable<ConverterTestType> YieldConverterTypes(IEnumerable<string> types) | 359 | private IEnumerable<ConverterTestType> YieldConverterTypes(IEnumerable<string> types) |
| 397 | { | 360 | { |
| 398 | if (null != types) | 361 | if (null != types) |
| 399 | { | 362 | { |
| 400 | foreach (var type in types) | 363 | foreach (var type in types) |
| 401 | { | 364 | { |
| 402 | ConverterTestType itt; | ||
| 403 | 365 | ||
| 404 | if (Enum.TryParse<ConverterTestType>(type, true, out itt)) | 366 | if (Enum.TryParse<ConverterTestType>(type, true, out var itt)) |
| 405 | { | 367 | { |
| 406 | yield return itt; | 368 | yield return itt; |
| 407 | } | 369 | } |
| 408 | else // not a known ConverterTestType | 370 | else // not a known ConverterTestType |
| 409 | { | 371 | { |
| 410 | this.OnError(ConverterTestType.ConverterTestTypeUnknown, (XObject)null, "Unknown error type: '{0}'.", type); | 372 | this.OnError(ConverterTestType.ConverterTestTypeUnknown, null, "Unknown error type: '{0}'.", type); |
| 411 | } | 373 | } |
| 412 | } | 374 | } |
| 413 | } | 375 | } |
| @@ -417,9 +379,8 @@ namespace WixToolset.Tools.WixCop | |||
| 417 | { | 379 | { |
| 418 | foreach (var element in elements) | 380 | foreach (var element in elements) |
| 419 | { | 381 | { |
| 420 | XNamespace ns; | ||
| 421 | 382 | ||
| 422 | if (deprecatedToUpdatedNamespaces.TryGetValue(element.Name.Namespace, out ns)) | 383 | if (deprecatedToUpdatedNamespaces.TryGetValue(element.Name.Namespace, out var ns)) |
| 423 | { | 384 | { |
| 424 | element.Name = ns.GetName(element.Name.LocalName); | 385 | element.Name = ns.GetName(element.Name.LocalName); |
| 425 | } | 386 | } |
| @@ -456,67 +417,33 @@ namespace WixToolset.Tools.WixCop | |||
| 456 | /// <param name="level">The depth level that should match this whitespace.</param> | 417 | /// <param name="level">The depth level that should match this whitespace.</param> |
| 457 | /// <param name="whitespace">The whitespace to validate.</param> | 418 | /// <param name="whitespace">The whitespace to validate.</param> |
| 458 | /// <returns>true if the whitespace is legal; false otherwise.</returns> | 419 | /// <returns>true if the whitespace is legal; false otherwise.</returns> |
| 459 | private static bool IsLegalWhitespace(int indentationAmount, int level, string whitespace) | 420 | private static bool LeadingWhitespaceValid(int indentationAmount, int level, string whitespace) |
| 460 | { | 421 | { |
| 461 | // strip off leading newlines; there can be an arbitrary number of these | 422 | // Strip off leading newlines; there can be an arbitrary number of these. |
| 462 | while (whitespace.StartsWith(XDocumentNewLine, StringComparison.Ordinal)) | 423 | whitespace = whitespace.TrimStart(XDocumentNewLine); |
| 463 | { | ||
| 464 | whitespace = whitespace.Substring(XDocumentNewLine.Length); | ||
| 465 | } | ||
| 466 | 424 | ||
| 467 | // check the length | 425 | var indentation = new string(' ', level * indentationAmount); |
| 468 | if (whitespace.Length != level * indentationAmount) | ||
| 469 | { | ||
| 470 | return false; | ||
| 471 | } | ||
| 472 | 426 | ||
| 473 | // check the spaces | 427 | return whitespace == indentation; |
| 474 | foreach (var character in whitespace) | ||
| 475 | { | ||
| 476 | if (' ' != character) | ||
| 477 | { | ||
| 478 | return false; | ||
| 479 | } | ||
| 480 | } | ||
| 481 | |||
| 482 | return true; | ||
| 483 | } | 428 | } |
| 484 | 429 | ||
| 485 | /// <summary> | 430 | /// <summary> |
| 486 | /// Fix the whitespace in a Whitespace node. | 431 | /// Fix the whitespace in a whitespace node. |
| 487 | /// </summary> | 432 | /// </summary> |
| 488 | /// <param name="indentationAmount">Indentation value to use when validating leading whitespace.</param> | 433 | /// <param name="indentationAmount">Indentation value to use when validating leading whitespace.</param> |
| 489 | /// <param name="level">The depth level of the desired whitespace.</param> | 434 | /// <param name="level">The depth level of the desired whitespace.</param> |
| 490 | /// <param name="whitespace">The whitespace node to fix.</param> | 435 | /// <param name="whitespace">The whitespace node to fix.</param> |
| 491 | private static void FixWhitespace(int indentationAmount, int level, XText whitespace) | 436 | private static void FixupWhitespace(int indentationAmount, int level, XText whitespace) |
| 492 | { | 437 | { |
| 493 | var newLineCount = 0; | 438 | var value = new StringBuilder(whitespace.Value.Length); |
| 494 | |||
| 495 | for (var i = 0; i + 1 < whitespace.Value.Length; ++i) | ||
| 496 | { | ||
| 497 | if (XDocumentNewLine == whitespace.Value.Substring(i, 2)) | ||
| 498 | { | ||
| 499 | ++i; // skip an extra character | ||
| 500 | ++newLineCount; | ||
| 501 | } | ||
| 502 | } | ||
| 503 | |||
| 504 | if (0 == newLineCount) | ||
| 505 | { | ||
| 506 | newLineCount = 1; | ||
| 507 | } | ||
| 508 | 439 | ||
| 509 | // reset the whitespace value | 440 | // Keep any previous preceeding new lines. |
| 510 | whitespace.Value = String.Empty; | 441 | var newlines = whitespace.Value.TakeWhile(c => c == XDocumentNewLine).Count(); |
| 511 | 442 | ||
| 512 | // add the correct number of newlines | 443 | // Ensure there is always at least one new line before the indentation. |
| 513 | for (var i = 0; i < newLineCount; ++i) | 444 | value.Append(XDocumentNewLine, newlines == 0 ? 1 : newlines); |
| 514 | { | ||
| 515 | whitespace.Value = String.Concat(whitespace.Value, XDocumentNewLine); | ||
| 516 | } | ||
| 517 | 445 | ||
| 518 | // add the correct number of spaces based on configured indentation amount | 446 | whitespace.Value = value.Append(' ', level * indentationAmount).ToString(); |
| 519 | whitespace.Value = String.Concat(whitespace.Value, new string(' ', level * indentationAmount)); | ||
| 520 | } | 447 | } |
| 521 | 448 | ||
| 522 | /// <summary> | 449 | /// <summary> |
