Date of release: 2008-06-16
Notes:
-
This release may be incompatible with the later 2.4 releases, as it's only a preview release. Depending on user feedback and on our realizations it can still change considerably.
-
Since 2.4 is a full decimal point move in version number, there are backward incompatible changes in it. Look for the "Warning"-s below, as some may affects you. (Note that we don't plan to stop the basic maintenance of the 2.3.x branch after the final release of 2.4, so you definitely will not have to upgrade in older projects.)
-
This change log is relative to the yet unreleased 2.3.14 (as of this writing). See the 2.3.14 change log here.
-
A conversion utility exists to convert older FreeMarker templates to use the new 2.4 constructs and recommendations. See the class freemarker.core.helpers.Canonicalize. You can run it from the command line with no arguments to get a list of options.
-
This documentation is only partially updated for 2.4 (except the version history of course), so some parts are possibly inaccurate, also some new features are not documented yet.
Feedback is welcome! Preferably use the freemarker-devel mailing list for that (subscription is optional, but recommended if you don't want to miss some answers).
-
New directives var, set: These directives deprecate the assign, local and global directives, and allow all kind of bocks having own "local variables", not just macros. See more details and examples here...
-
Changes regarding null-s:
-
Warning, backward incompatible change! The template language is now null-aware. Thus, null is now a keyword (so if you used it as a variable name, there could be problems now). In FreeMarker 2.3.x, variables (Java Bean properties, list and map items, etc.) that exist but store null were undistinguishable from variables that don't exist at all. That said, as far as the template language is concerned, there was no such thing as null at all. Starting from 2.4, null still pretty much behaves as a variable is doesn't exist, except that:
-
It can be assigned to other variables, e.g., [#set x = null], or [#set x = aVariableThatIsNull], where aVariableThatIsNull still must exist, only its value can be null.
-
It can be passed as parameter to directive/method (aka. macro/function) calls, e.g., ${aJavaMethod(null)} or ${aJavaMethod(aVariableThatIsNull)}, where, again, aVariableThatIsNull must exist. Even if the parameter is mandatory, it will not treat the parameter as missing, so there will not be missing parameter error.
Otherwise a null value behaves exactly like a missing variable, so, logically:
-
The exp!defaultExp, exp!, and exp?? operators treat null values like missing variables. For example, x!"N/A" will evaluate to "N/A" both if the x variable doesn't exists and if it exists but stores null.
-
You can't have a null in additions, multiplications, divisions, comparisons(!), etc., exactly like you couldn't have missing variables there (unless you specify a default value, of course). To test if something is null, write theVariable?is_null, not theVariable == null.
-
null is not converted to string automatically, so for example to print a possibly null variable you should still write something like: ${possiblyNull!"N/A"}
-
If you pass a null to a directive/method (aka. macro/function), then although it will not treat the parameter as omitted, if the parameter has a default value, it will still replace the null with that.
-
-
Two new built-ins: is_null, is_defined (used like expression?is_null, expression?is_defined): The first simply checks if a value is null or not; it will die with error if the examined variable doesn't exist. The second tests if a variable exists, but it doesn't care if it stores a null value or not. Comparing these with the ?? operator, var?? returns true if the variable exists and is non-null, and returns false otherwise (i.e., if the variable doesn't exist or exists but it's value is null). So in almost all cases you will want to use ??; the is_null and is_defined built-ins are pretty much only for advanced users.
-
Warning, backward incompatible change! Bug fixed: Passing undefined variable to a directive/macro/function/method is now always an error, not even parameter default values can suppress that. Thus, ${anObject.aMethod(possiblyUndefined)} is wrong, use ${anObject.aMethod(possiblyUndefined!null)} instead. [@macroWithDefautlParam possiblyUndefined /] is wrong, use [@macroWithDefautlParam possiblyUndefined!null /] instead. The lenience of FTL in these cases was a bug that was unfortunately exposed by many for emulating null/omitted parameters. (Note that now null is a keyword, but earlier it wasn't, as FTL wasn't null-aware.)
-
-
New directive: embed. This meant to replace include in almost all use cases. The difference to include is that in the case of embed the embedding and the embedded template have their own namespaces, so they don't overwrite or see each other's variables. (In the case of include the included template has used the namespace of the including template, possibly causing maintenance headaches due to accidental name clashes.) So, if you just want to insert the output of a template into another template, you should use embed from now. If you want to use the macros and other variables of another template, then you should use import. Using include is almost always a bad practice. (See: embed in the reference.)
-
Warning, slightly backward incompatible change! The recommended syntax is from now is the square bracket syntax, for example: [#list xs as x]...[/#list] and [@myMacro /]. Unlike in 2.3.x, you do not need to start the file with [#ftl] for this to work. Templates with angle bracket syntax will still work (without configuring anything), because the template syntax is now auto-detected based on the first thing in the template that looks like the beginning of an FTL tag. In principle it's still not a 100% backward compatible change, as if your template starts with static text that contains <# or [# followed by a valid directive name character, or <@ or [@ (followed by anything), the syntax will be detected falsely. If you have this problem, use Configuration.setTagSyntax(int) to force angle bracket syntax, or start the template with a <#ftl> or a comment to ensure proper syntax detection. (See: appendix about angle bracket syntax in 2.4)
-
New built-in: scope. It let you read and modify the local variables of enclosing macro calls, that is, in the case of [@theParent]...[@theChild /]...[/@theParent] the theChild macro can communicate with the enclosing theParent macro without using global variables, through the local variables of the theParent macro call. For more details and examples see the related reference section.
-
Warning, slightly backward incompatible change! Lazy evaluation of arguments for default and string built-ins: expr1?default(expr2) will not evaluate expr2 when expr1 is defined. Also, booleanExpr?string(trueExpr, falseExpr) will evaluate exactly one of either trueExpr or falseExpr depending on the value of booleanExpr but never both. This will cause backward compatibility problems in the unlikely case when the parameter expression evaluations had side-effects that you have relied on. More info: the default built-in, the string built-in for booleans.
-
New parameter to the ftl directive: strict_vars (boolean). If this parameter is set to true, the set directive will set only already existing variables; if the target variable is missing, the template processing will be aborted with error. That said, the variable must be already created with var, as the set directive will never create a variable, only modify it. Thus, a typo in the target variable name will cause an error, rather than silently creating a new variable. Also, in templates where strict_vars is true, it's not allowed to use the following directives: assign, local, global, include(!). Instead of these you must use the new set, var and embed directives.
-
New built-in: use_defaults. This can be used to create a variation of a callable object (like of a macro) that is the same as the original except that some of its originally required parameters have default value, or some of the optional parameters has different default values. (Some may know this trick as "currying".) See more in the reference...
-
New directives: lt_lines, rt_lines, t_lines, for trimming many lines without repeatedly using the lt, rt or t directive. These new directives has nested content (i.e., you use them as [#t_lines]...[/#t_lines]), and they will apply the corresponding single-line directive (lt, rt or t, respectively) on all lines in it. [TODO: document in the reference, link it here]
-
Functions (the things defined with [#function ...]) now support named parameters when calling the function, like myFunction(x = 1, y = 2) works; earlier only thing like myFunction(1, 2) did. Methods also support calling by named parameters now, although only those that the Java programmers has specifically prepared for that (with Java 5 annotations; see later).
-
[/#] now works (similarly to [/@]), but use it only when it doesn't decrease the understandability of the template, i.e., when the start-tag and the end-tag are close to each other and easy to pair visually.
-
Added a new number format specifier, "computer". This uses the same formatting as exp?c.
-
XML processing (assuming you are using the standard XML wrapper): To test if a node exists, from now it's enough to write something like [#if employee.degress] instead of [#if employee.degress[0]??]. That's because now the query result is a boolean as well, which is false if the node list is empty, and true otherwise. (Yes, this is actually not a change on the FTL side, but primarily template authors are affected.)
-
New built-in: source. This returns the text of the expression it is applied to, e.g., a.b.c[0]?source will be the string "a.b.c[0]". This can be used for tricks like, combined with the escape directive ([#escape x as x!(x?source)]), it can print the expression that contain undefined variable errors. [TODO: document it in the reference, link it]
-
Warning, slightly backward incompatible change! The superfluous whitespace removal algorithm was slightly reworked, to remove more typical superfluous whitespace. [TODO: document details]
-
Bug fixed: Warning, backward incompatible change! The default value operator (exp!exp) had wrong precedence at its right side, causing things like ${x!y + 1} be misinterpreted as ${x!(y + 1)} in FreeMarker 2.3.x. From now, something like ${x!y + 1} is correctly interpreted as ${(x!y) + 1}. If you want the old meaning, use parentheses.
-
Warning, backward incompatible change! FreeMarker 2.4 is developed for J2SE 5. However, we still release a separate 1.4 compatible jar (included in the standard distribution tar.gz), that is created with the help of Retrotranslator. So you will be able to use FreeMarker 2.4 on J2SE 1.4 (or at least we don't know about problems with that yet). J2SE 1.3 and earlier is not supported anymore.
-
Directives (transforms) and methods implemented in Java now support calling by named parameters (like [@myDirective width=50 color="red"] and myMethod(from=10, to=20)). This applies both to methods/directives implemented directly by using the TemplateMethodModel and TemplateDirectiveModel (and TemplateTransformModel) interfaces, and to Java methods exposed by the object wrapper. The names of the parameters can be specified using the newly introduced freemarker.template.Parameters annotation. Also, with the same annotation the default value of the parameters can be specified as well.
For example, here we specify that the name of the first parameter is "title", and the name of the second parameter is "score", also that if the score parameter is missing or null, then it defaults to 50:




@freemarker.template.Parameters("title, score=50") public String myMethod(String t, int sc) { ... }



Assuming we access this method from the template as obj.myMethod, these are all legal calls to it:




${obj.myMethod(title="foo", score=100)} ${obj.myMethod(title="foo")} ${obj.myMethod("foo", 100)} ${obj.myMethod("foo")}



The annotation value uses exactly the same syntax as the template language inside the [#macro name parameterList] or [#function name parameterList] tag as the parameterList part.
-
New marker interface, freemarker.template.LazilyEvaluatableArguments can be implemented by custom TemplateMethodModel-s to have their argument lists lazily evaluated (each argument evaluated only when first used by the method during the call, instead of evaluating all arguments in left-to-right order before the call). More info...
-
Public tree-walker API for tools that walk the FreeMarker AST (Abstract Syntax Tree). This can be used by tool writers to walk the FreeMarker AST using visitor pattern. See freemakrer.core.ast.ASTVisitor. For examples of usage, you can refer to where it is used internally, for example, in freemarker.template.PostParseVisitor and freemarker.template.WhitespaceAdjuster. Also, the template converter utility is an implementation of the ASTVisitor API too; see freemarker.core.helpers.DefaultTreeDumper and freemarker.core.helpers.CanonicalizingTreeDumper.
-
Support for subjecting FreeMarker templates to Java security policy based on the location they were loaded from.
-
FreemarkerServlet improvement: You can specify multiple paths in the TemplatePath servlet init-param, separated with commas. In that case a MultiTemplateLoader will be created. For example you can specify "/, class://com/example/templates", in which case the templates will be loaded from the Web application root directory, or if the template is not found there, then from the com.example.templates package.
-
Warning, backward incompatible change! The default of simpleMapWrapper property of BeansWrapper is true now. This means that the methods and fields of Map-s will not be exposed by default, only the pure key-set will define the subvariables of the templates. Note that the default object wrapper of FreeMarker always had simpleMapWrapper set to true, so if you have used only the default object wrapper, then there is no change for you.
-
Warning, backward incompatible change! Some very old backward compatibility options were removed: non-strict syntax (like <if x>...</if>), classic compatibility mode (i.e., FreeMarker 1.x expression semantic simulation).
-
TemplateTransformModel is now deprecated, TemplateDirectiveModel is recommended instead.
-
FreeMarker is now JSR-223 (javax.script) aware.
-
TemplateCache now can be extended, and the custom implementation plugged in as a configuration setting.
-
XML wrapping: NodeListModel now implements TemplateBooleanModel. It will be false if the node list contains no nodes, true otherwise.
-
TemplateModel.JAVA_NULL was added that corresponds to the new null value of the template language.
-
Warning, backward incompatible change! JDOM support was removed.
-
Warning, slightly backward incompatible change! The default XPath engine is Jaxen from now. That is, if Jaxen is found in the class path, it will be used instead of Xalan. Be sure that if Jaxen is present, it is at least Jaxen 1.1 beta 8.
-
Warning, slightly backward incompatible change! At least Jython 2.2 is required for the Jython support to work.


