{"id":260,"date":"2011-03-01T03:21:04","date_gmt":"2011-03-01T03:21:04","guid":{"rendered":"http:\/\/www.ericwhite.com\/home2\/bm8qcmjy\/public_html\/blog\/?p=260"},"modified":"2011-03-01T16:14:50","modified_gmt":"2011-03-01T16:14:50","slug":"a-super-simple-template-system","status":"publish","type":"post","link":"https:\/\/www.ericwhite.com\/blog\/2011\/03\/01\/a-super-simple-template-system\/","title":{"rendered":"A Super-Simple Template System"},"content":{"rendered":"<p>In the last post, I explored Text Templates (T4), and determined that using T4 text templates for my code generation needs would add complexity and not yield sufficient ROI (although I did determine that a doc gen example using T4 is interesting in its own right). However, my exploration into T4 text templates yielded one important point, which is that delimiting blocks of code using <strong>&lt;#<\/strong> and <strong>#&gt;<\/strong> is a good approach. This post details my super-simple template system, which will be more than adequate for building this first version of a doc gen system.<\/p>\n<p>This post is the ninth in a series of blog posts on generating Open XML documents. Here is the complete list: <a href=\"https:\/\/www.ericwhite.com\/blog\/map\/generating-open-xml-wordprocessingml-documents-blog-post-series\/\">Generating Open XML WordprocessingML Documents Blog Post Series<\/a><\/p>\n<p>For what it\u2019s worth, I did a fair amount of reading of the C#, VB, and XML specs and determined to my own satisfaction that those combinations are fine. I could go on for about two pages, detailing exactly where the hash mark is allowed in all three languages, and why <strong>&lt;#<\/strong> and <strong>#&gt;<\/strong> are safe, but I\u2019ll spare you the ordeal. In any case, these are the combinations that the T4 architects and program managers decided on, and I\u2019m certain that an extraordinary amount of time was spent designing the T4 syntax.<\/p>\n<p>I am going to make one more simplification, which is that in my super-simple template system, the <strong>&lt;#<\/strong> must be the first two non-whitespace characters on a line, and that <strong>#&gt;<\/strong> must be the last two non-whitespace characters. You will see that this makes the LINQ projection that processes the template very simple.  Ultimately, this template system would be best implemented by defining a grammar and writing or using a real parser, but my main objective is to build a small example that enables us to explore document generation, so a shortcut is in order here.<\/p>\n<p>To allow for further enhancements in the future, I\u2019m going to specify that the contents will be a small XML document. While this makes the syntax a bit more verbose, we gain such advantages as XML schema validation, extensibility, and a familiar syntax. Following is an example of a template using this system. It contains two insertion blocks, one with the name of <strong>Using<\/strong>, and the other with the name of <strong>GeneratorMain<\/strong>:<\/p>\n<p><code>&lt;#&nbsp;&lt;Insert&nbsp;Name=\"Using\"\/&gt;&nbsp;#&gt;<\/p>\n<p>namespace&nbsp;GenDocs<br \/>\n{<br \/>\n&nbsp;&nbsp;&nbsp;&nbsp;class&nbsp;Generator<br \/>\n&nbsp;&nbsp;&nbsp;&nbsp;{<br \/>\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;static&nbsp;void&nbsp;Main(string[]&nbsp;args)<br \/>\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{<br \/>\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;#&nbsp;&lt;Insert&nbsp;Name=\"GeneratorMain\"\/&gt;&nbsp;#&gt;<br \/>\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br \/>\n&nbsp;&nbsp;&nbsp;&nbsp;}<br \/>\n}<\/code><\/p>\n<p>The Using insertion block might be replaced with this:<br \/>\n<code><br \/>\nusing System;<br \/>\nusing System.Collections.Generic;<br \/>\nusing System.Linq;<br \/>\nusing System.Text;<br \/>\n<\/code><\/p>\n<p>The GeneratorMain insertion block could be replaced with this:<\/p>\n<p><code>Console.WriteLine(\"Hello world\");<\/code><\/p>\n<p>Processing this template would then result in the following C# program:<\/p>\n<p><code>using&nbsp;System;<br \/>\nusing&nbsp;System.Collections.Generic;<br \/>\nusing&nbsp;System.Linq;<br \/>\nusing&nbsp;System.Text;<\/p>\n<p>namespace&nbsp;GenDocs<br \/>\n{<br \/>\n&nbsp;&nbsp;&nbsp;&nbsp;class&nbsp;Generator<br \/>\n&nbsp;&nbsp;&nbsp;&nbsp;{<br \/>\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;static&nbsp;void&nbsp;Main(string[]&nbsp;args)<br \/>\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{<br \/>\nConsole.WriteLine(\"Hello&nbsp;world\");<br \/>\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br \/>\n&nbsp;&nbsp;&nbsp;&nbsp;}<br \/>\n}<\/code><\/p>\n<p>The LINQ projection that processes this template, in its entirety, is:<\/p>\n<p><code>using&nbsp;System;<br \/>\nusing&nbsp;System.Collections.Generic;<br \/>\nusing&nbsp;System.IO;<br \/>\nusing&nbsp;System.Linq;<br \/>\nusing&nbsp;System.Text;<br \/>\nusing&nbsp;System.Xml.Linq;<\/p>\n<p>class&nbsp;Program<br \/>\n{<br \/>\n&nbsp;&nbsp;&nbsp;&nbsp;\/\/&nbsp;Simulated&nbsp;method&nbsp;that&nbsp;returns&nbsp;the&nbsp;text&nbsp;of&nbsp;a&nbsp;tagged&nbsp;content&nbsp;control.<br \/>\n&nbsp;&nbsp;&nbsp;&nbsp;static&nbsp;string&nbsp;GetTextFromContentControl(string&nbsp;tag)<br \/>\n&nbsp;&nbsp;&nbsp;&nbsp;{<br \/>\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if&nbsp;(tag&nbsp;==&nbsp;\"Using\")<br \/>\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return<br \/>\n@\"using&nbsp;System;<br \/>\nusing&nbsp;System.Collections.Generic;<br \/>\nusing&nbsp;System.Linq;<br \/>\nusing&nbsp;System.Text;\";<br \/>\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if&nbsp;(tag&nbsp;==&nbsp;\"GeneratorMain\")<br \/>\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return&nbsp;@\"Console.WriteLine(\"\"Hello&nbsp;world\"\");\";<br \/>\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return&nbsp;\"error\";<br \/>\n&nbsp;&nbsp;&nbsp;&nbsp;}<\/p>\n<p>&nbsp;&nbsp;&nbsp;&nbsp;static&nbsp;void&nbsp;Main(string[]&nbsp;args)<br \/>\n&nbsp;&nbsp;&nbsp;&nbsp;{<br \/>\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;string[]&nbsp;templateCode&nbsp;=&nbsp;File.ReadAllLines(\"template.txt\");<br \/>\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;var&nbsp;filledTemplate&nbsp;=&nbsp;templateCode<br \/>\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;.Select(l&nbsp;=&gt;<br \/>\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{<br \/>\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;string&nbsp;trimmed&nbsp;=&nbsp;l.Trim();<br \/>\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if&nbsp;(trimmed.StartsWith(\"&lt;#\")&nbsp;&amp;&amp;&nbsp;trimmed.EndsWith(\"#&gt;\"))<br \/>\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{<br \/>\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;XElement&nbsp;insert&nbsp;=&nbsp;XElement.Parse(trimmed.Substring(2,&nbsp;trimmed.Length&nbsp;-&nbsp;4));<br \/>\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;string&nbsp;tag&nbsp;=&nbsp;insert.Attribute(\"Name\").Value;<br \/>\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return&nbsp;GetTextFromContentControl(tag);<br \/>\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br \/>\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;else<br \/>\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return&nbsp;l;<br \/>\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;})<br \/>\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;.ToArray();<br \/>\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;File.WriteAllLines(\"GeneratedDocGenProgram.cs\",&nbsp;filledTemplate);<\/p>\n<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;\/\/&nbsp;Print&nbsp;out&nbsp;the&nbsp;template&nbsp;for&nbsp;demonstration&nbsp;purposes.<br \/>\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Console.WriteLine(File.ReadAllText(\"GeneratedDocGenProgram.cs\"));<br \/>\n&nbsp;&nbsp;&nbsp;&nbsp;}<br \/>\n}<br \/>\n<\/code><\/p>\n<p>To run this example, create a new C# console application. Save the template as a file named <strong>template.txt<\/strong> in the bin directory, and then run it. The example produces the following output:<\/p>\n<p><code>using&nbsp;System;<br \/>\nusing&nbsp;System.Collections.Generic;<br \/>\nusing&nbsp;System.Linq;<br \/>\nusing&nbsp;System.Text;<\/p>\n<p>namespace&nbsp;GenDocs<br \/>\n{<br \/>\n&nbsp;&nbsp;&nbsp;&nbsp;class&nbsp;Generator<br \/>\n&nbsp;&nbsp;&nbsp;&nbsp;{<br \/>\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;static&nbsp;void&nbsp;Main(string[]&nbsp;args)<br \/>\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{<br \/>\nConsole.WriteLine(\"Hello&nbsp;world\");<br \/>\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br \/>\n&nbsp;&nbsp;&nbsp;&nbsp;}<br \/>\n}<br \/>\n<\/code><\/p>\n","protected":false},"excerpt":{"rendered":"<p>In the last post, I explored Text Templates (T4), and determined that using T4 text templates for my code generation needs would add complexity and not yield sufficient ROI (although I did determine that a doc gen example using T4 is interesting in its own right). However, my exploration into T4 text templates yielded one [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_bbp_topic_count":0,"_bbp_reply_count":0,"_bbp_total_topic_count":0,"_bbp_total_reply_count":0,"_bbp_voice_count":0,"_bbp_anonymous_reply_count":0,"_bbp_topic_count_hidden":0,"_bbp_reply_count_hidden":0,"_bbp_forum_subforum_count":0,"_s2mail":"","footnotes":""},"categories":[7,3,5],"tags":[],"class_list":["post-260","post","type-post","status-publish","format-standard","hentry","category-document-generation-series","category-open-xml","category-wordprocessingml"],"_links":{"self":[{"href":"https:\/\/www.ericwhite.com\/blog\/wp-json\/wp\/v2\/posts\/260","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.ericwhite.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.ericwhite.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.ericwhite.com\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.ericwhite.com\/blog\/wp-json\/wp\/v2\/comments?post=260"}],"version-history":[{"count":4,"href":"https:\/\/www.ericwhite.com\/blog\/wp-json\/wp\/v2\/posts\/260\/revisions"}],"predecessor-version":[{"id":264,"href":"https:\/\/www.ericwhite.com\/blog\/wp-json\/wp\/v2\/posts\/260\/revisions\/264"}],"wp:attachment":[{"href":"https:\/\/www.ericwhite.com\/blog\/wp-json\/wp\/v2\/media?parent=260"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.ericwhite.com\/blog\/wp-json\/wp\/v2\/categories?post=260"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.ericwhite.com\/blog\/wp-json\/wp\/v2\/tags?post=260"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}