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
*#

Case Substitution

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