From aa6f15ec4998a77622fafe9c510b3b547c595679 Mon Sep 17 00:00:00 2001 From: Rob Mensching Date: Tue, 14 Nov 2017 23:21:52 -0800 Subject: Refactor CompilerCore to ParserHelper and other clean up --- src/WixToolset.Core/AppCommon.cs | 3 +- src/WixToolset.Core/BinderCore.cs | 44 -- src/WixToolset.Core/Common.cs | 7 +- src/WixToolset.Core/Compiler.cs | 13 +- src/WixToolset.Core/CompilerCore.cs | 760 +++---------------- src/WixToolset.Core/Converter.cs | 2 +- src/WixToolset.Core/Extensibility/HeatExtension.cs | 2 +- src/WixToolset.Core/Extensibility/IHeatCore.cs | 2 +- .../Extensibility/MutatorExtension.cs | 2 +- .../ExtensibilityServices/ParseHelper.cs | 824 +++++++++++++++++++++ .../TupleDefinitionCreator.cs | 52 ++ src/WixToolset.Core/HarvesterCore.cs | 3 +- src/WixToolset.Core/HeatCore.cs | 4 +- src/WixToolset.Core/Link/WixGroupingOrdering.cs | 4 +- src/WixToolset.Core/Localizer.cs | 2 +- src/WixToolset.Core/Mutator.cs | 4 +- src/WixToolset.Core/Preprocessor.cs | 3 +- src/WixToolset.Core/TupleDefinitionCreator.cs | 52 -- src/WixToolset.Core/WixToolsetServiceProvider.cs | 7 + src/WixToolset.Data.WindowsInstaller/Output.cs | 5 - 20 files changed, 997 insertions(+), 798 deletions(-) delete mode 100644 src/WixToolset.Core/BinderCore.cs create mode 100644 src/WixToolset.Core/ExtensibilityServices/ParseHelper.cs create mode 100644 src/WixToolset.Core/ExtensibilityServices/TupleDefinitionCreator.cs delete mode 100644 src/WixToolset.Core/TupleDefinitionCreator.cs diff --git a/src/WixToolset.Core/AppCommon.cs b/src/WixToolset.Core/AppCommon.cs index 5fde632a..7716155a 100644 --- a/src/WixToolset.Core/AppCommon.cs +++ b/src/WixToolset.Core/AppCommon.cs @@ -1,10 +1,9 @@ // 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. -namespace WixToolset +namespace WixToolset.Core { using System; using System.Collections.Specialized; - using System.Configuration; using System.Globalization; using System.IO; using System.Reflection; diff --git a/src/WixToolset.Core/BinderCore.cs b/src/WixToolset.Core/BinderCore.cs deleted file mode 100644 index 84173b99..00000000 --- a/src/WixToolset.Core/BinderCore.cs +++ /dev/null @@ -1,44 +0,0 @@ -// 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. - -namespace WixToolset -{ - using WixToolset.Data; - using WixToolset.Extensibility; - - /// - /// Core class for the binder. - /// - internal class BinderCore : IBinderCore - { - public IBinderFileManagerCore FileManagerCore { get; set; } - - /// - /// Gets whether the binder core encountered an error while processing. - /// - /// Flag if core encountered an error during processing. - public bool EncounteredError - { - get { return Messaging.Instance.EncounteredError; } - } - - /// - /// Generate an identifier by hashing data from the row. - /// - /// Three letter or less prefix for generated row identifier. - /// Information to hash. - /// The generated identifier. - public string CreateIdentifier(string prefix, params string[] args) - { - return Common.GenerateIdentifier(prefix, args); - } - - /// - /// Sends a message to the message delegate if there is one. - /// - /// Message event arguments. - public void OnMessage(MessageEventArgs e) - { - Messaging.Instance.OnMessage(e); - } - } -} diff --git a/src/WixToolset.Core/Common.cs b/src/WixToolset.Core/Common.cs index 28e7ee7b..9a0d3aec 100644 --- a/src/WixToolset.Core/Common.cs +++ b/src/WixToolset.Core/Common.cs @@ -1,6 +1,6 @@ // 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. -namespace WixToolset +namespace WixToolset.Core { using System; using System.Diagnostics; @@ -13,6 +13,7 @@ namespace WixToolset using System.Xml; using System.Xml.Linq; using WixToolset.Data; + using WixToolset.Extensibility; /// /// Common Wix utility methods and types. @@ -766,8 +767,8 @@ namespace WixToolset /// The attribute's YesNoType value. internal static string GetInnerText(XElement node) { - XText text = node.Nodes().Where(n => XmlNodeType.Text == n.NodeType || XmlNodeType.CDATA == n.NodeType).Cast().FirstOrDefault(); - return (null == text) ? null : text.Value; + var text = node.Nodes().Where(n => XmlNodeType.Text == n.NodeType || XmlNodeType.CDATA == n.NodeType).Cast().FirstOrDefault(); + return text?.Value; } /// diff --git a/src/WixToolset.Core/Compiler.cs b/src/WixToolset.Core/Compiler.cs index 903aae61..4b1ef033 100644 --- a/src/WixToolset.Core/Compiler.cs +++ b/src/WixToolset.Core/Compiler.cs @@ -1,6 +1,6 @@ // 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. -namespace WixToolset +namespace WixToolset.Core { using System; using System.Collections; @@ -11,7 +11,6 @@ namespace WixToolset using System.IO; using System.Text.RegularExpressions; using System.Xml.Linq; - using WixToolset.Core; using WixToolset.Core.Native; using WixToolset.Data; using WixToolset.Data.Tuples; @@ -22,7 +21,6 @@ namespace WixToolset /// /// Compiler of the WiX toolset. /// - [SuppressMessage("Microsoft.Naming", "CA1724:TypeNamesShouldNotMatchNamespaces")] public sealed class Compiler { public const string UpgradeDetectedProperty = "WIX_UPGRADE_DETECTED"; @@ -40,7 +38,7 @@ namespace WixToolset private const string BURN_BUNDLE_ORIGINAL_SOURCE_FOLDER = "WixBundleOriginalSourceFolder"; private const string BURN_BUNDLE_LAST_USED_SOURCE = "WixBundleLastUsedSource"; - // if these are true you know you are building a module or product + // If these are true you know you are building a module or product // but if they are false you cannot not be sure they will not end // up a product or module. Use these flags carefully. private bool compilingModule; @@ -123,16 +121,15 @@ namespace WixToolset // Try to compile it. try { - var creator = context.ServiceProvider.GetService(); + var parseHelper = context.ServiceProvider.GetService(); - this.Core = new CompilerCore(target, creator, extensionsByNamespace); - this.Core.CurrentPlatform = this.Context.Platform; + this.Core = new CompilerCore(target, parseHelper, extensionsByNamespace); this.Core.ShowPedanticMessages = this.ShowPedanticMessages; this.componentIdPlaceholdersResolver = new WixVariableResolver(); // parse the document var source = context.Source; - SourceLineNumber sourceLineNumbers = Preprocessor.GetSourceLineNumbers(source.Root); + var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(source.Root); if ("Wix" == source.Root.Name.LocalName) { if (CompilerCore.WixNamespace == source.Root.Name.Namespace) diff --git a/src/WixToolset.Core/CompilerCore.cs b/src/WixToolset.Core/CompilerCore.cs index 46a2e435..26c19acc 100644 --- a/src/WixToolset.Core/CompilerCore.cs +++ b/src/WixToolset.Core/CompilerCore.cs @@ -1,6 +1,6 @@ // 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. -namespace WixToolset +namespace WixToolset.Core { using System; using System.Collections; @@ -17,6 +17,7 @@ namespace WixToolset using WixToolset.Data; using WixToolset.Data.Tuples; using WixToolset.Extensibility; + using WixToolset.Extensibility.Services; using Wix = WixToolset.Data.Serialize; internal enum ValueListKind @@ -40,7 +41,7 @@ namespace WixToolset /// /// Core class for the compiler. /// - internal sealed class CompilerCore //: ICompilerCore + internal sealed class CompilerCore { internal static readonly XNamespace W3SchemaPrefix = "http://www.w3.org/"; internal static readonly XNamespace WixNamespace = "http://wixtoolset.org/schemas/v4/wxs"; @@ -50,19 +51,6 @@ namespace WixToolset private const string IllegalLongFilenameCharacters = @"[\\\?|><:/\*""]"; // illegal: \ ? | > < : / * " private static readonly Regex IllegalLongFilename = new Regex(IllegalLongFilenameCharacters, RegexOptions.Compiled); - private const string LegalLongFilenameCharacters = @"[^\\\?|><:/\*""]"; // opposite of illegal above. - private static readonly Regex LegalLongFilename = new Regex(String.Concat("^", LegalLongFilenameCharacters, @"{1,259}$"), RegexOptions.Compiled); - - private const string LegalRelativeLongFilenameCharacters = @"[^\?|><:/\*""]"; // (like legal long, but we allow '\') illegal: ? | > < : / * " - private static readonly Regex LegalRelativeLongFilename = new Regex(String.Concat("^", LegalRelativeLongFilenameCharacters, @"{1,259}$"), RegexOptions.Compiled); - - private const string LegalWildcardLongFilenameCharacters = @"[^\\|><:/""]"; // illegal: \ | > < : / " - private static readonly Regex LegalWildcardLongFilename = new Regex(String.Concat("^", LegalWildcardLongFilenameCharacters, @"{1,259}$")); - - private static readonly Regex PutGuidHere = new Regex(@"PUT\-GUID\-(?:\d+\-)?HERE", RegexOptions.Singleline); - - private static readonly Regex LegalIdentifierWithAccess = new Regex(@"^((?public|internal|protected|private)\s+)?(?[_A-Za-z][0-9A-Za-z_\.]*)$", RegexOptions.Compiled | RegexOptions.ExplicitCapture); - public const int DefaultMaximumUncompressedMediaSize = 200; // Default value is 200 MB public const int MinValueOfMaxCabSizeForLargeFileSplitting = 20; // 20 MB public const int MaxValueOfMaxCabSizeForLargeFileSplitting = 2 * 1024; // 2048 MB (i.e. 2 GB) @@ -141,7 +129,7 @@ namespace WixToolset }); private Dictionary extensions; - private ITupleDefinitionCreator creator; + private IParseHelper parseHelper; private Intermediate intermediate; private HashSet activeSectionInlinedDirectoryIds; @@ -152,10 +140,10 @@ namespace WixToolset /// /// The Intermediate object representing compiled source document. /// The WiX extensions collection. - internal CompilerCore(Intermediate intermediate, ITupleDefinitionCreator creator, Dictionary extensions) + internal CompilerCore(Intermediate intermediate, IParseHelper parseHelper, Dictionary extensions) { this.extensions = extensions; - this.creator = creator; + this.parseHelper = parseHelper; this.intermediate = intermediate; } @@ -165,12 +153,6 @@ namespace WixToolset /// The section the compiler is currently emitting symbols into. public IntermediateSection ActiveSection { get; private set; } - /// - /// Gets or sets the platform which the compiler will use when defaulting 64-bit attributes and elements. - /// - /// The platform which the compiler will use when defaulting 64-bit attributes and elements. - public Platform CurrentPlatform { get; set; } - /// /// Gets whether the compiler core encountered an error while processing. /// @@ -231,12 +213,7 @@ namespace WixToolset /// true if the filename is ambiguous; false otherwise. public static bool IsAmbiguousFilename(string filename) { - if (null == filename || 0 == filename.Length) - { - return false; - } - - return CompilerCore.AmbiguousFilename.IsMatch(filename); + return String.IsNullOrEmpty(filename) ? false : CompilerCore.AmbiguousFilename.IsMatch(filename); } /// @@ -246,7 +223,7 @@ namespace WixToolset /// true if the value is an identifier; false otherwise. public bool IsValidIdentifier(string value) { - return Common.IsIdentifier(value); + return this.parseHelper.IsValidIdentifier(value); } /// @@ -256,14 +233,7 @@ namespace WixToolset /// True if the identifier is a valid loc identifier. public bool IsValidLocIdentifier(string identifier) { - if (String.IsNullOrEmpty(identifier)) - { - return false; - } - - Match match = Common.WixVariableRegex.Match(identifier); - - return (match.Success && "loc" == match.Groups["namespace"].Value && 0 == match.Index && identifier.Length == match.Length); + return this.parseHelper.IsValidIdentifier(identifier); } /// @@ -275,34 +245,7 @@ namespace WixToolset /// True if the filename is a valid long filename public bool IsValidLongFilename(string filename, bool allowWildcards = false, bool allowRelative = false) { - if (String.IsNullOrEmpty(filename)) - { - return false; - } - - // check for a non-period character (all periods is not legal) - bool nonPeriodFound = false; - foreach (char character in filename) - { - if ('.' != character) - { - nonPeriodFound = true; - break; - } - } - - if (allowWildcards) - { - return (nonPeriodFound && CompilerCore.LegalWildcardLongFilename.IsMatch(filename)); - } - else if (allowRelative) - { - return (nonPeriodFound && CompilerCore.LegalRelativeLongFilename.IsMatch(filename)); - } - else - { - return (nonPeriodFound && CompilerCore.LegalLongFilename.IsMatch(filename)); - } + return this.parseHelper.IsValidLongFilename(filename, allowWildcards, allowRelative); } /// @@ -313,7 +256,7 @@ namespace WixToolset /// True if the filename is a valid short filename public bool IsValidShortFilename(string filename, bool allowWildcards) { - return Common.IsValidShortFilename(filename, allowWildcards); + return this.parseHelper.IsValidShortFilename(filename, allowWildcards); } /// @@ -337,52 +280,7 @@ namespace WixToolset /// The generated 8.3-compliant short file/directory name. public string CreateShortName(string longName, bool keepExtension, bool allowWildcards, params string[] args) { - // canonicalize the long name if its not a localization identifier (they are case-sensitive) - if (!this.IsValidLocIdentifier(longName)) - { - longName = longName.ToLowerInvariant(); - } - - // collect all the data - List strings = new List(1 + args.Length); - strings.Add(longName); - strings.AddRange(args); - - // prepare for hashing - string stringData = String.Join("|", strings); - byte[] data = Encoding.UTF8.GetBytes(stringData); - - // hash the data - byte[] hash; - using (SHA1 sha1 = new SHA1CryptoServiceProvider()) - { - hash = sha1.ComputeHash(data); - } - - // generate the short file/directory name without an extension - StringBuilder shortName = new StringBuilder(Convert.ToBase64String(hash)); - shortName.Remove(8, shortName.Length - 8).Replace('+', '-').Replace('/', '_'); - - if (keepExtension) - { - string extension = Path.GetExtension(longName); - - if (4 < extension.Length) - { - extension = extension.Substring(0, 4); - } - - shortName.Append(extension); - - // check the generated short name to ensure its still legal (the extension may not be legal) - if (!this.IsValidShortFilename(shortName.ToString(), allowWildcards)) - { - // remove the extension (by truncating the generated file name back to the generated characters) - shortName.Length -= extension.Length; - } - } - - return shortName.ToString().ToLowerInvariant(); + return this.parseHelper.CreateShortName(longName, keepExtension, allowWildcards, args); } /// @@ -422,8 +320,7 @@ namespace WixToolset /// The node's inner text trimmed. public string GetTrimmedInnerText(XElement element) { - string value = Common.GetInnerText(element); - return (null == value) ? null : value.Trim(); + return this.parseHelper.GetTrimmedInnerText(element); } /// @@ -431,23 +328,9 @@ namespace WixToolset /// /// The element to ensure inner text is a condition. /// The value converted into a safe condition. - [SuppressMessage("Microsoft.Design", "CA1059:MembersShouldNotExposeCertainConcreteTypes")] public string GetConditionInnerText(XElement element) { - string value = element.Value; - if (0 < value.Length) - { - value = value.Trim(); - value = value.Replace('\t', ' '); - value = value.Replace('\r', ' '); - value = value.Replace('\n', ' '); - } - else // return null for a non-existant condition - { - value = null; - } - - return value; + return this.parseHelper.GetConditionInnerText(element); } /// @@ -458,7 +341,7 @@ namespace WixToolset /// The generated GUID for the given namespace and value. public string CreateGuid(Guid namespaceGuid, string value) { - return Uuid.NewUuid(namespaceGuid, value).ToString("B").ToUpperInvariant(); + return this.parseHelper.CreateGuid(namespaceGuid, value); } /// @@ -510,46 +393,7 @@ namespace WixToolset /// Identifier of the leaf directory created. public string CreateDirectoryReferenceFromInlineSyntax(SourceLineNumber sourceLineNumbers, XAttribute attribute, string parentId) { - string id = null; - string[] inlineSyntax = this.GetAttributeInlineDirectorySyntax(sourceLineNumbers, attribute, true); - - if (null != inlineSyntax) - { - // Special case the single entry in the inline syntax since it is the most common case - // and needs no extra processing. It's just a reference to an existing directory. - if (1 == inlineSyntax.Length) - { - id = inlineSyntax[0]; - this.CreateSimpleReference(sourceLineNumbers, "Directory", id); - } - else // start creating rows for the entries in the inline syntax - { - id = parentId; - - int pathStartsAt = 0; - if (inlineSyntax[0].EndsWith(":")) - { - // TODO: should overriding the parent identifier with a specific id be an error or a warning or just let it slide? - //if (null != parentId) - //{ - // this.core.OnMessage(WixErrors.Xxx(sourceLineNumbers)); - //} - - id = inlineSyntax[0].TrimEnd(':'); - this.CreateSimpleReference(sourceLineNumbers, "Directory", id); - - pathStartsAt = 1; - } - - for (int i = pathStartsAt; i < inlineSyntax.Length; ++i) - { - Identifier inlineId = this.CreateDirectoryRow(sourceLineNumbers, null, id, inlineSyntax[i]); - id = inlineId.Id; - } - } - } - - return id; + return this.parseHelper.CreateDirectoryReferenceFromInlineSyntax(this.ActiveSection, sourceLineNumbers, attribute, parentId); } /// @@ -566,56 +410,6 @@ namespace WixToolset patchReferenceRow.Set(1, String.Join("/", primaryKeys)); } - /// - /// Creates a Registry row in the active section. - /// - /// Source and line number of the current row. - /// The registry entry root. - /// The registry entry key. - /// The registry entry name. - /// The registry entry value. - /// The component which will control installation/uninstallation of the registry entry. - /// If true, "escape" leading '#' characters so the value is written as a REG_SZ. - public Identifier CreateRegistryRow(SourceLineNumber sourceLineNumbers, int root, string key, string name, string value, string componentId, bool escapeLeadingHash) - { - Identifier id = null; - - if (!this.EncounteredError) - { - if (-1 > root || 3 < root) - { - throw new ArgumentOutOfRangeException("root"); - } - - if (null == key) - { - throw new ArgumentNullException("key"); - } - - if (null == componentId) - { - throw new ArgumentNullException("componentId"); - } - - // escape the leading '#' character for string registry values - if (escapeLeadingHash && null != value && value.StartsWith("#", StringComparison.Ordinal)) - { - value = String.Concat("#", value); - } - - id = this.CreateIdentifier("reg", componentId, root.ToString(CultureInfo.InvariantCulture.NumberFormat), key.ToLowerInvariant(), (null != name ? name.ToLowerInvariant() : name)); - - var row = this.CreateRow(sourceLineNumbers, TupleDefinitionType.Registry, id); - row.Set(1, root); - row.Set(2, key); - row.Set(3, name); - row.Set(4, value); - row.Set(5, componentId); - } - - return id; - } - /// /// Creates a Registry row in the active section. /// @@ -627,7 +421,7 @@ namespace WixToolset /// The component which will control installation/uninstallation of the registry entry. public Identifier CreateRegistryRow(SourceLineNumber sourceLineNumbers, int root, string key, string name, string value, string componentId) { - return this.CreateRegistryRow(sourceLineNumbers, root, key, name, value, componentId, true); + return this.parseHelper.CreateRegistryRow(this.ActiveSection, sourceLineNumbers, root, key, name, value, componentId, true); } /// @@ -646,9 +440,7 @@ namespace WixToolset // If this simple reference hasn't been added to the active section already, add it. if (this.activeSectionSimpleReferences.Add(id)) { - var wixSimpleReferenceRow = (WixSimpleReferenceTuple)this.CreateRow(sourceLineNumbers, TupleDefinitionType.WixSimpleReference); - wixSimpleReferenceRow.Table = tableName; - wixSimpleReferenceRow.PrimaryKeys = joinedKeys; + this.parseHelper.CreateSimpleReference(this.ActiveSection, sourceLineNumbers, tableName, primaryKeys); } } } @@ -665,21 +457,7 @@ namespace WixToolset { if (!this.EncounteredError) { - if (null == parentId || ComplexReferenceParentType.Unknown == parentType) - { - return; - } - - if (null == childId) - { - throw new ArgumentNullException("childId"); - } - - var row = (WixGroupTuple)this.CreateRow(sourceLineNumbers, TupleDefinitionType.WixGroup); - row.ParentId = parentId; - row.ParentType = parentType; - row.ChildId = childId; - row.ChildType = childType; + this.parseHelper.CreateWixGroupRow(this.ActiveSection, sourceLineNumbers, parentType, parentId, childType, childId); } } @@ -693,16 +471,7 @@ namespace WixToolset { if (!this.EncounteredError) { - var row = this.CreateRow(sourceLineNumbers, TupleDefinitionType.WixEnsureTable); - row.Set(0, tableName); - - // We don't add custom table definitions to the tableDefinitions collection, - // so if it's not in there, it better be a custom table. If the Id is just wrong, - // instead of a custom table, we get an unresolved reference at link time. - if (!this.creator.TryGetTupleDefinitionByName(tableName, out var ignored)) - { - this.CreateSimpleReference(sourceLineNumbers, "WixCustomTable", tableName); - } + this.parseHelper.EnsureTable(this.ActiveSection, sourceLineNumbers, tableName); } } @@ -716,7 +485,7 @@ namespace WixToolset [SuppressMessage("Microsoft.Design", "CA1059:MembersShouldNotExposeCertainConcreteTypes")] public string GetAttributeValue(SourceLineNumber sourceLineNumbers, XAttribute attribute, EmptyRule emptyRule = EmptyRule.CanBeWhitespaceOnly) { - return Common.GetAttributeValue(sourceLineNumbers, attribute, emptyRule); + return this.parseHelper.GetAttributeValue(sourceLineNumbers, attribute, emptyRule); } /// @@ -800,7 +569,7 @@ namespace WixToolset [SuppressMessage("Microsoft.Design", "CA1059:MembersShouldNotExposeCertainConcreteTypes")] public int GetAttributeIntegerValue(SourceLineNumber sourceLineNumbers, XAttribute attribute, int minimum, int maximum) { - return Common.GetAttributeIntegerValue(sourceLineNumbers, attribute, minimum, maximum); + return this.parseHelper.GetAttributeIntegerValue(sourceLineNumbers, attribute, minimum, maximum); } /// @@ -813,39 +582,7 @@ namespace WixToolset /// The attribute's long value or a special value if an error occurred during conversion. public long GetAttributeLongValue(SourceLineNumber sourceLineNumbers, XAttribute attribute, long minimum, long maximum) { - Debug.Assert(minimum > CompilerConstants.LongNotSet && minimum > CompilerConstants.IllegalLong, "The legal values for this attribute collide with at least one sentinel used during parsing."); - - string value = this.GetAttributeValue(sourceLineNumbers, attribute); - - if (0 < value.Length) - { - try - { - long longValue = Convert.ToInt64(value, CultureInfo.InvariantCulture.NumberFormat); - - if (CompilerConstants.LongNotSet == longValue || CompilerConstants.IllegalLong == longValue) - { - this.OnMessage(WixErrors.IntegralValueSentinelCollision(sourceLineNumbers, longValue)); - } - else if (minimum > longValue || maximum < longValue) - { - this.OnMessage(WixErrors.IntegralValueOutOfRange(sourceLineNumbers, attribute.Parent.Name.LocalName, attribute.Name.LocalName, longValue, minimum, maximum)); - longValue = CompilerConstants.IllegalLong; - } - - return longValue; - } - catch (FormatException) - { - this.OnMessage(WixErrors.IllegalLongValue(sourceLineNumbers, attribute.Parent.Name.LocalName, attribute.Name.LocalName, value)); - } - catch (OverflowException) - { - this.OnMessage(WixErrors.IllegalLongValue(sourceLineNumbers, attribute.Parent.Name.LocalName, attribute.Name.LocalName, value)); - } - } - - return CompilerConstants.IllegalLong; + return this.parseHelper.GetAttributeLongValue(sourceLineNumbers, attribute, minimum, maximum); } /// @@ -956,73 +693,9 @@ namespace WixToolset /// Determines whether the guid can be automatically generated. /// If true, no error is raised on empty value. If false, an error is raised. /// The attribute's guid value or a special value if an error occurred. - [SuppressMessage("Microsoft.Design", "CA1059:MembersShouldNotExposeCertainConcreteTypes")] - [SuppressMessage("Microsoft.Performance", "CA1807:AvoidUnnecessaryStringCreation")] public string GetAttributeGuidValue(SourceLineNumber sourceLineNumbers, XAttribute attribute, bool generatable = false, bool canBeEmpty = false) { - if (null == attribute) - { - throw new ArgumentNullException("attribute"); - } - - EmptyRule emptyRule = canBeEmpty ? EmptyRule.CanBeEmpty : EmptyRule.CanBeWhitespaceOnly; - string value = this.GetAttributeValue(sourceLineNumbers, attribute, emptyRule); - - if (String.IsNullOrEmpty(value) && canBeEmpty) - { - return String.Empty; - } - else if (!String.IsNullOrEmpty(value)) - { - // If the value starts and ends with braces or parenthesis, accept that and strip them off. - if ((value.StartsWith("{", StringComparison.Ordinal) && value.EndsWith("}", StringComparison.Ordinal)) - || (value.StartsWith("(", StringComparison.Ordinal) && value.EndsWith(")", StringComparison.Ordinal))) - { - value = value.Substring(1, value.Length - 2); - } - - try - { - Guid guid; - - if (generatable && "*".Equals(value, StringComparison.Ordinal)) - { - return value; - } - - if (CompilerCore.PutGuidHere.IsMatch(value)) - { - this.OnMessage(WixErrors.ExampleGuid(sourceLineNumbers, attribute.Parent.Name.LocalName, attribute.Name.LocalName, value)); - return CompilerConstants.IllegalGuid; - } - else if (value.StartsWith("!(loc", StringComparison.Ordinal) || value.StartsWith("$(loc", StringComparison.Ordinal) || value.StartsWith("!(wix", StringComparison.Ordinal)) - { - return value; - } - else - { - guid = new Guid(value); - } - - string uppercaseGuid = guid.ToString().ToUpper(CultureInfo.InvariantCulture); - - if (this.ShowPedanticMessages) - { - if (uppercaseGuid != value) - { - this.OnMessage(WixErrors.GuidContainsLowercaseLetters(sourceLineNumbers, attribute.Parent.Name.LocalName, attribute.Name.LocalName, value)); - } - } - - return String.Concat("{", uppercaseGuid, "}"); - } - catch (FormatException) - { - this.OnMessage(WixErrors.IllegalGuidValue(sourceLineNumbers, attribute.Parent.Name.LocalName, attribute.Name.LocalName, value)); - } - } - - return CompilerConstants.IllegalGuid; + return this.parseHelper.GetAttributeGuidValue(sourceLineNumbers, attribute, generatable, canBeEmpty); } /// @@ -1033,27 +706,7 @@ namespace WixToolset /// The attribute's identifier value or a special value if an error occurred. public Identifier GetAttributeIdentifier(SourceLineNumber sourceLineNumbers, XAttribute attribute) { - string value = Common.GetAttributeValue(sourceLineNumbers, attribute, EmptyRule.CanBeEmpty); - AccessModifier access = AccessModifier.Public; - - Match match = CompilerCore.LegalIdentifierWithAccess.Match(value); - if (!match.Success) - { - return null; - } - else if (match.Groups["access"].Success) - { - access = (AccessModifier)Enum.Parse(typeof(AccessModifier), match.Groups["access"].Value, true); - } - - value = match.Groups["id"].Value; - - if (Common.IsIdentifier(value) && 72 < value.Length) - { - this.OnMessage(WixWarnings.IdentifierTooLong(sourceLineNumbers, attribute.Parent.Name.LocalName, attribute.Name.LocalName, value)); - } - - return new Identifier(value, access); + return this.parseHelper.GetAttributeIdentifier(sourceLineNumbers, attribute); } /// @@ -1064,7 +717,7 @@ namespace WixToolset /// The attribute's identifier value or a special value if an error occurred. public string GetAttributeIdentifierValue(SourceLineNumber sourceLineNumbers, XAttribute attribute) { - return Common.GetAttributeIdentifierValue(sourceLineNumbers, attribute); + return this.parseHelper.GetAttributeIdentifierValue(sourceLineNumbers, attribute); } /// @@ -1075,23 +728,7 @@ namespace WixToolset /// The attribute's YesNoType value. public YesNoType GetAttributeYesNoValue(SourceLineNumber sourceLineNumbers, XAttribute attribute) { - string value = this.GetAttributeValue(sourceLineNumbers, attribute); - - YesNoType result = YesNoType.IllegalValue; - if (value.Equals("yes") || value.Equals("true")) - { - result = YesNoType.Yes; - } - else if (value.Equals("no") || value.Equals("false")) - { - result = YesNoType.No; - } - else - { - this.OnMessage(WixErrors.IllegalYesNoValue(sourceLineNumbers, attribute.Parent.Name.LocalName, attribute.Name.LocalName, value)); - } - - return result; + return this.parseHelper.GetAttributeYesNoValue(sourceLineNumbers, attribute); } /// @@ -1103,28 +740,7 @@ namespace WixToolset [SuppressMessage("Microsoft.Design", "CA1059:MembersShouldNotExposeCertainConcreteTypes")] public YesNoDefaultType GetAttributeYesNoDefaultValue(SourceLineNumber sourceLineNumbers, XAttribute attribute) { - string value = this.GetAttributeValue(sourceLineNumbers, attribute); - - if (0 < value.Length) - { - switch (Wix.Enums.ParseYesNoDefaultType(value)) - { - case Wix.YesNoDefaultType.@default: - return YesNoDefaultType.Default; - case Wix.YesNoDefaultType.no: - return YesNoDefaultType.No; - case Wix.YesNoDefaultType.yes: - return YesNoDefaultType.Yes; - case Wix.YesNoDefaultType.NotSet: - // Previous code never returned 'NotSet'! - break; - default: - this.OnMessage(WixErrors.IllegalYesNoDefaultValue(sourceLineNumbers, attribute.Parent.Name.LocalName, attribute.Name.LocalName, value)); - break; - } - } - - return YesNoDefaultType.IllegalValue; + return this.parseHelper.GetAttributeYesNoDefaultValue(sourceLineNumbers, attribute); } /// @@ -1202,41 +818,7 @@ namespace WixToolset [SuppressMessage("Microsoft.Design", "CA1059:MembersShouldNotExposeCertainConcreteTypes")] public string GetAttributeLongFilename(SourceLineNumber sourceLineNumbers, XAttribute attribute, bool allowWildcards = false, bool allowRelative = false) { - if (null == attribute) - { - throw new ArgumentNullException("attribute"); - } - - string value = this.GetAttributeValue(sourceLineNumbers, attribute); - - if (0 < value.Length) - { - if (!this.IsValidLongFilename(value, allowWildcards, allowRelative) && !this.IsValidLocIdentifier(value)) - { - if (allowRelative) - { - this.OnMessage(WixErrors.IllegalRelativeLongFilename(sourceLineNumbers, attribute.Parent.Name.LocalName, attribute.Name.LocalName, value)); - } - else - { - this.OnMessage(WixErrors.IllegalLongFilename(sourceLineNumbers, attribute.Parent.Name.LocalName, attribute.Name.LocalName, value)); - } - } - else if (allowRelative) - { - string normalizedPath = value.Replace('\\', '/'); - if (normalizedPath.StartsWith("../", StringComparison.Ordinal) || normalizedPath.Contains("/../")) - { - this.OnMessage(WixErrors.PayloadMustBeRelativeToCache(sourceLineNumbers, attribute.Parent.Name.LocalName, attribute.Name.LocalName, value)); - } - } - else if (CompilerCore.IsAmbiguousFilename(value)) - { - this.OnMessage(WixWarnings.AmbiguousFileOrDirectoryName(sourceLineNumbers, attribute.Parent.Name.LocalName, attribute.Name.LocalName, value)); - } - } - - return value; + return this.parseHelper.GetAttributeLongFilename(sourceLineNumbers, attribute, allowWildcards, allowRelative); } /// @@ -1247,31 +829,7 @@ namespace WixToolset /// The attribute's version value. public string GetAttributeVersionValue(SourceLineNumber sourceLineNumbers, XAttribute attribute) { - string value = this.GetAttributeValue(sourceLineNumbers, attribute); - - if (!String.IsNullOrEmpty(value)) - { - try - { - return new Version(value).ToString(); - } - catch (FormatException) // illegal integer in version - { - // Allow versions to contain binder variables. - if (Common.ContainsValidBinderVariable(value)) - { - return value; - } - - this.OnMessage(WixErrors.IllegalVersionValue(sourceLineNumbers, attribute.Parent.Name.LocalName, attribute.Name.LocalName, value)); - } - catch (ArgumentException) - { - this.OnMessage(WixErrors.IllegalVersionValue(sourceLineNumbers, attribute.Parent.Name.LocalName, attribute.Name.LocalName, value)); - } - } - - return null; + return this.parseHelper.GetAttributeVersionValue(sourceLineNumbers, attribute); } /// @@ -1453,7 +1011,7 @@ namespace WixToolset /// True if a property is found in the string. public bool ContainsProperty(string possibleProperty) { - return Common.ContainsProperty(possibleProperty); + return this.parseHelper.ContainsProperty(possibleProperty); } /// @@ -1465,8 +1023,7 @@ namespace WixToolset [SuppressMessage("Microsoft.Globalization", "CA1303:DoNotPassLiteralsAsLocalizedParameters", MessageId = "System.InvalidOperationException.#ctor(System.String)")] public Identifier CreateIdentifier(string prefix, params string[] args) { - string id = Common.GenerateIdentifier(prefix, args); - return new Identifier(id, AccessModifier.Private); + return this.parseHelper.CreateIdentifier(prefix, args); } /// @@ -1476,8 +1033,7 @@ namespace WixToolset /// public Identifier CreateIdentifierFromFilename(string filename) { - string id = Common.GetIdentifierFromName(filename); - return new Identifier(id, AccessModifier.Private); + return this.parseHelper.CreateIdentifierFromFilename(filename); } /// @@ -1488,22 +1044,7 @@ namespace WixToolset /// Extra information about the context in which this element is being parsed. public void ParseExtensionAttribute(XElement element, XAttribute attribute, IDictionary context = null) { - // Ignore attributes defined by the W3C because we'll assume they are always right. - if ((String.IsNullOrEmpty(attribute.Name.NamespaceName) && attribute.Name.LocalName.Equals("xmlns", StringComparison.Ordinal)) || - attribute.Name.NamespaceName.StartsWith(CompilerCore.W3SchemaPrefix.NamespaceName, StringComparison.Ordinal)) - { - return; - } - - if (this.TryFindExtension(attribute.Name.NamespaceName, out var extension)) - { - extension.ParseAttribute(element, attribute, context); - } - else - { - SourceLineNumber sourceLineNumbers = Preprocessor.GetSourceLineNumbers(element); - this.OnMessage(WixErrors.UnhandledExtensionAttribute(sourceLineNumbers, element.Name.LocalName, attribute.Name.LocalName, attribute.Name.NamespaceName)); - } + this.parseHelper.ParseExtensionAttribute(this.extensions.Values, this.intermediate, this.ActiveSection, element, attribute, context); } /// @@ -1514,16 +1055,7 @@ namespace WixToolset /// Extra information about the context in which this element is being parsed. public void ParseExtensionElement(XElement parentElement, XElement element, IDictionary context = null) { - if (this.TryFindExtension(element.Name.Namespace, out var extension)) - { - SourceLineNumber sourceLineNumbers = Preprocessor.GetSourceLineNumbers(parentElement); - extension.ParseElement(parentElement, element, context); - } - else - { - SourceLineNumber childSourceLineNumbers = Preprocessor.GetSourceLineNumbers(element); - this.OnMessage(WixErrors.UnhandledExtensionElement(childSourceLineNumbers, parentElement.Name.LocalName, element.Name.LocalName, element.Name.NamespaceName)); - } + this.parseHelper.ParseExtensionElement(this.extensions.Values, this.intermediate, this.ActiveSection, parentElement, element, context); } /// @@ -1532,17 +1064,7 @@ namespace WixToolset /// Element to parse children. public void ParseForExtensionElements(XElement element) { - foreach (XElement child in element.Elements()) - { - if (element.Name.Namespace == child.Name.Namespace) - { - this.UnexpectedElement(element, child); - } - else - { - this.ParseExtensionElement(element, child); - } - } + this.parseHelper.ParseForExtensionElements(this.extensions.Values, this.intermediate, this.ActiveSection, element); } /// @@ -1553,20 +1075,7 @@ namespace WixToolset /// Extra information about the context in which this element is being parsed. public ComponentKeyPath ParsePossibleKeyPathExtensionElement(XElement parentElement, XElement element, IDictionary context) { - ComponentKeyPath keyPath = null; - - ICompilerExtension extension; - if (this.TryFindExtension(element.Name.Namespace, out extension)) - { - keyPath = extension.ParsePossibleKeyPathElement(parentElement, element, context); - } - else - { - SourceLineNumber childSourceLineNumbers = Preprocessor.GetSourceLineNumbers(element); - this.OnMessage(WixErrors.UnhandledExtensionElement(childSourceLineNumbers, parentElement.Name.LocalName, element.Name.LocalName, element.Name.NamespaceName)); - } - - return keyPath; + return this.parseHelper.ParsePossibleKeyPathExtensionElement(this.extensions.Values, this.intermediate, this.ActiveSection, parentElement, element, context); } /// @@ -1576,8 +1085,7 @@ namespace WixToolset /// The unexpected attribute. public void UnexpectedAttribute(XElement element, XAttribute attribute) { - SourceLineNumber sourceLineNumbers = Preprocessor.GetSourceLineNumbers(element); - Common.UnexpectedAttribute(sourceLineNumbers, attribute); + this.parseHelper.UnexpectedAttribute(element, attribute); } /// @@ -1587,9 +1095,7 @@ namespace WixToolset /// The unexpected child element. public void UnexpectedElement(XElement parentElement, XElement childElement) { - SourceLineNumber sourceLineNumbers = Preprocessor.GetSourceLineNumbers(childElement); - - this.OnMessage(WixErrors.UnexpectedElement(sourceLineNumbers, parentElement.Name.LocalName, childElement.Name.LocalName)); + this.parseHelper.UnexpectedElement(parentElement, childElement); } /// @@ -1666,30 +1172,6 @@ namespace WixToolset return section; } - /// - /// Creates a WiX complex reference in the active section. - /// - /// Source line information. - /// The parent type. - /// The parent id. - /// The parent language. - /// The child type. - /// The child id. - /// Whether the child is primary. - internal void CreateWixComplexReferenceRow(SourceLineNumber sourceLineNumbers, ComplexReferenceParentType parentType, string parentId, string parentLanguage, ComplexReferenceChildType childType, string childId, bool isPrimary) - { - if (!this.EncounteredError) - { - var wixComplexReferenceRow = (WixComplexReferenceTuple)this.CreateRow(sourceLineNumbers, TupleDefinitionType.WixComplexReference); - wixComplexReferenceRow.Parent = parentId; - wixComplexReferenceRow.ParentType = parentType; - wixComplexReferenceRow.ParentLanguage = parentLanguage; - wixComplexReferenceRow.Child = childId; - wixComplexReferenceRow.ChildType = childType; - wixComplexReferenceRow.IsPrimary = isPrimary; - } - } - /// /// Creates WixComplexReference and WixGroup rows in the active section. /// @@ -1702,8 +1184,7 @@ namespace WixToolset /// Whether the child is primary. public void CreateComplexReference(SourceLineNumber sourceLineNumbers, ComplexReferenceParentType parentType, string parentId, string parentLanguage, ComplexReferenceChildType childType, string childId, bool isPrimary) { - this.CreateWixComplexReferenceRow(sourceLineNumbers, parentType, parentId, parentLanguage, childType, childId, isPrimary); - this.CreateWixGroupRow(sourceLineNumbers, parentType, parentId, childType, childId); + this.parseHelper.CreateComplexReference(this.ActiveSection, sourceLineNumbers, parentType, parentId, parentLanguage, childType, childId, isPrimary); } /// @@ -1719,56 +1200,57 @@ namespace WixToolset /// Identifier for the newly created row. internal Identifier CreateDirectoryRow(SourceLineNumber sourceLineNumbers, Identifier id, string parentId, string name, string shortName = null, string sourceName = null, string shortSourceName = null) { - string defaultDir = null; - - if (name.Equals("SourceDir") || this.IsValidShortFilename(name, false)) - { - defaultDir = name; - } - else - { - if (String.IsNullOrEmpty(shortName)) - { - shortName = this.CreateShortName(name, false, false, "Directory", parentId); - } - - defaultDir = String.Concat(shortName, "|", name); - } - - if (!String.IsNullOrEmpty(sourceName)) - { - if (this.IsValidShortFilename(sourceName, false)) - { - defaultDir = String.Concat(defaultDir, ":", sourceName); - } - else - { - if (String.IsNullOrEmpty(shortSourceName)) - { - shortSourceName = this.CreateShortName(sourceName, false, false, "Directory", parentId); - } - - defaultDir = String.Concat(defaultDir, ":", shortSourceName, "|", sourceName); - } - } - - // For anonymous directories, create the identifier. If this identifier already exists in the - // active section, bail so we don't add duplicate anonymous directory rows (which are legal - // but bloat the intermediate and ultimately make the linker do "busy work"). - if (null == id) - { - id = this.CreateIdentifier("dir", parentId, name, shortName, sourceName, shortSourceName); - - if (!this.activeSectionInlinedDirectoryIds.Add(id.Id)) - { - return id; - } - } - - var row = this.CreateRow(sourceLineNumbers, TupleDefinitionType.Directory, id); - row.Set(1, parentId); - row.Set(2, defaultDir); - return id; + //string defaultDir = null; + + //if (name.Equals("SourceDir") || this.IsValidShortFilename(name, false)) + //{ + // defaultDir = name; + //} + //else + //{ + // if (String.IsNullOrEmpty(shortName)) + // { + // shortName = this.CreateShortName(name, false, false, "Directory", parentId); + // } + + // defaultDir = String.Concat(shortName, "|", name); + //} + + //if (!String.IsNullOrEmpty(sourceName)) + //{ + // if (this.IsValidShortFilename(sourceName, false)) + // { + // defaultDir = String.Concat(defaultDir, ":", sourceName); + // } + // else + // { + // if (String.IsNullOrEmpty(shortSourceName)) + // { + // shortSourceName = this.CreateShortName(sourceName, false, false, "Directory", parentId); + // } + + // defaultDir = String.Concat(defaultDir, ":", shortSourceName, "|", sourceName); + // } + //} + + //// For anonymous directories, create the identifier. If this identifier already exists in the + //// active section, bail so we don't add duplicate anonymous directory rows (which are legal + //// but bloat the intermediate and ultimately make the linker do "busy work"). + //if (null == id) + //{ + // id = this.CreateIdentifier("dir", parentId, name, shortName, sourceName, shortSourceName); + + // if (!this.activeSectionInlinedDirectoryIds.Add(id.Id)) + // { + // return id; + // } + //} + + //var row = this.CreateRow(sourceLineNumbers, TupleDefinitionType.Directory, id); + //row.Set(1, parentId); + //row.Set(2, defaultDir); + //return id; + return this.parseHelper.CreateDirectoryRow(this.ActiveSection, sourceLineNumbers, id, parentId, name, shortName, sourceName, shortSourceName, this.activeSectionInlinedDirectoryIds); } /// @@ -1780,65 +1262,7 @@ namespace WixToolset /// Inline directory syntax split into array of strings or null if the syntax did not parse. internal string[] GetAttributeInlineDirectorySyntax(SourceLineNumber sourceLineNumbers, XAttribute attribute, bool resultUsedToCreateReference = false) { - string[] result = null; - string value = this.GetAttributeValue(sourceLineNumbers, attribute); - - if (!String.IsNullOrEmpty(value)) - { - int pathStartsAt = 0; - result = value.Split(new char[] { '\\' }, StringSplitOptions.RemoveEmptyEntries); - if (result[0].EndsWith(":", StringComparison.Ordinal)) - { - string id = result[0].TrimEnd(':'); - if (1 == result.Length) - { - this.OnMessage(WixErrors.InlineDirectorySyntaxRequiresPath(sourceLineNumbers, attribute.Parent.Name.LocalName, attribute.Name.LocalName, value, id)); - return null; - } - else if (!this.IsValidIdentifier(id)) - { - this.OnMessage(WixErrors.IllegalIdentifier(sourceLineNumbers, attribute.Parent.Name.LocalName, attribute.Name.LocalName, value, id)); - return null; - } - - pathStartsAt = 1; - } - else if (resultUsedToCreateReference && 1 == result.Length) - { - if (value.EndsWith("\\")) - { - if (!this.IsValidLongFilename(result[0])) - { - this.OnMessage(WixErrors.IllegalLongFilename(sourceLineNumbers, attribute.Parent.Name.LocalName, attribute.Name.LocalName, value, result[0])); - return null; - } - } - else if (!this.IsValidIdentifier(result[0])) - { - this.OnMessage(WixErrors.IllegalIdentifier(sourceLineNumbers, attribute.Parent.Name.LocalName, attribute.Name.LocalName, value, result[0])); - return null; - } - - return result; // return early to avoid additional checks below. - } - - // Check each part of the relative path to ensure that it is a valid directory name. - for (int i = pathStartsAt; i < result.Length; ++i) - { - if (!this.IsValidLongFilename(result[i])) - { - this.OnMessage(WixErrors.IllegalLongFilename(sourceLineNumbers, attribute.Parent.Name.LocalName, attribute.Name.LocalName, value, result[i])); - return null; - } - } - - if (1 < result.Length && !value.EndsWith("\\")) - { - this.OnMessage(WixWarnings.BackslashTerminateInlineDirectorySyntax(sourceLineNumbers, attribute.Parent.Name.LocalName, attribute.Name.LocalName, value)); - } - } - - return result; + return this.parseHelper.GetAttributeInlineDirectorySyntax(sourceLineNumbers, attribute, resultUsedToCreateReference); } /// diff --git a/src/WixToolset.Core/Converter.cs b/src/WixToolset.Core/Converter.cs index 6ae2f984..c0297758 100644 --- a/src/WixToolset.Core/Converter.cs +++ b/src/WixToolset.Core/Converter.cs @@ -1,6 +1,6 @@ // 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. -namespace WixToolset +namespace WixToolset.Core { using System; using System.Collections.Generic; diff --git a/src/WixToolset.Core/Extensibility/HeatExtension.cs b/src/WixToolset.Core/Extensibility/HeatExtension.cs index 48e1a93b..2b4a6823 100644 --- a/src/WixToolset.Core/Extensibility/HeatExtension.cs +++ b/src/WixToolset.Core/Extensibility/HeatExtension.cs @@ -1,6 +1,6 @@ // 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. -namespace WixToolset.Extensibility +namespace WixToolset.Core.Extensibility { using System; using System.IO; diff --git a/src/WixToolset.Core/Extensibility/IHeatCore.cs b/src/WixToolset.Core/Extensibility/IHeatCore.cs index dbfc8929..031c8132 100644 --- a/src/WixToolset.Core/Extensibility/IHeatCore.cs +++ b/src/WixToolset.Core/Extensibility/IHeatCore.cs @@ -1,6 +1,6 @@ // 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. -namespace WixToolset.Extensibility +namespace WixToolset.Core.Extensibility { using WixToolset.Data; diff --git a/src/WixToolset.Core/Extensibility/MutatorExtension.cs b/src/WixToolset.Core/Extensibility/MutatorExtension.cs index 9de64180..f95b5b11 100644 --- a/src/WixToolset.Core/Extensibility/MutatorExtension.cs +++ b/src/WixToolset.Core/Extensibility/MutatorExtension.cs @@ -1,6 +1,6 @@ // 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. -namespace WixToolset.Extensibility +namespace WixToolset.Core.Extensibility { using System; using System.Collections.Generic; diff --git a/src/WixToolset.Core/ExtensibilityServices/ParseHelper.cs b/src/WixToolset.Core/ExtensibilityServices/ParseHelper.cs new file mode 100644 index 00000000..87ad0da8 --- /dev/null +++ b/src/WixToolset.Core/ExtensibilityServices/ParseHelper.cs @@ -0,0 +1,824 @@ +// 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. + +namespace WixToolset.Core.ExtensibilityServices +{ + using System; + using System.Collections.Generic; + using System.Diagnostics; + using System.Globalization; + using System.IO; + using System.Security.Cryptography; + using System.Text; + using System.Text.RegularExpressions; + using System.Xml.Linq; + using WixToolset.Data; + using WixToolset.Data.Tuples; + using WixToolset.Extensibility; + using WixToolset.Extensibility.Services; + + internal class ParseHelper : IParseHelper + { + private const string LegalLongFilenameCharacters = @"[^\\\?|><:/\*""]"; // opposite of illegal above. + private static readonly Regex LegalLongFilename = new Regex(String.Concat("^", LegalLongFilenameCharacters, @"{1,259}$"), RegexOptions.Compiled); + + private const string LegalRelativeLongFilenameCharacters = @"[^\?|><:/\*""]"; // (like legal long, but we allow '\') illegal: ? | > < : / * " + private static readonly Regex LegalRelativeLongFilename = new Regex(String.Concat("^", LegalRelativeLongFilenameCharacters, @"{1,259}$"), RegexOptions.Compiled); + + private const string LegalWildcardLongFilenameCharacters = @"[^\\|><:/""]"; // illegal: \ | > < : / " + private static readonly Regex LegalWildcardLongFilename = new Regex(String.Concat("^", LegalWildcardLongFilenameCharacters, @"{1,259}$")); + + private static readonly Regex LegalIdentifierWithAccess = new Regex(@"^((?public|internal|protected|private)\s+)?(?[_A-Za-z][0-9A-Za-z_\.]*)$", RegexOptions.Compiled | RegexOptions.ExplicitCapture); + + private static readonly Regex PutGuidHere = new Regex(@"PUT\-GUID\-(?:\d+\-)?HERE", RegexOptions.Singleline); + + public ParseHelper(IServiceProvider serviceProvider) + { + this.ServiceProvider = serviceProvider; + } + + private IServiceProvider ServiceProvider { get; } + + private ITupleDefinitionCreator Creator { get; set; } + + public bool ContainsProperty(string possibleProperty) + { + return Common.ContainsProperty(possibleProperty); + } + + public void CreateComplexReference(IntermediateSection section, SourceLineNumber sourceLineNumbers, ComplexReferenceParentType parentType, string parentId, string parentLanguage, ComplexReferenceChildType childType, string childId, bool isPrimary) + { + var wixComplexReferenceRow = (WixComplexReferenceTuple)this.CreateRow(section, sourceLineNumbers, TupleDefinitionType.WixComplexReference); + wixComplexReferenceRow.Parent = parentId; + wixComplexReferenceRow.ParentType = parentType; + wixComplexReferenceRow.ParentLanguage = parentLanguage; + wixComplexReferenceRow.Child = childId; + wixComplexReferenceRow.ChildType = childType; + wixComplexReferenceRow.IsPrimary = isPrimary; + + this.CreateWixGroupRow(section, sourceLineNumbers, parentType, parentId, childType, childId); + } + + public Identifier CreateDirectoryRow(IntermediateSection section, SourceLineNumber sourceLineNumbers, Identifier id, string parentId, string name, string shortName = null, string sourceName = null, string shortSourceName = null, ISet sectionInlinedDirectoryIds = null) + { + string defaultDir = null; + + if (name.Equals("SourceDir") || this.IsValidShortFilename(name, false)) + { + defaultDir = name; + } + else + { + if (String.IsNullOrEmpty(shortName)) + { + shortName = this.CreateShortName(name, false, false, "Directory", parentId); + } + + defaultDir = String.Concat(shortName, "|", name); + } + + if (!String.IsNullOrEmpty(sourceName)) + { + if (this.IsValidShortFilename(sourceName, false)) + { + defaultDir = String.Concat(defaultDir, ":", sourceName); + } + else + { + if (String.IsNullOrEmpty(shortSourceName)) + { + shortSourceName = this.CreateShortName(sourceName, false, false, "Directory", parentId); + } + + defaultDir = String.Concat(defaultDir, ":", shortSourceName, "|", sourceName); + } + } + + // For anonymous directories, create the identifier. If this identifier already exists in the + // active section, bail so we don't add duplicate anonymous directory rows (which are legal + // but bloat the intermediate and ultimately make the linker do "busy work"). + if (null == id) + { + id = this.CreateIdentifier("dir", parentId, name, shortName, sourceName, shortSourceName); + + if (!sectionInlinedDirectoryIds.Add(id.Id)) + { + return id; + } + } + + var row = this.CreateRow(section, sourceLineNumbers, TupleDefinitionType.Directory, id); + row.Set(1, parentId); + row.Set(2, defaultDir); + return id; + } + + public string CreateDirectoryReferenceFromInlineSyntax(IntermediateSection section, SourceLineNumber sourceLineNumbers, XAttribute attribute, string parentId) + { + string id = null; + string[] inlineSyntax = this.GetAttributeInlineDirectorySyntax(sourceLineNumbers, attribute, true); + + if (null != inlineSyntax) + { + // Special case the single entry in the inline syntax since it is the most common case + // and needs no extra processing. It's just a reference to an existing directory. + if (1 == inlineSyntax.Length) + { + id = inlineSyntax[0]; + this.CreateSimpleReference(section, sourceLineNumbers, "Directory", id); + } + else // start creating rows for the entries in the inline syntax + { + id = parentId; + + int pathStartsAt = 0; + if (inlineSyntax[0].EndsWith(":")) + { + // TODO: should overriding the parent identifier with a specific id be an error or a warning or just let it slide? + //if (null != parentId) + //{ + // this.core.OnMessage(WixErrors.Xxx(sourceLineNumbers)); + //} + + id = inlineSyntax[0].TrimEnd(':'); + this.CreateSimpleReference(section, sourceLineNumbers, "Directory", id); + + pathStartsAt = 1; + } + + for (int i = pathStartsAt; i < inlineSyntax.Length; ++i) + { + Identifier inlineId = this.CreateDirectoryRow(section, sourceLineNumbers, null, id, inlineSyntax[i]); + id = inlineId.Id; + } + } + } + + return id; + } + + public string CreateGuid(Guid namespaceGuid, string value) + { + return Uuid.NewUuid(namespaceGuid, value).ToString("B").ToUpperInvariant(); + } + + public Identifier CreateIdentifier(string prefix, params string[] args) + { + var id = Common.GenerateIdentifier(prefix, args); + return new Identifier(id, AccessModifier.Private); + } + + public Identifier CreateIdentifierFromFilename(string filename) + { + var id = Common.GetIdentifierFromName(filename); + return new Identifier(id, AccessModifier.Private); + } + + public Identifier CreateRegistryRow(IntermediateSection section, SourceLineNumber sourceLineNumbers, int root, string key, string name, string value, string componentId, bool escapeLeadingHash) + { + Identifier id = null; + + if (-1 > root || 3 < root) + { + throw new ArgumentOutOfRangeException("root"); + } + + if (null == key) + { + throw new ArgumentNullException("key"); + } + + if (null == componentId) + { + throw new ArgumentNullException("componentId"); + } + + // Escape the leading '#' character for string registry values. + if (escapeLeadingHash && null != value && value.StartsWith("#", StringComparison.Ordinal)) + { + value = String.Concat("#", value); + } + + id = this.CreateIdentifier("reg", componentId, root.ToString(CultureInfo.InvariantCulture.NumberFormat), key.ToLowerInvariant(), (null != name ? name.ToLowerInvariant() : name)); + + var row = this.CreateRow(section, sourceLineNumbers, TupleDefinitionType.Registry, id); + row.Set(1, root); + row.Set(2, key); + row.Set(3, name); + row.Set(4, value); + row.Set(5, componentId); + + return id; + } + + public void CreateSimpleReference(IntermediateSection section, SourceLineNumber sourceLineNumbers, string tableName, params string[] primaryKeys) + { + var joinedKeys = String.Join("/", primaryKeys); + var id = String.Concat(tableName, ":", joinedKeys); + + var wixSimpleReferenceRow = (WixSimpleReferenceTuple)this.CreateRow(section, sourceLineNumbers, TupleDefinitionType.WixSimpleReference); + wixSimpleReferenceRow.Table = tableName; + wixSimpleReferenceRow.PrimaryKeys = joinedKeys; + } + + public void CreateWixGroupRow(IntermediateSection section, SourceLineNumber sourceLineNumbers, ComplexReferenceParentType parentType, string parentId, ComplexReferenceChildType childType, string childId) + { + if (null == parentId || ComplexReferenceParentType.Unknown == parentType) + { + return; + } + + if (null == childId) + { + throw new ArgumentNullException("childId"); + } + + var row = (WixGroupTuple)this.CreateRow(section, sourceLineNumbers, TupleDefinitionType.WixGroup); + row.ParentId = parentId; + row.ParentType = parentType; + row.ChildId = childId; + row.ChildType = childType; + } + + public IntermediateTuple CreateRow(IntermediateSection section, SourceLineNumber sourceLineNumbers, string tableName, Identifier identifier = null) + { + if (this.Creator == null) + { + this.CreateTupleDefinitionCreator(); + } + + if (!this.Creator.TryGetTupleDefinitionByName(tableName, out var tupleDefinition)) + { + throw new ArgumentException(nameof(tableName)); + } + + return CreateRow(section, sourceLineNumbers, tupleDefinition, identifier); + } + + public IntermediateTuple CreateRow(IntermediateSection section, SourceLineNumber sourceLineNumbers, TupleDefinitionType tupleType, Identifier identifier = null) + { + var tupleDefinition = TupleDefinitions.ByType(tupleType); + + return CreateRow(section, sourceLineNumbers, tupleDefinition, identifier); + } + + public string CreateShortName(string longName, bool keepExtension, bool allowWildcards, params string[] args) + { + // canonicalize the long name if its not a localization identifier (they are case-sensitive) + if (!this.IsValidLocIdentifier(longName)) + { + longName = longName.ToLowerInvariant(); + } + + // collect all the data + List strings = new List(1 + args.Length); + strings.Add(longName); + strings.AddRange(args); + + // prepare for hashing + string stringData = String.Join("|", strings); + byte[] data = Encoding.UTF8.GetBytes(stringData); + + // hash the data + byte[] hash; + using (var sha1 = new SHA1CryptoServiceProvider()) + { + hash = sha1.ComputeHash(data); + } + + // generate the short file/directory name without an extension + StringBuilder shortName = new StringBuilder(Convert.ToBase64String(hash)); + shortName.Remove(8, shortName.Length - 8).Replace('+', '-').Replace('/', '_'); + + if (keepExtension) + { + string extension = Path.GetExtension(longName); + + if (4 < extension.Length) + { + extension = extension.Substring(0, 4); + } + + shortName.Append(extension); + + // check the generated short name to ensure its still legal (the extension may not be legal) + if (!this.IsValidShortFilename(shortName.ToString(), allowWildcards)) + { + // remove the extension (by truncating the generated file name back to the generated characters) + shortName.Length -= extension.Length; + } + } + + return shortName.ToString().ToLowerInvariant(); + } + + public void EnsureTable(IntermediateSection section, SourceLineNumber sourceLineNumbers, string tableName) + { + var row = this.CreateRow(section, sourceLineNumbers, TupleDefinitionType.WixEnsureTable); + row.Set(0, tableName); + + if (this.Creator == null) + { + this.CreateTupleDefinitionCreator(); + } + + // We don't add custom table definitions to the tableDefinitions collection, + // so if it's not in there, it better be a custom table. If the Id is just wrong, + // instead of a custom table, we get an unresolved reference at link time. + if (!this.Creator.TryGetTupleDefinitionByName(tableName, out var ignored)) + { + this.CreateSimpleReference(section, sourceLineNumbers, "WixCustomTable", tableName); + } + } + + public string GetAttributeGuidValue(SourceLineNumber sourceLineNumbers, XAttribute attribute, bool generatable = false, bool canBeEmpty = false) + { + if (null == attribute) + { + throw new ArgumentNullException("attribute"); + } + + EmptyRule emptyRule = canBeEmpty ? EmptyRule.CanBeEmpty : EmptyRule.CanBeWhitespaceOnly; + string value = this.GetAttributeValue(sourceLineNumbers, attribute, emptyRule); + + if (String.IsNullOrEmpty(value) && canBeEmpty) + { + return String.Empty; + } + else if (!String.IsNullOrEmpty(value)) + { + // If the value starts and ends with braces or parenthesis, accept that and strip them off. + if ((value.StartsWith("{", StringComparison.Ordinal) && value.EndsWith("}", StringComparison.Ordinal)) + || (value.StartsWith("(", StringComparison.Ordinal) && value.EndsWith(")", StringComparison.Ordinal))) + { + value = value.Substring(1, value.Length - 2); + } + + if (generatable && "*".Equals(value, StringComparison.Ordinal)) + { + return value; + } + + if (ParseHelper.PutGuidHere.IsMatch(value)) + { + Messaging.Instance.OnMessage(WixErrors.ExampleGuid(sourceLineNumbers, attribute.Parent.Name.LocalName, attribute.Name.LocalName, value)); + return CompilerConstants.IllegalGuid; + } + else if (value.StartsWith("!(loc", StringComparison.Ordinal) || value.StartsWith("$(loc", StringComparison.Ordinal) || value.StartsWith("!(wix", StringComparison.Ordinal)) + { + return value; + } + else if (Guid.TryParse(value, out var guid)) + { + var uppercaseGuid = guid.ToString().ToUpperInvariant(); + + // TODO: This used to be a pedantic error, what should it be now? + //if (uppercaseGuid != value) + //{ + // Messaging.Instance.OnMessage(WixErrors.GuidContainsLowercaseLetters(sourceLineNumbers, attribute.Parent.Name.LocalName, attribute.Name.LocalName, value)); + //} + + return String.Concat("{", uppercaseGuid, "}"); + } + else + { + Messaging.Instance.OnMessage(WixErrors.IllegalGuidValue(sourceLineNumbers, attribute.Parent.Name.LocalName, attribute.Name.LocalName, value)); + } + } + + return CompilerConstants.IllegalGuid; + } + + public Identifier GetAttributeIdentifier(SourceLineNumber sourceLineNumbers, XAttribute attribute) + { + var access = AccessModifier.Public; + var value = Common.GetAttributeValue(sourceLineNumbers, attribute, EmptyRule.CanBeEmpty); + + var match = ParseHelper.LegalIdentifierWithAccess.Match(value); + if (!match.Success) + { + return null; + } + else if (match.Groups["access"].Success) + { + access = (AccessModifier)Enum.Parse(typeof(AccessModifier), match.Groups["access"].Value, true); + } + + value = match.Groups["id"].Value; + + if (Common.IsIdentifier(value) && 72 < value.Length) + { + Messaging.Instance.OnMessage(WixWarnings.IdentifierTooLong(sourceLineNumbers, attribute.Parent.Name.LocalName, attribute.Name.LocalName, value)); + } + + return new Identifier(value, access); + } + + public string GetAttributeIdentifierValue(SourceLineNumber sourceLineNumbers, XAttribute attribute) + { + return Common.GetAttributeIdentifierValue(sourceLineNumbers, attribute); + } + + public string[] GetAttributeInlineDirectorySyntax(SourceLineNumber sourceLineNumbers, XAttribute attribute, bool resultUsedToCreateReference = false) + { + string[] result = null; + string value = this.GetAttributeValue(sourceLineNumbers, attribute); + + if (!String.IsNullOrEmpty(value)) + { + int pathStartsAt = 0; + result = value.Split(new char[] { '\\' }, StringSplitOptions.RemoveEmptyEntries); + if (result[0].EndsWith(":", StringComparison.Ordinal)) + { + string id = result[0].TrimEnd(':'); + if (1 == result.Length) + { + Messaging.Instance.OnMessage(WixErrors.InlineDirectorySyntaxRequiresPath(sourceLineNumbers, attribute.Parent.Name.LocalName, attribute.Name.LocalName, value, id)); + return null; + } + else if (!this.IsValidIdentifier(id)) + { + Messaging.Instance.OnMessage(WixErrors.IllegalIdentifier(sourceLineNumbers, attribute.Parent.Name.LocalName, attribute.Name.LocalName, value, id)); + return null; + } + + pathStartsAt = 1; + } + else if (resultUsedToCreateReference && 1 == result.Length) + { + if (value.EndsWith("\\")) + { + if (!this.IsValidLongFilename(result[0], false, false)) + { + Messaging.Instance.OnMessage(WixErrors.IllegalLongFilename(sourceLineNumbers, attribute.Parent.Name.LocalName, attribute.Name.LocalName, value, result[0])); + return null; + } + } + else if (!this.IsValidIdentifier(result[0])) + { + Messaging.Instance.OnMessage(WixErrors.IllegalIdentifier(sourceLineNumbers, attribute.Parent.Name.LocalName, attribute.Name.LocalName, value, result[0])); + return null; + } + + return result; // return early to avoid additional checks below. + } + + // Check each part of the relative path to ensure that it is a valid directory name. + for (int i = pathStartsAt; i < result.Length; ++i) + { + if (!this.IsValidLongFilename(result[i], false, false)) + { + Messaging.Instance.OnMessage(WixErrors.IllegalLongFilename(sourceLineNumbers, attribute.Parent.Name.LocalName, attribute.Name.LocalName, value, result[i])); + return null; + } + } + + if (1 < result.Length && !value.EndsWith("\\")) + { + Messaging.Instance.OnMessage(WixWarnings.BackslashTerminateInlineDirectorySyntax(sourceLineNumbers, attribute.Parent.Name.LocalName, attribute.Name.LocalName, value)); + } + } + + return result; + } + + public int GetAttributeIntegerValue(SourceLineNumber sourceLineNumbers, XAttribute attribute, int minimum, int maximum) + { + return Common.GetAttributeIntegerValue(sourceLineNumbers, attribute, minimum, maximum); + } + + public string GetAttributeLongFilename(SourceLineNumber sourceLineNumbers, XAttribute attribute, bool allowWildcards, bool allowRelative) + { + if (null == attribute) + { + throw new ArgumentNullException("attribute"); + } + + string value = this.GetAttributeValue(sourceLineNumbers, attribute); + + if (0 < value.Length) + { + if (!this.IsValidLongFilename(value, allowWildcards, allowRelative) && !this.IsValidLocIdentifier(value)) + { + if (allowRelative) + { + Messaging.Instance.OnMessage(WixErrors.IllegalRelativeLongFilename(sourceLineNumbers, attribute.Parent.Name.LocalName, attribute.Name.LocalName, value)); + } + else + { + Messaging.Instance.OnMessage(WixErrors.IllegalLongFilename(sourceLineNumbers, attribute.Parent.Name.LocalName, attribute.Name.LocalName, value)); + } + } + else if (allowRelative) + { + string normalizedPath = value.Replace('\\', '/'); + if (normalizedPath.StartsWith("../", StringComparison.Ordinal) || normalizedPath.Contains("/../")) + { + Messaging.Instance.OnMessage(WixErrors.PayloadMustBeRelativeToCache(sourceLineNumbers, attribute.Parent.Name.LocalName, attribute.Name.LocalName, value)); + } + } + else if (CompilerCore.IsAmbiguousFilename(value)) + { + Messaging.Instance.OnMessage(WixWarnings.AmbiguousFileOrDirectoryName(sourceLineNumbers, attribute.Parent.Name.LocalName, attribute.Name.LocalName, value)); + } + } + + return value; + } + + public long GetAttributeLongValue(SourceLineNumber sourceLineNumbers, XAttribute attribute, long minimum, long maximum) + { + Debug.Assert(minimum > CompilerConstants.LongNotSet && minimum > CompilerConstants.IllegalLong, "The legal values for this attribute collide with at least one sentinel used during parsing."); + + string value = this.GetAttributeValue(sourceLineNumbers, attribute); + + if (0 < value.Length) + { + try + { + long longValue = Convert.ToInt64(value, CultureInfo.InvariantCulture.NumberFormat); + + if (CompilerConstants.LongNotSet == longValue || CompilerConstants.IllegalLong == longValue) + { + Messaging.Instance.OnMessage(WixErrors.IntegralValueSentinelCollision(sourceLineNumbers, longValue)); + } + else if (minimum > longValue || maximum < longValue) + { + Messaging.Instance.OnMessage(WixErrors.IntegralValueOutOfRange(sourceLineNumbers, attribute.Parent.Name.LocalName, attribute.Name.LocalName, longValue, minimum, maximum)); + longValue = CompilerConstants.IllegalLong; + } + + return longValue; + } + catch (FormatException) + { + Messaging.Instance.OnMessage(WixErrors.IllegalLongValue(sourceLineNumbers, attribute.Parent.Name.LocalName, attribute.Name.LocalName, value)); + } + catch (OverflowException) + { + Messaging.Instance.OnMessage(WixErrors.IllegalLongValue(sourceLineNumbers, attribute.Parent.Name.LocalName, attribute.Name.LocalName, value)); + } + } + + return CompilerConstants.IllegalLong; + } + + public string GetAttributeValue(SourceLineNumber sourceLineNumbers, XAttribute attribute, EmptyRule emptyRule = EmptyRule.CanBeWhitespaceOnly) + { + return Common.GetAttributeValue(sourceLineNumbers, attribute, emptyRule); + } + + public string GetAttributeVersionValue(SourceLineNumber sourceLineNumbers, XAttribute attribute) + { + var value = this.GetAttributeValue(sourceLineNumbers, attribute); + + if (!String.IsNullOrEmpty(value)) + { + if (Version.TryParse(value, out var version)) + { + return version.ToString(); + } + + // Allow versions to contain binder variables. + if (Common.ContainsValidBinderVariable(value)) + { + return value; + } + + Messaging.Instance.OnMessage(WixErrors.IllegalVersionValue(sourceLineNumbers, attribute.Parent.Name.LocalName, attribute.Name.LocalName, value)); + } + + return null; + } + + public YesNoDefaultType GetAttributeYesNoDefaultValue(SourceLineNumber sourceLineNumbers, XAttribute attribute) + { + var value = this.GetAttributeValue(sourceLineNumbers, attribute); + + switch (value) + { + case "yes": + case "true": + return YesNoDefaultType.Yes; + + case "no": + case "false": + return YesNoDefaultType.No; + + case "default": + return YesNoDefaultType.Default; + + default: + Messaging.Instance.OnMessage(WixErrors.IllegalYesNoDefaultValue(sourceLineNumbers, attribute.Parent.Name.LocalName, attribute.Name.LocalName, value)); + return YesNoDefaultType.IllegalValue; + } + } + + public YesNoType GetAttributeYesNoValue(SourceLineNumber sourceLineNumbers, XAttribute attribute) + { + var value = this.GetAttributeValue(sourceLineNumbers, attribute); + + switch (value) + { + case "yes": + case "true": + return YesNoType.Yes; + + case "no": + case "false": + return YesNoType.No; + + default: + Messaging.Instance.OnMessage(WixErrors.IllegalYesNoValue(sourceLineNumbers, attribute.Parent.Name.LocalName, attribute.Name.LocalName, value)); + return YesNoType.IllegalValue; + } + } + + public string GetConditionInnerText(XElement element) + { + var value = Common.GetInnerText(element)?.Trim().Replace('\t', ' ').Replace('\r', ' ').Replace('\n', ' '); + + // Return null for a non-existant condition. + return String.IsNullOrEmpty(value) ? null : value; + } + + public string GetTrimmedInnerText(XElement element) + { + var value = Common.GetInnerText(element); + return value?.Trim(); + } + + public bool IsValidIdentifier(string value) + { + return Common.IsIdentifier(value); + } + + public bool IsValidLocIdentifier(string identifier) + { + if (String.IsNullOrEmpty(identifier)) + { + return false; + } + + var match = Common.WixVariableRegex.Match(identifier); + + return (match.Success && "loc" == match.Groups["namespace"].Value && 0 == match.Index && identifier.Length == match.Length); + } + + public bool IsValidLongFilename(string filename, bool allowWildcards, bool allowRelative) + { + if (String.IsNullOrEmpty(filename)) + { + return false; + } + + // Check for a non-period character (all periods is not legal) + bool nonPeriodFound = false; + foreach (char character in filename) + { + if ('.' != character) + { + nonPeriodFound = true; + break; + } + } + + if (allowWildcards) + { + return (nonPeriodFound && ParseHelper.LegalWildcardLongFilename.IsMatch(filename)); + } + else if (allowRelative) + { + return (nonPeriodFound && ParseHelper.LegalRelativeLongFilename.IsMatch(filename)); + } + else + { + return (nonPeriodFound && ParseHelper.LegalLongFilename.IsMatch(filename)); + } + } + + public bool IsValidShortFilename(string filename, bool allowWildcards = false) + { + return Common.IsValidShortFilename(filename, allowWildcards); + } + + public void ParseExtensionAttribute(IEnumerable extensions, Intermediate intermediate, IntermediateSection section, XElement element, XAttribute attribute, IDictionary context = null) + { + // Ignore attributes defined by the W3C because we'll assume they are always right. + if ((String.IsNullOrEmpty(attribute.Name.NamespaceName) && attribute.Name.LocalName.Equals("xmlns", StringComparison.Ordinal)) || + attribute.Name.NamespaceName.StartsWith(CompilerCore.W3SchemaPrefix.NamespaceName, StringComparison.Ordinal)) + { + return; + } + + if (ParseHelper.TryFindExtension(extensions, attribute.Name.NamespaceName, out var extension)) + { + extension.ParseAttribute(intermediate, section, element, attribute, context); + } + else + { + var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(element); + Messaging.Instance.OnMessage(WixErrors.UnhandledExtensionAttribute(sourceLineNumbers, element.Name.LocalName, attribute.Name.LocalName, attribute.Name.NamespaceName)); + } + } + + public void ParseExtensionElement(IEnumerable extensions, Intermediate intermediate, IntermediateSection section, XElement parentElement, XElement element, IDictionary context = null) + { + if (ParseHelper.TryFindExtension(extensions, element.Name.Namespace, out var extension)) + { + SourceLineNumber sourceLineNumbers = Preprocessor.GetSourceLineNumbers(parentElement); + extension.ParseElement(intermediate, section, parentElement, element, context); + } + else + { + var childSourceLineNumbers = Preprocessor.GetSourceLineNumbers(element); + Messaging.Instance.OnMessage(WixErrors.UnhandledExtensionElement(childSourceLineNumbers, parentElement.Name.LocalName, element.Name.LocalName, element.Name.NamespaceName)); + } + } + + public ComponentKeyPath ParsePossibleKeyPathExtensionElement(IEnumerable extensions, Intermediate intermediate, IntermediateSection section, XElement parentElement, XElement element, IDictionary context) + { + ComponentKeyPath keyPath = null; + + if (ParseHelper.TryFindExtension(extensions, element.Name.Namespace, out var extension)) + { + keyPath = extension.ParsePossibleKeyPathElement(intermediate, section, parentElement, element, context); + } + else + { + var childSourceLineNumbers = Preprocessor.GetSourceLineNumbers(element); + Messaging.Instance.OnMessage(WixErrors.UnhandledExtensionElement(childSourceLineNumbers, parentElement.Name.LocalName, element.Name.LocalName, element.Name.NamespaceName)); + } + + return keyPath; + } + + public void ParseForExtensionElements(IEnumerable extensions, Intermediate intermediate, IntermediateSection section, XElement element) + { + foreach (XElement child in element.Elements()) + { + if (element.Name.Namespace == child.Name.Namespace) + { + this.UnexpectedElement(element, child); + } + else + { + this.ParseExtensionElement(extensions, intermediate, section, element, child); + } + } + } + + public void UnexpectedAttribute(XElement element, XAttribute attribute) + { + var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(element); + Common.UnexpectedAttribute(sourceLineNumbers, attribute); + } + + public void UnexpectedElement(XElement parentElement, XElement childElement) + { + var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(childElement); + Messaging.Instance.OnMessage(WixErrors.UnexpectedElement(sourceLineNumbers, parentElement.Name.LocalName, childElement.Name.LocalName)); + } + + private void CreateTupleDefinitionCreator() + { + this.Creator = (ITupleDefinitionCreator)this.ServiceProvider.GetService(typeof(ITupleDefinitionCreator)); + } + + private static IntermediateTuple CreateRow(IntermediateSection section, SourceLineNumber sourceLineNumbers, IntermediateTupleDefinition tupleDefinition, Identifier identifier) + { + var row = tupleDefinition.CreateTuple(sourceLineNumbers, identifier); + + if (null != identifier) + { + if (row.Definition.FieldDefinitions[0].Type == IntermediateFieldType.Number) + { + row.Set(0, Convert.ToInt32(identifier.Id)); + } + else + { + row.Set(0, identifier.Id); + } + } + + section.Tuples.Add(row); + + return row; + } + + private static bool TryFindExtension(IEnumerable extensions, XNamespace ns, out ICompilerExtension extension) + { + extension = null; + + foreach (var ext in extensions) + { + if (ext.Namespace == ns) + { + extension = ext; + break; + } + } + + return extension != null; + } + } +} diff --git a/src/WixToolset.Core/ExtensibilityServices/TupleDefinitionCreator.cs b/src/WixToolset.Core/ExtensibilityServices/TupleDefinitionCreator.cs new file mode 100644 index 00000000..4075def8 --- /dev/null +++ b/src/WixToolset.Core/ExtensibilityServices/TupleDefinitionCreator.cs @@ -0,0 +1,52 @@ +// 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. + +namespace WixToolset.Core.ExtensibilityServices +{ + using System; + using System.Collections.Generic; + using WixToolset.Data; + using WixToolset.Extensibility; + using WixToolset.Extensibility.Services; + + internal class TupleDefinitionCreator : ITupleDefinitionCreator + { + public TupleDefinitionCreator(IServiceProvider serviceProvider) + { + this.ServiceProvider = serviceProvider; + } + + private IServiceProvider ServiceProvider { get; } + + private IEnumerable ExtensionData { get; set; } + + public bool TryGetTupleDefinitionByName(string name, out IntermediateTupleDefinition tupleDefinition) + { + tupleDefinition = TupleDefinitions.ByName(name); + + if (tupleDefinition == null) + { + if (this.ExtensionData == null) + { + this.LoadExtensionData(); + } + + foreach (var data in this.ExtensionData) + { + if (data.TryGetTupleDefinitionByName(name, out tupleDefinition)) + { + break; + } + } + } + + return tupleDefinition != null; + } + + private void LoadExtensionData() + { + var extensionManager = (IExtensionManager)this.ServiceProvider.GetService(typeof(IExtensionManager)); + + this.ExtensionData = extensionManager.Create(); + } + } +} diff --git a/src/WixToolset.Core/HarvesterCore.cs b/src/WixToolset.Core/HarvesterCore.cs index 66a693f2..87e3c33f 100644 --- a/src/WixToolset.Core/HarvesterCore.cs +++ b/src/WixToolset.Core/HarvesterCore.cs @@ -1,12 +1,11 @@ // 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. -namespace WixToolset +namespace WixToolset.Core { using System; using System.Diagnostics.CodeAnalysis; using System.IO; using WixToolset.Data; - using Wix = WixToolset.Data.Serialize; /// /// The WiX Toolset harvester core. diff --git a/src/WixToolset.Core/HeatCore.cs b/src/WixToolset.Core/HeatCore.cs index 01233c40..2384a1ed 100644 --- a/src/WixToolset.Core/HeatCore.cs +++ b/src/WixToolset.Core/HeatCore.cs @@ -1,9 +1,9 @@ // 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. -namespace WixToolset.Tools +namespace WixToolset.Core { + using WixToolset.Core.Extensibility; using WixToolset.Data; - using WixToolset.Extensibility; /// /// The WiX Toolset Harvester application core. diff --git a/src/WixToolset.Core/Link/WixGroupingOrdering.cs b/src/WixToolset.Core/Link/WixGroupingOrdering.cs index 4dd1596c..f15ebc75 100644 --- a/src/WixToolset.Core/Link/WixGroupingOrdering.cs +++ b/src/WixToolset.Core/Link/WixGroupingOrdering.cs @@ -1,16 +1,14 @@ // 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. -namespace WixToolset.Link +namespace WixToolset.Core.Link { using System; - using System.Collections; using System.Collections.ObjectModel; using System.Collections.Generic; using System.Diagnostics; using System.Globalization; using System.Linq; using System.Text; - using WixToolset.Extensibility; using WixToolset.Data; using WixToolset.Data.Tuples; diff --git a/src/WixToolset.Core/Localizer.cs b/src/WixToolset.Core/Localizer.cs index a19c32fb..8265c026 100644 --- a/src/WixToolset.Core/Localizer.cs +++ b/src/WixToolset.Core/Localizer.cs @@ -1,6 +1,6 @@ // 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. -namespace WixToolset +namespace WixToolset.Core { using System; using System.Collections.Generic; diff --git a/src/WixToolset.Core/Mutator.cs b/src/WixToolset.Core/Mutator.cs index d37815f4..f935b570 100644 --- a/src/WixToolset.Core/Mutator.cs +++ b/src/WixToolset.Core/Mutator.cs @@ -1,10 +1,10 @@ // 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. -namespace WixToolset +namespace WixToolset.Core { using System; using System.Collections; - using WixToolset.Extensibility; + using WixToolset.Core.Extensibility; using Wix = WixToolset.Data.Serialize; /// diff --git a/src/WixToolset.Core/Preprocessor.cs b/src/WixToolset.Core/Preprocessor.cs index 85b3dab8..23f568a8 100644 --- a/src/WixToolset.Core/Preprocessor.cs +++ b/src/WixToolset.Core/Preprocessor.cs @@ -1,9 +1,8 @@ // 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. -namespace WixToolset +namespace WixToolset.Core { using System; - using System.Collections; using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using System.Globalization; diff --git a/src/WixToolset.Core/TupleDefinitionCreator.cs b/src/WixToolset.Core/TupleDefinitionCreator.cs deleted file mode 100644 index 8c9b9d29..00000000 --- a/src/WixToolset.Core/TupleDefinitionCreator.cs +++ /dev/null @@ -1,52 +0,0 @@ -// 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. - -namespace WixToolset.Core -{ - using System; - using System.Collections.Generic; - using WixToolset.Data; - using WixToolset.Extensibility; - using WixToolset.Extensibility.Services; - - internal class TupleDefinitionCreator : ITupleDefinitionCreator - { - public TupleDefinitionCreator(IServiceProvider serviceProvider) - { - this.ServiceProvider = serviceProvider; - } - - private IServiceProvider ServiceProvider { get; } - - private IEnumerable ExtensionData { get; set; } - - public bool TryGetTupleDefinitionByName(string name, out IntermediateTupleDefinition tupleDefinition) - { - tupleDefinition = TupleDefinitions.ByName(name); - - if (tupleDefinition == null) - { - if (this.ExtensionData == null) - { - this.LoadExtensionData(); - } - - foreach (var data in this.ExtensionData) - { - if (data.TryGetTupleDefinitionByName(name, out tupleDefinition)) - { - break; - } - } - } - - return tupleDefinition != null; - } - - private void LoadExtensionData() - { - var extensionManager = (IExtensionManager)this.ServiceProvider.GetService(typeof(IExtensionManager)); - - this.ExtensionData = extensionManager.Create(); - } - } -} diff --git a/src/WixToolset.Core/WixToolsetServiceProvider.cs b/src/WixToolset.Core/WixToolsetServiceProvider.cs index dd49e7ed..8693461b 100644 --- a/src/WixToolset.Core/WixToolsetServiceProvider.cs +++ b/src/WixToolset.Core/WixToolsetServiceProvider.cs @@ -3,6 +3,7 @@ namespace WixToolset.Core { using System; + using WixToolset.Core.ExtensibilityServices; using WixToolset.Data; using WixToolset.Extensibility; using WixToolset.Extensibility.Services; @@ -10,6 +11,7 @@ namespace WixToolset.Core public class WixToolsetServiceProvider : IServiceProvider { private ExtensionManager extensionManager; + private ParseHelper parseHelper; private TupleDefinitionCreator tupleDefinitionCreator; public object GetService(Type serviceType) @@ -58,6 +60,11 @@ namespace WixToolset.Core return this.tupleDefinitionCreator = this.tupleDefinitionCreator ?? new TupleDefinitionCreator(this); } + if (serviceType == typeof(IParseHelper)) + { + return this.parseHelper = this.parseHelper ?? new ParseHelper(this); + } + throw new ArgumentException($"Unknown service type: {serviceType.Name}", nameof(serviceType)); } } diff --git a/src/WixToolset.Data.WindowsInstaller/Output.cs b/src/WixToolset.Data.WindowsInstaller/Output.cs index 71faeac7..26772872 100644 --- a/src/WixToolset.Data.WindowsInstaller/Output.cs +++ b/src/WixToolset.Data.WindowsInstaller/Output.cs @@ -186,7 +186,6 @@ namespace WixToolset.Data bool empty = reader.IsEmptyElement; Output output = new Output(SourceLineNumber.CreateFromUri(reader.BaseURI)); - SectionType sectionType = SectionType.Unknown; Version version = null; while (reader.MoveToNextAttribute()) @@ -201,22 +200,18 @@ namespace WixToolset.Data { case "Bundle": output.Type = OutputType.Bundle; - sectionType = SectionType.Bundle; break; case "Module": output.Type = OutputType.Module; - sectionType = SectionType.Module; break; case "Patch": output.Type = OutputType.Patch; break; case "PatchCreation": output.Type = OutputType.PatchCreation; - sectionType = SectionType.PatchCreation; break; case "Product": output.Type = OutputType.Product; - sectionType = SectionType.Product; break; case "Transform": output.Type = OutputType.Transform; -- cgit v1.2.3-55-g6feb