// 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
{
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Text;
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.
/// Flag if rows in this table create symbols.
/// Flag if table is unreal.
/// Flag if table is part of UX Manifest.
public TableDefinition(string name, IList columns, bool createSymbols, bool unreal, bool bootstrapperApplicationData = false)
{
this.Name = name;
this.CreateSymbols = createSymbols;
this.Unreal = unreal;
this.BootstrapperApplicationData = bootstrapperApplicationData;
this.Columns = new ReadOnlyCollection(columns);
}
///
/// Gets if rows in this table create symbols.
///
/// Flag if rows in this table create symbols.
public bool CreateSymbols { get; private set; }
///
/// 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 if the table is a part of the bootstrapper application data manifest.
///
/// Flag if table is a part of the bootstrapper application data manifest.
public bool BootstrapperApplicationData { get; private set; }
///
/// Gets the collection of column definitions for this table.
///
/// Collection of column definitions for this table.
public IList 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]
{
get { return this.Columns[columnIndex]; }
}
///
/// Gets the table definition in IDT format.
///
/// Whether to keep columns added in a transform.
/// Table definition in IDT format.
public string ToIdtDefinition(bool keepAddedColumns)
{
bool first = true;
StringBuilder columnString = new StringBuilder();
StringBuilder dataString = new StringBuilder();
StringBuilder tableString = new StringBuilder();
tableString.Append(this.Name);
foreach (ColumnDefinition column in this.Columns)
{
// conditionally keep columns added in a transform; otherwise,
// break because columns can only be added at the end
if (column.Added && !keepAddedColumns)
{
break;
}
if (!first)
{
columnString.Append('\t');
dataString.Append('\t');
}
columnString.Append(column.Name);
dataString.Append(column.IdtType);
if (column.PrimaryKey)
{
tableString.AppendFormat("\t{0}", column.Name);
}
first = false;
}
columnString.Append("\r\n");
columnString.Append(dataString);
columnString.Append("\r\n");
columnString.Append(tableString);
columnString.Append("\r\n");
return columnString.ToString();
}
///
/// Adds the validation rows to the _Validation table.
///
/// The _Validation table.
public void AddValidationRows(Table validationTable)
{
foreach (ColumnDefinition columnDef in this.Columns)
{
Row row = validationTable.CreateRow(null);
row[0] = this.Name;
row[1] = columnDef.Name;
if (columnDef.Nullable)
{
row[2] = "Y";
}
else
{
row[2] = "N";
}
if (columnDef.IsMinValueSet)
{
row[3] = columnDef.MinValue;
}
if (columnDef.IsMaxValueSet)
{
row[4] = columnDef.MaxValue;
}
row[5] = columnDef.KeyTable;
if (columnDef.IsKeyColumnSet)
{
row[6] = columnDef.KeyColumn;
}
if (ColumnCategory.Unknown != columnDef.Category)
{
row[7] = columnDef.Category.ToString();
}
row[8] = columnDef.Possibilities;
row[9] = columnDef.Description;
}
}
///
/// 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
int 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.Count - this.Columns.Count);
// compare name, type, and length of each column
for (int i = 0; 0 == ret && this.Columns.Count > i; i++)
{
ColumnDefinition thisColumnDef = this.Columns[i];
ColumnDefinition updatedColumnDef = updated.Columns[i];
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)
{
bool empty = reader.IsEmptyElement;
bool createSymbols = false;
string name = null;
bool unreal = false;
bool bootstrapperApplicationData = false;
while (reader.MoveToNextAttribute())
{
switch (reader.LocalName)
{
case "createSymbols":
createSymbols = reader.Value.Equals("yes");
break;
case "name":
name = reader.Value;
break;
case "unreal":
unreal = reader.Value.Equals("yes");
break;
case "bootstrapperApplicationData":
bootstrapperApplicationData = reader.Value.Equals("yes");
break;
}
}
if (null == name)
{
throw new XmlException();
}
List columns = new List();
bool hasPrimaryKeyColumn = false;
// parse the child elements
if (!empty)
{
bool done = false;
while (!done && reader.Read())
{
switch (reader.NodeType)
{
case XmlNodeType.Element:
switch (reader.LocalName)
{
case "columnDefinition":
ColumnDefinition 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(WixDataErrors.RealTableMissingPrimaryKeyColumn(SourceLineNumber.CreateFromUri(reader.BaseURI), name));
}
if (!done)
{
throw new XmlException();
}
}
TableDefinition tableDefinition = new TableDefinition(name, columns, createSymbols, unreal, bootstrapperApplicationData);
return tableDefinition;
}
///
/// 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.CreateSymbols)
{
writer.WriteAttributeString("createSymbols", "yes");
}
if (this.Unreal)
{
writer.WriteAttributeString("unreal", "yes");
}
if (this.BootstrapperApplicationData)
{
writer.WriteAttributeString("bootstrapperApplicationData", "yes");
}
foreach (ColumnDefinition columnDefinition in this.Columns)
{
columnDefinition.Write(writer);
}
writer.WriteEndElement();
}
}
}