// 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.Extensions
{
using System;
using System.Collections.ObjectModel;
using System.Globalization;
using WixToolset.Data;
using WixToolset.Extensibility;
///
/// The compiler for the WiX toolset dependency extension.
///
public sealed class DependencyBinder : BinderExtension
{
private Output output;
///
/// Called after all output changes occur and right before the output is bound into its final format.
///
public override void Finish(Output output)
{
// Only process MSI packages.
if (OutputType.Product != output.Type)
{
return;
}
this.output = output;
Table wixDependencyTable = output.Tables["WixDependency"];
Table wixDependencyProviderTable = output.Tables["WixDependencyProvider"];
Table wixDependencyRefTable = output.Tables["WixDependencyRef"];
// Make sure there's something to do.
if (null != wixDependencyRefTable)
{
KeyedRowCollection wixDependencyRows = new KeyedRowCollection(wixDependencyTable);
KeyedRowCollection wixDependencyProviderRows = new KeyedRowCollection(wixDependencyProviderTable);
// For each relationship, get the provides and requires rows to generate registry values.
foreach (Row wixDependencyRefRow in wixDependencyRefTable.Rows)
{
string providesId = (string)wixDependencyRefRow[0];
string requiresId = (string)wixDependencyRefRow[1];
Row wixDependencyRow = null;
if (wixDependencyRows.Contains(requiresId))
{
wixDependencyRow = wixDependencyRows[requiresId];
}
Row wixDependencyProviderRow = null;
if (wixDependencyProviderRows.Contains(providesId))
{
wixDependencyProviderRow = wixDependencyProviderRows[providesId];
}
// If we found both rows, generate the registry values.
if (null != wixDependencyRow && null != wixDependencyProviderRow)
{
// Format the root registry key using the required provider key and the current provider key.
string requiresKey = (string)wixDependencyRow[1];
string providesKey = (string)wixDependencyProviderRow[2];
string keyRequires = String.Format(@"{0}{1}\{2}\{3}", DependencyCommon.RegistryRoot, requiresKey, DependencyCommon.RegistryDependents, providesKey);
// Get the component ID from the provider.
string componentId = (string)wixDependencyProviderRow[1];
Row row = this.CreateRegistryRow(wixDependencyRow);
row[0] = this.Core.CreateIdentifier("reg", providesId, requiresId, "(Default)");
row[1] = -1;
row[2] = keyRequires;
row[3] = "*";
row[4] = null;
row[5] = componentId;
string minVersion = (string)wixDependencyRow[2];
if (!String.IsNullOrEmpty(minVersion))
{
row = this.CreateRegistryRow(wixDependencyRow);
row[0] = this.Core.CreateIdentifier("reg", providesId, requiresId, "MinVersion");
row[1] = -1;
row[2] = keyRequires;
row[3] = "MinVersion";
row[4] = minVersion;
row[5] = componentId;
}
string maxVersion = (string)wixDependencyRow[3];
if (!String.IsNullOrEmpty(minVersion))
{
row = this.CreateRegistryRow(wixDependencyRow);
row[0] = this.Core.CreateIdentifier("reg", providesId, requiresId, "MaxVersion");
row[1] = -1;
row[2] = keyRequires;
row[3] = "MaxVersion";
row[4] = maxVersion;
row[5] = componentId;
}
if (null != wixDependencyRow[4])
{
int attributes = (int)wixDependencyRow[4];
row = this.CreateRegistryRow(wixDependencyRow);
row[0] = this.Core.CreateIdentifier("reg", providesId, requiresId, "Attributes");
row[1] = -1;
row[2] = keyRequires;
row[3] = "Attributes";
row[4] = String.Concat("#", attributes.ToString(CultureInfo.InvariantCulture.NumberFormat));
row[5] = componentId;
}
}
}
}
}
///
/// Creates a registry row using source information from the given .
///
/// The from which the section and source line information are retrieved.
/// A new Registry row.
private Row CreateRegistryRow(Row referenceRow)
{
TableDefinition tableDefinition = this.Core.TableDefinitions["Registry"];
// Create the row from the main tables, which were populated during link anyway.
// We still associate the table with the dependency row's section to maintain servicing.
Table table = this.output.EnsureTable(tableDefinition, referenceRow.Table.Section);
Row row = table.CreateRow(referenceRow.SourceLineNumbers);
// Set the section ID for patching and return the new row.
row.SectionId = referenceRow.SectionId;
return row;
}
///
/// A keyed collection of instances for O(1) lookup.
///
private sealed class KeyedRowCollection : KeyedCollection
{
///
/// Initializes the class with all rows from the specified .
///
/// The containing rows to index.
internal KeyedRowCollection(Table table)
{
if (null != table)
{
foreach (Row row in table.Rows)
{
this.Add(row);
}
}
}
///
/// Gets the primary key for the .
///
/// The to index.
/// The primary key for the .
protected override string GetKeyForItem(Row row)
{
return row.GetPrimaryKey('/');
}
}
}
}