freemarker-notifications mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From ddek...@apache.org
Subject [05/54] [partial] incubator-freemarker git commit: Top level package name change to org.apache.freemarker, and some of of the internal package structure changes. Other smaller cleanup. To be continued...
Date Thu, 16 Feb 2017 23:08:30 GMT
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/ecb4e230/src/main/java/org/apache/freemarker/core/Configuration.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/freemarker/core/Configuration.java b/src/main/java/org/apache/freemarker/core/Configuration.java
new file mode 100644
index 0000000..84cbed2
--- /dev/null
+++ b/src/main/java/org/apache/freemarker/core/Configuration.java
@@ -0,0 +1,3201 @@
+/*
+ * 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 org.apache.freemarker.core;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+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.TreeSet;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+
+import org.apache.freemarker.core.ast.BugException;
+import org.apache.freemarker.core.ast.CSSOutputFormat;
+import org.apache.freemarker.core.ast.CombinedMarkupOutputFormat;
+import org.apache.freemarker.core.ast.Configurable;
+import org.apache.freemarker.core.ast.Environment;
+import org.apache.freemarker.core.ast.HTMLOutputFormat;
+import org.apache.freemarker.core.ast.JSONOutputFormat;
+import org.apache.freemarker.core.ast.JavaScriptOutputFormat;
+import org.apache.freemarker.core.ast.MarkupOutputFormat;
+import org.apache.freemarker.core.ast.OutputFormat;
+import org.apache.freemarker.core.ast.ParseException;
+import org.apache.freemarker.core.ast.ParserConfiguration;
+import org.apache.freemarker.core.ast.PlainTextOutputFormat;
+import org.apache.freemarker.core.ast.RTFOutputFormat;
+import org.apache.freemarker.core.ast.TemplateConfiguration;
+import org.apache.freemarker.core.ast.TemplateMarkupOutputModel;
+import org.apache.freemarker.core.ast.UndefinedOutputFormat;
+import org.apache.freemarker.core.ast.UnregisteredOutputFormatException;
+import org.apache.freemarker.core.ast.XHTMLOutputFormat;
+import org.apache.freemarker.core.ast.XMLOutputFormat;
+import org.apache.freemarker.core.ast._CoreAPI;
+import org.apache.freemarker.core.ast._DelayedJQuote;
+import org.apache.freemarker.core.ast._MiscTemplateException;
+import org.apache.freemarker.core.ast._ObjectBuilderSettingEvaluator;
+import org.apache.freemarker.core.ast._SettingEvaluationEnvironment;
+import org.apache.freemarker.core.ast._SortedArraySet;
+import org.apache.freemarker.core.ast._UnmodifiableCompositeSet;
+import org.apache.freemarker.core.model.ObjectWrapper;
+import org.apache.freemarker.core.model.TemplateHashModelEx;
+import org.apache.freemarker.core.model.TemplateModel;
+import org.apache.freemarker.core.model.TemplateModelException;
+import org.apache.freemarker.core.model.TemplateModelIterator;
+import org.apache.freemarker.core.model.TemplateScalarModel;
+import org.apache.freemarker.core.model.TemplateSequenceModel;
+import org.apache.freemarker.core.model.impl.DefaultObjectWrapper;
+import org.apache.freemarker.core.model.impl.DefaultObjectWrapperBuilder;
+import org.apache.freemarker.core.model.impl.SimpleCollection;
+import org.apache.freemarker.core.model.impl.SimpleDate;
+import org.apache.freemarker.core.model.impl.SimpleHash;
+import org.apache.freemarker.core.model.impl.SimpleObjectWrapper;
+import org.apache.freemarker.core.model.impl.SimpleSequence;
+import org.apache.freemarker.core.model.impl._StaticObjectWrappers;
+import org.apache.freemarker.core.model.impl.beans.BeansWrapper;
+import org.apache.freemarker.core.model.impl.beans.BeansWrapperBuilder;
+import org.apache.freemarker.core.templateresolver.CacheStorage;
+import org.apache.freemarker.core.templateresolver.ClassTemplateLoader;
+import org.apache.freemarker.core.templateresolver.FileTemplateLoader;
+import org.apache.freemarker.core.templateresolver.MalformedTemplateNameException;
+import org.apache.freemarker.core.templateresolver.MruCacheStorage;
+import org.apache.freemarker.core.templateresolver.MultiTemplateLoader;
+import org.apache.freemarker.core.templateresolver.SoftCacheStorage;
+import org.apache.freemarker.core.templateresolver.TemplateCache;
+import org.apache.freemarker.core.templateresolver.TemplateCache.MaybeMissingTemplate;
+import org.apache.freemarker.core.util.CaptureOutput;
+import org.apache.freemarker.core.util.ClassUtil;
+import org.apache.freemarker.core.util.Constants;
+import org.apache.freemarker.core.util.HtmlEscape;
+import org.apache.freemarker.core.util.NormalizeNewlines;
+import org.apache.freemarker.core.util.NullArgumentException;
+import org.apache.freemarker.core.util.SecurityUtilities;
+import org.apache.freemarker.core.util.StandardCompress;
+import org.apache.freemarker.core.util.StringUtil;
+import org.apache.freemarker.core.util.XmlEscape;
+import org.apache.freemarker.core.templateresolver.TemplateConfigurationFactory;
+import org.apache.freemarker.core.templateresolver.TemplateLoader;
+import org.apache.freemarker.core.templateresolver.TemplateLookupContext;
+import org.apache.freemarker.core.templateresolver.TemplateLookupStrategy;
+import org.apache.freemarker.core.templateresolver.TemplateNameFormat;
+import org.apache.freemarker.core.templateresolver.URLTemplateLoader;
+
+/**
+ * <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 X, Y, Z 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.html");
+ *  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 String VERSION_PROPERTIES_PATH = "org/apache/freemarker/core/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 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.
+     */
+    @Deprecated
+    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.
+     */
+    @Deprecated
+    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.
+     */
+    @Deprecated
+    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.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";
+    
+    private static final String[] SETTING_NAMES_SNAKE_CASE = new String[] {
+        // Must be sorted alphabetically!
+        AUTO_ESCAPING_POLICY_KEY_SNAKE_CASE,
+        CACHE_STORAGE_KEY_SNAKE_CASE,
+        DEFAULT_ENCODING_KEY_SNAKE_CASE,
+        INCOMPATIBLE_IMPROVEMENTS_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,
+        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[] {
+        // Must be sorted alphabetically!
+        AUTO_ESCAPING_POLICY_KEY_CAMEL_CASE,
+        CACHE_STORAGE_KEY_CAMEL_CASE,
+        DEFAULT_ENCODING_KEY_CAMEL_CASE,
+        INCOMPATIBLE_IMPROVEMENTS_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,
+        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<String, OutputFormat>();
+        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);
+    }
+    
+    public static final int AUTO_DETECT_TAG_SYNTAX = 0;
+    public static final int ANGLE_BRACKET_TAG_SYNTAX = 1;
+    public static final int SQUARE_BRACKET_TAG_SYNTAX = 2;
+
+    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 3.0.0 (an {@link #Configuration(Version) incompatible improvements break-point}) */
+    public static final Version VERSION_3_0_0 = new Version(3, 0, 0);
+    
+    /** 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 Version VERSION;
+    static {
+        try {
+            Properties vp = new Properties();
+            InputStream ins = Configuration.class.getClassLoader()
+                    .getResourceAsStream(VERSION_PROPERTIES_PATH);
+            if (ins == null) {
+                throw new RuntimeException("Version file is missing.");
+            } else {
+                try {
+                    vp.load(ins);
+                } finally {
+                    ins.close();
+                }
+                
+                String versionString  = getRequiredVersionProperty(vp, "version");
+                
+                Date buildDate;
+                {
+                    String buildDateStr = getRequiredVersionProperty(vp, "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(vp, "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 = "org.apache.freemarker.core.ast._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 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 namingConvention = AUTO_DETECT_NAMING_CONVENTION;
+    private int tabSize = 8;  // Default from JavaCC 3.x 
+
+    private TemplateCache cache;
+    
+    private boolean templateLoaderExplicitlySet;
+    private boolean templateLookupStrategyExplicitlySet;
+    private boolean templateNameFormatExplicitlySet;
+    private boolean cacheStorageExplicitlySet;
+    
+    private boolean objectWrapperExplicitlySet;
+    private boolean templateExceptionHandlerExplicitlySet;
+    private boolean logTemplateExceptionsExplicitlySet;
+    
+    private HashMap/*<String, TemplateModel>*/ sharedVariables = new HashMap();
+
+    /**
+     * Needed so that it doesn't mater in what order do you call {@link #setSharedVaribles(Map)}
+     * and {@link #setObjectWrapper(ObjectWrapper)}. When the user configures FreeMarker from Spring XML, he has no
+     * control over the order, so it has to work on both ways.
+     */
+    private HashMap/*<String, Object>*/ rewrappableSharedVariables = null;
+    
+    private String defaultEncoding = SecurityUtilities.getSystemProperty("file.encoding", "utf-8");
+    private ConcurrentMap localeToCharsetMap = new ConcurrentHashMap();
+    
+    /**
+     * @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
+     * FreeMarker version that you are actually using. In older projects it's also usually better to keep this high,
+     * however you better check the changes activated (find them below), at least 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 always 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>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 older projects. This is the default in the
+     *     FreeMarker 2.3.x series.
+     *   </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
+     *         {@code 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#clearClassIntrospecitonCache()} 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 org.apache.freemarker.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>
+     * </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);
+        
+        // We postpone this until here (rather that doing this in static initializer) for two reason:
+        // - Class initialization errors are often not reported very well
+        // - This way we avoid the error if FM isn't actually used
+        checkFreeMarkerVersionClash();
+        
+        NullArgumentException.check("incompatibleImprovements", 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(
+                null,
+                getDefaultCacheStorage(),
+                getDefaultTemplateLookupStrategy(),
+                getDefaultTemplateNameFormat(),
+                null,
+                this);
+        cache.clear(); // for fully BC behavior
+        cache.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(false);
+        cache.setDelay(oldCache.getDelay());
+        cache.setLocalizedLookup(localizedLookup);
+    }
+    
+    private void recreateTemplateCache() {
+        recreateTemplateCacheWith(cache.getTemplateLoader(), cache.getCacheStorage(),
+                cache.getTemplateLookupStrategy(), cache.getTemplateNameFormat(),
+                getTemplateConfigurations());
+    }
+    
+    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 boolean getDefaultLogTemplateExceptions() {
+        return getDefaultLogTemplateExceptions(getIncompatibleImprovements());
+    }
+    
+    private ObjectWrapper getDefaultObjectWrapper() {
+        return getDefaultObjectWrapper(getIncompatibleImprovements());
+    }
+    
+    // Package visible as Configurable needs this to initialize the field defaults.
+    final static TemplateExceptionHandler getDefaultTemplateExceptionHandler(Version incompatibleImprovements) {
+        return TemplateExceptionHandler.DEBUG_HANDLER;
+    }
+
+    // Package visible as Configurable needs this to initialize the field defaults.
+    final static boolean getDefaultLogTemplateExceptions(Version incompatibleImprovements) {
+        return true;
+    }
+    
+    @Override
+    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)
+     */
+    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");
+    }
+
+    /**
+     * Clears language-to-encoding map.
+     * @see #loadBuiltInEncodingMap
+     * @see #setEncoding
+     */
+    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.
+     */
+    @Deprecated
+    static 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...}.
+     */
+    @Deprecated
+    static 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.
+     * 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 Configuration
+        synchronized (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(null);
+            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 a {@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;
+    }
+
+    /**
+     * 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 #isTemplateLookupStrategyExplicitlySet()} will return {@code false}.
+     * 
+     * @since 2.3.22
+     */
+    public void unsetTemplateLookupStrategy() {
+        if (templateLookupStrategyExplicitlySet) {
+            setTemplateLookupStrategy(getDefaultTemplateLookupStrategy());
+            templateLookupStrategyExplicitlySet = false;
+        }
+    }
+    
+    /**
+     * Tells if {@link #setTemplateLookupStrategy(TemplateLookupStrategy)} (or equivalent) was already called on this
+     * instance.
+     * 
+     * @since 2.3.22
+     */
+    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 org.apache.freemarker.core.templateresolver.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" is removed from the API as it's not safe to set anything after publishing the Configuration
+        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 Configuration
+        synchronized (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 org.apache.freemarker.servlet.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 {
+            // Don't introduce linking-time dependency on servlets
+            final Class webappTemplateLoaderClass = ClassUtil.forName(
+                    "org.apache.freemarker.servlet.WebAppTemplateLoader");
+            
+            // Don't introduce linking-time dependency on servlets
+            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.
+     */
+    @Deprecated
+    public 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" exists 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();
+    }
+    
+    @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;
+    }
+    
+    @Override
+    public 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;
+    }    
+    
+    /**
+     * @since 2.3.22
+     */
+    @Override
+    public 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;
+    }
+
+    /**
+     * Use {@link #Configuration(Version)} instead if possible; see the meaning of the parameter there.
+     * If the default value of a setting depends on the {@code incompatibleImprovements} and t

<TRUNCATED>


Mime
View raw message