From 49f1209035aac1fcfad5dbbe25f7b2306d3be86c Mon Sep 17 00:00:00 2001 From: Rob Mensching Date: Thu, 7 Dec 2017 14:19:05 -0800 Subject: Support MSI backends creating custom tables and remove WixToolset.Data.WindowsInstaller --- .../Bind/BindDatabaseCommand.cs | 18 +- .../Bind/BindTransformCommand.cs | 1 + .../Bind/CopyTransformDataCommand.cs | 9 +- .../Bind/CreateCabinetsCommand.cs | 3 +- .../Bind/CreateIdtFileCommand.cs | 228 ++++++++++++++++++++ .../Bind/CreateOutputFromIRCommand.cs | 25 ++- .../Bind/GenerateDatabaseCommand.cs | 55 ++++- .../Bind/MergeModulesCommand.cs | 7 +- .../Bind/ModularaizeCommand.cs | 238 +++++++++++++++++++++ .../Bind/UpdateControlTextCommand.cs | 3 +- .../Bind/UpdateFileFacadesCommand.cs | 1 + .../Bind/UpdateMediaSequencesCommand.cs | 4 +- 12 files changed, 564 insertions(+), 28 deletions(-) create mode 100644 src/WixToolset.Core.WindowsInstaller/Bind/CreateIdtFileCommand.cs create mode 100644 src/WixToolset.Core.WindowsInstaller/Bind/ModularaizeCommand.cs (limited to 'src/WixToolset.Core.WindowsInstaller/Bind') diff --git a/src/WixToolset.Core.WindowsInstaller/Bind/BindDatabaseCommand.cs b/src/WixToolset.Core.WindowsInstaller/Bind/BindDatabaseCommand.cs index 012998e6..9e30aed2 100644 --- a/src/WixToolset.Core.WindowsInstaller/Bind/BindDatabaseCommand.cs +++ b/src/WixToolset.Core.WindowsInstaller/Bind/BindDatabaseCommand.cs @@ -10,6 +10,7 @@ namespace WixToolset.Core.WindowsInstaller.Bind using WixToolset.Data; using WixToolset.Data.Bind; using WixToolset.Data.Tuples; + using WixToolset.Data.WindowsInstaller; using WixToolset.Extensibility; using WixToolset.Extensibility.Services; @@ -21,7 +22,7 @@ namespace WixToolset.Core.WindowsInstaller.Bind // As outlined in RFC 4122, this is our namespace for generating name-based (version 3) UUIDs. internal static readonly Guid WixComponentGuidNamespace = new Guid("{3064E5C6-FB63-4FE9-AC49-E446A792EFA5}"); - public BindDatabaseCommand(IBindContext context, Validator validator) + public BindDatabaseCommand(IBindContext context, IEnumerable backendExtension, Validator validator) { this.TableDefinitions = WindowsInstallerStandardInternal.GetTableDefinitions(); @@ -40,7 +41,7 @@ namespace WixToolset.Core.WindowsInstaller.Bind this.Validator = validator; this.WixVariableResolver = context.WixVariableResolver; - this.BackendExtensions = context.ExtensionManager.Create(); + this.BackendExtensions = backendExtension; } private IEnumerable BindPaths { get; } @@ -298,7 +299,7 @@ namespace WixToolset.Core.WindowsInstaller.Bind // Try to put as much above here as possible, updating the IR is better. Output output; { - var command = new CreateOutputFromIRCommand(section, this.TableDefinitions); + var command = new CreateOutputFromIRCommand(section, this.TableDefinitions, this.BackendExtensions); command.Execute(); output = command.Output; @@ -313,13 +314,8 @@ namespace WixToolset.Core.WindowsInstaller.Bind // Modularize identifiers and add tables with real streams to the import tables. if (OutputType.Module == output.Type) { - // Gather all the suppress modularization identifiers - var suppressModularizationIdentifiers = new HashSet(section.Tuples.OfType().Select(s => s.WixSuppressModularization)); - - foreach (var table in output.Tables) - { - table.Modularize(modularizationGuid, suppressModularizationIdentifiers); - } + var command = new ModularaizeCommand(output, modularizationGuid, section.Tuples.OfType()); + command.Execute(); } #if TODO_FINISH_UPDATE @@ -897,7 +893,7 @@ namespace WixToolset.Core.WindowsInstaller.Bind { Dictionary componentGuidConditions = new Dictionary(componentTable.Rows.Count); - foreach (Data.Rows.ComponentRow row in componentTable.Rows) + foreach (Data.WindowsInstaller.Rows.ComponentRow row in componentTable.Rows) { // we don't care about unmanaged components and if there's a * GUID remaining, // there's already an error that prevented it from being replaced with a real GUID. diff --git a/src/WixToolset.Core.WindowsInstaller/Bind/BindTransformCommand.cs b/src/WixToolset.Core.WindowsInstaller/Bind/BindTransformCommand.cs index b4027834..49440cea 100644 --- a/src/WixToolset.Core.WindowsInstaller/Bind/BindTransformCommand.cs +++ b/src/WixToolset.Core.WindowsInstaller/Bind/BindTransformCommand.cs @@ -10,6 +10,7 @@ namespace WixToolset.Core.WindowsInstaller.Bind using WixToolset.Extensibility; using WixToolset.Msi; using WixToolset.Core.Native; + using WixToolset.Data.WindowsInstaller; internal class BindTransformCommand { diff --git a/src/WixToolset.Core.WindowsInstaller/Bind/CopyTransformDataCommand.cs b/src/WixToolset.Core.WindowsInstaller/Bind/CopyTransformDataCommand.cs index 0dcddb99..559d440c 100644 --- a/src/WixToolset.Core.WindowsInstaller/Bind/CopyTransformDataCommand.cs +++ b/src/WixToolset.Core.WindowsInstaller/Bind/CopyTransformDataCommand.cs @@ -5,12 +5,13 @@ namespace WixToolset.Core.WindowsInstaller.Bind using System; using System.Collections.Generic; using System.Diagnostics; - using WixToolset.Data; - using WixToolset.Data.Rows; - using WixToolset.Extensibility; - using WixToolset.Core.Native; using WixToolset.Core.Bind; + using WixToolset.Core.Native; + using WixToolset.Data; using WixToolset.Data.Tuples; + using WixToolset.Data.WindowsInstaller; + using WixToolset.Data.WindowsInstaller.Rows; + using WixToolset.Extensibility; internal class CopyTransformDataCommand { diff --git a/src/WixToolset.Core.WindowsInstaller/Bind/CreateCabinetsCommand.cs b/src/WixToolset.Core.WindowsInstaller/Bind/CreateCabinetsCommand.cs index a449397d..0aa50bd9 100644 --- a/src/WixToolset.Core.WindowsInstaller/Bind/CreateCabinetsCommand.cs +++ b/src/WixToolset.Core.WindowsInstaller/Bind/CreateCabinetsCommand.cs @@ -12,8 +12,9 @@ namespace WixToolset.Core.WindowsInstaller.Bind using WixToolset.Core.Bind; using WixToolset.Data; using WixToolset.Data.Bind; - using WixToolset.Data.Rows; using WixToolset.Data.Tuples; + using WixToolset.Data.WindowsInstaller; + using WixToolset.Data.WindowsInstaller.Rows; using WixToolset.Extensibility; /// diff --git a/src/WixToolset.Core.WindowsInstaller/Bind/CreateIdtFileCommand.cs b/src/WixToolset.Core.WindowsInstaller/Bind/CreateIdtFileCommand.cs new file mode 100644 index 00000000..1fc7d068 --- /dev/null +++ b/src/WixToolset.Core.WindowsInstaller/Bind/CreateIdtFileCommand.cs @@ -0,0 +1,228 @@ +// 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.Core.WindowsInstaller.Bind +{ + using System; + using System.Globalization; + using System.IO; + using System.Text; + using WixToolset.Data; + using WixToolset.Data.WindowsInstaller; + + internal class CreateIdtFileCommand + { + public CreateIdtFileCommand(Table table, int codepage, string intermediateFolder, bool keepAddedColumns) + { + this.Table = table; + this.Codepage = codepage; + this.IntermediateFolder = intermediateFolder; + this.KeepAddedColumns = keepAddedColumns; + } + + private Table Table { get; } + + private int Codepage { get; set; } + + private string IntermediateFolder { get; } + + private bool KeepAddedColumns { get; } + + public string IdtPath { get; private set; } + + public void Execute() + { + // write out the table to an IDT file + Encoding encoding; + + // If UTF8 encoding, use the UTF8-specific constructor to avoid writing + // the byte order mark at the beginning of the file + if (this.Codepage == Encoding.UTF8.CodePage) + { + encoding = new UTF8Encoding(false, true); + } + else + { + if (this.Codepage == 0) + { + this.Codepage = Encoding.ASCII.CodePage; + } + + encoding = Encoding.GetEncoding(this.Codepage, new EncoderExceptionFallback(), new DecoderExceptionFallback()); + } + + this.IdtPath = Path.Combine(this.IntermediateFolder, String.Concat(this.Table.Name, ".idt")); + + using (var idtWriter = new StreamWriter(this.IdtPath, false, encoding)) + { + this.TableToIdtDefinition(this.Table, idtWriter, this.KeepAddedColumns); + } + } + + private void TableToIdtDefinition(Table table, StreamWriter writer, bool keepAddedColumns) + { + if (table.Definition.Unreal) + { + return; + } + + if (TableDefinition.MaxColumnsInRealTable < table.Definition.Columns.Count) + { + throw new WixException(WixDataErrors.TooManyColumnsInRealTable(table.Definition.Name, table.Definition.Columns.Count, TableDefinition.MaxColumnsInRealTable)); + } + + // Tack on the table header, and flush before we start writing bytes directly to the stream. + var header = this.TableDefinitionToIdtDefinition(table.Definition, keepAddedColumns); + writer.Write(header); + writer.Flush(); + + using (var binary = new BinaryWriter(writer.BaseStream, writer.Encoding, true)) + { + // Create an encoding that replaces characters with question marks, and doesn't throw. We'll + // use this in case of errors + Encoding convertEncoding = Encoding.GetEncoding(writer.Encoding.CodePage); + + foreach (Row row in table.Rows) + { + if (row.Redundant) + { + continue; + } + + string rowString = this.RowToIdtDefinition(row, keepAddedColumns); + byte[] rowBytes; + + try + { + // GetBytes will throw an exception if any character doesn't match our current encoding + rowBytes = writer.Encoding.GetBytes(rowString); + } + catch (EncoderFallbackException) + { + Messaging.Instance.OnMessage(WixDataErrors.InvalidStringForCodepage(row.SourceLineNumbers, Convert.ToString(writer.Encoding.WindowsCodePage, CultureInfo.InvariantCulture))); + + rowBytes = convertEncoding.GetBytes(rowString); + } + + binary.Write(rowBytes, 0, rowBytes.Length); + } + } + } + + private string TableDefinitionToIdtDefinition(TableDefinition definition, bool keepAddedColumns) + { + var first = true; + var columnString = new StringBuilder(); + var dataString = new StringBuilder(); + var tableString = new StringBuilder(); + + tableString.Append(definition.Name); + foreach (var column in definition.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(ColumnIdtType(column)); + + 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(); + } + + private string RowToIdtDefinition(Row row, bool keepAddedColumns) + { + var first = true; + var sb = new StringBuilder(); + + foreach (var field in row.Fields) + { + // Conditionally keep columns added in a transform; otherwise, + // break because columns can only be added at the end. + if (field.Column.Added && !keepAddedColumns) + { + break; + } + + if (first) + { + first = false; + } + else + { + sb.Append('\t'); + } + + sb.Append(this.FieldToIdtValue(field)); + } + sb.Append("\r\n"); + + return sb.ToString(); + } + + private string FieldToIdtValue(Field field) + { + var data = field.AsString(); + + if (String.IsNullOrEmpty(data)) + { + return data; + } + + // Special field value idt-specific escaping. + return data.Replace('\t', '\x10') + .Replace('\r', '\x11') + .Replace('\n', '\x19'); + } + + + /// + /// Gets the type of the column in IDT format. + /// + /// IDT format for column type. + private static string ColumnIdtType(ColumnDefinition column) + { + char typeCharacter; + switch (column.Type) + { + case ColumnType.Number: + typeCharacter = column.Nullable ? 'I' : 'i'; + break; + case ColumnType.Preserved: + case ColumnType.String: + typeCharacter = column.Nullable ? 'S' : 's'; + break; + case ColumnType.Localized: + typeCharacter = column.Nullable ? 'L' : 'l'; + break; + case ColumnType.Object: + typeCharacter = column.Nullable ? 'V' : 'v'; + break; + default: + throw new InvalidOperationException(String.Format(CultureInfo.CurrentUICulture, WixDataStrings.EXP_UnknownColumnType, column.Type)); + } + + return String.Concat(typeCharacter, column.Length); + } + } +} diff --git a/src/WixToolset.Core.WindowsInstaller/Bind/CreateOutputFromIRCommand.cs b/src/WixToolset.Core.WindowsInstaller/Bind/CreateOutputFromIRCommand.cs index a19a53f1..4e053c12 100644 --- a/src/WixToolset.Core.WindowsInstaller/Bind/CreateOutputFromIRCommand.cs +++ b/src/WixToolset.Core.WindowsInstaller/Bind/CreateOutputFromIRCommand.cs @@ -3,20 +3,26 @@ namespace WixToolset.Core.WindowsInstaller.Bind { using System; + using System.Collections.Generic; using System.Linq; using WixToolset.Core.Native; using WixToolset.Data; - using WixToolset.Data.Rows; using WixToolset.Data.Tuples; + using WixToolset.Data.WindowsInstaller; + using WixToolset.Data.WindowsInstaller.Rows; + using WixToolset.Extensibility; internal class CreateOutputFromIRCommand { - public CreateOutputFromIRCommand(IntermediateSection section, TableDefinitionCollection tableDefinitions) + public CreateOutputFromIRCommand(IntermediateSection section, TableDefinitionCollection tableDefinitions, IEnumerable backendExtensions) { this.Section = section; this.TableDefinitions = tableDefinitions; + this.BackendExtensions = backendExtensions; } + private IEnumerable BackendExtensions { get; } + private TableDefinitionCollection TableDefinitions { get; } private IntermediateSection Section { get; } @@ -60,6 +66,10 @@ namespace WixToolset.Core.WindowsInstaller.Bind // Ignored. break; + case TupleDefinitionType.MustBeFromAnExtension: + this.AddTupleFromExtension(tuple, output); + break; + default: this.AddTupleDefaultly(tuple, output); break; @@ -206,6 +216,17 @@ namespace WixToolset.Core.WindowsInstaller.Bind } } + private void AddTupleFromExtension(IntermediateTuple tuple, Output output) + { + foreach (var extension in this.BackendExtensions) + { + if (extension.TryAddTupleToOutput(tuple, output)) + { + break; + } + } + } + private void AddTupleDefaultly(IntermediateTuple tuple, Output output) { if (!this.TableDefinitions.TryGet(tuple.Definition.Name, out var tableDefinition)) diff --git a/src/WixToolset.Core.WindowsInstaller/Bind/GenerateDatabaseCommand.cs b/src/WixToolset.Core.WindowsInstaller/Bind/GenerateDatabaseCommand.cs index a3d3ecf7..e4e66559 100644 --- a/src/WixToolset.Core.WindowsInstaller/Bind/GenerateDatabaseCommand.cs +++ b/src/WixToolset.Core.WindowsInstaller/Bind/GenerateDatabaseCommand.cs @@ -12,6 +12,7 @@ namespace WixToolset.Core.WindowsInstaller.Bind using WixToolset.Extensibility; using WixToolset.Msi; using WixToolset.Core.Native; + using WixToolset.Data.WindowsInstaller; internal class GenerateDatabaseCommand { @@ -44,14 +45,56 @@ namespace WixToolset.Core.WindowsInstaller.Bind // Add the _Validation rows. if (!this.SuppressAddingValidationRows) { - Table validationTable = this.Output.EnsureTable(this.TableDefinitions["_Validation"]); + var validationTable = this.Output.EnsureTable(this.TableDefinitions["_Validation"]); - foreach (Table table in this.Output.Tables) + foreach (var table in this.Output.Tables) { if (!table.Definition.Unreal) { // Add the validation rows for this table. - table.Definition.AddValidationRows(validationTable); + foreach (ColumnDefinition columnDef in table.Definition.Columns) + { + var row = validationTable.CreateRow(null); + + row[0] = table.Name; + + row[1] = columnDef.Name; + + if (columnDef.Nullable) + { + row[2] = "Y"; + } + else + { + row[2] = "N"; + } + + if (columnDef.MinValue.HasValue) + { + row[3] = columnDef.MinValue.Value; + } + + if (columnDef.MaxValue.HasValue) + { + row[4] = columnDef.MaxValue.Value; + } + + row[5] = columnDef.KeyTable; + + if (columnDef.KeyColumn.HasValue) + { + row[6] = columnDef.KeyColumn.Value; + } + + if (ColumnCategory.Unknown != columnDef.Category) + { + row[7] = columnDef.Category.ToString(); + } + + row[8] = columnDef.Possibilities; + + row[9] = columnDef.Description; + } } } } @@ -133,7 +176,11 @@ namespace WixToolset.Core.WindowsInstaller.Bind { try { - db.ImportTable(this.Output.Codepage, importTable, baseDirectory, this.KeepAddedColumns); + //db.ImportTable(this.Output.Codepage, importTable, baseDirectory, this.KeepAddedColumns); + var command = new CreateIdtFileCommand(importTable, this.Output.Codepage, baseDirectory, this.KeepAddedColumns); + command.Execute(); + + db.Import(command.IdtPath); } catch (WixInvalidIdtException) { diff --git a/src/WixToolset.Core.WindowsInstaller/Bind/MergeModulesCommand.cs b/src/WixToolset.Core.WindowsInstaller/Bind/MergeModulesCommand.cs index dcf67c05..32a05d93 100644 --- a/src/WixToolset.Core.WindowsInstaller/Bind/MergeModulesCommand.cs +++ b/src/WixToolset.Core.WindowsInstaller/Bind/MergeModulesCommand.cs @@ -8,12 +8,13 @@ namespace WixToolset.Core.WindowsInstaller.Bind using System.IO; using System.Runtime.InteropServices; using System.Text; + using WixToolset.Core.Bind; + using WixToolset.Core.Native; using WixToolset.Data; - using WixToolset.Data.Rows; + using WixToolset.Data.WindowsInstaller; + using WixToolset.Data.WindowsInstaller.Rows; using WixToolset.MergeMod; using WixToolset.Msi; - using WixToolset.Core.Native; - using WixToolset.Core.Bind; /// /// Update file information. diff --git a/src/WixToolset.Core.WindowsInstaller/Bind/ModularaizeCommand.cs b/src/WixToolset.Core.WindowsInstaller/Bind/ModularaizeCommand.cs new file mode 100644 index 00000000..03538fc3 --- /dev/null +++ b/src/WixToolset.Core.WindowsInstaller/Bind/ModularaizeCommand.cs @@ -0,0 +1,238 @@ +// 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.Core.WindowsInstaller.Bind +{ + using System; + using System.Collections.Generic; + using System.Diagnostics; + using System.Globalization; + using System.Linq; + using System.Text; + using System.Text.RegularExpressions; + using WixToolset.Data; + using WixToolset.Data.Tuples; + using WixToolset.Data.WindowsInstaller; + + internal class ModularaizeCommand + { + public ModularaizeCommand(Output output, string modularizationGuid, IEnumerable suppressTuples) + { + this.Output = output; + this.ModularizationGuid = modularizationGuid; + + // Gather all the unique suppress modularization identifiers. + this.SuppressModularizationIdentifiers = new HashSet(suppressTuples.Select(s => s.WixSuppressModularization)); + } + + private Output Output { get; } + + private string ModularizationGuid { get; } + + private HashSet SuppressModularizationIdentifiers { get; } + + public void Execute() + { + foreach (var table in this.Output.Tables) + { + this.ModularizeTable(table); + } + } + + /// + /// Modularize the table. + /// + /// String containing the GUID of the Merge Module, if appropriate. + /// Optional collection of identifiers that should not be modularized. + public void ModularizeTable(Table table) + { + var modularizedColumns = new List(); + + // find the modularized columns + for (var i = 0; i < table.Definition.Columns.Count; ++i) + { + if (ColumnModularizeType.None != table.Definition.Columns[i].ModularizeType) + { + modularizedColumns.Add(i); + } + } + + if (0 < modularizedColumns.Count) + { + foreach (var row in table.Rows) + { + foreach (var modularizedColumn in modularizedColumns) + { + var field = row.Fields[modularizedColumn]; + + if (field.Data != null) + { + field.Data = this.ModularizedRowFieldValue(row, field); + } + } + } + } + } + + private string ModularizedRowFieldValue(Row row, Field field) + { + var fieldData = field.AsString(); + + if (!(WindowsInstallerStandard.IsStandardAction(fieldData) || WindowsInstallerStandard.IsStandardProperty(fieldData))) + { + 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 (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 (Common.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" == row[2].ToString() || "Icon" == row[2].ToString()) && Common.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 (!Common.IsIdentifier(fieldData)) + { + throw new InvalidOperationException(String.Format(CultureInfo.CurrentUICulture, WixDataStrings.EXP_CannotModularizeIllegalID, fieldData)); + } + + // if we're not supposed to suppress modularization of this identifier + if (!this.SuppressModularizationIdentifiers.Contains(fieldData)) + { + fieldData = String.Concat(fieldData, ".", this.ModularizationGuid); + } + break; + + case ColumnModularizeType.Property: + case ColumnModularizeType.Condition: + Regex regex; + if (ColumnModularizeType.Property == modularizeType) + { + regex = new Regex(@"\[(?[#$!]?[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_\.]*|(?[!$\?&]?[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(@"(?NOT|EQV|XOR|OR|AND|IMP)|(?"".*?"")|(?%[a-zA-Z_][a-zA-Z0-9_\.]*)|(?[!$\?&]?[a-zA-Z_][a-zA-Z0-9_\.]*)",RegexOptions.Singleline | RegexOptions.IgnoreCase | RegexOptions.ExplicitCapture); + } + + var matches = regex.Matches(fieldData); + + var sb = new StringBuilder(fieldData); + + // Notice how this code walks backward through the list + // because it modifies the string as we through it. + for (var i = matches.Count - 1; 0 <= i; i--) + { + var group = matches[i].Groups["identifier"]; + if (group.Success) + { + var identifier = group.Value; + if (!WindowsInstallerStandard.IsStandardProperty(identifier) && !this.SuppressModularizationIdentifiers.Contains(identifier)) + { + sb.Insert(group.Index + group.Length, '.'); + sb.Insert(group.Index + group.Length + 1, this.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 (!this.SuppressModularizationIdentifiers.Contains(fieldData) && + 0 < fieldData.Length && !Char.IsDigit(fieldData, 0)) + { + fieldData = String.Concat(fieldData, ".", this.ModularizationGuid); + } + break; + + case ColumnModularizeType.Icon: + if (!this.SuppressModularizationIdentifiers.Contains(fieldData)) + { + var start = fieldData.LastIndexOf(".", StringComparison.Ordinal); + if (-1 == start) + { + fieldData = String.Concat(fieldData, ".", this.ModularizationGuid); + } + else + { + fieldData = String.Concat(fieldData.Substring(0, start), ".", this.ModularizationGuid, fieldData.Substring(start)); + } + } + break; + + case ColumnModularizeType.SemicolonDelimited: + var keys = fieldData.Split(';'); + for (var i = 0; i < keys.Length; ++i) + { + if (!String.IsNullOrEmpty(keys[i])) + { + keys[i] = String.Concat(keys[i], ".", this.ModularizationGuid); + } + } + + fieldData = String.Join(";", keys); + break; + } + } + + return fieldData; + } + } +} diff --git a/src/WixToolset.Core.WindowsInstaller/Bind/UpdateControlTextCommand.cs b/src/WixToolset.Core.WindowsInstaller/Bind/UpdateControlTextCommand.cs index 9579e0f8..dddc9380 100644 --- a/src/WixToolset.Core.WindowsInstaller/Bind/UpdateControlTextCommand.cs +++ b/src/WixToolset.Core.WindowsInstaller/Bind/UpdateControlTextCommand.cs @@ -5,7 +5,8 @@ namespace WixToolset.Core.WindowsInstaller.Bind using System; using System.IO; using WixToolset.Data; - using WixToolset.Data.Rows; + using WixToolset.Data.WindowsInstaller; + using WixToolset.Data.WindowsInstaller.Rows; internal class UpdateControlTextCommand { diff --git a/src/WixToolset.Core.WindowsInstaller/Bind/UpdateFileFacadesCommand.cs b/src/WixToolset.Core.WindowsInstaller/Bind/UpdateFileFacadesCommand.cs index a9eb2a8f..cf620e72 100644 --- a/src/WixToolset.Core.WindowsInstaller/Bind/UpdateFileFacadesCommand.cs +++ b/src/WixToolset.Core.WindowsInstaller/Bind/UpdateFileFacadesCommand.cs @@ -14,6 +14,7 @@ namespace WixToolset.Core.WindowsInstaller.Bind using WixToolset.Core.Bind; using WixToolset.Data; using WixToolset.Data.Tuples; + using WixToolset.Data.WindowsInstaller; using WixToolset.Msi; /// diff --git a/src/WixToolset.Core.WindowsInstaller/Bind/UpdateMediaSequencesCommand.cs b/src/WixToolset.Core.WindowsInstaller/Bind/UpdateMediaSequencesCommand.cs index db74eda5..0767adb0 100644 --- a/src/WixToolset.Core.WindowsInstaller/Bind/UpdateMediaSequencesCommand.cs +++ b/src/WixToolset.Core.WindowsInstaller/Bind/UpdateMediaSequencesCommand.cs @@ -2,13 +2,13 @@ namespace WixToolset.Core.WindowsInstaller.Bind { - using System; using System.Collections.Generic; using System.Linq; using WixToolset.Core.Bind; using WixToolset.Data; - using WixToolset.Data.Rows; using WixToolset.Data.Tuples; + using WixToolset.Data.WindowsInstaller; + using WixToolset.Data.WindowsInstaller.Rows; internal class UpdateMediaSequencesCommand { -- cgit v1.2.3-55-g6feb