namespace QWERTYkez.WordProcessor; /// /// Множественные динамические замены с извлечением параметра из "$key|param$". /// Переиспользует движок (форматирование Run-ов, /// разрывы страниц, смена ориентации, разбиение на параграфы — всё как в оригинале). /// internal static class DynamicMultiReplaceExt { // ============================================================ // ReplaceString (Func>) // ============================================================ internal static void ReplaceStringMultiInBody( Body body, string oldValue, Func> factory, StringComparison comparisonType) { if (body is null || factory is null) return; DynamicReplaceExt.ParsePattern(oldValue, out var prefix, out var suffix); var paragraphs = body.Elements().ToList(); for (int i = paragraphs.Count - 1; i >= 0; i--) ProcessDynamicStringMulti(paragraphs[i], prefix, suffix, factory, comparisonType); } internal static void ReplaceStringMultiInTables( OpenXmlElement element, string oldValue, Func> factory, StringComparison comparisonType) { if (element is null || factory is null) return; DynamicReplaceExt.ParsePattern(oldValue, out var prefix, out var suffix); foreach (var table in TableTextProcessor.GetAllTables(element)) { var paragraphs = TableTextProcessor.GetTableParagraphs(table).ToList(); for (int i = paragraphs.Count - 1; i >= 0; i--) ProcessDynamicStringMulti(paragraphs[i], prefix, suffix, factory, comparisonType); } } internal static void ReplaceStringMultiInHeadersFooters( WordprocessingDocument doc, string oldValue, Func> factory, StringComparison comparisonType) { if (doc is null || factory is null) return; DynamicReplaceExt.ParsePattern(oldValue, out var prefix, out var suffix); foreach (var header in HeaderFooterProcessor.GetAllHeaders(doc)) ProcessStringMultiInElement(header, prefix, suffix, factory, comparisonType); foreach (var footer in HeaderFooterProcessor.GetAllFooters(doc)) ProcessStringMultiInElement(footer, prefix, suffix, factory, comparisonType); } private static void ProcessStringMultiInElement( OpenXmlElement element, string prefix, string suffix, Func> factory, StringComparison comparisonType) { var paragraphs = element.Descendants().ToList(); var tables = TableTextProcessor.GetAllTables(element).ToList(); var tableParagraphs = tables.SelectMany(t => TableTextProcessor.GetTableParagraphs(t)).ToList(); var allParagraphs = paragraphs.Concat(tableParagraphs).Distinct().ToList(); for (int i = allParagraphs.Count - 1; i >= 0; i--) ProcessDynamicStringMulti(allParagraphs[i], prefix, suffix, factory, comparisonType); } private static void ProcessDynamicStringMulti( Paragraph paragraph, string prefix, string suffix, Func> factory, StringComparison comparisonType) { if (paragraph is null || paragraph.Parent is null) return; var text = paragraph.InnerText; if (string.IsNullOrEmpty(text)) return; var matches = DynamicReplaceExt.FindMatches(text, prefix, suffix, comparisonType); if (matches.Count == 0) return; // Строим словарь уникальных matchedText -> значения для этого параграфа var dict = new Dictionary>(); for (int i = 0; i < matches.Count; i++) { var m = matches[i]; string matchedText = text.Substring(m.Start, m.End - m.Start); if (dict.ContainsKey(matchedText)) continue; IEnumerable values; try { values = factory(m.Param); } catch { continue; } if (values is null) continue; var list = values as IList ?? values.ToList(); if (list.Count == 0) continue; dict[matchedText] = list; } if (dict.Count == 0) return; var newParas = MultiReplaceExt.ProcessParagraphWithMergedBoundaries(paragraph, dict, comparisonType); if (newParas is not null && newParas.Count > 0) ParagraphReplacer.ReplaceParagraph(paragraph, newParas); } // ============================================================ // ReplaceItem (Func>) // ============================================================ internal static void ReplaceItemMultiInBody( Body body, string oldValue, Func> factory, StringComparison comparisonType) { if (body is null || factory is null) return; DynamicReplaceExt.ParsePattern(oldValue, out var prefix, out var suffix); var paragraphs = body.Elements().ToList(); for (int i = paragraphs.Count - 1; i >= 0; i--) ProcessDynamicItemMulti(paragraphs[i], prefix, suffix, factory, comparisonType); } internal static void ReplaceItemMultiInTables( OpenXmlElement element, string oldValue, Func> factory, StringComparison comparisonType) { if (element is null || factory is null) return; DynamicReplaceExt.ParsePattern(oldValue, out var prefix, out var suffix); foreach (var table in TableTextProcessor.GetAllTables(element)) { var paragraphs = TableTextProcessor.GetTableParagraphs(table).ToList(); for (int i = paragraphs.Count - 1; i >= 0; i--) ProcessDynamicItemMulti(paragraphs[i], prefix, suffix, factory, comparisonType); } } internal static void ReplaceItemMultiInHeadersFooters( WordprocessingDocument doc, string oldValue, Func> factory, StringComparison comparisonType) { if (doc is null || factory is null) return; DynamicReplaceExt.ParsePattern(oldValue, out var prefix, out var suffix); foreach (var header in HeaderFooterProcessor.GetAllHeaders(doc)) ProcessItemMultiInElement(header, prefix, suffix, factory, comparisonType); foreach (var footer in HeaderFooterProcessor.GetAllFooters(doc)) ProcessItemMultiInElement(footer, prefix, suffix, factory, comparisonType); } private static void ProcessItemMultiInElement( OpenXmlElement element, string prefix, string suffix, Func> factory, StringComparison comparisonType) { var paragraphs = element.Descendants().ToList(); var tables = TableTextProcessor.GetAllTables(element).ToList(); var tableParagraphs = tables.SelectMany(t => TableTextProcessor.GetTableParagraphs(t)).ToList(); var allParagraphs = paragraphs.Concat(tableParagraphs).Distinct().ToList(); for (int i = allParagraphs.Count - 1; i >= 0; i--) ProcessDynamicItemMulti(allParagraphs[i], prefix, suffix, factory, comparisonType); } private static void ProcessDynamicItemMulti( Paragraph paragraph, string prefix, string suffix, Func> factory, StringComparison comparisonType) { if (paragraph is null || paragraph.Parent is null) return; var text = paragraph.InnerText; if (string.IsNullOrEmpty(text)) return; var matches = DynamicReplaceExt.FindMatches(text, prefix, suffix, comparisonType); if (matches.Count == 0) return; var dict = new Dictionary>(); for (int i = 0; i < matches.Count; i++) { var m = matches[i]; string matchedText = text.Substring(m.Start, m.End - m.Start); if (dict.ContainsKey(matchedText)) continue; IEnumerable values; try { values = factory(m.Param); } catch { continue; } if (values is null) continue; var list = values as IList ?? values.ToList(); if (list.Count == 0) continue; dict[matchedText] = list; } if (dict.Count == 0) return; var newParas = MultiReplaceExt.ProcessParagraphWithMergedBoundaries(paragraph, dict, comparisonType); if (newParas is not null && newParas.Count > 0) ParagraphReplacer.ReplaceParagraph(paragraph, newParas); } }