FreeMarker源码分析(八)

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. */
    @Deprecated
    public static final String INCOMPATIBLE_IMPROVEMENTS = INCOMPATIBLE_IMPROVEMENTS_KEY_SNAKE_CASE;
    /** @deprecated Use {@link #INCOMPATIBLE_IMPROVEMENTS_KEY} instead. */
    @Deprecated
    public static final String INCOMPATIBLE_ENHANCEMENTS = "incompatible_enhancements";

    /** Legacy, snake case ({@code like_this}) variation of the setting name. @since 2.3.29 */
    public static final String FALLBACK_ON_NULL_LOOP_VARIABLE_KEY_SNAKE_CASE = "fallback_on_null_loop_variable";
    /** Modern, camel case ({@code likeThis}) variation of the setting name. @since 2.3.29 */
    public static final String FALLBACK_ON_NULL_LOOP_VARIABLE_KEY_CAMEL_CASE = "fallbackOnNullLoopVariable";
    /** Alias to the {@code ..._SNAKE_CASE} variation. @since 2.3.25 */
    public static final String FALLBACK_ON_NULL_LOOP_VARIABLE_KEY = FALLBACK_ON_NULL_LOOP_VARIABLE_KEY_SNAKE_CASE;

    private static final String[] SETTING_NAMES_SNAKE_CASE = new String[] {
        
        AUTO_ESCAPING_POLICY_KEY_SNAKE_CASE,
        CACHE_STORAGE_KEY_SNAKE_CASE,
        DEFAULT_ENCODING_KEY_SNAKE_CASE,
        FALLBACK_ON_NULL_LOOP_VARIABLE_KEY_SNAKE_CASE,
        INCOMPATIBLE_IMPROVEMENTS_KEY_SNAKE_CASE,
        INTERPOLATION_SYNTAX_KEY_SNAKE_CASE,
        LOCALIZED_LOOKUP_KEY_SNAKE_CASE,
        NAMING_CONVENTION_KEY_SNAKE_CASE,
        OUTPUT_FORMAT_KEY_SNAKE_CASE,
        RECOGNIZE_STANDARD_FILE_EXTENSIONS_KEY_SNAKE_CASE,
        REGISTERED_CUSTOM_OUTPUT_FORMATS_KEY_SNAKE_CASE,
        STRICT_SYNTAX_KEY_SNAKE_CASE,
        TAB_SIZE_KEY_SNAKE_CASE,
        TAG_SYNTAX_KEY_SNAKE_CASE,
        TEMPLATE_CONFIGURATIONS_KEY_SNAKE_CASE,
        TEMPLATE_LOADER_KEY_SNAKE_CASE,
        TEMPLATE_LOOKUP_STRATEGY_KEY_SNAKE_CASE,
        TEMPLATE_NAME_FORMAT_KEY_SNAKE_CASE,
        TEMPLATE_UPDATE_DELAY_KEY_SNAKE_CASE,
        WHITESPACE_STRIPPING_KEY_SNAKE_CASE,
    };

    private static final String[] SETTING_NAMES_CAMEL_CASE = new String[] {
        //要求必须按字母顺序排序
        AUTO_ESCAPING_POLICY_KEY_CAMEL_CASE,
        CACHE_STORAGE_KEY_CAMEL_CASE,
        DEFAULT_ENCODING_KEY_CAMEL_CASE,
        FALLBACK_ON_NULL_LOOP_VARIABLE_KEY_CAMEL_CASE,
        INCOMPATIBLE_IMPROVEMENTS_KEY_CAMEL_CASE,
        INTERPOLATION_SYNTAX_KEY_CAMEL_CASE,
        LOCALIZED_LOOKUP_KEY_CAMEL_CASE,
        NAMING_CONVENTION_KEY_CAMEL_CASE,
        OUTPUT_FORMAT_KEY_CAMEL_CASE,
        RECOGNIZE_STANDARD_FILE_EXTENSIONS_KEY_CAMEL_CASE,
        REGISTERED_CUSTOM_OUTPUT_FORMATS_KEY_CAMEL_CASE,
        STRICT_SYNTAX_KEY_CAMEL_CASE,
        TAB_SIZE_KEY_CAMEL_CASE,
        TAG_SYNTAX_KEY_CAMEL_CASE,
        TEMPLATE_CONFIGURATIONS_KEY_CAMEL_CASE,
        TEMPLATE_LOADER_KEY_CAMEL_CASE,
        TEMPLATE_LOOKUP_STRATEGY_KEY_CAMEL_CASE,
        TEMPLATE_NAME_FORMAT_KEY_CAMEL_CASE,
        TEMPLATE_UPDATE_DELAY_KEY_CAMEL_CASE,
        WHITESPACE_STRIPPING_KEY_CAMEL_CASE
    };
    
    private static final Map<String, OutputFormat> STANDARD_OUTPUT_FORMATS;
    static {
        STANDARD_OUTPUT_FORMATS = new HashMap<>();
        STANDARD_OUTPUT_FORMATS.put(UndefinedOutputFormat.INSTANCE.getName(), UndefinedOutputFormat.INSTANCE);
        STANDARD_OUTPUT_FORMATS.put(HTMLOutputFormat.INSTANCE.getName(), HTMLOutputFormat.INSTANCE);
        STANDARD_OUTPUT_FORMATS.put(XHTMLOutputFormat.INSTANCE.getName(), XHTMLOutputFormat.INSTANCE);
        STANDARD_OUTPUT_FORMATS.put(XMLOutputFormat.INSTANCE.getName(), XMLOutputFormat.INSTANCE);
        STANDARD_OUTPUT_FORMATS.put(RTFOutputFormat.INSTANCE.getName(), RTFOutputFormat.INSTANCE);
        STANDARD_OUTPUT_FORMATS.put(PlainTextOutputFormat.INSTANCE.getName(), PlainTextOutputFormat.INSTANCE);
        STANDARD_OUTPUT_FORMATS.put(CSSOutputFormat.INSTANCE.getName(), CSSOutputFormat.INSTANCE);
        STANDARD_OUTPUT_FORMATS.put(JavaScriptOutputFormat.INSTANCE.getName(), JavaScriptOutputFormat.INSTANCE);
        STANDARD_OUTPUT_FORMATS.put(JSONOutputFormat.INSTANCE.getName(), JSONOutputFormat.INSTANCE);
    }
    
    /**
     *

解析器根据的值决定是{@link #ANGLE_BRACKET_TAG_SYNTAX}还是{@link #SQUARE_BRACKET_TAG_SYNTAX}

*第一个标签(像{@code [#if x]}或{@code <#if x>})是mets。注意{@code[=…}是不是标签,但是

*一个插值表达式,所以它不用于标签语法的自动检测。
     */
    public static final int AUTO_DETECT_TAG_SYNTAX = 0;
    
    /** For example {@code <#if x><@foo /></#if>} */
    public static final int ANGLE_BRACKET_TAG_SYNTAX = 1;
    
    /**
     * For example {@code [#if x][@foo /][/#if]}.
     * It does <em>not</em> change <code>${x}</code> to {@code [=x]}; that's square bracket <em>interpolation</em>
     * syntax ({@link #SQUARE_BRACKET_INTERPOLATION_SYNTAX}).
     */
    public static final int SQUARE_BRACKET_TAG_SYNTAX = 2;

    /** <code>${expression}</code> and the deprecated <code>#{expression; numFormat}</code> @since 2.3.28 */
    public static final int LEGACY_INTERPOLATION_SYNTAX = 20;
    
    /** <code>${expression}</code> only (not <code>#{expression; numFormat}</code>) @since 2.3.28 */
    public static final int DOLLAR_INTERPOLATION_SYNTAX = 21;
    
    /**
     * <code>[=expression]</code> instead of <code>${expression}</code>.
     * It does <em>not</em> change {@code <#if x>} to {@code [#if x]}; that's square bracket <em>tag</em> syntax
     * ({@link #SQUARE_BRACKET_TAG_SYNTAX}).
     * @since 2.3.28
     */
    public static final int SQUARE_BRACKET_INTERPOLATION_SYNTAX = 22;
    
    public static final int AUTO_DETECT_NAMING_CONVENTION = 10;
    public static final int LEGACY_NAMING_CONVENTION = 11;
    public static final int CAMEL_CASE_NAMING_CONVENTION = 12;

    /**
     * Don't enable auto-escaping, regardless of what the {@link OutputFormat} is. Note that a {@code 
     * <#ftl auto_esc=true>} in the template will override this.
     */
    public static final int DISABLE_AUTO_ESCAPING_POLICY = 20;
    /**
     * Enable auto-escaping if the output format supports it and {@link MarkupOutputFormat#isAutoEscapedByDefault()} is
     * {@code true}.
     */
    public static final int ENABLE_IF_DEFAULT_AUTO_ESCAPING_POLICY = 21;
    /** Enable auto-escaping if the {@link OutputFormat} supports it. */
    public static final int ENABLE_IF_SUPPORTED_AUTO_ESCAPING_POLICY = 22;
    
    /** FreeMarker version 2.3.0 (an {@link #Configuration(Version) incompatible improvements break-point}) */
    public static final Version VERSION_2_3_0 = new Version(2, 3, 0);
    
    /** FreeMarker version 2.3.19 (an {@link #Configuration(Version) incompatible improvements break-point}) */
    public static final Version VERSION_2_3_19 = new Version(2, 3, 19);
    
    /** FreeMarker version 2.3.20 (an {@link #Configuration(Version) incompatible improvements break-point}) */
    public static final Version VERSION_2_3_20 = new Version(2, 3, 20);
    
    /** FreeMarker version 2.3.21 (an {@link #Configuration(Version) incompatible improvements break-point}) */
    public static final Version VERSION_2_3_21 = new Version(2, 3, 21);

    /** FreeMarker version 2.3.22 (an {@link #Configuration(Version) incompatible improvements break-point}) */
    public static final Version VERSION_2_3_22 = new Version(2, 3, 22);

    /** FreeMarker version 2.3.23 (an {@link #Configuration(Version) incompatible improvements break-point}) */
    public static final Version VERSION_2_3_23 = new Version(2, 3, 23);

    /** FreeMarker version 2.3.24 (an {@link #Configuration(Version) incompatible improvements break-point}) */
    public static final Version VERSION_2_3_24 = new Version(2, 3, 24);

    /** FreeMarker version 2.3.25 (an {@link #Configuration(Version) incompatible improvements break-point}) */
    public static final Version VERSION_2_3_25 = new Version(2, 3, 25);

    /** FreeMarker version 2.3.26 (an {@link #Configuration(Version) incompatible improvements break-point}) */
    public static final Version VERSION_2_3_26 = new Version(2, 3, 26);

    /** FreeMarker version 2.3.27 (an {@link #Configuration(Version) incompatible improvements break-point}) */
    public static final Version VERSION_2_3_27 = new Version(2, 3, 27);

    /** FreeMarker version 2.3.28 (an {@link #Configuration(Version) incompatible improvements break-point}) */
    public static final Version VERSION_2_3_28 = new Version(2, 3, 28);

    /** FreeMarker version 2.3.29 (an {@link #Configuration(Version) incompatible improvements break-point}) */
    public static final Version VERSION_2_3_29 = new Version(2, 3, 29);

    /** FreeMarker version 2.3.30 (an {@link #Configuration(Version) incompatible improvements break-point}) */
    public static final Version VERSION_2_3_30 = new Version(2, 3, 30);

    /** FreeMarker version 2.3.31 (an {@link #Configuration(Version) incompatible improvements break-point}) */
    public static final Version VERSION_2_3_31 = new Version(2, 3, 31);

    /** The default of {@link #getIncompatibleImprovements()}, currently {@link #VERSION_2_3_0}. */
    public static final Version DEFAULT_INCOMPATIBLE_IMPROVEMENTS = Configuration.VERSION_2_3_0;
    /** @deprecated Use {@link #DEFAULT_INCOMPATIBLE_IMPROVEMENTS} instead. */
    @Deprecated
    public static final String DEFAULT_INCOMPATIBLE_ENHANCEMENTS = DEFAULT_INCOMPATIBLE_IMPROVEMENTS.toString();
    /** @deprecated Use {@link #DEFAULT_INCOMPATIBLE_IMPROVEMENTS} instead. */
    @Deprecated
    public static final int PARSED_DEFAULT_INCOMPATIBLE_ENHANCEMENTS = DEFAULT_INCOMPATIBLE_IMPROVEMENTS.intValue(); 
    
    private static final String NULL = "null";
    private static final String DEFAULT = "default";
    private static final String JVM_DEFAULT = "JVM default";
    
    private static final Version VERSION;
    static {
        try {
            Properties props = ClassUtil.loadProperties(Configuration.class, VERSION_PROPERTIES_PATH);
            
            String versionString  = getRequiredVersionProperty(props, "version");
            
            Date buildDate;
            {
                String buildDateStr = getRequiredVersionProperty(props, "buildTimestamp");
                if (buildDateStr.endsWith("Z")) {
                    buildDateStr = buildDateStr.substring(0, buildDateStr.length() - 1) + "+0000";
                }
                try {
                    buildDate = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZ", Locale.US).parse(buildDateStr);
                } catch (java.text.ParseException e) {
                    buildDate = null;
                }
            }
            
            final Boolean gaeCompliant = Boolean.valueOf(getRequiredVersionProperty(props, "isGAECompliant"));
            
            VERSION = new Version(versionString, gaeCompliant, buildDate);
        } catch (IOException e) {
            throw new RuntimeException("Failed to load and parse " + VERSION_PROPERTIES_PATH, e);
        }
    }
    
    private static final String FM_24_DETECTION_CLASS_NAME = "freemarker.core._2_4_OrLaterMarker";
    private static final boolean FM_24_DETECTED;
    static {
        boolean fm24detected;
        try {
            Class.forName(FM_24_DETECTION_CLASS_NAME);
            fm24detected = true;
        } catch (ClassNotFoundException e) {
            fm24detected = false;
        } catch (LinkageError e) {
            fm24detected = true;
        } catch (Throwable e) {
            // Unexpected. We assume that there's no clash.
            fm24detected = false;
        }
        FM_24_DETECTED = fm24detected;
    }
    
    private final static Object defaultConfigLock = new Object();

    private static volatile Configuration defaultConfig;

    private boolean strictSyntax = true;
    private volatile boolean localizedLookup = true;
    private boolean whitespaceStripping = true;
    private int autoEscapingPolicy = ENABLE_IF_DEFAULT_AUTO_ESCAPING_POLICY;
    private OutputFormat outputFormat = UndefinedOutputFormat.INSTANCE;
    private boolean outputFormatExplicitlySet;
    private Boolean recognizeStandardFileExtensions;
    private Map<String, ? extends OutputFormat> registeredCustomOutputFormats = Collections.emptyMap(); 
    private Version incompatibleImprovements;
    private int tagSyntax = ANGLE_BRACKET_TAG_SYNTAX;
    private int interpolationSyntax = LEGACY_INTERPOLATION_SYNTAX;
    private int namingConvention = AUTO_DETECT_NAMING_CONVENTION;
    private int tabSize = 8;  // Default from JavaCC 3.x
    private boolean fallbackOnNullLoopVariable = true;  // Default for backward compatibility
    private boolean preventStrippings;

    private TemplateCache cache;
    
    private boolean templateLoaderExplicitlySet;
    private boolean templateLookupStrategyExplicitlySet;
    private boolean templateNameFormatExplicitlySet;
    private boolean cacheStorageExplicitlySet;
    
    private boolean objectWrapperExplicitlySet;
    private boolean templateExceptionHandlerExplicitlySet;
    private boolean attemptExceptionReporterExplicitlySet;
    private boolean logTemplateExceptionsExplicitlySet;
    private boolean wrapUncheckedExceptionsExplicitlySet;
    private boolean localeExplicitlySet;
    private boolean defaultEncodingExplicitlySet;
    private boolean timeZoneExplicitlySet;

    
    private HashMap/*<String, TemplateModel>*/ sharedVariables = new HashMap();

    
     // 当用户从Spring XML配置FreeMarker时,他没有控制订单,所以它必须在两种方式上工作。
     
    private HashMap/*<String, Object>*/ rewrappableSharedVariables = null;
    
    private String defaultEncoding = getDefaultDefaultEncoding();
    private ConcurrentMap localeToCharsetMap = new ConcurrentHashMap();
    
    /**
     * Same as {@link #Configuration(Version) Configuration(Configuration.DEFAULT_INCOMPATIBLE_IMPROVEMENTS)}.
     * 
     * @deprecated Use {@link #Configuration(Version)} instead. Note that the version can be still modified later with
     *     {@link Configuration#setIncompatibleImprovements(Version)} (or
     *     {@link Configuration#setSettings(Properties)}).  
     */
    @Deprecated
    public Configuration() {
        this(DEFAULT_INCOMPATIBLE_IMPROVEMENTS);
    }

    /**
     * Creates a new instance and sets which of the non-backward-compatible bugfixes/improvements should be enabled.
     * Note that the specified versions corresponds to the {@code incompatible_improvements} configuration setting, and
     * can be changed later, with {@link #setIncompatibleImprovements(Version)} for example. 
     *
     * <p><b>About the "incompatible improvements" setting</b>
     *
     * <p>This setting value is the FreeMarker version number where the not 100% backward compatible bug fixes and
     * improvements that you want to enable were already implemented. In new projects you should set this to the fixed
     * FreeMarker version that you start the development with. In older projects it's also usually better to keep
     * this high, however you should check the changes activated (find them below), especially if not only the 3rd
     * version number (the micro version) of {@code incompatibleImprovements} is increased. Generally, as far as you
     * only increase the last version number of this setting, the changes are low risk. The default value is 2.3.0 to
     * maximize backward compatibility, but that value isn't recommended.
     * 
     * <p>Bugfixes and improvements that are fully backward compatible, also those that are important security fixes,
     * are enabled regardless of the incompatible improvements setting.
     *
     * <p>Do NOT ever use {@link #getVersion()} to set the "incompatible improvements". Always use a fixed value, like
     * {@link #VERSION_2_3_30}. Otherwise your application can break as you upgrade FreeMarker. (As of 2.3.30, doing
     * this will be logged as an error. As of 2.4.0, it will be probably disallowed, by throwing exception.)
     * 
     * <p>An important consequence of setting this setting is that now your application will check if the stated minimum
     * FreeMarker version requirement is met. Like if you set this setting to 2.3.22, but accidentally the application
     * is deployed with FreeMarker 2.3.21, then FreeMarker will fail, telling that a higher version is required. After
     * all, the fixes/improvements you have requested aren't available on a lower version.
     * 
     * <p>Note that as FreeMarker's minor (2nd) or major (1st) version number increments, it's possible that emulating
     * some of the old bugs will become unsupported, that is, even if you set this setting to a low value, it silently
     * wont bring back the old behavior anymore. Information about that will be present here.
     * 
     * <p>Currently the effects of this setting are:
     * <ul>
     *   <li><p>
     *     2.3.0: This is the lowest supported value, the version used in very old projects. This is the default in the
     *     FreeMarker 2.3.x series (the one used by the deprecated {@link #Configuration()} constructor) for maximum
     *     backward compatibility.
     *   </li>
     *   <li><p>
     *     2.3.19 (or higher): Bug fix: Wrong {@code #} tags were printed as static text instead of
     *     causing parsing error when there was no correct {@code #} or {@code @} tag earlier in the
     *     same template.
     *   </li>
     *   <li><p>
     *     2.3.20 (or higher): {@code ?html} will escape apostrophe-quotes just like {@code ?xhtml} does. Utilizing
     *     this is highly recommended, because otherwise if interpolations are used inside attribute values that use
     *     apostrophe-quotation (<tt>&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>
     *       
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值