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

import java.io.IOException;
import java.io.InputStream;
import java.io.Writer;
import java.text.NumberFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.LinkedList;
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 freemarker.cache.AndMatcher;
import freemarker.cache.ConditionalTemplateConfigurationFactory;
import freemarker.cache.FileNameGlobMatcher;
import freemarker.cache.FirstMatchTemplateConfigurationFactory;
import freemarker.cache.MergingTemplateConfigurationFactory;
import freemarker.cache.NotMatcher;
import freemarker.cache.OrMatcher;
import freemarker.cache.PathGlobMatcher;
import freemarker.cache.PathRegexMatcher;
import freemarker.cache.TemplateLoader;
import freemarker.ext.beans.BeansWrapper;
import freemarker.ext.beans.BeansWrapperBuilder;
import freemarker.template.AttemptExceptionReporter;
import freemarker.template.Configuration;
import freemarker.template.DefaultObjectWrapper;
import freemarker.template.ObjectWrapper;
import freemarker.template.SimpleObjectWrapper;
import freemarker.template.Template;
import freemarker.template.TemplateException;
import freemarker.template.TemplateExceptionHandler;
import freemarker.template.TemplateMethodModel;
import freemarker.template.TemplateModel;
import freemarker.template.Version;
import freemarker.template._TemplateAPI;
import freemarker.template.utility.NullArgumentException;
import freemarker.template.utility.StringUtil;

This is a common superclass of Configuration, Template, and Environment classes. It provides settings that are common to each of them. FreeMarker uses a three-level setting hierarchy - the return value of every setting getter method on Configurable objects inherits its value from its parent Configurable object, unless explicitly overridden by a call to a corresponding setter method on the object itself. The parent of an Environment object is a Template object, the parent of a Template object is a Configuration object.
/** * This is a common superclass of {@link freemarker.template.Configuration}, * {@link freemarker.template.Template}, and {@link Environment} classes. * It provides settings that are common to each of them. FreeMarker * uses a three-level setting hierarchy - the return value of every setting * getter method on <code>Configurable</code> objects inherits its value from its parent * <code>Configurable</code> object, unless explicitly overridden by a call to a * corresponding setter method on the object itself. The parent of an * <code>Environment</code> object is a <code>Template</code> object, the * parent of a <code>Template</code> object is a <code>Configuration</code> * object. */
public class Configurable { static final String C_TRUE_FALSE = "true,false"; private static final String NULL = "null"; private static final String DEFAULT = "default"; private static final String DEFAULT_2_3_0 = "default_2_3_0"; private static final String JVM_DEFAULT = "JVM default";
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 LOCALE_KEY_SNAKE_CASE = "locale";
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 LOCALE_KEY_CAMEL_CASE = "locale";
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 LOCALE_KEY = LOCALE_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 NUMBER_FORMAT_KEY_SNAKE_CASE = "number_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 NUMBER_FORMAT_KEY_CAMEL_CASE = "numberFormat";
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 NUMBER_FORMAT_KEY = NUMBER_FORMAT_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 CUSTOM_NUMBER_FORMATS_KEY_SNAKE_CASE = "custom_number_formats";
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 CUSTOM_NUMBER_FORMATS_KEY_CAMEL_CASE = "customNumberFormats";
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 CUSTOM_NUMBER_FORMATS_KEY = CUSTOM_NUMBER_FORMATS_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 TIME_FORMAT_KEY_SNAKE_CASE = "time_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 TIME_FORMAT_KEY_CAMEL_CASE = "timeFormat";
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 TIME_FORMAT_KEY = TIME_FORMAT_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 DATE_FORMAT_KEY_SNAKE_CASE = "date_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 DATE_FORMAT_KEY_CAMEL_CASE = "dateFormat";
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 DATE_FORMAT_KEY = DATE_FORMAT_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 CUSTOM_DATE_FORMATS_KEY_SNAKE_CASE = "custom_date_formats";
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 CUSTOM_DATE_FORMATS_KEY_CAMEL_CASE = "customDateFormats";
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 CUSTOM_DATE_FORMATS_KEY = CUSTOM_DATE_FORMATS_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 DATETIME_FORMAT_KEY_SNAKE_CASE = "datetime_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 DATETIME_FORMAT_KEY_CAMEL_CASE = "datetimeFormat";
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 DATETIME_FORMAT_KEY = DATETIME_FORMAT_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 TIME_ZONE_KEY_SNAKE_CASE = "time_zone";
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 TIME_ZONE_KEY_CAMEL_CASE = "timeZone";
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 TIME_ZONE_KEY = TIME_ZONE_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 SQL_DATE_AND_TIME_TIME_ZONE_KEY_SNAKE_CASE = "sql_date_and_time_time_zone";
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 SQL_DATE_AND_TIME_TIME_ZONE_KEY_CAMEL_CASE = "sqlDateAndTimeTimeZone";
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 SQL_DATE_AND_TIME_TIME_ZONE_KEY = SQL_DATE_AND_TIME_TIME_ZONE_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 CLASSIC_COMPATIBLE_KEY_SNAKE_CASE = "classic_compatible";
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 CLASSIC_COMPATIBLE_KEY_CAMEL_CASE = "classicCompatible";
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 CLASSIC_COMPATIBLE_KEY = CLASSIC_COMPATIBLE_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_EXCEPTION_HANDLER_KEY_SNAKE_CASE = "template_exception_handler";
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_EXCEPTION_HANDLER_KEY_CAMEL_CASE = "templateExceptionHandler";
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_EXCEPTION_HANDLER_KEY = TEMPLATE_EXCEPTION_HANDLER_KEY_SNAKE_CASE;
Legacy, snake case (like_this) variation of the setting name. @since 2.3.27
/** Legacy, snake case ({@code like_this}) variation of the setting name. @since 2.3.27 */
public static final String ATTEMPT_EXCEPTION_REPORTER_KEY_SNAKE_CASE = "attempt_exception_reporter";
Modern, camel case (likeThis) variation of the setting name. @since 2.3.27
/** Modern, camel case ({@code likeThis}) variation of the setting name. @since 2.3.27 */
public static final String ATTEMPT_EXCEPTION_REPORTER_KEY_CAMEL_CASE = "attemptExceptionReporter";
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 ATTEMPT_EXCEPTION_REPORTER_KEY = ATTEMPT_EXCEPTION_REPORTER_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 ARITHMETIC_ENGINE_KEY_SNAKE_CASE = "arithmetic_engine";
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 ARITHMETIC_ENGINE_KEY_CAMEL_CASE = "arithmeticEngine";
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 ARITHMETIC_ENGINE_KEY = ARITHMETIC_ENGINE_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 OBJECT_WRAPPER_KEY_SNAKE_CASE = "object_wrapper";
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 OBJECT_WRAPPER_KEY_CAMEL_CASE = "objectWrapper";
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 OBJECT_WRAPPER_KEY = OBJECT_WRAPPER_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 BOOLEAN_FORMAT_KEY_SNAKE_CASE = "boolean_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 BOOLEAN_FORMAT_KEY_CAMEL_CASE = "booleanFormat";
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 BOOLEAN_FORMAT_KEY = BOOLEAN_FORMAT_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 OUTPUT_ENCODING_KEY_SNAKE_CASE = "output_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 OUTPUT_ENCODING_KEY_CAMEL_CASE = "outputEncoding";
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_ENCODING_KEY = OUTPUT_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 URL_ESCAPING_CHARSET_KEY_SNAKE_CASE = "url_escaping_charset";
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 URL_ESCAPING_CHARSET_KEY_CAMEL_CASE = "urlEscapingCharset";
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 URL_ESCAPING_CHARSET_KEY = URL_ESCAPING_CHARSET_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_BEAN_MODELS_KEY_SNAKE_CASE = "strict_bean_models";
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_BEAN_MODELS_KEY_CAMEL_CASE = "strictBeanModels";
Alias to the ..._SNAKE_CASE variation due to backward compatibility constraints. @since 2.3.22
/** Alias to the {@code ..._SNAKE_CASE} variation due to backward compatibility constraints. @since 2.3.22 */
public static final String STRICT_BEAN_MODELS_KEY = STRICT_BEAN_MODELS_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_FLUSH_KEY_SNAKE_CASE = "auto_flush";
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_FLUSH_KEY_CAMEL_CASE = "autoFlush";
Alias to the ..._SNAKE_CASE variation due to backward compatibility constraints. @since 2.3.17
/** Alias to the {@code ..._SNAKE_CASE} variation due to backward compatibility constraints. @since 2.3.17 */
public static final String AUTO_FLUSH_KEY = AUTO_FLUSH_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 NEW_BUILTIN_CLASS_RESOLVER_KEY_SNAKE_CASE = "new_builtin_class_resolver";
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 NEW_BUILTIN_CLASS_RESOLVER_KEY_CAMEL_CASE = "newBuiltinClassResolver";
Alias to the ..._SNAKE_CASE variation due to backward compatibility constraints. @since 2.3.17
/** Alias to the {@code ..._SNAKE_CASE} variation due to backward compatibility constraints. @since 2.3.17 */
public static final String NEW_BUILTIN_CLASS_RESOLVER_KEY = NEW_BUILTIN_CLASS_RESOLVER_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 SHOW_ERROR_TIPS_KEY_SNAKE_CASE = "show_error_tips";
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 SHOW_ERROR_TIPS_KEY_CAMEL_CASE = "showErrorTips";
Alias to the ..._SNAKE_CASE variation due to backward compatibility constraints. @since 2.3.21
/** Alias to the {@code ..._SNAKE_CASE} variation due to backward compatibility constraints. @since 2.3.21 */
public static final String SHOW_ERROR_TIPS_KEY = SHOW_ERROR_TIPS_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 API_BUILTIN_ENABLED_KEY_SNAKE_CASE = "api_builtin_enabled";
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 API_BUILTIN_ENABLED_KEY_CAMEL_CASE = "apiBuiltinEnabled";
Alias to the ..._SNAKE_CASE variation due to backward compatibility constraints. @since 2.3.22
/** Alias to the {@code ..._SNAKE_CASE} variation due to backward compatibility constraints. @since 2.3.22 */
public static final String API_BUILTIN_ENABLED_KEY = API_BUILTIN_ENABLED_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 LOG_TEMPLATE_EXCEPTIONS_KEY_SNAKE_CASE = "log_template_exceptions";
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 LOG_TEMPLATE_EXCEPTIONS_KEY_CAMEL_CASE = "logTemplateExceptions";
Alias to the ..._SNAKE_CASE variation due to backward compatibility constraints. @since 2.3.22
/** Alias to the {@code ..._SNAKE_CASE} variation due to backward compatibility constraints. @since 2.3.22 */
public static final String LOG_TEMPLATE_EXCEPTIONS_KEY = LOG_TEMPLATE_EXCEPTIONS_KEY_SNAKE_CASE;
Legacy, snake case (like_this) variation of the setting name. @since 2.3.27
/** Legacy, snake case ({@code like_this}) variation of the setting name. @since 2.3.27 */
public static final String WRAP_UNCHECKED_EXCEPTIONS_KEY_SNAKE_CASE = "wrap_unchecked_exceptions";
Modern, camel case (likeThis) variation of the setting name. @since 2.3.27
/** Modern, camel case ({@code likeThis}) variation of the setting name. @since 2.3.27 */
public static final String WRAP_UNCHECKED_EXCEPTIONS_KEY_CAMEL_CASE = "wrapUncheckedExceptions";
Alias to the ..._SNAKE_CASE variation due to backward compatibility constraints. @since 2.3.27
/** Alias to the {@code ..._SNAKE_CASE} variation due to backward compatibility constraints. @since 2.3.27 */
public static final String WRAP_UNCHECKED_EXCEPTIONS_KEY = WRAP_UNCHECKED_EXCEPTIONS_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 LAZY_IMPORTS_KEY_SNAKE_CASE = "lazy_imports";
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 LAZY_IMPORTS_KEY_CAMEL_CASE = "lazyImports";
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 LAZY_IMPORTS_KEY = LAZY_IMPORTS_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 LAZY_AUTO_IMPORTS_KEY_SNAKE_CASE = "lazy_auto_imports";
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 LAZY_AUTO_IMPORTS_KEY_CAMEL_CASE = "lazyAutoImports";
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 LAZY_AUTO_IMPORTS_KEY = LAZY_AUTO_IMPORTS_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 AUTO_IMPORT_KEY_SNAKE_CASE = "auto_import";
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 AUTO_IMPORT_KEY_CAMEL_CASE = "autoImport";
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_IMPORT_KEY = AUTO_IMPORT_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 AUTO_INCLUDE_KEY_SNAKE_CASE = "auto_include";
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 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;
Deprecated:Use STRICT_BEAN_MODELS_KEY instead.
/** @deprecated Use {@link #STRICT_BEAN_MODELS_KEY} instead. */
@Deprecated public static final String STRICT_BEAN_MODELS = STRICT_BEAN_MODELS_KEY; private static final String[] SETTING_NAMES_SNAKE_CASE = new String[] { // Must be sorted alphabetically! API_BUILTIN_ENABLED_KEY_SNAKE_CASE, ARITHMETIC_ENGINE_KEY_SNAKE_CASE, ATTEMPT_EXCEPTION_REPORTER_KEY_SNAKE_CASE, AUTO_FLUSH_KEY_SNAKE_CASE, AUTO_IMPORT_KEY_SNAKE_CASE, AUTO_INCLUDE_KEY_SNAKE_CASE, BOOLEAN_FORMAT_KEY_SNAKE_CASE, CLASSIC_COMPATIBLE_KEY_SNAKE_CASE, CUSTOM_DATE_FORMATS_KEY_SNAKE_CASE, CUSTOM_NUMBER_FORMATS_KEY_SNAKE_CASE, DATE_FORMAT_KEY_SNAKE_CASE, DATETIME_FORMAT_KEY_SNAKE_CASE, LAZY_AUTO_IMPORTS_KEY_SNAKE_CASE, LAZY_IMPORTS_KEY_SNAKE_CASE, LOCALE_KEY_SNAKE_CASE, LOG_TEMPLATE_EXCEPTIONS_KEY_SNAKE_CASE, NEW_BUILTIN_CLASS_RESOLVER_KEY_SNAKE_CASE, NUMBER_FORMAT_KEY_SNAKE_CASE, OBJECT_WRAPPER_KEY_SNAKE_CASE, OUTPUT_ENCODING_KEY_SNAKE_CASE, SHOW_ERROR_TIPS_KEY_SNAKE_CASE, SQL_DATE_AND_TIME_TIME_ZONE_KEY_SNAKE_CASE, STRICT_BEAN_MODELS_KEY, TEMPLATE_EXCEPTION_HANDLER_KEY_SNAKE_CASE, TIME_FORMAT_KEY_SNAKE_CASE, TIME_ZONE_KEY_SNAKE_CASE, URL_ESCAPING_CHARSET_KEY_SNAKE_CASE, WRAP_UNCHECKED_EXCEPTIONS_KEY_SNAKE_CASE }; private static final String[] SETTING_NAMES_CAMEL_CASE = new String[] { // Must be sorted alphabetically! API_BUILTIN_ENABLED_KEY_CAMEL_CASE, ARITHMETIC_ENGINE_KEY_CAMEL_CASE, ATTEMPT_EXCEPTION_REPORTER_KEY_CAMEL_CASE, AUTO_FLUSH_KEY_CAMEL_CASE, AUTO_IMPORT_KEY_CAMEL_CASE, AUTO_INCLUDE_KEY_CAMEL_CASE, BOOLEAN_FORMAT_KEY_CAMEL_CASE, CLASSIC_COMPATIBLE_KEY_CAMEL_CASE, CUSTOM_DATE_FORMATS_KEY_CAMEL_CASE, CUSTOM_NUMBER_FORMATS_KEY_CAMEL_CASE, DATE_FORMAT_KEY_CAMEL_CASE, DATETIME_FORMAT_KEY_CAMEL_CASE, LAZY_AUTO_IMPORTS_KEY_CAMEL_CASE, LAZY_IMPORTS_KEY_CAMEL_CASE, LOCALE_KEY_CAMEL_CASE, LOG_TEMPLATE_EXCEPTIONS_KEY_CAMEL_CASE, NEW_BUILTIN_CLASS_RESOLVER_KEY_CAMEL_CASE, NUMBER_FORMAT_KEY_CAMEL_CASE, OBJECT_WRAPPER_KEY_CAMEL_CASE, OUTPUT_ENCODING_KEY_CAMEL_CASE, SHOW_ERROR_TIPS_KEY_CAMEL_CASE, SQL_DATE_AND_TIME_TIME_ZONE_KEY_CAMEL_CASE, STRICT_BEAN_MODELS_KEY_CAMEL_CASE, TEMPLATE_EXCEPTION_HANDLER_KEY_CAMEL_CASE, TIME_FORMAT_KEY_CAMEL_CASE, TIME_ZONE_KEY_CAMEL_CASE, URL_ESCAPING_CHARSET_KEY_CAMEL_CASE, WRAP_UNCHECKED_EXCEPTIONS_KEY_CAMEL_CASE }; private Configurable parent; private Properties properties; private HashMap<Object, Object> customAttributes; private Locale locale; private String numberFormat; private String timeFormat; private String dateFormat; private String dateTimeFormat; private TimeZone timeZone; private TimeZone sqlDataAndTimeTimeZone; private boolean sqlDataAndTimeTimeZoneSet; private String booleanFormat; private String trueStringValue; // deduced from booleanFormat private String falseStringValue; // deduced from booleanFormat private Integer classicCompatible; private TemplateExceptionHandler templateExceptionHandler; private AttemptExceptionReporter attemptExceptionReporter; private ArithmeticEngine arithmeticEngine; private ObjectWrapper objectWrapper; private String outputEncoding; private boolean outputEncodingSet; private String urlEscapingCharset; private boolean urlEscapingCharsetSet; private Boolean autoFlush; private TemplateClassResolver newBuiltinClassResolver; private Boolean showErrorTips; private Boolean apiBuiltinEnabled; private Boolean logTemplateExceptions; private Boolean wrapUncheckedExceptions; private Map<String, ? extends TemplateDateFormatFactory> customDateFormats; private Map<String, ? extends TemplateNumberFormatFactory> customNumberFormats; private LinkedHashMap<String, String> autoImports; private ArrayList<String> autoIncludes; private Boolean lazyImports; private Boolean lazyAutoImports; private boolean lazyAutoImportsSet;
Creates a top-level configurable, one that doesn't inherit from a parent, and thus stores the default values.
Deprecated:This shouldn't even be public; don't use it.
/** * Creates a top-level configurable, one that doesn't inherit from a parent, and thus stores the default values. * * @deprecated This shouldn't even be public; don't use it. */
@Deprecated public Configurable() { this(Configuration.DEFAULT_INCOMPATIBLE_IMPROVEMENTS); }
Intended to be called from inside FreeMarker only. Creates a top-level configurable, one that doesn't inherit from a parent, and thus stores the default values. Called by the Configuration constructor.
/** * Intended to be called from inside FreeMarker only. * Creates a top-level configurable, one that doesn't inherit from a parent, and thus stores the default values. * Called by the {@link Configuration} constructor. */
protected Configurable(Version incompatibleImprovements) { _TemplateAPI.checkVersionNotNullAndSupported(incompatibleImprovements); parent = null; properties = new Properties(); locale = _TemplateAPI.getDefaultLocale(); properties.setProperty(LOCALE_KEY, locale.toString()); timeZone = _TemplateAPI.getDefaultTimeZone(); properties.setProperty(TIME_ZONE_KEY, timeZone.getID()); sqlDataAndTimeTimeZone = null; properties.setProperty(SQL_DATE_AND_TIME_TIME_ZONE_KEY, String.valueOf(sqlDataAndTimeTimeZone)); numberFormat = "number"; properties.setProperty(NUMBER_FORMAT_KEY, numberFormat); timeFormat = ""; properties.setProperty(TIME_FORMAT_KEY, timeFormat); dateFormat = ""; properties.setProperty(DATE_FORMAT_KEY, dateFormat); dateTimeFormat = ""; properties.setProperty(DATETIME_FORMAT_KEY, dateTimeFormat); classicCompatible = Integer.valueOf(0); properties.setProperty(CLASSIC_COMPATIBLE_KEY, classicCompatible.toString()); templateExceptionHandler = _TemplateAPI.getDefaultTemplateExceptionHandler(incompatibleImprovements); properties.setProperty(TEMPLATE_EXCEPTION_HANDLER_KEY, templateExceptionHandler.getClass().getName()); wrapUncheckedExceptions = _TemplateAPI.getDefaultWrapUncheckedExceptions(incompatibleImprovements); attemptExceptionReporter = _TemplateAPI.getDefaultAttemptExceptionReporter(incompatibleImprovements); arithmeticEngine = ArithmeticEngine.BIGDECIMAL_ENGINE; properties.setProperty(ARITHMETIC_ENGINE_KEY, arithmeticEngine.getClass().getName()); objectWrapper = Configuration.getDefaultObjectWrapper(incompatibleImprovements); // bug: setProperty missing autoFlush = Boolean.TRUE; properties.setProperty(AUTO_FLUSH_KEY, autoFlush.toString()); newBuiltinClassResolver = TemplateClassResolver.UNRESTRICTED_RESOLVER; properties.setProperty(NEW_BUILTIN_CLASS_RESOLVER_KEY, newBuiltinClassResolver.getClass().getName()); showErrorTips = Boolean.TRUE; properties.setProperty(SHOW_ERROR_TIPS_KEY, showErrorTips.toString()); apiBuiltinEnabled = Boolean.FALSE; properties.setProperty(API_BUILTIN_ENABLED_KEY, apiBuiltinEnabled.toString()); logTemplateExceptions = Boolean.valueOf( _TemplateAPI.getDefaultLogTemplateExceptions(incompatibleImprovements)); properties.setProperty(LOG_TEMPLATE_EXCEPTIONS_KEY, logTemplateExceptions.toString()); // outputEncoding and urlEscapingCharset defaults to null, // which means "not specified" setBooleanFormat(C_TRUE_FALSE); customAttributes = new HashMap(); customDateFormats = Collections.emptyMap(); customNumberFormats = Collections.emptyMap(); lazyImports = false; lazyAutoImportsSet = true; initAutoImportsMap(); initAutoIncludesList(); }
Creates a new instance. Normally you do not need to use this constructor, as you don't use Configurable directly, but its subclasses.
/** * Creates a new instance. Normally you do not need to use this constructor, * as you don't use <code>Configurable</code> directly, but its subclasses. */
public Configurable(Configurable parent) { this.parent = parent; properties = new Properties(parent.properties); customAttributes = new HashMap<Object, Object>(0); } @Override protected Object clone() throws CloneNotSupportedException { Configurable copy = (Configurable) super.clone(); if (properties != null) { copy.properties = new Properties(properties); } if (customAttributes != null) { copy.customAttributes = (HashMap) customAttributes.clone(); } if (autoImports != null) { copy.autoImports = (LinkedHashMap<String, String>) autoImports.clone(); } if (autoIncludes != null) { copy.autoIncludes = (ArrayList<String>) autoIncludes.clone(); } return copy; }
Returns the parent Configurable object of this object. The parent stores the default setting values for this Configurable. For example, the parent of a Template object is a Configuration object, so values not specified on Template-level are get from the Configuration object.

Note on the parent of Environment: If you set incompatible_improvements to at least 2.3.22, it will be always the "main" Template, that is, the template for whose processing the Environment was created. With lower incompatible_improvements, the current parent can temporary change during template execution, for example when your are inside an #include-d template (among others). Thus, don't build on which Template the parent of Environment is during template execution, unless you set incompatible_improvements to 2.3.22 or higher.

Returns:The parent Configurable object, or null if this is the root Configurable object (i.e, if it's the Configuration object).
/** * Returns the parent {@link Configurable} object of this object. The parent stores the default setting values for * this {@link Configurable}. For example, the parent of a {@link freemarker.template.Template} object is a * {@link Configuration} object, so values not specified on {@link Template}-level are get from the * {@link Configuration} object. * * <p> * Note on the parent of {@link Environment}: If you set {@link Configuration#setIncompatibleImprovements(Version) * incompatible_improvements} to at least 2.3.22, it will be always the "main" {@link Template}, that is, the * template for whose processing the {@link Environment} was created. With lower {@code incompatible_improvements}, * the current parent can temporary change <em>during template execution</em>, for example when your are inside an * {@code #include}-d template (among others). Thus, don't build on which {@link Template} the parent of * {@link Environment} is during template execution, unless you set {@code incompatible_improvements} to 2.3.22 or * higher. * * @return The parent {@link Configurable} object, or {@code null} if this is the root {@link Configurable} object * (i.e, if it's the {@link Configuration} object). */
public final Configurable getParent() { return parent; }
Reparenting support. This is used by Environment when it includes a template - the included template becomes the parent configurable during its evaluation.
/** * Reparenting support. This is used by Environment when it includes a * template - the included template becomes the parent configurable during * its evaluation. */
void setParent(Configurable parent) { this.parent = parent; }
Toggles the "Classic Compatible" mode. For a comprehensive description of this mode, see isClassicCompatible().
/** * Toggles the "Classic Compatible" mode. For a comprehensive description * of this mode, see {@link #isClassicCompatible()}. */
public void setClassicCompatible(boolean classicCompatibility) { this.classicCompatible = Integer.valueOf(classicCompatibility ? 1 : 0); properties.setProperty(CLASSIC_COMPATIBLE_KEY, classicCompatibilityIntToString(classicCompatible)); }
Same as setClassicCompatible(boolean), but allows some extra values.
Params:
  • classicCompatibility – 0 means false, 1 means true, 2 means true but with emulating bugs in early 2.x classic-compatibility mode. Currently 2 affects how booleans are converted to string; with 1 it's always "true"/"", but with 2 it's "true"/"false" for values wrapped by BeansWrapper as then Boolean.toString() prevails. Note that someBoolean?string will always consistently format the boolean according the boolean_format setting, just like in FreeMarker 2.3 and later.
/** * Same as {@link #setClassicCompatible(boolean)}, but allows some extra values. * * @param classicCompatibility {@code 0} means {@code false}, {@code 1} means {@code true}, * {@code 2} means {@code true} but with emulating bugs in early 2.x classic-compatibility mode. Currently * {@code 2} affects how booleans are converted to string; with {@code 1} it's always {@code "true"}/{@code ""}, * but with {@code 2} it's {@code "true"}/{@code "false"} for values wrapped by {@link BeansWrapper} as then * {@link Boolean#toString()} prevails. Note that {@code someBoolean?string} will always consistently format the * boolean according the {@code boolean_format} setting, just like in FreeMarker 2.3 and later. */
public void setClassicCompatibleAsInt(int classicCompatibility) { if (classicCompatibility < 0 || classicCompatibility > 2) { throw new IllegalArgumentException("Unsupported \"classicCompatibility\": " + classicCompatibility); } this.classicCompatible = Integer.valueOf(classicCompatibility); } private String classicCompatibilityIntToString(Integer i) { if (i == null) return null; else if (i.intValue() == 0) return MiscUtil.C_FALSE; else if (i.intValue() == 1) return MiscUtil.C_TRUE; else return i.toString(); }
Returns whether the engine runs in the "Classic Compatibile" mode. When this mode is active, the engine behavior is altered in following way: (these resemble the behavior of the 1.7.x line of FreeMarker engine, now named "FreeMarker Classic", hence the name).
  • handle undefined expressions gracefully. Namely when an expression "expr" evaluates to null:
    • in <assign varname=expr> directive, or in ${expr} directive, or in otherexpr == expr, or in otherexpr != expr, or in hash[expr], or in expr[keyOrIndex] (since 2.3.20), or in expr.key (since 2.3.20), then it's treated as empty string.
    • as argument of <list expr as item> or <foreach item in expr>, the loop body is not executed (as if it were a 0-length list)
    • as argument of <if> directive, or on other places where a boolean expression is expected, it's treated as false
  • Non-boolean models are accepted in <if> directive, or as operands of logical operators. "Empty" models (zero-length string, empty sequence or hash) are evaluated as false, all others are evaluated as true.
  • When boolean value is treated as a string (i.e. output in ${...} directive, or concatenated with other string), true values are converted to string "true", false values are converted to empty string. Except, if the value of the setting is 2, it will be formatted according the boolean_format setting, just like in 2.3.20 and later.
  • Scalar models supplied to <list> and <foreach> are treated as a one-element list consisting of the passed model.
  • Paths parameter of <include> will be interpreted as absolute path.
In all other aspects, the engine is a 2.1 engine even in compatibility mode - you don't lose any of the new functionality by enabling it.
/** * Returns whether the engine runs in the "Classic Compatibile" mode. * When this mode is active, the engine behavior is altered in following * way: (these resemble the behavior of the 1.7.x line of FreeMarker engine, * now named "FreeMarker Classic", hence the name). * <ul> * <li>handle undefined expressions gracefully. Namely when an expression * "expr" evaluates to null: * <ul> * <li> * in <tt>&lt;assign varname=expr&gt;</tt> directive, * or in <tt>${expr}</tt> directive, * or in <tt>otherexpr == expr</tt>, * or in <tt>otherexpr != expr</tt>, * or in <tt>hash[expr]</tt>, * or in <tt>expr[keyOrIndex]</tt> (since 2.3.20), * or in <tt>expr.key</tt> (since 2.3.20), * then it's treated as empty string. * </li> * <li>as argument of <tt>&lt;list expr as item&gt;</tt> or * <tt>&lt;foreach item in expr&gt;</tt>, the loop body is not executed * (as if it were a 0-length list) * </li> * <li>as argument of <tt>&lt;if&gt;</tt> directive, or on other places where a * boolean expression is expected, it's treated as false * </li> * </ul> * </li> * <li>Non-boolean models are accepted in <tt>&lt;if&gt;</tt> directive, * or as operands of logical operators. "Empty" models (zero-length string, * empty sequence or hash) are evaluated as false, all others are evaluated as * true.</li> * <li>When boolean value is treated as a string (i.e. output in * <tt>${...}</tt> directive, or concatenated with other string), true * values are converted to string "true", false values are converted to * empty string. Except, if the value of the setting is <tt>2</tt>, it will be * formatted according the <tt>boolean_format</tt> setting, just like in * 2.3.20 and later. * </li> * <li>Scalar models supplied to <tt>&lt;list&gt;</tt> and * <tt>&lt;foreach&gt;</tt> are treated as a one-element list consisting * of the passed model. * </li> * <li>Paths parameter of <tt>&lt;include&gt;</tt> will be interpreted as * absolute path. * </li> * </ul> * In all other aspects, the engine is a 2.1 engine even in compatibility * mode - you don't lose any of the new functionality by enabling it. */
public boolean isClassicCompatible() { return classicCompatible != null ? classicCompatible.intValue() != 0 : parent.isClassicCompatible(); } public int getClassicCompatibleAsInt() { return classicCompatible != null ? classicCompatible.intValue() : parent.getClassicCompatibleAsInt(); }
Tells if this setting is set directly in this object or its value is coming from the parent.
Since:2.3.24
/** * Tells if this setting is set directly in this object or its value is coming from the {@link #getParent() parent}. * * @since 2.3.24 */
public boolean isClassicCompatibleSet() { return classicCompatible != null; }
Sets the locale used for number and date formatting (among others), also the locale used for searching localized template variations when no locale was explicitly requested. On the Configuration level it defaults to the default locale of system (of the JVM), for server-side application usually you should set it explicitly in the Configuration to use the preferred locale of your application instead.
See Also:
/** * Sets the locale used for number and date formatting (among others), also the locale used for searching * localized template variations when no locale was explicitly requested. On the {@link Configuration} level it * defaults to the default locale of system (of the JVM), for server-side application usually you should set it * explicitly in the {@link Configuration} to use the preferred locale of your application instead. * * @see Configuration#getTemplate(String, Locale) */
public void setLocale(Locale locale) { NullArgumentException.check("locale", locale); this.locale = locale; properties.setProperty(LOCALE_KEY, locale.toString()); }
Getter pair of setLocale(Locale). Not null.
/** * Getter pair of {@link #setLocale(Locale)}. Not {@code null}. */
public Locale getLocale() { return locale != null ? locale : parent.getLocale(); }
Tells if this setting is set directly in this object or its value is coming from the parent.
Since:2.3.24
/** * Tells if this setting is set directly in this object or its value is coming from the {@link #getParent() parent}. * * @since 2.3.24 */
public boolean isLocaleSet() { return locale != null; }
Sets the time zone to use when formatting date/time values. Defaults to the system time zone (TimeZone.getDefault()), regardless of the "locale" FreeMarker setting, so in a server application you probably want to set it explicitly in the Environment to match the preferred time zone of target audience (like the Web page visitor).

If you or the templates set the time zone, you should probably also set setSQLDateAndTimeTimeZone(TimeZone)!

See Also:
/** * Sets the time zone to use when formatting date/time values. * Defaults to the system time zone ({@link TimeZone#getDefault()}), regardless of the "locale" FreeMarker setting, * so in a server application you probably want to set it explicitly in the {@link Environment} to match the * preferred time zone of target audience (like the Web page visitor). * * <p>If you or the templates set the time zone, you should probably also set * {@link #setSQLDateAndTimeTimeZone(TimeZone)}! * * @see #setSQLDateAndTimeTimeZone(TimeZone) */
public void setTimeZone(TimeZone timeZone) { NullArgumentException.check("timeZone", timeZone); this.timeZone = timeZone; properties.setProperty(TIME_ZONE_KEY, timeZone.getID()); }
The getter pair of setTimeZone(TimeZone).
/** * The getter pair of {@link #setTimeZone(TimeZone)}. */
public TimeZone getTimeZone() { return timeZone != null ? timeZone : parent.getTimeZone(); }
Tells if this setting is set directly in this object or its value is coming from the parent.
Since:2.3.24
/** * Tells if this setting is set directly in this object or its value is coming from the {@link #getParent() parent}. * * @since 2.3.24 */
public boolean isTimeZoneSet() { return timeZone != null; }
Sets the time zone used when dealing with java.sql.Date and java.sql.Time values. It defaults to null for backward compatibility, but in most applications this should be set to the JVM default time zone (server default time zone), because that's what most JDBC drivers will use when constructing the java.sql.Date and java.sql.Time values. If this setting is null, FreeMarker will use the value of (getTimeZone()) for java.sql.Date and java.sql.Time values, which often gives bad results.

This setting doesn't influence the formatting of other kind of values (like of java.sql.Timestamp or plain java.util.Date values).

To decide what value you need, a few things has to be understood:

  • Date-only and time-only values in SQL-oriented databases usually store calendar and clock field values directly (year, month, day, or hour, minute, seconds (with decimals)), as opposed to a set of points on the physical time line. Thus, unlike SQL timestamps, these values usually aren't meant to be shown differently depending on the time zone of the audience.
  • When a JDBC query has to return a date-only or time-only value, it has to convert it to a point on the physical time line, because that's what Date and its subclasses store (milliseconds since the epoch). Obviously, this is impossible to do. So JDBC just chooses a physical time which, when rendered with the JVM default time zone, will give the same field values as those stored in the database. (Actually, you can give JDBC a calendar, and so it can use other time zones too, but most application won't care using those overloads.) For example, assume that the system time zone is GMT+02:00. Then, 2014-07-12 in the database will be translated to physical time 2014-07-11 22:00:00 UTC, because that rendered in GMT+02:00 gives 2014-07-12 00:00:00. Similarly, 11:57:00 in the database will be translated to physical time 1970-01-01 09:57:00 UTC. Thus, the physical time stored in the returned value depends on the default system time zone of the JDBC client, not just on the content of the database. (This used to be the default behavior of ORM-s, like Hibernate, too.)
  • The value of the time_zone FreeMarker configuration setting sets the time zone used for the template output. For example, when a web page visitor has a preferred time zone, the web application framework may calls Environment.setTimeZone(TimeZone) with that time zone. Thus, the visitor will see java.sql.Timestamp and plain java.util.Date values as they look in his own time zone. While this is desirable for those types, as they meant to represent physical points on the time line, this is not necessarily desirable for date-only and time-only values. When sql_date_and_time_time_zone is null, time_zone is used for rendering all kind of date/time/dateTime values, including java.sql.Date and java.sql.Time, and then if, for example, time_zone is GMT+00:00, the values from the earlier examples will be shown as 2014-07-11 (one day off) and 09:57:00 (2 hours off). While those are the time zone correct renderings, those values are probably meant to be shown "as is".
  • You may wonder why this setting isn't simply "SQL time zone", that is, why's this time zone not applied to java.sql.Timestamp values as well. Timestamps in databases refer to a point on the physical time line, and thus doesn't have the inherent problem of date-only and time-only values. FreeMarker assumes that the JDBC driver converts time stamps coming from the database so that they store the distance from the epoch (1970-01-01 00:00:00 UTC), as requested by the Date API. Then time stamps can be safely rendered in different time zones, and thus need no special treatment.
Params:
See Also:
Since:2.3.21
/** * Sets the time zone used when dealing with {@link java.sql.Date java.sql.Date} and * {@link java.sql.Time java.sql.Time} values. It defaults to {@code null} for backward compatibility, but in most * applications this should be set to the JVM default time zone (server default time zone), because that's what * most JDBC drivers will use when constructing the {@link java.sql.Date java.sql.Date} and * {@link java.sql.Time java.sql.Time} values. If this setting is {@code null}, FreeMarker will use the value of * ({@link #getTimeZone()}) for {@link java.sql.Date java.sql.Date} and {@link java.sql.Time java.sql.Time} values, * which often gives bad results. * * <p>This setting doesn't influence the formatting of other kind of values (like of * {@link java.sql.Timestamp java.sql.Timestamp} or plain {@link java.util.Date java.util.Date} values). * * <p>To decide what value you need, a few things has to be understood: * <ul> * <li>Date-only and time-only values in SQL-oriented databases usually store calendar and clock field * values directly (year, month, day, or hour, minute, seconds (with decimals)), as opposed to a set of points * on the physical time line. Thus, unlike SQL timestamps, these values usually aren't meant to be shown * differently depending on the time zone of the audience. * * <li>When a JDBC query has to return a date-only or time-only value, it has to convert it to a point on the * physical time line, because that's what {@link java.util.Date} and its subclasses store (milliseconds since * the epoch). Obviously, this is impossible to do. So JDBC just chooses a physical time which, when rendered * <em>with the JVM default time zone</em>, will give the same field values as those stored * in the database. (Actually, you can give JDBC a calendar, and so it can use other time zones too, but most * application won't care using those overloads.) For example, assume that the system time zone is GMT+02:00. * Then, 2014-07-12 in the database will be translated to physical time 2014-07-11 22:00:00 UTC, because that * rendered in GMT+02:00 gives 2014-07-12 00:00:00. Similarly, 11:57:00 in the database will be translated to * physical time 1970-01-01 09:57:00 UTC. Thus, the physical time stored in the returned value depends on the * default system time zone of the JDBC client, not just on the content of the database. (This used to be the * default behavior of ORM-s, like Hibernate, too.) * * <li>The value of the {@code time_zone} FreeMarker configuration setting sets the time zone used for the * template output. For example, when a web page visitor has a preferred time zone, the web application framework * may calls {@link Environment#setTimeZone(TimeZone)} with that time zone. Thus, the visitor will * see {@link java.sql.Timestamp java.sql.Timestamp} and plain {@link java.util.Date java.util.Date} values as * they look in his own time zone. While * this is desirable for those types, as they meant to represent physical points on the time line, this is not * necessarily desirable for date-only and time-only values. When {@code sql_date_and_time_time_zone} is * {@code null}, {@code time_zone} is used for rendering all kind of date/time/dateTime values, including * {@link java.sql.Date java.sql.Date} and {@link java.sql.Time java.sql.Time}, and then if, for example, * {@code time_zone} is GMT+00:00, the * values from the earlier examples will be shown as 2014-07-11 (one day off) and 09:57:00 (2 hours off). While * those are the time zone correct renderings, those values are probably meant to be shown "as is". * * <li>You may wonder why this setting isn't simply "SQL time zone", that is, why's this time zone not applied to * {@link java.sql.Timestamp java.sql.Timestamp} values as well. Timestamps in databases refer to a point on * the physical time line, and thus doesn't have the inherent problem of date-only and time-only values. * FreeMarker assumes that the JDBC driver converts time stamps coming from the database so that they store * the distance from the epoch (1970-01-01 00:00:00 UTC), as requested by the {@link java.util.Date} API. * Then time stamps can be safely rendered in different time zones, and thus need no special treatment. * </ul> * * @param tz Maybe {@code null}, in which case {@link java.sql.Date java.sql.Date} and * {@link java.sql.Time java.sql.Time} values will be formatted in the time zone returned by * {@link #getTimeZone()}. * (Note that since {@code null} is an allowed value for this setting, it will not cause * {@link #getSQLDateAndTimeTimeZone()} to fall back to the parent configuration.) * * @see #setTimeZone(TimeZone) * * @since 2.3.21 */
public void setSQLDateAndTimeTimeZone(TimeZone tz) { sqlDataAndTimeTimeZone = tz; sqlDataAndTimeTimeZoneSet = true; properties.setProperty(SQL_DATE_AND_TIME_TIME_ZONE_KEY, tz != null ? tz.getID() : "null"); }
Returns:null if the value of getTimeZone() should be used for formatting java.sql.Date and java.sql.Time values, otherwise the time zone that should be used to format the values of those two types.
Since:2.3.21
/** * The getter pair of {@link #setSQLDateAndTimeTimeZone(TimeZone)}. * * @return {@code null} if the value of {@link #getTimeZone()} should be used for formatting * {@link java.sql.Date java.sql.Date} and {@link java.sql.Time java.sql.Time} values, otherwise the time zone * that should be used to format the values of those two types. * * @since 2.3.21 */
public TimeZone getSQLDateAndTimeTimeZone() { return sqlDataAndTimeTimeZoneSet ? sqlDataAndTimeTimeZone : (parent != null ? parent.getSQLDateAndTimeTimeZone() : null); }
Tells if this setting is set directly in this object or its value is coming from the parent.
Since:2.3.24
/** * Tells if this setting is set directly in this object or its value is coming from the {@link #getParent() parent}. * * @since 2.3.24 */
public boolean isSQLDateAndTimeTimeZoneSet() { return sqlDataAndTimeTimeZoneSet; }
Sets the default number format used to convert numbers to strings. Currently, this is one of these:

Defaults to "number".

/** * Sets the default number format used to convert numbers to strings. Currently, this is one of these: * <ul> * <li>{@code "number"}: The number format returned by {@link NumberFormat#getNumberInstance(Locale)}</li> * <li>{@code "currency"}: The number format returned by {@link NumberFormat#getCurrencyInstance(Locale)}</li> * <li>{@code "percent"}: The number format returned by {@link NumberFormat#getPercentInstance(Locale)}</li> * <li>{@code "computer"}: The number format used by FTL's {@code c} built-in (like in {@code someNumber?c}).</li> * <li>{@link java.text.DecimalFormat} pattern (like {@code "0.##"}). This syntax is extended by FreeMarker * so that you can specify options like the rounding mode and the symbols used after a 2nd semicolon. For * example, {@code ",000;; roundingMode=halfUp groupingSeparator=_"} will format numbers like {@code ",000"} * would, but with half-up rounding mode, and {@code _} as the group separator. See more about "extended Java * decimal format" in the FreeMarker Manual. * </li> * <li>If the string starts with {@code @} character followed by a letter then it's interpreted as a custom number * format, but only if either {@link Configuration#getIncompatibleImprovements()} is at least 2.3.24, or * there's any custom formats defined (even if custom date/time/dateTime format). The format of a such string * is <code>"@<i>name</i>"</code> or <code>"@<i>name</i> <i>parameters</i>"</code>, where * <code><i>name</i></code> is the key in the {@link Map} set by {@link #setCustomNumberFormats(Map)}, and * <code><i>parameters</i></code> is parsed by the custom {@link TemplateNumberFormat}. * </li> * </ul> * * * <p>Defaults to <tt>"number"</tt>. */
public void setNumberFormat(String numberFormat) { NullArgumentException.check("numberFormat", numberFormat); this.numberFormat = numberFormat; properties.setProperty(NUMBER_FORMAT_KEY, numberFormat); }
Getter pair of setNumberFormat(String).
/** * Getter pair of {@link #setNumberFormat(String)}. */
public String getNumberFormat() { return numberFormat != null ? numberFormat : parent.getNumberFormat(); }
Tells if this setting is set directly in this object or its value is coming from the parent.
Since:2.3.24
/** * Tells if this setting is set directly in this object or its value is coming from the {@link #getParent() parent}. * * @since 2.3.24 */
public boolean isNumberFormatSet() { return numberFormat != null; }
Getter pair of setCustomNumberFormats(Map<String,? extends TemplateNumberFormatFactory>); do not modify the returned Map! To be consistent with other setting getters, if this setting was set directly on this Configurable object, this simply returns that value, otherwise it returns the value from the parent Configurable. So beware, the returned value doesn't reflect the Map key granularity fallback logic that FreeMarker actually uses for this setting (for that, use getCustomNumberFormat(String)). The returned value isn't a snapshot; it may or may not shows the changes later made to this setting on this Configurable level (but usually it's well defined if until what point settings are possibly modified).

The return value is never null; called on the Configuration (top) level, it defaults to an empty Map.

See Also:
Since:2.3.24
/** * Getter pair of {@link #setCustomNumberFormats(Map)}; do not modify the returned {@link Map}! To be consistent * with other setting getters, if this setting was set directly on this {@link Configurable} object, this simply * returns that value, otherwise it returns the value from the parent {@link Configurable}. So beware, the returned * value doesn't reflect the {@link Map} key granularity fallback logic that FreeMarker actually uses for this * setting (for that, use {@link #getCustomNumberFormat(String)}). The returned value isn't a snapshot; it may or * may not shows the changes later made to this setting on this {@link Configurable} level (but usually it's well * defined if until what point settings are possibly modified). * * <p> * The return value is never {@code null}; called on the {@link Configuration} (top) level, it defaults to an empty * {@link Map}. * * @see #getCustomNumberFormatsWithoutFallback() * * @since 2.3.24 */
public Map<String, ? extends TemplateNumberFormatFactory> getCustomNumberFormats() { return customNumberFormats == null ? parent.getCustomNumberFormats() : customNumberFormats; }
Like getCustomNumberFormats(), but doesn't fall back to the parent Configurable.
Since:2.3.25
/** * Like {@link #getCustomNumberFormats()}, but doesn't fall back to the parent {@link Configurable}. * * @since 2.3.25 */
public Map<String, ? extends TemplateNumberFormatFactory> getCustomNumberFormatsWithoutFallback() { return customNumberFormats; }
Associates names with formatter factories, which then can be referred by the number_format setting with values starting with @name. Beware, if you specify any custom formats here, an initial @ followed by a letter will have special meaning in number/date/time/datetime format strings, even if incompatible_improvements is less than 2.3.24 (starting with incompatible_improvements 2.3.24 @ always has special meaning).
Params:
  • customNumberFormats – Can't be null. The name must start with an UNICODE letter, and can only contain UNICODE letters and digits (not _).
Since:2.3.24
/** * Associates names with formatter factories, which then can be referred by the {@link #setNumberFormat(String) * number_format} setting with values starting with <code>@<i>name</i></code>. Beware, if you specify any custom * formats here, an initial {@code @} followed by a letter will have special meaning in number/date/time/datetime * format strings, even if {@link Configuration#getIncompatibleImprovements() incompatible_improvements} is less * than 2.3.24 (starting with {@link Configuration#getIncompatibleImprovements() incompatible_improvements} 2.3.24 * {@code @} always has special meaning). * * @param customNumberFormats * Can't be {@code null}. The name must start with an UNICODE letter, and can only contain UNICODE * letters and digits (not {@code _}). * * @since 2.3.24 */
public void setCustomNumberFormats(Map<String, ? extends TemplateNumberFormatFactory> customNumberFormats) { NullArgumentException.check("customNumberFormats", customNumberFormats); validateFormatNames(customNumberFormats.keySet()); this.customNumberFormats = customNumberFormats; } private void validateFormatNames(Set<String> keySet) { for (String name : keySet) { if (name.length() == 0) { throw new IllegalArgumentException("Format names can't be 0 length"); } char firstChar = name.charAt(0); if (firstChar == '@') { throw new IllegalArgumentException( "Format names can't start with '@'. '@' is only used when referring to them from format " + "strings. In: " + name); } if (!Character.isLetter(firstChar)) { throw new IllegalArgumentException("Format name must start with letter: " + name); } for (int i = 1; i < name.length(); i++) { // Note that we deliberately don't allow "_" here. if (!Character.isLetterOrDigit(name.charAt(i))) { throw new IllegalArgumentException("Format name can only contain letters and digits: " + name); } } } }
Tells if this setting is set directly in this object or its value is coming from the parent.
Since:2.3.24
/** * Tells if this setting is set directly in this object or its value is coming from the {@link #getParent() parent}. * * @since 2.3.24 */
public boolean isCustomNumberFormatsSet() { return customNumberFormats != null; }
Gets the custom name format registered for the name.
Since:2.3.24
/** * Gets the custom name format registered for the name. * * @since 2.3.24 */
public TemplateNumberFormatFactory getCustomNumberFormat(String name) { TemplateNumberFormatFactory r; if (customNumberFormats != null) { r = customNumberFormats.get(name); if (r != null) { return r; } } return parent != null ? parent.getCustomNumberFormat(name) : null; }
Tells if this configurable object or its parent defines any custom formats.
Since:2.3.24
/** * Tells if this configurable object or its parent defines any custom formats. * * @since 2.3.24 */
public boolean hasCustomFormats() { return customNumberFormats != null && !customNumberFormats.isEmpty() || customDateFormats != null && !customDateFormats.isEmpty() || getParent() != null && getParent().hasCustomFormats(); }
The string value for the boolean true and false values, intended for human audience (not for a computer language), separated with comma. For example, "yes,no". Note that white-space is significant, so "yes, no" is WRONG (unless you want that leading space before "no").

For backward compatibility the default is "true,false", but using that value is denied for automatic boolean-to-string conversion (like ${myBoolean} will fail with it), only myBool?string will allow it, which is deprecated since FreeMarker 2.3.20.

Note that automatic boolean-to-string conversion only exists since FreeMarker 2.3.20. Earlier this setting only influenced the result of myBool?string.

/** * The string value for the boolean {@code true} and {@code false} values, intended for human audience (not for a * computer language), separated with comma. For example, {@code "yes,no"}. Note that white-space is significant, * so {@code "yes, no"} is WRONG (unless you want that leading space before "no"). * * <p>For backward compatibility the default is {@code "true,false"}, but using that value is denied for automatic * boolean-to-string conversion (like <code>${myBoolean}</code> will fail with it), only {@code myBool?string} will * allow it, which is deprecated since FreeMarker 2.3.20. * * <p>Note that automatic boolean-to-string conversion only exists since FreeMarker 2.3.20. Earlier this setting * only influenced the result of {@code myBool?string}. */
public void setBooleanFormat(String booleanFormat) { NullArgumentException.check("booleanFormat", booleanFormat); int commaIdx = booleanFormat.indexOf(','); if (commaIdx == -1) { throw new IllegalArgumentException( "Setting value must be a string that contains two comma-separated values for true and false, " + "respectively."); } this.booleanFormat = booleanFormat; properties.setProperty(BOOLEAN_FORMAT_KEY, booleanFormat); if (booleanFormat.equals(C_TRUE_FALSE)) { // C_TRUE_FALSE is the default for BC, but it's not a good default for human audience formatting, so we // pretend that it wasn't set. trueStringValue = null; falseStringValue = null; } else { trueStringValue = booleanFormat.substring(0, commaIdx); falseStringValue = booleanFormat.substring(commaIdx + 1); } }
The getter pair of setBooleanFormat(String).
/** * The getter pair of {@link #setBooleanFormat(String)}. */
public String getBooleanFormat() { return booleanFormat != null ? booleanFormat : parent.getBooleanFormat(); }
Tells if this setting is set directly in this object or its value is coming from the parent.
Since:2.3.24
/** * Tells if this setting is set directly in this object or its value is coming from the {@link #getParent() parent}. * * @since 2.3.24 */
public boolean isBooleanFormatSet() { return booleanFormat != null; } String formatBoolean(boolean value, boolean fallbackToTrueFalse) throws TemplateException { if (value) { String s = getTrueStringValue(); if (s == null) { if (fallbackToTrueFalse) { return MiscUtil.C_TRUE; } else { throw new _MiscTemplateException(getNullBooleanFormatErrorDescription()); } } else { return s; } } else { String s = getFalseStringValue(); if (s == null) { if (fallbackToTrueFalse) { return MiscUtil.C_FALSE; } else { throw new _MiscTemplateException(getNullBooleanFormatErrorDescription()); } } else { return s; } } } private _ErrorDescriptionBuilder getNullBooleanFormatErrorDescription() { return new _ErrorDescriptionBuilder( "Can't convert boolean to string automatically, because the \"", BOOLEAN_FORMAT_KEY ,"\" setting was ", new _DelayedJQuote(getBooleanFormat()), (getBooleanFormat().equals(C_TRUE_FALSE) ? ", which is the legacy default computer-language format, and hence isn't accepted." : ".") ).tips( "If you just want \"true\"/\"false\" result as you are generting computer-language output, " + "use \"?c\", like ${myBool?c}.", "You can write myBool?string('yes', 'no') and like to specify boolean formatting in place.", new Object[] { "If you need the same two values on most places, the programmers should set the \"", BOOLEAN_FORMAT_KEY ,"\" setting to something like \"yes,no\"." } ); }
Returns the string to which true is converted to for human audience, or null if automatic coercion to string is not allowed. The default value is null.

This value is deduced from the "boolean_format" setting. Confusingly, for backward compatibility (at least until 2.4) that defaults to "true,false", yet this defaults to null. That's so because "true,false" is treated exceptionally, as that default is a historical mistake in FreeMarker, since it targets computer language output, not human writing. Thus it's ignored.

Since:2.3.20
/** * Returns the string to which {@code true} is converted to for human audience, or {@code null} if automatic * coercion to string is not allowed. The default value is {@code null}. * * <p>This value is deduced from the {@code "boolean_format"} setting. * Confusingly, for backward compatibility (at least until 2.4) that defaults to {@code "true,false"}, yet this * defaults to {@code null}. That's so because {@code "true,false"} is treated exceptionally, as that default is a * historical mistake in FreeMarker, since it targets computer language output, not human writing. Thus it's * ignored. * * @since 2.3.20 */
String getTrueStringValue() { // The first step deliberately tests booleanFormat instead of trueStringValue! return booleanFormat != null ? trueStringValue : (parent != null ? parent.getTrueStringValue() : null); }
Same as getTrueStringValue() but with false.
Since:2.3.20
/** * Same as {@link #getTrueStringValue()} but with {@code false}. * @since 2.3.20 */
String getFalseStringValue() { // The first step deliberately tests booleanFormat instead of falseStringValue! return booleanFormat != null ? falseStringValue : (parent != null ? parent.getFalseStringValue() : null); }
Sets the format used to convert Date-s that are time (no date part) values to string-s, also the format that someString?time will use to parse strings.

For the possible values see setDateTimeFormat(String).

Defaults to "", which is equivalent to "medium".

/** * Sets the format used to convert {@link java.util.Date}-s that are time (no date part) values to string-s, also * the format that {@code someString?time} will use to parse strings. * * <p>For the possible values see {@link #setDateTimeFormat(String)}. * * <p>Defaults to {@code ""}, which is equivalent to {@code "medium"}. */
public void setTimeFormat(String timeFormat) { NullArgumentException.check("timeFormat", timeFormat); this.timeFormat = timeFormat; properties.setProperty(TIME_FORMAT_KEY, timeFormat); }
The getter pair of setTimeFormat(String).
/** * The getter pair of {@link #setTimeFormat(String)}. */
public String getTimeFormat() { return timeFormat != null ? timeFormat : parent.getTimeFormat(); }
Tells if this setting is set directly in this object or its value is coming from the parent.
Since:2.3.24
/** * Tells if this setting is set directly in this object or its value is coming from the {@link #getParent() parent}. * * @since 2.3.24 */
public boolean isTimeFormatSet() { return timeFormat != null; }
Sets the format used to convert Date-s that are date-only (no time part) values to string-s, also the format that someString?date will use to parse strings.

For the possible values see setDateTimeFormat(String).

Defaults to "" which is equivalent to "medium".

/** * Sets the format used to convert {@link java.util.Date}-s that are date-only (no time part) values to string-s, * also the format that {@code someString?date} will use to parse strings. * * <p>For the possible values see {@link #setDateTimeFormat(String)}. * * <p>Defaults to {@code ""} which is equivalent to {@code "medium"}. */
public void setDateFormat(String dateFormat) { NullArgumentException.check("dateFormat", dateFormat); this.dateFormat = dateFormat; properties.setProperty(DATE_FORMAT_KEY, dateFormat); }
The getter pair of setDateFormat(String).
/** * The getter pair of {@link #setDateFormat(String)}. */
public String getDateFormat() { return dateFormat != null ? dateFormat : parent.getDateFormat(); }
Tells if this setting is set directly in this object or its value is coming from the parent.
Since:2.3.24
/** * Tells if this setting is set directly in this object or its value is coming from the {@link #getParent() parent}. * * @since 2.3.24 */
public boolean isDateFormatSet() { return dateFormat != null; }
Sets the format used to convert Date-s that are date-time (timestamp) values to string-s, also the format that someString?datetime will use to parse strings.

The possible setting values are (the quotation marks aren't part of the value itself):

  • Patterns accepted by Java's SimpleDateFormat, for example "dd.MM.yyyy HH:mm:ss" (where HH means 24 hours format) or "MM/dd/yyyy hh:mm:ss a" (where a prints AM or PM, if the current language is English).

  • "xs" for XML Schema format, or "iso" for ISO 8601:2004 format. These formats allow various additional options, separated with space, like in "iso m nz" (or with _, like in "iso_m_nz"; this is useful in a case like lastModified?string.iso_m_nz). The options and their meanings are:

    • Accuracy options:
      ms = Milliseconds, always shown with all 3 digits, even if it's all 0-s. Example: 13:45:05.800
      s = Seconds (fraction seconds are dropped even if non-0), like 13:45:05
      m = Minutes, like 13:45. This isn't allowed for "xs".
      h = Hours, like 13. This isn't allowed for "xs".
      Neither = Up to millisecond accuracy, but trailing millisecond 0-s are removed, also the whole milliseconds part if it would be 0 otherwise. Example: 13:45:05.8

    • Time zone offset visibility options:
      fz = "Force Zone", always show time zone offset (even for for java.sql.Date and java.sql.Time values). But, because ISO 8601 doesn't allow for dates (means date without time of the day) to show the zone offset, this option will have no effect in the case of "iso" with dates.
      nz = "No Zone", never show time zone offset
      Neither = always show time zone offset, except for java.sql.Date and java.sql.Time, and for "iso" date values.

    • Time zone options:
      u = Use UTC instead of what the time_zone setting suggests. However, java.sql.Date and java.sql.Time aren't affected by this (see setSQLDateAndTimeTimeZone(TimeZone) to understand why)
      fu = "Force UTC", that is, use UTC instead of what the time_zone or the sql_date_and_time_time_zone setting suggests. This also effects java.sql.Date and java.sql.Time values
      Neither = Use the time zone suggested by the time_zone or the sql_date_and_time_time_zone configuration setting (setTimeZone(TimeZone) and setSQLDateAndTimeTimeZone(TimeZone)).

    The options can be specified in any order.

    Options from the same category are mutually exclusive, like using m and s together is an error.

    The accuracy and time zone offset visibility options don't influence parsing, only formatting. For example, even if you use "iso m nz", "2012-01-01T15:30:05.125+01" will be parsed successfully and with milliseconds accuracy. The time zone options (like "u") influence what time zone is chosen only when parsing a string that doesn't contain time zone offset.

    Parsing with "iso" understands both extend format and basic format, like 20141225T235018. It doesn't, however, support the parsing of all kind of ISO 8601 strings: if there's a date part, it must use year, month and day of the month values (not week of the year), and the day can't be omitted.

    The output of "iso" is deliberately so that it's also a good representation of the value with XML Schema format, except for 0 and negative years, where it's impossible. Also note that the time zone offset is omitted for date values in the "iso" format, while it's preserved for the "xs" format.

  • "short", "medium", "long", or "full", which that has locale-dependent meaning defined by the Java platform (see in the documentation of DateFormat). For date-time values, you can specify the length of the date and time part independently, be separating them with _, like "short_medium". ("medium" means "medium_medium" for date-time values.)

  • Anything that starts with "@" followed by a letter is interpreted as a custom date/time/dateTime format, but only if either Configuration.getIncompatibleImprovements() is at least 2.3.24, or there's any custom formats defined (even if custom number format). The format of such string is "@name" or "@name parameters", where name is the key in the Map set by setCustomDateFormats(Map<String,? extends TemplateDateFormatFactory>), and parameters is parsed by the custom number format.

Defaults to "", which is equivalent to "medium_medium".

/** * Sets the format used to convert {@link java.util.Date}-s that are date-time (timestamp) values to string-s, * also the format that {@code someString?datetime} will use to parse strings. * * <p>The possible setting values are (the quotation marks aren't part of the value itself): * * <ul> * <li><p>Patterns accepted by Java's {@link SimpleDateFormat}, for example {@code "dd.MM.yyyy HH:mm:ss"} (where * {@code HH} means 24 hours format) or {@code "MM/dd/yyyy hh:mm:ss a"} (where {@code a} prints AM or PM, if * the current language is English). * * <li><p>{@code "xs"} for XML Schema format, or {@code "iso"} for ISO 8601:2004 format. * These formats allow various additional options, separated with space, like in * {@code "iso m nz"} (or with {@code _}, like in {@code "iso_m_nz"}; this is useful in a case like * {@code lastModified?string.iso_m_nz}). The options and their meanings are: * * <ul> * <li><p>Accuracy options:<br> * {@code ms} = Milliseconds, always shown with all 3 digits, even if it's all 0-s. * Example: {@code 13:45:05.800}<br> * {@code s} = Seconds (fraction seconds are dropped even if non-0), like {@code 13:45:05}<br> * {@code m} = Minutes, like {@code 13:45}. This isn't allowed for "xs".<br> * {@code h} = Hours, like {@code 13}. This isn't allowed for "xs".<br> * Neither = Up to millisecond accuracy, but trailing millisecond 0-s are removed, also the whole * milliseconds part if it would be 0 otherwise. Example: {@code 13:45:05.8} * * <li><p>Time zone offset visibility options:<br> * {@code fz} = "Force Zone", always show time zone offset (even for for * {@link java.sql.Date java.sql.Date} and {@link java.sql.Time java.sql.Time} values). * But, because ISO 8601 doesn't allow for dates (means date without time of the day) to * show the zone offset, this option will have no effect in the case of {@code "iso"} with * dates.<br> * {@code nz} = "No Zone", never show time zone offset<br> * Neither = always show time zone offset, except for {@link java.sql.Date java.sql.Date} * and {@link java.sql.Time java.sql.Time}, and for {@code "iso"} date values. * * <li><p>Time zone options:<br> * {@code u} = Use UTC instead of what the {@code time_zone} setting suggests. However, * {@link java.sql.Date java.sql.Date} and {@link java.sql.Time java.sql.Time} aren't affected * by this (see {@link #setSQLDateAndTimeTimeZone(TimeZone)} to understand why)<br> * {@code fu} = "Force UTC", that is, use UTC instead of what the {@code time_zone} or the * {@code sql_date_and_time_time_zone} setting suggests. This also effects * {@link java.sql.Date java.sql.Date} and {@link java.sql.Time java.sql.Time} values<br> * Neither = Use the time zone suggested by the {@code time_zone} or the * {@code sql_date_and_time_time_zone} configuration setting ({@link #setTimeZone(TimeZone)} and * {@link #setSQLDateAndTimeTimeZone(TimeZone)}). * </ul> * * <p>The options can be specified in any order.</p> * * <p>Options from the same category are mutually exclusive, like using {@code m} and {@code s} * together is an error. * * <p>The accuracy and time zone offset visibility options don't influence parsing, only formatting. * For example, even if you use "iso m nz", "2012-01-01T15:30:05.125+01" will be parsed successfully and with * milliseconds accuracy. * The time zone options (like "u") influence what time zone is chosen only when parsing a string that doesn't * contain time zone offset. * * <p>Parsing with {@code "iso"} understands both extend format and basic format, like * {@code 20141225T235018}. It doesn't, however, support the parsing of all kind of ISO 8601 strings: if * there's a date part, it must use year, month and day of the month values (not week of the year), and the * day can't be omitted. * * <p>The output of {@code "iso"} is deliberately so that it's also a good representation of the value with * XML Schema format, except for 0 and negative years, where it's impossible. Also note that the time zone * offset is omitted for date values in the {@code "iso"} format, while it's preserved for the {@code "xs"} * format. * * <li><p>{@code "short"}, {@code "medium"}, {@code "long"}, or {@code "full"}, which that has locale-dependent * meaning defined by the Java platform (see in the documentation of {@link java.text.DateFormat}). * For date-time values, you can specify the length of the date and time part independently, be separating * them with {@code _}, like {@code "short_medium"}. ({@code "medium"} means * {@code "medium_medium"} for date-time values.) * * <li><p>Anything that starts with {@code "@"} followed by a letter is interpreted as a custom * date/time/dateTime format, but only if either {@link Configuration#getIncompatibleImprovements()} * is at least 2.3.24, or there's any custom formats defined (even if custom number format). The format of * such string is <code>"@<i>name</i>"</code> or <code>"@<i>name</i> <i>parameters</i>"</code>, where * <code><i>name</i></code> is the key in the {@link Map} set by {@link #setCustomDateFormats(Map)}, and * <code><i>parameters</i></code> is parsed by the custom number format. * * </ul> * * <p>Defaults to {@code ""}, which is equivalent to {@code "medium_medium"}. */
public void setDateTimeFormat(String dateTimeFormat) { NullArgumentException.check("dateTimeFormat", dateTimeFormat); this.dateTimeFormat = dateTimeFormat; properties.setProperty(DATETIME_FORMAT_KEY, dateTimeFormat); }
The getter pair of setDateTimeFormat(String).
/** * The getter pair of {@link #setDateTimeFormat(String)}. */
public String getDateTimeFormat() { return dateTimeFormat != null ? dateTimeFormat : parent.getDateTimeFormat(); }
Tells if this setting is set directly in this object or its value is coming from the parent.
Since:2.3.24
/** * Tells if this setting is set directly in this object or its value is coming from the {@link #getParent() parent}. * * @since 2.3.24 */
public boolean isDateTimeFormatSet() { return dateTimeFormat != null; }
Getter pair of setCustomDateFormats(Map<String,? extends TemplateDateFormatFactory>); do not modify the returned Map! To be consistent with other setting getters, if this setting was set directly on this Configurable object, this simply returns that value, otherwise it returns the value from the parent Configurable. So beware, the returned value doesn't reflect the Map key granularity fallback logic that FreeMarker actually uses for this setting (for that, use getCustomDateFormat(String)). The returned value isn't a snapshot; it may or may not shows the changes later made to this setting on this Configurable level (but usually it's well defined if until what point settings are possibly modified).

The return value is never null; called on the Configuration (top) level, it defaults to an empty Map.

See Also:
Since:2.3.24
/** * Getter pair of {@link #setCustomDateFormats(Map)}; do not modify the returned {@link Map}! To be consistent with * other setting getters, if this setting was set directly on this {@link Configurable} object, this simply returns * that value, otherwise it returns the value from the parent {@link Configurable}. So beware, the returned value * doesn't reflect the {@link Map} key granularity fallback logic that FreeMarker actually uses for this setting * (for that, use {@link #getCustomDateFormat(String)}). The returned value isn't a snapshot; it may or may not * shows the changes later made to this setting on this {@link Configurable} level (but usually it's well defined if * until what point settings are possibly modified). * * <p> * The return value is never {@code null}; called on the {@link Configuration} (top) level, it defaults to an empty * {@link Map}. * * @see #getCustomDateFormatsWithoutFallback() * * @since 2.3.24 */
public Map<String, ? extends TemplateDateFormatFactory> getCustomDateFormats() { return customDateFormats == null ? parent.getCustomDateFormats() : customDateFormats; }
Like getCustomDateFormats(), but doesn't fall back to the parent Configurable, nor does it provide a non-null default when called as the method of a Configuration.
Since:2.3.25
/** * Like {@link #getCustomDateFormats()}, but doesn't fall back to the parent {@link Configurable}, nor does it * provide a non-{@code null} default when called as the method of a {@link Configuration}. * * @since 2.3.25 */
public Map<String, ? extends TemplateDateFormatFactory> getCustomDateFormatsWithoutFallback() { return customDateFormats; }
Associates names with formatter factories, which then can be referred by the date_format, time_format, and datetime_format settings with values starting with @name. Beware, if you specify any custom formats here, an initial @ followed by a letter will have special meaning in number/date/time/datetime format strings, even if incompatible_improvements is less than 2.3.24 (starting with incompatible_improvements 2.3.24 @ always has special meaning).
Params:
  • customDateFormats – Can't be null. The name must start with an UNICODE letter, and can only contain UNICODE letters and digits.
Since:2.3.24
/** * Associates names with formatter factories, which then can be referred by the {@link #setDateTimeFormat(String) * date_format}, {@link #setDateTimeFormat(String) time_format}, and {@link #setDateTimeFormat(String) * datetime_format} settings with values starting with <code>@<i>name</i></code>. Beware, if you specify any custom * formats here, an initial {@code @} followed by a letter will have special meaning in number/date/time/datetime * format strings, even if {@link Configuration#getIncompatibleImprovements() incompatible_improvements} is less * than 2.3.24 (starting with {@link Configuration#getIncompatibleImprovements() incompatible_improvements} 2.3.24 * {@code @} always has special meaning). * * @param customDateFormats * Can't be {@code null}. The name must start with an UNICODE letter, and can only contain UNICODE * letters and digits. * * @since 2.3.24 */
public void setCustomDateFormats(Map<String, ? extends TemplateDateFormatFactory> customDateFormats) { NullArgumentException.check("customDateFormats", customDateFormats); validateFormatNames(customDateFormats.keySet()); this.customDateFormats = customDateFormats; }
Tells if this setting is set directly in this object or its value is coming from the parent.
Since:2.3.24
/** * Tells if this setting is set directly in this object or its value is coming from the {@link #getParent() parent}. * * @since 2.3.24 */
public boolean isCustomDateFormatsSet() { return this.customDateFormats != null; }
Gets the custom name format registered for the name.
Since:2.3.24
/** * Gets the custom name format registered for the name. * * @since 2.3.24 */
public TemplateDateFormatFactory getCustomDateFormat(String name) { TemplateDateFormatFactory r; if (customDateFormats != null) { r = customDateFormats.get(name); if (r != null) { return r; } } return parent != null ? parent.getCustomDateFormat(name) : null; }
Sets the exception handler used to handle exceptions occurring inside templates. The default is TemplateExceptionHandler.DEBUG_HANDLER. The recommended values are:

All of these will let the exception propagate further, so that you can catch it around Template.process(Object, Writer) for example. The difference is in what they print on the output before they do that.

Note that the TemplateExceptionHandler is not meant to be used for generating HTTP error pages. Neither is it meant to be used to roll back the printed output. These should be solved outside template processing when the exception raises from Template.process. TemplateExceptionHandler meant to be used if you want to include special content in the template output, or if you want to suppress certain exceptions. If you suppress an exception, and the getLogTemplateExceptions() returns false, then it's the responsibility of the TemplateExceptionHandler to log the exception (if you want it to be logged).

See Also:
/** * Sets the exception handler used to handle exceptions occurring inside templates. * The default is {@link TemplateExceptionHandler#DEBUG_HANDLER}. The recommended values are: * * <ul> * <li>In production systems: {@link TemplateExceptionHandler#RETHROW_HANDLER} * <li>During development of HTML templates: {@link TemplateExceptionHandler#HTML_DEBUG_HANDLER} * <li>During development of non-HTML templates: {@link TemplateExceptionHandler#DEBUG_HANDLER} * </ul> * * <p>All of these will let the exception propagate further, so that you can catch it around * {@link Template#process(Object, Writer)} for example. The difference is in what they print on the output before * they do that. * * <p>Note that the {@link TemplateExceptionHandler} is not meant to be used for generating HTTP error pages. * Neither is it meant to be used to roll back the printed output. These should be solved outside template * processing when the exception raises from {@link Template#process(Object, Writer) Template.process}. * {@link TemplateExceptionHandler} meant to be used if you want to include special content <em>in</em> the template * output, or if you want to suppress certain exceptions. If you suppress an exception, and the * {@link Environment#getLogTemplateExceptions()} returns {@code false}, then it's the responsibility of the * {@link TemplateExceptionHandler} to log the exception (if you want it to be logged). * * @see #setLogTemplateExceptions(boolean) * @see #setAttemptExceptionReporter(AttemptExceptionReporter) */
public void setTemplateExceptionHandler(TemplateExceptionHandler templateExceptionHandler) { NullArgumentException.check("templateExceptionHandler", templateExceptionHandler); this.templateExceptionHandler = templateExceptionHandler; properties.setProperty(TEMPLATE_EXCEPTION_HANDLER_KEY, templateExceptionHandler.getClass().getName()); } /** * The getter pair of {@link #setTemplateExceptionHandler(TemplateExceptionHandler)}. */ public TemplateExceptionHandler getTemplateExceptionHandler() { return templateExceptionHandler != null ? templateExceptionHandler : parent.getTemplateExceptionHandler(); }
Tells if this setting is set directly in this object or its value is coming from the parent.
Since:2.3.24
/** * Tells if this setting is set directly in this object or its value is coming from the {@link #getParent() parent}. * * @since 2.3.24 */
public boolean isTemplateExceptionHandlerSet() { return templateExceptionHandler != null; }
Specifies how exceptions handled (and hence suppressed) by an #attempt blocks will be logged or otherwise reported. The default value is AttemptExceptionReporter.LOG_ERROR_REPORTER.

Note that #attempt is not supposed to be a general purpose error handler mechanism, like try is in Java. It's for decreasing the impact of unexpected errors, by making it possible that only part of the page is going down, instead of the whole page. But it's still an error, something that someone should fix. So the error should be reported, not just ignored in a custom AttemptExceptionReporter-s.

The AttemptExceptionReporter is invoked regardless of the value of the log_template_exceptions setting. The AttemptExceptionReporter is not invoked if the TemplateExceptionHandler has suppressed the exception.

Since:2.3.27
/** * Specifies how exceptions handled (and hence suppressed) by an {@code #attempt} blocks will be logged or otherwise * reported. The default value is {@link AttemptExceptionReporter#LOG_ERROR_REPORTER}. * * <p>Note that {@code #attempt} is not supposed to be a general purpose error handler mechanism, like {@code try} * is in Java. It's for decreasing the impact of unexpected errors, by making it possible that only part of the * page is going down, instead of the whole page. But it's still an error, something that someone should fix. So the * error should be reported, not just ignored in a custom {@link AttemptExceptionReporter}-s. * * <p>The {@link AttemptExceptionReporter} is invoked regardless of the value of the * {@link #setLogTemplateExceptions(boolean) log_template_exceptions} setting. * The {@link AttemptExceptionReporter} is not invoked if the {@link TemplateExceptionHandler} has suppressed the * exception. * * @since 2.3.27 */
public void setAttemptExceptionReporter(AttemptExceptionReporter attemptExceptionReporter) { NullArgumentException.check("attemptExceptionReporter", attemptExceptionReporter); this.attemptExceptionReporter = attemptExceptionReporter; }
Since:2.3.27
/** * The getter pair of {@link #setAttemptExceptionReporter(AttemptExceptionReporter)}. * * @since 2.3.27 */
public AttemptExceptionReporter getAttemptExceptionReporter() { return attemptExceptionReporter != null ? attemptExceptionReporter : parent.getAttemptExceptionReporter(); }
Tells if this setting is set directly in this object or its value is coming from the parent.
Since:2.3.27
/** * Tells if this setting is set directly in this object or its value is coming from the {@link #getParent() parent}. * * @since 2.3.27 */
public boolean isAttemptExceptionReporterSet() { return attemptExceptionReporter != null; }
Sets the arithmetic engine used to perform arithmetic operations. The default is ArithmeticEngine.BIGDECIMAL_ENGINE.
/** * Sets the arithmetic engine used to perform arithmetic operations. * The default is {@link ArithmeticEngine#BIGDECIMAL_ENGINE}. */
public void setArithmeticEngine(ArithmeticEngine arithmeticEngine) { NullArgumentException.check("arithmeticEngine", arithmeticEngine); this.arithmeticEngine = arithmeticEngine; properties.setProperty(ARITHMETIC_ENGINE_KEY, arithmeticEngine.getClass().getName()); } /** * The getter pair of {@link #setArithmeticEngine(ArithmeticEngine)}. */ public ArithmeticEngine getArithmeticEngine() { return arithmeticEngine != null ? arithmeticEngine : parent.getArithmeticEngine(); }
Tells if this setting is set directly in this object or its value is coming from the parent.
Since:2.3.24
/** * Tells if this setting is set directly in this object or its value is coming from the {@link #getParent() parent}. * * @since 2.3.24 */
public boolean isArithmeticEngineSet() { return arithmeticEngine != null; }
Sets the object wrapper used to wrap objects to TemplateModel-s. The default is ObjectWrapper.DEFAULT_WRAPPER.
/** * Sets the object wrapper used to wrap objects to {@link TemplateModel}-s. * The default is {@link ObjectWrapper#DEFAULT_WRAPPER}. */
public void setObjectWrapper(ObjectWrapper objectWrapper) { NullArgumentException.check("objectWrapper", objectWrapper); this.objectWrapper = objectWrapper; properties.setProperty(OBJECT_WRAPPER_KEY, objectWrapper.getClass().getName()); }
The getter pair of setObjectWrapper(ObjectWrapper).
/** * The getter pair of {@link #setObjectWrapper(ObjectWrapper)}. */
public ObjectWrapper getObjectWrapper() { return objectWrapper != null ? objectWrapper : parent.getObjectWrapper(); }
Tells if this setting is set directly in this object or its value is coming from the parent.
Since:2.3.24
/** * Tells if this setting is set directly in this object or its value is coming from the {@link #getParent() parent}. * * @since 2.3.24 */
public boolean isObjectWrapperSet() { return objectWrapper != null; }
Informs FreeMarker about the charset used for the output. As FreeMarker outputs character stream (not byte stream), it's not aware of the output charset unless the software that encloses it tells it with this setting. Some templates may use FreeMarker features that require this information. Setting this to null means that the output encoding is not known.

Defaults to null (unknown).

/** * Informs FreeMarker about the charset used for the output. As FreeMarker outputs character stream (not * byte stream), it's not aware of the output charset unless the software that encloses it tells it * with this setting. Some templates may use FreeMarker features that require this information. * Setting this to {@code null} means that the output encoding is not known. * * <p>Defaults to {@code null} (unknown). */
public void setOutputEncoding(String outputEncoding) { this.outputEncoding = outputEncoding; // java.util.Properties doesn't allow null value! if (outputEncoding != null) { properties.setProperty(OUTPUT_ENCODING_KEY, outputEncoding); } else { properties.remove(OUTPUT_ENCODING_KEY); } outputEncodingSet = true; } public String getOutputEncoding() { return outputEncodingSet ? outputEncoding : (parent != null ? parent.getOutputEncoding() : null); }
Tells if this setting is set directly in this object or its value is coming from the parent.
Since:2.3.24
/** * Tells if this setting is set directly in this object or its value is coming from the {@link #getParent() parent}. * * @since 2.3.24 */
public boolean isOutputEncodingSet() { return outputEncodingSet; }
Sets the URL escaping (URL encoding, percentage encoding) charset. If null, the output encoding (setOutputEncoding(String)) will be used for URL escaping. Defaults to null.
/** * Sets the URL escaping (URL encoding, percentage encoding) charset. If {@code null}, the output encoding * ({@link #setOutputEncoding(String)}) will be used for URL escaping. * * Defaults to {@code null}. */
public void setURLEscapingCharset(String urlEscapingCharset) { this.urlEscapingCharset = urlEscapingCharset; // java.util.Properties doesn't allow null value! if (urlEscapingCharset != null) { properties.setProperty(URL_ESCAPING_CHARSET_KEY, urlEscapingCharset); } else { properties.remove(URL_ESCAPING_CHARSET_KEY); } urlEscapingCharsetSet = true; } public String getURLEscapingCharset() { return urlEscapingCharsetSet ? urlEscapingCharset : (parent != null ? parent.getURLEscapingCharset() : null); }
Tells if this setting is set directly in this object or its value is coming from the parent.
Since:2.3.24
/** * Tells if this setting is set directly in this object or its value is coming from the {@link #getParent() parent}. * * @since 2.3.24 */
public boolean isURLEscapingCharsetSet() { return urlEscapingCharsetSet; }
Sets the TemplateClassResolver that is used when the new built-in is called in a template. That is, when a template contains the "com.example.SomeClassName"?new expression, this object will be called to resolve the "com.example.SomeClassName" string to a class. The default value is TemplateClassResolver.UNRESTRICTED_RESOLVER in FreeMarker 2.3.x, and TemplateClassResolver.SAFER_RESOLVER starting from FreeMarker 2.4.0. If you allow users to upload templates, it's important to use a custom restrictive TemplateClassResolver.
Since:2.3.17
/** * Sets the {@link TemplateClassResolver} that is used when the * <code>new</code> built-in is called in a template. That is, when * a template contains the <code>"com.example.SomeClassName"?new</code> * expression, this object will be called to resolve the * <code>"com.example.SomeClassName"</code> string to a class. The default * value is {@link TemplateClassResolver#UNRESTRICTED_RESOLVER} in * FreeMarker 2.3.x, and {@link TemplateClassResolver#SAFER_RESOLVER} * starting from FreeMarker 2.4.0. If you allow users to upload templates, * it's important to use a custom restrictive {@link TemplateClassResolver}. * * @since 2.3.17 */
public void setNewBuiltinClassResolver(TemplateClassResolver newBuiltinClassResolver) { NullArgumentException.check("newBuiltinClassResolver", newBuiltinClassResolver); this.newBuiltinClassResolver = newBuiltinClassResolver; properties.setProperty(NEW_BUILTIN_CLASS_RESOLVER_KEY, newBuiltinClassResolver.getClass().getName()); }
Retrieves the TemplateClassResolver used to resolve classes when "SomeClassName"?new is called in a template.
Since:2.3.17
/** * Retrieves the {@link TemplateClassResolver} used * to resolve classes when "SomeClassName"?new is called in a template. * * @since 2.3.17 */
public TemplateClassResolver getNewBuiltinClassResolver() { return newBuiltinClassResolver != null ? newBuiltinClassResolver : parent.getNewBuiltinClassResolver(); }
Tells if this setting is set directly in this object or its value is coming from the parent.
Since:2.3.24
/** * Tells if this setting is set directly in this object or its value is coming from the {@link #getParent() parent}. * * @since 2.3.24 */
public boolean isNewBuiltinClassResolverSet() { return newBuiltinClassResolver != null; }
Sets whether the output Writer is automatically flushed at the end of Template.process(Object, Writer) (and its overloads). The default is true.

Using false is needed for example when a Web page is composed from several boxes (like portlets, GUI panels, etc.) that aren't inserted with #include (or with similar directives) into a master FreeMarker template, rather they are all processed with a separate Template.process(Object, Writer) call. In a such scenario the automatic flushes would commit the HTTP response after each box, hence interfering with full-page buffering, and also possibly decreasing performance with too frequent and too early response buffer flushes.

Since:2.3.17
/** * Sets whether the output {@link Writer} is automatically flushed at * the end of {@link Template#process(Object, Writer)} (and its * overloads). The default is {@code true}. * * <p>Using {@code false} is needed for example when a Web page is composed * from several boxes (like portlets, GUI panels, etc.) that aren't inserted * with <tt>#include</tt> (or with similar directives) into a master * FreeMarker template, rather they are all processed with a separate * {@link Template#process(Object, Writer)} call. In a such scenario the * automatic flushes would commit the HTTP response after each box, hence * interfering with full-page buffering, and also possibly decreasing * performance with too frequent and too early response buffer flushes. * * @since 2.3.17 */
public void setAutoFlush(boolean autoFlush) { this.autoFlush = Boolean.valueOf(autoFlush); properties.setProperty(AUTO_FLUSH_KEY, String.valueOf(autoFlush)); }
Since:2.3.17
/** * See {@link #setAutoFlush(boolean)} * * @since 2.3.17 */
public boolean getAutoFlush() { return autoFlush != null ? autoFlush.booleanValue() : (parent != null ? parent.getAutoFlush() : true); }
Tells if this setting is set directly in this object or its value is coming from the parent.
Since:2.3.24
/** * Tells if this setting is set directly in this object or its value is coming from the {@link #getParent() parent}. * * @since 2.3.24 */
public boolean isAutoFlushSet() { return autoFlush != null; }
Sets if tips should be shown in error messages of errors arising during template processing. The default is true.
Since:2.3.21
/** * Sets if tips should be shown in error messages of errors arising during template processing. * The default is {@code true}. * * @since 2.3.21 */
public void setShowErrorTips(boolean showTips) { this.showErrorTips = Boolean.valueOf(showTips); properties.setProperty(SHOW_ERROR_TIPS_KEY, String.valueOf(showTips)); }
Since:2.3.21
/** * See {@link #setShowErrorTips(boolean)} * * @since 2.3.21 */
public boolean getShowErrorTips() { return showErrorTips != null ? showErrorTips.booleanValue() : (parent != null ? parent.getShowErrorTips() : true); }
Tells if this setting is set directly in this object or its value is coming from the parent.
Since:2.3.24
/** * Tells if this setting is set directly in this object or its value is coming from the {@link #getParent() parent}. * * @since 2.3.24 */
public boolean isShowErrorTipsSet() { return showErrorTips != null; }
Specifies if ?api can be used in templates. Defaults to false so that updating FreeMarker won't decrease the security of existing applications.
Since:2.3.22
/** * Specifies if {@code ?api} can be used in templates. Defaults to {@code false} so that updating FreeMarker won't * decrease the security of existing applications. * * @since 2.3.22 */
public void setAPIBuiltinEnabled(boolean value) { apiBuiltinEnabled = Boolean.valueOf(value); properties.setProperty(API_BUILTIN_ENABLED_KEY, String.valueOf(value)); }
Since:2.3.22
/** * See {@link #setAPIBuiltinEnabled(boolean)} * * @since 2.3.22 */
public boolean isAPIBuiltinEnabled() { return apiBuiltinEnabled != null ? apiBuiltinEnabled.booleanValue() : (parent != null ? parent.isAPIBuiltinEnabled() : false); }
Tells if this setting is set directly in this object or its value is coming from the parent.
Since:2.3.24
/** * Tells if this setting is set directly in this object or its value is coming from the {@link #getParent() parent}. * * @since 2.3.24 */
public boolean isAPIBuiltinEnabledSet() { return apiBuiltinEnabled != null; }
Specifies if TemplateException-s thrown by template processing are logged by FreeMarker or not. The default is true for backward compatibility, but that results in logging the exception twice in properly written applications, because there the TemplateException thrown by the public FreeMarker API is also logged by the caller (even if only as the cause exception of a higher level exception). Hence, in modern applications it should be set to false. Note that this setting has no effect on the logging of exceptions caught by #attempt; by default those are always logged as errors (because those exceptions won't bubble up to the API caller), however, that can be changed with the attempt_exception_reporter setting.
Since:2.3.22
/** * Specifies if {@link TemplateException}-s thrown by template processing are logged by FreeMarker or not. The * default is {@code true} for backward compatibility, but that results in logging the exception twice in properly * written applications, because there the {@link TemplateException} thrown by the public FreeMarker API is also * logged by the caller (even if only as the cause exception of a higher level exception). Hence, in modern * applications it should be set to {@code false}. Note that this setting has no effect on the logging of exceptions * caught by {@code #attempt}; by default those are always logged as errors (because those exceptions won't bubble * up to the API caller), however, that can be changed with the {@link * #setAttemptExceptionReporter(AttemptExceptionReporter) attempt_exception_reporter} setting. * * @since 2.3.22 */
public void setLogTemplateExceptions(boolean value) { logTemplateExceptions = Boolean.valueOf(value); properties.setProperty(LOG_TEMPLATE_EXCEPTIONS_KEY, String.valueOf(value)); }
Since:2.3.22
/** * See {@link #setLogTemplateExceptions(boolean)} * * @since 2.3.22 */
public boolean getLogTemplateExceptions() { return logTemplateExceptions != null ? logTemplateExceptions.booleanValue() : (parent != null ? parent.getLogTemplateExceptions() : true); }
Tells if this setting is set directly in this object or its value is coming from the parent.
Since:2.3.24
/** * Tells if this setting is set directly in this object or its value is coming from the {@link #getParent() parent}. * * @since 2.3.24 */
public boolean isLogTemplateExceptionsSet() { return logTemplateExceptions != null; }
Specifies if unchecked exceptions thrown during expression evaluation or during executing custom directives (and transform) will be wrapped into TemplateException-s, or will bubble up to the caller of Template.process(Object, Writer, ObjectWrapper) as is. The default is false for backward compatibility (as some applications catch certain unchecked exceptions thrown by the template processing to do something special), but the recommended value is true. When this is true, the unchecked exceptions will be wrapped into a TemplateException-s, thus the exception will include the location in the template (not just the Java stack trace). Another consequence of the wrapping is that the TemplateExceptionHandler will be invoked for the exception (as that only handles TemplateException-s, it wasn't invoked for unchecked exceptions). When this setting is false, unchecked exception will be thrown by Template.process(Object, Writer, ObjectWrapper). Note that plain Java methods called from templates aren't user defined TemplateMethodModel-s, and have always wrapped the thrown exception into TemplateException, regardless of this setting.
Since:2.3.27
/** * Specifies if unchecked exceptions thrown during expression evaluation or during executing custom directives (and * transform) will be wrapped into {@link TemplateException}-s, or will bubble up to the caller of * {@link Template#process(Object, Writer, ObjectWrapper)} as is. The default is {@code false} for backward * compatibility (as some applications catch certain unchecked exceptions thrown by the template processing to do * something special), but the recommended value is {@code true}. * When this is {@code true}, the unchecked exceptions will be wrapped into a {@link TemplateException}-s, thus the * exception will include the location in the template (not * just the Java stack trace). Another consequence of the wrapping is that the {@link TemplateExceptionHandler} will * be invoked for the exception (as that only handles {@link TemplateException}-s, it wasn't invoked for unchecked * exceptions). When this setting is {@code false}, unchecked exception will be thrown by * {@link Template#process(Object, Writer, ObjectWrapper)}. * Note that plain Java methods called from templates aren't user defined {@link TemplateMethodModel}-s, and have * always wrapped the thrown exception into {@link TemplateException}, regardless of this setting. * * @since 2.3.27 */
public void setWrapUncheckedExceptions(boolean wrapUncheckedExceptions) { this.wrapUncheckedExceptions = wrapUncheckedExceptions; }
Since:2.3.27
/** * The getter pair of {@link #setWrapUncheckedExceptions(boolean)}. * * @since 2.3.27 */
public boolean getWrapUncheckedExceptions() { return wrapUncheckedExceptions != null ? wrapUncheckedExceptions : (parent != null ? parent.getWrapUncheckedExceptions() : false /* [2.4] true */); }
Since:2.3.27
/** * @since 2.3.27 */
public boolean isWrapUncheckedExceptionsSet() { return wrapUncheckedExceptions != null; }
The getter pair of setLazyImports(boolean).
Since:2.3.25
/** * The getter pair of {@link #setLazyImports(boolean)}. * * @since 2.3.25 */
public boolean getLazyImports() { return lazyImports != null ? lazyImports.booleanValue() : parent.getLazyImports(); }
Specifies if <#import ...> (and Environment.importLib(String, String)) should delay the loading and processing of the imported templates until the content of the imported namespace is actually accessed. This makes the overhead of unused imports negligible. Note that turning on lazy importing isn't entirely transparent, as accessing global variables (usually created with <#global ...=...>) that should be created by the imported template won't trigger the loading and processing of the lazily imported template (because globals aren't accessed through the namespace variable), so the global variable will just be missing. In general, you lose the strict control over when the namespace initializing code in the imported template will be executed, though it shouldn't mater for most well designed imported templates. Another drawback is that importing a missing or otherwise broken template will be successful, and the problem will remain hidden until (and if) the namespace content is actually used. Note that the namespace initializing code will run with the same locale as it was at the point of the <#import ...> call (other settings won't be handled specially like that).

The default is false (and thus imports are eager) for backward compatibility, which can cause perceivable overhead if you have many imports and only a few of them is actually used.

This setting also affects auto-imports, unless you have set a non-null value with setLazyAutoImports(Boolean).

See Also:
Since:2.3.25
/** * Specifies if {@code <#import ...>} (and {@link Environment#importLib(String, String)}) should delay the loading * and processing of the imported templates until the content of the imported namespace is actually accessed. This * makes the overhead of <em>unused</em> imports negligible. Note that turning on lazy importing isn't entirely * transparent, as accessing global variables (usually created with {@code <#global ...=...>}) that should be * created by the imported template won't trigger the loading and processing of the lazily imported template * (because globals aren't accessed through the namespace variable), so the global variable will just be missing. * In general, you lose the strict control over when the namespace initializing code in the imported template will * be executed, though it shouldn't mater for most well designed imported templates. * Another drawback is that importing a missing or otherwise broken template will be successful, and the problem * will remain hidden until (and if) the namespace content is actually used. Note that the namespace initializing * code will run with the same {@linkplain Configurable#getLocale() locale} as it was at the point of the * {@code <#import ...>} call (other settings won't be handled specially like that). * * <p> * The default is {@code false} (and thus imports are eager) for backward compatibility, which can cause * perceivable overhead if you have many imports and only a few of them is actually used. * * <p> * This setting also affects {@linkplain #setAutoImports(Map) auto-imports}, unless you have set a non-{@code null} * value with {@link #setLazyAutoImports(Boolean)}. * * @see #setLazyAutoImports(Boolean) * * @since 2.3.25 */
public void setLazyImports(boolean lazyImports) { this.lazyImports = Boolean.valueOf(lazyImports); }
Tells if this setting is set directly in this object or its value is coming from the parent.
Since:2.3.25
/** * Tells if this setting is set directly in this object or its value is coming from the {@link #getParent() parent}. * * @since 2.3.25 */
public boolean isLazyImportsSet() { return lazyImports != null; }
The getter pair of setLazyAutoImports(Boolean).
Since:2.3.25
/** * The getter pair of {@link #setLazyAutoImports(Boolean)}. * * @since 2.3.25 */
public Boolean getLazyAutoImports() { return lazyAutoImportsSet ? lazyAutoImports : parent.getLazyAutoImports(); }
Specifies if auto-imports will be lazy imports. This is useful to make the overhead of unused auto-imports negligible. If this is set to null, getLazyImports() specifies the behavior of auto-imports too. The default value is null.
Since:2.3.25
/** * Specifies if {@linkplain #setAutoImports(Map) auto-imports} will be * {@link #setLazyImports(boolean) lazy imports}. This is useful to make the overhead of <em>unused</em> * auto-imports negligible. If this is set to {@code null}, {@link #getLazyImports()} specifies the behavior of * auto-imports too. The default value is {@code null}. * * @since 2.3.25 */
public void setLazyAutoImports(Boolean lazyAutoImports) { this.lazyAutoImports = lazyAutoImports; lazyAutoImportsSet = true; }
Tells if this setting is set directly in this object or its value is coming from the parent.
Since:2.3.25
/** * Tells if this setting is set directly in this object or its value is coming from the {@link #getParent() parent}. * * @since 2.3.25 */
public boolean isLazyAutoImportsSet() { return lazyAutoImportsSet; }
Adds an invisible #import templateName as namespaceVarName at the beginning of the main template (that's the top-level template that wasn't included/imported from another template). While it only affects the main template directly, as the imports will create a global variable there, the imports will be visible from the further imported templates too (note that Configuration.getIncompatibleImprovements() set to 2.3.24 fixes a rarely surfacing bug with that).

It's recommended to set the lazy_auto_imports setting (setLazyAutoImports(Boolean)) to true when using this, so that auto-imports that are unused in a template won't degrade performance by unnecessary loading and initializing the imported library.

If the imports aren't lazy, the order of the imports will be the same as the order in which they were added with this method. (Calling this method with an already added namespaceVarName will move that to the end of the auto-import order.)

The auto-import is added directly to the Configurable on which this method is called (not to the parents or children), but when the main template is processed, the auto-imports are collected from all the Configurable levels, in parent-to-child order: Configuration, Template (the main template), Environment. If the same namespaceVarName occurs on multiple levels, the one on the child level is used, and the clashing import from the parent level is skipped.

If there are also auto-includes (see addAutoInclude(String)), those will be executed after the auto-imports.

See Also:
/** * Adds an invisible <code>#import <i>templateName</i> as <i>namespaceVarName</i></code> at the beginning of the * main template (that's the top-level template that wasn't included/imported from another template). While it only * affects the main template directly, as the imports will create a global variable there, the imports will be * visible from the further imported templates too (note that {@link Configuration#getIncompatibleImprovements()} * set to 2.3.24 fixes a rarely surfacing bug with that). * * <p> * It's recommended to set the {@code lazy_auto_imports} setting ({@link Configuration#setLazyAutoImports(Boolean)}) * to {@code true} when using this, so that auto-imports that are unused in a template won't degrade performance by * unnecessary loading and initializing the imported library. * * <p> * If the imports aren't lazy, the order of the imports will be the same as the order in which they were added with * this method. (Calling this method with an already added {@code namespaceVarName} will move that to the end * of the auto-import order.) * * <p> * The auto-import is added directly to the {@link Configurable} on which this method is called (not to the parents * or children), but when the main template is processed, the auto-imports are collected from all the * {@link Configurable} levels, in parent-to-child order: {@link Configuration}, {@link Template} (the main * template), {@link Environment}. If the same {@code namespaceVarName} occurs on multiple levels, the one on the * child level is used, and the clashing import from the parent level is skipped. * * <p>If there are also auto-includes (see {@link #addAutoInclude(String)}), those will be executed after * the auto-imports. * * @see #setAutoImports(Map) */
public void addAutoImport(String namespaceVarName, String templateName) { // "synchronized" is removed from the API as it's not safe to set anything after publishing the Configuration synchronized (this) { if (autoImports == null) { initAutoImportsMap(); } else { // This was a List earlier, so re-inserted items must go to the end, hence we remove() before put(). autoImports.remove(namespaceVarName); } autoImports.put(namespaceVarName, templateName); } } private void initAutoImportsMap() { autoImports = new LinkedHashMap<String, String>(4); }
Removes an auto-import from this Configurable level (not from the parents or children); see addAutoImport(String, String). Does nothing if the auto-import doesn't exist.
/** * Removes an auto-import from this {@link Configurable} level (not from the parents or children); * see {@link #addAutoImport(String, String)}. Does nothing if the auto-import doesn't exist. */
public void removeAutoImport(String namespaceVarName) { // "synchronized" is removed from the API as it's not safe to set anything after publishing the Configuration synchronized (this) { if (autoImports != null) { autoImports.remove(namespaceVarName); } } }
Removes all auto-imports, then calls addAutoImport(String, String) for each Map-entry (the entry key is the namespaceVarName). The order of the auto-imports will be the same as Map.keySet() returns the keys (but the order of imports doesn't mater for properly designed libraries anyway).
Params:
  • map – Maps the namespace variable names to the template names; not null
/** * Removes all auto-imports, then calls {@link #addAutoImport(String, String)} for each {@link Map}-entry (the entry * key is the {@code namespaceVarName}). The order of the auto-imports will be the same as {@link Map#keySet()} * returns the keys (but the order of imports doesn't mater for properly designed libraries anyway). * * @param map * Maps the namespace variable names to the template names; not {@code null} */
public void setAutoImports(Map map) { NullArgumentException.check("map", map); // "synchronized" is removed from the API as it's not safe to set anything after publishing the Configuration synchronized (this) { if (autoImports != null) { autoImports.clear(); } for (Map.Entry<?, ?> entry : ((Map<?, ?>) map).entrySet()) { Object key = entry.getKey(); if (!(key instanceof String)) { throw new IllegalArgumentException( "Key in Map wasn't a String, but a(n) " + key.getClass().getName() + "."); } Object value = entry.getValue(); if (!(value instanceof String)) { throw new IllegalArgumentException( "Value in Map wasn't a String, but a(n) " + value.getClass().getName() + "."); } addAutoImport((String) key, (String) value); } } }
Getter pair of setAutoImports(Map); do not modify the returned Map! To be consistent with other setting getters, if this setting was set directly on this Configurable object, this simply returns that value, otherwise it returns the value from the parent Configurable. So beware, the returned value doesn't reflect the Map key granularity fallback logic that FreeMarker actually uses for this setting. The returned value is not the same Map object that was set with setAutoImports(Map), only its content is the same. The returned value isn't a snapshot; it may or may not shows the changes later made to this setting on this Configurable level (but usually it's well defined if until what point settings are possibly modified).

The return value is never null; called on the Configuration (top) level, it defaults to an empty Map.

See Also:
Since:2.3.25
/** * Getter pair of {@link #setAutoImports(Map)}; do not modify the returned {@link Map}! To be consistent with other * setting getters, if this setting was set directly on this {@link Configurable} object, this simply returns that * value, otherwise it returns the value from the parent {@link Configurable}. So beware, the returned value doesn't * reflect the {@link Map} key granularity fallback logic that FreeMarker actually uses for this setting. The * returned value is not the same {@link Map} object that was set with {@link #setAutoImports(Map)}, only its * content is the same. The returned value isn't a snapshot; it may or may not shows the changes later made to this * setting on this {@link Configurable} level (but usually it's well defined if until what point settings are * possibly modified). * * <p> * The return value is never {@code null}; called on the {@link Configuration} (top) level, it defaults to an empty * {@link Map}. * * @see #getAutoImportsWithoutFallback() * * @since 2.3.25 */
public Map<String, String> getAutoImports() { return autoImports != null ? autoImports : parent.getAutoImports(); }
Tells if this setting is set directly in this object or its value is coming from the parent.
Since:2.3.25
/** * Tells if this setting is set directly in this object or its value is coming from the {@link #getParent() parent}. * * @since 2.3.25 */
public boolean isAutoImportsSet() { return autoImports != null; }
Like getAutoImports(), but doesn't fall back to the parent Configurable (and so it can be null).
Since:2.3.25
/** * Like {@link #getAutoImports()}, but doesn't fall back to the parent {@link Configurable} (and so it can be * {@code null}). * * @since 2.3.25 */
public Map<String, String> getAutoImportsWithoutFallback() { return autoImports; }
Adds an invisible #include templateName at the beginning of the main template (that's the top-level template that wasn't included/imported from another template).

The order of the inclusions will be the same as the order in which they were added with this method.

The auto-include is added directly to the Configurable on which this method is called (not to the parents or children), but when the main template is processed, the auto-includes are collected from all the Configurable levels, in parent-to-child order: Configuration, Template (the main template), Environment.

If there are also auto-imports (addAutoImport(String, String)), those imports will be executed before the auto-includes, hence the namespace variables are accessible for the auto-included templates.

Calling addAutoInclude(String) with an already added template name will just move that to the end of the auto-include list (within the same Configurable level). This works even if the same template name appears on different Configurable levels, in which case only the inclusion on the lowest (child) level will be executed.

See Also:
/** * Adds an invisible <code>#include <i>templateName</i></code> at the beginning of the main template (that's the * top-level template that wasn't included/imported from another template). * * <p> * The order of the inclusions will be the same as the order in which they were added with this method. * * <p> * The auto-include is added directly to the {@link Configurable} on which this method is called (not to the parents * or children), but when the main template is processed, the auto-includes are collected from all the * {@link Configurable} levels, in parent-to-child order: {@link Configuration}, {@link Template} (the main * template), {@link Environment}. * * <p> * If there are also auto-imports ({@link #addAutoImport(String, String)}), those imports will be executed before * the auto-includes, hence the namespace variables are accessible for the auto-included templates. * * <p> * Calling {@link #addAutoInclude(String)} with an already added template name will just move that to the end of the * auto-include list (within the same {@link Configurable} level). This works even if the same template name appears * on different {@link Configurable} levels, in which case only the inclusion on the lowest (child) level will be * executed. * * @see #setAutoIncludes(List) */
public void addAutoInclude(String templateName) { addAutoInclude(templateName, false); }
Params:
  • keepDuplicate – Used for emulating legacy glitch, where duplicates weren't removed if the inclusion was added via setAutoIncludes(List).
/** * @param keepDuplicate * Used for emulating legacy glitch, where duplicates weren't removed if the inclusion was added via * {@link #setAutoIncludes(List)}. */
private void addAutoInclude(String templateName, boolean keepDuplicate) { // "synchronized" is removed from the API as it's not safe to set anything after publishing the Configuration synchronized (this) { if (autoIncludes == null) { initAutoIncludesList(); } else { if (!keepDuplicate) { autoIncludes.remove(templateName); } } autoIncludes.add(templateName); } } private void initAutoIncludesList() { autoIncludes = new ArrayList<String>(4); }
Removes all auto-includes, then calls addAutoInclude(String) for each List items.

Before incompatible improvements 2.3.25 it doesn't filter out duplicates from the list if this method was called on a Configuration instance.

/** * Removes all auto-includes, then calls {@link #addAutoInclude(String)} for each {@link List} items. * * <p>Before {@linkplain Configuration#Configuration(Version) incompatible improvements} 2.3.25 it doesn't filter * out duplicates from the list if this method was called on a {@link Configuration} instance. */
public void setAutoIncludes(List templateNames) { NullArgumentException.check("templateNames", templateNames); // "synchronized" is removed from the API as it's not safe to set anything after publishing the Configuration synchronized (this) { if (autoIncludes != null) { autoIncludes.clear(); } for (Object templateName : templateNames) { if (!(templateName instanceof String)) { throw new IllegalArgumentException("List items must be String-s."); } addAutoInclude((String) templateName, this instanceof Configuration && ((Configuration) this) .getIncompatibleImprovements().intValue() < _TemplateAPI.VERSION_INT_2_3_25); } } }
Getter pair of setAutoIncludes(List); do not modify the returned List! To be consistent with other setting getters, if this setting was set directly on this Configurable object, this simply returns that value, otherwise it returns the value from the parent Configurable. So beware, the returned value doesn't reflect the List concatenation logic that FreeMarker actually uses for this setting. The returned value is not the same List object that was set with setAutoIncludes(List), only its content is the same (except that duplicate are removed). The returned value isn't a snapshot; it may or may not shows the changes later made to this setting on this Configurable level (but usually it's well defined if until what point settings are possibly modified).

The return value is never null; called on the Configuration (top) level, it defaults to an empty List.

See Also:
Since:2.3.25
/** * Getter pair of {@link #setAutoIncludes(List)}; do not modify the returned {@link List}! To be consistent with * other setting getters, if this setting was set directly on this {@link Configurable} object, this simply returns * that value, otherwise it returns the value from the parent {@link Configurable}. So beware, the returned value * doesn't reflect the {@link List} concatenation logic that FreeMarker actually uses for this setting. The returned * value is not the same {@link List} object that was set with {@link #setAutoIncludes(List)}, only its content is * the same (except that duplicate are removed). The returned value isn't a snapshot; it may or may not shows the * changes later made to this setting on this {@link Configurable} level (but usually it's well defined if until * what point settings are possibly modified). * * <p> * The return value is never {@code null}; called on the {@link Configuration} (top) level, it defaults to an empty * {@link List}. * * @see #getAutoIncludesWithoutFallback() * * @since 2.3.25 */
public List<String> getAutoIncludes() { return autoIncludes != null ? autoIncludes : parent.getAutoIncludes(); }
Tells if this setting is set directly in this object or its value is coming from the parent.
Since:2.3.25
/** * Tells if this setting is set directly in this object or its value is coming from the {@link #getParent() parent}. * * @since 2.3.25 */
public boolean isAutoIncludesSet() { return autoIncludes != null; }
Like getAutoIncludes(), but doesn't fall back to the parent Configurable (and so it can be null).
Since:2.3.25
/** * Like {@link #getAutoIncludes()}, but doesn't fall back to the parent {@link Configurable} (and so it can be * {@code null}). * * @since 2.3.25 */
public List<String> getAutoIncludesWithoutFallback() { return autoIncludes; }
Removes the auto-include from this Configurable level (not from the parents or children); see addAutoInclude(String). Does nothing if the template is not there.
/** * Removes the auto-include from this {@link Configurable} level (not from the parents or children); see * {@link #addAutoInclude(String)}. Does nothing if the template is not there. */
public void removeAutoInclude(String templateName) { // "synchronized" is removed from the API as it's not safe to set anything after publishing the Configuration synchronized (this) { if (autoIncludes != null) { autoIncludes.remove(templateName); } } } private static final String ALLOWED_CLASSES_SNAKE_CASE = "allowed_classes"; private static final String TRUSTED_TEMPLATES_SNAKE_CASE = "trusted_templates"; private static final String ALLOWED_CLASSES_CAMEL_CASE = "allowedClasses"; private static final String TRUSTED_TEMPLATES_CAMEL_CASE = "trustedTemplates";
Sets a FreeMarker setting by a name and string value. If you can configure FreeMarker directly with Java (or other programming language), you should use the dedicated setter methods instead (like setObjectWrapper(ObjectWrapper). This meant to be used only when you get settings from somewhere as String-String name-value pairs (typically, as a Properties object). Below you find an overview of the settings available.

Note: As of FreeMarker 2.3.23, setting names can be written in camel case too. For example, instead of date_format you can also use dateFormat. It's likely that camel case will become to the recommended convention in the future.

The list of settings commonly supported in all Configurable subclasses:

Configuration (a subclass of Configurable) also understands these:

Regarding object builder expressions (used by the setting values where it was indicated):

  • Before FreeMarker 2.3.21 it had to be a fully qualified class name, and nothing else.

  • Since 2.3.21, the generic syntax is: className(constrArg1, constrArg2, ... constrArgN, propName1=propValue1, propName2=propValue2, ... propNameN=propValueN), where className is the fully qualified class name of the instance to create (except if we have builder class or INSTANCE field around, but see that later), constrArg-s are the values of constructor arguments, and propName=propValue-s set JavaBean properties (like x=1 means setX(1)) on the created instance. You can have any number of constructor arguments and property setters, including 0. Constructor arguments must precede any property setters.

  • Example: com.example.MyObjectWrapper(1, 2, exposeFields=true, cacheSize=5000) is nearly equivalent with this Java code: obj = new com.example.MyObjectWrapper(1, 2); obj.setExposeFields(true); obj.setCacheSize(5000);
  • If you have no constructor arguments and property setters, and the className class has a public static INSTANCE field, the value of that filed will be the value of the expression, and the constructor won't be called. Note that if you use the backward compatible syntax, where these's no parenthesis after the class name, then it will not look for INSTANCE.

  • If there exists a class named classNameBuilder, then that class will be instantiated instead with the given constructor arguments, and the JavaBean properties of that builder instance will be set. After that, the public build() method of the instance will be called, whose return value will be the value of the whole expression. (The builder class and the build() method is simply found by name, there's no special interface to implement.) Note that if you use the backward compatible syntax, where these's no parenthesis after the class name, then it will not look for builder class. Note that if you have a builder class, you don't actually need a className class (since 2.3.24); after all, classNameBuilder.build() can return any kind of object.

  • Currently, the values of arguments and properties can only be one of these:

    • A numerical literal, like 123 or -1.5. The value will be automatically converted to the type of the target (just like in FTL). However, a target type is only available if the number will be a parameter to a method or constructor, not when it's a value (or key) in a List or Map literal. Thus in the last case the type of number will be like in Java language, like 1 is an int, and 1.0 is a double, and 1.0f is a float, etc. In all cases, the standard Java type postfixes can be used ("f", "d", "l"), plus "bd" for BigDecimal and "bi" for BigInteger.
    • A boolean literal: true or false
    • The null literal: null
    • A string literal with FTL syntax, except that it can't contain ${...}-s and #{...}-s. Examples: "Line 1\nLine 2" or r"C:\temp".
    • A list literal (since 2.3.24) with FTL-like syntax, for example [ 'foo', 2, true ]. If the parameter is expected to be array, the list will be automatically converted to array. The list items can be any kind of expression, like even object builder expressions.
    • A map literal (since 2.3.24) with FTL-like syntax, for example { 'foo': 2, 'bar': true }. The keys and values can be any kind of expression, like even object builder expressions. The resulting Java object will be a Map that keeps the item order (LinkedHashMap as of this writing).
    • A reference to a public static filed, like Configuration.AUTO_DETECT_TAG_SYNTAX or com.example.MyClass.MY_CONSTANT.
    • An object builder expression. That is, object builder expressions can be nested into each other.
  • The same kind of expression as for parameters can also be used as top-level expressions (though it's rarely useful, apart from using null).
  • The top-level object builder expressions may omit (). In that case, for backward compatibility, the INSTANCE field and the builder class is not searched, so the instance will be always created with its parameterless constructor. (This behavior will possibly change in 2.4.) The () can't be omitted for nested expressions.

  • The following classes can be referred to with simple (unqualified) name instead of fully qualified name: DefaultObjectWrapper, BeansWrapper, SimpleObjectWrapper, Locale, TemplateConfiguration, PathGlobMatcher, FileNameGlobMatcher, PathRegexMatcher, AndMatcher, OrMatcher, NotMatcher, ConditionalTemplateConfigurationFactory, MergingTemplateConfigurationFactory, FirstMatchTemplateConfigurationFactory, HTMLOutputFormat, XMLOutputFormat, RTFOutputFormat, PlainTextOutputFormat, UndefinedOutputFormat, Configuration.

  • TimeZone objects can be created like TimeZone("UTC"), despite that there's no a such constructor (since 2.3.24).

  • The classes and methods that the expression meant to access must be all public.

Params:
  • name – the name of the setting.
  • value – the string that describes the new value of the setting.
Throws:
/** * Sets a FreeMarker setting by a name and string value. If you can configure FreeMarker directly with Java (or * other programming language), you should use the dedicated setter methods instead (like * {@link #setObjectWrapper(ObjectWrapper)}. This meant to be used only when you get settings from somewhere * as {@link String}-{@link String} name-value pairs (typically, as a {@link Properties} object). Below you find an * overview of the settings available. * * <p>Note: As of FreeMarker 2.3.23, setting names can be written in camel case too. For example, instead of * {@code date_format} you can also use {@code dateFormat}. It's likely that camel case will become to the * recommended convention in the future. * * <p>The list of settings commonly supported in all {@link Configurable} subclasses: * <ul> * <li><p>{@code "locale"}: * See {@link #setLocale(Locale)}. * <br>String value: local codes with the usual format in Java, such as {@code "en_US"}, or since 2.3.26, * "JVM default" (ignoring case) to use the default locale of the Java environment. * * <li><p>{@code "classic_compatible"}: * See {@link #setClassicCompatible(boolean)} and {@link Configurable#setClassicCompatibleAsInt(int)}. * <br>String value: {@code "true"}, {@code "false"}, also since 2.3.20 {@code 0} or {@code 1} or {@code 2}. * (Also accepts {@code "yes"}, {@code "no"}, {@code "t"}, {@code "f"}, {@code "y"}, {@code "n"}.) * Case insensitive. * * <li><p>{@code "custom_number_formats"}: See {@link #setCustomNumberFormats(Map)}. * <br>String value: Interpreted as an <a href="#fm_obe">object builder expression</a>. * <br>Example: <code>{ "hex": com.example.HexTemplateNumberFormatFactory, * "gps": com.example.GPSTemplateNumberFormatFactory }</code> * * <li><p>{@code "custom_date_formats"}: See {@link #setCustomDateFormats(Map)}. * <br>String value: Interpreted as an <a href="#fm_obe">object builder expression</a>. * <br>Example: <code>{ "trade": com.example.TradeTemplateDateFormatFactory, * "log": com.example.LogTemplateDateFormatFactory }</code> * * <li><p>{@code "template_exception_handler"}: * See {@link #setTemplateExceptionHandler(TemplateExceptionHandler)}. * <br>String value: If the value contains dot, then it's interpreted as an <a href="#fm_obe">object builder * expression</a>. * If the value does not contain dot, then it must be one of these predefined values (case insensitive): * {@code "rethrow"} (means {@link TemplateExceptionHandler#RETHROW_HANDLER}), * {@code "debug"} (means {@link TemplateExceptionHandler#DEBUG_HANDLER}), * {@code "html_debug"} (means {@link TemplateExceptionHandler#HTML_DEBUG_HANDLER}), * {@code "ignore"} (means {@link TemplateExceptionHandler#IGNORE_HANDLER}), or * {@code "default"} (only allowed for {@link Configuration} instances) for the default value. * * <li><p>{@code "attempt_exception_reporter"}: * See {@link #setAttemptExceptionReporter(AttemptExceptionReporter)}. * <br>String value: If the value contains dot, then it's interpreted as an <a href="#fm_obe">object builder * expression</a>. * If the value does not contain dot, then it must be one of these predefined values (case insensitive): * {@code "log_error"} (means {@link AttemptExceptionReporter#LOG_ERROR_REPORTER}), * {@code "log_warn"} (means {@link AttemptExceptionReporter#LOG_WARN_REPORTER}), or * {@code "default"} (only allowed for {@link Configuration} instances) for the default value. * * <li><p>{@code "arithmetic_engine"}: * See {@link #setArithmeticEngine(ArithmeticEngine)}. * <br>String value: If the value contains dot, then it's interpreted as an <a href="#fm_obe">object builder * expression</a>. * If the value does not contain dot, * then it must be one of these special values (case insensitive): * {@code "bigdecimal"}, {@code "conservative"}. * * <li><p>{@code "object_wrapper"}: * See {@link #setObjectWrapper(ObjectWrapper)}. * <br>String value: If the value contains dot, then it's interpreted as an <a href="#fm_obe">object builder * expression</a>, with the addition that {@link BeansWrapper}, {@link DefaultObjectWrapper} and * {@link SimpleObjectWrapper} can be referred without package name. For example, these strings are valid * values: {@code "DefaultObjectWrapper(2.3.21, forceLegacyNonListCollections=false, iterableSupport=true)"}, * {@code "BeansWrapper(2.3.21, simpleMapWrapper=true)"}. * <br>If the value does not contain dot, then it must be one of these special values (case insensitive): * {@code "default"} means the default of {@link Configuration} (the default depends on the * {@code Configuration#Configuration(Version) incompatible_improvements}, but a bug existed in 2.3.21 where * that was ignored), * {@code "default_2_3_0"} (means the deprecated {@link ObjectWrapper#DEFAULT_WRAPPER}) * {@code "simple"} (means the deprecated {@link ObjectWrapper#SIMPLE_WRAPPER}), * {@code "beans"} (means the deprecated {@link BeansWrapper#BEANS_WRAPPER} * or {@link BeansWrapperBuilder#build()}), * {@code "jython"} (means {@link freemarker.ext.jython.JythonWrapper#DEFAULT_WRAPPER}) * * <li><p>{@code "number_format"}: See {@link #setNumberFormat(String)}. * * <li><p>{@code "boolean_format"}: See {@link #setBooleanFormat(String)} . * * <li><p>{@code "date_format", "time_format", "datetime_format"}: * See {@link #setDateFormat(String)}, {@link #setTimeFormat(String)}, {@link #setDateTimeFormat(String)}. * * <li><p>{@code "time_zone"}: * See {@link #setTimeZone(TimeZone)}. * <br>String value: With the format as {@link TimeZone#getTimeZone} defines it. Also, since 2.3.21 * {@code "JVM default"} can be used that will be replaced with the actual JVM default time zone when * {@link #setSetting(String, String)} is called. * For example {@code "GMT-8:00"} or {@code "America/Los_Angeles"} * <br>If you set this setting, consider setting {@code sql_date_and_time_time_zone} * too (see below)! * * <li><p>{@code sql_date_and_time_time_zone}: * See {@link #setSQLDateAndTimeTimeZone(TimeZone)}. * Since 2.3.21. * <br>String value: With the format as {@link TimeZone#getTimeZone} defines it. Also, {@code "JVM default"} * can be used that will be replaced with the actual JVM default time zone when * {@link #setSetting(String, String)} is called. Also {@code "null"} can be used, which has the same effect * as {@link #setSQLDateAndTimeTimeZone(TimeZone) setSQLDateAndTimeTimeZone(null)}. * * <li><p>{@code "output_encoding"}: * See {@link #setOutputEncoding(String)}. * * <li><p>{@code "url_escaping_charset"}: * See {@link #setURLEscapingCharset(String)}. * * <li><p>{@code "auto_flush"}: * See {@link #setAutoFlush(boolean)}. * Since 2.3.17. * <br>String value: {@code "true"}, {@code "false"}, {@code "y"}, etc. * * <li><p>{@code "auto_import"}: * See {@link Configuration#setAutoImports(Map)} * <br>String value is something like: * <br>{@code /lib/form.ftl as f, /lib/widget as w, "/lib/odd name.ftl" as odd} * * <li><p>{@code "auto_include"}: Sets the list of auto-includes. * See {@link Configuration#setAutoIncludes(List)} * <br>String value is something like: * <br>{@code /include/common.ftl, "/include/evil name.ftl"} * * <li><p>{@code "lazy_auto_imports"}: * See {@link Configuration#setLazyAutoImports(Boolean)}. * <br>String value: {@code "true"}, {@code "false"} (also the equivalents: {@code "yes"}, {@code "no"}, * {@code "t"}, {@code "f"}, {@code "y"}, {@code "n"}), case insensitive. Also can be {@code "null"}. * <li><p>{@code "lazy_imports"}: * See {@link Configuration#setLazyImports(boolean)}. * <br>String value: {@code "true"}, {@code "false"} (also the equivalents: {@code "yes"}, {@code "no"}, * {@code "t"}, {@code "f"}, {@code "y"}, {@code "n"}), case insensitive. * * <li><p>{@code "new_builtin_class_resolver"}: * See {@link #setNewBuiltinClassResolver(TemplateClassResolver)}. * Since 2.3.17. * The value must be one of these (ignore the quotation marks): * <ol> * <li><p>{@code "unrestricted"}: * Use {@link TemplateClassResolver#UNRESTRICTED_RESOLVER} * <li><p>{@code "safer"}: * Use {@link TemplateClassResolver#SAFER_RESOLVER} * <li><p>{@code "allows_nothing"} (or {@code "allowsNothing"}): * Use {@link TemplateClassResolver#ALLOWS_NOTHING_RESOLVER} * <li><p>Something that contains colon will use * {@link OptInTemplateClassResolver} and is expected to * store comma separated values (possibly quoted) segmented * with {@code "allowed_classes:"} (or {@code "allowedClasses:"}) and/or * {@code "trusted_templates:"} (or {@code "trustedTemplates:"}). Examples of valid values: * * <table style="width: auto; border-collapse: collapse" border="1" * summary="trusted_template value examples"> * <tr> * <th>Setting value * <th>Meaning * <tr> * <td> * {@code allowed_classes: com.example.C1, com.example.C2, * trusted_templates: lib/*, safe.ftl} * <td> * Only allow instantiating the {@code com.example.C1} and * {@code com.example.C2} classes. But, allow templates * within the {@code lib/} directory (like * {@code lib/foo/bar.ftl}) and template {@code safe.ftl} * (that does not match {@code foo/safe.ftl}, only * exactly {@code safe.ftl}) to instantiate anything * that {@link TemplateClassResolver#SAFER_RESOLVER} allows. * <tr> * <td> * {@code allowed_classes: com.example.C1, com.example.C2} * <td>Only allow instantiating the {@code com.example.C1} and * {@code com.example.C2} classes. There are no * trusted templates. * <tr> * <td> {@code trusted_templates: lib/*, safe.ftl} * <td> * Do not allow instantiating any classes, except in * templates inside {@code lib/} or in template * {@code safe.ftl}. * </table> * * <p>For more details see {@link OptInTemplateClassResolver}. * * <li><p>Otherwise if the value contains dot, it's interpreted as an <a href="#fm_obe">object builder * expression</a>. * </ol> * * <li><p>{@code "show_error_tips"}: * See {@link #setShowErrorTips(boolean)}. * Since 2.3.21. * <br>String value: {@code "true"}, {@code "false"}, {@code "y"}, etc. * * <li><p>{@code api_builtin_enabled}: * See {@link #setAPIBuiltinEnabled(boolean)}. * Since 2.3.22. * <br>String value: {@code "true"}, {@code "false"}, {@code "y"}, etc. * * </ul> * * <p>{@link Configuration} (a subclass of {@link Configurable}) also understands these:</p> * <ul> * <li><p>{@code "auto_escaping"}: * See {@link Configuration#setAutoEscapingPolicy(int)} * <br>String value: {@code "enable_if_default"} or {@code "enableIfDefault"} for * {@link Configuration#ENABLE_IF_DEFAULT_AUTO_ESCAPING_POLICY}, * {@code "enable_if_supported"} or {@code "enableIfSupported"} for * {@link Configuration#ENABLE_IF_SUPPORTED_AUTO_ESCAPING_POLICY} * {@code "disable"} for {@link Configuration#DISABLE_AUTO_ESCAPING_POLICY}. * * <li><p>{@code "default_encoding"}: * See {@link Configuration#setDefaultEncoding(String)}; since 2.3.26 also accepts value "JVM default" * (not case sensitive) to set the Java environment default value. * <br>As the default value is the system default, which can change * from one server to another, <b>you should always set this!</b> * * <li><p>{@code "localized_lookup"}: * See {@link Configuration#setLocalizedLookup}. * <br>String value: {@code "true"}, {@code "false"} (also the equivalents: {@code "yes"}, {@code "no"}, * {@code "t"}, {@code "f"}, {@code "y"}, {@code "n"}). * Case insensitive. * * <li><p>{@code "output_format"}: * See {@link Configuration#setOutputFormat(OutputFormat)}. * <br>String value: {@code "default"} (case insensitive) for the default, or an * <a href="#fm_obe">object builder expression</a> that gives an {@link OutputFormat}, for example * {@code HTMLOutputFormat} or {@code XMLOutputFormat}. * * <li><p>{@code "registered_custom_output_formats"}: * See {@link Configuration#setRegisteredCustomOutputFormats(Collection)}. * <br>String value: an <a href="#fm_obe">object builder expression</a> that gives a {@link List} of * {@link OutputFormat}-s. * Example: {@code [com.example.MyOutputFormat(), com.example.MyOtherOutputFormat()]} * * <li><p>{@code "strict_syntax"}: * See {@link Configuration#setStrictSyntaxMode}. Deprecated. * <br>String value: {@code "true"}, {@code "false"}, {@code yes}, etc. * * <li><p>{@code "whitespace_stripping"}: * See {@link Configuration#setWhitespaceStripping}. * <br>String value: {@code "true"}, {@code "false"}, {@code yes}, etc. * * <li><p>{@code "cache_storage"}: * See {@link Configuration#setCacheStorage}. * <br>String value: If the value contains dot, then it's interpreted as an <a href="#fm_obe">object builder * expression</a>. * If the value does not contain dot, * then a {@link freemarker.cache.MruCacheStorage} will be used with the * maximum strong and soft sizes specified with the setting value. Examples * of valid setting values: * * <table style="width: auto; border-collapse: collapse" border="1" summary="cache_storage value examples"> * <tr><th>Setting value<th>max. strong size<th>max. soft size * <tr><td>{@code "strong:50, soft:500"}<td>50<td>500 * <tr><td>{@code "strong:100, soft"}<td>100<td>{@code Integer.MAX_VALUE} * <tr><td>{@code "strong:100"}<td>100<td>0 * <tr><td>{@code "soft:100"}<td>0<td>100 * <tr><td>{@code "strong"}<td>{@code Integer.MAX_VALUE}<td>0 * <tr><td>{@code "soft"}<td>0<td>{@code Integer.MAX_VALUE} * </table> * * <p>The value is not case sensitive. The order of <tt>soft</tt> and <tt>strong</tt> * entries is not significant. * * <li><p>{@code "template_update_delay"}: * Template update delay in <b>seconds</b> (not in milliseconds) if no unit is specified; see * {@link Configuration#setTemplateUpdateDelayMilliseconds(long)} for more. * <br>String value: Valid positive integer, optionally followed by a time unit (recommended). The default * unit is seconds. It's strongly recommended to specify the unit for clarity, like in "500 ms" or "30 s". * Supported units are: "s" (seconds), "ms" (milliseconds), "m" (minutes), "h" (hours). The whitespace between * the unit and the number is optional. Units are only supported since 2.3.23. * * <li><p>{@code "tag_syntax"}: * See {@link Configuration#setTagSyntax(int)}. * <br>String value: Must be one of * {@code "auto_detect"}, {@code "angle_bracket"}, and {@code "square_bracket"} (like {@code [#if x]}). * <br>Note that setting the {@code "tagSyntax"} to {@code "square_bracket"} does <em>not</em> change * <code>${x}</code> to {@code [=...]}; that's <em>interpolation</em> syntax, so use the * {@code "interpolation_syntax"} setting for that, not this setting. * * <li><p>{@code "interpolation_syntax"} (since 2.3.28): * See {@link Configuration#setInterpolationSyntax(int)}. * <br>String value: Must be one of * {@code "legacy"}, {@code "dollar"}, and {@code "square_bracket"} (like {@code [=x]}). * <br>Note that setting the {@code "interpolation_syntax"} to {@code "square_bracket"} does <em>not</em> * change {@code <#if x>} to {@code [#if x]}; that's <em>tag</em> syntax, so use the * {@code "tag_syntax"} setting for that, not this setting. * * <li><p>{@code "naming_convention"}: * See {@link Configuration#setNamingConvention(int)}. * <br>String value: Must be one of * {@code "auto_detect"}, {@code "legacy"}, and {@code "camel_case"}. * * <li><p>{@code "incompatible_improvements"}: * See {@link Configuration#setIncompatibleImprovements(Version)}. * <br>String value: version number like {@code 2.3.20}. * * <li><p>{@code "incompatible_enhancements"}: * See: {@link Configuration#setIncompatibleEnhancements(String)}. * This setting name is deprecated, use {@code "incompatible_improvements"} instead. * * <li><p>{@code "recognize_standard_file_extensions"}: * See {@link Configuration#setRecognizeStandardFileExtensions(boolean)}. * <br>String value: {@code "default"} (case insensitive) for the default, or {@code "true"}, {@code "false"}, * {@code yes}, etc. * * <li><p>{@code "template_configurations"}: * See: {@link Configuration#setTemplateConfigurations(freemarker.cache.TemplateConfigurationFactory)}. * <br>String value: Interpreted as an <a href="#fm_obe">object builder expression</a>, * can be {@code null}. * * <li><p>{@code "template_loader"}: * See: {@link Configuration#setTemplateLoader(TemplateLoader)}. * <br>String value: {@code "default"} (case insensitive) for the default, or else interpreted as an * <a href="#fm_obe">object builder expression</a>. {@code "null"} is also allowed since 2.3.26. * * <li><p>{@code "template_lookup_strategy"}: * See: {@link Configuration#setTemplateLookupStrategy(freemarker.cache.TemplateLookupStrategy)}. * <br>String value: {@code "default"} (case insensitive) for the default, or else interpreted as an * <a href="#fm_obe">object builder expression</a>. * * <li><p>{@code "template_name_format"}: * See: {@link Configuration#setTemplateNameFormat(freemarker.cache.TemplateNameFormat)}. * <br>String value: {@code "default"} (case insensitive) for the default, {@code "default_2_3_0"} * for {@link freemarker.cache.TemplateNameFormat#DEFAULT_2_3_0}, {@code "default_2_4_0"} for * {@link freemarker.cache.TemplateNameFormat#DEFAULT_2_4_0}. * </ul> * * <p><a name="fm_obe"></a>Regarding <em>object builder expressions</em> (used by the setting values where it was * indicated): * <ul> * <li><p>Before FreeMarker 2.3.21 it had to be a fully qualified class name, and nothing else.</li> * <li><p>Since 2.3.21, the generic syntax is: * <tt><i>className</i>(<i>constrArg1</i>, <i>constrArg2</i>, ... <i>constrArgN</i>, * <i>propName1</i>=<i>propValue1</i>, <i>propName2</i>=<i>propValue2</i>, ... * <i>propNameN</i>=<i>propValueN</i>)</tt>, * where * <tt><i>className</i></tt> is the fully qualified class name of the instance to create (except if we have * builder class or <tt>INSTANCE</tt> field around, but see that later), * <tt><i>constrArg</i></tt>-s are the values of constructor arguments, * and <tt><i>propName</i>=<i>propValue</i></tt>-s set JavaBean properties (like <tt>x=1</tt> means * <tt>setX(1)</tt>) on the created instance. You can have any number of constructor arguments and property * setters, including 0. Constructor arguments must precede any property setters. * </li> * <li> * Example: <tt>com.example.MyObjectWrapper(1, 2, exposeFields=true, cacheSize=5000)</tt> is nearly * equivalent with this Java code: * <tt>obj = new com.example.MyObjectWrapper(1, 2); obj.setExposeFields(true); obj.setCacheSize(5000);</tt> * </li> * <li> * <p>If you have no constructor arguments and property setters, and the <tt><i>className</i></tt> class has * a public static {@code INSTANCE} field, the value of that filed will be the value of the expression, and * the constructor won't be called. Note that if you use the backward compatible * syntax, where these's no parenthesis after the class name, then it will not look for {@code INSTANCE}. * </li> * <li> * <p>If there exists a class named <tt><i>className</i>Builder</tt>, then that class will be instantiated * instead with the given constructor arguments, and the JavaBean properties of that builder instance will be * set. After that, the public <tt>build()</tt> method of the instance will be called, whose return value * will be the value of the whole expression. (The builder class and the <tt>build()</tt> method is simply * found by name, there's no special interface to implement.) Note that if you use the backward compatible * syntax, where these's no parenthesis after the class name, then it will not look for builder class. Note * that if you have a builder class, you don't actually need a <tt><i>className</i></tt> class (since 2.3.24); * after all, <tt><i>className</i>Builder.build()</tt> can return any kind of object. * </li> * <li> * <p>Currently, the values of arguments and properties can only be one of these: * <ul> * <li>A numerical literal, like {@code 123} or {@code -1.5}. The value will be automatically converted to * the type of the target (just like in FTL). However, a target type is only available if the number will * be a parameter to a method or constructor, not when it's a value (or key) in a {@code List} or * {@code Map} literal. Thus in the last case the type of number will be like in Java language, like * {@code 1} is an {@code int}, and {@code 1.0} is a {@code double}, and {@code 1.0f} is a {@code float}, * etc. In all cases, the standard Java type postfixes can be used ("f", "d", "l"), plus "bd" for * {@code BigDecimal} and "bi" for {@code BigInteger}.</li> * <li>A boolean literal: {@code true} or {@code false} * <li>The null literal: {@code null} * <li>A string literal with FTL syntax, except that it can't contain <tt>${...}</tt>-s and * <tt>#{...}</tt>-s. Examples: {@code "Line 1\nLine 2"} or {@code r"C:\temp"}. * <li>A list literal (since 2.3.24) with FTL-like syntax, for example {@code [ 'foo', 2, true ]}. * If the parameter is expected to be array, the list will be automatically converted to array. * The list items can be any kind of expression, like even object builder expressions. * <li>A map literal (since 2.3.24) with FTL-like syntax, for example <code>{ 'foo': 2, 'bar': true }</code>. * The keys and values can be any kind of expression, like even object builder expressions. * The resulting Java object will be a {@link Map} that keeps the item order ({@link LinkedHashMap} as * of this writing). * <li>A reference to a public static filed, like {@code Configuration.AUTO_DETECT_TAG_SYNTAX} or * {@code com.example.MyClass.MY_CONSTANT}. * <li>An object builder expression. That is, object builder expressions can be nested into each other. * </ul> * </li> * <li> * The same kind of expression as for parameters can also be used as top-level expressions (though it's * rarely useful, apart from using {@code null}). * </li> * <li> * <p>The top-level object builder expressions may omit {@code ()}. In that case, for backward compatibility, * the {@code INSTANCE} field and the builder class is not searched, so the instance will be always * created with its parameterless constructor. (This behavior will possibly change in 2.4.) The {@code ()} * can't be omitted for nested expressions. * </li> * <li> * <p>The following classes can be referred to with simple (unqualified) name instead of fully qualified name: * {@link DefaultObjectWrapper}, {@link BeansWrapper}, {@link SimpleObjectWrapper}, {@link Locale}, * {@link TemplateConfiguration}, {@link PathGlobMatcher}, {@link FileNameGlobMatcher}, {@link PathRegexMatcher}, * {@link AndMatcher}, {@link OrMatcher}, {@link NotMatcher}, {@link ConditionalTemplateConfigurationFactory}, * {@link MergingTemplateConfigurationFactory}, {@link FirstMatchTemplateConfigurationFactory}, * {@link HTMLOutputFormat}, {@link XMLOutputFormat}, {@link RTFOutputFormat}, {@link PlainTextOutputFormat}, * {@link UndefinedOutputFormat}, {@link Configuration}. * </li> * <li> * <p>{@link TimeZone} objects can be created like {@code TimeZone("UTC")}, despite that there's no a such * constructor (since 2.3.24). * </li> * <li> * <p>The classes and methods that the expression meant to access must be all public. * </li> * </ul> * * @param name the name of the setting. * @param value the string that describes the new value of the setting. * * @throws UnknownSettingException if the name is wrong. * @throws TemplateException if the new value of the setting can't be set for any other reasons. */
public void setSetting(String name, String value) throws TemplateException { boolean unknown = false; try { if (LOCALE_KEY.equals(name)) { if (JVM_DEFAULT.equalsIgnoreCase(value)) { setLocale(Locale.getDefault()); } else { setLocale(StringUtil.deduceLocale(value)); } } else if (NUMBER_FORMAT_KEY_SNAKE_CASE.equals(name) || NUMBER_FORMAT_KEY_CAMEL_CASE.equals(name)) { setNumberFormat(value); } else if (CUSTOM_NUMBER_FORMATS_KEY_SNAKE_CASE.equals(name) || CUSTOM_NUMBER_FORMATS_KEY_CAMEL_CASE.equals(name)) { Map map = (Map) _ObjectBuilderSettingEvaluator.eval( value, Map.class, false, _SettingEvaluationEnvironment.getCurrent()); _CoreAPI.checkSettingValueItemsType("Map keys", String.class, map.keySet()); _CoreAPI.checkSettingValueItemsType("Map values", TemplateNumberFormatFactory.class, map.values()); setCustomNumberFormats(map); } else if (TIME_FORMAT_KEY_SNAKE_CASE.equals(name) || TIME_FORMAT_KEY_CAMEL_CASE.equals(name)) { setTimeFormat(value); } else if (DATE_FORMAT_KEY_SNAKE_CASE.equals(name) || DATE_FORMAT_KEY_CAMEL_CASE.equals(name)) { setDateFormat(value); } else if (DATETIME_FORMAT_KEY_SNAKE_CASE.equals(name) || DATETIME_FORMAT_KEY_CAMEL_CASE.equals(name)) { setDateTimeFormat(value); } else if (CUSTOM_DATE_FORMATS_KEY_SNAKE_CASE.equals(name) || CUSTOM_DATE_FORMATS_KEY_CAMEL_CASE.equals(name)) { Map map = (Map) _ObjectBuilderSettingEvaluator.eval( value, Map.class, false, _SettingEvaluationEnvironment.getCurrent()); _CoreAPI.checkSettingValueItemsType("Map keys", String.class, map.keySet()); _CoreAPI.checkSettingValueItemsType("Map values", TemplateDateFormatFactory.class, map.values()); setCustomDateFormats(map); } else if (TIME_ZONE_KEY_SNAKE_CASE.equals(name) || TIME_ZONE_KEY_CAMEL_CASE.equals(name)) { setTimeZone(parseTimeZoneSettingValue(value)); } else if (SQL_DATE_AND_TIME_TIME_ZONE_KEY_SNAKE_CASE.equals(name) || SQL_DATE_AND_TIME_TIME_ZONE_KEY_CAMEL_CASE.equals(name)) { setSQLDateAndTimeTimeZone(value.equals("null") ? null : parseTimeZoneSettingValue(value)); } else if (CLASSIC_COMPATIBLE_KEY_SNAKE_CASE.equals(name) || CLASSIC_COMPATIBLE_KEY_CAMEL_CASE.equals(name)) { char firstChar; if (value != null && value.length() > 0) { firstChar = value.charAt(0); } else { firstChar = 0; } if (Character.isDigit(firstChar) || firstChar == '+' || firstChar == '-') { setClassicCompatibleAsInt(Integer.parseInt(value)); } else { setClassicCompatible(value != null ? StringUtil.getYesNo(value) : false); } } else if (TEMPLATE_EXCEPTION_HANDLER_KEY_SNAKE_CASE.equals(name) || TEMPLATE_EXCEPTION_HANDLER_KEY_CAMEL_CASE.equals(name)) { if (value.indexOf('.') == -1) { if ("debug".equalsIgnoreCase(value)) { setTemplateExceptionHandler( TemplateExceptionHandler.DEBUG_HANDLER); } else if ("html_debug".equalsIgnoreCase(value) || "htmlDebug".equals(value)) { setTemplateExceptionHandler( TemplateExceptionHandler.HTML_DEBUG_HANDLER); } else if ("ignore".equalsIgnoreCase(value)) { setTemplateExceptionHandler( TemplateExceptionHandler.IGNORE_HANDLER); } else if ("rethrow".equalsIgnoreCase(value)) { setTemplateExceptionHandler( TemplateExceptionHandler.RETHROW_HANDLER); } else if (DEFAULT.equalsIgnoreCase(value) && this instanceof Configuration) { ((Configuration) this).unsetTemplateExceptionHandler(); } else { throw invalidSettingValueException(name, value); } } else { setTemplateExceptionHandler((TemplateExceptionHandler) _ObjectBuilderSettingEvaluator.eval( value, TemplateExceptionHandler.class, false, _SettingEvaluationEnvironment.getCurrent())); } } else if (ATTEMPT_EXCEPTION_REPORTER_KEY_SNAKE_CASE.equals(name) || ATTEMPT_EXCEPTION_REPORTER_KEY_CAMEL_CASE.equals(name)) { if (value.indexOf('.') == -1) { if ("log_error".equalsIgnoreCase(value) || "logError".equals(value)) { setAttemptExceptionReporter( AttemptExceptionReporter.LOG_ERROR_REPORTER); } else if ("log_warn".equalsIgnoreCase(value) || "logWarn".equals(value)) { setAttemptExceptionReporter( AttemptExceptionReporter.LOG_WARN_REPORTER); } else if (DEFAULT.equalsIgnoreCase(value) && this instanceof Configuration) { ((Configuration) this).unsetAttemptExceptionReporter(); } else { throw invalidSettingValueException(name, value); } } else { setAttemptExceptionReporter((AttemptExceptionReporter) _ObjectBuilderSettingEvaluator.eval( value, AttemptExceptionReporter.class, false, _SettingEvaluationEnvironment.getCurrent())); } } else if (ARITHMETIC_ENGINE_KEY_SNAKE_CASE.equals(name) || ARITHMETIC_ENGINE_KEY_CAMEL_CASE.equals(name)) { if (value.indexOf('.') == -1) { if ("bigdecimal".equalsIgnoreCase(value)) { setArithmeticEngine(ArithmeticEngine.BIGDECIMAL_ENGINE); } else if ("conservative".equalsIgnoreCase(value)) { setArithmeticEngine(ArithmeticEngine.CONSERVATIVE_ENGINE); } else { throw invalidSettingValueException(name, value); } } else { setArithmeticEngine((ArithmeticEngine) _ObjectBuilderSettingEvaluator.eval( value, ArithmeticEngine.class, false, _SettingEvaluationEnvironment.getCurrent())); } } else if (OBJECT_WRAPPER_KEY_SNAKE_CASE.equals(name) || OBJECT_WRAPPER_KEY_CAMEL_CASE.equals(name)) { if (DEFAULT.equalsIgnoreCase(value)) { if (this instanceof Configuration) { ((Configuration) this).unsetObjectWrapper(); } else { setObjectWrapper(Configuration.getDefaultObjectWrapper(Configuration.VERSION_2_3_0)); } } else if (DEFAULT_2_3_0.equalsIgnoreCase(value)) { setObjectWrapper(Configuration.getDefaultObjectWrapper(Configuration.VERSION_2_3_0)); } else if ("simple".equalsIgnoreCase(value)) { setObjectWrapper(ObjectWrapper.SIMPLE_WRAPPER); } else if ("beans".equalsIgnoreCase(value)) { setObjectWrapper(ObjectWrapper.BEANS_WRAPPER); } else if ("jython".equalsIgnoreCase(value)) { Class clazz = Class.forName( "freemarker.ext.jython.JythonWrapper"); setObjectWrapper( (ObjectWrapper) clazz.getField("INSTANCE").get(null)); } else { setObjectWrapper((ObjectWrapper) _ObjectBuilderSettingEvaluator.eval( value, ObjectWrapper.class, false, _SettingEvaluationEnvironment.getCurrent())); } } else if (BOOLEAN_FORMAT_KEY_SNAKE_CASE.equals(name) || BOOLEAN_FORMAT_KEY_CAMEL_CASE.equals(name)) { setBooleanFormat(value); } else if (OUTPUT_ENCODING_KEY_SNAKE_CASE.equals(name) || OUTPUT_ENCODING_KEY_CAMEL_CASE.equals(name)) { setOutputEncoding(value); } else if (URL_ESCAPING_CHARSET_KEY_SNAKE_CASE.equals(name) || URL_ESCAPING_CHARSET_KEY_CAMEL_CASE.equals(name)) { setURLEscapingCharset(value); } else if (STRICT_BEAN_MODELS_KEY_SNAKE_CASE.equals(name) || STRICT_BEAN_MODELS_KEY_CAMEL_CASE.equals(name)) { setStrictBeanModels(StringUtil.getYesNo(value)); } else if (AUTO_FLUSH_KEY_SNAKE_CASE.equals(name) || AUTO_FLUSH_KEY_CAMEL_CASE.equals(name)) { setAutoFlush(StringUtil.getYesNo(value)); } else if (SHOW_ERROR_TIPS_KEY_SNAKE_CASE.equals(name) || SHOW_ERROR_TIPS_KEY_CAMEL_CASE.equals(name)) { setShowErrorTips(StringUtil.getYesNo(value)); } else if (API_BUILTIN_ENABLED_KEY_SNAKE_CASE.equals(name) || API_BUILTIN_ENABLED_KEY_CAMEL_CASE.equals(name)) { setAPIBuiltinEnabled(StringUtil.getYesNo(value)); } else if (NEW_BUILTIN_CLASS_RESOLVER_KEY_SNAKE_CASE.equals(name) || NEW_BUILTIN_CLASS_RESOLVER_KEY_CAMEL_CASE.equals(name)) { if ("unrestricted".equals(value)) { setNewBuiltinClassResolver(TemplateClassResolver.UNRESTRICTED_RESOLVER); } else if ("safer".equals(value)) { setNewBuiltinClassResolver(TemplateClassResolver.SAFER_RESOLVER); } else if ("allows_nothing".equals(value) || "allowsNothing".equals(value)) { setNewBuiltinClassResolver(TemplateClassResolver.ALLOWS_NOTHING_RESOLVER); } else if (value.indexOf(":") != -1) { List segments = parseAsSegmentedList(value); Set allowedClasses = null; List trustedTemplates = null; for (int i = 0; i < segments.size(); i++) { KeyValuePair kv = (KeyValuePair) segments.get(i); String segmentKey = (String) kv.getKey(); List segmentValue = (List) kv.getValue(); if (segmentKey.equals(ALLOWED_CLASSES_SNAKE_CASE) || segmentKey.equals(ALLOWED_CLASSES_CAMEL_CASE)) { allowedClasses = new HashSet(segmentValue); } else if (segmentKey.equals(TRUSTED_TEMPLATES_SNAKE_CASE) || segmentKey.equals(TRUSTED_TEMPLATES_CAMEL_CASE)) { trustedTemplates = segmentValue; } else { throw new ParseException( "Unrecognized list segment key: " + StringUtil.jQuote(segmentKey) + ". Supported keys are: " + "\"" + ALLOWED_CLASSES_SNAKE_CASE + "\", " + "\"" + ALLOWED_CLASSES_CAMEL_CASE + "\", " + "\"" + TRUSTED_TEMPLATES_SNAKE_CASE + "\", " + "\"" + TRUSTED_TEMPLATES_CAMEL_CASE + "\". ", 0, 0); } } setNewBuiltinClassResolver( new OptInTemplateClassResolver(allowedClasses, trustedTemplates)); } else if ("allow_nothing".equals(value)) { throw new IllegalArgumentException( "The correct value would be: allows_nothing"); } else if ("allowNothing".equals(value)) { throw new IllegalArgumentException( "The correct value would be: allowsNothing"); } else if (value.indexOf('.') != -1) { setNewBuiltinClassResolver((TemplateClassResolver) _ObjectBuilderSettingEvaluator.eval( value, TemplateClassResolver.class, false, _SettingEvaluationEnvironment.getCurrent())); } else { throw invalidSettingValueException(name, value); } } else if (LOG_TEMPLATE_EXCEPTIONS_KEY_SNAKE_CASE.equals(name) || LOG_TEMPLATE_EXCEPTIONS_KEY_CAMEL_CASE.equals(name)) { setLogTemplateExceptions(StringUtil.getYesNo(value)); } else if (WRAP_UNCHECKED_EXCEPTIONS_KEY_SNAKE_CASE.equals(name) || WRAP_UNCHECKED_EXCEPTIONS_KEY_CAMEL_CASE.equals(name)) { setWrapUncheckedExceptions(StringUtil.getYesNo(value)); } else if (LAZY_AUTO_IMPORTS_KEY_SNAKE_CASE.equals(name) || LAZY_AUTO_IMPORTS_KEY_CAMEL_CASE.equals(name)) { setLazyAutoImports(value.equals(NULL) ? null : Boolean.valueOf(StringUtil.getYesNo(value))); } else if (LAZY_IMPORTS_KEY_SNAKE_CASE.equals(name) || LAZY_IMPORTS_KEY_CAMEL_CASE.equals(name)) { setLazyImports(StringUtil.getYesNo(value)); } else if (AUTO_INCLUDE_KEY_SNAKE_CASE.equals(name) || AUTO_INCLUDE_KEY_CAMEL_CASE.equals(name)) { setAutoIncludes(parseAsList(value)); } else if (AUTO_IMPORT_KEY_SNAKE_CASE.equals(name) || AUTO_IMPORT_KEY_CAMEL_CASE.equals(name)) { setAutoImports(parseAsImportList(value)); } else { unknown = true; } } catch (Exception e) { throw settingValueAssignmentException(name, value, e); } if (unknown) { throw unknownSettingException(name); } }
Returns the valid setting names that aren't Configuration-only.
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 setting names that aren't {@link Configuration}-only. * * @param camelCase * If we want the setting names with camel case naming convention, or with snake case (legacy) naming * convention. * * @see Configuration#getSettingNames(boolean) * * @since 2.3.24 */
public Set<String> getSettingNames(boolean camelCase) { return new _SortedArraySet<String>(camelCase ? SETTING_NAMES_CAMEL_CASE : SETTING_NAMES_SNAKE_CASE); } private TimeZone parseTimeZoneSettingValue(String value) { TimeZone tz; if (JVM_DEFAULT.equalsIgnoreCase(value)) { tz = TimeZone.getDefault(); } else { tz = TimeZone.getTimeZone(value); } return tz; }
Deprecated:Set this on the ObjectWrapper itself.
/** * @deprecated Set this on the {@link ObjectWrapper} itself. */
@Deprecated public void setStrictBeanModels(boolean strict) { if (!(objectWrapper instanceof BeansWrapper)) { throw new IllegalStateException("The value of the " + OBJECT_WRAPPER_KEY + " setting isn't a " + BeansWrapper.class.getName() + "."); } ((BeansWrapper) objectWrapper).setStrict(strict); }
Returns the textual representation of a setting.
Params:
  • key – the setting key. Can be any of standard XXX_KEY constants, or a custom key.
Deprecated:It's not possible in general to convert setting values to string, and thus it's impossible to ensure that setSetting(String, String) will work with the returned value correctly.
/** * Returns the textual representation of a setting. * @param key the setting key. Can be any of standard <tt>XXX_KEY</tt> * constants, or a custom key. * * @deprecated It's not possible in general to convert setting values to string, * and thus it's impossible to ensure that {@link #setSetting(String, String)} will work with * the returned value correctly. */
@Deprecated public String getSetting(String key) { return properties.getProperty(key); }
This meant to return the String-to-String Map of the settings. So it actually should return a Properties object, but it doesn't by mistake. The returned Map is read-only, but it will reflect the further configuration changes (aliasing effect).
Deprecated:This method was always defective, and certainly it always will be. Don't use it. (Simply, it's hardly possible in general to convert setting values to text in a way that ensures that setSettings(Properties) will work with them correctly.)
/** * This meant to return the String-to-String <code>Map</code> of the * settings. So it actually should return a <code>Properties</code> object, * but it doesn't by mistake. The returned <code>Map</code> is read-only, * but it will reflect the further configuration changes (aliasing effect). * * @deprecated This method was always defective, and certainly it always * will be. Don't use it. (Simply, it's hardly possible in general to * convert setting values to text in a way that ensures that * {@link #setSettings(Properties)} will work with them correctly.) */
@Deprecated public Map getSettings() { return Collections.unmodifiableMap(properties); } protected Environment getEnvironment() { return this instanceof Environment ? (Environment) this : Environment.getCurrentEnvironment(); }
Creates the exception that should be thrown when a setting name isn't recognized.
/** * Creates the exception that should be thrown when a setting name isn't recognized. */
protected TemplateException unknownSettingException(String name) { return new UnknownSettingException( getEnvironment(), name, getCorrectedNameForUnknownSetting(name)); }
Params:
  • name – The wrong name
Returns:The corrected name, or null if there's no known correction
Since:2.3.21
/** * @param name The wrong name * @return The corrected name, or {@code null} if there's no known correction * @since 2.3.21 */
protected String getCorrectedNameForUnknownSetting(String name) { return null; }
Since:2.3.21
/** * @since 2.3.21 */
protected TemplateException settingValueAssignmentException(String name, String value, Throwable cause) { return new SettingValueAssignmentException(getEnvironment(), name, value, cause); } protected TemplateException invalidSettingValueException(String name, String value) { return new _MiscTemplateException(getEnvironment(), "Invalid value for setting ", new _DelayedJQuote(name), ": ", new _DelayedJQuote(value)); }
The setting name was not recognized.
/** * The setting name was not recognized. */
public static class UnknownSettingException extends _MiscTemplateException { private UnknownSettingException(Environment env, String name, String correctedName) { super(env, "Unknown FreeMarker configuration setting: ", new _DelayedJQuote(name), correctedName == null ? (Object) "" : new Object[] { ". You may meant: ", new _DelayedJQuote(correctedName) }); } }
The setting name was recognized, but its value couldn't be parsed or the setting couldn't be set for some other reason. This exception always has a cause exception.
Since:2.3.21
/** * The setting name was recognized, but its value couldn't be parsed or the setting couldn't be set for some * other reason. This exception always has a cause exception. * * @since 2.3.21 */
public static class SettingValueAssignmentException extends _MiscTemplateException { private SettingValueAssignmentException(Environment env, String name, String value, Throwable cause) { super(cause, env, "Failed to set FreeMarker configuration setting ", new _DelayedJQuote(name), " to value ", new _DelayedJQuote(value), "; see cause exception."); } }
Set the settings stored in a Properties object.
Throws:
  • TemplateException – if the Properties object contains invalid keys, or invalid setting values, or any other error occurs while changing the settings.
/** * Set the settings stored in a <code>Properties</code> object. * * @throws TemplateException if the <code>Properties</code> object contains * invalid keys, or invalid setting values, or any other error occurs * while changing the settings. */
public void setSettings(Properties props) throws TemplateException { final _SettingEvaluationEnvironment prevEnv = _SettingEvaluationEnvironment.startScope(); try { for (Iterator it = props.keySet().iterator(); it.hasNext(); ) { String key = (String) it.next(); setSetting(key, props.getProperty(key).trim()); } } finally { _SettingEvaluationEnvironment.endScope(prevEnv); } }
Reads a setting list (key and element pairs) from the input stream. The stream has to follow the usual .properties format.
Throws:
  • TemplateException – if the stream contains invalid keys, or invalid setting values, or any other error occurs while changing the settings.
  • IOException – if an error occurred when reading from the input stream.
/** * Reads a setting list (key and element pairs) from the input stream. * The stream has to follow the usual <code>.properties</code> format. * * @throws TemplateException if the stream contains * invalid keys, or invalid setting values, or any other error occurs * while changing the settings. * @throws IOException if an error occurred when reading from the input stream. */
public void setSettings(InputStream propsIn) throws TemplateException, IOException { Properties p = new Properties(); p.load(propsIn); setSettings(p); }
Internal entry point for setting unnamed custom attributes.
See Also:
  • CustomAttribute
/** * Internal entry point for setting unnamed custom attributes. * * @see CustomAttribute */
void setCustomAttribute(Object key, Object value) { synchronized (customAttributes) { customAttributes.put(key, value); } }
Internal entry point for getting unnamed custom attributes.
See Also:
  • CustomAttribute
/** * Internal entry point for getting unnamed custom attributes. * * @see CustomAttribute */
Object getCustomAttribute(Object key, CustomAttribute attr) { synchronized (customAttributes) { Object o = customAttributes.get(key); if (o == null && !customAttributes.containsKey(key)) { o = attr.create(); customAttributes.put(key, o); } return o; } } boolean isCustomAttributeSet(Object key) { return customAttributes.containsKey(key); }
For internal usage only, copies the custom attributes set directly on this objects into another Configurable. The target Configurable is assumed to be not seen be other thread than the current one yet. (That is, the operation is not synchronized on the target Configurable, only on the source Configurable)
Since:2.3.24
/** * For internal usage only, copies the custom attributes set directly on this objects into another * {@link Configurable}. The target {@link Configurable} is assumed to be not seen be other thread than the current * one yet. (That is, the operation is not synchronized on the target {@link Configurable}, only on the source * {@link Configurable}) * * @since 2.3.24 */
void copyDirectCustomAttributes(Configurable target, boolean overwriteExisting) { synchronized (customAttributes) { for (Entry<?, ?> custAttrEnt : customAttributes.entrySet()) { Object custAttrKey = custAttrEnt.getKey(); if (overwriteExisting || !target.isCustomAttributeSet(custAttrKey)) { if (custAttrKey instanceof String) { target.setCustomAttribute((String) custAttrKey, custAttrEnt.getValue()); } else { target.setCustomAttribute(custAttrKey, custAttrEnt.getValue()); } } } } }
Sets a named custom attribute for this configurable.
Params:
  • name – the name of the custom attribute
  • value – the value of the custom attribute. You can set the value to null, however note that there is a semantic difference between an attribute set to null and an attribute that is not present, see removeCustomAttribute(String).
/** * Sets a named custom attribute for this configurable. * * @param name the name of the custom attribute * @param value the value of the custom attribute. You can set the value to * null, however note that there is a semantic difference between an * attribute set to null and an attribute that is not present, see * {@link #removeCustomAttribute(String)}. */
public void setCustomAttribute(String name, Object value) { synchronized (customAttributes) { customAttributes.put(name, value); } }
Returns an array with names of all custom attributes defined directly on this configurable. (That is, it doesn't contain the names of custom attributes defined indirectly on its parent configurables.) The returned array is never null, but can be zero-length. The order of elements in the returned array is not defined and can change between invocations.
/** * Returns an array with names of all custom attributes defined directly * on this configurable. (That is, it doesn't contain the names of custom attributes * defined indirectly on its parent configurables.) The returned array is never null, * but can be zero-length. * The order of elements in the returned array is not defined and can change * between invocations. */
public String[] getCustomAttributeNames() { synchronized (customAttributes) { Collection names = new LinkedList(customAttributes.keySet()); for (Iterator iter = names.iterator(); iter.hasNext(); ) { if (!(iter.next() instanceof String)) { iter.remove(); } } return (String[]) names.toArray(new String[names.size()]); } }
Removes a named custom attribute for this configurable. Note that this is different than setting the custom attribute value to null. If you set the value to null, getCustomAttribute(String) will return null, while if you remove the attribute, it will return the value of the attribute in the parent configurable (if there is a parent configurable, that is).
Params:
  • name – the name of the custom attribute
/** * Removes a named custom attribute for this configurable. Note that this * is different than setting the custom attribute value to null. If you * set the value to null, {@link #getCustomAttribute(String)} will return * null, while if you remove the attribute, it will return the value of * the attribute in the parent configurable (if there is a parent * configurable, that is). * * @param name the name of the custom attribute */
public void removeCustomAttribute(String name) { synchronized (customAttributes) { customAttributes.remove(name); } }
Retrieves a named custom attribute for this configurable. If the attribute is not present in the configurable, and the configurable has a parent, then the parent is looked up as well.
Params:
  • name – the name of the custom attribute
Returns:the value of the custom attribute. Note that if the custom attribute was created with <#ftl attributes={...}>, then this value is already unwrapped (i.e. it's a String, or a List, or a Map, ...etc., not a FreeMarker specific class).
/** * Retrieves a named custom attribute for this configurable. If the * attribute is not present in the configurable, and the configurable has * a parent, then the parent is looked up as well. * * @param name the name of the custom attribute * * @return the value of the custom attribute. Note that if the custom attribute * was created with <tt>&lt;#ftl&nbsp;attributes={...}&gt;</tt>, then this value is already * unwrapped (i.e. it's a <code>String</code>, or a <code>List</code>, or a * <code>Map</code>, ...etc., not a FreeMarker specific class). */
public Object getCustomAttribute(String name) { Object retval; synchronized (customAttributes) { retval = customAttributes.get(name); if (retval == null && customAttributes.containsKey(name)) { return null; } } if (retval == null && parent != null) { return parent.getCustomAttribute(name); } return retval; }
Executes the auto-imports and auto-includes for the main template of this environment. This is not meant to be called or overridden by code outside of FreeMarker.
/** * Executes the auto-imports and auto-includes for the main template of this environment. * This is not meant to be called or overridden by code outside of FreeMarker. */
protected void doAutoImportsAndIncludes(Environment env) throws TemplateException, IOException { if (parent != null) parent.doAutoImportsAndIncludes(env); } protected ArrayList parseAsList(String text) throws ParseException { return new SettingStringParser(text).parseAsList(); } protected ArrayList parseAsSegmentedList(String text) throws ParseException { return new SettingStringParser(text).parseAsSegmentedList(); } protected HashMap parseAsImportList(String text) throws ParseException { return new SettingStringParser(text).parseAsImportList(); } private static class KeyValuePair { private final Object key; private final Object value; KeyValuePair(Object key, Object value) { this.key = key; this.value = value; } Object getKey() { return key; } Object getValue() { return value; } }
Helper class for parsing setting values given with string.
/** * Helper class for parsing setting values given with string. */
private static class SettingStringParser { private String text; private int p; private int ln; private SettingStringParser(String text) { this.text = text; this.p = 0; this.ln = text.length(); } ArrayList parseAsSegmentedList() throws ParseException { ArrayList segments = new ArrayList(); ArrayList currentSegment = null; char c; while (true) { c = skipWS(); if (c == ' ') break; String item = fetchStringValue(); c = skipWS(); if (c == ':') { currentSegment = new ArrayList(); segments.add(new KeyValuePair(item, currentSegment)); } else { if (currentSegment == null) { throw new ParseException( "The very first list item must be followed by \":\" so " + "it will be the key for the following sub-list.", 0, 0); } currentSegment.add(item); } if (c == ' ') break; if (c != ',' && c != ':') throw new ParseException( "Expected \",\" or \":\" or the end of text but " + "found \"" + c + "\"", 0, 0); p++; } return segments; } ArrayList parseAsList() throws ParseException { char c; ArrayList seq = new ArrayList(); while (true) { c = skipWS(); if (c == ' ') break; seq.add(fetchStringValue()); c = skipWS(); if (c == ' ') break; if (c != ',') throw new ParseException( "Expected \",\" or the end of text but " + "found \"" + c + "\"", 0, 0); p++; } return seq; } HashMap parseAsImportList() throws ParseException { char c; HashMap map = new HashMap(); while (true) { c = skipWS(); if (c == ' ') break; String lib = fetchStringValue(); c = skipWS(); if (c == ' ') throw new ParseException( "Unexpected end of text: expected \"as\"", 0, 0); String s = fetchKeyword(); if (!s.equalsIgnoreCase("as")) throw new ParseException( "Expected \"as\", but found " + StringUtil.jQuote(s), 0, 0); c = skipWS(); if (c == ' ') throw new ParseException( "Unexpected end of text: expected gate hash name", 0, 0); String ns = fetchStringValue(); map.put(ns, lib); c = skipWS(); if (c == ' ') break; if (c != ',') throw new ParseException( "Expected \",\" or the end of text but " + "found \"" + c + "\"", 0, 0); p++; } return map; } String fetchStringValue() throws ParseException { String w = fetchWord(); if (w.startsWith("'") || w.startsWith("\"")) { w = w.substring(1, w.length() - 1); } return StringUtil.FTLStringLiteralDec(w); } String fetchKeyword() throws ParseException { String w = fetchWord(); if (w.startsWith("'") || w.startsWith("\"")) { throw new ParseException( "Keyword expected, but a string value found: " + w, 0, 0); } return w; } char skipWS() { char c; while (p < ln) { c = text.charAt(p); if (!Character.isWhitespace(c)) return c; p++; } return ' '; } private String fetchWord() throws ParseException { if (p == ln) throw new ParseException( "Unexpeced end of text", 0, 0); char c = text.charAt(p); int b = p; if (c == '\'' || c == '"') { boolean escaped = false; char q = c; p++; while (p < ln) { c = text.charAt(p); if (!escaped) { if (c == '\\') { escaped = true; } else if (c == q) { break; } } else { escaped = false; } p++; } if (p == ln) { throw new ParseException("Missing " + q, 0, 0); } p++; return text.substring(b, p); } else { do { c = text.charAt(p); if (!(Character.isLetterOrDigit(c) || c == '/' || c == '\\' || c == '_' || c == '.' || c == '-' || c == '!' || c == '*' || c == '?')) break; p++; } while (p < ln); if (b == p) { throw new ParseException("Unexpected character: " + c, 0, 0); } else { return text.substring(b, p); } } } } }