// 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.Dtf.WindowsInstaller { using System; using System.IO; using System.Text; using System.Globalization; using System.Diagnostics.CodeAnalysis; /// /// Defines a single column of a table in an installer database. /// /// Once created, a ColumnInfo object is immutable. public class ColumnInfo { private string name; private Type type; private int size; private bool isRequired; private bool isTemporary; private bool isLocalizable; /// /// Creates a new ColumnInfo object from a column definition. /// /// name of the column /// column definition string /// public ColumnInfo(string name, string columnDefinition) : this(name, typeof(String), 0, false, false, false) { if (name == null) { throw new ArgumentNullException("name"); } if (columnDefinition == null) { throw new ArgumentNullException("columnDefinition"); } switch (Char.ToLower(columnDefinition[0], CultureInfo.InvariantCulture)) { case 'i': this.type = typeof(Int32); break; case 'j': this.type = typeof(Int32); this.isTemporary = true; break; case 'g': this.type = typeof(String); this.isTemporary = true; break; case 'l': this.type = typeof(String); this.isLocalizable = true; break; case 'o': this.type = typeof(Stream); this.isTemporary = true; break; case 's': this.type = typeof(String); break; case 'v': this.type = typeof(Stream); break; default: throw new InstallerException(); } this.isRequired = Char.IsLower(columnDefinition[0]); this.size = Int32.Parse( columnDefinition.Substring(1), CultureInfo.InvariantCulture.NumberFormat); if (this.type == typeof(Int32) && this.size <= 2) { this.type = typeof(Int16); } } /// /// Creates a new ColumnInfo object from a list of parameters. /// /// name of the column /// type of the column; must be one of the following: /// Int16, Int32, String, or Stream /// the maximum number of characters for String columns; /// ignored for other column types /// true if the column is required to have a non-null value public ColumnInfo(string name, Type type, int size, bool isRequired) : this(name, type, size, isRequired, false, false) { } /// /// Creates a new ColumnInfo object from a list of parameters. /// /// name of the column /// type of the column; must be one of the following: /// Int16, Int32, String, or Stream /// the maximum number of characters for String columns; /// ignored for other column types /// true if the column is required to have a non-null value /// true to if the column is only in-memory and /// not persisted with the database /// for String columns, indicates the column /// is localizable; ignored for other column types public ColumnInfo(string name, Type type, int size, bool isRequired, bool isTemporary, bool isLocalizable) { if (name == null) { throw new ArgumentNullException("name"); } if (type == typeof(Int32)) { size = 4; isLocalizable = false; } else if (type == typeof(Int16)) { size = 2; isLocalizable = false; } else if (type == typeof(String)) { } else if (type == typeof(Stream)) { isLocalizable = false; } else { throw new ArgumentOutOfRangeException("type"); } this.name = name; this.type = type; this.size = size; this.isRequired = isRequired; this.isTemporary = isTemporary; this.isLocalizable = isLocalizable; } /// /// Gets the name of the column. /// /// name of the column public string Name { get { return this.name; } } /// /// Gets the type of the column as a System.Type. This is one of the following: Int16, Int32, String, or Stream /// /// type of the column [SuppressMessage("Microsoft.Naming", "CA1721:PropertyNamesShouldNotMatchGetMethods")] public Type Type { get { return this.type; } } /// /// Gets the type of the column as an integer that can be cast to a System.Data.DbType. This is one of the following: Int16, Int32, String, or Binary /// /// equivalent DbType of the column as an integer public int DBType { get { if (this.type == typeof(Int16)) return 10; else if (this.type == typeof(Int32)) return 11; else if (this.type == typeof(Stream)) return 1; else return 16; } } /// /// Gets the size of the column. /// /// The size of integer columns this is either 2 or 4. For string columns this is the maximum /// recommended length of the string, or 0 for unlimited length. For stream columns, 0 is returned. public int Size { get { return this.size; } } /// /// Gets a value indicating whether the column must be non-null when inserting a record. /// /// required status of the column public bool IsRequired { get { return this.isRequired; } } /// /// Gets a value indicating whether the column is temporary. Temporary columns are not persisted /// when the database is saved to disk. /// /// temporary status of the column public bool IsTemporary { get { return this.isTemporary; } } /// /// Gets a value indicating whether the column is a string column that is localizable. /// /// localizable status of the column public bool IsLocalizable { get { return this.isLocalizable; } } /// /// Gets an SQL fragment that can be used to create this column within a CREATE TABLE statement. /// /// SQL fragment to be used for creating the column ///

/// Examples: /// /// LONG /// SHORT TEMPORARY /// CHAR(0) LOCALIZABLE /// CHAR(72) NOT NULL LOCALIZABLE /// OBJECT /// ///

public string SqlCreateString { get { StringBuilder s = new StringBuilder(); s.AppendFormat("`{0}` ", this.name); if (this.type == typeof(Int16)) s.Append("SHORT"); else if (this.type == typeof(Int32)) s.Append("LONG"); else if (this.type == typeof(String)) s.AppendFormat("CHAR({0})", this.size); else s.Append("OBJECT"); if (this.isRequired) s.Append(" NOT NULL"); if (this.isTemporary) s.Append(" TEMPORARY"); if (this.isLocalizable) s.Append(" LOCALIZABLE"); return s.ToString(); } } /// /// Gets a short string defining the type and size of the column. /// /// /// The definition string consists /// of a single letter representing the data type followed by the width of the column (in characters /// when applicable, bytes otherwise). A width of zero designates an unbounded width (for example, /// long text fields and streams). An uppercase letter indicates that null values are allowed in /// the column. /// ///

/// /// s? - String, variable length (?=1-255) /// s0 - String, variable length /// i2 - Short integer /// i4 - Long integer /// v0 - Binary Stream /// g? - Temporary string (?=0-255) /// j? - Temporary integer (?=0,1,2,4) /// O0 - Temporary object (stream) /// l? - Localizable string, variable length (?=1-255) /// l0 - Localizable string, variable length /// ///

public string ColumnDefinitionString { get { char t; if (this.type == typeof(Int16) || this.type == typeof(Int32)) { t = (this.isTemporary ? 'j' : 'i'); } else if (this.type == typeof(String)) { t = (this.isTemporary ? 'g' : this.isLocalizable ? 'l' : 's'); } else { t = (this.isTemporary ? 'O' : 'v'); } return String.Format( CultureInfo.InvariantCulture, "{0}{1}", (this.isRequired ? t : Char.ToUpper(t, CultureInfo.InvariantCulture)), this.size); } } /// /// Gets the name of the column. /// /// Name of the column. public override string ToString() { return this.Name; } } }