diff options
| author | Rob Mensching <rob@firegiant.com> | 2018-12-22 09:33:44 -0800 |
|---|---|---|
| committer | Rob Mensching <rob@firegiant.com> | 2018-12-22 09:33:44 -0800 |
| commit | 229f9f79b7230124b1e270e636608ceaf95a11bc (patch) | |
| tree | b653d29b3d1b3aac3eec5d9e198750fad17905e0 /src/TablesAndTuples/Program.cs | |
| parent | d08a1f7f46ad7ffe0874133ba20117a7aca0ede4 (diff) | |
| download | wix-229f9f79b7230124b1e270e636608ceaf95a11bc.tar.gz wix-229f9f79b7230124b1e270e636608ceaf95a11bc.tar.bz2 wix-229f9f79b7230124b1e270e636608ceaf95a11bc.zip | |
Quick tool to help convert tables.xml to .cs
Diffstat (limited to 'src/TablesAndTuples/Program.cs')
| -rw-r--r-- | src/TablesAndTuples/Program.cs | 348 |
1 files changed, 348 insertions, 0 deletions
diff --git a/src/TablesAndTuples/Program.cs b/src/TablesAndTuples/Program.cs new file mode 100644 index 00000000..c15c3c6d --- /dev/null +++ b/src/TablesAndTuples/Program.cs | |||
| @@ -0,0 +1,348 @@ | |||
| 1 | using System; | ||
| 2 | using System.Collections.Generic; | ||
| 3 | using System.IO; | ||
| 4 | using System.Linq; | ||
| 5 | using System.Text; | ||
| 6 | using System.Xml.Linq; | ||
| 7 | using SimpleJson; | ||
| 8 | |||
| 9 | namespace TablesAndTuples | ||
| 10 | { | ||
| 11 | class Program | ||
| 12 | { | ||
| 13 | static readonly XNamespace ns = "http://wixtoolset.org/schemas/v4/wi/tables"; | ||
| 14 | static readonly XName TableDefinition = ns + "tableDefinition"; | ||
| 15 | static readonly XName ColumnDefinition = ns + "columnDefinition"; | ||
| 16 | static readonly XName Name = "name"; | ||
| 17 | static readonly XName Type = "type"; | ||
| 18 | |||
| 19 | static void Main(string[] args) | ||
| 20 | { | ||
| 21 | if (args.Length == 0) | ||
| 22 | { | ||
| 23 | return; | ||
| 24 | } | ||
| 25 | else if (Path.GetExtension(args[0]) == ".xml") | ||
| 26 | { | ||
| 27 | if (args.Length < 2) | ||
| 28 | { | ||
| 29 | Console.WriteLine("Need to specify output json file as well."); | ||
| 30 | } | ||
| 31 | |||
| 32 | ReadXmlWriteJson(Path.GetFullPath(args[0]), Path.GetFullPath(args[1])); | ||
| 33 | } | ||
| 34 | else if (Path.GetExtension(args[0]) == ".json") | ||
| 35 | { | ||
| 36 | string prefix = null; | ||
| 37 | if (args.Length < 2) | ||
| 38 | { | ||
| 39 | Console.WriteLine("Need to specify output folder."); | ||
| 40 | } | ||
| 41 | else if (args.Length > 2) | ||
| 42 | { | ||
| 43 | prefix = args[2]; | ||
| 44 | } | ||
| 45 | |||
| 46 | ReadJsonWriteCs(Path.GetFullPath(args[0]), Path.GetFullPath(args[1]), prefix); | ||
| 47 | } | ||
| 48 | } | ||
| 49 | |||
| 50 | private static void ReadXmlWriteJson(string inputPath, string outputPath) | ||
| 51 | { | ||
| 52 | var doc = XDocument.Load(inputPath); | ||
| 53 | |||
| 54 | var array = new JsonArray(); | ||
| 55 | |||
| 56 | foreach (var tableDefinition in doc.Descendants(TableDefinition)) | ||
| 57 | { | ||
| 58 | var tupleType = tableDefinition.Attribute(Name).Value; | ||
| 59 | |||
| 60 | var fields = new JsonArray(); | ||
| 61 | |||
| 62 | foreach (var columnDefinition in tableDefinition.Elements(ColumnDefinition)) | ||
| 63 | { | ||
| 64 | var fieldName = columnDefinition.Attribute(Name).Value; | ||
| 65 | var type = columnDefinition.Attribute(Type).Value; | ||
| 66 | |||
| 67 | if (type == "localized") | ||
| 68 | { | ||
| 69 | type = "string"; | ||
| 70 | } | ||
| 71 | else if (type == "object") | ||
| 72 | { | ||
| 73 | type = "path"; | ||
| 74 | } | ||
| 75 | |||
| 76 | var field = new JsonObject | ||
| 77 | { | ||
| 78 | { fieldName, type } | ||
| 79 | }; | ||
| 80 | |||
| 81 | fields.Add(field); | ||
| 82 | } | ||
| 83 | |||
| 84 | var obj = new JsonObject | ||
| 85 | { | ||
| 86 | { tupleType, fields } | ||
| 87 | }; | ||
| 88 | array.Add(obj); | ||
| 89 | } | ||
| 90 | |||
| 91 | array.Sort(CompareTupleDefinitions); | ||
| 92 | |||
| 93 | var strat = new PocoJsonSerializerStrategy(); | ||
| 94 | var json = SimpleJson.SimpleJson.SerializeObject(array, strat); | ||
| 95 | |||
| 96 | Directory.CreateDirectory(Path.GetDirectoryName(outputPath)); | ||
| 97 | File.WriteAllText(outputPath, json); | ||
| 98 | } | ||
| 99 | |||
| 100 | private static void ReadJsonWriteCs(string inputPath, string outputFolder, string prefix) | ||
| 101 | { | ||
| 102 | var json = File.ReadAllText(inputPath); | ||
| 103 | var tuples = SimpleJson.SimpleJson.DeserializeObject(json) as JsonArray; | ||
| 104 | |||
| 105 | var tupleNames = new List<string>(); | ||
| 106 | |||
| 107 | foreach (var tupleDefinition in tuples.Cast<JsonObject>()) | ||
| 108 | { | ||
| 109 | var tupleName = tupleDefinition.Keys.Single(); | ||
| 110 | var fields = tupleDefinition.Values.Single() as JsonArray; | ||
| 111 | |||
| 112 | var list = GetFields(fields).ToList(); | ||
| 113 | |||
| 114 | tupleNames.Add(tupleName); | ||
| 115 | |||
| 116 | var text = GenerateTupleFileText(prefix, tupleName, list); | ||
| 117 | |||
| 118 | var pathTuple = Path.Combine(outputFolder, tupleName + "Tuple.cs"); | ||
| 119 | Console.WriteLine("Writing: {0}", pathTuple); | ||
| 120 | File.WriteAllText(pathTuple, text); | ||
| 121 | } | ||
| 122 | |||
| 123 | var content = TupleNamesFileContent(prefix, tupleNames); | ||
| 124 | var pathNames = Path.Combine(outputFolder, String.Concat(prefix, "TupleDefinitions.cs")); | ||
| 125 | Console.WriteLine("Writing: {0}", pathNames); | ||
| 126 | File.WriteAllText(pathNames, content); | ||
| 127 | } | ||
| 128 | |||
| 129 | private static IEnumerable<(string Name, string Type, string ClrType, string AsFunction)> GetFields(JsonArray fields) | ||
| 130 | { | ||
| 131 | foreach (var field in fields.Cast<JsonObject>()) | ||
| 132 | { | ||
| 133 | var fieldName = field.Keys.Single(); | ||
| 134 | var fieldType = field.Values.Single() as string; | ||
| 135 | |||
| 136 | var clrType = ConvertToClrType(fieldType); | ||
| 137 | fieldType = ConvertToFieldType(fieldType); | ||
| 138 | |||
| 139 | var asFunction = $"As{fieldType}()"; | ||
| 140 | |||
| 141 | yield return (Name: fieldName, Type: fieldType, ClrType: clrType, AsFunction: asFunction); | ||
| 142 | } | ||
| 143 | } | ||
| 144 | |||
| 145 | private static string GenerateTupleFileText(string prefix, string tupleName, List<(string Name, string Type, string ClrType, string AsFunction)> tupleFields) | ||
| 146 | { | ||
| 147 | var ns = prefix ?? "Data"; | ||
| 148 | var toString = String.IsNullOrEmpty(prefix) ? null : ".ToString()"; | ||
| 149 | |||
| 150 | var startTupleDef = String.Join(Environment.NewLine, | ||
| 151 | "// 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.", | ||
| 152 | "", | ||
| 153 | "namespace WixToolset.{2}", | ||
| 154 | "{", | ||
| 155 | " using WixToolset.Data;", | ||
| 156 | " using WixToolset.{2}.Tuples;", | ||
| 157 | "", | ||
| 158 | " public static partial class {3}TupleDefinitions", | ||
| 159 | " {", | ||
| 160 | " public static readonly IntermediateTupleDefinition {1} = new IntermediateTupleDefinition(", | ||
| 161 | " {3}TupleDefinitionType.{1}{4},", | ||
| 162 | " new[]", | ||
| 163 | " {"); | ||
| 164 | var fieldDef = | ||
| 165 | " new IntermediateFieldDefinition(nameof({1}TupleFields.{2}), IntermediateFieldType.{3}),"; | ||
| 166 | var endTupleDef = String.Join(Environment.NewLine, | ||
| 167 | " },", | ||
| 168 | " typeof({1}Tuple));", | ||
| 169 | " }", | ||
| 170 | "}", | ||
| 171 | "", | ||
| 172 | "namespace WixToolset.{2}.Tuples", | ||
| 173 | "{", | ||
| 174 | " using WixToolset.Data;", | ||
| 175 | "", | ||
| 176 | " public enum {1}TupleFields", | ||
| 177 | " {"); | ||
| 178 | var fieldEnum = | ||
| 179 | " {2},"; | ||
| 180 | var startTuple = String.Join(Environment.NewLine, | ||
| 181 | " }", | ||
| 182 | "", | ||
| 183 | " public class {1}Tuple : IntermediateTuple", | ||
| 184 | " {", | ||
| 185 | " public {1}Tuple() : base({3}TupleDefinitions.{1}, null, null)", | ||
| 186 | " {", | ||
| 187 | " }", | ||
| 188 | "", | ||
| 189 | " public {1}Tuple(SourceLineNumber sourceLineNumber, Identifier id = null) : base({3}TupleDefinitions.{1}, sourceLineNumber, id)", | ||
| 190 | " {", | ||
| 191 | " }", | ||
| 192 | "", | ||
| 193 | " public IntermediateField this[{1}TupleFields index] => this.Fields[(int)index];"); | ||
| 194 | var fieldProp = String.Join(Environment.NewLine, | ||
| 195 | "", | ||
| 196 | " public {4} {2}", | ||
| 197 | " {", | ||
| 198 | " get => this.Fields[(int){1}TupleFields.{2}].{5};", | ||
| 199 | " set => this.Set((int){1}TupleFields.{2}, value);", | ||
| 200 | " }"); | ||
| 201 | var endTuple = String.Join(Environment.NewLine, | ||
| 202 | " }", | ||
| 203 | "}"); | ||
| 204 | |||
| 205 | var sb = new StringBuilder(); | ||
| 206 | |||
| 207 | sb.AppendLine(startTupleDef.Replace("{1}", tupleName).Replace("{2}", ns).Replace("{3}", prefix).Replace("{4}", toString)); | ||
| 208 | foreach (var field in tupleFields) | ||
| 209 | { | ||
| 210 | sb.AppendLine(fieldDef.Replace("{1}", tupleName).Replace("{2}", field.Name).Replace("{3}", field.Type).Replace("{4}", field.ClrType).Replace("{5}", field.AsFunction)); | ||
| 211 | } | ||
| 212 | sb.AppendLine(endTupleDef.Replace("{1}", tupleName).Replace("{2}", ns).Replace("{3}", prefix)); | ||
| 213 | foreach (var field in tupleFields) | ||
| 214 | { | ||
| 215 | sb.AppendLine(fieldEnum.Replace("{1}", tupleName).Replace("{2}", field.Name).Replace("{3}", field.Type).Replace("{4}", field.ClrType).Replace("{5}", field.AsFunction)); | ||
| 216 | } | ||
| 217 | sb.AppendLine(startTuple.Replace("{1}", tupleName).Replace("{2}", ns).Replace("{3}", prefix)); | ||
| 218 | foreach (var field in tupleFields) | ||
| 219 | { | ||
| 220 | sb.AppendLine(fieldProp.Replace("{1}", tupleName).Replace("{2}", field.Name).Replace("{3}", field.Type).Replace("{4}", field.ClrType).Replace("{5}", field.AsFunction)); | ||
| 221 | } | ||
| 222 | sb.Append(endTuple); | ||
| 223 | |||
| 224 | return sb.ToString(); | ||
| 225 | } | ||
| 226 | |||
| 227 | private static string TupleNamesFileContent(string prefix, List<string> tupleNames) | ||
| 228 | { | ||
| 229 | var ns = prefix ?? "Data"; | ||
| 230 | |||
| 231 | var header = String.Join(Environment.NewLine, | ||
| 232 | "// 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.", | ||
| 233 | "", | ||
| 234 | "namespace WixToolset.{2}", | ||
| 235 | "{", | ||
| 236 | " using System;", | ||
| 237 | " using WixToolset.Data;", | ||
| 238 | "", | ||
| 239 | " public enum {3}TupleDefinitionType", | ||
| 240 | " {"); | ||
| 241 | var namesFormat = | ||
| 242 | " {1},"; | ||
| 243 | var midpoint = String.Join(Environment.NewLine, | ||
| 244 | " }", | ||
| 245 | "", | ||
| 246 | " public static partial class {3}TupleDefinitions", | ||
| 247 | " {", | ||
| 248 | " public static readonly Version Version = new Version(\"4.0.0\");", | ||
| 249 | "", | ||
| 250 | " public static IntermediateTupleDefinition ByName(string name)", | ||
| 251 | " {", | ||
| 252 | " if (!Enum.TryParse(name, out {3}TupleDefinitionType type))", | ||
| 253 | " {", | ||
| 254 | " return null;", | ||
| 255 | " }", | ||
| 256 | "", | ||
| 257 | " return ByType(type);", | ||
| 258 | " }", | ||
| 259 | "", | ||
| 260 | " public static IntermediateTupleDefinition ByType({3}TupleDefinitionType type)", | ||
| 261 | " {", | ||
| 262 | " switch (type)", | ||
| 263 | " {"); | ||
| 264 | |||
| 265 | var caseFormat = String.Join(Environment.NewLine, | ||
| 266 | " case {3}TupleDefinitionType.{1}:", | ||
| 267 | " return {3}TupleDefinitions.{1};", | ||
| 268 | ""); | ||
| 269 | |||
| 270 | var footer = String.Join(Environment.NewLine, | ||
| 271 | " default:", | ||
| 272 | " throw new ArgumentOutOfRangeException(nameof(type));", | ||
| 273 | " }", | ||
| 274 | " }", | ||
| 275 | " }", | ||
| 276 | "}"); | ||
| 277 | |||
| 278 | var sb = new StringBuilder(); | ||
| 279 | |||
| 280 | sb.AppendLine(header.Replace("{2}", ns).Replace("{3}", prefix)); | ||
| 281 | foreach (var tupleName in tupleNames) | ||
| 282 | { | ||
| 283 | sb.AppendLine(namesFormat.Replace("{1}", tupleName).Replace("{2}", ns).Replace("{3}", prefix)); | ||
| 284 | } | ||
| 285 | sb.AppendLine(midpoint.Replace("{2}", ns).Replace("{3}", prefix)); | ||
| 286 | foreach (var tupleName in tupleNames) | ||
| 287 | { | ||
| 288 | sb.AppendLine(caseFormat.Replace("{1}", tupleName).Replace("{2}", ns).Replace("{3}", prefix)); | ||
| 289 | } | ||
| 290 | sb.AppendLine(footer); | ||
| 291 | |||
| 292 | return sb.ToString(); | ||
| 293 | } | ||
| 294 | |||
| 295 | private static string ConvertToFieldType(string fieldType) | ||
| 296 | { | ||
| 297 | switch (fieldType.ToLowerInvariant()) | ||
| 298 | { | ||
| 299 | case "bool": | ||
| 300 | return "Bool"; | ||
| 301 | |||
| 302 | case "string": | ||
| 303 | case "preserved": | ||
| 304 | return "String"; | ||
| 305 | |||
| 306 | case "number": | ||
| 307 | return "Number"; | ||
| 308 | |||
| 309 | case "path": | ||
| 310 | return "Path"; | ||
| 311 | } | ||
| 312 | |||
| 313 | throw new ArgumentException(fieldType); | ||
| 314 | } | ||
| 315 | |||
| 316 | private static string ConvertToClrType(string fieldType) | ||
| 317 | { | ||
| 318 | switch (fieldType.ToLowerInvariant()) | ||
| 319 | { | ||
| 320 | case "bool": | ||
| 321 | return "bool"; | ||
| 322 | |||
| 323 | case "string": | ||
| 324 | case "preserved": | ||
| 325 | return "string"; | ||
| 326 | |||
| 327 | case "number": | ||
| 328 | return "int"; | ||
| 329 | |||
| 330 | case "path": | ||
| 331 | return "string"; | ||
| 332 | } | ||
| 333 | |||
| 334 | throw new ArgumentException(fieldType); | ||
| 335 | } | ||
| 336 | |||
| 337 | private static int CompareTupleDefinitions(object x, object y) | ||
| 338 | { | ||
| 339 | var first = (JsonObject)x; | ||
| 340 | var second = (JsonObject)y; | ||
| 341 | |||
| 342 | var firstType = first.Keys.Single(); | ||
| 343 | var secondType = second.Keys.Single(); | ||
| 344 | |||
| 345 | return firstType.CompareTo(secondType); | ||
| 346 | } | ||
| 347 | } | ||
| 348 | } | ||
