This page provides a quick reference of the FreeMarker language
 

1 Variables

 

1.1 Top-level variables

Accessing top-level variables (those not defined in the template) is achieved in FreeMarker by just writing the variable name between brackets and with a dollar sign at the start, for example : ${variable}. If the top-level variable is not defined, FreeMarker will return an error and abort the template processing. Also, the name of a variable can only contain letters, digits, underline (_), dollar sign ($), at sign (@) and hash (#) and must not start with a digit.

 

1.2 'Plain' variables

In FreeMarker it is possible to define new variables within the template. This is achieved with the assign directive :

Example
Copy
<#assign x = 1>
${x}
<#assign x = 3*x>
${x}

The output will be

Copy
1
3
 

1.3 Special variables

Special variables are variables defined by the FreeMarker engine itself and give general information about the FreeMarker instance. A full list of the FreeMarker's special variables can be found at : http://docs.huihoo.com/freemarker/2.3.20/ref_specvar.html

 

2 Strings

String values can be defined between either quotation marks (") or apostrophe-quotes ('). There are some special characters that need to be escaped in order to be part of the string. An exception to the list of escapable characters is the dollar sign ($), which requires a special type of escaping obtained by prepending an r letter before the start of the string.

Example
Copy
${"Hello ${user}"}
${"I can escape with \\ ${user}"}
${r"Now I can read dollar signs $"}

The output will be

Copy
Hello deister
I can escape with \ deister
Now I can read dollar signs $
 

2.1 String operations

Strings can be concatenated with top-level variables or other strings just by connecting them with the plus sign or just by simply writing the top-level variable inside curly brackets and with a dollar sign at the start. Accessing a single character of a string is achieved by simply writing the index of the character between brackets as if the string was a sequence.

Example
Copy
${"Hello" + user}
${"Hello" + "World"}
${user[0]}
${user[0..4]}

This will output

Copy
Hellodeister
HelloWorld
d
deist
 

3 Numbers and booleans

 

3.1 Numbers

Numerical values are defined just by typing in the number without quotation marks. Plus (+) and minus (-) symbols are accepted at the beginning of the number, and the decimal separator is defined with a point (.)

Example
Copy
${-3} ${+4} ${-5.00} ${006} ${3.1415}
This will output
Copy
-3 4 -5 6 3.1415
 

3.2 Booleans and logical operations

Boolean types are defined by writing 'false' or 'true', without quotation marks

As for the logical operations, the equal operator is simply '=' (or '==' in java / C), while not equal is '!='. For numbers, we also have '<=' and '=>' . Finally, the logical expressions 'and' and 'or' are simply '&&' and '||' respectively

 

4 Sequences

Sequences are analogous to the classic concept of arrays, present in other programming languages. This way, sequences contain a list of subvalues which can be any of the other in this section (string, number, sequence, hash...). Its definition requires the subvariables to be put inside square brackets and this to be put inside a list element as follows : <#list ["Joe", "Fred","Julia", "Kate"] ></#list>. Also, items inside lists are technically expressions, so arithmetical or logic expressions will be evaluated if inserted in a sequence element.

Concatenation of sequences can be done with the same syntax that the concatenation of strings uses, that is, connect the sequence elements to concatenate with a plus sign (+). To access a single element of the sequence we use the index of the element between brackets. In case we want to access multiple elements of the sequence with a single statement, we can write the first and the last element separated by two single points.

Finally to run over each element of a sequence we must define a loop variable at the end of the declaration of the list that will store the current element of the sequence for the current iteration of the loop. For example :

Example
Copy
<#list ["Joe", "Fred"] + ["Julia", "Kate"] as user>
${user}
</#list>

The output will be

Copy
Joe
Fred
Julia
Kate
 

5 Hashes

Hashes are defined as key/value pairs similar to those of a map. Each key contains a single value, which can be any of the above (number, string, boolean...). The key must be written between quotation marks and followed by a colon and its value. Each key/value pair will be separated from the others by a comma. To access a hash value, we can either put a point followed by the key of the value (hash.key) or enter the key between brackets and quotation marks (hash["key"]). In addition, concatenation of hashes is structurally exact to sequence and string concatenation.

Example
Copy
<#assign ages = {"Joe":23, "Fred":25} + {"John":30, "Julia":18}>
Joe is ${ages.Joe}
Fred is ${ages.Fred}
Julia is ${ages.Julia}
John is ${ages["John"]}
The output will be
Copy
Joe is 23
Fred is 25
Julia is 18
John is 30
 

6 Built-ins

Built-ins are small functionalities that are usually applied to format variables. These are used after the variable to which they apply and a question mark, for example: var?builtin. Although the complete list of built-ins can be found at : http://docs.huihoo.com/freemarker/2.3.20/ref_builtins.html we'll review some of them in detail.

 

6.1 String Built-ins

  1. cap_first: Returns the string with the first letter converted to upper case

  2. lower_case / upper_case : Returns the string converted to upper/lower case

  3. trim : Returns the string without any leading and trailing white-spaces.

Example
Copy
<#assign company=" deister ">
${company?cap_first}
${company?lower_case}
${company?upper_case}
${company?trim}

The output will be

Copy
Deister 
 deister 
 DEISTER 
deister
 

6.2 Sequence Built-ins

  1. size : Returns the number of elements in the sequence

  2. seq_contains : Checks whether sequence contains specified element or not

Example
Copy
<#assign friends=["Joe", "Fred","Julia", "Kate"]>
${friends?size}
${friends?seq_contains("Kate")?string("yes", "no")}

The output will be

Copy
4
yes
 

6.3 Number Built-ins

  1. c : Converts the number to string for a computer language

  2. floor / ceiling : Floor rounds the number downwards and ceiling rounds the number upwards.

Example
Copy
<#assign x=42.075>
${x?c}
${x?floor}
${x?ceiling}

The output will be

Copy
42.075
42
43
 

7 FTLResultSets

Throughout axional Studio, data (resulting from a query for example) is usually presented as java RestultSets. ResultSets are json objects that most importantly have a rows property containing all the data and a metadata property containing information of the type of the data. It is convenient, then, to have some methods defined that allow us to process this type of information. The most important methods available are:

  1. getRows() : Returns the rows property of the resultSet.
  2. getCols() : Returns the metadata (columns) property of the resultset.
  3. getHeaders() : Returns the columns headers if any. (hasHeaders() returns true or false if they exist or not).
  4. hasHeaders() : Returns whether the resultSet has headers or not(
  5. getTitle() : Returns the title of the resultSet, if any.
  6. col(resultset, columnIndex) : Returns the column object with matching with columnIndex.

  7. isNullable() : Returns whether the column can be of value null

  8. isRightAligned() : Returns whether the columns has its content right-aligned or not for display

  9. getColumnName() : Returns the name of the column.

  10. getColumnLabel() : Returns the label of the column.

  11. getColumnType() : Returns the type of the column.

  12. getColumnStyle() : Returns the style of the columns, including font color, background color...

  13. getColumnsHref() : Returns the hyperlink, if any, related to the column.

Most of the time, we would only want to run over all the data (rows) of the resultset. This can be achieved with the following example :

Example

Considering the minimal resultset :

Copy
{
  "message": null,
  "stackTrace": null,
  "type": "SELECT",
  "time": 3,
  "errorCode": 0,
  "SQLState": null,
  "user": "deister",
  "dbms": "demo_ftl_resultset",
  "serial": 0,
  "serial4": 0,
  "serial8": 0,
  "warnings": {
    "message": null,
    "stackTrace": ""
  },
  "sql": "select * from demo_ftl_resultset",
  "stdOut": null,
  "stdErr": null,
  "count": 0,
  "resultSet": {
    "rowset": [
      ["IL", "Peoria",  8700.0],
      ["MA", "Boston", 6900.0],
      ["MD", "Rockville",  92970.0],
      ["NC", "Morrisville", 16500.0],
      ["NC", "Winston-Salem", 19200.0],
      ["WA", "Seattle", 26100.0]
    ],
    "total": 6,
    "maxrows": -1,
    "metadata": [{
      "columnName": "state",
      "catalogName": " ",
      "columnClassName": "java.lang.String",
      "columnDisplaySize": 30,
      "columnViewSize": 2,
      "columnLabel": "state",
      "columnType": 12,
      "columnTypeName": "varchar",
      "precision": 30,
      "scale": 0,
      "schemaName": " ",
      "tableName": "demo_ftl_resultset",
      "isNullable": 1,
      "isRowid": false,
      "isNumeric": false
    }, {
      "columnName": "city",
      "catalogName": " ",
      "columnClassName": "java.lang.String",
      "columnDisplaySize": 30,
      "columnViewSize": 18,
      "columnLabel": "city",
      "columnType": 12,
      "columnTypeName": "varchar",
      "precision": 30,
      "scale": 0,
      "schemaName": " ",
      "tableName": "demo_ftl_resultset",
      "isNullable": 1,
      "isRowid": false,
      "isNumeric": false
    }, {
      "columnName": "value",
      "catalogName": " ",
      "columnClassName": "java.math.BigDecimal",
      "columnDisplaySize": 32,
      "columnViewSize": 8,
      "columnLabel": "value",
      "columnType": 3,
      "columnTypeName": "decimal",
      "precision": 32,
      "scale": 0,
      "schemaName": " ",
      "tableName": "demo_ftl_resultset",
      "isNullable": 1,
      "isRowid": false,
      "isNumeric": true
    }],
    "attributes": {}
  }
}

Let's say we want to build a table with the data in it. We can do so with the following FTL :

Copy
<#list resultSet.getRows() as row>
  <tr>
   <#list row.getCols() as col>
     <td>
       ${row[col.getColumnName()]}
     </td>
   </#list>
  </tr>
</#list>

We would obtain

Copy
<tr>
    <td>IL</td><td>Peoria</td><td>8700.0</td>
</tr>
<tr>
    <td>MA</td><td>Boston</td><td>6900.0</td>
</tr>
<tr>
    <td>MD</td><td>Rockville</td><td>92970.0</td>
</tr>
<tr>
    <td>NC</td><td>Morrisville</td><td>16500.0</td>
</tr>
<tr>
    <td>NC</td><td>Winston-Salem</td><td>19200.0</td>
</tr>
<tr>
    <td>WA</td><td>Seattle</td><td>26100.0</td>
</tr>
 

8 Handling missing values

An error will occur and abort the template processing if you try to access a missing variable. However two special operators can suppress this error, and handle the problematic situation. The handled variable can be top-level variable, hash subvariable, or sequence subvariable as well. Furthermore these operators handle the situation when a method call doesn't return a value (from the viewpoint of Java programmers: it returns null or it's return type is void), so it's more correct to say that these operators handle missing values in general, rather than just missing variables.

For those who know what's Java null, FreeMarker 2.3.x treats them as missing values. Simply, the template language doesn't know the concept of null. For example, if you have a bean that has a maidenName property, and the value of that property is null, then that's the same as if there were no such property at all, as far as the template is concerned (assuming you didn't configured FreeMarker to use some extreme object wrapper, that is). The result of a method call that returns null is also treated as a missing variable (again, assuming that you use some usual object wrapper).

 

8.1 Default value operator

Synopsis: unsafe_expr!default_expr or unsafe_expr! or (unsafe_expr)!default_expr or (unsafe_expr)!

This operator allows you to specify a default value for the case when the value is missing.

The default value can be any kind of expression, so it doesn't have to be a string. For example you can write hits!0 or colors!["red", "green", "blue"]. There is no restriction regarding the complexity of the expression that specifies the default value, for example you can write: cargo.weight!(item.weight * itemCount + 10).

Example

Assume no variable called mouse is present:

Copy
${mouse!"No mouse."}
<#assign mouse="Jerry">
${mouse!"No mouse."}

The output will be

Copy
No mouse.
Jerry
 

8.2 Missing value test operator

Synopsis: unsafe_expr?? or (unsafe_expr)??