template engine
This commit is contained in:
84
Inspectron.Epson.TemplateEngine/Parsing/TemplateNode.cs
Normal file
84
Inspectron.Epson.TemplateEngine/Parsing/TemplateNode.cs
Normal file
@@ -0,0 +1,84 @@
|
||||
namespace Inspectron.Epson.TemplateEngine.Parsing;
|
||||
|
||||
public abstract class TemplateNode
|
||||
{
|
||||
public List<TemplateNode> Children { get; set; } = new();
|
||||
}
|
||||
|
||||
public class ReceiptNode : TemplateNode { }
|
||||
|
||||
public class LineNode : TemplateNode
|
||||
{
|
||||
public string? Text { get; set; }
|
||||
public bool Bold { get; set; }
|
||||
public bool Big { get; set; }
|
||||
public bool Tall { get; set; }
|
||||
public bool Red { get; set; }
|
||||
public string Align { get; set; } = "left";
|
||||
public int? LineSpacing { get; set; }
|
||||
public bool Wrap { get; set; }
|
||||
public int WrapIndent { get; set; }
|
||||
}
|
||||
|
||||
public class ColumnsNode : TemplateNode
|
||||
{
|
||||
public string? Left { get; set; }
|
||||
public string? Right { get; set; }
|
||||
public bool Bold { get; set; }
|
||||
public bool Big { get; set; }
|
||||
public bool Tall { get; set; }
|
||||
public bool Red { get; set; }
|
||||
public int? LineSpacing { get; set; }
|
||||
public bool Wrap { get; set; }
|
||||
public int WrapIndent { get; set; }
|
||||
}
|
||||
|
||||
public class RowNode : TemplateNode
|
||||
{
|
||||
public List<ColumnDef> Columns { get; set; } = new();
|
||||
public bool Bold { get; set; }
|
||||
public bool Big { get; set; }
|
||||
public bool Tall { get; set; }
|
||||
public bool Red { get; set; }
|
||||
public int? LineSpacing { get; set; }
|
||||
}
|
||||
|
||||
public class ColumnDef
|
||||
{
|
||||
public string? Text { get; set; }
|
||||
public int? Width { get; set; }
|
||||
public string Align { get; set; } = "left";
|
||||
}
|
||||
|
||||
public class SeparatorNode : TemplateNode
|
||||
{
|
||||
public char Character { get; set; } = '-';
|
||||
}
|
||||
|
||||
public class CutNode : TemplateNode { }
|
||||
|
||||
public class FeedNode : TemplateNode
|
||||
{
|
||||
public int Lines { get; set; } = 1;
|
||||
}
|
||||
|
||||
public class ForeachNode : TemplateNode
|
||||
{
|
||||
public string Items { get; set; } = "";
|
||||
public string Var { get; set; } = "";
|
||||
}
|
||||
|
||||
public class IfNode : TemplateNode
|
||||
{
|
||||
public string Test { get; set; } = "";
|
||||
}
|
||||
|
||||
public class ElseNode : TemplateNode { }
|
||||
|
||||
public class TableNode : TemplateNode
|
||||
{
|
||||
public List<ColumnDef> Columns { get; set; } = new();
|
||||
public string? HeaderItems { get; set; }
|
||||
public string? Items { get; set; }
|
||||
public string? Var { get; set; }
|
||||
}
|
||||
250
Inspectron.Epson.TemplateEngine/Parsing/TemplateParser.cs
Normal file
250
Inspectron.Epson.TemplateEngine/Parsing/TemplateParser.cs
Normal file
@@ -0,0 +1,250 @@
|
||||
using System.Xml.Linq;
|
||||
|
||||
namespace Inspectron.Epson.TemplateEngine.Parsing;
|
||||
|
||||
public class TemplateParser
|
||||
{
|
||||
public ReceiptNode Parse(string xml)
|
||||
{
|
||||
XDocument doc;
|
||||
try
|
||||
{
|
||||
doc = XDocument.Parse(xml);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
throw new TemplateParsingException($"Invalid XML: {ex.Message}", ex);
|
||||
}
|
||||
|
||||
var root = doc.Root;
|
||||
if (root == null || root.Name.LocalName != "receipt")
|
||||
throw new TemplateParsingException("Root element must be <receipt>");
|
||||
|
||||
var receiptNode = new ReceiptNode();
|
||||
ParseChildren(root, receiptNode.Children);
|
||||
return receiptNode;
|
||||
}
|
||||
|
||||
private void ParseChildren(XElement parent, List<TemplateNode> children)
|
||||
{
|
||||
foreach (var element in parent.Elements())
|
||||
{
|
||||
var node = ParseElement(element);
|
||||
if (node != null)
|
||||
children.Add(node);
|
||||
}
|
||||
}
|
||||
|
||||
private TemplateNode? ParseElement(XElement element)
|
||||
{
|
||||
return element.Name.LocalName switch
|
||||
{
|
||||
"line" => ParseLine(element),
|
||||
"columns" => ParseColumns(element),
|
||||
"row" => ParseRow(element),
|
||||
"separator" => ParseSeparator(element),
|
||||
"cut" => new CutNode(),
|
||||
"feed" => ParseFeed(element),
|
||||
"foreach" => ParseForeach(element),
|
||||
"if" => ParseIf(element),
|
||||
"else" => ParseElse(element),
|
||||
"table" => ParseTable(element),
|
||||
_ => throw new TemplateParsingException($"Unknown element: <{element.Name.LocalName}>")
|
||||
};
|
||||
}
|
||||
|
||||
private LineNode ParseLine(XElement element)
|
||||
{
|
||||
var node = new LineNode();
|
||||
ApplyFormatting(element, node);
|
||||
|
||||
// Text content: either inner text (with {{}} expressions) or empty line
|
||||
var text = GetTextContent(element);
|
||||
node.Text = text;
|
||||
|
||||
node.Align = GetAttr(element, "align", "left");
|
||||
node.Wrap = GetBoolAttr(element, "wrap");
|
||||
node.WrapIndent = GetIntAttr(element, "wrapIndent", 0);
|
||||
|
||||
return node;
|
||||
}
|
||||
|
||||
private ColumnsNode ParseColumns(XElement element)
|
||||
{
|
||||
var node = new ColumnsNode();
|
||||
ApplyFormatting(element, node);
|
||||
|
||||
node.Left = GetAttr(element, "left", "");
|
||||
node.Right = GetAttr(element, "right", "");
|
||||
node.Wrap = GetBoolAttr(element, "wrap");
|
||||
node.WrapIndent = GetIntAttr(element, "wrapIndent", 0);
|
||||
|
||||
return node;
|
||||
}
|
||||
|
||||
private RowNode ParseRow(XElement element)
|
||||
{
|
||||
var node = new RowNode();
|
||||
ApplyFormatting(element, node);
|
||||
|
||||
foreach (var colElement in element.Elements("col"))
|
||||
{
|
||||
var col = new ColumnDef
|
||||
{
|
||||
Text = GetTextContent(colElement),
|
||||
Width = GetNullableIntAttr(colElement, "width"),
|
||||
Align = GetAttr(colElement, "align", "left")
|
||||
};
|
||||
node.Columns.Add(col);
|
||||
}
|
||||
|
||||
return node;
|
||||
}
|
||||
|
||||
private SeparatorNode ParseSeparator(XElement element)
|
||||
{
|
||||
var charAttr = GetAttr(element, "char", "-");
|
||||
return new SeparatorNode
|
||||
{
|
||||
Character = string.IsNullOrEmpty(charAttr) ? '-' : charAttr[0]
|
||||
};
|
||||
}
|
||||
|
||||
private FeedNode ParseFeed(XElement element)
|
||||
{
|
||||
return new FeedNode
|
||||
{
|
||||
Lines = GetIntAttr(element, "lines", 1)
|
||||
};
|
||||
}
|
||||
|
||||
private ForeachNode ParseForeach(XElement element)
|
||||
{
|
||||
var items = GetRequiredAttr(element, "items", "foreach")!;
|
||||
var var_ = GetRequiredAttr(element, "var", "foreach")!;
|
||||
|
||||
var node = new ForeachNode { Items = items, Var = var_ };
|
||||
ParseChildren(element, node.Children);
|
||||
return node;
|
||||
}
|
||||
|
||||
private IfNode ParseIf(XElement element)
|
||||
{
|
||||
var test = GetRequiredAttr(element, "test", "if")!;
|
||||
|
||||
var node = new IfNode { Test = test };
|
||||
ParseChildren(element, node.Children);
|
||||
return node;
|
||||
}
|
||||
|
||||
private ElseNode ParseElse(XElement element)
|
||||
{
|
||||
var node = new ElseNode();
|
||||
ParseChildren(element, node.Children);
|
||||
return node;
|
||||
}
|
||||
|
||||
private TableNode ParseTable(XElement element)
|
||||
{
|
||||
var node = new TableNode
|
||||
{
|
||||
Items = GetAttr(element, "items", null),
|
||||
Var = GetAttr(element, "var", null),
|
||||
HeaderItems = GetAttr(element, "headerItems", null)
|
||||
};
|
||||
|
||||
foreach (var colElement in element.Elements("col"))
|
||||
{
|
||||
var col = new ColumnDef
|
||||
{
|
||||
Text = GetTextContent(colElement),
|
||||
Width = GetNullableIntAttr(colElement, "width"),
|
||||
Align = GetAttr(colElement, "align", "left")
|
||||
};
|
||||
node.Columns.Add(col);
|
||||
}
|
||||
|
||||
// Parse non-col children (col elements are consumed above as column definitions)
|
||||
foreach (var child in element.Elements().Where(e => e.Name.LocalName != "col"))
|
||||
{
|
||||
var childNode = ParseElement(child);
|
||||
if (childNode != null)
|
||||
node.Children.Add(childNode);
|
||||
}
|
||||
|
||||
return node;
|
||||
}
|
||||
|
||||
private static void ApplyFormatting(XElement element, LineNode node)
|
||||
{
|
||||
node.Bold = GetBoolAttr(element, "bold");
|
||||
node.Big = GetBoolAttr(element, "big");
|
||||
node.Tall = GetBoolAttr(element, "tall");
|
||||
node.Red = GetBoolAttr(element, "red");
|
||||
node.LineSpacing = GetNullableIntAttr(element, "lineSpacing");
|
||||
}
|
||||
|
||||
private static void ApplyFormatting(XElement element, ColumnsNode node)
|
||||
{
|
||||
node.Bold = GetBoolAttr(element, "bold");
|
||||
node.Big = GetBoolAttr(element, "big");
|
||||
node.Tall = GetBoolAttr(element, "tall");
|
||||
node.Red = GetBoolAttr(element, "red");
|
||||
node.LineSpacing = GetNullableIntAttr(element, "lineSpacing");
|
||||
}
|
||||
|
||||
private static void ApplyFormatting(XElement element, RowNode node)
|
||||
{
|
||||
node.Bold = GetBoolAttr(element, "bold");
|
||||
node.Big = GetBoolAttr(element, "big");
|
||||
node.Tall = GetBoolAttr(element, "tall");
|
||||
node.Red = GetBoolAttr(element, "red");
|
||||
node.LineSpacing = GetNullableIntAttr(element, "lineSpacing");
|
||||
}
|
||||
|
||||
private static string GetTextContent(XElement element)
|
||||
{
|
||||
// Get all inner text content (may include {{}} expressions)
|
||||
// Use element.Value to get concatenated text of all text nodes
|
||||
if (!element.HasElements)
|
||||
return element.Value;
|
||||
|
||||
// If element has child elements, only get direct text nodes
|
||||
return string.Concat(element.Nodes().OfType<XText>().Select(t => t.Value));
|
||||
}
|
||||
|
||||
private static string? GetRequiredAttr(XElement element, string name, string elementName)
|
||||
{
|
||||
var attr = element.Attribute(name);
|
||||
if (attr == null)
|
||||
throw new TemplateParsingException($"<{elementName}> requires '{name}' attribute");
|
||||
return attr.Value;
|
||||
}
|
||||
|
||||
private static string GetAttr(XElement element, string name, string? defaultValue)
|
||||
{
|
||||
var attr = element.Attribute(name);
|
||||
return attr?.Value ?? defaultValue ?? "";
|
||||
}
|
||||
|
||||
private static bool GetBoolAttr(XElement element, string name)
|
||||
{
|
||||
var attr = element.Attribute(name);
|
||||
if (attr == null) return false;
|
||||
return attr.Value.Equals("true", StringComparison.OrdinalIgnoreCase);
|
||||
}
|
||||
|
||||
private static int GetIntAttr(XElement element, string name, int defaultValue)
|
||||
{
|
||||
var attr = element.Attribute(name);
|
||||
if (attr == null) return defaultValue;
|
||||
return int.TryParse(attr.Value, out var v) ? v : defaultValue;
|
||||
}
|
||||
|
||||
private static int? GetNullableIntAttr(XElement element, string name)
|
||||
{
|
||||
var attr = element.Attribute(name);
|
||||
if (attr == null) return null;
|
||||
return int.TryParse(attr.Value, out var v) ? v : null;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user