// 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();
}
}
}
}