143 lines
4.9 KiB
C#
143 lines
4.9 KiB
C#
namespace QWERTYkez.ExcelProcessor;
|
|
|
|
internal static class PlaceholderFinder
|
|
{
|
|
/// <summary>Находит плейсхолдеры $...$ в указанных листах.</summary>
|
|
public static ISet<string> FindInDocument(SpreadsheetDocument doc, params WorksheetPart[] worksheets)
|
|
{
|
|
var result = new NormalizedSet();
|
|
if (doc.WorkbookPart is null) return result;
|
|
|
|
var sharedStringTable = doc.WorkbookPart.SharedStringTablePart?.SharedStringTable;
|
|
foreach (var ws in worksheets)
|
|
ProcessWorksheet(ws, sharedStringTable, result);
|
|
|
|
return result;
|
|
}
|
|
|
|
/// <summary>Находит плейсхолдеры во всех листах документа.</summary>
|
|
public static ISet<string> FindInDocument(SpreadsheetDocument doc)
|
|
{
|
|
if (doc.WorkbookPart is null) return new NormalizedSet();
|
|
return FindInDocument(doc, [.. doc.WorkbookPart.WorksheetParts]);
|
|
}
|
|
|
|
static void ProcessWorksheet(WorksheetPart wsPart, SharedStringTable? sharedStrings, ISet<string> result)
|
|
{
|
|
var worksheet = wsPart.Worksheet;
|
|
if (worksheet is null) return;
|
|
|
|
ProcessCells(worksheet, sharedStrings, result);
|
|
ProcessHeaderFooter(worksheet, result);
|
|
}
|
|
|
|
static void ProcessCells(Worksheet worksheet, SharedStringTable? sharedStrings, ISet<string> result)
|
|
{
|
|
var sheetData = worksheet.GetFirstChild<SheetData>();
|
|
if (sheetData is null) return;
|
|
|
|
List<string>? tempList = null; // буфер для плейсхолдеров из одного текста
|
|
|
|
foreach (var row in sheetData.Elements<Row>())
|
|
{
|
|
foreach (var cell in row.Elements<Cell>())
|
|
{
|
|
string text = GetCellValue(cell, sharedStrings);
|
|
if (string.IsNullOrEmpty(text)) continue;
|
|
|
|
#if DEBUG
|
|
if (text.Contains('$'))
|
|
Debug.WriteLine($"[PlaceholderFinder] Cell {cell.CellReference}: '{text}'");
|
|
#endif
|
|
if (FindPlaceholdersInText(text, ref tempList))
|
|
{
|
|
foreach (var ph in tempList!)
|
|
result.Add(ph);
|
|
tempList.Clear();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
static void ProcessHeaderFooter(Worksheet worksheet, ISet<string> result)
|
|
{
|
|
var hf = worksheet.Descendants<HeaderFooter>().FirstOrDefault();
|
|
if (hf is null) return;
|
|
|
|
var texts = new[]
|
|
{
|
|
hf.OddHeader?.Text, hf.OddFooter?.Text,
|
|
hf.EvenHeader?.Text, hf.EvenFooter?.Text,
|
|
hf.FirstHeader?.Text, hf.FirstFooter?.Text
|
|
};
|
|
|
|
List<string>? tempList = null;
|
|
foreach (var text in texts)
|
|
{
|
|
if (string.IsNullOrEmpty(text)) continue;
|
|
if (FindPlaceholdersInText(text!, ref tempList))
|
|
{
|
|
foreach (var ph in tempList!)
|
|
result.Add(ph);
|
|
tempList.Clear();
|
|
}
|
|
}
|
|
}
|
|
|
|
static string GetCellValue(Cell cell, SharedStringTable? sharedStrings)
|
|
{
|
|
if (cell?.CellValue is null && cell?.InlineString is null)
|
|
return string.Empty;
|
|
|
|
// Встроенная строка
|
|
if (cell.InlineString is not null)
|
|
return string.Concat(cell.InlineString.Descendants<Text>().Select(t => t.Text));
|
|
|
|
// Обычное значение или общая строка
|
|
string raw = cell.CellValue!.InnerText;
|
|
if (cell.DataType?.Value == CellValues.SharedString)
|
|
{
|
|
if (sharedStrings is not null && int.TryParse(raw, out int idx) && idx >= 0 && idx < sharedStrings.Count!)
|
|
{
|
|
var si = sharedStrings.ElementAt(idx);
|
|
return string.Concat(si.Descendants<Text>().Select(t => t.Text));
|
|
}
|
|
return string.Empty;
|
|
}
|
|
return raw;
|
|
}
|
|
|
|
static unsafe bool FindPlaceholdersInText(string text, ref List<string>? output)
|
|
{
|
|
fixed (char* pText = text)
|
|
{
|
|
char* start = pText;
|
|
char* end = pText + text.Length;
|
|
bool found = false;
|
|
|
|
while (start < end)
|
|
{
|
|
// Ищем '$'
|
|
while (start < end && *start != '$') start++;
|
|
if (start >= end) break;
|
|
char* open = start++;
|
|
if (start >= end) break;
|
|
|
|
// Ищем закрывающий '$'
|
|
char* contentStart = start;
|
|
while (start < end && *start != '$') start++;
|
|
if (start >= end) break;
|
|
char* close = start++;
|
|
|
|
int len = (int)(close - contentStart);
|
|
if (len > 0)
|
|
{
|
|
found = true;
|
|
output ??= [];
|
|
output.Add(new string(contentStart, 0, len));
|
|
}
|
|
}
|
|
return found;
|
|
}
|
|
}
|
|
} |