// 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.Harvesters.Extensibility { using System; using System.Collections.Generic; using System.Text; using Wix = WixToolset.Harvesters.Serialize; /// /// The base mutator extension. Any of these methods can be overridden to change /// the behavior of the mutator. /// public abstract class BaseMutatorExtension : IMutatorExtension { /// /// Gets or sets the mutator core for the extension. /// /// The mutator core for the extension. public IHarvesterCore Core { get; set; } /// /// Gets the sequence of the extension. /// /// The sequence of the extension. public abstract int Sequence { get; } /// /// Mutate a WiX document. /// /// The Wix document element. public virtual void Mutate(Wix.Wix wix) { } /// /// Mutate a WiX document as a string. /// /// The Wix document element as a string. /// The mutated Wix document as a string. public virtual string Mutate(string wixString) { return wixString; } /// /// Generate unique MSI identifiers. /// protected class IdentifierGenerator { /// /// /// public const int MaxProductIdentifierLength = 72; /// /// /// public const int MaxModuleIdentifierLength = 35; private string baseName; private int maxLength; private Dictionary existingIdentifiers; private Dictionary possibleIdentifiers; private IHarvesterCore harvesterCore; /// /// Instantiate a new IdentifierGenerator. /// /// The base resource name to use if a resource name contains no usable characters. /// public IdentifierGenerator(string baseName, IHarvesterCore harvesterCore) { this.baseName = baseName; this.maxLength = IdentifierGenerator.MaxProductIdentifierLength; this.existingIdentifiers = new Dictionary(StringComparer.OrdinalIgnoreCase); this.possibleIdentifiers = new Dictionary(StringComparer.OrdinalIgnoreCase); this.harvesterCore = harvesterCore; } /// /// Gets or sets the maximum length for generated identifiers. /// /// Maximum length for generated identifiers. (Default is 72.) public int MaxIdentifierLength { get { return this.maxLength; } set { this.maxLength = value; } } /// /// Index an existing identifier for collision detection. /// /// The identifier. public void IndexExistingIdentifier(string identifier) { if (null == identifier) { throw new ArgumentNullException("identifier"); } this.existingIdentifiers[identifier] = null; } /// /// Index a resource name for collision detection. /// /// The resource name. public void IndexName(string name) { if (null == name) { throw new ArgumentNullException("name"); } string identifier = this.CreateIdentifier(name, 0); if (this.possibleIdentifiers.ContainsKey(identifier)) { this.possibleIdentifiers[identifier] = String.Empty; } else { this.possibleIdentifiers.Add(identifier, null); } } /// /// Get the identifier for the given resource name. /// /// The resource name. /// A legal MSI identifier. public string GetIdentifier(string name) { if (null == name) { throw new ArgumentNullException("name"); } for (int i = 0; i <= Int32.MaxValue; i++) { string identifier = this.CreateIdentifier(name, i); if (this.existingIdentifiers.ContainsKey(identifier) || // already used (0 == i && 0 != this.possibleIdentifiers.Count && null != this.possibleIdentifiers[identifier]) || // needs an index because its duplicated (0 != i && this.possibleIdentifiers.ContainsKey(identifier))) // collides with another possible identifier { continue; } else // use this identifier { this.existingIdentifiers.Add(identifier, null); return identifier; } } throw new InvalidOperationException("Could not find a unique identifier for the given resource name."); } /// /// Create a legal MSI identifier from a resource name and an index. /// /// The name of the resource for which an identifier should be created. /// An index to append to the end of the identifier to make it unique. /// A legal MSI identifier. public string CreateIdentifier(string name, int index) { if (null == name) { throw new ArgumentNullException("name"); } StringBuilder identifier = new StringBuilder(); // Convert the name to a standard MSI identifier identifier.Append(this.harvesterCore.CreateIdentifierFromFilename(name)); // no legal identifier characters were found, use the base id instead if (0 == identifier.Length) { identifier.Append(this.baseName); } // truncate the identifier if it's too long (reserve 3 characters for up to 99 collisions) int adjustedMaxLength = this.MaxIdentifierLength - (index != 0 ? 3 : 0); if (adjustedMaxLength < identifier.Length) { identifier.Length = adjustedMaxLength; } // if the index is not zero, then append it to the identifier name if (0 != index) { identifier.AppendFormat("_{0}", index); } return identifier.ToString(); } } } }