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 | |
| parent | 6a6653c3fbda30e194d80f917bae5d7366c26644 (diff) | |
| download | wix-d98126dc766b9b063d2d26ced62553d4a5a218b5.tar.gz wix-d98126dc766b9b063d2d26ced62553d4a5a218b5.tar.bz2 wix-d98126dc766b9b063d2d26ced62553d4a5a218b5.zip | |
Minor project cleanup
Diffstat (limited to 'src')
| -rw-r--r-- | src/WixToolset.Core.TestPackage/WixRunnerResult.cs | 2 | ||||
| -rw-r--r-- | src/WixToolset.Core/Converter.cs | 619 |
2 files changed, 1 insertions, 620 deletions
diff --git a/src/WixToolset.Core.TestPackage/WixRunnerResult.cs b/src/WixToolset.Core.TestPackage/WixRunnerResult.cs index 45e31c2d..8fc7e14f 100644 --- a/src/WixToolset.Core.TestPackage/WixRunnerResult.cs +++ b/src/WixToolset.Core.TestPackage/WixRunnerResult.cs | |||
| @@ -15,7 +15,7 @@ namespace WixToolset.Core.TestPackage | |||
| 15 | 15 | ||
| 16 | public WixRunnerResult AssertSuccess() | 16 | public WixRunnerResult AssertSuccess() |
| 17 | { | 17 | { |
| 18 | Assert.True(0 == this.ExitCode, $"MSBuild failed unexpectedly. Output:\r\n{String.Join("\r\n", this.Messages.Select(m => m.ToString()).ToArray())}"); | 18 | Assert.True(0 == this.ExitCode, $"\r\n\r\nWixRunner failed with exit code: {this.ExitCode}\r\n Output: {String.Join("\r\n ", this.Messages.Select(m => m.ToString()).ToArray())}\r\n"); |
| 19 | return this; | 19 | return this; |
| 20 | } | 20 | } |
| 21 | } | 21 | } |
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 | } | ||
