Release date: 2019-08-17

Changes on the FTL side

  • Added new built-ins: ?filter(predicate), ?map(mapper), ?take_while(predicate), ?drop_while(predicate). These allow using lambda expressions, like users?filter(user -> user.superuser) or users?map(user -> user.name), or accept a function/method as parameter. Lambda expressions are also new in this release, but they can only be used in said built-ins, so they aren't like in Java for example, and also, unlike the similar Java methods, these built-ins aren't lazy in general, only in specific cases (see more here). The main goal of adding these built-ins was to allow conditionally skipping elements in the list directive without nested if-s that interfere with the sep directive, and the loop variable built-ins (see examples here).

  • Added new built-ins for truncating text. string?truncate(length) truncates the text to the given length, and by default adds [...] at the end if truncation has happened. Truncation happens at word boundaries, unless the result is too short that way, in which case it falls back to truncation mid word. There's also ?truncate_w to force Word Boundary truncation, and ?truncate_c (for Character Boundary) that doesn't care about word boundaries. The truncation algorithm is pluggable in the FreeMarker configuration. See the reference for more details.

  • ?sequence now collaborates with seq?size, seq[index], seq[range], and with some other built-ins (filter, map, join, etc.) to spare collecting all the elements into the memory when possible. For example anIterator?sequence[1] will now just fetch the first 2 items, while earlier it has built a sequence that contains all the elements, only to get the 2nd element from that. Or, anIterator?sequence?size will now just count the elements, without collecting them into the memory. See the reference for more details.

  • Extended decimal format parameter "multiplier" was incorrectly written as "multipier". Now both words are recognized.

  • ?min and ?max will now immediately stop with error when applied on a right unbounded numerical range (like 1..), as that would run forever anyway.

Changes on the Java side

  • FREEMARKER-109: In JSP TLD-s, line breaks inside function parameter lists have caused IllegalArgumentException "Invalid function signature".

  • FREEMARKER-104: More helpful log and error messages (especially, no NullPointerException cause exception logged during FreeMarker XPath support initialization) if no XPath implementation is available because Java 9 modules don't allow accessing the internal Xalan that's stored under com.sun packages. (The messages now recommend adding Apache Xalan or Jaxen as dependency if you need XPath support.)

  • The boolean_format configuration setting now can be set to "c". Then ${aBoolean} will behave as ${aBoolean?c}. This should only be used if you are generating output for non-human (computer) consumption only. If your output has pieces for human audience too, it's still recommended to use ${aBoolean?c} where true/false output is needed, and either not set the boolean_format at all, or set it to something that's appropriate for everyday users (like "yes,no").

  • New configuration setting, fallback_on_null_loop_variable: Specifies the behavior when reading a loop variable (like i in <#list items as i>, or in <@myMacro items; i>) that's null (missing); if true, FreeMarker will look for a variable with the same name in higher variable scopes, or if false the variable will be simply null (missing). For backward compatibility the default is true. The recommended value for new projects is false, as otherwise adding new variables to higher scopes (typically to the data-model) can unintentionally change the behavior of templates.

  • If the result of seq?size is compared to an integer literal in a template, like in seq?size != 0, or seq?size < 1, and to decide the answer it's enough to know if seq is empty or not (i.e., the exact size isn't needed), and seq implements TemplateCollectionModelEx, FreeMarker will call TemplateCollectionModelEx.isEmpty() instead of size(). Furthermore, if seq is the result of ?filter, or of a similar built-ins that can provide lazily generated result, it will do counting to figure out the size (rather than constructing the whole sequence in memory), and will limit how far it counts based on what literal the result of ?size is compared with.

  • Added TemplateModelUtils.wrapAsHashUnion(ObjectWrapper, List<?>) and wrapAsHashUnion(ObjectWrapper, Object...), which is useful when you want to compose the data-model from multiple objects in a way so that their entries (Map key-value pairs, bean properties, etc.) appear together on the top level of the data-model.

  • HTMLOutputFormat, XMLOutputFormat, XHTMLOutputFormat aren't final classes anymore, furthermore XHTMLOutputFormat now extends XMLOutputFormat. Same applies to the respective TemplateOutputModel-s (TemplateHTMLOutputModel is not final anymore, etc.). This allows defining new custom markup output format classes that will work with program logic that's only prepared for the standard markup output formats, because instanceof SomeStandardOutputFromat will return true for them.

  • When configuring FreeMarker with string values (like with a .properties file), in the settings that support the "object builder" syntax, now you can create a TemplateMarkupOutputModel value with the new markup function, like markup(HTMLOutputFormat(), "<p>Example</p>").

  • BeansWrapper.clearClassIntrospecitonCache was deprecated as there's a typo in the method name; use clearClassIntrospectionCache instead.