294 lines
8.5 KiB
C#
294 lines
8.5 KiB
C#
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<PlaceholderOccurrence> ParseOccurrences(string text)
|
|
{
|
|
var result = new List<PlaceholderOccurrence>();
|
|
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<NotePlaceholderBinding> bindings, Func<string, string> getValue)
|
|
{
|
|
templateText = templateText ?? string.Empty;
|
|
|
|
var s = NormalizeNewLines(templateText);
|
|
|
|
var map = new Dictionary<int, string>();
|
|
foreach (var b in bindings ?? Enumerable.Empty<NotePlaceholderBinding>())
|
|
{
|
|
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();
|
|
}
|
|
|
|
/// <summary>
|
|
/// 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.
|
|
/// </summary>
|
|
public static List<NotePlaceholderBinding> BuildEffectiveValueKeyBindings(IEnumerable<NotePlaceholderBinding> bindings)
|
|
{
|
|
var list = (bindings ?? Enumerable.Empty<NotePlaceholderBinding>())
|
|
.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");
|
|
}
|
|
}
|
|
}
|