Automation Syntax
Comments
Comments allows descriptive text to be included that is not placed into the output of the Automation Engine. Comments are a useful way of reminding yourself and explaining to others what your Automation Template statements are doing, or any other purpose you find useful. Below is an example of a comment in the Automation Template.
## This is a single line comment.
A single line comment begins with ## and finishes at the end of the line. If you're going to write a few lines of commentary, there's no need to have numerous single line comments. Multi-line comments, which begin with # and end with #, are available to handle this scenario.
This is text that is outside the multi-line comment. Online visitors can see it. #* Thus begins a multi-line comment. Online visitors won't see this text because the Templating Engine will ignore it. *# Here is text outside the multi-line comment; it is visible.
Here are a few examples to clarify how single line and multi-line comments work:
This text is visible. ## This text is not. This text is visible. This text is visible. #* This text, as part of a multi-line comment, is not visible. This text is not visible; it is also part of the multi-line comment. This text still not visible. *# This text is outside the comment, so it is visible. ## This text is not visible.
There is a third type of comment, the Automation Template comment block, which may be used to store any sort of extra information you want to track in the template (e.g. javadoc-style author and versioning information):
#** This is a comment block and may be used to store such information as the document author and versioning information: @author John Doe @version 5 *#
References
There are three types of references: variables, properties and methods. As a designer, you and your engineers must come to an agreement on the specific names of references so you can use them correctly in your templates.
Variables
The shorthand notation of a variable consists of a leading "$" character followed by an Identifier. An Identifier must start with an alphabetic character (a .. z or A .. Z). The rest of the characters are limited to the following types of characters:
- alphabetic (a .. z, A .. Z)
- numeric (0 .. 9)
- hyphen ("-")
- underscore ("_")
Here are some examples of valid variable references:
$foo $mudSlinger $mud-slinger $mud_slinger $mudSlinger1
When you reference a variable, such as $foo, the variable can get its value from either a set directive in the template, or from the Java code. For example, if the Java variable $foo has the value bar at the time the template is requested, bar replaces all instances of $foo on the web page. Alternatively, if I include the statement
#set( $foo = "bar" )
The output will be the same for all instances of $foo that follow this directive.
Properties
The second flavor references are properties, and properties have a distinctive format. The shorthand notation consists of a leading $ character followed an Identifier, followed by a dot character (".") and another Identifier. These are examples of valid property references:
$customer.Address $purchase.Total
Take the first example, $customer.Address. It can have two meanings. It can mean, Look in the hashtable identified as customer and return the value associated with the key Address. But $customer.Address can also be referring to a method (references that refer to methods will be discussed in the next section); $customer.Address could be an abbreviated way of writing $customer.getAddress(). When your code is requested, the Automation will determine which of these two possibilities makes sense, and then return the appropriate value.
Methods
A method is defined in the Java code and is capable of doing something useful, like running a calculation or arriving at a decision. Methods are references that consist of a leading "$" character followed an Identifier, followed by a Method Body. A Method Body consists of an Identifier followed by an left parenthesis character ("("), followed by an optional parameter list, followed by right parenthesis character (")"). These are examples of valid method references:
$customer.getAddress() $purchase.getTotal() $page.setTitle( "My Home Page" ) $person.setAttributes( ["Strange", "Weird", "Excited"] )
The first two examples -- $customer.getAddress() and $purchase.getTotal() -- may look similar to those used in the Properties section above, $customer.Address and $purchase.Total. If you guessed that these examples must be related some in some fashion, you are correct!
Properties can be used as a shorthand notation for Methods. The Property $customer.Address has the exact same effect as using the Method $customer.getAddress(). It is generally preferable to use a Property when available. The main difference between Properties and Methods is that you can specify a parameter list to a Method.
The shorthand notation can be used for the following Methods
$sun.getPlanets() $annelid.getDirt() $album.getPhoto()
We might expect these methods to return the names of planets belonging to the sun, feed our earthworm, or get a photograph from an album. Only the long notation works for the following Methods.
$sun.getPlanet( ["Earth", "Mars", "Neptune"] ) ## Can't pass a parameter list with $sun.Planets $sisyphus.pushRock() ## Automation assumes I mean $sisyphus.getRock() $book.setTitle( "Homage to Catalonia" ) ## Can't pass a parameter
All array references are treated as if they are fixed-length lists. This means that you can call java.util.List methods on array references. So, if you have a reference to an array (let's say this one is a String[] with three values), you can do:
$myarray.isEmpty() $myarray.size() $myarray.get(2) $myarray.set(1, 'test')
Now that you are familiar with references, you can begin to apply them effectively in your templates. References take advantage of some Java principles that template designers will find easy to use. For example:
$foo $foo.getBar() ## is the same as $foo.Bar $data.setUser("jon") ## is the same as #set( $data.User = "jon" ) $data.getRequest().getServerName() ## is the same as $data.Request.ServerName ## is the same as ${data.Request.ServerName}
These examples illustrate alternative uses for the same references. The Automation Engine takes advantage of Java's introspection and bean features to resolve the reference names to both objects in the Context as well as the objects methods. It is possible to embed and evaluate references almost anywhere in your template.
The Automation Engine is case sensitive; however, its developers have strove to catch and correct user errors wherever possible. When the method getFoo() is referred to in a template by $bar.foo
, the engine will first try $getfoo
. If this fails, it will then try $getFoo
. Similarly, when a template refers to $bar.Foo
, the engine will try $getFoo() first and then try getfoo().
Note: References to instance variables in a template are not resolved. Only references to the attribute equivalents of JavaBean getter/setter methods are resolved (i.e. $foo.Name
does resolve to the class Foo's getName()
instance method, but not to a public Name
instance variable of Foo).
Directives
References allow template designers to generate dynamic content for web sites, while directives -- easy to use script elements that can be used to creatively manipulate the output of Java code -- permit web designers to truly take charge of the appearance and content of the web site.
Directives always begin with a #
. Like references, the name of the directive may be bracketed by a {
and a }
symbol. This is useful with directives that are immediately followed by text. For example the following produces an error:
#if($a==1)true enough#elseno way!#end
In such a case, use the brackets to separate #else
from the rest of the line.
#if($a==1)true enough#{else}no way!#end
Set
The #set directive is used for setting the value of a reference. A value can be assigned to either a variable reference or a property reference, and this occurs in brackets, as demonstrated:
#set( $primate = "monkey" ) #set( $customer.Behavior = $primate )
The left hand side (LHS) of the assignment must be a variable reference or a property reference. The right hand side (RHS) can be one of the following types:
- Variable reference
- String literal
- Property reference
- Method reference
- Number literal
- ArrayList
- Map
These examples demonstrate each of the aforementioned types:
#set( $monkey = $bill ) ## variable reference #set( $monkey.Friend = "monica" ) ## string literal #set( $monkey.Blame = $whitehouse.Leak ) ## property reference #set( $monkey.Plan = $spindoctor.weave($web) ) ## method reference #set( $monkey.Number = 123 ) ##number literal #set( $monkey.Say = ["Not", $my, "fault"] ) ## ArrayList #set( $monkey.Map = {"banana" : "good", "roast beef" : "bad"}) ## Map
NOTE: For the ArrayList example the elements defined with the [..] operator are accessible using the methods defined in the ArrayList class. So, for example, you could access the first element above using $monkey.Say.get(0).
Similarly, for the Map example, the elements defined within the { } operator are accessible using the methods defined in the Map class. So, for example, you could access the first element above using $monkey.Map.get("banana") to return a String 'good', or even $monkey.Map.banana to return the same value.
The RHS can also be a simple arithmetic expression:
#set( $value = $foo + 1 ) #set( $value = $bar - 1 ) #set( $value = $foo * $bar ) #set( $value = $foo / $bar )
If the RHS is a property or method reference that evaluates to null, it will not be assigned to the LHS. It is usually not possible to remove an existing reference from the context via this mechanism. This can be confusing for newcomers. For example:
#set( $result = $query.criteria("name") ) The result of the first query is $result #set( $result = $query.criteria("address") ) The result of the second query is $result
If $query.criteria("name") returns the string "bill", and $query.criteria("address") returns null, the above syntax will render as the following:
The result of the first query is bill The result of the second query is bill
This tends to confuse newcomers who construct #foreach loops that attempt to #set a reference via a property or method reference, then immediately test that reference with an #if directive. For example:
#set( $criteria = ["name", "address"] ) #foreach( $criterion in $criteria ) #set( $result = $query.criteria($criterion) ) #if( $result ) Query was successful #end #end
In the above example, it would not be wise to rely on the evaluation of $result to determine if a query was successful. After $result has been #set (added to the context), it cannot be set back to null (removed from the context). The details of the #if and #foreach directives are covered later in this document.
One solution to this would be to pre-set $result to false. Then if the $query.criteria() call fails, you can check.
#set( $criteria = ["name", "address"] ) #foreach( $criterion in $criteria ) #set( $result = false ) #set( $result = $query.criteria($criterion) ) #if( $result ) Query was successful #end #end
The #set directive does not have an #end statement.
Literals
When using the #set directive, string literals that are enclosed in double quote characters will be parsed and rendered, as shown:
#set( $directoryRoot = "www" ) #set( $templateName = "index.vm" ) #set( $template = "$directoryRoot/$templateName" ) $template
The output will be
www/index.vm
However, when the string literal is enclosed in single quote characters, it will not be parsed:
#set( $foo = "bar" ) $foo #set( $blargh = '$foo' ) $blargh
This renders as:
bar $foo
By default, this feature of using single quotes to render unparsed text is available.
Alternately, the #[[don't parse me!]]# syntax allows the template designer to easily use large chunks of uninterpreted and unparsed content in Automation Template code. This can be especially useful in place of escaping multiple directives or escaping sections which have content that would otherwise be invalid (and thus unparseable) Automation Template.
#[[ #foreach ($woogie in $boogie) nothing will happen to $woogie #end ]]#
Renders as:
#foreach ($woogie in $boogie) nothing will happen to $woogie #end
Conditionals
If / ElseIf / Else
The #if directive allows for text to be included when the web page is generated, on the conditional that the if statement is true. For example:
#if( $foo ) <strong>Hello!</strong> #end
The variable $foo is evaluated to determine whether it is true, which will happen under one of two circumstances: (i) $foo is a boolean (true/false) which has a true value, or (ii) the value is not null. Remember that the Autometion Engine context only contains Objects, so when we say 'boolean', it will be represented as a Boolean (the class). This is true even for methods that return boolean
- the introspection infrastructure will return a Boolean
of the same logical value.
The content between the #if and the #end statements become the output if the evaluation is true. In this case, if $foo is true, the output will be: "Hello!". Conversely, if $foo has a null value, or if it is a boolean false, the statement evaluates as false, and there is no output.
An #elseif or #else element can be used with an #if element. Note that the Automation Engine will stop at the first expression that is found to be true. In the following example, suppose that $foo has a value of 15 and $bar has a value of 6.
#if( $foo < 10 ) **Go North** #elseif( $foo == 10 ) **Go East** #elseif( $bar == 6 ) **Go South** #else **Go West** #end
In this example, $foo is greater than 10, so the first two comparisons fail. Next $bar is compared to 6, which is true, so the output is Go South.
Relational and Logical Operators
The Automation Engine uses the equivalent operator to determine the relationships between variables. Here is a simple example to illustrate how the equivalent operator is used.
#set ($foo = "deoxyribonucleic acid") #set ($bar = "ribonucleic acid") #if ($foo == $bar) In this case it's clear they aren't equivalent. So... #else They are not equivalent and this will be the output. #end
Note that the semantics of == are slightly different than Java where == can only be used to test object equality. In the Automation Syntax the equivalent operator can be used to directly compare numbers, strings, or objects. When the objects are of different classes, the string representations are obtained by calling toString()
for each object and then compared.
The Authomation Syntax has logical AND, OR and NOT operators as well. Below are examples demonstrating the use of the logical AND, OR and NOT operators.
## logical AND #if( $foo && $bar ) ** This AND that** #end
The #if() directive will only evaluate to true if both $foo and $bar are true. If $foo is false, the expression will evaluate to false; $bar will not be evaluated. If $foo is true, the Automation Engine will then check the value of $bar; if $bar is true, then the entire expression is true and This AND that becomes the output. If $bar is false, then there will be no output as the entire expression is false.
Logical OR operators work the same way, except only one of the references need evaluate to true in order for the entire expression to be considered true. Consider the following example.
## logical OR #if( $foo || $bar ) **This OR That** #end
If $foo is true, the Automation Engine has no need to look at $bar; whether $bar is true or false, the expression will be true, and This OR That will be output. If $foo is false, however, $bar must be checked. In this case, if $bar is also false, the expression evaluates to false and there is no output. On the other hand, if $bar is true, then the entire expression is true, and the output is This OR That
With logical NOT operators, there is only one argument :
##logical NOT #if( !$foo ) **NOT that** #end
Here, the if $foo is true, then !$foo evaluates to false, and there is no output. If $foo is false, then !$foo evaluates to true and NOT that will be output. Be careful not to confuse this with the quiet reference $!foo which is something altogether different.
There are text versions of all logical operators, including eq, ne, and, or, not, gt, ge, lt, and le.
One more useful note. When you wish to include text immediately following a #else directive you will need to use curly brackets immediately surrounding the directive to differentiate it from the following text. (Any directive can be delimited by curly brackets, although this is most useful for #else).
#if( $foo == $bar)it's true!#{else}it's not!#end
Loops
Foreach Loop
The #foreach element allows for looping. For example:
<ul> #foreach( $product in $allProducts ) <li>$product</li> #end </ul>
This #foreach loop causes the $allProducts list (the object) to be looped over for all of the products (targets) in the list. Each time through the loop, the value from $allProducts is placed into the $product variable.
The contents of the $allProducts variable is a Vector, a Hashtable or an Array. The value assigned to the $product variable is a Java Object and can be referenced from a variable as such. For example, if $product was really a Product class in Java, its name could be retrieved by referencing the $product.Name method (ie: $Product.getName()).
Lets say that $allProducts is a Hashtable. If you wanted to retrieve the key values for the Hashtable as well as the objects within the Hashtable, you can use code like this:
<ul> #foreach( $key in $allProducts.keySet() ) <li>Key: $key -> Value: $allProducts.get($key)</li> #end </ul>
The Automation Syntax provides an easy way to get the loop counter so that you can do something like the following:
<table> #foreach( $customer in $customerList ) <tr><td>$foreach.count</td><td>$customer.Name</td></tr> #end </table>
The Automation Syntax also now provides an easy way to tell if you are on the last iteration of a loop:
#foreach( $customer in $customerList ) $customer.Name#if( $foreach.hasNext ),#end #end
If you want a zero-based index of the #foreach loop, you can just use $foreach.index instead of $foreach.count. Likewise, $foreach.first and $foreach.last are provided to compliment $foreach.hasNext. If you want to access these properties for an outer #foreach loop, you can reference them directly through the $foreach.parent or $foreach.topmost properties (e.g. $foreach.parent.index or $foreach.topmost.hasNext).
If you want to stop looping in a foreach from within your template, you can now use the #break directive to stop looping at any time:
## list first 5 customers only #foreach( $customer in $customerList ) #if( $foreach.count > 5 ) #break #end $customer.Name #end
Include
The #include script element allows the template designer to import a local file, which is then inserted into the location where the #include directive is defined. The contents of the file are not rendered through the Automation Engine. For security reasons, the file to be included may only be under TEMPLATE_ROOT.
#include( "one.txt" )
The file to which the #include directive refers is enclosed in quotes. If more than one file will be included, they should be separated by commas.
#include( "one.gif","two.txt","three.htm" )
The file being included need not be referenced by name; in fact, it is often preferable to use a variable instead of a filename. This could be useful for targeting output according to criteria determined when the page request is submitted. Here is an example showing both a filename and a variable.
#include( "greetings.txt", $seasonalstock )
Parse
The #parse script element allows the template designer to import a local file that contains Automation Syntax. The Automation Engine will parse the script and render the template specified.
#parse( "me.vm" )
Like the #include directive, #parse can take a variable rather than a template. Any templates to which #parse refers must be included under TEMPLATE_ROOT. Unlike the #include directive, #parse will only take a single argument.
Automation Engine templates can have #parse statements referring to templates that in turn have #parse statements. Recursion is permitted, for example, if the template dofoo.vm
contains the following lines:
Count down. #set( $count = 8 ) #parse( "parsefoo.vm" ) All done with dofoo.vm!
It would reference the template parsefoo.vm
, which might contain the following Automation Template:
$count #set( $count = $count - 1 ) #if( $count > 0 ) #parse( "parsefoo.vm" ) #else All done with parsefoo.vm! #end
After "Count down." is displayed, the Automation Engine passes through parsefoo.vm
, counting down from 8. When the count reaches 0, it will display the "All done with parsefoo.vm!" message. At this point, the Automation Engine will return to dofoo.vm
and output the "All done with dofoo.vm!" message.
Break
The #break directive stops any further rendering of the current execution scope. An "execution scope" is essentially any directive with content (i.e. #foreach, #parse, #evaluate, #define, #macro, or #@somebodymacro) or any "root" scope (i.e. template.merge(...). Unlike #stop, #break will only stop the innermost, immediate scope, not all of them.
If you wish to break out of a specific execution scope that is not necessarily the most immediate one, then you can pass the scope control reference (i.e. $foreach, $template, $evaluate, $define, $macro, or $somebodymacro) as an argument to #break. (e.g. #break($macro)). This will stop rendering of all scopes up to the specified one. When within nested scopes of the same type, remember that you can always access the parent(s) via $.parent or $.topmost and pass those to #break instead (e.g. #break($foreach.parent) or #break($macro.topmost)).
Stop
The #stop directive stops any further rendering and execution of the template. This is true even when the directive is nested within another template accessed through #parse or located in a macro. The resulting merged output will contain all the content up to the point the #stop directive was encountered. This is handy as an early exit from a template. For debugging purposes, you may provide a message argument (e.g. #stop('$foo was not in context') ) that will be written to the logs (DEBUG level, of course) upon completion of the stop command.
Evaluate
The #evaluate directive can be used to dynamically evaluate the Automation Template. This allows the template to evaluate a string that is created at render time. Such a string might be used to internationalize the template or to include parts of a template from a database.
The example below will display abc
.
#set($source1 = "abc") #set($select = "1") #set($dynamicsource = "$source$select") ## $dynamicsource is now the string '$source1' #evaluate($dynamicsource)
Define
The #define directive lets one assign a block of Automation Template to a reference.
The example below will display Hello World!
.
#define( $block )Hello $who#end #set( $who = 'World!' ) $block