@joebigelow / wix-1 / commits / 1c933963

Add ability for an extension to have a custom strongly typed row during binding. Remove some unused functionality in Table and Row. Use strongRowType in core tables.

Add ability for an extension to have a custom strongly typed row during binding. Remove some unused functionality in Table and Row. Use strongRowType in core tables.

Sean Hall committed Apr 18, 2020 at 13:53 UTC 1c933963d1354495d4a31a08baf7f1240b5fbfce
16 files changed +136 -94
src/WixToolset.Data/WindowsInstaller/Row.cs
-16
@@ -46,22 +46,6 @@ namespace WixToolset.Data.WindowsInstaller
46 }
47 }
48
49 - /// <summary>
50 - /// Creates a shallow copy of a row from another row.
51 - /// </summary>
52 - /// <param name="source">The row the data is copied from.</param>
53 - protected Row(Row source)
54 - {
55 - this.Table = source.Table;
56 - this.TableDefinition = source.TableDefinition;
57 - this.Number = source.Number;
58 - this.Operation = source.Operation;
59 - this.Redundant = source.Redundant;
60 - this.SectionId = source.SectionId;
61 - this.SourceLineNumbers = source.SourceLineNumbers;
62 - this.Fields = source.Fields;
63 - }
64 -
49 /// <summary>
50 /// Gets or sets the row transform operation.
51 /// </summary>
src/WixToolset.Data/WindowsInstaller/Rows/BBControlRow.cs
+5
@@ -19,6 +19,11 @@ namespace WixToolset.Data.WindowsInstaller.Rows
19 {
20 }
21
22 + public BBControlRow(SourceLineNumber sourceLineNumbers, TableDefinition tableDefinition) :
23 + base(sourceLineNumbers, tableDefinition)
24 + {
25 + }
26 +
27 /// <summary>
28 /// Gets or sets the dialog of the Control row.
29 /// </summary>
src/WixToolset.Data/WindowsInstaller/Rows/ComponentRow.cs
+5
@@ -19,6 +19,11 @@ namespace WixToolset.Data.WindowsInstaller.Rows
19 {
20 }
21
22 + public ComponentRow(SourceLineNumber sourceLineNumbers, TableDefinition tableDefinition) :
23 + base(sourceLineNumbers, tableDefinition)
24 + {
25 + }
26 +
27 /// <summary>
28 /// Gets or sets the identifier for this Component row.
29 /// </summary>
src/WixToolset.Data/WindowsInstaller/Rows/ControlRow.cs
+5
@@ -19,6 +19,11 @@ namespace WixToolset.Data.WindowsInstaller.Rows
19 {
20 }
21
22 + public ControlRow(SourceLineNumber sourceLineNumbers, TableDefinition tableDefinition) :
23 + base(sourceLineNumbers, tableDefinition)
24 + {
25 + }
26 +
27 /// <summary>
28 /// Gets or sets the dialog of the Control row.
29 /// </summary>
src/WixToolset.Data/WindowsInstaller/Rows/MediaRow.cs
+5
@@ -17,6 +17,11 @@ namespace WixToolset.Data.WindowsInstaller.Rows
17 {
18 }
19
20 + public MediaRow(SourceLineNumber sourceLineNumbers, TableDefinition tableDefinition) :
21 + base(sourceLineNumbers, tableDefinition)
22 + {
23 + }
24 +
25 /// <summary>
26 /// Gets or sets the disk id for this media row.
27 /// </summary>
src/WixToolset.Data/WindowsInstaller/Rows/PropertyRow.cs
+5
@@ -17,6 +17,11 @@ namespace WixToolset.Data.WindowsInstaller.Rows
17 {
18 }
19
20 + public PropertyRow(SourceLineNumber sourceLineNumbers, TableDefinition tableDefinition) :
21 + base(sourceLineNumbers, tableDefinition)
22 + {
23 + }
24 +
25 /// <summary>
26 /// Gets and sets the property name for the row.
27 /// </summary>
src/WixToolset.Data/WindowsInstaller/Rows/UpgradeRow.cs
+5
@@ -17,6 +17,11 @@ namespace WixToolset.Data.WindowsInstaller.Rows
17 {
18 }
19
20 + public UpgradeRow(SourceLineNumber sourceLineNumbers, TableDefinition tableDefinition) :
21 + base(sourceLineNumbers, tableDefinition)
22 + {
23 + }
24 +
25 /// <summary>
26 /// Gets and sets the upgrade code for the row.
27 /// </summary>
src/WixToolset.Data/WindowsInstaller/Rows/WixActionRow.cs
+2 -7
@@ -23,13 +23,8 @@ namespace WixToolset.Data.WindowsInstaller.Rows
23 {
24 }
25
26 - /// <summary>
27 - /// Instantiates an ActionRow by copying data from another ActionRow.
28 - /// </summary>
29 - /// <param name="source">The row the data is copied from.</param>
30 - /// <remarks>The previous and next action collections are not copied.</remarks>
31 - private WixActionRow(WixActionRow source)
32 - : base(source)
26 + public WixActionRow(SourceLineNumber sourceLineNumbers, TableDefinition tableDefinition) :
27 + base(sourceLineNumbers, tableDefinition)
28 {
29 }
30
src/WixToolset.Data/WindowsInstaller/Rows/WixComplexReferenceRow.cs
+5
@@ -20,6 +20,11 @@ namespace WixToolset.Data.WindowsInstaller.Rows
20 {
21 }
22
23 + public WixComplexReferenceRow(SourceLineNumber sourceLineNumbers, TableDefinition tableDefinition)
24 + : base(sourceLineNumbers, tableDefinition)
25 + {
26 + }
27 +
28 /// <summary>
29 /// Gets the parent type of the complex reference.
30 /// </summary>
src/WixToolset.Data/WindowsInstaller/Rows/WixGroupRow.cs
+5
@@ -19,6 +19,11 @@ namespace WixToolset.Data.WindowsInstaller.Rows
19 {
20 }
21
22 + public WixGroupRow(SourceLineNumber sourceLineNumbers, TableDefinition tableDefinition)
23 + : base(sourceLineNumbers, tableDefinition)
24 + {
25 + }
26 +
27 /// <summary>
28 /// Gets or sets the parent identifier of the complex reference.
29 /// </summary>
src/WixToolset.Data/WindowsInstaller/Rows/WixMediaTemplateRow.cs
+5
@@ -17,6 +17,11 @@ namespace WixToolset.Data.WindowsInstaller.Rows
17 {
18 }
19
20 + public WixMediaTemplateRow(SourceLineNumber sourceLineNumbers, TableDefinition tableDefinition)
21 + : base(sourceLineNumbers, tableDefinition)
22 + {
23 + }
24 +
25 /// <summary>
26 /// Gets or sets the cabinet template name for this media template row.
27 /// </summary>
src/WixToolset.Data/WindowsInstaller/Rows/WixPropertyRow.cs
+5
@@ -18,6 +18,11 @@ namespace WixToolset.Data.WindowsInstaller.Rows
18 {
19 }
20
21 + public WixPropertyRow(SourceLineNumber sourceLineNumbers, TableDefinition tableDefinition) :
22 + base(sourceLineNumbers, tableDefinition)
23 + {
24 + }
25 +
26 /// <summary>
27 /// Gets and sets the id for this property row.
28 /// </summary>
src/WixToolset.Data/WindowsInstaller/Table.cs
+4 -70
@@ -6,7 +6,6 @@ namespace WixToolset.Data.WindowsInstaller
6 using System.Collections.Generic;
7 using System.Diagnostics;
8 using System.Xml;
9 - using WixToolset.Data.WindowsInstaller.Rows;
9
10 /// <summary>
11 /// Object that represents a table in a database.
@@ -49,79 +48,14 @@ namespace WixToolset.Data.WindowsInstaller
48 public IList<Row> Rows { get; }
49
50 /// <summary>
52 - /// Creates a new row in the table.
51 + /// Creates a new row and adds it to the table.
52 /// </summary>
53 /// <param name="sourceLineNumbers">Original source lines for this row.</param>
55 - /// <param name="add">Specifies whether to only create the row or add it to the table automatically.</param>
54 /// <returns>Row created in table.</returns>
57 - public Row CreateRow(SourceLineNumber sourceLineNumbers, bool add = true)
55 + public Row CreateRow(SourceLineNumber sourceLineNumbers)
56 {
59 - Row row;
60 -
61 - switch (this.Name)
62 - {
63 - case "BBControl":
64 - row = new BBControlRow(sourceLineNumbers, this);
65 - break;
66 - case "Component":
67 - row = new ComponentRow(sourceLineNumbers, this);
68 - break;
69 - case "Control":
70 - row = new ControlRow(sourceLineNumbers, this);
71 - break;
72 - case "File":
73 - row = new FileRow(sourceLineNumbers, this);
74 - break;
75 - case "Media":
76 - row = new MediaRow(sourceLineNumbers, this);
77 - break;
78 - case "Property":
79 - row = new PropertyRow(sourceLineNumbers, this);
80 - break;
81 - case "Upgrade":
82 - row = new UpgradeRow(sourceLineNumbers, this);
83 - break;
84 - case "WixAction":
85 - row = new WixActionRow(sourceLineNumbers, this);
86 - break;
87 - case "WixComplexReference":
88 - row = new WixComplexReferenceRow(sourceLineNumbers, this);
89 - break;
90 - case "WixDeltaPatchFile":
91 - row = new WixDeltaPatchFileRow(sourceLineNumbers, this);
92 - break;
93 - case "WixDeltaPatchSymbolPaths":
94 - row = new WixDeltaPatchSymbolPathsRow(sourceLineNumbers, this);
95 - break;
96 - case "WixGroup":
97 - row = new WixGroupRow(sourceLineNumbers, this);
98 - break;
99 - case "WixMedia":
100 - row = new WixMediaRow(sourceLineNumbers, this);
101 - break;
102 - case "WixMediaTemplate":
103 - row = new WixMediaTemplateRow(sourceLineNumbers, this);
104 - break;
105 - case "WixMerge":
106 - row = new WixMergeRow(sourceLineNumbers, this);
107 - break;
108 - case "WixProperty":
109 - row = new WixPropertyRow(sourceLineNumbers, this);
110 - break;
111 - case "WixSimpleReference":
112 - row = new WixSimpleReferenceRow(sourceLineNumbers, this);
113 - break;
114 -
115 - default:
116 - row = new Row(sourceLineNumbers, this);
117 - break;
118 - }
119 -
120 - if (add)
121 - {
122 - this.Rows.Add(row);
123 - }
124 -
57 + var row = this.Definition.CreateRow(sourceLineNumbers, this);
58 + this.Rows.Add(row);
59 return row;
60 }
61
src/WixToolset.Data/WindowsInstaller/TableDefinition.cs
+32 -1
@@ -26,18 +26,22 @@ namespace WixToolset.Data.WindowsInstaller
26 /// <param name="columns">Column definitions for the table.</param>
27 /// <param name="unreal">Flag if table is unreal.</param>
28 /// <param name="tupleIdIsPrimaryKey">Whether the primary key is the id of the tuple definition associated with this table.</param>
29 - public TableDefinition(string name, IntermediateTupleDefinition tupleDefinition, IEnumerable<ColumnDefinition> columns, bool unreal = false, bool tupleIdIsPrimaryKey = false)
29 + public TableDefinition(string name, IntermediateTupleDefinition tupleDefinition, IEnumerable<ColumnDefinition> columns, bool unreal = false, bool tupleIdIsPrimaryKey = false, Type strongRowType = null)
30 {
31 this.Name = name;
32 this.TupleDefinition = tupleDefinition;
33 this.TupleIdIsPrimaryKey = tupleIdIsPrimaryKey;
34 this.Unreal = unreal;
35 this.Columns = columns?.ToArray();
36 + this.StrongRowType = strongRowType ?? typeof(Row);
37
38 if (this.Columns == null || this.Columns.Length == 0)
39 {
40 throw new ArgumentOutOfRangeException(nameof(columns));
41 }
42 +#if DEBUG
43 + if (this.StrongRowType != typeof(Row) && !this.StrongRowType.IsSubclassOf(typeof(Row))) { throw new ArgumentException(nameof(strongRowType)); }
44 +#endif
45 }
46
47 /// <summary>
@@ -70,6 +74,8 @@ namespace WixToolset.Data.WindowsInstaller
74 /// <value>Flag if table is unreal.</value>
75 public bool TupleIdIsPrimaryKey { get; }
76
77 + private Type StrongRowType { get; }
78 +
79 /// <summary>
80 /// Gets the column definition in the table by index.
81 /// </summary>
@@ -77,6 +83,31 @@ namespace WixToolset.Data.WindowsInstaller
83 /// <value>Column definition in the table by index.</value>
84 public ColumnDefinition this[int columnIndex] => this.Columns[columnIndex];
85
86 + /// <summary>
87 + /// In general this method shouldn't be used - create rows from a Table instead.
88 + /// Creates a new row object of the type specified in this definition.
89 + /// </summary>
90 + /// <param name="sourceLineNumbers">Original source lines for this row.</param>
91 + /// <returns>Created row.</returns>
92 + public Row CreateRow(SourceLineNumber sourceLineNumbers)
93 + {
94 + var result = (Row)Activator.CreateInstance(this.StrongRowType, sourceLineNumbers, this);
95 + return result;
96 + }
97 +
98 + /// <summary>
99 + /// Creates a new row object of the type specified in this definition for the given table.
100 + /// External callers should create the row from the table.
101 + /// </summary>
102 + /// <param name="sourceLineNumbers">Original source lines for this row.</param>
103 + /// <param name="table">The owning table for this row.</param>
104 + /// <returns>Created row.</returns>
105 + internal Row CreateRow(SourceLineNumber sourceLineNumbers, Table table)
106 + {
107 + var result = (Row)Activator.CreateInstance(this.StrongRowType, sourceLineNumbers, table);
108 + return result;
109 + }
110 +
111 /// <summary>
112 /// Compares this table definition to another table definition.
113 /// </summary>
src/WixToolset.Data/WindowsInstaller/WindowsInstallerTableDefinitions.cs
+19
@@ -2,6 +2,8 @@
2
3 namespace WixToolset.Data.WindowsInstaller
4 {
5 + using WixToolset.Data.WindowsInstaller.Rows;
6 +
7 public static class WindowsInstallerTableDefinitions
8 {
9 public static readonly TableDefinition ActionText = new TableDefinition(
@@ -111,6 +113,7 @@ namespace WixToolset.Data.WindowsInstaller
113 new ColumnDefinition("Property", ColumnType.String, 72, primaryKey: true, nullable: false, ColumnCategory.Identifier, description: "Name of property, uppercase if settable by launcher or loader.", modularizeType: ColumnModularizeType.Column),
114 new ColumnDefinition("Value", ColumnType.Localized, 0, primaryKey: false, nullable: false, ColumnCategory.Text, description: "String value for property. Never null or empty."),
115 },
116 + strongRowType: typeof(PropertyRow),
117 tupleIdIsPrimaryKey: true
118 );
119
@@ -129,6 +132,7 @@ namespace WixToolset.Data.WindowsInstaller
132 new ColumnDefinition("Attributes", ColumnType.Number, 4, primaryKey: false, nullable: true, ColumnCategory.Unknown, minValue: 0, maxValue: 2147483647, description: "A 32-bit word that specifies the attribute flags to be applied to this control."),
133 new ColumnDefinition("Text", ColumnType.Localized, 50, primaryKey: false, nullable: true, ColumnCategory.Text, description: "A string used to set the initial text contained within a control (if appropriate)."),
134 },
135 + strongRowType: typeof(BBControlRow),
136 tupleIdIsPrimaryKey: false
137 );
138
@@ -200,6 +204,7 @@ namespace WixToolset.Data.WindowsInstaller
204 new ColumnDefinition("DiskId", ColumnType.Number, 4, primaryKey: false, nullable: false, ColumnCategory.Unknown, minValue: 1, maxValue: 32767, description: "Disk identifier for the file.", unreal: true),
205 new ColumnDefinition("Source", ColumnType.Object, 0, primaryKey: false, nullable: false, ColumnCategory.Binary, description: "Path to source of file.", unreal: true),
206 },
207 + strongRowType: typeof(FileRow),
208 tupleIdIsPrimaryKey: true
209 );
210
@@ -258,6 +263,7 @@ namespace WixToolset.Data.WindowsInstaller
263 new ColumnDefinition("Condition", ColumnType.String, 255, primaryKey: false, nullable: true, ColumnCategory.Condition, description: "A conditional statement that will disable this component if the specified condition evaluates to the 'True' state. If a component is disabled, it will not be installed, regardless of the 'Action' state associated with the component.", modularizeType: ColumnModularizeType.Condition, forceLocalizable: true),
264 new ColumnDefinition("KeyPath", ColumnType.String, 72, primaryKey: false, nullable: true, ColumnCategory.Identifier, keyTable: "File;Registry;ODBCDataSource", keyColumn: 1, description: "Either the primary key into the File table, Registry table, or ODBCDataSource table. This extract path is stored when the component is installed, and is used to detect the presence of the component and to return the path to it.", modularizeType: ColumnModularizeType.Column),
265 },
266 + strongRowType: typeof(ComponentRow),
267 tupleIdIsPrimaryKey: true
268 );
269
@@ -353,6 +359,7 @@ namespace WixToolset.Data.WindowsInstaller
359 new ColumnDefinition("Control_Next", ColumnType.String, 50, primaryKey: false, nullable: true, ColumnCategory.Identifier, keyTable: "Control", keyColumn: 2, description: "The name of an other control on the same dialog. This link defines the tab order of the controls. The links have to form one or more cycles!"),
360 new ColumnDefinition("Help", ColumnType.Localized, 50, primaryKey: false, nullable: true, ColumnCategory.Text, description: "The help strings used with the button. The text is optional. "),
361 },
362 + strongRowType: typeof(ControlRow),
363 tupleIdIsPrimaryKey: false
364 );
365
@@ -708,6 +715,7 @@ namespace WixToolset.Data.WindowsInstaller
715 new ColumnDefinition("VolumeLabel", ColumnType.String, 32, primaryKey: false, nullable: true, ColumnCategory.Text, description: "The label attributed to the volume."),
716 new ColumnDefinition("Source", ColumnType.String, 72, primaryKey: false, nullable: true, ColumnCategory.Property, description: "The property defining the location of the cabinet file."),
717 },
718 + strongRowType: typeof(MediaRow),
719 tupleIdIsPrimaryKey: false
720 );
721
@@ -1330,6 +1338,7 @@ namespace WixToolset.Data.WindowsInstaller
1338 new ColumnDefinition("Remove", ColumnType.String, 255, primaryKey: false, nullable: true, ColumnCategory.Formatted, description: "The list of features to remove when uninstalling a product from this set. The default is \"ALL\"."),
1339 new ColumnDefinition("ActionProperty", ColumnType.String, 72, primaryKey: false, nullable: false, ColumnCategory.UpperCase, description: "The property to set when a product in this set is found."),
1340 },
1341 + strongRowType: typeof(UpgradeRow),
1342 tupleIdIsPrimaryKey: false
1343 );
1344
@@ -1666,6 +1675,7 @@ namespace WixToolset.Data.WindowsInstaller
1675 new ColumnDefinition("Overridable", ColumnType.Number, 2, primaryKey: false, nullable: false, ColumnCategory.Unknown),
1676 },
1677 unreal: true,
1678 + strongRowType: typeof(WixActionRow),
1679 tupleIdIsPrimaryKey: false
1680 );
1681
@@ -1695,6 +1705,7 @@ namespace WixToolset.Data.WindowsInstaller
1705 new ColumnDefinition("Attributes", ColumnType.Number, 4, primaryKey: false, nullable: false, ColumnCategory.Unknown),
1706 },
1707 unreal: true,
1708 + strongRowType: typeof(WixComplexReferenceRow),
1709 tupleIdIsPrimaryKey: false
1710 );
1711
@@ -1814,6 +1825,7 @@ namespace WixToolset.Data.WindowsInstaller
1825 new ColumnDefinition("ChildType", ColumnType.String, 0, primaryKey: true, nullable: false, ColumnCategory.Unknown, description: "Primary key used to identify a particular child type in a child table."),
1826 },
1827 unreal: true,
1828 + strongRowType: typeof(WixGroupRow),
1829 tupleIdIsPrimaryKey: false
1830 );
1831
@@ -1923,6 +1935,7 @@ namespace WixToolset.Data.WindowsInstaller
1935 new ColumnDefinition("Layout", ColumnType.String, 0, primaryKey: false, nullable: true, ColumnCategory.Unknown),
1936 },
1937 unreal: true,
1938 + strongRowType: typeof(WixMediaRow),
1939 tupleIdIsPrimaryKey: false
1940 );
1941
@@ -1939,6 +1952,7 @@ namespace WixToolset.Data.WindowsInstaller
1952 new ColumnDefinition("MaximumCabinetSizeForLargeFileSplitting", ColumnType.Number, 4, primaryKey: false, nullable: false, ColumnCategory.Unknown),
1953 },
1954 unreal: true,
1955 + strongRowType: typeof(WixMediaTemplateRow),
1956 tupleIdIsPrimaryKey: false
1957 );
1958
@@ -1957,6 +1971,7 @@ namespace WixToolset.Data.WindowsInstaller
1971 new ColumnDefinition("Feature_", ColumnType.String, 72, primaryKey: false, nullable: false, ColumnCategory.Unknown),
1972 },
1973 unreal: true,
1974 + strongRowType: typeof(WixMergeRow),
1975 tupleIdIsPrimaryKey: true
1976 );
1977
@@ -1987,6 +2002,7 @@ namespace WixToolset.Data.WindowsInstaller
2002 new ColumnDefinition("SymbolPaths", ColumnType.Preserved, 0, primaryKey: false, nullable: true, ColumnCategory.Text),
2003 },
2004 unreal: true,
2005 + strongRowType: typeof(WixDeltaPatchFileRow),
2006 tupleIdIsPrimaryKey: false
2007 );
2008
@@ -2000,6 +2016,7 @@ namespace WixToolset.Data.WindowsInstaller
2016 new ColumnDefinition("SymbolPaths", ColumnType.Preserved, 0, primaryKey: false, nullable: false, ColumnCategory.Text),
2017 },
2018 unreal: true,
2019 + strongRowType: typeof(WixDeltaPatchSymbolPathsRow),
2020 tupleIdIsPrimaryKey: false
2021 );
2022
@@ -2012,6 +2029,7 @@ namespace WixToolset.Data.WindowsInstaller
2029 new ColumnDefinition("Attributes", ColumnType.Number, 4, primaryKey: false, nullable: false, ColumnCategory.Unknown),
2030 },
2031 unreal: true,
2032 + strongRowType: typeof(WixPropertyRow),
2033 tupleIdIsPrimaryKey: false
2034 );
2035
@@ -2024,6 +2042,7 @@ namespace WixToolset.Data.WindowsInstaller
2042 new ColumnDefinition("PrimaryKeys", ColumnType.String, 0, primaryKey: false, nullable: false, ColumnCategory.Unknown),
2043 },
2044 unreal: true,
2045 + strongRowType: typeof(WixSimpleReferenceRow),
2046 tupleIdIsPrimaryKey: false
2047 );
2048
src/test/WixToolsetTest.Data/WindowsInstallerTableDefinitionsFixture.cs new
+29
@@ -0,0 +1,29 @@
1 +// 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.
2 +
3 +namespace WixToolsetTest.Data
4 +{
5 + using WixToolset.Data.WindowsInstaller;
6 + using Xunit;
7 +
8 + public class WindowsInstallerTableDefinitionsFixture
9 + {
10 + [Fact]
11 + public void CanCreateWindowsInstallerRows()
12 + {
13 + foreach (var tableDefinition in WindowsInstallerTableDefinitions.All)
14 + {
15 + var table = new Table(tableDefinition);
16 + var rowFromTable = table.CreateRow(null);
17 + var rowFromTableDefinition = tableDefinition.CreateRow(null);
18 + var expectedRowTypeName = tableDefinition.Name.Replace("_", "") + "Row";
19 + var expectedRowType = rowFromTable.GetType();
20 +
21 + Assert.Equal(expectedRowType, rowFromTableDefinition.GetType());
22 + if (typeof(Row) != expectedRowType)
23 + {
24 + Assert.Equal(expectedRowTypeName, expectedRowType.Name);
25 + }
26 + }
27 + }
28 + }
29 +}