2021SC@SDUSC
目录
DefaultEnumerationAdapter.java
Configuration.java
代码分析
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package freemarker.template;
import java.io.File;
import java.io.IOException;
import java.io.Writer;
import java.lang.reflect.InvocationTargetException;
import java.net.URLConnection;
import java.text.DecimalFormat;
import java.text.SimpleDateFormat;
import java.util.Collection;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Properties;
import java.util.Set;
import java.util.TimeZone;
import java.util.TreeSet;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import freemarker.cache.CacheStorage;
import freemarker.cache.ClassTemplateLoader;
import freemarker.cache.FileTemplateLoader;
import freemarker.cache.MruCacheStorage;
import freemarker.cache.MultiTemplateLoader;
import freemarker.cache.SoftCacheStorage;
import freemarker.cache.TemplateCache;
import freemarker.cache.TemplateCache.MaybeMissingTemplate;
import freemarker.cache.TemplateConfigurationFactory;
import freemarker.cache.TemplateLoader;
import freemarker.cache.TemplateLookupContext;
import freemarker.cache.TemplateLookupStrategy;
import freemarker.cache.TemplateNameFormat;
import freemarker.cache.URLTemplateLoader;
import freemarker.core.BugException;
import freemarker.core.CSSOutputFormat;
import freemarker.core.CombinedMarkupOutputFormat;
import freemarker.core.Configurable;
import freemarker.core.Environment;
import freemarker.core.HTMLOutputFormat;
import freemarker.core.JSONOutputFormat;
import freemarker.core.JavaScriptOutputFormat;
import freemarker.core.MarkupOutputFormat;
import freemarker.core.OutputFormat;
import freemarker.core.ParseException;
import freemarker.core.ParserConfiguration;
import freemarker.core.PlainTextOutputFormat;
import freemarker.core.RTFOutputFormat;
import freemarker.core.TemplateConfiguration;
import freemarker.core.TemplateMarkupOutputModel;
import freemarker.core.UndefinedOutputFormat;
import freemarker.core.UnregisteredOutputFormatException;
import freemarker.core.XHTMLOutputFormat;
import freemarker.core.XMLOutputFormat;
import freemarker.core._CoreAPI;
import freemarker.core._DelayedJQuote;
import freemarker.core._MiscTemplateException;
import freemarker.core._ObjectBuilderSettingEvaluator;
import freemarker.core._SettingEvaluationEnvironment;
import freemarker.core._SortedArraySet;
import freemarker.core._UnmodifiableCompositeSet;
import freemarker.ext.beans.BeansWrapper;
import freemarker.ext.beans.BeansWrapperBuilder;
import freemarker.log.Logger;
import freemarker.template.utility.CaptureOutput;
import freemarker.template.utility.ClassUtil;
import freemarker.template.utility.Constants;
import freemarker.template.utility.HtmlEscape;
import freemarker.template.utility.NormalizeNewlines;
import freemarker.template.utility.NullArgumentException;
import freemarker.template.utility.SecurityUtilities;
import freemarker.template.utility.StandardCompress;
import freemarker.template.utility.StringUtil;
import freemarker.template.utility.XmlEscape;
/**
* <b>The main entry point into the FreeMarker API</b>; encapsulates the configuration settings of FreeMarker,
* also serves as a central template-loading and caching service.
*
* <p>This class is meant to be used in a singleton pattern. That is, you create an instance of this at the beginning of
* the application life-cycle, set its {@link #setSetting(String, String) configuration settings} there (either with the
* setter methods like {@link #setTemplateLoader(TemplateLoader)} or by loading a {@code .properties} file), and then
* use that single instance everywhere in your application. Frequently re-creating {@link Configuration} is a typical
* and grave mistake from performance standpoint, as the {@link Configuration} holds the template cache, and often also
* the class introspection cache, which then will be lost. (Note that, naturally, having multiple long-lived instances,
* like one per component that internally uses FreeMarker is fine.)
*
* <p>The basic usage pattern is like:
*
* <pre>
* // Where the application is initialized; in general you do this ONLY ONCE in the application life-cycle!
* Configuration cfg = new Configuration(VERSION_<i>X</i>_<i>Y</i>_<i>Z</i>));
* // Where VERSION_<i>X</i>_<i>Y</i>_<i>Z</i> enables the not-100%-backward-compatible fixes introduced in
* // FreeMarker version X.Y.Z and earlier (see {@link #Configuration(Version)}).
* cfg.set<i>SomeSetting</i>(...);
* cfg.set<i>OtherSetting</i>(...);
* ...
*
* // Later, whenever the application needs a template (so you may do this a lot, and from multiple threads):
* {@link Template Template} myTemplate = cfg.{@link #getTemplate(String) getTemplate}("myTemplate.ftlh");
* myTemplate.{@link Template#process(Object, java.io.Writer) process}(dataModel, out);</pre>
*
* <p>A couple of settings that you should not leave on its default value are:
* <ul>
* <li>{@link #setTemplateLoader(TemplateLoader) template_loader}: The default value is deprecated and in fact quite
* useless. (For the most common cases you can use the convenience methods,
* {@link #setDirectoryForTemplateLoading(File)} and {@link #setClassForTemplateLoading(Class, String)} and
* {@link #setClassLoaderForTemplateLoading(ClassLoader, String)} too.)
* <li>{@link #setDefaultEncoding(String) default_encoding}: The default value is system dependent, which makes it
* fragile on servers, so it should be set explicitly, like to "UTF-8" nowadays.
* <li>{@link #setTemplateExceptionHandler(TemplateExceptionHandler) template_exception_handler}: For developing
* HTML pages, the most convenient value is {@link TemplateExceptionHandler#HTML_DEBUG_HANDLER}. For production,
* {@link TemplateExceptionHandler#RETHROW_HANDLER} is safer to use.
* <!-- 2.4: recommend the new object wrapper here -->
* </ul>
*
* <p>A {@link Configuration} object is thread-safe only after you have stopped modifying the configuration settings,
* and you have <b>safely published</b> it (see JSR 133 and related literature) to other threads. Generally, you set
* everything directly after you have instantiated the {@link Configuration} object, then you don't change the settings
* anymore, so then it's safe to make it accessible (again, via a "safe publication" technique) from multiple threads.
* The methods that aren't for modifying settings, like {@link #getTemplate(String)}, are thread-safe.
*/
public class Configuration extends Configurable implements Cloneable, ParserConfiguration {
private static final Logger CACHE_LOG = Logger.getLogger("freemarker.cache");
private static final String VERSION_PROPERTIES_PATH = "/freemarker/version.properties";
/** Legacy, snake case ({@code like_this}) variation of the setting name. @since 2.3.23 */
public static final String DEFAULT_ENCODING_KEY_SNAKE_CASE = "default_encoding";
/** Modern, camel case ({@code likeThis}) variation of the setting name. @since 2.3.23 */
public static final String DEFAULT_ENCODING_KEY_CAMEL_CASE = "defaultEncoding";
/** Alias to the {@code ..._SNAKE_CASE} variation due to backward compatibility constraints. */
public static final String DEFAULT_ENCODING_KEY = DEFAULT_ENCODING_KEY_SNAKE_CASE;
/** Legacy, snake case ({@code like_this}) variation of the setting name. @since 2.3.23 */
public static final String LOCALIZED_LOOKUP_KEY_SNAKE_CASE = "localized_lookup";
/** Modern, camel case ({@code likeThis}) variation of the setting name. @since 2.3.23 */
public static final String LOCALIZED_LOOKUP_KEY_CAMEL_CASE = "localizedLookup";
/** Alias to the {@code ..._SNAKE_CASE} variation due to backward compatibility constraints. */
public static final String LOCALIZED_LOOKUP_KEY = LOCALIZED_LOOKUP_KEY_SNAKE_CASE;
/** Legacy, snake case ({@code like_this}) variation of the setting name. @since 2.3.23 */
public static final String STRICT_SYNTAX_KEY_SNAKE_CASE = "strict_syntax";
/** Modern, camel case ({@code likeThis}) variation of the setting name. @since 2.3.23 */
public static final String STRICT_SYNTAX_KEY_CAMEL_CASE = "strictSyntax";
/** Alias to the {@code ..._SNAKE_CASE} variation due to backward compatibility constraints. */
public static final String STRICT_SYNTAX_KEY = STRICT_SYNTAX_KEY_SNAKE_CASE;
/** Legacy, snake case ({@code like_this}) variation of the setting name. @since 2.3.23 */
public static final String WHITESPACE_STRIPPING_KEY_SNAKE_CASE = "whitespace_stripping";
/** Modern, camel case ({@code likeThis}) variation of the setting name. @since 2.3.23 */
public static final String WHITESPACE_STRIPPING_KEY_CAMEL_CASE = "whitespaceStripping";
/** Alias to the {@code ..._SNAKE_CASE} variation due to backward compatibility constraints. */
public static final String WHITESPACE_STRIPPING_KEY = WHITESPACE_STRIPPING_KEY_SNAKE_CASE;
/** Legacy, snake case ({@code like_this}) variation of the setting name. @since 2.3.24 */
public static final String OUTPUT_FORMAT_KEY_SNAKE_CASE = "output_format";
/** Modern, camel case ({@code likeThis}) variation of the setting name. @since 2.3.24 */
public static final String OUTPUT_FORMAT_KEY_CAMEL_CASE = "outputFormat";
/** Alias to the {@code ..._SNAKE_CASE} variation due to backward compatibility constraints. */
public static final String OUTPUT_FORMAT_KEY = OUTPUT_FORMAT_KEY_SNAKE_CASE;
/** Legacy, snake case ({@code like_this}) variation of the setting name. @since 2.3.24 */
public static final String RECOGNIZE_STANDARD_FILE_EXTENSIONS_KEY_SNAKE_CASE = "recognize_standard_file_extensions";
/** Modern, camel case ({@code likeThis}) variation of the setting name. @since 2.3.24 */
public static final String RECOGNIZE_STANDARD_FILE_EXTENSIONS_KEY_CAMEL_CASE = "recognizeStandardFileExtensions";
/** Alias to the {@code ..._SNAKE_CASE} variation due to backward compatibility constraints. */
public static final String RECOGNIZE_STANDARD_FILE_EXTENSIONS_KEY
= RECOGNIZE_STANDARD_FILE_EXTENSIONS_KEY_SNAKE_CASE;
/** Legacy, snake case ({@code like_this}) variation of the setting name. @since 2.3.24 */
public static final String REGISTERED_CUSTOM_OUTPUT_FORMATS_KEY_SNAKE_CASE = "registered_custom_output_formats";
/** Modern, camel case ({@code likeThis}) variation of the setting name. @since 2.3.24 */
public static final String REGISTERED_CUSTOM_OUTPUT_FORMATS_KEY_CAMEL_CASE = "registeredCustomOutputFormats";
/** Alias to the {@code ..._SNAKE_CASE} variation due to backward compatibility constraints. */
public static final String REGISTERED_CUSTOM_OUTPUT_FORMATS_KEY = REGISTERED_CUSTOM_OUTPUT_FORMATS_KEY_SNAKE_CASE;
/** Legacy, snake case ({@code like_this}) variation of the setting name. @since 2.3.24 */
public static final String AUTO_ESCAPING_POLICY_KEY_SNAKE_CASE = "auto_escaping_policy";
/** Modern, camel case ({@code likeThis}) variation of the setting name. @since 2.3.24 */
public static final String AUTO_ESCAPING_POLICY_KEY_CAMEL_CASE = "autoEscapingPolicy";
/** Alias to the {@code ..._SNAKE_CASE} variation due to backward compatibility constraints. */
public static final String AUTO_ESCAPING_POLICY_KEY = AUTO_ESCAPING_POLICY_KEY_SNAKE_CASE;
/** Legacy, snake case ({@code like_this}) variation of the setting name. @since 2.3.23 */
public static final String CACHE_STORAGE_KEY_SNAKE_CASE = "cache_storage";
/** Modern, camel case ({@code likeThis}) variation of the setting name. @since 2.3.23 */
public static final String CACHE_STORAGE_KEY_CAMEL_CASE = "cacheStorage";
/** Alias to the {@code ..._SNAKE_CASE} variation due to backward compatibility constraints. */
public static final String CACHE_STORAGE_KEY = CACHE_STORAGE_KEY_SNAKE_CASE;
/** Legacy, snake case ({@code like_this}) variation of the setting name. @since 2.3.23 */
public static final String TEMPLATE_UPDATE_DELAY_KEY_SNAKE_CASE = "template_update_delay";
/** Modern, camel case ({@code likeThis}) variation of the setting name. @since 2.3.23 */
public static final String TEMPLATE_UPDATE_DELAY_KEY_CAMEL_CASE = "templateUpdateDelay";
/** Alias to the {@code ..._SNAKE_CASE} variation due to backward compatibility constraints. */
public static final String TEMPLATE_UPDATE_DELAY_KEY = TEMPLATE_UPDATE_DELAY_KEY_SNAKE_CASE;
/**
* Legacy, snake case ({@code like_this}) variation of the setting name. @since 2.3.23
* @deprecated Use {@link Configurable#AUTO_IMPORT_KEY_SNAKE_CASE} instead.
*/
public static final String AUTO_IMPORT_KEY_SNAKE_CASE = "auto_import";
/**
* Modern, camel case ({@code likeThis}) variation of the setting name. @since 2.3.23
* @deprecated Use {@link Configurable#AUTO_IMPORT_KEY_CAMEL_CASE} instead.
*/
public static final String AUTO_IMPORT_KEY_CAMEL_CASE = "autoImport";
/**
* Alias to the {@code ..._SNAKE_CASE} variation due to backward compatibility constraints.
* @deprecated Use {@link Configurable#AUTO_IMPORT_KEY_SNAKE_CASE} instead.
*/
public static final String AUTO_IMPORT_KEY = AUTO_IMPORT_KEY_SNAKE_CASE;
/** Legacy, snake case ({@code like_this}) variation of the setting name. @since 2.3.23 */
public static final String AUTO_INCLUDE_KEY_SNAKE_CASE = "auto_include";
/** Modern, camel case ({@code likeThis}) variation of the setting name. @since 2.3.23 */
public static final String AUTO_INCLUDE_KEY_CAMEL_CASE = "autoInclude";
/** Alias to the {@code ..._SNAKE_CASE} variation due to backward compatibility constraints. */
public static final String AUTO_INCLUDE_KEY = AUTO_INCLUDE_KEY_SNAKE_CASE;
/** Legacy, snake case ({@code like_this}) variation of the setting name. @since 2.3.23 */
public static final String TAG_SYNTAX_KEY_SNAKE_CASE = "tag_syntax";
/** Modern, camel case ({@code likeThis}) variation of the setting name. @since 2.3.23 */
public static final String TAG_SYNTAX_KEY_CAMEL_CASE = "tagSyntax";
/** Alias to the {@code ..._SNAKE_CASE} variation due to backward compatibility constraints. */
public static final String TAG_SYNTAX_KEY = TAG_SYNTAX_KEY_SNAKE_CASE;
/** Legacy, snake case ({@code like_this}) variation of the setting name. @since 2.3.28 */
public static final String INTERPOLATION_SYNTAX_KEY_SNAKE_CASE = "interpolation_syntax";
/** Modern, camel case ({@code likeThis}) variation of the setting name. @since 2.3.28 */
public static final String INTERPOLATION_SYNTAX_KEY_CAMEL_CASE = "interpolationSyntax";
/** Alias to the {@code ..._SNAKE_CASE} variation due to backward compatibility constraints. */
public static final String INTERPOLATION_SYNTAX_KEY = INTERPOLATION_SYNTAX_KEY_SNAKE_CASE;
/** Legacy, snake case ({@code like_this}) variation of the setting name. @since 2.3.23 */
public static final String NAMING_CONVENTION_KEY_SNAKE_CASE = "naming_convention";
/** Modern, camel case ({@code likeThis}) variation of the setting name. @since 2.3.23 */
public static final String NAMING_CONVENTION_KEY_CAMEL_CASE = "namingConvention";
/** Alias to the {@code ..._SNAKE_CASE} variation due to backward compatibility constraints. */
public static final String NAMING_CONVENTION_KEY = NAMING_CONVENTION_KEY_SNAKE_CASE;
/** Legacy, snake case ({@code like_this}) variation of the setting name. @since 2.3.25 */
public static final String TAB_SIZE_KEY_SNAKE_CASE = "tab_size";
/** Modern, camel case ({@code likeThis}) variation of the setting name. @since 2.3.25 */
public static final String TAB_SIZE_KEY_CAMEL_CASE = "tabSize";
/** Alias to the {@code ..._SNAKE_CASE} variation. @since 2.3.25 */
public static final String TAB_SIZE_KEY = TAB_SIZE_KEY_SNAKE_CASE;
/** Legacy, snake case ({@code like_this}) variation of the setting name. @since 2.3.23 */
public static final String TEMPLATE_LOADER_KEY_SNAKE_CASE = "template_loader";
/** Modern, camel case ({@code likeThis}) variation of the setting name. @since 2.3.23 */
public static final String TEMPLATE_LOADER_KEY_CAMEL_CASE = "templateLoader";
/** Alias to the {@code ..._SNAKE_CASE} variation due to backward compatibility constraints. */
public static final String TEMPLATE_LOADER_KEY = TEMPLATE_LOADER_KEY_SNAKE_CASE;
/** Legacy, snake case ({@code like_this}) variation of the setting name. @since 2.3.23 */
public static final String TEMPLATE_LOOKUP_STRATEGY_KEY_SNAKE_CASE = "template_lookup_strategy";
/** Modern, camel case ({@code likeThis}) variation of the setting name. @since 2.3.23 */
public static final String TEMPLATE_LOOKUP_STRATEGY_KEY_CAMEL_CASE = "templateLookupStrategy";
/** Alias to the {@code ..._SNAKE_CASE} variation due to backward compatibility constraints. */
public static final String TEMPLATE_LOOKUP_STRATEGY_KEY = TEMPLATE_LOOKUP_STRATEGY_KEY_SNAKE_CASE;
/** Legacy, snake case ({@code like_this}) variation of the setting name. @since 2.3.23 */
public static final String TEMPLATE_NAME_FORMAT_KEY_SNAKE_CASE = "template_name_format";
/** Modern, camel case ({@code likeThis}) variation of the setting name. @since 2.3.23 */
public static final String TEMPLATE_NAME_FORMAT_KEY_CAMEL_CASE = "templateNameFormat";
/** Alias to the {@code ..._SNAKE_CASE} variation due to backward compatibility constraints. */
public static final String TEMPLATE_NAME_FORMAT_KEY = TEMPLATE_NAME_FORMAT_KEY_SNAKE_CASE;
/** Legacy, snake case ({@code like_this}) variation of the setting name. @since 2.3.24 */
public static final String TEMPLATE_CONFIGURATIONS_KEY_SNAKE_CASE = "template_configurations";
/** Modern, camel case ({@code likeThis}) variation of the setting name. @since 2.3.24 */
public static final String TEMPLATE_CONFIGURATIONS_KEY_CAMEL_CASE = "templateConfigurations";
/** Alias to the {@code ..._SNAKE_CASE} variation. @since 2.3.24 */
public static final String TEMPLATE_CONFIGURATIONS_KEY = TEMPLATE_CONFIGURATIONS_KEY_SNAKE_CASE;
/** Legacy, snake case ({@code like_this}) variation of the setting name. @since 2.3.23 */
public static final String INCOMPATIBLE_IMPROVEMENTS_KEY_SNAKE_CASE = "incompatible_improvements";
/** Modern, camel case ({@code likeThis}) variation of the setting name. @since 2.3.23 */
public static final String INCOMPATIBLE_IMPROVEMENTS_KEY_CAMEL_CASE = "incompatibleImprovements";
/** Alias to the {@code ..._SNAKE_CASE} variation due to backward compatibility constraints. */
public static final String INCOMPATIBLE_IMPROVEMENTS_KEY = INCOMPATIBLE_IMPROVEMENTS_KEY_SNAKE_CASE;
/** @deprecated Use {@link #INCOMPATIBLE_IMPROVEMENTS_KEY} instead. */
@Deprecated
public static final String INCOMPATIBLE_IMPROVEMENTS = INCOMPATIBLE_IMPROVEMENTS_KEY_SNAKE_CASE;
/** @deprecated Use {@link #INCOMPATIBLE_IMPROVEMENTS_KEY} instead. */
@Deprecated
public static final String INCOMPATIBLE_ENHANCEMENTS = "incompatible_enhancements";
/** Legacy, snake case ({@code like_this}) variation of the setting name. @since 2.3.29 */
public static final String FALLBACK_ON_NULL_LOOP_VARIABLE_KEY_SNAKE_CASE = "fallback_on_null_loop_variable";
/** Modern, camel case ({@code likeThis}) variation of the setting name. @since 2.3.29 */
public static final String FALLBACK_ON_NULL_LOOP_VARIABLE_KEY_CAMEL_CASE = "fallbackOnNullLoopVariable";
/** Alias to the {@code ..._SNAKE_CASE} variation. @since 2.3.25 */
public static final String FALLBACK_ON_NULL_LOOP_VARIABLE_KEY = FALLBACK_ON_NULL_LOOP_VARIABLE_KEY_SNAKE_CASE;
private static final String[] SETTING_NAMES_SNAKE_CASE = new String[] {
AUTO_ESCAPING_POLICY_KEY_SNAKE_CASE,
CACHE_STORAGE_KEY_SNAKE_CASE,
DEFAULT_ENCODING_KEY_SNAKE_CASE,
FALLBACK_ON_NULL_LOOP_VARIABLE_KEY_SNAKE_CASE,
INCOMPATIBLE_IMPROVEMENTS_KEY_SNAKE_CASE,
INTERPOLATION_SYNTAX_KEY_SNAKE_CASE,
LOCALIZED_LOOKUP_KEY_SNAKE_CASE,
NAMING_CONVENTION_KEY_SNAKE_CASE,
OUTPUT_FORMAT_KEY_SNAKE_CASE,
RECOGNIZE_STANDARD_FILE_EXTENSIONS_KEY_SNAKE_CASE,
REGISTERED_CUSTOM_OUTPUT_FORMATS_KEY_SNAKE_CASE,
STRICT_SYNTAX_KEY_SNAKE_CASE,
TAB_SIZE_KEY_SNAKE_CASE,
TAG_SYNTAX_KEY_SNAKE_CASE,
TEMPLATE_CONFIGURATIONS_KEY_SNAKE_CASE,
TEMPLATE_LOADER_KEY_SNAKE_CASE,
TEMPLATE_LOOKUP_STRATEGY_KEY_SNAKE_CASE,
TEMPLATE_NAME_FORMAT_KEY_SNAKE_CASE,
TEMPLATE_UPDATE_DELAY_KEY_SNAKE_CASE,
WHITESPACE_STRIPPING_KEY_SNAKE_CASE,
};
private static final String[] SETTING_NAMES_CAMEL_CASE = new String[] {
//要求必须按字母顺序排序
AUTO_ESCAPING_POLICY_KEY_CAMEL_CASE,
CACHE_STORAGE_KEY_CAMEL_CASE,
DEFAULT_ENCODING_KEY_CAMEL_CASE,
FALLBACK_ON_NULL_LOOP_VARIABLE_KEY_CAMEL_CASE,
INCOMPATIBLE_IMPROVEMENTS_KEY_CAMEL_CASE,
INTERPOLATION_SYNTAX_KEY_CAMEL_CASE,
LOCALIZED_LOOKUP_KEY_CAMEL_CASE,
NAMING_CONVENTION_KEY_CAMEL_CASE,
OUTPUT_FORMAT_KEY_CAMEL_CASE,
RECOGNIZE_STANDARD_FILE_EXTENSIONS_KEY_CAMEL_CASE,
REGISTERED_CUSTOM_OUTPUT_FORMATS_KEY_CAMEL_CASE,
STRICT_SYNTAX_KEY_CAMEL_CASE,
TAB_SIZE_KEY_CAMEL_CASE,
TAG_SYNTAX_KEY_CAMEL_CASE,
TEMPLATE_CONFIGURATIONS_KEY_CAMEL_CASE,
TEMPLATE_LOADER_KEY_CAMEL_CASE,
TEMPLATE_LOOKUP_STRATEGY_KEY_CAMEL_CASE,
TEMPLATE_NAME_FORMAT_KEY_CAMEL_CASE,
TEMPLATE_UPDATE_DELAY_KEY_CAMEL_CASE,
WHITESPACE_STRIPPING_KEY_CAMEL_CASE
};
private static final Map<String, OutputFormat> STANDARD_OUTPUT_FORMATS;
static {
STANDARD_OUTPUT_FORMATS = new HashMap<>();
STANDARD_OUTPUT_FORMATS.put(UndefinedOutputFormat.INSTANCE.getName(), UndefinedOutputFormat.INSTANCE);
STANDARD_OUTPUT_FORMATS.put(HTMLOutputFormat.INSTANCE.getName(), HTMLOutputFormat.INSTANCE);
STANDARD_OUTPUT_FORMATS.put(XHTMLOutputFormat.INSTANCE.getName(), XHTMLOutputFormat.INSTANCE);
STANDARD_OUTPUT_FORMATS.put(XMLOutputFormat.INSTANCE.getName(), XMLOutputFormat.INSTANCE);
STANDARD_OUTPUT_FORMATS.put(RTFOutputFormat.INSTANCE.getName(), RTFOutputFormat.INSTANCE);
STANDARD_OUTPUT_FORMATS.put(PlainTextOutputFormat.INSTANCE.getName(), PlainTextOutputFormat.INSTANCE);
STANDARD_OUTPUT_FORMATS.put(CSSOutputFormat.INSTANCE.getName(), CSSOutputFormat.INSTANCE);
STANDARD_OUTPUT_FORMATS.put(JavaScriptOutputFormat.INSTANCE.getName(), JavaScriptOutputFormat.INSTANCE);
STANDARD_OUTPUT_FORMATS.put(JSONOutputFormat.INSTANCE.getName(), JSONOutputFormat.INSTANCE);
}
/**
*
解析器根据的值决定是{@link #ANGLE_BRACKET_TAG_SYNTAX}还是{@link #SQUARE_BRACKET_TAG_SYNTAX}
*第一个标签(像{@code [#if x]}或{@code <#if x>})是mets。注意{@code[=…}是不是标签,但是
*一个插值表达式,所以它不用于标签语法的自动检测。
*/
public static final int AUTO_DETECT_TAG_SYNTAX = 0;
/** For example {@code <#if x><@foo /></#if>} */
public static final int ANGLE_BRACKET_TAG_SYNTAX = 1;
/**
* For example {@code [#if x][@foo /][/#if]}.
* It does <em>not</em> change <code>${x}</code> to {@code [=x]}; that's square bracket <em>interpolation</em>
* syntax ({@link #SQUARE_BRACKET_INTERPOLATION_SYNTAX}).
*/
public static final int SQUARE_BRACKET_TAG_SYNTAX = 2;
/** <code>${expression}</code> and the deprecated <code>#{expression; numFormat}</code> @since 2.3.28 */
public static final int LEGACY_INTERPOLATION_SYNTAX = 20;
/** <code>${expression}</code> only (not <code>#{expression; numFormat}</code>) @since 2.3.28 */
public static final int DOLLAR_INTERPOLATION_SYNTAX = 21;
/**
* <code>[=expression]</code> instead of <code>${expression}</code>.
* It does <em>not</em> change {@code <#if x>} to {@code [#if x]}; that's square bracket <em>tag</em> syntax
* ({@link #SQUARE_BRACKET_TAG_SYNTAX}).
* @since 2.3.28
*/
public static final int SQUARE_BRACKET_INTERPOLATION_SYNTAX = 22;
public static final int AUTO_DETECT_NAMING_CONVENTION = 10;
public static final int LEGACY_NAMING_CONVENTION = 11;
public static final int CAMEL_CASE_NAMING_CONVENTION = 12;
/**
* Don't enable auto-escaping, regardless of what the {@link OutputFormat} is. Note that a {@code
* <#ftl auto_esc=true>} in the template will override this.
*/
public static final int DISABLE_AUTO_ESCAPING_POLICY = 20;
/**
* Enable auto-escaping if the output format supports it and {@link MarkupOutputFormat#isAutoEscapedByDefault()} is
* {@code true}.
*/
public static final int ENABLE_IF_DEFAULT_AUTO_ESCAPING_POLICY = 21;
/** Enable auto-escaping if the {@link OutputFormat} supports it. */
public static final int ENABLE_IF_SUPPORTED_AUTO_ESCAPING_POLICY = 22;
/** FreeMarker version 2.3.0 (an {@link #Configuration(Version) incompatible improvements break-point}) */
public static final Version VERSION_2_3_0 = new Version(2, 3, 0);
/** FreeMarker version 2.3.19 (an {@link #Configuration(Version) incompatible improvements break-point}) */
public static final Version VERSION_2_3_19 = new Version(2, 3, 19);
/** FreeMarker version 2.3.20 (an {@link #Configuration(Version) incompatible improvements break-point}) */
public static final Version VERSION_2_3_20 = new Version(2, 3, 20);
/** FreeMarker version 2.3.21 (an {@link #Configuration(Version) incompatible improvements break-point}) */
public static final Version VERSION_2_3_21 = new Version(2, 3, 21);
/** FreeMarker version 2.3.22 (an {@link #Configuration(Version) incompatible improvements break-point}) */
public static final Version VERSION_2_3_22 = new Version(2, 3, 22);
/** FreeMarker version 2.3.23 (an {@link #Configuration(Version) incompatible improvements break-point}) */
public static final Version VERSION_2_3_23 = new Version(2, 3, 23);
/** FreeMarker version 2.3.24 (an {@link #Configuration(Version) incompatible improvements break-point}) */
public static final Version VERSION_2_3_24 = new Version(2, 3, 24);
/** FreeMarker version 2.3.25 (an {@link #Configuration(Version) incompatible improvements break-point}) */
public static final Version VERSION_2_3_25 = new Version(2, 3, 25);
/** FreeMarker version 2.3.26 (an {@link #Configuration(Version) incompatible improvements break-point}) */
public static final Version VERSION_2_3_26 = new Version(2, 3, 26);
/** FreeMarker version 2.3.27 (an {@link #Configuration(Version) incompatible improvements break-point}) */
public static final Version VERSION_2_3_27 = new Version(2, 3, 27);
/** FreeMarker version 2.3.28 (an {@link #Configuration(Version) incompatible improvements break-point}) */
public static final Version VERSION_2_3_28 = new Version(2, 3, 28);
/** FreeMarker version 2.3.29 (an {@link #Configuration(Version) incompatible improvements break-point}) */
public static final Version VERSION_2_3_29 = new Version(2, 3, 29);
/** FreeMarker version 2.3.30 (an {@link #Configuration(Version) incompatible improvements break-point}) */
public static final Version VERSION_2_3_30 = new Version(2, 3, 30);
/** FreeMarker version 2.3.31 (an {@link #Configuration(Version) incompatible improvements break-point}) */
public static final Version VERSION_2_3_31 = new Version(2, 3, 31);
/** The default of {@link #getIncompatibleImprovements()}, currently {@link #VERSION_2_3_0}. */
public static final Version DEFAULT_INCOMPATIBLE_IMPROVEMENTS = Configuration.VERSION_2_3_0;
/** @deprecated Use {@link #DEFAULT_INCOMPATIBLE_IMPROVEMENTS} instead. */
@Deprecated
public static final String DEFAULT_INCOMPATIBLE_ENHANCEMENTS = DEFAULT_INCOMPATIBLE_IMPROVEMENTS.toString();
/** @deprecated Use {@link #DEFAULT_INCOMPATIBLE_IMPROVEMENTS} instead. */
@Deprecated
public static final int PARSED_DEFAULT_INCOMPATIBLE_ENHANCEMENTS = DEFAULT_INCOMPATIBLE_IMPROVEMENTS.intValue();
private static final String NULL = "null";
private static final String DEFAULT = "default";
private static final String JVM_DEFAULT = "JVM default";
private static final Version VERSION;
static {
try {
Properties props = ClassUtil.loadProperties(Configuration.class, VERSION_PROPERTIES_PATH);
String versionString = getRequiredVersionProperty(props, "version");
Date buildDate;
{
String buildDateStr = getRequiredVersionProperty(props, "buildTimestamp");
if (buildDateStr.endsWith("Z")) {
buildDateStr = buildDateStr.substring(0, buildDateStr.length() - 1) + "+0000";
}
try {
buildDate = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZ", Locale.US).parse(buildDateStr);
} catch (java.text.ParseException e) {
buildDate = null;
}
}
final Boolean gaeCompliant = Boolean.valueOf(getRequiredVersionProperty(props, "isGAECompliant"));
VERSION = new Version(versionString, gaeCompliant, buildDate);
} catch (IOException e) {
throw new RuntimeException("Failed to load and parse " + VERSION_PROPERTIES_PATH, e);
}
}
private static final String FM_24_DETECTION_CLASS_NAME = "freemarker.core._2_4_OrLaterMarker";
private static final boolean FM_24_DETECTED;
static {
boolean fm24detected;
try {
Class.forName(FM_24_DETECTION_CLASS_NAME);
fm24detected = true;
} catch (ClassNotFoundException e) {
fm24detected = false;
} catch (LinkageError e) {
fm24detected = true;
} catch (Throwable e) {
// Unexpected. We assume that there's no clash.
fm24detected = false;
}
FM_24_DETECTED = fm24detected;
}
private final static Object defaultConfigLock = new Object();
private static volatile Configuration defaultConfig;
private boolean strictSyntax = true;
private volatile boolean localizedLookup = true;
private boolean whitespaceStripping = true;
private int autoEscapingPolicy = ENABLE_IF_DEFAULT_AUTO_ESCAPING_POLICY;
private OutputFormat outputFormat = UndefinedOutputFormat.INSTANCE;
private boolean outputFormatExplicitlySet;
private Boolean recognizeStandardFileExtensions;
private Map<String, ? extends OutputFormat> registeredCustomOutputFormats = Collections.emptyMap();
private Version incompatibleImprovements;
private int tagSyntax = ANGLE_BRACKET_TAG_SYNTAX;
private int interpolationSyntax = LEGACY_INTERPOLATION_SYNTAX;
private int namingConvention = AUTO_DETECT_NAMING_CONVENTION;
private int tabSize = 8; // Default from JavaCC 3.x
private boolean fallbackOnNullLoopVariable = true; // Default for backward compatibility
private boolean preventStrippings;
private TemplateCache cache;
private boolean templateLoaderExplicitlySet;
private boolean templateLookupStrategyExplicitlySet;
private boolean templateNameFormatExplicitlySet;
private boolean cacheStorageExplicitlySet;
private boolean objectWrapperExplicitlySet;
private boolean templateExceptionHandlerExplicitlySet;
private boolean attemptExceptionReporterExplicitlySet;
private boolean logTemplateExceptionsExplicitlySet;
private boolean wrapUncheckedExceptionsExplicitlySet;
private boolean localeExplicitlySet;
private boolean defaultEncodingExplicitlySet;
private boolean timeZoneExplicitlySet;
private HashMap/*<String, TemplateModel>*/ sharedVariables = new HashMap();
// 当用户从Spring XML配置FreeMarker时,他没有控制订单,所以它必须在两种方式上工作。
private HashMap/*<String, Object>*/ rewrappableSharedVariables = null;
private String defaultEncoding = getDefaultDefaultEncoding();
private ConcurrentMap localeToCharsetMap = new ConcurrentHashMap();
/**
* Same as {@link #Configuration(Version) Configuration(Configuration.DEFAULT_INCOMPATIBLE_IMPROVEMENTS)}.
*
* @deprecated Use {@link #Configuration(Version)} instead. Note that the version can be still modified later with
* {@link Configuration#setIncompatibleImprovements(Version)} (or
* {@link Configuration#setSettings(Properties)}).
*/
@Deprecated
public Configuration() {
this(DEFAULT_INCOMPATIBLE_IMPROVEMENTS);
}
/**
* Creates a new instance and sets which of the non-backward-compatible bugfixes/improvements should be enabled.
* Note that the specified versions corresponds to the {@code incompatible_improvements} configuration setting, and
* can be changed later, with {@link #setIncompatibleImprovements(Version)} for example.
*
* <p><b>About the "incompatible improvements" setting</b>
*
* <p>This setting value is the FreeMarker version number where the not 100% backward compatible bug fixes and
* improvements that you want to enable were already implemented. In new projects you should set this to the fixed
* FreeMarker version that you start the development with. In older projects it's also usually better to keep
* this high, however you should check the changes activated (find them below), especially if not only the 3rd
* version number (the micro version) of {@code incompatibleImprovements} is increased. Generally, as far as you
* only increase the last version number of this setting, the changes are low risk. The default value is 2.3.0 to
* maximize backward compatibility, but that value isn't recommended.
*
* <p>Bugfixes and improvements that are fully backward compatible, also those that are important security fixes,
* are enabled regardless of the incompatible improvements setting.
*
* <p>Do NOT ever use {@link #getVersion()} to set the "incompatible improvements". Always use a fixed value, like
* {@link #VERSION_2_3_30}. Otherwise your application can break as you upgrade FreeMarker. (As of 2.3.30, doing
* this will be logged as an error. As of 2.4.0, it will be probably disallowed, by throwing exception.)
*
* <p>An important consequence of setting this setting is that now your application will check if the stated minimum
* FreeMarker version requirement is met. Like if you set this setting to 2.3.22, but accidentally the application
* is deployed with FreeMarker 2.3.21, then FreeMarker will fail, telling that a higher version is required. After
* all, the fixes/improvements you have requested aren't available on a lower version.
*
* <p>Note that as FreeMarker's minor (2nd) or major (1st) version number increments, it's possible that emulating
* some of the old bugs will become unsupported, that is, even if you set this setting to a low value, it silently
* wont bring back the old behavior anymore. Information about that will be present here.
*
* <p>Currently the effects of this setting are:
* <ul>
* <li><p>
* 2.3.0: This is the lowest supported value, the version used in very old projects. This is the default in the
* FreeMarker 2.3.x series (the one used by the deprecated {@link #Configuration()} constructor) for maximum
* backward compatibility.
* </li>
* <li><p>
* 2.3.19 (or higher): Bug fix: Wrong {@code #} tags were printed as static text instead of
* causing parsing error when there was no correct {@code #} or {@code @} tag earlier in the
* same template.
* </li>
* <li><p>
* 2.3.20 (or higher): {@code ?html} will escape apostrophe-quotes just like {@code ?xhtml} does. Utilizing
* this is highly recommended, because otherwise if interpolations are used inside attribute values that use
* apostrophe-quotation (<tt><foo bar='${val}'></tt>) instead of plain quotation mark
* (<tt><foo bar="${val}"></tt>), they might produce HTML/XML that's not well-formed. Note that
* {@code ?html} didn't do this because long ago there was no cross-browser way of doing this, but it's not a
* concern anymore.
* </li>
* <li><p>
* 2.3.21 (or higher):
* <ul>
* <li><p>
* The <em>default</em> of the {@code object_wrapper} setting ({@link #getObjectWrapper()}) changes from
* {@link ObjectWrapper#DEFAULT_WRAPPER} to another almost identical {@link DefaultObjectWrapper} singleton,
* returned by {@link DefaultObjectWrapperBuilder#build()}. The new default object wrapper's
* "incompatible improvements" version is set to the same as of the {@link Configuration}.
* See {@link BeansWrapper#BeansWrapper(Version)} for further details. Furthermore, the new default
* object wrapper doesn't allow changing its settings; setter methods throw {@link IllegalStateException}).
* (If anything tries to call setters on the old default in your application, that's a dangerous bug that
* won't remain hidden now. As the old default is a singleton too, potentially shared by independently
* developed components, most of them expects the out-of-the-box behavior from it (and the others are
* necessarily buggy). Also, then concurrency glitches can occur (and even pollute the class introspection
* cache) because the singleton is modified after publishing to other threads.)
* Furthermore the new default object wrapper shares class introspection cache with other
* {@link BeansWrapper}-s created with {@link BeansWrapperBuilder}, which has an impact as
* {@link BeansWrapper#clearClassIntrospectionCache()} will be disallowed; see more about it there.
* </li>
* <li><p>
* The {@code ?iso_...} built-ins won't show the time zone offset for {@link java.sql.Time} values anymore,
* because most databases store time values that aren't in any time zone, but just store hour, minute,
* second, and decimal second field values. If you still want to show the offset (like for PostgreSQL
* "time with time zone" columns you should), you can force showing the time zone offset by using
* {@code myTime?string.iso_fz} (and its other variants).
* </li>
* <li><p>{@code ?is_enumerable} correctly returns {@code false} for Java methods get from Java objects that
* are wrapped with {@link BeansWrapper} and its subclasses, like {@link DefaultObjectWrapper}. Although
* method values implement {@link TemplateSequenceModel} (because of a historical design quirk in
* {@link BeansWrapper}), trying to {@code #list} them will cause error, hence they aren't enumerable.
* </li>
* <li><p>
* {@code ?c} will return {@code "INF"}, {@code "-INF"} and {@code "NaN"} for positive/negative infinity
* and IEEE floating point Not-a-Number, respectively. These are the XML Schema compatible representations
* of these special values. Earlier it has returned what {@link DecimalFormat} did with US locale, none of
* which was understood by any (common) computer language.
* </li>
* <li><p>
*