You are viewing a plain text version of this content. The canonical link for it is here.
Posted to notifications@freemarker.apache.org by dd...@apache.org on 2017/05/14 10:53:18 UTC

[35/51] [partial] incubator-freemarker git commit: Migrated from Ant to Gradle, and modularized the project. This is an incomplete migration; there are some TODO-s in the build scripts, and release related tasks are still missing. What works: Building th

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/3fd56062/freemarker-core/src/main/java/org/apache/freemarker/core/MutableProcessingConfiguration.java
----------------------------------------------------------------------
diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/MutableProcessingConfiguration.java b/freemarker-core/src/main/java/org/apache/freemarker/core/MutableProcessingConfiguration.java
new file mode 100644
index 0000000..c5c4c82
--- /dev/null
+++ b/freemarker-core/src/main/java/org/apache/freemarker/core/MutableProcessingConfiguration.java
@@ -0,0 +1,2418 @@
+/*
+ * 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.nio.charset.Charset;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.LinkedHashMap;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+import java.util.Properties;
+import java.util.Set;
+import java.util.TimeZone;
+
+import org.apache.freemarker.core.arithmetic.ArithmeticEngine;
+import org.apache.freemarker.core.arithmetic.impl.BigDecimalArithmeticEngine;
+import org.apache.freemarker.core.arithmetic.impl.ConservativeArithmeticEngine;
+import org.apache.freemarker.core.model.ObjectWrapper;
+import org.apache.freemarker.core.model.impl.DefaultObjectWrapper;
+import org.apache.freemarker.core.model.impl.RestrictedObjectWrapper;
+import org.apache.freemarker.core.outputformat.OutputFormat;
+import org.apache.freemarker.core.outputformat.impl.HTMLOutputFormat;
+import org.apache.freemarker.core.outputformat.impl.PlainTextOutputFormat;
+import org.apache.freemarker.core.outputformat.impl.RTFOutputFormat;
+import org.apache.freemarker.core.outputformat.impl.UndefinedOutputFormat;
+import org.apache.freemarker.core.outputformat.impl.XMLOutputFormat;
+import org.apache.freemarker.core.templateresolver.AndMatcher;
+import org.apache.freemarker.core.templateresolver.ConditionalTemplateConfigurationFactory;
+import org.apache.freemarker.core.templateresolver.FileNameGlobMatcher;
+import org.apache.freemarker.core.templateresolver.FirstMatchTemplateConfigurationFactory;
+import org.apache.freemarker.core.templateresolver.MergingTemplateConfigurationFactory;
+import org.apache.freemarker.core.templateresolver.NotMatcher;
+import org.apache.freemarker.core.templateresolver.OrMatcher;
+import org.apache.freemarker.core.templateresolver.PathGlobMatcher;
+import org.apache.freemarker.core.templateresolver.PathRegexMatcher;
+import org.apache.freemarker.core.templateresolver.impl.DefaultTemplateNameFormat;
+import org.apache.freemarker.core.templateresolver.impl.DefaultTemplateNameFormatFM2;
+import org.apache.freemarker.core.util.FTLUtil;
+import org.apache.freemarker.core.util.GenericParseException;
+import org.apache.freemarker.core.util.OptInTemplateClassResolver;
+import org.apache.freemarker.core.util._ClassUtil;
+import org.apache.freemarker.core.util._CollectionUtil;
+import org.apache.freemarker.core.util._KeyValuePair;
+import org.apache.freemarker.core.util._NullArgumentException;
+import org.apache.freemarker.core.util._SortedArraySet;
+import org.apache.freemarker.core.util._StringUtil;
+import org.apache.freemarker.core.valueformat.TemplateDateFormatFactory;
+import org.apache.freemarker.core.valueformat.TemplateNumberFormatFactory;
+
+/**
+ * Extended by FreeMarker core classes (not by you) that support specifying {@link ProcessingConfiguration} setting
+ * values. <b>New abstract methods may be added any time in future FreeMarker versions, so don't try to implement this
+ * interface yourself!</b>
+ */
+public abstract class MutableProcessingConfiguration<SelfT extends MutableProcessingConfiguration<SelfT>>
+        implements ProcessingConfiguration {
+    public static final String NULL_VALUE = "null";
+    public static final String DEFAULT_VALUE = "default";
+    public static final String JVM_DEFAULT_VALUE = "JVM default";
+    
+    /** Legacy, snake case ({@code like_this}) variation of the setting name. @since 2.3.23 */
+    public static final String LOCALE_KEY_SNAKE_CASE = "locale";
+    /** Modern, camel case ({@code likeThis}) variation of the setting name. @since 2.3.23 */
+    public static final String LOCALE_KEY_CAMEL_CASE = "locale";
+    /** Alias to the {@code ..._SNAKE_CASE} variation due to backward compatibility constraints. */
+    public static final String LOCALE_KEY = LOCALE_KEY_SNAKE_CASE;
+    
+    /** Legacy, snake case ({@code like_this}) variation of the setting name. @since 2.3.23 */
+    public static final String NUMBER_FORMAT_KEY_SNAKE_CASE = "number_format";
+    /** Modern, camel case ({@code likeThis}) variation of the setting name. @since 2.3.23 */
+    public static final String NUMBER_FORMAT_KEY_CAMEL_CASE = "numberFormat";
+    /** Alias to the {@code ..._SNAKE_CASE} variation due to backward compatibility constraints. */
+    public static final String NUMBER_FORMAT_KEY = NUMBER_FORMAT_KEY_SNAKE_CASE;
+    
+    /** Legacy, snake case ({@code like_this}) variation of the setting name. @since 2.3.23 */
+    public static final String CUSTOM_NUMBER_FORMATS_KEY_SNAKE_CASE = "custom_number_formats";
+    /** Modern, camel case ({@code likeThis}) variation of the setting name. @since 2.3.23 */
+    public static final String CUSTOM_NUMBER_FORMATS_KEY_CAMEL_CASE = "customNumberFormats";
+    /** Alias to the {@code ..._SNAKE_CASE} variation due to backward compatibility constraints. */
+    public static final String CUSTOM_NUMBER_FORMATS_KEY = CUSTOM_NUMBER_FORMATS_KEY_SNAKE_CASE;
+    
+    /** Legacy, snake case ({@code like_this}) variation of the setting name. @since 2.3.23 */
+    public static final String TIME_FORMAT_KEY_SNAKE_CASE = "time_format";
+    /** Modern, camel case ({@code likeThis}) variation of the setting name. @since 2.3.23 */
+    public static final String TIME_FORMAT_KEY_CAMEL_CASE = "timeFormat";
+    /** Alias to the {@code ..._SNAKE_CASE} variation due to backward compatibility constraints. */
+    public static final String TIME_FORMAT_KEY = TIME_FORMAT_KEY_SNAKE_CASE;
+    
+    /** Legacy, snake case ({@code like_this}) variation of the setting name. @since 2.3.23 */
+    public static final String DATE_FORMAT_KEY_SNAKE_CASE = "date_format";
+    /** Modern, camel case ({@code likeThis}) variation of the setting name. @since 2.3.23 */
+    public static final String DATE_FORMAT_KEY_CAMEL_CASE = "dateFormat";
+    /** Alias to the {@code ..._SNAKE_CASE} variation due to backward compatibility constraints. */
+    public static final String DATE_FORMAT_KEY = DATE_FORMAT_KEY_SNAKE_CASE;
+    
+    /** Legacy, snake case ({@code like_this}) variation of the setting name. @since 2.3.23 */
+    public static final String CUSTOM_DATE_FORMATS_KEY_SNAKE_CASE = "custom_date_formats";
+    /** Modern, camel case ({@code likeThis}) variation of the setting name. @since 2.3.23 */
+    public static final String CUSTOM_DATE_FORMATS_KEY_CAMEL_CASE = "customDateFormats";
+    /** Alias to the {@code ..._SNAKE_CASE} variation due to backward compatibility constraints. */
+    public static final String CUSTOM_DATE_FORMATS_KEY = CUSTOM_DATE_FORMATS_KEY_SNAKE_CASE;
+    
+    /** Legacy, snake case ({@code like_this}) variation of the setting name. @since 2.3.23 */
+    public static final String DATETIME_FORMAT_KEY_SNAKE_CASE = "datetime_format";
+    /** Modern, camel case ({@code likeThis}) variation of the setting name. @since 2.3.23 */
+    public static final String DATETIME_FORMAT_KEY_CAMEL_CASE = "datetimeFormat";
+    /** Alias to the {@code ..._SNAKE_CASE} variation due to backward compatibility constraints. */
+    public static final String DATETIME_FORMAT_KEY = DATETIME_FORMAT_KEY_SNAKE_CASE;
+    
+    /** Legacy, snake case ({@code like_this}) variation of the setting name. @since 2.3.23 */
+    public static final String TIME_ZONE_KEY_SNAKE_CASE = "time_zone";
+    /** Modern, camel case ({@code likeThis}) variation of the setting name. @since 2.3.23 */
+    public static final String TIME_ZONE_KEY_CAMEL_CASE = "timeZone";
+    /** Alias to the {@code ..._SNAKE_CASE} variation due to backward compatibility constraints. */
+    public static final String TIME_ZONE_KEY = TIME_ZONE_KEY_SNAKE_CASE;
+    
+    /** Legacy, snake case ({@code like_this}) variation of the setting name. @since 2.3.23 */
+    public static final String SQL_DATE_AND_TIME_TIME_ZONE_KEY_SNAKE_CASE = "sql_date_and_time_time_zone";
+    /** Modern, camel case ({@code likeThis}) variation of the setting name. @since 2.3.23 */
+    public static final String SQL_DATE_AND_TIME_TIME_ZONE_KEY_CAMEL_CASE = "sqlDateAndTimeTimeZone";
+    /** Alias to the {@code ..._SNAKE_CASE} variation due to backward compatibility constraints. */
+    public static final String SQL_DATE_AND_TIME_TIME_ZONE_KEY = SQL_DATE_AND_TIME_TIME_ZONE_KEY_SNAKE_CASE;
+    
+    /** Legacy, snake case ({@code like_this}) variation of the setting name. @since 2.3.23 */
+    public static final String TEMPLATE_EXCEPTION_HANDLER_KEY_SNAKE_CASE = "template_exception_handler";
+    /** Modern, camel case ({@code likeThis}) variation of the setting name. @since 2.3.23 */
+    public static final String TEMPLATE_EXCEPTION_HANDLER_KEY_CAMEL_CASE = "templateExceptionHandler";
+    /** Alias to the {@code ..._SNAKE_CASE} variation due to backward compatibility constraints. */
+    public static final String TEMPLATE_EXCEPTION_HANDLER_KEY = TEMPLATE_EXCEPTION_HANDLER_KEY_SNAKE_CASE;
+    
+    /** Legacy, snake case ({@code like_this}) variation of the setting name. @since 2.3.23 */
+    public static final String ARITHMETIC_ENGINE_KEY_SNAKE_CASE = "arithmetic_engine";
+    /** Modern, camel case ({@code likeThis}) variation of the setting name. @since 2.3.23 */
+    public static final String ARITHMETIC_ENGINE_KEY_CAMEL_CASE = "arithmeticEngine";
+    /** Alias to the {@code ..._SNAKE_CASE} variation due to backward compatibility constraints. */
+    public static final String ARITHMETIC_ENGINE_KEY = ARITHMETIC_ENGINE_KEY_SNAKE_CASE;
+    
+    /** Legacy, snake case ({@code like_this}) variation of the setting name. @since 2.3.23 */
+    public static final String OBJECT_WRAPPER_KEY_SNAKE_CASE = "object_wrapper";
+    /** Modern, camel case ({@code likeThis}) variation of the setting name. @since 2.3.23 */
+    public static final String OBJECT_WRAPPER_KEY_CAMEL_CASE = "objectWrapper";
+    /** Alias to the {@code ..._SNAKE_CASE} variation due to backward compatibility constraints. */
+    public static final String OBJECT_WRAPPER_KEY = OBJECT_WRAPPER_KEY_SNAKE_CASE;
+    
+    /** Legacy, snake case ({@code like_this}) variation of the setting name. @since 2.3.23 */
+    public static final String BOOLEAN_FORMAT_KEY_SNAKE_CASE = "boolean_format";
+    /** Modern, camel case ({@code likeThis}) variation of the setting name. @since 2.3.23 */
+    public static final String BOOLEAN_FORMAT_KEY_CAMEL_CASE = "booleanFormat";
+    /** Alias to the {@code ..._SNAKE_CASE} variation due to backward compatibility constraints. */
+    public static final String BOOLEAN_FORMAT_KEY = BOOLEAN_FORMAT_KEY_SNAKE_CASE;
+    
+    /** Legacy, snake case ({@code like_this}) variation of the setting name. @since 2.3.23 */
+    public static final String OUTPUT_ENCODING_KEY_SNAKE_CASE = "output_encoding";
+    /** Modern, camel case ({@code likeThis}) variation of the setting name. @since 2.3.23 */
+    public static final String OUTPUT_ENCODING_KEY_CAMEL_CASE = "outputEncoding";
+    /** Alias to the {@code ..._SNAKE_CASE} variation due to backward compatibility constraints. */
+    public static final String OUTPUT_ENCODING_KEY = OUTPUT_ENCODING_KEY_SNAKE_CASE;
+    
+    /** Legacy, snake case ({@code like_this}) variation of the setting name. @since 2.3.23 */
+    public static final String URL_ESCAPING_CHARSET_KEY_SNAKE_CASE = "url_escaping_charset";
+    /** Modern, camel case ({@code likeThis}) variation of the setting name. @since 2.3.23 */
+    public static final String URL_ESCAPING_CHARSET_KEY_CAMEL_CASE = "urlEscapingCharset";
+    /** Alias to the {@code ..._SNAKE_CASE} variation due to backward compatibility constraints. */
+    public static final String URL_ESCAPING_CHARSET_KEY = URL_ESCAPING_CHARSET_KEY_SNAKE_CASE;
+    
+    /** Legacy, snake case ({@code like_this}) variation of the setting name. @since 2.3.23 */
+    public static final String AUTO_FLUSH_KEY_SNAKE_CASE = "auto_flush";
+    /** Modern, camel case ({@code likeThis}) variation of the setting name. @since 2.3.23 */
+    public static final String AUTO_FLUSH_KEY_CAMEL_CASE = "autoFlush";
+    /** Alias to the {@code ..._SNAKE_CASE} variation due to backward compatibility constraints. @since 2.3.17 */
+    public static final String AUTO_FLUSH_KEY = AUTO_FLUSH_KEY_SNAKE_CASE;
+    
+    /** Legacy, snake case ({@code like_this}) variation of the setting name. @since 2.3.23 */
+    public static final String NEW_BUILTIN_CLASS_RESOLVER_KEY_SNAKE_CASE = "new_builtin_class_resolver";
+    /** Modern, camel case ({@code likeThis}) variation of the setting name. @since 2.3.23 */
+    public static final String NEW_BUILTIN_CLASS_RESOLVER_KEY_CAMEL_CASE = "newBuiltinClassResolver";
+    /** Alias to the {@code ..._SNAKE_CASE} variation due to backward compatibility constraints. @since 2.3.17 */
+    public static final String NEW_BUILTIN_CLASS_RESOLVER_KEY = NEW_BUILTIN_CLASS_RESOLVER_KEY_SNAKE_CASE;
+    
+    /** Legacy, snake case ({@code like_this}) variation of the setting name. @since 2.3.23 */
+    public static final String SHOW_ERROR_TIPS_KEY_SNAKE_CASE = "show_error_tips";
+    /** Modern, camel case ({@code likeThis}) variation of the setting name. @since 2.3.23 */
+    public static final String SHOW_ERROR_TIPS_KEY_CAMEL_CASE = "showErrorTips";
+    /** Alias to the {@code ..._SNAKE_CASE} variation due to backward compatibility constraints. @since 2.3.21 */
+    public static final String SHOW_ERROR_TIPS_KEY = SHOW_ERROR_TIPS_KEY_SNAKE_CASE;
+    
+    /** Legacy, snake case ({@code like_this}) variation of the setting name. @since 2.3.23 */
+    public static final String API_BUILTIN_ENABLED_KEY_SNAKE_CASE = "api_builtin_enabled";
+    /** Modern, camel case ({@code likeThis}) variation of the setting name. @since 2.3.23 */
+    public static final String API_BUILTIN_ENABLED_KEY_CAMEL_CASE = "apiBuiltinEnabled";
+    /** Alias to the {@code ..._SNAKE_CASE} variation due to backward compatibility constraints. @since 2.3.22 */
+    public static final String API_BUILTIN_ENABLED_KEY = API_BUILTIN_ENABLED_KEY_SNAKE_CASE;
+    
+    /** Legacy, snake case ({@code like_this}) variation of the setting name. @since 2.3.23 */
+    public static final String LOG_TEMPLATE_EXCEPTIONS_KEY_SNAKE_CASE = "log_template_exceptions";
+    /** Modern, camel case ({@code likeThis}) variation of the setting name. @since 2.3.23 */
+    public static final String LOG_TEMPLATE_EXCEPTIONS_KEY_CAMEL_CASE = "logTemplateExceptions";
+    /** Alias to the {@code ..._SNAKE_CASE} variation due to backward compatibility constraints. @since 2.3.22 */
+    public static final String LOG_TEMPLATE_EXCEPTIONS_KEY = LOG_TEMPLATE_EXCEPTIONS_KEY_SNAKE_CASE;
+
+    /** Legacy, snake case ({@code like_this}) variation of the setting name. @since 2.3.25 */
+    public static final String LAZY_IMPORTS_KEY_SNAKE_CASE = "lazy_imports";
+    /** Modern, camel case ({@code likeThis}) variation of the setting name. @since 2.3.25 */
+    public static final String LAZY_IMPORTS_KEY_CAMEL_CASE = "lazyImports";
+    /** Alias to the {@code ..._SNAKE_CASE} variation due to backward compatibility constraints. */
+    public static final String LAZY_IMPORTS_KEY = LAZY_IMPORTS_KEY_SNAKE_CASE;
+
+    /** Legacy, snake case ({@code like_this}) variation of the setting name. @since 2.3.25 */
+    public static final String LAZY_AUTO_IMPORTS_KEY_SNAKE_CASE = "lazy_auto_imports";
+    /** Modern, camel case ({@code likeThis}) variation of the setting name. @since 2.3.25 */
+    public static final String LAZY_AUTO_IMPORTS_KEY_CAMEL_CASE = "lazyAutoImports";
+    /** Alias to the {@code ..._SNAKE_CASE} variation due to backward compatibility constraints. */
+    public static final String LAZY_AUTO_IMPORTS_KEY = LAZY_AUTO_IMPORTS_KEY_SNAKE_CASE;
+    
+    /** Legacy, snake case ({@code like_this}) variation of the setting name. @since 2.3.25 */
+    public static final String AUTO_IMPORT_KEY_SNAKE_CASE = "auto_import";
+    /** Modern, camel case ({@code likeThis}) variation of the setting name. @since 2.3.25 */
+    public static final String AUTO_IMPORT_KEY_CAMEL_CASE = "autoImport";
+    /** Alias to the {@code ..._SNAKE_CASE} variation due to backward compatibility constraints. */
+    public static final String AUTO_IMPORT_KEY = AUTO_IMPORT_KEY_SNAKE_CASE;
+    
+    /** Legacy, snake case ({@code like_this}) variation of the setting name. @since 2.3.25 */
+    public static final String AUTO_INCLUDE_KEY_SNAKE_CASE = "auto_include";
+    /** Modern, camel case ({@code likeThis}) variation of the setting name. @since 2.3.25 */
+    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;
+    
+    private static final String[] SETTING_NAMES_SNAKE_CASE = new String[] {
+        // Must be sorted alphabetically!
+        API_BUILTIN_ENABLED_KEY_SNAKE_CASE,
+        ARITHMETIC_ENGINE_KEY_SNAKE_CASE,
+        AUTO_FLUSH_KEY_SNAKE_CASE,
+        AUTO_IMPORT_KEY_SNAKE_CASE,
+        AUTO_INCLUDE_KEY_SNAKE_CASE,
+        BOOLEAN_FORMAT_KEY_SNAKE_CASE,
+        CUSTOM_DATE_FORMATS_KEY_SNAKE_CASE,
+        CUSTOM_NUMBER_FORMATS_KEY_SNAKE_CASE,
+        DATE_FORMAT_KEY_SNAKE_CASE,
+        DATETIME_FORMAT_KEY_SNAKE_CASE,
+        LAZY_AUTO_IMPORTS_KEY_SNAKE_CASE,
+        LAZY_IMPORTS_KEY_SNAKE_CASE,
+        LOCALE_KEY_SNAKE_CASE,
+        LOG_TEMPLATE_EXCEPTIONS_KEY_SNAKE_CASE,
+        NEW_BUILTIN_CLASS_RESOLVER_KEY_SNAKE_CASE,
+        NUMBER_FORMAT_KEY_SNAKE_CASE,
+        OBJECT_WRAPPER_KEY_SNAKE_CASE,
+        OUTPUT_ENCODING_KEY_SNAKE_CASE,
+        SHOW_ERROR_TIPS_KEY_SNAKE_CASE,
+        SQL_DATE_AND_TIME_TIME_ZONE_KEY_SNAKE_CASE,
+        TEMPLATE_EXCEPTION_HANDLER_KEY_SNAKE_CASE,
+        TIME_FORMAT_KEY_SNAKE_CASE,
+        TIME_ZONE_KEY_SNAKE_CASE,
+        URL_ESCAPING_CHARSET_KEY_SNAKE_CASE
+    };
+    
+    private static final String[] SETTING_NAMES_CAMEL_CASE = new String[] {
+        // Must be sorted alphabetically!
+        API_BUILTIN_ENABLED_KEY_CAMEL_CASE,
+        ARITHMETIC_ENGINE_KEY_CAMEL_CASE,
+        AUTO_FLUSH_KEY_CAMEL_CASE,
+        AUTO_IMPORT_KEY_CAMEL_CASE,
+        AUTO_INCLUDE_KEY_CAMEL_CASE,
+        BOOLEAN_FORMAT_KEY_CAMEL_CASE,
+        CUSTOM_DATE_FORMATS_KEY_CAMEL_CASE,
+        CUSTOM_NUMBER_FORMATS_KEY_CAMEL_CASE,
+        DATE_FORMAT_KEY_CAMEL_CASE,
+        DATETIME_FORMAT_KEY_CAMEL_CASE,
+        LAZY_AUTO_IMPORTS_KEY_CAMEL_CASE,
+        LAZY_IMPORTS_KEY_CAMEL_CASE,
+        LOCALE_KEY_CAMEL_CASE,
+        LOG_TEMPLATE_EXCEPTIONS_KEY_CAMEL_CASE,
+        NEW_BUILTIN_CLASS_RESOLVER_KEY_CAMEL_CASE,
+        NUMBER_FORMAT_KEY_CAMEL_CASE,
+        OBJECT_WRAPPER_KEY_CAMEL_CASE,
+        OUTPUT_ENCODING_KEY_CAMEL_CASE,
+        SHOW_ERROR_TIPS_KEY_CAMEL_CASE,
+        SQL_DATE_AND_TIME_TIME_ZONE_KEY_CAMEL_CASE,
+        TEMPLATE_EXCEPTION_HANDLER_KEY_CAMEL_CASE,
+        TIME_FORMAT_KEY_CAMEL_CASE,
+        TIME_ZONE_KEY_CAMEL_CASE,
+        URL_ESCAPING_CHARSET_KEY_CAMEL_CASE
+    };
+
+    private Locale locale;
+    private String numberFormat;
+    private String timeFormat;
+    private String dateFormat;
+    private String dateTimeFormat;
+    private TimeZone timeZone;
+    private TimeZone sqlDateAndTimeTimeZone;
+    private boolean sqlDateAndTimeTimeZoneSet;
+    private String booleanFormat;
+    private TemplateExceptionHandler templateExceptionHandler;
+    private ArithmeticEngine arithmeticEngine;
+    private ObjectWrapper objectWrapper;
+    private Charset outputEncoding;
+    private boolean outputEncodingSet;
+    private Charset urlEscapingCharset;
+    private boolean urlEscapingCharsetSet;
+    private Boolean autoFlush;
+    private TemplateClassResolver newBuiltinClassResolver;
+    private Boolean showErrorTips;
+    private Boolean apiBuiltinEnabled;
+    private Boolean logTemplateExceptions;
+    private Map<String, TemplateDateFormatFactory> customDateFormats;
+    private Map<String, TemplateNumberFormatFactory> customNumberFormats;
+    private LinkedHashMap<String, String> autoImports;
+    private ArrayList<String> autoIncludes;
+    private Boolean lazyImports;
+    private Boolean lazyAutoImports;
+    private boolean lazyAutoImportsSet;
+    private Map<Object, Object> customAttributes;
+
+    /**
+     * Creates a new instance. Normally you do not need to use this constructor,
+     * as you don't use <code>MutableProcessingConfiguration</code> directly, but its subclasses.
+     */
+    protected MutableProcessingConfiguration() {
+        // Empty
+    }
+
+    @Override
+    public Locale getLocale() {
+         return isLocaleSet() ? locale : getDefaultLocale();
+    }
+
+    /**
+     * Returns the value the getter method returns when the setting is not set (possibly by inheriting the setting value
+     * from another {@link ProcessingConfiguration}), or throws {@link SettingValueNotSetException}.
+     */
+    protected abstract Locale getDefaultLocale();
+
+    @Override
+    public boolean isLocaleSet() {
+        return locale != null;
+    }
+
+    /**
+     * Setter pair of {@link ProcessingConfiguration#getLocale()}.
+     */
+    public void setLocale(Locale locale) {
+        _NullArgumentException.check("locale", locale);
+        this.locale = locale;
+    }
+
+    /**
+     * Fluent API equivalent of {@link #setLocale(Locale)}
+     */
+    public SelfT locale(Locale value) {
+        setLocale(value);
+        return self();
+    }
+
+    /**
+     * Resets the setting value as if it was never set (but it doesn't affect the value inherited from another
+     * {@link ProcessingConfiguration}).
+     */
+    public void unsetLocale() {
+        locale = null;
+    }
+
+    /**
+     * Setter pair of {@link ProcessingConfiguration#getTimeZone()}.
+     */
+    public void setTimeZone(TimeZone timeZone) {
+        _NullArgumentException.check("timeZone", timeZone);
+        this.timeZone = timeZone;
+    }
+
+    /**
+     * Fluent API equivalent of {@link #setTimeZone(TimeZone)}
+     */
+    public SelfT timeZone(TimeZone value) {
+        setTimeZone(value);
+        return self();
+    }
+    /**
+     * Resets the setting value as if it was never set (but it doesn't affect the value inherited from another
+     * {@link ProcessingConfiguration}).
+     */
+    public void unsetTimeZone() {
+        this.timeZone = null;
+    }
+
+    @Override
+    public TimeZone getTimeZone() {
+         return isTimeZoneSet() ? timeZone : getDefaultTimeZone();
+    }
+
+    /**
+     * Returns the value the getter method returns when the setting is not set (possibly by inheriting the setting value
+     * from another {@link ProcessingConfiguration}), or throws {@link SettingValueNotSetException}.
+     */
+    protected abstract TimeZone getDefaultTimeZone();
+
+    @Override
+    public boolean isTimeZoneSet() {
+        return timeZone != null;
+    }
+    
+    /**
+     * Setter pair of {@link ProcessingConfiguration#getSQLDateAndTimeTimeZone()}.
+     */
+    public void setSQLDateAndTimeTimeZone(TimeZone tz) {
+        sqlDateAndTimeTimeZone = tz;
+        sqlDateAndTimeTimeZoneSet = true;
+    }
+
+    /**
+     * Fluent API equivalent of {@link #setSQLDateAndTimeTimeZone(TimeZone)}
+     */
+    public SelfT sqlDateAndTimeTimeZone(TimeZone value) {
+        setSQLDateAndTimeTimeZone(value);
+        return self();
+    }
+
+    /**
+     * Resets the setting value as if it was never set (but it doesn't affect the value inherited from another
+     * {@link ProcessingConfiguration}).
+     */
+    public void unsetSQLDateAndTimeTimeZone() {
+        sqlDateAndTimeTimeZone = null;
+        sqlDateAndTimeTimeZoneSet = false;
+    }
+
+    @Override
+    public TimeZone getSQLDateAndTimeTimeZone() {
+        return sqlDateAndTimeTimeZoneSet
+                ? sqlDateAndTimeTimeZone
+                : getDefaultSQLDateAndTimeTimeZone();
+    }
+
+    /**
+     * Returns the value the getter method returns when the setting is not set (possibly by inheriting the setting value
+     * from another {@link ProcessingConfiguration}), or throws {@link SettingValueNotSetException}.
+     */
+    protected abstract TimeZone getDefaultSQLDateAndTimeTimeZone();
+
+    @Override
+    public boolean isSQLDateAndTimeTimeZoneSet() {
+        return sqlDateAndTimeTimeZoneSet;
+    }
+
+    /**
+     * Setter pair of {@link #getNumberFormat()}
+     */
+    public void setNumberFormat(String numberFormat) {
+        _NullArgumentException.check("numberFormat", numberFormat);
+        this.numberFormat = numberFormat;
+    }
+
+    /**
+     * Fluent API equivalent of {@link #setNumberFormat(String)}
+     */
+    public SelfT numberFormat(String value) {
+        setNumberFormat(value);
+        return self();
+    }
+
+    /**
+     * Resets the setting value as if it was never set (but it doesn't affect the value inherited from another
+     * {@link ProcessingConfiguration}).
+     */
+    public void unsetNumberFormat() {
+        numberFormat = null;
+    }
+
+    @Override
+    public String getNumberFormat() {
+         return isNumberFormatSet() ? numberFormat : getDefaultNumberFormat();
+    }
+
+    /**
+     * Returns the value the getter method returns when the setting is not set (possibly by inheriting the setting value
+     * from another {@link ProcessingConfiguration}), or throws {@link SettingValueNotSetException}.
+     */
+    protected abstract String getDefaultNumberFormat();
+
+    @Override
+    public boolean isNumberFormatSet() {
+        return numberFormat != null;
+    }
+    
+    @Override
+    public Map<String, TemplateNumberFormatFactory> getCustomNumberFormats() {
+         return isCustomNumberFormatsSet() ? customNumberFormats : getDefaultCustomNumberFormats();
+    }
+
+    protected abstract Map<String, TemplateNumberFormatFactory> getDefaultCustomNumberFormats();
+
+    /**
+     * Setter pair of {@link #getCustomNumberFormats()}. Note that custom number formats are get through
+     * {@link #getCustomNumberFormat(String)}, not directly though this {@link Map}, so number formats from
+     * {@link ProcessingConfiguration}-s on less specific levels are inherited without you copying them into this
+     * {@link Map}.
+     *
+     * @param customNumberFormats
+     *      Not {@code null}.
+     */
+    public void setCustomNumberFormats(Map<String, TemplateNumberFormatFactory> customNumberFormats) {
+        _NullArgumentException.check("customNumberFormats", customNumberFormats);
+        validateFormatNames(customNumberFormats.keySet());
+        this.customNumberFormats = customNumberFormats;
+    }
+
+    /**
+     * Fluent API equivalent of {@link #setCustomNumberFormats(Map)}
+     */
+    public SelfT customNumberFormats(Map<String, TemplateNumberFormatFactory> value) {
+        setCustomNumberFormats(value);
+        return self();
+    }
+
+    /**
+     * Resets the setting value as if it was never set (but it doesn't affect the value inherited from another
+     * {@link ProcessingConfiguration}).
+     */
+    public void unsetCustomNumberFormats() {
+        customNumberFormats = null;
+    }
+
+    private void validateFormatNames(Set<String> keySet) {
+        for (String name : keySet) {
+            if (name.length() == 0) {
+                throw new IllegalArgumentException("Format names can't be 0 length");
+            }
+            char firstChar = name.charAt(0);
+            if (firstChar == '@') {
+                throw new IllegalArgumentException(
+                        "Format names can't start with '@'. '@' is only used when referring to them from format "
+                        + "strings. In: " + name);
+            }
+            if (!Character.isLetter(firstChar)) {
+                throw new IllegalArgumentException("Format name must start with letter: " + name);
+            }
+            for (int i = 1; i < name.length(); i++) {
+                // Note that we deliberately don't allow "_" here.
+                if (!Character.isLetterOrDigit(name.charAt(i))) {
+                    throw new IllegalArgumentException("Format name can only contain letters and digits: " + name);
+                }
+            }
+        }
+    }
+
+    @Override
+    public boolean isCustomNumberFormatsSet() {
+        return customNumberFormats != null;
+    }
+
+    @Override
+    public TemplateNumberFormatFactory getCustomNumberFormat(String name) {
+        TemplateNumberFormatFactory r;
+        if (customNumberFormats != null) {
+            r = customNumberFormats.get(name);
+            if (r != null) {
+                return r;
+            }
+        }
+        return getDefaultCustomNumberFormat(name);
+    }
+
+    protected abstract TemplateNumberFormatFactory getDefaultCustomNumberFormat(String name);
+
+    /**
+     * Setter pair of {@link #getBooleanFormat()}.
+     */
+    public void setBooleanFormat(String booleanFormat) {
+        _NullArgumentException.check("booleanFormat", booleanFormat);
+        
+        int commaIdx = booleanFormat.indexOf(',');
+        if (commaIdx == -1) {
+            throw new IllegalArgumentException(
+                    "Setting value must be string that contains two comma-separated values for true and false, " +
+                    "respectively.");
+        }
+        
+        this.booleanFormat = booleanFormat; 
+    }
+
+    /**
+     * Fluent API equivalent of {@link #setBooleanFormat(String)}
+     */
+    public SelfT booleanFormat(String value) {
+        setBooleanFormat(value);
+        return self();
+    }
+
+    /**
+     * Resets the setting value as if it was never set (but it doesn't affect the value inherited from another
+     * {@link ProcessingConfiguration}).
+     */
+    public void unsetBooleanFormat() {
+        booleanFormat = null;
+    }
+    
+    @Override
+    public String getBooleanFormat() {
+         return isBooleanFormatSet() ? booleanFormat : getDefaultBooleanFormat();
+    }
+
+    /**
+     * Returns the value the getter method returns when the setting is not set (possibly by inheriting the setting value
+     * from another {@link ProcessingConfiguration}), or throws {@link SettingValueNotSetException}.
+     */
+    protected abstract String getDefaultBooleanFormat();
+
+    @Override
+    public boolean isBooleanFormatSet() {
+        return booleanFormat != null;
+    }
+
+    /**
+     * Setter pair of {@link #getTimeFormat()}
+     */
+    public void setTimeFormat(String timeFormat) {
+        _NullArgumentException.check("timeFormat", timeFormat);
+        this.timeFormat = timeFormat;
+    }
+
+    /**
+     * Fluent API equivalent of {@link #setTimeFormat(String)}
+     */
+    public SelfT timeFormat(String value) {
+        setTimeFormat(value);
+        return self();
+    }
+
+    /**
+     * Resets the setting value as if it was never set (but it doesn't affect the value inherited from another
+     * {@link ProcessingConfiguration}).
+     */
+    public void unsetTimeFormat() {
+        timeFormat = null;
+    }
+
+    @Override
+    public String getTimeFormat() {
+         return isTimeFormatSet() ? timeFormat : getDefaultTimeFormat();
+    }
+
+    /**
+     * Returns the value the getter method returns when the setting is not set (possibly by inheriting the setting value
+     * from another {@link ProcessingConfiguration}), or throws {@link SettingValueNotSetException}.
+     */
+    protected abstract String getDefaultTimeFormat();
+
+    @Override
+    public boolean isTimeFormatSet() {
+        return timeFormat != null;
+    }
+
+    /**
+     * Setter pair of {@link #getDateFormat()}.
+     */
+    public void setDateFormat(String dateFormat) {
+        _NullArgumentException.check("dateFormat", dateFormat);
+        this.dateFormat = dateFormat;
+    }
+
+    /**
+     * Fluent API equivalent of {@link #setDateFormat(String)}
+     */
+    public SelfT dateFormat(String value) {
+        setDateFormat(value);
+        return self();
+    }
+
+    /**
+     * Resets the setting value as if it was never set (but it doesn't affect the value inherited from another
+     * {@link ProcessingConfiguration}).
+     */
+    public void unsetDateFormat() {
+        dateFormat = null;
+    }
+
+    @Override
+    public String getDateFormat() {
+         return isDateFormatSet() ? dateFormat : getDefaultDateFormat();
+    }
+
+    /**
+     * Returns the value the getter method returns when the setting is not set (possibly by inheriting the setting value
+     * from another {@link ProcessingConfiguration}), or throws {@link SettingValueNotSetException}.
+     */
+    protected abstract String getDefaultDateFormat();
+
+    @Override
+    public boolean isDateFormatSet() {
+        return dateFormat != null;
+    }
+
+    /**
+     * Setter pair of {@link #getDateTimeFormat()}
+     */
+    public void setDateTimeFormat(String dateTimeFormat) {
+        _NullArgumentException.check("dateTimeFormat", dateTimeFormat);
+        this.dateTimeFormat = dateTimeFormat;
+    }
+
+    /**
+     * Fluent API equivalent of {@link #setDateTimeFormat(String)}
+     */
+    public SelfT dateTimeFormat(String value) {
+        setDateTimeFormat(value);
+        return self();
+    }
+
+    /**
+     * Resets the setting value as if it was never set (but it doesn't affect the value inherited from another
+     * {@link ProcessingConfiguration}).
+     */
+    public void unsetDateTimeFormat() {
+        this.dateTimeFormat = null;
+    }
+
+    @Override
+    public String getDateTimeFormat() {
+         return isDateTimeFormatSet() ? dateTimeFormat : getDefaultDateTimeFormat();
+    }
+
+    /**
+     * Returns the value the getter method returns when the setting is not set (possibly by inheriting the setting value
+     * from another {@link ProcessingConfiguration}), or throws {@link SettingValueNotSetException}.
+     */
+    protected abstract String getDefaultDateTimeFormat();
+
+    @Override
+    public boolean isDateTimeFormatSet() {
+        return dateTimeFormat != null;
+    }
+
+    @Override
+    public Map<String, TemplateDateFormatFactory> getCustomDateFormats() {
+         return isCustomDateFormatsSet() ? customDateFormats : getDefaultCustomDateFormats();
+    }
+
+    protected abstract Map<String, TemplateDateFormatFactory> getDefaultCustomDateFormats();
+
+    /**
+     * Setter pair of {@link #getCustomDateFormat(String)}.
+     */
+    public void setCustomDateFormats(Map<String, TemplateDateFormatFactory> customDateFormats) {
+        _NullArgumentException.check("customDateFormats", customDateFormats);
+        validateFormatNames(customDateFormats.keySet());
+        this.customDateFormats = customDateFormats;
+    }
+
+    /**
+     * Fluent API equivalent of {@link #setCustomDateFormats(Map)}
+     */
+    public SelfT customDateFormats(Map<String, TemplateDateFormatFactory> value) {
+        setCustomDateFormats(value);
+        return self();
+    }
+
+    /**
+     * Resets the setting value as if it was never set (but it doesn't affect the value inherited from another
+     * {@link ProcessingConfiguration}).
+     */
+    public void unsetCustomDateFormats() {
+        this.customDateFormats = null;
+    }
+
+    @Override
+    public boolean isCustomDateFormatsSet() {
+        return customDateFormats != null;
+    }
+
+    @Override
+    public TemplateDateFormatFactory getCustomDateFormat(String name) {
+        TemplateDateFormatFactory r;
+        if (customDateFormats != null) {
+            r = customDateFormats.get(name);
+            if (r != null) {
+                return r;
+            }
+        }
+        return getDefaultCustomDateFormat(name);
+    }
+
+    protected abstract TemplateDateFormatFactory getDefaultCustomDateFormat(String name);
+
+    /**
+     * Setter pair of {@link #getTemplateExceptionHandler()}
+     */
+    public void setTemplateExceptionHandler(TemplateExceptionHandler templateExceptionHandler) {
+        _NullArgumentException.check("templateExceptionHandler", templateExceptionHandler);
+        this.templateExceptionHandler = templateExceptionHandler;
+    }
+
+    /**
+     * Fluent API equivalent of {@link #setTemplateExceptionHandler(TemplateExceptionHandler)}
+     */
+    public SelfT templateExceptionHandler(TemplateExceptionHandler value) {
+        setTemplateExceptionHandler(value);
+        return self();
+    }
+
+    /**
+     * Resets the setting value as if it was never set (but it doesn't affect the value inherited from another
+     * {@link ProcessingConfiguration}).
+     */
+    public void unsetTemplateExceptionHandler() {
+        templateExceptionHandler = null;
+    }
+
+    @Override
+    public TemplateExceptionHandler getTemplateExceptionHandler() {
+         return isTemplateExceptionHandlerSet()
+                ? templateExceptionHandler : getDefaultTemplateExceptionHandler();
+    }
+
+    /**
+     * Returns the value the getter method returns when the setting is not set (possibly by inheriting the setting value
+     * from another {@link ProcessingConfiguration}), or throws {@link SettingValueNotSetException}.
+     */
+    protected abstract TemplateExceptionHandler getDefaultTemplateExceptionHandler();
+
+    @Override
+    public boolean isTemplateExceptionHandlerSet() {
+        return templateExceptionHandler != null;
+    }
+
+    /**
+     * Setter pair of {@link #getArithmeticEngine()}
+     */
+    public void setArithmeticEngine(ArithmeticEngine arithmeticEngine) {
+        _NullArgumentException.check("arithmeticEngine", arithmeticEngine);
+        this.arithmeticEngine = arithmeticEngine;
+    }
+
+    /**
+     * Fluent API equivalent of {@link #setArithmeticEngine(ArithmeticEngine)}
+     */
+    public SelfT arithmeticEngine(ArithmeticEngine value) {
+        setArithmeticEngine(value);
+        return self();
+    }
+
+    /**
+     * Resets the setting value as if it was never set (but it doesn't affect the value inherited from another
+     * {@link ProcessingConfiguration}).
+     */
+    public void unsetArithmeticEngine() {
+        this.arithmeticEngine = null;
+    }
+
+    @Override
+    public ArithmeticEngine getArithmeticEngine() {
+         return isArithmeticEngineSet() ? arithmeticEngine : getDefaultArithmeticEngine();
+    }
+
+    /**
+     * Returns the value the getter method returns when the setting is not set (possibly by inheriting the setting value
+     * from another {@link ProcessingConfiguration}), or throws {@link SettingValueNotSetException}.
+     */
+    protected abstract ArithmeticEngine getDefaultArithmeticEngine();
+
+    @Override
+    public boolean isArithmeticEngineSet() {
+        return arithmeticEngine != null;
+    }
+
+    /**
+     * Setter pair of {@link #getObjectWrapper()}
+     */
+    public void setObjectWrapper(ObjectWrapper objectWrapper) {
+        _NullArgumentException.check("objectWrapper", objectWrapper);
+        this.objectWrapper = objectWrapper;
+    }
+
+    /**
+     * Resets the setting value as if it was never set (but it doesn't affect the value inherited from another
+     * {@link ProcessingConfiguration}).
+     */
+    public void unsetObjectWrapper() {
+        objectWrapper = null;
+    }
+
+    /**
+     * Fluent API equivalent of {@link #setObjectWrapper(ObjectWrapper)}
+     */
+    public SelfT objectWrapper(ObjectWrapper value) {
+        setObjectWrapper(value);
+        return self();
+    }
+
+    @Override
+    public ObjectWrapper getObjectWrapper() {
+         return isObjectWrapperSet()
+                ? objectWrapper : getDefaultObjectWrapper();
+    }
+
+    /**
+     * Returns the value the getter method returns when the setting is not set (possibly by inheriting the setting value
+     * from another {@link ProcessingConfiguration}), or throws {@link SettingValueNotSetException}.
+     */
+    protected abstract ObjectWrapper getDefaultObjectWrapper();
+
+    @Override
+    public boolean isObjectWrapperSet() {
+        return objectWrapper != null;
+    }
+
+    /**
+     * The setter pair of {@link #getOutputEncoding()}
+     */
+    public void setOutputEncoding(Charset outputEncoding) {
+        this.outputEncoding = outputEncoding;
+        outputEncodingSet = true;
+    }
+
+    /**
+     * Fluent API equivalent of {@link #setOutputEncoding(Charset)}
+     */
+    public SelfT outputEncoding(Charset value) {
+        setOutputEncoding(value);
+        return self();
+    }
+
+    /**
+     * Resets the setting value as if it was never set (but it doesn't affect the value inherited from another
+     * {@link ProcessingConfiguration}).
+     */
+    public void unsetOutputEncoding() {
+        this.outputEncoding = null;
+        outputEncodingSet = false;
+    }
+
+    @Override
+    public Charset getOutputEncoding() {
+        return isOutputEncodingSet()
+                ? outputEncoding
+                : getDefaultOutputEncoding();
+    }
+
+    /**
+     * Returns the value the getter method returns when the setting is not set (possibly by inheriting the setting value
+     * from another {@link ProcessingConfiguration}), or throws {@link SettingValueNotSetException}.
+     */
+    protected abstract Charset getDefaultOutputEncoding();
+
+    @Override
+    public boolean isOutputEncodingSet() {
+        return outputEncodingSet;
+    }
+
+    /**
+     * The setter pair of {@link #getURLEscapingCharset()}.
+     */
+    public void setURLEscapingCharset(Charset urlEscapingCharset) {
+        this.urlEscapingCharset = urlEscapingCharset;
+        urlEscapingCharsetSet = true;
+    }
+
+    /**
+     * Fluent API equivalent of {@link #setURLEscapingCharset(Charset)}
+     */
+    public SelfT urlEscapingCharset(Charset value) {
+        setURLEscapingCharset(value);
+        return self();
+    }
+
+    /**
+     * Resets the setting value as if it was never set (but it doesn't affect the value inherited from another
+     * {@link ProcessingConfiguration}).
+     */
+    public void unsetURLEscapingCharset() {
+        this.urlEscapingCharset = null;
+        urlEscapingCharsetSet = false;
+    }
+
+    @Override
+    public Charset getURLEscapingCharset() {
+        return isURLEscapingCharsetSet() ? urlEscapingCharset : getDefaultURLEscapingCharset();
+    }
+
+    /**
+     * Returns the value the getter method returns when the setting is not set (possibly by inheriting the setting value
+     * from another {@link ProcessingConfiguration}), or throws {@link SettingValueNotSetException}.
+     */
+    protected abstract Charset getDefaultURLEscapingCharset();
+
+    @Override
+    public boolean isURLEscapingCharsetSet() {
+        return urlEscapingCharsetSet;
+    }
+
+    /**
+     * Setter pair of {@link #getNewBuiltinClassResolver()}
+     */
+    public void setNewBuiltinClassResolver(TemplateClassResolver newBuiltinClassResolver) {
+        _NullArgumentException.check("newBuiltinClassResolver", newBuiltinClassResolver);
+        this.newBuiltinClassResolver = newBuiltinClassResolver;
+    }
+
+    /**
+     * Fluent API equivalent of {@link #setNewBuiltinClassResolver(TemplateClassResolver)}
+     */
+    public SelfT newBuiltinClassResolver(TemplateClassResolver value) {
+        setNewBuiltinClassResolver(value);
+        return self();
+    }
+
+    /**
+     * Resets the setting value as if it was never set (but it doesn't affect the value inherited from another
+     * {@link ProcessingConfiguration}).
+     */
+    public void unsetNewBuiltinClassResolver() {
+        this.newBuiltinClassResolver = null;
+    }
+
+    @Override
+    public TemplateClassResolver getNewBuiltinClassResolver() {
+         return isNewBuiltinClassResolverSet()
+                ? newBuiltinClassResolver : getDefaultNewBuiltinClassResolver();
+    }
+
+    /**
+     * Returns the value the getter method returns when the setting is not set (possibly by inheriting the setting value
+     * from another {@link ProcessingConfiguration}), or throws {@link SettingValueNotSetException}.
+     */
+    protected abstract TemplateClassResolver getDefaultNewBuiltinClassResolver();
+
+    @Override
+    public boolean isNewBuiltinClassResolverSet() {
+        return newBuiltinClassResolver != null;
+    }
+
+    /**
+     * Setter pair of {@link #getAutoFlush()}
+     */
+    public void setAutoFlush(boolean autoFlush) {
+        this.autoFlush = autoFlush;
+    }
+
+    /**
+     * Fluent API equivalent of {@link #setAutoFlush(boolean)}
+     */
+    public SelfT autoFlush(boolean value) {
+        setAutoFlush(value);
+        return self();
+    }
+
+    /**
+     * Resets the setting value as if it was never set (but it doesn't affect the value inherited from another
+     * {@link ProcessingConfiguration}).
+     */
+    public void unsetAutoFlush() {
+        this.autoFlush = null;
+    }
+
+    @Override
+    public boolean getAutoFlush() {
+         return isAutoFlushSet() ? autoFlush : getDefaultAutoFlush();
+    }
+
+    /**
+     * Returns the value the getter method returns when the setting is not set (possibly by inheriting the setting value
+     * from another {@link ProcessingConfiguration}), or throws {@link SettingValueNotSetException}.
+     */
+    protected abstract boolean getDefaultAutoFlush();
+
+    @Override
+    public boolean isAutoFlushSet() {
+        return autoFlush != null;
+    }
+
+    /**
+     * Setter pair of {@link #getShowErrorTips()}
+     */
+    public void setShowErrorTips(boolean showTips) {
+        showErrorTips = showTips;
+    }
+
+    /**
+     * Fluent API equivalent of {@link #setShowErrorTips(boolean)}
+     */
+    public SelfT showErrorTips(boolean value) {
+        setShowErrorTips(value);
+        return self();
+    }
+
+    /**
+     * Resets the setting value as if it was never set (but it doesn't affect the value inherited from another
+     * {@link ProcessingConfiguration}).
+     */
+    public void unsetShowErrorTips() {
+        showErrorTips = null;
+    }
+
+    @Override
+    public boolean getShowErrorTips() {
+         return isShowErrorTipsSet() ? showErrorTips : getDefaultShowErrorTips();
+    }
+
+    /**
+     * Returns the value the getter method returns when the setting is not set (possibly by inheriting the setting value
+     * from another {@link ProcessingConfiguration}), or throws {@link SettingValueNotSetException}.
+     */
+    protected abstract boolean getDefaultShowErrorTips();
+
+    @Override
+    public boolean isShowErrorTipsSet() {
+        return showErrorTips != null;
+    }
+
+    /**
+     * Setter pair of {@link #getAPIBuiltinEnabled()}
+     */
+    public void setAPIBuiltinEnabled(boolean value) {
+        apiBuiltinEnabled = Boolean.valueOf(value);
+    }
+
+    /**
+     * Fluent API equivalent of {@link #setAPIBuiltinEnabled(boolean)}
+     */
+    public SelfT apiBuiltinEnabled(boolean value) {
+        setAPIBuiltinEnabled(value);
+        return self();
+    }
+
+    /**
+     * Resets the setting value as if it was never set (but it doesn't affect the value inherited from another
+     * {@link ProcessingConfiguration}).
+     */
+    public void unsetAPIBuiltinEnabled() {
+        apiBuiltinEnabled = null;
+    }
+
+    @Override
+    public boolean getAPIBuiltinEnabled() {
+         return isAPIBuiltinEnabledSet() ? apiBuiltinEnabled : getDefaultAPIBuiltinEnabled();
+    }
+
+    /**
+     * Returns the value the getter method returns when the setting is not set (possibly by inheriting the setting value
+     * from another {@link ProcessingConfiguration}), or throws {@link SettingValueNotSetException}.
+     */
+    protected abstract boolean getDefaultAPIBuiltinEnabled();
+
+    @Override
+    public boolean isAPIBuiltinEnabledSet() {
+        return apiBuiltinEnabled != null;
+    }
+
+    @Override
+    public boolean getLogTemplateExceptions() {
+         return isLogTemplateExceptionsSet() ? logTemplateExceptions : getDefaultLogTemplateExceptions();
+    }
+
+    /**
+     * Returns the value the getter method returns when the setting is not set (possibly by inheriting the setting value
+     * from another {@link ProcessingConfiguration}), or throws {@link SettingValueNotSetException}.
+     */
+    protected abstract boolean getDefaultLogTemplateExceptions();
+
+    @Override
+    public boolean isLogTemplateExceptionsSet() {
+        return logTemplateExceptions != null;
+    }
+
+    /**
+     * Setter pair of {@link #getLogTemplateExceptions()}
+     */
+    public void setLogTemplateExceptions(boolean value) {
+        logTemplateExceptions = value;
+    }
+
+    /**
+     * Fluent API equivalent of {@link #setLogTemplateExceptions(boolean)}
+     */
+    public SelfT logTemplateExceptions(boolean value) {
+        setLogTemplateExceptions(value);
+        return self();
+    }
+
+    /**
+     * Resets the setting value as if it was never set (but it doesn't affect the value inherited from another
+     * {@link ProcessingConfiguration}).
+     */
+    public void unsetLogTemplateExceptions() {
+        logTemplateExceptions = null;
+    }
+
+    @Override
+    public boolean getLazyImports() {
+         return isLazyImportsSet() ? lazyImports : getDefaultLazyImports();
+    }
+
+    /**
+     * Returns the value the getter method returns when the setting is not set (possibly by inheriting the setting value
+     * from another {@link ProcessingConfiguration}), or throws {@link SettingValueNotSetException}.
+     */
+    protected abstract boolean getDefaultLazyImports();
+
+    /**
+     * Setter pair of {@link #getLazyImports()}
+     */
+    public void setLazyImports(boolean lazyImports) {
+        this.lazyImports = lazyImports;
+    }
+
+    /**
+     * Fluent API equivalent of {@link #setLazyImports(boolean)}
+     */
+    public SelfT lazyImports(boolean lazyImports) {
+        setLazyImports(lazyImports);
+        return  self();
+    }
+
+    /**
+     * Resets the setting value as if it was never set (but it doesn't affect the value inherited from another
+     * {@link ProcessingConfiguration}).
+     */
+    public void unsetLazyImports() {
+        this.lazyImports = null;
+    }
+
+    @Override
+    public boolean isLazyImportsSet() {
+        return lazyImports != null;
+    }
+
+    @Override
+    public Boolean getLazyAutoImports() {
+        return isLazyAutoImportsSet() ? lazyAutoImports : getDefaultLazyAutoImports();
+    }
+
+    /**
+     * Returns the value the getter method returns when the setting is not set (possibly by inheriting the setting value
+     * from another {@link ProcessingConfiguration}), or throws {@link SettingValueNotSetException}.
+     */
+    protected abstract Boolean getDefaultLazyAutoImports();
+
+    /**
+     * Setter pair of {@link #getLazyAutoImports()}
+     */
+    public void setLazyAutoImports(Boolean lazyAutoImports) {
+        this.lazyAutoImports = lazyAutoImports;
+        lazyAutoImportsSet = true;
+    }
+
+    /**
+     * Fluent API equivalent of {@link #setLazyAutoImports(Boolean)}
+     */
+    public SelfT lazyAutoImports(Boolean lazyAutoImports) {
+        setLazyAutoImports(lazyAutoImports);
+        return self();
+    }
+
+    /**
+     * Resets the setting value as if it was never set (but it doesn't affect the value inherited from another
+     * {@link ProcessingConfiguration}).
+     */
+    public void unsetLazyAutoImports() {
+        lazyAutoImports = null;
+        lazyAutoImportsSet = false;
+    }
+
+    @Override
+    public boolean isLazyAutoImportsSet() {
+        return lazyAutoImportsSet;
+    }
+    
+    /**
+     * Adds the auto-import at the end of {@link #getAutoImports()}. If an auto-import with the same namespace variable
+     * name already exists in the {@link Map}, it will be removed before the new one is added.
+     */
+    public void addAutoImport(String namespaceVarName, String templateName) {
+        if (autoImports == null) {
+            initAutoImportsMap();
+        } else {
+            // This was a List earlier, so re-inserted items must go to the end, hence we remove() before put().
+            autoImports.remove(namespaceVarName);
+        }
+        autoImports.put(namespaceVarName, templateName);
+    }
+
+    private void initAutoImportsMap() {
+        autoImports = new LinkedHashMap<>(4);
+    }
+    
+    /**
+     * Removes an auto-import from {@link #getAutoImports()} (but doesn't affect auto-imports inherited from another
+     * {@link ParsingConfiguration}). Does nothing if the auto-import doesn't exist.
+     */
+    public void removeAutoImport(String namespaceVarName) {
+        if (autoImports != null) {
+            autoImports.remove(namespaceVarName);
+        }
+    }
+    
+    /**
+     * Setter pair of {@link #getAutoImports()}.
+     * 
+     * @param map
+     *            Maps the namespace variable names to the template names; not {@code null}. The content of the
+     *            {@link Map} is copied into another {@link Map}, to avoid aliasing problems.
+     */
+    public void setAutoImports(Map map) {
+        _NullArgumentException.check("map", map);
+        
+        if (autoImports != null) {
+            autoImports.clear();
+        }
+        for (Map.Entry<?, ?> entry : ((Map<?, ?>) map).entrySet()) {
+            Object key = entry.getKey();
+            if (!(key instanceof String)) {
+                throw new IllegalArgumentException(
+                        "Key in Map wasn't a String, but a(n) " + key.getClass().getName() + ".");
+            }
+
+            Object value = entry.getValue();
+            if (!(value instanceof String)) {
+                throw new IllegalArgumentException(
+                        "Value in Map wasn't a String, but a(n) " + value.getClass().getName() + ".");
+            }
+
+            addAutoImport((String) key, (String) value);
+        }
+    }
+
+    /**
+     * Fluent API equivalent of {@link #setAutoImports(Map)}
+     */
+    public SelfT autoImports(Map map) {
+        setAutoImports(map);
+        return self();
+    }
+
+     /**
+     * Resets the setting value as if it was never set (but it doesn't affect the value inherited from another
+     * {@link ProcessingConfiguration}).
+     */
+    public void unsetAutoImports() {
+        autoImports = null;
+    }
+
+    @Override
+    public Map<String, String> getAutoImports() {
+         return isAutoImportsSet() ? autoImports : getDefaultAutoImports();
+    }
+
+    /**
+     * Returns the value the getter method returns when the setting is not set (possibly by inheriting the setting value
+     * from another {@link ProcessingConfiguration}), or throws {@link SettingValueNotSetException}.
+     */
+    protected abstract Map<String,String> getDefaultAutoImports();
+
+    @Override
+    public boolean isAutoImportsSet() {
+        return autoImports != null;
+    }
+
+    /**
+     * Adds an auto-include to {@link #getAutoIncludes()}. If the template name is already in the {@link List}, then it
+     * will be removed before it's added again (so in effect it's moved to the end of the {@link List}).
+     */
+    public void addAutoInclude(String templateName) {
+        if (autoIncludes == null) {
+            initAutoIncludesList();
+        } else {
+            autoIncludes.remove(templateName);
+        }
+        autoIncludes.add(templateName);
+    }
+
+    private void initAutoIncludesList() {
+        autoIncludes = new ArrayList<>(4);
+    }
+    
+    /**
+     * Setter pair of {@link #getAutoIncludes()}
+     *
+     * @param templateNames Not {@code null}. The {@link List} will be copied to avoid aliasing problems.
+     */
+    public void setAutoIncludes(List templateNames) {
+        _NullArgumentException.check("templateNames", templateNames);
+        if (autoIncludes != null) {
+            autoIncludes.clear();
+        }
+        for (Object templateName : templateNames) {
+            if (!(templateName instanceof String)) {
+                throw new IllegalArgumentException("List items must be String-s.");
+            }
+            addAutoInclude((String) templateName);
+        }
+    }
+
+    /**
+     * Fluent API equivalent of {@link #setAutoIncludes(List)}
+     */
+    public SelfT autoIncludes(List templateNames) {
+        setAutoIncludes(templateNames);
+        return self();
+    }
+
+    @Override
+    public List<String> getAutoIncludes() {
+         return isAutoIncludesSet() ? autoIncludes : getDefaultAutoIncludes();
+    }
+
+    /**
+     * Returns the value the getter method returns when the setting is not set (possibly by inheriting the setting value
+     * from another {@link ProcessingConfiguration}), or throws {@link SettingValueNotSetException}.
+     */
+    protected abstract List<String> getDefaultAutoIncludes();
+
+    @Override
+    public boolean isAutoIncludesSet() {
+        return autoIncludes != null;
+    }
+    
+    /**
+     * Removes the auto-include from the {@link #getAutoIncludes()} {@link List} (but it doesn't affect the
+     * {@link List}-s inherited from other {@link ProcessingConfiguration}-s). Does nothing if the template is not
+     * in the {@link List}.
+     */
+    public void removeAutoInclude(String templateName) {
+        // "synchronized" is removed from the API as it's not safe to set anything after publishing the Configuration
+        synchronized (this) {
+            if (autoIncludes != null) {
+                autoIncludes.remove(templateName);
+            }
+        }
+    }
+    
+    private static final String ALLOWED_CLASSES = "allowed_classes";
+    private static final String TRUSTED_TEMPLATES = "trusted_templates";
+    
+    /**
+     * Sets a FreeMarker setting by a name and string value. If you can configure FreeMarker directly with Java (or
+     * other programming language), you should use the dedicated setter methods instead (like
+     * {@link #setObjectWrapper(ObjectWrapper)}. This meant to be used only when you get settings from somewhere
+     * as {@link String}-{@link String} name-value pairs (typically, as a {@link Properties} object). Below you find an
+     * overview of the settings available.
+     * 
+     * <p>Note: As of FreeMarker 2.3.23, setting names can be written in camel case too. For example, instead of
+     * {@code date_format} you can also use {@code dateFormat}. It's likely that camel case will become to the
+     * recommended convention in the future.
+     * 
+     * <p>The list of settings commonly supported in all {@link MutableProcessingConfiguration} subclasses:
+     * <ul>
+     *   <li><p>{@code "locale"}:
+     *       See {@link #setLocale(Locale)}.
+     *       <br>String value: local codes with the usual format in Java, such as {@code "en_US"}, or
+     *       "JVM default" (ignoring case) to use the default locale of the Java environment.
+     *
+     *   <li><p>{@code "custom_number_formats"}: See {@link #setCustomNumberFormats(Map)}.
+     *   <br>String value: Interpreted as an <a href="#fm_obe">object builder expression</a>.
+     *   <br>Example: <code>{ "hex": com.example.HexTemplateNumberFormatFactory,
+     *   "gps": com.example.GPSTemplateNumberFormatFactory }</code>
+     *
+     *   <li><p>{@code "custom_date_formats"}: See {@link #setCustomDateFormats(Map)}.
+     *   <br>String value: Interpreted as an <a href="#fm_obe">object builder expression</a>.
+     *   <br>Example: <code>{ "trade": com.example.TradeTemplateDateFormatFactory,
+     *   "log": com.example.LogTemplateDateFormatFactory }</code>
+     *       
+     *   <li><p>{@code "template_exception_handler"}:
+     *       See {@link #setTemplateExceptionHandler(TemplateExceptionHandler)}.
+     *       <br>String value: If the value contains dot, then it's interpreted as an <a href="#fm_obe">object builder
+     *       expression</a>.
+     *       If the value does not contain dot, then it must be one of these predefined values (case insensitive):
+     *       {@code "rethrow"} (means {@link TemplateExceptionHandler#RETHROW_HANDLER}),
+     *       {@code "debug"} (means {@link TemplateExceptionHandler#DEBUG_HANDLER}),
+     *       {@code "html_debug"} (means {@link TemplateExceptionHandler#HTML_DEBUG_HANDLER}),
+     *       {@code "ignore"} (means {@link TemplateExceptionHandler#IGNORE_HANDLER}),
+     *       {@code "default"} (only allowed for {@link Configuration} instances) for the default.
+     *       
+     *   <li><p>{@code "arithmetic_engine"}:
+     *       See {@link #setArithmeticEngine(ArithmeticEngine)}.  
+     *       <br>String value: If the value contains dot, then it's interpreted as an <a href="#fm_obe">object builder
+     *       expression</a>.
+     *       If the value does not contain dot,
+     *       then it must be one of these special values (case insensitive):
+     *       {@code "bigdecimal"}, {@code "conservative"}.
+     *       
+     *   <li><p>{@code "object_wrapper"}:
+     *       See {@link #setObjectWrapper(ObjectWrapper)}.
+     *       <br>String value: If the value contains dot, then it's interpreted as an <a href="#fm_obe">object builder
+     *       expression</a>, with the addition that {@link DefaultObjectWrapper}, {@link DefaultObjectWrapper} and
+     *       {@link RestrictedObjectWrapper} can be referred without package name. For example, these strings are valid
+     *       values: {@code "DefaultObjectWrapper(3.0.0)"},
+     *       {@code "DefaultObjectWrapper(2.3.21, simpleMapWrapper=true)"}.
+     *       <br>If the value does not contain dot, then it must be one of these special values (case insensitive):
+     *       {@code "default"} means the default of {@link Configuration},
+     *       {@code "restricted"} means the a {@link RestrictedObjectWrapper} instance.
+     *
+     *   <li><p>{@code "number_format"}: See {@link #setNumberFormat(String)}.
+     *   
+     *   <li><p>{@code "boolean_format"}: See {@link #setBooleanFormat(String)} .
+     *   
+     *   <li><p>{@code "date_format", "time_format", "datetime_format"}:
+     *       See {@link #setDateFormat(String)}, {@link #setTimeFormat(String)}, {@link #setDateTimeFormat(String)}. 
+     *        
+     *   <li><p>{@code "time_zone"}:
+     *       See {@link #setTimeZone(TimeZone)}.
+     *       <br>String value: With the format as {@link TimeZone#getTimeZone} defines it. Also, since 2.3.21
+     *       {@code "JVM default"} can be used that will be replaced with the actual JVM default time zone when
+     *       {@link #setSetting(String, String)} is called.
+     *       For example {@code "GMT-8:00"} or {@code "America/Los_Angeles"}
+     *       <br>If you set this setting, consider setting {@code sql_date_and_time_time_zone}
+     *       too (see below)! 
+     *       
+     *   <li><p>{@code sql_date_and_time_time_zone}:
+     *       See {@link #setSQLDateAndTimeTimeZone(TimeZone)}.
+     *       Since 2.3.21.
+     *       <br>String value: With the format as {@link TimeZone#getTimeZone} defines it. Also, {@code "JVM default"}
+     *       can be used that will be replaced with the actual JVM default time zone when
+     *       {@link #setSetting(String, String)} is called. Also {@code "null"} can be used, which has the same effect
+     *       as {@link #setSQLDateAndTimeTimeZone(TimeZone) setSQLDateAndTimeTimeZone(null)}.
+     *       
+     *   <li><p>{@code "output_encoding"}:
+     *       See {@link #setOutputEncoding(Charset)}.
+     *       
+     *   <li><p>{@code "url_escaping_charset"}:
+     *       See {@link #setURLEscapingCharset(Charset)}.
+     *       
+     *   <li><p>{@code "auto_flush"}:
+     *       See {@link #setAutoFlush(boolean)}.
+     *       Since 2.3.17.
+     *       <br>String value: {@code "true"}, {@code "false"}, {@code "y"},  etc.
+     *       
+     *   <li><p>{@code "auto_import"}:
+     *       See {@link Configuration#getAutoImports()}
+     *       <br>String value is something like:
+     *       <br>{@code /lib/form.ftl as f, /lib/widget as w, "/lib/odd name.ftl" as odd}
+     *       
+     *   <li><p>{@code "auto_include"}: Sets the list of auto-includes.
+     *       See {@link Configuration#getAutoIncludes()}
+     *       <br>String value is something like:
+     *       <br>{@code /include/common.ftl, "/include/evil name.ftl"}
+     *       
+     *   <li><p>{@code "lazy_auto_imports"}:
+     *       See {@link Configuration#getLazyAutoImports()}.
+     *       <br>String value: {@code "true"}, {@code "false"} (also the equivalents: {@code "yes"}, {@code "no"},
+     *       {@code "t"}, {@code "f"}, {@code "y"}, {@code "n"}), case insensitive. Also can be {@code "null"}.
+
+     *   <li><p>{@code "lazy_imports"}:
+     *       See {@link Configuration#getLazyImports()}.
+     *       <br>String value: {@code "true"}, {@code "false"} (also the equivalents: {@code "yes"}, {@code "no"},
+     *       {@code "t"}, {@code "f"}, {@code "y"}, {@code "n"}), case insensitive.
+     *       
+     *   <li><p>{@code "new_builtin_class_resolver"}:
+     *       See {@link #setNewBuiltinClassResolver(TemplateClassResolver)}.
+     *       Since 2.3.17.
+     *       The value must be one of these (ignore the quotation marks):
+     *       <ol>
+     *         <li><p>{@code "unrestricted"}:
+     *             Use {@link TemplateClassResolver#UNRESTRICTED_RESOLVER}
+     *         <li><p>{@code "allows_nothing"}:
+     *             Use {@link TemplateClassResolver#ALLOWS_NOTHING_RESOLVER}
+     *         <li><p>Something that contains colon will use
+     *             {@link OptInTemplateClassResolver} and is expected to
+     *             store comma separated values (possibly quoted) segmented
+     *             with {@code "allowed_classes:"} and/or
+     *             {@code "trusted_templates:"}. Examples of valid values:
+     *             
+     *             <table style="width: auto; border-collapse: collapse" border="1"
+     *                  summary="trusted_template value examples">
+     *               <tr>
+     *                 <th>Setting value
+     *                 <th>Meaning
+     *               <tr>
+     *                 <td>
+     *                   {@code allowed_classes: com.example.C1, com.example.C2,
+     *                   trusted_templates: lib/*, safe.ftl}                 
+     *                 <td>
+     *                   Only allow instantiating the {@code com.example.C1} and
+     *                   {@code com.example.C2} classes. But, allow templates
+     *                   within the {@code lib/} directory (like
+     *                   {@code lib/foo/bar.ftl}) and template {@code safe.ftl}
+     *                   (that does not match {@code foo/safe.ftl}, only
+     *                   exactly {@code safe.ftl}) to instantiate anything
+     *                   that {@link TemplateClassResolver#UNRESTRICTED_RESOLVER} allows.
+     *               <tr>
+     *                 <td>
+     *                   {@code allowed_classes: com.example.C1, com.example.C2}
+     *                 <td>Only allow instantiating the {@code com.example.C1} and
+     *                   {@code com.example.C2} classes. There are no
+     *                   trusted templates.
+     *               <tr>
+     *                 <td>
+                         {@code trusted_templates: lib/*, safe.ftl}                 
+     *                 <td>
+     *                   Do not allow instantiating any classes, except in
+     *                   templates inside {@code lib/} or in template 
+     *                   {@code safe.ftl}.
+     *             </table>
+     *             
+     *             <p>For more details see {@link OptInTemplateClassResolver}.
+     *             
+     *         <li><p>Otherwise if the value contains dot, it's interpreted as an <a href="#fm_obe">object builder
+     *             expression</a>.
+     *       </ol>
+     *       Note that the {@code safer} option was removed in FreeMarker 3.0.0, as it has become equivalent with
+     *       {@code "unrestricted"}, as the classes it has blocked were removed from FreeMarker.
+     *   <li><p>{@code "show_error_tips"}:
+     *       See {@link #setShowErrorTips(boolean)}.
+     *       Since 2.3.21.
+     *       <br>String value: {@code "true"}, {@code "false"}, {@code "y"},  etc.
+     *       
+     *   <li><p>{@code api_builtin_enabled}:
+     *       See {@link #setAPIBuiltinEnabled(boolean)}.
+     *       Since 2.3.22.
+     *       <br>String value: {@code "true"}, {@code "false"}, {@code "y"},  etc.
+     *       
+     * </ul>
+     * 
+     * <p>{@link Configuration} (a subclass of {@link MutableProcessingConfiguration}) also understands these:</p>
+     * <ul>
+     *   <li><p>{@code "auto_escaping"}:
+     *       See {@link Configuration#getAutoEscapingPolicy()}
+     *       <br>String value: {@code "enable_if_default"} or {@code "enableIfDefault"} for
+     *       {@link ParsingConfiguration#ENABLE_IF_DEFAULT_AUTO_ESCAPING_POLICY},
+     *       {@code "enable_if_supported"} or {@code "enableIfSupported"} for
+     *       {@link ParsingConfiguration#ENABLE_IF_SUPPORTED_AUTO_ESCAPING_POLICY}
+     *       {@code "disable"} for {@link ParsingConfiguration#DISABLE_AUTO_ESCAPING_POLICY}.
+     *       
+     *   <li><p>{@code "sourceEncoding"}:
+     *       See {@link Configuration#getSourceEncoding()}; since 2.3.26 also accepts value "JVM default"
+     *       (not case sensitive) to set the Java environment default value.
+     *       <br>As the default value is the system default, which can change
+     *       from one server to another, <b>you should always set this!</b>
+     *       
+     *   <li><p>{@code "localized_lookup"}:
+     *       See {@link Configuration#getLocalizedLookup()}.
+     *       <br>String value: {@code "true"}, {@code "false"} (also the equivalents: {@code "yes"}, {@code "no"},
+     *       {@code "t"}, {@code "f"}, {@code "y"}, {@code "n"}).
+     *       ASTDirCase insensitive.
+     *       
+     *   <li><p>{@code "output_format"}:
+     *       See {@link ParsingConfiguration#getOutputFormat()}.
+     *       <br>String value: {@code "default"} (case insensitive) for the default, or an
+     *       <a href="#fm_obe">object builder expression</a> that gives an {@link OutputFormat}, for example
+     *       {@code HTMLOutputFormat} or {@code XMLOutputFormat}.
+     *       
+     *   <li><p>{@code "registered_custom_output_formats"}:
+     *       See {@link Configuration#getRegisteredCustomOutputFormats()}.
+     *       <br>String value: an <a href="#fm_obe">object builder expression</a> that gives a {@link List} of
+     *       {@link OutputFormat}-s.
+     *       Example: {@code [com.example.MyOutputFormat(), com.example.MyOtherOutputFormat()]}
+     *       
+     *   <li><p>{@code "whitespace_stripping"}:
+     *       See {@link ParsingConfiguration#getWhitespaceStripping()}.
+     *       <br>String value: {@code "true"}, {@code "false"}, {@code yes}, etc.
+     *       
+     *   <li><p>{@code "cache_storage"}:
+     *       See {@link Configuration#getCacheStorage()}.
+     *       <br>String value: If the value contains dot, then it's interpreted as an <a href="#fm_obe">object builder
+     *       expression</a>.
+     *       If the value does not contain dot,
+     *       then a {@link org.apache.freemarker.core.templateresolver.impl.MruCacheStorage} will be used with the
+     *       maximum strong and soft sizes specified with the setting value. Examples
+     *       of valid setting values:
+     *       
+     *       <table style="width: auto; border-collapse: collapse" border="1" summary="cache_storage value examples">
+     *         <tr><th>Setting value<th>max. strong size<th>max. soft size
+     *         <tr><td>{@code "strong:50, soft:500"}<td>50<td>500
+     *         <tr><td>{@code "strong:100, soft"}<td>100<td>{@code Integer.MAX_VALUE}
+     *         <tr><td>{@code "strong:100"}<td>100<td>0
+     *         <tr><td>{@code "soft:100"}<td>0<td>100
+     *         <tr><td>{@code "strong"}<td>{@code Integer.MAX_VALUE}<td>0
+     *         <tr><td>{@code "soft"}<td>0<td>{@code Integer.MAX_VALUE}
+     *       </table>
+     *       
+     *       <p>The value is not case sensitive. The order of <tt>soft</tt> and <tt>strong</tt>
+     *       entries is not significant.
+     *       
+     *   <li><p>{@code "template_update_delay"}:
+     *       Template update delay in <b>seconds</b> (not in milliseconds) if no unit is specified; see
+     *       {@link Configuration#getTemplateUpdateDelayMilliseconds()} for more.
+     *       <br>String value: Valid positive integer, optionally followed by a time unit (recommended). The default
+     *       unit is seconds. It's strongly recommended to specify the unit for clarity, like in "500 ms" or "30 s".
+     *       Supported units are: "s" (seconds), "ms" (milliseconds), "m" (minutes), "h" (hours). The whitespace between
+     *       the unit and the number is optional. Units are only supported since 2.3.23.
+     *       
+     *   <li><p>{@code "tag_syntax"}:
+     *       See {@link ParsingConfiguration#getTagSyntax()}.
+     *       <br>String value: Must be one of
+     *       {@code "auto_detect"}, {@code "angle_bracket"}, and {@code "square_bracket"}. 
+     *       
+     *   <li><p>{@code "naming_convention"}:
+     *       See {@link ParsingConfiguration#getNamingConvention()}.
+     *       <br>String value: Must be one of
+     *       {@code "auto_detect"}, {@code "legacy"}, and {@code "camel_case"}.
+     *       
+     *   <li><p>{@code "incompatible_improvements"}:
+     *       See {@link Configuration#getIncompatibleImprovements()}.
+     *       <br>String value: version number like {@code 2.3.20}.
+     *       
+     *   <li><p>{@code "recognize_standard_file_extensions"}:
+     *       See {@link Configuration#getRecognizeStandardFileExtensions()}.
+     *       <br>String value: {@code "default"} (case insensitive) for the default, or {@code "true"}, {@code "false"},
+     *       {@code yes}, etc.
+     *       
+     *   <li><p>{@code "template_configurations"}:
+     *       See: {@link Configuration#getTemplateConfigurations()}.
+     *       <br>String value: Interpreted as an <a href="#fm_obe">object builder expression</a>,
+     *       can be {@code null}.
+     *       
+     *   <li><p>{@code "template_loader"}:
+     *       See: {@link Configuration#getTemplateLoader()}.
+     *       <br>String value: {@code "default"} (case insensitive) for the default, or else interpreted as an
+     *       <a href="#fm_obe">object builder expression</a>. {@code "null"} is also allowed.
+     *       
+     *   <li><p>{@code "template_lookup_strategy"}:
+     *       See: {@link Configuration#getTemplateLookupStrategy()}.
+     *       <br>String value: {@code "default"} (case insensitive) for the default, or else interpreted as an
+     *       <a href="#fm_obe">object builder expression</a>.
+     *       
+     *   <li><p>{@code "template_name_format"}:
+     *       See: {@link Configuration#getTemplateNameFormat()}.
+     *       <br>String value: {@code "default"} (case insensitive) for the default, {@code "default_2_3_0"}
+     *       for {@link DefaultTemplateNameFormatFM2#INSTANCE}, {@code "default_2_4_0"} for
+     *       {@link DefaultTemplateNameFormat#INSTANCE}.
+     * </ul>
+     * 
+     * <p><a name="fm_obe"></a>Regarding <em>object builder expressions</em> (used by the setting values where it was
+     * indicated):
+     * <ul>
+     *   <li><p>Before FreeMarker 2.3.21 it had to be a fully qualified class name, and nothing else.</li>
+     *   <li><p>Since 2.3.21, the generic syntax is:
+     *       <tt><i>className</i>(<i>constrArg1</i>, <i>constrArg2</i>, ... <i>constrArgN</i>,
+     *       <i>propName1</i>=<i>propValue1</i>, <i>propName2</i>=<i>propValue2</i>, ...
+     *       <i>propNameN</i>=<i>propValueN</i>)</tt>,
+     *       where
+     *       <tt><i>className</i></tt> is the fully qualified class name of the instance to invoke (except if we have
+     *       builder class or <tt>INSTANCE</tt> field around, but see that later),
+     *       <tt><i>constrArg</i></tt>-s are the values of constructor arguments,
+     *       and <tt><i>propName</i>=<i>propValue</i></tt>-s set JavaBean properties (like <tt>x=1</tt> means
+     *       <tt>setX(1)</tt>) on the created instance. You can have any number of constructor arguments and property
+     *       setters, including 0. Constructor arguments must precede any property setters.   
+     *   </li>
+     *   <li>
+     *     Example: <tt>com.example.MyObjectWrapper(1, 2, exposeFields=true, cacheSize=5000)</tt> is nearly
+     *     equivalent with this Java code:
+     *     <tt>obj = new com.example.MyObjectWrapper(1, 2); obj.setExposeFields(true); obj.setCacheSize(5000);</tt>
+     *   </li>
+     *   <li>
+     *      <p>If you have no constructor arguments and property setters, and the <tt><i>className</i></tt> class has
+     *      a public static {@code INSTANCE} field, the value of that filed will be the value of the expression, and
+     *      the constructor won't be called.
+     *   </li>
+     *   <li>
+     *      <p>If there exists a class named <tt><i>className</i>Builder</tt>, then that class will be instantiated
+     *      instead with the given constructor arguments, and the JavaBean properties of that builder instance will be
+     *      set. After that, the public <tt>build()</tt> method of the instance will be called, whose return value
+     *      will be the value of the whole expression. (The builder class and the <tt>build()</tt> method is simply
+     *      found by name, there's no special interface to implement.)Note that if you have a builder class, you don't
+     *      actually need a <tt><i>className</i></tt> class (since 2.3.24); after all,
+     *      <tt><i>className</i>Builder.build()</tt> can return any kind of object. 
+     *   </li>
+     *   <li>
+     *      <p>Currently, the values of arguments and properties can only be one of these:
+     *      <ul>
+     *        <li>A numerical literal, like {@code 123} or {@code -1.5}. The value will be automatically converted to
+     *        the type of the target (just like in FTL). However, a target type is only available if the number will
+     *        be a parameter to a method or constructor, not when it's a value (or key) in a {@code List} or
+     *        {@code Map} literal. Thus in the last case the type of number will be like in Java language, like
+     *        {@code 1} is an {@code int}, and {@code 1.0} is a {@code double}, and {@code 1.0f} is a {@code float},
+     *        etc. In all cases, the standard Java type postfixes can be used ("f", "d", "l"), plus "bd" for
+     *        {@code BigDecimal} and "bi" for {@code BigInteger}.</li>
+     *        <li>A boolean literal: {@code true} or {@code false}
+     *        <li>The null literal: {@code null}
+     *        <li>A string literal with FTL syntax, except that  it can't contain <tt>${...}</tt>-s and
+     *            <tt>#{...}</tt>-s. Examples: {@code "Line 1\nLine 2"} or {@code r"C:\temp"}.
+     *        <li>A list literal (since 2.3.24) with FTL-like syntax, for example {@code [ 'foo', 2, true ]}.
+     *            If the parameter is expected to be array, the list will be automatically converted to array.
+     *            The list items can be any kind of expression, like even object builder expressions.
+     *        <li>A map literal (since 2.3.24) with FTL-like syntax, for example <code>{ 'foo': 2, 'bar': true }</code>.
+     *            The keys and values can be any kind of expression, like even object builder expressions.
+     *            The resulting Java object will be a {@link Map} that keeps the item order ({@link LinkedHashMap} as
+     *            of this writing).
+     *        <li>A reference to a public static filed, like {@code Configuration.AUTO_DETECT_TAG_SYNTAX} or
+     *            {@code com.example.MyClass.MY_CONSTANT}.
+     *        <li>An object builder expression. That is, object builder expressions can be nested into each other. 
+     *      </ul>
+     *   </li>
+     *   <li>
+     *     The same kind of expression as for parameters can also be used as top-level expressions (though it's
+     *     rarely useful, apart from using {@code null}).
+     *   </li>
+     *   <li>
+     *     <p>The top-level object builder expressions may omit {@code ()}.
+     *   </li>
+     *   <li>
+     *     <p>The following classes can be referred to with simple (unqualified) name instead of fully qualified name:
+     *     {@link DefaultObjectWrapper}, {@link DefaultObjectWrapper}, {@link RestrictedObjectWrapper}, {@link Locale},
+     *     {@link TemplateConfiguration}, {@link PathGlobMatcher}, {@link FileNameGlobMatcher}, {@link PathRegexMatcher},
+     *     {@link AndMatcher}, {@link OrMatcher}, {@link NotMatcher}, {@link ConditionalTemplateConfigurationFactory},
+     *     {@link MergingTemplateConfigurationFactory}, {@link FirstMatchTemplateConfigurationFactory},
+     *     {@link HTMLOutputFormat}, {@link XMLOutputFormat}, {@link RTFOutputFormat}, {@link PlainTextOutputFormat},
+     *     {@link UndefinedOutputFormat}, {@link Configuration}, {@link TemplateLanguage}.
+     *   </li>
+     *   <li>
+     *     <p>{@link TimeZone} objects can be created like {@code TimeZone("UTC")}, despite that there's no a such
+     *     constructor.
+     *   </li>
+     *   <li>
+     *     <p>{@link Charset} objects can be created like {@code Charset("ISO-8859-5")}, despite that there's no a such
+     *     constructor.
+     *   </li>
+     *   <li>
+     *     <p>The classes and methods that the expression meant to access must be all public.
+     *   </li>
+     * </ul>
+     * 
+     * @param name the name of the setting.
+     * @param value the string that describes the new value of the setting.
+     * 
+     * @throws UnknownConfigurationSettingException if the name is wrong.
+     * @throws ConfigurationSettingValueException if the new value of the setting can't be set for any other reasons.
+     */
+    public void setSetting(String name, String value) throws ConfigurationException {
+        boolean unknown = false;
+        try {
+            if (LOCALE_KEY.equals(name)) {
+                if (JVM_DEFAULT_VALUE.equalsIgnoreCase(value)) {
+                    setLocale(Locale.getDefault());
+                } else {
+                    setLocale(_StringUtil.deduceLocale(value));
+                }
+            } else if (NUMBER_FORMAT_KEY_SNAKE_CASE.equals(name) || NUMBER_FORMAT_KEY_CAMEL_CASE.equals(name)) {
+                setNumberFormat(value);
+            } else if (CUSTOM_NUMBER_FORMATS_KEY_SNAKE_CASE.equals(name)
+                    || CUSTOM_NUMBER_FORMATS_KEY_CAMEL_CASE.equals(name)) {
+                Map map = (Map) _ObjectBuilderSettingEvaluator.eval(
+                                value, Map.class, false, _SettingEvaluationEnvironment.getCurrent());
+                checkSettingValueItemsType("Map keys", String.class, map.keySet());
+                checkSettingValueItemsType("Map values", TemplateNumberFormatFactory.class, map.values());
+                setCustomNumberFormats(map);
+            } else if (TIME_FORMAT_KEY_SNAKE_CASE.equals(name) || TIME_FORMAT_KEY_CAMEL_CASE.equals(name)) {
+                setTimeFormat(value);
+            } else if (DATE_FORMAT_KEY_SNAKE_CASE.equals(name) || DATE_FORMAT_KEY_CAMEL_CASE.equals(name)) {
+                setDateFormat(value);
+            } else if (DATETIME_FORMAT_KEY_SNAKE_CASE.equals(name) || DATETIME_FORMAT_KEY_CAMEL_CASE.equals(name)) {
+                setDateTimeFormat(value);
+            } else if (CUSTOM_DATE_FORMATS_KEY_SNAKE_CASE.equals(name)
+                    || CUSTOM_DATE_FORMATS_KEY_CAMEL_CASE.equals(name)) {
+                Map map = (Map) _ObjectBuilderSettingEvaluator.eval(
+                                value, Map.class, false, _SettingEvaluationEnvironment.getCurrent());
+                checkSettingValueItemsType("Map keys", String.class, map.keySet());
+                checkSettingValueItemsType("Map values", TemplateDateFormatFactory.class, map.values());
+                setCustomDateFormats(map);
+            } else if (TIME_ZONE_KEY_SNAKE_CASE.equals(name) || TIME_ZONE_KEY_CAMEL_CASE.equals(name)) {
+                setTimeZone(parseTimeZoneSettingValue(value));
+            } else if (SQL_DATE_AND_TIME_TIME_ZONE_KEY_SNAKE_CASE.equals(name)
+                    || SQL_DATE_AND_TIME_TIME_ZONE_KEY_CAMEL_CASE.equals(name)) {
+                setSQLDateAndTimeTimeZone(value.equals("null") ? null : parseTimeZoneSettingValue(value));
+            } else if (TEMPLATE_EXCEPTION_HANDLER_KEY_SNAKE_CASE.equals(name)
+                    || TEMPLATE_EXCEPTION_HANDLER_KEY_CAMEL_CASE.equals(name)) {
+                if (value.indexOf('.') == -1) {
+                    if ("debug".equalsIgnoreCase(value)) {
+                        setTemplateExceptionHandler(
+                                TemplateExceptionHandler.DEBUG_HANDLER);
+                    } else if ("html_debug".equalsIgnoreCase(value) || "htmlDebug".equals(value)) {
+                        setTemplateExceptionHandler(
+                                TemplateExceptionHandler.HTML_DEBUG_HANDLER);
+                    } else if ("ignore".equalsIgnoreCase(value)) {
+                        setTemplateExceptionHandler(
+                                TemplateExceptionHandler.IGNORE_HANDLER);
+                    } else if ("rethrow".equalsIgnoreCase(value)) {
+                        setTemplateExceptionHandler(
+                                TemplateExceptionHandler.RETHROW_HANDLER);
+                    } else if (DEFAULT_VALUE.equalsIgnoreCase(value)
+                            && this instanceof Configuration.ExtendableBuilder) {
+                        unsetTemplateExceptionHandler();
+                    } else {
+                        throw new ConfigurationSettingValueException(
+                                name, value,
+                                "No such predefined template exception handler name");
+                    }
+                } else {
+                    setTemplateExceptionHandler((TemplateExceptionHandler) _ObjectBuilderSettingEvaluator.eval(
+                            value, TemplateExceptionHandler.class, false, _SettingEvaluationEnvironment.getCurrent()));
+                }
+            } else if (ARITHMETIC_ENGINE_KEY_SNAKE_CASE.equals(name) || ARITHMETIC_ENGINE_KEY_CAMEL_CASE.equals(name)) {
+                if (value.indexOf('.') == -1) { 
+                    if ("bigdecimal".equalsIgnoreCase(value)) {
+                        setArithmeticEngine(BigDecimalArithmeticEngine.INSTANCE);
+                    } else if ("conservative".equalsIgnoreCase(value)) {
+                        setArithmeticEngine(ConservativeArithmeticEngine.INSTANCE);
+                    } else {
+                        throw new ConfigurationSettingValueException(
+                                name, value, "No such predefined arithmetical engine name");
+                    }
+                } else {
+                    setArithmeticEngine((ArithmeticEngine) _ObjectBuilderSettingEvaluator.eval(
+                            value, ArithmeticEngine.class, false, _SettingEvaluationEnvironment.getCurrent()));
+                }
+            } else if (OBJECT_WRAPPER_KEY_SNAKE_CASE.equals(name) || OBJECT_WRAPPER_KEY_CAMEL_CASE.equals(name)) {
+                if (DEFAULT_VALUE.equalsIgnoreCase(value)) {
+                    if (this instanceof Configuration.Extend

<TRUNCATED>