Report System Part II
In the previous post I told you we decided to write our own report system, so we’ll have an html page for each letter or report fixed contents and some kind of a special pattern for their interchangeable parts.
From now I will only say report but remember it could be a letter too.
Our program read through those html pages and find their interchangeable parts according to the pattern and replace them with the actual values(which were determined by users).
So after some discussions between me and my boss we found out we should have a Parser module which extract out interchangeable parts. After that we pass those parsed parameters to a Getter module which gets their actual values and then we give those values back to the parser again and it replaces them in the report, So we’ll end up with a complete report with actual values in it, PERFECT.
Just so you know our patterns were these:
public const string ParameterPattern = @"~(?<param>[\w\d.]+)~";
public const string TemplatePattern = @"\|(?<key>[\w\d\.]+)\[\](?<value>[^\|]*)\|";
*Because “~” and “ | ” don’t have any meaning in html we used them.* |
We used Regex for our patterns. First one is for simple things like ~LetterNo~ or ~Title~ and etc which will be set by users directly. Second one is for repeatable things like collections or one-to-many and many-to-many relationships between our Entities in the system.
For example imagine we have a File entity which has a one-to-many relationship with Person entity(each File contains a collection of Persons) so we should be able to get values for all of those persons and put them in the report, ergo the string in the page is something like this which matches with the pattern:
Person[]< ~Name~><~Family~> |
now for each person in the collection we get Name and Family values and put them in the report (in this case we have two persons for simplicity of example):
[Test]
public void ShouldGetAllSimpleParameters()
{
var parameters = FieldParser.GetAllParameters("This is a ~test~ input! ~bye~");
Assert.AreEqual(2, parameters.Count);
Assert.IsTrue(parameters.Contains("bye"));
Assert.IsTrue(parameters.Contains("test"));
}
public static IList<string> GetAllParameters(string text);
[Test]
public void ShouldGetTemplateKey()
{
var templateKey = FieldParser.GetTemplateKey("|Persons[] Name|");
Assert.AreEqual("Persons", templateKey");
}
[Test]
public void ShouldGetTemplateValue()
{
var templateValue = FieldParser.GetTemplateValue("|Persons[] Name|");
Assert.AreEqual("Name", templateValue");
}
public static string GetTemplateKey(string template)
{
var match = Regex.Match(template, TemplatePattern);
if (match.success)
return match.Groups["key"].Value;
return null;
}
public static string GetTemplateValue(string template)
{
var match = Regex.Match(template, TemplatePattern);
if (match.success)
return match.Groups["value"].Value;
return null;
}
[Test]
public void ShouldBeAbleToSetParameters()
{
var text = "Please replace ~this~ and ~that~";
var replacingValueDic = new Dictionary<string, string>();
replacingValueDic.Add("this", "me");
replacingValueDic.Add("that", "you");
var replacedText = FieldParser.SetParameters(text, replacingValueDic);
Assert.AreEqual("Please replace me and you", replacedText);
}
public static string SetParameters(string text, IDictionary<string, string> replacingValues)
{
if (replacingValues == null)
return text;
var replacedText = text;
foreach(var replacingValue in replacingValues)
replacedText = replacedText.Replace("~" + replacingValue.key + "~", replacingValue.value);
return replacedText;
}
[Test]
public void ShouldReplaceCorrectlyInRepeatTemplates()
{
var text = "|Persons[]<tr><td>~Name~</td><td>~Family~</td></tr>|";
var replacedText = FieldParser.RepeatTemplates(text, new Func<object, string, object>(StubGetter));
//SubGetter is for testing purposes which returns a collection with two persons in it
Assert.AreEqual("<tr><td>mark</td><td>johnson</td></tr><tr>" +
"<td>john</td><td>markson</td></tr>", replacedText);
}
//this method replace actual values for all the parameters in a template
//and do that as many times as needed according to the related collection
//count like Person collection or whatever;
public static string RepeateTemplates(string text, Func<object, string, object> valueGetter);
public static string Parse(string text, Func<object, string, object> valueGetter)
{
text = HandleAllTemplates(text, getter);
text = HandleOtherParameters(text, getter);
return text;
}