Expressions
Page Contents
- Intro.
- Quick overview (cheat sheet)
- Specify values directly
- Retrieving variables
- String operations
- Sequence operations
- Hash operations
- Arithmetical calculations
- Comparison
- Logical operations
- Built-ins
- Method call
- Treating undefined variables and null values
- Parentheses
- White-space in expressions
- Operator precedence
When you supply values for interpolations or directive parameters you can use variables or more complex expressions. For example, if x is the number 8 and y is 5, the value of (x + y)/2 resolves to the numerical value 6.5.
Before we go into details, some concrete examples:
-
When you supply value for interpolations: The usage of interpolations is ${expression} where expression gives the value you want to insert into the output as text. So ${(5 + 8)/2} prints something like ''6.5'' to the output (if the language of your output is US English).
-
When you supply a value for the directive parameter: You have already seen the if directive in the Getting Started section. The syntax of this directive is: [#if expression]...[/#if]. The expression here must evaluate to a boolean value. For example in [#if 2 < 3] the 2 < 3 (2 is less than 3) is an expression which evaluates to true.
Quick overview (cheat sheet)
This is a reminder for those of you who already know FreeMarker or are just experienced programmers:
- Specify values directly
-
Retrieving
variables
- Top-level variables: user
- Retrieving data from a hash: user.name or user["name"]
- Retrieving data from a sequence: products[5]
- Special variables: .language, .version, etc.
-
String
operations
- Interpolation (or concatenation): "Hello ${user}!" or "Hello " + user + "!")
- Getting a character: name[0]
-
Sequence
operations
- Concatenation: users + ["guest"]
- Sequence slice: products[10..19] or products[5..]
-
Hash
operations
- Concatenation: passwords + {"joe": "secret42"}
- Arithmetical calculations: (x * 1.5 + 10) / 2 - y % 100
- Comparison: x == y, x != y, x < y, x > y, x >= y, x <= y, x < y, etc.
- Logical operations: !registered && (firstVisit || fromEurope)
- Built-ins: name?upper_case
- Method call: repeat("What", 3)
-
Treating
undefined variables and null values:
- Overview
- Default value: name!"unknown" or (user.name)!"unknown" or name! or (user.name)!
- Missing value test: name?? or (user.name)??
See also: Operator precedence
Specify values directly
Often you want to specify a value directly and not as a result of some calculations.
Strings
To specify a string value directly you give the text in quotation marks, e.g., "some text" or in apostrophe-quote, e.g., 'some text'. The two forms are equivalent. If the text itself contains the character used for the quoting (either " or ') or backslashes, you have to precede them with a backslash; this is called escaping. You can type any other character, including line breaks, in the text directly. Example:
| |||
will print:
| |||
Note
Of course, you could simply type the above text into the template, without using ${...}. But we do it here just for the sake of example, to demonstrate string literal expressions.
Below is the list of all supported escape sequences. All other usage of backlash in string literals is an error and any attempt to use the template will fail.
| Escape sequence | Meaning |
|---|---|
| \" | Quotation mark (u0022) |
| \' | Apostrophe (a.k.a. apostrophe-quote) (u0027) |
| \\ | Back slash (u005C) |
| \n | Line feed (u000A) |
| \r | Carriage return (u000D) |
| \t | Horizontal tabulation (a.k.a. tab) (u0009) |
| \b | Backspace (u0008) |
| \f | Form feed (u000C) |
| \l | Less-than sign: < |
| \g | Greater-than sign: > |
| \a | Ampersand: & |
| \xCode | Character given with its hexadecimal Unicode code (UCS code) |
The Code after the \x is 1 to 4 hexadecimal digits. For example this all put a copyright sign into the string: "\xA9 1999-2001", "\x0A9 1999-2001", "\x00A9 1999-2001". When the character directly after the last hexadecimal digit can be interpreted as hexadecimal digit, you must use all 4 digits or else FreeMarker will be confused.
Note that the character sequence ${ (and #{ for backward compatibility) has special meaning. It's used to insert the value of expressions (typically: the value of variables, as in "Hello ${user}!"); this will be explained later. If you want to print ${, you should use raw string literals as explained below.
A special kind of string literals is the raw string literals. In raw string literals, backslash and ${ (and #{) have no special meaning, they are considered as plain characters. To indicate that a string literal is a raw string literal, you have to put an r directly before the opening quotation mark or apostrophe-quote. Example:
| |||
will print:
| |||
Numbers
To specify a numerical value directly you type the number without quotation marks. You have to use the dot as your decimal separator and must not use any grouping separator symbols. You can use - or + to indicate the sign (+ is redundant). Scientific notation is not yet supported (so 1E3 is wrong). Also, you cannot omit the 0 before the decimal separator (so .5 is wrong).
Examples of valid number literals: 0.08, -5.013, 8, 008, 11, +11
Note that numerical literals like 08, +8, 8.00 and 8 are totally equivalent as they all symbolize the number eight. Thus, ${08}, ${+8}, ${8.00} and ${8} will all print exactly same. (Unlike in most programming languages, there is nothing like floating point versus integers either. Like both 1/2 and 1.0/2 is 0.5.)
Booleans
To specify a boolean value you write true or false. Don't use quotation marks.
Sequences
To specify a literal sequence, you list the subvariables separated by commas, and put the whole list into square brackets. For example:
| |||
will print:
| |||
The items in the list are expressions, so you can do this for example: [2 + 2, [1, 2, 3, 4], "whatnot"]. Here the first subvariable will be the number 4, the second will be another sequence, and the third subvariable will be the string "whatnot".
You can define sequences that store a numerical range with start..end, where start and end are expressions that resolve to numerical values. For example 2..5 is the same as [2, 3, 4, 5], but the former is much more efficient (occupies less memory and faster). Note that the square brackets are missing. You can define decreasing numerical ranges too, e.g.: 5..2. (Furthermore, you can omit the end, for example 5.., in which case the sequence will contain 5, 6, 7, 8, etc., up to the infinity.)
Hashes
To specify a hash in a template, you list the key/value pairs separated by commas, and put the list into curly brackets. The key and value within a key/value pair are separated with a colon. Here is an example: {"name": "green mouse", "price": 150}. Note that both the names and the values are expressions. However, the lookup names must be strings.
Null
Null is a value used in the Java language to indicate missing or undefined things. Although this concept is not needed for FTL, as it accesses Java object you will have to run into it sometimes. The FTL literal that symbolizes this value is null.
Retrieving variables
Top-level variables
To access a top-level variable, you simply use the variable name. For example, the expression user will evaluate to the value of variable stored with name ''user''. So this will print what you store there:
| |||
If there is no such top-level variable, then an error will result when FreeMarker tries to evaluate the expression, and it aborts template processing (unless programmers has configured FreeMarker differently).
In this expression the variable name can contain only letters (including non-Latin letters), digits (including non-Latin digits), underline (_), dollar ($), at sign (@) and hash (#). Furthermore, the name must not start with digit.
Retrieving data from a hash
If we already have a hash as a result of an expression, then we can get its subvariable with a dot and the name of the subvariable. Assume that we have this data-model:
| |||
Now we can read the title with book.title, since the book expression will return a hash (as explained in the last chapter). Applying this logic further, we can read the name of the author with this expression: book.author.name.
There is an alternative syntax if we want to give the subvariable name with an expression: book["title"]. In the square brackets you can give any expression as long as it evaluates to a string. So with this data-model you can also read the title with book[test]. More examples; these are all equivalent: book.author.name, book["author"].name, book.author.["name"], book["author"]["name"].
When you use the dot syntax, the same restrictions apply regarding the variable name as with top-level variables (name can contain only letters, digits, _, $, @, etc.). There are no such restrictions when you use the square bracket syntax, since the name is the result of an arbitrary expression. (Note, that to help the FreeMarker XML support, if the subvariable name is * (asterisk) or **, then you do not have to use square bracket syntax.)
As with the top-level variables, trying to access a non-existent subvariable causes an error and aborts the processing of the template (unless programmers has configured FreeMarker differently).
Retrieving data from a sequence
This is the same as for hashes, but you can use the square bracket syntax only, and the expression in the brackets must evaluate to a number, not a string. For example to get the name of the first animal of the example data-model (remember that the number of the first item is 0, not 1): animals[0].name
Special variables
Special variables are variables defined by the FreeMarker engine itself. To access them, you use the .variable_name syntax. For example, to print the version number of FreeMarker you write:
| |||
Normally you don't need to use special variables; they are mostly for expert users. The complete list of special variables can be found in the reference.
String operations
Interpolation (or concatenation)
If you want to insert the value of an expression into a string, you can use ${...} (and #{...}) in string literals. ${...} behaves similarly as in text sections (except that the escape directive has no effect on it). For example (assume that user is ''Big Joe''):
| |||
will print:
| |||
Alternatively, you can use the + operator to achieve similar result:
| |||
This will print the same as the example with the ${...}-s.
Warning!
A frequent mistake of users is the usage of interpolations in places where it shouldn't/can't be used. Interpolations work only in text sections (e.g., <h1>Hello ${name}!</h1>) and in string literals (e.g., [#embed "/footer/${company}.html"]). A typical bad usage is [#if ${isBig}]Wow![/#if], which is syntactically WRONG. You should simply write [#if isBig]Wow![/#if]. Also, [#if "${isBig}"]Wow![/#if] is WRONG, since this way the parameter value will be a string, and the if directive wants a boolean, so it will cause a runtime error.
Getting a character
You can get a single character of a string at a given index similarly as you can read the subvariable of a sequence, e.g., user[0]. The result will be a string whose length is 1; FTL doesn't have a separate character type. As with sequence subvariables, the index must be a number that is at least 0 and less than the length of the string, or else an error will abort the template processing.
Since the sequence subvariable syntax and the character getter syntax clashes, you can use the character getter syntax only if the variable is not a sequence as well (FTL supports multi-typed values), since in that case the sequence behavior prevails. (To work this around, you can use the string built-in, e.g., user?string[0]. Don't worry if you don't understand this yet; built-ins will be discussed later.)
Example (assume that user is ''Big Joe''):
| |||
will print (note that the index of the first character is 0):
| |||
Note
You can get a range of characters in the same way as you get a sequence slice, e.g ${user[1..4]} and ${user[4..]}. However, it's now deprecated to utilize this, and instead you should use the substring built-in; built-ins will be discussed later.
Sequence operations
Concatenation
You can concatenate sequences in the same way as strings, with +. Example:
| |||
will print:
| |||
Sequence concatenation is not to be used for many repeated concatenations, like for appending items to a sequence inside a loop. It's just for things like [#list users + admins + coadmins as person]. Although concatenating sequences is fast and its speed is independently of the size of the concatenated sequences, the resulting sequence will be always a little bit slower to read than the original two sequences were. This way the result of many repeated concatenations is a sequence that is too slow to read.
Sequence slice
With [firstindex..lastindex] you can get a slice of a sequence, where firstindex and lastindex are expressions evaluate to number. For example, if seq stores the sequence "a", "b", "c", "d", "e", "f" then the expression seq[1..4] will evaluate to a sequence that contains "b", "c", "d", "e" (since the item at index 1 is "b", and the item at index 4 is "e").
The lastindex can be omitted, in which case it defaults to the index of the last item of the sequence. For example, if seq stores the sequence "a", "b", "c", "d", "e", "f" again, then seq[3..] will evaluate to a sequence that contains "d", "e", "f".
An attempt to access a subvariable past the last subvariable or before the first subvariable of the sequence will cause an error and abort the processing of the template.
Hash operations
Concatenation
You can concatenate hashes in the same way as strings, with +. If both hashes contain the same key, the hash on the right-hand side of the + takes precedence. Example (the set directive creates a new variable and sets its value to the value of the expression on the right side of the =):
| |||
will print:
| |||
Note that hash concatenation is not to be used for many repeated concatenations, like for adding items to a hash inside a loop. It's the same as with the sequence concatenation.
Arithmetical calculations
This is the basic 4-function calculator arithmetic plus the modulus operator. So the operators are:
- Addition: +
- Subtraction: -
- Multiplication: *
- Division: /
- Modulus (remainder): %
Example:
| |||
Assuming that x is 5, it will print:
| |||
Both operands must be expressions which evaluate to a number. So the example below will cause an error when FreeMarker tries to evaluate it, since "5" is a string and not the number 5:
| |||
There is an exception to the above rule. The + operator, is used to concatenate strings as well. If on one side of + is a string and on the other side of + is a numerical value, then it will convert the numerical value to string (using the format appropriate for language of the page) and then use the + as string concatenation operator. Example:
| |||
will output this:
| |||
Generally, FreeMarker never converts a string to a number automatically, but it may convert a number to a string automatically.
People often want only the integer part of the result of a division (or of other calculations). This is possible with the int built-in. (Built-ins are explained later):
| |||
Assuming that x is 5, it will print:
| |||
Comparison
Sometimes you want to know if two values are equal or not, or which value is the greater.
To show concrete examples I will use the if directive here. The usage of if directive is: [#if expression]...[/#if], where expression must evaluate to a boolean value or else an error will abort the processing of the template. If the value of expression is true then the things between the begin and end-tag will be processed, otherwise they will be skipped.
To test two values for equality you use ==. To test two values for inequality you use !=. For example, assume that user is ''Big Joe'':
| |||
The user == "Big Joe" expression in the [#if ...] will evaluate to the boolean true, so the above will say ''It is Big Joe''.
The expressions on both sides of the == or != must evaluate to a scalar. Furthermore, the two scalars must have the same type (i.e., strings can only be compared to strings and numbers can only be compared to numbers, etc.) or else an error will abort template processing. For example [#if 1 == "1"] will cause an error. Note that FreeMarker does exact comparison, so string comparisons are case and white-space sensitive: "x" and "x " and "X" are not equal values.
For numerical and date values you can also use <, <=, >= and >. You can't use them for strings! Example:
| |||
Alternative syntaxes:
-
gt, lt, gte, lte can be used instead of >, <, >= and <=
-
> and < can be used instead of angle brackets, like you can write >= instead of >=
-
= can be used instead of ==, although = is used for other purposes as well, so using == is considered a better practice.
Warning!
You can't compare anything with null, because null indicates the lack of a value (and FTL does value comparisons, not pointer/identity comparisons). To test if user is null, use user?is_null (the ? syntax will be explained later).
Logical operations
Just the usual logical operators:
- Logical or: ||
- Logical and: &&
- Logical not: !
The operators will work with boolean values only. Otherwise an error will abort the template processing.
Example:
| |||
Built-ins
Built-ins provide, as the name suggest, certain built-in functionality that is always available. Typically, a built-in provides a transformed version of a value, or some information about a value. The syntax for accessing a built-in is like that of accessing a subvariable in a hash, except that you use the question mark instead of a dot. For example, to get the upper case version of a string: user?upper_case.
You can find the complete list of built-ins in the Reference. For now, just a few of the more important ones:
-
Built-ins to use with strings:
-
html: The string with all special HTML characters replaced with entity references (E.g., < with <)
-
cap_first: The string with the first letter converted to upper case
-
lower_case: The lowercase version of the string
-
upper_case: The uppercase version of the string
-
length: The length of the string
-
trim: The string without leading and trailing white-spaces
-
-
Built-ins to use with sequences:
-
size: The number of elements in the sequence
-
-
Built-ins to use with numbers:
-
int: The integer part of a number (e.g., 1.9?int is 1)
-
Example:
| |||
Assuming that test stores the string ''Tom & Jerry'', the output will be:
| |||
Note the test?upper_case?html. Since the result of test?upper_case is a string, you can use the html built-in with it. Also note how similar it looks to test.upper_case.html.
Another example:
| |||
Assuming that seasons stores the sequence "winter", "spring", "summer", "autumn", the output will be:
| |||
Method call
If you have a value that's a method then you can use the method call operation on it. The method call operation is a comma-separated list of expressions in parentheses. These values are called parameters. The method call operation passes these values to the method which will in turn return a result. This result will be the value of the whole method call expression.
For example, assume the programmers have made available a method variable called repeat in the data-model. You give a string as the first parameter, and a number as the second parameter, and it returns a string which repeats the first parameter the number of times specified by the second parameter.
| |||
will print:
| |||
Here repeat was evaluated to the method variable (according to how you access top-level variables) and then ("What", 3) invoked that method.
I would like to emphasize that method calls are just plain expressions, like everything else. So this:
| |||
will print this:
| |||
Treating undefined variables and null values
Overview
Let's say we have this data-model:
| |||
Here n is null, and m is undefined (i.e., it's not there at all).
For those who don't know null, it's a special value extensively used in the Java world for indicating that something is missing or on some other way undefined or not applicable. This is something that FTL doesn't need, and really just causes trouble and confusion, but since FreeMarker works together with Java programs (mostly), it just couldn't avoid null-s. So FTL knows null (since FreeMarker 2.4).
As far as the template author is concerned in most use-cases, the value of both m and n is missing. So in general, the difference between a missing variable and a variable that is null is subtle, like both ${n} and ${m} will abort the template processing with error. But there is a difference:
-
Any attempt to read an undefined variable is an error
-
Reading a variable that is null is OK, there is nothing special in it. null is just a value like 123 or "Joe". But FreeMarker is reluctant to do anything with a null value, because it means "not a value/value unknown". So you cant compare it to anything, can't convert it to string, can't add it to a number or string, etc. The only situations where using a null value (wherever it come from -- it need not come from a variable after all) will not abort template processing with an error are:
-
Assigning it to a variable, e.g., [#set x = n]
-
Using it as a subvariable in a sequence or hash literal, e.g., [1, n, 3], {"a": 1, "b": n, "c": 3}
-
Using it as a parameter when calling something (method, macro, etc.), e.g., repeat(n, 2). But then it is still possible that the called stuff (method, macro, etc.) doesn't accept null as the parameter value, so you end up with an error again.
-
To recapitulate, an error will occur and abort the template processing if you try to access a missing variable, or if you try to do something with a null value other than storing it or passing it as parameter. But I have lied. There is few special operators for treating such missing values, and those can suppress the error and handle the problematic situation. These will be introduced below.
Note
If you wonder why is FreeMarker so picky about missing values, read this FAQ entry.
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. Missing means that either unsafe_expr is a reference to the variable that is not defined at all, or the value of unsafe_expr is null.
Example. Assume no variable called mouse is exists initially:
| |||
The output will be:
| |||
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: weight!(item.weight * itemCount + 10). (Note that the spaces around the ! are used only for visual clarity; they are optional.)
Warning!
Sadly, in FreeMarker 2.3.x there was a programming mistake that made the precedence of ! (when it's used as default value operator) very low at its right side. This meant that, for example, ${x!1 + y} was misinterpreted by FreeMarker as ${x!(1 + y)}, instead of the correct ${(x!1) + y} meaning. As starting from FreeMarker 2.4 this mistake is fixed, if you have utilized this bug, now you must put the composite expression after the ! into parentheses, like in ${x!(1 + y)}.
If the default value is omitted, then it will be empty string and empty sequence and empty hash at the same time. (This is possible because FreeMarker allows multi-type values.) Note the consequence that you can't omit the default value if you want it to be 0 or false. Example:
| |||
The output will be:
| |||
Warning!
Due to syntactical ambiguities [@something a=x! b=y /] will be interpreted as [@something a=x!(b=y) /], that is, the b=y will be interpreted as a comparison that gives the default value for x, rather than the specification of the b parameter. To prevent this, write: [@something a=(x!) b=y /]
You can use this operator in two ways with non-top-level variables:
| |||
This will handle if color is missing inside product (and returns "red" if so), but will not handle if product is missing. That is, the product variable itself must exist and be non-null, otherwise the template processing will die with error.
| |||
This will handle if product.color is missing. That is, if product is missing, or product exists but it does not contain color, the result will be "red", and no error will occur. The important difference between this and the previous example is that when surrounded with parentheses, it is allowed for any component of the expression to be missing (i.e., undefined or null), while without parentheses only the last component of the expression is allowed to be missing.
Of course, the default value operator can be used with sequence subvariables as well:
| |||
The output will be:
| |||
However, a negative sequence index (as seq[-1]!'-') will always cause an error, you can't suppress that with this or any other operator.
Missing value test operator
Synopsis: unsafe_expr?? or (unsafe_expr)??
This operator tells if a value is missing or not. Depending on that, the result is either true or false. (Missing means that either unsafe_expr is a reference to the variable that is not defined at all, or the value of unsafe_expr is null.)
Example. Assume no variable called mouse is present:
| |||
The output will be:
| |||
With non-top-level variables the rules are the same as with the default value operator, that is, you can write product.color?? and (product.color)??. Also you can write things like seq[2]??, again similarly as with the default value operator.
Note
If you only want to know if a defined variable is null, use the is_null built-in.
Parentheses
Parentheses can be used to group any expressions. Some examples:
| |||
Note that the parentheses of a method call expressions have nothing to do with the parentheses used for grouping.
White-space in expressions
FTL ignores superfluous white-space in expressions. So these are totally equivalent:
| |||
and
| |||
and
| |||
Operator precedence
The following table shows the precedence assigned to the operators. The operators in this table are listed in precedence order: the higher in the table an operator appears, the higher its precedence. Operators with higher precedence are evaluated before operators with a relatively lower precedence. Operators on the same line have equal precedence. When binary operators (operators with two ''parameters'', as + and -) of equal precedence appear next to each other, they are evaluated in left-to-right order.
| Operator group | Operators |
|---|---|
| highest precedence operators | [subvarName] [subStringRange] . ? (methodParams) expr! expr!default expr?? |
| unary prefix operators | +expr -expr !expr |
| multiplicative | * / % |
| additive | + - |
| relational | < > <= >= (and quivalents: gt, lt, etc.) |
| equality | == != (and equivalents: =) |
| logical AND | && |
| logical OR | || |
| numerical range | .. |
For those of you who master C, Java language or JavaScript, note that the precedence rules are the same as in those languages, except that FTL has some operators that do not exist in those languages.