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
ortemplateName?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
andsequence?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 theinterpolation_syntax
configuration setting, or theConfiguration.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 anIllegalArgumentException
(unless ther
flag was specified).
Changes on the Java side
-
Added new
ParserConfiguration
(such asConfiguration
andTemplateConfiguration
) 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 withString
-String
key-value pairs (like withConfiguration.setSetting(String, String)
or in a.properties
file), it's now possible to specify the standard output formats by short name (likeoutput_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 aObject 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 likesize()
is exposed as a JavaBean property viaMethodAppearanceDecision.exposeAsProperty
, but there's also a "real" JavaBean property (likegetSize()
) with identical name. By default the real property isn't replaced, but now withreplaceExistingProperty
it can be. -
DirectiveCallPlace
now has aTemplate getTemplate()
method, so you can query if from which template was yourTemplateDirectiveModel
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 asConfiguration.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 aTemplateModelExceptoin
has occurred in the body (nested content) of a JSPSimpleTag
(typically, anInvalidReferenceException
), that has caused aClassCastException
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 (typicallyInvalidReferenceException
-s) intoTemplateModelException
-s is now avoided when theTemplateException
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, sinceBigDecimal
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 toBigDecimal
. -
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 aTemplateHashModelEx2.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 unwrappedTemplateModel
implementsTemplateHashModelEx2.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 aLinkedHashMap
instead of into a plainHashMap
. -
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.