OnlineSalesAutoCrop/Api/OnlineSalesAutoCrop.CoreAPI.Models/Global/ImportHelper.cs
2026-06-14 12:46:29 +06:00

457 lines
9.9 KiB
C#

using System;
using System.Collections.Generic;
using System.Data;
using System.IO;
namespace OnlineSalesAutoCrop.CoreAPI.Models.Global
{
public sealed class ImportHelper : IDisposable
{
#region Declaration
private DataTable _table;
#endregion
#region Constructor & Desctructor
public ImportHelper()
{
Delimiter = '\0';
_table = null;
}
~ImportHelper()
{
_table?.Dispose();
}
#endregion
#region Properties
#region Property Delimiter: char
public static char Delimiter { get; set; }
#endregion
#endregion
#region Formats Class
public sealed class Formats
{
public static readonly IFormatter Text = new TextFormatter();
public static readonly IFormatter Excel = new ExcelFormatter();
private Formats() { }
}
#endregion
#region IFormater Interface
public interface IFormatter
{
/// <summary>
/// Return the DataTable after successful operation.
/// </summary>
/// <param name="fileSpec">Valid name of the file.</param>
/// <param name="firstRowColumnHeader">Used the first line as header.</param>
/// <returns>DataTable if successful.</returns>
DataTable Import(string fileSpec, bool firstRowColumnHeader);
}
#endregion
#region Excel Formatter Implementation
public class ExcelFormatter : IFormatter
{
#region Declaration & Constructor
public ExcelFormatter()
{
}
#endregion
#region Functions
public DataTable Import(string fileSpec, bool firstRowColumnHeader)
{
try
{
return ExcelReader.GetDataSetFromFile(fileSpec: fileSpec, firstRowColumnHeader: firstRowColumnHeader).Tables[0];
}
catch (Exception e)
{
throw new InvalidOperationException(e.Message, e);
}
}
#endregion
}
#endregion
#region Text Formatter Implementation
public class TextFormatter : IFormatter
{
#region Declaration & Construct
private DataTable _dt;
private readonly char CharQlfr = '"';
private readonly string StringQlfr = "\"";
private readonly string ReplacingQlfr = "\"\"";
private readonly string ReplacedChar = ((char)1).ToString();
public TextFormatter()
{
_dt = null;
}
#endregion
#region Properties
private int EndColumnIndex { get; set; }
#endregion
#region Functions
private void MakeColumn(bool hasHeader, string line)
{
try
{
string[] columns = ParseData(line);
EndColumnIndex = columns.Length - 1;
if (hasHeader)
{
for (int i = 0; i <= EndColumnIndex; i++)
{
DataColumn dc = new(string.Format("Col{0}", i))
{
Caption = columns[i],
ReadOnly = true,
DataType = typeof(string)
};
_dt.Columns.Add(dc);
}
}
else
{
for (int i = 0; i <= EndColumnIndex; i++)
{
DataColumn dc = new(string.Format("Col{0}", i))
{
Caption = string.Format("Column {0}", i),
ReadOnly = true,
DataType = typeof(string)
};
_dt.Columns.Add(dc);
}
}
}
catch (Exception e)
{
throw new InvalidOperationException(e.Message, e);
}
}
private string[] ParseData(string data)
{
List<string> cols = [];
try
{
//To be use as a temporary
List<string> tmpCols = [];
//Parse qualifier
data = ParseQualifier(tmpCols, data);
//Make actual columns data
tmpCols = [];
ParseData(tmpCols, data);
//Convert back to original value
foreach (var item in tmpCols)
{
string value = item.Replace(ReplacedChar, StringQlfr);
cols.Add(value);
}
tmpCols.Clear();
}
catch (Exception e)
{
throw new InvalidOperationException(e.Message, e);
}
return [.. cols];
}
private void ParseData(List<string> cols, string data)
{
try
{
//Find out position of qualifier
int pos = data.IndexOf(CharQlfr);
//If there is no qualifier in the data do nothing
if (pos == -1)
{
cols.AddRange(data.Split(Delimiter));
return;
}
//Second part of the data after qualifies
string part2 = data[pos..];
//If qualifier is beginning of the data
if (pos == 0)
{
//Remove beginning qualifier
string part1 = data[1..];
//Find out next position of the qualifier, next qualifier must exists since qualifier starts
pos = part1.IndexOf(CharQlfr);
//Read remaining data by removing the delimiter
part2 = part1[(pos + 1)..];
//Remove delimiter if still exists at the start of the data
if (part2.StartsWith(Delimiter.ToString()))
part2 = part2[1..];
//Read data just before the position
part1 = part1[..pos];
//Remove delimiter if still exists at the start of the data
if (part1.EndsWith(Delimiter.ToString()))
part1 = part1[..^1];
//Add to the column collection
cols.Add(part1);
}
else
{
//Read data just before the position
string part1 = data[..pos];
//Remove end delimiter if still exists
if (part1.EndsWith(Delimiter.ToString()))
part1 = part1[..^1];
//If this is a valid data, Add to column collection splitting by delimiter of the first part
if (part1.Length > 0)
cols.AddRange(part1.Split(Delimiter));
}
//Call recursively by using second part of the data
ParseData(cols, part2);
}
catch (Exception e)
{
throw new InvalidOperationException(e.Message, e);
}
}
private string ParseQualifier(List<string> cols, string data)
{
try
{
//Find out the position of qualifier
int pos = data.IndexOf(CharQlfr);
//If there is no qualifier in the data do nothing
if (pos == -1)
{
cols.Add(data);
}
else
{
//Read left part of the data of current position need to add into collection
string remData = data[..pos];
cols.Add(remData);
//Scan each character of remaining part of the data
for (int i = pos; i < data.Length; i++)
{
//Read character at current position
string value = data.Substring(i, 1);
//Add this character into collection
if (i == pos)
{
cols.Add(value);
}
else
{
//Read two characters from current position
string pairValue = i + 1 < data.Length ? data.Substring(i, 2) : data.Substring(i, 1);
//If current character is a qualifier but is not a replaceable qualifier
if (value[0] == CharQlfr && !pairValue.Equals(ReplacingQlfr))
{
//Add this character into collection
cols.Add(value);
//If current position is the last position of the data just exit from loop
if (i + 1 >= data.Length)
break;
//Read remaining part of the data
data = data[(i + 1)..];
//Call this function recursively and exit from loop
ParseQualifier(cols, data);
break;
}
else if (pairValue.Equals(ReplacingQlfr)) //If this is a replaceable qualifier just replace the with the special character
{
value = ReplacedChar;
//Increase the current position by 1
i++;
}
//Add value into collection
cols.Add(value);
}
}
}
}
catch (Exception e)
{
throw new InvalidOperationException(e.Message, e);
}
return string.Join("", [.. cols]);
}
public DataTable Import(string fileSpec, bool firstRowColumnHeader)
{
try
{
int rowIdx = -1;
bool firstLine = true;
_dt = new DataTable("TextReader");
using StreamReader sr = new(fileSpec);
while (sr.Peek() != -1)
{
string line = sr.ReadLine();
if (line.Trim().Length <= 0)
continue;
if (firstLine)
MakeColumn(firstRowColumnHeader, line);
if (firstRowColumnHeader && firstLine)
{
firstLine = false;
continue;
}
firstLine = false;
rowIdx++;
DataRow dr = _dt.NewRow();
object[] columns = ParseData(line);
for (int i = 0; i < columns.Length; i++)
{
dr[string.Format("Col{0}", i)] = columns[i];
}
_dt.Rows.Add(dr);
}
sr.Close();
}
catch (Exception e)
{
throw new InvalidOperationException(e.Message, e);
}
return _dt;
}
#endregion
}
#endregion
#region Functions
public void Reset()
{
_table = new DataTable("ImportHelper");
}
/// <summary>
///
/// </summary>
/// <param name="formatter"></param>
/// <param name="fileSpec"></param>
/// <returns></returns>
public DataTable Import(IFormatter formatter, string fileSpec)
{
if (formatter == null)
throw new ArgumentException("Need to specify a formatter", nameof(formatter));
try
{
_table = Import(formatter, fileSpec, false);
}
catch (Exception e)
{
throw new InvalidOperationException(e.Message, e);
}
return _table;
}
/// <summary>
///
/// </summary>
/// <param name="formatter"></param>
/// <param name="fileSpec"></param>
/// <param name="firstRowColumnHeader"></param>
/// <returns></returns>
public DataTable Import(IFormatter formatter, string fileSpec, bool firstRowColumnHeader)
{
if (formatter == null)
throw new ArgumentException("Need to specify a formatter", nameof(formatter));
if (string.IsNullOrEmpty(fileSpec) || string.IsNullOrWhiteSpace(fileSpec))
throw new InvalidOperationException("Provide a valid file name to read.");
if (formatter == Formats.Text && Delimiter == '\0')
throw new InvalidOperationException("Provide a field delimiter of the file.");
try
{
_table = formatter.Import(fileSpec, firstRowColumnHeader);
}
catch (Exception e)
{
throw new InvalidOperationException(e.Message);
}
return _table;
}
/// <summary>
///
/// </summary>
public void Dispose()
{
_table?.Dispose();
_table = null;
GC.SuppressFinalize(this);
}
#endregion
}
}