// 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.Data.WindowsInstaller
{
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Xml;
///
/// Output is generated by the linker.
///
public sealed class WindowsInstallerData
{
internal const string XmlNamespaceUri = "http://wixtoolset.org/schemas/v4/windowsinstallerdata";
internal const string XmlElementName = "windowsInstallerData";
private static readonly Version CurrentVersion = new Version("4.0.0.0");
private const string WixOutputStreamName = "wix-wid.xml";
///
/// Creates a new empty output object.
///
/// The source line information for the output.
public WindowsInstallerData(SourceLineNumber sourceLineNumbers)
{
this.SourceLineNumbers = sourceLineNumbers;
this.SubStorages = new List();
this.Tables = new TableIndexedCollection();
}
///
/// Gets the type of the output.
///
/// Type of the output.
public OutputType Type { get; set; }
///
/// Gets or sets the codepage for this output.
///
/// Codepage of the output.
public int Codepage { get; set; }
///
/// Gets the source line information for this output.
///
/// The source line information for this output.
public SourceLineNumber SourceLineNumbers { get; private set; }
///
/// Gets the substorages in this output.
///
/// The substorages in this output.
public ICollection SubStorages { get; private set; }
///
/// Gets the tables contained in this output.
///
/// Collection of tables.
public TableIndexedCollection Tables { get; private set; }
///
/// Ensure this output contains a particular table.
///
/// Definition of the table that should exist.
/// Optional section to use for the table. If one is not provided, the entry section will be used.
/// The table in this output.
public Table EnsureTable(TableDefinition tableDefinition)
{
if (!this.Tables.TryGetTable(tableDefinition.Name, out var table))
{
table = new Table(tableDefinition);
this.Tables.Add(table);
}
return table;
}
///
/// Saves an output to a WixOutput container.
///
/// Container to save to.
public void Save(WixOutput wixout)
{
using (var writer = XmlWriter.Create(wixout.CreateDataStream(WixOutputStreamName)))
{
writer.WriteStartDocument();
this.Write(writer);
writer.WriteEndDocument();
}
}
///
/// Gets table by name.
///
public bool TryGetTable(string tableName, out Table table) => this.Tables.TryGetTable(tableName, out table);
///
/// Loads an output from a path on disk.
///
/// Path to output file saved on disk.
/// Suppresses wix.dll version mismatch check.
/// Output object.
public static WindowsInstallerData Load(string path, bool suppressVersionCheck = false)
{
using (var wixOutput = WixOutput.Read(path))
{
return WindowsInstallerData.Load(wixOutput, suppressVersionCheck);
}
}
///
/// Loads an output from a WixOutput object.
///
/// WixOutput object.
/// Suppresses wix.dll version mismatch check.
/// Output object.
public static WindowsInstallerData Load(WixOutput wixOutput, bool suppressVersionCheck = false)
{
using (var stream = wixOutput.GetDataStream(WixOutputStreamName))
using (var reader = XmlReader.Create(stream, null, wixOutput.Uri.AbsoluteUri))
{
try
{
reader.MoveToContent();
return WindowsInstallerData.Read(reader, suppressVersionCheck);
}
catch (XmlException xe)
{
throw new WixCorruptFileException(wixOutput.Uri.AbsoluteUri, "wixout", xe);
}
}
}
///
/// Processes an XmlReader and builds up the output object.
///
/// Reader to get data from.
/// Suppresses wix.dll version mismatch check.
/// The Output represented by the Xml.
internal static WindowsInstallerData Read(XmlReader reader, bool suppressVersionCheck)
{
if (!reader.LocalName.Equals(WindowsInstallerData.XmlElementName))
{
throw new XmlException();
}
var empty = reader.IsEmptyElement;
var output = new WindowsInstallerData(SourceLineNumber.CreateFromUri(reader.BaseURI));
Version version = null;
while (reader.MoveToNextAttribute())
{
switch (reader.LocalName)
{
case "codepage":
output.Codepage = Convert.ToInt32(reader.Value, CultureInfo.InvariantCulture.NumberFormat);
break;
case "type":
switch (reader.Value)
{
case "Bundle":
output.Type = OutputType.Bundle;
break;
case "Module":
output.Type = OutputType.Module;
break;
case "Patch":
output.Type = OutputType.Patch;
break;
case "PatchCreation":
output.Type = OutputType.PatchCreation;
break;
case "Product":
output.Type = OutputType.Product;
break;
case "Transform":
output.Type = OutputType.Transform;
break;
default:
throw new XmlException();
}
break;
case "version":
version = new Version(reader.Value);
break;
}
}
if (!suppressVersionCheck && null != version && !WindowsInstallerData.CurrentVersion.Equals(version))
{
throw new WixException(ErrorMessages.VersionMismatch(SourceLineNumber.CreateFromUri(reader.BaseURI), WindowsInstallerData.XmlElementName, version.ToString(), WindowsInstallerData.CurrentVersion.ToString()));
}
// loop through the rest of the xml building up the Output object
TableDefinitionCollection tableDefinitions = null;
var tables = new List();
if (!empty)
{
var done = false;
// loop through all the fields in a row
while (!done && reader.Read())
{
switch (reader.NodeType)
{
case XmlNodeType.Element:
switch (reader.LocalName)
{
case "subStorage":
output.SubStorages.Add(SubStorage.Read(reader));
break;
case "table":
if (null == tableDefinitions)
{
throw new XmlException();
}
tables.Add(Table.Read(reader, tableDefinitions));
break;
case "tableDefinitions":
tableDefinitions = TableDefinitionCollection.Read(reader);
break;
default:
throw new XmlException();
}
break;
case XmlNodeType.EndElement:
done = true;
break;
}
}
if (!done)
{
throw new XmlException();
}
}
output.Tables = new TableIndexedCollection(tables);
return output;
}
///
/// Persists an output in an XML format.
///
/// XmlWriter where the Output should persist itself as XML.
internal void Write(XmlWriter writer)
{
writer.WriteStartElement(WindowsInstallerData.XmlElementName, XmlNamespaceUri);
writer.WriteAttributeString("type", this.Type.ToString());
if (0 != this.Codepage)
{
writer.WriteAttributeString("codepage", this.Codepage.ToString(CultureInfo.InvariantCulture));
}
writer.WriteAttributeString("version", WindowsInstallerData.CurrentVersion.ToString());
// Collect all the table definitions and write them.
var tableDefinitions = new TableDefinitionCollection();
foreach (var table in this.Tables)
{
tableDefinitions.Add(table.Definition);
}
tableDefinitions.Write(writer);
foreach (var table in this.Tables.OrderBy(t => t.Name))
{
table.Write(writer);
}
foreach (var subStorage in this.SubStorages)
{
subStorage.Write(writer);
}
writer.WriteEndElement();
}
}
}