using System;
using System.Collections.Generic;
using System.Text;
using System.Text.RegularExpressions;
using System.Data;
using System.Reflection;
using System.Collections;
using System.Data.SqlClient;
namespace FillTextTemplate
{
class Program
{
public class CustomerInfo
{
public string Name;
}
public class PartInfo
{
public string PartNumber;
public string Description;
public bool IsRestricted;
public List<PartInfo> SimilarParts = null;
public List<int> WarehouseIDs = null;
}
public class ItemInfo
{
public PartInfo Part;
public decimal Quantity;
public decimal UnitPrice;
public decimal TotalPrice
{
get
{
return Quantity * UnitPrice;
}
}
}
public class OrderInfo
{
public CustomerInfo Customer = null;
public int OrderNumber;
public List<ItemInfo> Items = null;
public decimal TotalPrice
{
get
{
if (Items == null) return 0M;
decimal sum = 0.0M;
foreach (var i in Items) sum += i.TotalPrice;
return sum;
}
}
}
static void Main(string[] args)
{
PartInfo widget1 = new PartInfo()
{
PartNumber = "ABC-001",
Description = "Widget #1",
IsRestricted = true,
WarehouseIDs = new List<int>() { 1, 2 }
};
PartInfo widget2 = new PartInfo()
{
PartNumber = "ABC-002",
Description = "Widget #2",
SimilarParts = new List<PartInfo>() { widget1 },
WarehouseIDs = new List<int>() { 2, 3, 4 }
};
PartInfo widget3 = new PartInfo()
{
PartNumber = "ABC-003",
Description = "Widget #3",
SimilarParts = new List<PartInfo>() { widget1, widget2 }
};
OrderInfo order = new OrderInfo()
{
Customer = new CustomerInfo() { Name = "Michael Bray" },
OrderNumber = 173123,
Items = new List<ItemInfo>() {
new ItemInfo() { Part = widget1, Quantity = 2, UnitPrice = 30 },
new ItemInfo() { Part = widget2, Quantity = 4.5M, UnitPrice = 10 },
new ItemInfo() { Part = widget3, Quantity = 60, UnitPrice = 4.25M }
}
};
string template, filled;
template = "Hello @Customer.Name, your order #@OrderNumber has been fulfilled. "
+ "The items are:\r\n\r\n"
+ "Part Number Description R Quantity Unit Price Total Price Similar Part Numbers Warehouse IDs\r\n"
+ "----------- -------------------- - -------- ---------- ----------- -------------------- -------------\r\n"
+ "@?[[^ @Items.Count > 0 ^]][[^@Items[[#@Part.PartNumber{-12} @Part.Description{-20} @?[[$ @Part.IsRestricted==True $]][[$Y$]][[$ $]] @Quantity{8}{F2} @UnitPrice{11}{C} @TotalPrice{12}{C} @Part.SimilarParts{-20}[[%@PartNumber,%]] @Part.WarehouseIDs{-10}[[%@$,%]]\r\n#]]"
+ " -----------\r\n"
+ " GRAND TOTAL: @TotalPrice{12}{C}\r\n"
+ "^]][[^ NONE!!! ^]]";
filled = Demo.FillTemplate(template, order);
template = "Parts List:\r\n"
+ "Part Number Description Is Restricted\r\n"
+ "------------- --------------------- -------------\r\n"
+ "@$[[% @PartNumber{-12} @Description{-20} @?[[# @IsRestricted == True #]][[# YES #]][[# NO #]] \r\n%]]";
filled = Demo.FillTemplate(template, new List<PartInfo>() { widget1, widget2, widget3 });
}
}
public static class Demo
{
private static Regex rgxFillTemplate = new Regex(@"(?<VarName>@(\w+(\.\w+)*|\$))({(?<Width>-?\d+)})?({(?<Format>.+?)}|(((?<Open>\[{2}(?<CloseC>.))).*?(?<SubExpr-Open>\k<CloseC>\]{2})){1,3})?", RegexOptions.Compiled | RegexOptions.Singleline);
private static Regex rgxConditional = new Regex(@"(?<VarName>@\??)(((?<Open>\[{2}(?<CloseC>.))).*?(?<SubExpr-Open>\k<CloseC>\]{2})){2,3}", RegexOptions.Compiled | RegexOptions.Singleline);
public static string FillTemplate(string template, object ds)
{
StringBuilder sb = new StringBuilder(template);
MatchCollection mc = rgxFillTemplate.Matches(template);
IDataRecord dr = ds as IDataRecord;
IDataReader drr = ds as IDataReader;
foreach (Match m in mc)
{
object o = ds;
string varName = m.Groups["VarName"].Value.TrimStart('@');
string width = m.Groups["Width"].Success ? "," + m.Groups["Width"].Value : string.Empty;
string format = m.Groups["Format"].Success ? ":" + m.Groups["Format"].Value : string.Empty;
string varFormat = "{0" + width + format + "}";
string varSubExpr = m.Groups["SubExpr"].Success ? m.Groups["SubExpr"].Value : null;
string vv = string.Format(varFormat, string.Empty);
if (dr != null)
{
try
{
if ((varName == "$") && (varSubExpr != null) && (drr != null))
{
List<string> vvs = new List<string>();
while (drr.Read())
{
vvs.Add(FillTemplate(varSubExpr, dr));
}
vv = string.Format(varFormat, string.Join(string.Empty, vvs.ToArray()));
}
else
{
int vi = dr.GetOrdinal(varName);
vv = string.Format(varFormat, dr.IsDBNull(vi) ? string.Empty : dr[vi]);
}
}
catch (IndexOutOfRangeException) { }
}
else
{
string[] varPath = varName.Split('.');
for (int vi = 0; vi < varPath.Length; vi++)
{
string vn = varPath[vi];
if (vn == "$") break;
Type t = o.GetType();
var mems = t.GetMember(vn, BindingFlags.FlattenHierarchy | BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Static);
object fo = null;
foreach (var mi in mems)
{
if (mi != null)
{
if (mi is PropertyInfo)
{
PropertyInfo pi = mi as PropertyInfo;
fo = pi.GetValue(o, null);
break;
}
else if (mi is FieldInfo)
{
FieldInfo fi = mi as FieldInfo;
fo = fi.GetValue(o);
break;
}
}
}
o = fo;
if (o == null) break;
}
if (o != null)
{
if (varSubExpr != null)
{
List<string> vvs = new List<string>();
if (o is IEnumerable)
{
IEnumerable oe = o as IEnumerable;
foreach (var oo in oe) vvs.Add(FillTemplate(varSubExpr, oo));
}
vv = string.Format(varFormat, string.Join(string.Empty, vvs.ToArray()));
}
else vv = string.Format(varFormat, o);
}
}
sb = sb.Replace(m.Value, vv);
}
mc = rgxConditional.Matches(sb.ToString());
foreach (Match m in mc)
{
int numPhrases = m.Groups["SubExpr"].Captures.Count;
string conditional = m.Groups["SubExpr"].Captures[0].Value;
string yesExpr = m.Groups["SubExpr"].Captures[1].Value;
string noExpr = string.Empty;
if (numPhrases == 3) noExpr = m.Groups["SubExpr"].Captures[2].Value;
Match cm = Regex.Match(conditional, @"^\s*(?<Left>.*?)\s*(?<Op>\<=?|\>=?|==|!=)\s*(?<Right>.*?)\s*$");
if (!cm.Success) sb = sb.Replace(m.Value, FillTemplate(noExpr, ds));
else
{
bool success = true;
int cv;
string sLeft = cm.Groups["Left"].Value;
string sRight = cm.Groups["Right"].Value;
string sOp = cm.Groups["Op"].Value;
decimal dLeft = 0, dRight = 0;
success = success && decimal.TryParse(sLeft, out dLeft);
success = success && decimal.TryParse(sRight, out dRight);
if (success) cv = Comparer<decimal>.Default.Compare(dLeft, dRight);
else cv = Comparer<string>.Default.Compare(sLeft, sRight);
string repExpr = string.Empty;
switch (sOp)
{
case "<": repExpr = (cv < 0) ? yesExpr : noExpr; break;
case ">": repExpr = (cv > 0) ? yesExpr : noExpr; break;
case "<=": repExpr = (cv <= 0) ? yesExpr : noExpr; break;
case ">=": repExpr = (cv >= 0) ? yesExpr : noExpr; break;
case "==": repExpr = (cv == 0) ? yesExpr : noExpr; break;
case "!=": repExpr = (cv != 0) ? yesExpr : noExpr; break;
}
sb = sb.Replace(m.Value, FillTemplate(repExpr, ds));
}
}
return sb.ToString();
}
}
}