Code Templates and snippets
Introduction
We use Code templates for the code generation. This allows us to write common code with placeholders. It should be easy to read and maintain, and avoids writing code several times.
We use code templates for the Object Relational Mapping (ORM), ie. for the typed data tables and typed datasets. This code was previously generated with CodeDOM, but that got too complicated. The alternative would have been to have the source in strings in the generator code, but that is hard to read and to maintain.
We also use code templates for the Winforms generation, for the same reasons.
It might help to have a look at the template files, they are in csharp/ICT/PetraTools/Templates (on Git).
Concepts
Placeholders
A placeholder is always with curly brackets, with a hash sign (#) and the name in capital letters.
eg. this is a place holder: {#NAMESPACE}
Now in the code generator, we call FTemplate.ReplacePlaceHolder("NAMESPACE", FCodeStorage.FNamespace);
.
You could also call FTemplate.SetCodelet("NAMESPACE", FCodeStorage.FNamespace);
, it has the same effect, but ReplacePlaceHolder allows only one replacement, while SetCodelet overwrites previous assignments.
Alternatively, we can add several lines in a reserved place: eg.
private void ShowData() { FPetraUtilsObject.DisableDataChangedEvent(); {#SHOWDATA} [...] }
In the code generator, you will find the line:
FTemplate.AddToCodelet("SHOWDATA", "ShowDataManual();" + Environment.NewLine);
Note that all lines inserted into a place holder will have the same identation as the place holder had.
You can also clear a place holder, so that it will not appear in the result at all:
FTemplate.SetCodelet(String.Empty);
Snippets
Some code is needed several times inside a file. It has to be inserted by the generator.
The snippets are written at the end of a template file.
A snippet starts with a tag with two hash values (eg. {##SHOWDATAFORCOLUMN}), and ends when a new snippet starts or at the bottom of the file.
example:
{##SHOWDATAFORCOLUMN} {#IFDEF NOTDEFAULTTABLE} {#IFDEF CANBENULL} if ({#NOTDEFAULTTABLE} == null || {#NOTDEFAULTTABLE}[0].Is{#COLUMNNAME}Null()) { {#SETNULLVALUE} } else [...]
In the generator, you have to create a new Template for the snippet:
ProcessTemplate snippetShowData = writer.Template.GetSnippet("SHOWDATAFORCOLUMN");
You will insert the placeholders in that snippet, eg.:
snippetShowData.SetCodelet("CANBENULL", !AField.bNotNull ? "yes" : ""); snippetShowData.SetCodelet("DETERMINECONTROLISNULL", this.GetControlValue(ctrl, null));
And then insert the snippet into the main template, into a placeholder:
writer.Template.InsertSnippet("SHOWDETAILS", snippetShowData);
INCLUDE
To avoid too much duplicate code, you can write template code once into one file, and include that template file into other template files.
The syntax is:
{#INCLUDE copyvalues.cs}
This is used for most of the Winforms templates to include commonly used snippets.
IFDEF/IFNDEF
To generate code differently depending for several situations, you can create sections that will be enabled or disabled depending whether the referenced placeholder is empty or has a value.
Please note that {#IFDEF}, {#IFNDEF} and the closing tags {#ENDIF} and {#ENDIFN} need to have no leading nor trailing spaces.
a few examples:
{#IFDEF SAVEDETAILS} // get the details from the previously selected row if (FPreviouslySelectedDetailRow != null) { GetDetailsFromControls(FPreviouslySelectedDetailRow); } {#ENDIF SAVEDETAILS}
{#IFNDEF CANBENULL} {#SETCONTROLVALUE} {#ENDIFN CANBENULL}