// 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.Linq;
using System.Xml;
///
/// Definition of a table in a database.
///
public sealed class TableDefinition : IComparable
{
///
/// Tracks the maximum number of columns supported in a real table.
/// This is a Windows Installer limitation.
///
public const int MaxColumnsInRealTable = 32;
///
/// Creates a table definition.
///
/// Name of table to create.
/// Column definitions for the table.
/// Flag if table is unreal.
public TableDefinition(string name, IEnumerable columns, bool unreal = false)
{
this.Name = name;
this.Unreal = unreal;
this.Columns = columns.ToArray();
}
///
/// Gets the name of the table.
///
/// Name of the table.
public string Name { get; private set; }
///
/// Gets if the table is unreal.
///
/// Flag if table is unreal.
public bool Unreal { get; private set; }
///
/// Gets the collection of column definitions for this table.
///
/// Collection of column definitions for this table.
public ColumnDefinition[] Columns { get; private set; }
///
/// Gets the column definition in the table by index.
///
/// Index of column to locate.
/// Column definition in the table by index.
public ColumnDefinition this[int columnIndex] => this.Columns[columnIndex];
///
/// Compares this table definition to another table definition.
///
///
/// Only Windows Installer traits are compared, allowing for updates to WiX-specific table definitions.
///
/// The updated to compare with this target definition.
/// 0 if the tables' core properties are the same; otherwise, non-0.
public int CompareTo(TableDefinition updated)
{
// by definition, this object is greater than null
if (null == updated)
{
return 1;
}
// compare the table names
var ret = String.Compare(this.Name, updated.Name, StringComparison.Ordinal);
// compare the column count
if (0 == ret)
{
// transforms can only add columns
ret = Math.Min(0, updated.Columns.Length - this.Columns.Length);
// compare name, type, and length of each column
for (var i = 0; 0 == ret && this.Columns.Length > i; i++)
{
var thisColumnDef = this.Columns[i];
var updatedColumnDef = updated.Columns[i];
// Igmore unreal columns when comparing table definitions.
if (thisColumnDef.Unreal || updatedColumnDef.Unreal)
{
continue;
}
ret = thisColumnDef.CompareTo(updatedColumnDef);
}
}
return ret;
}
///
/// Parses table definition from xml reader.
///
/// Reader to get data from.
/// The TableDefintion represented by the Xml.
internal static TableDefinition Read(XmlReader reader)
{
var empty = reader.IsEmptyElement;
string name = null;
var unreal = false;
var bootstrapperApplicationData = false;
while (reader.MoveToNextAttribute())
{
switch (reader.LocalName)
{
case "name":
name = reader.Value;
break;
case "unreal":
unreal = reader.Value.Equals("yes");
break;
}
}
if (null == name)
{
throw new XmlException();
}
var columns = new List();
var hasPrimaryKeyColumn = false;
// parse the child elements
if (!empty)
{
var done = false;
while (!done && reader.Read())
{
switch (reader.NodeType)
{
case XmlNodeType.Element:
switch (reader.LocalName)
{
case "columnDefinition":
var columnDefinition = ColumnDefinition.Read(reader);
columns.Add(columnDefinition);
if (columnDefinition.PrimaryKey)
{
hasPrimaryKeyColumn = true;
}
break;
default:
throw new XmlException();
}
break;
case XmlNodeType.EndElement:
done = true;
break;
}
}
if (!unreal && !bootstrapperApplicationData && !hasPrimaryKeyColumn)
{
throw new WixException(ErrorMessages.RealTableMissingPrimaryKeyColumn(SourceLineNumber.CreateFromUri(reader.BaseURI), name));
}
if (!done)
{
throw new XmlException();
}
}
return new TableDefinition(name, columns.ToArray(), unreal);
}
///
/// Persists an output in an XML format.
///
/// XmlWriter where the Output should persist itself as XML.
internal void Write(XmlWriter writer)
{
writer.WriteStartElement("tableDefinition", TableDefinitionCollection.XmlNamespaceUri);
writer.WriteAttributeString("name", this.Name);
if (this.Unreal)
{
writer.WriteAttributeString("unreal", "yes");
}
foreach (var columnDefinition in this.Columns)
{
columnDefinition.Write(writer);
}
writer.WriteEndElement();
}
}
}