aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorSean Hall <r.sean.hall@gmail.com>2020-04-05 20:20:55 +1000
committerSean Hall <r.sean.hall@gmail.com>2020-04-05 21:51:53 +1000
commitd854e2a53a8943e17c004a983d78ea58cbf6f1be (patch)
tree1381e9115f6d1c91fdd6d56e932d04b717b150f5 /src
parent8185b8482fe9576281a3a89947e54182e213a24b (diff)
downloadwix-d854e2a53a8943e17c004a983d78ea58cbf6f1be.tar.gz
wix-d854e2a53a8943e17c004a983d78ea58cbf6f1be.tar.bz2
wix-d854e2a53a8943e17c004a983d78ea58cbf6f1be.zip
Add ability to TablesAndTuples to generate TableDefinitions.cs.
Diffstat (limited to 'src')
-rw-r--r--src/TablesAndTuples/ColumnDefinitionEnums.cs56
-rw-r--r--src/TablesAndTuples/Program.cs142
-rw-r--r--src/TablesAndTuples/WixColumnDefinition.cs296
-rw-r--r--src/TablesAndTuples/WixTableDefinition.cs159
4 files changed, 650 insertions, 3 deletions
diff --git a/src/TablesAndTuples/ColumnDefinitionEnums.cs b/src/TablesAndTuples/ColumnDefinitionEnums.cs
new file mode 100644
index 00000000..ac50e1cd
--- /dev/null
+++ b/src/TablesAndTuples/ColumnDefinitionEnums.cs
@@ -0,0 +1,56 @@
1namespace 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}
diff --git a/src/TablesAndTuples/Program.cs b/src/TablesAndTuples/Program.cs
index c15c3c6d..bcec604c 100644
--- a/src/TablesAndTuples/Program.cs
+++ b/src/TablesAndTuples/Program.cs
@@ -1,8 +1,9 @@
1using System; 1using System;
2using System.Collections.Generic; 2using System.Collections.Generic;
3using System.IO; 3using System.IO;
4using System.Linq; 4using System.Linq;
5using System.Text; 5using System.Text;
6using System.Text.RegularExpressions;
6using System.Xml.Linq; 7using System.Xml.Linq;
7using SimpleJson; 8using SimpleJson;
8 9
@@ -27,9 +28,23 @@ namespace TablesAndTuples
27 if (args.Length < 2) 28 if (args.Length < 2)
28 { 29 {
29 Console.WriteLine("Need to specify output json file as well."); 30 Console.WriteLine("Need to specify output json file as well.");
31 return;
30 } 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"));
31 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);
33 } 48 }
34 else if (Path.GetExtension(args[0]) == ".json") 49 else if (Path.GetExtension(args[0]) == ".json")
35 { 50 {
@@ -37,6 +52,7 @@ namespace TablesAndTuples
37 if (args.Length < 2) 52 if (args.Length < 2)
38 { 53 {
39 Console.WriteLine("Need to specify output folder."); 54 Console.WriteLine("Need to specify output folder.");
55 return;
40 } 56 }
41 else if (args.Length > 2) 57 else if (args.Length > 2)
42 { 58 {
@@ -47,8 +63,10 @@ namespace TablesAndTuples
47 } 63 }
48 } 64 }
49 65
50 private static void ReadXmlWriteJson(string inputPath, string outputPath) 66 private static void ReadXmlWriteJson(string inputPath, string outputPath, string csOutputPath, string prefix)
51 { 67 {
68 ReadXmlWriteCs(inputPath, csOutputPath, prefix);
69
52 var doc = XDocument.Load(inputPath); 70 var doc = XDocument.Load(inputPath);
53 71
54 var array = new JsonArray(); 72 var array = new JsonArray();
@@ -97,6 +115,14 @@ namespace TablesAndTuples
97 File.WriteAllText(outputPath, json); 115 File.WriteAllText(outputPath, json);
98 } 116 }
99 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
100 private static void ReadJsonWriteCs(string inputPath, string outputFolder, string prefix) 126 private static void ReadJsonWriteCs(string inputPath, string outputFolder, string prefix)
101 { 127 {
102 var json = File.ReadAllText(inputPath); 128 var json = File.ReadAllText(inputPath);
@@ -142,6 +168,116 @@ namespace TablesAndTuples
142 } 168 }
143 } 169 }
144 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
145 private static string GenerateTupleFileText(string prefix, string tupleName, List<(string Name, string Type, string ClrType, string AsFunction)> tupleFields) 281 private static string GenerateTupleFileText(string prefix, string tupleName, List<(string Name, string Type, string ClrType, string AsFunction)> tupleFields)
146 { 282 {
147 var ns = prefix ?? "Data"; 283 var ns = prefix ?? "Data";
diff --git a/src/TablesAndTuples/WixColumnDefinition.cs b/src/TablesAndTuples/WixColumnDefinition.cs
new file mode 100644
index 00000000..a40bacfa
--- /dev/null
+++ b/src/TablesAndTuples/WixColumnDefinition.cs
@@ -0,0 +1,296 @@
1using System;
2using System.Xml;
3
4namespace 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}
diff --git a/src/TablesAndTuples/WixTableDefinition.cs b/src/TablesAndTuples/WixTableDefinition.cs
new file mode 100644
index 00000000..baada41d
--- /dev/null
+++ b/src/TablesAndTuples/WixTableDefinition.cs
@@ -0,0 +1,159 @@
1using System.Collections.Generic;
2using System.Linq;
3using System.Xml;
4
5namespace 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}