using System.Text;
using System.Xml.Linq;
using ExcelDataReader;
namespace CadSearchGui;
///
/// Converte il file "Distinta Base" esportato dal gestionale (SpreadsheetML
/// XML con estensione .xls/.xml, oppure .xls/.xlsx binario vero) nelle sole
/// colonne Componente/Descrizione in CSV.
///
static class DistintaBaseImporter
{
private static readonly XNamespace Ss = "urn:schemas-microsoft-com:office:spreadsheet";
private const string NomeFoglio = "Distinta Base";
static DistintaBaseImporter()
{
Encoding.RegisterProvider(System.Text.CodePagesEncodingProvider.Instance);
}
public static (string PercorsoCsv, List<(string Componente, string Descrizione)> Righe) ConvertiInCsv(string percorsoInput)
{
var righeGrezze = EImmagineSpreadsheetMl(percorsoInput)
? LeggiRigheSpreadsheetMl(percorsoInput)
: LeggiRigheExcelBinario(percorsoInput);
var righe = righeGrezze.Skip(1) // salta l'intestazione
.Where(r => r.Componente.Length > 0 || r.Descrizione.Length > 0)
.ToList();
var percorsoCsv = Path.Combine(
Path.GetDirectoryName(Path.GetFullPath(percorsoInput))!,
Path.GetFileNameWithoutExtension(percorsoInput) + ".csv");
using var writer = new StreamWriter(percorsoCsv, false, Encoding.UTF8);
writer.WriteLine("Componente;Descrizione");
foreach (var (componente, descrizione) in righe)
{
writer.WriteLine($"{EscapeCsv(componente)};{EscapeCsv(descrizione)}");
}
return (percorsoCsv, righe);
}
/// I file XML "SpreadsheetML" iniziano con l'intestazione XML testuale;
/// i file .xls/.xlsx binari veri iniziano invece con una firma binaria (OLE2 o ZIP).
private static bool EImmagineSpreadsheetMl(string percorso)
{
Span primiByte = stackalloc byte[5];
using var stream = File.OpenRead(percorso);
var letti = stream.Read(primiByte);
return letti >= 5 && Encoding.ASCII.GetString(primiByte) == " LeggiRigheSpreadsheetMl(string percorsoInput)
{
var doc = XDocument.Load(percorsoInput);
var foglio = doc.Descendants(Ss + "Worksheet")
.FirstOrDefault(w => (string?)w.Attribute(Ss + "Name") == NomeFoglio)
?? throw new InvalidDataException($"Foglio '{NomeFoglio}' non trovato nel file.");
var righeXml = foglio.Descendants(Ss + "Row").ToList();
if (righeXml.Count == 0)
throw new InvalidDataException($"Il foglio '{NomeFoglio}' non contiene righe.");
return righeXml.Select(LeggiColonneBeCSpreadsheetMl).ToList();
}
private static (string Componente, string Descrizione) LeggiColonneBeCSpreadsheetMl(XElement riga)
{
string b = "", c = "";
int colonna = 0;
foreach (var cella in riga.Elements(Ss + "Cell"))
{
// ss:Index salta le celle vuote iniziali: senza attributo la colonna รจ quella precedente + 1
var indice = cella.Attribute(Ss + "Index");
colonna = indice != null ? int.Parse(indice.Value) : colonna + 1;
if (colonna == 2) b = cella.Element(Ss + "Data")?.Value ?? "";
else if (colonna == 3) c = cella.Element(Ss + "Data")?.Value ?? "";
}
return (b, c);
}
private static List<(string Componente, string Descrizione)> LeggiRigheExcelBinario(string percorsoInput)
{
using var stream = File.Open(percorsoInput, FileMode.Open, FileAccess.Read, FileShare.Read);
using var reader = ExcelReaderFactory.CreateReader(stream);
var dataSet = reader.AsDataSet();
var tabella = dataSet.Tables[NomeFoglio]
?? throw new InvalidDataException($"Foglio '{NomeFoglio}' non trovato nel file.");
var righe = new List<(string Componente, string Descrizione)>();
foreach (System.Data.DataRow riga in tabella.Rows)
{
var b = riga.Table.Columns.Count > 1 ? riga[1]?.ToString() ?? "" : "";
var c = riga.Table.Columns.Count > 2 ? riga[2]?.ToString() ?? "" : "";
righe.Add((b, c));
}
return righe;
}
private static string EscapeCsv(string valore)
{
if (valore.Contains(';') || valore.Contains('"') || valore.Contains('\n'))
return $"\"{valore.Replace("\"", "\"\"")}\"";
return valore;
}
}