2021SC@SDUSC

目录

Configuration.java

代码分析

DefaultEnumerationAdapter.java

代码分析

SimpleSequence.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. */@Deprecatedpublic static final String INCOMPATIBLE_IMPROVEMENTS = INCOMPATIBLE_IMPROVEMENTS_KEY_SNAKE_CASE;/** @deprecated Use {@link #INCOMPATIBLE_IMPROVEMENTS_KEY} instead. */@Deprecatedpublic 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. */@Deprecatedpublic static final String DEFAULT_INCOMPATIBLE_ENHANCEMENTS = DEFAULT_INCOMPATIBLE_IMPROVEMENTS.toString();/** @deprecated Use {@link #DEFAULT_INCOMPATIBLE_IMPROVEMENTS} instead. */@Deprecatedpublic 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.xprivate boolean fallbackOnNullLoopVariable = true;  // Default for backward compatibilityprivate 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)}).  */@Deprecatedpublic 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>&lt;foo bar='${val}'&gt;</tt>) instead of plain quotation mark*     (<tt>&lt;foo bar="${val}"&gt;</tt>), they might produce HTML/XML that's not well-formed. Note that*     {@code ?html} didn't do this because long ago there was no cross-browser way of doing this, but it's not a*     concern anymore.*   </li>*   <li><p>*     2.3.21 (or higher):*     <ul>*       <li><p>*         The <em>default</em> of the {@code object_wrapper} setting ({@link #getObjectWrapper()}) changes from*         {@link ObjectWrapper#DEFAULT_WRAPPER} to another almost identical {@link DefaultObjectWrapper} singleton,*         returned by {@link DefaultObjectWrapperBuilder#build()}. The new default object wrapper's*         "incompatible improvements" version is set to the same as of the {@link Configuration}.*         See {@link BeansWrapper#BeansWrapper(Version)} for further details. Furthermore, the new default*         object wrapper doesn't allow changing its settings; setter methods throw {@link IllegalStateException}).*         (If anything tries to call setters on the old default in your application, that's a dangerous bug that*         won't remain hidden now. As the old default is a singleton too, potentially shared by independently*         developed components, most of them expects the out-of-the-box behavior from it (and the others are*         necessarily buggy). Also, then concurrency glitches can occur (and even pollute the class introspection*         cache) because the singleton is modified after publishing to other threads.)*         Furthermore the new default object wrapper shares class introspection cache with other*         {@link BeansWrapper}-s created with {@link BeansWrapperBuilder}, which has an impact as*         {@link BeansWrapper#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>*          FTL hash literals that repeat keys now only have the key once with {@code ?keys}, and only has the last*          value associated to that key with {@code ?values}. This is consistent with the behavior of*          {@code hash[key]} and how maps work in Java.       *       </li>*       <li><p>In most cases (where FreeMarker is able to do that), for {@link TemplateLoader}-s that use*         {@link URLConnection}, {@code URLConnection#setUseCaches(boolean)} will called with {@code false},*         so that only FreeMarker will do caching, not the URL scheme's handler.*         See {@link URLTemplateLoader#setURLConnectionUsesCaches(Boolean)} for more details.*       </li>*       <li><p>*         The default of the {@code template_loader} setting ({@link Configuration#getTemplateLoader()}) changes*         to {@code null}, which means that FreeMarker will not find any templates. Earlier*         the default was a {@link FileTemplateLoader} that used the current directory as the root. This was*         dangerous and fragile as you usually don't have good control over what the current directory will be.*         Luckily, the old default almost never looked for the templates at the right place*         anyway, so pretty much all applications had to set the {@code template_loader} setting, so it's unlikely*         that changing the default breaks your application.*       </li>*       <li><p>*          Right-unlimited ranges become readable (like listable), so {@code <#list 1.. as i>...</#list>} works.*          Earlier they were only usable for slicing (like {@code hits[10..]}).*       </li>*       <li><p>*          Empty ranges return {@link Constants#EMPTY_SEQUENCE} instead of an empty {@link SimpleSequence}. This*          is in theory backward compatible, as the API only promises to give something that implements*          {@link TemplateSequenceModel}.*       </li>*       <li><p>*          Unclosed comments ({@code <#-- ...}) and {@code #noparse}-s won't be silently closed at the end of*          template anymore, but cause a parsing error instead.*       </li>*     </ul>*   </li>*   <li><p>*     2.3.22 (or higher):*     <ul>*       <li><p>*          {@link DefaultObjectWrapper} has some substantial changes with {@code incompatibleImprovements} 2.3.22;*          check them out at {@link DefaultObjectWrapper#DefaultObjectWrapper(Version)}. It's important to know*          that if you set the {@code object_wrapper} setting (to an other value than {@code "default"}), rather*          than leaving it on its default value, the {@code object_wrapper} won't inherit the*          {@code incompatibleImprovements} of the {@link Configuration}. In that case, if you want the 2.3.22*          improvements of {@link DefaultObjectWrapper}, you have to set it in the {@link DefaultObjectWrapper}*          object itself too! (Note that it's OK to use a {@link DefaultObjectWrapper} with a different*          {@code incompatibleImprovements} version number than that of the {@link Configuration}, if that's*          really what you want.)*       </li>*       <li><p>*          In templates, {@code .template_name} will <em>always</em> return the main (top level) template's name.*          It won't be affected by {@code #include} and {@code #nested} anymore. This is unintended, a bug with*          {@code incompatible_improvement} 2.3.22 (a consequence of the lower level fixing described in the next*          point). The old behavior of {@code .template_name} is restored if you set*          {@code incompatible_improvement} to 2.3.23 (while {@link Configurable#getParent()}) of*          {@link Environment} keeps the changed behavior shown in the next point). *       </li>*       <li><p>*          {@code #include} and {@code #nested} doesn't change the parent {@link Template} (see*          {@link Configurable#getParent()}) of the {@link Environment} anymore to the {@link Template} that's*          included or whose namespace {@code #nested} "returns" to. Thus, the parent of {@link Environment} will*          be now always the main {@link Template}. (The main {@link Template} is the {@link Template} whose*          {@code process} or {@code createProcessingEnvironment} method was called to initiate the output*          generation.) Note that apart from the effect on FTL's {@code .template_name} (see*          previous point), this should only matter if you have set settings directly on {@link Template} objects,*          and almost nobody does that. Also note that macro calls have never changed the {@link Environment}*          parent to the {@link Template} that contains the macro definition, so this mechanism was always broken.*          As now we consistently never change the parent, the behavior when calling macros didn't change.*       </li>*       <li><p>*          When using {@code freemarker.ext.servlet.FreemarkerServlet}:*          <ul>*             <li>*               <p>When using custom JSP tag libraries: Fixes bug where some kind of*               values, when put into the JSP <em>page</em> scope (via {@code #global} or via the JSP*               {@code PageContext} API) and later read back with the JSP {@code PageContext} API (typically in a*               custom JSP tag), might come back as FreeMarker {@link TemplateModel} objects instead of as objects*               with a standard Java type. Other Servlet scopes aren't affected. It's highly unlikely that*               something expects the presence of this bug. The affected values are of the FTL types listed below,*               and to trigger the bug, they either had to be created directly in the template (like as an FTL*               literal or with {@code ?date}/{@code time}/{@code datetime}), or you had to use*               {@link DefaultObjectWrapper} or {@link SimpleObjectWrapper} (or a subclass of them):*               *               <ul>*                 <li>FTL date/time/date-time values may came back as {@link SimpleDate}-s, now they come back as*                 {@link java.util.Date java.util.Date}-s instead.</li>*             *                 <li>FTL sequence values may came back as {@link SimpleSequence}-s, now they come back as*                 {@link java.util.List}-s as expected. This at least stands assuming that the*                 {@link Configuration#setSetting(String, String) object_wrapper} configuration setting is a*                 subclass of {@link BeansWrapper} (such as {@link DefaultObjectWrapper}, which is the default),*                 but that's practically always the case in applications that use FreeMarker's JSP extension*                 (otherwise it can still work, but it depends on the quality and capabilities of the*                 {@link ObjectWrapper} implementation).</li>*             *                 <li>FTL hash values may came back as {@link SimpleHash}-es, now they come back as*                 {@link java.util.Map}-s as expected (again, assuming that the object wrapper is a subclass of*                 {@link BeansWrapper}, like preferably {@link DefaultObjectWrapper}, which is also the default).*                 </li>*             *                 <li>FTL collection values may came back as {@link SimpleCollection}-s, now they come back as*                 {@link java.util.Collection}-s as expected (again, assuming that the object wrapper is a subclass*                 of {@link BeansWrapper}, like preferably {@link DefaultObjectWrapper}).</li>*               </ul>*             </li>*             <li><p>*               Initial {@code "["} in the {@code TemplatePath} init-param*               has special meaning; it's used for specifying multiple comma separated locations, like in*               {@code <param-value>[ WEB-INF/templates, classpath:com/example/myapp/templates ]</param-value>}*             </li>*             <li><p>*               Initial <tt>"{"</tt> in the {@code TemplatePath} init-param is reserved for future purposes, and*               thus will throw exception.*             </li>*          </ul>*       </li>*     </ul>*   </li>*   <li><p>*     2.3.23 (or higher):*     <ul>*       <li><p>*          Fixed a loophole in the implementation of the long existing parse-time rule that says that*          {@code #break}, in the FTL source code itself, must occur nested inside a breakable directive, such as*          {@code #list} or {@code #switch}. This check could be circumvented with {@code #macro} or*          {@code #function}, like this:*          {@code <#list 1..1 as x><#macro callMeLater><#break></#macro></#list><@callMeLater />}.*          After activating this fix, this will be a parse time error.*       </li>*       <li><p>*          If you have used {@code incompatible_improvements} 2.3.22 earlier, know that there the behavior of the*          {@code .template_name} special variable used in templates was accidentally altered, but now it's*          restored to be backward compatible with 2.3.0. (Ironically, the restored legacy behavior itself is*          broken when it comes to macro invocations, we just keep it for backward compatibility. If you need fixed*          behavior, use {@code .current_template_name} or {@code .main_template_name} instead.)*       </li>*     </ul>*   </li>*   <li><p>*     2.3.24 (or higher):*     <ul>*       <li><p>*          The default of the*          {@link #setRecognizeStandardFileExtensions(boolean) recognize_standard_file_extensions}*          setting changes to {@code true}, which means that templates whose name ends with {@code ".ftlh"} or*          {@code ".ftlx"} will automatically get {@link HTMLOutputFormat#INSTANCE} or*          {@link XMLOutputFormat#INSTANCE} output format respectively, in both cases with*          {@link #ENABLE_IF_DEFAULT_AUTO_ESCAPING_POLICY} {@link #setAutoEscapingPolicy(int) auto_escaping_policy}.*          These "file" extensions aren't case sensitive.*       </li>*       <li><p>*          In number format and date format strings (like in the {@code number_format} setting, or in templates in*          {@code n?string("0.##")}), an initial {@code '@'} has special meaning; they refer to a custom format*          with the name given after the {@code @} (see: {@link #setCustomNumberFormats(Map)},*          {@link #setCustomDateFormats(Map)}, {@link #setNumberFormat(String)}, and {@link #setDateTimeFormat}).*          If the custom format doesn't exist, that will be an error. To have a literal {@code @} as the first*          character in the output, it has to be written as {@code @@}. Again, all this only applies to the very*          first character of the format string, so {@code @} characters elsewhere must not be doubled. Also, if*          there are any custom formats defined, initial {@code '@'} will have the new meaning regardless of*          the value of the {@code incompatible_improvements} setting. So you don't need to set the*          {@code incompatible_improvements} only to use custom formats. *       </li>*       <li><p>*          Expressions inside interpolations that were inside <em>string literal expressions</em>*          (not <code>${...}</code>-s in general), like in <code>&lt;#assign s="Hello ${name}!"&gt;</code>, has*          always used {@code incompatbileImprovement}-s 0 (2.3.0 in effect). Now it's fixed.*       </li>*       <li><p>*          {@link DefaultObjectWrapper} has some minor changes with {@code incompatibleImprovements} 2.3.24;*          check them out at {@link DefaultObjectWrapper#DefaultObjectWrapper(Version)}. It's important to know*          that if you set the {@code object_wrapper} setting (to an other value than {@code "default"}), rather*          than leaving it on its default value, the {@code object_wrapper} won't inherit the*          {@code incompatibleImprovements} of the {@link Configuration}. In that case, if you want the 2.3.24*          improvements of {@link DefaultObjectWrapper}, you have to set it in the {@link DefaultObjectWrapper}*          object itself too! (Note that it's OK to use a {@link DefaultObjectWrapper} with a different*          {@code incompatibleImprovements} version number than that of the {@link Configuration}, if that's*          really what you want.)*       </li>*       <li><p>*          Fixed bug: The {@code #import} directive meant to copy the library variable into a global variable if*          it's executed in the main namespace, but that haven't happened when the imported template was already*          imported earlier in another namespace. *       </li>*       <li><p>*          {@code ?is_sequence} doesn't return {@code true} for Java methods wrapped by {@link BeansWrapper} and*          its subclasses (most notably {@link DefaultObjectWrapper}) anymore, as they only implement the*          {@code [index]} operator, but not {@code ?size}, which causes {@code <#list ...>} to fail among others.*          (They shouldn't implement either, but this is historical heritage.)*     </ul>*   </li>*   <li><p>*     2.3.25 (or higher):*     <ul>*       <li><p>*          When calling {@link Configurable#setAutoIncludes(List)} on a {@link Configuration}, it filters out*          duplicates from the list, similarly as repeatedly calling {@link Configurable#addAutoInclude(String)}*          would, hence avoiding repeated inclusions. Calling {@link Configurable#setAutoIncludes(List)} on other*          {@link Configurable}-s always do this filtering regardless of the incompatible improvements setting. *     </ul>*   </li>*   <li><p>*     2.3.26 (or higher):*     <ul>*       <li><p>*          {@link BeansWrapper} and {@link DefaultObjectWrapper} now exposes Java 8 default methods (and the bean*          properties they define); see {@link BeansWrapper#BeansWrapper(Version)}. *     </ul>*   </li>*   <li><p>*     2.3.27 (or higher):*     <ul>*       <li><p>*          {@link BeansWrapper} and {@link DefaultObjectWrapper} now prefers the non-indexed JavaBean property*          read method over the indexed read method when Java 8 exposes both;*          see {@link BeansWrapper#BeansWrapper(Version)}.*       <li><p>*          The following unchecked exceptions (but not their subclasses) will be wrapped into*          {@link TemplateException}-s when thrown during evaluating expressions or calling directives:*          {@link NullPointerException}, {@link ClassCastException}, {@link IndexOutOfBoundsException}, and*          {@link InvocationTargetException}. The goal of this is the same as of setting*          {@link #setWrapUncheckedExceptions(boolean) wrap_unchecked_exceptions} to {@code true} (see more there),*          but this is more backward compatible, as it avoids wrapping unchecked exceptions that the calling*          application is likely to catch specifically (like application-specific unchecked exceptions).*       <li><p>*          When the {@link Writer} returned by {@link TemplateTransformModel#getWriter(Writer, Map)} implements*          {@link TransformControl}, exceptions that are used internally by FreeMarker for flow control (for*          {@code <#return>}, {@code <#break>}, etc.) won't be passed to*          {@link TransformControl#onError(Throwable)} anymore. Earlier, if {@code onError} didn't rethrow the*          exception (though almost all implementation does), you couldn't use said directives inside the*          transformed block. It's very unlikely that user code is affected by this, partially because these aren't*          commonly implemented interfaces (especially not {@link TransformControl}), and because it's unlikely*          that templates utilize the the bug that's not fixed.*     </ul>*   </li>*   <li><p>*     2.3.28 (or higher):*     <ul>*       <li><p>When calling a macro or function (things defined in a template, not directly in Java) and the*           argument list contains {@code .current_template_name}, now it will correctly evaluate to the template*           that contains the call, rather than to the template that contains the macro or function definition.*           (Of course, the parameter default value expression is still evaluated in the context of the called*           macro or function.) Similarly, {@code .macro_caller_template_name} (which itself was added in 2.3.28),*           when used in a macro call argument, won't be incorrectly evaluated in the context of the called macro.*       <li><p>Fixed legacy parser glitch where a tag can be closed with an illegal {@code ]} (when it's not part*           of an expression) despite that the tag syntax is set to angle brackets. For example {@code <#if x]}*           worked just like {@code <#if x>}. Note that it doesn't affect the legal usage of {@code ]}, like*           {@code <#if x[0]>} works correctly without this fix as well. *     </ul>*   </li>*   <li><p>*     2.3.31 (or higher):*     <ul>*       <li><p>When you set the {@code number_format} setting to {@code "computer"} (or you call*       {@link Environment#getCNumberFormat()}), the format now matches the behavior of {@code ?c}, when formatting*       infinite (positive and negative), and NaN. Matching the behavior of {@code ?c} was always the intent,*       but before this incompatible improvement, the {@code "computer"} format always behaved like {@code ?c}*       before Incompatible Improvements 2.3.21, where instead of INF, and NaN, the results used unicode characters*       U+221E, and U+FFFD.*     </ul>*   </li>* </ul>* * @throws IllegalArgumentException*             If {@code incompatibleImmprovements} refers to a version that wasn't released yet when the currently*             used FreeMarker version was released, or is less than 2.3.0, or is {@code null}.* * @since 2.3.21*/public Configuration(Version incompatibleImprovements) {super(incompatibleImprovements);// 这个延迟到这.而不是在静态初始化器中做这个checkFreeMarkerVersionClash();NullArgumentException.check("incompatibleImprovements", incompatibleImprovements);checkCurrentVersionNotRecycled(incompatibleImprovements);this.incompatibleImprovements = incompatibleImprovements;createTemplateCache();loadBuiltInSharedVariables();}private static void checkFreeMarkerVersionClash() {if (FM_24_DETECTED) {throw new RuntimeException("Clashing FreeMarker versions (" + VERSION + " and some post-2.3.x) detected: "+ "found post-2.3.x class " + FM_24_DETECTION_CLASS_NAME + ". You probably have two different "+ "freemarker.jar-s in the classpath.");}}private void createTemplateCache() {cache = new TemplateCache(getDefaultTemplateLoader(),getDefaultCacheStorage(),getDefaultTemplateLookupStrategy(),getDefaultTemplateNameFormat(),null,this);cache.clear(); // for fully BC behaviorcache.setDelay(5000);}private void recreateTemplateCacheWith(TemplateLoader loader, CacheStorage storage,TemplateLookupStrategy templateLookupStrategy, TemplateNameFormat templateNameFormat,TemplateConfigurationFactory templateConfigurations) {TemplateCache oldCache = cache;cache = new TemplateCache(loader, storage, templateLookupStrategy, templateNameFormat, templateConfigurations, this);cache.clear(); // 为了完全的 BC行为cache.setDelay(oldCache.getDelay());cache.setLocalizedLookup(localizedLookup);}private void recreateTemplateCache() {recreateTemplateCacheWith(cache.getTemplateLoader(), cache.getCacheStorage(),cache.getTemplateLookupStrategy(), cache.getTemplateNameFormat(),getTemplateConfigurations());}private TemplateLoader getDefaultTemplateLoader() {return createDefaultTemplateLoader(getIncompatibleImprovements(), getTemplateLoader());}static TemplateLoader createDefaultTemplateLoader(Version incompatibleImprovements) {return createDefaultTemplateLoader(incompatibleImprovements, null);}private static TemplateLoader createDefaultTemplateLoader(Version incompatibleImprovements, TemplateLoader existingTemplateLoader) {if (incompatibleImprovements.intValue() < _TemplateAPI.VERSION_INT_2_3_21) {if (existingTemplateLoader instanceof LegacyDefaultFileTemplateLoader) {return existingTemplateLoader;}try {return new LegacyDefaultFileTemplateLoader();} catch (Exception e) {CACHE_LOG.warn("Couldn't create legacy default TemplateLoader which accesses the current directory. "+ "(Use new Configuration(Configuration.VERSION_2_3_21) or higher to avoid this.)", e);return null;}} else {return null;}}private static class LegacyDefaultFileTemplateLoader extends FileTemplateLoader {public LegacyDefaultFileTemplateLoader() throws IOException {super();}}private TemplateLookupStrategy getDefaultTemplateLookupStrategy() {return getDefaultTemplateLookupStrategy(getIncompatibleImprovements());}static TemplateLookupStrategy getDefaultTemplateLookupStrategy(Version incompatibleImprovements) {return TemplateLookupStrategy.DEFAULT_2_3_0;}private TemplateNameFormat getDefaultTemplateNameFormat() {return getDefaultTemplateNameFormat(getIncompatibleImprovements());}static TemplateNameFormat getDefaultTemplateNameFormat(Version incompatibleImprovements) {return TemplateNameFormat.DEFAULT_2_3_0;}private CacheStorage getDefaultCacheStorage() {return createDefaultCacheStorage(getIncompatibleImprovements(), getCacheStorage()); }static CacheStorage createDefaultCacheStorage(Version incompatibleImprovements, CacheStorage existingCacheStorage) {if (existingCacheStorage instanceof DefaultSoftCacheStorage) {return existingCacheStorage;}return new DefaultSoftCacheStorage(); }static CacheStorage createDefaultCacheStorage(Version incompatibleImprovements) {return createDefaultCacheStorage(incompatibleImprovements, null); }private static class DefaultSoftCacheStorage extends SoftCacheStorage {// Nothing to override}private TemplateExceptionHandler getDefaultTemplateExceptionHandler() {return getDefaultTemplateExceptionHandler(getIncompatibleImprovements());}private AttemptExceptionReporter getDefaultAttemptExceptionReporter() {return getDefaultAttemptExceptionReporter(getIncompatibleImprovements());}private boolean getDefaultLogTemplateExceptions() {return getDefaultLogTemplateExceptions(getIncompatibleImprovements());}private boolean getDefaultWrapUncheckedExceptions() {return getDefaultWrapUncheckedExceptions(getIncompatibleImprovements());}private ObjectWrapper getDefaultObjectWrapper() {return getDefaultObjectWrapper(getIncompatibleImprovements());}// 可配置的Package需要这个来初始化字段默认值static TemplateExceptionHandler getDefaultTemplateExceptionHandler(Version incompatibleImprovements) {return TemplateExceptionHandler.DEBUG_HANDLER;}//可配置的Package需要这个来初始化字段默认值。static AttemptExceptionReporter getDefaultAttemptExceptionReporter(Version incompatibleImprovements) {return AttemptExceptionReporter.LOG_ERROR_REPORTER;}static boolean getDefaultLogTemplateExceptions(Version incompatibleImprovements) {return true;}static boolean getDefaultWrapUncheckedExceptions(Version incompatibleImprovements) {return false;}@Override
//clone()方法给copy实例进行赋值,包括各项属性public Object clone() {try {Configuration copy = (Configuration) super.clone();copy.sharedVariables = new HashMap(sharedVariables);copy.localeToCharsetMap = new ConcurrentHashMap(localeToCharsetMap);copy.recreateTemplateCacheWith(cache.getTemplateLoader(), cache.getCacheStorage(),cache.getTemplateLookupStrategy(), cache.getTemplateNameFormat(),cache.getTemplateConfigurations());return copy;} catch (CloneNotSupportedException e) {throw new BugException("Cloning failed", e);}}private void loadBuiltInSharedVariables() {sharedVariables.put("capture_output", new CaptureOutput());sharedVariables.put("compress", StandardCompress.INSTANCE);sharedVariables.put("html_escape", new HtmlEscape());sharedVariables.put("normalize_newlines", new NormalizeNewlines());sharedVariables.put("xml_escape", new XmlEscape());}/*** Loads a preset language-to-encoding map, similarly as if you have called* {@link #clearEncodingMap()} and then did multiple {@link #setEncoding(Locale, String)} calls.* It assumes the usual character encodings for most languages.* The previous content of the encoding map will be lost.* This default map currently contains the following mappings:* * <table style="width: auto; border-collapse: collapse" border="1" summary="preset language to encoding mapping">*   <tr><td>ar</td><td>ISO-8859-6</td></tr>*   <tr><td>be</td><td>ISO-8859-5</td></tr>*   <tr><td>bg</td><td>ISO-8859-5</td></tr>*   <tr><td>ca</td><td>ISO-8859-1</td></tr>*   <tr><td>cs</td><td>ISO-8859-2</td></tr>*   <tr><td>da</td><td>ISO-8859-1</td></tr>*   <tr><td>de</td><td>ISO-8859-1</td></tr>*   <tr><td>el</td><td>ISO-8859-7</td></tr>*   <tr><td>en</td><td>ISO-8859-1</td></tr>*   <tr><td>es</td><td>ISO-8859-1</td></tr>*   <tr><td>et</td><td>ISO-8859-1</td></tr>*   <tr><td>fi</td><td>ISO-8859-1</td></tr>*   <tr><td>fr</td><td>ISO-8859-1</td></tr>*   <tr><td>hr</td><td>ISO-8859-2</td></tr>*   <tr><td>hu</td><td>ISO-8859-2</td></tr>*   <tr><td>is</td><td>ISO-8859-1</td></tr>*   <tr><td>it</td><td>ISO-8859-1</td></tr>*   <tr><td>iw</td><td>ISO-8859-8</td></tr>*   <tr><td>ja</td><td>Shift_JIS</td></tr>*   <tr><td>ko</td><td>EUC-KR</td></tr>    *   <tr><td>lt</td><td>ISO-8859-2</td></tr>*   <tr><td>lv</td><td>ISO-8859-2</td></tr>*   <tr><td>mk</td><td>ISO-8859-5</td></tr>*   <tr><td>nl</td><td>ISO-8859-1</td></tr>*   <tr><td>no</td><td>ISO-8859-1</td></tr>*   <tr><td>pl</td><td>ISO-8859-2</td></tr>*   <tr><td>pt</td><td>ISO-8859-1</td></tr>*   <tr><td>ro</td><td>ISO-8859-2</td></tr>*   <tr><td>ru</td><td>ISO-8859-5</td></tr>*   <tr><td>sh</td><td>ISO-8859-5</td></tr>*   <tr><td>sk</td><td>ISO-8859-2</td></tr>*   <tr><td>sl</td><td>ISO-8859-2</td></tr>*   <tr><td>sq</td><td>ISO-8859-2</td></tr>*   <tr><td>sr</td><td>ISO-8859-5</td></tr>*   <tr><td>sv</td><td>ISO-8859-1</td></tr>*   <tr><td>tr</td><td>ISO-8859-9</td></tr>*   <tr><td>uk</td><td>ISO-8859-5</td></tr>*   <tr><td>zh</td><td>GB2312</td></tr>*   <tr><td>zh_TW</td><td>Big5</td></tr>* </table>* * @see #clearEncodingMap()* @see #setEncoding(Locale, String)* @see #setDefaultEncoding(String)*///这个方法是为了把关键变量传到localeToCharsetMap中public void loadBuiltInEncodingMap() {localeToCharsetMap.clear();localeToCharsetMap.put("ar", "ISO-8859-6");localeToCharsetMap.put("be", "ISO-8859-5");localeToCharsetMap.put("bg", "ISO-8859-5");localeToCharsetMap.put("ca", "ISO-8859-1");localeToCharsetMap.put("cs", "ISO-8859-2");localeToCharsetMap.put("da", "ISO-8859-1");localeToCharsetMap.put("de", "ISO-8859-1");localeToCharsetMap.put("el", "ISO-8859-7");localeToCharsetMap.put("en", "ISO-8859-1");localeToCharsetMap.put("es", "ISO-8859-1");localeToCharsetMap.put("et", "ISO-8859-1");localeToCharsetMap.put("fi", "ISO-8859-1");localeToCharsetMap.put("fr", "ISO-8859-1");localeToCharsetMap.put("hr", "ISO-8859-2");localeToCharsetMap.put("hu", "ISO-8859-2");localeToCharsetMap.put("is", "ISO-8859-1");localeToCharsetMap.put("it", "ISO-8859-1");localeToCharsetMap.put("iw", "ISO-8859-8");localeToCharsetMap.put("ja", "Shift_JIS");localeToCharsetMap.put("ko", "EUC-KR");    localeToCharsetMap.put("lt", "ISO-8859-2");localeToCharsetMap.put("lv", "ISO-8859-2");localeToCharsetMap.put("mk", "ISO-8859-5");localeToCharsetMap.put("nl", "ISO-8859-1");localeToCharsetMap.put("no", "ISO-8859-1");localeToCharsetMap.put("pl", "ISO-8859-2");localeToCharsetMap.put("pt", "ISO-8859-1");localeToCharsetMap.put("ro", "ISO-8859-2");localeToCharsetMap.put("ru", "ISO-8859-5");localeToCharsetMap.put("sh", "ISO-8859-5");localeToCharsetMap.put("sk", "ISO-8859-2");localeToCharsetMap.put("sl", "ISO-8859-2");localeToCharsetMap.put("sq", "ISO-8859-2");localeToCharsetMap.put("sr", "ISO-8859-5");localeToCharsetMap.put("sv", "ISO-8859-1");localeToCharsetMap.put("tr", "ISO-8859-9");localeToCharsetMap.put("uk", "ISO-8859-5");localeToCharsetMap.put("zh", "GB2312");localeToCharsetMap.put("zh_TW", "Big5");}//清空作用public void clearEncodingMap() {localeToCharsetMap.clear();}/*** Returns the default (singleton) Configuration object. Note that you can* create as many separate configurations as you wish; this global instance* is provided for convenience, or when you have no reason to use a separate* instance.* * @deprecated The usage of the static singleton (the "default")* {@link Configuration} instance can easily cause erroneous, unpredictable* behavior. This is because multiple independent software components may use* FreeMarker internally inside the same application, so they will interfere* because of the common {@link Configuration} instance. Each such component* should use its own private {@link Configuration} object instead, that it* typically creates with <code>new Configuration()</code> when the component* is initialized.*/@Deprecatedstatic public Configuration getDefaultConfiguration() {Configuration defaultConfig = Configuration.defaultConfig;if (defaultConfig == null) {synchronized (defaultConfigLock) {defaultConfig = Configuration.defaultConfig;if (defaultConfig == null) {defaultConfig = new Configuration();Configuration.defaultConfig = defaultConfig; }}}return defaultConfig;}/*** Sets the Configuration object that will be retrieved from future calls* to {@link #getDefaultConfiguration()}.* * @deprecated Using the "default" {@link Configuration} instance can* easily lead to erroneous, unpredictable behaviour.* See more {@link Configuration#getDefaultConfiguration() here...}.*/@Deprecatedstatic public void setDefaultConfiguration(Configuration config) {synchronized (defaultConfigLock) {defaultConfig = config;}}/*** Sets a {@link TemplateLoader} that is used to look up and load templates;* as a side effect the template cache will be emptied (unless the new and the old values are the same).* By providing your own {@link TemplateLoader} implementation, you can load templates from whatever kind of* storages, like from relational databases, NoSQL-storages, etc.* * <p>Convenience methods exists to install commonly used loaders, instead of using this method:* {@link #setClassForTemplateLoading(Class, String)}, * {@link #setClassLoaderForTemplateLoading(ClassLoader, String)}, * {@link #setDirectoryForTemplateLoading(File)}, and* {@link #setServletContextForTemplateLoading(Object, String)}.* * <p>You can chain several {@link TemplateLoader}-s together with {@link MultiTemplateLoader}.* * <p>Default value: You should always set the template loader instead of relying on the default value.* (But if you still care what it is, before "incompatible improvements" 2.3.21 it's a {@link FileTemplateLoader}* that uses the current directory as its root; as it's hard tell what that directory will be, it's not very useful* and dangerous. Starting with "incompatible improvements" 2.3.21 the default is {@code null}.)   */public void setTemplateLoader(TemplateLoader templateLoader) {// "synchronized" is removed from the API as it's not safe to set anything after publishing the Configurationsynchronized (this) {if (cache.getTemplateLoader() != templateLoader) {recreateTemplateCacheWith(templateLoader, cache.getCacheStorage(),cache.getTemplateLookupStrategy(), cache.getTemplateNameFormat(),cache.getTemplateConfigurations());}templateLoaderExplicitlySet = true;}}/*** Resets the setting to its default, as if it was never set. This means that when you change the* {@code incompatibe_improvements} setting later, the default will also change as appropriate. Also * {@link #isTemplateLoaderExplicitlySet()} will return {@code false}.* * @since 2.3.22*/public void unsetTemplateLoader() {if (templateLoaderExplicitlySet) {setTemplateLoader(getDefaultTemplateLoader());templateLoaderExplicitlySet = false;}}/*** Tells if {@link #setTemplateLoader(TemplateLoader)} (or equivalent) was already called on this instance.* * @since 2.3.22*/public boolean isTemplateLoaderExplicitlySet() {return templateLoaderExplicitlySet;}/*** The getter pair of {@link #setTemplateLoader(TemplateLoader)}.*/public TemplateLoader getTemplateLoader() {if (cache == null) {return null;}return cache.getTemplateLoader();}/*** Sets the {@link TemplateLookupStrategy} that is used to look up templates based on the requested name; as a side* effect the template cache will be emptied. The default value is {@link TemplateLookupStrategy#DEFAULT_2_3_0}.* * @since 2.3.22*/public void setTemplateLookupStrategy(TemplateLookupStrategy templateLookupStrategy) {if (cache.getTemplateLookupStrategy() != templateLookupStrategy) {recreateTemplateCacheWith(cache.getTemplateLoader(), cache.getCacheStorage(),templateLookupStrategy, cache.getTemplateNameFormat(),cache.getTemplateConfigurations());}templateLookupStrategyExplicitlySet = true;}//将设置重置为其默认值,就好像它从未设置过一样public void unsetTemplateLookupStrategy() {if (templateLookupStrategyExplicitlySet) {setTemplateLookupStrategy(getDefaultTemplateLookupStrategy());templateLookupStrategyExplicitlySet = false;}}//告诉{@link #setTemplateLookupStrategy(TemplateLookupStrategy)}(或等效)已经被调用public boolean isTemplateLookupStrategyExplicitlySet() {return templateLookupStrategyExplicitlySet;}/*** The getter pair of {@link #setTemplateLookupStrategy(TemplateLookupStrategy)}.*/public TemplateLookupStrategy getTemplateLookupStrategy() {if (cache == null) {return null;}return cache.getTemplateLookupStrategy();}/*** Sets the template name format used. The default is {@link TemplateNameFormat#DEFAULT_2_3_0}, while the* recommended value for new projects is {@link TemplateNameFormat#DEFAULT_2_4_0}.* * @since 2.3.22*/public void setTemplateNameFormat(TemplateNameFormat templateNameFormat) {if (cache.getTemplateNameFormat() != templateNameFormat) {recreateTemplateCacheWith(cache.getTemplateLoader(), cache.getCacheStorage(),cache.getTemplateLookupStrategy(), templateNameFormat,cache.getTemplateConfigurations());}templateNameFormatExplicitlySet = true;}/*** Resets the setting to its default, as if it was never set. This means that when you change the* {@code incompatibe_improvements} setting later, the default will also change as appropriate. Also * {@link #isTemplateNameFormatExplicitlySet()} will return {@code false}.* * @since 2.3.22*/public void unsetTemplateNameFormat() {if (templateNameFormatExplicitlySet) {setTemplateNameFormat(getDefaultTemplateNameFormat());templateNameFormatExplicitlySet = false;}}/*** Tells if {@link #setTemplateNameFormat(TemplateNameFormat)} (or equivalent) was already called on this instance.* * @since 2.3.22*/public boolean isTemplateNameFormatExplicitlySet() {return templateNameFormatExplicitlySet;}/*** The getter pair of {@link #setTemplateNameFormat(TemplateNameFormat)}.*/public TemplateNameFormat getTemplateNameFormat() {if (cache == null) {return null;}return cache.getTemplateNameFormat();}/*** Sets a {@link TemplateConfigurationFactory} that will configure individual templates where their settings differ* from those coming from the common {@link Configuration} object. A typical use case for that is specifying the* {@link TemplateConfiguration#setOutputFormat(OutputFormat) outputFormat} for templates based on their file* extension or parent directory.* * <p>* Note that the settings suggested by standard file extensions are stronger than that you set here. See* {@link #setRecognizeStandardFileExtensions(boolean)} for more information about standard file extensions.* * <p>See "Template configurations" in the FreeMarker Manual for examples.* * @since 2.3.24*/public void setTemplateConfigurations(TemplateConfigurationFactory templateConfigurations) {if (cache.getTemplateConfigurations() != templateConfigurations) {if (templateConfigurations != null) {templateConfigurations.setConfiguration(this);}recreateTemplateCacheWith(cache.getTemplateLoader(), cache.getCacheStorage(),cache.getTemplateLookupStrategy(), cache.getTemplateNameFormat(),templateConfigurations);}}/*** The getter pair of {@link #setTemplateConfigurations(TemplateConfigurationFactory)}.*/public TemplateConfigurationFactory getTemplateConfigurations() {if (cache == null) {return null;}return cache.getTemplateConfigurations();}/*** Sets the {@link CacheStorage} used for caching {@link Template}-s;* the earlier content of the template cache will be dropt.* * The default is a {@link SoftCacheStorage}. If the total size of the {@link Template}* objects is significant but most templates are used rarely, using a* {@link MruCacheStorage} instead might be advisable. If you don't want caching at* all, use {@link freemarker.cache.NullCacheStorage} (you can't use {@code null}).* * <p>Note that setting the cache storage will re-create the template cache, so* all its content will be lost.*/public void setCacheStorage(CacheStorage cacheStorage) {// "synchronized" 从API中删除,因为在发布配置后设置任何东西都不安全synchronized (this) {if (getCacheStorage() != cacheStorage) {recreateTemplateCacheWith(cache.getTemplateLoader(), cacheStorage,cache.getTemplateLookupStrategy(), cache.getTemplateNameFormat(),cache.getTemplateConfigurations());}cacheStorageExplicitlySet = true;}}/*** Resets the setting to its default, as if it was never set. This means that when you change the* {@code incompatibe_improvements} setting later, the default will also change as appropriate. Also * {@link #isCacheStorageExplicitlySet()} will return {@code false}.* * @since 2.3.22*/public void unsetCacheStorage() {if (cacheStorageExplicitlySet) {setCacheStorage(getDefaultCacheStorage());cacheStorageExplicitlySet = false;}}/*** Tells if {@link #setCacheStorage(CacheStorage)} (or equivalent) was already called on this instance.* * @since 2.3.22*/public boolean isCacheStorageExplicitlySet() {return cacheStorageExplicitlySet;}/*** The getter pair of {@link #setCacheStorage(CacheStorage)}.* * @since 2.3.20*/public CacheStorage getCacheStorage() {// "synchronized" is removed from the API as it's not safe to set anything after publishing the Configurationsynchronized (this) {if (cache == null) {return null;}return cache.getCacheStorage();}}/*** Sets the file system directory from which to load templates. This is equivalent to* {@code setTemplateLoader(new FileTemplateLoader(dir))}, so see* {@link FileTemplateLoader#FileTemplateLoader(File)} for more details.* * <p>* Note that FreeMarker can load templates from non-file-system sources too. See* {@link #setTemplateLoader(TemplateLoader)} from more details.* * <p>* Note that this shouldn't be used for loading templates that are coming from a WAR; use* {@link #setServletContextForTemplateLoading(Object, String)} then. Servlet containers might not unpack the WAR* file, in which case you clearly can't access the contained files via {@link File}. Even if the WAR is unpacked,* the servlet container might not expose the location as a {@link File}.* {@link #setServletContextForTemplateLoading(Object, String)} on the other hand will work in all these cases.*/public void setDirectoryForTemplateLoading(File dir) throws IOException {TemplateLoader tl = getTemplateLoader();if (tl instanceof FileTemplateLoader) {String path = ((FileTemplateLoader) tl).baseDir.getCanonicalPath();if (path.equals(dir.getCanonicalPath()))return;}setTemplateLoader(new FileTemplateLoader(dir));}/*** Sets the servlet context from which to load templates.* This is equivalent to {@code setTemplateLoader(new WebappTemplateLoader(sctxt, path))}* or {@code setTemplateLoader(new WebappTemplateLoader(sctxt))} if {@code path} was* {@code null}, so see {@code freemarker.cache.WebappTemplateLoader} for more details.* * @param servletContext the {@code javax.servlet.ServletContext} object. (The declared type is {@link Object}*        to prevent class loading error when using FreeMarker in an environment where*        there's no servlet classes available.)* @param path the path relative to the ServletContext.** @see #setTemplateLoader(TemplateLoader)*/public void setServletContextForTemplateLoading(Object servletContext, String path) {try {// 不要引入对servlet的链接时间依赖ClassUtil.forName("freemarker.cache.WebappTemplateLoader");// 不要引入对servlet的链接时间依赖final Class servletContextClass = ClassUtil.forName("javax.servlet.ServletContext");final Class[] constructorParamTypes;final Object[] constructorParams;if (path == null) {constructorParamTypes = new Class[] { servletContextClass };constructorParams = new Object[] { servletContext };} else {constructorParamTypes = new Class[] { servletContextClass, String.class };constructorParams = new Object[] { servletContext, path };}setTemplateLoader( (TemplateLoader)webappTemplateLoaderClass.getConstructor(constructorParamTypes).newInstance(constructorParams));} catch (Exception e) {throw new BugException(e);}}/*** Sets the class whose {@link Class#getResource(String)} method will be used to load templates, from the inside the* package specified. See {@link ClassTemplateLoader#ClassTemplateLoader(Class, String)} for more details.* * @param basePackagePath*            Separate steps with {@code "/"}, not {@code "."}, and note that it matters if this starts with*            {@code /} or not. See {@link ClassTemplateLoader#ClassTemplateLoader(Class, String)} for more details.* * @see #setClassLoaderForTemplateLoading(ClassLoader, String)* @see #setTemplateLoader(TemplateLoader)*/public void setClassForTemplateLoading(Class resourceLoaderClass, String basePackagePath) {setTemplateLoader(new ClassTemplateLoader(resourceLoaderClass, basePackagePath));}/*** Sets the {@link ClassLoader} whose {@link ClassLoader#getResource(String)} method will be used to load templates,* from the inside the package specified. See {@link ClassTemplateLoader#ClassTemplateLoader(Class, String)} for* more details.* * @param basePackagePath*            Separate steps with {@code "/"}, not {@code "."}. See*            {@link ClassTemplateLoader#ClassTemplateLoader(Class, String)} for more details.* * @see #setClassForTemplateLoading(Class, String)* @see #setTemplateLoader(TemplateLoader)* * @since 2.3.22*/public void setClassLoaderForTemplateLoading(ClassLoader classLoader, String basePackagePath) {setTemplateLoader(new ClassTemplateLoader(classLoader, basePackagePath));}/*** Sets the time in seconds that must elapse before checking whether there is a newer version of a template "file"* than the cached one.* * <p>* Historical note: Despite what the API documentation said earlier, this method is <em>not</em> thread-safe. While* it works well on most hardware, it's not guaranteed that FreeMarker will see the update in all threads, and* theoretically it's also possible that it will see a value that's a binary mixture of the new and the old one.* * @deprecated Use {@link #setTemplateUpdateDelayMilliseconds(long)} instead, because the time granularity of this method*             is often misunderstood to be milliseconds.*/@Deprecatedpublic void setTemplateUpdateDelay(int seconds) {cache.setDelay(1000L * seconds);}/*** Sets the time in milliseconds that must elapse before checking whether there is a newer version of a template* "file" than the cached one. Defaults to 5000 ms.* * <p>* When you get a template via {@link #getTemplate(String)} (or some of its overloads). FreeMarker will try to get* the template from the template cache. If the template is found, and at least this amount of time was elapsed* since the template last modification date was checked, FreeMarker will re-check the last modification date (this* could mean I/O), possibly reloading the template and updating the cache as a consequence (can mean even more* I/O). The {@link #getTemplate(String)} (or some of its overloads) call will only return after this all is* done, so it will return the fresh template.* * @since 2.3.23*/public void setTemplateUpdateDelayMilliseconds(long millis) {cache.setDelay(millis);}/*** The getter pair of {@link #setTemplateUpdateDelayMilliseconds(long)}.* * @since 2.3.23*/public long getTemplateUpdateDelayMilliseconds() {return cache.getDelay();}/*** Sets whether directives such as {@code if}, {@code else}, etc must be written as {@code #if}, {@code #else}, etc.* Defaults to {@code true}.* * <p>When this is {@code true},* any tag not starting with &lt;# or &lt;/# or &lt;@ or &lt;/@ is considered as plain text* and will go to the output as is. Tag starting with &lt;# or &lt;/# must* be valid FTL tag, or else the template is invalid (i.e. &lt;#noSuchDirective&gt;* is an error).* * @deprecated Only {@code true} (the default) value will be supported sometimes in the future.*/@Deprecatedpublic void setStrictSyntaxMode(boolean b) {strictSyntax = b;}@Override
/把一个对象的值传给另外一个对象public void setObjectWrapper(ObjectWrapper objectWrapper) {ObjectWrapper prevObjectWrapper = getObjectWrapper();super.setObjectWrapper(objectWrapper);objectWrapperExplicitlySet = true;if (objectWrapper != prevObjectWrapper) {try {setSharedVariablesFromRewrappableSharedVariables();} catch (TemplateModelException e) {throw new RuntimeException("Failed to re-wrap earliearly set shared variables with the newly set object wrapper",e);}}}/*** Resets the setting to its default, as if it was never set. This means that when you change the* {@code incompatibe_improvements} setting later, the default will also change as appropriate. Also * {@link #isObjectWrapperExplicitlySet()} will return {@code false}.* * @since 2.3.22*/public void unsetObjectWrapper() {if (objectWrapperExplicitlySet) {setObjectWrapper(getDefaultObjectWrapper());objectWrapperExplicitlySet = false;}}/*** Tells if {@link #setObjectWrapper(ObjectWrapper)} (or equivalent) was already called on this instance.* * @since 2.3.22*/public boolean isObjectWrapperExplicitlySet() {return objectWrapperExplicitlySet;}@Overridepublic void setLocale(Locale locale) {super.setLocale(locale);localeExplicitlySet = true;}/*** Resets the setting to its default, as if it was never set.* * @since 2.3.26*/public void unsetLocale() {if (localeExplicitlySet) {setLocale(getDefaultLocale());localeExplicitlySet = false;}}/*** Tells if {@link #setLocale(Locale)} (or equivalent) was already called on this instance, or it just holds the* default value.* * @since 2.3.26*/public boolean isLocaleExplicitlySet() {return localeExplicitlySet;}static Locale getDefaultLocale() {return Locale.getDefault();}@Overridepublic void setTimeZone(TimeZone timeZone) {super.setTimeZone(timeZone);timeZoneExplicitlySet = true;}/*** Resets the setting to its default, as if it was never set.* * @since 2.3.26*/public void unsetTimeZone() {if (timeZoneExplicitlySet) {setTimeZone(getDefaultTimeZone());timeZoneExplicitlySet = false;}}/*** Tells if {@link #setTimeZone(TimeZone)} (or equivalent) was already called on this instance, or it just holds the* default value.* * @since 2.3.26*/public boolean isTimeZoneExplicitlySet() {return timeZoneExplicitlySet;}static TimeZone getDefaultTimeZone() {return TimeZone.getDefault();}@Overridepublic void setTemplateExceptionHandler(TemplateExceptionHandler templateExceptionHandler) {super.setTemplateExceptionHandler(templateExceptionHandler);templateExceptionHandlerExplicitlySet = true;}/*** Resets the setting to its default, as if it was never set. This means that when you change the* {@code incompatibe_improvements} setting later, the default will also change as appropriate. Also * {@link #isTemplateExceptionHandlerExplicitlySet()} will return {@code false}.* * @since 2.3.22*/public void unsetTemplateExceptionHandler() {if (templateExceptionHandlerExplicitlySet) {setTemplateExceptionHandler(getDefaultTemplateExceptionHandler());templateExceptionHandlerExplicitlySet = false;}}/*** Tells if {@link #setTemplateExceptionHandler(TemplateExceptionHandler)} (or equivalent) was already called on* this instance.* * @since 2.3.22*/public boolean isTemplateExceptionHandlerExplicitlySet() {return templateExceptionHandlerExplicitlySet;}@Overridepublic void setAttemptExceptionReporter(AttemptExceptionReporter attemptExceptionReporter) {super.setAttemptExceptionReporter(attemptExceptionReporter);attemptExceptionReporterExplicitlySet = true;}/*** Resets the setting to its default, as if it was never set. This means that when you change the* {@code incompatibe_improvements} setting later, the default will also change as appropriate. Also * {@link #isAttemptExceptionReporterExplicitlySet()} will return {@code false}.* * @since 2.3.27*/public void unsetAttemptExceptionReporter() {if (attemptExceptionReporterExplicitlySet) {setAttemptExceptionReporter(getDefaultAttemptExceptionReporter());attemptExceptionReporterExplicitlySet = false;}}/*** Tells if {@link #setAttemptExceptionReporter(AttemptExceptionReporter)} (or equivalent) was already called on* this instance.* * @since 2.3.27*/public boolean isAttemptExceptionReporterExplicitlySet() {return attemptExceptionReporterExplicitlySet;}    /*** {@inheritDoc}* * @since 2.3.22*/@Overridepublic void setLogTemplateExceptions(boolean value) {super.setLogTemplateExceptions(value);logTemplateExceptionsExplicitlySet = true;}/*** Resets the setting to its default, as if it was never set. This means that when you change the* {@code incompatibe_improvements} setting later, the default will also change as appropriate. Also * {@link #isTemplateExceptionHandlerExplicitlySet()} will return {@code false}.* * @since 2.3.22*/public void unsetLogTemplateExceptions() {if (logTemplateExceptionsExplicitlySet) {setLogTemplateExceptions(getDefaultLogTemplateExceptions());logTemplateExceptionsExplicitlySet = false;}}/*** Tells if {@link #setLogTemplateExceptions(boolean)} (or equivalent) was already called on this instance.* * @since 2.3.22*/public boolean isLogTemplateExceptionsExplicitlySet() {return logTemplateExceptionsExplicitlySet;}/*** {@inheritDoc}* * @since 2.3.27*/@Overridepublic void setWrapUncheckedExceptions(boolean value) {super.setWrapUncheckedExceptions(value);wrapUncheckedExceptionsExplicitlySet = true;}/*** @since 2.3.27*/public void unsetWrapUncheckedExceptions() {if (wrapUncheckedExceptionsExplicitlySet) {setWrapUncheckedExceptions(getDefaultWrapUncheckedExceptions());wrapUncheckedExceptionsExplicitlySet = false;}}/*** Tells if {@link #setWrapUncheckedExceptions} (or equivalent) was already called on this instance.* * @since 2.3.27*/public boolean isWrapUncheckedExceptionsExplicitlySet() {return wrapUncheckedExceptionsExplicitlySet;}/*** The getter pair of {@link #setStrictSyntaxMode}.*/@Overridepublic boolean getStrictSyntaxMode() {return strictSyntax;}/*** Use {@link #Configuration(Version)} instead if possible; see the meaning of the parameter there.* * <p>Do NOT ever use {@link #getVersion()} to set the "incompatible improvements". Always use a fixed value, like* {@link #VERSION_2_3_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>If the default value of a setting depends on the {@code incompatibleImprovements} and the value of that setting* was never set in this {@link Configuration} object through the public API, its value will be set to the default* value appropriate for the new {@code incompatibleImprovements}. (This adjustment of a setting value doesn't* count as setting that setting, so setting {@code incompatibleImprovements} for multiple times also works as* expected.) Note that if the {@code template_loader} have to be changed because of this, the template cache will* be emptied.* * @throws IllegalArgumentException*             If {@code incompatibleImmprovements} refers to a version that wasn't released yet when the currently*             used FreeMarker version was released, or is less than 2.3.0, or is {@code null}.* * @since 2.3.20*/public void setIncompatibleImprovements(Version incompatibleImprovements) {_TemplateAPI.checkVersionNotNullAndSupported(incompatibleImprovements);if (!this.incompatibleImprovements.equals(incompatibleImprovements)) {checkCurrentVersionNotRecycled(incompatibleImprovements);this.incompatibleImprovements = incompatibleImprovements;if (!templateLoaderExplicitlySet) {templateLoaderExplicitlySet = true; unsetTemplateLoader();}if (!templateLookupStrategyExplicitlySet) {templateLookupStrategyExplicitlySet = true;unsetTemplateLookupStrategy();}if (!templateNameFormatExplicitlySet) {templateNameFormatExplicitlySet = true;unsetTemplateNameFormat();}if (!cacheStorageExplicitlySet) {cacheStorageExplicitlySet = true;unsetCacheStorage();}if (!templateExceptionHandlerExplicitlySet) {templateExceptionHandlerExplicitlySet = true;unsetTemplateExceptionHandler();}if (!attemptExceptionReporterExplicitlySet) {attemptExceptionReporterExplicitlySet = true;unsetAttemptExceptionReporter();}if (!logTemplateExceptionsExplicitlySet) {logTemplateExceptionsExplicitlySet = true;unsetLogTemplateExceptions();}if (!wrapUncheckedExceptionsExplicitlySet) {wrapUncheckedExceptionsExplicitlySet = true;unsetWrapUncheckedExceptions();}if (!objectWrapperExplicitlySet) {objectWrapperExplicitlySet = true;unsetObjectWrapper();}recreateTemplateCache();}}private static void checkCurrentVersionNotRecycled(Version incompatibleImprovements) {_TemplateAPI.checkCurrentVersionNotRecycled(incompatibleImprovements,"freemarker.configuration", "Configuration");}/*** @see #setIncompatibleImprovements(Version)* @return Never {@code null}. * @since 2.3.20*/@Overridepublic Version getIncompatibleImprovements() {return incompatibleImprovements;}/*** @deprecated Use {@link #Configuration(Version)}, or*    as last chance, {@link #setIncompatibleImprovements(Version)} instead.*/@Deprecatedpublic void setIncompatibleEnhancements(String version) {setIncompatibleImprovements(new Version(version));}/*** @deprecated Use {@link #getIncompatibleImprovements()} instead.*/@Deprecatedpublic String getIncompatibleEnhancements() {return incompatibleImprovements.toString();}/*** @deprecated Use {@link #getIncompatibleImprovements()} instead.*/@Deprecatedpublic int getParsedIncompatibleEnhancements() {return getIncompatibleImprovements().intValue();}/*** Sets whether the FTL parser will try to remove* superfluous white-space around certain FTL tags.*/public void setWhitespaceStripping(boolean b) {whitespaceStripping = b;}/*** Gets whether the FTL parser will try to remove* superfluous white-space around certain FTL tags.** @see #setWhitespaceStripping*/@Overridepublic boolean getWhitespaceStripping() {return whitespaceStripping;}/*** Sets when auto-escaping should be enabled depending on the current {@linkplain OutputFormat output format};* default is {@link #ENABLE_IF_DEFAULT_AUTO_ESCAPING_POLICY}.* * <p>Note that the default output format, {@link UndefinedOutputFormat}, is a non-escaping format, so there* auto-escaping will be off.* * <p>Note that the templates can turn auto-escaping on/off locally with directives like* {@code <#ftl auto_esc=...>}, {@code <#autoesc>...</#autoesc>}, and {@code <#noautoesc>...</#noautoesc>}, which* are ignoring the auto-escaping policy.* * <p><b>About auto-escaping</b></p>* * <p>* Auto-escaping has significance when a value is printed with <code>${...}</code> (or <code>#{...}</code>). If* auto-escaping is on, FreeMarker will assume that the value is plain text (as opposed to markup or some kind of* rich text), so it will escape it according the current output format (see {@link #setOutputFormat(OutputFormat)}* and {@link TemplateConfiguration#setOutputFormat(OutputFormat)}). If auto-escaping is off, FreeMarker will assume* that the string value is already in the output format, so it prints it as is to the output.** <p>Further notes on auto-escaping:* <ul>*   <li>When printing numbers, dates, and other kind of non-string values with <code>${...}</code>, they will be*       first converted to string (according the formatting settings and locale), then they are escaped just like*       string values.*   <li>When printing {@link TemplateMarkupOutputModel}-s, they aren't escaped again (they are already escaped).*   <li>Auto-escaping doesn't do anything if the current output format isn't an {@link MarkupOutputFormat}.*       That's the case for the default output format, {@link UndefinedOutputFormat}, and also for*       {@link PlainTextOutputFormat}.*   <li>The output format inside a string literal expression is always {@link PlainTextOutputFormat}*       (regardless of the output format of the containing template), which is a non-escaping format. Thus for*       example, with <code>&lt;#assign s = "foo${bar}"&gt;</code>, {@code bar} will always get into {@code s}*       without escaping, but with <code>&lt;#assign s&gt;foo${bar}&lt;#assign&gt;</code> it may will be escaped.* </ul>* * <p>Note that what you set here is just a default, which can be overridden for individual templates via* {@link #setTemplateConfigurations(TemplateConfigurationFactory)}. This setting is also overridden by the standard file* extensions; see them at {@link #setRecognizeStandardFileExtensions(boolean)}.* * @param autoEscapingPolicy*          One of the {@link #ENABLE_IF_DEFAULT_AUTO_ESCAPING_POLICY},*          {@link #ENABLE_IF_SUPPORTED_AUTO_ESCAPING_POLICY}, and {@link #DISABLE_AUTO_ESCAPING_POLICY} constants.  * * @see TemplateConfiguration#setAutoEscapingPolicy(int)* @see Configuration#setOutputFormat(OutputFormat)* @see TemplateConfiguration#setOutputFormat(OutputFormat)* * @since 2.3.24*/public void setAutoEscapingPolicy(int autoEscapingPolicy) {_TemplateAPI.validateAutoEscapingPolicyValue(autoEscapingPolicy);int prevAutoEscaping = getAutoEscapingPolicy();this.autoEscapingPolicy = autoEscapingPolicy;if (prevAutoEscaping != autoEscapingPolicy) {clearTemplateCache();}}/*** Getter pair of {@link #setAutoEscapingPolicy(int)}* * @since 2.3.24*/@Overridepublic int getAutoEscapingPolicy() {return autoEscapingPolicy;}/*** Sets the default output format. Usually, you should leave this on its default, which is* {@link UndefinedOutputFormat#INSTANCE}, and then use standard file extensions like "ftlh" (for HTML) or "ftlx"* (for XML) and ensure that {@link #setRecognizeStandardFileExtensions(boolean)} is {@code true} (see more there).* Where you can't use the standard extensions, templates still can be associated to output formats with* patterns matching their name (their path) using {@link #setTemplateConfigurations(TemplateConfigurationFactory)}.* But if all templates will have the same output format, you may use {@link #setOutputFormat(OutputFormat)} after* all, to a value like {@link HTMLOutputFormat#INSTANCE}, {@link XMLOutputFormat#INSTANCE}, etc. Also note* that templates can specify their own output format like {@code * <#ftl output_format="HTML">}, which overrides any configuration settings.* * <p>* The output format is mostly important because of auto-escaping (see {@link #setAutoEscapingPolicy(int)}), but* maybe also used by the embedding application to set the HTTP response MIME type, etc.* * @param outputFormat*            Not {@code null}; use {@link UndefinedOutputFormat#INSTANCE} instead.* * @see #setRegisteredCustomOutputFormats(Collection)* @see #setTemplateConfigurations(TemplateConfigurationFactory)* @see #setRecognizeStandardFileExtensions(boolean)* @see #setAutoEscapingPolicy(int)* * @since 2.3.24*/public void setOutputFormat(OutputFormat outputFormat) {if (outputFormat == null) {throw new NullArgumentException("outputFormat","You may meant: " + UndefinedOutputFormat.class.getSimpleName() + ".INSTANCE");}OutputFormat prevOutputFormat = getOutputFormat();this.outputFormat = outputFormat;outputFormatExplicitlySet = true;if (prevOutputFormat != outputFormat) {clearTemplateCache();}}/*** Getter pair of {@link #setOutputFormat(OutputFormat)}* * @since 2.3.24*/@Overridepublic OutputFormat getOutputFormat() {return outputFormat;}/*** Tells if {@link #setOutputFormat(OutputFormat)} (or equivalent) was already called on this instance.* * @since 2.3.24*/public boolean isOutputFormatExplicitlySet() {return outputFormatExplicitlySet;}/*** Resets the setting to its default, as if it was never set. This means that when you change the* {@code incompatibe_improvements} setting later, the default will also change as appropriate. Also* {@link #isOutputFormatExplicitlySet()} will return {@code false}.* * @since 2.3.24*/public void unsetOutputFormat() {outputFormat = UndefinedOutputFormat.INSTANCE;outputFormatExplicitlySet = false;}/*** Returns the output format for a name.* * @param name*            Either the name of the output format as it was registered with*            {@link Configuration#setRegisteredCustomOutputFormats(Collection)}, or a combined output format name.*            A combined output format is created ad-hoc from the registered formats. For example, if you need RTF*            embedded into HTML, the name will be <code>HTML{RTF}</code>, where "HTML" and "RTF" refer to the*            existing formats. This logic can be used recursively, so for example <code>XML{HTML{RTF}}</code> is*            also valid.* * @return Not {@code null}.* * @throws UnregisteredOutputFormatException*             If there's no output format registered with the given name.* @throws IllegalArgumentException*             If the usage of <code>{</code> and <code>}</code> in the name is syntactically wrong, or if not all*             {@link OutputFormat}-s are {@link MarkupOutputFormat}-s in the <code>...{...}</code> expression.* * @since 2.3.24*/public OutputFormat getOutputFormat(String name) throws UnregisteredOutputFormatException {if (name.length() == 0) {throw new IllegalArgumentException("0-length format name");}if (name.charAt(name.length() - 1) == '}') {// Combined markupint openBrcIdx = name.indexOf('{');if (openBrcIdx == -1) {throw new IllegalArgumentException("Missing opening '{' in: " + name);}MarkupOutputFormat outerOF = getMarkupOutputFormatForCombined(name.substring(0, openBrcIdx));MarkupOutputFormat innerOF = getMarkupOutputFormatForCombined(name.substring(openBrcIdx + 1, name.length() - 1));return new CombinedMarkupOutputFormat(name, outerOF, innerOF);} else {OutputFormat custOF = registeredCustomOutputFormats.get(name);if (custOF != null) {return custOF;}OutputFormat stdOF = STANDARD_OUTPUT_FORMATS.get(name);if (stdOF == null) {StringBuilder sb = new StringBuilder();sb.append("Unregistered output format name, ");sb.append(StringUtil.jQuote(name));sb.append(". The output formats registered in the Configuration are: ");Set<String> registeredNames = new TreeSet<>();registeredNames.addAll(STANDARD_OUTPUT_FORMATS.keySet());registeredNames.addAll(registeredCustomOutputFormats.keySet());boolean first = true;for (String registeredName : registeredNames) {if (first) {first = false;} else {sb.append(", ");}sb.append(StringUtil.jQuote(registeredName));}throw new UnregisteredOutputFormatException(sb.toString());}return stdOF;}}private MarkupOutputFormat getMarkupOutputFormatForCombined(String outerName)throws UnregisteredOutputFormatException {OutputFormat of = getOutputFormat(outerName);if (!(of instanceof MarkupOutputFormat)) {throw new IllegalArgumentException("The \"" + outerName + "\" output format can't be used in "+ "...{...} expression, because it's not a markup format.");}MarkupOutputFormat outerOF = (MarkupOutputFormat) of;return outerOF;}/*** Sets the custom output formats that can be referred by their unique name ({@link OutputFormat#getName()}) from* templates. Names are also used to look up the {@link OutputFormat} for standard file extensions; see them at* {@link #setRecognizeStandardFileExtensions(boolean)}.* * <p>* When there's a clash between a custom output format name and a standard output format name, the custom format* will win, thus you can override the meaning of standard output format names. Except, it's not allowed to override* {@link UndefinedOutputFormat} and {@link PlainTextOutputFormat}.* * <p>* The default value is an empty collection.* * @param registeredCustomOutputFormats*            The collection of the {@link OutputFormat}-s, each must be different and has a unique name (*            {@link OutputFormat#getName()}) within this collection.* * @throws IllegalArgumentException*             When multiple different {@link OutputFormat}-s have the same name in the parameter collection. When*             the same {@link OutputFormat} object occurs for multiple times in the collection. If an*             {@link OutputFormat} name is 0 long. If an {@link OutputFormat} name doesn't start with letter or*             digit. If an {@link OutputFormat} name contains {@code '+'} or <code>'{'</code> or <code>'}'</code>.*             If an {@link OutputFormat} name equals to {@link UndefinedOutputFormat#getName()} or*             {@link PlainTextOutputFormat#getName()}.* * @since 2.3.24*/public void setRegisteredCustomOutputFormats(Collection<? extends OutputFormat> registeredCustomOutputFormats) {NullArgumentException.check(registeredCustomOutputFormats);Map<String, OutputFormat> m = new LinkedHashMap<>(registeredCustomOutputFormats.size() * 4 / 3, 1f);for (OutputFormat outputFormat : registeredCustomOutputFormats) {String name = outputFormat.getName();if (name.equals(UndefinedOutputFormat.INSTANCE.getName())) {throw new IllegalArgumentException("The \"" + name + "\" output format can't be redefined");}if (name.equals(PlainTextOutputFormat.INSTANCE.getName())) {throw new IllegalArgumentException("The \"" + name + "\" output format can't be redefined");}if (name.length() == 0) {throw new IllegalArgumentException("The output format name can't be 0 long");}if (!Character.isLetterOrDigit(name.charAt(0))) {throw new IllegalArgumentException("The output format name must start with letter or digit: "+ name);}if (name.indexOf('+') != -1) {throw new IllegalArgumentException("The output format name can't contain \"+\" character: "+ name);}if (name.indexOf('{') != -1) {throw new IllegalArgumentException("The output format name can't contain \"{\" character: "+ name);}if (name.indexOf('}') != -1) {throw new IllegalArgumentException("The output format name can't contain \"}\" character: "+ name);}OutputFormat replaced = m.put(outputFormat.getName(), outputFormat);if (replaced != null) {if (replaced == outputFormat) {throw new IllegalArgumentException("Duplicate output format in the collection: " + outputFormat);}throw new IllegalArgumentException("Clashing output format names between " + replaced + " and " + outputFormat + ".");}}this.registeredCustomOutputFormats = Collections.unmodifiableMap(m);clearTemplateCache();}/*** Getter pair of {@link #setRegisteredCustomOutputFormats(Collection)}.* * @since 2.3.24*/public Collection<? extends OutputFormat> getRegisteredCustomOutputFormats() {return registeredCustomOutputFormats.values();}/*** Sets if the "file" extension part of the source name ({@link Template#getSourceName()}) will influence certain* parsing settings. For backward compatibility, it defaults to {@code false} if* {@link #getIncompatibleImprovements()} is less than 2.3.24. Starting from {@code incompatibleImprovements}* 2.3.24, it defaults to {@code true}, so the following standard file extensions take their effect:* * <ul>*   <li>{@code ftlh}: Sets {@link TemplateConfiguration#setOutputFormat(OutputFormat) outputFormat} to*       {@code "HTML"} (i.e., {@link HTMLOutputFormat#INSTANCE}, unless the {@code "HTML"} name is overridden by*       {@link #setRegisteredCustomOutputFormats(Collection)}) and*       {@link TemplateConfiguration#setAutoEscapingPolicy(int) autoEscapingPolicy} to*       {@link #ENABLE_IF_DEFAULT_AUTO_ESCAPING_POLICY}.*   <li>{@code ftlx}: Sets {@link TemplateConfiguration#setOutputFormat(OutputFormat) outputFormat} to*       {@code "XML"} (i.e., {@link XMLOutputFormat#INSTANCE}, unless the {@code "XML"} name is overridden by*       {@link #setRegisteredCustomOutputFormats(Collection)}) and*       {@link TemplateConfiguration#setAutoEscapingPolicy(int) autoEscapingPolicy} to*       {@link #ENABLE_IF_DEFAULT_AUTO_ESCAPING_POLICY}.* </ul>* * <p>These file extensions are not case sensitive. The file extension is the part after the last dot in the source* name. If the source name contains no dot, then it has no file extension.* * <p>The settings activated by these file extensions override the setting values dictated by* {@link #setTemplateConfigurations(TemplateConfigurationFactory)}.*/public void setRecognizeStandardFileExtensions(boolean recognizeStandardFileExtensions) {boolean prevEffectiveValue = getRecognizeStandardFileExtensions();this.recognizeStandardFileExtensions = Boolean.valueOf(recognizeStandardFileExtensions);if (prevEffectiveValue != recognizeStandardFileExtensions) {clearTemplateCache();}}/*** Resets the setting to its default, as if it was never set. This means that when you change the* {@code incompatibe_improvements} setting later, the default will also change as appropriate. Also * {@link #isRecognizeStandardFileExtensionsExplicitlySet()} will return {@code false}.* * @since 2.3.24*/public void unsetRecognizeStandardFileExtensions() {if (recognizeStandardFileExtensions != null) {recognizeStandardFileExtensions = null;}}/*** Tells if {@link #setRecognizeStandardFileExtensions(boolean)} (or equivalent) was already called on this* instance.* * @since 2.3.24*/public boolean isRecognizeStandardFileExtensionsExplicitlySet() {return recognizeStandardFileExtensions != null;}/*** Getter pair of {@link #setRecognizeStandardFileExtensions(boolean)}.* * @since 2.3.24*/@Overridepublic boolean getRecognizeStandardFileExtensions() {return recognizeStandardFileExtensions == null? incompatibleImprovements.intValue() >= _TemplateAPI.VERSION_INT_2_3_24: recognizeStandardFileExtensions.booleanValue();}/*** Determines the tag syntax (like {@code <#if x>} VS {@code [#if x]}) of the template files * that has no {@code #ftl} header to decide that. Don't confuse this with the interpolation syntax* ({@link #setInterpolationSyntax(int)}); they are independent.* * <p>The {@code tagSyntax} parameter must be one of:* <ul>*   <li>{@link Configuration#AUTO_DETECT_TAG_SYNTAX}:*     Use the syntax of the first FreeMarker tag (can be anything, like <tt>#list</tt>,*     <tt>#include</tt>, user defined, etc.)*   <li>{@link Configuration#ANGLE_BRACKET_TAG_SYNTAX}:*     Use the angle bracket tag syntax (the normal syntax), like {@code <#include ...>}*   <li>{@link Configuration#SQUARE_BRACKET_TAG_SYNTAX}:*     Use the square bracket tag syntax, like {@code [#include ...]}. Note that this does <em>not</em> change*     <code>${x}</code> to {@code [=...]}; that's <em>interpolation</em> syntax, so use*     {@link #setInterpolationSyntax(int)} for that.* </ul>** <p>In FreeMarker 2.3.x {@link Configuration#ANGLE_BRACKET_TAG_SYNTAX} is the* default for better backward compatibility. Starting from 2.4.x {@link* Configuration#AUTO_DETECT_TAG_SYNTAX} is the default, so it's recommended to use* that even for 2.3.x.* * <p>This setting is ignored for the templates that have {@code ftl} directive in* it. For those templates the syntax used for the {@code ftl} directive determines* the syntax.* * @see #setInterpolationSyntax(int)*/public void setTagSyntax(int tagSyntax) {_TemplateAPI.valideTagSyntaxValue(tagSyntax);this.tagSyntax = tagSyntax;}/*** The getter pair of {@link #setTagSyntax(int)}.*/@Overridepublic int getTagSyntax() {return tagSyntax;}/*** Determines the interpolation syntax (like <code>${x}</code> VS <code>[=x]</code>) of the template files. Don't* confuse this with the tag syntax ({@link #setTagSyntax(int)}); they are independent.* * <p>* The {@code interpolationSyntax} parameter must be one of {@link Configuration#LEGACY_INTERPOLATION_SYNTAX},* {@link Configuration#DOLLAR_INTERPOLATION_SYNTAX}, and {@link Configuration#SQUARE_BRACKET_INTERPOLATION_SYNTAX}.* Note that {@link Configuration#SQUARE_BRACKET_INTERPOLATION_SYNTAX} does <em>not</em> change {@code <#if x>} to* {@code [#if x]}; that's <em>tag</em> syntax, so use {@link #setTagSyntax(int)} for that.* * @see #setTagSyntax(int)* * @since 2.3.28*/public void setInterpolationSyntax(int interpolationSyntax) {_TemplateAPI.valideInterpolationSyntaxValue(interpolationSyntax);this.interpolationSyntax = interpolationSyntax;}/*** The getter pair of {@link #setInterpolationSyntax(int)}.* * @since 2.3.28*/@Overridepublic int getInterpolationSyntax() {return interpolationSyntax;}/*** Sets the naming convention used for the identifiers that are part of the template language. The available naming* conventions are legacy (directive (tag) names are all-lower-case {@code likethis}, others are snake case* {@code like_this}), and camel case ({@code likeThis}). The default is auto-detect, which detects the naming* convention used and enforces that same naming convention for the whole template.* * <p>* This setting doesn't influence what naming convention is used for the setting names outside templates. Also, it* won't ever convert the names of user-defined things, like of data-model members, or the names of user defined* macros/functions. It only influences the names of the built-in directives ({@code #elseIf} VS {@code elseif}),* built-ins ({@code ?upper_case} VS {@code ?upperCase} ), special variables ({@code .data_model} VS* {@code .dataModel}).* * <p>* Which convention to use: FreeMarker prior to 2.3.23 has only supported* {@link Configuration#LEGACY_NAMING_CONVENTION}, so that's how most templates and examples out there are written* as of 2015. But as templates today are mostly written by programmers and often access Java API-s which already* use camel case, {@link Configuration#CAMEL_CASE_NAMING_CONVENTION} is the recommended option for most projects.* However, it's no necessary to make a application-wide decision; see auto-detection below.* * <p>* FreeMarker will decide the naming convention automatically for each template individually when this setting is* set to {@link #AUTO_DETECT_NAMING_CONVENTION} (which is the default). The naming convention of a template is* decided when the first core (non-user-defined) identifier is met during parsing (not during processing) where the* naming convention is relevant (like for {@code s?upperCase} or {@code s?upper_case} it's relevant, but for* {@code s?length} it isn't). At that point, the naming convention of the template is decided, and any later core* identifier that uses a different convention will be a parsing error. As the naming convention is decided per* template, it's not a problem if a template and the other template it {@code #include}-s/{@code #import} uses a* different convention.* * <p>* FreeMarker always enforces the same naming convention to be used consistently within the same template "file".* Additionally, when this setting is set to non-{@link #AUTO_DETECT_NAMING_CONVENTION}, the selected naming* convention is enforced on all templates. Thus such a setup can be used to enforce an application-wide naming* convention.* * <p>* Non-strict tags (a long deprecated syntax from FreeMarker 1, activated via {@link #setStrictSyntaxMode(boolean)})* are only recognized as FTL tags when they are using the {@link Configuration#LEGACY_NAMING_CONVENTION} syntax,* regardless of this setting. As they aren't exempt from the naming convention consistency enforcement, generally,* you can't use strict {@link Configuration#CAMEL_CASE_NAMING_CONVENTION} tags mixed with non-strict tags.* * @param namingConvention*            One of the {@link #AUTO_DETECT_NAMING_CONVENTION} or {@link #LEGACY_NAMING_CONVENTION}*            {@link #CAMEL_CASE_NAMING_CONVENTION}.* * @throws IllegalArgumentException*             If the parameter isn't one of the valid constants.* * @since 2.3.23*/public void setNamingConvention(int namingConvention) {_TemplateAPI.validateNamingConventionValue(namingConvention);this.namingConvention = namingConvention;}/*** The getter pair of {@link #setNamingConvention(int)}.* * @since 2.3.23*/@Overridepublic int getNamingConvention() {return namingConvention;}/*** Sets the assumed display width of the tab character (ASCII 9), which influences the column number shown in error* messages (or the column number you get through other API-s). So for example if the users edit templates in an* editor where the tab width is set to 4, you should set this to 4 so that the column numbers printed by FreeMarker* will match the column number shown in the editor. This setting doesn't affect the output of templates, as a tab* in the template will remain a tab in the output too. If you set this setting to 1, then tab characters will be* kept in the return value of {@link Template#getSource(int, int, int, int)}, otherwise they will be replaced with* the appropriate number of spaces.* * @param tabSize*            At least 1, at most 256.* * @since 2.3.25*/public void setTabSize(int tabSize) {if (tabSize < 1) {throw new IllegalArgumentException("\"tabSize\" must be at least 1, but was " + tabSize);}// To avoid integer overflows:if (tabSize > 256) {throw new IllegalArgumentException("\"tabSize\" can't be more than 256, but was " + tabSize);}this.tabSize = tabSize;}/*** The getter pair of {@link #setTabSize(int)}.* * @since 2.3.25*/@Overridepublic int getTabSize() {return tabSize;}/*** The getter pair of {@link #setFallbackOnNullLoopVariable(boolean)}.** @since 2.3.29*/public boolean getFallbackOnNullLoopVariable() {return fallbackOnNullLoopVariable;}/*** Specifies the behavior when reading a loop variable (like {@code i} in {@code <#list items as i>}, or in* {@code <@myMacro items; i>}) that's {@code null} (missing); if {@code true}, FreeMarker will look for a variable* with the same name in higher variable scopes, or if {@code false} the variable will be simply {@code null}* (missing). For backward compatibility the default is {@code true}. The recommended value for new projects is* {@code false}, as otherwise adding new variables to higher scopes (typically to the data-model) can* unintentionally change the behavior of templates. You have to be quite unlucky for that to happen though:* The newly added variable has to have the same name as the loop variable, and there must be some null (missing)* values in what you loop through.** <p>This setting doesn't influence the behavior of lambdas, like {@code items?filter(i -> i?hasContent)}, as they* never had this problem. Reading a lambda argument never falls back to higher scopes.** @since 2.3.29*/public void setFallbackOnNullLoopVariable(boolean fallbackOnNullLoopVariable) {this.fallbackOnNullLoopVariable = fallbackOnNullLoopVariable;}/*** Getter pair of {@link #setPreventStrippings(boolean)}.* * @since 2.3.27*/boolean getPreventStrippings() {return preventStrippings;}/*** Used internally; added for the FreeMarker 2 to FreeMarker 3 converter, prevents the stripping/removal of AST* nodes so that the source code can be fully reproduced from the AST.* * @since 2.3.27*/void setPreventStrippings(boolean preventStrippings) {this.preventStrippings = preventStrippings;}/*** Retrieves the template with the given name from the template cache, loading it into the cache first if it's* missing/staled.* * <p>* This is a shorthand for {@link #getTemplate(String, Locale, Object, String, boolean, boolean)* getTemplate(name, null, null, null, true, false)}; see more details there.* * <p>* See {@link Configuration} for an example of basic usage.*/public Template getTemplate(String name)throws TemplateNotFoundException, MalformedTemplateNameException, ParseException, IOException {return getTemplate(name, null, null, null, true, false);}/*** Shorthand for {@link #getTemplate(String, Locale, Object, String, boolean, boolean)* getTemplate(name, locale, null, null, true, false)}.*/public Template getTemplate(String name, Locale locale)throws TemplateNotFoundException, MalformedTemplateNameException, ParseException, IOException {return getTemplate(name, locale, null, null, true, false);}/*** Shorthand for {@link #getTemplate(String, Locale, Object, String, boolean, boolean)* getTemplate(name, null, null, encoding, true, false)}.*/public Template getTemplate(String name, String encoding)throws TemplateNotFoundException, MalformedTemplateNameException, ParseException, IOException {return getTemplate(name, null, null, encoding, true, false);}/*** Shorthand for {@link #getTemplate(String, Locale, Object, String, boolean, boolean)* getTemplate(name, locale, null, encoding, true, false)}.*/public Template getTemplate(String name, Locale locale, String encoding)throws TemplateNotFoundException, MalformedTemplateNameException, ParseException, IOException {return getTemplate(name, locale, null, encoding, true, false);}/*** Shorthand for {@link #getTemplate(String, Locale, Object, String, boolean, boolean)* getTemplate(name, locale, null, encoding, parseAsFTL, false)}.*/public Template getTemplate(String name, Locale locale, String encoding, boolean parseAsFTL)throws TemplateNotFoundException, MalformedTemplateNameException, ParseException, IOException {return getTemplate(name, locale, null, encoding, parseAsFTL, false);}/*** Shorthand for {@link #getTemplate(String, Locale, Object, String, boolean, boolean)* getTemplate(name, locale, null, encoding, parseAsFTL, ignoreMissing)}.* * @since 2.3.21*/public Template getTemplate(String name, Locale locale, String encoding, boolean parseAsFTL, boolean ignoreMissing)throws TemplateNotFoundException, MalformedTemplateNameException, ParseException, IOException {return getTemplate(name, locale, null, encoding, parseAsFTL, ignoreMissing);}/*** Retrieves the template with the given name (and according the specified further parameters) from the template* cache, loading it into the cache first if it's missing/staled.* * <p>* This method is thread-safe.* * <p>* See {@link Configuration} for an example of basic usage.** @param name*            The name or path of the template, which is not a real path, but interpreted inside the current*            {@link TemplateLoader}. Can't be {@code null}. The exact syntax of the name depends on the underlying*            {@link TemplateLoader}, but the cache makes some assumptions. First, the name is expected to be a*            hierarchical path, with path components separated by a slash character (not with backslash!). The path*            (the name) given here must <em>not</em> begin with slash; it's always interpreted relative to the*            "template root directory". Then, the {@code ..} and {@code .} path meta-elements will be resolved. For*            example, if the name is {@code a/../b/./c.ftl}, then it will be simplified to {@code b/c.ftl}. The*            rules regarding this are the same as with conventional UN*X paths. The path must not reach outside the*            template root directory, that is, it can't be something like {@code "../templates/my.ftl"} (not even*            if this path happens to be equivalent with {@code "/my.ftl"}). Furthermore, the path is allowed to*            contain at most one path element whose name is {@code *} (asterisk). This path meta-element triggers*            the <i>acquisition mechanism</i>. If the template is not found in the location described by the*            concatenation of the path left to the asterisk (called base path) and the part to the right of the*            asterisk (called resource path), the cache will attempt to remove the rightmost path component from*            the base path ("go up one directory") and concatenate that with the resource path. The process is*            repeated until either a template is found, or the base path is completely exhausted.** @param locale*            The requested locale of the template. This is what {@link Template#getLocale()} on the resulting*            {@link Template} will return (unless it's overridden via {@link #getTemplateConfigurations()}). This*            parameter can be {@code null} since 2.3.22, in which case it defaults to*            {@link Configuration#getLocale()} (note that {@link Template#getLocale()} will give the default value,*            not {@code null}). This parameter also drives localized template lookup. Assuming that you have*            specified {@code en_US} as the locale and {@code myTemplate.ftl} as the name of the template, and the*            default {@link TemplateLookupStrategy} is used and*            {@code #setLocalizedLookup(boolean) localized_lookup} is {@code true}, FreeMarker will first try to*            retrieve {@code myTemplate_en_US.html}, then {@code myTemplate.en.ftl}, and finally*            {@code myTemplate.ftl}. Note that that the template's locale will be {@code en_US} even if it only*            finds {@code myTemplate.ftl}. Note that when the {@code locale} setting is overridden with a*            {@link TemplateConfiguration} provided by {@link #getTemplateConfigurations()}, that overrides the*            value specified here, but only after the localized lookup, that is, it modifies the template*            found by the localized lookup.* * @param customLookupCondition*            This value can be used by a custom {@link TemplateLookupStrategy}; has no effect with the default one.*            Can be {@code null} (though it's up to the custom {@link TemplateLookupStrategy} if it allows that).*            This object will be used as part of the cache key, so it must to have a proper*            {@link Object#equals(Object)} and {@link Object#hashCode()} method. It also should have reasonable*            {@link Object#toString()}, as it's possibly quoted in error messages. The expected type is up to the*            custom {@link TemplateLookupStrategy}. See also:*            {@link TemplateLookupContext#getCustomLookupCondition()}.** @param encoding*            Deprecated mechanism, {@code null} is the recommended; the charset used to interpret the template*            source code bytes (if it's read from a binary source). Can be {@code null} since 2.3.22, in which case*            it will default to {@link Configuration#getEncoding(Locale)} where {@code Locale} is the*            {@code locale} parameter (when {@code locale} was {@code null} too, the its default value is used*            instead). Why is this deprecated: It doesn't make sense to get the <em>same</em> template with*            different encodings, hence, it's error prone to specify the encoding where you get the template.*            Instead, if you have template "files" with different charsets, you should use*            {@link #setTemplateConfigurations(TemplateConfigurationFactory)}, where you can associate encodings to*            individual templates based on their names (like which "directory" are they in, what's their file*            extension, etc.). The encoding associated with the templates that way overrides the encoding that you*            specify here.** @param parseAsFTL*            If {@code true}, the loaded template is parsed and interpreted normally, as a regular FreeMarker*            template. If {@code false}, the loaded template is treated as a static text, so <code>${...}</code>,*            {@code <#...>} etc. will not have special meaning in it.* * @param ignoreMissing*            If {@code true}, the method won't throw {@link TemplateNotFoundException} if the template doesn't*            exist, instead it returns {@code null}. Other kind of exceptions won't be suppressed.* * @return the requested template; maybe {@code null} when the {@code ignoreMissing} parameter is {@code true}.* * @throws TemplateNotFoundException*             If the template could not be found. Note that this exception extends {@link IOException}.* @throws MalformedTemplateNameException*             If the template name given was in violation with the {@link TemplateNameFormat} in use. Note that*             this exception extends {@link IOException}.* @throws ParseException*             (extends <code>IOException</code>) if the template is syntactically bad. Note that this exception*             extends {@link IOException}.* @throws IOException*             If there was some other problem with reading the template "file". Note that the other exceptions*             extend {@link IOException}, so this should be catched the last.* * @since 2.3.22*/public Template getTemplate(String name, Locale locale, Object customLookupCondition,String encoding, boolean parseAsFTL, boolean ignoreMissing)throws TemplateNotFoundException, MalformedTemplateNameException, ParseException, IOException {if (locale == null) {locale = getLocale();}if (encoding == null) {encoding = getEncoding(locale);}final MaybeMissingTemplate maybeTemp = cache.getTemplate(name, locale, customLookupCondition, encoding, parseAsFTL);final Template temp = maybeTemp.getTemplate();if (temp == null) {if (ignoreMissing) {return null;}TemplateLoader tl = getTemplateLoader();  String msg; if (tl == null) {msg = "Don't know where to load template " + StringUtil.jQuote(name)+ " from because the \"template_loader\" FreeMarker "+ "setting wasn't set (Configuration.setTemplateLoader), so it's null.";} else {final String missingTempNormName = maybeTemp.getMissingTemplateNormalizedName();final String missingTempReason = maybeTemp.getMissingTemplateReason();final TemplateLookupStrategy templateLookupStrategy = getTemplateLookupStrategy();msg = "Template not found for name " + StringUtil.jQuote(name)+ (missingTempNormName != null && name != null&& !removeInitialSlash(name).equals(missingTempNormName)? " (normalized: " + StringUtil.jQuote(missingTempNormName) + ")": "")+ (customLookupCondition != null ? " and custom lookup condition "+ StringUtil.jQuote(customLookupCondition) : "")+ "."+ (missingTempReason != null? "\nReason given: " + ensureSentenceIsClosed(missingTempReason): "")+ "\nThe name was interpreted by this TemplateLoader: "+ StringUtil.tryToString(tl) + "."+ (!isKnownNonConfusingLookupStrategy(templateLookupStrategy)? "\n(Before that, the name was possibly changed by this lookup strategy: "+ StringUtil.tryToString(templateLookupStrategy) + ".)": "")// Suspected reasons or warning:+ (!templateLoaderExplicitlySet? "\nWarning: The \"template_loader\" FreeMarker setting "+ "wasn't set (Configuration.setTemplateLoader), and using the default value "+ "is most certainly not intended and dangerous, and can be the cause of this error.": "")+ (missingTempReason == null && name.indexOf('\\') != -1? "\nWarning: The name contains backslash (\"\\\") instead of slash (\"/\"); "+ "template names should use slash only.": "");}String normName = maybeTemp.getMissingTemplateNormalizedName();throw new TemplateNotFoundException(normName != null ? normName : name,customLookupCondition,msg);}return temp;}private boolean isKnownNonConfusingLookupStrategy(TemplateLookupStrategy templateLookupStrategy) {return templateLookupStrategy == TemplateLookupStrategy.DEFAULT_2_3_0;}private String removeInitialSlash(String name) {return name.startsWith("/") ? name.substring(1) : name;}private String ensureSentenceIsClosed(String s) {if (s == null || s.length() == 0) {return s;}final char lastChar = s.charAt(s.length() - 1);return lastChar == '.' || lastChar == '!' || lastChar == '?' ? s : s + ".";}/*** Sets the charset used for decoding byte sequences to character sequences when* reading template files in a locale for which no explicit encoding* was specified via {@link #setEncoding(Locale, String)}. Note that by default there is no locale specified for* any locale, so the default encoding is always in effect.* * <p>Defaults to the default system encoding, which can change from one server to* another, so <b>you should always set this setting</b>. If you don't know what charset your should chose,* {@code "UTF-8"} is usually a good choice.* * <p>Note that individual templates may specify their own charset by starting with* <tt>&lt;#ftl encoding="..."&gt;</tt>* * @param encoding The name of the charset, such as {@code "UTF-8"} or {@code "ISO-8859-1"}*/public void setDefaultEncoding(String encoding) {defaultEncoding = encoding;defaultEncodingExplicitlySet = true;}/*** Gets the default encoding for converting bytes to characters when* reading template files in a locale for which no explicit encoding* was specified. Defaults to the default system encoding.*/public String getDefaultEncoding() {return defaultEncoding;}/*** Resets the setting to its default, as if it was never set.* * @since 2.3.26*/public void unsetDefaultEncoding() {if (defaultEncodingExplicitlySet) {setDefaultEncoding(getDefaultDefaultEncoding());defaultEncodingExplicitlySet = false;}}/*** Tells if {@link #setDefaultEncoding(String)} (or equivalent) was already called on this instance, or it just holds the* default value.* * @since 2.3.26*/public boolean isDefaultEncodingExplicitlySet() {return defaultEncodingExplicitlySet;}static private String getDefaultDefaultEncoding() {return getJVMDefaultEncoding();}static private String getJVMDefaultEncoding() {return SecurityUtilities.getSystemProperty("file.encoding", "utf-8");}/*** Gets the preferred character encoding for the given locale, or the * default encoding if no encoding is set explicitly for the specified* locale. You can associate encodings with locales using * {@link #setEncoding(Locale, String)} or {@link #loadBuiltInEncodingMap()}.* * @param locale Shouldn't be {@code null}, though for backward compatibility it's accepted when the locale to*               encoding {@link Map} (see earlier) is empty.*/public String getEncoding(Locale locale) {if (localeToCharsetMap.isEmpty()) {return defaultEncoding;} else {// Try for a full name match (may include country and variant)NullArgumentException.check("locale", locale);String charset = (String) localeToCharsetMap.get(locale.toString());if (charset == null) {if (locale.getVariant().length() > 0) {Locale l = new Locale(locale.getLanguage(), locale.getCountry());charset = (String) localeToCharsetMap.get(l.toString());if (charset != null) {localeToCharsetMap.put(locale.toString(), charset);}} charset = (String) localeToCharsetMap.get(locale.getLanguage());if (charset != null) {localeToCharsetMap.put(locale.toString(), charset);}}return charset != null ? charset : defaultEncoding;}}/*** Sets the character set encoding to use for templates of* a given locale. If there is no explicit encoding set for some* locale, then the default encoding will be used, what you can* set with {@link #setDefaultEncoding}.** @see #clearEncodingMap* @see #loadBuiltInEncodingMap*/public void setEncoding(Locale locale, String encoding) {localeToCharsetMap.put(locale.toString(), encoding);}/*** Adds a shared variable to the configuration.* Shared sharedVariables are sharedVariables that are visible* as top-level sharedVariables for all templates which use this* configuration, if the data model does not contain a* variable with the same name.** <p>Never use <tt>TemplateModel</tt> implementation that is not thread-safe for shared sharedVariables,* if the configuration is used by multiple threads! It is the typical situation for Servlet based Web sites.* * <p>This method is <b>not</b> thread safe; use it with the same restrictions as those that modify setting values. ** @param name the name used to access the data object from your template.*     If a shared variable with this name already exists, it will replace*     that.*     * @see #setAllSharedVariables* @see #setSharedVariable(String,Object)*/public void setSharedVariable(String name, TemplateModel tm) {Object replaced = sharedVariables.put(name, tm);if (replaced != null && rewrappableSharedVariables != null) {rewrappableSharedVariables.remove(name);}}/*** Returns the set containing the names of all defined shared sharedVariables.* The method returns a new Set object on each call that is completely* disconnected from the Configuration. That is, modifying the set will have* no effect on the Configuration object.*/public Set getSharedVariableNames() {return new HashSet(sharedVariables.keySet());}/*** Adds shared variable to the configuration; It uses {@link Configurable#getObjectWrapper()} to wrap the * {@code value}, so it's important that the object wrapper is set before this.* * <p>This method is <b>not</b> thread safe; use it with the same restrictions as those that modify setting values.* * <p>The added value should be thread safe, if you are running templates from multiple threads with this* configuration.** @throws TemplateModelException If some of the variables couldn't be wrapped via {@link #getObjectWrapper()}.** @see #setSharedVaribles(Map)* @see #setSharedVariable(String,TemplateModel)* @see #setAllSharedVariables(TemplateHashModelEx)*/public void setSharedVariable(String name, Object value) throws TemplateModelException {setSharedVariable(name, getObjectWrapper().wrap(value));}/*** Replaces all shared variables (removes all previously added ones).** <p>The values in the map can be {@link TemplateModel}-s or plain Java objects which will be immediately converted* to {@link TemplateModel} with the {@link ObjectWrapper} returned by {@link #getObjectWrapper()}. If* {@link #setObjectWrapper(ObjectWrapper)} is called later, this conversion will be re-applied. Thus, ignoring some* extra resource usage, it doesn't mater if in what order are {@link #setObjectWrapper(ObjectWrapper)} and* {@link #setSharedVaribles(Map)} called. This is essential when you don't have control over the order in which* the setters are called. ** <p>The values in the map must be thread safe, if you are running templates from multiple threads with* this configuration. This means that both the plain Java object and the {@link TemplateModel}-s created from them* by the {@link ObjectWrapper} must be thread safe. (The standard {@link ObjectWrapper}-s of FreeMarker create* thread safe {@link TemplateModel}-s.) The {@link Map} itself need not be thread-safe.* * <p>This setter method has no getter pair because of the tricky relation ship with* {@link #setSharedVariable(String, Object)}.* * @throws TemplateModelException If some of the variables couldn't be wrapped via {@link #getObjectWrapper()}.*  * @since 2.3.29*/public void setSharedVariables(Map<String, ?> map) throws TemplateModelException {rewrappableSharedVariables = new HashMap(map);sharedVariables.clear();setSharedVariablesFromRewrappableSharedVariables();}/*** Same as {@link #setSharedVariables(Map)}, but with typo in the name.* @since 2.3.21* @deprecated Use {@link #setSharedVariables(Map)} instead. */public void setSharedVaribles(Map/*<String, Object>*/ map) throws TemplateModelException {setSharedVariables(map);}private void setSharedVariablesFromRewrappableSharedVariables() throws TemplateModelException {if (rewrappableSharedVariables == null) return;for (Iterator it = rewrappableSharedVariables.entrySet().iterator(); it.hasNext(); ) {Map.Entry/*<String, Object>*/ ent = (Entry) it.next();String name = (String) ent.getKey();Object value = ent.getValue();TemplateModel valueAsTM;if (value instanceof TemplateModel) {valueAsTM = (TemplateModel) value;} else {valueAsTM = getObjectWrapper().wrap(value);}sharedVariables.put(name, valueAsTM);}}/*** Adds all object in the hash as shared variable to the configuration; it's like doing several* {@link #setSharedVariable(String, Object)} calls, one for each hash entry. It doesn't remove the already added* shared variable before doing this.** <p>Never use <tt>TemplateModel</tt> implementation that is not thread-safe for shared shared variable values,* if the configuration is used by multiple threads! It is the typical situation for Servlet based Web sites.** <p>This method is <b>not</b> thread safe; use it with the same restrictions as those that modify setting values. ** @param hash a hash model whose objects will be copied to the* configuration with same names as they are given in the hash.* If a shared variable with these names already exist, it will be replaced* with those from the map.** @see #setSharedVaribles(Map)* @see #setSharedVariable(String,Object)* @see #setSharedVariable(String,TemplateModel)*/public void setAllSharedVariables(TemplateHashModelEx hash) throws TemplateModelException {TemplateModelIterator keys = hash.keys().iterator();TemplateModelIterator values = hash.values().iterator();while (keys.hasNext()) {setSharedVariable(((TemplateScalarModel) keys.next()).getAsString(), values.next());}}/*** Gets a shared variable. Shared shared variables are variables that are* available to all templates. When a template is processed, and an identifier* is undefined in the data model, a shared variable object with the same identifier* is then looked up in the configuration. There are several predefined shared variables* that are always available through this method; see the FreeMarker manual* for a comprehensive list of them.** @see #setSharedVariable(String,Object)* @see #setSharedVariable(String,TemplateModel)* @see #setAllSharedVariables*/public TemplateModel getSharedVariable(String name) {return (TemplateModel) sharedVariables.get(name);}/*** Removes all shared variables, except the predefined ones (compress, html_escape, etc.).*/public void clearSharedVariables() {sharedVariables.clear();loadBuiltInSharedVariables();}/*** Removes all entries from the template cache, thus forcing reloading of templates* on subsequent <code>getTemplate</code> calls.* * <p>This method is thread-safe and can be called while the engine processes templates.*/public void clearTemplateCache() {cache.clear();}/*** Equivalent to {@link* #removeTemplateFromCache(String, Locale, Object, String, boolean)* removeTemplateFromCache(name, thisCfg.getLocale(), null, thisCfg.getEncoding(thisCfg.getLocale()), true)}.* @since 2.3.19*/public void removeTemplateFromCache(String name) throws IOException {Locale loc = getLocale();removeTemplateFromCache(name, loc, null, getEncoding(loc), true);}/*** Equivalent to {@link* #removeTemplateFromCache(String, Locale, Object, String, boolean)* removeTemplateFromCache(name, locale, null, thisCfg.getEncoding(locale), true)}.* @since 2.3.19*/public void removeTemplateFromCache(String name, Locale locale) throws IOException {removeTemplateFromCache(name, locale, null, getEncoding(locale), true);}/*** Equivalent to {@link* #removeTemplateFromCache(String, Locale, Object, String, boolean)* removeTemplateFromCache(name, thisCfg.getLocale(), null, encoding, true)}.* @since 2.3.19*/public void removeTemplateFromCache(String name, String encoding) throws IOException {removeTemplateFromCache(name, getLocale(), null, encoding, true);}/*** Equivalent to {@link* #removeTemplateFromCache(String, Locale, Object, String, boolean)* removeTemplateFromCache(name, locale, null, encoding, true)}.* @since 2.3.19*/public void removeTemplateFromCache(String name, Locale locale, String encoding) throws IOException {removeTemplateFromCache(name, locale, null, encoding, true);}/*** Equivalent to {@link* #removeTemplateFromCache(String, Locale, Object, String, boolean)* removeTemplateFromCache(name, locale, null, encoding, parse)}.* @since 2.3.19*/public void removeTemplateFromCache(String name, Locale locale, String encoding, boolean parse)throws IOException {removeTemplateFromCache(name, locale, null, encoding, parse);}    /*** Removes a template from the template cache, hence forcing the re-loading* of it when it's next time requested. This is to give the application* finer control over cache updating than {@link #setTemplateUpdateDelay(int)}* alone does.* * <p>For the meaning of the parameters, see* {@link #getTemplate(String, Locale, Object, String, boolean, boolean)}.* * <p>This method is thread-safe and can be called while the engine processes templates.* * @since 2.3.28*/public void removeTemplateFromCache(String name, Locale locale, Object customLookupCondition, String encoding, boolean parse)throws IOException {cache.removeTemplate(name, locale, customLookupCondition, encoding, parse);}    /*** The getter pair of {@link #setLocalizedLookup(boolean)}.* * <p>This method is thread-safe and can be called while the engine works.*/public boolean getLocalizedLookup() {return cache.getLocalizedLookup();}/*** Enables/disables localized template lookup. Enabled by default.* * <p>* With the default {@link TemplateLookupStrategy}, localized lookup works like this: Let's say your locale setting* is {@code Locale("en", "AU")}, and you call {@link Configuration#getTemplate(String) cfg.getTemplate("foo.ftl")}.* Then FreeMarker will look for the template under these names, stopping at the first that exists:* {@code "foo_en_AU.ftl"}, {@code "foo_en.ftl"}, {@code "foo.ftl"}. See the description of the default value at* {@link #setTemplateLookupStrategy(TemplateLookupStrategy)} for a more details. If you need to generate different* template names, use {@link #setTemplateLookupStrategy(TemplateLookupStrategy)} with your custom* {@link TemplateLookupStrategy}.* * <p>Note that changing the value of this setting causes the template cache to be emptied so that old lookup* results won't be reused (since 2.3.22). * * <p>* Historical note: Despite what the API documentation said earlier, this method is <em>not</em> thread-safe. While* setting it can't cause any serious problems, and in fact it works well on most hardware, it's not guaranteed that* FreeMarker will see the update in all threads.*/public void setLocalizedLookup(boolean localizedLookup) {this.localizedLookup = localizedLookup;cache.setLocalizedLookup(localizedLookup);}@Overridepublic void setSetting(String name, String value) throws TemplateException {boolean unknown = false;try {if ("TemplateUpdateInterval".equalsIgnoreCase(name)) {name = TEMPLATE_UPDATE_DELAY_KEY;} else if ("DefaultEncoding".equalsIgnoreCase(name)) {name = DEFAULT_ENCODING_KEY;}if (DEFAULT_ENCODING_KEY_SNAKE_CASE.equals(name) || DEFAULT_ENCODING_KEY_CAMEL_CASE.equals(name)) {if (JVM_DEFAULT.equalsIgnoreCase(value)) {setDefaultEncoding(getJVMDefaultEncoding()); } else {setDefaultEncoding(value);}} else if (LOCALIZED_LOOKUP_KEY_SNAKE_CASE.equals(name) || LOCALIZED_LOOKUP_KEY_CAMEL_CASE.equals(name)) {setLocalizedLookup(StringUtil.getYesNo(value));} else if (STRICT_SYNTAX_KEY_SNAKE_CASE.equals(name) || STRICT_SYNTAX_KEY_CAMEL_CASE.equals(name)) {setStrictSyntaxMode(StringUtil.getYesNo(value));} else if (WHITESPACE_STRIPPING_KEY_SNAKE_CASE.equals(name)|| WHITESPACE_STRIPPING_KEY_CAMEL_CASE.equals(name)) {setWhitespaceStripping(StringUtil.getYesNo(value));} else if (AUTO_ESCAPING_POLICY_KEY_SNAKE_CASE.equals(name) || AUTO_ESCAPING_POLICY_KEY_CAMEL_CASE.equals(name)) {if ("enable_if_default".equals(value) || "enableIfDefault".equals(value)) {setAutoEscapingPolicy(ENABLE_IF_DEFAULT_AUTO_ESCAPING_POLICY);} else if ("enable_if_supported".equals(value) || "enableIfSupported".equals(value)) {setAutoEscapingPolicy(ENABLE_IF_SUPPORTED_AUTO_ESCAPING_POLICY);} else if ("disable".equals(value)) {setAutoEscapingPolicy(DISABLE_AUTO_ESCAPING_POLICY);} else {throw invalidSettingValueException(name, value);}} else if (OUTPUT_FORMAT_KEY_SNAKE_CASE.equals(name) || OUTPUT_FORMAT_KEY_CAMEL_CASE.equals(name)) {if (value.equalsIgnoreCase(DEFAULT)) {unsetOutputFormat();} else {OutputFormat stdOF = STANDARD_OUTPUT_FORMATS.get(value);setOutputFormat(stdOF != null ? stdOF: (OutputFormat) _ObjectBuilderSettingEvaluator.eval(value, OutputFormat.class, true, _SettingEvaluationEnvironment.getCurrent()));}} else if (REGISTERED_CUSTOM_OUTPUT_FORMATS_KEY_SNAKE_CASE.equals(name)|| REGISTERED_CUSTOM_OUTPUT_FORMATS_KEY_CAMEL_CASE.equals(name)) {List list = (List) _ObjectBuilderSettingEvaluator.eval(value, List.class, true, _SettingEvaluationEnvironment.getCurrent());for (Object item : list) {if (!(item instanceof OutputFormat)) {throw new _MiscTemplateException(getEnvironment(),"Invalid value for setting ", new _DelayedJQuote(name), ": List items must be "+ OutputFormat.class.getName() + " instances, in: ", value);}}setRegisteredCustomOutputFormats(list);} else if (RECOGNIZE_STANDARD_FILE_EXTENSIONS_KEY_SNAKE_CASE.equals(name)|| RECOGNIZE_STANDARD_FILE_EXTENSIONS_KEY_CAMEL_CASE.equals(name)) {if (value.equalsIgnoreCase(DEFAULT)) {unsetRecognizeStandardFileExtensions();} else {setRecognizeStandardFileExtensions(StringUtil.getYesNo(value));}} else if (CACHE_STORAGE_KEY_SNAKE_CASE.equals(name) || CACHE_STORAGE_KEY_CAMEL_CASE.equals(name)) {if (value.equalsIgnoreCase(DEFAULT)) {unsetCacheStorage();} if (value.indexOf('.') == -1) {int strongSize = 0;int softSize = 0;Map map = StringUtil.parseNameValuePairList(value, String.valueOf(Integer.MAX_VALUE));Iterator it = map.entrySet().iterator();while (it.hasNext()) {Map.Entry ent = (Map.Entry) it.next();String pname = (String) ent.getKey();int pvalue;try {pvalue = Integer.parseInt((String) ent.getValue());} catch (NumberFormatException e) {throw invalidSettingValueException(name, value);}if ("soft".equalsIgnoreCase(pname)) {softSize = pvalue;} else if ("strong".equalsIgnoreCase(pname)) {strongSize = pvalue;} else {throw invalidSettingValueException(name, value);}}if (softSize == 0 && strongSize == 0) {throw invalidSettingValueException(name, value);}setCacheStorage(new MruCacheStorage(strongSize, softSize));} else {setCacheStorage((CacheStorage) _ObjectBuilderSettingEvaluator.eval(value, CacheStorage.class, false, _SettingEvaluationEnvironment.getCurrent()));}} else if (TEMPLATE_UPDATE_DELAY_KEY_SNAKE_CASE.equals(name)|| TEMPLATE_UPDATE_DELAY_KEY_CAMEL_CASE.equals(name)) {long multiplier;String valueWithoutUnit;if (value.endsWith("ms")) {multiplier = 1;valueWithoutUnit = rightTrim(value.substring(0, value.length() - 2));} else if (value.endsWith("s")) {multiplier = 1000;valueWithoutUnit = rightTrim(value.substring(0, value.length() - 1));} else if (value.endsWith("m")) {multiplier = 1000 * 60;valueWithoutUnit = rightTrim(value.substring(0, value.length() - 1));} else if (value.endsWith("h")) {multiplier = 1000 * 60 * 60;valueWithoutUnit = rightTrim(value.substring(0, value.length() - 1));} else {multiplier = 1000;  // Default is seconds for backward compatibilityvalueWithoutUnit = value;}setTemplateUpdateDelayMilliseconds(Integer.parseInt(valueWithoutUnit) * multiplier);} else if (TAG_SYNTAX_KEY_SNAKE_CASE.equals(name) || TAG_SYNTAX_KEY_CAMEL_CASE.equals(name)) {if ("auto_detect".equals(value) || "autoDetect".equals(value)) {setTagSyntax(AUTO_DETECT_TAG_SYNTAX);} else if ("angle_bracket".equals(value) || "angleBracket".equals(value)) {setTagSyntax(ANGLE_BRACKET_TAG_SYNTAX);} else if ("square_bracket".equals(value) || "squareBracket".equals(value)) {setTagSyntax(SQUARE_BRACKET_TAG_SYNTAX);} else {throw invalidSettingValueException(name, value);}} else if (INTERPOLATION_SYNTAX_KEY_SNAKE_CASE.equals(name)|| INTERPOLATION_SYNTAX_KEY_CAMEL_CASE.equals(name)) {if ("legacy".equals(value)) {setInterpolationSyntax(LEGACY_INTERPOLATION_SYNTAX);} else if ("dollar".equals(value)) {setInterpolationSyntax(DOLLAR_INTERPOLATION_SYNTAX);} else if ("square_bracket".equals(value) || "squareBracket".equals(value)) {setInterpolationSyntax(SQUARE_BRACKET_INTERPOLATION_SYNTAX);} else {throw invalidSettingValueException(name, value);}} else if (NAMING_CONVENTION_KEY_SNAKE_CASE.equals(name) || NAMING_CONVENTION_KEY_CAMEL_CASE.equals(name)) {if ("auto_detect".equals(value) || "autoDetect".equals(value)) {setNamingConvention(AUTO_DETECT_NAMING_CONVENTION);} else if ("legacy".equals(value)) {setNamingConvention(LEGACY_NAMING_CONVENTION);} else if ("camel_case".equals(value) || "camelCase".equals(value)) {setNamingConvention(CAMEL_CASE_NAMING_CONVENTION);} else {throw invalidSettingValueException(name, value);}} else if (TAB_SIZE_KEY_SNAKE_CASE.equals(name) || TAB_SIZE_KEY_CAMEL_CASE.equals(name)) {setTabSize(Integer.parseInt(value));} else if (INCOMPATIBLE_IMPROVEMENTS_KEY_SNAKE_CASE.equals(name)|| INCOMPATIBLE_IMPROVEMENTS_KEY_CAMEL_CASE.equals(name)) {setIncompatibleImprovements(new Version(value));} else if (INCOMPATIBLE_ENHANCEMENTS.equals(name)) {setIncompatibleEnhancements(value);} else if (TEMPLATE_LOADER_KEY_SNAKE_CASE.equals(name) || TEMPLATE_LOADER_KEY_CAMEL_CASE.equals(name)) {if (value.equalsIgnoreCase(DEFAULT)) {unsetTemplateLoader();} else {setTemplateLoader((TemplateLoader) _ObjectBuilderSettingEvaluator.eval(value, TemplateLoader.class, true, _SettingEvaluationEnvironment.getCurrent()));}} else if (TEMPLATE_LOOKUP_STRATEGY_KEY_SNAKE_CASE.equals(name)|| TEMPLATE_LOOKUP_STRATEGY_KEY_CAMEL_CASE.equals(name)) {if (value.equalsIgnoreCase(DEFAULT)) {unsetTemplateLookupStrategy();} else {setTemplateLookupStrategy((TemplateLookupStrategy) _ObjectBuilderSettingEvaluator.eval(value, TemplateLookupStrategy.class, false, _SettingEvaluationEnvironment.getCurrent()));}} else if (TEMPLATE_NAME_FORMAT_KEY_SNAKE_CASE.equals(name)|| TEMPLATE_NAME_FORMAT_KEY_CAMEL_CASE.equals(name)) {if (value.equalsIgnoreCase(DEFAULT)) {unsetTemplateNameFormat();} else if (value.equalsIgnoreCase("default_2_3_0")) {setTemplateNameFormat(TemplateNameFormat.DEFAULT_2_3_0);} else if (value.equalsIgnoreCase("default_2_4_0")) {setTemplateNameFormat(TemplateNameFormat.DEFAULT_2_4_0);} else {throw invalidSettingValueException(name, value);}} else if (TEMPLATE_CONFIGURATIONS_KEY_SNAKE_CASE.equals(name)|| TEMPLATE_CONFIGURATIONS_KEY_CAMEL_CASE.equals(name)) {if (value.equals(NULL)) {setTemplateConfigurations(null);} else {setTemplateConfigurations((TemplateConfigurationFactory) _ObjectBuilderSettingEvaluator.eval(value, TemplateConfigurationFactory.class, false, _SettingEvaluationEnvironment.getCurrent()));}} else if (FALLBACK_ON_NULL_LOOP_VARIABLE_KEY_SNAKE_CASE.equals(name)|| FALLBACK_ON_NULL_LOOP_VARIABLE_KEY_CAMEL_CASE.equals(name)) {setFallbackOnNullLoopVariable(StringUtil.getYesNo(value));} else {unknown = true;}} catch (Exception e) {throw settingValueAssignmentException(name, value, e);}if (unknown) {super.setSetting(name, value);}}private String rightTrim(String s) {int ln = s.length();while (ln > 0 && Character.isWhitespace(s.charAt(ln - 1))) {ln--;}return s.substring(0, ln);}/*** Returns the valid {@link Configuration} setting names. Naturally, this includes the {@link Configurable} setting* names too.* * @param camelCase*            If we want the setting names with camel case naming convention, or with snake case (legacy) naming*            convention.* * @see Configurable#getSettingNames(boolean)* * @since 2.3.24*/@Overridepublic Set<String> getSettingNames(boolean camelCase) {return new _UnmodifiableCompositeSet<>(super.getSettingNames(camelCase),new _SortedArraySet<>(camelCase ? SETTING_NAMES_CAMEL_CASE : SETTING_NAMES_SNAKE_CASE));}@Overrideprotected String getCorrectedNameForUnknownSetting(String name) {if ("encoding".equals(name) || "charset".equals(name) || "default_charset".equals(name)) {// [2.4] Default might changes to camel-casereturn DEFAULT_ENCODING_KEY;}if ("defaultCharset".equals(name)) {return DEFAULT_ENCODING_KEY_CAMEL_CASE;}return super.getCorrectedNameForUnknownSetting(name);}@Overrideprotected void doAutoImportsAndIncludes(Environment env) throws TemplateException, IOException {Template t = env.getMainTemplate();doAutoImports(env, t);doAutoIncludes(env, t);}private void doAutoImports(Environment env, Template t) throws IOException, TemplateException {Map<String, String> envAutoImports = env.getAutoImportsWithoutFallback();Map<String, String> tAutoImports = t.getAutoImportsWithoutFallback();boolean lazyAutoImports = env.getLazyAutoImports() != null ? env.getLazyAutoImports().booleanValue(): env.getLazyImports();for (Map.Entry<String, String> autoImport : getAutoImportsWithoutFallback().entrySet()) {String nsVarName = autoImport.getKey();if ((tAutoImports == null || !tAutoImports.containsKey(nsVarName))&& (envAutoImports == null || !envAutoImports.containsKey(nsVarName))) {env.importLib(autoImport.getValue(), nsVarName, lazyAutoImports);}}if (tAutoImports != null) {for (Map.Entry<String, String> autoImport : tAutoImports.entrySet()) {String nsVarName = autoImport.getKey();if (envAutoImports == null || !envAutoImports.containsKey(nsVarName)) {env.importLib(autoImport.getValue(), nsVarName, lazyAutoImports);}}}if (envAutoImports != null) {for (Map.Entry<String, String> autoImport : envAutoImports.entrySet()) {String nsVarName = autoImport.getKey();env.importLib(autoImport.getValue(), nsVarName, lazyAutoImports);}}}private void doAutoIncludes(Environment env, Template t) throws TemplateException, IOException,TemplateNotFoundException, MalformedTemplateNameException, ParseException {// We can't store autoIncludes in LinkedHashSet-s because setAutoIncludes(List) allows duplicates,// unfortunately. Yet we have to prevent duplicates among Configuration levels, with the lowest levels having// priority. So we build some Set-s to do that, but we avoid the most common cases where they aren't needed.List<String> tAutoIncludes = t.getAutoIncludesWithoutFallback();List<String> envAutoIncludes = env.getAutoIncludesWithoutFallback();for (String templateName : getAutoIncludesWithoutFallback()) {if ((tAutoIncludes == null || !tAutoIncludes.contains(templateName))&& (envAutoIncludes == null || !envAutoIncludes.contains(templateName))) {env.include(getTemplate(templateName, env.getLocale()));}}if (tAutoIncludes != null) {for (String templateName : tAutoIncludes) {if (envAutoIncludes == null || !envAutoIncludes.contains(templateName)) {env.include(getTemplate(templateName, env.getLocale()));}}}if (envAutoIncludes != null) {for (String templateName : envAutoIncludes) {env.include(getTemplate(templateName, env.getLocale()));}}}/*** Returns FreeMarker version number string. * * @deprecated Use {@link #getVersion()} instead.*/@Deprecatedpublic static String getVersionNumber() {return VERSION.toString();}/*** Returns FreeMarker version information, most importantly the major.minor.micro version numbers;* do NOT use this as the value of the {@code incompatible_improvements} setting (as the parameter to* {@link Configuration#Configuration(Version)}), as then your application can break when you upgrade FreeMarker!* Use a constant value, like {@link #VERSION_2_3_28}, to protect your application from fixes/changes that aren't* entirely backward compatible. Fixes and features that are backward compatible are always enabled. * * On FreeMarker version numbering rules:* <ul>*   <li>For final/stable releases the version number is like major.minor.micro, like 2.3.19. (Historically,*       when micro was 0 the version strings was like major.minor instead of the proper major.minor.0, but that's*       not like that anymore.)*   <li>When only the micro version is increased, compatibility with previous versions with the same*       major.minor is kept. Thus <tt>freemarker.jar</tt> can be replaced in an existing application without*       breaking it.</li>*   <li>For non-final/unstable versions (that almost nobody uses), the format is:*       <ul>*         <li>Starting from 2.3.20: major.minor.micro-extraInfo, like*             2.3.20-nightly_20130506T123456Z, 2.4.0-RC01. The major.minor.micro*             always indicates the target we move towards, so 2.3.20-nightly or 2.3.20-M01 is*             after 2.3.19 and will eventually become to 2.3.20. "PRE", "M" and "RC" (uppercase!) means*             "preview", "milestone" and "release candidate" respectively, and is always followed by a 2 digit*             0-padded counter, like M03 is the 3rd milestone release of a given major.minor.micro.</li> *         <li>Before 2.3.20: The extraInfo wasn't preceded by a "-".*             Instead of "nightly" there was "mod", where the major.minor.micro part has indicated where*             are we coming from, so 2.3.19mod (read as: 2.3.19 modified) was after 2.3.19 but before 2.3.20.*             Also, "pre" and "rc" was lowercase, and was followd by a number without 0-padding.</li>*       </ul>* </ul>* * @since 2.3.20*/ public static Version getVersion() {return VERSION;}/*** Returns the default object wrapper for a given "incompatible_improvements" version.* * @see #setIncompatibleImprovements(Version)* * @since 2.3.21*/public static ObjectWrapper getDefaultObjectWrapper(Version incompatibleImprovements) {if (incompatibleImprovements.intValue() < _TemplateAPI.VERSION_INT_2_3_21) {return ObjectWrapper.DEFAULT_WRAPPER;} else {return new DefaultObjectWrapperBuilder(incompatibleImprovements).build();}}/*** Same as {@link #getSupportedBuiltInNames(int)} with argument {@link #getNamingConvention()}.* * @since 2.3.20*/public Set getSupportedBuiltInNames() {return getSupportedBuiltInNames(getNamingConvention());}/*** Returns the names of the supported "built-ins". These are the ({@code expr?builtin_name}-like things). As of this* writing, this information doesn't depend on the configuration options, so it could be a static method, but* to be future-proof, it's an instance method. * * @param namingConvention*            One of {@link #AUTO_DETECT_NAMING_CONVENTION}, {@link #LEGACY_NAMING_CONVENTION}, and*            {@link #CAMEL_CASE_NAMING_CONVENTION}. If it's {@link #AUTO_DETECT_NAMING_CONVENTION} then the union*            of the names in all the naming conventions is returned. * * @since 2.3.24*/public Set<String> getSupportedBuiltInNames(int namingConvention) {return _CoreAPI.getSupportedBuiltInNames(namingConvention);}/*** Same as {@link #getSupportedBuiltInDirectiveNames(int)} with argument {@link #getNamingConvention()}.* * @since 2.3.21*/public Set getSupportedBuiltInDirectiveNames() {return getSupportedBuiltInDirectiveNames(getNamingConvention());}/*** Returns the names of the directives that are predefined by FreeMarker. These are the things that you call like* <tt>&lt;#directiveName ...&gt;</tt>.* * @param namingConvention*            One of {@link #AUTO_DETECT_NAMING_CONVENTION}, {@link #LEGACY_NAMING_CONVENTION}, and*            {@link #CAMEL_CASE_NAMING_CONVENTION}. If it's {@link #AUTO_DETECT_NAMING_CONVENTION} then the union*            of the names in all the naming conventions is returned. * * @since 2.3.24*/public Set<String> getSupportedBuiltInDirectiveNames(int namingConvention) {if (namingConvention == AUTO_DETECT_NAMING_CONVENTION) {return _CoreAPI.ALL_BUILT_IN_DIRECTIVE_NAMES;} else if (namingConvention == LEGACY_NAMING_CONVENTION) {return _CoreAPI.LEGACY_BUILT_IN_DIRECTIVE_NAMES;} else if (namingConvention == CAMEL_CASE_NAMING_CONVENTION) {return _CoreAPI.CAMEL_CASE_BUILT_IN_DIRECTIVE_NAMES;} else {throw new IllegalArgumentException("Unsupported naming convention constant: " + namingConvention);}}//返回由FreeMarker预定义的指令的名称private static String getRequiredVersionProperty(Properties vp, String properyName) {String s = vp.getProperty(properyName);if (s == null) {throw new RuntimeException("Version file is corrupt: \"" + properyName + "\" property is missing.");}return s;}}

DefaultEnumerationAdapter.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.Serializable;
import java.util.Enumeration;
import java.util.Iterator;import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import freemarker.ext.util.WrapperTemplateModel;
import freemarker.template.utility.ObjectWrapperWithAPISupport;/*** Adapts an {@link Enumeration} to the corresponding {@link TemplateModel} interface(s), most importantly to* {@link TemplateCollectionModel}. Putting aside that it wraps an {@link Enumeration} instead of an {@link Iterator},* this is identical to {@link DefaultIteratorAdapter}, so see further details there.* * @since 2.3.26*/
@SuppressWarnings("serial")
public class DefaultEnumerationAdapter extends WrappingTemplateModel implements TemplateCollectionModel,AdapterTemplateModel, WrapperTemplateModel, TemplateModelWithAPISupport, Serializable {@SuppressFBWarnings(value="SE_BAD_FIELD", justification="We hope it's Seralizable")private final Enumeration<?> enumeration;private boolean enumerationOwnedBySomeone;//工厂方法创建新的适配器实例。public static DefaultEnumerationAdapter adapt(Enumeration<?> enumeration, ObjectWrapper wrapper) {return new DefaultEnumerationAdapter(enumeration, wrapper);}private DefaultEnumerationAdapter(Enumeration<?> enumeration, ObjectWrapper wrapper) {super(wrapper);this.enumeration = enumeration;}
//从接口复制的描述:wrappertemplatmodel检索由该模型包装的原始对象。@Overridepublic Object getWrappedObject() {return enumeration;}@Overridepublic Object getAdaptedObject(Class<?> hint) {return getWrappedObject();}
//从接口复制的描述:TemplateCollectionModel,检索用于迭代该集合中的元素的模板模型迭代器。@Overridepublic TemplateModelIterator iterator() throws TemplateModelException {return new SimpleTemplateModelIterator();}
//接口wrappertemplatmodel中的getWrappedObje@Overridepublic TemplateModel getAPI() throws TemplateModelException {return ((ObjectWrapperWithAPISupport) getObjectWrapper()).wrapAsAPI(enumeration);}/*** Not thread-safe.*/private class SimpleTemplateModelIterator implements TemplateModelIterator {private boolean enumerationOwnedByMe;@Overridepublic TemplateModel next() throws TemplateModelException {if (!enumerationOwnedByMe) {checkNotOwner();enumerationOwnedBySomeone = true;enumerationOwnedByMe = true;}if (!enumeration.hasMoreElements()) {throw new TemplateModelException("The collection has no more items.");}Object value = enumeration.nextElement();return value instanceof TemplateModel ? (TemplateModel) value : wrap(value);}@Overridepublic boolean hasNext() throws TemplateModelException {// Calling hasNext may looks safe, but I have met sync. problems.if (!enumerationOwnedByMe) {checkNotOwner();}return enumeration.hasMoreElements();}private void checkNotOwner() throws TemplateModelException {if (enumerationOwnedBySomeone) {throw new TemplateModelException("This collection value wraps a java.util.Enumeration, thus it can be listed only once.");}}}}

SimpleSequence.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.Serializable;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;import freemarker.ext.beans.BeansWrapper;/*** A simple implementation of the {@link TemplateSequenceModel} interface, using its own underlying {@link List} for* storing the list items. If you are wrapping an already existing {@link List} or {@code array}, you should certainly* use {@link DefaultMapAdapter} or {@link DefaultArrayAdapter} (see comparison below).* * <p>* This class is thread-safe if you don't call modifying methods (like {@link #add(Object)}) after you have made the* object available for multiple threads (assuming you have published it safely to the other threads; see JSR-133 Java* Memory Model). These methods aren't called by FreeMarker, so it's usually not a concern.* * <p>* <b>{@link SimpleSequence} VS {@link DefaultListAdapter}/{@link DefaultArrayAdapter} - Which to use when?</b>* </p>* * <p>* For a {@link List} or {@code array} that exists regardless of FreeMarker, only you need to access it from templates,* {@link DefaultMapAdapter} should be the default choice, as it can be unwrapped to the originally wrapped object* (important when passing it to Java methods from the template). It also has more predictable performance (no spikes).* * <p>* For a sequence that's made specifically to be used from templates, creating an empty {@link SimpleSequence} then* filling it with {@link SimpleSequence#add(Object)} is usually the way to go, as the resulting sequence is* significantly faster to read from templates than a {@link DefaultListAdapter} (though it's somewhat slower to read* from a plain Java method to which it had to be passed adapted to a {@link List}).* * <p>* It also matters if for how many times will the <em>same</em> {@link List} entry be read from the template(s) later,* on average. If, on average, you read each entry for more than 4 times, {@link SimpleSequence} will be most* certainly faster, but if for 2 times or less (and especially if not at all) then {@link DefaultMapAdapter} will* be faster. Before choosing based on performance though, pay attention to the behavioral differences;* {@link SimpleSequence} will shallow-copy the original {@link List} at construction time, so it won't reflect* {@link List} content changes after the {@link SimpleSequence} construction, also {@link SimpleSequence} can't be* unwrapped to the original wrapped instance.** @see DefaultListAdapter* @see DefaultArrayAdapter* @see TemplateSequenceModel*/
public class SimpleSequence extends WrappingTemplateModel implements TemplateSequenceModel, Serializable {/*** The {@link List} that stored the elements of this sequence. It migth contains both {@link TemplateModel} elements* and non-{@link TemplateModel} elements.*/protected final List list;private List unwrappedList;/*** Constructs an empty simple sequence that will use the the default object * wrapper set in * {@link WrappingTemplateModel#setDefaultObjectWrapper(ObjectWrapper)}.* * @deprecated Use {@link #SimpleSequence(ObjectWrapper)} instead.*/@Deprecatedpublic SimpleSequence() {this((ObjectWrapper) null);}//使用wrappingtemplatmodel . setdefaultobjectwrapper (ObjectWrapper)中设置的默认对象包装器,构造一个带有预分配容量的空简单序列。public SimpleSequence(int capacity) {list = new ArrayList(capacity);}/*** Constructs a simple sequence that will contain the elements* from the specified {@link Collection} and will use the the default * object wrapper set in * {@link WrappingTemplateModel#setDefaultObjectWrapper(ObjectWrapper)}.* @param collection the collection containing initial values. Note that a* copy of the collection is made for internal use.* * @deprecated Use {@link #SimpleSequence(Collection, ObjectWrapper)}.*/@Deprecatedpublic SimpleSequence(Collection collection) {this(collection, null);}/*** Constructs a simple sequence from the passed collection model, which shouldn't be added to later. The internal* list will be build immediately (not lazily). The resulting sequence shouldn't be extended with* {@link #add(Object)}, because the appropriate {@link ObjectWrapper} won't be available; use* {@link #SimpleSequence(Collection, ObjectWrapper)} instead, if you need that.*/public SimpleSequence(TemplateCollectionModel tcm) throws TemplateModelException {ArrayList alist = new ArrayList();for (TemplateModelIterator it = tcm.iterator(); it.hasNext(); ) {alist.add(it.next());}alist.trimToSize();list = alist;}//使用指定的对象包装器构造一个空序列。public SimpleSequence(ObjectWrapper wrapper) {super(wrapper);list = new ArrayList();}/*** Constructs an empty simple sequence with preallocated capacity.* * @param wrapper*            See the similar parameter of {@link SimpleSequence#SimpleSequence(ObjectWrapper)}.* * @since 2.3.21*/public SimpleSequence(int capacity, ObjectWrapper wrapper) {super(wrapper);list = new ArrayList(capacity);}    /*** Constructs a simple sequence that will contain the elements from the specified {@link Collection}; consider* using {@link DefaultListAdapter} instead.* * @param collection*            The collection containing the initial items of the sequence. A shallow copy of this collection is made*            immediately for internal use (thus, later modification on the parameter collection won't be visible in*            the resulting sequence). The items however, will be only wrapped with the {@link ObjectWrapper}*            lazily, when first needed.* @param wrapper*            See the similar parameter of {@link SimpleSequence#SimpleSequence(ObjectWrapper)}.*/public SimpleSequence(Collection collection, ObjectWrapper wrapper) {super(wrapper);list = new ArrayList(collection);}//将一个任意对象添加到该序列的末尾。如果新添加的对象没有实现接口,它将被包装到适当的{@link TemplateModel}接口中public void add(Object obj) {list.add(obj);unwrappedList = null;}/*** Adds a boolean value to the end of this sequence. The newly added boolean will be immediately converted into* {@link TemplateBooleanModel#TRUE} or {@link TemplateBooleanModel#FALSE}, without using the {@link ObjectWrapper}.** @param b*            The boolean value to be added.* * @deprecated Use {@link #add(Object)} instead, as this bypasses the {@link ObjectWrapper}.*/@Deprecatedpublic void add(boolean b) {add(b ? TemplateBooleanModel.TRUE : TemplateBooleanModel.FALSE);}/***生成基础列表的深层副本,展开已转换为的任何值.当第二次(或以后)被调用时,它只是重用第一个结果,除非序列从那时起被修改。*/@Deprecatedpublic List toList() throws TemplateModelException {if (unwrappedList == null) {Class listClass = list.getClass();List result = null;try {result = (List) listClass.newInstance();} catch (Exception e) {throw new TemplateModelException("Error instantiating an object of type " + listClass.getName(),e);}BeansWrapper bw = BeansWrapper.getDefaultInstance();for (int i = 0; i < list.size(); i++) {Object elem = list.get(i);if (elem instanceof TemplateModel) {elem = bw.unwrap((TemplateModel) elem);}result.add(elem);}unwrappedList = result;}return unwrappedList;}//返回位于列表指定索引处的项。如果该项目还不是{@link templatmodel},它将自动换行它现在到一个,并把它写回支持列表。@Overridepublic TemplateModel get(int index) throws TemplateModelException {try {Object value = list.get(index);if (value instanceof TemplateModel) {return (TemplateModel) value;}TemplateModel tm = wrap(value);list.set(index, tm);return tm;} catch (IndexOutOfBoundsException e) {return null;}}@Overridepublic int size() {return list.size();}//返回list的同步包装器。public SimpleSequence synchronizedWrapper() {return new SynchronizedSequence();}@Overridepublic String toString() {return list.toString();}private class SynchronizedSequence extends SimpleSequence {private SynchronizedSequence() {super(SimpleSequence.this.getObjectWrapper());}@Overridepublic void add(Object obj) {synchronized (SimpleSequence.this) {SimpleSequence.this.add(obj);}}@Overridepublic TemplateModel get(int i) throws TemplateModelException {synchronized (SimpleSequence.this) {return SimpleSequence.this.get(i);}}@Overridepublic int size() {synchronized (SimpleSequence.this) {return SimpleSequence.this.size();}}@Overridepublic List toList() throws TemplateModelException {synchronized (SimpleSequence.this) {return SimpleSequence.this.toList();}}@Overridepublic SimpleSequence synchronizedWrapper() {return this;}}}

FreeMarker源码分析(八)相关推荐

  1. Spring Security源码分析八:Spring Security 退出

    为什么80%的码农都做不了架构师?>>> Spring Security是一个能够为基于Spring的企业应用系统提供声明式的安全访问控制解决方案的安全框架.它提供了一组可以在Spr ...

  2. 【转】ABP源码分析八:Logger集成

    ABP使用Castle日志记录工具,并且可以使用不同的日志类库,比如:Log4Net, NLog, Serilog... 等等.对于所有的日志类库,Castle提供了一个通用的接口来实现,我们可以很方 ...

  3. Spring源码分析八:Mybatis ORM映射框架原理

    文章目录 (一)Mybatis单独操作数据库程序 1.1.数据库表 1.2.建立PO 1.3.建立mapper接口映射 1.4.建立Mybatis配置文件 1.5.建立mapper映射文件 1.6.测 ...

  4. mybatis的使用及源码分析(八) mybatis的rowbounds分析

    Mybatis提供了一个简单的逻辑分页类RowBounds,其原理类似于在内存中做了一个分页,不是数据库层面的分页,性能不算好,谨慎使用 一. RowBounds源码分析 1 RowBounds源码: ...

  5. MyBatis框架的使用及源码分析(八) MapperMethod

    从 <MyBatis框架中Mapper映射配置的使用及原理解析(七) MapperProxy,MapperProxyFactory> 文中,我们知道Mapper,通过MapperProxy ...

  6. Freemarker源码分析(9)core.JSONParser类

    2021SC@SDUSC core.JSONParser 总览图: JSONParser类用于解析json字符串,将其输出成对应的已经包装好的TemplateModel对象 代码: class JSO ...

  7. tcp/ip 协议栈Linux内核源码分析八 路由子系统分析三 路由表

    内核版本:3.4.39 Linux路由子系统代码量虽说不是很多,但是难度还是有的,最近在分析路由子系统这一块,对它的框架有了基本的了解,如果要想掌握的话估计还得再花点时间阅读代码,先把框架记录下来.路 ...

  8. 【转】ABP源码分析一:整体项目结构及目录

    ABP是一套非常优秀的web应用程序架构,适合用来搭建集中式架构的web应用程序. 整个Abp的Infrastructure是以Abp这个package为核心模块(core)+15个模块(module ...

  9. Spring源码分析(三)

    Spring源码分析 第三章 手写Ioc和Aop 文章目录 Spring源码分析 前言 一.模拟业务场景 (一) 功能介绍 (二) 关键功能代码 (三) 问题分析 二.使用ioc和aop重构 (一) ...

  10. Mybatis源码分析: MapperMethod功能讲解

    canmengqian </div><!--end: blogTitle 博客的标题和副标题 --> <div id="navigator"> ...

最新文章

  1. 快学习使用Linux吧,避免勒索病毒大肆入侵
  2. 荣耀mgaic2鸿蒙系统,华为没有抛弃荣耀!我看着当年4400买的荣耀Magic2,不争气地哭了...
  3. springboot+maven实现模块化编程
  4. javascript变量声明语法的应用和分号讲究适用性
  5. 说干就干的p2psearcher2013
  6. 一站式实时数仓开发:当FLINK SQL遇见ULTRON
  7. centos6.5安装redmine2.6
  8. aspnet_regsql.exe 工具参数解析
  9. CMMI3—CMMI5认证的详细流程
  10. 华为设备配置VRRP冗余链路 防止单点故障
  11. 修改dns服务器转发器,域服务器dns设置转发器
  12. 2020年计算机学什么语言,最受企业认可的十大编程语言,2020年学习不后悔
  13. nali: 给dig,traceroute等命令的输出的ip附加上地理信息
  14. android平台串口调试工具,串口调试助手下载-串口助手 安卓版v1.4-PC6安卓网
  15. 计算机锁屏之后QQ音乐停止播放了,MAC电脑如何在息屏状态下让QQ音乐能继续播放音乐...
  16. Python必会的单元测试框架 —— unittest
  17. python学习——主要的代码结构,模块和包
  18. python模拟器ipad_Appium+python自动化19-iOS模拟器(iOS Simulator)安装自家APP【转载】
  19. 维基百科创建和百度百科建立有何不同?
  20. 如何打jar包和运行jar包

热门文章

  1. Android动态换肤框架PrettySkin原理篇(一)- LayoutInflater的理解及使用
  2. idea新建sourceFolder
  3. android中新建文件夹在哪里,Android SdCard 新建文件夹并在文件夹中读、写文件
  4. 安卓手机设置指定文件夹下的图片、视频不被相册读取到
  5. opensource项目_2020 Opensource.com夏季阅读列表
  6. [转帖]AMD、英特尔为何争相走向胶水多核处理器?真相在此
  7. AT32F435_437_USB_MSC_SDIO
  8. Android关于BottomNavigationView效果实现指南
  9. 生活,人艰不拆......
  10. Android 解析微信基础表情并显示