The suppored types are:
-
Scalars:
- String
- Number
- Boolean
- Date-like (date, time, or date-time)
-
Containers:
- Hash
- Sequence
- Collection
- Subroutines:
- Miscellaneous/seldom used:
Scalars
These are the basic, simple kind of values. They can be:
-
String: It is simple text, e.g., the name of a product.
If you want to give a string value directly in the template, rather than use a variable that comes from the data model, you write the text between quotation marks, e.g.,
"green mouse"
or'green mouse'
. (More details regarding the syntax can be found later.) -
Number: For example the price of a product. Whole numbers and non-whole numbers are not distinguished; there is only a single number type. So for example 3/2 will be always 1.5, and never 1. Just like if you are using a calculator.
If you want to give a numerical value directly in the template, then you write for example:
150
or-90.05
or0.001
. (More details regarding the syntax can be found later.) -
Boolean: A boolean value represents a logical true or false (yes or no). For example, if a the visitor has been logged in or not. Typically you use booleans as the condition of the
if
directive, like<#if loggedIn >...</#if>
or<#if price == 0>...</#if>
; in the last case the result of theprice == 0
part is a boolean value.In the templates you can directly specify a boolean with the reserved words
true
andfalse
. -
Date: A date-like value stores date/time related data. It has three variations:
-
Date: Like April 4, 2003. Day precision, no time of day part.
-
Time: Like 10:19:18 PM. Millisecond precision, no date part.
-
Date-time (sometimes called "time stamp") as April 4, 2003 10:19:18 PM. Both date and time, with millisecond precision.
Unfortunately, because of the limitations of the Java platform, FreeMarker sometimes can't decide which parts of the date are in use (i.e., if it is date-time, a date or a time). The solution for this problem is an advanced topic that will be discussed later.
It is possible to define date-like values directly in templates, but this is an advanced topic that will be explained later.
-
Bear in mind that FreeMarker distinguishes strings from
numbers, booleans and date-like values. For example, while the
string "150"
looks like the number
150
, a string is still just arbitrary sequence of
characters, and you can't do arithmetic with it, can't compare it
with another number, etc.
Containers
These are the values whose purpose is to contain other variables; they are just containers. The contained variables are often referred as sub variables. The container types are:
-
Hash: Associates a unique lookup name with each of its sub variables. The name is an unrestricted string. A hash doesn't define an ordering for the sub variables in it. That is, there is no such thing as the first subvariable, and the second subvariable, etc.; the variables are just accessed by name.
-
Sequence: Associates an integer number with each of its sub variables. The first subvariable is associated with 0, the second with 1, the third to 2, and so on; the sub variables are ordered. These numbers are often called the indexes of the sub variables. Sequences are usually dense, i.e., all indexes up to the index of the last subvariable have an associated subvariable, but it's not strictly necessary. The type of the subvariable values need not be the same.
-
Collection: A collection, from the viewpoint of the template author, is a restricted sequence. You cannot access its size or retrieve its sub variables by index, but they can be still listed with the
list
directive.
Note that since a value can have multiple types, it is possible for a value to be both a hash and a sequence, in which case it would support index-based access as well as access by lookup name. However, typically a container will be either a hash or a sequence, not both.
As the value of the variables stored in hashes and sequences (and collections) can be anything, it can be a hash or sequence (or collection) as well. This way you can build arbitrarily deep structures.
The data-model itself (or better said the root of it) is a hash.
Subroutines
Methods and functions
A value that is a method or a function is used to calculate another value, influenced by the parameters you give to it.
For programmer types: Methods/functions are first-class values, just like in functional programming languages. This means that functions/methods can be the parameters or return values of other functions/methods, you can assign them to variables, and so on.
Suppose that programmers have put the method variable
avg
in the data-model that can be used to
calculate the average of numbers. If you give the 3 and 5 as
parameters when you access avg
, then you get
the value 4.
The usage of methods will be explained later, but perhaps this example helps to understand what methods are:
The average of 3 and 5 is: ${avg(3, 5)} The average of 6 and 10 and 20 is: ${avg(6, 10, 20)} The average of the price of a python and an elephant is: ${avg(animals.python.price, animals.elephant.price)}
this will output:
The average of 3 and 5 is: 4 The average of 6 and 10 and 20 is: 12 The average of the price of a python and an elephant is: 4999.5
What is the difference between a method and a function? As
far as the template author is concerned, nothing. Well not really
nothing, as methods typically come from the data-model (as they reflect the methods of Java
objects), and functions are defined in templates (with
the function
directive -- an advanced topic), but both can be used on
the same way.
User-defined directives
A value of this type can be used as user-defined directive (with other words, as FreeMarker tag). An user-defined directive is a subroutine, something like a little reusable template fragment. But this is an advanced topic that will be explained later in its own chapter.
For programmer types: user-defined directives (such as macros), are first-class values too, just like functions/methods are.
Just to get an idea about user-defined directives (so just
ignore this if you won't understand), assume we have a variable,
box
, whose value is a user-defined directive
that prints some kind of fancy HTML message box with a title bar
and a message in it. The box
variable could be
used in the template like this (for example):
<@box title="Attention!"> Too much copy-pasting may leads to maintenance headaches. </@box>
Function/method versus user-defined directive
This is for advanced users again (so ignore it if you don't understand). It's a frequent dilemma if you should use a function/method or an user-defined directive to implement something. The rule of thumb is: Implement the facility as user-defined directive instead of as function/method if:
-
... the output (the return value) is markup (HTML, XML, etc.). The main reason is that the result of functions are subject to automatic XML-escaping (due to the nature of
${...}
), while the output of user-defined directives are not (due to the nature of<@...>
; its output is assumed to be markup, and hence already escaped). -
... it's the side-effect that is important and not the return value. For example, a directive whose purpose is to add an entry to the server log is like that. (In fact you can't have a return value for a user-defined directive, but some kind of feedback is still possible by setting non-local variables.)
-
... it will do flow control (like for example
list
orif
directives do). You just can't do that with a function/method anyway.
The Java methods of FreeMarker-unaware Java objects are normally visible as methods in templates, regardless of the nature of the Java method. That said, you have no choice there.
Miscellaneous
Nodes
Node variables represent a node in a tree structure, and are used mostly with XML processing, which is an advanced, and specialized topic.
Still, a quick overview for advanced
users: A node is similar to a sequence that stores
other nodes, which are often referred as the children nodes. A
node stores a reference to its container node, which is often
referred as the parent node. The main point of being a node is the
topological information; other data must be stored by utilizing
that a value can have multiple types. Like, a value may be both a
node and a number, in which case it can store a number as the
"pay-load". Apart from the topological information, a node can
store some metainformation as well: a node name, a node type
(string), and a node namespace (string). For example, if the node
symbolizes a h1
element in an XHTML document,
then its name could be "h1"
, it's node type
could be "element"
, and it's namespace could be
"http://www.w3.org/1999/xhtml"
. But it's up to
the designer of the data-model if what meaning these
metainformations have, and if they are used at all. The way of
retrieving the topological and metainformations is described in a later chapter (that you
don't have to understand at this point).