summaryrefslogtreecommitdiff
path: root/src/internal/TablesAndTuples/Program.cs
diff options
context:
space:
mode:
authorRob Mensching <rob@firegiant.com>2021-04-22 16:36:39 -0700
committerRob Mensching <rob@firegiant.com>2021-04-22 16:50:15 -0700
commit6a24996a2e831cfe402398af65b31fb1ecd575a9 (patch)
treee377370df4bc7901745c6b5f50268fa665edb3f8 /src/internal/TablesAndTuples/Program.cs
parent2587a8b46b705d53156f5cd1bd866f855049081d (diff)
downloadwix-6a24996a2e831cfe402398af65b31fb1ecd575a9.tar.gz
wix-6a24996a2e831cfe402398af65b31fb1ecd575a9.tar.bz2
wix-6a24996a2e831cfe402398af65b31fb1ecd575a9.zip
Move WixBuildTools into internal
Diffstat (limited to 'src/internal/TablesAndTuples/Program.cs')
-rw-r--r--src/internal/TablesAndTuples/Program.cs528
1 files changed, 528 insertions, 0 deletions
diff --git a/src/internal/TablesAndTuples/Program.cs b/src/internal/TablesAndTuples/Program.cs
new file mode 100644
index 00000000..634acf9d
--- /dev/null
+++ b/src/internal/TablesAndTuples/Program.cs
@@ -0,0 +1,528 @@
1using System;
2using System.Collections.Generic;
3using System.IO;
4using System.Linq;
5using System.Text;
6using System.Text.RegularExpressions;
7using System.Xml.Linq;
8using SimpleJson;
9
10namespace TablesAndSymbols
11{
12 class Program
13 {
14 static void Main(string[] args)
15 {
16 if (args.Length == 0)
17 {
18 return;
19 }
20 else if (Path.GetExtension(args[0]) == ".xml")
21 {
22 if (args.Length < 2)
23 {
24 Console.WriteLine("Need to specify output json file as well.");
25 return;
26 }
27 if (Path.GetExtension(args[1]) != ".json")
28 {
29 Console.WriteLine("Output needs to be .json");
30 return;
31 }
32
33 string prefix = null;
34 if (args.Length > 2)
35 {
36 prefix = args[2];
37 }
38
39 var csFile = Path.Combine(Path.GetDirectoryName(args[1]), String.Concat(prefix ?? "WindowsInstaller", "TableDefinitions.cs"));
40
41 ReadXmlWriteJson(Path.GetFullPath(args[0]), Path.GetFullPath(args[1]), Path.GetFullPath(csFile), prefix);
42 }
43 else if (Path.GetExtension(args[0]) == ".json")
44 {
45 string prefix = null;
46 if (args.Length < 2)
47 {
48 Console.WriteLine("Need to specify output folder.");
49 return;
50 }
51 else if (args.Length > 2)
52 {
53 prefix = args[2];
54 }
55
56 ReadJsonWriteCs(Path.GetFullPath(args[0]), Path.GetFullPath(args[1]), prefix);
57 }
58 }
59
60 private static void ReadXmlWriteJson(string inputPath, string outputPath, string csOutputPath, string prefix)
61 {
62 var tableDefinitions = ReadXmlWriteCs(inputPath, csOutputPath, prefix);
63
64 var array = new JsonArray();
65
66 foreach (var tableDefinition in tableDefinitions)
67 {
68 if (tableDefinition.Symbolless)
69 {
70 continue;
71 }
72 var symbolType = tableDefinition.SymbolDefinitionName;
73
74 var fields = new JsonArray();
75 var firstField = true;
76
77 foreach (var columnDefinition in tableDefinition.Columns)
78 {
79 if (firstField)
80 {
81 firstField = false;
82 if (tableDefinition.SymbolIdIsPrimaryKey)
83 {
84 continue;
85 }
86 }
87
88 var fieldName = columnDefinition.Name;
89 fieldName = Regex.Replace(fieldName, "^([^_]+)_([^_]*)$", x =>
90 {
91 return $"{x.Groups[2].Value}{x.Groups[1].Value}Ref";
92 });
93 var type = columnDefinition.Type.ToString().ToLower();
94
95 if (type == "localized")
96 {
97 type = "string";
98 }
99 else if (type == "object")
100 {
101 type = "path";
102 }
103 else if (columnDefinition.Type == ColumnType.Number && columnDefinition.Length == 2 &&
104 columnDefinition.MinValue == 0 && columnDefinition.MaxValue == 1)
105 {
106 type = "bool";
107 }
108
109 if (columnDefinition.Type == ColumnType.Number && columnDefinition.Nullable)
110 {
111 type += "?";
112 }
113
114 var field = new JsonObject
115 {
116 { fieldName, type }
117 };
118
119 fields.Add(field);
120 }
121
122 var obj = new JsonObject
123 {
124 { symbolType, fields }
125 };
126 array.Add(obj);
127 }
128
129 array.Sort(CompareSymbolDefinitions);
130
131 var strat = new PocoJsonSerializerStrategy();
132 var json = SimpleJson.SimpleJson.SerializeObject(array, strat);
133
134 Directory.CreateDirectory(Path.GetDirectoryName(outputPath));
135 File.WriteAllText(outputPath, json);
136 }
137
138 private static List<WixTableDefinition> ReadXmlWriteCs(string inputPath, string outputPath, string prefix)
139 {
140 var tableDefinitions = WixTableDefinition.LoadCollection(inputPath);
141 var text = GenerateCsTableDefinitionsFileText(prefix, tableDefinitions);
142 Console.WriteLine("Writing: {0}", outputPath);
143 File.WriteAllText(outputPath, text);
144 return tableDefinitions;
145 }
146
147 private static void ReadJsonWriteCs(string inputPath, string outputFolder, string prefix)
148 {
149 var json = File.ReadAllText(inputPath);
150 var symbols = SimpleJson.SimpleJson.DeserializeObject(json) as JsonArray;
151
152 var symbolNames = new List<string>();
153
154 foreach (var symbolDefinition in symbols.Cast<JsonObject>())
155 {
156 var symbolName = symbolDefinition.Keys.Single();
157 var fields = symbolDefinition.Values.Single() as JsonArray;
158
159 var list = GetFields(fields).ToList();
160
161 symbolNames.Add(symbolName);
162
163 var text = GenerateSymbolFileText(prefix, symbolName, list);
164
165 var pathSymbol = Path.Combine(outputFolder, symbolName + "Symbol.cs");
166 Console.WriteLine("Writing: {0}", pathSymbol);
167 File.WriteAllText(pathSymbol, text);
168 }
169
170 var content = SymbolNamesFileContent(prefix, symbolNames);
171 var pathNames = Path.Combine(outputFolder, String.Concat(prefix, "SymbolDefinitions.cs"));
172 Console.WriteLine("Writing: {0}", pathNames);
173 File.WriteAllText(pathNames, content);
174 }
175
176 private static IEnumerable<(string Name, string Type, string ClrType, string AsFunction)> GetFields(JsonArray fields)
177 {
178 foreach (var field in fields.Cast<JsonObject>())
179 {
180 var fieldName = field.Keys.Single();
181 var fieldType = field.Values.Single() as string;
182
183 var clrType = ConvertToClrType(fieldType);
184 fieldType = ConvertToFieldType(fieldType);
185
186 var asFunction = $"As{(clrType.Contains("?") ? "Nullable" : "")}{fieldType}()";
187
188 yield return (Name: fieldName, Type: fieldType, ClrType: clrType, AsFunction: asFunction);
189 }
190 }
191
192 private static string GenerateCsTableDefinitionsFileText(string prefix, List<WixTableDefinition> tableDefinitions)
193 {
194 var ns = prefix ?? "Data";
195
196 var startClassDef = String.Join(Environment.NewLine,
197 "// 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.",
198 "",
199 "namespace WixToolset.{1}",
200 "{",
201 " using WixToolset.Data.WindowsInstaller;",
202 "",
203 " public static class {2}TableDefinitions",
204 " {");
205 var startTableDef = String.Join(Environment.NewLine,
206 " public static readonly TableDefinition {1} = new TableDefinition(",
207 " \"{2}\",",
208 " {3},",
209 " new[]",
210 " {");
211 var columnDef =
212 " new ColumnDefinition(\"{1}\", ColumnType.{2}, {3}, primaryKey: {4}, nullable: {5}, ColumnCategory.{6}";
213 var endColumnsDef = String.Join(Environment.NewLine,
214 " },");
215 var unrealDef =
216 " unreal: true,";
217 var endTableDef = String.Join(Environment.NewLine,
218 " symbolIdIsPrimaryKey: {1}",
219 " );",
220 "");
221 var startAllTablesDef = String.Join(Environment.NewLine,
222 " public static readonly TableDefinition[] All = new[]",
223 " {");
224 var allTableDef =
225 " {1},";
226 var endAllTablesDef =
227 " };";
228 var endClassDef = String.Join(Environment.NewLine,
229 " }",
230 "}");
231
232 var sb = new StringBuilder();
233
234 sb.AppendLine(startClassDef.Replace("{1}", ns).Replace("{2}", prefix));
235 foreach (var tableDefinition in tableDefinitions)
236 {
237 var symbolDefinition = tableDefinition.Symbolless ? "null" : $"{prefix}SymbolDefinitions.{tableDefinition.SymbolDefinitionName}";
238 sb.AppendLine(startTableDef.Replace("{1}", tableDefinition.VariableName).Replace("{2}", tableDefinition.Name).Replace("{3}", symbolDefinition));
239 foreach (var columnDefinition in tableDefinition.Columns)
240 {
241 sb.Append(columnDef.Replace("{1}", columnDefinition.Name).Replace("{2}", columnDefinition.Type.ToString()).Replace("{3}", columnDefinition.Length.ToString())
242 .Replace("{4}", columnDefinition.PrimaryKey.ToString().ToLower()).Replace("{5}", columnDefinition.Nullable.ToString().ToLower()).Replace("{6}", columnDefinition.Category.ToString()));
243 if (columnDefinition.MinValue.HasValue)
244 {
245 sb.AppendFormat(", minValue: {0}", columnDefinition.MinValue.Value);
246 }
247 if (columnDefinition.MaxValue.HasValue)
248 {
249 sb.AppendFormat(", maxValue: {0}", columnDefinition.MaxValue.Value);
250 }
251 if (!String.IsNullOrEmpty(columnDefinition.KeyTable))
252 {
253 sb.AppendFormat(", keyTable: \"{0}\"", columnDefinition.KeyTable);
254 }
255 if (columnDefinition.KeyColumn.HasValue)
256 {
257 sb.AppendFormat(", keyColumn: {0}", columnDefinition.KeyColumn.Value);
258 }
259 if (!String.IsNullOrEmpty(columnDefinition.Possibilities))
260 {
261 sb.AppendFormat(", possibilities: \"{0}\"", columnDefinition.Possibilities);
262 }
263 if (!String.IsNullOrEmpty(columnDefinition.Description))
264 {
265 sb.AppendFormat(", description: \"{0}\"", columnDefinition.Description.Replace("\\", "\\\\").Replace("\"", "\\\""));
266 }
267 if (columnDefinition.ModularizeType.HasValue && columnDefinition.ModularizeType.Value != ColumnModularizeType.None)
268 {
269 sb.AppendFormat(", modularizeType: ColumnModularizeType.{0}", columnDefinition.ModularizeType.ToString());
270 }
271 if (columnDefinition.ForceLocalizable)
272 {
273 sb.Append(", forceLocalizable: true");
274 }
275 if (columnDefinition.UseCData)
276 {
277 sb.Append(", useCData: true");
278 }
279 if (columnDefinition.Unreal)
280 {
281 sb.Append(", unreal: true");
282 }
283 sb.AppendLine("),");
284 }
285 sb.AppendLine(endColumnsDef);
286 if (tableDefinition.Unreal)
287 {
288 sb.AppendLine(unrealDef);
289 }
290 sb.AppendLine(endTableDef.Replace("{1}", tableDefinition.SymbolIdIsPrimaryKey.ToString().ToLower()));
291 }
292 sb.AppendLine(startAllTablesDef);
293 foreach (var tableDefinition in tableDefinitions)
294 {
295 sb.AppendLine(allTableDef.Replace("{1}", tableDefinition.VariableName));
296 }
297 sb.AppendLine(endAllTablesDef);
298 sb.AppendLine(endClassDef);
299
300 return sb.ToString();
301 }
302
303 private static string GenerateSymbolFileText(string prefix, string symbolName, List<(string Name, string Type, string ClrType, string AsFunction)> symbolFields)
304 {
305 var ns = prefix ?? "Data";
306 var toString = String.IsNullOrEmpty(prefix) ? null : ".ToString()";
307
308 var startFileDef = String.Join(Environment.NewLine,
309 "// 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.",
310 "",
311 "namespace WixToolset.{2}",
312 "{");
313 var usingDataDef =
314 " using WixToolset.Data;";
315 var startSymbolDef = String.Join(Environment.NewLine,
316 " using WixToolset.{2}.Symbols;",
317 "",
318 " public static partial class {3}SymbolDefinitions",
319 " {",
320 " public static readonly IntermediateSymbolDefinition {1} = new IntermediateSymbolDefinition(",
321 " {3}SymbolDefinitionType.{1}{4},",
322 " new{5}[]",
323 " {");
324 var fieldDef =
325 " new IntermediateFieldDefinition(nameof({1}SymbolFields.{2}), IntermediateFieldType.{3}),";
326 var endSymbolDef = String.Join(Environment.NewLine,
327 " },",
328 " typeof({1}Symbol));",
329 " }",
330 "}",
331 "",
332 "namespace WixToolset.{2}.Symbols",
333 "{");
334 var startEnumDef = String.Join(Environment.NewLine,
335 " public enum {1}SymbolFields",
336 " {");
337 var fieldEnum =
338 " {2},";
339 var startSymbol = String.Join(Environment.NewLine,
340 " }",
341 "",
342 " public class {1}Symbol : IntermediateSymbol",
343 " {",
344 " public {1}Symbol() : base({3}SymbolDefinitions.{1}, null, null)",
345 " {",
346 " }",
347 "",
348 " public {1}Symbol(SourceLineNumber sourceLineNumber, Identifier id = null) : base({3}SymbolDefinitions.{1}, sourceLineNumber, id)",
349 " {",
350 " }",
351 "",
352 " public IntermediateField this[{1}SymbolFields index] => this.Fields[(int)index];");
353 var fieldProp = String.Join(Environment.NewLine,
354 "",
355 " public {4} {2}",
356 " {",
357 " get => {6}this.Fields[(int){1}SymbolFields.{2}]{5};",
358 " set => this.Set((int){1}SymbolFields.{2}, value);",
359 " }");
360 var endSymbol = String.Join(Environment.NewLine,
361 " }",
362 "}");
363
364 var sb = new StringBuilder();
365
366 sb.AppendLine(startFileDef.Replace("{2}", ns));
367 if (ns != "Data")
368 {
369 sb.AppendLine(usingDataDef);
370 }
371 sb.AppendLine(startSymbolDef.Replace("{1}", symbolName).Replace("{2}", ns).Replace("{3}", prefix).Replace("{4}", toString).Replace("{5}", symbolFields.Any() ? null : " IntermediateFieldDefinition"));
372 foreach (var field in symbolFields)
373 {
374 sb.AppendLine(fieldDef.Replace("{1}", symbolName).Replace("{2}", field.Name).Replace("{3}", field.Type));
375 }
376 sb.AppendLine(endSymbolDef.Replace("{1}", symbolName).Replace("{2}", ns).Replace("{3}", prefix));
377 if (ns != "Data")
378 {
379 sb.AppendLine(usingDataDef);
380 sb.AppendLine();
381 }
382 sb.AppendLine(startEnumDef.Replace("{1}", symbolName));
383 foreach (var field in symbolFields)
384 {
385 sb.AppendLine(fieldEnum.Replace("{1}", symbolName).Replace("{2}", field.Name));
386 }
387 sb.AppendLine(startSymbol.Replace("{1}", symbolName).Replace("{2}", ns).Replace("{3}", prefix));
388 foreach (var field in symbolFields)
389 {
390 var useCast = ns == "Data" && field.AsFunction != "AsPath()";
391 var cast = useCast ? $"({field.ClrType})" : null;
392 var asFunction = useCast ? null : $".{field.AsFunction}";
393 sb.AppendLine(fieldProp.Replace("{1}", symbolName).Replace("{2}", field.Name).Replace("{3}", field.Type).Replace("{4}", field.ClrType).Replace("{5}", asFunction).Replace("{6}", cast));
394 }
395 sb.Append(endSymbol);
396
397 return sb.ToString();
398 }
399
400 private static string SymbolNamesFileContent(string prefix, List<string> symbolNames)
401 {
402 var ns = prefix ?? "Data";
403
404 var header = String.Join(Environment.NewLine,
405 "// 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.",
406 "",
407 "namespace WixToolset.{2}",
408 "{",
409 " using System;",
410 " using WixToolset.Data;",
411 "",
412 " public enum {3}SymbolDefinitionType",
413 " {");
414 var namesFormat =
415 " {1},";
416 var midpoint = String.Join(Environment.NewLine,
417 " }",
418 "",
419 " public static partial class {3}SymbolDefinitions",
420 " {",
421 " public static readonly Version Version = new Version(\"4.0.0\");",
422 "",
423 " public static IntermediateSymbolDefinition ByName(string name)",
424 " {",
425 " if (!Enum.TryParse(name, out {3}SymbolDefinitionType type))",
426 " {",
427 " return null;",
428 " }",
429 "",
430 " return ByType(type);",
431 " }",
432 "",
433 " public static IntermediateSymbolDefinition ByType({3}SymbolDefinitionType type)",
434 " {",
435 " switch (type)",
436 " {");
437
438 var caseFormat = String.Join(Environment.NewLine,
439 " case {3}SymbolDefinitionType.{1}:",
440 " return {3}SymbolDefinitions.{1};",
441 "");
442
443 var footer = String.Join(Environment.NewLine,
444 " default:",
445 " throw new ArgumentOutOfRangeException(nameof(type));",
446 " }",
447 " }",
448 " }",
449 "}");
450
451 var sb = new StringBuilder();
452
453 sb.AppendLine(header.Replace("{2}", ns).Replace("{3}", prefix));
454 foreach (var symbolName in symbolNames)
455 {
456 sb.AppendLine(namesFormat.Replace("{1}", symbolName).Replace("{2}", ns).Replace("{3}", prefix));
457 }
458 sb.AppendLine(midpoint.Replace("{2}", ns).Replace("{3}", prefix));
459 foreach (var symbolName in symbolNames)
460 {
461 sb.AppendLine(caseFormat.Replace("{1}", symbolName).Replace("{2}", ns).Replace("{3}", prefix));
462 }
463 sb.AppendLine(footer);
464
465 return sb.ToString();
466 }
467
468 private static string ConvertToFieldType(string fieldType)
469 {
470 switch (fieldType.ToLowerInvariant())
471 {
472 case "bool":
473 return "Bool";
474 case "bool?":
475 return "Number";
476
477 case "string":
478 case "preserved":
479 return "String";
480
481 case "number":
482 case "number?":
483 return "Number";
484
485 case "path":
486 return "Path";
487 }
488
489 throw new ArgumentException(fieldType);
490 }
491
492 private static string ConvertToClrType(string fieldType)
493 {
494 switch (fieldType.ToLowerInvariant())
495 {
496 case "bool":
497 return "bool";
498 case "bool?":
499 return "bool?";
500
501 case "string":
502 case "preserved":
503 return "string";
504
505 case "number":
506 return "int";
507 case "number?":
508 return "int?";
509
510 case "path":
511 return "IntermediateFieldPathValue";
512 }
513
514 throw new ArgumentException(fieldType);
515 }
516
517 private static int CompareSymbolDefinitions(object x, object y)
518 {
519 var first = (JsonObject)x;
520 var second = (JsonObject)y;
521
522 var firstType = first.Keys.Single();
523 var secondType = second.Keys.Single();
524
525 return firstType.CompareTo(secondType);
526 }
527 }
528}