using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace CadParamPluging.Common { public static class NoteTemplateEngine { public sealed class PlaceholderOccurrence { public int Index { get; set; } public int Start { get; set; } public int LineNumber { get; set; } public int ColumnInLine { get; set; } public string LineText { get; set; } } public static int CountPlaceholders(string text) { if (string.IsNullOrEmpty(text)) { return 0; } var count = 0; for (var i = 0; i < text.Length;) { if (text[i] != '*') { i++; continue; } var j = i; while (j < text.Length && text[j] == '*') { j++; } var len = j - i; if (len != 4) { count += len; } i = j; } return count; } public static List ParseOccurrences(string text) { var result = new List(); if (string.IsNullOrEmpty(text)) { return result; } var s = NormalizeNewLines(text); // Keep empty lines for correct line number mapping. var lines = s.Split(new[] { '\n' }, StringSplitOptions.None); // Build line start offsets. var lineStartOffsets = new int[lines.Length]; var offset = 0; for (var i = 0; i < lines.Length; i++) { lineStartOffsets[i] = offset; offset += (lines[i] ?? string.Empty).Length; if (i < lines.Length - 1) { offset += 1; // '\n' } } var placeholderIndex = 0; var lineIdx = 0; var lineStart = lines.Length > 0 ? lineStartOffsets[0] : 0; for (var i = 0; i < s.Length;) { // Move line index if needed. while (lineIdx + 1 < lineStartOffsets.Length && i >= lineStartOffsets[lineIdx] + (lines[lineIdx] ?? string.Empty).Length + 1) { lineIdx++; lineStart = lineStartOffsets[lineIdx]; } if (s[i] != '*') { i++; continue; } var j = i; while (j < s.Length && s[j] == '*') { j++; } var runLen = j - i; if (runLen == 4) { i = j; continue; } var lineText = (lineIdx >= 0 && lineIdx < lines.Length) ? (lines[lineIdx] ?? string.Empty) : string.Empty; for (var k = 0; k < runLen; k++) { placeholderIndex++; var pos = i + k; var col = pos - lineStart; if (col < 0) col = 0; result.Add(new PlaceholderOccurrence { Index = placeholderIndex, Start = pos, LineNumber = lineIdx + 1, ColumnInLine = col, LineText = lineText }); } i = j; } return result; } public static string BuildNumberedPreviewText(string templateText) { templateText = templateText ?? string.Empty; var s = NormalizeNewLines(templateText); var sb = new StringBuilder(s.Length + 64); var placeholderIndex = 0; for (var i = 0; i < s.Length;) { if (s[i] != '*') { sb.Append(s[i]); i++; continue; } var j = i; while (j < s.Length && s[j] == '*') { j++; } var runLen = j - i; if (runLen == 4) { sb.Append("****"); i = j; continue; } for (var k = 0; k < runLen; k++) { placeholderIndex++; sb.Append("【#"); sb.Append(placeholderIndex); sb.Append("】"); } i = j; } return sb.ToString(); } public static string Render(string templateText, IEnumerable bindings, Func getValue) { templateText = templateText ?? string.Empty; var s = NormalizeNewLines(templateText); var map = new Dictionary(); foreach (var b in bindings ?? Enumerable.Empty()) { if (b == null) { continue; } if (b.Index <= 0) { continue; } var key = (b.ParamKey ?? string.Empty).Trim(); if (key.Length == 0) { continue; } if (!map.ContainsKey(b.Index)) { map[b.Index] = key; } } var sb = new StringBuilder(s.Length + 64); var placeholderIndex = 0; for (var i = 0; i < s.Length;) { if (s[i] != '*') { sb.Append(s[i]); i++; continue; } var j = i; while (j < s.Length && s[j] == '*') { j++; } var runLen = j - i; if (runLen == 4) { sb.Append("****"); i = j; continue; } for (var k = 0; k < runLen; k++) { placeholderIndex++; if (map.TryGetValue(placeholderIndex, out var key) && getValue != null) { var v = getValue(key); if (!string.IsNullOrWhiteSpace(v)) { sb.Append(v); continue; } } sb.Append('*'); } i = j; } return sb.ToString(); } /// /// When the same ParamKey is bound by multiple placeholders, each placeholder should have its own value key, /// otherwise values would overwrite each other. /// This method returns an equivalent bindings list where ParamKey is replaced by an effective value key. /// public static List BuildEffectiveValueKeyBindings(IEnumerable bindings) { var list = (bindings ?? Enumerable.Empty()) .Where(b => b != null && b.Index > 0 && !string.IsNullOrWhiteSpace(b.ParamKey)) .Select(b => new NotePlaceholderBinding { Index = b.Index, ParamKey = b.ParamKey.Trim() }) .ToList(); var dup = list .GroupBy(b => b.ParamKey, StringComparer.OrdinalIgnoreCase) .ToDictionary(g => g.Key, g => g.Count(), StringComparer.OrdinalIgnoreCase); foreach (var b in list) { if (dup.TryGetValue(b.ParamKey, out var cnt) && cnt > 1) { b.ParamKey = string.Format("{0}@{1}", b.ParamKey, b.Index); } } return list; } private static string NormalizeNewLines(string text) { return (text ?? string.Empty) .Replace("\r\n", "\n") .Replace("\r", "\n"); } } }