- Add SourceLocation struct (XNeedle namespace) with FilePath, Encoding, and LineNumber (-1 sentinel when unavailable) - Add SLoc property to Context; Clone() propagates it for free - Expose XmlRoundtripWriter.DetectEncoding as internal static - Add XmlRoundtripWriter.CreateToString(filePath, encoding) returning a (writer, StringBuilder) tuple for in-memory style-preserving rendering - Update XmlFragParser.ChecksumData to accept tagName only; use OpeningTagAnchor to skip the opening tag robustly, and AnyChar.Until() to non-greedily capture content through the closing tag - Thread SourceLocation from Program.cs through Rule.Transform/Operate, refreshing LineNumber per node via IXmlLineInfo - Implement ContextFunctions.crc: render node via CreateToString, extract digest slice via ChecksumData, encode with SLoc.Encoding, return CRC32 as double for arithmetic compatibility Clanker: claude-sonnet_4.6
110 lines
3.2 KiB
C#
110 lines
3.2 KiB
C#
using System;
|
|
using System.Linq;
|
|
using System.Text;
|
|
using Sprache;
|
|
using XNeedle.Formats;
|
|
using XNeedle.Parser;
|
|
using XNeedle.Parser.Elements;
|
|
|
|
namespace XNeedle.Transformation
|
|
{
|
|
public class ContextFunctions
|
|
{
|
|
public static string hex(Context ctx, double value)
|
|
{
|
|
uint v = (uint)value;
|
|
return $"#x{v:X8}";
|
|
}
|
|
|
|
public static double GetLevel(Context ctx)
|
|
{
|
|
int level = 0;
|
|
System.Xml.Linq.XElement? n = ctx.Node;
|
|
while (null != (n = n?.Parent))
|
|
level++;
|
|
return level;
|
|
}
|
|
public static double crc(Context ctx)
|
|
{
|
|
if (ctx.Node is null)
|
|
return 0;
|
|
|
|
// Step 1: Render the element to a string, preserving source style
|
|
var (writer, buffer) = XmlRoundtripWriter.CreateToString(ctx.SLoc.FilePath, ctx.SLoc.Encoding);
|
|
ctx.Node.WriteTo(writer);
|
|
writer.Flush();
|
|
string xml = buffer.ToString();
|
|
|
|
// Step 2: Extract all text after the opening tag through and including the closing tag
|
|
string tagName = ctx.Node.Name.LocalName;
|
|
string data = XmlFragParser.ChecksumData(tagName).Parse(xml);
|
|
|
|
// Step 3: Digest into CRC32 using the source encoding
|
|
var crc32 = new System.IO.Hashing.Crc32();
|
|
crc32.Append(ctx.SLoc.Encoding.GetBytes(data));
|
|
uint result = BitConverter.ToUInt32(crc32.GetCurrentHash());
|
|
|
|
return (double)result;
|
|
}
|
|
|
|
public static string text(Context ctx)
|
|
{
|
|
if (ctx.Node is null) return "";
|
|
return ctx.Node.Value;
|
|
}
|
|
|
|
public static string text(Context ctx, string txt)
|
|
{
|
|
if (ctx.Node is null) return "";
|
|
ctx.Node.SetValue(txt);
|
|
return txt;
|
|
}
|
|
|
|
public static string cdata(Context ctx)
|
|
{
|
|
if (ctx.Node is null) return "";
|
|
var cdataNode = ctx.Node.Nodes().OfType<System.Xml.Linq.XCData>().FirstOrDefault();
|
|
return cdataNode?.Value ?? "";
|
|
}
|
|
|
|
public static string cdata(Context ctx, string txt)
|
|
{
|
|
if (ctx.Node is null) return "";
|
|
var cdataNode = ctx.Node.Nodes().OfType<System.Xml.Linq.XCData>().FirstOrDefault();
|
|
if (cdataNode != null)
|
|
{
|
|
cdataNode.Value = txt;
|
|
}
|
|
else
|
|
{
|
|
ctx.Node.Add(new System.Xml.Linq.XCData(txt));
|
|
}
|
|
return txt;
|
|
}
|
|
|
|
public static string attr(Context ctx, string name, string value)
|
|
{
|
|
if (ctx.Node is null) return "";
|
|
var attr = ctx.Node.Attribute(name);
|
|
if (attr != null)
|
|
{
|
|
attr.Value = value;
|
|
}
|
|
else
|
|
{
|
|
ctx.Node.SetAttributeValue(name, value);
|
|
}
|
|
return "";
|
|
}
|
|
|
|
public static string attr(Context ctx, string name)
|
|
{
|
|
var attr = ctx.Node?.Attribute(name);
|
|
if (attr != null)
|
|
{
|
|
return attr.Value;
|
|
}
|
|
return "";
|
|
}
|
|
}
|
|
} |