/*
 * 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.
 */

package freemarker.template;

import java.io.File;
import java.io.IOException;
import java.io.Writer;
import java.lang.reflect.InvocationTargetException;
import java.net.URLConnection;
import java.text.DecimalFormat;
import java.text.SimpleDateFormat;
import java.util.Collection;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Properties;
import java.util.Set;
import java.util.TimeZone;
import java.util.TreeSet;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;

import freemarker.cache.CacheStorage;
import freemarker.cache.ClassTemplateLoader;
import freemarker.cache.FileTemplateLoader;
import freemarker.cache.MruCacheStorage;
import freemarker.cache.MultiTemplateLoader;
import freemarker.cache.SoftCacheStorage;
import freemarker.cache.TemplateCache;
import freemarker.cache.TemplateCache.MaybeMissingTemplate;
import freemarker.cache.TemplateConfigurationFactory;
import freemarker.cache.TemplateLoader;
import freemarker.cache.TemplateLookupContext;
import freemarker.cache.TemplateLookupStrategy;
import freemarker.cache.TemplateNameFormat;
import freemarker.cache.URLTemplateLoader;
import freemarker.core.BugException;
import freemarker.core.CSSOutputFormat;
import freemarker.core.CombinedMarkupOutputFormat;
import freemarker.core.Configurable;
import freemarker.core.Environment;
import freemarker.core.HTMLOutputFormat;
import freemarker.core.JSONOutputFormat;
import freemarker.core.JavaScriptOutputFormat;
import freemarker.core.MarkupOutputFormat;
import freemarker.core.OutputFormat;
import freemarker.core.ParseException;
import freemarker.core.ParserConfiguration;
import freemarker.core.PlainTextOutputFormat;
import freemarker.core.RTFOutputFormat;
import freemarker.core.TemplateConfiguration;
import freemarker.core.TemplateMarkupOutputModel;
import freemarker.core.UndefinedOutputFormat;
import freemarker.core.UnregisteredOutputFormatException;
import freemarker.core.XHTMLOutputFormat;
import freemarker.core.XMLOutputFormat;
import freemarker.core._CoreAPI;
import freemarker.core._DelayedJQuote;
import freemarker.core._MiscTemplateException;
import freemarker.core._ObjectBuilderSettingEvaluator;
import freemarker.core._SettingEvaluationEnvironment;
import freemarker.core._SortedArraySet;
import freemarker.core._UnmodifiableCompositeSet;
import freemarker.ext.beans.BeansWrapper;
import freemarker.ext.beans.BeansWrapperBuilder;
import freemarker.log.Logger;
import freemarker.template.utility.CaptureOutput;
import freemarker.template.utility.ClassUtil;
import freemarker.template.utility.Constants;
import freemarker.template.utility.HtmlEscape;
import freemarker.template.utility.NormalizeNewlines;
import freemarker.template.utility.NullArgumentException;
import freemarker.template.utility.SecurityUtilities;
import freemarker.template.utility.StandardCompress;
import freemarker.template.utility.StringUtil;
import freemarker.template.utility.XmlEscape;

The main entry point into the FreeMarker API; encapsulates the configuration settings of FreeMarker, also serves as a central template-loading and caching service.

This class is meant to be used in a singleton pattern. That is, you create an instance of this at the beginning of the application life-cycle, set its configuration settings there (either with the setter methods like setTemplateLoader(TemplateLoader) or by loading a .properties file), and then use that single instance everywhere in your application. Frequently re-creating Configuration is a typical and grave mistake from performance standpoint, as the Configuration holds the template cache, and often also the class introspection cache, which then will be lost. (Note that, naturally, having multiple long-lived instances, like one per component that internally uses FreeMarker is fine.)

The basic usage pattern is like:

 // Where the application is initialized; in general you do this ONLY ONCE in the application life-cycle!
 Configuration cfg = new Configuration(VERSION_X_Y_Z));
 // Where VERSION_X_Y_Z enables the not-100%-backward-compatible fixes introduced in // FreeMarker version X.Y.Z and earlier (see Configuration(Version)). cfg.setSomeSetting(...);
 cfg.setOtherSetting(...); ... // Later, whenever the application needs a template (so you may do this a lot, and from multiple threads): Template myTemplate = cfg.getTemplate("myTemplate.ftlh"); myTemplate.process(dataModel, out);

A couple of settings that you should not leave on its default value are:

A Configuration object is thread-safe only after you have stopped modifying the configuration settings, and you have safely published it (see JSR 133 and related literature) to other threads. Generally, you set everything directly after you have instantiated the Configuration object, then you don't change the settings anymore, so then it's safe to make it accessible (again, via a "safe publication" technique) from multiple threads. The methods that aren't for modifying settings, like getTemplate(String), are thread-safe.

/** * <b>The main entry point into the FreeMarker API</b>; encapsulates the configuration settings of FreeMarker, * also serves as a central template-loading and caching service. * * <p>This class is meant to be used in a singleton pattern. That is, you create an instance of this at the beginning of * the application life-cycle, set its {@link #setSetting(String, String) configuration settings} there (either with the * setter methods like {@link #setTemplateLoader(TemplateLoader)} or by loading a {@code .properties} file), and then * use that single instance everywhere in your application. Frequently re-creating {@link Configuration} is a typical * and grave mistake from performance standpoint, as the {@link Configuration} holds the template cache, and often also * the class introspection cache, which then will be lost. (Note that, naturally, having multiple long-lived instances, * like one per component that internally uses FreeMarker is fine.) * * <p>The basic usage pattern is like: * * <pre> * // Where the application is initialized; in general you do this ONLY ONCE in the application life-cycle! * Configuration cfg = new Configuration(VERSION_<i>X</i>_<i>Y</i>_<i>Z</i>)); * // Where VERSION_<i>X</i>_<i>Y</i>_<i>Z</i> enables the not-100%-backward-compatible fixes introduced in * // FreeMarker version X.Y.Z and earlier (see {@link #Configuration(Version)}). * cfg.set<i>SomeSetting</i>(...); * cfg.set<i>OtherSetting</i>(...); * ... * * // Later, whenever the application needs a template (so you may do this a lot, and from multiple threads): * {@link Template Template} myTemplate = cfg.{@link #getTemplate(String) getTemplate}("myTemplate.ftlh"); * myTemplate.{@link Template#process(Object, java.io.Writer) process}(dataModel, out);</pre> * * <p>A couple of settings that you should not leave on its default value are: * <ul> * <li>{@link #setTemplateLoader(TemplateLoader) template_loader}: The default value is deprecated and in fact quite * useless. (For the most common cases you can use the convenience methods, * {@link #setDirectoryForTemplateLoading(File)} and {@link #setClassForTemplateLoading(Class, String)} and * {@link #setClassLoaderForTemplateLoading(ClassLoader, String)} too.) * <li>{@link #setDefaultEncoding(String) default_encoding}: The default value is system dependent, which makes it * fragile on servers, so it should be set explicitly, like to "UTF-8" nowadays. * <li>{@link #setTemplateExceptionHandler(TemplateExceptionHandler) template_exception_handler}: For developing * HTML pages, the most convenient value is {@link TemplateExceptionHandler#HTML_DEBUG_HANDLER}. For production, * {@link TemplateExceptionHandler#RETHROW_HANDLER} is safer to use. * <!-- 2.4: recommend the new object wrapper here --> * </ul> * * <p>A {@link Configuration} object is thread-safe only after you have stopped modifying the configuration settings, * and you have <b>safely published</b> it (see JSR 133 and related literature) to other threads. Generally, you set * everything directly after you have instantiated the {@link Configuration} object, then you don't change the settings * anymore, so then it's safe to make it accessible (again, via a "safe publication" technique) from multiple threads. * The methods that aren't for modifying settings, like {@link #getTemplate(String)}, are thread-safe. */
public class Configuration extends Configurable implements Cloneable, ParserConfiguration { private static final Logger CACHE_LOG = Logger.getLogger("freemarker.cache"); private static final String VERSION_PROPERTIES_PATH = "/freemarker/version.properties";
Legacy, snake case (like_this) variation of the setting name. @since 2.3.23
/** Legacy, snake case ({@code like_this}) variation of the setting name. @since 2.3.23 */
public static final String DEFAULT_ENCODING_KEY_SNAKE_CASE = "default_encoding";
Modern, camel case (likeThis) variation of the setting name. @since 2.3.23
/** Modern, camel case ({@code likeThis}) variation of the setting name. @since 2.3.23 */
public static final String DEFAULT_ENCODING_KEY_CAMEL_CASE = "defaultEncoding";
Alias to the ..._SNAKE_CASE variation due to backward compatibility constraints.
/** Alias to the {@code ..._SNAKE_CASE} variation due to backward compatibility constraints. */
public static final String DEFAULT_ENCODING_KEY = DEFAULT_ENCODING_KEY_SNAKE_CASE;
Legacy, snake case (like_this) variation of the setting name. @since 2.3.23
/** Legacy, snake case ({@code like_this}) variation of the setting name. @since 2.3.23 */
public static final String LOCALIZED_LOOKUP_KEY_SNAKE_CASE = "localized_lookup";
Modern, camel case (likeThis) variation of the setting name. @since 2.3.23
/** Modern, camel case ({@code likeThis}) variation of the setting name. @since 2.3.23 */
public static final String LOCALIZED_LOOKUP_KEY_CAMEL_CASE = "localizedLookup";
Alias to the ..._SNAKE_CASE variation due to backward compatibility constraints.
/** Alias to the {@code ..._SNAKE_CASE} variation due to backward compatibility constraints. */
public static final String LOCALIZED_LOOKUP_KEY = LOCALIZED_LOOKUP_KEY_SNAKE_CASE;
Legacy, snake case (like_this) variation of the setting name. @since 2.3.23
/** Legacy, snake case ({@code like_this}) variation of the setting name. @since 2.3.23 */
public static final String STRICT_SYNTAX_KEY_SNAKE_CASE = "strict_syntax";
Modern, camel case (likeThis) variation of the setting name. @since 2.3.23
/** Modern, camel case ({@code likeThis}) variation of the setting name. @since 2.3.23 */
public static final String STRICT_SYNTAX_KEY_CAMEL_CASE = "strictSyntax";
Alias to the ..._SNAKE_CASE variation due to backward compatibility constraints.
/** Alias to the {@code ..._SNAKE_CASE} variation due to backward compatibility constraints. */
public static final String STRICT_SYNTAX_KEY = STRICT_SYNTAX_KEY_SNAKE_CASE;
Legacy, snake case (like_this) variation of the setting name. @since 2.3.23
/** Legacy, snake case ({@code like_this}) variation of the setting name. @since 2.3.23 */
public static final String WHITESPACE_STRIPPING_KEY_SNAKE_CASE = "whitespace_stripping";
Modern, camel case (likeThis) variation of the setting name. @since 2.3.23
/** Modern, camel case ({@code likeThis}) variation of the setting name. @since 2.3.23 */
public static final String WHITESPACE_STRIPPING_KEY_CAMEL_CASE = "whitespaceStripping";
Alias to the ..._SNAKE_CASE variation due to backward compatibility constraints.
/** Alias to the {@code ..._SNAKE_CASE} variation due to backward compatibility constraints. */
public static final String WHITESPACE_STRIPPING_KEY = WHITESPACE_STRIPPING_KEY_SNAKE_CASE;
Legacy, snake case (like_this) variation of the setting name. @since 2.3.24
/** Legacy, snake case ({@code like_this}) variation of the setting name. @since 2.3.24 */
public static final String OUTPUT_FORMAT_KEY_SNAKE_CASE = "output_format";
Modern, camel case (likeThis) variation of the setting name. @since 2.3.24
/** Modern, camel case ({@code likeThis}) variation of the setting name. @since 2.3.24 */
public static final String OUTPUT_FORMAT_KEY_CAMEL_CASE = "outputFormat";
Alias to the ..._SNAKE_CASE variation due to backward compatibility constraints.
/** Alias to the {@code ..._SNAKE_CASE} variation due to backward compatibility constraints. */
public static final String OUTPUT_FORMAT_KEY = OUTPUT_FORMAT_KEY_SNAKE_CASE;
Legacy, snake case (like_this) variation of the setting name. @since 2.3.24
/** Legacy, snake case ({@code like_this}) variation of the setting name. @since 2.3.24 */
public static final String RECOGNIZE_STANDARD_FILE_EXTENSIONS_KEY_SNAKE_CASE = "recognize_standard_file_extensions";
Modern, camel case (likeThis) variation of the setting name. @since 2.3.24
/** Modern, camel case ({@code likeThis}) variation of the setting name. @since 2.3.24 */
public static final String RECOGNIZE_STANDARD_FILE_EXTENSIONS_KEY_CAMEL_CASE = "recognizeStandardFileExtensions";
Alias to the ..._SNAKE_CASE variation due to backward compatibility constraints.
/** Alias to the {@code ..._SNAKE_CASE} variation due to backward compatibility constraints. */
public static final String RECOGNIZE_STANDARD_FILE_EXTENSIONS_KEY = RECOGNIZE_STANDARD_FILE_EXTENSIONS_KEY_SNAKE_CASE;
Legacy, snake case (like_this) variation of the setting name. @since 2.3.24
/** Legacy, snake case ({@code like_this}) variation of the setting name. @since 2.3.24 */
public static final String REGISTERED_CUSTOM_OUTPUT_FORMATS_KEY_SNAKE_CASE = "registered_custom_output_formats";
Modern, camel case (likeThis) variation of the setting name. @since 2.3.24
/** Modern, camel case ({@code likeThis}) variation of the setting name. @since 2.3.24 */
public static final String REGISTERED_CUSTOM_OUTPUT_FORMATS_KEY_CAMEL_CASE = "registeredCustomOutputFormats";
Alias to the ..._SNAKE_CASE variation due to backward compatibility constraints.
/** Alias to the {@code ..._SNAKE_CASE} variation due to backward compatibility constraints. */
public static final String REGISTERED_CUSTOM_OUTPUT_FORMATS_KEY = REGISTERED_CUSTOM_OUTPUT_FORMATS_KEY_SNAKE_CASE;
Legacy, snake case (like_this) variation of the setting name. @since 2.3.24
/** Legacy, snake case ({@code like_this}) variation of the setting name. @since 2.3.24 */
public static final String AUTO_ESCAPING_POLICY_KEY_SNAKE_CASE = "auto_escaping_policy";
Modern, camel case (likeThis) variation of the setting name. @since 2.3.24
/** Modern, camel case ({@code likeThis}) variation of the setting name. @since 2.3.24 */
public static final String AUTO_ESCAPING_POLICY_KEY_CAMEL_CASE = "autoEscapingPolicy";
Alias to the ..._SNAKE_CASE variation due to backward compatibility constraints.
/** Alias to the {@code ..._SNAKE_CASE} variation due to backward compatibility constraints. */
public static final String AUTO_ESCAPING_POLICY_KEY = AUTO_ESCAPING_POLICY_KEY_SNAKE_CASE;
Legacy, snake case (like_this) variation of the setting name. @since 2.3.23
/** Legacy, snake case ({@code like_this}) variation of the setting name. @since 2.3.23 */
public static final String CACHE_STORAGE_KEY_SNAKE_CASE = "cache_storage";
Modern, camel case (likeThis) variation of the setting name. @since 2.3.23
/** Modern, camel case ({@code likeThis}) variation of the setting name. @since 2.3.23 */
public static final String CACHE_STORAGE_KEY_CAMEL_CASE = "cacheStorage";
Alias to the ..._SNAKE_CASE variation due to backward compatibility constraints.
/** Alias to the {@code ..._SNAKE_CASE} variation due to backward compatibility constraints. */
public static final String CACHE_STORAGE_KEY = CACHE_STORAGE_KEY_SNAKE_CASE;
Legacy, snake case (like_this) variation of the setting name. @since 2.3.23
/** Legacy, snake case ({@code like_this}) variation of the setting name. @since 2.3.23 */
public static final String TEMPLATE_UPDATE_DELAY_KEY_SNAKE_CASE = "template_update_delay";
Modern, camel case (likeThis) variation of the setting name. @since 2.3.23
/** Modern, camel case ({@code likeThis}) variation of the setting name. @since 2.3.23 */
public static final String TEMPLATE_UPDATE_DELAY_KEY_CAMEL_CASE = "templateUpdateDelay";
Alias to the ..._SNAKE_CASE variation due to backward compatibility constraints.
/** Alias to the {@code ..._SNAKE_CASE} variation due to backward compatibility constraints. */
public static final String TEMPLATE_UPDATE_DELAY_KEY = TEMPLATE_UPDATE_DELAY_KEY_SNAKE_CASE;
Legacy, snake case (like_this) variation of the setting name. @since 2.3.23
Deprecated:Use Configurable.AUTO_IMPORT_KEY_SNAKE_CASE instead.
/** * Legacy, snake case ({@code like_this}) variation of the setting name. @since 2.3.23 * @deprecated Use {@link Configurable#AUTO_IMPORT_KEY_SNAKE_CASE} instead. */
public static final String AUTO_IMPORT_KEY_SNAKE_CASE = "auto_import";
Modern, camel case (likeThis) variation of the setting name. @since 2.3.23
Deprecated:Use Configurable.AUTO_IMPORT_KEY_CAMEL_CASE instead.
/** * Modern, camel case ({@code likeThis}) variation of the setting name. @since 2.3.23 * @deprecated Use {@link Configurable#AUTO_IMPORT_KEY_CAMEL_CASE} instead. */
public static final String AUTO_IMPORT_KEY_CAMEL_CASE = "autoImport";
Alias to the ..._SNAKE_CASE variation due to backward compatibility constraints.
Deprecated:Use Configurable.AUTO_IMPORT_KEY_SNAKE_CASE instead.
/** * Alias to the {@code ..._SNAKE_CASE} variation due to backward compatibility constraints. * @deprecated Use {@link Configurable#AUTO_IMPORT_KEY_SNAKE_CASE} instead. */
public static final String AUTO_IMPORT_KEY = AUTO_IMPORT_KEY_SNAKE_CASE;
Legacy, snake case (like_this) variation of the setting name. @since 2.3.23
/** Legacy, snake case ({@code like_this}) variation of the setting name. @since 2.3.23 */
public static final String AUTO_INCLUDE_KEY_SNAKE_CASE = "auto_include";
Modern, camel case (likeThis) variation of the setting name. @since 2.3.23
/** Modern, camel case ({@code likeThis}) variation of the setting name. @since 2.3.23 */
public static final String AUTO_INCLUDE_KEY_CAMEL_CASE = "autoInclude";
Alias to the ..._SNAKE_CASE variation due to backward compatibility constraints.
/** Alias to the {@code ..._SNAKE_CASE} variation due to backward compatibility constraints. */
public static final String AUTO_INCLUDE_KEY = AUTO_INCLUDE_KEY_SNAKE_CASE;
Legacy, snake case (like_this) variation of the setting name. @since 2.3.23
/** Legacy, snake case ({@code like_this}) variation of the setting name. @since 2.3.23 */
public static final String TAG_SYNTAX_KEY_SNAKE_CASE = "tag_syntax";
Modern, camel case (likeThis) variation of the setting name. @since 2.3.23
/** Modern, camel case ({@code likeThis}) variation of the setting name. @since 2.3.23 */
public static final String TAG_SYNTAX_KEY_CAMEL_CASE = "tagSyntax";
Alias to the ..._SNAKE_CASE variation due to backward compatibility constraints.
/** Alias to the {@code ..._SNAKE_CASE} variation due to backward compatibility constraints. */
public static final String TAG_SYNTAX_KEY = TAG_SYNTAX_KEY_SNAKE_CASE;
Legacy, snake case (like_this) variation of the setting name. @since 2.3.28
/** Legacy, snake case ({@code like_this}) variation of the setting name. @since 2.3.28 */
public static final String INTERPOLATION_SYNTAX_KEY_SNAKE_CASE = "interpolation_syntax";
Modern, camel case (likeThis) variation of the setting name. @since 2.3.28
/** Modern, camel case ({@code likeThis}) variation of the setting name. @since 2.3.28 */
public static final String INTERPOLATION_SYNTAX_KEY_CAMEL_CASE = "interpolationSyntax";
Alias to the ..._SNAKE_CASE variation due to backward compatibility constraints.
/** Alias to the {@code ..._SNAKE_CASE} variation due to backward compatibility constraints. */
public static final String INTERPOLATION_SYNTAX_KEY = INTERPOLATION_SYNTAX_KEY_SNAKE_CASE;
Legacy, snake case (like_this) variation of the setting name. @since 2.3.23
/** Legacy, snake case ({@code like_this}) variation of the setting name. @since 2.3.23 */
public static final String NAMING_CONVENTION_KEY_SNAKE_CASE = "naming_convention";
Modern, camel case (likeThis) variation of the setting name. @since 2.3.23
/** Modern, camel case ({@code likeThis}) variation of the setting name. @since 2.3.23 */
public static final String NAMING_CONVENTION_KEY_CAMEL_CASE = "namingConvention";
Alias to the ..._SNAKE_CASE variation due to backward compatibility constraints.
/** Alias to the {@code ..._SNAKE_CASE} variation due to backward compatibility constraints. */
public static final String NAMING_CONVENTION_KEY = NAMING_CONVENTION_KEY_SNAKE_CASE;
Legacy, snake case (like_this) variation of the setting name. @since 2.3.25
/** Legacy, snake case ({@code like_this}) variation of the setting name. @since 2.3.25 */
public static final String TAB_SIZE_KEY_SNAKE_CASE = "tab_size";
Modern, camel case (likeThis) variation of the setting name. @since 2.3.25
/** Modern, camel case ({@code likeThis}) variation of the setting name. @since 2.3.25 */
public static final String TAB_SIZE_KEY_CAMEL_CASE = "tabSize";
Alias to the ..._SNAKE_CASE variation. @since 2.3.25
/** Alias to the {@code ..._SNAKE_CASE} variation. @since 2.3.25 */
public static final String TAB_SIZE_KEY = TAB_SIZE_KEY_SNAKE_CASE;
Legacy, snake case (like_this) variation of the setting name. @since 2.3.23
/** Legacy, snake case ({@code like_this}) variation of the setting name. @since 2.3.23 */
public static final String TEMPLATE_LOADER_KEY_SNAKE_CASE = "template_loader";
Modern, camel case (likeThis) variation of the setting name. @since 2.3.23
/** Modern, camel case ({@code likeThis}) variation of the setting name. @since 2.3.23 */
public static final String TEMPLATE_LOADER_KEY_CAMEL_CASE = "templateLoader";
Alias to the ..._SNAKE_CASE variation due to backward compatibility constraints.
/** Alias to the {@code ..._SNAKE_CASE} variation due to backward compatibility constraints. */
public static final String TEMPLATE_LOADER_KEY = TEMPLATE_LOADER_KEY_SNAKE_CASE;
Legacy, snake case (like_this) variation of the setting name. @since 2.3.23
/** Legacy, snake case ({@code like_this}) variation of the setting name. @since 2.3.23 */
public static final String TEMPLATE_LOOKUP_STRATEGY_KEY_SNAKE_CASE = "template_lookup_strategy";
Modern, camel case (likeThis) variation of the setting name. @since 2.3.23
/** Modern, camel case ({@code likeThis}) variation of the setting name. @since 2.3.23 */
public static final String TEMPLATE_LOOKUP_STRATEGY_KEY_CAMEL_CASE = "templateLookupStrategy";
Alias to the ..._SNAKE_CASE variation due to backward compatibility constraints.
/** Alias to the {@code ..._SNAKE_CASE} variation due to backward compatibility constraints. */
public static final String TEMPLATE_LOOKUP_STRATEGY_KEY = TEMPLATE_LOOKUP_STRATEGY_KEY_SNAKE_CASE;
Legacy, snake case (like_this) variation of the setting name. @since 2.3.23
/** Legacy, snake case ({@code like_this}) variation of the setting name. @since 2.3.23 */
public static final String TEMPLATE_NAME_FORMAT_KEY_SNAKE_CASE = "template_name_format";
Modern, camel case (likeThis) variation of the setting name. @since 2.3.23
/** Modern, camel case ({@code likeThis}) variation of the setting name. @since 2.3.23 */
public static final String TEMPLATE_NAME_FORMAT_KEY_CAMEL_CASE = "templateNameFormat";
Alias to the ..._SNAKE_CASE variation due to backward compatibility constraints.
/** Alias to the {@code ..._SNAKE_CASE} variation due to backward compatibility constraints. */
public static final String TEMPLATE_NAME_FORMAT_KEY = TEMPLATE_NAME_FORMAT_KEY_SNAKE_CASE;
Legacy, snake case (like_this) variation of the setting name. @since 2.3.24
/** Legacy, snake case ({@code like_this}) variation of the setting name. @since 2.3.24 */
public static final String TEMPLATE_CONFIGURATIONS_KEY_SNAKE_CASE = "template_configurations";
Modern, camel case (likeThis) variation of the setting name. @since 2.3.24
/** Modern, camel case ({@code likeThis}) variation of the setting name. @since 2.3.24 */
public static final String TEMPLATE_CONFIGURATIONS_KEY_CAMEL_CASE = "templateConfigurations";
Alias to the ..._SNAKE_CASE variation. @since 2.3.24
/** Alias to the {@code ..._SNAKE_CASE} variation. @since 2.3.24 */
public static final String TEMPLATE_CONFIGURATIONS_KEY = TEMPLATE_CONFIGURATIONS_KEY_SNAKE_CASE;
Legacy, snake case (like_this) variation of the setting name. @since 2.3.23
/** Legacy, snake case ({@code like_this}) variation of the setting name. @since 2.3.23 */
public static final String INCOMPATIBLE_IMPROVEMENTS_KEY_SNAKE_CASE = "incompatible_improvements";
Modern, camel case (likeThis) variation of the setting name. @since 2.3.23
/** Modern, camel case ({@code likeThis}) variation of the setting name. @since 2.3.23 */
public static final String INCOMPATIBLE_IMPROVEMENTS_KEY_CAMEL_CASE = "incompatibleImprovements";
Alias to the ..._SNAKE_CASE variation due to backward compatibility constraints.
/** Alias to the {@code ..._SNAKE_CASE} variation due to backward compatibility constraints. */
public static final String INCOMPATIBLE_IMPROVEMENTS_KEY = INCOMPATIBLE_IMPROVEMENTS_KEY_SNAKE_CASE;
Deprecated:Use INCOMPATIBLE_IMPROVEMENTS_KEY instead.
/** @deprecated Use {@link #INCOMPATIBLE_IMPROVEMENTS_KEY} instead. */
@Deprecated public static final String INCOMPATIBLE_IMPROVEMENTS = INCOMPATIBLE_IMPROVEMENTS_KEY_SNAKE_CASE;
Deprecated:Use INCOMPATIBLE_IMPROVEMENTS_KEY instead.
/** @deprecated Use {@link #INCOMPATIBLE_IMPROVEMENTS_KEY} instead. */
@Deprecated public static final String INCOMPATIBLE_ENHANCEMENTS = "incompatible_enhancements"; private static final String[] SETTING_NAMES_SNAKE_CASE = new String[] { // Must be sorted alphabetically! AUTO_ESCAPING_POLICY_KEY_SNAKE_CASE, CACHE_STORAGE_KEY_SNAKE_CASE, DEFAULT_ENCODING_KEY_SNAKE_CASE, INCOMPATIBLE_IMPROVEMENTS_KEY_SNAKE_CASE, INTERPOLATION_SYNTAX_KEY_SNAKE_CASE, LOCALIZED_LOOKUP_KEY_SNAKE_CASE, NAMING_CONVENTION_KEY_SNAKE_CASE, OUTPUT_FORMAT_KEY_SNAKE_CASE, RECOGNIZE_STANDARD_FILE_EXTENSIONS_KEY_SNAKE_CASE, REGISTERED_CUSTOM_OUTPUT_FORMATS_KEY_SNAKE_CASE, STRICT_SYNTAX_KEY_SNAKE_CASE, TAB_SIZE_KEY_SNAKE_CASE, TAG_SYNTAX_KEY_SNAKE_CASE, TEMPLATE_CONFIGURATIONS_KEY_SNAKE_CASE, TEMPLATE_LOADER_KEY_SNAKE_CASE, TEMPLATE_LOOKUP_STRATEGY_KEY_SNAKE_CASE, TEMPLATE_NAME_FORMAT_KEY_SNAKE_CASE, TEMPLATE_UPDATE_DELAY_KEY_SNAKE_CASE, WHITESPACE_STRIPPING_KEY_SNAKE_CASE, }; private static final String[] SETTING_NAMES_CAMEL_CASE = new String[] { // Must be sorted alphabetically! AUTO_ESCAPING_POLICY_KEY_CAMEL_CASE, CACHE_STORAGE_KEY_CAMEL_CASE, DEFAULT_ENCODING_KEY_CAMEL_CASE, INCOMPATIBLE_IMPROVEMENTS_KEY_CAMEL_CASE, INTERPOLATION_SYNTAX_KEY_CAMEL_CASE, LOCALIZED_LOOKUP_KEY_CAMEL_CASE, NAMING_CONVENTION_KEY_CAMEL_CASE, OUTPUT_FORMAT_KEY_CAMEL_CASE, RECOGNIZE_STANDARD_FILE_EXTENSIONS_KEY_CAMEL_CASE, REGISTERED_CUSTOM_OUTPUT_FORMATS_KEY_CAMEL_CASE, STRICT_SYNTAX_KEY_CAMEL_CASE, TAB_SIZE_KEY_CAMEL_CASE, TAG_SYNTAX_KEY_CAMEL_CASE, TEMPLATE_CONFIGURATIONS_KEY_CAMEL_CASE, TEMPLATE_LOADER_KEY_CAMEL_CASE, TEMPLATE_LOOKUP_STRATEGY_KEY_CAMEL_CASE, TEMPLATE_NAME_FORMAT_KEY_CAMEL_CASE, TEMPLATE_UPDATE_DELAY_KEY_CAMEL_CASE, WHITESPACE_STRIPPING_KEY_CAMEL_CASE }; private static final Map<String, OutputFormat> STANDARD_OUTPUT_FORMATS; static { STANDARD_OUTPUT_FORMATS = new HashMap<String, OutputFormat>(); STANDARD_OUTPUT_FORMATS.put(UndefinedOutputFormat.INSTANCE.getName(), UndefinedOutputFormat.INSTANCE); STANDARD_OUTPUT_FORMATS.put(HTMLOutputFormat.INSTANCE.getName(), HTMLOutputFormat.INSTANCE); STANDARD_OUTPUT_FORMATS.put(XHTMLOutputFormat.INSTANCE.getName(), XHTMLOutputFormat.INSTANCE); STANDARD_OUTPUT_FORMATS.put(XMLOutputFormat.INSTANCE.getName(), XMLOutputFormat.INSTANCE); STANDARD_OUTPUT_FORMATS.put(RTFOutputFormat.INSTANCE.getName(), RTFOutputFormat.INSTANCE); STANDARD_OUTPUT_FORMATS.put(PlainTextOutputFormat.INSTANCE.getName(), PlainTextOutputFormat.INSTANCE); STANDARD_OUTPUT_FORMATS.put(CSSOutputFormat.INSTANCE.getName(), CSSOutputFormat.INSTANCE); STANDARD_OUTPUT_FORMATS.put(JavaScriptOutputFormat.INSTANCE.getName(), JavaScriptOutputFormat.INSTANCE); STANDARD_OUTPUT_FORMATS.put(JSONOutputFormat.INSTANCE.getName(), JSONOutputFormat.INSTANCE); }
The parser decides between ANGLE_BRACKET_TAG_SYNTAX and SQUARE_BRACKET_TAG_SYNTAX based on the first tag (like [#if x] or <#if x>) it mets. Note that [=...] is not a tag, but an interpolation, so it's not used for tag syntax auto-detection.
/** * The parser decides between {@link #ANGLE_BRACKET_TAG_SYNTAX} and {@link #SQUARE_BRACKET_TAG_SYNTAX} based on the * first tag (like {@code [#if x]} or {@code <#if x>}) it mets. Note that {@code [=...]} is <em>not</em> a tag, but * an interpolation, so it's not used for tag syntax auto-detection. */
public static final int AUTO_DETECT_TAG_SYNTAX = 0;
For example <#if x><@foo /></#if>
/** For example {@code <#if x><@foo /></#if>} */
public static final int ANGLE_BRACKET_TAG_SYNTAX = 1;
For example [#if x][@foo /][/#if]. It does not change ${x} to [=x]; that's square bracket interpolation syntax (SQUARE_BRACKET_INTERPOLATION_SYNTAX).
/** * For example {@code [#if x][@foo /][/#if]}. * It does <em>not</em> change <code>${x}</code> to {@code [=x]}; that's square bracket <em>interpolation</em> * syntax ({@link #SQUARE_BRACKET_INTERPOLATION_SYNTAX}). */
public static final int SQUARE_BRACKET_TAG_SYNTAX = 2;
${expression} and the deprecated #{expression; numFormat} @since 2.3.28
/** <code>${expression}</code> and the deprecated <code>#{expression; numFormat}</code> @since 2.3.28 */
public static final int LEGACY_INTERPOLATION_SYNTAX = 20;
${expression} only (not #{expression; numFormat}) @since 2.3.28
/** <code>${expression}</code> only (not <code>#{expression; numFormat}</code>) @since 2.3.28 */
public static final int DOLLAR_INTERPOLATION_SYNTAX = 21;
[=expression] instead of ${expression}. It does not change <#if x> to [#if x]; that's square bracket tag syntax (SQUARE_BRACKET_TAG_SYNTAX).
Since:2.3.28
/** * <code>[=expression]</code> instead of <code>${expression}</code>. * It does <em>not</em> change {@code <#if x>} to {@code [#if x]}; that's square bracket <em>tag</em> syntax * ({@link #SQUARE_BRACKET_TAG_SYNTAX}). * @since 2.3.28 */
public static final int SQUARE_BRACKET_INTERPOLATION_SYNTAX = 22; public static final int AUTO_DETECT_NAMING_CONVENTION = 10; public static final int LEGACY_NAMING_CONVENTION = 11; public static final int CAMEL_CASE_NAMING_CONVENTION = 12;
Don't enable auto-escaping, regardless of what the OutputFormat is. Note that a <#ftl auto_esc=true> in the template will override this.
/** * Don't enable auto-escaping, regardless of what the {@link OutputFormat} is. Note that a {@code * <#ftl auto_esc=true>} in the template will override this. */
public static final int DISABLE_AUTO_ESCAPING_POLICY = 20;
Enable auto-escaping if the output format supports it and MarkupOutputFormat.isAutoEscapedByDefault() is true.
/** * Enable auto-escaping if the output format supports it and {@link MarkupOutputFormat#isAutoEscapedByDefault()} is * {@code true}. */
public static final int ENABLE_IF_DEFAULT_AUTO_ESCAPING_POLICY = 21;
Enable auto-escaping if the OutputFormat supports it.
/** Enable auto-escaping if the {@link OutputFormat} supports it. */
public static final int ENABLE_IF_SUPPORTED_AUTO_ESCAPING_POLICY = 22;
FreeMarker version 2.3.0 (an incompatible improvements break-point)
/** FreeMarker version 2.3.0 (an {@link #Configuration(Version) incompatible improvements break-point}) */
public static final Version VERSION_2_3_0 = new Version(2, 3, 0);
FreeMarker version 2.3.19 (an incompatible improvements break-point)
/** FreeMarker version 2.3.19 (an {@link #Configuration(Version) incompatible improvements break-point}) */
public static final Version VERSION_2_3_19 = new Version(2, 3, 19);
FreeMarker version 2.3.20 (an incompatible improvements break-point)
/** FreeMarker version 2.3.20 (an {@link #Configuration(Version) incompatible improvements break-point}) */
public static final Version VERSION_2_3_20 = new Version(2, 3, 20);
FreeMarker version 2.3.21 (an incompatible improvements break-point)
/** FreeMarker version 2.3.21 (an {@link #Configuration(Version) incompatible improvements break-point}) */
public static final Version VERSION_2_3_21 = new Version(2, 3, 21);
FreeMarker version 2.3.22 (an incompatible improvements break-point)
/** FreeMarker version 2.3.22 (an {@link #Configuration(Version) incompatible improvements break-point}) */
public static final Version VERSION_2_3_22 = new Version(2, 3, 22);
FreeMarker version 2.3.23 (an incompatible improvements break-point)
/** FreeMarker version 2.3.23 (an {@link #Configuration(Version) incompatible improvements break-point}) */
public static final Version VERSION_2_3_23 = new Version(2, 3, 23);
FreeMarker version 2.3.24 (an incompatible improvements break-point)
/** FreeMarker version 2.3.24 (an {@link #Configuration(Version) incompatible improvements break-point}) */
public static final Version VERSION_2_3_24 = new Version(2, 3, 24);
FreeMarker version 2.3.25 (an incompatible improvements break-point)
/** FreeMarker version 2.3.25 (an {@link #Configuration(Version) incompatible improvements break-point}) */
public static final Version VERSION_2_3_25 = new Version(2, 3, 25);
FreeMarker version 2.3.26 (an incompatible improvements break-point)
/** FreeMarker version 2.3.26 (an {@link #Configuration(Version) incompatible improvements break-point}) */
public static final Version VERSION_2_3_26 = new Version(2, 3, 26);
FreeMarker version 2.3.27 (an incompatible improvements break-point)
/** FreeMarker version 2.3.27 (an {@link #Configuration(Version) incompatible improvements break-point}) */
public static final Version VERSION_2_3_27 = new Version(2, 3, 27);
FreeMarker version 2.3.28 (an incompatible improvements break-point)
/** FreeMarker version 2.3.28 (an {@link #Configuration(Version) incompatible improvements break-point}) */
public static final Version VERSION_2_3_28 = new Version(2, 3, 28);
The default of getIncompatibleImprovements(), currently VERSION_2_3_0.
/** The default of {@link #getIncompatibleImprovements()}, currently {@link #VERSION_2_3_0}. */
public static final Version DEFAULT_INCOMPATIBLE_IMPROVEMENTS = Configuration.VERSION_2_3_0;
Deprecated:Use DEFAULT_INCOMPATIBLE_IMPROVEMENTS instead.
/** @deprecated Use {@link #DEFAULT_INCOMPATIBLE_IMPROVEMENTS} instead. */
@Deprecated public static final String DEFAULT_INCOMPATIBLE_ENHANCEMENTS = DEFAULT_INCOMPATIBLE_IMPROVEMENTS.toString();
Deprecated:Use DEFAULT_INCOMPATIBLE_IMPROVEMENTS instead.
/** @deprecated Use {@link #DEFAULT_INCOMPATIBLE_IMPROVEMENTS} instead. */
@Deprecated public static final int PARSED_DEFAULT_INCOMPATIBLE_ENHANCEMENTS = DEFAULT_INCOMPATIBLE_IMPROVEMENTS.intValue(); private static final String NULL = "null"; private static final String DEFAULT = "default"; private static final String JVM_DEFAULT = "JVM default"; private static final Version VERSION; static { try { Properties props = ClassUtil.loadProperties(Configuration.class, VERSION_PROPERTIES_PATH); String versionString = getRequiredVersionProperty(props, "version"); Date buildDate; { String buildDateStr = getRequiredVersionProperty(props, "buildTimestamp"); if (buildDateStr.endsWith("Z")) { buildDateStr = buildDateStr.substring(0, buildDateStr.length() - 1) + "+0000"; } try { buildDate = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZ", Locale.US).parse(buildDateStr); } catch (java.text.ParseException e) { buildDate = null; } } final Boolean gaeCompliant = Boolean.valueOf(getRequiredVersionProperty(props, "isGAECompliant")); VERSION = new Version(versionString, gaeCompliant, buildDate); } catch (IOException e) { throw new RuntimeException("Failed to load and parse " + VERSION_PROPERTIES_PATH, e); } } private static final String FM_24_DETECTION_CLASS_NAME = "freemarker.core._2_4_OrLaterMarker"; private static final boolean FM_24_DETECTED; static { boolean fm24detected; try { Class.forName(FM_24_DETECTION_CLASS_NAME); fm24detected = true; } catch (ClassNotFoundException e) { fm24detected = false; } catch (LinkageError e) { fm24detected = true; } catch (Throwable e) { // Unexpected. We assume that there's no clash. fm24detected = false; } FM_24_DETECTED = fm24detected; } private final static Object defaultConfigLock = new Object(); private static volatile Configuration defaultConfig; private boolean strictSyntax = true; private volatile boolean localizedLookup = true; private boolean whitespaceStripping = true; private int autoEscapingPolicy = ENABLE_IF_DEFAULT_AUTO_ESCAPING_POLICY; private OutputFormat outputFormat = UndefinedOutputFormat.INSTANCE; private boolean outputFormatExplicitlySet; private Boolean recognizeStandardFileExtensions; private Map<String, ? extends OutputFormat> registeredCustomOutputFormats = Collections.emptyMap(); private Version incompatibleImprovements; private int tagSyntax = ANGLE_BRACKET_TAG_SYNTAX; private int interpolationSyntax = LEGACY_INTERPOLATION_SYNTAX; private int namingConvention = AUTO_DETECT_NAMING_CONVENTION; private int tabSize = 8; // Default from JavaCC 3.x private boolean preventStrippings; private TemplateCache cache; private boolean templateLoaderExplicitlySet; private boolean templateLookupStrategyExplicitlySet; private boolean templateNameFormatExplicitlySet; private boolean cacheStorageExplicitlySet; private boolean objectWrapperExplicitlySet; private boolean templateExceptionHandlerExplicitlySet; private boolean attemptExceptionReporterExplicitlySet; private boolean logTemplateExceptionsExplicitlySet; private boolean wrapUncheckedExceptionsExplicitlySet; private boolean localeExplicitlySet; private boolean defaultEncodingExplicitlySet; private boolean timeZoneExplicitlySet; private HashMap/*<String, TemplateModel>*/ sharedVariables = new HashMap();
Needed so that it doesn't mater in what order do you call setSharedVaribles(Map) and setObjectWrapper(ObjectWrapper). When the user configures FreeMarker from Spring XML, he has no control over the order, so it has to work on both ways.
/** * Needed so that it doesn't mater in what order do you call {@link #setSharedVaribles(Map)} * and {@link #setObjectWrapper(ObjectWrapper)}. When the user configures FreeMarker from Spring XML, he has no * control over the order, so it has to work on both ways. */
private HashMap/*<String, Object>*/ rewrappableSharedVariables = null; private String defaultEncoding = getDefaultDefaultEncoding(); private ConcurrentMap localeToCharsetMap = new ConcurrentHashMap();
Deprecated:Use Configuration(Version) instead. Note that the version can be still modified later with setIncompatibleImprovements(Version) (or Configurable.setSettings(Properties)).
/** * Same as {@link #Configuration(Version) Configuration(Configuration.DEFAULT_INCOMPATIBLE_IMPROVEMENTS)}. * * @deprecated Use {@link #Configuration(Version)} instead. Note that the version can be still modified later with * {@link Configuration#setIncompatibleImprovements(Version)} (or * {@link Configuration#setSettings(Properties)}). */
@Deprecated public Configuration() { this(DEFAULT_INCOMPATIBLE_IMPROVEMENTS); }
Creates a new instance and sets which of the non-backward-compatible bugfixes/improvements should be enabled. Note that the specified versions corresponds to the incompatible_improvements configuration setting, and can be changed later, with setIncompatibleImprovements(Version) for example.

About the "incompatible improvements" setting

This setting value is the FreeMarker version number where the not 100% backward compatible bug fixes and improvements that you want to enable were already implemented. In new projects you should set this to the fixed FreeMarker version that you start the development with. In older projects it's also usually better to keep this high, however you should check the changes activated (find them below), especially if not only the 3rd version number (the micro version) of incompatibleImprovements is increased. Generally, as far as you only increase the last version number of this setting, the changes are low risk. The default value is 2.3.0 to maximize backward compatibility, but that value isn't recommended.

Bugfixes and improvements that are fully backward compatible, also those that are important security fixes, are enabled regardless of the incompatible improvements setting.

Do NOT ever use getVersion() to set the "incompatible improvements". Always use a fixed value, like VERSION_2_3_28. Otherwise your application can break as you upgrade FreeMarker.

An important consequence of setting this setting is that now your application will check if the stated minimum FreeMarker version requirement is met. Like if you set this setting to 2.3.22, but accidentally the application is deployed with FreeMarker 2.3.21, then FreeMarker will fail, telling that a higher version is required. After all, the fixes/improvements you have requested aren't available on a lower version.

Note that as FreeMarker's minor (2nd) or major (1st) version number increments, it's possible that emulating some of the old bugs will become unsupported, that is, even if you set this setting to a low value, it silently wont bring back the old behavior anymore. Information about that will be present here.

Currently the effects of this setting are:

  • 2.3.0: This is the lowest supported value, the version used in very old projects. This is the default in the FreeMarker 2.3.x series (the one used by the deprecated Configuration() constructor) for maximum backward compatibility.

  • 2.3.19 (or higher): Bug fix: Wrong # tags were printed as static text instead of causing parsing error when there was no correct # or @ tag earlier in the same template.

  • 2.3.20 (or higher): ?html will escape apostrophe-quotes just like ?xhtml does. Utilizing this is highly recommended, because otherwise if interpolations are used inside attribute values that use apostrophe-quotation (<foo bar='${val}'>) instead of plain quotation mark (<foo bar="${val}">), they might produce HTML/XML that's not well-formed. Note that ?html didn't do this because long ago there was no cross-browser way of doing this, but it's not a concern anymore.

  • 2.3.21 (or higher):

    • The default of the object_wrapper setting (Configurable.getObjectWrapper()) changes from ObjectWrapper.DEFAULT_WRAPPER to another almost identical DefaultObjectWrapper singleton, returned by DefaultObjectWrapperBuilder.build(). The new default object wrapper's "incompatible improvements" version is set to the same as of the Configuration. See BeansWrapper(Version) for further details. Furthermore, the new default object wrapper doesn't allow changing its settings; setter methods throw IllegalStateException). (If anything tries to call setters on the old default in your application, that's a dangerous bug that won't remain hidden now. As the old default is a singleton too, potentially shared by independently developed components, most of them expects the out-of-the-box behavior from it (and the others are necessarily buggy). Also, then concurrency glitches can occur (and even pollute the class introspection cache) because the singleton is modified after publishing to other threads.) Furthermore the new default object wrapper shares class introspection cache with other BeansWrapper-s created with BeansWrapperBuilder, which has an impact as BeansWrapper.clearClassIntrospecitonCache() will be disallowed; see more about it there.

    • The ?iso_... built-ins won't show the time zone offset for Time values anymore, because most databases store time values that aren't in any time zone, but just store hour, minute, second, and decimal second field values. If you still want to show the offset (like for PostgreSQL "time with time zone" columns you should), you can force showing the time zone offset by using myTime?string.iso_fz (and its other variants).

    • ?is_enumerable correctly returns false for Java methods get from Java objects that are wrapped with BeansWrapper and its subclasses, like DefaultObjectWrapper. Although method values implement TemplateSequenceModel (because of a historical design quirk in BeansWrapper), trying to #list them will cause error, hence they aren't enumerable.

    • ?c will return "INF", "-INF" and "NaN" for positive/negative infinity and IEEE floating point Not-a-Number, respectively. These are the XML Schema compatible representations of these special values. Earlier it has returned what DecimalFormat did with US locale, none of which was understood by any (common) computer language.

    • FTL hash literals that repeat keys now only have the key once with ?keys, and only has the last value associated to that key with ?values. This is consistent with the behavior of hash[key] and how maps work in Java.

    • In most cases (where FreeMarker is able to do that), for TemplateLoader-s that use URLConnection, URLConnection#setUseCaches(boolean) will called with false, so that only FreeMarker will do caching, not the URL scheme's handler. See URLTemplateLoader.setURLConnectionUsesCaches(Boolean) for more details.

    • The default of the template_loader setting (getTemplateLoader()) changes to null, which means that FreeMarker will not find any templates. Earlier the default was a FileTemplateLoader that used the current directory as the root. This was dangerous and fragile as you usually don't have good control over what the current directory will be. Luckily, the old default almost never looked for the templates at the right place anyway, so pretty much all applications had to set the template_loader setting, so it's unlikely that changing the default breaks your application.

    • Right-unlimited ranges become readable (like listable), so <#list 1.. as i>...</#list> works. Earlier they were only usable for slicing (like hits[10..]).

    • Empty ranges return Constants.EMPTY_SEQUENCE instead of an empty SimpleSequence. This is in theory backward compatible, as the API only promises to give something that implements TemplateSequenceModel.

    • Unclosed comments (<#-- ...) and #noparse-s won't be silently closed at the end of template anymore, but cause a parsing error instead.

  • 2.3.22 (or higher):

    • DefaultObjectWrapper has some substantial changes with incompatibleImprovements 2.3.22; check them out at DefaultObjectWrapper(Version). It's important to know that if you set the object_wrapper setting (to an other value than "default"), rather than leaving it on its default value, the object_wrapper won't inherit the incompatibleImprovements of the Configuration. In that case, if you want the 2.3.22 improvements of DefaultObjectWrapper, you have to set it in the DefaultObjectWrapper object itself too! (Note that it's OK to use a DefaultObjectWrapper with a different incompatibleImprovements version number than that of the Configuration, if that's really what you want.)

    • In templates, .template_name will always return the main (top level) template's name. It won't be affected by #include and #nested anymore. This is unintended, a bug with incompatible_improvement 2.3.22 (a consequence of the lower level fixing described in the next point). The old behavior of .template_name is restored if you set incompatible_improvement to 2.3.23 (while Configurable.getParent()) of Environment keeps the changed behavior shown in the next point).

    • #include and #nested doesn't change the parent Template (see Configurable.getParent()) of the Environment anymore to the Template that's included or whose namespace #nested "returns" to. Thus, the parent of Environment will be now always the main Template. (The main Template is the Template whose process or createProcessingEnvironment method was called to initiate the output generation.) Note that apart from the effect on FTL's .template_name (see previous point), this should only matter if you have set settings directly on Template objects, and almost nobody does that. Also note that macro calls have never changed the Environment parent to the Template that contains the macro definition, so this mechanism was always broken. As now we consistently never change the parent, the behavior when calling macros didn't change.

    • When using freemarker.ext.servlet.FreemarkerServlet:

      • When using custom JSP tag libraries: Fixes bug where some kind of values, when put into the JSP page scope (via #global or via the JSP PageContext API) and later read back with the JSP PageContext API (typically in a custom JSP tag), might come back as FreeMarker TemplateModel objects instead of as objects with a standard Java type. Other Servlet scopes aren't affected. It's highly unlikely that something expects the presence of this bug. The affected values are of the FTL types listed below, and to trigger the bug, they either had to be created directly in the template (like as an FTL literal or with ?date/time/datetime), or you had to use DefaultObjectWrapper or SimpleObjectWrapper (or a subclass of them):

      • Initial "[" in the TemplatePath init-param has special meaning; it's used for specifying multiple comma separated locations, like in <param-value>[ WEB-INF/templates, classpath:com/example/myapp/templates ]</param-value>

      • Initial "{" in the TemplatePath init-param is reserved for future purposes, and thus will throw exception.

  • 2.3.23 (or higher):

    • Fixed a loophole in the implementation of the long existing parse-time rule that says that #break, in the FTL source code itself, must occur nested inside a breakable directive, such as #list or #switch. This check could be circumvented with #macro or #function, like this: <#list 1..1 as x><#macro callMeLater><#break></#macro></#list><@callMeLater />. After activating this fix, this will be a parse time error.

    • If you have used incompatible_improvements 2.3.22 earlier, know that there the behavior of the .template_name special variable used in templates was accidentally altered, but now it's restored to be backward compatible with 2.3.0. (Ironically, the restored legacy behavior itself is broken when it comes to macro invocations, we just keep it for backward compatibility. If you need fixed behavior, use .current_template_name or .main_template_name instead.)

  • 2.3.24 (or higher):

    • The default of the recognize_standard_file_extensions setting changes to true, which means that templates whose name ends with ".ftlh" or ".ftlx" will automatically get HTMLOutputFormat.INSTANCE or XMLOutputFormat.INSTANCE output format respectively, in both cases with ENABLE_IF_DEFAULT_AUTO_ESCAPING_POLICY auto_escaping_policy. These "file" extensions aren't case sensitive.

    • In number format and date format strings (like in the number_format setting, or in templates in n?string("0.##")), an initial '@' has special meaning; they refer to a custom format with the name given after the @ (see: Configurable.setCustomNumberFormats(Map<String,? extends TemplateNumberFormatFactory>), Configurable.setCustomDateFormats(Map<String,? extends TemplateDateFormatFactory>), Configurable.setNumberFormat(String), and Configurable.setDateTimeFormat). If the custom format doesn't exist, that will be an error. To have a literal @ as the first character in the output, it has to be written as @@. Again, all this only applies to the very first character of the format string, so @ characters elsewhere must not be doubled. Also, if there are any custom formats defined, initial '@' will have the new meaning regardless of the value of the incompatible_improvements setting. So you don't need to set the incompatible_improvements only to use custom formats.

    • Expressions inside interpolations that were inside string literal expressions (not ${...}-s in general), like in <#assign s="Hello ${name}!">, has always used incompatbileImprovement-s 0 (2.3.0 in effect). Now it's fixed.

    • DefaultObjectWrapper has some minor changes with incompatibleImprovements 2.3.24; check them out at DefaultObjectWrapper(Version). It's important to know that if you set the object_wrapper setting (to an other value than "default"), rather than leaving it on its default value, the object_wrapper won't inherit the incompatibleImprovements of the Configuration. In that case, if you want the 2.3.24 improvements of DefaultObjectWrapper, you have to set it in the DefaultObjectWrapper object itself too! (Note that it's OK to use a DefaultObjectWrapper with a different incompatibleImprovements version number than that of the Configuration, if that's really what you want.)

    • Fixed bug: The #import directive meant to copy the library variable into a global variable if it's executed in the main namespace, but that haven't happened when the imported template was already imported earlier in another namespace.

    • ?is_sequence doesn't return true for Java methods wrapped by BeansWrapper and its subclasses (most notably DefaultObjectWrapper) anymore, as they only implement the [index] operator, but not ?size, which causes <#list ...> to fail among others. (They shouldn't implement either, but this is historical heritage.)

  • 2.3.25 (or higher):

  • 2.3.26 (or higher):

  • 2.3.27 (or higher):

  • 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.) Similarly, .macro_caller_template_name (which itself was added in 2.3.28), when used in a macro call argument, won't be incorrectly evaluated in the context of the called macro.

    • 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 it doesn't affect the legal usage of ], like <#if x[0]> works correctly without this fix as well.

Throws:
  • IllegalArgumentException – If incompatibleImmprovements refers to a version that wasn't released yet when the currently used FreeMarker version was released, or is less than 2.3.0, or is null.
Since:2.3.21
/** * Creates a new instance and sets which of the non-backward-compatible bugfixes/improvements should be enabled. * Note that the specified versions corresponds to the {@code incompatible_improvements} configuration setting, and * can be changed later, with {@link #setIncompatibleImprovements(Version)} for example. * * <p><b>About the "incompatible improvements" setting</b> * * <p>This setting value is the FreeMarker version number where the not 100% backward compatible bug fixes and * improvements that you want to enable were already implemented. In new projects you should set this to the fixed * FreeMarker version that you start the development with. In older projects it's also usually better to keep * this high, however you should check the changes activated (find them below), especially if not only the 3rd * version number (the micro version) of {@code incompatibleImprovements} is increased. Generally, as far as you * only increase the last version number of this setting, the changes are low risk. The default value is 2.3.0 to * maximize backward compatibility, but that value isn't recommended. * * <p>Bugfixes and improvements that are fully backward compatible, also those that are important security fixes, * are enabled regardless of the incompatible improvements setting. * * <p>Do NOT ever use {@link #getVersion()} to set the "incompatible improvements". Always use a fixed value, like * {@link #VERSION_2_3_28}. Otherwise your application can break as you upgrade FreeMarker. * * <p>An important consequence of setting this setting is that now your application will check if the stated minimum * FreeMarker version requirement is met. Like if you set this setting to 2.3.22, but accidentally the application * is deployed with FreeMarker 2.3.21, then FreeMarker will fail, telling that a higher version is required. After * all, the fixes/improvements you have requested aren't available on a lower version. * * <p>Note that as FreeMarker's minor (2nd) or major (1st) version number increments, it's possible that emulating * some of the old bugs will become unsupported, that is, even if you set this setting to a low value, it silently * wont bring back the old behavior anymore. Information about that will be present here. * * <p>Currently the effects of this setting are: * <ul> * <li><p> * 2.3.0: This is the lowest supported value, the version used in very old projects. This is the default in the * FreeMarker 2.3.x series (the one used by the deprecated {@link #Configuration()} constructor) for maximum * backward compatibility. * </li> * <li><p> * 2.3.19 (or higher): Bug fix: Wrong {@code #} tags were printed as static text instead of * causing parsing error when there was no correct {@code #} or {@code @} tag earlier in the * same template. * </li> * <li><p> * 2.3.20 (or higher): {@code ?html} will escape apostrophe-quotes just like {@code ?xhtml} does. Utilizing * this is highly recommended, because otherwise if interpolations are used inside attribute values that use * apostrophe-quotation (<tt>&lt;foo bar='${val}'&gt;</tt>) instead of plain quotation mark * (<tt>&lt;foo bar="${val}"&gt;</tt>), they might produce HTML/XML that's not well-formed. Note that * {@code ?html} didn't do this because long ago there was no cross-browser way of doing this, but it's not a * concern anymore. * </li> * <li><p> * 2.3.21 (or higher): * <ul> * <li><p> * The <em>default</em> of the {@code object_wrapper} setting ({@link #getObjectWrapper()}) changes from * {@link ObjectWrapper#DEFAULT_WRAPPER} to another almost identical {@link DefaultObjectWrapper} singleton, * returned by {@link DefaultObjectWrapperBuilder#build()}. The new default object wrapper's * "incompatible improvements" version is set to the same as of the {@link Configuration}. * See {@link BeansWrapper#BeansWrapper(Version)} for further details. Furthermore, the new default * object wrapper doesn't allow changing its settings; setter methods throw {@link IllegalStateException}). * (If anything tries to call setters on the old default in your application, that's a dangerous bug that * won't remain hidden now. As the old default is a singleton too, potentially shared by independently * developed components, most of them expects the out-of-the-box behavior from it (and the others are * necessarily buggy). Also, then concurrency glitches can occur (and even pollute the class introspection * cache) because the singleton is modified after publishing to other threads.) * Furthermore the new default object wrapper shares class introspection cache with other * {@link BeansWrapper}-s created with {@link BeansWrapperBuilder}, which has an impact as * {@link BeansWrapper#clearClassIntrospecitonCache()} will be disallowed; see more about it there. * </li> * <li><p> * The {@code ?iso_...} built-ins won't show the time zone offset for {@link java.sql.Time} values anymore, * because most databases store time values that aren't in any time zone, but just store hour, minute, * second, and decimal second field values. If you still want to show the offset (like for PostgreSQL * "time with time zone" columns you should), you can force showing the time zone offset by using * {@code myTime?string.iso_fz} (and its other variants). * </li> * <li><p>{@code ?is_enumerable} correctly returns {@code false} for Java methods get from Java objects that * are wrapped with {@link BeansWrapper} and its subclasses, like {@link DefaultObjectWrapper}. Although * method values implement {@link TemplateSequenceModel} (because of a historical design quirk in * {@link BeansWrapper}), trying to {@code #list} them will cause error, hence they aren't enumerable. * </li> * <li><p> * {@code ?c} will return {@code "INF"}, {@code "-INF"} and {@code "NaN"} for positive/negative infinity * and IEEE floating point Not-a-Number, respectively. These are the XML Schema compatible representations * of these special values. Earlier it has returned what {@link DecimalFormat} did with US locale, none of * which was understood by any (common) computer language. * </li> * <li><p> * FTL hash literals that repeat keys now only have the key once with {@code ?keys}, and only has the last * value associated to that key with {@code ?values}. This is consistent with the behavior of * {@code hash[key]} and how maps work in Java. * </li> * <li><p>In most cases (where FreeMarker is able to do that), for {@link TemplateLoader}-s that use * {@link URLConnection}, {@code URLConnection#setUseCaches(boolean)} will called with {@code false}, * so that only FreeMarker will do caching, not the URL scheme's handler. * See {@link URLTemplateLoader#setURLConnectionUsesCaches(Boolean)} for more details. * </li> * <li><p> * The default of the {@code template_loader} setting ({@link Configuration#getTemplateLoader()}) changes * to {@code null}, which means that FreeMarker will not find any templates. Earlier * the default was a {@link FileTemplateLoader} that used the current directory as the root. This was * dangerous and fragile as you usually don't have good control over what the current directory will be. * Luckily, the old default almost never looked for the templates at the right place * anyway, so pretty much all applications had to set the {@code template_loader} setting, so it's unlikely * that changing the default breaks your application. * </li> * <li><p> * Right-unlimited ranges become readable (like listable), so {@code <#list 1.. as i>...</#list>} works. * Earlier they were only usable for slicing (like {@code hits[10..]}). * </li> * <li><p> * Empty ranges return {@link Constants#EMPTY_SEQUENCE} instead of an empty {@link SimpleSequence}. This * is in theory backward compatible, as the API only promises to give something that implements * {@link TemplateSequenceModel}. * </li> * <li><p> * Unclosed comments ({@code <#-- ...}) and {@code #noparse}-s won't be silently closed at the end of * template anymore, but cause a parsing error instead. * </li> * </ul> * </li> * <li><p> * 2.3.22 (or higher): * <ul> * <li><p> * {@link DefaultObjectWrapper} has some substantial changes with {@code incompatibleImprovements} 2.3.22; * check them out at {@link DefaultObjectWrapper#DefaultObjectWrapper(Version)}. It's important to know * that if you set the {@code object_wrapper} setting (to an other value than {@code "default"}), rather * than leaving it on its default value, the {@code object_wrapper} won't inherit the * {@code incompatibleImprovements} of the {@link Configuration}. In that case, if you want the 2.3.22 * improvements of {@link DefaultObjectWrapper}, you have to set it in the {@link DefaultObjectWrapper} * object itself too! (Note that it's OK to use a {@link DefaultObjectWrapper} with a different * {@code incompatibleImprovements} version number than that of the {@link Configuration}, if that's * really what you want.) * </li> * <li><p> * In templates, {@code .template_name} will <em>always</em> return the main (top level) template's name. * It won't be affected by {@code #include} and {@code #nested} anymore. This is unintended, a bug with * {@code incompatible_improvement} 2.3.22 (a consequence of the lower level fixing described in the next * point). The old behavior of {@code .template_name} is restored if you set * {@code incompatible_improvement} to 2.3.23 (while {@link Configurable#getParent()}) of * {@link Environment} keeps the changed behavior shown in the next point). * </li> * <li><p> * {@code #include} and {@code #nested} doesn't change the parent {@link Template} (see * {@link Configurable#getParent()}) of the {@link Environment} anymore to the {@link Template} that's * included or whose namespace {@code #nested} "returns" to. Thus, the parent of {@link Environment} will * be now always the main {@link Template}. (The main {@link Template} is the {@link Template} whose * {@code process} or {@code createProcessingEnvironment} method was called to initiate the output * generation.) Note that apart from the effect on FTL's {@code .template_name} (see * previous point), this should only matter if you have set settings directly on {@link Template} objects, * and almost nobody does that. Also note that macro calls have never changed the {@link Environment} * parent to the {@link Template} that contains the macro definition, so this mechanism was always broken. * As now we consistently never change the parent, the behavior when calling macros didn't change. * </li> * <li><p> * When using {@code freemarker.ext.servlet.FreemarkerServlet}: * <ul> * <li> * <p>When using custom JSP tag libraries: Fixes bug where some kind of * values, when put into the JSP <em>page</em> scope (via {@code #global} or via the JSP * {@code PageContext} API) and later read back with the JSP {@code PageContext} API (typically in a * custom JSP tag), might come back as FreeMarker {@link TemplateModel} objects instead of as objects * with a standard Java type. Other Servlet scopes aren't affected. It's highly unlikely that * something expects the presence of this bug. The affected values are of the FTL types listed below, * and to trigger the bug, they either had to be created directly in the template (like as an FTL * literal or with {@code ?date}/{@code time}/{@code datetime}), or you had to use * {@link DefaultObjectWrapper} or {@link SimpleObjectWrapper} (or a subclass of them): * * <ul> * <li>FTL date/time/date-time values may came back as {@link SimpleDate}-s, now they come back as * {@link java.util.Date java.util.Date}-s instead.</li> * * <li>FTL sequence values may came back as {@link SimpleSequence}-s, now they come back as * {@link java.util.List}-s as expected. This at least stands assuming that the * {@link Configuration#setSetting(String, String) object_wrapper} configuration setting is a * subclass of {@link BeansWrapper} (such as {@link DefaultObjectWrapper}, which is the default), * but that's practically always the case in applications that use FreeMarker's JSP extension * (otherwise it can still work, but it depends on the quality and capabilities of the * {@link ObjectWrapper} implementation).</li> * * <li>FTL hash values may came back as {@link SimpleHash}-es, now they come back as * {@link java.util.Map}-s as expected (again, assuming that the object wrapper is a subclass of * {@link BeansWrapper}, like preferably {@link DefaultObjectWrapper}, which is also the default). * </li> * * <li>FTL collection values may came back as {@link SimpleCollection}-s, now they come back as * {@link java.util.Collection}-s as expected (again, assuming that the object wrapper is a subclass * of {@link BeansWrapper}, like preferably {@link DefaultObjectWrapper}).</li> * </ul> * </li> * <li><p> * Initial {@code "["} in the {@code TemplatePath} init-param * has special meaning; it's used for specifying multiple comma separated locations, like in * {@code <param-value>[ WEB-INF/templates, classpath:com/example/myapp/templates ]</param-value>} * </li> * <li><p> * Initial <tt>"{"</tt> in the {@code TemplatePath} init-param is reserved for future purposes, and * thus will throw exception. * </li> * </ul> * </li> * </ul> * </li> * <li><p> * 2.3.23 (or higher): * <ul> * <li><p> * Fixed a loophole in the implementation of the long existing parse-time rule that says that * {@code #break}, in the FTL source code itself, must occur nested inside a breakable directive, such as * {@code #list} or {@code #switch}. This check could be circumvented with {@code #macro} or * {@code #function}, like this: * {@code <#list 1..1 as x><#macro callMeLater><#break></#macro></#list><@callMeLater />}. * After activating this fix, this will be a parse time error. * </li> * <li><p> * If you have used {@code incompatible_improvements} 2.3.22 earlier, know that there the behavior of the * {@code .template_name} special variable used in templates was accidentally altered, but now it's * restored to be backward compatible with 2.3.0. (Ironically, the restored legacy behavior itself is * broken when it comes to macro invocations, we just keep it for backward compatibility. If you need fixed * behavior, use {@code .current_template_name} or {@code .main_template_name} instead.) * </li> * </ul> * </li> * <li><p> * 2.3.24 (or higher): * <ul> * <li><p> * The default of the * {@link #setRecognizeStandardFileExtensions(boolean) recognize_standard_file_extensions} * setting changes to {@code true}, which means that templates whose name ends with {@code ".ftlh"} or * {@code ".ftlx"} will automatically get {@link HTMLOutputFormat#INSTANCE} or * {@link XMLOutputFormat#INSTANCE} output format respectively, in both cases with * {@link #ENABLE_IF_DEFAULT_AUTO_ESCAPING_POLICY} {@link #setAutoEscapingPolicy(int) auto_escaping_policy}. * These "file" extensions aren't case sensitive. * </li> * <li><p> * In number format and date format strings (like in the {@code number_format} setting, or in templates in * {@code n?string("0.##")}), an initial {@code '@'} has special meaning; they refer to a custom format * with the name given after the {@code @} (see: {@link #setCustomNumberFormats(Map)}, * {@link #setCustomDateFormats(Map)}, {@link #setNumberFormat(String)}, and {@link #setDateTimeFormat}). * If the custom format doesn't exist, that will be an error. To have a literal {@code @} as the first * character in the output, it has to be written as {@code @@}. Again, all this only applies to the very * first character of the format string, so {@code @} characters elsewhere must not be doubled. Also, if * there are any custom formats defined, initial {@code '@'} will have the new meaning regardless of * the value of the {@code incompatible_improvements} setting. So you don't need to set the * {@code incompatible_improvements} only to use custom formats. * </li> * <li><p> * Expressions inside interpolations that were inside <em>string literal expressions</em> * (not <code>${...}</code>-s in general), like in <code>&lt;#assign s="Hello ${name}!"&gt;</code>, has * always used {@code incompatbileImprovement}-s 0 (2.3.0 in effect). Now it's fixed. * </li> * <li><p> * {@link DefaultObjectWrapper} has some minor changes with {@code incompatibleImprovements} 2.3.24; * check them out at {@link DefaultObjectWrapper#DefaultObjectWrapper(Version)}. It's important to know * that if you set the {@code object_wrapper} setting (to an other value than {@code "default"}), rather * than leaving it on its default value, the {@code object_wrapper} won't inherit the * {@code incompatibleImprovements} of the {@link Configuration}. In that case, if you want the 2.3.24 * improvements of {@link DefaultObjectWrapper}, you have to set it in the {@link DefaultObjectWrapper} * object itself too! (Note that it's OK to use a {@link DefaultObjectWrapper} with a different * {@code incompatibleImprovements} version number than that of the {@link Configuration}, if that's * really what you want.) * </li> * <li><p> * Fixed bug: The {@code #import} directive meant to copy the library variable into a global variable if * it's executed in the main namespace, but that haven't happened when the imported template was already * imported earlier in another namespace. * </li> * <li><p> * {@code ?is_sequence} doesn't return {@code true} for Java methods wrapped by {@link BeansWrapper} and * its subclasses (most notably {@link DefaultObjectWrapper}) anymore, as they only implement the * {@code [index]} operator, but not {@code ?size}, which causes {@code <#list ...>} to fail among others. * (They shouldn't implement either, but this is historical heritage.) * </ul> * </li> * <li><p> * 2.3.25 (or higher): * <ul> * <li><p> * When calling {@link Configurable#setAutoIncludes(List)} on a {@link Configuration}, it filters out * duplicates from the list, similarly as repeatedly calling {@link Configurable#addAutoInclude(String)} * would, hence avoiding repeated inclusions. Calling {@link Configurable#setAutoIncludes(List)} on other * {@link Configurable}-s always do this filtering regardless of the incompatible improvements setting. * </ul> * </li> * <li><p> * 2.3.26 (or higher): * <ul> * <li><p> * {@link BeansWrapper} and {@link DefaultObjectWrapper} now exposes Java 8 default methods (and the bean * properties they define); see {@link BeansWrapper#BeansWrapper(Version)}. * </ul> * </li> * <li><p> * 2.3.27 (or higher): * <ul> * <li><p> * {@link BeansWrapper} and {@link DefaultObjectWrapper} now prefers the non-indexed JavaBean property * read method over the indexed read method when Java 8 exposes both; * see {@link BeansWrapper#BeansWrapper(Version)}. * <li><p> * The following unchecked exceptions (but not their subclasses) will be wrapped into * {@link TemplateException}-s when thrown during evaluating expressions or calling directives: * {@link NullPointerException}, {@link ClassCastException}, {@link IndexOutOfBoundsException}, and * {@link InvocationTargetException}. The goal of this is the same as of setting * {@link #setWrapUncheckedExceptions(boolean) wrap_unchecked_exceptions} to {@code true} (see more there), * but this is more backward compatible, as it avoids wrapping unchecked exceptions that the calling * application is likely to catch specifically (like application-specific unchecked exceptions). * <li><p> * When the {@link Writer} returned by {@link TemplateTransformModel#getWriter(Writer, Map)} implements * {@link TransformControl}, exceptions that are used internally by FreeMarker for flow control (for * {@code <#return>}, {@code <#break>}, etc.) won't be passed to * {@link TransformControl#onError(Throwable)} anymore. Earlier, if {@code onError} didn't rethrow the * exception (though almost all implementation does), you couldn't use said directives inside the * transformed block. It's very unlikely that user code is affected by this, partially because these aren't * commonly implemented interfaces (especially not {@link TransformControl}), and because it's unlikely * that templates utilize the the bug that's not fixed. * </ul> * </li> * <li><p> * 2.3.28 (or higher): * <ul> * <li><p>When calling a macro or function (things defined in a template, not directly in Java) and the * argument list contains {@code .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.) Similarly, {@code .macro_caller_template_name} (which itself was added in 2.3.28), * when used in a macro call argument, won't be incorrectly evaluated in the context of the called macro. * <li><p>Fixed legacy parser glitch where a tag can be closed with an illegal {@code ]} (when it's not part * of an expression) despite that the tag syntax is set to angle brackets. For example {@code <#if x]} * worked just like {@code <#if x>}. Note that it doesn't affect the legal usage of {@code ]}, like * {@code <#if x[0]>} works correctly without this fix as well. * </ul> * </li> * </ul> * * @throws IllegalArgumentException * If {@code incompatibleImmprovements} refers to a version that wasn't released yet when the currently * used FreeMarker version was released, or is less than 2.3.0, or is {@code null}. * * @since 2.3.21 */
public Configuration(Version incompatibleImprovements) { super(incompatibleImprovements); // We postpone this until here (rather that doing this in static initializer) for two reason: // - Class initialization errors are often not reported very well // - This way we avoid the error if FM isn't actually used checkFreeMarkerVersionClash(); NullArgumentException.check("incompatibleImprovements", incompatibleImprovements); this.incompatibleImprovements = incompatibleImprovements; createTemplateCache(); loadBuiltInSharedVariables(); } private static void checkFreeMarkerVersionClash() { if (FM_24_DETECTED) { throw new RuntimeException("Clashing FreeMarker versions (" + VERSION + " and some post-2.3.x) detected: " + "found post-2.3.x class " + FM_24_DETECTION_CLASS_NAME + ". You probably have two different " + "freemarker.jar-s in the classpath."); } } private void createTemplateCache() { cache = new TemplateCache( getDefaultTemplateLoader(), getDefaultCacheStorage(), getDefaultTemplateLookupStrategy(), getDefaultTemplateNameFormat(), null, this); cache.clear(); // for fully BC behavior cache.setDelay(5000); } private void recreateTemplateCacheWith( TemplateLoader loader, CacheStorage storage, TemplateLookupStrategy templateLookupStrategy, TemplateNameFormat templateNameFormat, TemplateConfigurationFactory templateConfigurations) { TemplateCache oldCache = cache; cache = new TemplateCache( loader, storage, templateLookupStrategy, templateNameFormat, templateConfigurations, this); cache.clear(); // for fully BC behavior cache.setDelay(oldCache.getDelay()); cache.setLocalizedLookup(localizedLookup); } private void recreateTemplateCache() { recreateTemplateCacheWith(cache.getTemplateLoader(), cache.getCacheStorage(), cache.getTemplateLookupStrategy(), cache.getTemplateNameFormat(), getTemplateConfigurations()); } private TemplateLoader getDefaultTemplateLoader() { return createDefaultTemplateLoader(getIncompatibleImprovements(), getTemplateLoader()); } static TemplateLoader createDefaultTemplateLoader(Version incompatibleImprovements) { return createDefaultTemplateLoader(incompatibleImprovements, null); } private static TemplateLoader createDefaultTemplateLoader( Version incompatibleImprovements, TemplateLoader existingTemplateLoader) { if (incompatibleImprovements.intValue() < _TemplateAPI.VERSION_INT_2_3_21) { if (existingTemplateLoader instanceof LegacyDefaultFileTemplateLoader) { return existingTemplateLoader; } try { return new LegacyDefaultFileTemplateLoader(); } catch (Exception e) { CACHE_LOG.warn("Couldn't create legacy default TemplateLoader which accesses the current directory. " + "(Use new Configuration(Configuration.VERSION_2_3_21) or higher to avoid this.)", e); return null; } } else { return null; } } private static class LegacyDefaultFileTemplateLoader extends FileTemplateLoader { public LegacyDefaultFileTemplateLoader() throws IOException { super(); } } private TemplateLookupStrategy getDefaultTemplateLookupStrategy() { return getDefaultTemplateLookupStrategy(getIncompatibleImprovements()); } static TemplateLookupStrategy getDefaultTemplateLookupStrategy(Version incompatibleImprovements) { return TemplateLookupStrategy.DEFAULT_2_3_0; } private TemplateNameFormat getDefaultTemplateNameFormat() { return getDefaultTemplateNameFormat(getIncompatibleImprovements()); } static TemplateNameFormat getDefaultTemplateNameFormat(Version incompatibleImprovements) { return TemplateNameFormat.DEFAULT_2_3_0; } private CacheStorage getDefaultCacheStorage() { return createDefaultCacheStorage(getIncompatibleImprovements(), getCacheStorage()); } static CacheStorage createDefaultCacheStorage(Version incompatibleImprovements, CacheStorage existingCacheStorage) { if (existingCacheStorage instanceof DefaultSoftCacheStorage) { return existingCacheStorage; } return new DefaultSoftCacheStorage(); } static CacheStorage createDefaultCacheStorage(Version incompatibleImprovements) { return createDefaultCacheStorage(incompatibleImprovements, null); } private static class DefaultSoftCacheStorage extends SoftCacheStorage { // Nothing to override } private TemplateExceptionHandler getDefaultTemplateExceptionHandler() { return getDefaultTemplateExceptionHandler(getIncompatibleImprovements()); } private AttemptExceptionReporter getDefaultAttemptExceptionReporter() { return getDefaultAttemptExceptionReporter(getIncompatibleImprovements()); } private boolean getDefaultLogTemplateExceptions() { return getDefaultLogTemplateExceptions(getIncompatibleImprovements()); } private boolean getDefaultWrapUncheckedExceptions() { return getDefaultWrapUncheckedExceptions(getIncompatibleImprovements()); } private ObjectWrapper getDefaultObjectWrapper() { return getDefaultObjectWrapper(getIncompatibleImprovements()); } // Package visible as Configurable needs this to initialize the field defaults. static TemplateExceptionHandler getDefaultTemplateExceptionHandler(Version incompatibleImprovements) { return TemplateExceptionHandler.DEBUG_HANDLER; } // Package visible as Configurable needs this to initialize the field defaults. static AttemptExceptionReporter getDefaultAttemptExceptionReporter(Version incompatibleImprovements) { return AttemptExceptionReporter.LOG_ERROR_REPORTER; } // Package visible as Configurable needs this to initialize the field defaults. static boolean getDefaultLogTemplateExceptions(Version incompatibleImprovements) { return true; } // Package visible as Configurable needs this to initialize the field defaults. static boolean getDefaultWrapUncheckedExceptions(Version incompatibleImprovements) { return false; } @Override public Object clone() { try { Configuration copy = (Configuration) super.clone(); copy.sharedVariables = new HashMap(sharedVariables); copy.localeToCharsetMap = new ConcurrentHashMap(localeToCharsetMap); copy.recreateTemplateCacheWith( cache.getTemplateLoader(), cache.getCacheStorage(), cache.getTemplateLookupStrategy(), cache.getTemplateNameFormat(), cache.getTemplateConfigurations()); return copy; } catch (CloneNotSupportedException e) { throw new BugException("Cloning failed", e); } } private void loadBuiltInSharedVariables() { sharedVariables.put("capture_output", new CaptureOutput()); sharedVariables.put("compress", StandardCompress.INSTANCE); sharedVariables.put("html_escape", new HtmlEscape()); sharedVariables.put("normalize_newlines", new NormalizeNewlines()); sharedVariables.put("xml_escape", new XmlEscape()); }
Loads a preset language-to-encoding map, similarly as if you have called clearEncodingMap() and then did multiple setEncoding(Locale, String) calls. It assumes the usual character encodings for most languages. The previous content of the encoding map will be lost. This default map currently contains the following mappings:
arISO-8859-6
beISO-8859-5
bgISO-8859-5
caISO-8859-1
csISO-8859-2
daISO-8859-1
deISO-8859-1
elISO-8859-7
enISO-8859-1
esISO-8859-1
etISO-8859-1
fiISO-8859-1
frISO-8859-1
hrISO-8859-2
huISO-8859-2
isISO-8859-1
itISO-8859-1
iwISO-8859-8
jaShift_JIS
koEUC-KR
ltISO-8859-2
lvISO-8859-2
mkISO-8859-5
nlISO-8859-1
noISO-8859-1
plISO-8859-2
ptISO-8859-1
roISO-8859-2
ruISO-8859-5
shISO-8859-5
skISO-8859-2
slISO-8859-2
sqISO-8859-2
srISO-8859-5
svISO-8859-1
trISO-8859-9
ukISO-8859-5
zhGB2312
zh_TWBig5
See Also:
/** * Loads a preset language-to-encoding map, similarly as if you have called * {@link #clearEncodingMap()} and then did multiple {@link #setEncoding(Locale, String)} calls. * It assumes the usual character encodings for most languages. * The previous content of the encoding map will be lost. * This default map currently contains the following mappings: * * <table style="width: auto; border-collapse: collapse" border="1" summary="preset language to encoding mapping"> * <tr><td>ar</td><td>ISO-8859-6</td></tr> * <tr><td>be</td><td>ISO-8859-5</td></tr> * <tr><td>bg</td><td>ISO-8859-5</td></tr> * <tr><td>ca</td><td>ISO-8859-1</td></tr> * <tr><td>cs</td><td>ISO-8859-2</td></tr> * <tr><td>da</td><td>ISO-8859-1</td></tr> * <tr><td>de</td><td>ISO-8859-1</td></tr> * <tr><td>el</td><td>ISO-8859-7</td></tr> * <tr><td>en</td><td>ISO-8859-1</td></tr> * <tr><td>es</td><td>ISO-8859-1</td></tr> * <tr><td>et</td><td>ISO-8859-1</td></tr> * <tr><td>fi</td><td>ISO-8859-1</td></tr> * <tr><td>fr</td><td>ISO-8859-1</td></tr> * <tr><td>hr</td><td>ISO-8859-2</td></tr> * <tr><td>hu</td><td>ISO-8859-2</td></tr> * <tr><td>is</td><td>ISO-8859-1</td></tr> * <tr><td>it</td><td>ISO-8859-1</td></tr> * <tr><td>iw</td><td>ISO-8859-8</td></tr> * <tr><td>ja</td><td>Shift_JIS</td></tr> * <tr><td>ko</td><td>EUC-KR</td></tr> * <tr><td>lt</td><td>ISO-8859-2</td></tr> * <tr><td>lv</td><td>ISO-8859-2</td></tr> * <tr><td>mk</td><td>ISO-8859-5</td></tr> * <tr><td>nl</td><td>ISO-8859-1</td></tr> * <tr><td>no</td><td>ISO-8859-1</td></tr> * <tr><td>pl</td><td>ISO-8859-2</td></tr> * <tr><td>pt</td><td>ISO-8859-1</td></tr> * <tr><td>ro</td><td>ISO-8859-2</td></tr> * <tr><td>ru</td><td>ISO-8859-5</td></tr> * <tr><td>sh</td><td>ISO-8859-5</td></tr> * <tr><td>sk</td><td>ISO-8859-2</td></tr> * <tr><td>sl</td><td>ISO-8859-2</td></tr> * <tr><td>sq</td><td>ISO-8859-2</td></tr> * <tr><td>sr</td><td>ISO-8859-5</td></tr> * <tr><td>sv</td><td>ISO-8859-1</td></tr> * <tr><td>tr</td><td>ISO-8859-9</td></tr> * <tr><td>uk</td><td>ISO-8859-5</td></tr> * <tr><td>zh</td><td>GB2312</td></tr> * <tr><td>zh_TW</td><td>Big5</td></tr> * </table> * * @see #clearEncodingMap() * @see #setEncoding(Locale, String) * @see #setDefaultEncoding(String) */
public void loadBuiltInEncodingMap() { localeToCharsetMap.clear(); localeToCharsetMap.put("ar", "ISO-8859-6"); localeToCharsetMap.put("be", "ISO-8859-5"); localeToCharsetMap.put("bg", "ISO-8859-5"); localeToCharsetMap.put("ca", "ISO-8859-1"); localeToCharsetMap.put("cs", "ISO-8859-2"); localeToCharsetMap.put("da", "ISO-8859-1"); localeToCharsetMap.put("de", "ISO-8859-1"); localeToCharsetMap.put("el", "ISO-8859-7"); localeToCharsetMap.put("en", "ISO-8859-1"); localeToCharsetMap.put("es", "ISO-8859-1"); localeToCharsetMap.put("et", "ISO-8859-1"); localeToCharsetMap.put("fi", "ISO-8859-1"); localeToCharsetMap.put("fr", "ISO-8859-1"); localeToCharsetMap.put("hr", "ISO-8859-2"); localeToCharsetMap.put("hu", "ISO-8859-2"); localeToCharsetMap.put("is", "ISO-8859-1"); localeToCharsetMap.put("it", "ISO-8859-1"); localeToCharsetMap.put("iw", "ISO-8859-8"); localeToCharsetMap.put("ja", "Shift_JIS"); localeToCharsetMap.put("ko", "EUC-KR"); localeToCharsetMap.put("lt", "ISO-8859-2"); localeToCharsetMap.put("lv", "ISO-8859-2"); localeToCharsetMap.put("mk", "ISO-8859-5"); localeToCharsetMap.put("nl", "ISO-8859-1"); localeToCharsetMap.put("no", "ISO-8859-1"); localeToCharsetMap.put("pl", "ISO-8859-2"); localeToCharsetMap.put("pt", "ISO-8859-1"); localeToCharsetMap.put("ro", "ISO-8859-2"); localeToCharsetMap.put("ru", "ISO-8859-5"); localeToCharsetMap.put("sh", "ISO-8859-5"); localeToCharsetMap.put("sk", "ISO-8859-2"); localeToCharsetMap.put("sl", "ISO-8859-2"); localeToCharsetMap.put("sq", "ISO-8859-2"); localeToCharsetMap.put("sr", "ISO-8859-5"); localeToCharsetMap.put("sv", "ISO-8859-1"); localeToCharsetMap.put("tr", "ISO-8859-9"); localeToCharsetMap.put("uk", "ISO-8859-5"); localeToCharsetMap.put("zh", "GB2312"); localeToCharsetMap.put("zh_TW", "Big5"); }
Clears language-to-encoding map.
See Also:
/** * Clears language-to-encoding map. * @see #loadBuiltInEncodingMap * @see #setEncoding */
public void clearEncodingMap() { localeToCharsetMap.clear(); }
Returns the default (singleton) Configuration object. Note that you can create as many separate configurations as you wish; this global instance is provided for convenience, or when you have no reason to use a separate instance.
Deprecated:The usage of the static singleton (the "default") Configuration instance can easily cause erroneous, unpredictable behavior. This is because multiple independent software components may use FreeMarker internally inside the same application, so they will interfere because of the common Configuration instance. Each such component should use its own private Configuration object instead, that it typically creates with new Configuration() when the component is initialized.
/** * Returns the default (singleton) Configuration object. Note that you can * create as many separate configurations as you wish; this global instance * is provided for convenience, or when you have no reason to use a separate * instance. * * @deprecated The usage of the static singleton (the "default") * {@link Configuration} instance can easily cause erroneous, unpredictable * behavior. This is because multiple independent software components may use * FreeMarker internally inside the same application, so they will interfere * because of the common {@link Configuration} instance. Each such component * should use its own private {@link Configuration} object instead, that it * typically creates with <code>new Configuration()</code> when the component * is initialized. */
@Deprecated static public Configuration getDefaultConfiguration() { Configuration defaultConfig = Configuration.defaultConfig; if (defaultConfig == null) { synchronized (defaultConfigLock) { defaultConfig = Configuration.defaultConfig; if (defaultConfig == null) { defaultConfig = new Configuration(); Configuration.defaultConfig = defaultConfig; } } } return defaultConfig; }
Sets the Configuration object that will be retrieved from future calls to getDefaultConfiguration().
Deprecated:Using the "default" Configuration instance can easily lead to erroneous, unpredictable behaviour. See more here....
/** * Sets the Configuration object that will be retrieved from future calls * to {@link #getDefaultConfiguration()}. * * @deprecated Using the "default" {@link Configuration} instance can * easily lead to erroneous, unpredictable behaviour. * See more {@link Configuration#getDefaultConfiguration() here...}. */
@Deprecated static public void setDefaultConfiguration(Configuration config) { synchronized (defaultConfigLock) { defaultConfig = config; } }
Sets a TemplateLoader that is used to look up and load templates; as a side effect the template cache will be emptied (unless the new and the old values are the same). By providing your own TemplateLoader implementation, you can load templates from whatever kind of storages, like from relational databases, NoSQL-storages, etc.

Convenience methods exists to install commonly used loaders, instead of using this method: setClassForTemplateLoading(Class, String), setClassLoaderForTemplateLoading(ClassLoader, String), setDirectoryForTemplateLoading(File), and setServletContextForTemplateLoading(Object, String).

You can chain several TemplateLoader-s together with MultiTemplateLoader.

Default value: You should always set the template loader instead of relying on the default value. (But if you still care what it is, before "incompatible improvements" 2.3.21 it's a FileTemplateLoader that uses the current directory as its root; as it's hard tell what that directory will be, it's not very useful and dangerous. Starting with "incompatible improvements" 2.3.21 the default is null.)

/** * Sets a {@link TemplateLoader} that is used to look up and load templates; * as a side effect the template cache will be emptied (unless the new and the old values are the same). * By providing your own {@link TemplateLoader} implementation, you can load templates from whatever kind of * storages, like from relational databases, NoSQL-storages, etc. * * <p>Convenience methods exists to install commonly used loaders, instead of using this method: * {@link #setClassForTemplateLoading(Class, String)}, * {@link #setClassLoaderForTemplateLoading(ClassLoader, String)}, * {@link #setDirectoryForTemplateLoading(File)}, and * {@link #setServletContextForTemplateLoading(Object, String)}. * * <p>You can chain several {@link TemplateLoader}-s together with {@link MultiTemplateLoader}. * * <p>Default value: You should always set the template loader instead of relying on the default value. * (But if you still care what it is, before "incompatible improvements" 2.3.21 it's a {@link FileTemplateLoader} * that uses the current directory as its root; as it's hard tell what that directory will be, it's not very useful * and dangerous. Starting with "incompatible improvements" 2.3.21 the default is {@code null}.) */
public void setTemplateLoader(TemplateLoader templateLoader) { // "synchronized" is removed from the API as it's not safe to set anything after publishing the Configuration synchronized (this) { if (cache.getTemplateLoader() != templateLoader) { recreateTemplateCacheWith(templateLoader, cache.getCacheStorage(), cache.getTemplateLookupStrategy(), cache.getTemplateNameFormat(), cache.getTemplateConfigurations()); } templateLoaderExplicitlySet = true; } }
Resets the setting to its default, as if it was never set. This means that when you change the incompatibe_improvements setting later, the default will also change as appropriate. Also isTemplateLoaderExplicitlySet() will return false.
Since:2.3.22
/** * Resets the setting to its default, as if it was never set. This means that when you change the * {@code incompatibe_improvements} setting later, the default will also change as appropriate. Also * {@link #isTemplateLoaderExplicitlySet()} will return {@code false}. * * @since 2.3.22 */
public void unsetTemplateLoader() { if (templateLoaderExplicitlySet) { setTemplateLoader(getDefaultTemplateLoader()); templateLoaderExplicitlySet = false; } }
Tells if setTemplateLoader(TemplateLoader) (or equivalent) was already called on this instance.
Since:2.3.22
/** * Tells if {@link #setTemplateLoader(TemplateLoader)} (or equivalent) was already called on this instance. * * @since 2.3.22 */
public boolean isTemplateLoaderExplicitlySet() { return templateLoaderExplicitlySet; } /** * The getter pair of {@link #setTemplateLoader(TemplateLoader)}. */ public TemplateLoader getTemplateLoader() { if (cache == null) { return null; } return cache.getTemplateLoader(); }
Sets the TemplateLookupStrategy that is used to look up templates based on the requested name; as a side effect the template cache will be emptied. The default value is TemplateLookupStrategy.DEFAULT_2_3_0.
Since:2.3.22
/** * Sets the {@link TemplateLookupStrategy} that is used to look up templates based on the requested name; as a side * effect the template cache will be emptied. The default value is {@link TemplateLookupStrategy#DEFAULT_2_3_0}. * * @since 2.3.22 */
public void setTemplateLookupStrategy(TemplateLookupStrategy templateLookupStrategy) { if (cache.getTemplateLookupStrategy() != templateLookupStrategy) { recreateTemplateCacheWith(cache.getTemplateLoader(), cache.getCacheStorage(), templateLookupStrategy, cache.getTemplateNameFormat(), cache.getTemplateConfigurations()); } templateLookupStrategyExplicitlySet = true; }
Resets the setting to its default, as if it was never set. This means that when you change the incompatibe_improvements setting later, the default will also change as appropriate. Also isTemplateLookupStrategyExplicitlySet() will return false.
Since:2.3.22
/** * Resets the setting to its default, as if it was never set. This means that when you change the * {@code incompatibe_improvements} setting later, the default will also change as appropriate. Also * {@link #isTemplateLookupStrategyExplicitlySet()} will return {@code false}. * * @since 2.3.22 */
public void unsetTemplateLookupStrategy() { if (templateLookupStrategyExplicitlySet) { setTemplateLookupStrategy(getDefaultTemplateLookupStrategy()); templateLookupStrategyExplicitlySet = false; } }
Tells if setTemplateLookupStrategy(TemplateLookupStrategy) (or equivalent) was already called on this instance.
Since:2.3.22
/** * Tells if {@link #setTemplateLookupStrategy(TemplateLookupStrategy)} (or equivalent) was already called on this * instance. * * @since 2.3.22 */
public boolean isTemplateLookupStrategyExplicitlySet() { return templateLookupStrategyExplicitlySet; } /** * The getter pair of {@link #setTemplateLookupStrategy(TemplateLookupStrategy)}. */ public TemplateLookupStrategy getTemplateLookupStrategy() { if (cache == null) { return null; } return cache.getTemplateLookupStrategy(); }
Sets the template name format used. The default is TemplateNameFormat.DEFAULT_2_3_0, while the recommended value for new projects is TemplateNameFormat.DEFAULT_2_4_0.
Since:2.3.22
/** * Sets the template name format used. The default is {@link TemplateNameFormat#DEFAULT_2_3_0}, while the * recommended value for new projects is {@link TemplateNameFormat#DEFAULT_2_4_0}. * * @since 2.3.22 */
public void setTemplateNameFormat(TemplateNameFormat templateNameFormat) { if (cache.getTemplateNameFormat() != templateNameFormat) { recreateTemplateCacheWith(cache.getTemplateLoader(), cache.getCacheStorage(), cache.getTemplateLookupStrategy(), templateNameFormat, cache.getTemplateConfigurations()); } templateNameFormatExplicitlySet = true; }
Resets the setting to its default, as if it was never set. This means that when you change the incompatibe_improvements setting later, the default will also change as appropriate. Also isTemplateNameFormatExplicitlySet() will return false.
Since:2.3.22
/** * Resets the setting to its default, as if it was never set. This means that when you change the * {@code incompatibe_improvements} setting later, the default will also change as appropriate. Also * {@link #isTemplateNameFormatExplicitlySet()} will return {@code false}. * * @since 2.3.22 */
public void unsetTemplateNameFormat() { if (templateNameFormatExplicitlySet) { setTemplateNameFormat(getDefaultTemplateNameFormat()); templateNameFormatExplicitlySet = false; } }
Tells if setTemplateNameFormat(TemplateNameFormat) (or equivalent) was already called on this instance.
Since:2.3.22
/** * Tells if {@link #setTemplateNameFormat(TemplateNameFormat)} (or equivalent) was already called on this instance. * * @since 2.3.22 */
public boolean isTemplateNameFormatExplicitlySet() { return templateNameFormatExplicitlySet; } /** * The getter pair of {@link #setTemplateNameFormat(TemplateNameFormat)}. */ public TemplateNameFormat getTemplateNameFormat() { if (cache == null) { return null; } return cache.getTemplateNameFormat(); }
Sets a TemplateConfigurationFactory that will configure individual templates where their settings differ from those coming from the common Configuration object. A typical use case for that is specifying the outputFormat for templates based on their file extension or parent directory.

Note that the settings suggested by standard file extensions are stronger than that you set here. See setRecognizeStandardFileExtensions(boolean) for more information about standard file extensions.

See "Template configurations" in the FreeMarker Manual for examples.

Since:2.3.24
/** * Sets a {@link TemplateConfigurationFactory} that will configure individual templates where their settings differ * from those coming from the common {@link Configuration} object. A typical use case for that is specifying the * {@link TemplateConfiguration#setOutputFormat(OutputFormat) outputFormat} for templates based on their file * extension or parent directory. * * <p> * Note that the settings suggested by standard file extensions are stronger than that you set here. See * {@link #setRecognizeStandardFileExtensions(boolean)} for more information about standard file extensions. * * <p>See "Template configurations" in the FreeMarker Manual for examples. * * @since 2.3.24 */
public void setTemplateConfigurations(TemplateConfigurationFactory templateConfigurations) { if (cache.getTemplateConfigurations() != templateConfigurations) { if (templateConfigurations != null) { templateConfigurations.setConfiguration(this); } recreateTemplateCacheWith(cache.getTemplateLoader(), cache.getCacheStorage(), cache.getTemplateLookupStrategy(), cache.getTemplateNameFormat(), templateConfigurations); } } /** * The getter pair of {@link #setTemplateConfigurations(TemplateConfigurationFactory)}. */ public TemplateConfigurationFactory getTemplateConfigurations() { if (cache == null) { return null; } return cache.getTemplateConfigurations(); }
Sets the CacheStorage used for caching Template-s; the earlier content of the template cache will be dropt. The default is a SoftCacheStorage. If the total size of the Template objects is significant but most templates are used rarely, using a MruCacheStorage instead might be advisable. If you don't want caching at all, use NullCacheStorage (you can't use null).

Note that setting the cache storage will re-create the template cache, so all its content will be lost.

/** * Sets the {@link CacheStorage} used for caching {@link Template}-s; * the earlier content of the template cache will be dropt. * * The default is a {@link SoftCacheStorage}. If the total size of the {@link Template} * objects is significant but most templates are used rarely, using a * {@link MruCacheStorage} instead might be advisable. If you don't want caching at * all, use {@link freemarker.cache.NullCacheStorage} (you can't use {@code null}). * * <p>Note that setting the cache storage will re-create the template cache, so * all its content will be lost. */
public void setCacheStorage(CacheStorage cacheStorage) { // "synchronized" is removed from the API as it's not safe to set anything after publishing the Configuration synchronized (this) { if (getCacheStorage() != cacheStorage) { recreateTemplateCacheWith(cache.getTemplateLoader(), cacheStorage, cache.getTemplateLookupStrategy(), cache.getTemplateNameFormat(), cache.getTemplateConfigurations()); } cacheStorageExplicitlySet = true; } }
Resets the setting to its default, as if it was never set. This means that when you change the incompatibe_improvements setting later, the default will also change as appropriate. Also isCacheStorageExplicitlySet() will return false.
Since:2.3.22
/** * Resets the setting to its default, as if it was never set. This means that when you change the * {@code incompatibe_improvements} setting later, the default will also change as appropriate. Also * {@link #isCacheStorageExplicitlySet()} will return {@code false}. * * @since 2.3.22 */
public void unsetCacheStorage() { if (cacheStorageExplicitlySet) { setCacheStorage(getDefaultCacheStorage()); cacheStorageExplicitlySet = false; } }
Tells if setCacheStorage(CacheStorage) (or equivalent) was already called on this instance.
Since:2.3.22
/** * Tells if {@link #setCacheStorage(CacheStorage)} (or equivalent) was already called on this instance. * * @since 2.3.22 */
public boolean isCacheStorageExplicitlySet() { return cacheStorageExplicitlySet; }
The getter pair of setCacheStorage(CacheStorage).
Since:2.3.20
/** * The getter pair of {@link #setCacheStorage(CacheStorage)}. * * @since 2.3.20 */
public CacheStorage getCacheStorage() { // "synchronized" is removed from the API as it's not safe to set anything after publishing the Configuration synchronized (this) { if (cache == null) { return null; } return cache.getCacheStorage(); } }
Sets the file system directory from which to load templates. This is equivalent to setTemplateLoader(new FileTemplateLoader(dir)), so see FileTemplateLoader(File) for more details.

Note that FreeMarker can load templates from non-file-system sources too. See setTemplateLoader(TemplateLoader) from more details.

Note that this shouldn't be used for loading templates that are coming from a WAR; use setServletContextForTemplateLoading(Object, String) then. Servlet containers might not unpack the WAR file, in which case you clearly can't access the contained files via File. Even if the WAR is unpacked, the servlet container might not expose the location as a File. setServletContextForTemplateLoading(Object, String) on the other hand will work in all these cases.

/** * Sets the file system directory from which to load templates. This is equivalent to * {@code setTemplateLoader(new FileTemplateLoader(dir))}, so see * {@link FileTemplateLoader#FileTemplateLoader(File)} for more details. * * <p> * Note that FreeMarker can load templates from non-file-system sources too. See * {@link #setTemplateLoader(TemplateLoader)} from more details. * * <p> * Note that this shouldn't be used for loading templates that are coming from a WAR; use * {@link #setServletContextForTemplateLoading(Object, String)} then. Servlet containers might not unpack the WAR * file, in which case you clearly can't access the contained files via {@link File}. Even if the WAR is unpacked, * the servlet container might not expose the location as a {@link File}. * {@link #setServletContextForTemplateLoading(Object, String)} on the other hand will work in all these cases. */
public void setDirectoryForTemplateLoading(File dir) throws IOException { TemplateLoader tl = getTemplateLoader(); if (tl instanceof FileTemplateLoader) { String path = ((FileTemplateLoader) tl).baseDir.getCanonicalPath(); if (path.equals(dir.getCanonicalPath())) return; } setTemplateLoader(new FileTemplateLoader(dir)); }
Sets the servlet context from which to load templates. This is equivalent to setTemplateLoader(new WebappTemplateLoader(sctxt, path)) or setTemplateLoader(new WebappTemplateLoader(sctxt)) if path was null, so see freemarker.cache.WebappTemplateLoader for more details.
Params:
  • servletContext – the javax.servlet.ServletContext object. (The declared type is Object to prevent class loading error when using FreeMarker in an environment where there's no servlet classes available.)
  • path – the path relative to the ServletContext.
See Also:
/** * Sets the servlet context from which to load templates. * This is equivalent to {@code setTemplateLoader(new WebappTemplateLoader(sctxt, path))} * or {@code setTemplateLoader(new WebappTemplateLoader(sctxt))} if {@code path} was * {@code null}, so see {@code freemarker.cache.WebappTemplateLoader} for more details. * * @param servletContext the {@code javax.servlet.ServletContext} object. (The declared type is {@link Object} * to prevent class loading error when using FreeMarker in an environment where * there's no servlet classes available.) * @param path the path relative to the ServletContext. * * @see #setTemplateLoader(TemplateLoader) */
public void setServletContextForTemplateLoading(Object servletContext, String path) { try { // Don't introduce linking-time dependency on servlets final Class webappTemplateLoaderClass = ClassUtil.forName("freemarker.cache.WebappTemplateLoader"); // Don't introduce linking-time dependency on servlets final Class servletContextClass = ClassUtil.forName("javax.servlet.ServletContext"); final Class[] constructorParamTypes; final Object[] constructorParams; if (path == null) { constructorParamTypes = new Class[] { servletContextClass }; constructorParams = new Object[] { servletContext }; } else { constructorParamTypes = new Class[] { servletContextClass, String.class }; constructorParams = new Object[] { servletContext, path }; } setTemplateLoader( (TemplateLoader) webappTemplateLoaderClass .getConstructor(constructorParamTypes) .newInstance(constructorParams)); } catch (Exception e) { throw new BugException(e); } }
Sets the class whose Class.getResource(String) method will be used to load templates, from the inside the package specified. See ClassTemplateLoader(Class<?>, String) for more details.
Params:
See Also:
/** * Sets the class whose {@link Class#getResource(String)} method will be used to load templates, from the inside the * package specified. See {@link ClassTemplateLoader#ClassTemplateLoader(Class, String)} for more details. * * @param basePackagePath * Separate steps with {@code "/"}, not {@code "."}, and note that it matters if this starts with * {@code /} or not. See {@link ClassTemplateLoader#ClassTemplateLoader(Class, String)} for more details. * * @see #setClassLoaderForTemplateLoading(ClassLoader, String) * @see #setTemplateLoader(TemplateLoader) */
public void setClassForTemplateLoading(Class resourceLoaderClass, String basePackagePath) { setTemplateLoader(new ClassTemplateLoader(resourceLoaderClass, basePackagePath)); }
Sets the ClassLoader whose ClassLoader.getResource(String) method will be used to load templates, from the inside the package specified. See ClassTemplateLoader(Class<?>, String) for more details.
Params:
See Also:
Since:2.3.22
/** * Sets the {@link ClassLoader} whose {@link ClassLoader#getResource(String)} method will be used to load templates, * from the inside the package specified. See {@link ClassTemplateLoader#ClassTemplateLoader(Class, String)} for * more details. * * @param basePackagePath * Separate steps with {@code "/"}, not {@code "."}. See * {@link ClassTemplateLoader#ClassTemplateLoader(Class, String)} for more details. * * @see #setClassForTemplateLoading(Class, String) * @see #setTemplateLoader(TemplateLoader) * * @since 2.3.22 */
public void setClassLoaderForTemplateLoading(ClassLoader classLoader, String basePackagePath) { setTemplateLoader(new ClassTemplateLoader(classLoader, basePackagePath)); }
Sets the time in seconds that must elapse before checking whether there is a newer version of a template "file" than the cached one.

Historical note: Despite what the API documentation said earlier, this method is not thread-safe. While it works well on most hardware, it's not guaranteed that FreeMarker will see the update in all threads, and theoretically it's also possible that it will see a value that's a binary mixture of the new and the old one.

Deprecated:Use setTemplateUpdateDelayMilliseconds(long) instead, because the time granularity of this method is often misunderstood to be milliseconds.
/** * Sets the time in seconds that must elapse before checking whether there is a newer version of a template "file" * than the cached one. * * <p> * Historical note: Despite what the API documentation said earlier, this method is <em>not</em> thread-safe. While * it works well on most hardware, it's not guaranteed that FreeMarker will see the update in all threads, and * theoretically it's also possible that it will see a value that's a binary mixture of the new and the old one. * * @deprecated Use {@link #setTemplateUpdateDelayMilliseconds(long)} instead, because the time granularity of this method * is often misunderstood to be milliseconds. */
@Deprecated public void setTemplateUpdateDelay(int seconds) { cache.setDelay(1000L * seconds); }
Sets the time in milliseconds that must elapse before checking whether there is a newer version of a template "file" than the cached one. Defaults to 5000 ms.

When you get a template via getTemplate(String) (or some of its overloads). FreeMarker will try to get the template from the template cache. If the template is found, and at least this amount of time was elapsed since the template last modification date was checked, FreeMarker will re-check the last modification date (this could mean I/O), possibly reloading the template and updating the cache as a consequence (can mean even more I/O). The getTemplate(String) (or some of its overloads) call will only return after this all is done, so it will return the fresh template.

Since:2.3.23
/** * Sets the time in milliseconds that must elapse before checking whether there is a newer version of a template * "file" than the cached one. Defaults to 5000 ms. * * <p> * When you get a template via {@link #getTemplate(String)} (or some of its overloads). FreeMarker will try to get * the template from the template cache. If the template is found, and at least this amount of time was elapsed * since the template last modification date was checked, FreeMarker will re-check the last modification date (this * could mean I/O), possibly reloading the template and updating the cache as a consequence (can mean even more * I/O). The {@link #getTemplate(String)} (or some of its overloads) call will only return after this all is * done, so it will return the fresh template. * * @since 2.3.23 */
public void setTemplateUpdateDelayMilliseconds(long millis) { cache.setDelay(millis); }
Since:2.3.23
/** * The getter pair of {@link #setTemplateUpdateDelayMilliseconds(long)}. * * @since 2.3.23 */
public long getTemplateUpdateDelayMilliseconds() { return cache.getDelay(); }
Sets whether directives such as if, else, etc must be written as #if, #else, etc. Defaults to true.

When this is true, any tag not starting with <# or </# or <@ or </@ is considered as plain text and will go to the output as is. Tag starting with <# or </# must be valid FTL tag, or else the template is invalid (i.e. <#noSuchDirective> is an error).

Deprecated:Only true (the default) value will be supported sometimes in the future.
/** * Sets whether directives such as {@code if}, {@code else}, etc must be written as {@code #if}, {@code #else}, etc. * Defaults to {@code true}. * * <p>When this is {@code true}, * any tag not starting with &lt;# or &lt;/# or &lt;@ or &lt;/@ is considered as plain text * and will go to the output as is. Tag starting with &lt;# or &lt;/# must * be valid FTL tag, or else the template is invalid (i.e. &lt;#noSuchDirective&gt; * is an error). * * @deprecated Only {@code true} (the default) value will be supported sometimes in the future. */
@Deprecated public void setStrictSyntaxMode(boolean b) { strictSyntax = b; } @Override public void setObjectWrapper(ObjectWrapper objectWrapper) { ObjectWrapper prevObjectWrapper = getObjectWrapper(); super.setObjectWrapper(objectWrapper); objectWrapperExplicitlySet = true; if (objectWrapper != prevObjectWrapper) { try { setSharedVariablesFromRewrappableSharedVariables(); } catch (TemplateModelException e) { throw new RuntimeException( "Failed to re-wrap earliearly set shared variables with the newly set object wrapper", e); } } }
Resets the setting to its default, as if it was never set. This means that when you change the incompatibe_improvements setting later, the default will also change as appropriate. Also isObjectWrapperExplicitlySet() will return false.
Since:2.3.22
/** * Resets the setting to its default, as if it was never set. This means that when you change the * {@code incompatibe_improvements} setting later, the default will also change as appropriate. Also * {@link #isObjectWrapperExplicitlySet()} will return {@code false}. * * @since 2.3.22 */
public void unsetObjectWrapper() { if (objectWrapperExplicitlySet) { setObjectWrapper(getDefaultObjectWrapper()); objectWrapperExplicitlySet = false; } }
Tells if setObjectWrapper(ObjectWrapper) (or equivalent) was already called on this instance.
Since:2.3.22
/** * Tells if {@link #setObjectWrapper(ObjectWrapper)} (or equivalent) was already called on this instance. * * @since 2.3.22 */
public boolean isObjectWrapperExplicitlySet() { return objectWrapperExplicitlySet; } @Override public void setLocale(Locale locale) { super.setLocale(locale); localeExplicitlySet = true; }
Resets the setting to its default, as if it was never set.
Since:2.3.26
/** * Resets the setting to its default, as if it was never set. * * @since 2.3.26 */
public void unsetLocale() { if (localeExplicitlySet) { setLocale(getDefaultLocale()); localeExplicitlySet = false; } }
Tells if setLocale(Locale) (or equivalent) was already called on this instance, or it just holds the default value.
Since:2.3.26
/** * Tells if {@link #setLocale(Locale)} (or equivalent) was already called on this instance, or it just holds the * default value. * * @since 2.3.26 */
public boolean isLocaleExplicitlySet() { return localeExplicitlySet; } static Locale getDefaultLocale() { return Locale.getDefault(); } @Override public void setTimeZone(TimeZone timeZone) { super.setTimeZone(timeZone); timeZoneExplicitlySet = true; }
Resets the setting to its default, as if it was never set.
Since:2.3.26
/** * Resets the setting to its default, as if it was never set. * * @since 2.3.26 */
public void unsetTimeZone() { if (timeZoneExplicitlySet) { setTimeZone(getDefaultTimeZone()); timeZoneExplicitlySet = false; } }
Tells if setTimeZone(TimeZone) (or equivalent) was already called on this instance, or it just holds the default value.
Since:2.3.26
/** * Tells if {@link #setTimeZone(TimeZone)} (or equivalent) was already called on this instance, or it just holds the * default value. * * @since 2.3.26 */
public boolean isTimeZoneExplicitlySet() { return timeZoneExplicitlySet; } static TimeZone getDefaultTimeZone() { return TimeZone.getDefault(); } @Override public void setTemplateExceptionHandler(TemplateExceptionHandler templateExceptionHandler) { super.setTemplateExceptionHandler(templateExceptionHandler); templateExceptionHandlerExplicitlySet = true; }
Resets the setting to its default, as if it was never set. This means that when you change the incompatibe_improvements setting later, the default will also change as appropriate. Also isTemplateExceptionHandlerExplicitlySet() will return false.
Since:2.3.22
/** * Resets the setting to its default, as if it was never set. This means that when you change the * {@code incompatibe_improvements} setting later, the default will also change as appropriate. Also * {@link #isTemplateExceptionHandlerExplicitlySet()} will return {@code false}. * * @since 2.3.22 */
public void unsetTemplateExceptionHandler() { if (templateExceptionHandlerExplicitlySet) { setTemplateExceptionHandler(getDefaultTemplateExceptionHandler()); templateExceptionHandlerExplicitlySet = false; } }
Tells if setTemplateExceptionHandler(TemplateExceptionHandler) (or equivalent) was already called on this instance.
Since:2.3.22
/** * Tells if {@link #setTemplateExceptionHandler(TemplateExceptionHandler)} (or equivalent) was already called on * this instance. * * @since 2.3.22 */
public boolean isTemplateExceptionHandlerExplicitlySet() { return templateExceptionHandlerExplicitlySet; } @Override public void setAttemptExceptionReporter(AttemptExceptionReporter attemptExceptionReporter) { super.setAttemptExceptionReporter(attemptExceptionReporter); attemptExceptionReporterExplicitlySet = true; }
Resets the setting to its default, as if it was never set. This means that when you change the incompatibe_improvements setting later, the default will also change as appropriate. Also isAttemptExceptionReporterExplicitlySet() will return false.
Since:2.3.27
/** * Resets the setting to its default, as if it was never set. This means that when you change the * {@code incompatibe_improvements} setting later, the default will also change as appropriate. Also * {@link #isAttemptExceptionReporterExplicitlySet()} will return {@code false}. * * @since 2.3.27 */
public void unsetAttemptExceptionReporter() { if (attemptExceptionReporterExplicitlySet) { setAttemptExceptionReporter(getDefaultAttemptExceptionReporter()); attemptExceptionReporterExplicitlySet = false; } }
Tells if setAttemptExceptionReporter(AttemptExceptionReporter) (or equivalent) was already called on this instance.
Since:2.3.27
/** * Tells if {@link #setAttemptExceptionReporter(AttemptExceptionReporter)} (or equivalent) was already called on * this instance. * * @since 2.3.27 */
public boolean isAttemptExceptionReporterExplicitlySet() { return attemptExceptionReporterExplicitlySet; }
{@inheritDoc}
Since:2.3.22
/** * {@inheritDoc} * * @since 2.3.22 */
@Override public void setLogTemplateExceptions(boolean value) { super.setLogTemplateExceptions(value); logTemplateExceptionsExplicitlySet = true; }
Resets the setting to its default, as if it was never set. This means that when you change the incompatibe_improvements setting later, the default will also change as appropriate. Also isTemplateExceptionHandlerExplicitlySet() will return false.
Since:2.3.22
/** * Resets the setting to its default, as if it was never set. This means that when you change the * {@code incompatibe_improvements} setting later, the default will also change as appropriate. Also * {@link #isTemplateExceptionHandlerExplicitlySet()} will return {@code false}. * * @since 2.3.22 */
public void unsetLogTemplateExceptions() { if (logTemplateExceptionsExplicitlySet) { setLogTemplateExceptions(getDefaultLogTemplateExceptions()); logTemplateExceptionsExplicitlySet = false; } }
Tells if setLogTemplateExceptions(boolean) (or equivalent) was already called on this instance.
Since:2.3.22
/** * Tells if {@link #setLogTemplateExceptions(boolean)} (or equivalent) was already called on this instance. * * @since 2.3.22 */
public boolean isLogTemplateExceptionsExplicitlySet() { return logTemplateExceptionsExplicitlySet; }
{@inheritDoc}
Since:2.3.27
/** * {@inheritDoc} * * @since 2.3.27 */
@Override public void setWrapUncheckedExceptions(boolean value) { super.setWrapUncheckedExceptions(value); wrapUncheckedExceptionsExplicitlySet = true; }
Since:2.3.27
/** * @since 2.3.27 */
public void unsetWrapUncheckedExceptions() { if (wrapUncheckedExceptionsExplicitlySet) { setWrapUncheckedExceptions(getDefaultWrapUncheckedExceptions()); wrapUncheckedExceptionsExplicitlySet = false; } }
Tells if setWrapUncheckedExceptions (or equivalent) was already called on this instance.
Since:2.3.27
/** * Tells if {@link #setWrapUncheckedExceptions} (or equivalent) was already called on this instance. * * @since 2.3.27 */
public boolean isWrapUncheckedExceptionsExplicitlySet() { return wrapUncheckedExceptionsExplicitlySet; }
The getter pair of setStrictSyntaxMode.
/** * The getter pair of {@link #setStrictSyntaxMode}. */
public boolean getStrictSyntaxMode() { return strictSyntax; }
Use Configuration(Version) instead if possible; see the meaning of the parameter there.

Do NOT ever use getVersion() to set the "incompatible improvements". Always use a fixed value, like VERSION_2_3_28. Otherwise your application can break as you upgrade FreeMarker.

If the default value of a setting depends on the incompatibleImprovements and the value of that setting was never set in this Configuration object through the public API, its value will be set to the default value appropriate for the new incompatibleImprovements. (This adjustment of a setting value doesn't count as setting that setting, so setting incompatibleImprovements for multiple times also works as expected.) Note that if the template_loader have to be changed because of this, the template cache will be emptied.

Throws:
  • IllegalArgumentException – If incompatibleImmprovements refers to a version that wasn't released yet when the currently used FreeMarker version was released, or is less than 2.3.0, or is null.
Since:2.3.20
/** * Use {@link #Configuration(Version)} instead if possible; see the meaning of the parameter there. * * <p>Do NOT ever use {@link #getVersion()} to set the "incompatible improvements". Always use a fixed value, like * {@link #VERSION_2_3_28}. Otherwise your application can break as you upgrade FreeMarker. * * <p>If the default value of a setting depends on the {@code incompatibleImprovements} and the value of that setting * was never set in this {@link Configuration} object through the public API, its value will be set to the default * value appropriate for the new {@code incompatibleImprovements}. (This adjustment of a setting value doesn't * count as setting that setting, so setting {@code incompatibleImprovements} for multiple times also works as * expected.) Note that if the {@code template_loader} have to be changed because of this, the template cache will * be emptied. * * @throws IllegalArgumentException * If {@code incompatibleImmprovements} refers to a version that wasn't released yet when the currently * used FreeMarker version was released, or is less than 2.3.0, or is {@code null}. * * @since 2.3.20 */
public void setIncompatibleImprovements(Version incompatibleImprovements) { _TemplateAPI.checkVersionNotNullAndSupported(incompatibleImprovements); if (!this.incompatibleImprovements.equals(incompatibleImprovements)) { this.incompatibleImprovements = incompatibleImprovements; if (!templateLoaderExplicitlySet) { templateLoaderExplicitlySet = true; unsetTemplateLoader(); } if (!templateLookupStrategyExplicitlySet) { templateLookupStrategyExplicitlySet = true; unsetTemplateLookupStrategy(); } if (!templateNameFormatExplicitlySet) { templateNameFormatExplicitlySet = true; unsetTemplateNameFormat(); } if (!cacheStorageExplicitlySet) { cacheStorageExplicitlySet = true; unsetCacheStorage(); } if (!templateExceptionHandlerExplicitlySet) { templateExceptionHandlerExplicitlySet = true; unsetTemplateExceptionHandler(); } if (!attemptExceptionReporterExplicitlySet) { attemptExceptionReporterExplicitlySet = true; unsetAttemptExceptionReporter(); } if (!logTemplateExceptionsExplicitlySet) { logTemplateExceptionsExplicitlySet = true; unsetLogTemplateExceptions(); } if (!wrapUncheckedExceptionsExplicitlySet) { wrapUncheckedExceptionsExplicitlySet = true; unsetWrapUncheckedExceptions(); } if (!objectWrapperExplicitlySet) { objectWrapperExplicitlySet = true; unsetObjectWrapper(); } recreateTemplateCache(); } }
See Also:
  • setIncompatibleImprovements(Version)
Returns:Never null.
Since:2.3.20
/** * @see #setIncompatibleImprovements(Version) * @return Never {@code null}. * @since 2.3.20 */
public Version getIncompatibleImprovements() { return incompatibleImprovements; }
Deprecated:Use Configuration(Version), or as last chance, setIncompatibleImprovements(Version) instead.
/** * @deprecated Use {@link #Configuration(Version)}, or * as last chance, {@link #setIncompatibleImprovements(Version)} instead. */
@Deprecated public void setIncompatibleEnhancements(String version) { setIncompatibleImprovements(new Version(version)); }
Deprecated:Use getIncompatibleImprovements() instead.
/** * @deprecated Use {@link #getIncompatibleImprovements()} instead. */
@Deprecated public String getIncompatibleEnhancements() { return incompatibleImprovements.toString(); }
Deprecated:Use getIncompatibleImprovements() instead.
/** * @deprecated Use {@link #getIncompatibleImprovements()} instead. */
@Deprecated public int getParsedIncompatibleEnhancements() { return getIncompatibleImprovements().intValue(); }
Sets whether the FTL parser will try to remove superfluous white-space around certain FTL tags.
/** * Sets whether the FTL parser will try to remove * superfluous white-space around certain FTL tags. */
public void setWhitespaceStripping(boolean b) { whitespaceStripping = b; }
Gets whether the FTL parser will try to remove superfluous white-space around certain FTL tags.
See Also:
  • setWhitespaceStripping
/** * Gets whether the FTL parser will try to remove * superfluous white-space around certain FTL tags. * * @see #setWhitespaceStripping */
public boolean getWhitespaceStripping() { return whitespaceStripping; }
Sets when auto-escaping should be enabled depending on the current output format; default is ENABLE_IF_DEFAULT_AUTO_ESCAPING_POLICY. Note that the default output format, UndefinedOutputFormat, is a non-escaping format, so there auto-escaping will be off. Note that the templates can turn auto-escaping on/off locally with directives like <#ftl auto_esc=...>, which will ignore the policy.

About auto-escaping

Auto-escaping has significance when a value is printed with ${...} (or #{...}). If auto-escaping is on, FreeMarker will assume that the value is plain text (as opposed to markup or some kind of rich text), so it will escape it according the current output format (see setOutputFormat(OutputFormat) and TemplateConfiguration.setOutputFormat(OutputFormat)). If auto-escaping is off, FreeMarker will assume that the string value is already in the output format, so it prints it as is to the output.

Further notes on auto-escaping:

  • When printing numbers, dates, and other kind of non-string values with ${...}, they will be first converted to string (according the formatting settings and locale), then they are escaped just like string values.
  • When printing TemplateMarkupOutputModel-s, they aren't escaped again (they are already escaped).
  • Auto-escaping doesn't do anything if the current output format isn't an MarkupOutputFormat. That's the case for the default output format, UndefinedOutputFormat, and also for PlainTextOutputFormat.
  • The output format inside a string literal expression is always PlainTextOutputFormat (regardless of the output format of the containing template), which is a non-escaping format. Thus for example, with <#assign s = "foo${bar}">, bar will always get into s without escaping, but with <#assign s>foo${bar}<#assign> it may will be escaped.

Note that what you set here is just a default, which can be overridden for individual templates via setTemplateConfigurations(TemplateConfigurationFactory). This setting is also overridden by the standard file extensions; see them at setRecognizeStandardFileExtensions(boolean).

Params:
See Also:
Since:2.3.24
/** * Sets when auto-escaping should be enabled depending on the current {@linkplain OutputFormat output format}; * default is {@link #ENABLE_IF_DEFAULT_AUTO_ESCAPING_POLICY}. Note that the default output format, * {@link UndefinedOutputFormat}, is a non-escaping format, so there auto-escaping will be off. * Note that the templates can turn auto-escaping on/off locally with directives like {@code <#ftl auto_esc=...>}, * which will ignore the policy. * * <p><b>About auto-escaping</b></p> * * <p> * Auto-escaping has significance when a value is printed with <code>${...}</code> (or <code>#{...}</code>). If * auto-escaping is on, FreeMarker will assume that the value is plain text (as opposed to markup or some kind of * rich text), so it will escape it according the current output format (see {@link #setOutputFormat(OutputFormat)} * and {@link TemplateConfiguration#setOutputFormat(OutputFormat)}). If auto-escaping is off, FreeMarker will assume * that the string value is already in the output format, so it prints it as is to the output. * * <p>Further notes on auto-escaping: * <ul> * <li>When printing numbers, dates, and other kind of non-string values with <code>${...}</code>, they will be * first converted to string (according the formatting settings and locale), then they are escaped just like * string values. * <li>When printing {@link TemplateMarkupOutputModel}-s, they aren't escaped again (they are already escaped). * <li>Auto-escaping doesn't do anything if the current output format isn't an {@link MarkupOutputFormat}. * That's the case for the default output format, {@link UndefinedOutputFormat}, and also for * {@link PlainTextOutputFormat}. * <li>The output format inside a string literal expression is always {@link PlainTextOutputFormat} * (regardless of the output format of the containing template), which is a non-escaping format. Thus for * example, with <code>&lt;#assign s = "foo${bar}"&gt;</code>, {@code bar} will always get into {@code s} * without escaping, but with <code>&lt;#assign s&gt;foo${bar}&lt;#assign&gt;</code> it may will be escaped. * </ul> * * <p>Note that what you set here is just a default, which can be overridden for individual templates via * {@link #setTemplateConfigurations(TemplateConfigurationFactory)}. This setting is also overridden by the standard file * extensions; see them at {@link #setRecognizeStandardFileExtensions(boolean)}. * * @param autoEscapingPolicy * One of the {@link #ENABLE_IF_DEFAULT_AUTO_ESCAPING_POLICY}, * {@link #ENABLE_IF_SUPPORTED_AUTO_ESCAPING_POLICY}, and {@link #DISABLE_AUTO_ESCAPING_POLICY} constants. * * @see TemplateConfiguration#setAutoEscapingPolicy(int) * @see Configuration#setOutputFormat(OutputFormat) * @see TemplateConfiguration#setOutputFormat(OutputFormat) * * @since 2.3.24 */
public void setAutoEscapingPolicy(int autoEscapingPolicy) { _TemplateAPI.validateAutoEscapingPolicyValue(autoEscapingPolicy); int prevAutoEscaping = getAutoEscapingPolicy(); this.autoEscapingPolicy = autoEscapingPolicy; if (prevAutoEscaping != autoEscapingPolicy) { clearTemplateCache(); } }
Since:2.3.24
/** * Getter pair of {@link #setAutoEscapingPolicy(int)} * * @since 2.3.24 */
public int getAutoEscapingPolicy() { return autoEscapingPolicy; }
Sets the default output format. Usually, you should leave this on its default, which is UndefinedOutputFormat.INSTANCE, and then use standard file extensions like "ftlh" (for HTML) or "ftlx" (for XML) and ensure that setRecognizeStandardFileExtensions(boolean) is true (see more there). Where you can't use the standard extensions, templates still can be associated to output formats with patterns matching their name (their path) using setTemplateConfigurations(TemplateConfigurationFactory). But if all templates will have the same output format, you may use setOutputFormat(OutputFormat) after all, to a value like HTMLOutputFormat.INSTANCE, XMLOutputFormat.INSTANCE, etc. Also note that templates can specify their own output format like <#ftl output_format="HTML">, which overrides any configuration settings.

The output format is mostly important because of auto-escaping (see setAutoEscapingPolicy(int)), but maybe also used by the embedding application to set the HTTP response MIME type, etc.

See Also:
Since:2.3.24
/** * Sets the default output format. Usually, you should leave this on its default, which is * {@link UndefinedOutputFormat#INSTANCE}, and then use standard file extensions like "ftlh" (for HTML) or "ftlx" * (for XML) and ensure that {@link #setRecognizeStandardFileExtensions(boolean)} is {@code true} (see more there). * Where you can't use the standard extensions, templates still can be associated to output formats with * patterns matching their name (their path) using {@link #setTemplateConfigurations(TemplateConfigurationFactory)}. * But if all templates will have the same output format, you may use {@link #setOutputFormat(OutputFormat)} after * all, to a value like {@link HTMLOutputFormat#INSTANCE}, {@link XMLOutputFormat#INSTANCE}, etc. Also note * that templates can specify their own output format like {@code * <#ftl output_format="HTML">}, which overrides any configuration settings. * * <p> * The output format is mostly important because of auto-escaping (see {@link #setAutoEscapingPolicy(int)}), but * maybe also used by the embedding application to set the HTTP response MIME type, etc. * * @see #setRegisteredCustomOutputFormats(Collection) * @see #setTemplateConfigurations(TemplateConfigurationFactory) * @see #setRecognizeStandardFileExtensions(boolean) * @see #setAutoEscapingPolicy(int) * * @since 2.3.24 */
public void setOutputFormat(OutputFormat outputFormat) { if (outputFormat == null) { throw new NullArgumentException( "outputFormat", "You may meant: " + UndefinedOutputFormat.class.getSimpleName() + ".INSTANCE"); } OutputFormat prevOutputFormat = getOutputFormat(); this.outputFormat = outputFormat; outputFormatExplicitlySet = true; if (prevOutputFormat != outputFormat) { clearTemplateCache(); } }
Since:2.3.24
/** * Getter pair of {@link #setOutputFormat(OutputFormat)} * * @since 2.3.24 */
public OutputFormat getOutputFormat() { return outputFormat; }
Tells if setOutputFormat(OutputFormat) (or equivalent) was already called on this instance.
Since:2.3.24
/** * Tells if {@link #setOutputFormat(OutputFormat)} (or equivalent) was already called on this instance. * * @since 2.3.24 */
public boolean isOutputFormatExplicitlySet() { return outputFormatExplicitlySet; }
Resets the setting to its default, as if it was never set. This means that when you change the incompatibe_improvements setting later, the default will also change as appropriate. Also isOutputFormatExplicitlySet() will return false.
Since:2.3.24
/** * Resets the setting to its default, as if it was never set. This means that when you change the * {@code incompatibe_improvements} setting later, the default will also change as appropriate. Also * {@link #isOutputFormatExplicitlySet()} will return {@code false}. * * @since 2.3.24 */
public void unsetOutputFormat() { outputFormat = UndefinedOutputFormat.INSTANCE; outputFormatExplicitlySet = false; }
Returns the output format for a name.
Params:
  • name – Either the name of the output format as it was registered with setRegisteredCustomOutputFormats(Collection<? extends OutputFormat>), or a combined output format name. A combined output format is created ad-hoc from the registered formats. For example, if you need RTF embedded into HTML, the name will be HTML{RTF}, where "HTML" and "RTF" refer to the existing formats. This logic can be used recursively, so for example XML{HTML{RTF}} is also valid.
Throws:
Returns:Not null.
Since:2.3.24
/** * Returns the output format for a name. * * @param name * Either the name of the output format as it was registered with * {@link Configuration#setRegisteredCustomOutputFormats(Collection)}, or a combined output format name. * A combined output format is created ad-hoc from the registered formats. For example, if you need RTF * embedded into HTML, the name will be <code>HTML{RTF}</code>, where "HTML" and "RTF" refer to the * existing formats. This logic can be used recursively, so for example <code>XML{HTML{RTF}}</code> is * also valid. * * @return Not {@code null}. * * @throws UnregisteredOutputFormatException * If there's no output format registered with the given name. * @throws IllegalArgumentException * If the usage of <code>{</code> and <code>}</code> in the name is syntactically wrong, or if not all * {@link OutputFormat}-s are {@link MarkupOutputFormat}-s in the <code>...{...}</code> expression. * * @since 2.3.24 */
public OutputFormat getOutputFormat(String name) throws UnregisteredOutputFormatException { if (name.length() == 0) { throw new IllegalArgumentException("0-length format name"); } if (name.charAt(name.length() - 1) == '}') { // Combined markup int openBrcIdx = name.indexOf('{'); if (openBrcIdx == -1) { throw new IllegalArgumentException("Missing opening '{' in: " + name); } MarkupOutputFormat outerOF = getMarkupOutputFormatForCombined(name.substring(0, openBrcIdx)); MarkupOutputFormat innerOF = getMarkupOutputFormatForCombined( name.substring(openBrcIdx + 1, name.length() - 1)); return new CombinedMarkupOutputFormat(name, outerOF, innerOF); } else { OutputFormat custOF = registeredCustomOutputFormats.get(name); if (custOF != null) { return custOF; } OutputFormat stdOF = STANDARD_OUTPUT_FORMATS.get(name); if (stdOF == null) { StringBuilder sb = new StringBuilder(); sb.append("Unregistered output format name, "); sb.append(StringUtil.jQuote(name)); sb.append(". The output formats registered in the Configuration are: "); Set<String> registeredNames = new TreeSet<String>(); registeredNames.addAll(STANDARD_OUTPUT_FORMATS.keySet()); registeredNames.addAll(registeredCustomOutputFormats.keySet()); boolean first = true; for (String registeredName : registeredNames) { if (first) { first = false; } else { sb.append(", "); } sb.append(StringUtil.jQuote(registeredName)); } throw new UnregisteredOutputFormatException(sb.toString()); } return stdOF; } } private MarkupOutputFormat getMarkupOutputFormatForCombined(String outerName) throws UnregisteredOutputFormatException { OutputFormat of = getOutputFormat(outerName); if (!(of instanceof MarkupOutputFormat)) { throw new IllegalArgumentException("The \"" + outerName + "\" output format can't be used in " + "...{...} expression, because it's not a markup format."); } MarkupOutputFormat outerOF = (MarkupOutputFormat) of; return outerOF; }
Sets the custom output formats that can be referred by their unique name (OutputFormat.getName()) from templates. Names are also used to look up the OutputFormat for standard file extensions; see them at setRecognizeStandardFileExtensions(boolean).

When there's a clash between a custom output format name and a standard output format name, the custom format will win, thus you can override the meaning of standard output format names. Except, it's not allowed to override UndefinedOutputFormat and PlainTextOutputFormat.

The default value is an empty collection.

Params:
Throws:
Since:2.3.24
/** * Sets the custom output formats that can be referred by their unique name ({@link OutputFormat#getName()}) from * templates. Names are also used to look up the {@link OutputFormat} for standard file extensions; see them at * {@link #setRecognizeStandardFileExtensions(boolean)}. * * <p> * When there's a clash between a custom output format name and a standard output format name, the custom format * will win, thus you can override the meaning of standard output format names. Except, it's not allowed to override * {@link UndefinedOutputFormat} and {@link PlainTextOutputFormat}. * * <p> * The default value is an empty collection. * * @param registeredCustomOutputFormats * The collection of the {@link OutputFormat}-s, each must be different and has a unique name ( * {@link OutputFormat#getName()}) within this collection. * * @throws IllegalArgumentException * When multiple different {@link OutputFormat}-s have the same name in the parameter collection. When * the same {@link OutputFormat} object occurs for multiple times in the collection. If an * {@link OutputFormat} name is 0 long. If an {@link OutputFormat} name doesn't start with letter or * digit. If an {@link OutputFormat} name contains {@code '+'} or <code>'{'</code> or <code>'}'</code>. * If an {@link OutputFormat} name equals to {@link UndefinedOutputFormat#getName()} or * {@link PlainTextOutputFormat#getName()}. * * @since 2.3.24 */
public void setRegisteredCustomOutputFormats(Collection<? extends OutputFormat> registeredCustomOutputFormats) { NullArgumentException.check(registeredCustomOutputFormats); Map<String, OutputFormat> m = new LinkedHashMap<String, OutputFormat>( registeredCustomOutputFormats.size() * 4 / 3, 1f); for (OutputFormat outputFormat : registeredCustomOutputFormats) { String name = outputFormat.getName(); if (name.equals(UndefinedOutputFormat.INSTANCE.getName())) { throw new IllegalArgumentException( "The \"" + name + "\" output format can't be redefined"); } if (name.equals(PlainTextOutputFormat.INSTANCE.getName())) { throw new IllegalArgumentException( "The \"" + name + "\" output format can't be redefined"); } if (name.length() == 0) { throw new IllegalArgumentException("The output format name can't be 0 long"); } if (!Character.isLetterOrDigit(name.charAt(0))) { throw new IllegalArgumentException("The output format name must start with letter or digit: " + name); } if (name.indexOf('+') != -1) { throw new IllegalArgumentException("The output format name can't contain \"+\" character: " + name); } if (name.indexOf('{') != -1) { throw new IllegalArgumentException("The output format name can't contain \"{\" character: " + name); } if (name.indexOf('}') != -1) { throw new IllegalArgumentException("The output format name can't contain \"}\" character: " + name); } OutputFormat replaced = m.put(outputFormat.getName(), outputFormat); if (replaced != null) { if (replaced == outputFormat) { throw new IllegalArgumentException( "Duplicate output format in the collection: " + outputFormat); } throw new IllegalArgumentException( "Clashing output format names between " + replaced + " and " + outputFormat + "."); } } this.registeredCustomOutputFormats = Collections.unmodifiableMap(m); clearTemplateCache(); }
Since:2.3.24
/** * Getter pair of {@link #setRegisteredCustomOutputFormats(Collection)}. * * @since 2.3.24 */
public Collection<? extends OutputFormat> getRegisteredCustomOutputFormats() { return registeredCustomOutputFormats.values(); }
Sets if the "file" extension part of the source name (Template.getSourceName()) will influence certain parsing settings. For backward compatibility, it defaults to false if getIncompatibleImprovements() is less than 2.3.24. Starting from incompatibleImprovements 2.3.24, it defaults to true, so the following standard file extensions take their effect:

These file extensions are not case sensitive. The file extension is the part after the last dot in the source name. If the source name contains no dot, then it has no file extension.

The settings activated by these file extensions override the setting values dictated by setTemplateConfigurations(TemplateConfigurationFactory).

/** * Sets if the "file" extension part of the source name ({@link Template#getSourceName()}) will influence certain * parsing settings. For backward compatibility, it defaults to {@code false} if * {@link #getIncompatibleImprovements()} is less than 2.3.24. Starting from {@code incompatibleImprovements} * 2.3.24, it defaults to {@code true}, so the following standard file extensions take their effect: * * <ul> * <li>{@code ftlh}: Sets {@link TemplateConfiguration#setOutputFormat(OutputFormat) outputFormat} to * {@code "HTML"} (i.e., {@link HTMLOutputFormat#INSTANCE}, unless the {@code "HTML"} name is overridden by * {@link #setRegisteredCustomOutputFormats(Collection)}) and * {@link TemplateConfiguration#setAutoEscapingPolicy(int) autoEscapingPolicy} to * {@link #ENABLE_IF_DEFAULT_AUTO_ESCAPING_POLICY}. * <li>{@code ftlx}: Sets {@link TemplateConfiguration#setOutputFormat(OutputFormat) outputFormat} to * {@code "XML"} (i.e., {@link XMLOutputFormat#INSTANCE}, unless the {@code "XML"} name is overridden by * {@link #setRegisteredCustomOutputFormats(Collection)}) and * {@link TemplateConfiguration#setAutoEscapingPolicy(int) autoEscapingPolicy} to * {@link #ENABLE_IF_DEFAULT_AUTO_ESCAPING_POLICY}. * </ul> * * <p>These file extensions are not case sensitive. The file extension is the part after the last dot in the source * name. If the source name contains no dot, then it has no file extension. * * <p>The settings activated by these file extensions override the setting values dictated by * {@link #setTemplateConfigurations(TemplateConfigurationFactory)}. */
public void setRecognizeStandardFileExtensions(boolean recognizeStandardFileExtensions) { boolean prevEffectiveValue = getRecognizeStandardFileExtensions(); this.recognizeStandardFileExtensions = Boolean.valueOf(recognizeStandardFileExtensions); if (prevEffectiveValue != recognizeStandardFileExtensions) { clearTemplateCache(); } }
Resets the setting to its default, as if it was never set. This means that when you change the incompatibe_improvements setting later, the default will also change as appropriate. Also isRecognizeStandardFileExtensionsExplicitlySet() will return false.
Since:2.3.24
/** * Resets the setting to its default, as if it was never set. This means that when you change the * {@code incompatibe_improvements} setting later, the default will also change as appropriate. Also * {@link #isRecognizeStandardFileExtensionsExplicitlySet()} will return {@code false}. * * @since 2.3.24 */
public void unsetRecognizeStandardFileExtensions() { if (recognizeStandardFileExtensions != null) { recognizeStandardFileExtensions = null; } }
Tells if setRecognizeStandardFileExtensions(boolean) (or equivalent) was already called on this instance.
Since:2.3.24
/** * Tells if {@link #setRecognizeStandardFileExtensions(boolean)} (or equivalent) was already called on this * instance. * * @since 2.3.24 */
public boolean isRecognizeStandardFileExtensionsExplicitlySet() { return recognizeStandardFileExtensions != null; }
Since:2.3.24
/** * Getter pair of {@link #setRecognizeStandardFileExtensions(boolean)}. * * @since 2.3.24 */
public boolean getRecognizeStandardFileExtensions() { return recognizeStandardFileExtensions == null ? incompatibleImprovements.intValue() >= _TemplateAPI.VERSION_INT_2_3_24 : recognizeStandardFileExtensions.booleanValue(); }
Determines the tag syntax (like <#if x> VS [#if x]) of the template files that has no #ftl header to decide that. Don't confuse this with the interpolation syntax (setInterpolationSyntax(int)); they are independent.

The tagSyntax parameter must be one of:

In FreeMarker 2.3.x ANGLE_BRACKET_TAG_SYNTAX is the default for better backward compatibility. Starting from 2.4.x AUTO_DETECT_TAG_SYNTAX is the default, so it's recommended to use that even for 2.3.x.

This setting is ignored for the templates that have ftl directive in it. For those templates the syntax used for the ftl directive determines the syntax.

See Also:
/** * Determines the tag syntax (like {@code <#if x>} VS {@code [#if x]}) of the template files * that has no {@code #ftl} header to decide that. Don't confuse this with the interpolation syntax * ({@link #setInterpolationSyntax(int)}); they are independent. * * <p>The {@code tagSyntax} parameter must be one of: * <ul> * <li>{@link Configuration#AUTO_DETECT_TAG_SYNTAX}: * Use the syntax of the first FreeMarker tag (can be anything, like <tt>#list</tt>, * <tt>#include</tt>, user defined, etc.) * <li>{@link Configuration#ANGLE_BRACKET_TAG_SYNTAX}: * Use the angle bracket tag syntax (the normal syntax), like {@code <#include ...>} * <li>{@link Configuration#SQUARE_BRACKET_TAG_SYNTAX}: * Use the square bracket tag syntax, like {@code [#include ...]}. Note that this does <em>not</em> change * <code>${x}</code> to {@code [=...]}; that's <em>interpolation</em> syntax, so use * {@link #setInterpolationSyntax(int)} for that. * </ul> * * <p>In FreeMarker 2.3.x {@link Configuration#ANGLE_BRACKET_TAG_SYNTAX} is the * default for better backward compatibility. Starting from 2.4.x {@link * Configuration#AUTO_DETECT_TAG_SYNTAX} is the default, so it's recommended to use * that even for 2.3.x. * * <p>This setting is ignored for the templates that have {@code ftl} directive in * it. For those templates the syntax used for the {@code ftl} directive determines * the syntax. * * @see #setInterpolationSyntax(int) */
public void setTagSyntax(int tagSyntax) { _TemplateAPI.valideTagSyntaxValue(tagSyntax); this.tagSyntax = tagSyntax; }
The getter pair of setTagSyntax(int).
/** * The getter pair of {@link #setTagSyntax(int)}. */
public int getTagSyntax() { return tagSyntax; }
Determines the interpolation syntax (like ${x} VS [=x]) of the template files. Don't confuse this with the tag syntax (setTagSyntax(int)); they are independent.

The interpolationSyntax parameter must be one of LEGACY_INTERPOLATION_SYNTAX, DOLLAR_INTERPOLATION_SYNTAX, and SQUARE_BRACKET_INTERPOLATION_SYNTAX. Note that SQUARE_BRACKET_INTERPOLATION_SYNTAX does not change <#if x> to [#if x]; that's tag syntax, so use setTagSyntax(int) for that.

See Also:
Since:2.3.28
/** * Determines the interpolation syntax (like <code>${x}</code> VS <code>[=x]</code>) of the template files. Don't * confuse this with the tag syntax ({@link #setTagSyntax(int)}); they are independent. * * <p> * The {@code interpolationSyntax} parameter must be one of {@link Configuration#LEGACY_INTERPOLATION_SYNTAX}, * {@link Configuration#DOLLAR_INTERPOLATION_SYNTAX}, and {@link Configuration#SQUARE_BRACKET_INTERPOLATION_SYNTAX}. * Note that {@link Configuration#SQUARE_BRACKET_INTERPOLATION_SYNTAX} does <em>not</em> change {@code <#if x>} to * {@code [#if x]}; that's <em>tag</em> syntax, so use {@link #setTagSyntax(int)} for that. * * @see #setTagSyntax(int) * * @since 2.3.28 */
public void setInterpolationSyntax(int interpolationSyntax) { _TemplateAPI.valideInterpolationSyntaxValue(interpolationSyntax); this.interpolationSyntax = interpolationSyntax; }
The getter pair of setInterpolationSyntax(int).
Since:2.3.28
/** * The getter pair of {@link #setInterpolationSyntax(int)}. * * @since 2.3.28 */
public int getInterpolationSyntax() { return interpolationSyntax; }
Sets the naming convention used for the identifiers that are part of the template language. The available naming conventions are legacy (directive (tag) names are all-lower-case likethis, others are snake case like_this), and camel case (likeThis). The default is auto-detect, which detects the naming convention used and enforces that same naming convention for the whole template.

This setting doesn't influence what naming convention is used for the setting names outside templates. Also, it won't ever convert the names of user-defined things, like of data-model members, or the names of user defined macros/functions. It only influences the names of the built-in directives (#elseIf VS elseif), built-ins (?upper_case VS ?upperCase ), special variables (.data_model VS .dataModel).

Which convention to use: FreeMarker prior to 2.3.23 has only supported LEGACY_NAMING_CONVENTION, so that's how most templates and examples out there are written as of 2015. But as templates today are mostly written by programmers and often access Java API-s which already use camel case, CAMEL_CASE_NAMING_CONVENTION is the recommended option for most projects. However, it's no necessary to make a application-wide decision; see auto-detection below.

FreeMarker will decide the naming convention automatically for each template individually when this setting is set to AUTO_DETECT_NAMING_CONVENTION (which is the default). The naming convention of a template is decided when the first core (non-user-defined) identifier is met during parsing (not during processing) where the naming convention is relevant (like for s?upperCase or s?upper_case it's relevant, but for s?length it isn't). At that point, the naming convention of the template is decided, and any later core identifier that uses a different convention will be a parsing error. As the naming convention is decided per template, it's not a problem if a template and the other template it #include-s/#import uses a different convention.

FreeMarker always enforces the same naming convention to be used consistently within the same template "file". Additionally, when this setting is set to non-AUTO_DETECT_NAMING_CONVENTION, the selected naming convention is enforced on all templates. Thus such a setup can be used to enforce an application-wide naming convention.

Non-strict tags (a long deprecated syntax from FreeMarker 1, activated via setStrictSyntaxMode(boolean)) are only recognized as FTL tags when they are using the LEGACY_NAMING_CONVENTION syntax, regardless of this setting. As they aren't exempt from the naming convention consistency enforcement, generally, you can't use strict CAMEL_CASE_NAMING_CONVENTION tags mixed with non-strict tags.

Params:
Throws:
Since:2.3.23
/** * Sets the naming convention used for the identifiers that are part of the template language. The available naming * conventions are legacy (directive (tag) names are all-lower-case {@code likethis}, others are snake case * {@code like_this}), and camel case ({@code likeThis}). The default is auto-detect, which detects the naming * convention used and enforces that same naming convention for the whole template. * * <p> * This setting doesn't influence what naming convention is used for the setting names outside templates. Also, it * won't ever convert the names of user-defined things, like of data-model members, or the names of user defined * macros/functions. It only influences the names of the built-in directives ({@code #elseIf} VS {@code elseif}), * built-ins ({@code ?upper_case} VS {@code ?upperCase} ), special variables ({@code .data_model} VS * {@code .dataModel}). * * <p> * Which convention to use: FreeMarker prior to 2.3.23 has only supported * {@link Configuration#LEGACY_NAMING_CONVENTION}, so that's how most templates and examples out there are written * as of 2015. But as templates today are mostly written by programmers and often access Java API-s which already * use camel case, {@link Configuration#CAMEL_CASE_NAMING_CONVENTION} is the recommended option for most projects. * However, it's no necessary to make a application-wide decision; see auto-detection below. * * <p> * FreeMarker will decide the naming convention automatically for each template individually when this setting is * set to {@link #AUTO_DETECT_NAMING_CONVENTION} (which is the default). The naming convention of a template is * decided when the first core (non-user-defined) identifier is met during parsing (not during processing) where the * naming convention is relevant (like for {@code s?upperCase} or {@code s?upper_case} it's relevant, but for * {@code s?length} it isn't). At that point, the naming convention of the template is decided, and any later core * identifier that uses a different convention will be a parsing error. As the naming convention is decided per * template, it's not a problem if a template and the other template it {@code #include}-s/{@code #import} uses a * different convention. * * <p> * FreeMarker always enforces the same naming convention to be used consistently within the same template "file". * Additionally, when this setting is set to non-{@link #AUTO_DETECT_NAMING_CONVENTION}, the selected naming * convention is enforced on all templates. Thus such a setup can be used to enforce an application-wide naming * convention. * * <p> * Non-strict tags (a long deprecated syntax from FreeMarker 1, activated via {@link #setStrictSyntaxMode(boolean)}) * are only recognized as FTL tags when they are using the {@link Configuration#LEGACY_NAMING_CONVENTION} syntax, * regardless of this setting. As they aren't exempt from the naming convention consistency enforcement, generally, * you can't use strict {@link Configuration#CAMEL_CASE_NAMING_CONVENTION} tags mixed with non-strict tags. * * @param namingConvention * One of the {@link #AUTO_DETECT_NAMING_CONVENTION} or {@link #LEGACY_NAMING_CONVENTION} * {@link #CAMEL_CASE_NAMING_CONVENTION}. * * @throws IllegalArgumentException * If the parameter isn't one of the valid constants. * * @since 2.3.23 */
public void setNamingConvention(int namingConvention) { _TemplateAPI.validateNamingConventionValue(namingConvention); this.namingConvention = namingConvention; }
The getter pair of setNamingConvention(int).
Since:2.3.23
/** * The getter pair of {@link #setNamingConvention(int)}. * * @since 2.3.23 */
public int getNamingConvention() { return namingConvention; }
Sets the assumed display width of the tab character (ASCII 9), which influences the column number shown in error messages (or the column number you get through other API-s). So for example if the users edit templates in an editor where the tab width is set to 4, you should set this to 4 so that the column numbers printed by FreeMarker will match the column number shown in the editor. This setting doesn't affect the output of templates, as a tab in the template will remain a tab in the output too. If you set this setting to 1, then tab characters will be kept in the return value of Template.getSource(int, int, int, int), otherwise they will be replaced with the appropriate number of spaces.
Params:
  • tabSize – At least 1, at most 256.
Since:2.3.25
/** * Sets the assumed display width of the tab character (ASCII 9), which influences the column number shown in error * messages (or the column number you get through other API-s). So for example if the users edit templates in an * editor where the tab width is set to 4, you should set this to 4 so that the column numbers printed by FreeMarker * will match the column number shown in the editor. This setting doesn't affect the output of templates, as a tab * in the template will remain a tab in the output too. If you set this setting to 1, then tab characters will be * kept in the return value of {@link Template#getSource(int, int, int, int)}, otherwise they will be replaced with * the appropriate number of spaces. * * @param tabSize * At least 1, at most 256. * * @since 2.3.25 */
public void setTabSize(int tabSize) { if (tabSize < 1) { throw new IllegalArgumentException("\"tabSize\" must be at least 1, but was " + tabSize); } // To avoid integer overflows: if (tabSize > 256) { throw new IllegalArgumentException("\"tabSize\" can't be more than 256, but was " + tabSize); } this.tabSize = tabSize; }
The getter pair of setTabSize(int).
Since:2.3.25
/** * The getter pair of {@link #setTabSize(int)}. * * @since 2.3.25 */
public int getTabSize() { return tabSize; }
Since:2.3.27
/** * Getter pair of {@link #setPreventStrippings(boolean)}. * * @since 2.3.27 */
boolean getPreventStrippings() { return preventStrippings; }
Used internally; added for the FreeMarker 2 to FreeMarker 3 converter, prevents the stripping/removal of AST nodes so that the source code can be fully reproduced from the AST.
Since:2.3.27
/** * Used internally; added for the FreeMarker 2 to FreeMarker 3 converter, prevents the stripping/removal of AST * nodes so that the source code can be fully reproduced from the AST. * * @since 2.3.27 */
void setPreventStrippings(boolean preventStrippings) { this.preventStrippings = preventStrippings; }
Retrieves the template with the given name from the template cache, loading it into the cache first if it's missing/staled.

This is a shorthand for getTemplate(name, null, null, null, true, false); see more details there.

See Configuration for an example of basic usage.

/** * Retrieves the template with the given name from the template cache, loading it into the cache first if it's * missing/staled. * * <p> * This is a shorthand for {@link #getTemplate(String, Locale, Object, String, boolean, boolean) * getTemplate(name, null, null, null, true, false)}; see more details there. * * <p> * See {@link Configuration} for an example of basic usage. */
public Template getTemplate(String name) throws TemplateNotFoundException, MalformedTemplateNameException, ParseException, IOException { return getTemplate(name, null, null, null, true, false); } /** * Shorthand for {@link #getTemplate(String, Locale, Object, String, boolean, boolean) * getTemplate(name, locale, null, null, true, false)}. */ public Template getTemplate(String name, Locale locale) throws TemplateNotFoundException, MalformedTemplateNameException, ParseException, IOException { return getTemplate(name, locale, null, null, true, false); } /** * Shorthand for {@link #getTemplate(String, Locale, Object, String, boolean, boolean) * getTemplate(name, null, null, encoding, true, false)}. */ public Template getTemplate(String name, String encoding) throws TemplateNotFoundException, MalformedTemplateNameException, ParseException, IOException { return getTemplate(name, null, null, encoding, true, false); } /** * Shorthand for {@link #getTemplate(String, Locale, Object, String, boolean, boolean) * getTemplate(name, locale, null, encoding, true, false)}. */ public Template getTemplate(String name, Locale locale, String encoding) throws TemplateNotFoundException, MalformedTemplateNameException, ParseException, IOException { return getTemplate(name, locale, null, encoding, true, false); } /** * Shorthand for {@link #getTemplate(String, Locale, Object, String, boolean, boolean) * getTemplate(name, locale, null, encoding, parseAsFTL, false)}. */ public Template getTemplate(String name, Locale locale, String encoding, boolean parseAsFTL) throws TemplateNotFoundException, MalformedTemplateNameException, ParseException, IOException { return getTemplate(name, locale, null, encoding, parseAsFTL, false); }
Since:2.3.21
/** * Shorthand for {@link #getTemplate(String, Locale, Object, String, boolean, boolean) * getTemplate(name, locale, null, encoding, parseAsFTL, ignoreMissing)}. * * @since 2.3.21 */
public Template getTemplate(String name, Locale locale, String encoding, boolean parseAsFTL, boolean ignoreMissing) throws TemplateNotFoundException, MalformedTemplateNameException, ParseException, IOException { return getTemplate(name, locale, null, encoding, parseAsFTL, ignoreMissing); }
Retrieves the template with the given name (and according the specified further parameters) from the template cache, loading it into the cache first if it's missing/staled.

This method is thread-safe.

See Configuration for an example of basic usage.

Params:
  • name – The name or path of the template, which is not a real path, but interpreted inside the current TemplateLoader. Can't be null. The exact syntax of the name depends on the underlying TemplateLoader, but the cache makes some assumptions. First, the name is expected to be a hierarchical path, with path components separated by a slash character (not with backslash!). The path (the name) given here must not begin with slash; it's always interpreted relative to the "template root directory". Then, the .. and . path meta-elements will be resolved. For example, if the name is a/../b/./c.ftl, then it will be simplified to b/c.ftl. The rules regarding this are the same as with conventional UN*X paths. The path must not reach outside the template root directory, that is, it can't be something like "../templates/my.ftl" (not even if this path happens to be equivalent with "/my.ftl"). Furthermore, the path is allowed to contain at most one path element whose name is * (asterisk). This path meta-element triggers the acquisition mechanism. If the template is not found in the location described by the concatenation of the path left to the asterisk (called base path) and the part to the right of the asterisk (called resource path), the cache will attempt to remove the rightmost path component from the base path ("go up one directory") and concatenate that with the resource path. The process is repeated until either a template is found, or the base path is completely exhausted.
  • locale – The requested locale of the template. This is what Configurable.getLocale() on the resulting Template will return (unless it's overridden via getTemplateConfigurations()). This parameter can be null since 2.3.22, in which case it defaults to Configurable.getLocale() (note that Configurable.getLocale() will give the default value, not null). This parameter also drives localized template lookup. Assuming that you have specified en_US as the locale and myTemplate.ftl as the name of the template, and the default TemplateLookupStrategy is used and #setLocalizedLookup(boolean) localized_lookup is true, FreeMarker will first try to retrieve myTemplate_en_US.html, then myTemplate.en.ftl, and finally myTemplate.ftl. Note that that the template's locale will be en_US even if it only finds myTemplate.ftl. Note that when the locale setting is overridden with a TemplateConfiguration provided by getTemplateConfigurations(), that overrides the value specified here, but only after the localized lookup, that is, it modifies the template found by the localized lookup.
  • customLookupCondition – This value can be used by a custom TemplateLookupStrategy; has no effect with the default one. Can be null (though it's up to the custom TemplateLookupStrategy if it allows that). This object will be used as part of the cache key, so it must to have a proper Object.equals(Object) and Object.hashCode() method. It also should have reasonable Object.toString(), as it's possibly quoted in error messages. The expected type is up to the custom TemplateLookupStrategy. See also: TemplateLookupContext.getCustomLookupCondition().
  • encoding – Deprecated mechanism, null is the recommended; the charset used to interpret the template source code bytes (if it's read from a binary source). Can be null since 2.3.22, in which case it will default to getEncoding(Locale) where Locale is the locale parameter (when locale was null too, the its default value is used instead). Why is this deprecated: It doesn't make sense to get the same template with different encodings, hence, it's error prone to specify the encoding where you get the template. Instead, if you have template "files" with different charsets, you should use setTemplateConfigurations(TemplateConfigurationFactory), where you can associate encodings to individual templates based on their names (like which "directory" are they in, what's their file extension, etc.). The encoding associated with the templates that way overrides the encoding that you specify here.
  • parseAsFTL – If true, the loaded template is parsed and interpreted normally, as a regular FreeMarker template. If false, the loaded template is treated as a static text, so ${...}, <#...> etc. will not have special meaning in it.
  • ignoreMissing – If true, the method won't throw TemplateNotFoundException if the template doesn't exist, instead it returns null. Other kind of exceptions won't be suppressed.
Throws:
Returns:the requested template; maybe null when the ignoreMissing parameter is true.
Since:2.3.22
/** * Retrieves the template with the given name (and according the specified further parameters) from the template * cache, loading it into the cache first if it's missing/staled. * * <p> * This method is thread-safe. * * <p> * See {@link Configuration} for an example of basic usage. * * @param name * The name or path of the template, which is not a real path, but interpreted inside the current * {@link TemplateLoader}. Can't be {@code null}. The exact syntax of the name depends on the underlying * {@link TemplateLoader}, but the cache makes some assumptions. First, the name is expected to be a * hierarchical path, with path components separated by a slash character (not with backslash!). The path * (the name) given here must <em>not</em> begin with slash; it's always interpreted relative to the * "template root directory". Then, the {@code ..} and {@code .} path meta-elements will be resolved. For * example, if the name is {@code a/../b/./c.ftl}, then it will be simplified to {@code b/c.ftl}. The * rules regarding this are the same as with conventional UN*X paths. The path must not reach outside the * template root directory, that is, it can't be something like {@code "../templates/my.ftl"} (not even * if this path happens to be equivalent with {@code "/my.ftl"}). Furthermore, the path is allowed to * contain at most one path element whose name is {@code *} (asterisk). This path meta-element triggers * the <i>acquisition mechanism</i>. If the template is not found in the location described by the * concatenation of the path left to the asterisk (called base path) and the part to the right of the * asterisk (called resource path), the cache will attempt to remove the rightmost path component from * the base path ("go up one directory") and concatenate that with the resource path. The process is * repeated until either a template is found, or the base path is completely exhausted. * * @param locale * The requested locale of the template. This is what {@link Template#getLocale()} on the resulting * {@link Template} will return (unless it's overridden via {@link #getTemplateConfigurations()}). This * parameter can be {@code null} since 2.3.22, in which case it defaults to * {@link Configuration#getLocale()} (note that {@link Template#getLocale()} will give the default value, * not {@code null}). This parameter also drives localized template lookup. Assuming that you have * specified {@code en_US} as the locale and {@code myTemplate.ftl} as the name of the template, and the * default {@link TemplateLookupStrategy} is used and * {@code #setLocalizedLookup(boolean) localized_lookup} is {@code true}, FreeMarker will first try to * retrieve {@code myTemplate_en_US.html}, then {@code myTemplate.en.ftl}, and finally * {@code myTemplate.ftl}. Note that that the template's locale will be {@code en_US} even if it only * finds {@code myTemplate.ftl}. Note that when the {@code locale} setting is overridden with a * {@link TemplateConfiguration} provided by {@link #getTemplateConfigurations()}, that overrides the * value specified here, but only after the localized lookup, that is, it modifies the template * found by the localized lookup. * * @param customLookupCondition * This value can be used by a custom {@link TemplateLookupStrategy}; has no effect with the default one. * Can be {@code null} (though it's up to the custom {@link TemplateLookupStrategy} if it allows that). * This object will be used as part of the cache key, so it must to have a proper * {@link Object#equals(Object)} and {@link Object#hashCode()} method. It also should have reasonable * {@link Object#toString()}, as it's possibly quoted in error messages. The expected type is up to the * custom {@link TemplateLookupStrategy}. See also: * {@link TemplateLookupContext#getCustomLookupCondition()}. * * @param encoding * Deprecated mechanism, {@code null} is the recommended; the charset used to interpret the template * source code bytes (if it's read from a binary source). Can be {@code null} since 2.3.22, in which case * it will default to {@link Configuration#getEncoding(Locale)} where {@code Locale} is the * {@code locale} parameter (when {@code locale} was {@code null} too, the its default value is used * instead). Why is this deprecated: It doesn't make sense to get the <em>same</em> template with * different encodings, hence, it's error prone to specify the encoding where you get the template. * Instead, if you have template "files" with different charsets, you should use * {@link #setTemplateConfigurations(TemplateConfigurationFactory)}, where you can associate encodings to * individual templates based on their names (like which "directory" are they in, what's their file * extension, etc.). The encoding associated with the templates that way overrides the encoding that you * specify here. * * @param parseAsFTL * If {@code true}, the loaded template is parsed and interpreted normally, as a regular FreeMarker * template. If {@code false}, the loaded template is treated as a static text, so <code>${...}</code>, * {@code <#...>} etc. will not have special meaning in it. * * @param ignoreMissing * If {@code true}, the method won't throw {@link TemplateNotFoundException} if the template doesn't * exist, instead it returns {@code null}. Other kind of exceptions won't be suppressed. * * @return the requested template; maybe {@code null} when the {@code ignoreMissing} parameter is {@code true}. * * @throws TemplateNotFoundException * If the template could not be found. Note that this exception extends {@link IOException}. * @throws MalformedTemplateNameException * If the template name given was in violation with the {@link TemplateNameFormat} in use. Note that * this exception extends {@link IOException}. * @throws ParseException * (extends <code>IOException</code>) if the template is syntactically bad. Note that this exception * extends {@link IOException}. * @throws IOException * If there was some other problem with reading the template "file". Note that the other exceptions * extend {@link IOException}, so this should be catched the last. * * @since 2.3.22 */
public Template getTemplate(String name, Locale locale, Object customLookupCondition, String encoding, boolean parseAsFTL, boolean ignoreMissing) throws TemplateNotFoundException, MalformedTemplateNameException, ParseException, IOException { if (locale == null) { locale = getLocale(); } if (encoding == null) { encoding = getEncoding(locale); } final MaybeMissingTemplate maybeTemp = cache.getTemplate(name, locale, customLookupCondition, encoding, parseAsFTL); final Template temp = maybeTemp.getTemplate(); if (temp == null) { if (ignoreMissing) { return null; } TemplateLoader tl = getTemplateLoader(); String msg; if (tl == null) { msg = "Don't know where to load template " + StringUtil.jQuote(name) + " from because the \"template_loader\" FreeMarker " + "setting wasn't set (Configuration.setTemplateLoader), so it's null."; } else { final String missingTempNormName = maybeTemp.getMissingTemplateNormalizedName(); final String missingTempReason = maybeTemp.getMissingTemplateReason(); final TemplateLookupStrategy templateLookupStrategy = getTemplateLookupStrategy(); msg = "Template not found for name " + StringUtil.jQuote(name) + (missingTempNormName != null && name != null && !removeInitialSlash(name).equals(missingTempNormName) ? " (normalized: " + StringUtil.jQuote(missingTempNormName) + ")" : "") + (customLookupCondition != null ? " and custom lookup condition " + StringUtil.jQuote(customLookupCondition) : "") + "." + (missingTempReason != null ? "\nReason given: " + ensureSentenceIsClosed(missingTempReason) : "") + "\nThe name was interpreted by this TemplateLoader: " + StringUtil.tryToString(tl) + "." + (!isKnownNonConfusingLookupStrategy(templateLookupStrategy) ? "\n(Before that, the name was possibly changed by this lookup strategy: " + StringUtil.tryToString(templateLookupStrategy) + ".)" : "") // Suspected reasons or warning: + (!templateLoaderExplicitlySet ? "\nWarning: The \"template_loader\" FreeMarker setting " + "wasn't set (Configuration.setTemplateLoader), and using the default value " + "is most certainly not intended and dangerous, and can be the cause of this error." : "") + (missingTempReason == null && name.indexOf('\\') != -1 ? "\nWarning: The name contains backslash (\"\\\") instead of slash (\"/\"); " + "template names should use slash only." : ""); } String normName = maybeTemp.getMissingTemplateNormalizedName(); throw new TemplateNotFoundException( normName != null ? normName : name, customLookupCondition, msg); } return temp; } private boolean isKnownNonConfusingLookupStrategy(TemplateLookupStrategy templateLookupStrategy) { return templateLookupStrategy == TemplateLookupStrategy.DEFAULT_2_3_0; } private String removeInitialSlash(String name) { return name.startsWith("/") ? name.substring(1) : name; } private String ensureSentenceIsClosed(String s) { if (s == null || s.length() == 0) { return s; } final char lastChar = s.charAt(s.length() - 1); return lastChar == '.' || lastChar == '!' || lastChar == '?' ? s : s + "."; }
Sets the charset used for decoding byte sequences to character sequences when reading template files in a locale for which no explicit encoding was specified via setEncoding(Locale, String). Note that by default there is no locale specified for any locale, so the default encoding is always in effect.

Defaults to the default system encoding, which can change from one server to another, so you should always set this setting. If you don't know what charset your should chose, "UTF-8" is usually a good choice.

Note that individual templates may specify their own charset by starting with <#ftl encoding="...">

Params:
  • encoding – The name of the charset, such as "UTF-8" or "ISO-8859-1"
/** * Sets the charset used for decoding byte sequences to character sequences when * reading template files in a locale for which no explicit encoding * was specified via {@link #setEncoding(Locale, String)}. Note that by default there is no locale specified for * any locale, so the default encoding is always in effect. * * <p>Defaults to the default system encoding, which can change from one server to * another, so <b>you should always set this setting</b>. If you don't know what charset your should chose, * {@code "UTF-8"} is usually a good choice. * * <p>Note that individual templates may specify their own charset by starting with * <tt>&lt;#ftl encoding="..."&gt;</tt> * * @param encoding The name of the charset, such as {@code "UTF-8"} or {@code "ISO-8859-1"} */
public void setDefaultEncoding(String encoding) { defaultEncoding = encoding; defaultEncodingExplicitlySet = true; }
Gets the default encoding for converting bytes to characters when reading template files in a locale for which no explicit encoding was specified. Defaults to the default system encoding.
/** * Gets the default encoding for converting bytes to characters when * reading template files in a locale for which no explicit encoding * was specified. Defaults to the default system encoding. */
public String getDefaultEncoding() { return defaultEncoding; }
Resets the setting to its default, as if it was never set.
Since:2.3.26
/** * Resets the setting to its default, as if it was never set. * * @since 2.3.26 */
public void unsetDefaultEncoding() { if (defaultEncodingExplicitlySet) { setDefaultEncoding(getDefaultDefaultEncoding()); defaultEncodingExplicitlySet = false; } }
Tells if setDefaultEncoding(String) (or equivalent) was already called on this instance, or it just holds the default value.
Since:2.3.26
/** * Tells if {@link #setDefaultEncoding(String)} (or equivalent) was already called on this instance, or it just holds the * default value. * * @since 2.3.26 */
public boolean isDefaultEncodingExplicitlySet() { return defaultEncodingExplicitlySet; } static private String getDefaultDefaultEncoding() { return getJVMDefaultEncoding(); } static private String getJVMDefaultEncoding() { return SecurityUtilities.getSystemProperty("file.encoding", "utf-8"); }
Gets the preferred character encoding for the given locale, or the default encoding if no encoding is set explicitly for the specified locale. You can associate encodings with locales using setEncoding(Locale, String) or loadBuiltInEncodingMap().
Params:
  • locale – Shouldn't be null, though for backward compatibility it's accepted when the locale to encoding Map (see earlier) is empty.
/** * Gets the preferred character encoding for the given locale, or the * default encoding if no encoding is set explicitly for the specified * locale. You can associate encodings with locales using * {@link #setEncoding(Locale, String)} or {@link #loadBuiltInEncodingMap()}. * * @param locale Shouldn't be {@code null}, though for backward compatibility it's accepted when the locale to * encoding {@link Map} (see earlier) is empty. */
public String getEncoding(Locale locale) { if (localeToCharsetMap.isEmpty()) { return defaultEncoding; } else { // Try for a full name match (may include country and variant) NullArgumentException.check("locale", locale); String charset = (String) localeToCharsetMap.get(locale.toString()); if (charset == null) { if (locale.getVariant().length() > 0) { Locale l = new Locale(locale.getLanguage(), locale.getCountry()); charset = (String) localeToCharsetMap.get(l.toString()); if (charset != null) { localeToCharsetMap.put(locale.toString(), charset); } } charset = (String) localeToCharsetMap.get(locale.getLanguage()); if (charset != null) { localeToCharsetMap.put(locale.toString(), charset); } } return charset != null ? charset : defaultEncoding; } }
Sets the character set encoding to use for templates of a given locale. If there is no explicit encoding set for some locale, then the default encoding will be used, what you can set with setDefaultEncoding.
See Also:
/** * Sets the character set encoding to use for templates of * a given locale. If there is no explicit encoding set for some * locale, then the default encoding will be used, what you can * set with {@link #setDefaultEncoding}. * * @see #clearEncodingMap * @see #loadBuiltInEncodingMap */
public void setEncoding(Locale locale, String encoding) { localeToCharsetMap.put(locale.toString(), encoding); }
Adds a shared variable to the configuration. Shared sharedVariables are sharedVariables that are visible as top-level sharedVariables for all templates which use this configuration, if the data model does not contain a variable with the same name.

Never use TemplateModel implementation that is not thread-safe for shared sharedVariables, if the configuration is used by multiple threads! It is the typical situation for Servlet based Web sites.

This method is not thread safe; use it with the same restrictions as those that modify setting values.

Params:
  • name – the name used to access the data object from your template. If a shared variable with this name already exists, it will replace that.
See Also:
/** * Adds a shared variable to the configuration. * Shared sharedVariables are sharedVariables that are visible * as top-level sharedVariables for all templates which use this * configuration, if the data model does not contain a * variable with the same name. * * <p>Never use <tt>TemplateModel</tt> implementation that is not thread-safe for shared sharedVariables, * if the configuration is used by multiple threads! It is the typical situation for Servlet based Web sites. * * <p>This method is <b>not</b> thread safe; use it with the same restrictions as those that modify setting values. * * @param name the name used to access the data object from your template. * If a shared variable with this name already exists, it will replace * that. * * @see #setAllSharedVariables * @see #setSharedVariable(String,Object) */
public void setSharedVariable(String name, TemplateModel tm) { Object replaced = sharedVariables.put(name, tm); if (replaced != null && rewrappableSharedVariables != null) { rewrappableSharedVariables.remove(name); } }
Returns the set containing the names of all defined shared sharedVariables. The method returns a new Set object on each call that is completely disconnected from the Configuration. That is, modifying the set will have no effect on the Configuration object.
/** * Returns the set containing the names of all defined shared sharedVariables. * The method returns a new Set object on each call that is completely * disconnected from the Configuration. That is, modifying the set will have * no effect on the Configuration object. */
public Set getSharedVariableNames() { return new HashSet(sharedVariables.keySet()); }
Adds shared variable to the configuration; It uses Configurable.getObjectWrapper() to wrap the value, so it's important that the object wrapper is set before this.

This method is not thread safe; use it with the same restrictions as those that modify setting values.

The added value should be thread safe, if you are running templates from multiple threads with this configuration.

Throws:
See Also:
/** * Adds shared variable to the configuration; It uses {@link Configurable#getObjectWrapper()} to wrap the * {@code value}, so it's important that the object wrapper is set before this. * * <p>This method is <b>not</b> thread safe; use it with the same restrictions as those that modify setting values. * * <p>The added value should be thread safe, if you are running templates from multiple threads with this * configuration. * * @throws TemplateModelException If some of the variables couldn't be wrapped via {@link #getObjectWrapper()}. * * @see #setSharedVaribles(Map) * @see #setSharedVariable(String,TemplateModel) * @see #setAllSharedVariables(TemplateHashModelEx) */
public void setSharedVariable(String name, Object value) throws TemplateModelException { setSharedVariable(name, getObjectWrapper().wrap(value)); }
Replaces all shared variables (removes all previously added ones).

The values in the map can be TemplateModel-s or plain Java objects which will be immediately converted to TemplateModel with the ObjectWrapper returned by Configurable.getObjectWrapper(). If setObjectWrapper(ObjectWrapper) is called later, this conversion will be re-applied. Thus, ignoring some extra resource usage, it doesn't mater if in what order are setObjectWrapper(ObjectWrapper) and setSharedVaribles(Map) called. This is essential when you don't have control over the order in which the setters are called.

The values in the map must be thread safe, if you are running templates from multiple threads with this configuration. This means that both the plain Java object and the TemplateModel-s created from them by the ObjectWrapper must be thread safe. (The standard ObjectWrapper-s of FreeMarker create thread safe TemplateModel-s.) The Map itself need not be thread-safe.

This setter method has no getter pair because of the tricky relation ship with setSharedVariable(String, Object).

Throws:
Since:2.3.21
/** * Replaces all shared variables (removes all previously added ones). * * <p>The values in the map can be {@link TemplateModel}-s or plain Java objects which will be immediately converted * to {@link TemplateModel} with the {@link ObjectWrapper} returned by {@link #getObjectWrapper()}. If * {@link #setObjectWrapper(ObjectWrapper)} is called later, this conversion will be re-applied. Thus, ignoring some * extra resource usage, it doesn't mater if in what order are {@link #setObjectWrapper(ObjectWrapper)} and * {@link #setSharedVaribles(Map)} called. This is essential when you don't have control over the order in which * the setters are called. * * <p>The values in the map must be thread safe, if you are running templates from multiple threads with * this configuration. This means that both the plain Java object and the {@link TemplateModel}-s created from them * by the {@link ObjectWrapper} must be thread safe. (The standard {@link ObjectWrapper}-s of FreeMarker create * thread safe {@link TemplateModel}-s.) The {@link Map} itself need not be thread-safe. * * <p>This setter method has no getter pair because of the tricky relation ship with * {@link #setSharedVariable(String, Object)}. * * @throws TemplateModelException If some of the variables couldn't be wrapped via {@link #getObjectWrapper()}. * * @since 2.3.21 */
public void setSharedVaribles(Map/*<String, Object>*/ map) throws TemplateModelException { rewrappableSharedVariables = new HashMap(map); sharedVariables.clear(); setSharedVariablesFromRewrappableSharedVariables(); } private void setSharedVariablesFromRewrappableSharedVariables() throws TemplateModelException { if (rewrappableSharedVariables == null) return; for (Iterator it = rewrappableSharedVariables.entrySet().iterator(); it.hasNext(); ) { Map.Entry/*<String, Object>*/ ent = (Entry) it.next(); String name = (String) ent.getKey(); Object value = ent.getValue(); TemplateModel valueAsTM; if (value instanceof TemplateModel) { valueAsTM = (TemplateModel) value; } else { valueAsTM = getObjectWrapper().wrap(value); } sharedVariables.put(name, valueAsTM); } }
Adds all object in the hash as shared variable to the configuration; it's like doing several setSharedVariable(String, Object) calls, one for each hash entry. It doesn't remove the already added shared variable before doing this.

Never use TemplateModel implementation that is not thread-safe for shared shared variable values, if the configuration is used by multiple threads! It is the typical situation for Servlet based Web sites.

This method is not thread safe; use it with the same restrictions as those that modify setting values.

Params:
  • hash – a hash model whose objects will be copied to the configuration with same names as they are given in the hash. If a shared variable with these names already exist, it will be replaced with those from the map.
See Also:
/** * Adds all object in the hash as shared variable to the configuration; it's like doing several * {@link #setSharedVariable(String, Object)} calls, one for each hash entry. It doesn't remove the already added * shared variable before doing this. * * <p>Never use <tt>TemplateModel</tt> implementation that is not thread-safe for shared shared variable values, * if the configuration is used by multiple threads! It is the typical situation for Servlet based Web sites. * * <p>This method is <b>not</b> thread safe; use it with the same restrictions as those that modify setting values. * * @param hash a hash model whose objects will be copied to the * configuration with same names as they are given in the hash. * If a shared variable with these names already exist, it will be replaced * with those from the map. * * @see #setSharedVaribles(Map) * @see #setSharedVariable(String,Object) * @see #setSharedVariable(String,TemplateModel) */
public void setAllSharedVariables(TemplateHashModelEx hash) throws TemplateModelException { TemplateModelIterator keys = hash.keys().iterator(); TemplateModelIterator values = hash.values().iterator(); while (keys.hasNext()) { setSharedVariable(((TemplateScalarModel) keys.next()).getAsString(), values.next()); } }
Gets a shared variable. Shared sharedVariables are sharedVariables that are available to all templates. When a template is processed, and an identifier is undefined in the data model, a shared variable object with the same identifier is then looked up in the configuration. There are several predefined sharedVariables that are always available through this method, see the FreeMarker manual for a comprehensive list of them.
See Also:
/** * Gets a shared variable. Shared sharedVariables are sharedVariables that are * available to all templates. When a template is processed, and an identifier * is undefined in the data model, a shared variable object with the same identifier * is then looked up in the configuration. There are several predefined sharedVariables * that are always available through this method, see the FreeMarker manual * for a comprehensive list of them. * * @see #setSharedVariable(String,Object) * @see #setSharedVariable(String,TemplateModel) * @see #setAllSharedVariables */
public TemplateModel getSharedVariable(String name) { return (TemplateModel) sharedVariables.get(name); }
Removes all shared sharedVariables, except the predefined ones (compress, html_escape, etc.).
/** * Removes all shared sharedVariables, except the predefined ones (compress, html_escape, etc.). */
public void clearSharedVariables() { sharedVariables.clear(); loadBuiltInSharedVariables(); }
Removes all entries from the template cache, thus forcing reloading of templates on subsequent getTemplate calls.

This method is thread-safe and can be called while the engine processes templates.

/** * Removes all entries from the template cache, thus forcing reloading of templates * on subsequent <code>getTemplate</code> calls. * * <p>This method is thread-safe and can be called while the engine processes templates. */
public void clearTemplateCache() { cache.clear(); }
Since:2.3.19
/** * Equivalent to {@link * #removeTemplateFromCache(String, Locale, Object, String, boolean) * removeTemplateFromCache(name, thisCfg.getLocale(), null, thisCfg.getEncoding(thisCfg.getLocale()), true)}. * @since 2.3.19 */
public void removeTemplateFromCache(String name) throws IOException { Locale loc = getLocale(); removeTemplateFromCache(name, loc, null, getEncoding(loc), true); }
Since:2.3.19
/** * Equivalent to {@link * #removeTemplateFromCache(String, Locale, Object, String, boolean) * removeTemplateFromCache(name, locale, null, thisCfg.getEncoding(locale), true)}. * @since 2.3.19 */
public void removeTemplateFromCache(String name, Locale locale) throws IOException { removeTemplateFromCache(name, locale, null, getEncoding(locale), true); }
Since:2.3.19
/** * Equivalent to {@link * #removeTemplateFromCache(String, Locale, Object, String, boolean) * removeTemplateFromCache(name, thisCfg.getLocale(), null, encoding, true)}. * @since 2.3.19 */
public void removeTemplateFromCache(String name, String encoding) throws IOException { removeTemplateFromCache(name, getLocale(), null, encoding, true); }
Since:2.3.19
/** * Equivalent to {@link * #removeTemplateFromCache(String, Locale, Object, String, boolean) * removeTemplateFromCache(name, locale, null, encoding, true)}. * @since 2.3.19 */
public void removeTemplateFromCache(String name, Locale locale, String encoding) throws IOException { removeTemplateFromCache(name, locale, null, encoding, true); }
Since:2.3.19
/** * Equivalent to {@link * #removeTemplateFromCache(String, Locale, Object, String, boolean) * removeTemplateFromCache(name, locale, null, encoding, parse)}. * @since 2.3.19 */
public void removeTemplateFromCache( String name, Locale locale, String encoding, boolean parse) throws IOException { removeTemplateFromCache(name, locale, null, encoding, parse); }
Removes a template from the template cache, hence forcing the re-loading of it when it's next time requested. This is to give the application finer control over cache updating than setTemplateUpdateDelay(int) alone does.

For the meaning of the parameters, see getTemplate(String, Locale, Object, String, boolean, boolean).

This method is thread-safe and can be called while the engine processes templates.

Since:2.3.28
/** * Removes a template from the template cache, hence forcing the re-loading * of it when it's next time requested. This is to give the application * finer control over cache updating than {@link #setTemplateUpdateDelay(int)} * alone does. * * <p>For the meaning of the parameters, see * {@link #getTemplate(String, Locale, Object, String, boolean, boolean)}. * * <p>This method is thread-safe and can be called while the engine processes templates. * * @since 2.3.28 */
public void removeTemplateFromCache( String name, Locale locale, Object customLookupCondition, String encoding, boolean parse) throws IOException { cache.removeTemplate(name, locale, customLookupCondition, encoding, parse); }
The getter pair of setLocalizedLookup(boolean).

This method is thread-safe and can be called while the engine works.

/** * The getter pair of {@link #setLocalizedLookup(boolean)}. * * <p>This method is thread-safe and can be called while the engine works. */
public boolean getLocalizedLookup() { return cache.getLocalizedLookup(); }
Enables/disables localized template lookup. Enabled by default.

With the default TemplateLookupStrategy, localized lookup works like this: Let's say your locale setting is Locale("en", "AU"), and you call cfg.getTemplate("foo.ftl"). Then FreeMarker will look for the template under these names, stopping at the first that exists: "foo_en_AU.ftl", "foo_en.ftl", "foo.ftl". See the description of the default value at setTemplateLookupStrategy(TemplateLookupStrategy) for a more details. If you need to generate different template names, use setTemplateLookupStrategy(TemplateLookupStrategy) with your custom TemplateLookupStrategy.

Note that changing the value of this setting causes the template cache to be emptied so that old lookup results won't be reused (since 2.3.22).

Historical note: Despite what the API documentation said earlier, this method is not thread-safe. While setting it can't cause any serious problems, and in fact it works well on most hardware, it's not guaranteed that FreeMarker will see the update in all threads.

/** * Enables/disables localized template lookup. Enabled by default. * * <p> * With the default {@link TemplateLookupStrategy}, localized lookup works like this: Let's say your locale setting * is {@code Locale("en", "AU")}, and you call {@link Configuration#getTemplate(String) cfg.getTemplate("foo.ftl")}. * Then FreeMarker will look for the template under these names, stopping at the first that exists: * {@code "foo_en_AU.ftl"}, {@code "foo_en.ftl"}, {@code "foo.ftl"}. See the description of the default value at * {@link #setTemplateLookupStrategy(TemplateLookupStrategy)} for a more details. If you need to generate different * template names, use {@link #setTemplateLookupStrategy(TemplateLookupStrategy)} with your custom * {@link TemplateLookupStrategy}. * * <p>Note that changing the value of this setting causes the template cache to be emptied so that old lookup * results won't be reused (since 2.3.22). * * <p> * Historical note: Despite what the API documentation said earlier, this method is <em>not</em> thread-safe. While * setting it can't cause any serious problems, and in fact it works well on most hardware, it's not guaranteed that * FreeMarker will see the update in all threads. */
public void setLocalizedLookup(boolean localizedLookup) { this.localizedLookup = localizedLookup; cache.setLocalizedLookup(localizedLookup); } @Override public void setSetting(String name, String value) throws TemplateException { boolean unknown = false; try { if ("TemplateUpdateInterval".equalsIgnoreCase(name)) { name = TEMPLATE_UPDATE_DELAY_KEY; } else if ("DefaultEncoding".equalsIgnoreCase(name)) { name = DEFAULT_ENCODING_KEY; } if (DEFAULT_ENCODING_KEY_SNAKE_CASE.equals(name) || DEFAULT_ENCODING_KEY_CAMEL_CASE.equals(name)) { if (JVM_DEFAULT.equalsIgnoreCase(value)) { setDefaultEncoding(getJVMDefaultEncoding()); } else { setDefaultEncoding(value); } } else if (LOCALIZED_LOOKUP_KEY_SNAKE_CASE.equals(name) || LOCALIZED_LOOKUP_KEY_CAMEL_CASE.equals(name)) { setLocalizedLookup(StringUtil.getYesNo(value)); } else if (STRICT_SYNTAX_KEY_SNAKE_CASE.equals(name) || STRICT_SYNTAX_KEY_CAMEL_CASE.equals(name)) { setStrictSyntaxMode(StringUtil.getYesNo(value)); } else if (WHITESPACE_STRIPPING_KEY_SNAKE_CASE.equals(name) || WHITESPACE_STRIPPING_KEY_CAMEL_CASE.equals(name)) { setWhitespaceStripping(StringUtil.getYesNo(value)); } else if (AUTO_ESCAPING_POLICY_KEY_SNAKE_CASE.equals(name) || AUTO_ESCAPING_POLICY_KEY_CAMEL_CASE.equals(name)) { if ("enable_if_default".equals(value) || "enableIfDefault".equals(value)) { setAutoEscapingPolicy(ENABLE_IF_DEFAULT_AUTO_ESCAPING_POLICY); } else if ("enable_if_supported".equals(value) || "enableIfSupported".equals(value)) { setAutoEscapingPolicy(ENABLE_IF_SUPPORTED_AUTO_ESCAPING_POLICY); } else if ("disable".equals(value)) { setAutoEscapingPolicy(DISABLE_AUTO_ESCAPING_POLICY); } else { throw invalidSettingValueException(name, value); } } else if (OUTPUT_FORMAT_KEY_SNAKE_CASE.equals(name) || OUTPUT_FORMAT_KEY_CAMEL_CASE.equals(name)) { if (value.equalsIgnoreCase(DEFAULT)) { unsetOutputFormat(); } else { OutputFormat stdOF = STANDARD_OUTPUT_FORMATS.get(value); setOutputFormat( stdOF != null ? stdOF : (OutputFormat) _ObjectBuilderSettingEvaluator.eval( value, OutputFormat.class, true, _SettingEvaluationEnvironment.getCurrent())); } } else if (REGISTERED_CUSTOM_OUTPUT_FORMATS_KEY_SNAKE_CASE.equals(name) || REGISTERED_CUSTOM_OUTPUT_FORMATS_KEY_CAMEL_CASE.equals(name)) { List list = (List) _ObjectBuilderSettingEvaluator.eval( value, List.class, true, _SettingEvaluationEnvironment.getCurrent()); for (Object item : list) { if (!(item instanceof OutputFormat)) { throw new _MiscTemplateException(getEnvironment(), "Invalid value for setting ", new _DelayedJQuote(name), ": List items must be " + OutputFormat.class.getName() + " instances, in: ", value); } } setRegisteredCustomOutputFormats(list); } else if (RECOGNIZE_STANDARD_FILE_EXTENSIONS_KEY_SNAKE_CASE.equals(name) || RECOGNIZE_STANDARD_FILE_EXTENSIONS_KEY_CAMEL_CASE.equals(name)) { if (value.equalsIgnoreCase(DEFAULT)) { unsetRecognizeStandardFileExtensions(); } else { setRecognizeStandardFileExtensions(StringUtil.getYesNo(value)); } } else if (CACHE_STORAGE_KEY_SNAKE_CASE.equals(name) || CACHE_STORAGE_KEY_CAMEL_CASE.equals(name)) { if (value.equalsIgnoreCase(DEFAULT)) { unsetCacheStorage(); } if (value.indexOf('.') == -1) { int strongSize = 0; int softSize = 0; Map map = StringUtil.parseNameValuePairList( value, String.valueOf(Integer.MAX_VALUE)); Iterator it = map.entrySet().iterator(); while (it.hasNext()) { Map.Entry ent = (Map.Entry) it.next(); String pname = (String) ent.getKey(); int pvalue; try { pvalue = Integer.parseInt((String) ent.getValue()); } catch (NumberFormatException e) { throw invalidSettingValueException(name, value); } if ("soft".equalsIgnoreCase(pname)) { softSize = pvalue; } else if ("strong".equalsIgnoreCase(pname)) { strongSize = pvalue; } else { throw invalidSettingValueException(name, value); } } if (softSize == 0 && strongSize == 0) { throw invalidSettingValueException(name, value); } setCacheStorage(new MruCacheStorage(strongSize, softSize)); } else { setCacheStorage((CacheStorage) _ObjectBuilderSettingEvaluator.eval( value, CacheStorage.class, false, _SettingEvaluationEnvironment.getCurrent())); } } else if (TEMPLATE_UPDATE_DELAY_KEY_SNAKE_CASE.equals(name) || TEMPLATE_UPDATE_DELAY_KEY_CAMEL_CASE.equals(name)) { long multipier; String valueWithoutUnit; if (value.endsWith("ms")) { multipier = 1; valueWithoutUnit = rightTrim(value.substring(0, value.length() - 2)); } else if (value.endsWith("s")) { multipier = 1000; valueWithoutUnit = rightTrim(value.substring(0, value.length() - 1)); } else if (value.endsWith("m")) { multipier = 1000 * 60; valueWithoutUnit = rightTrim(value.substring(0, value.length() - 1)); } else if (value.endsWith("h")) { multipier = 1000 * 60 * 60; valueWithoutUnit = rightTrim(value.substring(0, value.length() - 1)); } else { multipier = 1000; // Default is seconds for backward compatibility valueWithoutUnit = value; } setTemplateUpdateDelayMilliseconds(Integer.parseInt(valueWithoutUnit) * multipier); } else if (TAG_SYNTAX_KEY_SNAKE_CASE.equals(name) || TAG_SYNTAX_KEY_CAMEL_CASE.equals(name)) { if ("auto_detect".equals(value) || "autoDetect".equals(value)) { setTagSyntax(AUTO_DETECT_TAG_SYNTAX); } else if ("angle_bracket".equals(value) || "angleBracket".equals(value)) { setTagSyntax(ANGLE_BRACKET_TAG_SYNTAX); } else if ("square_bracket".equals(value) || "squareBracket".equals(value)) { setTagSyntax(SQUARE_BRACKET_TAG_SYNTAX); } else { throw invalidSettingValueException(name, value); } } else if (INTERPOLATION_SYNTAX_KEY_SNAKE_CASE.equals(name) || INTERPOLATION_SYNTAX_KEY_CAMEL_CASE.equals(name)) { if ("legacy".equals(value)) { setInterpolationSyntax(LEGACY_INTERPOLATION_SYNTAX); } else if ("dollar".equals(value)) { setInterpolationSyntax(DOLLAR_INTERPOLATION_SYNTAX); } else if ("square_bracket".equals(value) || "squareBracket".equals(value)) { setInterpolationSyntax(SQUARE_BRACKET_INTERPOLATION_SYNTAX); } else { throw invalidSettingValueException(name, value); } } else if (NAMING_CONVENTION_KEY_SNAKE_CASE.equals(name) || NAMING_CONVENTION_KEY_CAMEL_CASE.equals(name)) { if ("auto_detect".equals(value) || "autoDetect".equals(value)) { setNamingConvention(AUTO_DETECT_NAMING_CONVENTION); } else if ("legacy".equals(value)) { setNamingConvention(LEGACY_NAMING_CONVENTION); } else if ("camel_case".equals(value) || "camelCase".equals(value)) { setNamingConvention(CAMEL_CASE_NAMING_CONVENTION); } else { throw invalidSettingValueException(name, value); } } else if (TAB_SIZE_KEY_SNAKE_CASE.equals(name) || TAB_SIZE_KEY_CAMEL_CASE.equals(name)) { setTabSize(Integer.parseInt(value)); } else if (INCOMPATIBLE_IMPROVEMENTS_KEY_SNAKE_CASE.equals(name) || INCOMPATIBLE_IMPROVEMENTS_KEY_CAMEL_CASE.equals(name)) { setIncompatibleImprovements(new Version(value)); } else if (INCOMPATIBLE_ENHANCEMENTS.equals(name)) { setIncompatibleEnhancements(value); } else if (TEMPLATE_LOADER_KEY_SNAKE_CASE.equals(name) || TEMPLATE_LOADER_KEY_CAMEL_CASE.equals(name)) { if (value.equalsIgnoreCase(DEFAULT)) { unsetTemplateLoader(); } else { setTemplateLoader((TemplateLoader) _ObjectBuilderSettingEvaluator.eval( value, TemplateLoader.class, true, _SettingEvaluationEnvironment.getCurrent())); } } else if (TEMPLATE_LOOKUP_STRATEGY_KEY_SNAKE_CASE.equals(name) || TEMPLATE_LOOKUP_STRATEGY_KEY_CAMEL_CASE.equals(name)) { if (value.equalsIgnoreCase(DEFAULT)) { unsetTemplateLookupStrategy(); } else { setTemplateLookupStrategy((TemplateLookupStrategy) _ObjectBuilderSettingEvaluator.eval( value, TemplateLookupStrategy.class, false, _SettingEvaluationEnvironment.getCurrent())); } } else if (TEMPLATE_NAME_FORMAT_KEY_SNAKE_CASE.equals(name) || TEMPLATE_NAME_FORMAT_KEY_CAMEL_CASE.equals(name)) { if (value.equalsIgnoreCase(DEFAULT)) { unsetTemplateNameFormat(); } else if (value.equalsIgnoreCase("default_2_3_0")) { setTemplateNameFormat(TemplateNameFormat.DEFAULT_2_3_0); } else if (value.equalsIgnoreCase("default_2_4_0")) { setTemplateNameFormat(TemplateNameFormat.DEFAULT_2_4_0); } else { throw invalidSettingValueException(name, value); } } else if (TEMPLATE_CONFIGURATIONS_KEY_SNAKE_CASE.equals(name) || TEMPLATE_CONFIGURATIONS_KEY_CAMEL_CASE.equals(name)) { if (value.equals(NULL)) { setTemplateConfigurations(null); } else { setTemplateConfigurations((TemplateConfigurationFactory) _ObjectBuilderSettingEvaluator.eval( value, TemplateConfigurationFactory.class, false, _SettingEvaluationEnvironment.getCurrent())); } } else { unknown = true; } } catch (Exception e) { throw settingValueAssignmentException(name, value, e); } if (unknown) { super.setSetting(name, value); } } private String rightTrim(String s) { int ln = s.length(); while (ln > 0 && Character.isWhitespace(s.charAt(ln - 1))) { ln--; } return s.substring(0, ln); }
Returns the valid Configuration setting names. Naturally, this includes the Configurable setting names too.
Params:
  • camelCase – If we want the setting names with camel case naming convention, or with snake case (legacy) naming convention.
See Also:
Since:2.3.24
/** * Returns the valid {@link Configuration} setting names. Naturally, this includes the {@link Configurable} setting * names too. * * @param camelCase * If we want the setting names with camel case naming convention, or with snake case (legacy) naming * convention. * * @see Configurable#getSettingNames(boolean) * * @since 2.3.24 */
@Override public Set<String> getSettingNames(boolean camelCase) { return new _UnmodifiableCompositeSet<String>( super.getSettingNames(camelCase), new _SortedArraySet<String>(camelCase ? SETTING_NAMES_CAMEL_CASE : SETTING_NAMES_SNAKE_CASE)); } @Override protected String getCorrectedNameForUnknownSetting(String name) { if ("encoding".equals(name) || "charset".equals(name) || "default_charset".equals(name)) { // [2.4] Default might changes to camel-case return DEFAULT_ENCODING_KEY; } if ("defaultCharset".equals(name)) { return DEFAULT_ENCODING_KEY_CAMEL_CASE; } return super.getCorrectedNameForUnknownSetting(name); } @Override protected void doAutoImportsAndIncludes(Environment env) throws TemplateException, IOException { Template t = env.getMainTemplate(); doAutoImports(env, t); doAutoIncludes(env, t); } private void doAutoImports(Environment env, Template t) throws IOException, TemplateException { Map<String, String> envAutoImports = env.getAutoImportsWithoutFallback(); Map<String, String> tAutoImports = t.getAutoImportsWithoutFallback(); boolean lazyAutoImports = env.getLazyAutoImports() != null ? env.getLazyAutoImports().booleanValue() : env.getLazyImports(); for (Map.Entry<String, String> autoImport : getAutoImportsWithoutFallback().entrySet()) { String nsVarName = autoImport.getKey(); if ((tAutoImports == null || !tAutoImports.containsKey(nsVarName)) && (envAutoImports == null || !envAutoImports.containsKey(nsVarName))) { env.importLib(autoImport.getValue(), nsVarName, lazyAutoImports); } } if (tAutoImports != null) { for (Map.Entry<String, String> autoImport : tAutoImports.entrySet()) { String nsVarName = autoImport.getKey(); if (envAutoImports == null || !envAutoImports.containsKey(nsVarName)) { env.importLib(autoImport.getValue(), nsVarName, lazyAutoImports); } } } if (envAutoImports != null) { for (Map.Entry<String, String> autoImport : envAutoImports.entrySet()) { String nsVarName = autoImport.getKey(); env.importLib(autoImport.getValue(), nsVarName, lazyAutoImports); } } } private void doAutoIncludes(Environment env, Template t) throws TemplateException, IOException, TemplateNotFoundException, MalformedTemplateNameException, ParseException { // We can't store autoIncludes in LinkedHashSet-s because setAutoIncludes(List) allows duplicates, // unfortunately. Yet we have to prevent duplicates among Configuration levels, with the lowest levels having // priority. So we build some Set-s to do that, but we avoid the most common cases where they aren't needed. List<String> tAutoIncludes = t.getAutoIncludesWithoutFallback(); List<String> envAutoIncludes = env.getAutoIncludesWithoutFallback(); for (String templateName : getAutoIncludesWithoutFallback()) { if ((tAutoIncludes == null || !tAutoIncludes.contains(templateName)) && (envAutoIncludes == null || !envAutoIncludes.contains(templateName))) { env.include(getTemplate(templateName, env.getLocale())); } } if (tAutoIncludes != null) { for (String templateName : tAutoIncludes) { if (envAutoIncludes == null || !envAutoIncludes.contains(templateName)) { env.include(getTemplate(templateName, env.getLocale())); } } } if (envAutoIncludes != null) { for (String templateName : envAutoIncludes) { env.include(getTemplate(templateName, env.getLocale())); } } }
Returns FreeMarker version number string.
Deprecated:Use getVersion() instead.
/** * Returns FreeMarker version number string. * * @deprecated Use {@link #getVersion()} instead. */
@Deprecated public static String getVersionNumber() { return VERSION.toString(); }
Returns FreeMarker version information, most importantly the major.minor.micro version numbers; do NOT use this as the value of the incompatible_improvements setting (as the parameter to Configuration(Version)), as then your application can break when you upgrade FreeMarker! Use a constant value, like VERSION_2_3_28, to protect your application from fixes/changes that aren't entirely backward compatible. Fixes and features that are backward compatible are always enabled. On FreeMarker version numbering rules:
  • For final/stable releases the version number is like major.minor.micro, like 2.3.19. (Historically, when micro was 0 the version strings was like major.minor instead of the proper major.minor.0, but that's not like that anymore.)
  • When only the micro version is increased, compatibility with previous versions with the same major.minor is kept. Thus freemarker.jar can be replaced in an existing application without breaking it.
  • For non-final/unstable versions (that almost nobody uses), the format is:
    • Starting from 2.3.20: major.minor.micro-extraInfo, like 2.3.20-nightly_20130506T123456Z, 2.4.0-RC01. The major.minor.micro always indicates the target we move towards, so 2.3.20-nightly or 2.3.20-M01 is after 2.3.19 and will eventually become to 2.3.20. "PRE", "M" and "RC" (uppercase!) means "preview", "milestone" and "release candidate" respectively, and is always followed by a 2 digit 0-padded counter, like M03 is the 3rd milestone release of a given major.minor.micro.
    • Before 2.3.20: The extraInfo wasn't preceded by a "-". Instead of "nightly" there was "mod", where the major.minor.micro part has indicated where are we coming from, so 2.3.19mod (read as: 2.3.19 modified) was after 2.3.19 but before 2.3.20. Also, "pre" and "rc" was lowercase, and was followd by a number without 0-padding.
Since:2.3.20
/** * Returns FreeMarker version information, most importantly the major.minor.micro version numbers; * do NOT use this as the value of the {@code incompatible_improvements} setting (as the parameter to * {@link Configuration#Configuration(Version)}), as then your application can break when you upgrade FreeMarker! * Use a constant value, like {@link #VERSION_2_3_28}, to protect your application from fixes/changes that aren't * entirely backward compatible. Fixes and features that are backward compatible are always enabled. * * On FreeMarker version numbering rules: * <ul> * <li>For final/stable releases the version number is like major.minor.micro, like 2.3.19. (Historically, * when micro was 0 the version strings was like major.minor instead of the proper major.minor.0, but that's * not like that anymore.) * <li>When only the micro version is increased, compatibility with previous versions with the same * major.minor is kept. Thus <tt>freemarker.jar</tt> can be replaced in an existing application without * breaking it.</li> * <li>For non-final/unstable versions (that almost nobody uses), the format is: * <ul> * <li>Starting from 2.3.20: major.minor.micro-extraInfo, like * 2.3.20-nightly_20130506T123456Z, 2.4.0-RC01. The major.minor.micro * always indicates the target we move towards, so 2.3.20-nightly or 2.3.20-M01 is * after 2.3.19 and will eventually become to 2.3.20. "PRE", "M" and "RC" (uppercase!) means * "preview", "milestone" and "release candidate" respectively, and is always followed by a 2 digit * 0-padded counter, like M03 is the 3rd milestone release of a given major.minor.micro.</li> * <li>Before 2.3.20: The extraInfo wasn't preceded by a "-". * Instead of "nightly" there was "mod", where the major.minor.micro part has indicated where * are we coming from, so 2.3.19mod (read as: 2.3.19 modified) was after 2.3.19 but before 2.3.20. * Also, "pre" and "rc" was lowercase, and was followd by a number without 0-padding.</li> * </ul> * </ul> * * @since 2.3.20 */
public static Version getVersion() { return VERSION; }
Returns the default object wrapper for a given "incompatible_improvements" version.
See Also:
  • setIncompatibleImprovements(Version)
Since:2.3.21
/** * Returns the default object wrapper for a given "incompatible_improvements" version. * * @see #setIncompatibleImprovements(Version) * * @since 2.3.21 */
public static ObjectWrapper getDefaultObjectWrapper(Version incompatibleImprovements) { if (incompatibleImprovements.intValue() < _TemplateAPI.VERSION_INT_2_3_21) { return ObjectWrapper.DEFAULT_WRAPPER; } else { return new DefaultObjectWrapperBuilder(incompatibleImprovements).build(); } }
Since:2.3.20
/** * Same as {@link #getSupportedBuiltInNames(int)} with argument {@link #getNamingConvention()}. * * @since 2.3.20 */
public Set getSupportedBuiltInNames() { return getSupportedBuiltInNames(getNamingConvention()); }
Returns the names of the supported "built-ins". These are the (expr?builtin_name-like things). As of this writing, this information doesn't depend on the configuration options, so it could be a static method, but to be future-proof, it's an instance method.
Params:
Since:2.3.24
/** * Returns the names of the supported "built-ins". These are the ({@code expr?builtin_name}-like things). As of this * writing, this information doesn't depend on the configuration options, so it could be a static method, but * to be future-proof, it's an instance method. * * @param namingConvention * One of {@link #AUTO_DETECT_NAMING_CONVENTION}, {@link #LEGACY_NAMING_CONVENTION}, and * {@link #CAMEL_CASE_NAMING_CONVENTION}. If it's {@link #AUTO_DETECT_NAMING_CONVENTION} then the union * of the names in all the naming conventions is returned. * * @since 2.3.24 */
public Set<String> getSupportedBuiltInNames(int namingConvention) { return _CoreAPI.getSupportedBuiltInNames(namingConvention); }
Since:2.3.21
/** * Same as {@link #getSupportedBuiltInDirectiveNames(int)} with argument {@link #getNamingConvention()}. * * @since 2.3.21 */
public Set getSupportedBuiltInDirectiveNames() { return getSupportedBuiltInDirectiveNames(getNamingConvention()); }
Returns the names of the directives that are predefined by FreeMarker. These are the things that you call like <#directiveName ...>.
Params:
Since:2.3.24
/** * Returns the names of the directives that are predefined by FreeMarker. These are the things that you call like * <tt>&lt;#directiveName ...&gt;</tt>. * * @param namingConvention * One of {@link #AUTO_DETECT_NAMING_CONVENTION}, {@link #LEGACY_NAMING_CONVENTION}, and * {@link #CAMEL_CASE_NAMING_CONVENTION}. If it's {@link #AUTO_DETECT_NAMING_CONVENTION} then the union * of the names in all the naming conventions is returned. * * @since 2.3.24 */
public Set<String> getSupportedBuiltInDirectiveNames(int namingConvention) { if (namingConvention == AUTO_DETECT_NAMING_CONVENTION) { return _CoreAPI.ALL_BUILT_IN_DIRECTIVE_NAMES; } else if (namingConvention == LEGACY_NAMING_CONVENTION) { return _CoreAPI.LEGACY_BUILT_IN_DIRECTIVE_NAMES; } else if (namingConvention == CAMEL_CASE_NAMING_CONVENTION) { return _CoreAPI.CAMEL_CASE_BUILT_IN_DIRECTIVE_NAMES; } else { throw new IllegalArgumentException("Unsupported naming convention constant: " + namingConvention); } } private static String getRequiredVersionProperty(Properties vp, String properyName) { String s = vp.getProperty(properyName); if (s == null) { throw new RuntimeException( "Version file is corrupt: \"" + properyName + "\" property is missing."); } return s; } }