// 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.IO;
using System.Linq;
using System.Xml;
///
/// Output is generated by the linker.
///
public sealed class Output
{
public const string XmlNamespaceUri = "http://wixtoolset.org/schemas/v4/wixout";
private static readonly Version CurrentVersion = new Version("4.0.0.0");
///
/// Creates a new empty output object.
///
/// The source line information for the output.
public Output(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; }
///
/// 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 Output Load(string path, bool suppressVersionCheck)
{
using (FileStream stream = File.OpenRead(path))
using (FileStructure fs = FileStructure.Read(stream))
{
if (FileFormat.Wixout != fs.FileFormat)
{
throw new WixUnexpectedFileFormatException(path, FileFormat.Wixout, fs.FileFormat);
}
Uri uri = new Uri(Path.GetFullPath(path));
using (XmlReader reader = XmlReader.Create(fs.GetDataStream(), null, uri.AbsoluteUri))
{
try
{
reader.MoveToContent();
return Output.Read(reader, suppressVersionCheck);
}
catch (XmlException xe)
{
throw new WixCorruptFileException(path, fs.FileFormat, xe);
}
}
}
}
///
/// Saves an output to a path on disk.
///
/// Path to save output file to on disk.
public void Save(string path)
{
Directory.CreateDirectory(Path.GetDirectoryName(Path.GetFullPath(path)));
using (FileStream stream = File.Create(path))
using (FileStructure fs = FileStructure.Create(stream, FileFormat.Wixout, null))
using (XmlWriter writer = XmlWriter.Create(fs.GetDataStream()))
{
writer.WriteStartDocument();
this.Write(writer);
writer.WriteEndDocument();
}
}
///
/// 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 Output Read(XmlReader reader, bool suppressVersionCheck)
{
if (!reader.LocalName.Equals("wixOutput"))
{
throw new XmlException();
}
bool empty = reader.IsEmptyElement;
Output output = new Output(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 && !Output.CurrentVersion.Equals(version))
{
throw new WixException(ErrorMessages.VersionMismatch(SourceLineNumber.CreateFromUri(reader.BaseURI), "wixOutput", version.ToString(), Output.CurrentVersion.ToString()));
}
// loop through the rest of the xml building up the Output object
TableDefinitionCollection tableDefinitions = null;
List tables = new List();
if (!empty)
{
bool 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;
}
///
/// 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 Table table))
{
table = new Table(tableDefinition);
this.Tables.Add(table);
}
return table;
}
///
/// Persists an output in an XML format.
///
/// XmlWriter where the Output should persist itself as XML.
internal void Write(XmlWriter writer)
{
writer.WriteStartElement("wixOutput", XmlNamespaceUri);
writer.WriteAttributeString("type", this.Type.ToString());
if (0 != this.Codepage)
{
writer.WriteAttributeString("codepage", this.Codepage.ToString(CultureInfo.InvariantCulture));
}
writer.WriteAttributeString("version", Output.CurrentVersion.ToString());
// Collect all the table definitions and write them.
TableDefinitionCollection tableDefinitions = new TableDefinitionCollection();
foreach (Table table in this.Tables)
{
tableDefinitions.Add(table.Definition);
}
tableDefinitions.Write(writer);
foreach (Table table in this.Tables.OrderBy(t => t.Name))
{
table.Write(writer);
}
foreach (SubStorage subStorage in this.SubStorages)
{
subStorage.Write(writer);
}
writer.WriteEndElement();
}
}
}