/// <summary> /// Creates a new column definition. /// </summary> /// <param name="name">Name of column.</param> /// <param name="type">Type of column</param> /// <param name="length">Length of column.</param> /// <param name="primaryKey">If column is primary key.</param> /// <param name="nullable">If column is nullable.</param> /// <param name="modularizeType">Type of modularization for column</param> /// <param name="localizable">If the column is localizable.</param> /// <param name="minValueSet">If the minimum of the value was set.</param> /// <param name="minValue">Minimum value for the column.</param> /// <param name="maxValueSet">If the maximum value was set.</param> /// <param name="maxValue">Maximum value for the colum.</param> /// <param name="keyTable">Optional name of table for foreign key.</param> /// <param name="keyColumnSet">If the key column was set.</param> /// <param name="keyColumn">Optional name of column for foreign key.</param> /// <param name="category">Validation category for column.</param> /// <param name="possibilities">Set of possible values for column.</param> /// <param name="description">Description of column in vaidation table.</param> /// <param name="escapeIdtCharacters">If characters should be escaped in IDT.</param> /// <param name="useCData">If whitespace should be preserved in a CDATA node.</param> public ColumnDefinition(string name, ColumnType type, int length, bool primaryKey, bool nullable, ColumnModularizeType modularizeType, bool localizable, bool minValueSet, int minValue, bool maxValueSet, int maxValue, string keyTable, bool keyColumnSet, int keyColumn, ColumnCategory category, string possibilities, string description, bool escapeIdtCharacters, bool useCData) { this.name = name; this.type = type; this.length = length; this.primaryKey = primaryKey; this.nullable = nullable; this.modularize = modularizeType; this.localizable = localizable; this.minValueSet = minValueSet; this.minValue = minValue; this.maxValueSet = maxValueSet; this.maxValue = maxValue; this.keyTable = keyTable; this.keyColumnSet = keyColumnSet; this.keyColumn = keyColumn; this.category = category; this.possibilities = possibilities; this.description = description; this.escapeIdtCharacters = escapeIdtCharacters; this.useCData = useCData; }
/// <summary> /// Gets the modularized version of the field data. /// </summary> /// <param name="field">The field to modularize.</param> /// <param name="modularizationGuid">String containing the GUID of the Merge Module to append the the field value, if appropriate.</param> /// <param name="suppressModularizationIdentifiers">Optional collection of identifiers that should not be modularized.</param> /// <remarks>moduleGuid is expected to be null when not being used to compile a Merge Module.</remarks> /// <returns>The modularized version of the field data.</returns> internal string GetModularizedValue(Field field, string modularizationGuid, Hashtable suppressModularizationIdentifiers) { Debug.Assert(null != field.Data && 0 < ((string)field.Data).Length); string fieldData = Convert.ToString(field.Data, CultureInfo.InvariantCulture); if (null != modularizationGuid && ColumnModularizeType.None != field.Column.ModularizeType && !(Util.IsStandardAction(fieldData) || Util.IsStandardProperty(fieldData))) { StringBuilder sb; int start; ColumnModularizeType modularizeType = field.Column.ModularizeType; // special logic for the ControlEvent table's Argument column // this column requires different modularization methods depending upon the value of the Event column if (ColumnModularizeType.ControlEventArgument == field.Column.ModularizeType) { switch (this[2].ToString()) { case "CheckExistingTargetPath": // redirectable property name case "CheckTargetPath": case "DoAction": // custom action name case "NewDialog": // dialog name case "SelectionBrowse": case "SetTargetPath": case "SpawnDialog": case "SpawnWaitDialog": if (CompilerCore.IsIdentifier(fieldData)) { modularizeType = ColumnModularizeType.Column; } else { modularizeType = ColumnModularizeType.Property; } break; default: // formatted modularizeType = ColumnModularizeType.Property; break; } } else if (ColumnModularizeType.ControlText == field.Column.ModularizeType) { // icons are stored in the Binary table, so they get column-type modularization if (("Bitmap" == this[2].ToString() || "Icon" == this[2].ToString()) && CompilerCore.IsIdentifier(fieldData)) { modularizeType = ColumnModularizeType.Column; } else { modularizeType = ColumnModularizeType.Property; } } switch (modularizeType) { case ColumnModularizeType.Column: // ensure the value is an identifier (otherwise it shouldn't be modularized this way) if (!CompilerCore.IsIdentifier(fieldData)) { throw new InvalidOperationException(String.Format(CultureInfo.CurrentUICulture, WixStrings.EXP_CannotModularizeIllegalID, fieldData)); } // if we're not supposed to suppress modularization of this identifier if (null == suppressModularizationIdentifiers || !suppressModularizationIdentifiers.Contains(fieldData)) { fieldData = String.Concat(fieldData, ".", modularizationGuid); } break; case ColumnModularizeType.Property: case ColumnModularizeType.Condition: Regex regex; if (ColumnModularizeType.Property == modularizeType) { regex = new Regex(@"\[(?<identifier>[#$!]?[a-zA-Z_][a-zA-Z0-9_\.]*)]", RegexOptions.Singleline | RegexOptions.ExplicitCapture); } else { Debug.Assert(ColumnModularizeType.Condition == modularizeType); // This heinous looking regular expression is actually quite an elegant way // to shred the entire condition into the identifiers that need to be // modularized. Let's break it down piece by piece: // // 1. Look for the operators: NOT, EQV, XOR, OR, AND, IMP (plus a space). Note that the // regular expression is case insensitive so we don't have to worry about // all the permutations of these strings. // 2. Look for quoted strings. Quoted strings are just text and are ignored // outright. // 3. Look for environment variables. These look like identifiers we might // otherwise be interested in but start with a percent sign. Like quoted // strings these enviroment variable references are ignored outright. // 4. Match all identifiers that are things that need to be modularized. Note // the special characters (!, $, ?, &) that denote Component and Feature states. regex = new Regex(@"NOT\s|EQV\s|XOR\s|OR\s|AND\s|IMP\s|"".*?""|%[a-zA-Z_][a-zA-Z0-9_\.]*|(?<identifier>[!$\?&]?[a-zA-Z_][a-zA-Z0-9_\.]*)", RegexOptions.Singleline | RegexOptions.IgnoreCase | RegexOptions.ExplicitCapture); // less performant version of the above with captures showing where everything lives // regex = new Regex(@"(?<operator>NOT|EQV|XOR|OR|AND|IMP)|(?<string>"".*?"")|(?<environment>%[a-zA-Z_][a-zA-Z0-9_\.]*)|(?<identifier>[!$\?&]?[a-zA-Z_][a-zA-Z0-9_\.]*)",RegexOptions.Singleline | RegexOptions.IgnoreCase | RegexOptions.ExplicitCapture); } MatchCollection matches = regex.Matches(fieldData); sb = new StringBuilder(fieldData); // notice how this code walks backward through the list // because it modifies the string as we through it for (int i = matches.Count - 1; 0 <= i; i--) { Group group = matches[i].Groups["identifier"]; if (group.Success) { string identifier = group.Value; if (!Util.IsStandardProperty(identifier) && (null == suppressModularizationIdentifiers || !suppressModularizationIdentifiers.Contains(identifier))) { sb.Insert(group.Index + group.Length, '.'); sb.Insert(group.Index + group.Length + 1, modularizationGuid); } } } fieldData = sb.ToString(); break; case ColumnModularizeType.CompanionFile: // if we're not supposed to ignore this identifier and the value does not start with // a digit, we must have a companion file so modularize it if ((null == suppressModularizationIdentifiers || !suppressModularizationIdentifiers.Contains(fieldData)) && 0 < fieldData.Length && !Char.IsDigit(fieldData, 0)) { fieldData = String.Concat(fieldData, ".", modularizationGuid); } break; case ColumnModularizeType.Icon: if (null == suppressModularizationIdentifiers || !suppressModularizationIdentifiers.Contains(fieldData)) { start = fieldData.LastIndexOf(".", StringComparison.Ordinal); if (-1 == start) { fieldData = String.Concat(fieldData, ".", modularizationGuid); } else { fieldData = String.Concat(fieldData.Substring(0, start), ".", modularizationGuid, fieldData.Substring(start)); } } break; case ColumnModularizeType.SemicolonDelimited: string[] keys = fieldData.Split(';'); for (int i = 0; i < keys.Length; ++i) { keys[i] = String.Concat(keys[i], ".", modularizationGuid); } fieldData = String.Join(";", keys); break; } } return(fieldData); }
/// <summary> /// Returns the field data in a format usable in IDT files. /// </summary> /// <param name="field">The field to modularize.</param> /// <param name="moduleGuid">String containing the GUID of the Merge Module to append the the field value, if appropriate.</param> /// <param name="ignoreModularizations">Optional collection of identifers that should not be modularized.</param> /// <remarks>moduleGuid is expected to be null when not being used to compile a Merge Module.</remarks> /// <returns>Field data in string IDT format.</returns> public string GetIdtValue(Field field, string moduleGuid, IgnoreModularizationCollection ignoreModularizations) { if (field.Column.IsUnreal) { return(null); } if (null == field.Data) { return(String.Empty); } string fieldData = Convert.ToString(field.Data); // special idt-specific escaping if (field.Column.EscapeIdtCharacters) { fieldData = fieldData.Replace('\t', '\x10'); fieldData = fieldData.Replace('\r', '\x11'); fieldData = fieldData.Replace('\n', '\x19'); } string idtValue; if (null != moduleGuid && ColumnModularizeType.None != field.Column.ModularizeType && !Common.IsExcludedFromModularization(fieldData)) { StringBuilder sb; int start; ColumnModularizeType modularizeType = field.Column.ModularizeType; // special logic for the ControlEvent table's Argument column // this column requires different modularization methods depending upon the value of the Event column if (ColumnModularizeType.ControlEventArgument == field.Column.ModularizeType) { switch (this.row[2].ToString()) { case "CheckExistingTargetPath": // redirectable property name case "CheckTargetPath": case "DoAction": // custom action name case "NewDialog": // dialog name case "SelectionBrowse": case "SetTargetPath": case "SpawnDialog": case "SpawnWaitDialog": if (CompilerCore.IsIdentifier(fieldData)) { modularizeType = ColumnModularizeType.Column; } else { modularizeType = ColumnModularizeType.Property; } break; default: // formatted modularizeType = ColumnModularizeType.Property; break; } } switch (modularizeType) { case ColumnModularizeType.Column: // ensure the value is an identifier (otherwise it shouldn't be modularized this way) if (!CompilerCore.IsIdentifier(fieldData)) { throw new InvalidOperationException(String.Format(CultureInfo.InvariantCulture, "The value '{0}' is not a legal identifier and therefore cannot be modularized.", fieldData)); } // if we're not supposed to ignore this identifier if (null == ignoreModularizations || !ignoreModularizations.ShouldIgnoreModularization(fieldData)) { idtValue = String.Concat(fieldData, ".", moduleGuid); } else { idtValue = fieldData; } break; case ColumnModularizeType.Property: case ColumnModularizeType.Condition: Regex regex; if (ColumnModularizeType.Property == modularizeType) { regex = new Regex(@"\[(?<identifier>[#$!]?[a-zA-Z_][a-zA-Z0-9_\.]*)]", RegexOptions.Compiled | RegexOptions.Singleline | RegexOptions.ExplicitCapture); } else { Debug.Assert(ColumnModularizeType.Condition == modularizeType); // This heinous looking regular expression is actually quite an elegant way // to shred the entire condition into the identifiers that need to be // modularized. Let's break it down piece by piece: // // 1. Look for the operators: NOT, EQV, XOR, OR, AND, IMP. Note that the // regular expression is case insensitive so we don't have to worry about // all the permutations of these strings. // 2. Look for quoted strings. Quoted strings are just text and are ignored // outright. // 3. Look for environment variables. These look like identifiers we might // otherwise be interested in but start with a percent sign. Like quoted // strings these enviroment variable references are ignored outright. // 4. Match all identifiers that are things that need to be modularized. Note // the special characters (!, $, ?, &) that denote Component and Feature states. regex = new Regex(@"NOT|EQV|XOR|OR|AND|IMP|"".*?""|%[a-zA-Z_][a-zA-Z0-9_\.]*|(?<identifier>[!$\?&]?[a-zA-Z_][a-zA-Z0-9_\.]*)", RegexOptions.Compiled | RegexOptions.Singleline | RegexOptions.IgnoreCase | RegexOptions.ExplicitCapture); // less performant version of the above with captures showing where everything lives // regex = new Regex(@"(?<operator>NOT|EQV|XOR|OR|AND|IMP)|(?<string>"".*?"")|(?<environment>%[a-zA-Z_][a-zA-Z0-9_\.]*)|(?<identifier>[!$\?&]?[a-zA-Z_][a-zA-Z0-9_\.]*)", RegexOptions.Compiled | RegexOptions.Singleline | RegexOptions.IgnoreCase | RegexOptions.ExplicitCapture); } MatchCollection matches = regex.Matches(fieldData); sb = new StringBuilder(fieldData); // notice how this code walks backward through the list // because it modifies the string as we through it for (int i = matches.Count - 1; 0 <= i; i--) { Group group = matches[i].Groups["identifier"]; if (group.Success) { string identifier = group.Value; if (!Common.IsStandardProperty(identifier) && (null == ignoreModularizations || !ignoreModularizations.ShouldIgnoreModularization(identifier))) { sb.Insert(group.Index + group.Length, '.'); sb.Insert(group.Index + group.Length + 1, moduleGuid); } } } idtValue = sb.ToString(); break; case ColumnModularizeType.CompanionFile: // if we're not supposed to ignore this identifier and the value does not start with // a digit, we must have a companion file so modularize it if ((null == ignoreModularizations || !ignoreModularizations.ShouldIgnoreModularization(fieldData)) && 0 < fieldData.Length && !Char.IsDigit(fieldData, 0)) { idtValue = String.Concat(fieldData, ".", moduleGuid); } else { idtValue = fieldData; } break; case ColumnModularizeType.Icon: if (null == ignoreModularizations || !ignoreModularizations.ShouldIgnoreModularization(fieldData)) { start = fieldData.LastIndexOf("."); if (-1 == start) { idtValue = String.Concat(fieldData, ".", moduleGuid); } else { idtValue = String.Concat(fieldData.Substring(0, start), ".", moduleGuid, fieldData.Substring(start)); } } else { idtValue = fieldData; } break; case ColumnModularizeType.SemicolonDelimited: string[] keys = fieldData.Split(";".ToCharArray()); for (int i = 0; i < keys.Length; ++i) { keys[i] = String.Concat(keys[i], ".", moduleGuid); } idtValue = String.Join(";", keys); break; default: idtValue = fieldData; break; } } else // no modularization necessary, just use the field data { idtValue = fieldData; } return(idtValue); }
/// <summary> /// Parses a column definition in a table definition. /// </summary> /// <param name="reader">Reader to get data from.</param> /// <returns>The ColumnDefintion represented by the Xml.</returns> internal static ColumnDefinition Parse(XmlReader reader) { Debug.Assert("columnDefinition" == reader.LocalName); bool added = false; ColumnCategory category = ColumnCategory.Unknown; string description = null; bool empty = reader.IsEmptyElement; bool escapeIdtCharacters = false; int keyColumn = -1; bool keyColumnSet = false; string keyTable = null; int length = -1; bool localizable = false; int maxValue = 0; bool maxValueSet = false; int minValue = 0; bool minValueSet = false; ColumnModularizeType modularize = ColumnModularizeType.None; string name = null; bool nullable = false; string possibilities = null; bool primaryKey = false; ColumnType type = ColumnType.Unknown; bool useCData = false; // parse the attributes while (reader.MoveToNextAttribute()) { switch (reader.LocalName) { case "added": added = Common.IsYes(SourceLineNumberCollection.FromUri(reader.BaseURI), "columnDefinition", reader.Name, reader.Value); 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 WixException(WixErrors.IllegalAttributeValue(SourceLineNumberCollection.FromUri(reader.BaseURI), "columnDefinition", reader.Name, reader.Value, "anyPath", "binary", "cabinet", "condition", "customSource", "defaultDir", "doubleInteger", "filename", "formatted", "formattedSddl", "guid", "identifier", "integer", "language", "lowerCase", "path", "paths", "property", "regPath", "shortcut", "template", "text", "timeDate", "upperCase", "version", "wildCardFilename")); } break; case "description": description = reader.Value; break; case "escapeIdtCharacters": escapeIdtCharacters = Common.IsYes(SourceLineNumberCollection.FromUri(reader.BaseURI), "columnDefinition", reader.Name, reader.Value); break; case "keyColumn": keyColumnSet = true; 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 = Common.IsYes(SourceLineNumberCollection.FromUri(reader.BaseURI), "columnDefinition", reader.Name, reader.Value); break; case "maxValue": maxValueSet = true; maxValue = Convert.ToInt32(reader.Value, 10); break; case "minValue": minValueSet = true; 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 WixException(WixErrors.IllegalAttributeValue(SourceLineNumberCollection.FromUri(reader.BaseURI), "columnDefinition", reader.Name, reader.Value, "column", "companionFile", "condition", "controlEventArgument", "controlText", "icon", "property", "semicolonDelimited")); } break; case "name": switch (reader.Value) { case "CREATE": case "DELETE": case "DROP": case "INSERT": throw new WixException(WixErrors.IllegalColumnName(SourceLineNumberCollection.FromUri(reader.BaseURI), "columnDefinition", reader.Name, reader.Value)); default: name = reader.Value; break; } break; case "nullable": nullable = Common.IsYes(SourceLineNumberCollection.FromUri(reader.BaseURI), "columnDefinition", reader.Name, reader.Value); break; case "primaryKey": primaryKey = Common.IsYes(SourceLineNumberCollection.FromUri(reader.BaseURI), "columnDefinition", reader.Name, reader.Value); 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 WixException(WixErrors.IllegalAttributeValue(SourceLineNumberCollection.FromUri(reader.BaseURI), "columnDefinition", reader.Name, reader.Value, "localized", "number", "object", "string")); } break; case "useCData": useCData = Common.IsYes(SourceLineNumberCollection.FromUri(reader.BaseURI), "columnDefinition", reader.Name, reader.Value); break; default: if (!reader.NamespaceURI.StartsWith("http://www.w3.org/", StringComparison.Ordinal)) { throw new WixException(WixErrors.UnexpectedAttribute(SourceLineNumberCollection.FromUri(reader.BaseURI), "columnDefinition", reader.Name)); } 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 WixException(WixErrors.UnexpectedElement(SourceLineNumberCollection.FromUri(reader.BaseURI), "columnDefinition", reader.Name)); case XmlNodeType.EndElement: done = true; break; } } if (!done) { throw new WixException(WixErrors.ExpectedEndElement(SourceLineNumberCollection.FromUri(reader.BaseURI), "columnDefinition")); } } ColumnDefinition columnDefinition = new ColumnDefinition(name, type, length, primaryKey, nullable, modularize, localizable, minValueSet, minValue, maxValueSet, maxValue, keyTable, keyColumnSet, keyColumn, category, possibilities, description, escapeIdtCharacters, useCData); columnDefinition.Added = added; return(columnDefinition); }
/// <summary> /// Parses a column definition in a table definition. /// </summary> /// <param name="reader">Reader to get data from.</param> /// <returns>The ColumnDefintion represented by the Xml.</returns> internal static ColumnDefinition Parse(XmlReader reader) { Debug.Assert("columnDefinition" == reader.LocalName); string name = null; ColumnType type = ColumnType.Unknown; int length = -1; bool primaryKey = false; bool symbol = false; bool nullable = false; ColumnModularizeType modularize = ColumnModularizeType.None; bool unreal = false; bool localizable = false; bool minValueSet = false; int minValue = 0; bool maxValueSet = false; int maxValue = 0; string keyTable = null; bool keyColumnSet = false; int keyColumn = -1; ColumnCategory category = ColumnCategory.Unknown; string possibilities = null; string description = null; bool escapeIdtCharacters = false; bool useCData = false; bool empty = reader.IsEmptyElement; // parse the attributes while (reader.MoveToNextAttribute()) { switch (reader.LocalName) { case "name": name = reader.Value; break; case "type": switch (reader.Value) { case "string": type = ColumnType.String; break; case "localized": type = ColumnType.Localized; break; case "localizedNumber": type = ColumnType.LocalizedNumber; break; case "number": type = ColumnType.Number; break; case "object": type = ColumnType.Object; break; default: throw new WixParseException(String.Format("The columnDefinition/@type attribute contains an unexpected value '{0}'.", reader.Value)); } break; case "length": length = Convert.ToInt32(reader.Value, 10); break; case "primaryKey": primaryKey = Common.IsYes(reader.Value, null, "columnDefinition", "primaryKey", name); break; case "nullable": nullable = Common.IsYes(reader.Value, null, "columnDefinition", "nullable", name); break; case "modularize": switch (reader.Value) { case "none": modularize = ColumnModularizeType.None; break; 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 "icon": modularize = ColumnModularizeType.Icon; break; case "property": modularize = ColumnModularizeType.Property; break; case "semicolonDelimited": modularize = ColumnModularizeType.SemicolonDelimited; break; default: throw new WixParseException(String.Format("The columnDefinition/@modularize attribute contains an unexpected value '{0}'.", reader.Value)); } break; case "symbol": symbol = Common.IsYes(reader.Value, null, "columnDefinition", "symbol", name); break; case "unreal": unreal = Common.IsYes(reader.Value, null, "columnDefinition", "unreal", name); break; case "localizable": localizable = Common.IsYes(reader.Value, null, "columnDefinition", "localizable", name); break; case "minValue": minValueSet = true; minValue = Convert.ToInt32(reader.Value, 10); break; case "maxValue": maxValueSet = true; maxValue = Convert.ToInt32(reader.Value, 10); break; case "keyTable": keyTable = reader.Value; break; case "keyColumn": keyColumnSet = true; keyColumn = Convert.ToInt32(reader.Value, 10); break; case "category": switch (reader.Value) { case "text": category = ColumnCategory.Text; break; case "upperCase": category = ColumnCategory.UpperCase; break; case "lowerCase": category = ColumnCategory.LowerCase; break; case "integer": category = ColumnCategory.Integer; break; case "doubleInteger": category = ColumnCategory.DoubleInteger; break; case "timeDate": category = ColumnCategory.TimeDate; break; case "identifier": category = ColumnCategory.Identifier; break; case "property": category = ColumnCategory.Property; break; case "filename": category = ColumnCategory.Filename; break; case "wildCardFilename": category = ColumnCategory.WildCardFilename; break; case "path": category = ColumnCategory.Path; break; case "paths": category = ColumnCategory.Paths; break; case "anyPath": category = ColumnCategory.AnyPath; break; case "defaultDir": category = ColumnCategory.DefaultDir; break; case "regPath": category = ColumnCategory.RegPath; break; case "formatted": category = ColumnCategory.Formatted; break; case "template": category = ColumnCategory.Template; break; case "condition": category = ColumnCategory.Condition; break; case "guid": category = ColumnCategory.Guid; break; case "version": category = ColumnCategory.Version; break; case "language": category = ColumnCategory.Language; break; case "binary": category = ColumnCategory.Binary; break; case "customSource": category = ColumnCategory.CustomSource; break; case "cabinet": category = ColumnCategory.Cabinet; break; case "shortcut": category = ColumnCategory.Shortcut; break; default: throw new WixParseException(String.Format("The columnDefinition/@category attribute contains an unexpected value '{0}'.", reader.Value)); } break; case "set": possibilities = reader.Value; break; case "description": description = reader.Value; break; case "escapeIdtCharacters": escapeIdtCharacters = Common.IsYes(reader.Value, null, "columnDefinition", reader.LocalName, name); break; case "useCData": useCData = Common.IsYes(reader.Value, null, "columnDefinition", reader.LocalName, name); break; default: throw new WixParseException(String.Format("The columnDefinition element contains an unexpected attribute '{0}'.", reader.Name)); } } // 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 WixParseException(String.Format("The columnDefinition element contains an unexpected child element {0}.", reader.Name)); case XmlNodeType.EndElement: done = true; break; } } if (!done) { throw new WixParseException("Missing end element while processing the columnDefinition element."); } } return new ColumnDefinition(name, type, length, primaryKey, symbol, nullable, modularize, unreal, localizable, minValueSet, minValue, maxValueSet, maxValue, keyTable, keyColumnSet, keyColumn, category, possibilities, description, escapeIdtCharacters, useCData); }
/// <summary> /// Creates a new column definition. /// </summary> /// <param name="name">Name of column.</param> /// <param name="type">Type of column</param> /// <param name="length">Length of column.</param> /// <param name="primaryKey">If column is primary key.</param> /// <param name="symbol">If column is part of the symbol.</param> /// <param name="nullable">If column is nullable.</param> /// <param name="modularizeType">Type of modularization for column</param> /// <param name="unreal">If column is unreal/virtual.</param> /// <param name="localizable">If the column is localizable.</param> /// <param name="minValueSet">If the minimum of the value was set.</param> /// <param name="minValue">Minimum value for the column.</param> /// <param name="maxValueSet">If the maximum value was set.</param> /// <param name="maxValue">Maximum value for the colum.</param> /// <param name="keyTable">Optional name of table for foreign key.</param> /// <param name="keyColumnSet">If the key column was set.</param> /// <param name="keyColumn">Optional name of column for foreign key.</param> /// <param name="category">Validation category for column.</param> /// <param name="possibilities">Set of possible values for column.</param> /// <param name="description">Description of column in vaidation table.</param> /// <param name="escapeIdtCharacters">If characters should be escaped in IDT.</param> /// <param name="useCData">If whitespace should be preserved in a CDATA node.</param> public ColumnDefinition(string name, ColumnType type, int length, bool primaryKey, bool symbol, bool nullable, ColumnModularizeType modularizeType, bool unreal, bool localizable, bool minValueSet, int minValue, bool maxValueSet, int maxValue, string keyTable, bool keyColumnSet, int keyColumn, ColumnCategory category, string possibilities, string description, bool escapeIdtCharacters, bool useCData) { this.name = name; this.type = type; this.length = length; this.primaryKey = primaryKey; this.symbol = symbol; this.nullable = nullable; this.modularize = modularizeType; this.unreal = unreal; this.localizable = localizable; this.minValueSet = minValueSet; this.minValue = minValue; this.maxValueSet = maxValueSet; this.maxValue = maxValue; this.keyTable = keyTable; this.keyColumnSet = keyColumnSet; this.keyColumn = keyColumn; this.category = category; this.possibilities = possibilities; this.description = description; this.escapeIdtCharacters = escapeIdtCharacters; this.useCData = useCData; }
public Output Execute() { string modularizationGuid = null; Output output = new Output(new SourceLineNumber(this.DatabasePath)); View validationView = null; // set the output type output.Type = this.OutputType; // get the codepage this.Database.Export("_ForceCodepage", this.IntermediateFolder, "_ForceCodepage.idt"); using (StreamReader sr = File.OpenText(Path.Combine(this.IntermediateFolder, "_ForceCodepage.idt"))) { string line; while (null != (line = sr.ReadLine())) { string[] data = line.Split('\t'); if (2 == data.Length) { output.Codepage = Convert.ToInt32(data[0], CultureInfo.InvariantCulture); } } } // get the summary information table if it exists; it won't if unbinding a transform if (!this.SkipSummaryInfo) { using (SummaryInformation summaryInformation = new SummaryInformation(this.Database)) { Table table = new Table(this.TableDefinitions["_SummaryInformation"]); for (int i = 1; 19 >= i; i++) { string value = summaryInformation.GetProperty(i); if (0 < value.Length) { Row row = table.CreateRow(output.SourceLineNumbers); row[0] = i; row[1] = value; } } output.Tables.Add(table); } } try { // open a view on the validation table if it exists if (this.Database.TableExists("_Validation")) { validationView = this.Database.OpenView("SELECT * FROM `_Validation` WHERE `Table` = ? AND `Column` = ?"); } // get the normal tables using (View tablesView = this.Database.OpenExecuteView("SELECT * FROM _Tables")) { while (true) { using (Record tableRecord = tablesView.Fetch()) { if (null == tableRecord) { break; } string tableName = tableRecord.GetString(1); using (View tableView = this.Database.OpenExecuteView(String.Format(CultureInfo.InvariantCulture, "SELECT * FROM `{0}`", tableName))) { ColumnDefinition[] columns; using (Record columnNameRecord = tableView.GetColumnInfo(MsiInterop.MSICOLINFONAMES), columnTypeRecord = tableView.GetColumnInfo(MsiInterop.MSICOLINFOTYPES)) { // index the primary keys HashSet <string> tablePrimaryKeys = new HashSet <string>(); using (Record primaryKeysRecord = this.Database.PrimaryKeys(tableName)) { int primaryKeysFieldCount = primaryKeysRecord.GetFieldCount(); for (int i = 1; i <= primaryKeysFieldCount; i++) { tablePrimaryKeys.Add(primaryKeysRecord.GetString(i)); } } int columnCount = columnNameRecord.GetFieldCount(); columns = new ColumnDefinition[columnCount]; for (int i = 1; i <= columnCount; i++) { string columnName = columnNameRecord.GetString(i); string idtType = columnTypeRecord.GetString(i); ColumnType columnType; int length; bool nullable; ColumnCategory columnCategory = ColumnCategory.Unknown; ColumnModularizeType columnModularizeType = ColumnModularizeType.None; bool primary = tablePrimaryKeys.Contains(columnName); int? minValue = null; int? maxValue = null; string keyTable = null; int? keyColumn = null; string category = null; string set = null; string description = null; // get the column type, length, and whether its nullable switch (Char.ToLower(idtType[0], CultureInfo.InvariantCulture)) { case 'i': columnType = ColumnType.Number; break; case 'l': columnType = ColumnType.Localized; break; case 's': columnType = ColumnType.String; break; case 'v': columnType = ColumnType.Object; break; default: // TODO: error columnType = ColumnType.Unknown; break; } length = Convert.ToInt32(idtType.Substring(1), CultureInfo.InvariantCulture); nullable = Char.IsUpper(idtType[0]); // try to get validation information if (null != validationView) { using (Record validationRecord = new Record(2)) { validationRecord.SetString(1, tableName); validationRecord.SetString(2, columnName); validationView.Execute(validationRecord); } using (Record validationRecord = validationView.Fetch()) { if (null != validationRecord) { string validationNullable = validationRecord.GetString(3); minValue = validationRecord.IsNull(4) ? null : (int?)validationRecord.GetInteger(4); maxValue = validationRecord.IsNull(5) ? null : (int?)validationRecord.GetInteger(5); keyTable = validationRecord.IsNull(6) ? null : validationRecord.GetString(6); keyColumn = validationRecord.IsNull(7) ? null : (int?)validationRecord.GetInteger(7); category = validationRecord.IsNull(8) ? null : validationRecord.GetString(8); set = validationRecord.IsNull(9) ? null : validationRecord.GetString(9); description = validationRecord.IsNull(10) ? null : validationRecord.GetString(10); // check the validation nullable value against the column definition if (null == validationNullable) { // TODO: warn for illegal validation nullable column } else if ((nullable && "Y" != validationNullable) || (!nullable && "N" != validationNullable)) { // TODO: warn for mismatch between column definition and validation nullable } // convert category to ColumnCategory if (null != category) { try { columnCategory = (ColumnCategory)Enum.Parse(typeof(ColumnCategory), category, true); } catch (ArgumentException) { columnCategory = ColumnCategory.Unknown; } } } else { // TODO: warn about no validation information } } } // guess the modularization type if ("Icon" == keyTable && 1 == keyColumn) { columnModularizeType = ColumnModularizeType.Icon; } else if ("Condition" == columnName) { columnModularizeType = ColumnModularizeType.Condition; } else if (ColumnCategory.Formatted == columnCategory || ColumnCategory.FormattedSDDLText == columnCategory) { columnModularizeType = ColumnModularizeType.Property; } else if (ColumnCategory.Identifier == columnCategory) { columnModularizeType = ColumnModularizeType.Column; } columns[i - 1] = new ColumnDefinition(columnName, columnType, length, primary, nullable, columnCategory, minValue, maxValue, keyTable, keyColumn, set, description, columnModularizeType, (ColumnType.Localized == columnType), true); } } TableDefinition tableDefinition = new TableDefinition(tableName, columns, false, false); // use our table definitions if core properties are the same; this allows us to take advantage // of wix concepts like localizable columns which current code assumes if (this.TableDefinitions.Contains(tableName) && 0 == tableDefinition.CompareTo(this.TableDefinitions[tableName])) { tableDefinition = this.TableDefinitions[tableName]; } Table table = new Table(tableDefinition); while (true) { using (Record rowRecord = tableView.Fetch()) { if (null == rowRecord) { break; } int recordCount = rowRecord.GetFieldCount(); Row row = table.CreateRow(output.SourceLineNumbers); for (int i = 0; recordCount > i && row.Fields.Length > i; i++) { if (rowRecord.IsNull(i + 1)) { if (!row.Fields[i].Column.Nullable) { // TODO: display an error for a null value in a non-nullable field OR // display a warning and put an empty string in the value to let the compiler handle it // (the second option is risky because the later code may make certain assumptions about // the contents of a row value) } } else { switch (row.Fields[i].Column.Type) { case ColumnType.Number: bool success = false; int intValue = rowRecord.GetInteger(i + 1); if (row.Fields[i].Column.IsLocalizable) { success = row.BestEffortSetField(i, Convert.ToString(intValue, CultureInfo.InvariantCulture)); } else { success = row.BestEffortSetField(i, intValue); } if (!success) { this.Messaging.Write(WarningMessages.BadColumnDataIgnored(row.SourceLineNumbers, Convert.ToString(intValue, CultureInfo.InvariantCulture), tableName, row.Fields[i].Column.Name)); } break; case ColumnType.Object: string sourceFile = "FILE NOT EXPORTED, USE THE dark.exe -x OPTION TO EXPORT BINARIES"; if (null != this.ExportBasePath) { string relativeSourceFile = Path.Combine(tableName, row.GetPrimaryKey('.')); sourceFile = Path.Combine(this.ExportBasePath, relativeSourceFile); // ensure the parent directory exists System.IO.Directory.CreateDirectory(Path.Combine(this.ExportBasePath, tableName)); using (FileStream fs = System.IO.File.Create(sourceFile)) { int bytesRead; byte[] buffer = new byte[512]; while (0 != (bytesRead = rowRecord.GetStream(i + 1, buffer, buffer.Length))) { fs.Write(buffer, 0, bytesRead); } } } row[i] = sourceFile; break; default: string value = rowRecord.GetString(i + 1); switch (row.Fields[i].Column.Category) { case ColumnCategory.Guid: value = value.ToUpper(CultureInfo.InvariantCulture); break; } // de-modularize if (!this.SuppressDemodularization && OutputType.Module == output.Type && ColumnModularizeType.None != row.Fields[i].Column.ModularizeType) { Regex modularization = new Regex(@"\.[0-9A-Fa-f]{8}_[0-9A-Fa-f]{4}_[0-9A-Fa-f]{4}_[0-9A-Fa-f]{4}_[0-9A-Fa-f]{12}"); if (null == modularizationGuid) { Match match = modularization.Match(value); if (match.Success) { modularizationGuid = String.Concat('{', match.Value.Substring(1).Replace('_', '-'), '}'); } } value = modularization.Replace(value, String.Empty); } // escape "$(" for the preprocessor value = value.Replace("$(", "$$("); // escape things that look like wix variables MatchCollection matches = Common.WixVariableRegex.Matches(value); for (int j = matches.Count - 1; 0 <= j; j--) { value = value.Insert(matches[j].Index, "!"); } row[i] = value; break; } } } } } output.Tables.Add(table); } } } } } finally { if (null != validationView) { validationView.Close(); } } // set the modularization guid as the PackageCode if (null != modularizationGuid) { Table table = output.Tables["_SummaryInformation"]; foreach (Row row in table.Rows) { if (9 == (int)row[0]) // PID_REVNUMBER { row[1] = modularizationGuid; } } } if (this.IsAdminImage) { GenerateWixFileTable(this.DatabasePath, output); GenerateSectionIds(output); } return(output); }
/// <summary> /// Parses a column definition in a table definition. /// </summary> /// <param name="reader">Reader to get data from.</param> /// <returns>The ColumnDefintion represented by the Xml.</returns> internal static ColumnDefinition Parse(XmlReader reader) { Debug.Assert("columnDefinition" == reader.LocalName); string name = null; ColumnType type = ColumnType.Unknown; int length = -1; bool primaryKey = false; bool symbol = false; bool nullable = false; ColumnModularizeType modularize = ColumnModularizeType.None; bool unreal = false; bool localizable = false; bool minValueSet = false; int minValue = 0; bool maxValueSet = false; int maxValue = 0; string keyTable = null; bool keyColumnSet = false; int keyColumn = -1; ColumnCategory category = ColumnCategory.Unknown; string possibilities = null; string description = null; bool escapeIdtCharacters = false; bool useCData = false; bool empty = reader.IsEmptyElement; // parse the attributes while (reader.MoveToNextAttribute()) { switch (reader.LocalName) { case "name": name = reader.Value; break; case "type": switch (reader.Value) { case "string": type = ColumnType.String; break; case "localized": type = ColumnType.Localized; break; case "localizedNumber": type = ColumnType.LocalizedNumber; break; case "number": type = ColumnType.Number; break; case "object": type = ColumnType.Object; break; default: throw new WixParseException(String.Format("The columnDefinition/@type attribute contains an unexpected value '{0}'.", reader.Value)); } break; case "length": length = Convert.ToInt32(reader.Value, 10); break; case "primaryKey": primaryKey = Common.IsYes(reader.Value, null, "columnDefinition", "primaryKey", name); break; case "nullable": nullable = Common.IsYes(reader.Value, null, "columnDefinition", "nullable", name); break; case "modularize": switch (reader.Value) { case "none": modularize = ColumnModularizeType.None; break; 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 "icon": modularize = ColumnModularizeType.Icon; break; case "property": modularize = ColumnModularizeType.Property; break; case "semicolonDelimited": modularize = ColumnModularizeType.SemicolonDelimited; break; default: throw new WixParseException(String.Format("The columnDefinition/@modularize attribute contains an unexpected value '{0}'.", reader.Value)); } break; case "symbol": symbol = Common.IsYes(reader.Value, null, "columnDefinition", "symbol", name); break; case "unreal": unreal = Common.IsYes(reader.Value, null, "columnDefinition", "unreal", name); break; case "localizable": localizable = Common.IsYes(reader.Value, null, "columnDefinition", "localizable", name); break; case "minValue": minValueSet = true; minValue = Convert.ToInt32(reader.Value, 10); break; case "maxValue": maxValueSet = true; maxValue = Convert.ToInt32(reader.Value, 10); break; case "keyTable": keyTable = reader.Value; break; case "keyColumn": keyColumnSet = true; keyColumn = Convert.ToInt32(reader.Value, 10); break; case "category": switch (reader.Value) { case "text": category = ColumnCategory.Text; break; case "upperCase": category = ColumnCategory.UpperCase; break; case "lowerCase": category = ColumnCategory.LowerCase; break; case "integer": category = ColumnCategory.Integer; break; case "doubleInteger": category = ColumnCategory.DoubleInteger; break; case "timeDate": category = ColumnCategory.TimeDate; break; case "identifier": category = ColumnCategory.Identifier; break; case "property": category = ColumnCategory.Property; break; case "filename": category = ColumnCategory.Filename; break; case "wildCardFilename": category = ColumnCategory.WildCardFilename; break; case "path": category = ColumnCategory.Path; break; case "paths": category = ColumnCategory.Paths; break; case "anyPath": category = ColumnCategory.AnyPath; break; case "defaultDir": category = ColumnCategory.DefaultDir; break; case "regPath": category = ColumnCategory.RegPath; break; case "formatted": category = ColumnCategory.Formatted; break; case "template": category = ColumnCategory.Template; break; case "condition": category = ColumnCategory.Condition; break; case "guid": category = ColumnCategory.Guid; break; case "version": category = ColumnCategory.Version; break; case "language": category = ColumnCategory.Language; break; case "binary": category = ColumnCategory.Binary; break; case "customSource": category = ColumnCategory.CustomSource; break; case "cabinet": category = ColumnCategory.Cabinet; break; case "shortcut": category = ColumnCategory.Shortcut; break; default: throw new WixParseException(String.Format("The columnDefinition/@category attribute contains an unexpected value '{0}'.", reader.Value)); } break; case "set": possibilities = reader.Value; break; case "description": description = reader.Value; break; case "escapeIdtCharacters": escapeIdtCharacters = Common.IsYes(reader.Value, null, "columnDefinition", reader.LocalName, name); break; case "useCData": useCData = Common.IsYes(reader.Value, null, "columnDefinition", reader.LocalName, name); break; default: throw new WixParseException(String.Format("The columnDefinition element contains an unexpected attribute '{0}'.", reader.Name)); } } // 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 WixParseException(String.Format("The columnDefinition element contains an unexpected child element {0}.", reader.Name)); case XmlNodeType.EndElement: done = true; break; } } if (!done) { throw new WixParseException("Missing end element while processing the columnDefinition element."); } } return(new ColumnDefinition(name, type, length, primaryKey, symbol, nullable, modularize, unreal, localizable, minValueSet, minValue, maxValueSet, maxValue, keyTable, keyColumnSet, keyColumn, category, possibilities, description, escapeIdtCharacters, useCData)); }
/// <summary> /// Unbind an MSI database file. /// </summary> /// <param name="databaseFile">The database file.</param> /// <param name="database">The opened database.</param> /// <param name="outputType">The type of output to create.</param> /// <param name="exportBasePath">The path where files should be exported.</param> /// <returns>The output representing the database.</returns> private Output UnbindDatabase(string databaseFile, Database database, OutputType outputType, string exportBasePath) { string modularizationGuid = null; Output output = new Output(SourceLineNumberCollection.FromFileName(databaseFile)); View validationView = null; // set the output type output.Type = outputType; // get the codepage database.Export("_ForceCodepage", this.TempFilesLocation, "_ForceCodepage.idt"); using (StreamReader sr = File.OpenText(Path.Combine(this.TempFilesLocation, "_ForceCodepage.idt"))) { string line; while (null != (line = sr.ReadLine())) { string[] data = line.Split('\t'); if (2 == data.Length) { output.Codepage = Convert.ToInt32(data[0], CultureInfo.InvariantCulture); } } } // get the summary information table using (SummaryInformation summaryInformation = new SummaryInformation(database)) { Table table = new Table(null, this.tableDefinitions["_SummaryInformation"]); for (int i = 1; 19 >= i; i++) { string value = summaryInformation.GetProperty(i); if (0 < value.Length) { Row row = table.CreateRow(output.SourceLineNumbers); row[0] = i; row[1] = value; } } output.Tables.Add(table); } try { // open a view on the validation table if it exists if (database.TableExists("_Validation")) { validationView = database.OpenView("SELECT * FROM `_Validation` WHERE `Table` = ? AND `Column` = ?"); } // get the normal tables using (View tablesView = database.OpenExecuteView("SELECT * FROM _Tables")) { Record tableRecord; while (null != (tableRecord = tablesView.Fetch())) { string tableName = tableRecord.GetString(1); using (View tableView = database.OpenExecuteView(String.Format(CultureInfo.InvariantCulture, "SELECT * FROM `{0}`", tableName))) { Record rowRecord; Table table; TableDefinition tableDefinition; if (this.tableDefinitions.Contains(tableName)) { tableDefinition = this.tableDefinitions[tableName]; // TODO: verify table definitions // - error for different column name or data type // - warn for additional columns // - need extra info for new columns in standard tables (MSI 4.0 changes) } else // custom table { TableDefinition customTableDefinition = new TableDefinition(tableName, false, false); Hashtable customTablePrimaryKeys = new Hashtable(); Record customColumnNameRecord = tableView.GetColumnInfo(MsiInterop.MSICOLINFONAMES); Record customColumnTypeRecord = tableView.GetColumnInfo(MsiInterop.MSICOLINFOTYPES); int customColumnCount = customColumnNameRecord.GetFieldCount(); // index the primary keys using (Record primaryKeysRecord = database.PrimaryKeys(tableName)) { int primaryKeysFieldCount = primaryKeysRecord.GetFieldCount(); for (int i = 1; i <= primaryKeysFieldCount; i++) { customTablePrimaryKeys[primaryKeysRecord.GetString(i)] = null; } } for (int i = 1; i <= customColumnCount; i++) { string columnName = customColumnNameRecord.GetString(i); string idtType = customColumnTypeRecord.GetString(i); ColumnType columnType; int length; bool nullable; ColumnCategory columnCategory = ColumnCategory.Unknown; ColumnModularizeType columnModularizeType = ColumnModularizeType.None; bool primary = customTablePrimaryKeys.Contains(columnName); bool minValueSet = false; int minValue = -1; bool maxValueSet = false; int maxValue = -1; string keyTable = null; bool keyColumnSet = false; int keyColumn = -1; string category = null; string set = null; string description = null; // get the column type, length, and whether its nullable switch (Char.ToLower(idtType[0], CultureInfo.InvariantCulture)) { case 'i': columnType = ColumnType.Number; break; case 'l': columnType = ColumnType.Localized; break; case 's': columnType = ColumnType.String; break; case 'v': columnType = ColumnType.Object; break; default: // TODO: error columnType = ColumnType.Unknown; break; } length = Convert.ToInt32(idtType.Substring(1), CultureInfo.InvariantCulture); nullable = Char.IsUpper(idtType[0]); // try to get validation information if (null != validationView) { Record validationRecord = new Record(2); validationRecord.SetString(1, tableName); validationRecord.SetString(2, columnName); validationView.Execute(validationRecord); validationRecord.Close(); if (null != (validationRecord = validationView.Fetch())) { string validationNullable = validationRecord.GetString(3); minValueSet = !validationRecord.IsNull(4); minValue = (minValueSet ? validationRecord.GetInteger(4) : -1); maxValueSet = !validationRecord.IsNull(5); maxValue = (maxValueSet ? validationRecord.GetInteger(5) : -1); keyTable = (!validationRecord.IsNull(6) ? validationRecord.GetString(6) : null); keyColumnSet = !validationRecord.IsNull(7); keyColumn = (keyColumnSet ? validationRecord.GetInteger(7) : -1); category = (!validationRecord.IsNull(8) ? validationRecord.GetString(8) : null); set = (!validationRecord.IsNull(9) ? validationRecord.GetString(9) : null); description = (!validationRecord.IsNull(10) ? validationRecord.GetString(10) : null); // check the validation nullable value against the column definition if (null == validationNullable) { // TODO: warn for illegal validation nullable column } else if ((nullable && "Y" != validationNullable) || (!nullable && "N" != validationNullable)) { // TODO: warn for mismatch between column definition and validation nullable } // convert category to ColumnCategory if (null != category) { try { columnCategory = (ColumnCategory)Enum.Parse(typeof(ColumnCategory), category, true); } catch (ArgumentException) { columnCategory = ColumnCategory.Unknown; } } validationRecord.Close(); } else { // TODO: warn about no validation information } } // guess the modularization type if ("Icon" == keyTable && 1 == keyColumn) { columnModularizeType = ColumnModularizeType.Icon; } else if ("Condition" == columnName) { columnModularizeType = ColumnModularizeType.Condition; } else if (ColumnCategory.Formatted == columnCategory) { columnModularizeType = ColumnModularizeType.Property; } else if (ColumnCategory.Identifier == columnCategory) { columnModularizeType = ColumnModularizeType.Column; } customTableDefinition.Columns.Add(new ColumnDefinition(columnName, columnType, length, primary, nullable, columnModularizeType, (ColumnType.Localized == columnType), minValueSet, minValue, maxValueSet, maxValue, keyTable, keyColumnSet, keyColumn, columnCategory, set, description, true, true)); } tableDefinition = customTableDefinition; customColumnNameRecord.Close(); customColumnTypeRecord.Close(); } table = new Table(null, tableDefinition); while (null != (rowRecord = tableView.Fetch())) { int recordCount = rowRecord.GetFieldCount(); Row row = table.CreateRow(output.SourceLineNumbers); for (int i = 0; recordCount > i && row.Fields.Length > i; i++) { if (rowRecord.IsNull(i + 1)) { if (!row.Fields[i].Column.IsNullable) { // TODO: display an error for a null value in a non-nullable field OR // display a warning and put an empty string in the value to let the compiler handle it // (the second option is risky because the later code may make certain assumptions about // the contents of a row value) } } else { switch (row.Fields[i].Column.Type) { case ColumnType.Number: if (row.Fields[i].Column.IsLocalizable) { row[i] = Convert.ToString(rowRecord.GetInteger(i + 1), CultureInfo.InvariantCulture); } else { row[i] = rowRecord.GetInteger(i + 1); } break; case ColumnType.Object: string sourceFile = "FILE NOT EXPORTED, USE THE dark.exe -x OPTION TO EXPORT BINARIES"; if (null != exportBasePath) { string relativeSourceFile = Path.Combine(tableName, row.GetPrimaryKey('.')); sourceFile = Path.Combine(exportBasePath, relativeSourceFile); // ensure the parent directory exists System.IO.Directory.CreateDirectory(Path.Combine(exportBasePath, tableName)); using (FileStream fs = System.IO.File.Create(sourceFile)) { int bytesRead; byte[] buffer = new byte[512]; while (0 != (bytesRead = rowRecord.GetStream(i + 1, buffer, buffer.Length))) { fs.Write(buffer, 0, bytesRead); } } } row[i] = sourceFile; break; default: string value = rowRecord.GetString(i + 1); switch (row.Fields[i].Column.Category) { case ColumnCategory.Guid: value = value.ToUpper(CultureInfo.InvariantCulture); break; } // de-modularize if (!this.suppressDemodularization && OutputType.Module == output.Type && ColumnModularizeType.None != row.Fields[i].Column.ModularizeType) { Regex modularization = new Regex(@"\.[0-9A-Fa-f]{8}_[0-9A-Fa-f]{4}_[0-9A-Fa-f]{4}_[0-9A-Fa-f]{4}_[0-9A-Fa-f]{12}"); if (null == modularizationGuid) { Match match = modularization.Match(value); if (match.Success) { modularizationGuid = String.Concat('{', match.Value.Substring(1).Replace('_', '-'), '}'); } } value = modularization.Replace(value, String.Empty); } // escape "$(" for the preprocessor value = value.Replace("$(", "$$("); // escape things that look like wix variables MatchCollection matches = Common.WixVariableRegex.Matches(value); for (int j = matches.Count - 1; 0 <= j; j--) { value = value.Insert(matches[j].Index, "!"); } row[i] = value; break; } } } rowRecord.Close(); } output.Tables.Add(table); } tableRecord.Close(); } } } finally { if (null != validationView) { validationView.Close(); } } // set the modularization guid as the PackageCode if (null != modularizationGuid) { Table table = output.Tables["_SummaryInformation"]; foreach (Row row in table.Rows) { if (9 == (int)row[0]) // PID_REVNUMBER { row[1] = modularizationGuid; } } } return(output); }