From 6a24996a2e831cfe402398af65b31fb1ecd575a9 Mon Sep 17 00:00:00 2001 From: Rob Mensching Date: Thu, 22 Apr 2021 16:36:39 -0700 Subject: Move WixBuildTools into internal --- src/internal/TablesAndTuples/Program.cs | 528 ++++++++++++++++++++++++++++++++ 1 file changed, 528 insertions(+) create mode 100644 src/internal/TablesAndTuples/Program.cs (limited to 'src/internal/TablesAndTuples/Program.cs') 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 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using System.Text.RegularExpressions; +using System.Xml.Linq; +using SimpleJson; + +namespace TablesAndSymbols +{ + class Program + { + static void Main(string[] args) + { + if (args.Length == 0) + { + return; + } + else if (Path.GetExtension(args[0]) == ".xml") + { + if (args.Length < 2) + { + Console.WriteLine("Need to specify output json file as well."); + return; + } + if (Path.GetExtension(args[1]) != ".json") + { + Console.WriteLine("Output needs to be .json"); + return; + } + + string prefix = null; + if (args.Length > 2) + { + prefix = args[2]; + } + + var csFile = Path.Combine(Path.GetDirectoryName(args[1]), String.Concat(prefix ?? "WindowsInstaller", "TableDefinitions.cs")); + + ReadXmlWriteJson(Path.GetFullPath(args[0]), Path.GetFullPath(args[1]), Path.GetFullPath(csFile), prefix); + } + else if (Path.GetExtension(args[0]) == ".json") + { + string prefix = null; + if (args.Length < 2) + { + Console.WriteLine("Need to specify output folder."); + return; + } + else if (args.Length > 2) + { + prefix = args[2]; + } + + ReadJsonWriteCs(Path.GetFullPath(args[0]), Path.GetFullPath(args[1]), prefix); + } + } + + private static void ReadXmlWriteJson(string inputPath, string outputPath, string csOutputPath, string prefix) + { + var tableDefinitions = ReadXmlWriteCs(inputPath, csOutputPath, prefix); + + var array = new JsonArray(); + + foreach (var tableDefinition in tableDefinitions) + { + if (tableDefinition.Symbolless) + { + continue; + } + var symbolType = tableDefinition.SymbolDefinitionName; + + var fields = new JsonArray(); + var firstField = true; + + foreach (var columnDefinition in tableDefinition.Columns) + { + if (firstField) + { + firstField = false; + if (tableDefinition.SymbolIdIsPrimaryKey) + { + continue; + } + } + + var fieldName = columnDefinition.Name; + fieldName = Regex.Replace(fieldName, "^([^_]+)_([^_]*)$", x => + { + return $"{x.Groups[2].Value}{x.Groups[1].Value}Ref"; + }); + var type = columnDefinition.Type.ToString().ToLower(); + + if (type == "localized") + { + type = "string"; + } + else if (type == "object") + { + type = "path"; + } + else if (columnDefinition.Type == ColumnType.Number && columnDefinition.Length == 2 && + columnDefinition.MinValue == 0 && columnDefinition.MaxValue == 1) + { + type = "bool"; + } + + if (columnDefinition.Type == ColumnType.Number && columnDefinition.Nullable) + { + type += "?"; + } + + var field = new JsonObject + { + { fieldName, type } + }; + + fields.Add(field); + } + + var obj = new JsonObject + { + { symbolType, fields } + }; + array.Add(obj); + } + + array.Sort(CompareSymbolDefinitions); + + var strat = new PocoJsonSerializerStrategy(); + var json = SimpleJson.SimpleJson.SerializeObject(array, strat); + + Directory.CreateDirectory(Path.GetDirectoryName(outputPath)); + File.WriteAllText(outputPath, json); + } + + private static List ReadXmlWriteCs(string inputPath, string outputPath, string prefix) + { + var tableDefinitions = WixTableDefinition.LoadCollection(inputPath); + var text = GenerateCsTableDefinitionsFileText(prefix, tableDefinitions); + Console.WriteLine("Writing: {0}", outputPath); + File.WriteAllText(outputPath, text); + return tableDefinitions; + } + + private static void ReadJsonWriteCs(string inputPath, string outputFolder, string prefix) + { + var json = File.ReadAllText(inputPath); + var symbols = SimpleJson.SimpleJson.DeserializeObject(json) as JsonArray; + + var symbolNames = new List(); + + foreach (var symbolDefinition in symbols.Cast()) + { + var symbolName = symbolDefinition.Keys.Single(); + var fields = symbolDefinition.Values.Single() as JsonArray; + + var list = GetFields(fields).ToList(); + + symbolNames.Add(symbolName); + + var text = GenerateSymbolFileText(prefix, symbolName, list); + + var pathSymbol = Path.Combine(outputFolder, symbolName + "Symbol.cs"); + Console.WriteLine("Writing: {0}", pathSymbol); + File.WriteAllText(pathSymbol, text); + } + + var content = SymbolNamesFileContent(prefix, symbolNames); + var pathNames = Path.Combine(outputFolder, String.Concat(prefix, "SymbolDefinitions.cs")); + Console.WriteLine("Writing: {0}", pathNames); + File.WriteAllText(pathNames, content); + } + + private static IEnumerable<(string Name, string Type, string ClrType, string AsFunction)> GetFields(JsonArray fields) + { + foreach (var field in fields.Cast()) + { + var fieldName = field.Keys.Single(); + var fieldType = field.Values.Single() as string; + + var clrType = ConvertToClrType(fieldType); + fieldType = ConvertToFieldType(fieldType); + + var asFunction = $"As{(clrType.Contains("?") ? "Nullable" : "")}{fieldType}()"; + + yield return (Name: fieldName, Type: fieldType, ClrType: clrType, AsFunction: asFunction); + } + } + + private static string GenerateCsTableDefinitionsFileText(string prefix, List tableDefinitions) + { + var ns = prefix ?? "Data"; + + var startClassDef = String.Join(Environment.NewLine, + "// 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.", + "", + "namespace WixToolset.{1}", + "{", + " using WixToolset.Data.WindowsInstaller;", + "", + " public static class {2}TableDefinitions", + " {"); + var startTableDef = String.Join(Environment.NewLine, + " public static readonly TableDefinition {1} = new TableDefinition(", + " \"{2}\",", + " {3},", + " new[]", + " {"); + var columnDef = + " new ColumnDefinition(\"{1}\", ColumnType.{2}, {3}, primaryKey: {4}, nullable: {5}, ColumnCategory.{6}"; + var endColumnsDef = String.Join(Environment.NewLine, + " },"); + var unrealDef = + " unreal: true,"; + var endTableDef = String.Join(Environment.NewLine, + " symbolIdIsPrimaryKey: {1}", + " );", + ""); + var startAllTablesDef = String.Join(Environment.NewLine, + " public static readonly TableDefinition[] All = new[]", + " {"); + var allTableDef = + " {1},"; + var endAllTablesDef = + " };"; + var endClassDef = String.Join(Environment.NewLine, + " }", + "}"); + + var sb = new StringBuilder(); + + sb.AppendLine(startClassDef.Replace("{1}", ns).Replace("{2}", prefix)); + foreach (var tableDefinition in tableDefinitions) + { + var symbolDefinition = tableDefinition.Symbolless ? "null" : $"{prefix}SymbolDefinitions.{tableDefinition.SymbolDefinitionName}"; + sb.AppendLine(startTableDef.Replace("{1}", tableDefinition.VariableName).Replace("{2}", tableDefinition.Name).Replace("{3}", symbolDefinition)); + foreach (var columnDefinition in tableDefinition.Columns) + { + sb.Append(columnDef.Replace("{1}", columnDefinition.Name).Replace("{2}", columnDefinition.Type.ToString()).Replace("{3}", columnDefinition.Length.ToString()) + .Replace("{4}", columnDefinition.PrimaryKey.ToString().ToLower()).Replace("{5}", columnDefinition.Nullable.ToString().ToLower()).Replace("{6}", columnDefinition.Category.ToString())); + if (columnDefinition.MinValue.HasValue) + { + sb.AppendFormat(", minValue: {0}", columnDefinition.MinValue.Value); + } + if (columnDefinition.MaxValue.HasValue) + { + sb.AppendFormat(", maxValue: {0}", columnDefinition.MaxValue.Value); + } + if (!String.IsNullOrEmpty(columnDefinition.KeyTable)) + { + sb.AppendFormat(", keyTable: \"{0}\"", columnDefinition.KeyTable); + } + if (columnDefinition.KeyColumn.HasValue) + { + sb.AppendFormat(", keyColumn: {0}", columnDefinition.KeyColumn.Value); + } + if (!String.IsNullOrEmpty(columnDefinition.Possibilities)) + { + sb.AppendFormat(", possibilities: \"{0}\"", columnDefinition.Possibilities); + } + if (!String.IsNullOrEmpty(columnDefinition.Description)) + { + sb.AppendFormat(", description: \"{0}\"", columnDefinition.Description.Replace("\\", "\\\\").Replace("\"", "\\\"")); + } + if (columnDefinition.ModularizeType.HasValue && columnDefinition.ModularizeType.Value != ColumnModularizeType.None) + { + sb.AppendFormat(", modularizeType: ColumnModularizeType.{0}", columnDefinition.ModularizeType.ToString()); + } + if (columnDefinition.ForceLocalizable) + { + sb.Append(", forceLocalizable: true"); + } + if (columnDefinition.UseCData) + { + sb.Append(", useCData: true"); + } + if (columnDefinition.Unreal) + { + sb.Append(", unreal: true"); + } + sb.AppendLine("),"); + } + sb.AppendLine(endColumnsDef); + if (tableDefinition.Unreal) + { + sb.AppendLine(unrealDef); + } + sb.AppendLine(endTableDef.Replace("{1}", tableDefinition.SymbolIdIsPrimaryKey.ToString().ToLower())); + } + sb.AppendLine(startAllTablesDef); + foreach (var tableDefinition in tableDefinitions) + { + sb.AppendLine(allTableDef.Replace("{1}", tableDefinition.VariableName)); + } + sb.AppendLine(endAllTablesDef); + sb.AppendLine(endClassDef); + + return sb.ToString(); + } + + private static string GenerateSymbolFileText(string prefix, string symbolName, List<(string Name, string Type, string ClrType, string AsFunction)> symbolFields) + { + var ns = prefix ?? "Data"; + var toString = String.IsNullOrEmpty(prefix) ? null : ".ToString()"; + + var startFileDef = String.Join(Environment.NewLine, + "// 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.", + "", + "namespace WixToolset.{2}", + "{"); + var usingDataDef = + " using WixToolset.Data;"; + var startSymbolDef = String.Join(Environment.NewLine, + " using WixToolset.{2}.Symbols;", + "", + " public static partial class {3}SymbolDefinitions", + " {", + " public static readonly IntermediateSymbolDefinition {1} = new IntermediateSymbolDefinition(", + " {3}SymbolDefinitionType.{1}{4},", + " new{5}[]", + " {"); + var fieldDef = + " new IntermediateFieldDefinition(nameof({1}SymbolFields.{2}), IntermediateFieldType.{3}),"; + var endSymbolDef = String.Join(Environment.NewLine, + " },", + " typeof({1}Symbol));", + " }", + "}", + "", + "namespace WixToolset.{2}.Symbols", + "{"); + var startEnumDef = String.Join(Environment.NewLine, + " public enum {1}SymbolFields", + " {"); + var fieldEnum = + " {2},"; + var startSymbol = String.Join(Environment.NewLine, + " }", + "", + " public class {1}Symbol : IntermediateSymbol", + " {", + " public {1}Symbol() : base({3}SymbolDefinitions.{1}, null, null)", + " {", + " }", + "", + " public {1}Symbol(SourceLineNumber sourceLineNumber, Identifier id = null) : base({3}SymbolDefinitions.{1}, sourceLineNumber, id)", + " {", + " }", + "", + " public IntermediateField this[{1}SymbolFields index] => this.Fields[(int)index];"); + var fieldProp = String.Join(Environment.NewLine, + "", + " public {4} {2}", + " {", + " get => {6}this.Fields[(int){1}SymbolFields.{2}]{5};", + " set => this.Set((int){1}SymbolFields.{2}, value);", + " }"); + var endSymbol = String.Join(Environment.NewLine, + " }", + "}"); + + var sb = new StringBuilder(); + + sb.AppendLine(startFileDef.Replace("{2}", ns)); + if (ns != "Data") + { + sb.AppendLine(usingDataDef); + } + sb.AppendLine(startSymbolDef.Replace("{1}", symbolName).Replace("{2}", ns).Replace("{3}", prefix).Replace("{4}", toString).Replace("{5}", symbolFields.Any() ? null : " IntermediateFieldDefinition")); + foreach (var field in symbolFields) + { + sb.AppendLine(fieldDef.Replace("{1}", symbolName).Replace("{2}", field.Name).Replace("{3}", field.Type)); + } + sb.AppendLine(endSymbolDef.Replace("{1}", symbolName).Replace("{2}", ns).Replace("{3}", prefix)); + if (ns != "Data") + { + sb.AppendLine(usingDataDef); + sb.AppendLine(); + } + sb.AppendLine(startEnumDef.Replace("{1}", symbolName)); + foreach (var field in symbolFields) + { + sb.AppendLine(fieldEnum.Replace("{1}", symbolName).Replace("{2}", field.Name)); + } + sb.AppendLine(startSymbol.Replace("{1}", symbolName).Replace("{2}", ns).Replace("{3}", prefix)); + foreach (var field in symbolFields) + { + var useCast = ns == "Data" && field.AsFunction != "AsPath()"; + var cast = useCast ? $"({field.ClrType})" : null; + var asFunction = useCast ? null : $".{field.AsFunction}"; + sb.AppendLine(fieldProp.Replace("{1}", symbolName).Replace("{2}", field.Name).Replace("{3}", field.Type).Replace("{4}", field.ClrType).Replace("{5}", asFunction).Replace("{6}", cast)); + } + sb.Append(endSymbol); + + return sb.ToString(); + } + + private static string SymbolNamesFileContent(string prefix, List symbolNames) + { + var ns = prefix ?? "Data"; + + var header = String.Join(Environment.NewLine, + "// 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.", + "", + "namespace WixToolset.{2}", + "{", + " using System;", + " using WixToolset.Data;", + "", + " public enum {3}SymbolDefinitionType", + " {"); + var namesFormat = + " {1},"; + var midpoint = String.Join(Environment.NewLine, + " }", + "", + " public static partial class {3}SymbolDefinitions", + " {", + " public static readonly Version Version = new Version(\"4.0.0\");", + "", + " public static IntermediateSymbolDefinition ByName(string name)", + " {", + " if (!Enum.TryParse(name, out {3}SymbolDefinitionType type))", + " {", + " return null;", + " }", + "", + " return ByType(type);", + " }", + "", + " public static IntermediateSymbolDefinition ByType({3}SymbolDefinitionType type)", + " {", + " switch (type)", + " {"); + + var caseFormat = String.Join(Environment.NewLine, + " case {3}SymbolDefinitionType.{1}:", + " return {3}SymbolDefinitions.{1};", + ""); + + var footer = String.Join(Environment.NewLine, + " default:", + " throw new ArgumentOutOfRangeException(nameof(type));", + " }", + " }", + " }", + "}"); + + var sb = new StringBuilder(); + + sb.AppendLine(header.Replace("{2}", ns).Replace("{3}", prefix)); + foreach (var symbolName in symbolNames) + { + sb.AppendLine(namesFormat.Replace("{1}", symbolName).Replace("{2}", ns).Replace("{3}", prefix)); + } + sb.AppendLine(midpoint.Replace("{2}", ns).Replace("{3}", prefix)); + foreach (var symbolName in symbolNames) + { + sb.AppendLine(caseFormat.Replace("{1}", symbolName).Replace("{2}", ns).Replace("{3}", prefix)); + } + sb.AppendLine(footer); + + return sb.ToString(); + } + + private static string ConvertToFieldType(string fieldType) + { + switch (fieldType.ToLowerInvariant()) + { + case "bool": + return "Bool"; + case "bool?": + return "Number"; + + case "string": + case "preserved": + return "String"; + + case "number": + case "number?": + return "Number"; + + case "path": + return "Path"; + } + + throw new ArgumentException(fieldType); + } + + private static string ConvertToClrType(string fieldType) + { + switch (fieldType.ToLowerInvariant()) + { + case "bool": + return "bool"; + case "bool?": + return "bool?"; + + case "string": + case "preserved": + return "string"; + + case "number": + return "int"; + case "number?": + return "int?"; + + case "path": + return "IntermediateFieldPathValue"; + } + + throw new ArgumentException(fieldType); + } + + private static int CompareSymbolDefinitions(object x, object y) + { + var first = (JsonObject)x; + var second = (JsonObject)y; + + var firstType = first.Keys.Single(); + var secondType = second.Keys.Single(); + + return firstType.CompareTo(secondType); + } + } +} -- cgit v1.2.3-55-g6feb