Files
Common-VS2019/SacsStandardDbas/Dbas/Proc/SaDbProcess_ConvertDbDefinition.cs
T
2024-02-27 15:45:02 +00:00

426 lines
17 KiB
C#

using Sarand.Net.Standard.Base.App;
using Sarand.Net.Standard.Base.Core;
using Sarand.Net.Standard.Base.Dbas;
using Sarand.Net.Standard.Base.IO;
using Sarand.Net.Standard.Base.Procs;
using Sarand.Net.Standard.Base.Utils;
using Sarand.Net.Standard.Dbas.Base;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Sarand.Net.Standard.Dbas.Proc
{
//=======================================================================
public class SaDbProcess_ConvertDbDefinition : SaProcess_Base
{
//-------------------------------------------------------------------
// Configuration.
public enum SourceDefinitionType
{
Unknown = 0,
CppCode
}
public enum TargetDefinitionType
{
Unknown = 0,
CsCode
}
public new class Config : SaProcess_Base.Config
{
public Config()
{
}
public SourceDefinitionType SourceDefinitionType { get; set; }
public String SourceDefinitionFilePath { get; set; }
public TargetDefinitionType TargetDefinitionType { get; set; }
public String TargetDefinitionFilePath { get; set; }
}
//-------------------------------------------------------------------
// Definitions.
private class Statistics
{
}
//-------------------------------------------------------------------
// Data members.
private Config config;
private readonly List<DbDfnTable> tableDfns = new List<DbDfnTable>();
private Statistics stats;
//-------------------------------------------------------------------
// Construction.
public SaDbProcess_ConvertDbDefinition(Config config) : base(config)
{
this.config = config;
}
protected override void Dispose(bool disposing)
{
if (disposing)
{
}
base.Dispose(disposing);
}
//-------------------------------------------------------------------
// Properties.
public override string ProcessDescription { get; } = "Database Definition Convertor";
/// <summary>Processing statistics.</summary>
public override List<StringPair> ProcessingStatistics
{
get
{
List<StringPair> processStats = new List<StringPair>();
if (stats != null)
{
//if (tableDfns.Count > 0)
processStats.Add("Tables found", tableDfns.Count.ToString());
processStats.Add("Columns found", tableDfns.GetColumnCount().ToString());
}
return processStats;
}
}
//-------------------------------------------------------------------
// P r o c e s s i n g
//-------------------------------------------------------------------
protected override bool Run()
{
// Initialise process.
tableDfns.Clear();
stats = new Statistics();
// Validate configuration.
if (!ValidateConfiguration()) return false;
ConvertCppDatabaseDefinition();
return OK;
}
protected bool ValidateConfiguration()
{
// Source file.
if (config.SourceDefinitionType <= 0)
return AddErrorMessage("Source definition type must be specified.");
if (StringUtil.IsEmpty(config.SourceDefinitionFilePath))
return AddErrorMessage("Source definition file must be specified.");
if (!FileUtil.IsFile(config.SourceDefinitionFilePath))
return AddErrorMessage("Source definition file not found.");
// Target file.
if (config.TargetDefinitionType <= 0)
return AddErrorMessage("Target definition type must be specified.");
if (StringUtil.IsEmpty(config.TargetDefinitionFilePath))
return AddErrorMessage("Target definition file must be specified.");
if (!DirectoryUtil.DirectoryExists(DirectoryUtil.GetDirectoryName(config.TargetDefinitionFilePath)))
return AddErrorMessage("Target folder not found.");
if (config.SourceDefinitionFilePath.EqualsCI(config.TargetDefinitionFilePath))
return AddErrorMessage("Target file must not be the same as the source file.");
return OK;
}
//-------------------------------------------------------------------
private bool ConvertCppDatabaseDefinition()
{
if (!OK) return false;
// Read source database definition.
ExtractDatabaseDefinition();
return OK;
}
//-------------------------------------------------------------------
// E x t r a c t S o u r c e D e f i n i t i o n
//-------------------------------------------------------------------
private class TableData
{
public String TableName { get; set; } = "";
public int TableIdx { get; set; } = -1;
}
private class ColumnData
{
public String TableName { get; set; } = "";
public String ColumnName { get; set; } = "";
public int DataType { get; set; } = -1;
public int ColumnLength { get; set; } = -1;
public int TableIdx { get; set; } = -1;
public int ColumnIdx { get; set; } = -1;
}
private class SourceDefinition_Params
{
protected SourceDefinition_Params() { }
public int LineNumber { get; set; } = -1;
public int TableIdx { get; set; } = -1;
public int ColumnIdx { get; set; } = -1;
}
private class SourceDefinition_Params_CppCode : SourceDefinition_Params
{
public bool TablesEnumFound { get; set; }
public bool InTablesEnum { get; set; }
public bool ColumnsEnumFound { get; set; }
public bool InColumnsEnum { get; set; }
public bool ColumnsDfnsFound { get; set; }
public bool InColumnsDfns { get; set; }
public Dictionary<String, TableData> TableMap { get; } = new Dictionary<String, TableData>();
public Dictionary<StringPair, ColumnData> ColumnMap { get; } = new Dictionary<StringPair, ColumnData>();
}
//-------------------------------------------------------------------
private bool ExtractDatabaseDefinition()
{
if (!OK) return false;
// Source definition parameters.
SourceDefinition_Params sourceDfnParams = null;
SourceDefinition_Params_CppCode sourceDfnParams_CppCode = null;
if (config.SourceDefinitionType == SourceDefinitionType.CppCode)
{
sourceDfnParams_CppCode = new SourceDefinition_Params_CppCode();
sourceDfnParams = sourceDfnParams_CppCode;
}
try
{
// Extract source database definition.
using (StreamReader file = new StreamReader(config.SourceDefinitionFilePath))
{
while (OK)
{
String line = file.ReadLine();
if (line == null) break;
sourceDfnParams.LineNumber++;
SetProgressInfo("Extracting source database definition.");
line = line.Trim(new char[] { ' ', '\t', '\r', '\n' });
if (config.SourceDefinitionType == SourceDefinitionType.CppCode)
{
ExtractDatabaseDefinitionLine_CppCode(sourceDfnParams_CppCode, line);
}
}
file.Close();
}
// Generate target database definition.
String dbDfnClassname = "SaDbDfn";
using (System.IO.StreamWriter file = new System.IO.StreamWriter(config.TargetDefinitionFilePath))
{
file.WriteLine("using Sarand.Net.Standard.Base.Core;");
file.WriteLine("using Sarand.Net.Standard.Base.Dbas;");
file.WriteLine("using System;");
file.WriteLine("using System.Collections.Generic;");
file.WriteLine("using System.Linq;");
file.WriteLine("");
file.WriteLine("//---------------------------------------------------------------------------");
file.WriteLine("// Code generated by Sarand Database Utilities " + DateTime.Now.Format("ddd d-MMM-yyyy HH:mm:ss"));
file.WriteLine("//---------------------------------------------------------------------------");
file.WriteLine("namespace sarand.database.definition");
file.WriteLine("{");
file.WriteLine("\tpublic class " + dbDfnClassname);
file.WriteLine("\t{");
file.WriteLine("\t\t//-------------------------------------------------------------------");
DatabaseDefinitionGenerator.GenerateDatabaseDefinitionTablesEnum(tableDfns, file);
DatabaseDefinitionGenerator.GenerateDatabaseDefinitionColumnsEnum(tableDfns, file);
DatabaseDefinitionGenerator.GenerateDatabaseDefinitionClass(tableDfns, dbDfnClassname, file);
file.WriteLine("\t}");
file.WriteLine("}");
file.Close();
}
}
catch (Exception exc) { SaLogger.Warn(ExceptionUtil.GetLogMessage(exc)); AddErrorMessage(ExceptionUtil.GetErrorStateMessage(exc)); }
return OK;
}
//-------------------------------------------------------------------
// C + + C o d e S o u r c e
//-------------------------------------------------------------------
private readonly Dictionary<int, String> cppColumnDataTypes = new Dictionary<int, string>()
{
{ 1, "VarChar" },
{ -7, "Bit" },
{ -6, "TinyInt" },
{ 5, "SmallInt" },
{ -16, "Int" },
{ -25, "BigInt" },
{ 7, "Single" },
{ 8, "Double" },
{ 93, "DateTime" }
};
private bool ExtractDatabaseDefinitionLine_CppCode(SourceDefinition_Params_CppCode sourceDfnParams, String line)
{
if (!OK) return false;
// C++ Code definition.
//---------------------------------------------------------------
// Table enums.
if (line.EqualsCI("enum eSaDbTable"))
{
// Table enum start.
if (sourceDfnParams.TablesEnumFound)
return AddErrorMessage("Found multiple table enums at line " + sourceDfnParams.LineNumber + ": " + line + ".");
sourceDfnParams.TablesEnumFound = true;
sourceDfnParams.InTablesEnum = true;
return OK;
}
if (sourceDfnParams.InTablesEnum)
{
// In Tables enum.
if (line.Equals("};") || line.EqualsCI("// Database column identifiers:"))
sourceDfnParams.InTablesEnum = false;
else if (!line.ContainsCI("eSaDbTblUnknown"))
{
if (!line.ContainsCI("eSaDbTbl"))
return AddErrorMessage("Invalid table definition on line " + sourceDfnParams.LineNumber + ": " + line + ".");
// Extract table name.
String tableName = line.ReplaceAll("eSaDbTbl", "").ReplaceAll(",", "");
if (StringUtil.IsEmpty(tableName))
return AddErrorMessage("Blank table name on line " + sourceDfnParams.LineNumber + ": " + line + ".");
if (tableDfns.Exists(item => item.Name.EqualsCI(tableName)))
return AddErrorMessage("Duplicate table name on line " + sourceDfnParams.LineNumber + ": " + line + ".");
// Add this table to the list.
sourceDfnParams.TableIdx++;
//SaLogger.Debug("Table #" + sourceDfnParams.TableIdx + ": " + tableName);
sourceDfnParams.TableMap.Add(tableName, new TableData() { TableName = tableName, TableIdx = sourceDfnParams.TableIdx });
tableDfns.Add(new DbDfnTable("", tableName, ""));
}
return OK;
}
//---------------------------------------------------------------
// Column enums.
if (line.EqualsCI("enum eSaDbColumn"))
{
// Column enum start.
if (sourceDfnParams.ColumnsEnumFound)
return AddErrorMessage("Found multiple columns enums at line " + sourceDfnParams.LineNumber + ": " + line + ".");
sourceDfnParams.ColumnsEnumFound = true;
sourceDfnParams.InColumnsEnum = true;
return OK;
}
if (sourceDfnParams.InColumnsEnum)
{
// In Columns enum.
if (line.Equals("};") || line.StartsWithCI("//----"))
sourceDfnParams.InColumnsEnum = false;
else if (!line.ContainsCI("eSaDbColUnknown"))
{
if (!line.ContainsCI("eSaDbCol"))
return AddErrorMessage("Invalid column definition on line " + sourceDfnParams.LineNumber + ": " + line + ".");
// Column definitions:
// eSaDbColUnknown = -1,
// #define eSaDbColVendorType_ListID (eSaDbColumn)4101
// #define eSaDbColSaCode_Notes (eSaDbColumn)101
// Extract Table/Column name pair - value starts 'eSaDbCol'.
int colDfnStartIdx = line.IndexOf("eSaDbCol");
String tableColumnName = line.Substring(colDfnStartIdx + "eSaDbCol".Length);
// Table/Column names end with space, equals or comma.
int colDfnEndIdx = tableColumnName.IndexOf(' ');
if (colDfnEndIdx > 0)
tableColumnName = tableColumnName.Left(colDfnEndIdx + 1);
colDfnEndIdx = tableColumnName.IndexOf('=');
if (colDfnEndIdx > 0)
tableColumnName = tableColumnName.Left(colDfnEndIdx + 1);
colDfnEndIdx = tableColumnName.IndexOf(',');
if (colDfnEndIdx > 0)
tableColumnName = tableColumnName.Left(colDfnEndIdx + 1);
//SaLogger.Debug("Table/Column name: " + tableColumnName);
// Split table and column names on Underscore.
String[] tableColumnNameParts = tableColumnName.Split('_');
if (tableColumnNameParts.Length != 2)
return AddErrorMessage("Invalid column definition on line (missing underscore?) " + sourceDfnParams.LineNumber + ": " + line + ".");
sourceDfnParams.ColumnIdx++;
String tableName = tableColumnNameParts[0];
String columnName = tableColumnNameParts[1];
//SaLogger.Debug("Column #" + sourceDfnParams.ColumnIdx + ": " + tableName + "." + columnName);
sourceDfnParams.ColumnMap.Add(new StringPair(tableName, columnName), new ColumnData() { TableName = tableName, ColumnName = columnName, ColumnIdx = sourceDfnParams.ColumnIdx });
}
return OK;
}
//---------------------------------------------------------------
// Column definitions.
if (line.StartsWithCI("SaDbColumnDefinition dfnColumn []"))
{
// Column definitions start.
if (sourceDfnParams.ColumnsDfnsFound)
return AddErrorMessage("Found multiple column definitions at line " + sourceDfnParams.LineNumber + ": " + line + ".");
sourceDfnParams.ColumnsDfnsFound = true;
sourceDfnParams.InColumnsDfns = true;
sourceDfnParams.ColumnIdx = -1;
return OK;
}
if (sourceDfnParams.InColumnsDfns)
{
if (line.Equals("{")) return OK;
if (line.Equals("};"))
{
sourceDfnParams.InColumnsDfns = false;
return OK;
}
// Column definition:
// { TableIdx,"ColumnName",DataType,Length }, ... [multiple per line]
// Column data types:
// eString = 1 SQL_C_CHAR
// eBool = -7 SQL_C_BIT
// eByte = -6 SQL_C_TINYINT
// eShortInt = 5 SQL_C_SHORT
// eLongInt = -16 SQL_C_SLONG
// eBigInt = -25 SQL_C_SBIGINT
// eReal = 7 SQL_C_FLOAT
// eDouble = 8 SQL_C_DOUBLE
// eTime = 93 SQL_C_TYPE_TIMESTAMP
foreach (String columnDfnDataRaw in line.Split(new String[] { "}," }, StringSplitOptions.RemoveEmptyEntries))
{
String columnDfnData = columnDfnDataRaw.ReplaceAll("{", "").TrimAll();
//SaLogger.Debug("Column dfn: " + columnDfnData);
String[] columnDfnParts = columnDfnData.Split(',');
if (columnDfnParts.Length != 4) return AddErrorMessage("Invalid column definition (expecting 4 parts) at line " + sourceDfnParams.LineNumber + ": " + line + ".");
String tableIdxRaw = columnDfnParts[0].TrimAll();
int tableIdx = StringUtil.ToInt(tableIdxRaw);
String columnName = columnDfnParts[1].TrimAll().ReplaceAll("\"", "");
String dataTypeRaw = columnDfnParts[2].TrimAll();
int dataType = StringUtil.ToInt(dataTypeRaw);
String columnLengthRaw = columnDfnParts[3].TrimAll();
int columnLength = StringUtil.ToInt(columnLengthRaw);
if (!tableIdxRaw.IsNumeric()) return AddErrorMessage("Invalid table index in column definition (" + tableIdxRaw + ") at line " + sourceDfnParams.LineNumber + ": " + line + ".");
if (StringUtil.IsEmpty(columnName)) return AddErrorMessage("Blank name in column definition at line " + sourceDfnParams.LineNumber + ": " + line + ".");
if (!dataTypeRaw.IsNumeric(true) || !cppColumnDataTypes.ContainsKey(dataType)) return AddErrorMessage("Invalid data type in column definition (" + dataTypeRaw + ") at line " + sourceDfnParams.LineNumber + ": " + line + ".");
if (!columnLengthRaw.IsNumeric()) return AddErrorMessage("Invalid column length in column definition (" + columnLengthRaw + ") at line " + sourceDfnParams.LineNumber + ": " + line + ".");
// Table definition.
String tableName = sourceDfnParams.TableMap.Values.ToList().Find(item => item.TableIdx == tableIdx)?.TableName ?? "";
if (StringUtil.IsEmpty(tableName)) return AddErrorMessage("Unable to identify table name for Table idx " + tableIdx + " at line " + sourceDfnParams.LineNumber + ": " + line + ".");
DbDfnTable tableDfn = tableDfns.GetTableByName(tableName);
if (tableDfn == null) return AddErrorMessage("Unable to retrieve table definition for Table name " + tableName + " at line " + sourceDfnParams.LineNumber + ": " + line + ".");
// Column data.
sourceDfnParams.ColumnIdx++;
ColumnData columnData = sourceDfnParams.ColumnMap.Values.ToList().Find(item => item.TableName.EqualsCI(tableName) && item.ColumnIdx == sourceDfnParams.ColumnIdx);
if (columnData == null) return AddErrorMessage("Unable to column data for table " + tableName + " and column " + columnName + " at line " + sourceDfnParams.LineNumber + ": " + line + ".");
if (columnData.ColumnName.EqualsCI(columnName)) return AddErrorMessage("Inconsistent column name for table " + tableName + " and column " + columnName + " at line " + sourceDfnParams.LineNumber + ": " + line + ".");
// Column definition.
DbDfnColumn columnDfn = new DbDfnColumn()
{
Table = tableName,
Name = columnName,
DataType = EnumUtil.ParseEnum(cppColumnDataTypes.GetValue(dataType, ""), DbColumnDataType.Unknown),
Size = columnLength
};
if (columnDfn.DataType < 0) return AddErrorMessage("Invalid data type for table " + tableName + " and column " + columnName + " at line " + sourceDfnParams.LineNumber + ": " + line + ".");
tableDfn.AddColumn(columnDfn);
}
return OK;
}
//---------------------------------------------------------------
return OK;
}
//-------------------------------------------------------------------
}
//=======================================================================
}