Add ability to TablesAndTuples to generate TableDefinitions.cs.
Sean Hall committed
Apr 5, 2020 at 20:20 UTC
d854e2a53a8943e17c004a983d78ea58cbf6f1be
4 files changed
+650
-3
src/TablesAndTuples/ColumnDefinitionEnums.cs
new
+56
@@ -0,0 +1,56 @@
1
+namespace TablesAndTuples
2
+{
3
+ public enum ColumnCategory
4
+ {
5
+ Unknown,
6
+ Text,
7
+ UpperCase,
8
+ LowerCase,
9
+ Integer,
10
+ DoubleInteger,
11
+ TimeDate,
12
+ Identifier,
13
+ Property,
14
+ Filename,
15
+ WildCardFilename,
16
+ Path,
17
+ Paths,
18
+ AnyPath,
19
+ DefaultDir,
20
+ RegPath,
21
+ Formatted,
22
+ Template,
23
+ Condition,
24
+ Guid,
25
+ Version,
26
+ Language,
27
+ Binary,
28
+ CustomSource,
29
+ Cabinet,
30
+ Shortcut,
31
+ FormattedSDDLText,
32
+ }
33
+
34
+ public enum ColumnModularizeType
35
+ {
36
+ None,
37
+ Column,
38
+ Icon,
39
+ CompanionFile,
40
+ Condition,
41
+ ControlEventArgument,
42
+ ControlText,
43
+ Property,
44
+ SemicolonDelimited,
45
+ }
46
+
47
+ public enum ColumnType
48
+ {
49
+ Unknown,
50
+ String,
51
+ Localized,
52
+ Number,
53
+ Object,
54
+ Preserved,
55
+ }
56
+}
src/TablesAndTuples/Program.cs
+139
-3
@@ -1,8 +1,9 @@
1
-using System;
1
+using System;
2
using System.Collections.Generic;
3
using System.IO;
4
using System.Linq;
5
using System.Text;
6
+using System.Text.RegularExpressions;
7
using System.Xml.Linq;
8
using SimpleJson;
9
@@ -27,9 +28,23 @@ namespace TablesAndTuples
28
if (args.Length < 2)
29
{
30
Console.WriteLine("Need to specify output json file as well.");
31
+ return;
32
}
33
+ if (Path.GetExtension(args[1]) != ".json")
34
+ {
35
+ Console.WriteLine("Output needs to be .json");
36
+ return;
37
+ }
38
+
39
+ string prefix = null;
40
+ if (args.Length > 2)
41
+ {
42
+ prefix = args[2];
43
+ }
44
+
45
+ var csFile = Path.Combine(Path.GetDirectoryName(args[1]), String.Concat(prefix ?? "WindowsInstaller", "TableDefinitions.cs"));
46
32
- ReadXmlWriteJson(Path.GetFullPath(args[0]), Path.GetFullPath(args[1]));
47
+ ReadXmlWriteJson(Path.GetFullPath(args[0]), Path.GetFullPath(args[1]), Path.GetFullPath(csFile), prefix);
48
}
49
else if (Path.GetExtension(args[0]) == ".json")
50
{
@@ -37,6 +52,7 @@ namespace TablesAndTuples
52
if (args.Length < 2)
53
{
54
Console.WriteLine("Need to specify output folder.");
55
+ return;
56
}
57
else if (args.Length > 2)
58
{
@@ -47,8 +63,10 @@ namespace TablesAndTuples
63
}
64
}
65
50
- private static void ReadXmlWriteJson(string inputPath, string outputPath)
66
+ private static void ReadXmlWriteJson(string inputPath, string outputPath, string csOutputPath, string prefix)
67
{
68
+ ReadXmlWriteCs(inputPath, csOutputPath, prefix);
69
+
70
var doc = XDocument.Load(inputPath);
71
72
var array = new JsonArray();
@@ -97,6 +115,14 @@ namespace TablesAndTuples
115
File.WriteAllText(outputPath, json);
116
}
117
118
+ private static void ReadXmlWriteCs(string inputPath, string outputPath, string prefix)
119
+ {
120
+ var tableDefinitions = WixTableDefinition.LoadCollection(inputPath);
121
+ var text = GenerateCsTableDefinitionsFileText(prefix, tableDefinitions);
122
+ Console.WriteLine("Writing: {0}", outputPath);
123
+ File.WriteAllText(outputPath, text);
124
+ }
125
+
126
private static void ReadJsonWriteCs(string inputPath, string outputFolder, string prefix)
127
{
128
var json = File.ReadAllText(inputPath);
@@ -142,6 +168,116 @@ namespace TablesAndTuples
168
}
169
}
170
171
+ private static string GenerateCsTableDefinitionsFileText(string prefix, List<WixTableDefinition> tableDefinitions)
172
+ {
173
+ var ns = prefix ?? "Data";
174
+
175
+ var startClassDef = String.Join(Environment.NewLine,
176
+ "// 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.",
177
+ "",
178
+ "namespace WixToolset.{1}",
179
+ "{",
180
+ " using WixToolset.Data.WindowsInstaller;",
181
+ "",
182
+ " public static class {2}TableDefinitions",
183
+ " {");
184
+ var startTableDef = String.Join(Environment.NewLine,
185
+ " public static readonly TableDefinition {1} = new TableDefinition(",
186
+ " \"{1}\",",
187
+ " new[]",
188
+ " {");
189
+ var columnDef =
190
+ " new ColumnDefinition(\"{1}\", ColumnType.{2}, {3}, primaryKey: {4}, nullable: {5}, ColumnCategory.{6}";
191
+ var endColumnsDef = String.Join(Environment.NewLine,
192
+ " },");
193
+ var unrealDef =
194
+ " unreal: true,";
195
+ var endTableDef = String.Join(Environment.NewLine,
196
+ " tupleDefinitionName: \"{1}\",",
197
+ " tupleIdIsPrimaryKey: {2}",
198
+ " );",
199
+ "");
200
+ var startAllTablesDef = String.Join(Environment.NewLine,
201
+ " public static readonly TableDefinition[] All = new[]",
202
+ " {");
203
+ var allTableDef =
204
+ " {1},";
205
+ var endAllTablesDef =
206
+ " };";
207
+ var endClassDef = String.Join(Environment.NewLine,
208
+ " }",
209
+ "}");
210
+
211
+ var sb = new StringBuilder();
212
+
213
+ sb.AppendLine(startClassDef.Replace("{1}", ns).Replace("{2}", prefix));
214
+ foreach (var tableDefinition in tableDefinitions)
215
+ {
216
+ sb.AppendLine(startTableDef.Replace("{1}", tableDefinition.Name));
217
+ foreach (var columnDefinition in tableDefinition.Columns)
218
+ {
219
+ sb.Append(columnDef.Replace("{1}", columnDefinition.Name).Replace("{2}", columnDefinition.Type.ToString()).Replace("{3}", columnDefinition.Length.ToString())
220
+ .Replace("{4}", columnDefinition.PrimaryKey.ToString().ToLower()).Replace("{5}", columnDefinition.Nullable.ToString().ToLower()).Replace("{6}", columnDefinition.Category.ToString()));
221
+ if (columnDefinition.MinValue.HasValue)
222
+ {
223
+ sb.AppendFormat(", minValue: {0}", columnDefinition.MinValue.Value);
224
+ }
225
+ if (columnDefinition.MaxValue.HasValue)
226
+ {
227
+ sb.AppendFormat(", maxValue: {0}", columnDefinition.MaxValue.Value);
228
+ }
229
+ if (!String.IsNullOrEmpty(columnDefinition.KeyTable))
230
+ {
231
+ sb.AppendFormat(", keyTable: \"{0}\"", columnDefinition.KeyTable);
232
+ }
233
+ if (columnDefinition.KeyColumn.HasValue)
234
+ {
235
+ sb.AppendFormat(", keyColumn: {0}", columnDefinition.KeyColumn.Value);
236
+ }
237
+ if (!String.IsNullOrEmpty(columnDefinition.Possibilities))
238
+ {
239
+ sb.AppendFormat(", possibilities: \"{0}\"", columnDefinition.Possibilities);
240
+ }
241
+ if (!String.IsNullOrEmpty(columnDefinition.Description))
242
+ {
243
+ sb.AppendFormat(", description: \"{0}\"", columnDefinition.Description);
244
+ }
245
+ if (columnDefinition.ModularizeType.HasValue && columnDefinition.ModularizeType.Value != ColumnModularizeType.None)
246
+ {
247
+ sb.AppendFormat(", modularizeType: ColumnModularizeType.{0}", columnDefinition.ModularizeType.ToString());
248
+ }
249
+ if (columnDefinition.ForceLocalizable)
250
+ {
251
+ sb.Append(", forceLocalizable: true");
252
+ }
253
+ if (columnDefinition.UseCData)
254
+ {
255
+ sb.Append(", useCData: true");
256
+ }
257
+ if (columnDefinition.Unreal)
258
+ {
259
+ sb.Append(", unreal: true");
260
+ }
261
+ sb.AppendLine("),");
262
+ }
263
+ sb.AppendLine(endColumnsDef);
264
+ if (tableDefinition.Unreal)
265
+ {
266
+ sb.AppendLine(unrealDef);
267
+ }
268
+ sb.AppendLine(endTableDef.Replace("{1}", tableDefinition.TupleDefinitionName).Replace("{2}", tableDefinition.TupleIdIsPrimaryKey.ToString().ToLower()));
269
+ }
270
+ sb.AppendLine(startAllTablesDef);
271
+ foreach (var tableDefinition in tableDefinitions)
272
+ {
273
+ sb.AppendLine(allTableDef.Replace("{1}", tableDefinition.Name));
274
+ }
275
+ sb.AppendLine(endAllTablesDef);
276
+ sb.AppendLine(endClassDef);
277
+
278
+ return sb.ToString();
279
+ }
280
+
281
private static string GenerateTupleFileText(string prefix, string tupleName, List<(string Name, string Type, string ClrType, string AsFunction)> tupleFields)
282
{
283
var ns = prefix ?? "Data";
src/TablesAndTuples/WixColumnDefinition.cs
new
+296
@@ -0,0 +1,296 @@
1
+using System;
2
+using System.Xml;
3
+
4
+namespace TablesAndTuples
5
+{
6
+ class WixColumnDefinition
7
+ {
8
+ public WixColumnDefinition(string name, ColumnType type, int length, bool primaryKey, bool nullable, ColumnCategory category, long? minValue = null, long? maxValue = null, string keyTable = null, int? keyColumn = null, string possibilities = null, string description = null, ColumnModularizeType? modularizeType = null, bool forceLocalizable = false, bool useCData = false, bool unreal = false)
9
+ {
10
+ this.Name = name;
11
+ this.Type = type;
12
+ this.Length = length;
13
+ this.PrimaryKey = primaryKey;
14
+ this.Nullable = nullable;
15
+ this.ModularizeType = modularizeType;
16
+ this.ForceLocalizable = forceLocalizable;
17
+ this.MinValue = minValue;
18
+ this.MaxValue = maxValue;
19
+ this.KeyTable = keyTable;
20
+ this.KeyColumn = keyColumn;
21
+ this.Category = category;
22
+ this.Possibilities = possibilities;
23
+ this.Description = description;
24
+ this.UseCData = useCData;
25
+ this.Unreal = unreal;
26
+ }
27
+
28
+ public string Name { get; }
29
+ public ColumnType Type { get; }
30
+ public int Length { get; }
31
+ public bool PrimaryKey { get; }
32
+ public bool Nullable { get; }
33
+ public ColumnModularizeType? ModularizeType { get; }
34
+ public bool ForceLocalizable { get; }
35
+ public long? MinValue { get; }
36
+ public long? MaxValue { get; }
37
+ public string KeyTable { get; }
38
+ public int? KeyColumn { get; }
39
+ public ColumnCategory Category { get; }
40
+ public string Possibilities { get; }
41
+ public string Description { get; }
42
+ public bool UseCData { get; }
43
+ public bool Unreal { get; }
44
+
45
+ internal static WixColumnDefinition Read(XmlReader reader)
46
+ {
47
+ if (!reader.LocalName.Equals("columnDefinition"))
48
+ {
49
+ throw new XmlException();
50
+ }
51
+
52
+ ColumnCategory category = ColumnCategory.Unknown;
53
+ string description = null;
54
+ bool empty = reader.IsEmptyElement;
55
+ int? keyColumn = null;
56
+ string keyTable = null;
57
+ int length = -1;
58
+ bool localizable = false;
59
+ long? maxValue = null;
60
+ long? minValue = null;
61
+ var modularize = ColumnModularizeType.None;
62
+ string name = null;
63
+ bool nullable = false;
64
+ string possibilities = null;
65
+ bool primaryKey = false;
66
+ var type = ColumnType.Unknown;
67
+ bool useCData = false;
68
+ bool unreal = false;
69
+
70
+ // parse the attributes
71
+ while (reader.MoveToNextAttribute())
72
+ {
73
+ switch (reader.LocalName)
74
+ {
75
+ case "category":
76
+ switch (reader.Value)
77
+ {
78
+ case "anyPath":
79
+ category = ColumnCategory.AnyPath;
80
+ break;
81
+ case "binary":
82
+ category = ColumnCategory.Binary;
83
+ break;
84
+ case "cabinet":
85
+ category = ColumnCategory.Cabinet;
86
+ break;
87
+ case "condition":
88
+ category = ColumnCategory.Condition;
89
+ break;
90
+ case "customSource":
91
+ category = ColumnCategory.CustomSource;
92
+ break;
93
+ case "defaultDir":
94
+ category = ColumnCategory.DefaultDir;
95
+ break;
96
+ case "doubleInteger":
97
+ category = ColumnCategory.DoubleInteger;
98
+ break;
99
+ case "filename":
100
+ category = ColumnCategory.Filename;
101
+ break;
102
+ case "formatted":
103
+ category = ColumnCategory.Formatted;
104
+ break;
105
+ case "formattedSddl":
106
+ category = ColumnCategory.FormattedSDDLText;
107
+ break;
108
+ case "guid":
109
+ category = ColumnCategory.Guid;
110
+ break;
111
+ case "identifier":
112
+ category = ColumnCategory.Identifier;
113
+ break;
114
+ case "integer":
115
+ category = ColumnCategory.Integer;
116
+ break;
117
+ case "language":
118
+ category = ColumnCategory.Language;
119
+ break;
120
+ case "lowerCase":
121
+ category = ColumnCategory.LowerCase;
122
+ break;
123
+ case "path":
124
+ category = ColumnCategory.Path;
125
+ break;
126
+ case "paths":
127
+ category = ColumnCategory.Paths;
128
+ break;
129
+ case "property":
130
+ category = ColumnCategory.Property;
131
+ break;
132
+ case "regPath":
133
+ category = ColumnCategory.RegPath;
134
+ break;
135
+ case "shortcut":
136
+ category = ColumnCategory.Shortcut;
137
+ break;
138
+ case "template":
139
+ category = ColumnCategory.Template;
140
+ break;
141
+ case "text":
142
+ category = ColumnCategory.Text;
143
+ break;
144
+ case "timeDate":
145
+ category = ColumnCategory.TimeDate;
146
+ break;
147
+ case "upperCase":
148
+ category = ColumnCategory.UpperCase;
149
+ break;
150
+ case "version":
151
+ category = ColumnCategory.Version;
152
+ break;
153
+ case "wildCardFilename":
154
+ category = ColumnCategory.WildCardFilename;
155
+ break;
156
+ default:
157
+ throw new InvalidOperationException();
158
+ }
159
+ break;
160
+ case "description":
161
+ description = reader.Value;
162
+ break;
163
+ case "keyColumn":
164
+ keyColumn = Convert.ToInt32(reader.Value, 10);
165
+ break;
166
+ case "keyTable":
167
+ keyTable = reader.Value;
168
+ break;
169
+ case "length":
170
+ length = Convert.ToInt32(reader.Value, 10);
171
+ break;
172
+ case "localizable":
173
+ localizable = reader.Value.Equals("yes");
174
+ break;
175
+ case "maxValue":
176
+ maxValue = Convert.ToInt32(reader.Value, 10);
177
+ break;
178
+ case "minValue":
179
+ minValue = Convert.ToInt32(reader.Value, 10);
180
+ break;
181
+ case "modularize":
182
+ switch (reader.Value)
183
+ {
184
+ case "column":
185
+ modularize = ColumnModularizeType.Column;
186
+ break;
187
+ case "companionFile":
188
+ modularize = ColumnModularizeType.CompanionFile;
189
+ break;
190
+ case "condition":
191
+ modularize = ColumnModularizeType.Condition;
192
+ break;
193
+ case "controlEventArgument":
194
+ modularize = ColumnModularizeType.ControlEventArgument;
195
+ break;
196
+ case "controlText":
197
+ modularize = ColumnModularizeType.ControlText;
198
+ break;
199
+ case "icon":
200
+ modularize = ColumnModularizeType.Icon;
201
+ break;
202
+ case "none":
203
+ modularize = ColumnModularizeType.None;
204
+ break;
205
+ case "property":
206
+ modularize = ColumnModularizeType.Property;
207
+ break;
208
+ case "semicolonDelimited":
209
+ modularize = ColumnModularizeType.SemicolonDelimited;
210
+ break;
211
+ default:
212
+ throw new XmlException();
213
+ }
214
+ break;
215
+ case "name":
216
+ switch (reader.Value)
217
+ {
218
+ case "CREATE":
219
+ case "DELETE":
220
+ case "DROP":
221
+ case "INSERT":
222
+ throw new XmlException();
223
+ default:
224
+ name = reader.Value;
225
+ break;
226
+ }
227
+ break;
228
+ case "nullable":
229
+ nullable = reader.Value.Equals("yes");
230
+ break;
231
+ case "primaryKey":
232
+ primaryKey = reader.Value.Equals("yes");
233
+ break;
234
+ case "set":
235
+ possibilities = reader.Value;
236
+ break;
237
+ case "type":
238
+ switch (reader.Value)
239
+ {
240
+ case "localized":
241
+ type = ColumnType.Localized;
242
+ break;
243
+ case "number":
244
+ type = ColumnType.Number;
245
+ break;
246
+ case "object":
247
+ type = ColumnType.Object;
248
+ break;
249
+ case "string":
250
+ type = ColumnType.String;
251
+ break;
252
+ case "preserved":
253
+ type = ColumnType.Preserved;
254
+ break;
255
+ default:
256
+ throw new XmlException();
257
+ }
258
+ break;
259
+ case "useCData":
260
+ useCData = reader.Value.Equals("yes");
261
+ break;
262
+ case "unreal":
263
+ unreal = reader.Value.Equals("yes");
264
+ break;
265
+ }
266
+ }
267
+
268
+ // parse the child elements (there should be none)
269
+ if (!empty)
270
+ {
271
+ bool done = false;
272
+
273
+ while (!done && reader.Read())
274
+ {
275
+ switch (reader.NodeType)
276
+ {
277
+ case XmlNodeType.Element:
278
+ throw new XmlException();
279
+ case XmlNodeType.EndElement:
280
+ done = true;
281
+ break;
282
+ }
283
+ }
284
+
285
+ if (!done)
286
+ {
287
+ throw new XmlException();
288
+ }
289
+ }
290
+
291
+ WixColumnDefinition columnDefinition = new WixColumnDefinition(name, type, length, primaryKey, nullable, category, minValue, maxValue, keyTable, keyColumn, possibilities, description, modularize, localizable, useCData, unreal);
292
+
293
+ return columnDefinition;
294
+ }
295
+ }
296
+}
src/TablesAndTuples/WixTableDefinition.cs
new
+159
@@ -0,0 +1,159 @@
1
+using System.Collections.Generic;
2
+using System.Linq;
3
+using System.Xml;
4
+
5
+namespace TablesAndTuples
6
+{
7
+ class WixTableDefinition
8
+ {
9
+ public WixTableDefinition(string name, IEnumerable<WixColumnDefinition> columns, bool unreal, string tupleDefinitionName, bool? tupleIdIsPrimaryKey)
10
+ {
11
+ this.Name = name;
12
+ this.Unreal = unreal;
13
+ this.Columns = columns?.ToArray();
14
+ this.TupleDefinitionName = tupleDefinitionName ?? name;
15
+ this.TupleIdIsPrimaryKey = tupleIdIsPrimaryKey ?? DeriveTupleIdIsPrimaryKey(this.Columns);
16
+ }
17
+
18
+ public string Name { get; }
19
+
20
+ public string TupleDefinitionName { get; }
21
+
22
+ public bool Unreal { get; }
23
+
24
+ public WixColumnDefinition[] Columns { get; }
25
+
26
+ public bool TupleIdIsPrimaryKey { get; }
27
+
28
+ static WixTableDefinition Read(XmlReader reader)
29
+ {
30
+ var empty = reader.IsEmptyElement;
31
+ string name = null;
32
+ string tupleDefinitionName = null;
33
+ var unreal = false;
34
+ bool? tupleIdIsPrimaryKey = null;
35
+
36
+ while (reader.MoveToNextAttribute())
37
+ {
38
+ switch (reader.LocalName)
39
+ {
40
+ case "name":
41
+ name = reader.Value;
42
+ break;
43
+ case "tupleDefinitionName":
44
+ tupleDefinitionName = reader.Value;
45
+ break;
46
+ case "tupleIdIsPrimaryKey":
47
+ tupleIdIsPrimaryKey = reader.Value.Equals("yes");
48
+ break;
49
+ case "unreal":
50
+ unreal = reader.Value.Equals("yes");
51
+ break;
52
+ }
53
+ }
54
+
55
+ if (null == name)
56
+ {
57
+ throw new XmlException();
58
+ }
59
+
60
+ var columns = new List<WixColumnDefinition>();
61
+
62
+ // parse the child elements
63
+ if (!empty)
64
+ {
65
+ var done = false;
66
+
67
+ while (!done && reader.Read())
68
+ {
69
+ switch (reader.NodeType)
70
+ {
71
+ case XmlNodeType.Element:
72
+ switch (reader.LocalName)
73
+ {
74
+ case "columnDefinition":
75
+ var columnDefinition = WixColumnDefinition.Read(reader);
76
+ columns.Add(columnDefinition);
77
+ break;
78
+ default:
79
+ throw new XmlException();
80
+ }
81
+ break;
82
+ case XmlNodeType.EndElement:
83
+ done = true;
84
+ break;
85
+ }
86
+ }
87
+
88
+ if (!done)
89
+ {
90
+ throw new XmlException();
91
+ }
92
+ }
93
+
94
+ return new WixTableDefinition(name, columns.ToArray(), unreal, tupleDefinitionName, tupleIdIsPrimaryKey);
95
+ }
96
+
97
+ static bool DeriveTupleIdIsPrimaryKey(WixColumnDefinition[] columns)
98
+ {
99
+ return columns[0].PrimaryKey &&
100
+ columns[0].Type == ColumnType.String &&
101
+ columns[0].Category == ColumnCategory.Identifier &&
102
+ !columns[0].Name.EndsWith("_") &&
103
+ (columns.Length == 1 || !columns.Skip(1).Any(t => t.PrimaryKey));
104
+ }
105
+
106
+ public static List<WixTableDefinition> LoadCollection(string inputPath)
107
+ {
108
+ using (var reader = XmlReader.Create(inputPath))
109
+ {
110
+ reader.MoveToContent();
111
+
112
+ if ("tableDefinitions" != reader.LocalName)
113
+ {
114
+ throw new XmlException();
115
+ }
116
+
117
+ var empty = reader.IsEmptyElement;
118
+ var tableDefinitions = new List<WixTableDefinition>();
119
+
120
+ while (reader.MoveToNextAttribute())
121
+ {
122
+ }
123
+
124
+ // parse the child elements
125
+ if (!empty)
126
+ {
127
+ var done = false;
128
+
129
+ while (!done && reader.Read())
130
+ {
131
+ switch (reader.NodeType)
132
+ {
133
+ case XmlNodeType.Element:
134
+ switch (reader.LocalName)
135
+ {
136
+ case "tableDefinition":
137
+ tableDefinitions.Add(WixTableDefinition.Read(reader));
138
+ break;
139
+ default:
140
+ throw new XmlException();
141
+ }
142
+ break;
143
+ case XmlNodeType.EndElement:
144
+ done = true;
145
+ break;
146
+ }
147
+ }
148
+
149
+ if (!done)
150
+ {
151
+ throw new XmlException();
152
+ }
153
+ }
154
+
155
+ return tableDefinitions;
156
+ }
157
+ }
158
+ }
159
+}