blob: 8b66e7f8d6a4a8e2b93fa75e0d5c94a921d228ff [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
Because the Manual won't be updated for a good while, we will lead
the FreeMarker 3 changelog here.
To understand the purpose of FreeMarker 3 (FM3 from now on), please see:
https://cwiki.apache.org/confluence/display/FREEMARKER/FreeMarker+3
Template language changes
=========================
Note that the template converter tool (freemarker-converter) can automatically apply many of these changes on FM2
templates. But changes that aren't purely syntactical (but behavioral) often can't be treated with 100% safety or at
all, in which case it's always noted for that change as "Converter note:".
Major template language changes / features
------------------------------------------
- Switched to camel case as the only supported naming convention. This is as if in FM2 you set the "namingConvention"
configuration setting to "camelCase", however the "namingConvention" setting was removed, as no other convention will
be supported (for the default dialect at least). Also, configuration setting names and values must use camel case
too (FM2 was forgiving there). Also, in XML processing, the @@ names were converted to camel case. This means
`#elseif` becomes ``#elseIf`, ``?upper_case` becomes `?upperCase`, and if you configure FreeMarker with Properties,
then `template_loader` becomes `templateLoader`, and if you use the XML features, then `node.@@nested_markup`
becomes `node.@@nestedMarkup`.
Note that the template converter tool takes care of this conversion.
- Parameters passed by position (as opposed to by name) must be separated by comma. Earlier the comma was optional.
(This is needed to allow passing parameters both by-position and by-name in the same invocation, which is not yet
implemented though.)
Examples:
`<@x x + 1 2 3 />` must now be written as `<@x x + 1, 2, 3 />`
`<#nested x y>` must now be written as `<#nested x, y>`
- #macro-s and #function
- Both #macro-s and #function-s can now define parameters that are passed by position, and parameters that are passed by name.
Function parameters are by default positional, which can be changed by adding `{named}` after the parameter name.
Macro parameters are by default named, which can be changed by adding `{positional}` after the parameter name.
Positional parameters always precede the named ones. There can be both a positional and a named varargs parameter;
the first is always a sequnce, the later is always a hash.
Examples:
- All named: `<#macro heading title icon>...</#macro>` `<@heading title="Test" icon="foo.jpg" />`
- Mixed: `<#macro heading title{positional} icon>...</#macro>` `<@heading "Test" icon="foo.jpg" />`
- All positional: `<#function f(x, y, r=0)>...</#function>` `${f(1, 2)} ${f(1, 2, 10)}`
- Mixed: `<#function f(x, y, r{named}=0)>...</#function>` `${f(1, 2)} ${f(1, 2, r=10)}`
- Positional varargs: `<#function sum(xs... abs{named}=false)>...</#function>` `${sum(1, 2, 3, abs=true)}
- Positional and named varargs: `<#function f(xs..., props{named}...)>...</#function>` `${f(1, 2, 3, x=1, y=2)}`
- In FM2, the same macro could be called with specifying all parameters by position, or by specifying all parameters
by name. In FM3 that won't work anymore, as now a parameter is either strictly positional or strictly named.
- Operator for handing null/missing values were reworked:
- The right-side operator precedence of the `exp!defaultExp` (and `exp!`) operator is now the same precedence on
both sides, which is lower than the precedence of `.`, but higher than the precedence of `+`. The converter takes
care of cases where this would change the meaning of the expression (like `x!y+1` is converted to `x!(y+1)`.)
- The value of `missing!` can now be used as boolean `false`, and as a function that returns `null` and accepts
any arguments, and as a directive that does nothing and allows any arguments (not only as "", empty sequence,
and empty hash).
- [TODO] Deeper changes are supposed to happen here later. (Some of the above changes will be meaningless then.)
Smaller template language changes
---------------------------------
Node: Changes already mentioned above aren't repeated here!
- Removed support for all old glitches that you could turn on/off with `incompatibleImprovements` in FM2 (see
them in the JavaDoc of FM2 `Configuration`).
Converter note: The otherwise very low risk behavioral changes here aren't covered by the converter tool.
- Removed the following operator aliases:
- `=` for comparison (it can only be used for assignment now). Use `==` instead.
- `&` for logical "and". Use `&&` instead.
- `\and`, `&amp;&amp;`. Use `and` instead. (HTML entity support meant to be re-added, but "globally")
- `|` for logical "or". Use `||` instead.
- `\lt`, `&lt;`. Use "lt" instead.
- `lte`, `\lte`, `&lt;=`. Use "le" instead (syntax from XSLT).
- `\gt`, `&gt;`. Use "gt" instead.
- `gte`, `\gte`, `&gt;=`. Use "ge" instead (syntax from XSLT).
- New keywords (can't be used as identifier): `and`, `or`, `le`, `ge`
- Removed some long deprecated template language directives:
- <#call ...> (deprecated by <@... />)
- <#comment>...</#comment> (deprecated by <#-- ... -->)
- <#transform ...>...</#transform> (deprecated by <@...>...</@...>)
- <#foreach x in xs>...</#foreach> (deprecated by <#list xs as x>...</#list>)
- Removed long deprecated `#{}` interpolations. They are treated as plain static text now.
Converter note: The template converter tool translates these to `${}` interpolations. For example `#{x}` is simply
translated to `${x}`, while `#{x; m1M3}` is translated to `${x?string('0.0##')}`). The output should remain the same.
- In `#macro` and `#function` the comma must only be used between by-position parameters (earlier the comma was
optional).
- In `#function`, parentheses are now required around parameter list (as in `<#function f(a, b)>`), even if there are
zero parameters (as in `<#function f()>`). In `#macro`, parentheses are not allowed around parameter list anymore
(so `#macro m(a b)` becomes to `#macro m a b`). This is to remain more consistent with the look-and-feel of the
invocation of the function or macro.
- Removed some long deprecated built-ins:
- `webSafe` (converted to `html`)
- `exists` (`foo?exists` is converted `foo??`)
- `default` (`foo?default(bar)` is converted to `foo!bar`).
- `if_exists` and `ifExists` (`foo?if_exists` is converted to `foo!`). (There's a slight difference though that
the return value can be called as directive (which does nothing), while with `?ifExists` that wasn't possible.)
- Comma is now required between sequence literal items (such as `[a, b, c]`). It's not well known, but in FM2 the comma
could be omitted.
- #include has no "encoding" and "parse" parameter anymore (as now only the Configuration is responsible for deciding those)
- You can't close #attempt/#recover with `</#recover>` anymore, only with `<#attempt>`. This was the
standard form in FM2 as well, and is consistent with how #if/#else works. (The template converter
tool does this conversion.)
- Inside a #switch, #case can't be after #default anymore (this is actually a bug in FM2)
(The template converter can't fix this automatically, but reports it as an error.)
- #else and #elseIf tags must be ended without "/", for example, <#else/> is illegal now, <#else> is legal.
- ${someBoolean?string} with the default booleanFormat setting value will not convert the boolean to true/false anymore,
instead it will fail just like ${someBoolean} would. Use ${someBoolean?c} instead - it's preferable in FM2 as well.
- Renamed `?datetime` and `?datetimeIfUnknown` and the `datetimeFormat` setting to
`?dateTime` and `?dateTimeIfUnknown` and `dateTimeFormat`. (In general, it's `dateTime`, not `datetime` everywhere.)
assertConverted("${.outputFormat}","${.output_format}");
- Removed the `.currentNode` (`.current_node`) special variable, which was a deprecated alias to `.node`
- Removed the `.templateName` (`.template_name`) special variable, which was deprecated by `.currentTemplateName`
since 2.3.23.
Converter note: This conversion is done, but note that in the rare case where a template has no name (when
creating a `Template` directly with its constructor using `null` as the `name` parameter) `.templateName` was an
empty string, while `.currentTemplateName` will be null.
- Refactoring of callable TemplateModel interfaces and related classes:
- TemplateDirectiveModel was redesigned (see in the major changes section)
- Removed `TemplateTransformModel`; `TemplateDirectiveModel` can do the same things, and more.
- Renamed `DirectiveCallPlace` to `CallPlace`
- Removed Environment.getDirectiveCallPlace(), as TemplateDirectiveModel-s now get the CallPlace as the
parameter of the `execute` method.
- `?isTransform` was removed (as there are no transforms anymore); use `?isDirective` instead.
- `?isMacro` was removed; use `?isDirective` instead, which returns `true` both for macros and directives defined otherwise.
- `?isMethod` was removed; use `?isFunction` instead, which returns `true` both for Java methods and functions defined
otherwise (such as with `#function`).
- The FM2 `?isCollection` and `?isEnumerable` was replaced by `?isIterable`. `?isCollectionEx` was renamed to
`?isCollection`. (These follows the similar nomenclature change in the TemplateModel API-s)
`?isIndexable` was removed, and can be replaced with `?isSequence`.
- The directive returned by `?interpret` doesn't allow nested content anymore. (It wasn't useful earlier either;
the nested content was simply executed after the interpreted string.)
- Changes in #macro/#functions
- See major changes first, in the earlier chapter
- It's not tolerated anymore if the caller of a macro has declared more nested content parameters than
what `#nested` passes to it (like in `<#macro m><#nested 1, 2></#macro> <@m ; i, j, k>...</@>`, where `k` has
no corresponding value in `#nested`).
(The template converter tool can't do this conversion.)
- In `#macro` (and `function`) named parameters with default values need not be at the end of the parameter list
anymore. (Positional parameters with default values need to be at the end of the list of positional parameters
only.)
- When parameter default expressions are evaluated, only the parameters defined earlier are already set.
So `<#macro m a b=a>` works, but `<#macro m a=b b>` won't work anymore (unless there's `b` outside the
parameter list), as `b` is not yet set when the default of `a` is calculated.
(The template converter tool can't do this conversion.)
- Built-ins don't convert their parameters to string anymore; in FM some (not all) of them did, because they
were based on the TemplateMethod interface (deprecated even in FM2) that required String paramteters and
was auto-converted by FM2. (The template converter tool can't do this conversion.)
- The string ranges like `someString[1..0]` (that is, where the end is one less than the beginning) aren't
tolerated anymore, and are errors. (The template converter tool can't do this conversion.)
- Added `?sequence`, which converts an iterable value into a sequence. This is to be used with care, as iterables
that are not also sequences sometimes contain a large number of items, and the resulting sequence will have to
store all of them.
- `?keys` and `?values` (used to return the keys and values in a hash) now returns a collection, not a sequence.
Note that for FM3 collections ?size works, but not accessing by index. This is to be more aligned with the
capabilities of java.util.Map. In case you know that you won't have a too large number of entries, and you need
a sequence of the keys or values, use `myMap?keys?sequence` or `myMap?values?sequence`.
(The template converter tool can't do this conversion. Because `?sequence` is a relatively expensive operation,
it shouldn't be invoked for multiple times on the same value, instead the result should be extracted into a
variable, like: `<#assign keySeq = keys?sequence>[... Do multiple things with keySeq here ...]`.
Java API changes
================
Major changes / features
------------------------
- Modularized the project. Now we have these published jar-s:
- org.apache.freemarker:freemarker-core
- org.apache.freemarker:freemarker-servlet
- org.apache.freemarker:freemarker-dom
- org.apache.freemarker:freemarker-spring
There are several other internal modules related to testing and such; these aren't published.
- Reorganized package structure: Everything starts with org.apache.freemarker, and continues with
subpackage that corresponds to the module (see modularization above). So we have
org.apache.freemarker.core (has nothing to do with the old freemarker.core), org.apache.freemarker.servlet (this
replaced freemarker.ext.servlet and freemarker.ext.jsp), org.apache.freemarker.dom (replaces freemarker.ext.dom), etc.
Directly inside org.apache.freemarker.core we have most of the classes that were in freemarker.template and
freemarker.core, however, model related classes (and object wrappers) were moved out to
org.apache.freemarker.core.model, and template loading and caching related classes to
org.apache.freemarker.core.templateresolver (we have a class called TemplateResolver; see later).
OutputFormat related classes were moved to org.apache.freemarker.core.outputformat.
ValueFormat related classes were moved to org.apache.freemarker.core.valueformat.
ArithmeticEngine related classes were moved to org.apache.freemarker.core.arithmetic.
- Added the org.apache.freemarker.core.templateresolver.TemplateResolver class and the `templateResolver` Configuration
setting. This allows replacing the whole template lookup, loading and caching logic with a custom implementation,
giving total control over this aspect of FreeMarker, in case replacing Configuration settings like `templateLoader`
or `templateCacheStorage` wasn't enough. The `TemplateResolver` implementation declares which of the template resolution
related Configuration settings it supports (for example, it may still want to rely on the `templateLoader`, but
doesn't use the other settings). It's an error to set a template resolution related settings that the TemplateResolver
doesn't support. The default implementation, `DefaultTemplateResolver`, supports all of them of course. Note that the
`TemplateCache` class was removed, and was basically replaced by `DefaultTemplateResolver`.
- Totally redesigned TemplateLoader interface. The FM2 TemplateLoader can't be adapted (wrapped) to it, but usually
it's fairly trivial to "rearrange" an old custom TemplateLoader for the new interface. The new TemplateLoader comes
with several advantages, such as:
- It can work more efficiently with sophisticated storage mechanisms like a database, as it's now possible to pack
together the existence check, the last modification change check, and reading operations into less storage level
operations (like you can do all of them with a single SQL statement).
- The new TemplateLoader allows returning the template content either as an InputStream or as a Reader. Almost all
TemplateLoader-s should return InputStream, and FreeMarker takes care of charset issues transparently (as a result,
TemplateLoader-s don't have to support re-reading a template anymore, as we solve charset detection misses in
memory). TemplateLoader-s that are inherently backed by text (String-s), such as templates stored in a varchar or
CLOB column, should return a Reader. Note that templates created from a Reader will have template.getEncoding()
null (logically, as no charset was involved), which was impossible in FreeMarker 2.
- The change detection of the template doesn't have to rely on a millisecond resolution timestamp anymore; you can
use what's most appropriate for the storage mechanism, such as a cryptographic hash or a revision number.
- Template lookups (where you try multiple names until you find the best template) can now be transactional and/or
atomic if the backing storage mechanism supports that, by utilizing the TemplateLoaderSession interface.
- TemplateLoader can now return template-level settings like the output format (MIME type basically) of the loaded
template, in case the backing storage stores such extra information. This mechanism can be used together with
the TemplateConfiguration mechanism (already familiar from FreeMarker 2), and overrides the individual settings
coming from there.
- Configuration is now immutable. You should use Configuration.Builder to set up the setting values, then create
the Configuration with the builder's build() method.
- Configuration defaults were changed to follow the current best practices (like default charset is UTF-8 everywhere)
- Reworked TemplateModel interfaces (see more details in the later sections):
- Callable `TemplateModel`-s (i.e., models that can be called like `foo(...)` or as `<@foo .../>`) were replaced by
`TemplateFunctionModel` and the reworked `TemplateDirectiveModel` interface. Both of these support padding arguments
by position and by name, even in the same call (e.g., `<@heading "Some title" icon="foo.jpg" />`, `sum(1, 2, 3, abs=true)`).
Because of the extended capabilities of this interface, callables defined inside templates (via `#function` and `#macro`)
are now just create `TemplateFunctionModel` and `TemplateDirectiveModel` objects.
- Listable TemplateModel-s and their intheriance hierarchy are now much more similar the Java collection API:
TemplateIterableModel > TemplateCollectionModel > TemplateSequenceModel [TODO: This last will be TemplateListModel].
- Removed freemarker.ext.log, our log abstraction layer from the times when there was no clear winner on this field.
Added org.slf4j:slf4j-api as required dependency instead.
- Added Spring support to the FreeMarker project (freemarker-spring module), instead of relying Spring developers
[Note: This is in very early stage, as of 2017-07-06.]
- Added `Configuration.templateLanguage` and the `TemplateLanguage` class, which encapsulate the syntax and some of the
semantics, most importantly the `outputFormat`.
- Each `TemplateLanguage` has an associated file extension, which should be sufficient for editors (IDE-s) to chose the
proper syntax highlighting. Syntax auto-detection was removed, as editors couldn't cope with it. Thus, the new file
extensions are somewhat complex, but serve tooling well. They are put together from 3 parts:
1. "f3" for FreeMarker 3. (Extensions starting with "f" are reserved for the FreeMarker project.)
2. Syntax: "a" for `ANGLE_BRACKET` `tagSyntax` and `DOLLAR` `interpolatonSyntax`, or "s" for `SQUARE_BRACKET`
`tagSyntax` and `SQUARE_BRACKET` `interpolationSyntax`. (Other permutations are possible, but has no standard
file extension reserved for them.)
3. Output format: Currently "h" for HTML, "x" for XML, "u" for undefined, "c" for configured (i.e., coming from
the `Configuration` or other kind of `ParsingConfiguration`). This is important for syntax highlighting of the
static parts, hence it's encoded into the file extension too. It's also important for security reasons,
So a common file extensions will be "f3ah".
The FM2 file extensions, "ftl", "ftlh", and "ftlx" by default give error (FM 2 templates not supported) to
prevent confusion.
Smaller Java API changes (categorized)
--------------------------------------
Node: Changes already mentioned above aren't repeated here!
Core / Configuration
....................
- Configuration is now immutable (see earlier), so `Configuration.Builder` was added, which extends
`Configuration.ExtendableBuilder`. It's now possible to change the `Configuration` setting defaults by using a custom
subclass instead of `Configuration.Builder`. (`FreeMarkerServlet` has switched to this approach, using its own
builder subclass to provide defaults that makes the sense in that particular application.)
- setClassForTemplateLoader, setDirectoryForTemplateLoading and the like were removed, instead there's just
setTemplateLoader. So for example. instead of setClassForTemplateLoader(Foo.class, "templates") now you have
to write setTemplateLoader(new ClassTemplateLoader(Foo.class, "templates")). While it's a bit longer, it shows
more clearly what's happening, and always supports all TemplateLoader constructor overloads.
- setSetting (and the like) doesn't throw ParseException (the same exception used when parsing templates) anymore,
but ConfigurationException. Also, on the places where ParseException was used for other than template parsing,
o.a.f.core.util.GenericParseException is used now instead, which doesn't have the template parsing related fields
that we can't fill.
- Removed `String Configurable.getSetting(String)` and `Properties getSettings()`. It has never worked well,
and is impossible to implement properly.
- Added ProcessingConfiguration interface for the read-only access of template processing settings. This is similar to
the FM2 ParserConfiguration interface.
- Renamed Configurable to MutableProcessingAndParserConfiguration. Made it abstract too.
- Made Template immutable (via public API-s). Template-specific settings now can only come from the
TemplateConfiguration associated to the template, or from the #ftl header for some settings (most notably for
custom attributes).
- Renamed ParserConfiguration to ParsingConfiguration, so that the name is more consistent with the new
ProcessingConfiguration.
- Changed the defaults of some Configuration settings:
- Changed the default of sourceEncoding ("encoding" earlier) to UTF-8 from the platform default charset.
Using the platform default charset was proven to be fragile in practice,
like applications could break when moved to another server if the application
was unwillingly relying on the default.
- Changed the default of templateExceptionHandler (template_exception_hander) to
TemplateExceptionHandler.RETHROW from DEBUG
- Changed the default of sqlDateAndTimeTimeZone (sqlDateAndTimeTimeZone) to
TimeZone.default() from null (where null meant falling back to the timeZone setting value)
- Changed the default of templateNameFormat (templateNameFormat) to what's equivalent to
FM2 DefaultTemplateNameFormat24. The old DefaultTemplateNameFormat was removed, and
DefaultTemplateNameFormat24 was renamed to DefaultTemplateNameFormat.
- Removed the logTemplateExceptions (log_template_exceptions) setting. FreeMarker now behaves as if it was false.
When a FreeMarker method throws an exception, the caller is responsible for either logging it or letting it bubble up.
- Removed the wrapUncheckedExceptions setting. FreeMarker now behaves as if it was true.
- Removed the strictSyntax setting, and so also the support for FTL tags without #. This was a FreeMarker 1.x
compatibility option.
- The `tagSyntax` setting doesn't support the `autoDetect` value anymore. It was removed mostly because tools (editors)
could almost never implement it. Instead, there will be ([TODO]) separate file extensions for each syntax variation.
- Renamed the `cacheStorage` Configuration setting to `templateCacheStorage`.
- Renamed the `localizedLookup` Configuration setting to `localizedTemplateLookup`.
- Renamed the `datetimeFormat` Configuration setting to `dateTimeFormat` (following Java 8 convention).
- Renamed the String key for the historically incorrect `autoInclude` Configuration setting to `autoIncludes`.
- Renamed the String key for the historically incorrect `autoImport` Configuration setting to `autoImports`.
- TemplateClassResolver.UNRESTRICTED_RESOLVER and ALLOWS_NOTHING_RESOLVER was renamed
to UNRESTRICTED and ALLOW_NOTHING. Also the String setting name "allows_nothing" and
"allowsNothing" were renamed to "allowNothing".
- TemplateExceptionHandler.IGNORE_HANDLER, RETHROW_HANDLER, DEBUG_HANDLER and
HTML_DEBUG_HANDLER was renamed to IGNORE, RETHROW, DEBUG and HTML_DEBUG
- AttemptExceptionReporter.LOG_ERROR_REPORTER and LOG_WARN_REPORTER was renamed to
LOG_ERROR and LOG_WARN (to be consistent with the new TemplateExceptionHandler names)
- Removed TemplateClassResolver.SAFER_RESOLVER, because the classes it has blocked were removed from FreeMarker, so it's
the same as UNRESTRICTED_RESOLVER
- When specifying the templateUpdateDelay configuration setting with a String (with Properties), the time unit is
required, unless the value is 0.
- Even for setting values that are class names without following `()` or other argument list, the INSTANCE field and
the builder class will be searched now, and used instead of the constructor of the class. Earlier they weren't for
backward compatibility.
- Removed the static default Configuration instance. (It's not possible to create a template with null Configuration
constructor argument anymore.)
- The strict_bean_models configuration setting was removed, as it should be set on the BeansWrapper itself
- When looking for a builder class in builder expressions used in setting values like `com.example.Foo()`, now we first
look for com.example.Foo.Builder, and only then com.example.FooBuilder.
- Removed Configuration.Builder.setEncoding(java.util.Locale, String) and the related other methods. Because of the new
logic of template encodings, the locale to encoding mapping doesn't make much sense anymore.
- As "attributes" can't be modifiable now that Configuration is immutable, and hence didn't fit into the new system,
the "attributes" setting was removed. In place of that, two new concepts were introduced, targeting two different
"attributes" use-cases:
- Custom states: CustomStateKey class and the CustomStateScope interface was introduced, which
is somewhat similar to the now removed CustomAttribute. CustomStateScope contains one method, Object
`getCustomState(CustomStateKey)``, which may calls `CustomStateKey.create()` to lazily create the state object
for the key. `Configuration`, `Template` and `Environment` implements `CustomStateScope`, so you can store state
in any of these scopes.
- Custom settings: These have the same mutability rules as normal settings, that is, you can't modify them except in
builder objects and in the `Environment`. You can get their values with `getCustomSetting(Serializble key[, Object
defaultValue])`. If no default is specified, and the custom setting is not set, a `CustomSettingNotSetException`
is thrown. Just like standard settings, custom settings automatically inherited from higher scopes.
- In the `#ftl` header the `attributes` parameter name was changed to `customSettings`
(The template converter tool takes care of this conversion.)
- Renamed Configuration.defaultEncoding to sourceEncoding, also added sourceEncoding to ParserConfiguration, and renamed
TemplateConfiguration.encoding and Template.encoding to sourceEncoding. (Before this, defaultEncoding was exclusive
to Configuration, but now it's like any other ParserConfiguration setting that can be overridden on the 3 levels.)
- Settings that have contained a charset name (sourceEncoding, outputEncoding, URLEscapingCharset) are now of type Charset,
not String. For string based configuration sources (such as .properties files) this means that:
- Unrecognized charset names are now errors
- For recognized names the charset name will be normalized (like "latin1" becomes to "ISO-8859-1").
- In "object builder expressions" Charset values can now be constructed like `Charset("ISO-8859-5")`.
Note that as the type of the settings have changed, now you can't just write something like
`TemplateConfiguration(sourceEncoding = "UTF-8")`, but `TemplateConfiguration(sourceEncoding = Charset("UTF-8"))`.
- Configuration-related int constants were replaced with enums:
- FM2 Configuration.${c}_TAG_SYNTAX with TagSyntax.${c}
- FM2 Configuration.${c}_INTERPOLATION_SYNTAX with InterpolationSyntax.${c}
- FM2 Configuration.${c}_AUTO_ESCAPING_POLICY with AutoEscapingPolicy.${c}
- Removed hasCustomFormats() from configuration related API-s (we don't need it anymore)
- The tagSyntax and interpolationSyntax settings were moved inside the templateLanguage setting, as they are only
applicable if the templateLanguage is a DefaultTemplateLanguage instance.
- Removed Configuration.fallbackOnNullLoopVariable, FM3 never falls back on such loop variables (as if the FM2
setting was set to false)
Core / Models and Object wrapping
.................................
- DefaultObjectWrapper is now immutable (has no setter methods), so most of the mutator methods (like setters) were
moved rom Configuration to Configuration.Builder.
- `DefaultObjectWrapper` now has a configuration setting, `extensions`, to add `DefaultObjectWrapperExtension`-s, which
meant to be used for wrapping application-specific objects specially. Along with this, `DefaultObjectWrapper` now has
two protected methods to customze the wrapping logic, `wrapSpecialObject(Object)` and `wrapGenericObject(Object)`; the
last replaces the `handleUnknownType(Object)` method from FM2. `DefaultObjectWrapperExtension`-s are applied before or
after `wrapSpecialObject`, depending on what `DefaultObjectWrapperExtension.getPhase()` returns. See the JavaDoc of
`DefaultObjectWrapper.wrap(Object)` for more on wrapping phases.
- `DefaultObjectWrapper` doesn't wrap W3C DOM nodes (XML) specially anymore, as DOM wrapping was factored out to a
separate jar (freemarker-dom) as part of modularizing FreeMarker. To ensure that DOM nodes are wrapped specially as
in FM2, the `extensions` setting of the `DefaultObjectWrapper` has to be so that it contains
`DOMDefaultObjectWrapperExtension.INSTANCE`. For example:
cfgBuilder.objectWrapper(
new DefaultObjectWrapper.Builder(Configuration.VERSION_3_0_0)
.extensions(DOMDefaultObjectWrapperExtension.INSTANCE)
.build())
- Moved the all the static final ObjectWrapper-s to the new _StaticObjectWrappers class, and made them
write protected (non-configurable). Also now they come from the pool that ObjectWrapper builders use.
- WrappingTemplateModel.objectWrapper is now final, and its statically stored default value can't be set anymore.
- Removed SimpleObjectWrapper deprecated parameterless constructor
- Removed the deprecated BeansWrapper.nullModel setting. So null is always wrapped to null now.
- Removed the overridable BeansWrapper.finetuneMethodAppearance method, which was deprecated by the
finetuneMethodAppearance setting (BeansWrapper.setFinetuneMethodAppearance).
- Removed BeansWrapper, which was the superclass of DefaultObjectWrapper, but wasn't recommended to be used as is.
Removed many BeansWrapper-related classes that DefaultObjectWrapper doesn't use. This includes ModelCache and
related classes, because DefaultObjectWrapper has only used the cache for "generic" classes (because that's where it
has fallen back to BeansWrapper.wrap), which is inconsistent and doesn't worth the caching overhead and complexity.
- Removed parameterless DefaultObjectWrapper and BeansWrapper constructors. Now specifying the
incomplatibleImprovement version is required.
- Removed DefaultObjectWrapper settings that only exist so that you can set backward compatible behavior instead of
the recommended value: useAdaptersForContainers, forceLegacyNonListCollections, iterableSupport, simpleMapWrapper
- Java methods (when using DefaultObjectWrapper) won't be accessible as sequences anyore. That is, earlier, instead of
obj.m(1), you could write obj.m[1]. This strange feature has led to some tricky cases, while almost nobody has
utilized it.
- SimpleObjectWrapper was renamed to RestrictedObjectWrapper. When configuring with properties, the `simple` setting
value for the `objectWrapper` (`object_wrapper`) setting is not supported anymore, instead `RestrictedObjectWrapper(3.0.0)`
can be written.
- Removed the global static final ObjectWrapper-s. It had a "few" consequences:
- Standard TemplateModel implementations that can have an ObjectWrapper contrucor parameter don't allow null there anymore.
Also, any constructor overloads where you cold omit the ObjectWrapper were removed (these were deprecated in FM2 too).
In FM2, such overloads has used the global static default DefaltObjectWrapper, but that was removed.
- If the ObjectWrapper is not a DefaultObjectWrapper (or a subclass of it), `className?new(args)` will only accept 0 arguments.
(Earlier we have fallen back to using the global static default DefaultObjectWrapper instance to handle argument unwrapping
and overloaded constructors.) Note that ?new is only used to instantiate TemplateModel-s, typically, template language
functions/directives implemented in Java, and so they hardly ever has an argument.
- FreemarkerServlet now requires that the ObjectWrapper it uses implements ObjectWrapperAndUnwrapper. (Thus, the return type
of FreemarerServlet.createDefaultObjectWrapper() has changed to ObjectWrapperAndUnwrapper.) The unwrapping functionality is
required when calling JSP custom tags/functions, and in FreeMarker 2 this was worked around with using the
global static default DefaultObjectWrapper when the ObjectWrapper wasn't an ObjectWrapperAndUnwrapper.
- If for an indexed JavaBean property there's both an indexed read method (like `Foo getFoo(int index)`) and a normal read method
(like Foo[] getFoo()), we prefer the normal read method, and so the result will be a clean FTL sequence (not a multi-type value
with sequence+method type). If there's only an indexed read method, then we don't expose the property anymore, but the indexed
read method can still be called as an usual method (as `myObj.getFoo(index)`). These changes were made because building on the
indexed read method we can't create a proper sequence (which the value of the property should be), since sequences are required
to support returning their size. (In FreeMarker 2 such sequences has thrown exception on calling size(), which caused more
problems and confusion than it solved.)
- Removed DefaultObjectWrapper.methodsShadowItems setting, in effect defaulting it to true. This has decided if the generic
get method (`get(String)`) had priority over methods of similar name. The generic get method is only recognized from its
name and parameter type, so it's a quite consfusing feature, and might will be removed alltogether.
- Renamed TemplateDateModel.DATETIME to DATE_TIME (to be consistent with "dateTime" used elsewhere).
- `TemplateMethod` and `TemplateMethodEx` was removed, taken over by `TemplateFunctionModel`. `TemplateFunctionModel` is
the common interface bith for wrapped Java methods and functions defined in the templates, or on any other ways.
`OverloadedMethodsModel` and `SimpleMethodModel` were renamed to `OverloadJavaMethodModel` and `SimpleJavaMethodModel`,
which of course extend `TemplateFunctionModel`, but allow inocations without `Environment` parameter.
- `org.apache.freemarker.core.util.CallableUtils` was added to help implementing function and directives in Java.
- `TemplateModelException` was removed, replaced with `TemplateException` on all places, except for
`ObjectWrapper.wrap(Object)` and `wrapAsAPI(Object)`, which now throws `ObjectWrappingException` instead (that extends
`TemplateException`).
In FM2 it `TemplateModelException` has extended `TemplateException` and was thrown by `TemplateModel` methods. As it
has turned out over time, some models call into FreeMarker functionality that can throw `TemplateException`, but the
method of the model couldn't throw that as `TemplateModelException` is more specific. Now it can. Also it's not very
useful in practice to catch `TemplateModelException` and the more generic `TemplateException` separately, so this
simplification was made.
- Reworked TemplateModel interfaces:
- Reworked callable `TemplateModel`-s (i.e., models that can be called like `foo(...)` or as `<@foo .../>`)
- Earlier there were several callable `TemplateModel` internfaces (`TemplateMethodModel`, `TemplateMethodModelEx`,
`TemplateDirectiveModel`, `TemplateTransformModel`). FM3 replaces them with only two new interfaces,
`TemplateDirectiveModel` (differs from the interface with identical name in FM2) and `TemplateFunctionModel`.
(These are both the subinterfaces of another new interface `TemplateCallableModel`.)
- All callable TemplateModel-s support passing parameters by position and by name, even in the same call
(e.g., `<@heading "Some title" icon="foo.jpg" />`, `sum(1, 2, 3, abs=true)`)
- `#macro` now produces a `TemplateDirectiveModel` and `#function` produces a `TemplateFunctionModel`. (Earlier, the
product was just a generic `TemplateModel` that could only be invoked using internal API-s, and had capabilities
that the callable TemplateModel-s coulnd't have.)
- Renamed `TemplateScalarModel` to `TemplateStringModel`. (The `TemplateScalarModel` name come from the early FM,
where strings, numbers, booleans, etc. weren't separated.)
- Reworked the list-like TemplateModel interfaces:
- Renamed `TemplateCollectionModel` to `TemplateIterableModel`, and `TemplateCollectionModelEx` to
`TemplateCollectionModel`. (Since java.util.Iterable was added to Java, the TemplateCollectionModel
has become an unfortunate name, especailly as later TemplateCollectionModelEx was added, that was closer
to java.util.Collection than TemplateCollectionModel.)
- `TemplateSequenceModel` now extends `TemplateCollecitonModel`, and `#list` and other built-in mechanims prefer
using `iterator()` instead of an index loop.
- `TemplateModelIterator.next()` isn't required to handle anymore the case when there's no next item. If someone
doesn't check that with `hasNext()` before calling `next()`, anything can happen. This is to allow more efficient
implementations, which has become important now that `#list` and such prefers iterators over index loops.
- `isEmpty` of FM2 `TemplateCollectionModelEx` (now `TemplateCollectionModel`) and FM2 `TemplateHashModel` was renamed
to `isEmptyCollection` and `isEmptyHash`, so that for multi-typed values can give different results depending on the
type. Also, for collections `size()` was renamed to `getCollectionSize()`, and for hashes `getHashSize()`.
- `TemplateSequenceModel.get(int)` is now expected to actually return `null` for any invalid index. In FM2 this was
the documented expectation, but many implementaitons throw exception instead of returning `null`, but FreeMarker
has tolerated that.
- `TemplateHashModelEx.keys()` and `values()` returns `TemplateCollectoinModel`, just as in FM2, but in FM3
`TemplateCollectoinModel` has `getCollectionSize()` and `isEmptyCollection` method. So this affects
`TemplateHashModelEx` implementations as well.
- Map-like interfaces:
- `TemplateHashModelEx2` was removed, as the `keyValuePairIterator()` method was moved to `TemplateHashModelEx`,
so now the two interfaces would be the same.
- `TemplateHashModel` doesn't have `isEmpty()` method anymore. (As the point of a pure `TemplateHashModel` is that
it's not able to list its keys, almost all `isEmpty()` implementations in FM2 were just dummies returning `false`.)
Note that `?hasContent` now returns `true` for `TemplateHashModel` that aren't also `TemplateHashModelEx2`-s,
based on the idea that for some `key` (which you may don't know) `get(key)` might return something.
- `TemplateModel.NOTHING` was removed without replacement.
- BeanModel.keys() and values() are now final methods. Override BeanModel.keySet() and/or get(String) instead.
- Methods that return void now return an empty string instead of `TemplateModel.NOTHING` (which was removed).
Thus, `${obj.voidReturningMethod()}` still works (it prints nothing, just as in FM2), but things like
`x + obj.voidReturningMethod()` now fail (unlike in FM2), as they are probably oversights. This all applies to
Java methods wrapped by the DefaultObjectWrapper (which, in FM3, is also used in place of the FM2 BeansWrapper).
- Added `null` literal (so null is now a keyword), and TemplateNullModel (it wasn't public in FM2).
[TODO] This change is incomplete, and eventually it will mean that the only legal way of representing a
Java null where a TemplateModel is expected will be TemplateNullModel.INSTANCE, not a Java null.
- Whem listing something that contains a null, reading the loop variable will never fall back to higher scopes
(like when configuration.fallbackOnNullLoopVariable was set to false in FM2)
Core / Template loading and caching
...................................
- Template constructors won't close the Reader passed in as argument anymore (because a Reader should be closed
by who has created it). This avoids some surprises from the past, such as the unablility to reset a Reader to a mark
after parsing. If you call those constructors, be sure that you close the Reader yourself. (Obviously, it doesn't
mater for StringReader-s.)
- All CacheStorage-s must be thread safe from now on (removed ConcurrentCacheStorage marker interface)
- Configuration.getTemplate has no "parse" parameter anymore. Similarly #include has no "parse" parameter anymore. Whether a
template is parsed can be specified via Configuration.templateConfigurations, for example based on the file extension. Thus,
a certain template is either always parsed or never parsed, and whoever gets or include it need not know about that.
Also added a new setting, "templateLanguage", which decides this; the two available values are
TemplateLanguage.FTL and TemplateLanguage.STATIC_TEXT.
- Configuration.getTemplate has no "encoding" parameter anymore. (Similarly #include has no "encoding" parameter
either). The charset of templates can be specified via Configuration.sourceEncoding and Configuration
.templateConfigurations (for example based on the directory it is in), or with the #ftl directive inside the
template. Thus, a given template always has the same charset, no mater how it's accessed.
- Require customLookupCondition-s to be Serializable.
- BeansWrapper.clearClassIntrospecitonCache was renamed to clearClassIntrospectionCache (note the typo)
DOM (XML)
.........
- Removed NodeModel static utility classes dealing with parsing XML to DOM. How it's best to do that is environment
and application dependent, and it has security implications. Since XML loading/parsing is not the topic of the
project, these were removed. Static methods that simplify an already loaded DOM have remained, because that's
FreeMarker-specific functionality.
Servlet
.......
- `FreeMarkerServlet` now use a `Configuration.ExtendableBuilder` to provide configuration defaults that makes for it.
Its API-s which were used for customizing `FreeMarkerServlet` has bean changed accordingly.
Spring
.......
This is about the Spring Framework Support (freemarker-spring): FREEMARKER-54, FREEMARKER-55
- ConfigurationFactoryBean, a new Spring Framework's FactoryBean to create Configuration, using Builder.
- SpringResourceTemplateLoader, a new TemplateLoader to load templates from Spring Framework's Resources.
- New FreeMarkerView and FreeMarkerViewResolver for MVC support. FreeMarkerView supports TaglibFactory and other
models by default like FreemarkerServlet does.
- Directives and Functions Support to replace Spring JSP Tag Libraries in spring.tld:
- <spring:htmlEscape ... /> : No need since FreeMarker Built-In's and escaping directives are better.
- <spring:escapeBody ... /> : No need since FreeMarker Built-In's and escaping directives are better.
- <spring:message ... /> : Replaced by spring.message function. e.g, ${spring.message(...)}
- <spring:theme ... /> : Replaced by spring.theme function. e.g, ${spring.theme(...)}
- <spring:argument ... /> : No need since spring.message(...) and spring.theme(...) functions support
positional varargs for variable length arguments.
- <spring:hasBindErrors ... /> : Replaced by <@spring.hasBindErrors ... /> directive.
- <spring:nestedPath ... /> : Replaced by <@spring.nestedPath ... /> directive.
- <spring:bind ... /> : Replaced by <@spring.bind ... /> directive.
- <spring:transform ... /> : Replaced by spring.transform(...) function.
- <spring:url ... /> : Replaced by spring.url(...) function.
- <spring:param ... /> : No need since spring.url(...) function supports named vargs for variable length parameters.
- <spring:eval /> : Replaced by spring.eval(...) function.
- Directives Support to replace Spring Form JSP Tag Libraries in spring-form.tld:
- <form:form ... /> : Replaced by <@form.form ... /> directive.
- <form:input ... /> : Replaced by <@form.input ... /> directive.
- <form:password ... /> : Replaced by <@form.password ... /> directive.
- <form:textarea ... /> : Replaced by <@form.textarea ... /> directive.
- <form:hidden ... /> : Replaced by <@form.hidden ... /> directive.
- <form:button ... /> : Replaced by <@form.button ... /> directive.
- <form:label ... /> : Replaced by <@form.label ... /> directive.
- <form:errors ... /> : Replaced by <@form.errors ... /> directive.
- <form:select ... /> : Replaced by <@form.select ... /> directive.
- <form:option ... /> : Replaced by <@form.option ... /> directive.
- <form:options ... /> : Replaced by <@form.options ... /> directive.
- <form:checkbox ... /> : Replaced by <@form.checkbox ... /> directive.
- <form:checkboxes ... /> : Replaced by <@form.checkboxes ... /> directive.
- <form:radiobutton ... /> : Replaced by <@form.radiobutton ... /> directive.
- <form:radiobuttons ... /> : Replaced by <@form.radiobuttons ... /> directive.
Core / Miscellaneous
....................
- Minimum Java version increased to 17
- Removed support for incompatibleImprovements before 3.0.0. So currently 3.0.0 is the only support value.
- Removed legacy extensions (so these have no module): rhino, jython, xml (not to be confused with dom), jdom, ant.
- Servlet 3.1 and JSP 2.3 and is the minimum requirement now (if Serlvet/JSP features are used at all).
- Log categories are now simply the full qualified class name of the logging classes. There are no predefined log
categories (such as "freemarker.runtime", etc.) anymore. This is to follow the common practices in logging.
- #include-d/#import-ed templates don't inherit the charset (encoding) of the #include-ing/#import-ing template.
(Because, the charset of a template file is now independent of how you access it; see in template loading changes.)
- #attempt doesn't create an additional debug level log entry when an exception is catched. It only
log an error level log (by default at least; see the `attemptExceptionReporter` setting)
- Marked most static utility classes as internal, and renamed them to start with "_" (for example StringUtils was
renamed to _StringUtil, thus people won't accidentally use it when they wanted to autocomplete to Apache Commons
StringUtil). Created published static utility class, o.a.f.core.util.FTLUtil, which contains some methods moved
over from the now internal utility classes.
- Template.sourceEncoding was renamed to Template.actualSourceEncoding, to emphasize that it's not the template-layer
equivalent of the sourceEncoding ParserConfiguration setting. This is in line with Template.actualTagSyntax and the
other "actual" properties. (Just as in FM2, Template.getParserConfiguration() still can be used get the
sourceEncoding used during parsing.)
- Template.name (getName()) was renamed to Template.lookupName (getLookupName()), and Template.sourceName (Template.getSourceName())
doesn't fall back to the lookup name anymore when it's null (however, Template.getSourceOrLookupName() was added for that). There's
no Template.name anymore, because since sourceName was introduced, and hence the concept of template name was split into the
lookup and the source name, its meaning wasn't clean (but it meant the lookup name). TemplateException and ParseException
now also have the same properites: getTemplateSourceName(), getTemplateLookupName(), and even getSourceOrLookupName().
Location information in error messages show getTemplateSourceOrLookupName().
- Removed all classes with "main" methods that were part of freemarker.jar. Such tools should be separate artifacts,
not part of the library, and they are often classified as CWE-489 "Leftover Debug Code". The removed classes are:
freemarker.core.CommandLine, freemarker.ext.dom.Transform, freemarker.template.utility.ToCanonical
- Removed classic_compatible (classicCompatible) setting, which was used to emulate some of the FreeMarker 1.x behavior
- Removed utility TemplateModel-s that can very easily mean a security problem: freemarker.template.utility.Execute and
freemarker.template.utility.ObjectConstructor
- Removed ResourceBundleLocalizedString and LocalizedString: Hardly anybody has discovered these, and they had no
JUnit coverage.
- Deleted o.a.f.core.util.DOMNodeModel (it has noting to do with the standard XML support, o.a.f.core.model.dom)
- Removed deprecated FMParser constructors.
- Removed Template.templateLanguageVersion, as we solely rely on incompatibleImprovements instead.
- Made TemplateModel classes used by the parser for literals Serializable. (Without this attribute values set in the
`#ftl` header wouldn't be always Serializable, which in turn will sabotage making Template-s Serializable in the
future.)
- util.ObjectFactory was renamed to CommonSupplier, and its createObject() method was renamed to
get(), to be more similar to the Java 8 API.
- Removed the legacy predefined shared variables: "html_escape", "normalize_newlines", "xml_escape", "capture_output",
"compress"
- Removed `NestedContentNotSupportedException`, as `TemplateDirectiveModel.isNestedContentSupported()` now takes care
of that problem.
- CallPlaceCustomDataInitializationException is not a checked exception anymore (now it extends RuntimeException)
- Renamed `FTLUtil` to `TemplateLanguageUtils` (as the FTL name will be abandoned in FM3, and supporting multiple
languages are planned in the future)
- Removed `UnexpectedTypeException` (a `TemplateException` subclass) and its subclasses (`NonStringException`, etc.),
using simple `TemplateException` instead. Catching these exceptions specifically wasn't very useful, while they
have bloated the public API (and the code).
- Added `org.apache.freemarker.core.util.CallableUtils`, to help in implementing (and invoking) `TemplateCallableModel`-s.
- Renamed `Environment.getDataModel()` to `getDataModelWithSharedVariableFallback()`
Renamed `Environment.getGlobalVariables()` to `getGloballyVisibleVariables()()`
- Removed `Template.createPlainTextTemplate`, as this convenience methods was hardly ever used.
- The extended BigDecimal format parser doesn't accept the "multipier" parameter name anymore. It has to be written with
correct spelling, as "multiplier".
- The booleanFormat setting now defaults to "", just like numberFormat, etc. It's not possible to format a boolean with
that setting value, it will give an error. On the other hand, "true,false" is now a legit setting value (although the
better practice is to set the format to "c", if you generate computer language output), but it's hardly a good practice
to use.