From 221da62c05ef2b515eb507c77655514cd0ec32a4 Mon Sep 17 00:00:00 2001 From: Rob Mensching Date: Thu, 7 Dec 2017 14:17:39 -0800 Subject: Reintegrate MSI constructs into WxToolset.Data.WindowsInstaller namespace --- .../WindowsInstaller/ColumnDefinition.cs | 685 +++++++++++++++++++++ 1 file changed, 685 insertions(+) create mode 100644 src/WixToolset.Data/WindowsInstaller/ColumnDefinition.cs (limited to 'src/WixToolset.Data/WindowsInstaller/ColumnDefinition.cs') diff --git a/src/WixToolset.Data/WindowsInstaller/ColumnDefinition.cs b/src/WixToolset.Data/WindowsInstaller/ColumnDefinition.cs new file mode 100644 index 00000000..b6d4f29f --- /dev/null +++ b/src/WixToolset.Data/WindowsInstaller/ColumnDefinition.cs @@ -0,0 +1,685 @@ +// 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.Globalization; + using System.Xml; + + /// + /// Definition of a table's column. + /// + public sealed class ColumnDefinition : IComparable + { + /// + /// Creates a new column definition. + /// + /// Name of column. + /// Type of column + /// Length of column. + /// If column is primary key. + /// If column is nullable. + /// Validation category for column. + /// Optional minimum value for the column. + /// Optional maximum value for the colum. + /// Optional name of table for foreign key. + /// Optional name of column for foreign key. + /// Set of possible values for column. + /// Description of column in vaidation table. + /// Type of modularization for column + /// If the column is localizable. + /// If whitespace should be preserved in a CDATA node. + public ColumnDefinition(string name, ColumnType type, int length, bool primaryKey, bool nullable, ColumnCategory category, long? minValue = null, long? maxValue = null, string keyTable = null, int? keyColumn = null, string possibilities = null, string description = null, ColumnModularizeType? modularizeType = null, bool forceLocalizable = false, bool useCData = false) + { + this.Name = name; + this.Type = type; + this.Length = length; + this.PrimaryKey = primaryKey; + this.Nullable = nullable; + this.ModularizeType = CalculateModularizationType(modularizeType, category); + this.IsLocalizable = forceLocalizable || ColumnType.Localized == type; + this.MinValue = minValue; + this.MaxValue = maxValue; + this.KeyTable = keyTable; + this.KeyColumn = keyColumn; + this.Category = category; + this.Possibilities = possibilities; + this.Description = description; + this.UseCData = useCData; + } + + /// + /// Gets whether this column was added via a transform. + /// + /// Whether this column was added via a transform. + public bool Added { get; set; } + + /// + /// Gets the name of the column. + /// + /// Name of column. + public string Name { get; } + + /// + /// Gets the type of the column. + /// + /// Type of column. + public ColumnType Type { get; } + + /// + /// Gets the length of the column. + /// + /// Length of column. + public int Length { get; } + + /// + /// Gets if the column is a primary key. + /// + /// true if column is primary key. + public bool PrimaryKey { get; } + + /// + /// Gets if the column is nullable. + /// + /// true if column is nullable. + public bool Nullable { get; } + + /// + /// Gets the type of modularization for this column. + /// + /// Column's modularization type. + public ColumnModularizeType ModularizeType { get; } + + /// + /// Gets if the column is localizable. Can be because the type is localizable, or because the column + /// was explicitly set to be so. + /// + /// true if column is localizable. + public bool IsLocalizable { get; } + + /// + /// Gets the minimum value for the column. + /// + /// Minimum value for the column. + public long? MinValue { get; } + + /// + /// Gets the maximum value for the column. + /// + /// Maximum value for the column. + public long? MaxValue { get; } + + /// + /// Gets the table that has the foreign key for this column + /// + /// Foreign key table name. + public string KeyTable { get; } + + /// + /// Gets the foreign key column that this column refers to. + /// + /// Foreign key column. + public int? KeyColumn { get; } + + /// + /// Gets the validation category for this column. + /// + /// Validation category. + public ColumnCategory Category { get; } + + /// + /// Gets the set of possibilities for this column. + /// + /// Set of possibilities for this column. + public string Possibilities { get; } + + /// + /// Gets the description for this column. + /// + /// Description of column. + public string Description { get; } + + /// + /// Gets if whitespace should be preserved in a CDATA node. + /// + /// true if whitespace should be preserved in a CDATA node. + public bool UseCData { get; } + + /// + /// Parses a column definition in a table definition. + /// + /// Reader to get data from. + /// The ColumnDefintion represented by the Xml. + internal static ColumnDefinition Read(XmlReader reader) + { + if (!reader.LocalName.Equals("columnDefinition")) + { + throw new XmlException(); + } + + bool added = false; + ColumnCategory category = ColumnCategory.Unknown; + string description = null; + bool empty = reader.IsEmptyElement; + int? keyColumn = null; + string keyTable = null; + int length = -1; + bool localizable = false; + long? maxValue = null; + long? minValue = null; + var modularize = ColumnModularizeType.None; + string name = null; + bool nullable = false; + string possibilities = null; + bool primaryKey = false; + var type = ColumnType.Unknown; + bool useCData = false; + + // parse the attributes + while (reader.MoveToNextAttribute()) + { + switch (reader.LocalName) + { + case "added": + added = reader.Value.Equals("yes"); + break; + case "category": + switch (reader.Value) + { + case "anyPath": + category = ColumnCategory.AnyPath; + break; + case "binary": + category = ColumnCategory.Binary; + break; + case "cabinet": + category = ColumnCategory.Cabinet; + break; + case "condition": + category = ColumnCategory.Condition; + break; + case "customSource": + category = ColumnCategory.CustomSource; + break; + case "defaultDir": + category = ColumnCategory.DefaultDir; + break; + case "doubleInteger": + category = ColumnCategory.DoubleInteger; + break; + case "filename": + category = ColumnCategory.Filename; + break; + case "formatted": + category = ColumnCategory.Formatted; + break; + case "formattedSddl": + category = ColumnCategory.FormattedSDDLText; + break; + case "guid": + category = ColumnCategory.Guid; + break; + case "identifier": + category = ColumnCategory.Identifier; + break; + case "integer": + category = ColumnCategory.Integer; + break; + case "language": + category = ColumnCategory.Language; + break; + case "lowerCase": + category = ColumnCategory.LowerCase; + break; + case "path": + category = ColumnCategory.Path; + break; + case "paths": + category = ColumnCategory.Paths; + break; + case "property": + category = ColumnCategory.Property; + break; + case "regPath": + category = ColumnCategory.RegPath; + break; + case "shortcut": + category = ColumnCategory.Shortcut; + break; + case "template": + category = ColumnCategory.Template; + break; + case "text": + category = ColumnCategory.Text; + break; + case "timeDate": + category = ColumnCategory.TimeDate; + break; + case "upperCase": + category = ColumnCategory.UpperCase; + break; + case "version": + category = ColumnCategory.Version; + break; + case "wildCardFilename": + category = ColumnCategory.WildCardFilename; + break; + default: + throw new InvalidOperationException(); + } + break; + case "description": + description = reader.Value; + break; + case "keyColumn": + keyColumn = Convert.ToInt32(reader.Value, 10); + break; + case "keyTable": + keyTable = reader.Value; + break; + case "length": + length = Convert.ToInt32(reader.Value, 10); + break; + case "localizable": + localizable = reader.Value.Equals("yes"); + break; + case "maxValue": + maxValue = Convert.ToInt32(reader.Value, 10); + break; + case "minValue": + minValue = Convert.ToInt32(reader.Value, 10); + break; + case "modularize": + switch (reader.Value) + { + case "column": + modularize = ColumnModularizeType.Column; + break; + case "companionFile": + modularize = ColumnModularizeType.CompanionFile; + break; + case "condition": + modularize = ColumnModularizeType.Condition; + break; + case "controlEventArgument": + modularize = ColumnModularizeType.ControlEventArgument; + break; + case "controlText": + modularize = ColumnModularizeType.ControlText; + break; + case "icon": + modularize = ColumnModularizeType.Icon; + break; + case "none": + modularize = ColumnModularizeType.None; + break; + case "property": + modularize = ColumnModularizeType.Property; + break; + case "semicolonDelimited": + modularize = ColumnModularizeType.SemicolonDelimited; + break; + default: + throw new XmlException(); + } + break; + case "name": + switch (reader.Value) + { + case "CREATE": + case "DELETE": + case "DROP": + case "INSERT": + throw new XmlException(); + default: + name = reader.Value; + break; + } + break; + case "nullable": + nullable = reader.Value.Equals("yes"); + break; + case "primaryKey": + primaryKey = reader.Value.Equals("yes"); + break; + case "set": + possibilities = reader.Value; + break; + case "type": + switch (reader.Value) + { + case "localized": + type = ColumnType.Localized; + break; + case "number": + type = ColumnType.Number; + break; + case "object": + type = ColumnType.Object; + break; + case "string": + type = ColumnType.String; + break; + case "preserved": + type = ColumnType.Preserved; + break; + default: + throw new XmlException(); + } + break; + case "useCData": + useCData = reader.Value.Equals("yes"); + break; + } + } + + // parse the child elements (there should be none) + if (!empty) + { + bool done = false; + + while (!done && reader.Read()) + { + switch (reader.NodeType) + { + case XmlNodeType.Element: + throw new XmlException(); + case XmlNodeType.EndElement: + done = true; + break; + } + } + + if (!done) + { + throw new XmlException(); + } + } + + ColumnDefinition columnDefinition = new ColumnDefinition(name, type, length, primaryKey, nullable, category, minValue, maxValue, keyTable, keyColumn, possibilities, description, modularize, localizable, useCData); + columnDefinition.Added = added; + + return columnDefinition; + } + + /// + /// Persists a ColumnDefinition in an XML format. + /// + /// XmlWriter where the Output should persist itself as XML. + internal void Write(XmlWriter writer) + { + writer.WriteStartElement("columnDefinition", TableDefinitionCollection.XmlNamespaceUri); + + writer.WriteAttributeString("name", this.Name); + + switch (this.Type) + { + case ColumnType.Localized: + writer.WriteAttributeString("type", "localized"); + break; + case ColumnType.Number: + writer.WriteAttributeString("type", "number"); + break; + case ColumnType.Object: + writer.WriteAttributeString("type", "object"); + break; + case ColumnType.String: + writer.WriteAttributeString("type", "string"); + break; + case ColumnType.Preserved: + writer.WriteAttributeString("type", "preserved"); + break; + } + + writer.WriteAttributeString("length", this.Length.ToString(CultureInfo.InvariantCulture.NumberFormat)); + + if (this.PrimaryKey) + { + writer.WriteAttributeString("primaryKey", "yes"); + } + + if (this.Nullable) + { + writer.WriteAttributeString("nullable", "yes"); + } + + if (this.IsLocalizable) + { + writer.WriteAttributeString("localizable", "yes"); + } + + if (this.Added) + { + writer.WriteAttributeString("added", "yes"); + } + + switch (this.ModularizeType) + { + case ColumnModularizeType.Column: + writer.WriteAttributeString("modularize", "column"); + break; + case ColumnModularizeType.CompanionFile: + writer.WriteAttributeString("modularize", "companionFile"); + break; + case ColumnModularizeType.Condition: + writer.WriteAttributeString("modularize", "condition"); + break; + case ColumnModularizeType.ControlEventArgument: + writer.WriteAttributeString("modularize", "controlEventArgument"); + break; + case ColumnModularizeType.ControlText: + writer.WriteAttributeString("modularize", "controlText"); + break; + case ColumnModularizeType.Icon: + writer.WriteAttributeString("modularize", "icon"); + break; + case ColumnModularizeType.None: + // this is the default value + break; + case ColumnModularizeType.Property: + writer.WriteAttributeString("modularize", "property"); + break; + case ColumnModularizeType.SemicolonDelimited: + writer.WriteAttributeString("modularize", "semicolonDelimited"); + break; + } + + if (this.MinValue.HasValue) + { + writer.WriteAttributeString("minValue", this.MinValue.Value.ToString(CultureInfo.InvariantCulture.NumberFormat)); + } + + if (this.MaxValue.HasValue) + { + writer.WriteAttributeString("maxValue", this.MaxValue.Value.ToString(CultureInfo.InvariantCulture.NumberFormat)); + } + + if (!String.IsNullOrEmpty(this.KeyTable)) + { + writer.WriteAttributeString("keyTable", this.KeyTable); + } + + if (this.KeyColumn.HasValue) + { + writer.WriteAttributeString("keyColumn", this.KeyColumn.HasValue.ToString(CultureInfo.InvariantCulture.NumberFormat)); + } + + switch (this.Category) + { + case ColumnCategory.AnyPath: + writer.WriteAttributeString("category", "anyPath"); + break; + case ColumnCategory.Binary: + writer.WriteAttributeString("category", "binary"); + break; + case ColumnCategory.Cabinet: + writer.WriteAttributeString("category", "cabinet"); + break; + case ColumnCategory.Condition: + writer.WriteAttributeString("category", "condition"); + break; + case ColumnCategory.CustomSource: + writer.WriteAttributeString("category", "customSource"); + break; + case ColumnCategory.DefaultDir: + writer.WriteAttributeString("category", "defaultDir"); + break; + case ColumnCategory.DoubleInteger: + writer.WriteAttributeString("category", "doubleInteger"); + break; + case ColumnCategory.Filename: + writer.WriteAttributeString("category", "filename"); + break; + case ColumnCategory.Formatted: + writer.WriteAttributeString("category", "formatted"); + break; + case ColumnCategory.FormattedSDDLText: + writer.WriteAttributeString("category", "formattedSddl"); + break; + case ColumnCategory.Guid: + writer.WriteAttributeString("category", "guid"); + break; + case ColumnCategory.Identifier: + writer.WriteAttributeString("category", "identifier"); + break; + case ColumnCategory.Integer: + writer.WriteAttributeString("category", "integer"); + break; + case ColumnCategory.Language: + writer.WriteAttributeString("category", "language"); + break; + case ColumnCategory.LowerCase: + writer.WriteAttributeString("category", "lowerCase"); + break; + case ColumnCategory.Path: + writer.WriteAttributeString("category", "path"); + break; + case ColumnCategory.Paths: + writer.WriteAttributeString("category", "paths"); + break; + case ColumnCategory.Property: + writer.WriteAttributeString("category", "property"); + break; + case ColumnCategory.RegPath: + writer.WriteAttributeString("category", "regPath"); + break; + case ColumnCategory.Shortcut: + writer.WriteAttributeString("category", "shortcut"); + break; + case ColumnCategory.Template: + writer.WriteAttributeString("category", "template"); + break; + case ColumnCategory.Text: + writer.WriteAttributeString("category", "text"); + break; + case ColumnCategory.TimeDate: + writer.WriteAttributeString("category", "timeDate"); + break; + case ColumnCategory.UpperCase: + writer.WriteAttributeString("category", "upperCase"); + break; + case ColumnCategory.Version: + writer.WriteAttributeString("category", "version"); + break; + case ColumnCategory.WildCardFilename: + writer.WriteAttributeString("category", "wildCardFilename"); + break; + } + + if (!String.IsNullOrEmpty(this.Possibilities)) + { + writer.WriteAttributeString("set", this.Possibilities); + } + + if (!String.IsNullOrEmpty(this.Description)) + { + writer.WriteAttributeString("description", this.Description); + } + + if (this.UseCData) + { + writer.WriteAttributeString("useCData", "yes"); + } + + writer.WriteEndElement(); + } + + /// + /// Compare this column definition to another column definition. + /// + /// + /// Only Windows Installer traits are compared, allowing for updates to WiX-specific table definitions. + /// + /// The to compare with this one. + /// 0 if the columns' core propeties are the same; otherwise, non-0. + public int CompareTo(ColumnDefinition other) + { + // by definition, this object is greater than null + if (null == other) + { + return 1; + } + + // compare column names + int ret = String.Compare(this.Name, other.Name, StringComparison.Ordinal); + + // compare column types + if (0 == ret) + { + ret = this.Type == other.Type ? 0 : -1; + + // compare column lengths + if (0 == ret) + { + ret = this.Length == other.Length ? 0 : -1; + + // compare whether both are primary keys + if (0 == ret) + { + ret = this.PrimaryKey == other.PrimaryKey ? 0 : -1; + + // compare nullability + if (0 == ret) + { + ret = this.Nullable == other.Nullable ? 0 : -1; + } + } + } + } + + return ret; + } + + private static ColumnModularizeType CalculateModularizationType(ColumnModularizeType? modularizeType, ColumnCategory category) + { + if (modularizeType.HasValue) + { + return modularizeType.Value; + } + + switch (category) + { + case ColumnCategory.Identifier: + case ColumnCategory.CustomSource: + return ColumnModularizeType.Column; + + case ColumnCategory.Condition: + return ColumnModularizeType.Condition; + + case ColumnCategory.AnyPath: + case ColumnCategory.Formatted: + case ColumnCategory.FormattedSDDLText: + case ColumnCategory.Path: + case ColumnCategory.Paths: + case ColumnCategory.RegPath: + case ColumnCategory.Template: + return ColumnModularizeType.Property; + + case ColumnCategory.Shortcut: + return ColumnModularizeType.Property; + } + + return ColumnModularizeType.None; + } + } +} -- cgit v1.2.3-55-g6feb