439 lines
18 KiB
C#
439 lines
18 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 : SacsProcess
|
|
{
|
|
//-------------------------------------------------------------------
|
|
// Configuration.
|
|
public enum SourceDefinitionType
|
|
{
|
|
Unknown = 0,
|
|
CppCode
|
|
}
|
|
public enum TargetDefinitionType
|
|
{
|
|
Unknown = 0,
|
|
CsCode
|
|
}
|
|
public new class Config : SacsProcess.Config
|
|
{
|
|
public Config ()
|
|
{
|
|
}
|
|
public SourceDefinitionType SourceDefinitionType { get; set; }
|
|
public String SourceDefinitionFilePath { get; set; }
|
|
public TargetDefinitionType TargetDefinitionType { get; set; }
|
|
public String TargetDefinitionFilePath { get; set; }
|
|
}
|
|
|
|
//-------------------------------------------------------------------
|
|
// Definitions.
|
|
public interface IProcessNotifier : ISacsProcessNotifier
|
|
{
|
|
}
|
|
private class Statistics
|
|
{
|
|
}
|
|
|
|
//-------------------------------------------------------------------
|
|
// Data members.
|
|
private Config config;
|
|
private IProcessNotifier processNotifier;
|
|
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";
|
|
public new IProcessNotifier ProcessNotifier
|
|
{
|
|
get { return processNotifier; }
|
|
set
|
|
{
|
|
processNotifier = value;
|
|
base.ProcessNotifier = value;
|
|
}
|
|
}
|
|
/// <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 SetProcessError("Source definition type must be specified.");
|
|
if (StringUtil.IsEmpty(config.SourceDefinitionFilePath))
|
|
return SetProcessError("Source definition file must be specified.");
|
|
if (!FileUtil.IsFile(config.SourceDefinitionFilePath))
|
|
return SetProcessError("Source definition file not found.");
|
|
|
|
// Target file.
|
|
if (config.TargetDefinitionType <= 0)
|
|
return SetProcessError("Target definition type must be specified.");
|
|
if (StringUtil.IsEmpty(config.TargetDefinitionFilePath))
|
|
return SetProcessError("Target definition file must be specified.");
|
|
if (!DirectoryUtil.DirectoryExists(DirectoryUtil.GetDirectoryName(config.TargetDefinitionFilePath)))
|
|
return SetProcessError("Target folder not found.");
|
|
|
|
if (config.SourceDefinitionFilePath.EqualsCI(config.TargetDefinitionFilePath))
|
|
return SetProcessError("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)); SetProcessError(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 SetProcessError("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 SetProcessError("Invalid table definition on line " + sourceDfnParams.LineNumber + ": " + line + ".");
|
|
// Extract table name.
|
|
String tableName = line.ReplaceAll("eSaDbTbl","").ReplaceAll(",","");
|
|
if (StringUtil.IsEmpty(tableName))
|
|
return SetProcessError("Blank table name on line " + sourceDfnParams.LineNumber + ": " + line + ".");
|
|
if (tableDfns.Exists(item => item.Name.EqualsCI(tableName)))
|
|
return SetProcessError("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 SetProcessError("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 SetProcessError("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 SetProcessError("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 SetProcessError("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 SetProcessError("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 SetProcessError("Invalid table index in column definition (" + tableIdxRaw + ") at line " + sourceDfnParams.LineNumber + ": " + line + ".");
|
|
if (StringUtil.IsEmpty(columnName)) return SetProcessError("Blank name in column definition at line " + sourceDfnParams.LineNumber + ": " + line + ".");
|
|
if (!dataTypeRaw.IsNumeric(true) || !cppColumnDataTypes.ContainsKey(dataType)) return SetProcessError("Invalid data type in column definition (" + dataTypeRaw + ") at line " + sourceDfnParams.LineNumber + ": " + line + ".");
|
|
if (!columnLengthRaw.IsNumeric()) return SetProcessError("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 SetProcessError("Unable to identify table name for Table idx " + tableIdx + " at line " + sourceDfnParams.LineNumber + ": " + line + ".");
|
|
DbDfnTable tableDfn = tableDfns.GetTableByName(tableName);
|
|
if (tableDfn == null) return SetProcessError("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 SetProcessError("Unable to column data for table " + tableName + " and column " + columnName + " at line " + sourceDfnParams.LineNumber + ": " + line + ".");
|
|
if (columnData.ColumnName.EqualsCI(columnName)) return SetProcessError("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 SetProcessError("Invalid data type for table " + tableName + " and column " + columnName + " at line " + sourceDfnParams.LineNumber + ": " + line + ".");
|
|
tableDfn.AddColumn(columnDfn);
|
|
}
|
|
return OK;
|
|
}
|
|
|
|
//---------------------------------------------------------------
|
|
return OK;
|
|
}
|
|
|
|
//-------------------------------------------------------------------
|
|
}
|
|
|
|
//=======================================================================
|
|
}
|