Release date: 2018-04-04

Changes on the FTL side

  • Added new special variable, get_optional_template (FREEMARKER-84). It can be used when you need to include or import a template that's possibly missing, and you need to handle that case on some special way. See the details here...

  • Added new special variable, caller_template_name (FREEMARKER-83), which returns the name (path) of the template from which the current macro or function was called. It's mostly useful if you want to resolve paths relative to the caller template.

  • Added new built-in, templateName?absolute_template_name or templateName?absolute_template_name(baseName), which can be used to convert a relative template name to an absolute template name. See more here...

  • Added new built-ins: sequence?min and sequence?max (FREEMARKER-86), which return the smallest and greatest item from a list of numbers or date/time/date-times. See more here...

  • The template language can now be configured to use [=expression] instead of ${expression} and #{expression}, which is very useful if you have a lot of ${...} or #{...} in the text that you are generating, and so they should be static text. See more about the square bracket interpolation syntax here. The template language can also be configured to only use ${expression}, and treat #{expression} as static text. (See the interpolation_syntax configuration setting, or the Configuration.setInterpolationSyntax(int) method.)

  • In string literals, \= is now a valid escape sequence, resulting in a =. This is useful when you are using the new [=exp] interpolation syntax, which can be escaped in a string literal like "Literal [\=x]".

  • Bug fixed (FREEMARKER-83); this fix is only active when incomplatible_improvements is set to 2.3.28 (or higher). When calling a macro or function (things defined in a template, not directly in Java) and the argument list contains .current_template_name, now it will correctly evaluate to the template that contains the call, rather than to the template that contains the macro or function definition. (Of course, the parameter default value expression is still evaluated in the context of the called macro or function.)

  • Bug fixed: When string?split(separator) is called with "" as the argument, the string will be split to characters now. Earlier it has thrown an IllegalArgumentException (unless the r flag was specified).

Changes on the Java side

  • Added new ParserConfiguration (such as Configuration and TemplateConfiguration) setting, interpolation_syntax. It has 3 possible values:

    • legacy (the default): Interpolations look like ${...} or #{...}. Note that #{...} is deprecated for a long time now.

    • dollar: Interpolations look like ${...}. With this syntax, #{...} will be just static text.

    • square_bracket: Interpolations look like [=...]. With this syntax ${...} and #{...} will be just static text. So it's useful if you generate output in a format where those (typically ${...}) are already used, such as to generate JSP pages, or to generate FreeMarker templates that use the default syntax.

  • When specifying the output_format configuration setting with String-String key-value pairs (like with Configuration.setSetting(String, String) or in a .properties file), it's now possible to specify the standard output formats by short name (like output_format=HTML) rather than by class name. (Custom formats still has to be referred by class name, as FreeMarker can't discover what their names are, since it's not aware of the custom classes.)

  • Added a new Configuration.removeTemplateFromCache overload that has a Object customLookupCondition parameter. This is useful to manually evacuate a template from the cache that was get with a non-null custom lookup condition.

  • Added new property to BeansWrapper.MethodAppearanceDecision: replaceExistingProperty. This is useful when a method like size() is exposed as a JavaBean property via MethodAppearanceDecision.exposeAsProperty, but there's also a "real" JavaBean property (like getSize()) with identical name. By default the real property isn't replaced, but now with replaceExistingProperty it can be.

  • DirectiveCallPlace now has a Template getTemplate() method, so you can query if from which template was your TemplateDirectiveModel called. (This has similar role as .caller_template_name for macros/functions.)

  • Added Environment.rootBasedToAbsoluteTemplateName(String), which converts the root based names typically used for the FreeMarker Java API-s (such as Configuration.getTemplate(name)) to an absolute path, which can be safely passed to <#include path> and such, as it won't be misinterpreted to be relative to the directory of the template.

  • Fixes in exception handling when calling JSP tags:

    • Bug fixed (FREEMARKER-88): If a TemplateException that's not a TemplateModelExceptoin has occurred in the body (nested content) of a JSP SimpleTag (typically, an InvalidReferenceException), that has caused a ClassCastException in the exception handling code, thus the template processing has thrown that instead of the original exception.

    • Bug fixed: For JSP Tag based custom tags, if an exception has occurred in the body (nested content), an IndexOutOfBoundsException might have occurred, replacing the original exception.

    • Wrapping of non-TemplateModelException TemplateException-s (typically InvalidReferenceException-s) into TemplateModelException-s is now avoided when the TemplateException occurs in the body of a JSP tag.

  • The default arithmetic engine (ArithmeticEngine.BIGDECIMAL_ENGINE) can now compare infinite (both positive and negative) to any other standard numerical type. Earlier, since BigDecimal can't represent infinite, it was only working in certain special cases. Also there were some performance optimizations to slightly decrease the impact and number of conversions to BigDecimal.

  • Avoided possible performance bottleneck when executing templates on many threads, caused by that java.beans.PropertyDescriptor.getReadMethod() is synchronized (FREEMARKER-80).

  • Added TemplateModelUtils.getKeyValuePairIterator(TemplateHashModelEx) static utility class, which can be used to get a TemplateHashModelEx2.KeyValuePairIterator even for a non-TemplateHashModelEx2 object. This simplifies Java code that iterates through key-value pairs.

  • freemarker.template.utility.DeepUnwrap (a rarely used utility) now utilizes when the unwrapped TemplateModel implements TemplateHashModelEx2.getKeyValuePairIterator(), and thus can unwrap such a hash value even if it has non-string keys. Also, it nows keeps the iteration order of the hashes, as it unwraps into a LinkedHashMap instead of into a plain HashMap.

  • freemarker.ext.beans.HashAdapter.size() was overridden for better performance.

  • When the incompatible_improvements setting is set to 2.3.28 (or greater): Fixed legacy parser glitch where a tag can be closed with an illegal ] (when it's not part of an expression) despite that the tag syntax is set to angle brackets. For example <#if x] worked just like <#if x>. Note that this legacy glitch didn't affect the legal usage of ], like <#if x[0]> has always worked correctly.

  • Fixed parser bug that disallowed using > as the top-level expression inside an interpolation (${...}). It had the same reason why <#if x > y> doesn't work as naively expected, but there's no real ambiguity in ${x > y}, so now it's allowed. Note that ${(x > y)?c} and ${(x > y)?string('y', 'n')}, which are how booleans are commonly printed, have always worked, as the > operation is not on the top-level inside the interpolation.

  • Fixed incorrect listing of valid roundingMode-s in extended Java decimal format parsing error message

Other changes

  • FreeMarker has graduated from the Apache Incubator (as of 2018-03-21), and now is a normal top-level project at Apache. Therefore, the version number doesn't contain "-incubating" anymore.