Add ability to TablesAndTuples to generate TableDefinitions.cs.

This commit is contained in:
Sean Hall 2020-04-05 20:20:55 +10:00
Родитель 8185b8482f
Коммит d854e2a53a
4 изменённых файлов: 650 добавлений и 3 удалений

Просмотреть файл

@ -0,0 +1,56 @@
namespace TablesAndTuples
{
public enum ColumnCategory
{
Unknown,
Text,
UpperCase,
LowerCase,
Integer,
DoubleInteger,
TimeDate,
Identifier,
Property,
Filename,
WildCardFilename,
Path,
Paths,
AnyPath,
DefaultDir,
RegPath,
Formatted,
Template,
Condition,
Guid,
Version,
Language,
Binary,
CustomSource,
Cabinet,
Shortcut,
FormattedSDDLText,
}
public enum ColumnModularizeType
{
None,
Column,
Icon,
CompanionFile,
Condition,
ControlEventArgument,
ControlText,
Property,
SemicolonDelimited,
}
public enum ColumnType
{
Unknown,
String,
Localized,
Number,
Object,
Preserved,
}
}

Просмотреть файл

@ -1,8 +1,9 @@
using System;
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;
@ -27,9 +28,23 @@ namespace TablesAndTuples
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;
}
ReadXmlWriteJson(Path.GetFullPath(args[0]), Path.GetFullPath(args[1]));
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")
{
@ -37,6 +52,7 @@ namespace TablesAndTuples
if (args.Length < 2)
{
Console.WriteLine("Need to specify output folder.");
return;
}
else if (args.Length > 2)
{
@ -47,8 +63,10 @@ namespace TablesAndTuples
}
}
private static void ReadXmlWriteJson(string inputPath, string outputPath)
private static void ReadXmlWriteJson(string inputPath, string outputPath, string csOutputPath, string prefix)
{
ReadXmlWriteCs(inputPath, csOutputPath, prefix);
var doc = XDocument.Load(inputPath);
var array = new JsonArray();
@ -97,6 +115,14 @@ namespace TablesAndTuples
File.WriteAllText(outputPath, json);
}
private static void 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);
}
private static void ReadJsonWriteCs(string inputPath, string outputFolder, string prefix)
{
var json = File.ReadAllText(inputPath);
@ -142,6 +168,116 @@ namespace TablesAndTuples
}
}
private static string GenerateCsTableDefinitionsFileText(string prefix, List<WixTableDefinition> 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(",
" \"{1}\",",
" 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,
" tupleDefinitionName: \"{1}\",",
" tupleIdIsPrimaryKey: {2}",
" );",
"");
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)
{
sb.AppendLine(startTableDef.Replace("{1}", tableDefinition.Name));
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);
}
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.TupleDefinitionName).Replace("{2}", tableDefinition.TupleIdIsPrimaryKey.ToString().ToLower()));
}
sb.AppendLine(startAllTablesDef);
foreach (var tableDefinition in tableDefinitions)
{
sb.AppendLine(allTableDef.Replace("{1}", tableDefinition.Name));
}
sb.AppendLine(endAllTablesDef);
sb.AppendLine(endClassDef);
return sb.ToString();
}
private static string GenerateTupleFileText(string prefix, string tupleName, List<(string Name, string Type, string ClrType, string AsFunction)> tupleFields)
{
var ns = prefix ?? "Data";

Просмотреть файл

@ -0,0 +1,296 @@
using System;
using System.Xml;
namespace TablesAndTuples
{
class WixColumnDefinition
{
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)
{
this.Name = name;
this.Type = type;
this.Length = length;
this.PrimaryKey = primaryKey;
this.Nullable = nullable;
this.ModularizeType = modularizeType;
this.ForceLocalizable = forceLocalizable;
this.MinValue = minValue;
this.MaxValue = maxValue;
this.KeyTable = keyTable;
this.KeyColumn = keyColumn;
this.Category = category;
this.Possibilities = possibilities;
this.Description = description;
this.UseCData = useCData;
this.Unreal = unreal;
}
public string Name { get; }
public ColumnType Type { get; }
public int Length { get; }
public bool PrimaryKey { get; }
public bool Nullable { get; }
public ColumnModularizeType? ModularizeType { get; }
public bool ForceLocalizable { get; }
public long? MinValue { get; }
public long? MaxValue { get; }
public string KeyTable { get; }
public int? KeyColumn { get; }
public ColumnCategory Category { get; }
public string Possibilities { get; }
public string Description { get; }
public bool UseCData { get; }
public bool Unreal { get; }
internal static WixColumnDefinition Read(XmlReader reader)
{
if (!reader.LocalName.Equals("columnDefinition"))
{
throw new XmlException();
}
ColumnCategory category = ColumnCategory.Unknown;
string description = null;
bool empty = reader.IsEmptyElement;
int? keyColumn = null;
string keyTable = null;
int length = -1;
bool localizable = false;
long? maxValue = null;
long? minValue = null;
var modularize = ColumnModularizeType.None;
string name = null;
bool nullable = false;
string possibilities = null;
bool primaryKey = false;
var type = ColumnType.Unknown;
bool useCData = false;
bool unreal = false;
// parse the attributes
while (reader.MoveToNextAttribute())
{
switch (reader.LocalName)
{
case "category":
switch (reader.Value)
{
case "anyPath":
category = ColumnCategory.AnyPath;
break;
case "binary":
category = ColumnCategory.Binary;
break;
case "cabinet":
category = ColumnCategory.Cabinet;
break;
case "condition":
category = ColumnCategory.Condition;
break;
case "customSource":
category = ColumnCategory.CustomSource;
break;
case "defaultDir":
category = ColumnCategory.DefaultDir;
break;
case "doubleInteger":
category = ColumnCategory.DoubleInteger;
break;
case "filename":
category = ColumnCategory.Filename;
break;
case "formatted":
category = ColumnCategory.Formatted;
break;
case "formattedSddl":
category = ColumnCategory.FormattedSDDLText;
break;
case "guid":
category = ColumnCategory.Guid;
break;
case "identifier":
category = ColumnCategory.Identifier;
break;
case "integer":
category = ColumnCategory.Integer;
break;
case "language":
category = ColumnCategory.Language;
break;
case "lowerCase":
category = ColumnCategory.LowerCase;
break;
case "path":
category = ColumnCategory.Path;
break;
case "paths":
category = ColumnCategory.Paths;
break;
case "property":
category = ColumnCategory.Property;
break;
case "regPath":
category = ColumnCategory.RegPath;
break;
case "shortcut":
category = ColumnCategory.Shortcut;
break;
case "template":
category = ColumnCategory.Template;
break;
case "text":
category = ColumnCategory.Text;
break;
case "timeDate":
category = ColumnCategory.TimeDate;
break;
case "upperCase":
category = ColumnCategory.UpperCase;
break;
case "version":
category = ColumnCategory.Version;
break;
case "wildCardFilename":
category = ColumnCategory.WildCardFilename;
break;
default:
throw new InvalidOperationException();
}
break;
case "description":
description = reader.Value;
break;
case "keyColumn":
keyColumn = Convert.ToInt32(reader.Value, 10);
break;
case "keyTable":
keyTable = reader.Value;
break;
case "length":
length = Convert.ToInt32(reader.Value, 10);
break;
case "localizable":
localizable = reader.Value.Equals("yes");
break;
case "maxValue":
maxValue = Convert.ToInt32(reader.Value, 10);
break;
case "minValue":
minValue = Convert.ToInt32(reader.Value, 10);
break;
case "modularize":
switch (reader.Value)
{
case "column":
modularize = ColumnModularizeType.Column;
break;
case "companionFile":
modularize = ColumnModularizeType.CompanionFile;
break;
case "condition":
modularize = ColumnModularizeType.Condition;
break;
case "controlEventArgument":
modularize = ColumnModularizeType.ControlEventArgument;
break;
case "controlText":
modularize = ColumnModularizeType.ControlText;
break;
case "icon":
modularize = ColumnModularizeType.Icon;
break;
case "none":
modularize = ColumnModularizeType.None;
break;
case "property":
modularize = ColumnModularizeType.Property;
break;
case "semicolonDelimited":
modularize = ColumnModularizeType.SemicolonDelimited;
break;
default:
throw new XmlException();
}
break;
case "name":
switch (reader.Value)
{
case "CREATE":
case "DELETE":
case "DROP":
case "INSERT":
throw new XmlException();
default:
name = reader.Value;
break;
}
break;
case "nullable":
nullable = reader.Value.Equals("yes");
break;
case "primaryKey":
primaryKey = reader.Value.Equals("yes");
break;
case "set":
possibilities = reader.Value;
break;
case "type":
switch (reader.Value)
{
case "localized":
type = ColumnType.Localized;
break;
case "number":
type = ColumnType.Number;
break;
case "object":
type = ColumnType.Object;
break;
case "string":
type = ColumnType.String;
break;
case "preserved":
type = ColumnType.Preserved;
break;
default:
throw new XmlException();
}
break;
case "useCData":
useCData = reader.Value.Equals("yes");
break;
case "unreal":
unreal = reader.Value.Equals("yes");
break;
}
}
// parse the child elements (there should be none)
if (!empty)
{
bool done = false;
while (!done && reader.Read())
{
switch (reader.NodeType)
{
case XmlNodeType.Element:
throw new XmlException();
case XmlNodeType.EndElement:
done = true;
break;
}
}
if (!done)
{
throw new XmlException();
}
}
WixColumnDefinition columnDefinition = new WixColumnDefinition(name, type, length, primaryKey, nullable, category, minValue, maxValue, keyTable, keyColumn, possibilities, description, modularize, localizable, useCData, unreal);
return columnDefinition;
}
}
}

Просмотреть файл

@ -0,0 +1,159 @@
using System.Collections.Generic;
using System.Linq;
using System.Xml;
namespace TablesAndTuples
{
class WixTableDefinition
{
public WixTableDefinition(string name, IEnumerable<WixColumnDefinition> columns, bool unreal, string tupleDefinitionName, bool? tupleIdIsPrimaryKey)
{
this.Name = name;
this.Unreal = unreal;
this.Columns = columns?.ToArray();
this.TupleDefinitionName = tupleDefinitionName ?? name;
this.TupleIdIsPrimaryKey = tupleIdIsPrimaryKey ?? DeriveTupleIdIsPrimaryKey(this.Columns);
}
public string Name { get; }
public string TupleDefinitionName { get; }
public bool Unreal { get; }
public WixColumnDefinition[] Columns { get; }
public bool TupleIdIsPrimaryKey { get; }
static WixTableDefinition Read(XmlReader reader)
{
var empty = reader.IsEmptyElement;
string name = null;
string tupleDefinitionName = null;
var unreal = false;
bool? tupleIdIsPrimaryKey = null;
while (reader.MoveToNextAttribute())
{
switch (reader.LocalName)
{
case "name":
name = reader.Value;
break;
case "tupleDefinitionName":
tupleDefinitionName = reader.Value;
break;
case "tupleIdIsPrimaryKey":
tupleIdIsPrimaryKey = reader.Value.Equals("yes");
break;
case "unreal":
unreal = reader.Value.Equals("yes");
break;
}
}
if (null == name)
{
throw new XmlException();
}
var columns = new List<WixColumnDefinition>();
// parse the child elements
if (!empty)
{
var done = false;
while (!done && reader.Read())
{
switch (reader.NodeType)
{
case XmlNodeType.Element:
switch (reader.LocalName)
{
case "columnDefinition":
var columnDefinition = WixColumnDefinition.Read(reader);
columns.Add(columnDefinition);
break;
default:
throw new XmlException();
}
break;
case XmlNodeType.EndElement:
done = true;
break;
}
}
if (!done)
{
throw new XmlException();
}
}
return new WixTableDefinition(name, columns.ToArray(), unreal, tupleDefinitionName, tupleIdIsPrimaryKey);
}
static bool DeriveTupleIdIsPrimaryKey(WixColumnDefinition[] columns)
{
return columns[0].PrimaryKey &&
columns[0].Type == ColumnType.String &&
columns[0].Category == ColumnCategory.Identifier &&
!columns[0].Name.EndsWith("_") &&
(columns.Length == 1 || !columns.Skip(1).Any(t => t.PrimaryKey));
}
public static List<WixTableDefinition> LoadCollection(string inputPath)
{
using (var reader = XmlReader.Create(inputPath))
{
reader.MoveToContent();
if ("tableDefinitions" != reader.LocalName)
{
throw new XmlException();
}
var empty = reader.IsEmptyElement;
var tableDefinitions = new List<WixTableDefinition>();
while (reader.MoveToNextAttribute())
{
}
// parse the child elements
if (!empty)
{
var done = false;
while (!done && reader.Read())
{
switch (reader.NodeType)
{
case XmlNodeType.Element:
switch (reader.LocalName)
{
case "tableDefinition":
tableDefinitions.Add(WixTableDefinition.Read(reader));
break;
default:
throw new XmlException();
}
break;
case XmlNodeType.EndElement:
done = true;
break;
}
}
if (!done)
{
throw new XmlException();
}
}
return tableDefinitions;
}
}
}
}