Ader Template Engine






4.92/5 (24 votes)
Aug 31, 2004
6 min read

173712

916
Library for generating text output from source template and input parameters.
Introduction
Ader TemplateEngine is a .NET class library (written in C#) for generating text output from source template and input parameters. It can be used in many scenarios: website page building, email generation, XML generation, source code generation, etc. Its idea is based on Antlr stringTemplate
, but the syntax is based on ColdFusion language.
Currently, .NET Ader TemplateEngine works with .NET 2.0 only, but I have plans on making it work with .NET 1.0/1.1 and maybe Java. Ader TemplateEngine is released under GNU General Public License.
Here is a very simple template:
Thank You for your order #order.billFirstName# #order.billLastName#.
<br>
Your Order Total is: #format(order.total, "C")#
The templates can have expressions, if
/elseif
/else
statements, foreach
statement and other templates.
Templates API:
There are two classes mainly used in Template Engine: Template
and TemplateManager
. Template
holds a single instance of a template and TemplateManager
is used for executing templates. Easiest way of creating templates is by using static methods of Template
or TemplateManager
:
Template template = Template.FromString(string name, string data);
Template template = Template.FromFile(string name, string filename);
Then you use it to instantiate TemplateManager
:
TemplateManager mngr = new TemplateManager(template);
or even easier:
TemplateManager mngr = TemplateManager.FromFile(filename);
TemplateManager mngr = TemplateManager.FromString(template);
When using FromString
method, the string passed contains template code. This method can be used to dynamically generate text without having templates in files. You use SetValue(string name, object value);
to add values that can be used within the templates.
E.g.:
mngr.SetValue("customer", new Customer("Tom", "Jackson"));
Then you can refer to customer
within the template. You can use any type of object for value. When the value of variable is to be output, ToString()
method will be called.
Expressions
Expressions are enclosed with #
(hash or pound) characters:
Example:
#firstName#
This example will output value of first name. If you need to output #
character, just escape it with another #
.
Example:
Your SS## is #ssnumber#
Inside of expression block, you can output any variable:
#somevar#
Access property of a variable:
#somestring.Length#
Property name is not case sensitive. So you can call: #string.length#
or #string.LENGTH#
. Or call a function:
#trim(somename)#
There are several built in functions, and additional functions can be easily added. The built in functions are:
equals(obj1, obj2)
- invokesequals
method onobj1
withobj2
as parameter. Returns boolean value.notequals(obj1, obj2)
- Returns!equals(obj1, obj2)
. Is equivalent to calling:not(equals(obj1, obj2))
.iseven(num)
- tests whether number is an even number.isodd(num)
- tests whether number is an odd number.isempty(string)
- tests whether string has 0 characters. Same asequals(string.Length, 0)
.isnotempty(string)
- tests whether string has at least 1 character.isnumber(num)
- tests whethernum
is of numeric type.toupper(string)
- convertsstring
to upper case.tolower(string)
- convertsstring
to lower case.isdefined(varname)
- tests whether variable namedvarname
is defined.ifdefined(varname, value)
- returns value ifvarname
is defined. Especially useful:#ifdefined("name", name)#
- will output value ofname
if it's defined, otherwise will output nothing.len(string)
- returns length ofstring
.tolist(collection, property, delim)
- will convertcollection
to string withdelim
as separator. If you passproperty
, the value of theproperty
will be evaluated on each element of the collection. If you omitproperty
, then the object itself will be used.Example: suppose you have list as:
ArrayList list = new ArrayList(); list.Add("one"); list.Add("two"); list.Add("three"); template.SetValue("mylist", list);
Then in your template:
#toList(mylist, " & ")#
the output will be: one & two & three
Suppose you have list as:
list.Add(new Customer("Tom", "Whatever")); list.Add(new Customer("Henry", "III")); list.Add(new Customer("Tom", "Jackson")); template.SetValue("mylist", list);
Then in template:
#toList(mylist, "firstName", ",")#
The output will be: Tom,Henry,Tom.
isnull(obj)
- tests whetherobj
isnull
.not(boolvalue)
- returns not (!) of boolean value.iif(booleanExpression, iftruevalue, iffalsevalue)
- same asbooleanExpression ? iftruevalue : iffalsevalue
in C#.Example:
#iif(isodd(i), "bgcolor=yellow", "bgcolor=red")#
will output bgcolor=yellow if
i
is odd number, and bgcolor=red ifi
is not odd number.format(object, formatstring)
- will callToString(formatstring)
onobject
.object
has to implementIFormattable
interface, otherwiseToString()
will be called.Example: suppose
total
is decimal with value 1208.45:#format(total, "C")#
will output: $1,208.45
trim(string)
- will trimstring
objectfilter(collection, booleanproperty)
- will return new List fromcollection
for those objects whose boolean property property evaluates totrue
.gt(obj1, obj2)
- will returntrue
ifobj1
>obj2
. (obj1
andobj2
must implementIComparable
. All numeric types do).lt(obj1, obj2)
- will returntrue
ifobj1
<obj2
. (obj1
andobj2
must implementIComparable
. All numeric types do).compare(obj1, obj2)
- will return -1 ifobj1
<obj2
, 0 ifobj1
=obj2
, and 1 ifobj1
>obj2
. (obj1
andobj2
must implementIComparable
. All numeric types do).or(bool1, bool2)
- will returntrue
if eitherbool1
orbool2
aretrue
.Example:
#or(equals(state, "IL"), equals(state, "NY"))# - returns true if state is either IL or NY
and(bool1, bool2)
- will returntrue
if bothbool1
andbool2
aretrue
.comparenocase(string1, string2)
- will do case insensitive comparison ofstring1
andstring2
and returntrue
if they are equal.stripnewlines(string)
- will return all\r\n
instances and replace them with space.
Built In Tags:
IF
You can also conditionally output text based on some expression using special
if
tag:<ad:if test="#booleanexpression#"> <ad:elseif test="#bool#"> <ad:else> </ad:if>
elseif
andelse
are optional. If test of "if
" evaluates totrue
, then block inside of "if
" will be output, otherwiseelseif
will be tested (if exists) and thenelse
.Example:
<ad:if test="#equals(cust.country, "US"))#"> You are US customer. <ad:else> You are from: #cust.country# country. </ad:if>
If
cust.country
is "US" then the output will be: You are US customer.FOREACH
You can loop through collection of elements (any object that implements
IEnumerable
interface) usingFOREACH
tag.<ad:foreach collection="#collection#" var="cust" index="i"> #i#: #cust.lastname#, #cust.firstname# </ad:foreach>
Suppose
customers
is array of customer objects:customers = Customer("Tom", "Jackson") Customer("Mary", "Foo")
The output will be:
- Jackson, Tom
- Foo, Mary
During execution, variable name that is passed as
var
attribute will be assigned with element from the collection.index
attribute can be omitted, and is used to represent index variable for the loop. It starts with 1 and gets incremented with each iteration.
Custom Templates:
You can also create your own templates inside of template file, that you can call. You do that using template
tag:
<ad:template name="ShowCustomer">
#customer.lastname#, #customer.firstname#
</ad:template>
<ad:showcustomer customer="#cust#" />
You can pass any attribute to the template, and you can use those inside of the template. The template can also access all variables that are defined outside of the template. When calling template, you have to put trailing slash at the end, or put closing tag:
<ad:showcustomer />
or
<ad:showcustomer></ad:showcustomer>
The template also receives special variable: innerText
that is the content of executing the inner elements of calling template.
<ad:template name="bold">
<b>#innerText#</b>
</ad:template>
<ad:bold>#cust.lastname#, #cust.firstname#</ad:bold>
the output will be:
<b>Jackson, Tom</b>
(if customer is Tom Jackson)
You can also nest those:
<ad:template name="italic">#innerText#</ad:template>
<ad:bold><ad:italic>This will be bold and italic</ad:italic></ad:bold>
You can also invoke templates based on the name, using apply
tag:
<ad:apply template="#usetemplate#">this is content</ad:apply>
If usetemplate
is "bold", then "bold" template will be called.
Templates can be nested inside other templates:
<ad:template name="doit">
<ad:template name="colorme">
<font color=#color#>#innerText#</font>
</ad:template>
<ad:colorme color="blue">colorize me</ad:colorme>
</ad:template>
colorme
template can only be used within doit
template. Templates can also be added programmatically:
TemplateManager mngr = ...;
mngr.AddTemplate(Template.FromString("bold", "<b>#innerText#</b>"));
Now, bold
template can be used anywhere within processing. Here is a sample based on order confirmation:
class Order
{
string firstname, lastname, address1, city, state, zip, country;
public string Address1
{
get { return this.address1; }
}
public string City
{
get { return this.city; }
}
public string Country
{
get { return this.country; }
}
public string Firstname
{
get { return this.firstname; }
}
public string Lastname
{
get { return this.lastname; }
}
public string State
{
get { return this.state; }
}
public string Zip
{
get { return this.zip; }
}
}
Order order = GetOrder();
TemplateManager mngr = TemplateManager.FromFile("order-confirmation.st");
mngr.SetValue("order", order);
System.IO.StringWriter writer = new System.IO.StringWriter();
mngr.Process(writer);
string emailBody = writer.ToString();
order-confirmation.st
<ad:showitem>
#item.sku# - #item.name#<br>
<ad:if test="#equals(item.qty, 1)#">
Price: #format(item.price, "C")#<br>
<ad:else>
You bought #item.qty# items for #format(item.price, "C")#
(total: #format(item.total, "C")#)
</ad:if>
</ad:showitem>
#order.firstname# #order.lastname#<br>
#order.address1#<br>
<ad:if test="#isnotempty(order.address2)#">#order.address2#<br></ad:if>
#order.city#, #order.zip# #order.state#
<br>
<table>
<ad:foreach collection="#order.orderitems#" var="orderitem" index="i">
<tr>
<td>#i#.</td>
<td bgcolor="#iif(isodd(i), "##DEDEDE", "white")#">
<ad:showitem item="#orderitem#" />
</td>
</tr>
</ad:foreach>
</table>
Shipping: #format(order.shipping, "C")#<br>
Taxes: #format(order.tax, "C")#<br>
Order Total: #format(order.total, "C")#<br>
Description of order-confirmation.st:
First, showitem
template is defined which shows a single line item of the order. item
is passed as attribute to showitem
. Then address is shown. Note how if
is used to conditionally display second line of address with ending <br> tag. Then each line item of order is looped through using ad:foreach
tag. iif
function is used to color every other line with #DEDEDE color.
Example #2 for constructing complex SQL queries:
string[] cols = new string[]{"id", "name", "email"};
TemplateManager mngr = TemplateManager.FromFile(file);
mngr.SetValue("colums", cols);
mngr.SetValue("tablename", "customer");
string query = mngr.Process();
And the template file is:
select #toList(columns, ",")# from #tablename#
Example 1 project has two sample templates that are used to process the same data. First, it outputs it as a C# class to the screen, then it uses HTML template to create HTML file.
If you have any questions, you can use AderTemplates forums.