/*
* 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><assign varname=expr></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><list expr as item></tt> or
* <tt><foreach item in expr></tt>, the loop body is not executed
* (as if it were a 0-length list)
* </li>
* <li>as argument of <tt><if></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><if></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><list></tt> and
* <tt><foreach></tt> are treated as a one-element list consisting
* of the passed model.
* </li>
* <li>Paths parameter of <tt><include></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: - tz – Maybe
null
, in which case java.sql.Date
and java.sql.Time
values will be formatted in the time zone returned by getTimeZone()
. (Note that since null
is an allowed value for this setting, it will not cause getSQLDateAndTimeTimeZone()
to fall back to the parent configuration.)
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");
}
The getter pair of setSQLDateAndTimeTimeZone(TimeZone)
. 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:
"number"
: The number format returned by NumberFormat.getNumberInstance(Locale)
"currency"
: The number format returned by NumberFormat.getCurrencyInstance(Locale)
"percent"
: The number format returned by NumberFormat.getPercentInstance(Locale)
"computer"
: The number format used by FTL's c
built-in (like in someNumber?c
).
DecimalFormat
pattern (like "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, ",000;; roundingMode=halfUp groupingSeparator=_"
will format numbers like ",000"
would, but with half-up rounding mode, and _
as the group separator. See more about "extended Java decimal format" in the FreeMarker Manual.
- If the string starts with
@
character followed by a letter then it's interpreted as a custom number format, but only if either 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 "@name"
or "@name parameters"
, where
name
is the key in the Map
set by setCustomNumberFormats(Map<String,? extends TemplateNumberFormatFactory>)
, and parameters
is parsed by the custom TemplateNumberFormat
.
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:
- In production systems:
TemplateExceptionHandler.RETHROW_HANDLER
- During development of HTML templates:
TemplateExceptionHandler.HTML_DEBUG_HANDLER
- During development of non-HTML templates:
TemplateExceptionHandler.DEBUG_HANDLER
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 setTemplateExceptionHandler(TemplateExceptionHandler)
. /**
* 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;
}
The getter pair of setAttemptExceptionReporter(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 setArithmeticEngine(ArithmeticEngine)
. /**
* 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;
}
The getter pair of setWrapUncheckedExceptions(boolean)
. 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;
}
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;
}
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:
"locale"
: See setLocale(Locale)
.
String value: local codes with the usual format in Java, such as "en_US"
, or since 2.3.26, "JVM default" (ignoring case) to use the default locale of the Java environment.
"classic_compatible"
: See setClassicCompatible(boolean)
and setClassicCompatibleAsInt(int)
.
String value: "true"
, "false"
, also since 2.3.20 0
or 1
or 2
. (Also accepts "yes"
, "no"
, "t"
, "f"
, "y"
, "n"
.) Case insensitive.
"custom_number_formats"
: See setCustomNumberFormats(Map<String,? extends TemplateNumberFormatFactory>)
.
String value: Interpreted as an object builder expression.
Example: { "hex": com.example.HexTemplateNumberFormatFactory,
"gps": com.example.GPSTemplateNumberFormatFactory }
"custom_date_formats"
: See setCustomDateFormats(Map<String,? extends TemplateDateFormatFactory>)
.
String value: Interpreted as an object builder expression.
Example: { "trade": com.example.TradeTemplateDateFormatFactory,
"log": com.example.LogTemplateDateFormatFactory }
"template_exception_handler"
: See setTemplateExceptionHandler(TemplateExceptionHandler)
.
String value: If the value contains dot, then it's interpreted as an object builder
expression. If the value does not contain dot, then it must be one of these predefined values (case insensitive): "rethrow"
(means TemplateExceptionHandler.RETHROW_HANDLER
), "debug"
(means TemplateExceptionHandler.DEBUG_HANDLER
), "html_debug"
(means TemplateExceptionHandler.HTML_DEBUG_HANDLER
), "ignore"
(means TemplateExceptionHandler.IGNORE_HANDLER
), or "default"
(only allowed for Configuration
instances) for the default value.
"attempt_exception_reporter"
: See setAttemptExceptionReporter(AttemptExceptionReporter)
.
String value: If the value contains dot, then it's interpreted as an object builder
expression. If the value does not contain dot, then it must be one of these predefined values (case insensitive): "log_error"
(means AttemptExceptionReporter.LOG_ERROR_REPORTER
), "log_warn"
(means AttemptExceptionReporter.LOG_WARN_REPORTER
), or "default"
(only allowed for Configuration
instances) for the default value.
"arithmetic_engine"
: See setArithmeticEngine(ArithmeticEngine)
.
String value: If the value contains dot, then it's interpreted as an object builder
expression. If the value does not contain dot, then it must be one of these special values (case insensitive): "bigdecimal"
, "conservative"
.
"object_wrapper"
: See setObjectWrapper(ObjectWrapper)
.
String value: If the value contains dot, then it's interpreted as an object builder
expression, with the addition that BeansWrapper
, DefaultObjectWrapper
and SimpleObjectWrapper
can be referred without package name. For example, these strings are valid values: "DefaultObjectWrapper(2.3.21, forceLegacyNonListCollections=false, iterableSupport=true)"
, "BeansWrapper(2.3.21, simpleMapWrapper=true)"
.
If the value does not contain dot, then it must be one of these special values (case insensitive): "default"
means the default of Configuration
(the default depends on the Configuration#Configuration(Version) incompatible_improvements
, but a bug existed in 2.3.21 where that was ignored), "default_2_3_0"
(means the deprecated ObjectWrapper.DEFAULT_WRAPPER
) "simple"
(means the deprecated ObjectWrapper.SIMPLE_WRAPPER
), "beans"
(means the deprecated ObjectWrapper.BEANS_WRAPPER
or BeansWrapperBuilder.build()
), "jython"
(means ObjectWrapper.DEFAULT_WRAPPER
)
"number_format"
: See setNumberFormat(String)
.
"boolean_format"
: See setBooleanFormat(String)
.
"date_format", "time_format", "datetime_format"
: See setDateFormat(String)
, setTimeFormat(String)
, setDateTimeFormat(String)
.
"time_zone"
: See setTimeZone(TimeZone)
.
String value: With the format as TimeZone.getTimeZone
defines it. Also, since 2.3.21 "JVM default"
can be used that will be replaced with the actual JVM default time zone when setSetting(String, String)
is called. For example "GMT-8:00"
or "America/Los_Angeles"
If you set this setting, consider setting sql_date_and_time_time_zone
too (see below)!
sql_date_and_time_time_zone
: See setSQLDateAndTimeTimeZone(TimeZone)
. Since 2.3.21.
String value: With the format as TimeZone.getTimeZone
defines it. Also, "JVM default"
can be used that will be replaced with the actual JVM default time zone when setSetting(String, String)
is called. Also "null"
can be used, which has the same effect as setSQLDateAndTimeTimeZone(null)
.
"output_encoding"
: See setOutputEncoding(String)
.
"url_escaping_charset"
: See setURLEscapingCharset(String)
.
"auto_flush"
: See setAutoFlush(boolean)
. Since 2.3.17.
String value: "true"
, "false"
, "y"
, etc.
"auto_import"
: See setAutoImports(Map)
String value is something like:
/lib/form.ftl as f, /lib/widget as w, "/lib/odd name.ftl" as odd
"auto_include"
: Sets the list of auto-includes. See setAutoIncludes(List)
String value is something like:
/include/common.ftl, "/include/evil name.ftl"
"lazy_auto_imports"
: See setLazyAutoImports(Boolean)
.
String value: "true"
, "false"
(also the equivalents: "yes"
, "no"
, "t"
, "f"
, "y"
, "n"
), case insensitive. Also can be "null"
.
"lazy_imports"
: See setLazyImports(boolean)
.
String value: "true"
, "false"
(also the equivalents: "yes"
, "no"
, "t"
, "f"
, "y"
, "n"
), case insensitive.
"new_builtin_class_resolver"
: See setNewBuiltinClassResolver(TemplateClassResolver)
. Since 2.3.17. The value must be one of these (ignore the quotation marks):
"unrestricted"
: Use TemplateClassResolver.UNRESTRICTED_RESOLVER
"safer"
: Use TemplateClassResolver.SAFER_RESOLVER
"allows_nothing"
(or "allowsNothing"
): Use TemplateClassResolver.ALLOWS_NOTHING_RESOLVER
Something that contains colon will use OptInTemplateClassResolver
and is expected to store comma separated values (possibly quoted) segmented with "allowed_classes:"
(or "allowedClasses:"
) and/or "trusted_templates:"
(or "trustedTemplates:"
). Examples of valid values:
Setting value
Meaning
allowed_classes: com.example.C1, com.example.C2,
trusted_templates: lib/*, safe.ftl
Only allow instantiating the com.example.C1
and com.example.C2
classes. But, allow templates within the lib/
directory (like lib/foo/bar.ftl
) and template safe.ftl
(that does not match foo/safe.ftl
, only exactly safe.ftl
) to instantiate anything that TemplateClassResolver.SAFER_RESOLVER
allows.
allowed_classes: com.example.C1, com.example.C2
Only allow instantiating the com.example.C1
and com.example.C2
classes. There are no trusted templates.
trusted_templates: lib/*, safe.ftl
Do not allow instantiating any classes, except in templates inside lib/
or in template safe.ftl
.
For more details see OptInTemplateClassResolver
.
Otherwise if the value contains dot, it's interpreted as an object builder
expression.
"show_error_tips"
: See setShowErrorTips(boolean)
. Since 2.3.21.
String value: "true"
, "false"
, "y"
, etc.
api_builtin_enabled
: See setAPIBuiltinEnabled(boolean)
. Since 2.3.22.
String value: "true"
, "false"
, "y"
, etc.
Configuration
(a subclass of Configurable
) also understands these:
"auto_escaping"
: See Configuration.setAutoEscapingPolicy(int)
String value: "enable_if_default"
or "enableIfDefault"
for Configuration.ENABLE_IF_DEFAULT_AUTO_ESCAPING_POLICY
, "enable_if_supported"
or "enableIfSupported"
for Configuration.ENABLE_IF_SUPPORTED_AUTO_ESCAPING_POLICY
"disable"
for Configuration.DISABLE_AUTO_ESCAPING_POLICY
.
"default_encoding"
: See Configuration.setDefaultEncoding(String)
; since 2.3.26 also accepts value "JVM default" (not case sensitive) to set the Java environment default value.
As the default value is the system default, which can change
from one server to another, you should always set this!
"localized_lookup"
: See Configuration.setLocalizedLookup
.
String value: "true"
, "false"
(also the equivalents: "yes"
, "no"
, "t"
, "f"
, "y"
, "n"
). Case insensitive.
"output_format"
: See Configuration.setOutputFormat(OutputFormat)
.
String value: "default"
(case insensitive) for the default, or an object builder expression that gives an OutputFormat
, for example HTMLOutputFormat
or XMLOutputFormat
.
"registered_custom_output_formats"
: See Configuration.setRegisteredCustomOutputFormats(Collection<? extends OutputFormat>)
.
String value: an object builder expression that gives a List
of OutputFormat
-s. Example: [com.example.MyOutputFormat(), com.example.MyOtherOutputFormat()]
"strict_syntax"
: See Configuration.setStrictSyntaxMode
. Deprecated.
String value: "true"
, "false"
, yes
, etc.
"whitespace_stripping"
: See Configuration.setWhitespaceStripping
.
String value: "true"
, "false"
, yes
, etc.
"cache_storage"
: See Configuration.setCacheStorage
.
String value: If the value contains dot, then it's interpreted as an object builder
expression. If the value does not contain dot, then a MruCacheStorage
will be used with the maximum strong and soft sizes specified with the setting value. Examples of valid setting values:
Setting value max. strong size max. soft size
"strong:50, soft:500"
50 500
"strong:100, soft"
100 Integer.MAX_VALUE
"strong:100"
100 0
"soft:100"
0 100
"strong"
Integer.MAX_VALUE
0
"soft"
0 Integer.MAX_VALUE
The value is not case sensitive. The order of soft and strong
entries is not significant.
"template_update_delay"
: Template update delay in seconds (not in milliseconds) if no unit is specified; see Configuration.setTemplateUpdateDelayMilliseconds(long)
for more.
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.
"tag_syntax"
: See Configuration.setTagSyntax(int)
.
String value: Must be one of "auto_detect"
, "angle_bracket"
, and "square_bracket"
(like [#if x]
).
Note that setting the "tagSyntax"
to "square_bracket"
does not change
${x}
to [=...]
; that's interpolation syntax, so use the "interpolation_syntax"
setting for that, not this setting.
"interpolation_syntax"
(since 2.3.28): See Configuration.setInterpolationSyntax(int)
.
String value: Must be one of "legacy"
, "dollar"
, and "square_bracket"
(like [=x]
).
Note that setting the "interpolation_syntax"
to "square_bracket"
does not change <#if x>
to [#if x]
; that's tag syntax, so use the "tag_syntax"
setting for that, not this setting.
"naming_convention"
: See Configuration.setNamingConvention(int)
.
String value: Must be one of "auto_detect"
, "legacy"
, and "camel_case"
.
"incompatible_improvements"
: See Configuration.setIncompatibleImprovements(Version)
.
String value: version number like 2.3.20
.
"incompatible_enhancements"
: See: Configuration.setIncompatibleEnhancements(String)
. This setting name is deprecated, use "incompatible_improvements"
instead.
"recognize_standard_file_extensions"
: See Configuration.setRecognizeStandardFileExtensions(boolean)
.
String value: "default"
(case insensitive) for the default, or "true"
, "false"
, yes
, etc.
"template_configurations"
: See: Configuration.setTemplateConfigurations(TemplateConfigurationFactory)
.
String value: Interpreted as an object builder expression, can be null
.
"template_loader"
: See: Configuration.setTemplateLoader(TemplateLoader)
.
String value: "default"
(case insensitive) for the default, or else interpreted as an object builder expression. "null"
is also allowed since 2.3.26.
"template_lookup_strategy"
: See: Configuration.setTemplateLookupStrategy(TemplateLookupStrategy)
.
String value: "default"
(case insensitive) for the default, or else interpreted as an object builder expression.
"template_name_format"
: See: Configuration.setTemplateNameFormat(TemplateNameFormat)
.
String value: "default"
(case insensitive) for the default, "default_2_3_0"
for TemplateNameFormat.DEFAULT_2_3_0
, "default_2_4_0"
for TemplateNameFormat.DEFAULT_2_4_0
.
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: - UnknownSettingException – if the name is wrong.
- TemplateException – if the new value of the setting can't be set for any other reasons.
/**
* 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><#ftl attributes={...}></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);
}
}
}
}
}