Dominic Höglinger 1899db4156 feat: implement ETG.2000-compatible CRC32 for XNeedle transforms
- 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
2026-04-24 05:28:39 +02:00

47 lines
1.3 KiB
C#

using System.Collections.Generic;
using System.Xml;
using System.Xml.Linq;
using System.Xml.XPath;
using XNeedle;
using XNeedle.Parser.Elements;
namespace XNeedle.Transformation
{
public class Rule : Part
{
public required string XPathSelector { get; set; }
public required IEnumerable<Part> Body;
public void Transform(XDocument xml, SourceLocation sloc)
{
var context = new Context();
context.Node = xml.Root;
context.SLoc = sloc;
Operate(context);
}
public override void Operate(Context ctx)
{
if(ctx.Node is null)
{
return;
}
var nodes = ctx.Node.XPathSelectElements(XPathSelector);
if(nodes is null)
{
return;
}
foreach (XElement n in nodes)
{
var loopCtx = ctx.Clone();
loopCtx.Node = n;
var lineInfo = (IXmlLineInfo)n;
loopCtx.SLoc = ctx.SLoc.WithLineNumber(lineInfo.HasLineInfo() ? lineInfo.LineNumber : -1);
foreach (var b in Body)
{
b.Operate(loopCtx);
}
}
}
}
}